Well, I have managed to make it work for simple cases in my JBoss 7 cluster with this (I want to do is to cast a generic rpc invocation on a jndi service):
@Singleton
@ApplicationScoped
@Startup
// @Startup Moved to resources/META-INF/ejb-jar.xml
@Listener
public class HAService {
...
@PostConstruct
public void postConstruct() {
// Get the jgroups channel from infinispan cacheManager
JGroupsTransport transport = (JGroupsTransport) oipCacheManager.getTransport();
channel = transport.getChannel();
// Mount a mux rpc dispatcher using such channel on this object
dispatcher = new MuxRpcDispatcher((short) 1, channel, null, null, this);
dispatcher.setMethodLookup(new MethodLookup() {
@Override
public Method findMethod(short id) {
try {
System.out.println("------>>> findmethod " + ServiceCall.class);
return HAService.this.getClass().getMethod("jndiDelegate", ServiceCall.class);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
});
// Start the dispatcher
dispatcher.start();
}
@PreDestroy
public void preDestroy() {
// Stop the dispatcher
dispatcher.stop();
}
public List callRemoteMethods(ServiceCall serviceCall) {
try {
RequestOptions opts = new RequestOptions(ResponseMode.GET_ALL, 5000);
log.debug("Sending rpc call to all nodes");
MethodCall methodCall = new MethodCall((short) 1);
methodCall.setArgs(serviceCall);
System.out.println("------ before callremote " + ServiceCall.class);
RspList response = dispatcher.callRemoteMethods(null, methodCall, opts);
System.out.println("------ after callremote " + ServiceCall.class);
log.debug(" responses -> {}", response);
return response.getResults();
} catch (Exception ex) {
ex.printStackTrace();
return new ArrayList(0);
}
}
public Object jndiDelegate(ServiceCall serviceCall) {
try {
Object service = JndiUtils.lookup(serviceCall.getJndiName());
Class[] parameterTypes = new Class[serviceCall.getArgs().length];
for (int i = 0; i < serviceCall.getArgs().length; i++) {
parameterTypes[i] = serviceCall.getArgs().getClass();
}
Method serviceMethod = service.getClass().getMethod(serviceCall.getMethod(), parameterTypes);
return serviceMethod.invoke(service, (Object[]) serviceCall.getArgs());
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
If the remote method has primitive arguments, no proble, everything works ok
But... when I try to serialize classes (this is the case as ServiceCall is a simple serializable wrapper) or invoke without the methodlookup I always receive:
09:06:25,161 DEBUG [oip.framework.core.ejb.ha.HAService] (EJB default - 5) Sending rpc call to all nodes
09:06:25,161 INFO [stdout] (EJB default - 5) ------ before callremote class oip.framework.core.ejb.ha.ServiceCall
09:06:25,165 INFO [stdout] (EJB default - 5) ------ after callremote class oip.framework.core.ejb.ha.ServiceCall
09:06:25,166 DEBUG [oip.framework.core.ejb.ha.HAService] (EJB default - 5) responses -> [sender=node2/oip, exception=java.lang.IllegalArgumentException: java.lang.ClassNotFoundException: oip.framework.core.ejb.ha.ServiceCall from [Module "org.jgroups:main" from local module loader @c97b99 (finder: local module finder @c985b7 (roots: O:\oip\jboss\modules,O:\oip\jboss\modules\system\layers\base))], received=true, suspected=false]
It seems the remote handler has some kind of issue with jboss classloaders and have no visibility to my module???
Note that on JBoss 7 (*if* you have JGroups 3.4.0.Alpha2 or later), you can simply reuse the channel exposed by Infinispan/JBossAS: [1]. You could then create the RpcDispatcher on top of the ForkChannel.
Same as the MuxRpcDispatcher, but you can also add protocols if you want to, dynamically.
It does not matter which way you follow, you will advertise that, in jboss, when you use the RpcDispatcher using such channel the classloader used to deserialize the content is the jgroups one, so you'll get ClassNotFoundException's, or even using MethodCall you won't be able to locate the method you want to call (in case of rpc)
The final solution is install a custom Marshaller in the dispatcher to allow specify the classloader we want to use in target node, as the guys of teiid do:
The marshaller's serialization and deserialization methods will need to resolve classes using AS7's ModuleLoader.
In Wildfly I've introduced a new CommandDispatcher to greatly simplify the process of performing cluster wide operations. It is similar to the RpcDispatcher in JGroups, but uses a command pattern rather than reflection, and plays nice with your deployment's classloader.