/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* Resin Open Source 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, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.jsf.application;
import java.io.*;
import java.util.*;
import java.util.logging.*;
import javax.faces.*;
import javax.faces.application.*;
import javax.faces.component.*;
import javax.faces.component.html.*;
import javax.faces.context.*;
import javax.faces.render.*;
import com.caucho.util.*;
import com.caucho.hessian.io.*;
import static com.caucho.jsf.application.SessionStateManager.StateSerializationMethod.HESSIAN;
public class SessionStateManager extends StateManager
{
private static final L10N L = new L10N(SessionStateManager.class);
private static final Logger log
= Logger.getLogger(SessionStateManager.class.getName());
private static final IntMap _typeMap = new IntMap();
private static final ArrayList<Class> _typeList = new ArrayList();
private StateSerializationMethod _stateSerializationMethod
= HESSIAN;
public void setStateSerializationMethod(StateSerializationMethod stateSerializationMethod)
{
_stateSerializationMethod = stateSerializationMethod;
}
@Override
public Object saveView(FacesContext context)
{
UIViewRoot root = context.getViewRoot();
if (root == null || root.isTransient())
return null;
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
StateSerializationWriter stateWriter
= createStateSerializationWriter(bos);
serialize(stateWriter, context, root, new HashSet<String>());
stateWriter.close();
byte []state = bos.toByteArray();
if (log.isLoggable(Level.FINE)) {
log.fine("JSF[" + root.getViewId() + "] serialize (" + state.length + " bytes)");
if (log.isLoggable(Level.FINER))
debugState(state);
}
if (! isSavingStateInClient(context)) {
Map sessionMap = context.getExternalContext().getSessionMap();
Map viewMap = (Map) sessionMap.get("caucho.jsf.view");
if (viewMap == null)
viewMap = new HashMap();
viewMap.put(root.getViewId(), state);
sessionMap.put("caucho.jsf.view", viewMap);
return "!";
}
return state;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private StateSerializationWriter createStateSerializationWriter(
ByteArrayOutputStream out)
throws IOException
{
if (HESSIAN.equals(_stateSerializationMethod))
return new HessianStateSerializationWriter(new Hessian2Output(out));
else
return new JavaStateSerializationWriter(new ObjectOutputStream(out));
}
private StateSerializationReader createStateSerializationReader(
ByteArrayInputStream in)
throws IOException
{
if (HESSIAN.equals(_stateSerializationMethod))
return new HessianStateSerializationReader(new Hessian2Input(in));
else {
ObjectInputStream ois = new ObjectInputStream(in) {
protected Class<?> resolveClass(ObjectStreamClass objectStreamClass)
throws IOException, ClassNotFoundException
{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return cl.loadClass(objectStreamClass.getName());
}
};
return new JavaStateSerializationReader(ois);
}
}
@Deprecated
@Override
public SerializedView saveSerializedView(FacesContext context)
{
return new SerializedView(saveView(context), null);
}
@Deprecated
public void writeState(FacesContext context,
SerializedView state)
throws IOException
{
Object [] stateArray = new Object [2];
stateArray [0] = state.getStructure();
stateArray [1] = state.getState();
writeState(context, stateArray);
}
public void writeState(FacesContext context, Object state)
throws IOException
{
ResponseStateManager rsm = context.getRenderKit().getResponseStateManager();
if (! (state instanceof Object []))
rsm.writeState(context, new Object []{state, null});
else
rsm.writeState(context, state);
}
@Override
public UIViewRoot restoreView(FacesContext context,
String viewId,
String renderKitId)
{
RenderKit renderKit = context.getRenderKit();
if (renderKit == null) {
RenderKitFactory renderKitFactory
= (RenderKitFactory) FactoryFinder.getFactory(
FactoryFinder.RENDER_KIT_FACTORY);
renderKit = renderKitFactory.getRenderKit(context, renderKitId);
}
ResponseStateManager rsm =
renderKit.getResponseStateManager();
Object state = rsm.getState(context, viewId);//sessionMap.get("caucho.jsf.view");
if (!isSavingStateInClient(context) && "!".equals(((Object [])state) [0])) {
Map viewMap = (Map) context.getExternalContext()
.getSessionMap()
.get("caucho.jsf.view");
if (viewMap != null)
state = viewMap.get(viewId);
}
if (state == null)
return null;
else if (state instanceof byte[])
return restoreView(context, (byte []) state, renderKitId);
if (state instanceof Object [])
return restoreView(context, (byte []) ((Object []) state) [0], renderKitId);
else if (state instanceof StateManager.SerializedView) {
StateManager.SerializedView serView
= (StateManager.SerializedView) state;
return restoreView(context, (byte[]) serView.getStructure(), renderKitId);
}
else
throw new IllegalStateException(L.l("unexpected saved state: '{0}'",
state));
}
private void serialize(StateSerializationWriter out,
FacesContext context,
UIComponent comp,
HashSet<String> idMap)
throws IOException
{
if (comp.isTransient())
return;
if (idMap.contains(comp.getId()))
throw new IllegalStateException(L.l("'{0}' is a duplicate component during serialization.",
comp.getId()));
if (comp.getId() != null)
idMap.add(comp.getId());
if (comp instanceof NamingContainer)
idMap = new HashSet<String>(8);
int typeId = _typeMap.get(comp.getClass());
out.writeInt(typeId);
if (typeId <= 0)
out.writeString(comp.getClass().getName());
int fullChildCount = comp.getChildCount();
if (fullChildCount > 0) {
int childCount = 0;
List<UIComponent> children = comp.getChildren();
for (int i = 0; i < fullChildCount; i++) {
UIComponent child = children.get(i);
if (! child.isTransient())
childCount++;
}
out.writeInt(childCount);
for (int i = 0; i < fullChildCount; i++) {
UIComponent child = children.get(i);
serialize(out, context, child, idMap);
}
}
else
out.writeInt(0);
int facetCount = comp.getFacetCount();
out.writeInt(facetCount);
if (facetCount > 0) {
for (Map.Entry<String,UIComponent> entry : comp.getFacets().entrySet()) {
out.writeString(entry.getKey());
serialize(out, context, entry.getValue(), idMap);
}
}
out.writeObject(comp.saveState(context));
}
private UIViewRoot restoreView(FacesContext context,
byte[] data,
String renderKitId)
{
if (data == null)
return null;
try {
ByteArrayInputStream bis = new ByteArrayInputStream(data);
StateSerializationReader stateReader
= createStateSerializationReader(bis);
return (UIViewRoot) deserialize(stateReader, context, renderKitId);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private UIComponent deserialize(StateSerializationReader in,
FacesContext context,
String renderKitId)
throws IOException,
ClassNotFoundException,
InstantiationException,
IllegalAccessException
{
int typeId = in.readInt();
Class type;
if (typeId > 0) {
type = _typeList.get(typeId);
}
else {
String typeName = in.readString();
ClassLoader loader = Thread.currentThread().getContextClassLoader();
type = Class.forName(typeName, false, loader);
}
final UIComponent comp = (UIComponent) type.newInstance();
if (UIViewRoot.class.equals(type)) {
final UIViewRoot viewRoot = (UIViewRoot) comp;
viewRoot.setRenderKitId(renderKitId);
context.setViewRoot(viewRoot);
}
int childCount = in.readInt();
for (int i = 0; i < childCount; i++) {
comp.getChildren().add(deserialize(in, context, renderKitId));
}
int facetCount = in.readInt();
for (int i = 0; i < facetCount; i++) {
String key = in.readString();
comp.getFacets().put(key, deserialize(in, context, renderKitId));
}
comp.restoreState(context, in.readObject());
return comp;
}
public String toString()
{
return "SessionStateManager[]";
}
private static void addType(Class type)
{
if (_typeMap.get(type) > 0)
return;
_typeMap.put(type, _typeList.size());
_typeList.add(type);
}
private void debugState(byte []state)
{
for (int i = 0; i < state.length; i++) {
if (i != 0 && i % 40 == 0)
System.out.println();
int ch = state[i];
if ('a' <= ch && ch <= 'z'
|| 'A' <= ch && ch <= 'Z'
|| '0' <= ch && ch <= '9'
|| ch == ' ' || ch == '[' || ch == '.' || ch == ']'
|| ch == '/' || ch == '\\'
|| ch == '-' || ch == '_' || ch == '{' || ch == '}'
|| ch == '#' || ch == '$' || ch == ':')
System.out.print((char) ch);
else {
System.out.print("x"
+ Integer.toHexString((ch / 16) & 0xf)
+ Integer.toHexString(ch & 0xf));
}
}
System.out.println();
}
static {
_typeList.add(null);
addType(UIColumn.class);
addType(UICommand.class);
addType(UIData.class);
addType(UIForm.class);
addType(UIGraphic.class);
addType(UIInput.class);
addType(UIMessage.class);
addType(UIMessages.class);
addType(UINamingContainer.class);
addType(UIOutput.class);
addType(UIPanel.class);
addType(UIParameter.class);
addType(UISelectBoolean.class);
addType(UISelectItem.class);
addType(UISelectItems.class);
addType(UISelectMany.class);
addType(UISelectOne.class);
addType(UIViewRoot.class);
addType(HtmlColumn.class);
addType(HtmlCommandButton.class);
addType(HtmlCommandLink.class);
addType(HtmlDataTable.class);
addType(HtmlForm.class);
addType(HtmlGraphicImage.class);
addType(HtmlInputHidden.class);
addType(HtmlInputSecret.class);
addType(HtmlInputText.class);
addType(HtmlInputTextarea.class);
addType(HtmlMessage.class);
addType(HtmlMessages.class);
addType(HtmlOutputFormat.class);
addType(HtmlOutputLabel.class);
addType(HtmlOutputLink.class);
addType(HtmlOutputText.class);
addType(HtmlPanelGrid.class);
addType(HtmlPanelGroup.class);
addType(HtmlSelectBooleanCheckbox.class);
addType(HtmlSelectManyCheckbox.class);
addType(HtmlSelectManyListbox.class);
addType(HtmlSelectManyMenu.class);
addType(HtmlSelectOneListbox.class);
addType(HtmlSelectOneMenu.class);
addType(HtmlSelectOneRadio.class);
}
public static enum StateSerializationMethod {
HESSIAN,
JAVA
}
static interface StateSerializationWriter {
public void writeInt(int value) throws IOException;
public void writeString(String value) throws IOException;
public void writeObject(Object object) throws IOException;
public void close()
throws IOException;
}
static interface StateSerializationReader {
public int readInt() throws IOException;
public String readString() throws IOException;
public Object readObject() throws IOException, ClassNotFoundException;
}
static class HessianStateSerializationWriter
implements StateSerializationWriter {
private Hessian2Output _out;
HessianStateSerializationWriter(Hessian2Output out)
{
_out = out;
}
public void writeString(String value)
throws IOException
{
_out.writeString(value);
}
public void writeObject(Object object)
throws IOException
{
_out.writeObject(object);
}
public void writeInt(int value)
throws IOException
{
_out.writeInt(value);
}
public void close()
throws IOException
{
_out.flush();
_out.close();
}
}
static class JavaStateSerializationWriter
implements StateSerializationWriter {
private ObjectOutputStream _out;
JavaStateSerializationWriter(ObjectOutputStream out)
{
_out = out;
}
public void writeString(String s)
throws IOException
{
_out.writeUTF(s);
}
public void writeInt(int i)
throws IOException
{
_out.writeInt(i);
}
public void writeObject(Object o)
throws IOException
{
_out.writeObject(o);
}
public void close()
throws IOException
{
_out.flush();
_out.close();
}
}
static class HessianStateSerializationReader
implements StateSerializationReader {
private Hessian2Input _in;
HessianStateSerializationReader(Hessian2Input in)
{
_in = in;
}
public String readString()
throws IOException
{
return _in.readString();
}
public int readInt()
throws IOException
{
return _in.readInt();
}
public Object readObject()
throws IOException, ClassNotFoundException
{
return _in.readObject();
}
}
static class JavaStateSerializationReader
implements StateSerializationReader {
private ObjectInputStream _in;
JavaStateSerializationReader(ObjectInputStream in)
{
_in = in;
}
public String readString()
throws IOException
{
return _in.readUTF();
}
public int readInt()
throws IOException
{
return _in.readInt();
}
public Object readObject()
throws IOException, ClassNotFoundException
{
return _in.readObject();
}
}
}