Skip to content

Commit

Permalink
Added serialization of proxies
Browse files Browse the repository at this point in the history
  • Loading branch information
drallen committed May 3, 2010
1 parent 2004320 commit 80269e6
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 32 deletions.
Expand Up @@ -36,16 +36,16 @@
import org.jboss.weld.injection.WeldInjectionPoint;

/**
* This special proxy factory is mostly used for abstract decorators. When
* a delegate field is injected, the abstract methods directly invoke the
* corresponding method on the delegate. All other cases forward the calls
* to the {@link BeanInstance} for further processing.
* This special proxy factory is mostly used for abstract decorators. When a
* delegate field is injected, the abstract methods directly invoke the
* corresponding method on the delegate. All other cases forward the calls to
* the {@link BeanInstance} for further processing.
*
* @author David Allen
*/
public class DecoratorProxyFactory<T> extends ProxyFactory<T>
{
private static final String PROXY_SUFFIX = "DecoratorProxy";
public static final String PROXY_SUFFIX = "DecoratorProxy";
private final WeldInjectionPoint<?, ?> delegateInjectionPoint;
private final CtClass delegateClass;
private final Field delegateField;
Expand Down
Expand Up @@ -34,7 +34,7 @@
*/
public class EnterpriseProxyFactory<T> extends ProxyFactory<T>
{
private static final String PROXY_SUFFIX = "EnterpriseProxy";
public static final String PROXY_SUFFIX = "EnterpriseProxy";

/**
* Produces a factory for a specific bean implementation.
Expand Down
77 changes: 65 additions & 12 deletions impl/src/main/java/org/jboss/weld/bean/proxy/ProxyFactory.java
Expand Up @@ -22,6 +22,7 @@
import static org.jboss.weld.logging.messages.BeanMessage.PROXY_INSTANTIATION_BEAN_ACCESS_FAILED;
import static org.jboss.weld.logging.messages.BeanMessage.PROXY_INSTANTIATION_FAILED;

import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.Modifier;
import java.security.ProtectionDomain;
Expand Down Expand Up @@ -58,13 +59,13 @@ public class ProxyFactory<T>
// The log provider
protected static final LocLogger log = loggerFactory().getLogger(BEAN);
// Default proxy class name suffix
private static final String PROXY_SUFFIX = "Proxy";
public static final String PROXY_SUFFIX = "Proxy";

protected final Class<?> beanType;
protected final ArrayList<Class<?>> additionalInterfaces = new ArrayList<Class<?>>();
protected final ClassLoader classLoader;
protected final ProtectionDomain protectionDomain;
protected final ClassPool classPool;
protected final ClassLoader classLoader;
protected final ProtectionDomain protectionDomain;
protected final ClassPool classPool;

/**
* Creates a new proxy factory from any type of BeanInstance. This bean
Expand Down Expand Up @@ -168,6 +169,18 @@ public Class<T> getProxyClass()
return proxyClass;
}

/**
* Convenience method to determine if an object is a proxy generated by this
* factory or any derived factory.
*
* @param proxySuspect the object suspected of being a proxy
* @return true only if it is a proxy object
*/
public static boolean isProxy(Object proxySuspect)
{
return proxySuspect instanceof Proxy;
}

/**
* Convenience method to set the underlying bean instance for a proxy.
*
Expand Down Expand Up @@ -236,8 +249,8 @@ private Class<T> createProxyClass(String proxyClassName) throws Exception
}

/**
* Adds a constructor for the proxy for each constructor declared
* by the base bean type.
* Adds a constructor for the proxy for each constructor declared by the base
* bean type.
*
* @param proxyClassType the Javassist class for the proxy
*/
Expand All @@ -264,12 +277,15 @@ protected void addConstructors(CtClass proxyClassType)
}
}

