package org.jboss.seam.faces;
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.util.HashSet;
import java.util.Set;
import org.jboss.seam.Component;
import org.jboss.seam.core.Init;
import org.jboss.seam.log.LogProvider;
import org.jboss.seam.log.Logging;
import com.sun.faces.spi.SerializationProvider;
/**
* This serialization provider can be used by JSF when restoring the view. It will check
* the hot deploy classloader(s) for class definitions that cannot be found in the context
* classloader. It is only needed when using client side state saving with hotdeployable classes
* in the view. To enable, add the following to web.xml:
*
* <context-param>
* <param-name>com.sun.faces.serializationProvider</param-name>
* <param-value>org.jboss.seam.faces.SeamDebugSerializationProvider</param-value>
* </context-param>
*/
public class SeamDebugSerializationProvider
implements SerializationProvider
{
public ObjectInputStream createObjectInputStream(InputStream source) throws IOException {
return new SeamObjectInputStream(source);
}
public ObjectOutputStream createObjectOutputStream(OutputStream destination) throws IOException {
return new ObjectOutputStream(destination);
}
static class SeamObjectInputStream
extends ObjectInputStream
{
private static final LogProvider log = Logging.getLogProvider(SeamObjectInputStream.class);
public SeamObjectInputStream(InputStream source)
throws IOException
{
super(source);
}
@Override
protected Class<?> resolveClass(ObjectStreamClass desc)
throws IOException,
ClassNotFoundException
{
String className = desc.getName();
try {
return Class.forName(className,
true,
Thread.currentThread().getContextClassLoader());
} catch (ClassNotFoundException e) {
Class found = lookupInHotDeployLoaders(className);
if (found != null) {
return found;
}
// can't find it - rethrow the class not found exception
throw e;
}
}
private Class lookupInHotDeployLoaders(String className) {
log.debug("need to check hotdeploy classloaders to resolve " + className);
// this code can be made tighter if we assume there is only one hotdeployable location
Set<ClassLoader> loaders = new HashSet<ClassLoader>();
for (String name: Init.instance().getHotDeployableComponents()) {
ClassLoader loader = loaderForComponent(name);
// make sure we only try the loader once
if (loaders.add(loader)) {
Class c = tryToLoadClass(loader,className);
if (c != null){
return c;
}
}
}
return null;
}
private Class tryToLoadClass(ClassLoader loader, String className) {
try {
return loader.loadClass(className);
} catch (Exception e) {
log.debug("class not found in loader" + loader);
return null;
}
}
private ClassLoader loaderForComponent(String name) {
Component component = (Component) Component.getInstance(name + ".component");
if (component == null) {
log.debug("Couldn't find component for " + name);
return null;
}
return component.getBeanClass().getClassLoader();
}
}
}