Author: nbelaevski
Date: 2009-11-20 10:16:08 -0500 (Fri, 20 Nov 2009)
New Revision: 15944
Modified:
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/application/AjaxStateManager.java
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/context/ContextInitParameters.java
Log:
https://jira.jboss.org/jira/browse/RF-8145
Modified:
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/application/AjaxStateManager.java
===================================================================
---
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/application/AjaxStateManager.java 2009-11-20
14:32:01 UTC (rev 15943)
+++
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/application/AjaxStateManager.java 2009-11-20
15:16:08 UTC (rev 15944)
@@ -1,667 +1,690 @@
-/**
- * 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.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.ObjectStreamClass;
-import java.io.StringWriter;
-import java.lang.reflect.Constructor;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import javax.faces.FacesException;
-import javax.faces.FactoryFinder;
-import javax.faces.application.StateManager;
-import javax.faces.component.UIComponentBase;
-import javax.faces.component.UIViewRoot;
-import javax.faces.context.ExternalContext;
-import javax.faces.context.FacesContext;
-import javax.faces.context.ResponseWriter;
-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.model.KeepAlive;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * @author shura
- *
- */
-public class AjaxStateManager extends StateManager {
-
-
- public static final String CAPTURED_VIEW_STATE =
"org.ajax4jsf.captured_view_state";
-
- private final class SeamStateManagerWrapper extends 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;
- }
-
- @SuppressWarnings("deprecation")
- public SerializedView saveSerializedView(FacesContext context) {
- // delegate to enclosed class method.
- Object[] viewState = buildViewState(context);
- return new SerializedView(viewState[0],viewState[1]);
- }
-
- @Override
- public Object saveView(FacesContext context) {
- // TODO Auto-generated method stub
- return buildViewState(context);
- }
- @SuppressWarnings("deprecation")
- public void writeState(FacesContext arg0, SerializedView arg1)
- throws IOException {
- // do nothing
- }
- }
-
- private static final Class<StateManager> STATE_MANAGER_ARGUMENTS =
StateManager.class;
-
- public static final int DEFAULT_NUMBER_OF_VIEWS = 16;
-
- public static final String AJAX_VIEW_SEQUENCE = AjaxStateManager.class.getName()
- + ".AJAX_VIEW_SEQUENCE";
- public static final String VIEW_SEQUENCE = AjaxStateManager.class.getName()
- + ".VIEW_SEQUENCE";
-
- private final StateManager parent;
-
- private StateManager seamStateManager;
-
- private final ComponentsLoader componentLoader;
-
- private static final Log _log = LogFactory.getLog(AjaxStateManager.class);
-
- public static final String VIEW_SEQUENCE_ATTRIBUTE = AjaxStateManager.class
- .getName()
- + ".view_sequence";
-
- /**
- * @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<? extends StateManager> seamStateManagerClass = classLoader
- .loadClass("org.jboss.seam.jsf.SeamStateManager")
- .asSubclass(StateManager.class);
- Constructor<? extends StateManager> constructor = seamStateManagerClass
- .getConstructor(STATE_MANAGER_ARGUMENTS);
- seamStateManager = constructor
- .newInstance(new Object[] { new SeamStateManagerWrapper() });
- 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) {
- Object treeState = context.getViewRoot().processSaveState(context);
- Object state[] = { treeState, getAdditionalState(context) };
- return state;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
javax.faces.application.StateManager#getTreeStructureToSave(javax.faces.context.FacesContext)
- */
- protected Object getTreeStructureToSave(FacesContext context) {
- TreeStructureNode treeStructure = new TreeStructureNode();
- treeStructure.apply(context, context.getViewRoot(),
- new HashSet<String>());
- return treeStructure;
- }
-
- /*
- * (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, Object state)
- throws IOException {
- RenderKit renderKit = getRenderKit(context);
- ResponseStateManager responseStateManager = renderKit
- .getResponseStateManager();
- Object[] stateArray = getStateArray( state );
- if(null == stateArray[0] && null == stateArray[1]){
- // Myfaces
https://issues.apache.org/jira/browse/MYFACES-1753 hack.
- stateArray=new Object[]{getLogicalViewId(context),null};
- }
- writeState(context, responseStateManager, stateArray);
- if (_log.isDebugEnabled()) {
- _log.debug("Write view state to the response");
- }
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
javax.faces.application.StateManager#writeState(javax.faces.context.FacesContext,
- * javax.faces.application.StateManager.SerializedView)
- */
- @SuppressWarnings("deprecation")
- public void writeState(FacesContext context, SerializedView state)
- throws IOException {
- RenderKit renderKit = getRenderKit(context);
- ResponseStateManager responseStateManager = renderKit
- .getResponseStateManager();
- Object[] stateArray;
- if(null == state.getState() && null == state.getStructure()){
- // MyFaces
https://issues.apache.org/jira/browse/MYFACES-1753 hack
- stateArray = new Object[]{getLogicalViewId(context),null};
- } else {
- stateArray = new Object[] {
- state.getStructure(),state.getState() };
- }
- writeState(context, responseStateManager, stateArray);
- if (_log.isDebugEnabled()) {
- _log.debug("Write view state to the response");
- }
- }
-
- /**
- * @param context
- * @param state
- * @param responseStateManager
- * @throws IOException
- * @throws FacesException
- */
- private Object[] getStateArray(Object state) throws IOException,
- FacesException {
- if (null != state && state.getClass().isArray()
- && state.getClass().getComponentType().equals(Object.class)) {
- Object stateArray[] = (Object[]) state;
- if (2 == stateArray.length) {
- return stateArray;
- } else {
- throw new FacesException("Unexpected length of the state object array
"+stateArray.length);
- }
- } else {
- throw new FacesException("Unexpected type of the state
"+state.getClass().getName());
- }
- }
-
- private void writeState(FacesContext context,
- ResponseStateManager responseStateManager, Object[] stateArray)
- throws IOException {
- // Capture writed state into string.
- ResponseWriter originalWriter = context.getResponseWriter();
- StringWriter buff = new StringWriter(128);
- try {
- ResponseWriter stateResponseWriter = originalWriter
- .cloneWithWriter(buff);
- context.setResponseWriter(stateResponseWriter);
- responseStateManager.writeState(context, stateArray);
- stateResponseWriter.flush();
- String stateString = buff.toString();
- originalWriter.write(stateString);
- String stateValue = getStateValue(stateString);
- context.getExternalContext().getRequestMap().put(CAPTURED_VIEW_STATE, stateValue);
- if (null != stateValue) {
- } else {
- }
- } finally {
- context.setResponseWriter(originalWriter);
- }
- }
-
- static final Pattern PATTERN =
Pattern.compile(".*<input.*(?:\\svalue=[\"\'](\\S*)[\"\']\\s).*name=[\"']"+ResponseStateManager.VIEW_STATE_PARAM+"[\"'].*>");
- static final Pattern PATTERN2 = Pattern.compile(".*<input
.*name=[\"']"+ResponseStateManager.VIEW_STATE_PARAM+"[\"'].*(?:\\svalue=[\"\'](\\S*)[\"\']\\s).*>");
-
-
- /**
- * Parse content of the writed viewState hidden input field for a state value.
- * @param input
- * @return
- */
- private String getStateValue(String input) {
- Matcher matcher = PATTERN.matcher(input);
- if(!matcher.matches()){
- matcher = PATTERN2.matcher(input);
- if(!matcher.matches()){
- return null;
- }
- }
- return matcher.group(1);
-}
-
- private static final Map<String,Class<?>> PRIMITIVE_CLASSES =
- new HashMap<String,Class<?>>(9, 1.0F);
-
- static {
- PRIMITIVE_CLASSES.put("boolean", boolean.class);
- PRIMITIVE_CLASSES.put("byte", byte.class);
- PRIMITIVE_CLASSES.put("char", char.class);
- PRIMITIVE_CLASSES.put("short", short.class);
- PRIMITIVE_CLASSES.put("int", int.class);
- PRIMITIVE_CLASSES.put("long", long.class);
- PRIMITIVE_CLASSES.put("float", float.class);
- PRIMITIVE_CLASSES.put("double", double.class);
- PRIMITIVE_CLASSES.put("void", void.class);
- }
-
- private static final Object handleRestoreState(FacesContext context, Object state) {
- if (ContextInitParameters.isSerializeServerState(context)) {
- ObjectInputStream ois = null;
- try {
- ois = new ObjectInputStream(new ByteArrayInputStream((byte[]) state)) {
- @Override
- protected Class<?> resolveClass(ObjectStreamClass desc)
- throws IOException, ClassNotFoundException {
- String name = desc.getName();
- try {
- return Class.forName(name, true,
- Thread.currentThread().getContextClassLoader());
- } catch (ClassNotFoundException cnfe) {
- Class<?> clazz = PRIMITIVE_CLASSES.get(name);
- if (clazz != null) {
- return clazz;
- } else {
- throw cnfe;
- }
- }
- }
- };
- 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;
- }
- }
-
- /*
- * (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;
- Object[] serializedView = null;
- if (isSavingStateInClient(context)) {
- serializedView = (Object[]) responseStateManager.getState(context,
- viewId);
-
- if (null != serializedView) {
- treeStructure = (TreeStructureNode) serializedView[0];
- state = (Object[]) serializedView[1];
- }
- } else {
- 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;
-
- }
-
- @SuppressWarnings("deprecation")
- public SerializedView saveSerializedView(FacesContext context) {
- Object[] stateViewArray;
- if (null == seamStateManager) {
- stateViewArray = buildViewState(context);
- } else {
- // Delegate save method to seam State Manager.
- stateViewArray=(Object[]) seamStateManager.saveView(context);
- }
- return new SerializedView(stateViewArray[0],stateViewArray[1]);
- }
-
- @Override
- public Object saveView(FacesContext context) {
- if (null == seamStateManager) {
- return buildViewState(context);
- } else {
- // Delegate save method to seam State Manager.
- return seamStateManager.saveView(context);
- }
- }
- /**
- * @param context
- * @return
- * @see
javax.faces.application.StateManager#isSavingStateInClient(javax.faces.context.FacesContext)
- */
- public boolean isSavingStateInClient(FacesContext context) {
- return parent.isSavingStateInClient(context);
- }
-
- protected Object[] restoreStateFromSession(FacesContext context,
- String viewId, String renderKitId) {
- String id = restoreLogicalViewId(context, viewId, renderKitId);
- StateHolder stateHolder = getStateHolder(context);
- Object[] restoredState = stateHolder.getState(context, viewId, id);
-
- if (restoredState != null && id != null) {
- context.getExternalContext().getRequestMap().put(AJAX_VIEW_SEQUENCE, id);
- }
-
- return restoredState;
- }
-
- /**
- * @param context
- * @return
- */
- protected Object[] buildViewState(FacesContext context) {
- Object[] viewStateArray = null;
- UIViewRoot viewRoot = context.getViewRoot();
- if (null != viewRoot && !viewRoot.isTransient()) {
- TreeStructureNode treeStructure = (TreeStructureNode)
getTreeStructureToSave(context);
- Object state = getComponentStateToSave(context);
- if (isSavingStateInClient(context)) {
- viewStateArray = new Object[]{treeStructure, state};
- } else {
- viewStateArray = saveStateInSession(context, treeStructure,
- handleSaveState(context, state));
- }
-
- }
- return viewStateArray;
- }
-
- /**
- * @param context
- * @param treeStructure
- * @param state
- * @return
- */
- protected Object[] saveStateInSession(FacesContext context,
- Object treeStructure, Object state) {
- Object[] serializedView;
- UIViewRoot viewRoot = context.getViewRoot();
- StateHolder stateHolder = getStateHolder(context);
- String id = getLogicalViewId(context);
- stateHolder.saveState(context, viewRoot.getViewId(), id, new Object[] {
- treeStructure, state });
- serializedView = new Object[]{id, null};
- return serializedView;
- }
-
- /**
- * @param context
- * @return
- */
- protected StateHolder getStateHolder(FacesContext context) {
- return AjaxStateHolder.getInstance(context);
- }
-
- protected Object getAdditionalState(FacesContext context) {
- Map<String, Object> keepAliveBeans = new HashMap<String, Object>();
- Map<String, Object> requestMap = context.getExternalContext()
- .getRequestMap();
- // Save all objects form request map wich marked by @KeepAlive
- // annotations
- for (Entry<String, Object> requestEntry : requestMap.entrySet()) {
- Object bean = requestEntry.getValue();
- // check value for a NULL -
- //
http://jira.jboss.com/jira/browse/RF-3576
- if (null != bean
- && bean.getClass().isAnnotationPresent(KeepAlive.class)) {
- keepAliveBeans.put(requestEntry.getKey(), bean);
- }
- }
- if (keepAliveBeans.size() > 0) {
- return UIComponentBase.saveAttachedState(context, keepAliveBeans);
- } else {
- return null;
- }
- }
-
- @SuppressWarnings("unchecked")
- protected void restoreAdditionalState(FacesContext context, Object state) {
- if (null != state) {
- boolean isAjax = AjaxContext.getCurrentInstance(context).isAjaxRequest();
-
- // Append all saved beans to the request map.
- Map beansMap = (Map) UIComponentBase.restoreAttachedState(context,
- state);
- Map<String, Object> requestMap = context.getExternalContext()
- .getRequestMap();
- for (Object key : beansMap.keySet()) {
- Object bean = beansMap.get(key);
- if (bean != null) {
- KeepAlive annotation = bean.getClass().getAnnotation(KeepAlive.class);
- if (annotation != null) {
- if (!isAjax && annotation.ajaxOnly()) {
-
- //skip ajax-only beans for non-ajax requests
- continue;
- }
- }
- }
-
- requestMap.put((String) key, bean);
- }
- }
- }
-
- /**
- * Restore logical view id from request.
- *
- * @param context
- * @param viewId
- * @param renderKitId
- * @return
- */
- @SuppressWarnings("deprecation")
- protected String restoreLogicalViewId(FacesContext context, String viewId,
- String renderKitId) {
- String id = (String) getRenderKit(context, renderKitId)
- .getResponseStateManager().getTreeStructureToRestore(context,
- viewId);
- return id;
- }
-
- /**
- * Return logical Id for current request view state. For a faces requests,
- * generate sequence numbers. For a ajax request, attempt to re-use id from
- * request submit.
- *
- * @param context
- * @return
- */
- protected String getLogicalViewId(FacesContext context) {
- AjaxContext ajaxContext = AjaxContext.getCurrentInstance(context);
- ExternalContext externalContext = context.getExternalContext();
- Object id=null;
- Map<String, Object> requestMap = externalContext.getRequestMap();
- id = requestMap.get(ajaxContext.isAjaxRequest()?AJAX_VIEW_SEQUENCE:VIEW_SEQUENCE);
- if (null != id) {
- return id.toString();
- }
- // Store sequence in session, to avoyd claster configuration problem
- // see
https://javaserverfaces.dev.java.net/issues/show_bug.cgi?id=662
- Object session = externalContext.getSession(true);
- int viewSequence;
- synchronized (session) {
- Map<String, Object> sessionMap = externalContext.getSessionMap();
- Integer sequence = (Integer) sessionMap
- .get(VIEW_SEQUENCE_ATTRIBUTE);
- if (null != sequence) {
- viewSequence = sequence.intValue();
- } else {
- viewSequence = 0;
- }
- if (viewSequence++ == Character.MAX_VALUE) {
- viewSequence = 0;
- }
- sessionMap.put(VIEW_SEQUENCE_ATTRIBUTE, new Integer(viewSequence));
- }
- String logicalViewId = UIViewRoot.UNIQUE_ID_PREFIX + ((int) viewSequence);
- // Store new viewId in the request parameters, to avoid re-increments in the same
request.
- requestMap.put(VIEW_SEQUENCE,logicalViewId);
- return logicalViewId;
- }
-
- protected RenderKit getRenderKit(FacesContext context) {
- String renderKitId = null;
- UIViewRoot viewRoot = context.getViewRoot();
- if (null != viewRoot) {
- renderKitId = viewRoot.getRenderKitId();
- }
- if (null == renderKitId) {
- renderKitId = context.getApplication().getViewHandler()
- .calculateRenderKitId(context);
- }
- return getRenderKit(context, renderKitId);
- }
-
- 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;
- }
-
-}
+/**
+ * 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.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+import javax.faces.FacesException;
+import javax.faces.FactoryFinder;
+import javax.faces.application.StateManager;
+import javax.faces.component.UIComponentBase;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseWriter;
+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.model.KeepAlive;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * @author shura
+ *
+ */
+public class AjaxStateManager extends StateManager {
+
+
+ public static final String CAPTURED_VIEW_STATE =
"org.ajax4jsf.captured_view_state";
+
+ private final class SeamStateManagerWrapper extends 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;
+ }
+
+ @SuppressWarnings("deprecation")
+ public SerializedView saveSerializedView(FacesContext context) {
+ // delegate to enclosed class method.
+ Object[] viewState = buildViewState(context);
+ return new SerializedView(viewState[0],viewState[1]);
+ }
+
+ @Override
+ public Object saveView(FacesContext context) {
+ // TODO Auto-generated method stub
+ return buildViewState(context);
+ }
+ @SuppressWarnings("deprecation")
+ public void writeState(FacesContext arg0, SerializedView arg1)
+ throws IOException {
+ // do nothing
+ }
+ }
+
+ private static final Class<StateManager> STATE_MANAGER_ARGUMENTS =
StateManager.class;
+
+ public static final int DEFAULT_NUMBER_OF_VIEWS = 16;
+
+ public static final String AJAX_VIEW_SEQUENCE = AjaxStateManager.class.getName()
+ + ".AJAX_VIEW_SEQUENCE";
+ public static final String VIEW_SEQUENCE = AjaxStateManager.class.getName()
+ + ".VIEW_SEQUENCE";
+
+ private final StateManager parent;
+
+ private StateManager seamStateManager;
+
+ private final ComponentsLoader componentLoader;
+
+ private static final Log _log = LogFactory.getLog(AjaxStateManager.class);
+
+ public static final String VIEW_SEQUENCE_ATTRIBUTE = AjaxStateManager.class
+ .getName()
+ + ".view_sequence";
+
+ private static final int UNCOMPRESSED_FLAG = 0;
+ private static final int COMPRESSED_FLAG = 1;
+
+ /**
+ * @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<? extends StateManager> seamStateManagerClass = classLoader
+ .loadClass("org.jboss.seam.jsf.SeamStateManager")
+ .asSubclass(StateManager.class);
+ Constructor<? extends StateManager> constructor = seamStateManagerClass
+ .getConstructor(STATE_MANAGER_ARGUMENTS);
+ seamStateManager = constructor
+ .newInstance(new Object[] { new SeamStateManagerWrapper() });
+ 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) {
+ Object treeState = context.getViewRoot().processSaveState(context);
+ Object state[] = { treeState, getAdditionalState(context) };
+ return state;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
javax.faces.application.StateManager#getTreeStructureToSave(javax.faces.context.FacesContext)
+ */
+ protected Object getTreeStructureToSave(FacesContext context) {
+ TreeStructureNode treeStructure = new TreeStructureNode();
+ treeStructure.apply(context, context.getViewRoot(),
+ new HashSet<String>());
+ return treeStructure;
+ }
+
+ /*
+ * (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, Object state)
+ throws IOException {
+ RenderKit renderKit = getRenderKit(context);
+ ResponseStateManager responseStateManager = renderKit
+ .getResponseStateManager();
+ Object[] stateArray = getStateArray( state );
+ if(null == stateArray[0] && null == stateArray[1]){
+ // Myfaces
https://issues.apache.org/jira/browse/MYFACES-1753 hack.
+ stateArray=new Object[]{getLogicalViewId(context),null};
+ }
+ writeState(context, responseStateManager, stateArray);
+ if (_log.isDebugEnabled()) {
+ _log.debug("Write view state to the response");
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
javax.faces.application.StateManager#writeState(javax.faces.context.FacesContext,
+ * javax.faces.application.StateManager.SerializedView)
+ */
+ @SuppressWarnings("deprecation")
+ public void writeState(FacesContext context, SerializedView state)
+ throws IOException {
+ RenderKit renderKit = getRenderKit(context);
+ ResponseStateManager responseStateManager = renderKit
+ .getResponseStateManager();
+ Object[] stateArray;
+ if(null == state.getState() && null == state.getStructure()){
+ // MyFaces
https://issues.apache.org/jira/browse/MYFACES-1753 hack
+ stateArray = new Object[]{getLogicalViewId(context),null};
+ } else {
+ stateArray = new Object[] {
+ state.getStructure(),state.getState() };
+ }
+ writeState(context, responseStateManager, stateArray);
+ if (_log.isDebugEnabled()) {
+ _log.debug("Write view state to the response");
+ }
+ }
+
+ /**
+ * @param context
+ * @param state
+ * @param responseStateManager
+ * @throws IOException
+ * @throws FacesException
+ */
+ private Object[] getStateArray(Object state) throws IOException,
+ FacesException {
+ if (null != state && state.getClass().isArray()
+ && state.getClass().getComponentType().equals(Object.class)) {
+ Object stateArray[] = (Object[]) state;
+ if (2 == stateArray.length) {
+ return stateArray;
+ } else {
+ throw new FacesException("Unexpected length of the state object array
"+stateArray.length);
+ }
+ } else {
+ throw new FacesException("Unexpected type of the state
"+state.getClass().getName());
+ }
+ }
+
+ private void writeState(FacesContext context,
+ ResponseStateManager responseStateManager, Object[] stateArray)
+ throws IOException {
+ // Capture writed state into string.
+ ResponseWriter originalWriter = context.getResponseWriter();
+ StringWriter buff = new StringWriter(128);
+ try {
+ ResponseWriter stateResponseWriter = originalWriter
+ .cloneWithWriter(buff);
+ context.setResponseWriter(stateResponseWriter);
+ responseStateManager.writeState(context, stateArray);
+ stateResponseWriter.flush();
+ String stateString = buff.toString();
+ originalWriter.write(stateString);
+ String stateValue = getStateValue(stateString);
+ context.getExternalContext().getRequestMap().put(CAPTURED_VIEW_STATE, stateValue);
+ if (null != stateValue) {
+ } else {
+ }
+ } finally {
+ context.setResponseWriter(originalWriter);
+ }
+ }
+
+ static final Pattern PATTERN =
Pattern.compile(".*<input.*(?:\\svalue=[\"\'](\\S*)[\"\']\\s).*name=[\"']"+ResponseStateManager.VIEW_STATE_PARAM+"[\"'].*>");
+ static final Pattern PATTERN2 = Pattern.compile(".*<input
.*name=[\"']"+ResponseStateManager.VIEW_STATE_PARAM+"[\"'].*(?:\\svalue=[\"\'](\\S*)[\"\']\\s).*>");
+
+
+ /**
+ * Parse content of the writed viewState hidden input field for a state value.
+ * @param input
+ * @return
+ */
+ private String getStateValue(String input) {
+ Matcher matcher = PATTERN.matcher(input);
+ if(!matcher.matches()){
+ matcher = PATTERN2.matcher(input);
+ if(!matcher.matches()){
+ return null;
+ }
+ }
+ return matcher.group(1);
+ }
+
+ private static final Map<String,Class<?>> PRIMITIVE_CLASSES =
+ new HashMap<String,Class<?>>(9, 1.0F);
+
+ static {
+ PRIMITIVE_CLASSES.put("boolean", boolean.class);
+ PRIMITIVE_CLASSES.put("byte", byte.class);
+ PRIMITIVE_CLASSES.put("char", char.class);
+ PRIMITIVE_CLASSES.put("short", short.class);
+ PRIMITIVE_CLASSES.put("int", int.class);
+ PRIMITIVE_CLASSES.put("long", long.class);
+ PRIMITIVE_CLASSES.put("float", float.class);
+ PRIMITIVE_CLASSES.put("double", double.class);
+ PRIMITIVE_CLASSES.put("void", void.class);
+ }
+
+ private static final Object handleRestoreState(FacesContext context, Object state) {
+ if (ContextInitParameters.isSerializeServerState(context)) {
+ ObjectInputStream ois = null;
+ try {
+ ByteArrayInputStream bais = new ByteArrayInputStream((byte[]) state);
+ InputStream is = bais;
+
+ int compressionFlag = is.read();
+ if(compressionFlag == COMPRESSED_FLAG) {
+ is = new GZIPInputStream(is);
+ }
+
+ ois = new ObjectInputStream(is) {
+ @Override
+ protected Class<?> resolveClass(ObjectStreamClass desc)
+ throws IOException, ClassNotFoundException {
+ String name = desc.getName();
+ try {
+ return Class.forName(name, true,
+ Thread.currentThread().getContextClassLoader());
+ } catch (ClassNotFoundException cnfe) {
+ Class<?> clazz = PRIMITIVE_CLASSES.get(name);
+ if (clazz != null) {
+ return clazz;
+ } else {
+ throw cnfe;
+ }
+ }
+ }
+ };
+ 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 {
+ OutputStream os = baos;
+ if (ContextInitParameters.isCompressServerState(context)) {
+ os.write(COMPRESSED_FLAG);
+ os = new GZIPOutputStream(os, 1024);
+ } else {
+ os.write(UNCOMPRESSED_FLAG);
+ }
+
+ oas = new ObjectOutputStream(os);
+ 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;
+ }
+ }
+
+ /*
+ * (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;
+ Object[] serializedView = null;
+ if (isSavingStateInClient(context)) {
+ serializedView = (Object[]) responseStateManager.getState(context,
+ viewId);
+
+ if (null != serializedView) {
+ treeStructure = (TreeStructureNode) serializedView[0];
+ state = (Object[]) serializedView[1];
+ }
+ } else {
+ 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;
+
+ }
+
+ @SuppressWarnings("deprecation")
+ public SerializedView saveSerializedView(FacesContext context) {
+ Object[] stateViewArray;
+ if (null == seamStateManager) {
+ stateViewArray = buildViewState(context);
+ } else {
+ // Delegate save method to seam State Manager.
+ stateViewArray=(Object[]) seamStateManager.saveView(context);
+ }
+ return new SerializedView(stateViewArray[0],stateViewArray[1]);
+ }
+
+ @Override
+ public Object saveView(FacesContext context) {
+ if (null == seamStateManager) {
+ return buildViewState(context);
+ } else {
+ // Delegate save method to seam State Manager.
+ return seamStateManager.saveView(context);
+ }
+ }
+ /**
+ * @param context
+ * @return
+ * @see
javax.faces.application.StateManager#isSavingStateInClient(javax.faces.context.FacesContext)
+ */
+ public boolean isSavingStateInClient(FacesContext context) {
+ return parent.isSavingStateInClient(context);
+ }
+
+ protected Object[] restoreStateFromSession(FacesContext context,
+ String viewId, String renderKitId) {
+ String id = restoreLogicalViewId(context, viewId, renderKitId);
+ StateHolder stateHolder = getStateHolder(context);
+ Object[] restoredState = stateHolder.getState(context, viewId, id);
+
+ if (restoredState != null && id != null) {
+ context.getExternalContext().getRequestMap().put(AJAX_VIEW_SEQUENCE, id);
+ }
+
+ return restoredState;
+ }
+
+ /**
+ * @param context
+ * @return
+ */
+ protected Object[] buildViewState(FacesContext context) {
+ Object[] viewStateArray = null;
+ UIViewRoot viewRoot = context.getViewRoot();
+ if (null != viewRoot && !viewRoot.isTransient()) {
+ TreeStructureNode treeStructure = (TreeStructureNode)
getTreeStructureToSave(context);
+ Object state = getComponentStateToSave(context);
+ if (isSavingStateInClient(context)) {
+ viewStateArray = new Object[]{treeStructure, state};
+ } else {
+ viewStateArray = saveStateInSession(context, treeStructure,
+ handleSaveState(context, state));
+ }
+
+ }
+ return viewStateArray;
+ }
+
+ /**
+ * @param context
+ * @param treeStructure
+ * @param state
+ * @return
+ */
+ protected Object[] saveStateInSession(FacesContext context,
+ Object treeStructure, Object state) {
+ Object[] serializedView;
+ UIViewRoot viewRoot = context.getViewRoot();
+ StateHolder stateHolder = getStateHolder(context);
+ String id = getLogicalViewId(context);
+ stateHolder.saveState(context, viewRoot.getViewId(), id, new Object[] {
+ treeStructure, state });
+ serializedView = new Object[]{id, null};
+ return serializedView;
+ }
+
+ /**
+ * @param context
+ * @return
+ */
+ protected StateHolder getStateHolder(FacesContext context) {
+ return AjaxStateHolder.getInstance(context);
+ }
+
+ protected Object getAdditionalState(FacesContext context) {
+ Map<String, Object> keepAliveBeans = new HashMap<String, Object>();
+ Map<String, Object> requestMap = context.getExternalContext()
+ .getRequestMap();
+ // Save all objects form request map wich marked by @KeepAlive
+ // annotations
+ for (Entry<String, Object> requestEntry : requestMap.entrySet()) {
+ Object bean = requestEntry.getValue();
+ // check value for a NULL -
+ //
http://jira.jboss.com/jira/browse/RF-3576
+ if (null != bean
+ && bean.getClass().isAnnotationPresent(KeepAlive.class)) {
+ keepAliveBeans.put(requestEntry.getKey(), bean);
+ }
+ }
+ if (keepAliveBeans.size() > 0) {
+ return UIComponentBase.saveAttachedState(context, keepAliveBeans);
+ } else {
+ return null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void restoreAdditionalState(FacesContext context, Object state) {
+ if (null != state) {
+ boolean isAjax = AjaxContext.getCurrentInstance(context).isAjaxRequest();
+
+ // Append all saved beans to the request map.
+ Map beansMap = (Map) UIComponentBase.restoreAttachedState(context,
+ state);
+ Map<String, Object> requestMap = context.getExternalContext()
+ .getRequestMap();
+ for (Object key : beansMap.keySet()) {
+ Object bean = beansMap.get(key);
+ if (bean != null) {
+ KeepAlive annotation = bean.getClass().getAnnotation(KeepAlive.class);
+ if (annotation != null) {
+ if (!isAjax && annotation.ajaxOnly()) {
+
+ //skip ajax-only beans for non-ajax requests
+ continue;
+ }
+ }
+ }
+
+ requestMap.put((String) key, bean);
+ }
+ }
+ }
+
+ /**
+ * Restore logical view id from request.
+ *
+ * @param context
+ * @param viewId
+ * @param renderKitId
+ * @return
+ */
+ @SuppressWarnings("deprecation")
+ protected String restoreLogicalViewId(FacesContext context, String viewId,
+ String renderKitId) {
+ String id = (String) getRenderKit(context, renderKitId)
+ .getResponseStateManager().getTreeStructureToRestore(context,
+ viewId);
+ return id;
+ }
+
+ /**
+ * Return logical Id for current request view state. For a faces requests,
+ * generate sequence numbers. For a ajax request, attempt to re-use id from
+ * request submit.
+ *
+ * @param context
+ * @return
+ */
+ protected String getLogicalViewId(FacesContext context) {
+ AjaxContext ajaxContext = AjaxContext.getCurrentInstance(context);
+ ExternalContext externalContext = context.getExternalContext();
+ Object id=null;
+ Map<String, Object> requestMap = externalContext.getRequestMap();
+ id = requestMap.get(ajaxContext.isAjaxRequest()?AJAX_VIEW_SEQUENCE:VIEW_SEQUENCE);
+ if (null != id) {
+ return id.toString();
+ }
+ // Store sequence in session, to avoyd claster configuration problem
+ // see
https://javaserverfaces.dev.java.net/issues/show_bug.cgi?id=662
+ Object session = externalContext.getSession(true);
+ int viewSequence;
+ synchronized (session) {
+ Map<String, Object> sessionMap = externalContext.getSessionMap();
+ Integer sequence = (Integer) sessionMap
+ .get(VIEW_SEQUENCE_ATTRIBUTE);
+ if (null != sequence) {
+ viewSequence = sequence.intValue();
+ } else {
+ viewSequence = 0;
+ }
+ if (viewSequence++ == Character.MAX_VALUE) {
+ viewSequence = 0;
+ }
+ sessionMap.put(VIEW_SEQUENCE_ATTRIBUTE, new Integer(viewSequence));
+ }
+ String logicalViewId = UIViewRoot.UNIQUE_ID_PREFIX + ((int) viewSequence);
+ // Store new viewId in the request parameters, to avoid re-increments in the same
request.
+ requestMap.put(VIEW_SEQUENCE,logicalViewId);
+ return logicalViewId;
+ }
+
+ protected RenderKit getRenderKit(FacesContext context) {
+ String renderKitId = null;
+ UIViewRoot viewRoot = context.getViewRoot();
+ if (null != viewRoot) {
+ renderKitId = viewRoot.getRenderKitId();
+ }
+ if (null == renderKitId) {
+ renderKitId = context.getApplication().getViewHandler()
+ .calculateRenderKitId(context);
+ }
+ return getRenderKit(context, renderKitId);
+ }
+
+ 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;
+ }
+
+}
Modified:
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/context/ContextInitParameters.java
===================================================================
---
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/context/ContextInitParameters.java 2009-11-20
14:32:01 UTC (rev 15943)
+++
branches/community/3.3.X/framework/impl/src/main/java/org/ajax4jsf/context/ContextInitParameters.java 2009-11-20
15:16:08 UTC (rev 15944)
@@ -51,7 +51,20 @@
"com.sun.faces.serializeServerState",
"org.apache.myfaces.SERIALIZE_STATE_IN_SESSION"
};
+
+ /**
+ * @since 3.3.3
+ */
+ public static final String[] COMPRESS_SERVER_STATE = new String[] {
+ "org.ajax4jsf.COMPRESS_SERVER_STATE",
+
+ /* detect MyFaces vs. RI */
+ "com.sun.faces.compressViewState",
+ "org.apache.myfaces.COMPRESS_STATE_IN_SESSION",
+ "com.sun.faces.COMPRESS_STATE"
+ };
+
public static final String[] NUMBER_OF_VIEWS_IN_SESSION =
{"com.sun.faces.numberOfViewsInSession"};
public static final String[] NUMBER_OF_LOGICAL_VIEWS_IN_SESSION =
{"com.sun.faces.numberOfLogicalViews"};
@@ -66,6 +79,15 @@
public static final boolean isSerializeServerState(FacesContext context) {
return getBoolean(context, SERIALIZE_SERVER_STATE, false);
}
+
+ /**
+ * @since 3.3.3
+ * @param context
+ * @return
+ */
+ public static final boolean isCompressServerState(FacesContext context) {
+ return getBoolean(context, COMPRESS_SERVER_STATE, true);
+ }
/**
* Get number of views for store in session by {@link AjaxStateManager}