/* * This file is part of the Echo Web Application Framework (hereinafter "Echo"). * Copyright (C) 2002-2009 NextApp, Inc. * * 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. * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. */ package nextapp.echo2.webcontainer; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.WeakHashMap; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionEvent; import nextapp.echo2.app.ApplicationInstance; import nextapp.echo2.app.Component; import nextapp.echo2.app.TaskQueueHandle; import nextapp.echo2.app.update.UpdateManager; import nextapp.echo2.webcontainer.util.IdTable; import nextapp.echo2.webrender.Connection; import nextapp.echo2.webrender.UserInstance; /** * Web application container user instance. */ public class ContainerInstance extends UserInstance { /** * Default asynchronous monitor callback interval (in milliseconds). */ private static final int DEFAULT_CALLBACK_INTERVAL = 500; /** * Returns the base HTML element id that should be used when rendering the * specified <code>Component</code>. * * @param component the component * @return the base HTML element id */ public static String getElementId(Component component) { return "c_" + component.getRenderId(); } /** * Creates a new Web Application Container instance using the provided * client <code>Connection</code>. The instance will automatically * be stored in the relevant <code>HttpSession</code> * * @param conn the client/server <code>Connection</code> for which the * instance is being instantiated */ public static void newInstance(Connection conn) { new ContainerInstance(conn); } private ApplicationInstance applicationInstance; private Map componentToRenderStateMap = new HashMap(); private transient IdTable idTable; private boolean initialized = false; private Map initialRequestParameterMap; private transient Map taskQueueToCallbackIntervalMap; /** * Creates a new <code>ContainerInstance</code>. * * @param conn the client/server <code>Connection</code> for which the * instance is being instantiated * @see #newInstance(nextapp.echo2.webrender.Connection) */ private ContainerInstance(Connection conn) { super(conn); setServerDelayMessage(DefaultServerDelayMessage.INSTANCE); initialRequestParameterMap = new HashMap(conn.getRequest().getParameterMap()); } /** * Returns the corresponding <code>ApplicationInstance</code> * for this user instance. * * @return the relevant <code>ApplicationInstance</code> */ public ApplicationInstance getApplicationInstance() { return applicationInstance; } /** * Determines the application-specified asynchronous monitoring * service callback interval. * * @return the callback interval, in ms */ public int getCallbackInterval() { if (taskQueueToCallbackIntervalMap == null || taskQueueToCallbackIntervalMap.size() == 0) { return DEFAULT_CALLBACK_INTERVAL; } Iterator it = taskQueueToCallbackIntervalMap.values().iterator(); int returnInterval = Integer.MAX_VALUE; while (it.hasNext()) { int interval = ((Integer) it.next()).intValue(); if (interval < returnInterval) { returnInterval = interval; } } return returnInterval; } /** * Retrieves the <code>Component</code> with the specified element id. * * @param elementId the element id, e.g., "c_42323" * @return the component (e.g., the component whose id is "42323") */ public Component getComponentByElementId(String elementId) { try { return applicationInstance.getComponentByRenderId(elementId.substring(2)); } catch (IndexOutOfBoundsException ex) { throw new IllegalArgumentException("Invalid component element id: " + elementId); } } /** * Retrieves the <code>IdTable</code> used by this * <code>ContainerInstance</code> to assign weakly-referenced unique * identifiers to arbitrary objects. * * @return the <code>IdTable</code> */ public IdTable getIdTable() { if (idTable == null) { idTable = new IdTable(); } return idTable; } /** * Returns an immutable <code>Map</code> containing the HTTP form * parameters sent on the initial request to the application. * * @return the initial request parameter map */ public Map getInitialRequestParameterMap() { return initialRequestParameterMap; } /** * Retrieves the <code>RenderState</code> of the specified * <code>Component</code>. * * @param component the component * @return the rendering state */ public RenderState getRenderState(Component component) { return (RenderState) componentToRenderStateMap.get(component); } /** * Convenience method to retrieve the application's * <code>UpdateManager</code>, which is used to synchronize * client and server states. * This method is equivalent to invoking * <code>getApplicationInstance().getUpdateManager()</code>. * * @return the <code>UpdateManager</code> */ public UpdateManager getUpdateManager() { return applicationInstance.getUpdateManager(); } /** * Initializes the <code>ContainerInstance</code>, creating an instance * of the target <code>ApplicationInstance</code> and initializing the state * of the application. * * @param conn the relevant <code>Connection</code> */ public void init(Connection conn) { if (initialized) { throw new IllegalStateException("Attempt to invoke ContainerInstance.init() on initialized instance."); } WebContainerServlet servlet = (WebContainerServlet) conn.getServlet(); applicationInstance = servlet.newApplicationInstance(); ContainerContext containerContext = new ContainerContextImpl(this); applicationInstance.setContextProperty(ContainerContext.CONTEXT_PROPERTY_NAME, containerContext); try { ApplicationInstance.setActive(applicationInstance); applicationInstance.doInit(); } finally { ApplicationInstance.setActive(null); } initialized = true; } /** * Determines if the <code>ContainerInstance</code> has been initialized, * i.e., whether its <code>init()</code> method has been invoked. * * @return true if the <code>ContainerInstance</code> is initialized */ boolean isInitialized() { return initialized; } /** * Removes the <code>RenderState</code> of the specified * <code>Component</code>. * * @param component the component */ public void removeRenderState(Component component) { componentToRenderStateMap.remove(component); } /** * Sets the <code>RenderState</code> of the specified * <code>Component</code>. * * @param component the component * @param renderState the render state */ public void setRenderState(Component component, RenderState renderState) { componentToRenderStateMap.put(component, renderState); } /** * Sets the interval between asynchronous callbacks from the client to check * for queued tasks for a given <code>TaskQueue</code>. If multiple * <code>TaskQueue</code>s are active, the smallest specified interval should * be used. The default interval is 500ms. * Application access to this method should be accessed via the * <code>ContainerContext</code>. * * @param taskQueue the <code>TaskQueue</code> * @param ms the number of milliseconds between asynchronous client * callbacks * @see nextapp.echo2.webcontainer.ContainerContext#setTaskQueueCallbackInterval(nextapp.echo2.app.TaskQueueHandle, int) */ public void setTaskQueueCallbackInterval(TaskQueueHandle taskQueue, int ms) { if (taskQueueToCallbackIntervalMap == null) { taskQueueToCallbackIntervalMap = new WeakHashMap(); } taskQueueToCallbackIntervalMap.put(taskQueue, new Integer(ms)); } /** * @see javax.servlet.http.HttpSessionActivationListener#sessionDidActivate(javax.servlet.http.HttpSessionEvent) */ public void sessionDidActivate(HttpSessionEvent e) { super.sessionDidActivate(e); if (applicationInstance != null) { applicationInstance.activate(); } } /** * @see javax.servlet.http.HttpSessionActivationListener#sessionWillPassivate(javax.servlet.http.HttpSessionEvent) */ public void sessionWillPassivate(HttpSessionEvent e) { if (applicationInstance != null) { applicationInstance.passivate(); } super.sessionWillPassivate(e); } /** * @see javax.servlet.http.HttpSessionBindingListener#valueUnbound(javax.servlet.http.HttpSessionBindingEvent) */ public void valueUnbound(HttpSessionBindingEvent e) { if (applicationInstance != null) { applicationInstance.dispose(); } super.valueUnbound(e); } }