Remote Method Invocation

RMI has been part of Java since version 1.1 and is central to how many of the platform's remoting solutions are created. CORBA support in Java is provided using RMI, and EJB uses RMI as the underlying mechanism for bean communication. Also, in the "Web Services with JAXRPC" section later in this chapter, you will see that JAXRPC builds on RMI concepts to expose Java objects as web services.

In this section, we look at four distinct examples. First, you see how to expose an arbitrary Java object as an RMI service. Second, you learn how to access this service via a proxy. The third topic we cover is how to expose an RMI service to CORBA using Spring, and finally, you see how to access this service using a Spring-created proxy.

Exposing Arbitrary Services

The traditional approach to building an RMI service involves first defining an interface for your service that extends the java.rmi.Remote interface. Your RMI service then implements this interface and most likely extends the java.rmi.server.UnicastRemoteObject class. The main drawback to this approach is that your remote service is then coupled to the RMI framework, thus reducing the chances for component reuse and making it difficult to change the remoting architecture used by your application.

Spring remoting allows you to overcome this problem using the RmiServiceExporter class. By using RmiServiceExporter, you can expose any arbitrary Java object as an RMI service using any Java interface. Your interface is not required to extend Remote, nor is your service class required to extend UnicastRemoteObject. In addition, RmiServiceExporter allows your remote service to be configured and exposed declaratively, which reduces your need to create executable code just to expose remote services. Another benefit of using Spring to expose RMI services is that you can avoid having to create stubs for your service because this is handled automatically by Spring.

The main benefit of Spring's approach to RMI service exposure is that it allows you to expose existing service objects in your application as remote services with no modifications. Provided that your service classes implement an interface, which is good design anyway, you can easily expose your application services remotely with minimal effort.

In Listing 16-1, you can see the HelloWorld interface that acts as the interface for our remote service.

Listing 16-1: The HelloWorld Interface

image from book
package com.apress.prospring.ch16.remoting;      public interface HelloWorld {          public String getMessage(); } 
image from book

Notice that this interface does not extend the Remote interface, and the getMessage() method is not declared to throw RemoteException as required by the RMI specification. In fact, this interface provides no indication that it will be exposed remotely at all. Likewise, the implementation of the HelloWorld interface shown in Listing 16-2 is equally RMI-independent.

Listing 16-2: The SimpleHelloWorld Class

image from book
package com.apress.prospring.ch16.remoting; ss public class SimpleHelloWorld implements HelloWorld {          public String getMessage() {         return "Hello World";     } }
image from book

As with the HelloWorld interface, the SimpleHelloWorld class is free from any RMI-specific details—indeed, it could well have been created before exposing it as an RMI service was even considered. To expose the SimpleHelloWorld class as an RMI service via the HelloWorld inter- face, simply configure an instance of RmiServiceExporter in your ApplicationContext with the appropriate details, as shown in Listing 16-3.

Listing 16-3: Exposing RMI Services with RmiServiceExporter

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"       "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean             />          <bean            >         <property name="serviceName">             <value>HelloWorld</value>         </property>         <property name="service">             <ref local="helloWorldService"/>         </property>         <property name="serviceInterface">             <value>com.apress.prospring.ch16.remoting.HelloWorld</value>         </property>         <property name="registryPort">             <value>9000</value>         </property>         <property name="servicePort">             <value>9001</value>         </property>     </bean>      </beans>
image from book

Here we declared two beans: the helloWorldService bean, which is the bean we want to expose, and the serviceExporter bean, which exposes the helloWorldService bean as an RMI service. In the serviceExporter bean, we set the serviceName property to HelloWorld. The RmiServiceExporter class requires this property and uses it as the service name when registering the service with the RMI registry. The service property is used to pass the RmiServiceExporter the bean instance that should be exported, and the serviceInterface property defines the interface that should be used as the remote interface. You can remotely invoke only methods on the service interface—if you have a method on your service object but not on the service interface, it cannot be invoked remotely. The registryPort property allows you to specify the port of the RMI registry, and the servicePort property allows you to specify which port the service uses for communication. By default, servicePort is set to 0, meaning a random port number is used.

