Rickard posted an example of how to do this for an RMI agent server on the rmi-users
mailing
list. I've included the cookbook recepie below. If you want to see the entire
discussion look
for 'Agents, RMI, Class loading' on the archives at:
http://archives.java.sun.com/archives/rmi-users.html
I believe is it illegal for an EJB to create a class loader, so the solution cannot be
directly
embeded into your EJB, but you can work around that or disable the security check.
It would be cool if there was a generic hook available in the EJB client side proxy
that would
all the client to specify a list of paths from which the EJB proxy could upload
classes for
this type of agent behavior.
--- Begin RMI mail list post excerpt:
Excerpt from Rickard's RMI cookbook:
When RMI unmarshals objects it uses three types of classloaders. It will
use the regular system classloader. If dynamic classloading is used,
then the internal URLClassLoaders that has been created for the various
codebases will be used. But, what we're interested in is the last
option: the context classloader. This classloader was introduced in JDK
1.2, and is associated with the current thread. This is good to have for
server/component oriented systems where the different components must
have their own classloading namespace. Common examples are EJB
containers (see jboss.org for example) and servlet engines. Another
common example is agent containers. The thread context classloader
(TCCL) can be set by calling
Thread.currentThread().setContextClassLoader(cl), and is retrieved by
calling Thread.currentThread().getContextClassLoader().
Now, in a regular call you will not have any way to set the TCCL before
RMI starts to unmarshal the incoming call parameters. What we want is to
say "for this RMI call you should use this TCCL". So how do we do this?
MarshalledObject to the rescue. java.rmi.MarshalledObject can be used to
marshal serializable objects to an object, which can then be sent around
pretty much as a byte array. When the original object is desired one
simply calls get() on it, and the object is deserialized. The key point
here is that the unmarshaling uses the same methods as regular RMI
marshaling. This means, for example, that it will use the TCCL if
possible.
Given this, how do we implement your scenario?
For example:
Your agent server has the following interface:
public interface AgentHost
extends Remote
{
public void addAgent(MarshalledObject agentState, byte[] agentJar)
throws IOException;
}
The "agentState" is simply the agent in its MO form, which is created by
doing:
MarshalledObject agentState = new MarshalledObject(agent);
The "agentJar" is the bytes for the JAR file where the classes for the
agent is located. By putting the classes in a JAR file you ensure that
you are not limited to only one class for the agent. "agentJar" is
created by simply reading the bytes from the JAR file with the agent
classes into the "classes" array.
So, now your client has both the agent and its classes. It is time to
upload it, so we call the server through RMI. On the server the
following happens:
public void addAgent(MarshalledObject agentState, byte[] agentJar)
throws IOException
{
// Store classes on local drive
File jarFile = File.createTempFile("agent",".jar");
OutputStream out = new FileOutputStream(jarFile);
out.write(agentJar);
out.close();
// Create classloader to local JAR
URLClassLoader agentLoader = new URLClassLoader(new URL[] {
jarFile.toURL() });
// Set TCCL
Thread.currentThread().setContextClassLoader(agentLoader);
// Get agent
// This will use classes from agentLoader since it was set as TCCL
Agent agent = (Agent)agentState.get();
// Run agent
agentEngine.activate(agent);
// Activate the agent somehow, possibly by spawning a thread and
calling a known method
// Agent successfully retrieved and activated. We're done.
}
I haven't tested the above so there may be minor glitches, but the gist
of it should work just fine. This is preciely how the JBoss EJB
container works BTW, so the general pattern ("MarshalledObject
tunneling") is well tested.
Good luck,
Rickard