More on Objects

The early part of today's lesson covered binding objects into a JNDI namespace. To recap, a bound object must implement the Serializable interface, and the object's class file must be available to the JNDI server.

The obvious means of making an object's class file available to the JNDI server is to set the server's class path to include the necessary directory or JAR file. However, this isn't always convenient, and the JNDI specification recognizes this situation and supports dynamic loading of classes when using a directory service.

Loading Classes from a Code Base

Provided the JNDI Service Provider is a Directory Service and that service supports Internet RFC 2713, the JNDI server can obtain necessary class files dynamically from any HTTP server.

RFC2713 defines an interoperable way of storing Java objects in an LDAP server. By defining how Java objects are stored and retrieved, the JNDI Naming Manager in Sun Microsystems' LDAP Service Provider can retrieve Java objects from the directory server and recreate them on the client's system.

You must configure the LDAP server to support the Java Schema defined in RFC2713. If you are using the OpenLDAP server, as previously configured in the “Attributes” section, supporting the Java Schema is a relatively simple change to the configuration:

1.
Stop the OpenLDAP slapd daemon.

2.
Edit the slapd configuration file (/usr/local/etc/openldap/slad.conf). After the existing line starting with include, add the following line:

include /usr/local/etc/openldap/schema/java.schema

3.
Save the file and restart the OpenLDAP server.

Defining a Code Base

From a programming point of view, Java class files must be made available via a Web server. A javaCodebase attribute supplies the details of the Web server location when binding the object into the JNDI directory namespace.

The following example uses the Sun Microsystems' LDAP Service Provider and an LDAP server, as discussed in the “Attributes” section earlier in today's lesson.

This example also requires an HTTP server to be running. Starting up the J2EE RI server will also start an HTTP server on port 8000. The J2EE RI stores its HTTP pages in the public_html directory in the J2EE RI home directory.

Listing 3.15 shows a simple class representing a book.

Listing 3.15. Full Text of Book.java
 1: import java.io.*;
 2: public class Book implements Serializable
 3: {
 4:     String title;
 5:     public Book(String title) {
 6:         this. title = title;
 7:     }
 8:     public String toString() {
 9:         return title;
10:     }
11: }
						

A Web server must make the book.class file available for download for the Sun Microsystems' LDAP Service provider to bind and look up Book objects. It is conventional to store downloadable class files in a separate sub-directory under the HTTP server's home directory. You normally call this sub-directory classes.

Use the following commands to copy the book.class file to the J2EE RI Web server. In both cases, you require appropriate permission to write to the J2EE RI home directory.

Under Windows

mkdir %J2EE_HOME%public_htmlclasses
copy Book.class %J2EE_HOME%public_htmlclasses

Under Linux and Unix

mkdir $J2EE_HOME/public_html/classes
cp Book.class $J2EE_HOME/public_html/classes

With the class files in place, a Book object can be bound to the LDAP namespace as Listing 3.16 shows. The code uses the javaCodebase attribute to specify the URL of the directory containing the Java class files and not the class file itself.

Listing 3.16. Full Text of JNDICodebase.java
 1: import javax.naming.*;
 2: import javax.naming.directory.*;
 3: public class JNDICodebase
 4: {
 5:     private final static String JNDI = "cn=book,o=Agency,c=us";
 6:     private final static String codeURL = "http://localhost:8000/classes";
 7:
 8:     public static void main(String[] args) {
 9:         try {
10:             DirContext ic = new InitialDirContext();
11:             Book book = new Book("Teach Yourself J2EE in 21 Days");
12:             Attributes attrs = new BasicAttributes();
13:             attrs.put("javaCodebase", codeURL);
14:             attrs.put("cn", "book");
15:             ic.rebind(JNDI, book, attrs);
16:             System.out.println("Bound "+JNDI);
17:         }
18:         catch (NamingException ex) {
19:              System.err.println(ex);
20:              System.exit(1);
21:         }
22:     }
23: }
						

Note that the example uses the code base URL of localhost:8000, which is required because the J2EE web server uses a non-standard HTTP port (the standard HTTP port is 80).

Note

The http.port entry in the web.properties file in the J2EE RI config directory defines the default port for the Web server.


With the name registered and the HTTP server running, the client can look up the bound object without having the Book class file in the search path. The Sun Microsystems' LDAP Service Provider automatically loads the class file. Listing 3.17 shows a simple client program.

Listing 3.17. Full Text of JNDILookupBook.java
 1: import javax.naming.*;
 2: public class JNDILookupBook
 3: {
 4:     private final static String JNDI = "cn=book, o=Agency,c=us ";
 5:
 6:     public static void main(String[] args) {
 7:         try {
 8:             Context ic = new InitialContext();
 9:             Book book = (Book)ic.lookup(JNDI);
10:             System.out.println(JNDI+"="+book);
11:         }
12:         catch (NamingException ex) {
13:              System.err.println(ex);
14:              System.exit(1);
15:         }
16:         catch (ClassCastException ex) {
17:              System.err.println(ex);
18:              System.exit(1);
19:         }
20:     }
21: }
						

