Package org.ajax4jsf.application

Source Code of org.ajax4jsf.application.AjaxStateManager$SynchronizedStateHolder

/**
* License Agreement.
*
* Rich Faces - Natural Ajax for Java Server Faces (JSF)
*
* Copyright (C) 2007 Exadel, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
*/

package org.ajax4jsf.application;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.application.StateManager;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.faces.render.ResponseStateManager;

import org.ajax4jsf.context.AjaxContext;
import org.ajax4jsf.context.ContextInitParameters;
import org.ajax4jsf.event.AjaxPhaseListener;
import org.ajax4jsf.util.LRUMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
* @author shura
*
*/
public class AjaxStateManager extends StateManager {

  private static final Class[] STATE_MANAGER_ARGUMENTS = new Class[] { StateManager.class };

  protected static final int DEFAULT_NUMBER_OF_VIEWS = 16;

  private static final String VIEW_STATES_MAP = AjaxStateManager.class
      .getName()
      + ".VIEW_STATES_MAP";

  private static final Object VIEW_SEQUENCE = AjaxStateManager.class
      .getName()
      + ".VIEW_SEQUENCE";

  private final StateManager parent;

  private StateManager seamStateManager;

  private final ComponentsLoader componentLoader;

  private volatile int viewSequence = 0;

  private Object viewSequenceMutex = "MUTEX";

  private static final Log _log = LogFactory.getLog(AjaxStateManager.class);

