spacer
spacer spacer
spacer
spacer
CONTACT US     
spacer
spacer
spacer SyncTank Solutions spacer Kokee spacer
spacer
spacer spacer
SERVICES
spacer spacer
SUPPORT
spacer spacer
ABOUT US
spacer spacer
HIGHLIGHTS
spacer spacer
PRESENTATIONS
spacer quilt pattern spacer
spacer
spacer

Managing Remote Java Services




CreatIng a distributed application using RMI is remarkably simple and straightforward, particularly considering how potentially powerful RMI applications can be. In this article I will illustrate how you can better develop and deploy distributed applications, which I will refer to as services.

Building one or two distributed services is relatively simple, however things becomes progressively more complicated as you begin to develop, debug and deploy multiple services. Managing the services can become unbearable when you have a team of developers working on one or multiple projects. It is important to keep in mind that distributed services by definition do not reside in the same physical location as the rest of you application code. Additionally, the services are designed to offer multiple applications access to the functionality each provides. Because distributed services will be accessed via the Java RMI Registry, changes that are made to the interfaces or classes require restarting the registry and reloading all of the classes in the Registry's classpath. I will explain the basic path that a developer typically follows as he or she becomes more familiar with RMI and the common pitfalls that can slow development down to a crawl. Finally, I'll provide a simple but powerful class that will manage RMI services for you.

The Steps to creating a service manager:

STEP 1. Use the implementation class to start your RMI service.
STEP 2. Automate the RMI Registry startup.
STEP 3. Create a properties. file to hold information about your services.
STEP 4. Create the service manager.
STEP 5. Add some polish.

RMI Review

To fully benefit from this article you should be familiar RMI. At the very least, you should have a basic understanding of how to:
  • Create an interface defining the methods that will be available remotely.
  • Create a remote implementation class that provides the functionality behind the interface.
  • Use the RMI compiler included in the JDK (rmic) on the remote implementation class to create '_stub' and '_skel' classes. These classes handle the network communication and serialization.
  • Register the implementation class with the RMI registry.
  • Call a remote method using Naming.lookup().
If the above doesn't seem familiar, you'll probably benefit by reading a back-issue on implementing RMI before continuing.

There are many issues that need to be addressed when you begin developing and debugging your RMI services. These can be broken down into two groups: development-related issues and deployment-related issues. As the RMI services manager class matures, I will cover certain issues and how to deal with them.

Step 1: Use the implementation class to start your RMI service.

Most developers new to RMI use a main method in the implementation class to register the class with the RMI Registry.

public static void main (String[] arg){
        System.setSecurityManager(new RMISecurityManager());
 	     try {
FooImpl mgr = new FooImpl();
            System.out.println("Binding Service Foo..");
            Naming.rebind("Foo",mgr);
            System.out.println("Foo Bound!");
        } catch (Exception e){
            e.printStackTrace();
        }
    }
} 

Code listing 1

This works, but it assumes that the RMI Registry is already started. This means that you have two Java Virtual Machines running on the server. In order to make changes to your implementation class you have to:

  • Recompile the code.
  • Move it to the server.
  • Stop both the RMI registry and your FooImpl.
  • Start the RMI registry.
  • Start FooImpl.
If you fail to stop the RMI registry, your old implementation class may still be in memory!

Step 2: Automate the RMI Registry startup

The next logical step is to start the RMI registry inside your main method.

