com.icesoft.faces.async.render.RenderManager.java Source code

Java tutorial

Introduction

Here is the source code for com.icesoft.faces.async.render.RenderManager.java

Source

/*
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * "The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * The Original Code is ICEfaces 1.5 open source software code, released
 * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
 * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
 * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 *
 * Contributor(s): _____________________.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
 * License), in which case the provisions of the LGPL License are
 * applicable instead of those above. If you wish to allow use of your
 * version of this file only under the terms of the LGPL License and not to
 * allow others to use your version of this file under the MPL, indicate
 * your decision by deleting the provisions above and replace them with
 * the notice and other provisions required by the LGPL License. If you do
 * not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the LGPL License."
 *
 */

package com.icesoft.faces.async.render;

import com.icesoft.faces.util.event.servlet.ContextEventRepeater;
import com.icesoft.faces.webapp.http.common.Configuration;
import com.icesoft.faces.webapp.http.servlet.ServletContextConfiguration;
import edu.emory.mathcs.backport.java.util.concurrent.ScheduledThreadPoolExecutor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * The RenderManager is the central class for developers wanting to do
 * server-initiated rendering.  The recommended use is to have a single
 * RenderManager for your application, typically configured as a
 * application-scope, managed bean.  Any class that needs to request server side
 * render calls can do so using the RenderManager.
 * <p/>
 * Server-initiated renders can be requested directly via the {@link
 * RenderManager#requestRender requestRender} method or you get a named {@link
 * GroupAsyncRenderer} and add a Renderable implementation to request and
 * receive render calls as part of a group.
 * <p/>
 * The RenderManager contains a reference to a single {@link RenderHub} that
 * uses a special queue and thread pool to optimize render calls for reliability
 * and scalability.
 */
public class RenderManager implements Disposable {
    static final int MIN = 1;
    public static final int ON_DEMAND = 2;
    public static final int INTERVAL = 3;
    public static final int DELAY = 4;
    static final int MAX = 4;

    private static RenderManager internalRenderManager;

    /**
     * No argument constructor suitable for using as a managed bean.
     */
    public RenderManager() {
        // do nothing.
    }

    /**
     * This is typically called when the application is shutting down and we
     * need to dispose of all the RenderManager's resources.  It iterates
     * through all of the named {@link GroupAsyncRenderer}s, calling dispose on
     * each of them in turn.  It then calls dispose on the {@link RenderHub}.
     * Once the RenderManager has been disposed, it can no longer be used.
     *
     * @deprecated
     */
    public void dispose() {
        // do nothing.
    }

    public BroadcastRenderer getBroadcastRenderer() {
        return internalRenderManager != null ? internalRenderManager.getBroadcastRenderer() : null;
    }

    /**
     * The DelayRenderer is a {@link GroupAsyncRenderer} that requests a
     * server-initiated render of all the Renderables in its collection.  The
     * request to render is made once at a set point in the future.
     * <p/>
     * This method returns the appropriate GroupAsyncRenderer based on the
     * renderer name. If the name is new, a new instance is created and stored
     * in the RenderManager's collection.  If the named GroupAsyncRenderer
     * already exists, and it's of the proper type, then the existing instance
     * is returned.
     *
     * @param rendererName the name of the GroupAsyncRenderer
     * @return a new or existing GroupAsyncRenderer, based on the renderer name
     */
    public DelayRenderer getDelayRenderer(final String rendererName) {
        return internalRenderManager != null ? internalRenderManager.getDelayRenderer(rendererName) : null;
    }

    public static RenderManager getInstance() {
        return internalRenderManager;
    }

    /**
     * The IntervalRenderer is a {@link GroupAsyncRenderer} that requests a
     * server-initiated render of all the Renderables in its collection.  The
     * request to render is made repeatedly at the set interval.
     * <p/>
     * This method returns the appropriate GroupAsyncRenderer based on the
     * renderer name. If the name is new, a new instance is created and stored
     * in the RenderManager's collection.  If the named GroupAsyncRenderer
     * already exists, and it's of the proper type, then the existing instance
     * is returned.
     *
     * @param rendererName the name of the GroupAsyncRenderer
     * @return a new or existing GroupAsyncRenderer, based on the renderer name
     */
    public IntervalRenderer getIntervalRenderer(final String rendererName) {
        return internalRenderManager != null ? internalRenderManager.getIntervalRenderer(rendererName) : null;
    }

    /**
     * The OnDemandRenderer is a {@link GroupAsyncRenderer} that requests a
     * server-initiated render of all the Renderables in its collection.  The
     * request is made immediately upon request.
     * <p/>
     * This method returns the appropriate GroupAsyncRenderer based on the
     * renderer name. If the name is new, a new instance is created and stored
     * in the RenderManager's collection.  If the named GroupAsyncRenderer
     * already exists, and it's of the proper type, then the existing instance
     * is returned.
     *
     * @param rendererName the name of the GroupAsyncRenderer
     * @return a new or existing GroupAsyncRenderer, based on the renderer name
     */
    public OnDemandRenderer getOnDemandRenderer(final String rendererName) {
        return internalRenderManager != null ? internalRenderManager.getOnDemandRenderer(rendererName) : null;
    }

