Using JNDI with the Bank Example

• When you use bind , youre explicitly allowing for paths, complex names, and more general queries. For example, if the context you bind into is a subcontext of another context, the object that has been bound may be accessible via a path-based lookup. Environment variables are explicitly local and can only be retrieved by name. Its not clear when environment variables are a good idea. Theyre often convenient theyre the conceptual equivalent of attaching a sticky note to the context, but they dont really add any functionality. And using such a weakly typed interface to store important information can only cause headaches. The final four methods are: public Name composeNameName name, Name prefix throws NamingException public String composeNameString name, String prefix throws NamingException public NameParser getNameParserName name throws NamingException public NameParser getNameParserString name throws NamingException These are an attempt to get around the fact that different naming services use different formatting conventions for their names. For example, the RMI registry uses strings with the following format: rmi: host:port humanReadableName The naming service we implemented, on the other hand, doesnt use a URL format. Instead, it just uses a path-based notation to choose between contexts. The NamingParser interface lets client code parse, and deal with components of names, without having to know the exact details of the format. JNDI Service Providers A JNDI Service Provider Implementation is an implementation of the JNDI interfaces, including the code that enables Initial Context to find the naming service so specified. As of this writing, JDK 1.3 ships with two JNDI Service Provider implementations: one that wraps the RMI registry, and one that wraps the CORBA naming service. In addition, you can download several additional service providers from Javasofts web site. Currently, the Javasoft web site contains JNDI wrappers for DNS, LDAP, and the filesystem. Of course, third-party software companies such as Novell provide JNDI wrappers for their directory-based products.

15.8.2 Using JNDI with the Bank Example

Now that weve said all this, its time to actually use JNDI. There is a wrapper around JNDI that allows us to use the JNDI interfaces to access the RMI registry. In order to use this, well need to make three minor changes to our application. The first thing we need to do is change our batch files. Well still be using the RMI registry, so that batch file is okay. However, the batch files that launch the client and the server need an additional command-line property. Heres the servers batch file: start java -Djava.naming.factory.initial=com.sun.jndi.rmi.registry. RegistryContextFactory com.ora.rmibook.chapter15.jndiaccounts.applications. ImplLauncher Bob 10000 Alex 1223 The only change is that weve set the java.naming.factory.initial property to be the name of the class that creates a JNDI wrapper around the RMI registry. We could also have set this property from within our code. Either of the following two pieces of code would accomplish the same task. The first version simply sets the property from within the application, while the second overrides the system property temporarily, for the creation of a single Context : first version System.properties.putContext.INITIAL_CONTEXT_FACTORY, com.sun.jndi.rmi.registry. RegistryContextFactory; second version Hashtable environment = new Hashtable ; environment.putContext.INITIAL_CONTEXT_FACTORY, com.sun.jndi.rmi.registry. RegistryContextFactory; return new InitialContextenvironment; Once weve changed our batch files, we need to alter our code to use JNDI. Heres the relevant part of the new version of our launch code: public class ImplLauncher { private static Context namingContext; public static void mainString[] args { try { namingContext = new InitialContext ; } catch Exception e { System.out.printlnNaming context unavailable; System.exit0; } Collection nameBalancePairs = getNameBalancePairsargs; Iterator i = nameBalancePairs.iterator ; whilei.hasNext { NameBalancePair nextNameBalancePair = NameBalancePair i.next ; launchServernextNameBalancePair; } } private static void launchServerNameBalancePair serverDescription { try { Account_Impl newAccount = new Account_ImplserverDescription.balance; namingContext.rebindserverDescription.name, newAccount; System.out.printlnAccount + serverDescription.name + successfully launched.; } catchException e { e.printStackTrace ; } } .... } This is almost identical to the our earlier code. The major difference is that we have to create an instance of InitialContext . The InitialContext class looks at the system property we set and uses the factory class to create the actual context we need in this case an instance of com.sun.jndi.rmi.registry.RegistryContext , to which it then forwards all method calls. After weve created the instance of InitialContext , we use the JNDI declaration of rebind , rather than the version defined in the RMI registrys interface. There is something tricky happening here. We call a method on an instance of InitialContext . That instance of InitialContext forwards the method call to the instance of com.sun.jndi.rmi.registry.RegistryContext . The instance of com.sun.jndi.rmi.registry.RegistryContext in turn forwards the method call to the RMI registry, which means that all the underlying limitations and assumptions of the RMI registry are still in place, and still in force. In particular, the RMI registry is flat and has no ability to create subcontexts. In addition, it assumes that the objects bound into it are serializable. Therefore, attempts to use subcontexts or bind nonserializable objects will throw exceptions, even though the code might be perfectly valid JNDI code that works with other service providers. JNDI provides a common interface to different naming services. However, you still need to be aware of the limitations of the underlying service providers your code uses. The last change we need to make is to our client code. Like our launch code, it will need to create an InitialContext . It will then use the InitialContext to retrieve objects from the registry. Heres the new implementation of getAccount : private void getAccount { try { Context namingContext = new InitialContext ; _account = Account namingContext.lookup_accountNameField.getText ; } catch Exception e { System.out.printlnCouldnt find account. Error was \n + e; e.printStackTrace ; } return; }

Chapter 16. The RMI Runtime

Before we can get to the last major design pattern in Chapt er 17 , factories, we need to take a short detour and discuss what weve been referring to as the RMI runtime. This chapter is devoted to exploring the code that exists between, and manages the connection between, the stub and the skeleton. By the end of this chapter, you will understand how the distributed garbage collector works, how RMI maintains connections between clients and servers, how to use the RMI logging facilities, and how to customize RMIs behavior at runtime.