Pages

Tuesday, November 11, 2008

RMI Tutorial : RMI and tomcat

Using RMI in java is very very easy, but, people like me will still get stuck using it, when trying to integrate with Tomcat. And, the help is very less outside in the internet.

There are two things to remember:
1. Provide sufficient access priviledges to the classes and jars using the RMI. This is done using catalina.policy present in $CATALINA_HOME/conf. Below is the addition to the policy files:

grant codeBase "file:${catalina.home}/webapps/MyAPP/WEB-INF/classes/-" { 
permission java.security.AllPermission "", "";
};

grant codeBase "file:${catalina.home}/webapps/MyAPP/WEB-INF/lib/-" {
permission java.security.AllPermission "", "";
};
grant codeBase "file:${catalina.home}/webapps/MyAPP/WEB-INF/lib/some-common-3.0.jar" {
permission java.io.FilePermission "*", "read, write";
};

2. Do not copy the entire code that is avalable on the internet for the server and client. The main() method given at lot of places in the web should not be copied as it is. Specifically, comment out the below snippet on both client and server:

//        if (System.getSecurityManager() == null) { 
// System.setSecurityManager(new RMISecurityManager());
// }

We do not want to install a new security manager. Tomcat provides the securitymanager, and lets use the same. Please do use the same if you want the RMI to work.
Tutorial:

Server side:

Step 1: Create an contract, an interface that extends java.rmi.Remote.
public interface IRemoteService extends Remote{ 

public final String serviceName = "MyRemoteService";
public abstract void startDoing() throws RemoteException;

public abstract void stopDoing() throws RemoteException;
}

Step 2: Write an implementation for the Interface.
public class RemoteServiceImpl implements IRemoteService { 
public RemoteServiceImpl(){
super();
}
public void startDoing() throws RemoteException {
return new MyTask().do();
}
public void stopDoing() throws RemoteException {
return new MyTask().dont();
}
}

Step 3: Either use a startup servlet or any class that is called after the server is up and running and before the remote service is invoked, and initialize the registry. In the blow snippet case, just create a new object some where before service will be invoked. Note in below code, there is no securitymanager present anywhere.
public class InitRemoteService { 
public static boolean isRegistered = false;
public static IRemoteService service;
public InitRemoteService(){
if(!isRegistered){
try {
service = new RemoteServiceImpl();
IRemoteService stub =
(IRemoteService) UnicastRemoteObject.exportObject(service, 0);
Registry registry = LocateRegistry.createRegistry(9345);
registry.rebind(IRemoteService.serviceName, stub);
System.out.println("Remote service bound");
isRegistered = true;
} catch (Exception e) {
System.err.println("Remote service exception:");
e.printStackTrace();
}
}
}
}

Client Side:

Step 1: Write the client as given below in the snippet. You can note, there is no security manager related code either here. It also, lists all the service names in the registry.
try { 
Registry registry = LocateRegistry.getRegistry(HOST,9345);
String[] names = registry.list();
for(String name1 : names){
System.out.println("~~~~" + name1 + "~~~~");
}
IDPRemoteService serv = (IDPRemoteService) registry.lookup(IDPRemoteService.serviceName);
System.out.println(serv.startDoing());
} catch (Exception e) {
System.err.println("Remoteservice exception:");
e.printStackTrace();
}

Enjoy, using RMI for remote object invocations, esp on servers like Tomcat that does not support EJB's or when EJB level of advanced concept is not required.

8 comments:

  1. Anonymous12:51 AM

    Hi,

    I am using Tomcat for deploying my RMI objects. When I freshly start the Tomcat, the client is able to invoke the RMI object.

    But when I reload / redeploy the application, the client throws the following exception :

    Exception in thread "main" java.lang.IllegalThreadStateException
    at java.lang.ThreadGroup.add(ThreadGroup.java:856)
    at java.lang.Thread.start(Thread.java:573)
    at java.lang.Shutdown.runHooks(Shutdown.java:128)
    at java.lang.Shutdown.sequence(Shutdown.java:173)
    at java.lang.Shutdown.exit(Shutdown.java:218)
    at java.lang.Runtime.exit(Runtime.java:90)
    at org.springframework.beans.factory.support.AbstractAutowir
    ...
    ...
    Caused by: org.springframework.remoting.RemoteLookupFailureException: Lookup of RMI stub failed; nested exception is java.rmi.UnmarshalException: error unmarshalling return; nested exception is:
    java.io.EOFException
    at org.springframework.remoting.rmi.RmiClientInterceptor.lookupStub(RmiClientInterceptor.java:214)
    at org.springframework.remoting.rmi.RmiClientInterceptor.prepare(RmiClientInterceptor.java:146)
    ...
    ...
    Caused by: java.rmi.UnmarshalException: error unmarshalling return; nested exception is:
    java.io.EOFException
    at sun.rmi.registry.RegistryImpl_Stub.lookup(Unknown Source)
    at java.rmi.Naming.lookup(Naming.java:84)
    ...
    ...
    Caused by: java.io.EOFException
    at java.io.ObjectInputStream$BlockDataInputStream.peekByte(ObjectInputStream.java:2502)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1267)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:339)
    at sun.rmi.server.MarshalInputStream.readLocation(MarshalInputStream.java:285)

    Please advice.

    Thanks
    Niles

    ReplyDelete
  2. Hi Niles,

    I would like you to try a few things given below. Please let me know if it worked.

    1. Turn off automatic webapp reloading for the application: in Tomcat's server.xml, find the <Context> declaration, and ensure it is set to: reloadable="false" [http://forum.springframework.org/showthread.php?t=38656]

    2. From : http://forum.springframework.org/archive/index.php/t-33073.html

    Let spring start the RMIRegistry throu RmiRegistryFactoryBean, which shuts it down correctly.

    So, the Service-Export looks like this:

    <bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactor yBean">
    <property name="port" value="1199"/>
    </bean>

    <bean class="org.springframework.remoting.rmi.RmiServiceExporte r">
    <property name="serviceName" value="ProductService" />
    <property name="service" ref="productService" />
    <property name="serviceInterface"
    value="com.myshop.ProductService" />
    <property name="registry" ref="registry"/>
    </bean>

    Thanks,
    Shreyas

    ReplyDelete
  3. Anonymous7:34 PM

    Good information. Thanks.

    ReplyDelete
  4. Anonymous3:40 PM

    Thanks. This was helpful.

    ReplyDelete
  5. Anonymous3:15 PM

    On the client side what is the IDPRemoteService?

    ReplyDelete
  6. Good question :) It should be IRemoteService. typo.

    ReplyDelete
  7. Anonymous1:30 PM

    Vielen Dank!

    ReplyDelete