    /**
     * Returns the named instance of an AsyncRenderer if it already exists
     * otherwise it returns null.
     *
     * @param rendererName The name of the AsycRender to retrieve.
     * @return An instance of an AsyncRenderer that is associated with the
     *         provided name.
     */
    public AsyncRenderer getRenderer(final String rendererName) {
        return internalRenderManager != null ? internalRenderManager.getRenderer(rendererName) : null;
    }

    public ServletContext getServletContext() {
        return internalRenderManager != null ? internalRenderManager.getServletContext() : null;
    }

    /**
     * @deprecated Replaced by {@link #getBroadcastRenderer()}. 
     */
    public boolean isBroadcasted() {
        return internalRenderManager != null && internalRenderManager.isBroadcasted();
    }

    /**
     * When an AsyncRenderer disposes itself, it also needs to remove itself
     * from the RenderManager's collection.
     *
     * @param renderer The Renderer to remove
     */
    public void removeRenderer(final AsyncRenderer renderer) {
        if (internalRenderManager != null) {
            internalRenderManager.removeRenderer(renderer);
        }
    }

    /**
     * Submits the supplied Renderable instance to the RenderHub for
     * server-initiated render.
     *
     * @param renderable The {@link Renderable} instance to render.
     */
    public void requestRender(final Renderable renderable) {
        if (internalRenderManager != null) {
            internalRenderManager.requestRender(renderable);
        }
    }

    /**
     * @deprecated Replaced by {@link #setBroadcastRenderer(BroadcastRenderer)}.
     */
    public void setBroadcasted(final boolean broadcasted) {
        if (internalRenderManager != null) {
            internalRenderManager.setBroadcasted(broadcasted);
        }
    }

    public void setBroadcastRenderer(final BroadcastRenderer broadcastRenderer) {

        if (internalRenderManager != null) {
            internalRenderManager.setBroadcastRenderer(broadcastRenderer);
        }
    }

    public static synchronized void setServletConfig(final ServletConfig servletConfig) {

        if (internalRenderManager == null) {
            internalRenderManager = new InternalRenderManager(servletConfig.getServletContext());
        }
    }

    public void setCorePoolSize(int corePoolSize) {
        internalRenderManager.setCorePoolSize(corePoolSize);
    }

    public void setMaxPoolSize(int maxPoolSize) {
        internalRenderManager.setMaxPoolSize(maxPoolSize);
    }

    public void setKeepAliveTime(long keepAliveTime) {
        internalRenderManager.setKeepAliveTime(keepAliveTime);
    }

    public void setRenderQueueCapacity(int renderQueueCapacity) {
        internalRenderManager.setRenderQueueCapacity(renderQueueCapacity);
    }

    /**
     * This method is used by {@link GroupAsyncRenderer}s that need to request
     * render calls based on some sort of schedule.  It uses a separate,
     * configurable thread pool and queue than the core rendering service.
     *
     * @return the scheduled executor for this RenderManager
     */
    ScheduledThreadPoolExecutor getScheduledService() {
        return internalRenderManager != null ? internalRenderManager.getScheduledService() : null;
    }

    void requestRender(final AsyncRenderer renderer) {
        if (internalRenderManager != null) {
            internalRenderManager.requestRender(renderer);
        }
    }

    private static class InternalRenderManager extends RenderManager implements Disposable {
        private static final Log LOG = LogFactory.getLog(InternalRenderManager.class);

        private final ServletContext servletContext;
        private final RenderHub renderHub;
        private final Map rendererGroupMap = Collections.synchronizedMap(new HashMap());
        private final ContextDestroyedListener shutdownListener = new ContextDestroyedListener(this);
        private BroadcastRenderer broadcastRenderer;

        private InternalRenderManager(final ServletContext servletContext) {
            this.servletContext = servletContext;
            Configuration configuration = new ServletContextConfiguration("com.icesoft.faces.async.render",
                    this.servletContext);
            renderHub = new RenderHub(configuration);
            setBroadcasted(configuration.getAttributeAsBoolean("broadcasted", false));
            ContextEventRepeater.addListener(shutdownListener);
        }