With the configuration of the RmiServiceExporter complete, all you need to do in your server application is load the ApplicationContext. When you do, Spring invokes the RmiServiceExporter automatically and exposes the service. Internally, the RmiServiceExporter class implements the InitializingBean interface to perform the actual service exposure process. This means that if you want service exposure to happen automatically, you must use an ApplicationContext, not a BeanFactory, because a BeanFactory does not auto-instantiate singletons.

Listing 16-4 shows a simple class that loads the configuration from Listing 16-3; it then simply waits for connections to the service.

Listing 16-4: Hosting the HelloWorld Service

image from book
package com.apress.prospring.ch16.remoting.rmi;      import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext;      public class HelloWorldHost {          public static void main(String[] args) throws Exception {         ApplicationContext ctx = new FileSystemXmlApplicationContext(         "./ch16/src/conf/rmi/helloWorld.xml");         System.out.println("Host Started...");     } } 
image from book

If you run this example, you will notice that it does not exit immediately because the remote service is running in the background, waiting for connections from clients. As you can see from the examples in this section, you can expose any of your service objects as a remote service quickly and easily using the RmiServiceExporter class, all without modifications to the service object or to the service interface. In the next section, you will see how to access this remote service transparently using a Spring-generated proxy.

Accessing an RMI Service Using Proxies

One of the most complex parts of building RMI-based applications is creating the client application. If it uses the traditional approach to creating RMI clients, your client application becomes coupled to RMI, making it difficult to change to a new remoting architecture. If this is the case, you are forced to deal with all the messy internals of RMI, such as RemoteExceptions and service lookup.

Thankfully, Spring provides a much simpler mechanism for interacting with an RMI service—a proxy generator, which removes the need for you to create plumbing code and eliminates the coupling of your application to Spring. Using the proxy generator, you can have Spring generate a proxy to the remote service that implements the service interface, thus allowing you to interact with the remote service via the service interface as though it were a local component. Spring hides all the RMI details, such as service lookup and exception handling; this allows you to code to the business interface rather than to a particular implementation.

The RMI proxy generator, RmiProxyFactoryBean, as with all proxy generators, implements the FactoryBean interface, which allows the proxy to be created and configured declaratively and then injected into a component as a dependency. Listing 16-5 shows a class that defines such a dependency.

Listing 16-5: The HelloWorldClient Class

image from book
package com.apress.prospring.ch16.remoting.rmi;      import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext;      import com.apress.prospring.ch16.remoting.HelloWorld;      public class HelloWorldClient {          private HelloWorld helloWorldService;          public static void main(String[] args) throws Exception {         ApplicationContext ctx = new FileSystemXmlApplicationContext(                 "./ch16/src/conf/rmi/helloWorldClient.xml");              HelloWorldClient helloWorldClient = (HelloWorldClient) ctx                 .getBean("helloWorldClient");         helloWorldClient.run();          }          public void run() {         System.out.println(helloWorldService.getMessage());     }          public void setHelloWorldService(HelloWorld helloWorldService) {         this.helloWorldService = helloWorldService;     } }
image from book

Here you can see that the HelloWorldClient class defines the helloWorldService class that expects an instance of HelloWorld to be provided. By using the RMI proxy generator, we can create a proxy to the SimpleHelloWorld service exposed in Listing 16-5. This proxy implements the HelloWorld interface and is injected into an instance of the HelloWorldClient class. Listing 16-6 shows the appropriate configuration for this.

Listing 16-6: Configuring an RMI Proxy

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean             >         <property name="serviceUrl">             <value>rmi://localhost:9000/HelloWorld</value>         </property>         <property name="serviceInterface">             <value>com.apress.prospring.ch16.remoting.HelloWorld</value>         </property>     </bean>          <bean             >         <property name="helloWorldService">             <ref local="helloWorldService"/>         </property>     </bean> </beans>
image from book