public class FooServer {
 public static void main (String[] arg){
        System.setSecurityManager(new RMISecurityManager());
        try {
            try{
                  java.rmi.registry.LocateRegistry.createRegistry(1099);
                System.out.println("RMI Registry started.");
            } catch (java.rmi.RemoteException er) {
                System.out.println("Registry already started.");
            }
            FooImpl mgr = new FooImpl();
            System.out.println("Binding Service Foo..");
            Naming.rebind("Foo",mgr);
            System.out.println("Foo");
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

Code listing 2

The code in listing 2 attempts to start the RMI registry on the default RMI port (1099). In the event that the RMI registry is already started, the method will continue to bind the 'Foo' service to the registry. This is certainly cleaner, and you now have one less virtual machine running on your server. If you are developing on your and have only one service to manage, you are relatively safe. If you are part of a team, or need to manage multiple services still have some issues:

  • If each of your developers binds services in the main method of the implementation class, even in the best of situations, you have to execute that code whenever you restart the RMI Registry. If you do not do this, all services will no longer be available! See Diagram 1.
  • To insure the RMI Registry is shut down, ALL JVM's that use this method of binding must be stopped. This could involve asking the members your development team if restarting is OK.
  • Because each of the services are started in the main method of a class, many Virtual Machines are running simultaneously, this will quickly degrade the performance of your applications on most platforms.
Diagram 1
Diagram 1

Making The Server Configurable

If you are developing services that will be deployed at a client site you need to give your client's administrator an easy way to stop and start the services that you provide. You should also provide simple 'hooks' to add more services as you develop them.

Step 3: Create a properties. file to hold information about your services.

The easiest way to store information about your remote services is to use a .properties file. Whether you deploy on NT, AS400, Solaris, etc.. most system administrators can handle working with a flat file that is of the form key=value. Listing 3 contains the contents of the services.property file. Note, the '\' and the newline character is ignored by Java and makes the file easier to read and maintain.

# remote services configuration file
# Russell Castagnaro 
# enter the service name and its implementation class under the 'services' property
services=\
service_registry=com.synctank.servers.ServiceRegistryImpl,\
foo_service=com.synctank.servers.FooImpl,\
rcuh_service=com.synctank.servers.RCUHImpl,\
etc_service=com.synctank.servers.EtcImpl

Listing 3

Step 4: Create the service manager

The services property contains all of the service names (for binding) and the fully qualified name of the implementation class. This property will be tokenized to access the name/ value pairs in the property string. Your developers can go directly to this file and add the name and implementation class to the services property to insure that their services are managed by the new class.

The service manager also allows you or a system administrator to bind the RMI registry to another port through a command line argument. This can come in very handy if you are deploying services behind a firewall since many firewalls only allow data packets through approved ports.

public static void main (String[] arg){
         int argPort = DEFAULT_PORT;
if (arg.length > 1) {
		  System.out.println("Usage for RemoteServicesManager");
		  System.out.println("java RemoteServicesManager");
		  System.out.println("java RemoteServicesManager ");
		  Return;
  } else if (arg.length == 1) {
	  try {
argPort = new Integer(arg[0]).intValue();
              } catch (Exception e) {}
	     }

  RemoteServicesManager rs = 
new RemoteServicesManager (argPort);
        // just bear with the run method..
     rs.run();
        System.out.println("Remote Services Manager started..");
 
    }
    public RemoteServicesManager(int _port){
        super();
        this.port = _port;

    }
}

Listing 4

Listing 4 illustrates how you can start the RMI Registry on a specific port. You still need to get the service names and classes from the properties file and bind them to the registry. This can be accomplished by loading the properties file into a java.util.Properties object and tokenizing the service and class names. Note that I've chosen to place this code in listing 5 in the run() method. The reasons will become obvious quite soon.

public void run() {
Properties services;
// the Configuration class just loads a Properties 
object from a file, among other things.. try{ services = Configuration.loadProperties("/java/services.properties"); } catch(Exception ioe){ System.out.println("Unable to load the services.properties."); ioe.printStackTrace(); return; } // start the RMI Registry try { System.out.println("Starting RMI Registry on port "+port+".."); java.rmi.registry.LocateRegistry.createRegistry(port); } catch (java.rmi.RemoteException ree){ System.out.println("RMI Registry already started."); } // get an enumeration of the Services offered Enumeration serviceNames = services.keys(); String serviceName=""; String serviceClass="java.lang.Object"; System.setSecurityManager(new RMISecurityManager()); // loop through all of the services and start them UnicastRemoteObject service; while (serviceNames.hasMoreElements()){ try{ // get the service name (for the RMI Name service) serviceName = serviceNames.nextElement().toString(); // get the fully qualified class name serviceClass = services.getProperty(serviceName); // create an instance of the Implementation class service = (UnicastRemoteObject)Class.
forName(serviceClass).newInstance(); System.out.println("Binding Service ("+serviceName +").."); Naming.rebind(serviceName,service); System.out.println(serviceName+ " bound!"); } catch (ClassCastException cce){ System.out.println("Unable to cast to "+serviceClass+"
for "+ serviceName + " service."); cce.printStackTrace(); } catch (NullPointerException npe){ System.out.println("Configuration problem.
Check the services.properties file."); npe.printStackTrace(); } catch (Exception e) { System.out.println("Unable to bind "+serviceName); e.printStackTrace(); } } System.out.println("Remote service binding complete."); }

Listing 5

Step 5: Add Some Polish

You now have a class that manages your remote services for you. Since you are concerned with functionality and ease of use, after binding these services to the registry, it would be nice to be able to stop the server without having to issue a ctl-c, ctl-z or kill command. By making the class extend java.lang.Thread and making a small code addition, you can provide this functionality. See listing 6.

public static void main (String[] arg){
 		int argPort = DEFAULT_PORT;
if (arg.length > 1) {
		  System.out.println("Usage for RemoteServicesManager");
		  System.out.println("java RemoteServicesManager");
		  System.out.println("java RemoteServicesManager ");
		  Return;
  } else if (arg.length == 1) {
	  try {
argPort = new Integer(arg[0]).intValue();
              } catch (Exception e) {}
	     }


        Thread rs = new RemoteServicesManager(DEFAULT_PORT);
        rs.start();
        System.out.println("Remote Services thread started..");
        try{
            BufferedReader in;
            // this allows for a graceful exit, enter 'end' instead 
// of ctl-c or ctl-z
            while (true){
                in = new BufferedReader(new 
                    InputStreamReader(System.in));
                String c = in.readLine();
                if (c.indexOf("end") > -1) {
                    rs.join();
                    break;
                } else {
                    System.out.println("You entered: " +
 c+ "\nEnter 'end' to stop");
                }
            }
        } catch (Exception e) {}
        // code to unbind the services and save the properties
  System.out.println("Ending..");
        System.exit(0);
            
    }

Listing 6

Once you implement this class it is quite simple to add functionality such as: logging messages to a log file, or providing a method for remote binding/un-binding of new/ old services. You can make this class accessible via a Servlet, or modify it to handle other types of remote services.

Deployment

I've only mentioned some of the development issues involved in creating solutions. Deployment issues that may arise when you introduce a client to distributed computing can be numerous. Most clients are more comfortable with terminal based or client/ server solutions. Distributed computing is a new paradigm, and your clients' confusion is somewhat understandable. I have been peppered with questions from concerned clients.
  • 'What do you mean it doesn't have to be installed on the same physical server?'
  • 'Our application server is running on NT, why should we install this on our AS400?'
  • 'But…. How does the application know where these services are located?'
  • 'How can we insure that the RMI services are started?'
  • 'Is this extensible?'
You will still have to answer these and more technical questions. However, providing a simple application that manages the distributed services will allow them to ease into distributed computing with a minimum of headaches. You can give a high level overview of RMI and distributed computing without most of the pain associated with training their support staff. Offering an easy way to configure, stop and start the managed services is the best way to get your clients' administrative staff to become comfortable with your products. Once a client realizes how easy these services are to use, you'll find you have many more allies. Another nice feature of this application is that it works quite well with the standard Java installers (ZeroG and InstallShield). Nothing is more impressive to a client than a simple installation.

Summary

Distributed application development is made much simpler in Java by utilizing the RMI packages. However, managing those services, either as a developer or end user, is potentially, quite a bit more complicated. By using one class to manage all of your remote services you can greatly simplify your Java development, deployment, and maintenance. You have gone through the trouble to create remote services for your organization or your client, now you can manage those services without purchasing a Java application server.






You have Privacy! Please read our Privacy Statement.
© SyncTank Solutions, Inc.
spacer