  /**
   * @param parent
   */
  public AjaxStateManager(StateManager parent) {
    super();
    this.parent = parent;
    componentLoader = new ComponentsLoaderImpl();
    // HACK - Seam perform significant operations before save tree state.
    // Try to create it instance by reflection,
    // to call in real state saving operations.
    ClassLoader classLoader = Thread.currentThread()
        .getContextClassLoader();
    if (null == classLoader) {
      classLoader = AjaxStateManager.class.getClassLoader();
    }
    try {
      Class seamStateManagerClass = classLoader
          .loadClass("org.jboss.seam.jsf.SeamStateManager");
      Constructor constructor = seamStateManagerClass
          .getConstructor(STATE_MANAGER_ARGUMENTS);
      seamStateManager = (StateManager) constructor
          .newInstance(new Object[] { new StateManager() {

            protected Object getComponentStateToSave(
                FacesContext arg0) {
              // do nothing
              return null;
            }

            protected Object getTreeStructureToSave(
                FacesContext arg0) {
              // do nothing
              return null;
            }

            protected void restoreComponentState(FacesContext arg0,
                UIViewRoot arg1, String arg2) {
              // do nothing

            }

            protected UIViewRoot restoreTreeStructure(
                FacesContext arg0, String arg1, String arg2) {
              // do nothing
              return null;
            }

            public UIViewRoot restoreView(FacesContext arg0,
                String arg1, String arg2) {
              // do nothing
              return null;
            }

            public SerializedView saveSerializedView(
                FacesContext arg0) {
              // delegate to enclosed class method.
              return buildSerializedView(arg0);
            }

            public void writeState(FacesContext arg0,
                SerializedView arg1) throws IOException {
              // do nothing
            }

          } });
      if (_log.isDebugEnabled()) {
        _log.debug("Create instance of the SeamStateManager");
      }
    } catch (Exception e) {
      seamStateManager = null;
      if (_log.isDebugEnabled()) {
        _log.debug("SeamStateManager is not present");
      }
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see javax.faces.application.StateManager#getComponentStateToSave(javax.faces.context.FacesContext)
   */
  protected Object getComponentStateToSave(FacesContext context) {
    throw new UnsupportedOperationException();
  }

  /*
   * (non-Javadoc)
   *
   * @see javax.faces.application.StateManager#getTreeStructureToSave(javax.faces.context.FacesContext)
   */
  protected Object getTreeStructureToSave(FacesContext context) {
    throw new UnsupportedOperationException();
  }

  /*
   * (non-Javadoc)
   *
   * @see javax.faces.application.StateManager#restoreComponentState(javax.faces.context.FacesContext,
   *      javax.faces.component.UIViewRoot, java.lang.String)
   */
  protected void restoreComponentState(FacesContext context,
      UIViewRoot viewRoot, String renderKitId) {
    throw new UnsupportedOperationException();
  }

  /*
   * (non-Javadoc)
   *
   * @see javax.faces.application.StateManager#restoreTreeStructure(javax.faces.context.FacesContext,
   *      java.lang.String, java.lang.String)
   */
  protected UIViewRoot restoreTreeStructure(FacesContext context,
      String viewId, String renderKitId) {
    throw new UnsupportedOperationException();
  }

  /*
   * (non-Javadoc)
   *
   * @see javax.faces.application.StateManager#writeState(javax.faces.context.FacesContext,
   *      javax.faces.application.StateManager.SerializedView)
   */
  public void writeState(FacesContext context, SerializedView state)
      throws IOException {
    parent.writeState(context, state);
    if (_log.isDebugEnabled()) {
      _log.debug("Write view state to the response");
    }
    context.getExternalContext().getRequestMap().put(
        AjaxPhaseListener.VIEW_STATE_SAVED_PARAM, Boolean.TRUE);
  }

  /*
   * (non-Javadoc)
   *
   * @see javax.faces.application.StateManager#restoreView(javax.faces.context.FacesContext,
   *      java.lang.String, java.lang.String)
   */
  public UIViewRoot restoreView(FacesContext context, String viewId,
      String renderKitId) {
    UIViewRoot viewRoot = null;
    ResponseStateManager responseStateManager = getRenderKit(context,
        renderKitId).getResponseStateManager();
    TreeStructureNode treeStructure = null;
    Object[] state = null;
    if (isSavingStateInClient(context)) {
      treeStructure = (TreeStructureNode) responseStateManager
          .getTreeStructureToRestore(context, viewId);
      // viewRoot = parent.restoreView(context, viewId, renderKitId);
      state = (Object[]) responseStateManager
          .getComponentStateToRestore(context);
    } else {
      Object[] serializedView = restoreStateFromSession(context, viewId,
          renderKitId);
      if (null != serializedView) {
        treeStructure = (TreeStructureNode) serializedView[0];
        state = (Object[]) handleRestoreState(context, serializedView[1]);
      }
    }
    if (null != treeStructure) {
      viewRoot = (UIViewRoot) treeStructure.restore(componentLoader);
      if (null != viewRoot && null != state) {
        viewRoot.processRestoreState(context, state[0]);
        restoreAdditionalState(context, state[1]);
      }
    }
    return viewRoot;

  }

  private static final Object handleRestoreState(FacesContext context, Object state) {
    if (ContextInitParameters.isSerializeServerState(context)) {
      ObjectInputStream ois = null;
          try {
            ois = new ObjectInputStream(new ByteArrayInputStream((byte[]) state));
            return ois.readObject();
          } catch (Exception e) {
              throw new FacesException(e);
          } finally {
              if (ois != null) {
                  try {
                      ois.close();
                  } catch (IOException ignored) { }
              }
          }
    } else {
      return state;
    }
  }

  private static final Object handleSaveState(FacesContext context, Object state) {
    if (ContextInitParameters.isSerializeServerState(context)) {
      ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
      ObjectOutputStream oas = null;
      try {
        oas = new ObjectOutputStream(baos);
        oas.writeObject(state);
        oas.flush();
      } catch (Exception e) {
        throw new FacesException(e);
      } finally {
        if (oas != null) {
          try {
            oas.close();
          } catch (IOException ignored) { }
        }
      }
      return baos.toByteArray();
    } else {
      return state;
    }
  }
 
  protected Object[] restoreStateFromSession(FacesContext context,
      String viewId, String renderKitId) {
    Object[] restoredState = null;
    Object id = getRenderKit(context, renderKitId)
        .getResponseStateManager().getTreeStructureToRestore(context,
            viewId);
    ExternalContext externalContext = context.getExternalContext();
    Object session = externalContext.getSession(false);
    if (null == session) {
      if (_log.isDebugEnabled()) {
        _log.debug("Can't restore view state : session expired");
      }
    } else {
      synchronized (session) {
        SynchronizedStateHolder viewStates = (SynchronizedStateHolder) externalContext.getSessionMap()
            .get(VIEW_STATES_MAP);
        if (null != viewStates) {
          synchronized (viewStates) {
            LRUMap stateMap = viewStates.getStateMap();
            LRUMap logicalStates = (LRUMap) stateMap.get(viewId);
            if (null != logicalStates) {
              if (null != id) {
                restoredState = (Object[]) logicalStates.get(id);
                externalContext.getRequestMap().put(VIEW_SEQUENCE,
                    id);
                if (null == restoredState) {
                  if (_log.isDebugEnabled()) {
                    _log
                        .debug("No saved view state found for a Id "
                            + id
                            );
                  }
                  // http://jira.jboss.com/jira/browse/RF-3542
//                  restoredState = (Object[]) logicalStates
//                      .get(logicalStates.lastKey());
                }
              } else {
                if (_log.isDebugEnabled()) {
                  _log
                      .debug("No version Id for a saved view state in request.");
                }
                // http://jira.jboss.com/jira/browse/RF-3542
//                restoredState = (Object[]) logicalStates
//                    .get(logicalStates.lastKey());
              }
            } else if (_log.isDebugEnabled()) {
              _log
                  .debug("Can't restore view state : no saved states for a ViewId "
                      + viewId);
            }
          }
        } else if (_log.isDebugEnabled()) {
          _log
              .debug("Can't restore view state : no saved view states in session");
        }

      }
    }

    return restoredState;
  }

  public SerializedView saveSerializedView(FacesContext context) {
    if (null == seamStateManager) {
      return buildSerializedView(context);
    } else {
      // Delegate save method to seam State Manager.
      return seamStateManager.saveSerializedView(context);
    }
  }

  /**
   * @param context
   * @return
   */
  protected SerializedView buildSerializedView(FacesContext context) {
    SerializedView serializedView = null;
    UIViewRoot viewRoot = context.getViewRoot();
    if (null != viewRoot && (!viewRoot.isTransient()) ) {
      TreeStructureNode treeStructure = new TreeStructureNode();
      treeStructure.apply(context, viewRoot, new HashSet());
      Object treeState = viewRoot.processSaveState(context);
      Object state[] = { treeState, getAdditionalState(context) };
      if (isSavingStateInClient(context)) {
        serializedView = new SerializedView(treeStructure, state);
      } else {
        serializedView = saveStateInSession(context, treeStructure,
            handleSaveState(context, state));
      }

    }
    return serializedView;
  }

  /**
   * @param context
   * @param treeStructure
   * @param state
   * @return
   */
  protected SerializedView saveStateInSession(FacesContext context,
      Object treeStructure, Object state) {
    SerializedView serializedView;
    UIViewRoot viewRoot = context.getViewRoot();
    ExternalContext externalContext = context.getExternalContext();
    Object session = externalContext.getSession(true);
    synchronized (session) {
      SynchronizedStateHolder viewStates = (SynchronizedStateHolder) externalContext.getSessionMap().get(
          VIEW_STATES_MAP);
      if (null == viewStates) {
        viewStates = new SynchronizedStateHolder(new LRUMap(getNumberOfViews(externalContext)));
        externalContext.getSessionMap()
            .put(VIEW_STATES_MAP, viewStates);
      }

      synchronized (viewStates) {
        Object id = getNextViewId(context);
        LRUMap stateMap = viewStates.getStateMap();
        LRUMap logicalViewsMap = (LRUMap) stateMap.get(viewRoot
            .getViewId());
        if (null == logicalViewsMap) {
          logicalViewsMap = new LRUMap(getNumberOfViews(externalContext));
        }
        // Renew last seen view.
        stateMap.put(viewRoot.getViewId(), logicalViewsMap);
        logicalViewsMap.put(id, new Object[] { treeStructure, state });
        serializedView = new SerializedView(id, null);
      }
    }
   

    return serializedView;
  }

  protected Object getAdditionalState(FacesContext context) {
    return null;
  }

  protected void restoreAdditionalState(FacesContext context, Object state) {

  }

  protected Object getNextViewId(FacesContext context) {
    AjaxContext ajaxContext = AjaxContext.getCurrentInstance(context);
    if (ajaxContext.isAjaxRequest(context)) {
      Object id = context.getExternalContext().getRequestMap().get(
          VIEW_SEQUENCE);
      if (null != id) {
        return id;
      }
    }
    synchronized (viewSequenceMutex) {
      if (viewSequence++ == Character.MAX_VALUE) {
        viewSequence = 0;
      }
    }
    return UIViewRoot.UNIQUE_ID_PREFIX + ((int) viewSequence);
  }

  protected int getNumberOfViews(ExternalContext externalContext) {
    return DEFAULT_NUMBER_OF_VIEWS;
  }

  protected RenderKit getRenderKit(FacesContext context, String renderKitId) {
    RenderKit renderKit = context.getRenderKit();
    if (null == renderKit) {
      RenderKitFactory factory = (RenderKitFactory) FactoryFinder
          .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
      renderKit = factory.getRenderKit(context, renderKitId);
    }
    return renderKit;
  }

  protected static final class SynchronizedStateHolder implements Serializable {

    /**
     *
     */
    private static final long serialVersionUID = -6218719119030970545L;
 
    private LRUMap stateMap;

    public SynchronizedStateHolder(LRUMap stateMap) {
      super();
      this.stateMap = stateMap;
    }
   
    public LRUMap getStateMap() {
      return stateMap;
    }

    private synchronized void readObject(java.io.ObjectInputStream s)
      throws IOException, ClassNotFoundException {

      s.defaultReadObject();
    }

    private synchronized void writeObject(java.io.ObjectOutputStream s) throws IOException {
     
      s.defaultWriteObject();
    }
   
  }
 
  protected static final class TreeStructureNode implements Externalizable {
    /**
     * TODO - implement Externalizable to reduce serialized state.
     */
    private static final long serialVersionUID = -9038742487716977911L;

    private static final String NULL_ID = "";

    private Map facets = null;

    private List children = null;

    private String type;

    private String id;

    public TreeStructureNode() {
    }

    public void apply(FacesContext context, UIComponent component,
        Set uniqueIds) {
      type = component.getClass().getName();
      id = component.getId();
      String clientId = component.getClientId(context);
      if (!uniqueIds.add(clientId)) {
        throw new IllegalStateException("duplicate Id for a component "
            + clientId);
      }
      Map componentFacets = component.getFacets();
      for (Iterator i = componentFacets.entrySet().iterator(); i
          .hasNext();) {
        Entry element = (Entry) i.next();
        UIComponent f = (UIComponent) element.getValue();
        if (!f.isTransient()) {
          TreeStructureNode facet = new TreeStructureNode();
          facet.apply(context, f, uniqueIds);
          if (null == facets) {
            facets = new HashMap();
          }
          facets.put(element.getKey(), facet);

        }
      }
      for (Iterator i = component.getChildren().iterator(); i.hasNext();) {
        UIComponent child = (UIComponent) i.next();
        if (!child.isTransient()) {
          TreeStructureNode t = new TreeStructureNode();
          t.apply(context, child, uniqueIds);
          if (null == children) {
            children = new ArrayList();
          }
          children.add(t);

        }
      }
    }

    public UIComponent restore(ComponentsLoader loader) {
      UIComponent component;
      component = loader.createComponent(type);
      component.setId(id);
      if (null != facets) {
        for (Iterator i = facets.entrySet().iterator(); i.hasNext();) {
          Entry element = (Entry) i.next();
          UIComponent facet = ((TreeStructureNode) element.getValue())
              .restore(loader);
          component.getFacets().put(element.getKey(), facet);
        }

      }
      if (null != children) {
        for (Iterator i = children.iterator(); i.hasNext();) {
          TreeStructureNode node = (TreeStructureNode) i.next();
          UIComponent child = node.restore(loader);
          component.getChildren().add(child);
        }

      }
      return component;
    }

    /**
     * @return the facets
     */
    public Map getFacets() {
      return facets;
    }

    /**
     * @param facets
     *            the facets to set
     */
    public void setFacets(Map facets) {
      this.facets = facets;
    }

    /**
     * @return the children
     */
    public List getChildren() {
      return children;
    }

    /**
     * @param children
     *            the children to set
     */
    public void setChildren(List children) {
      this.children = children;
    }

    /**
     * @return the type
     */
    public String getType() {
      return type;
    }

    /**
     * @param type
     *            the type to set
     */
    public void setType(String type) {
      this.type = type;
    }

    /**
     * @return the id
     */
    public String getId() {
      return id;
    }

    /**
     * @param id
     *            the id to set
     */
    public void setId(String id) {
      this.id = id;
    }

    public void readExternal(ObjectInput in) throws IOException,
        ClassNotFoundException {
      type = in.readUTF();
      id = in.readUTF();
      if (NULL_ID.equals(id)) {
        id = null;
      }
      int facetsSize = in.readInt();
      if (facetsSize > 0) {
        facets = new HashMap(facetsSize);
        for (int i = 0; i < facetsSize; i++) {
          String facetName = in.readUTF();
          TreeStructureNode facet = new TreeStructureNode();
          facet.readExternal(in);
          facets.put(facetName, facet);
        }
      }
      int childrenSize = in.readInt();
      if (childrenSize > 0) {
        children = new ArrayList(childrenSize);
        for (int i = 0; i < childrenSize; i++) {
          TreeStructureNode child = new TreeStructureNode();
          child.readExternal(in);
          children.add(child);
        }
      }
    }

    public void writeExternal(ObjectOutput out) throws IOException {
      out.writeUTF(type);
      out.writeUTF(null == id ? NULL_ID : id);
      if (null != facets) {
        out.writeInt(facets.size());
        for (Iterator i = facets.entrySet().iterator(); i.hasNext();) {
          Map.Entry entry = (Map.Entry) i.next();
          out.writeUTF((String) entry.getKey());
          TreeStructureNode node = (TreeStructureNode) entry.getValue();
          node.writeExternal(out);
        }

      } else {
        out.writeInt(0);
      }
      if (null != children) {
        out.writeInt(children.size());
        for (Iterator i = children.iterator(); i.hasNext();) {
          TreeStructureNode child = (TreeStructureNode) i.next();
          child.writeExternal(out);
        }

      } else {
        out.writeInt(0);
      }
    }
  }

}
TOP

Related Classes of org.ajax4jsf.application.AjaxStateManager$SynchronizedStateHolder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.