Spring Remoting Support and Developing RMI Service
Remote Method Invocation (RMI) : Spring supports RMI via RmiProxyFactoryBean and RmiServiceExporter. RmiServiceExporter exports any Spring-managed bean as an RMI service and registers. RmiProxyFactoryBean is a factory bean creating a proxy for an RMI service. This proxy object talks with remote RMI services on behalf of the client.
Spring’s HTTP invoker : Spring HTTP invokers use the standard Java serialization mechanism to expose services through HTTP. Spring supports HTTP invoker infrastructure via HttpInvokerProxyFactoryBean and HttpInvokerServiceExporter. HttpInvokerServiceExporter that exports the specified service bean as HTTP invoker service endpoint, accessible via an HTTP invoker proxy. HttpInvokerProxyFactoryBean is a factory bean for HTTP invoker proxies.
Hessian : Hessian offers a binary HTTP-based remoting protocol. Spring supports Hessian via HessianProxyFactoryBean and the HessianServiceExporter.
Burlap : Burlap is Caucho’s XML-based alternative to Hessian. Spring provides support classes such as BurlapProxyFactoryBean and BurlapServiceExporter.
JAX-RPC : Spring provides remoting support for web services via JAX-RPC (J2EE 1.4?s web service API).
JAX-WS : Spring provides remoting support for web services via JAX-WS (the successor of JAX-RPC, as introduced in Java EE 5 and Java 6).
JMS : The JMS remoting support in the Spring is provided by the JmsInvokerServiceExporter and JmsInvokerProxyFactoryBean classes.
Let us look at Spring RMI support to develop Spring RMI Service & Client.
Used Technologies :
JDK 1.6.0_31
Spring 3.1.1
Maven 3.0.2
STEP 1 : CREATE MAVEN PROJECT
A maven project is created as below. (It can be created by using Maven or IDE Plug-in).
STEP 2 : LIBRARIES
Spring dependencies are added to Maven’ s pom.xml.
<!-- Spring 3.1.x dependencies --> <properties> <spring.version>3.1.1.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependencies>
STEP 3 : CREATE USER CLASS
A new User Class is created.
package com.otv.user; import java.io.Serializable; /** * User Bean * * @author onlinetechvision.com * @since 24 Feb 2012 * @version 1.0.0 * */ public class User implements Serializable { private long id; private String name; private String surname; /** * Get User Id * * @return long id */ public long getId() { return id; } /** * Set User Id * * @param long id */ public void setId(long id) { this.id = id; } /** * Get User Name * * @return long id */ public String getName() { return name; } /** * Set User Name * * @param String name */ public void setName(String name) { this.name = name; } /** * Get User Surname * * @return long id */ public String getSurname() { return surname; } /** * Set User Surname * * @param String surname */ public void setSurname(String surname) { this.surname = surname; } @Override public String toString() { StringBuilder strBuilder = new StringBuilder(); strBuilder.append("Id : ").append(getId()); strBuilder.append(", Name : ").append(getName()); strBuilder.append(", Surname : ").append(getSurname()); return strBuilder.toString(); } }
STEP 4 : CREATE ICacheService INTERFACE
ICacheService Interface representing a remote cache service interface is created.
package com.otv.cache.service; import java.util.concurrent.ConcurrentHashMap; import com.otv.user.User; /** * Cache Service Interface * * @author onlinetechvision.com * @since 27 Feb 2012 * @version 1.0.0 * */ public interface ICacheService { /** * Get User Map * * @return ConcurrentHashMap User Map */ public ConcurrentHashMap<long, user> getUserMap(); }
STEP 5 : CREATE CacheService CLASS
CacheService Class is created by implementing ISchedulerService Interface. It provides access to the remote cache…
package com.otv.cache.service; import java.util.concurrent.ConcurrentHashMap; import com.otv.user.User; /** * Cache Service Implementation * * @author onlinetechvision.com * @since 6:04:49 PM * @version 1.0.0 * */ public class CacheService implements ICacheService { //User Map is injected... ConcurrentHashMap<long, user> userMap; /** * Get User Map * * @return ConcurrentHashMap User Map */ public ConcurrentHashMap<long, user> getUserMap() { return userMap; } /** * Set User Map * * @param ConcurrentHashMap User Map */ public void setUserMap(ConcurrentHashMap<long, user> userMap) { this.userMap = userMap; } }
STEP 6 : CREATE IRMIUserService INTERFACE
IRMIUserService Interface representing RMI service interface is created. Also, it provides remote methods for the RMI Clients…
package com.otv.rmi.server; import java.util.List; import com.otv.user.User; /** * RMI User Service Interface * * @author onlinetechvision.com * @since 24 Feb 2012 * @version 1.0.0 * */ public interface IRMIUserService { /** * Add User * * @param User user * @return boolean response of the method */ public boolean addUser(User user); /** * Delete User * * @param User user * @return boolean response of the method */ public boolean deleteUser(User user); /** * Get User List * * @return List user list */ public List<User> getUserList(); }
STEP 7 : CREATE RMIUserService CLASS
RMIUserService Class(a.k.a RMI Object) is created by implementing IRMIUserService Interface.
package com.otv.rmi.server; import java.util.ArrayList; import java.util.List; import org.apache.log4j.Logger; import com.otv.cache.service.ICacheService; import com.otv.user.User; /** * RMI User Service Implementation * * @author onlinetechvision.com * @since 24 Feb 2012 * @version 1.0.0 * */ public class RMIUserService implements IRMIUserService { private static Logger logger = Logger.getLogger(RMIUserService.class); //Remote Cache Service is injected... ICacheService cacheService; /** * Add User * * @param User user * @return boolean response of the method */ public boolean addUser(User user) { getCacheService().getUserMap().put(user.getId(), user); logger.debug("User has been added to cache. User : "+getCacheService().getUserMap().get(user.getId())); return true; } /** * Delete User * * @param User user * @return boolean response of the method */ public boolean deleteUser(User user) { getCacheService().getUserMap().put(user.getId(), user); logger.debug("User has been deleted from cache. User : "+user); return true; } /** * Get User List * * @return List user list */ public List<User> getUserList() { List<User> list = new ArrayList<User>(); list.addAll(getCacheService().getUserMap().values()); logger.debug("User List : "+list); return list; } /** * Get RMI User Service * * @return IRMIUserService RMI User Service */ public ICacheService getCacheService() { return cacheService; } /** * Set RMI User Service * * @param IRMIUserService RMI User Service */ public void setCacheService(ICacheService cacheService) { this.cacheService = cacheService; } }
STEP 8 : CREATE RMIServerStarter CLASS
RMI Server Starter Class is created. It starts the RMI Server.
package com.otv.rmi.server.starter; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * RMI Server Starter * * @author onlinetechvision.com * @since 27 Feb 2012 * @version 1.0.0 * */ public class RMIServerStarter { public static void main(String[] args) { //RMI Server Application Context is started... new ClassPathXmlApplicationContext("rmiServerAppContext.xml"); } }
STEP 9 : CREATE rmiServerAppContext.xml
RMI Server Application Context’ s content is shown as below.
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <!-- Beans Declaration --> <bean id="UserMap" class="java.util.concurrent.ConcurrentHashMap" /> <bean id="CacheService" class="com.otv.cache.service.CacheService"> <property name="userMap" ref="UserMap"/> </bean> <bean id="RMIUserService" class="com.otv.rmi.server.RMIUserService" > <property name="cacheService" ref="CacheService"/> </bean> <!-- RMI Server Declaration --> <bean class="org.springframework.remoting.rmi.RmiServiceExporter"> <!-- serviceName represents RMI Service Name --> <property name="serviceName" value="RMIUserService"/> <!-- service represents RMI Object(RMI Service Impl) --> <property name="service" ref="RMIUserService"/> <!-- serviceInterface represents RMI Service Interface exposed --> <property name="serviceInterface" value="com.otv.rmi.server.IRMIUserService"/> <!-- defaults to 1099 --> <property name="registryPort" value="1099"/> </bean> </beans>
STEP 10 : CREATE RMIServiceClient CLASS
RMIServiceClient Class is created. It calls the RMI User Service and performs user operations.
package com.otv.rmi.client; import org.apache.log4j.Logger; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.otv.rmi.server.IRMIUserService; import com.otv.rmi.server.RMIUserService; import com.otv.user.User; /** * RMI Service Client * * @author onlinetechvision.com * @since 24 Feb 2012 * @version 1.0.0 * */ public class RMIServiceClient { private static Logger logger = Logger.getLogger(RMIUserService.class); /** * Main method of the RMI Service Client * */ public static void main(String[] args) { logger.debug("RMI Service Client is starting..."); //RMI Client Application Context is started... ApplicationContext context = new ClassPathXmlApplicationContext("rmiClientAppContext.xml"); //Remote User Service is called via RMI Client Application Context... IRMIUserService rmiClient = (IRMIUserService) context.getBean("RMIUserService"); //New User is created... User user = new User(); user.setId(1); user.setName("Bruce"); user.setSurname("Willis"); //The user is added to the remote cache... rmiClient.addUser(user); //The users are gotten via remote cache... rmiClient.getUserList(); //The user is deleted from remote cache... rmiClient.deleteUser(user); logger.debug("RMI Service Client is stopped..."); } }
STEP 11 : CREATE rmiClientAppContext.xml
RMI Client Application Context’ s content is shown as below.
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <!-- RMI Client Declaration --> <bean id="RMIUserService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean"> <!-- serviceUrl represents RMI Service Url called--> <property name="serviceUrl" value="rmi://x.x.x.x:1099/RMIUserService"/> <!-- serviceInterface represents RMI Service Interface called --> <property name="serviceInterface" value="com.otv.rmi.server.IRMIUserService"/> <!-- refreshStubOnConnectFailure enforces automatic re-lookup of the stub if a call fails with a connect exception --> <property name="refreshStubOnConnectFailure" value="true"/> </bean> </beans>
STEP 12 : RUN PROJECT
If RMI Service Client is started when RMI Server runs, below RMI Server output logs will be shown. Also, RMI Server and Client can be run by opening two seperate consoles via your IDE. :
.... 04.03.2012 14:23:15 DEBUG (RmiBasedExporter.java:59) - RMI service [com.otv.rmi.server.RMIUserService@16dadf9] is an RMI invoker 04.03.2012 14:23:15 DEBUG (JdkDynamicAopProxy.java:113) - Creating JDK dynamic proxy: target source is SingletonTargetSource for target object [com.otv.rmi.server.RMIUserService@16dadf9] 04.03.2012 14:23:15 INFO (RmiServiceExporter.java:276) - Binding service 'RMIUserService' to RMI registry: RegistryImpl[UnicastServerRef [liveRef: [endpoint:[192.168.1.7:1099](local),objID:[0:0:0, 0]]]] 04.03.2012 14:23:15 DEBUG (AbstractAutowireCapableBeanFactory.java:458) - Finished creating instance of bean 'org.springframework.remoting.rmi.RmiServiceExporter#0' 04.03.2012 14:23:15 DEBUG (AbstractApplicationContext.java:845) - Unable to locate LifecycleProcessor with name 'lifecycleProcessor': using default [org.springframework.context.support.DefaultLifecycleProcessor@3c0007] 04.03.2012 14:23:15 DEBUG (AbstractBeanFactory.java:245) - Returning cached instance of singleton bean 'lifecycleProcessor' 04.03.2012 14:25:43 DEBUG (RemoteInvocationTraceInterceptor.java:73) - Incoming RmiServiceExporter remote call: com.otv.rmi.server.IRMIUserService.addUser 04.03.2012 14:25:43 DEBUG (RMIUserService.java:33) - User has been added to cache. User : Id : 1, Name : Bruce, Surname : Willis 04.03.2012 14:25:43 DEBUG (RemoteInvocationTraceInterceptor.java:79) - Finished processing of RmiServiceExporter remote call: com.otv.rmi.server.IRMIUserService.addUser 04.03.2012 14:25:43 DEBUG (RemoteInvocationTraceInterceptor.java:73) - Incoming RmiServiceExporter remote call: com.otv.rmi.server.IRMIUserService.getUserList 04.03.2012 14:25:43 DEBUG (RMIUserService.java:57) - User List : [Id : 1, Name : Bruce, Surname : Willis] 04.03.2012 14:25:43 DEBUG (RemoteInvocationTraceInterceptor.java:79) - Finished processing of RmiServiceExporter remote call: com.otv.rmi.server.IRMIUserService.getUserList 04.03.2012 14:25:43 DEBUG (RemoteInvocationTraceInterceptor.java:73) - Incoming RmiServiceExporter remote call: com.otv.rmi.server.IRMIUserService.deleteUser 04.03.2012 14:25:43 DEBUG (RMIUserService.java:45) - User has been deleted from cache. User : Id : 1, Name : Bruce, Surname : Willis 04.03.2012 14:25:43 DEBUG (RemoteInvocationTraceInterceptor.java:79) - Finished processing of RmiServiceExporter remote call: com.otv.rmi.server.IRMIUserService.deleteUser 04.03.2012 14:25:43 DEBUG (RemoteInvocationTraceInterceptor.java:73) - Incoming RmiServiceExporter remote call: com.otv.rmi.server.IRMIUserService.getUserList 04.03.2012 14:25:43 DEBUG (RMIUserService.java:57) - User List : [] 04.03.2012 14:25:43 DEBUG (RemoteInvocationTraceInterceptor.java:79) - Finished processing of RmiServiceExporter remote call: com.otv.rmi.server.IRMIUserService.getUserList
STEP 13 : DOWNLOAD
Reference: Spring Remoting Support and Developing RMI Service from our JCG partner Eren Avsarogullari at the Online Technology Vision blog.
Hello, thanks for the article.
I’m sure it is a typo because otherwise the code wouldn’t compile: the generic type signature of ConcurrentHashMap is obviously wrong. It rather needs to be: ConcurrentHashMap