I often hear from .NET programmers “I’d like to get into Java, not the language, but all that J2EE stuff …”. I am one of those people so I try to use any opportunity to try something I’ve never touched before.
We’re moving to a SOA model with the product at my day job. One of the fundamental questions is: “How does a service find another service?”. The standard answer is to use a naming and directory service and in Java you talk to one of these things with JNDI.
First, a few basics.
- Naming service is a fundamental facility in any computing system. It’s the means by which names are associated with objects and objects are found based on their names. For example, to access a file on the computer you must provide its name.
- Directory Service is an extension of the naming services. A directory service associates names with objects and also allows such objects to have attributes. Thus, you not only can look up an object by its name but also get the object’s attributes or search for the object based on its attributes.
By using a directory service, you can simplify applications and their administration by centralizing the storage of shared information. For our purposes such information includes SOAP service URIs. For example, you can find demoService (a previously agreed-upon name of the demo service) at http://localhost:20080/demo.
Client & Server
I picked up OpenDS, on open-source server from Sun. After a straightforward installation (set OPENDS_JAVA_HOME to a JRE location and run setup.bat) I had an LDAP server running as a Windows Service (OpenDS) on port 389. There’s a handy bat\control-panel.bat that launches a schema and object browser.
We can now access this server with JNDI, which comes standard with Java Platform 1.1.2 or later.
This outputs the attributes of my initial domain context that was created at setup time.
dc: appsecinc objectClass: domain, top
Let’s create a directory for our SOAP services. The goal is to be able to store a collection of service objects, each containing a well-defined URL and retrieve service URLs using the service names.
Extending the Schema
The OpenDS schema is stored in .ldif files in the config\schema directory. The directory schema can be extended by importing LDIF files, modifying those in the schema directory or programmatically with JNDI. I’ll write an import an .ldif file.
There’re several RFCs with various well-known attribute types, such as name or uid. We’re only missing serviceUri, which we can define as a custom attribute.
attributeTypes: ( 1.2.840.113556.1.8000.2554.999999.1 NAME 'serviceUri' DESC 'service URI' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 188.8.131.52.4.1.14184.108.40.206.15 )
Our Service type is defined as follows: an object with a common name (cn), a humanly readable name (name), an object class (Service), a unique identifier (uid) and an url to the service (serviceUri).
objectClasses: ( 1.2.840.113556.1.8000.2554.999999.2 NAME 'Service' DESC 'a SOAP Service' SUP top STRUCTURAL MUST ( cn $ name $ objectClass $ uid $ serviceUri ) )
You can generate a root OID using this script (save the script to disk and run
cscript script.vbs) and keep adding numbers to it. It’s just a globally unique number that identifies an attribute or a class. I generated OID 1.2.840.113556.1.8000.2554.999999.
Service in Java
Let’s define a Service Java class that can be used to read and write objects to the directory.
This is a simple container for attributes. The
UnimplementedDirContext is an empty class that throws NotImplementedException on two dozen methods that are required by a full
A Services Organization
We’d like to organize services under a Services organization. I’ve created that manually in the directory. The full directory path to the OU is
Writing to the Directory
A write is a call to
bind. Binding means connecting a name to an object. You can
rebind, ie. either create or update an existing object.
Here’s what we have in the directory now (this is the Manage Entries UI from the control panel tool that comes with OpenDS).
Retrieving from the Directory
In order to retrieve a strongly typed object from the directory we must supply an object factory. When the factory encounters an object with an
objectClass=Service, it will create an instance of such.
The initial directory context must be told to use this factory.
Finally, the retrieval becomes a simple lookup.
Deleting Directory Objects
To complete the picture, let’s delete a directory object. This is the opposite of
A Word on XML
All this requires server-side Java code and keeping the LDAP port 389 open.
Alternatively, OpenDS provides an implementation of Directory Services Markup Language (DSML), an XML API to directory services. It’s then possible to switch JNDI client code from LDAP to DSML using a Sun early access JNDI client for DSML.