private void addFields(CtClass proxyClassType)
protected void addFields(CtClass proxyClassType)
{
// The field for the instance locator
try
{
// The field representing the underlying instance or special method
// handling
proxyClassType.addField(new CtField(classPool.get("org.jboss.weld.bean.proxy.BeanInstance"), "beanInstance", proxyClassType));
// Special field used during serialization of a proxy
proxyClassType.addField(new CtField(CtClass.booleanType, "firstSerializationPhaseComplete", proxyClassType), "false");
}
catch (Exception e)
{
Expand All @@ -284,6 +300,42 @@ private void addMethods(CtClass proxyClassType)

// Add special proxy methods
addSpecialMethods(proxyClassType);

// Add serialization support methods
addSerializationSupport(proxyClassType);
}

/**
* Adds special serialization code be providing a writeReplace() method on
* the proxy. This method when first called will substitute the proxy
* object with an instance of {@link org.jboss.weld.proxy.util.SerializableProxy}.
* Subsequent calls will receive the proxy object itself permitting the substitute
* object to serialize the proxy.
*
* @param proxyClassType the Javassist class for the proxy class
*/
protected void addSerializationSupport(CtClass proxyClassType)
{
try
{
// Create a two phase writeReplace where the first call uses a
// replacement object and subsequent calls get the proxy object.
CtClass exception = classPool.get(ObjectStreamException.class.getName());
CtClass objectClass = classPool.get(Object.class.getName());
String writeReplaceBody = "{ " +
" if (firstSerializationPhaseComplete)" +
" return $0; " +
" else {" +
" firstSerializationPhaseComplete = true; " +
" return ((org.jboss.weld.serialization.spi.ProxyServices)org.jboss.weld.Container.instance().services().get(org.jboss.weld.serialization.spi.ProxyServices.class)).wrapForSerialization($0);" +
" } }";
proxyClassType.addMethod(CtNewMethod.make(objectClass, "writeReplace", null, new CtClass[] { exception }, writeReplaceBody, proxyClassType));
}
catch (Exception e)
{
throw new WeldException(e);
}

}

protected void addMethodsFromClass(CtClass proxyClassType)
Expand Down Expand Up @@ -387,7 +439,8 @@ protected String getSignatureClasses(CtMethod method) throws NotFoundException
}

/**
* Adds methods requiring special implementations rather than just delegation.
* Adds methods requiring special implementations rather than just
* delegation.
*
* @param proxyClassType the Javassist class description for the proxy type
*/
Expand Down Expand Up @@ -419,9 +472,9 @@ protected void addSpecialMethods(CtClass proxyClassType)
}

/**
* Creates the method body code for methods which forward the calls
* directly to the bean instance. These methods are not considered
* to be implemented by any superclass of the proxy.
* Creates the method body code for methods which forward the calls directly
* to the bean instance. These methods are not considered to be implemented
* by any superclass of the proxy.
*
* @param method a method
* @return code for the body of the method to be compiled
Expand Down
@@ -0,0 +1,116 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2008, Red Hat, Inc. and/or its affiliates, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jboss.weld.bean.proxy.util;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;

import org.jboss.weld.Container;
import org.jboss.weld.bean.proxy.ProxyFactory;
import org.jboss.weld.exceptions.ForbiddenStateException;
import org.jboss.weld.logging.messages.BeanMessage;
import org.jboss.weld.serialization.spi.ProxyServices;

/**
* A wrapper mostly for client proxies which provides header information
* useful to generate the client proxy class in a VM before the proxy
* object is deserialized. Only client proxies really need this
* extra step for serialization and deserialization since the other
* proxy classes are generated during bean archive deployment.
*
* @author David Allen
*/
public class SerializableProxy implements Serializable
{

private static final long serialVersionUID = -7682006876407447753L;

// Information required to generate client proxy classes
private final String proxyClassName;
private final String proxySuperClassName;

// The wrapped proxy object not serialized by default actions
private transient Object proxyObject;

public SerializableProxy(Object proxyObject)
{
if (!ProxyFactory.isProxy(proxyObject))
{
throw new ForbiddenStateException(BeanMessage.PROXY_REQUIRED);
}
this.proxyClassName = proxyObject.getClass().getName();
this.proxySuperClassName = proxyObject.getClass().getSuperclass().getName();
this.proxyObject = proxyObject;
}

/**
* Writes this object to the stream and also appends the serialization of
* the proxy object afterwards. This allows this wrapper to later recover
* the proxy class before trying to deserialize the proxy object.
*
* @param out the output stream of objects
* @throws IOException
*/
private void writeObject(ObjectOutputStream out) throws IOException
{
out.defaultWriteObject();
// Must use another OO stream since the proxy was replaced in the original
ObjectOutputStream out2 = new ObjectOutputStream(out);
out2.writeObject(proxyObject);
}

/**
* First reads the state of this object from the stream, then generates the
* proxy class if needed, and then deserializes the proxy object.
*
* @param in the object input stream
* @throws IOException
* @throws ClassNotFoundException
*/
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
{
in.defaultReadObject();
// Must use another OO stream per writeObject() above
ObjectInputStream in2 = new ObjectInputStream(in);
Class<?> proxyBeanType = Container.instance().services().get(ProxyServices.class).loadProxySuperClass(proxySuperClassName);
if (proxyClassName.endsWith(ProxyFactory.PROXY_SUFFIX))
{
generateClientProxyClass(proxyBeanType);
}
proxyObject = in2.readObject();
}

/**
* Always returns the original proxy object that was serialized.
*
* @return the proxy object
* @throws ObjectStreamException
*/
Object readResolve() throws ObjectStreamException
{
return proxyObject;
}

private <T> void generateClientProxyClass(Class<T> beanType)
{
new ProxyFactory<T>(beanType).getProxyClass();
}
}
Expand Up @@ -17,25 +17,26 @@