References

Sometimes, storing a serialized copy of an object in the Directory Service is inappropriate. Perhaps the object is too large or you must instantiate it dynamically because its construction depends on information that can vary from one client to another.

JNDI references provide a mechanism for storing an object by reference rather than by value. This mechanism only works if the underlying JNDI Service Provider supports Referenceable objects. The LDAP Service Provider from Sun Microsystems supports Referenceable objects.

Without going into too much detail, a reference to an object requires that a Factory class is available to build the object from the information the reference stores. From a design perspective, this requires two related classes:

  • The Object class that must implement the javax.naming.Referenceable interface

  • A Factory class that can create the required objects

The Referenceable interface requires an object to implement the getReference() method, which returns a Reference object. The Reference object defines the name of the class referred to and a Factory class that can be used to build the referenced object. Listing 3.18 shows a simple Book reference class.

Listing 3.18. Full Text of BookRef.java
 1: import java.io.*;
 2: import javax.naming.*;
 3: public class BookRef implements Referenceable
 4: {
 5:     String title;
 6:     public BookRef(String title) {
 7:         this.title = title;
 8:     }
 9:     public String toString() {
10:         return title;
11:     }
12:     public Reference getReference() throws NamingException {
13:         return new Reference(
14:             BookRef.class.getName(),
15:             new StringRefAddr("book", title),
16:             BookFactory.class.getName(),
17:             null);
18:     }
19: }
						

The second parameter to the Reference constructor uniquely defines the object. This requires a key to define the address type of the object (in this case, a String set to the value book) and a value for this specific object (in this case, the book's title). When the object is reconstructed, this address type and value will pass to the Factory object.

The Factory class must implement either javax.naming.spi.ObjectFactory or javax.naming.spi.DirObjectFactory, depending whether you use a Name Service or a Directory Service. Both classes require the Factory class to implement a getObjectInstance() method for creating reference objects. Listing 3.19 shows the BookFactory class used in the BookRef class shown in Listing 3.16.

Listing 3.19. Full Text of BookFactory.java
 1: import javax.naming.*;
 2: import javax.naming.spi.*;
 3: import java.util.Hashtable;
 4: public class BookFactory implements ObjectFactory {
 5:     public Object getObjectInstance(Object obj, Name name,
 6:         Context ctx, Hashtable env) throws Exception {
 7:         if (obj instanceof Reference) {
 8:             Reference ref = (Reference)obj;
 9:             if (ref.getClassName().equals(BookRef.class.getName())) {
10:                 RefAddr addr = ref.get("book");
11:                 if (addr != null) {
12:                     return new BookRef((String)addr.getContent());
13:                 }
14:                }
15:         }
16:         return null;
17:     }
18: }
						

The factory getObjectInstance() method checks that it is passed a Reference object and then checks that the class of the reference is a BookRef. If both of these conditions are true, the factory uses the address type book to look up the value of the object and then uses this to create a new BookRef object.

As far as name binding and object lookup are concerned, the client is unaware of the use of references. Listings 3.20 and 3.21 show how your code can use the BookRef class.

Listing 3.20. Full Text of JNDIBindBookRef.java
 1: import javax.naming.*;
 2: import javax.naming.directory.*;
 3: public class JNDIBindBookRef
 4: {
 5:     private final static String JNDI = " book";
 6:
 7:     public static void main(String[] args) {
 8:         try {
 9:             DirContext ic = new InitialDirContext();
10:             BookRef book = new BookRef("Teach Yourself J2EE in 21 Days");
11:             Attributes attrs = new BasicAttributes();
12:             attrs.put("cn", "book");
13:             ic.rebind(JNDI,book,attrs);
14:             System.out.println("Bound BookRef "+JNDI);
15:         }
16:         catch (NamingException ex) {
17:              System.err.println(ex);
18:              System.exit(1);
19:         }
20:     }
21: }
						

Listing 3.21. Full Text of JNDILookupBookRef.java
 1: import javax.naming.*;
 2: import javax.naming.directory.*;
 3: public class JNDILookupBookRef
 4: {
 5:     private final static String JNDI = "book";
 6:
 7:     public static void main(String[] args) {
 8:         try {
 9:             DirContext ic = new InitialDirContext();
10:             BookRef name = (BookRef)ic.lookup(JNDI);
11:             System.out.println(JNDI+"="+name);
12:         }
13:         catch (NamingException ex) {
14:              System.err.println(ex);
15:              System.exit(1);
16:         }
17:         catch (ClassCastException ex) {
18:              System.err.println(ex);
19:              System.exit(1);
20:         }
21:     }
22: }
						

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.21.12.140