The Context and InitialContext ObjectsWe have mentioned the term binding several times in this chapter. Although there is a Binding class in the JNDI API, you deal most often with something called a Context . A Context plays a central role in JNDI. It is used to bind, unbind, and locate objects that have been associated with a name in JNDI. The Context is represented by the javax.naming.Context interface. This interface has the necessary methods to put objects into the naming service and also to locate them. Table 4.2 lists the most commonly used methods from the Context interface when dealing with EJB. Table 4.2. Most Often Used Methods in the Context Interface for EJB
Many other methods are defined in the Context interface, but the ones in Table 4.2 are the ones that you will be using the most with EJB. All naming services have a root entry point. Think of this as the beginning of the namespace. All clients need to start from somewhere in the namespace and this starting point is called an InitialContext . Typically, the first step for any client that wants to use a naming service is to create an instance of the InitialContext . The javax.naming.InitialContext class represents the starting point for a client using a JNDI naming service. The InitialContext class implements the Context interface; it has all the methods in Table 4.2 and more at its disposal. To create an InitialContext , you just need to call the constructor for it. The InitialContext must be able to find the environment properties that we discussed earlier. The particular environment properties that are required depend on the naming service SPI that you are using. For our file system example, we need to specify the following two properties:
In fact, these two properties are typically always needed by the InitialContext to find and acquire a connection to the naming service. There are really just three steps in obtaining an InitialContext .
Caution A single instance of an InitialContext is not guaranteed to be thread-safe. If you have multiple threads that need to access the instance of the InitialContext , they should synchronize themselves or use separate instances of the InitialContext . You can pass null into the lookup method and get back a new reference to the same Context , which may have its environment modified without affecting the original Context . You can also access this new Context concurrently with the original. Listing 4.2 shows an example of creating an InitialContext to our file system naming service. There are two environment properties that we must specify when using the file system as a JNDI naming service. The first one is to tell which initial context factory we will be using. For the file system, the value of this property is Context.INITIAL_CONTEXT_FACTORY=com.sun.jndi.fscontext.RefFSContextFactory The second environment property is the provider URL. In the case of the file system, this will be the directory that we want to be the root context. As mentioned previously in this chapter, the format of this value is dependent on the service provider that's being used. For the file system, it should look like this: Context.PROVIDER_URL=file:///c:/jndi_root/ This file system service provider requires the prefix file:/// to be added to the root directory. For this example, we are going to pass the environment properties into the constructor of the InitialContext . The provider URL for this example comes from the command line because everyone might be using a different directory for the root context. As we said earlier, there are times when putting everything into the jndi.properties isn't the best approach. Listing 4.2 An Example of Obtaining an InitialContextimport javax.naming.*; import java.util.Hashtable; import java.util.Properties; public class JNDIClient { // Default Constructor public JNDIClient(){ super(); } // Create an InitialContext using the environment properties passed in public Context getInitialContext( Hashtable env ) throws NamingException{ // Create the InitialContext using a Hashtable of properties return new InitialContext( env ); } public static void main( String[] args ){ // Reference for the InitialContext Context initCtx = null; // Ensure that the providerURL is passed in on the command line if ( args.length == 0 ){ System.out.println( "Usage: JNDIClient <providerURL>" ); System.exit( 0 ); } // Create an instance of the JNDIClient JNDIClient client = new JNDIClient(); // Create the environment variables for the InitialContext Hashtable env = new Hashtable(); env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory" ); env.put( Context.PROVIDER_URL, args[0] ); try{ // try to get the InitialContext initCtx = client.getInitialContext( env ); if ( initCtx != null ) { System.out.println ("InitialContext was created successfully"); }else{ System.out.println ("InitialContext was not created successfully"); } }catch( NoInitialContextException ex ){ ex.printStackTrace(); }catch( NamingException ex ){ ex.printStackTrace(); }finally{ try{ System.out.println( "Closing the InitialContext" ); // Only call close on a non-null InitialContext reference if ( initCtx != null ) initCtx.close(); }catch( Exception ex ){ System.out.println( "Could not close the InitialContext" ); } } } } It's probably a good idea to walk through the example in Listing 4.2 and figure out what's really going on. The JNDIClient program takes a single argument, which is the providerURL . This argument will be passed into the environment properties. We could have also set the value in the jndi.properties resource file, but this way is easier for you to change and see what different results are obtained based on different values. For the file system SPI, this value represents the root directory. After a Hashtable is created with the correct values, the getInitialContext method is called and the environment properties are passed in. Remember, we could have also specified the values in a resource file or the system properties. We did it this way to provide a little more insight and to make it easier to change the providerURL value on the command line. You'll see an example later in this chapter on using a resource file. After the InitialContext object is created, the program immediately closes it because for this example, we are only trying to show how to create one. Assuming you are using the directory c:\jndi_root , if you run the preceding program from the command line and pass in the root directory like this: java JNDIClient file:///c:/jndi_root/ you should see the following output: C:\>java JNDIClient file:c:///jndi_root InitialContext was created successfully Closing the InitialContext C:\>
Closing an InitialContextYou should always close the InitialContext object when you are finished with it. This is similar to closing any other finite resource such as a JDBC connection. You should also ensure that you close the InitialContext even when an exception is thrown. The best way to do this is by putting the close method in a finally block. You also should put a try/catch around the close method. Listing 4.3 illustrates how you can ensure that the InitialContext is always closed when your application is finished with it. Listing 4.3 The Proper Method for Closing an InitialContexttry{ // some jndi work being performed }catch( Exception ex ){ // handle the exception }finally{ try{ if ( initCtx != null ) initCtx.close(); }catch( Exception ex ){ System.out.println( "Could not close the InitialContext" ); } } |