/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package renderkits.renderkit.xul;
import javax.faces.FacesException;
import javax.faces.application.StateManager;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.ResponseStateManager;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import renderkits.renderkit.SerializedView;
import renderkits.util.Base64;
import renderkits.util.ByteArrayGuard;
import renderkits.util.Util;
/**
* <p><strong>XULResponseStateManager</strong> is the helper class to
* {@link javax.faces.application.StateManager} that knows the specific
* rendering technology being used to generate the response. This class
* will write out state using hidden <code>XUL <text></code> elements.
*/
public class XULResponseStateManager extends ResponseStateManager {
//
// Protected Constants
//
// Log instance for this class
private static final Logger logger =
Util.getLogger(Util.FACES_LOGGER + Util.RENDERKIT_LOGGER);
private static final String FACES_VIEW_STATE =
"com.sun.faces.FACES_VIEW_STATE";
private static final String COMPRESS_STATE_PARAM =
"com.sun.faces.COMPRESS_STATE";
//
// Class Variables
//
//
// Instance Variables
//
private Boolean compressStateSet = null;
private ByteArrayGuard byteArrayGuard = null;
//
// Ivars used during actual client lifetime
//
// Relationship Instance Variables
//
// Constructors and Initializers
//
public XULResponseStateManager() {
super();
byteArrayGuard = new ByteArrayGuard();
}
//
// Class methods
//
//
// General Methods
//
//
// Methods From ResponseStateManager
//
public Object getState(FacesContext context, String viewId) {
Object stateArray[] = {getTreeStructure(context, viewId),
getComponentState(context)};
return stateArray;
}
public boolean isPostback(FacesContext context) {
boolean result = context.getExternalContext().getRequestParameterMap().
containsKey(javax.faces.render.ResponseStateManager.VIEW_STATE_PARAM);
return result;
}
public void writeState(FacesContext context,
Object state) throws IOException {
SerializedView view = null;
if (state instanceof SerializedView) {
view = (SerializedView) state;
} else {
if (state instanceof Object[]) {
Object[] stateArray = (Object[]) state;
if (2 == stateArray.length) {
StateManager stateManager =
context.getApplication().getStateManager();
view = new SerializedView(stateArray[0],
stateArray[1]);
} else {
//PENDING - I18N
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE,
"State is not an expected array of length 2.");
}
throw new IOException(
"State is not an expected array of length 2.");
}
} else {
//PENDING - I18N
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE,
"State is not an expected array of length 2.");
}
throw new IOException(
"State is not an expected array of length 2.");
}
}
writeState(context, view);
}
private void writeState(FacesContext context, SerializedView view)
throws IOException {
StateManager stateManager = context.getApplication().getStateManager();
ResponseWriter writer = context.getResponseWriter();
writer.startElement("textbox", context.getViewRoot());
writer.writeAttribute("name",
javax.faces.render.ResponseStateManager.VIEW_STATE_PARAM,
null);
writer.writeAttribute("id",
javax.faces.render.ResponseStateManager.VIEW_STATE_PARAM,
null);
if (stateManager.isSavingStateInClient(context)) {
GZIPOutputStream zos = null;
ObjectOutputStream oos = null;
boolean compress = isCompressStateSet(context);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
if (compress) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Compressing state before saving..");
}
zos = new GZIPOutputStream(bos);
oos = new ObjectOutputStream(zos);
} else {
oos = new ObjectOutputStream(bos);
}
oos.writeObject(view.getStructure());
oos.writeObject(view.getState());
oos.close();
if (compress) {
zos.close();
}
byte[] securedata = byteArrayGuard.encrypt(context,
bos.toByteArray());
bos.close();
String valueToWrite = (new String(Base64.encode(securedata),
"ISO-8859-1"));
writer.writeAttribute("value", valueToWrite, null);
writer.writeAttribute("hidden", "true", "hidden");
writer.writeText(valueToWrite, null);
} else {
writer.writeAttribute("value", view.getStructure(), null);
writer.writeAttribute("hidden", "true", "hidden");
writer.writeText(view.getStructure(), null);
}
writer.endElement("textbox");
writer.writeText("\n", null);
// write this out regardless of state saving mode
// Only write it out if there is a default render kit Identifier specified,
// and this render kit identifier is not the default.
String result = context.getApplication().getDefaultRenderKitId();
if ((null != result && !result.equals("XUL")) || result == null) {
writer.startElement("textbox", context.getViewRoot());
writer.writeAttribute("name",
ResponseStateManager.RENDER_KIT_ID_PARAM,
"name");
writer.writeAttribute("id",
ResponseStateManager.RENDER_KIT_ID_PARAM,
"id");
writer.writeAttribute("value", "XUL", "value");
writer.writeAttribute("hidden", "true", "hidden");
writer.writeText("XUL", null);
writer.endElement("textbox");
writer.writeText("\n", null);
}
}
private Object getComponentState(FacesContext context) {
// requestMap is a local variable so we don't need to synchronize
Map<String, Object> requestMap =
context.getExternalContext().getRequestMap();
Object state = requestMap.get(FACES_VIEW_STATE);
// null out the temporary attribute, since we don't need it anymore.
requestMap.remove(FACES_VIEW_STATE);
return state;
}
private Object getTreeStructure(FacesContext context,
String treeId) {
StateManager stateManager = context.getApplication().getStateManager();
Map<String, String> requestParamMap = context.getExternalContext()
.getRequestParameterMap();
String viewString = requestParamMap.get(
javax.faces.render.ResponseStateManager.VIEW_STATE_PARAM);
Object structure = null;
if (viewString == null) {
return null;
}
if (stateManager.isSavingStateInClient(context)) {
Object state = null;
ByteArrayInputStream bis = null;
GZIPInputStream gis = null;
ObjectInputStream ois = null;
boolean compress = isCompressStateSet(context);
try {
byte[] bytes = byteArrayGuard.decrypt(context,
(Base64.decode(viewString.getBytes())));
bis = new ByteArrayInputStream(bytes);
if (isCompressStateSet(context)) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Deflating state before restoring..");
}
gis = new GZIPInputStream(bis);
ois = new ObjectInputStream(gis);
} else {
ois = new ObjectInputStream(bis);
}
structure = ois.readObject();
state = ois.readObject();
Map<String, Object> requestMap =
context.getExternalContext().getRequestMap();
// store the state object temporarily in request scope
// until it is processed by getComponentState
// which resets it.
requestMap.put(FACES_VIEW_STATE, state);
bis.close();
if (compress) {
gis.close();
}
ois.close();
} catch (java.io.OptionalDataException ode) {
logger.log(Level.SEVERE, ode.getMessage(), ode);
throw new FacesException(ode);
} catch (java.lang.ClassNotFoundException cnfe) {
logger.log(Level.SEVERE, cnfe.getMessage(), cnfe);
throw new FacesException(cnfe);
} catch (java.io.IOException iox) {
logger.log(Level.SEVERE, iox.getMessage(), iox);
throw new FacesException(iox);
}
} else {
structure = viewString;
}
return structure;
}
private boolean isCompressStateSet(FacesContext context) {
if (null != compressStateSet) {
return compressStateSet.booleanValue();
}
compressStateSet = Boolean.TRUE;
String compressStateParam = context.getExternalContext().
getInitParameter(COMPRESS_STATE_PARAM);
if (compressStateParam != null) {
compressStateSet = Boolean.valueOf(compressStateParam);
}
return compressStateSet.booleanValue();
}
} // end of class XULResponseStateManager