package org.jboss.weld.bean.proxy.util;

import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain;

import org.jboss.weld.exceptions.WeldException;
import org.jboss.weld.logging.messages.BeanMessage;
import org.jboss.weld.serialization.spi.ProxyServices;

/**
* A default implementation of the {@link ProxyServices} which simply use the
* corresponding information from the proxy type. An exception is made for
* {@code java.*} and {@code javax.*} packages which are often associated
* with the system classloader and a more privileged ProtectionDomain.
* corresponding information from the proxy type. An exception is made for
* {@code java.*} and {@code javax.*} packages which are often associated with
* the system classloader and a more privileged ProtectionDomain.
*
* @author David Allen
*
*/
public class SimpleProxyServices implements ProxyServices
{

/* (non-Javadoc)
* @see org.jboss.weld.serialization.spi.ProxyServices#getClassLoader(java.lang.Class)
*/
public ClassLoader getClassLoader(Class<?> type)
{
if (type.getName().startsWith("java"))
Expand All @@ -48,9 +49,6 @@ public ClassLoader getClassLoader(Class<?> type)
}
}

/* (non-Javadoc)
* @see org.jboss.weld.serialization.spi.ProxyServices#getProtectionDomain(java.lang.Class)
*/
public ProtectionDomain getProtectionDomain(Class<?> type)
{
if (type.getName().startsWith("java"))
Expand All @@ -63,13 +61,35 @@ public ProtectionDomain getProtectionDomain(Class<?> type)
}
}

/* (non-Javadoc)
* @see org.jboss.weld.bootstrap.api.Service#cleanup()
*/
public void cleanup()
{
// This implementation requires no cleanup

}

public Object wrapForSerialization(Object proxyObject)
{
// Simply use our own replacement object for proxies
return new SerializableProxy(proxyObject);
}

public Class<?> loadProxySuperClass(final String className)
{
try
{
return (Class<?>) AccessController.doPrivileged(new PrivilegedExceptionAction<Object>()
{
public Object run() throws Exception
{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return Class.forName(className, true, cl);
}
});
}
catch (PrivilegedActionException pae)
{
throw new WeldException(BeanMessage.CANNOT_LOAD_CLASS, className, pae.getException());
}
}

}
Expand Up @@ -132,6 +132,7 @@ public enum BeanMessage
@MessageId("000095") GENERIC_SESSION_BEAN_MUST_BE_DEPENDENT,
@MessageId("000096") PRODUCER_FIELD_ON_SESSION_BEAN_MUST_BE_STATIC,
@MessageId("000097") PRODUCER_METHOD_WITH_TYPE_VARIABLE_RETURN_TYPE_MUST_BE_DEPENDENT,
@MessageId("000098") PRODUCER_METHOD_WITH_WILDCARD_RETURN_TYPE_MUST_BE_DEPENDENT;
@MessageId("000098") PRODUCER_METHOD_WITH_WILDCARD_RETURN_TYPE_MUST_BE_DEPENDENT,
@MessageId("000099") CANNOT_LOAD_CLASS;

}
Expand Up @@ -97,3 +97,4 @@ GENERIC_SESSION_BEAN_MUST_BE_DEPENDENT=Session bean with generic class {0} must
PRODUCER_FIELD_ON_SESSION_BEAN_MUST_BE_STATIC=Producer fields on session beans must be static. Field {0} declared on {1}
PRODUCER_METHOD_WITH_TYPE_VARIABLE_RETURN_TYPE_MUST_BE_DEPENDENT=A producer method with a parameterized return type with a type variable must be declared @Dependent scoped. Method {0}
PRODUCER_METHOD_WITH_WILDCARD_RETURN_TYPE_MUST_BE_DEPENDENT=A producer method with a parameterized return type with a wildcard must be declared @Dependent scoped. Method {0}
CANNOT_LOAD_CLASS=Cannot load class {0} during deserialization of proxy

0 comments on commit 80269e6

Please sign in to comment.