/*
* JBoss, Home of Professional Open Source.
* Copyright 2006, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.aop.proxy.container;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.jboss.aop.Advisor;
import org.jboss.aop.AspectManager;
import org.jboss.aop.InstanceAdvisor;
import org.jboss.aop.MethodInfo;
import org.jboss.aop.advice.AbstractAdvice;
import org.jboss.aop.advice.Interceptor;
import org.jboss.aop.advice.PerInstanceAdvice;
import org.jboss.aop.advice.PerInstanceInterceptor;
import org.jboss.aop.advice.PerJoinpointAdvice;
import org.jboss.aop.advice.PerJoinpointInterceptor;
import org.jboss.aop.instrument.Untransformable;
import org.jboss.aop.metadata.SimpleMetaData;
import org.jboss.aop.util.MethodHashing;
import org.jboss.util.id.GUID;
/**
*
* @author <a href="kabir.khan@jboss.com">Kabir Khan</a>
* @version $Revision: 1.1 $
*/
public class MarshalledContainerProxy implements Serializable
{
private static final long serialVersionUID = 1L;
//Fields to check if we are unmarshalling in the same JVM
public final static GUID GUID = new GUID();
private transient AspectManaged proxyInstance;
//Fields from the proxy, used when unmarshalling in the same JVM
private String proxyClassName;
private ContainerProxyCacheKey key;
private Object mixins[];
private Object delegate;
private Class<?> clazz;
private SimpleMetaData metadata;
//Interfaces resulting from a mixin or interface introduction
private String[] introducedInterfaces;
//Interfaces implemented by mixins that are already implemented by the target class
private Set<String> targetInterfaces = new HashSet<String>();
//The interceptor chains for each method used when unmarshalling in a different JVM
MarshalledInterceptors marshalledInterceptors;
//The instanceAdvisor domain name used when unmarshalling in a different JVM
private String instanceAdvisorDomainName;
public MarshalledContainerProxy(AspectManaged proxyInstance, ContainerProxyCacheKey key, Object[] mixins, Object delegate, Advisor currentAdvisor, SimpleMetaData metadata)
{
this.proxyInstance = proxyInstance;
Class<?> proxyClass = proxyInstance.getClass();
this.proxyClassName = proxyClass.getName();
this.key = key;
this.mixins = mixins;
this.delegate = delegate;
this.clazz = currentAdvisor.getClazz();
checkInstanceAdvisor(currentAdvisor);
this.metadata = metadata;
marshalledInterceptors = new MarshalledInterceptors(currentAdvisor, mixins);
Class<?>[] proxyInterfaces = proxyClass.getInterfaces();
ArrayList<String> ifs = new ArrayList<String>();
for (int i = 0 ; i < proxyInterfaces.length ; i++)
{
String name = proxyInterfaces[i].getName();
if (name.equals(Untransformable.class.getName()) ||
name.equals(Delegate.class.getName()) ||
name.equals(AspectManaged.class.getName()))
{
continue;
}
if (proxyInterfaces[i].isAssignableFrom(clazz))
{
targetInterfaces.add(clazz.getName());
continue;
}
ifs.add(proxyInterfaces[i].getName());
}
introducedInterfaces = ifs.toArray(new String[ifs.size()]);
}
private void checkInstanceAdvisor(Advisor advisor)
{
if (advisor instanceof InstanceProxyContainer)
{
AspectManager manager = advisor.getManager();
instanceAdvisorDomainName = manager.getManagerFQN();
}
}
public Object readResolve() throws ObjectStreamException
{
try
{
if (isLocal())
{
return localReadResolve();
}
return remoteReadResolve();
}
catch (Exception e)
{
InvalidObjectException ex = new InvalidObjectException(e.getMessage());
ex.setStackTrace(e.getStackTrace());
ex.initCause(e);
throw ex;
}
}
private Object localReadResolve() throws Exception
{
ClassLoader tcl = SecurityActions.getContextClassLoader();
Class<?> proxyClass = tcl.loadClass(proxyClassName);
Object proxy = proxyClass.newInstance();
Delegate delegate = (Delegate)proxy;
delegate.localUnmarshal(this);
return proxy;
}
private Object remoteReadResolve() throws Exception
{
if (marshalledInterceptors.getException() != null)
{
throw new Exception("Could not read object, an error happened when writing it on the server", marshalledInterceptors.getException());
}
MarshalledProxyAdvisor advisor = marshalledInterceptors.getMarshalledAdvisor();
advisor.setClazz(clazz);
boolean objectAsSuper = key.getClazz().equals(Object.class);
Class<?> proxyClass = ContainerProxyFactory.getProxyClass(objectAsSuper, key, advisor, this);
Delegate proxy = (Delegate)proxyClass.newInstance();
proxy.remoteUnmarshal(this, advisor);
return proxy;
}
private boolean isLocal()
{
return key.getGuid().equals(GUID);
}
public ContainerProxyCacheKey getKey()
{
return key;
}
public Object[] getMixins()
{
return mixins;
}
public Object getDelegate()
{
return delegate;
}
public Class<?> getClazz()
{
return clazz;
}
public SimpleMetaData getMetadata()
{
return metadata;
}
public String getInstanceAdvisorDomainName()
{
return instanceAdvisorDomainName;
}
protected String[] getIntroducedInterfaces()
{
return introducedInterfaces;
}
protected Set<String> getTargetInterfaces()
{
return targetInterfaces;
}
private class MarshalledInterceptors implements Serializable
{
private static final long serialVersionUID = 1L;
transient Advisor currentAdvisor;
Object[] mixins;
private Exception exception;
public MarshalledInterceptors(Advisor currentAdvisor, Object[] mixins)
{
this.currentAdvisor = currentAdvisor;
this.mixins = mixins;
}
public Exception getException()
{
return exception;
}
public MarshalledProxyAdvisor getMarshalledAdvisor()
{
return (MarshalledProxyAdvisor)currentAdvisor;
}
private void writeObject(ObjectOutputStream out) throws IOException
{
ObjectOutputStream test = new ObjectOutputStream(new ByteArrayOutputStream());
try
{
MethodInfo[] methodInfos = getMethodInfos();
MarshalledMethodInfo[] marshalledInfos = new MarshalledMethodInfo[methodInfos.length];
for (int i = 0 ; i < methodInfos.length ; i++)
{
MarshalledMethodInfo info = new MarshalledMethodInfo(MarshalledContainerProxy.this, methodInfos[i]);
marshalledInfos[i] = info;
try
{
test.writeObject(info);
continue;
}
catch (Exception e)
{
IOException ex = new IOException("An error happened serializing the info for " + getExceptionExpression(methodInfos[i]));
ex.initCause(e);
out.writeObject(ex);
return;
}
}
out.writeObject(marshalledInfos);
}
finally
{
try
{
test.close();
}
catch (Exception e)
{
}
}
}
private MethodInfo[] getMethodInfos()
{
if (currentAdvisor instanceof MarshalledProxyAdvisor)
{
return ((MarshalledProxyAdvisor)currentAdvisor).getMethodInfos();
}
return ((ClassProxyContainer)currentAdvisor).getMethodInfos();
}
private String getExceptionExpression(MethodInfo info)
{
Method m = info.getMethod();
StringBuilder expr = new StringBuilder();
if (m == null)
{
expr.append("a method");
}
else
{
expr.append(m.getDeclaringClass().getName());
expr.append(".");
expr.append(m.getName());
expr.append("(");
boolean first = true;
for (Class<?> c : m.getParameterTypes())
{
if (first)
{
first = false;
}
else
{
expr.append(", ");
}
expr.append(c.getName());
}
expr.append(")");
}
return expr.toString();
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
{
Object o = in.readObject();
if (o instanceof IOException)
{
exception = (IOException)o;
return;
}
MarshalledProxyAdvisor advisor = new MarshalledProxyAdvisor("Unmarshalled", AspectManager.getTopLevelAspectManager());
MarshalledMethodInfo[] marshalledInfos = (MarshalledMethodInfo[])o;
MethodInfo[] methodInfos = new MethodInfo[marshalledInfos.length];
for (int i = 0 ; i < marshalledInfos.length ; i++)
{
methodInfos[i] = marshalledInfos[i].getMethodInfo(advisor);
advisor.addMethodInfo(methodInfos[i]);
}
currentAdvisor = advisor;
}
}
private static class MarshalledMethodInfo implements Serializable
{
private static final long serialVersionUID = 1L;
transient MarshalledContainerProxy proxy;
long advisedHash;
long unadvisedHash;
Interceptor[] interceptors;
Class<?> clazz;
transient boolean requiresInstanceAdvisor;
public MarshalledMethodInfo(MarshalledContainerProxy proxy, MethodInfo info) throws IOException
{
this.proxy = proxy;
try
{
this.advisedHash = MethodHashing.methodHash(info.getMethod());
this.unadvisedHash = MethodHashing.methodHash(info.getUnadvisedMethod());
}
catch (Exception e)
{
throw new MethodHashingException(e);
}
clazz = info.getMethod().getDeclaringClass();
populateInterceptors(info);
}
public boolean getRequiresInstanceAdvisor()
{
return requiresInstanceAdvisor;
}
private void populateInterceptors(MethodInfo info)
{
Interceptor[] icptrs = info.getInterceptors();
if (icptrs != null)
{
ArrayList<Interceptor> allIcptrs = new ArrayList<Interceptor>(icptrs.length);
for (int i = 0 ; i < icptrs.length ; i++)
{
if (icptrs[i] instanceof AbstractAdvice == false)
{
Interceptor icptr = null;
if (icptrs[i] instanceof PerInstanceInterceptor && !Modifier.isStatic(info.getMethod().getModifiers()))
{
requiresInstanceAdvisor = true;
InstanceAdvisor ia = getProxyInstanceAdvisor();
icptr = ((PerInstanceInterceptor)icptrs[i]).getAspectInstance(ia);
}
else if (icptrs[i] instanceof PerJoinpointInterceptor && !Modifier.isStatic(info.getMethod().getModifiers()))
{
requiresInstanceAdvisor = true;
InstanceAdvisor ia = getProxyInstanceAdvisor();
icptr = ((PerJoinpointInterceptor)icptrs[i]).getAspectInstance(ia);
}
else
{
icptr = icptrs[i];
}
allIcptrs.add(icptr);
}
else
{
AbstractAdvice advice = (AbstractAdvice)icptrs[i];
Object aspectInstance = null;
if (icptrs[i] instanceof PerInstanceAdvice && !Modifier.isStatic(info.getMethod().getModifiers()))
{
requiresInstanceAdvisor = true;
InstanceAdvisor ia = getProxyInstanceAdvisor();
aspectInstance = ((PerInstanceAdvice)advice).getAspectInstance(ia);
}
else if (icptrs[i] instanceof PerJoinpointAdvice && !Modifier.isStatic(info.getMethod().getModifiers()))
{
requiresInstanceAdvisor = true;
InstanceAdvisor ia = getProxyInstanceAdvisor();
aspectInstance = ((PerJoinpointAdvice)advice).getAspectInstance(ia);
}
else
{
aspectInstance = advice.getAspectInstance();
}
if (aspectInstance != null)
{
MarshalledAdvice ma = new MarshalledAdvice(aspectInstance, icptrs[i].getName(), advice.getAdviceName());
allIcptrs.add(ma);
}
}
}
interceptors = allIcptrs.toArray(new Interceptor[allIcptrs.size()]);
}
}
private InstanceAdvisor getProxyInstanceAdvisor()
{
InstanceAdvisor ia = proxy.proxyInstance.getInstanceAdvisor();
proxy.checkInstanceAdvisor((InstanceProxyContainer)ia);
return ia;
}
public MethodInfo getMethodInfo(Advisor advisor)
{
MethodInfo info = new MethodInfo(clazz, advisedHash, unadvisedHash, advisor);
info.setInterceptors(interceptors);
return info;
}
public String toString()
{
return advisedHash + " " + ((interceptors == null) ? "null" : Arrays.asList(interceptors));
}
}
private static class MethodHashingException extends IOException
{
private static final long serialVersionUID = 1L;
MethodHashingException(Exception e)
{
super("Error hashing method");
super.initCause(e);
}
}
}