The important piece of code here is the helloWorldService declaration. Notice that two properties are required: serviceInterface and serviceUrl. The serviceInterface property tells the proxy generator which interface the generated proxy should implement. The serviceUrl points the proxy at the correct RMI service—in this case, this is the HelloWorld service running in the registry at port 9000 on the localhost.

Because the RmiProxyFactoryBean class implements the FactoryBean interface, you can treat it as though it is an instance of the service interface because this is the type that the FactoryBean is defined to return. Because of this, you can use the helloWorldService bean to satisfy the helloWorldService dependency of the helloWorldClient bean.

To test this example, first make sure the host class from Listing 16-4 is running, then run the HelloWorldClient class. After a short delay, you will see "Hello World" displayed in the HelloWorldClient window. Note that you can run the HelloWorldClient class as many times as you wish, provided the HelloWorldHost class is still running.

As is evident from this example, using proxies is a simple yet powerful way to access remote services. By using a proxy, you remove the burden on you to handle all the plumbing code involved with your remoting architecture of choice. As a result, you decrease the chance that bugs will creep into your application and increase the speed at which you can create applications. Another key benefit of using proxies is that doing so reduces the coupling between your application and your remoting architecture, making it easier for you to swap out remote components for local ones or to change the remoting architecture for another one.

Exposing CORBA Services

One excellent feature offered by RMI is that it allows you to expose access services using the Internet Inter-Orb Protocol (IIOP). This means that components written in other languages can access these services using CORBA. CORBA is a popular solution for interoperating between components written in different languages; as such, it enjoys extensive support in many of the most popular programming languages available today.

Exposing a Java service via RMI using IIOP rather than the default Java Remote Method Protocol (JRMP) is simply a matter of generating the correct stubs and then exposing the service via a CORBA Object Request Broker (ORB). As you saw in the earlier examples, when you are exposing a service using JRMP, Spring not only removes the need for your components to be coupled to the RMI infrastructure, but it also removes the need to create stubs. When you are exposing CORBA services, Spring is not quite as helpful, although it does take care of registering your service with the ORB.

In fact, Spring does not really supply classes to simplify CORBA service exposure and utilization; instead, it provides features to expose and look up remote services using JNDI. However, this feature is most useful when you are dealing with CORBA components because your application can interact with the ORB via JNDI.

In this section, you learn how to build an RMI service, generate the appropriate IIOP stubs, and then expose the IIOP service to the ORB using Spring. In order to run the example in this section, you need to obtain a JNDI provider for the CORBA Common Object Services (COS) name server. You can obtain the Sun implementation from the JNDI home page at http://java.sun.com/products/jndi/. This provider allows you to access the COS naming service using the JNDI API, thus allowing Spring to interact with it as well.

The first step you need to take when building an RMI service is to create the remote service interface. Your remote service interface must extend the java.rmi.Remote interface, and all methods in the interface must throw RemoteException. Listing 16-7 shows the remote service interface for this example.

Listing 16-7: The RemoteHelloWorld Interface

image from book
package com.apress.prospring.ch16.remoting.rmi;      import java.rmi.Remote; import java.rmi.RemoteException;      public interface RemoteHelloWorld extends Remote {          String getMessage() throws RemoteException;  }
image from book

This interface is very similar to the HelloWorld interface shown in Listing 16-1 except that it now meets the contract required by the RMI specification.

Next, you need to create an implementation of your remote interface. Listing 16-8 shows a trivial implementation of the RemoteHelloWorld interface.

Listing 16-8: The SimpleRemoteHelloWorld Class

image from book
package com.apress.prospring.ch16.remoting.rmi;      import java.rmi.RemoteException;      public class SimpleRemoteHelloWorld implements RemoteHelloWorld {          public String getMessage() throws RemoteException {         return "Hello World";     } }
image from book

