The event service demonstrated in the previous chapter is an excellent place to demonstrate the usefulness of the Connector pattern. You will use the event service as is but with a new event subscriber. The event subscriber is part of a larger application that receives events from the remote event service and posts them immediately to the user through a simple Java Swing user interface. The pattern implementation, shown in Figure 13-6, adds an application subscriber and the components that enable it to be a remote object.
As mentioned in the "Understanding the Structure of the Connector Pattern" section, the class diagram does not belie the nature of the deployment or some of the details inherent in actual implementation. In this case, the subscriber service implementation that resides in the Web tier uses an instance of the ApplicationSubscriberImpl_Stub instance to communicate with the ApplicationSubscriberImpl instance that resides in the application's process, as shown in Figure 13-7.
The interface, ApplicationSubscriber , abstracts away the actual concrete class that the subscriber holds, which is simply a good practice employed by RMI users. Using the interface model, the service implementation, Subscriber , can look up an ApplicationSubscriber interface in the naming service and not worry about the implementation returned to them. In this case, the subscriber receives the stub.
To leverage RMI, you will first define the interface that both the concrete implementation class and the proxy to the implementation class implement. The service implementation could use the interface as well, though the signatures to the methods do not have to match between the service implementation and the remote implementation class. Listing 13-1 contains the interface implementation.
public interface ApplicationSubscriber extends Remote { public void update(String eventId, String data) throws RemoteException; }
There should be nothing too interesting in the interface. To implement the class as a remote object, the class extends java.rmi.Remote and the methods in the class throw the java.rmi.RemoteException exception.
The class for the application subscriber implements the interface in Listing 13-1 and extends the java.rmi.server.UnicastRemoteObject class. This class extension makes your object remoteable and gives it the ability to be registered with the naming service from RMI and subsequently receive method calls from outside of the process.
The class is a part of a larger Swing-based application. Its responsibilities include the following:
Receiving events from a remote target ”in this case the service implementation subscriber that serves as a bridge to an external event service
Posting the events directly to a text area for the application user to see
To fulfill the second responsibility, the application passes a reference to the JTextArea graphic component on the constructor that this class instance uses to post messages for the user. Listing 13-2 illustrates the application-side subscriber that serves as the implementation class.
public class ApplicationSubscriberImpl extends java.rmi.server.UnicastRemoteObject implements ApplicationSubscriber { JTextArea ivTextArea = null; public ApplicationSubscriberImpl(JTextArea textArea) throws RemoteException { super(); ivTextArea = textArea; } public ApplicationSubscriberImpl(int port, JTextArea textArea) throws RemoteException { super(port); ivTextArea = textArea; } public void update(String eventId, String data) throws RemoteException { ivTextArea.append("Event ID: "+eventId); ivTextArea.append("Event Data: "+data); } }
The service implementation (the subscriber that serves as the Web Service) calls the update method on this class. This class, in turn , writes the data from the event to the application's text area by simply appending the event data to the text area. In reality, you would have a more user-centric mechanism for notifying the user of an important event that needs their attention.
This class requires special handling on instantiation. To facilitate communication between the service implementation deployed through Apache Axis and this application-side implementation class, the class must register with RMI's component registry, the Naming service. The service implementation can then use Naming to look up the class from an outside process and communicate through the proxy.
J2SE comes with an RMI compiler, rmic , that handles the details of building the application-side implementation stub generation. The stub is subsequently instantiated into the same process that runs the service implementation; thus, the interface must at least be in the class path for the Apache Axis engine.
Your build process ”in this case, the Apache Ant build process that comes with the source code for the book ”automatically builds the stubs when the code is recompiled.
The previous classes and the interface concerned themselves with the concrete behavior of the application that the service implementation calls upon receiving a request. Also in the previous sections, you built the mechanisms that the service implementation uses to have a conversation with the application-side (or logic tier) components.
This section enhances the subscriber from the event service built in the Publish/Subscribe pattern to forward messages to the application with an interest in the events published by the event service. The difference in the implementation code from the previous chapter occurs in the update method, shown in Listing 13-3. You no longer print information to the System.out stream in the body of the update method. Instead, you retrieve a proxy to the implementation, through RMI's Naming service, and call methods against the proxy.
public class Subscriber { private static String topic = "com.servicefoundry.books.events.ProductOrderUpdate"; ApplicationSubscriber appSubscriber = null; public Subscriber() { } public void update(String eventId, String data){ try { ApplicationSubscriber appSubscriber = (ApplicationSubscriber)Naming.lookup("ApplicationSubscriberImpl"); if(appSubscriber!=null){ appSubscriber.update(eventId, data); } } catch (Exception e) { e.printStackTrace(); } } }
Listing 13-3 retrieves the proxy from RMI's Naming every time Apache Axis calls the update method. This allows the application to start and stop at will without impacting the Web Service. The Web Service continues to receive events from the remote event service when the user application is not available, but it stops delivering events to the user application. One of the nice attributes of splitting the logic and Web tiers in terms of the process that contains them is the additional ability to leverage code and classes independently of the containers. For example, users can continue to use applications when the Web Service environment is not available.
This service implementation deploys to Apache Axis in the same manner as the subscriber in the previous chapter. The same Web Services Description Language (WSDL) created in the previous chapter describes the interface to this subscriber, and the event service has no realization that this implementation differs from the previous chapter's implementation.
Putting the new subscriber Web Service into action requires several steps worth noting. The steps described here do not necessarily have to occur in the order described; however, exceptions to the step order accompany the description:
Start the event service built in the previous chapter. This Web Service runs unmodified from the previous chapter.
Start the stand-alone application. By starting the stand-alone application, you also start the subscriber implementation class that resides within the application. Further, the application registers the RMI object with the RMI Naming service, with the application code shown in Listing 13-4, in the local RMI registry. After this step, other processes can download the remote stub class from Naming and communicate with the application's subscriber implementation. Recall that this step does not have to occur before the Web Service subscriber implementation starts because the Web Service will simply ignore events that post to it if an appropriate subscriber cannot be located.
Start the subscription Web Service built in this chapter and register it with the event service started in step 1. This step can occur before or after step 2. After the subscription Web Service starts, register its target Uniform Resource Locator (URL) with the event service started in step 1. At this point, events delivered to the event service, started in step 1, with the appropriate topic post to the event subscriber started in this step. The event subscriber started in this step then forwards the event to the application subscriber started in step 2.
Next, post an event to the Web Service with the client from the previous chapter.
Finally, view the results of the post to the Web Service in the stand-alone application's graphical text area.
Listing 13-4 contains a method run by the stand-alone application to register its subscriber to RMI's Naming component. The application uses this code as described in step 2 after initialization of the user interface but before clients can interact with the application.
public static void registerToRegistry( String name, Remote obj, boolean create) throws RemoteException, MalformedURLException{ if (name == null) throw new IllegalArgumentException( "registration name can not be null" ); try { Naming.rebind(name, obj); } catch (RemoteException ex){ if (create) { Registry r = LocateRegistry.createRegistry(Registry.REGISTRY_PORT); r.rebind(name, obj); } else throw ex; } }
The client creates the application subscriber and registers it with the code shown in Listing 13-5. The ivTextArea instance variable refers to a JTextArea graphical component created earlier in the application.
ApplicationSubscriberImpl obj = new ApplicationSubscriberImpl(ivTextArea); ApplicationSubscriberImpl.registerToRegistry( "ApplicationSubscriberImpl", obj, true);
In Listing 13-5, you registered the application subscriber using the name ApplicationSubscriberImpl . The Web Service implementation uses this name to locate the proxy to the application in RMI's Naming service, as shown previously in Listing 13-3. Once the service implementation in the Web tier has a reference to the proxy, the proxy facilitates the remote communication with the logic tier and the concrete implementation of the application subscriber logic.