null
, transactions are not propagated on
* optimized local method invocations.
*/
private static TransactionManager tm = null;
/**
* Factory for transaction propagation contexts.
*
* When set to a non-null value, it is used to get transaction
* propagation contexts for remote method invocations.
* If null
, transactions are not propagated on
* remote method invocations.
*/
protected static TransactionPropagationContextFactory tpcFactory = null;
/**
* This map maps JNDI names of containers to the remote interfaces
* of the container invokers of these containers.
*/
private static HashMap invokers = new HashMap(); // Prevent DGC
/**
* Return the remote interface of the container invoker for the
* container with the given JNDI name.
*/
private static ContainerRemote getLocal(String jndiName)
{
return (ContainerRemote)invokers.get(jndiName);
}
/**
* Add an invoker to the invokers map.
*/
public static void addLocal(String jndiName, ContainerRemote invoker)
{
invokers.put(jndiName, invoker);
}
/**
* Remove an invoker from the invokers map.
*/
public static void removeLocal(String jndiName)
{
invokers.remove(jndiName);
}
/**
* Set the transaction manager.
*/
public static void setTransactionManager(TransactionManager txMan)
{
tm = txMan;
}
/**
* Set the transaction propagation context factory.
*/
public static void setTPCFactory(TransactionPropagationContextFactory tpcf)
{
tpcFactory = tpcf;
}
// Constructors --------------------------------------------------
/**
* A public, no-args constructor for externalization to work.
*/
public GenericProxy()
{
// For externalization to work
}
/**
* Create a new GenericProxy.
*
* @param name
* The JNDI name of the container that we proxy for.
* @param container
* The remote interface of the container invoker of the
* container we proxy for.
* @param optimize
* If true, this proxy will attempt to optimize
* VM-local calls.
*/
protected GenericProxy(String name, ContainerRemote container,
boolean optimize)
{
this.name = name;
this.container = container;
this.optimize = optimize;
// get a context handle
this.initialContextHandle = InitialContextHandle.create();
}
// Public --------------------------------------------------------
/**
* Externalize this instance.
*
* If this instance lives in a different VM than its container
* invoker, the remote interface of the container invoker is
* not externalized.
*/
public void writeExternal(final ObjectOutput out)
throws IOException
{
out.writeUTF(name);
out.writeObject(isLocal() ? container : null);
out.writeLong(containerStartup);
out.writeBoolean(optimize);
out.writeObject(initialContextHandle);
}
/**
* Un-externalize this instance.
*
* If this instance is deserialized in the same VM as its container
* invoker, the remote interface of the container invoker is
* restored by looking up the name in the invokers map.
*/
public void readExternal(final ObjectInput in)
throws IOException, ClassNotFoundException
{
name = in.readUTF();
container = (ContainerRemote)in.readObject();
containerStartup = in.readLong();
optimize = in.readBoolean();
initialContextHandle = (InitialContextHandle)in.readObject();
if (isLocal())
{
// VM-local optimization; still follows RMI-semantics though
container = getLocal(name);
}
}
// Package protected ---------------------------------------------
/**
* Return the principal to use for invocations with this proxy.
*/
protected Principal getPrincipal()
{
return SecurityAssociation.getPrincipal();
}
/**
* Return the credentials to use for invocations with this proxy.
*/
protected Object getCredential()
{
return SecurityAssociation.getCredential();
}
/**
* Return the transaction associated with the current thread.
* Returns null
if the transaction manager was never
* set, or if no transaction is associated with the current thread.
*/
protected Transaction getTransaction()
throws SystemException
{
return (tm == null) ? null : tm.getTransaction();
}
/**
* Return the transaction propagation context of the transaction
* associated with the current thread.
* Returns null
if the transaction manager was never
* set, or if no transaction is associated with the current thread.
*/
protected Object getTransactionPropagationContext()
throws SystemException
{
return (tpcFactory == null) ? null : tpcFactory.getTransactionPropagationContext();
}
// Protected -----------------------------------------------------
/**
* The JNDI name of the container that we proxy for.
*/
protected String name;
/**
* The remote interface of the container invoker of the container
* we proxy for.
*/
protected ContainerRemote container;
/**
* If true, this proxy will attempt to optimize
* VM-local calls.
*/
protected boolean optimize; // = false;
/**
* Provides access to the correct naming context for handle objects.
*/
protected InitialContextHandle initialContextHandle;
/**
* Returns true
iff this instance lives in the same
* VM as its container.
*/
protected boolean isLocal()
{
return containerStartup == ContainerRemote.STARTUP;
}
/**
* Create an InitialContext using the saved environment or
* create a vanilla InitialContext when the enviroment
* is null.
*
* @return InitialContext suitable for the bean that this
* is a proxy for.
*
* @throws NamingException Failed to create InitialContext.
*/
protected InitialContext createInitialContext()
throws NamingException
{
return initialContextHandle.getInitialContext();
}
/**
* Invoke the container to handle this method invocation.
*
* If optimization is enabled and this is a local proxy, then the
* container is invoked directly, else a remote call is made.
*
* @param id ???
* @param method The method to invoke.
* @param args The arguments passed to the method.
*
* @throws Throwable Failed to invoke container.
*/
protected Object invokeContainer(final Object id,
final Method method,
final Object[] args)
throws Throwable
{
Object result;
// optimize if calling another bean in same EJB-application
if (optimize && isLocal()) {
result = container.invoke(id,
method,
args,
getTransaction(),
getPrincipal(),
getCredential());
}
else {
MarshalledObject mo = createMarshalledObject(id, method, args);
// Invoke on the remote server, enforce marshaling
if (isLocal()) {
// ensure marshaling of exceptions is done properly
try {
result = container.invoke(mo).get();
}
catch (Throwable e) {
throw (Throwable)new MarshalledObject(e).get();
}
}
else {
// Marshaling is done by RMI
result = container.invoke(mo).get();
}
}
return result;
}
/**
* Create a MarshalledObject suitable for
* invoking a remote container with.
*
* @param id ???
* @param method The method to invoke.
* @param args The arguments passed to the method.
* @return MarshalledObject suitable for invoking
* a remote container with.
*
* @throws SystemException Failed to get transaction.
* @throws IOException Failed to create MarshalledObject.
*/
protected MarshalledObject createMarshalledObject(final Object id,
final Method method,
final Object[] args)
throws SystemException, IOException
{
RemoteMethodInvocation rmi =
new RemoteMethodInvocation(id, method, args);
// Set the transaction propagation context
rmi.setTransactionPropagationContext(getTransactionPropagationContext());
// Set the security stuff
// MF fixme this will need to use "thread local" and therefore same construct as above
// rmi.setPrincipal(sm != null? sm.getPrincipal() : null);
// rmi.setCredential(sm != null? sm.getCredential() : null);
// is the credential thread local? (don't think so... but...)
rmi.setPrincipal(getPrincipal());
rmi.setCredential(getCredential());
return new MarshalledObject(rmi);
}
// Private -------------------------------------------------------
/**
* Time of ContainerRemote
class initialization as
* read when this instance was created. This time is used to
* determine if this instance lives in the same VM as the container.
*
* This is not completely fail-safe: If the ContainerRemote class
* is initialized in the server and at the client within the same
* millisecond, the proxy will think it is local, and the client
* will fail. This is, however, very unlikely to happen in real
* life, and next time the client is started it would run OK.
*/
private long containerStartup = ContainerRemote.STARTUP;
// Inner classes -------------------------------------------------
}