As with the RemoteHelloWorld interface, this class is not much different from its corresponding nonremote implementation shown in Listing 16-2. At this point, you should compile both the RemoteHelloWorld interface and the SimpleRemoteHelloWorld class so that they are available to create the IIOP stub.

Note 

If you are unfamiliar with the concepts of stubs or ties, we recommend that you read Java RMI by William Grosso (O'Reilly, 2001) for a comprehensive discussion of all things RMI.

Generating IIOP Stubs and Tags

To generate a remote stub for an RMI service, use the rmic tool that is included with your JDK. The rmic tool takes the remote service class and from this, generates the appropriate stubs. By default, rmic generates stubs for JRMP, but if you specify the –iiop switch, it generates IIOP stubs and ties instead. To generate stubs for the SimpleRemoteHelloWorld class, you must run the following command:

rmic -classpath bin -d bin –iiop ¿ com.apress.prospring.ch16.remoting.rmi.SimpleRemoteHelloWorld

The –classpath switch specifies the location of any classes needed by rmic. The rmic tool needs to be able to access both the class and the interface for your remote service, so make sure that they are both on the classpath specified by the –classpath switch. The –d switch specifies the root folder to which the generated classes should be written. The final argument is the fully qualified class name of the service class for which you are generating stubs. Running this command generates the _SimpleRemoteHelloWorld_Tie and _RemoteHelloWorld_stub class files in the same directory as the SimpleRemoteHelloWorld class.

Exposing the Service to CORBA Using Spring and JNDI

After you generate the stubs, the next step is to configure an instance of JndiRmiServiceExporter in your ApplicationContext to export the remote service to a JNDI location. In this case, we use the COS provider for JNDI to register the service with the CORBA ORB. Listing 16-9 shows this configuration file.

Listing 16-9: Exposing an RMI Service to CORBA Using JNDI

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean             />          <bean            >         <property name="jndiName">             <value>HelloWorld</value>         </property>         <property name="service">             <ref local="helloWorldService"/>         </property>         <property name="jndiEnvironment">             <props>                 <prop key="java.naming.factory.initial">                                com.sun.jndi.cosnaming.CNCtxFactory</prop>                 <prop key="java.naming.provider.url">iiop://localhost:1050</prop>             </props>         </property>     </bean> </beans> 
image from book

In this configuration, we declare two beans, helloWorldService and serviceExporter. The helloWorldService bean is simply an instance of the SimpleRemoteHelloWorld class to be managed by Spring. The important part of this code is the serviceExporter bean. For the serviceExporter bean, we specified three properties: jndiName, which is the name used to register the remote service with the ORB; service, which is the actual service object and must be of type Remote; and jndiEnvironment.

The jndiEnvironment property allows you to configure the InitialContext that Spring uses internally to perform JNDI operations. Here we specify that the CNCtxFactory class should be used to create the actual InitialContext implementation. CNCtxFactory is part of the COS JNDI provider and performs lookup and registration operations against the CORBA ORB specified using the java.naming.provider.url property. Basically this means that you should execute operations against the ORB running on port 1050 of the local machine. Do not worry too much about this yet; we discuss the ORB later in this section.

Creating a Host Application

With the configuration of the service exporter complete, all that remains from a code perspective is to create the host application that loads the ApplicationContext. Listing 16-10 shows the HelloWorldJndiHost class that loads the ApplicationContext and waits for user connections to the service.

Listing 16-10: The HelloWorldJndiHost Class

image from book
package com.apress.prospring.ch16.remoting.rmi;      import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext;      public class HelloWorldJndiHost {          public static void main(String[] args) throws Exception {         ApplicationContext ctx = new FileSystemXmlApplicationContext(         "./ch16/src/conf/rmi/helloWorldJndi.xml");         System.out.println("Host Started...");     } }
image from book

Here, the main() method simply loads the ApplicationContext using the configuration shown in Listing 16-9; it then prints a message to the console to notify the user that the host has started. As with the host class shown in Listing 16-4, this class does not exit automatically when it is run; this is because it is kept alive by the RMI framework that is exposing the remote service to CORBA.

Starting the Object Request Broker

If you try to run the host application at this point, it returns an error, informing you that it is unable to connect to the ORB. The JDK comes complete with an ORB that you can use when you develop CORBA applications using Java. You can start the ORB using the orbd application, which you can find in the bin directory of your JDK. To start the ORB using port 1050, as the host application requires in Listing 16-10, run the following command:

orbd –port 1050

Once the ORB is started, you can start up the host and host your CORBA component.

Accessing a CORBA Service

Just as Spring provides proxy support for JRMP RMI services, it can also generate a proxy for any remote service that can be accessed via JNDI. Using this feature, you can create a proxy to a remote IIOP service hosted in an ORB. In this section, you see how to use the JndiRmiProxyFactoryBean to generate a proxy to an RMI/IIOP service exposed via an ORB.

In order to create a proxy from a JNDI-accessible RMI resource, you must configure an instance of JndiRmiProxyFactoryBean with the appropriate JNDI access information and the name of the interface you want the proxy to implement. Listing 16-11 shows a configuration that generates a proxy for the SimpleRemoteHelloWorld service we created in Listing 16-8.

Listing 16-11: Configuring a JNDI RMI Proxy

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean             >         <property name="jndiName">             <value>HelloWorld</value>         </property>         <property name="serviceInterface">             <value>com.apress.prospring.ch16.remoting.HelloWorld</value>         </property>         <property name="jndiEnvironment">             <props>                 <prop key="java.naming.factory.initial">                             com.sun.jndi.cosnaming.CNCtxFactory</prop>                 <prop key="java.naming.provider.url">iiop://localhost:1050</prop>             </props>         </property>     </bean> </beans>
image from book

Here you can see that we configured the JndiRmiProxyFactoryBean class with the same JNDI information we used to configure JndiRmiServiceExporter in Listing 16-9; this is because it is the service we want to access. An interesting point to note here is that we used the HelloWorld interface from Listing 16-1 as the service interface, not the RemoteHelloWorld inter- face from Listing 16-7. One of the major benefits of the Spring-generated proxies is that you can use any interface you want for the proxy, as long as the names, return types, and parameter lists of the methods match those exposed by the remote service. In this case, we use the HelloWorld interface that exposes a getMessage() method; this method is identical to the RemoteHelloWorld.getMessage() method but does not throw RemoteException. The Spring-generated proxy takes care of handling any RemoteException on your behalf, leaving your client code free to code to the business interface rather than the remote interface.

Using the proxy is as simple as loading ApplicationContext and obtaining the corresponding bean in your application, as shown in Listing 16-12.

Listing 16-12: The HelloWorldJndiClient Class

image from book
package com.apress.prospring.ch16.remoting.rmi;      import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext;      import com.apress.prospring.ch16.remoting.HelloWorld;      public class HelloWorldJndiClient {          public static void main(String[] args) throws Exception {         ApplicationContext ctx = new FileSystemXmlApplicationContext(                 "./ch16/src/conf/rmi/helloWorldJndiClient.xml");              HelloWorld helloWorld = (HelloWorld) ctx.getBean("helloWorldService");         System.out.println(helloWorld.getMessage());          } }
image from book

Running this example with the HelloWorldJndiHost class also running results in the following output:

Hello World

From this example and the earlier example that shows the creation of a proxy to an RMI/JMRP service, you can see that Spring proxies greatly reduce the amount of code you need to write to access remote RMI services. One of the biggest benefits of Spring RMI proxies is that you can actually hide the fact that the service is remote; this allows your application to work directly with a business interface, relying on Spring to take care of all the RMI plumbing.



Pro Spring
Pro Spring
ISBN: 1590594614
EAN: 2147483647
Year: 2006
Pages: 189

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net