/*
* JBoss, the OpenSource EJB server
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.ejb.plugins.jrmp.interfaces;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Map;
import java.util.HashMap;
import java.security.Principal;
import java.security.MessageDigest;
import java.security.DigestOutputStream;
import javax.transaction.Transaction;
/**
* This Serializable object carries the method to invoke and an
* identifier for the target ojbect
*
* @author Rickard Öberg (rickard.oberg@telkel.com)
* @author Richard Monson-Haefel.
* @author Marc Fleury.
* @author Daniel O'Connor.
* @author Scott Stark/a>.
* @version $Revision: 1.13.4.1 $
*/
public final class RemoteMethodInvocation
implements java.io.Externalizable
{
// Constants -----------------------------------------------------
/** Serial Version Identifier. */
private static final long serialVersionUID = 6021873560918744612L;
// Static --------------------------------------------------------
static Map hashMap = new HashMap();
// Attributes ----------------------------------------------------
/** The method invocation target id */
Object id;
/** The method hash caclulated by getInterfaceHashes */
long hash;
/** The method invocation arguments */
Object[] args;
private Object tpc; // Transaction propagation context.
/** The calling thread identity */
private Principal identity;
/** The calling thread identity credentials */
private Object credential;
/** The map of methods set by the JRMPContainerInvoker */
transient Map methodMap;
/**
* Calculate method hashes. This algo is taken from RMI.
*
* @param intf
* @return
*/
public static Map getInterfaceHashes(Class intf)
{
// Create method hashes
Method[] methods = intf.getDeclaredMethods();
HashMap map = new HashMap();
for (int i = 0; i < methods.length; i++)
{
Method method = methods[i];
Class[] parameterTypes = method.getParameterTypes();
String methodDesc = method.getName()+"(";
for(int j = 0; j < parameterTypes.length; j++)
{
methodDesc += getTypeString(parameterTypes[j]);
}
methodDesc += ")"+getTypeString(method.getReturnType());
try
{
long hash = 0;
ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream(512);
MessageDigest messagedigest = MessageDigest.getInstance("SHA");
DataOutputStream dataoutputstream = new DataOutputStream(new DigestOutputStream(bytearrayoutputstream, messagedigest));
dataoutputstream.writeUTF(methodDesc);
dataoutputstream.flush();
byte abyte0[] = messagedigest.digest();
for(int j = 0; j < Math.min(8, abyte0.length); j++)
hash += (long)(abyte0[j] & 0xff) << j * 8;
map.put(method, new Long(hash));
} catch (Exception e)
{
e.printStackTrace();
}
}
return map;
}
static String getTypeString(Class cl)
{
if (cl == Byte.TYPE)
{
return "B";
} else if (cl == Character.TYPE)
{
return "C";
} else if (cl == Double.TYPE)
{
return "D";
} else if (cl == Float.TYPE)
{
return "F";
} else if (cl == Integer.TYPE)
{
return "I";
} else if (cl == Long.TYPE)
{
return "J";
} else if (cl == Short.TYPE)
{
return "S";
} else if (cl == Boolean.TYPE)
{
return "Z";
} else if (cl == Void.TYPE)
{
return "V";
} else if (cl.isArray())
{
return "["+getTypeString(cl.getComponentType());
} else
{
return "L"+cl.getName().replace('.','/')+";";
}
}
/*
* The use of hashCode is not enough to differenciate methods
* we override the hashCode
*
* The hashes are cached in a static for efficiency
*/
public static long calculateHash(Method method)
{
Class methodClass = method.getDeclaringClass();
Map methodHashes = (Map)hashMap.get(methodClass);
if (methodHashes == null)
{
methodHashes = getInterfaceHashes(methodClass);
hashMap.put(methodClass, methodHashes);
}
return ((Long)methodHashes.get(method)).longValue();
}
/** Remove the method declaring class method hash map. This is
called by the JRMPContainerInvoker in stop to release the referenced
classes.
*/
public static void clearHash(Method method)
{
Class methodClass = method.getDeclaringClass();
hashMap.remove(methodClass);
}
// Constructors --------------------------------------------------
public RemoteMethodInvocation()
{
// For externalization to work
}
public RemoteMethodInvocation(Method m, Object[] args)
{
this(null, m, args);
}
public RemoteMethodInvocation(Object id, Method m, Object[] args)
{
this.id = id;
this.args = args;
this.hash = calculateHash(m);
}
// Public --------------------------------------------------------
public Object getId() { return id; }
public Method getMethod()
{
Method m = (Method)methodMap.get(new Long(hash));
if (m == null)
throw new NullPointerException("METHOD IS NOT FOUND:"+hash+" "+methodMap);
return (Method)methodMap.get(new Long(hash));
}
public Object[] getArguments()
{
return args;
}
public void setMethodMap(Map methods)
{
methodMap = methods;
}
public void setTransactionPropagationContext(Object tpc)
{
this.tpc = tpc;
}
public Object getTransactionPropagationContext()
{
return tpc;
}
public void setPrincipal(Principal identity)
{
this.identity = identity;
}
public Principal getPrincipal()
{
return identity;
}
public Object getCredential()
{
return credential;
}
public void setCredential( Object credential )
{
this.credential = credential;
}
// Externalizable implementation ---------------------------------
public void writeExternal(java.io.ObjectOutput out)
throws IOException
{
out.writeObject(id);
out.writeLong(hash);
out.writeObject(args);
out.writeObject(tpc);
out.writeObject(identity);
out.writeObject(credential);
}
public void readExternal(java.io.ObjectInput in)
throws IOException, ClassNotFoundException
{
id = in.readObject();
hash = in.readLong();
args = (Object[])in.readObject();
tpc = in.readObject();
identity = (Principal)in.readObject();
credential = in.readObject();
}
}