        public void dispose() {
            synchronized (rendererGroupMap) {
                /*
                 * Bug 944
                 * We make a copy of the list of renderers to remove simply to
                 * iterate through them.  This avoids a concurrent modification
                 * issue when each individual renderers dispose method attempts
                 * to remove itself from the official groupMap.
                 */
                Iterator renderers = new ArrayList(rendererGroupMap.values()).iterator();
                while (renderers.hasNext()) {
                    AsyncRenderer renderer = (AsyncRenderer) renderers.next();
                    renderer.dispose();
                    if (LOG.isTraceEnabled()) {
                        LOG.trace("Renderer disposed: " + renderer);
                    }
                }
                rendererGroupMap.clear();
            }
            renderHub.dispose();
            LOG.debug("All renderers and render hub have been disposed.");
            ContextEventRepeater.removeListener(shutdownListener);
        }

        public BroadcastRenderer getBroadcastRenderer() {
            return broadcastRenderer;
        }

        public DelayRenderer getDelayRenderer(final String rendererName) {
            return (DelayRenderer) getRenderer(rendererName, DELAY);
        }

        public IntervalRenderer getIntervalRenderer(final String rendererName) {
            return (IntervalRenderer) getRenderer(rendererName, INTERVAL);
        }

        public OnDemandRenderer getOnDemandRenderer(final String rendererName) {
            return (OnDemandRenderer) getRenderer(rendererName, ON_DEMAND);
        }

        public AsyncRenderer getRenderer(final String rendererName) {
            if (rendererName == null) {
                return null;
            }
            return (AsyncRenderer) rendererGroupMap.get(rendererName);
        }

        public ServletContext getServletContext() {
            return servletContext;
        }

        public boolean isBroadcasted() {
            return broadcastRenderer != null;
        }

        public void removeRenderer(final AsyncRenderer renderer) {
            if (renderer == null) {
                if (LOG.isInfoEnabled()) {
                    LOG.info("Renderer is null");
                }
            } else if (rendererGroupMap.remove(renderer.getName()) == null) {
                if (LOG.isTraceEnabled()) {
                    LOG.trace("Renderer " + renderer.getName() + " not found");
                }
            } else {
                if (LOG.isTraceEnabled()) {
                    LOG.trace("Renderer " + renderer.getName() + " removed");
                }
            }
        }

        public void requestRender(final Renderable renderable) {
            renderHub.requestRender(renderable);
        }

        public void setBroadcasted(final boolean broadcasted) {
            // do nothing.
        }

        public void setCorePoolSize(int corePoolSize) {
            renderHub.setCorePoolSize(corePoolSize);
        }

        public void setMaxPoolSize(int maxPoolSize) {
            renderHub.setMaxPoolSize(maxPoolSize);
        }

        public void setKeepAliveTime(long keepAliveTime) {
            renderHub.setKeepAliveTime(keepAliveTime);
        }

        public void setBroadcastRenderer(final BroadcastRenderer broadcastRenderer) {

            this.broadcastRenderer = broadcastRenderer;
        }

        public void setRenderQueueCapacity(int renderQueueCapacity) {
            renderHub.setRenderQueueCapacity(renderQueueCapacity);
        }

        ScheduledThreadPoolExecutor getScheduledService() {
            return renderHub.getScheduledService();
        }

        void requestRender(final AsyncRenderer renderer) {
            if (renderer == null) {
                return;
            }
            if (broadcastRenderer != null && isBroadcasted() && renderer.isBroadcasted()) {

                broadcastRenderer.requestRender(renderer);
            }
        }

        /**
         * Internal utility method that returns the proper type of {@link
         * GroupAsyncRenderer} and ensures that is added to the managed
         * collection, indexed by name.
         *
         * @param name the unique name of the GroupAsyncRenderer
         * @param type the type of GroupAsyncRenderer
         * @return the requested type of GroupAsyncRenderer
         * @throws IllegalArgumentException if the <code>name</code> is <code>null</code> or
         *                                  empty, or if the <code>type</code> is illegal.
         */
        private synchronized AsyncRenderer getRenderer(final String name, final int type)
                throws IllegalArgumentException {

            if (name == null || name.trim().length() == 0) {
                throw new IllegalArgumentException("Illegal renderer name: " + name);
            }
            if (type < MIN || type > MAX) {
                throw new IllegalArgumentException("Illegal renderer type: " + type);
            }
            if (rendererGroupMap.containsKey(name)) {
                if (LOG.isTraceEnabled()) {
                    LOG.trace("Existing renderer retrieved: " + name);
                }
                return (AsyncRenderer) rendererGroupMap.get(name);
            }
            AsyncRenderer renderer;
            switch (type) {
            case ON_DEMAND:
                renderer = new OnDemandRenderer();
                break;
            case INTERVAL:
                renderer = new IntervalRenderer();
                break;
            case DELAY:
                renderer = new DelayRenderer();
                break;
            default:
                // this shouldn't happen
                renderer = null;
            }
            if (renderer != null) {
                renderer.setBroadcasted(broadcastRenderer != null);
                renderer.setName(name);
                rendererGroupMap.put(name, renderer);
            }
            if (LOG.isTraceEnabled()) {
                LOG.trace("New renderer retrieved: " + name);
            }
            return renderer;
        }
    }
}