001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License version 2
011: * as published by the Free Software Foundation.
012: *
013: * Resin Open Source is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
016: * of NON-INFRINGEMENT. See the GNU General Public License for more
017: * details.
018: *
019: * You should have received a copy of the GNU General Public License
020: * along with Resin Open Source; if not, write to the
021: *
022: * Free Software Foundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Scott Ferguson
027: */
028:
029: package com.caucho.jsf.application;
030:
031: import java.io.*;
032: import java.util.*;
033: import java.util.logging.*;
034:
035: import javax.faces.*;
036: import javax.faces.application.*;
037: import javax.faces.component.*;
038: import javax.faces.component.html.*;
039: import javax.faces.context.*;
040: import javax.faces.render.*;
041:
042: import javax.servlet.*;
043:
044: import com.caucho.util.*;
045: import com.caucho.hessian.io.*;
046:
047: public class SessionStateManager extends StateManager {
048: private static final L10N L = new L10N(SessionStateManager.class);
049: private static final Logger log = Logger
050: .getLogger(SessionStateManager.class.getName());
051:
052: private static final IntMap _typeMap = new IntMap();
053: private static final ArrayList<Class> _typeList = new ArrayList();
054:
055: @Override
056: public Object saveView(FacesContext context) {
057: UIViewRoot root = context.getViewRoot();
058:
059: if (root == null || root.isTransient())
060: return null;
061:
062: try {
063: ByteArrayOutputStream bos = new ByteArrayOutputStream();
064:
065: Hessian2Output out = new Hessian2Output(bos);
066:
067: serialize(out, context, root, new HashSet<String>());
068:
069: out.close();
070:
071: byte[] state = bos.toByteArray();
072:
073: if (log.isLoggable(Level.FINE)) {
074: log.fine("JSF[" + root.getViewId() + "] serialize ("
075: + state.length + " bytes)");
076:
077: if (log.isLoggable(Level.FINER))
078: debugState(state);
079: }
080:
081: if (!isSavingStateInClient(context)) {
082: context.getExternalContext().getSessionMap().put(
083: "caucho.jsf.view", state);
084: return "!";
085: }
086:
087: return state;
088: } catch (IOException e) {
089: throw new RuntimeException(e);
090: }
091: }
092:
093: @Deprecated
094: @Override
095: public SerializedView saveSerializedView(FacesContext context) {
096: return new SerializedView(saveView(context), null);
097: }
098:
099: @Deprecated
100: public void writeState(FacesContext context, SerializedView state)
101: throws IOException {
102: ResponseStateManager rsm = context.getRenderKit()
103: .getResponseStateManager();
104:
105: Object[] stateArray = new Object[2];
106: stateArray[0] = state.getStructure();
107: stateArray[1] = state.getState();
108:
109: rsm.writeState(context, stateArray);
110: }
111:
112: public void writeState(FacesContext context, Object state)
113: throws IOException {
114: writeState(context, new SerializedView(state, null));
115: }
116:
117: @Override
118: public UIViewRoot restoreView(FacesContext context, String viewId,
119: String renderKitId) {
120:
121: RenderKit renderKit = context.getRenderKit();
122:
123: if (renderKit == null) {
124: RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder
125: .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
126:
127: renderKit = renderKitFactory.getRenderKit(context,
128: renderKitId);
129: }
130:
131: ResponseStateManager rsm = renderKit.getResponseStateManager();
132:
133: Object state = rsm.getState(context, viewId);//sessionMap.get("caucho.jsf.view");
134:
135: if (!isSavingStateInClient(context)
136: && "!".equals(((Object[]) state)[0])) {
137: state = context.getExternalContext().getSessionMap().get(
138: "caucho.jsf.view");
139: }
140:
141: if (state == null)
142: return null;
143: else if (state instanceof byte[])
144: return restoreView(context, (byte[]) state);
145: else if (state instanceof StateManager.SerializedView) {
146: StateManager.SerializedView serView = (StateManager.SerializedView) state;
147:
148: return restoreView(context, (byte[]) serView.getStructure());
149: } else
150: throw new IllegalStateException(L.l(
151: "unexpected saved state: '{0}'", state));
152: }
153:
154: private void serialize(AbstractHessianOutput out,
155: FacesContext context, UIComponent comp,
156: HashSet<String> idMap) throws IOException {
157: if (comp.isTransient())
158: return;
159:
160: if (idMap.contains(comp.getId()))
161: throw new IllegalStateException(
162: L
163: .l(
164: "'{0}' is a duplicate component during serialization.",
165: comp.getId()));
166:
167: if (comp.getId() != null)
168: idMap.add(comp.getId());
169:
170: if (comp instanceof NamingContainer)
171: idMap = new HashSet<String>(8);
172:
173: int typeId = _typeMap.get(comp.getClass());
174: out.writeInt(typeId);
175: if (typeId <= 0)
176: out.writeString(comp.getClass().getName());
177:
178: int fullChildCount = comp.getChildCount();
179: if (fullChildCount > 0) {
180: int childCount = 0;
181:
182: List<UIComponent> children = comp.getChildren();
183:
184: for (int i = 0; i < fullChildCount; i++) {
185: UIComponent child = children.get(i);
186:
187: if (!child.isTransient())
188: childCount++;
189: }
190:
191: out.writeInt(childCount);
192:
193: for (int i = 0; i < fullChildCount; i++) {
194: UIComponent child = children.get(i);
195:
196: serialize(out, context, child, idMap);
197: }
198: } else
199: out.writeInt(0);
200:
201: int facetCount = comp.getFacetCount();
202: out.writeInt(facetCount);
203:
204: if (facetCount > 0) {
205: for (Map.Entry<String, UIComponent> entry : comp
206: .getFacets().entrySet()) {
207: out.writeString(entry.getKey());
208:
209: serialize(out, context, entry.getValue(), idMap);
210: }
211: }
212:
213: out.writeObject(comp.saveState(context));
214: }
215:
216: private UIViewRoot restoreView(FacesContext context, byte[] data) {
217: if (data == null)
218: return null;
219:
220: try {
221: ByteArrayInputStream bis = new ByteArrayInputStream(data);
222: Hessian2Input in = new Hessian2Input(bis);
223:
224: return (UIViewRoot) deserialize(in, context);
225: } catch (Exception e) {
226: throw new RuntimeException(e);
227: }
228: }
229:
230: private UIComponent deserialize(AbstractHessianInput in,
231: FacesContext context) throws IOException,
232: ClassNotFoundException, InstantiationException,
233: IllegalAccessException {
234: int typeId = in.readInt();
235:
236: Class type;
237:
238: if (typeId > 0) {
239: type = _typeList.get(typeId);
240: } else {
241: String typeName = in.readString();
242:
243: ClassLoader loader = Thread.currentThread()
244: .getContextClassLoader();
245:
246: type = Class.forName(typeName, false, loader);
247: }
248:
249: UIComponent comp = (UIComponent) type.newInstance();
250:
251: int childCount = in.readInt();
252: for (int i = 0; i < childCount; i++) {
253: comp.getChildren().add(deserialize(in, context));
254: }
255:
256: int facetCount = in.readInt();
257:
258: for (int i = 0; i < facetCount; i++) {
259: String key = in.readString();
260:
261: comp.getFacets().put(key, deserialize(in, context));
262: }
263:
264: comp.restoreState(context, in.readObject());
265:
266: return comp;
267: }
268:
269: public String toString() {
270: return "SessionStateManager[]";
271: }
272:
273: private static void addType(Class type) {
274: if (_typeMap.get(type) > 0)
275: return;
276:
277: _typeMap.put(type, _typeList.size());
278: _typeList.add(type);
279: }
280:
281: private void debugState(byte[] state) {
282: for (int i = 0; i < state.length; i++) {
283: if (i != 0 && i % 40 == 0)
284: System.out.println();
285:
286: int ch = state[i];
287:
288: if ('a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z'
289: || '0' <= ch && ch <= '9' || ch == ' ' || ch == '['
290: || ch == '.' || ch == ']' || ch == '/'
291: || ch == '\\' || ch == '-' || ch == '_'
292: || ch == '{' || ch == '}' || ch == '#' || ch == '$'
293: || ch == ':')
294: System.out.print((char) ch);
295: else {
296: System.out.print("x"
297: + Integer.toHexString((ch / 16) & 0xf)
298: + Integer.toHexString(ch & 0xf));
299: }
300: }
301: System.out.println();
302: }
303:
304: static {
305: _typeList.add(null);
306:
307: addType(UIColumn.class);
308: addType(UICommand.class);
309: addType(UIData.class);
310: addType(UIForm.class);
311: addType(UIGraphic.class);
312: addType(UIInput.class);
313: addType(UIMessage.class);
314: addType(UIMessages.class);
315: addType(UINamingContainer.class);
316: addType(UIOutput.class);
317: addType(UIPanel.class);
318: addType(UIParameter.class);
319: addType(UISelectBoolean.class);
320: addType(UISelectItem.class);
321: addType(UISelectItems.class);
322: addType(UISelectMany.class);
323: addType(UISelectOne.class);
324: addType(UIViewRoot.class);
325:
326: addType(HtmlColumn.class);
327: addType(HtmlCommandButton.class);
328: addType(HtmlCommandLink.class);
329: addType(HtmlDataTable.class);
330: addType(HtmlForm.class);
331: addType(HtmlGraphicImage.class);
332: addType(HtmlInputHidden.class);
333: addType(HtmlInputSecret.class);
334: addType(HtmlInputText.class);
335: addType(HtmlInputTextarea.class);
336: addType(HtmlMessage.class);
337: addType(HtmlMessages.class);
338: addType(HtmlOutputFormat.class);
339: addType(HtmlOutputLabel.class);
340: addType(HtmlOutputLink.class);
341: addType(HtmlOutputText.class);
342: addType(HtmlPanelGrid.class);
343: addType(HtmlPanelGroup.class);
344: addType(HtmlSelectBooleanCheckbox.class);
345: addType(HtmlSelectManyCheckbox.class);
346: addType(HtmlSelectManyListbox.class);
347: addType(HtmlSelectManyMenu.class);
348: addType(HtmlSelectOneListbox.class);
349: addType(HtmlSelectOneMenu.class);
350: addType(HtmlSelectOneRadio.class);
351: }
352: }
|