All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nextapp.echo.webcontainer.UserInstanceContainer Maven / Gradle / Ivy

package nextapp.echo.webcontainer;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionEvent;

/**
 * Container / manager of all UserInstance objects in the servlet session.
 */
public class UserInstanceContainer 
implements HttpSessionActivationListener, HttpSessionBindingListener, Serializable {
    
    /**
     * Creates a new Web Application Container instance using the provided
     * client Connection.  The instance will automatically
     * be stored in the relevant HttpSession
     * 
     * @param conn the client/server Connection for which the 
     *        instance is being instantiated
     */
    public static void newInstance(Connection conn) {
        new UserInstanceContainer(conn);
    }
    
    /**
     * Sequential UserInstance identifier generator.
     */
    private int nextUserInstanceId = 0;
    
    /**
     * Sequential initial request identifier generator. 
     */
    private int nextInitId = 0;
    
    /**
     * The default character encoding in which responses should be rendered.
     */
    private String characterEncoding = "UTF-8";
    
    /**
     * The URI of the servlet.
     */
    private String servletUri;
    
    /**
     * Mapping between client-generated unique browser window identifiers and UserInstance values.
     */
    private Map clientWindowIdToUserInstance = new HashMap();
    
    /**
     * Mapping between UserInstance identifiers and UserInstance values.
     */
    private Map idToUserInstance = new HashMap();
    
    /**
     * Mapping between initial request identifiers (as returned by createInitId()) and maps of initial
     * requested parameters retrieved from HttpServletRequest.getParameterMap().
     */
    private Map initIdToInitialRequestParameterMap = new HashMap();
    
    /**
     * The containing HttpSession.
     */
    private transient HttpSession session;
    
    private boolean windowSpecificUserInstances;

    /**
     * Creates a new UserInstance.
     * 
     * @param conn the client/server Connection for which the
     *        instance is being instantiated
     */
    private UserInstanceContainer(Connection conn) {
        super();
        conn.initUserInstanceContainer(this);
        windowSpecificUserInstances = conn.getServlet().getInstanceMode() == WebContainerServlet.INSTANCE_MODE_WINDOW;
    }
    
    /**
     * Disposes of all contained UserInstances.
     */
    private void dispose() {
        Iterator it = idToUserInstance.values().iterator();
        while (it.hasNext()) {
            UserInstance userInstance = (UserInstance) it.next();
            userInstance.dispose();
        }
    }
    
    /**
     * Creates an initial request identifier.
     * Stores request parameters provided by the connection, such that they may be assigned to the
     * later-generated UserInstance if required.
     * 
     * @param conn the Connection
     * @return a unique initial request identifier
     */
    public String createInitId(Connection conn) {
        Map parameterMap = new HashMap(conn.getRequest().getParameterMap());
        String initId = new Integer(nextInitId++).toString();
        initIdToInitialRequestParameterMap.put(initId, parameterMap);
        return initId;
    }
    
    /**
     * Creates or retrieves a UserInstance for the specified client window identifier and 
     * initial request identifier.  Invoked when a window is loaded for the first time or reloaded.
     * 
     * If a UserInstance exists for the specified clientWindowId, it is
     * returned.
     * 
     * If a UserInstance does not exist for the specified clientWindowId, one is
     * created and stored within this UserInstanceContainer.  The initial request parameters are
     * retrieved from the initialization-id-to-request-parameter-map and stored in the created
     * UserInstance.
     * 
     * @param clientWindowId the client-generated unique browser window identifier
     * @param initId the server-generated (by createInitId()) unique initialization
     *        request identifier
     * @return the existing or created UserInstance
     */
    synchronized UserInstance loadUserInstance(String clientWindowId, String initId) {
        if (!windowSpecificUserInstances) {
            clientWindowId = null;
        }
        UserInstance userInstance = (UserInstance) clientWindowIdToUserInstance.get(clientWindowId);
        if (userInstance == null) {
            String uiid;
            
            if (windowSpecificUserInstances) {
                uiid = new Integer(nextUserInstanceId++).toString();
            } else {
                uiid = null;
            }
            Map initialRequestParameterMap = (Map) initIdToInitialRequestParameterMap.remove(initId);
            userInstance = new UserInstance(this, uiid, clientWindowId, initialRequestParameterMap); 
            clientWindowIdToUserInstance.put(clientWindowId, userInstance);
            idToUserInstance.put(userInstance.getId(), userInstance);
        }
        return userInstance;
    }
    
    /**
     * Unloads a UserInstance from the container.
     * Disposes of the instance.
     * 
     * @param userInstance the instance to unload
     */
    synchronized void unloadUserInstance(UserInstance userInstance) {
        userInstance.dispose();
        clientWindowIdToUserInstance.remove(userInstance.getClientWindowId());
        idToUserInstance.remove(userInstance.getId());
    }
    
    /**
     * Retrieves an existing UserInstance by its assigned user instance identifier.
     * 
     * @param id the UserInstance identifier, i.e., value which would be returned by
     *        the UserInstance's getId() method
     * @return the UserInstnace, or null if none exists
     */
    synchronized UserInstance getUserInstanceById(String id) {
        return (UserInstance) idToUserInstance.get(id);
    }
    
    /**
     * Returns the default character encoding in which responses should be
     * rendered.
     * 
     * @return the default character encoding in which responses should be
     *         rendered
     */
    public String getCharacterEncoding() {
        return characterEncoding;
    }
    
    /**
     * Returns the id of the HTML element that will serve as the Root component.
     * This element must already be present in the DOM when the application is
     * first rendered.
     * 
     * @return the element id
     */
    public String getRootHtmlElementId() {
        return "approot";
    }
    
    /**
     * Returns the HttpSession containing this
     * UserInstanceContainer.
     * 
     * @return the HttpSession
     */
    HttpSession getSession() {
        return session;
    }
    
    /**
     * Determines the URI to invoke the specified Service.
     * 
     * @param service the Service
     * @return the URI
     */
    public String getServiceUri(Service service, String userInstanceId) {
        StringBuffer out = new StringBuffer(getServletUri());
        out.append("?");
        out.append(WebContainerServlet.SERVICE_ID_PARAMETER);
        out.append("=");
        out.append(service.getId());
        if (userInstanceId != null) {
            out.append("&");
            out.append(WebContainerServlet.USER_INSTANCE_ID_PARAMETER);
            out.append("=");
            out.append(userInstanceId);
        }
        return out.toString();
    }
    
    /**
     * Determines the URI to invoke the specified Service with
     * additional request parameters. The additional parameters are provided by
     * way of the parameterNames and parameterValues
     * arrays. The value of a parameter at a specific index in the
     * parameterNames array is provided in the
     * parameterValues array at the same index. The arrays must
     * thus be of equal length. Null values are allowed in the
     * parameterValues array, and in such cases only the parameter
     * name will be rendered in the returned URI.
     * 
     * @param service the Service
     * @param parameterNames the names of the additional URI parameters
     * @param parameterValues the values of the additional URI parameters
     * @return the URI
     */
    public String getServiceUri(Service service, String userInstanceId, String[] parameterNames, String[] parameterValues) {
        StringBuffer out = new StringBuffer(getServletUri());
        out.append("?");
        out.append(WebContainerServlet.SERVICE_ID_PARAMETER);
        out.append("=");
        out.append(service.getId());
        if (userInstanceId != null) {
            out.append("&");
            out.append(WebContainerServlet.USER_INSTANCE_ID_PARAMETER);
            out.append("=");
            out.append(userInstanceId);
        }
        for (int i = 0; i < parameterNames.length; ++i) {
            out.append("&");
            out.append(parameterNames[i]);
            if (parameterValues[i] != null) {
                out.append("=");
                out.append(parameterValues[i]);
            }
        }
        return out.toString();
    }
    
    /**
     * Returns the URI of the servlet managing this UserInstanceContainer.
     * 
     * @return the URI
     */
    public String getServletUri() {
        return servletUri;
    }
    
    /**
     * Sets the URI of the containing servlet.
     * 
     * @param servletUri the servlet URI
     */
    void setServletUri(String servletUri) {
        this.servletUri = servletUri;
    }

    /**
     * @see javax.servlet.http.HttpSessionActivationListener#sessionDidActivate(javax.servlet.http.HttpSessionEvent)
     * 
     * Recreates reference to session.
     * Notifies ApplicationInstance of activation.
     */
    public synchronized void sessionDidActivate(HttpSessionEvent e) {
        session = e.getSession();
        Iterator it = idToUserInstance.values().iterator();
        while (it.hasNext()) {
            UserInstance userInstance = (UserInstance) it.next();
            if (userInstance.getApplicationInstance() != null) {
                userInstance.getApplicationInstance().activate();
            }
        }
    }

    /**
     * @see javax.servlet.http.HttpSessionActivationListener#sessionWillPassivate(javax.servlet.http.HttpSessionEvent)
     * 
     * Notifies ApplicationInstance of passivation.
     * Discards reference to session.
     */
    public synchronized void sessionWillPassivate(HttpSessionEvent e) {
        Iterator it = idToUserInstance.values().iterator();
        while (it.hasNext()) {
            UserInstance userInstance = (UserInstance) it.next();
            if (userInstance.getApplicationInstance() != null) {
                userInstance.getApplicationInstance().passivate();
            }
        }
        session = null;
    }
    
    /**
     * Listener implementation of HttpSessionBindingListener.
     * Stores reference to session when invoked.
     * 
     * @see javax.servlet.http.HttpSessionBindingListener#valueBound(HttpSessionBindingEvent)
     */
    public void valueBound(HttpSessionBindingEvent e) {
        session = e.getSession();
    }

    /**
     * Listener implementation of HttpSessionBindingListener.
     * Disposes ApplicationInstance.
     * Removes reference to session when invoked.
     * 
     * @see javax.servlet.http.HttpSessionBindingListener#valueUnbound(HttpSessionBindingEvent)
     */
    public void valueUnbound(HttpSessionBindingEvent e) {
        dispose();
        session = null;
    }
}