JNDI integration

This page describes how JEZXmlConfig is integrated with JNDI.

JEZXmlConfig JNDI Service Provider

JEZXmlConfig have its own JNDI service provider accessible from net.sourceforge.jezxmlconfig.jndi.spi.XmlConfigInitCtxFactory class.
This is a simple hierarchical namespace implementation (non federated). This means that objects that want to access configuration accross JEZXmlConfig JNDI service provider must be present on the same machine...
In a future release, this JNDI service provider features will be enhanced. Furthermore, an LDAP client will be implemented to retreive configuration from an LDAP server.

This implementation uses the "java.naming.provider.url" system property value to retreive its repository root directory.
This property must contain an URI in the format "jezxmlconfig:/path" where path is the root directory path. For example, under Windows you can use "jezxmlconfig:/C:/DATA/MyConfRootDir", and under Unix "jezxmlconfig:/DATA/MyConfRootDir".
Under this directory available configurations are stored in text files (XML format), and organized in subdirectories.

File names (with absolute path from service provider root directory) correspond to JNDI names.
For example, if the servive provider root directory is "/DATA/myConfRootDir" and a configuration is bound to JNDI name "subdir/foo.xml", it will be stored in "/DATA/myConfRootDir/subdir/foo.xml" file.

Following diagram describes this example of JEZXmlConfig JNDI service provider repository structure:

Note: For security reasons, it is impossible to browse path upper than the service provider root directory.

Client JNDI Access

A program that uses JEZXmlConfig must define the "java.naming.provider.url" system properties to enable JEZXmlConfig JNDI service provider to manage its repository.

It can be done by adding the following option on the command line:

java -Djava.naming.provider.url=jezxmlconfig:/DATA/myConfDir my.package.MyMainClass ...

To get a configuration, you just have to call the right method (instead of getInstance()):

    XmlConfig config;
    try {
        config = XmlConfig.jndiLookup("foo.xml");
    }
    catch(XmlConfigException xce) {
        throw MyApplicationException("Cannot access 'foo.xml' configuration: " + xce.getMessage());
    }
    ...
Notes:
  1. The jndiLookup(jndiName) method loads configuration and keep it as "jndiName" configuration set.
    This means that after this call you can call getInstance(jndiName) to obtain the same configuration instance if you depend on the same class loader.
    Warning: This is discouraged in a J2EE environment because an EJB could depend on an other "class loader", and then the getInstance() call is likely not to function. In this case, always call jndiLookup(jndiName) method.
  2. If you call jndiLookup(jndiName) a second time, the retreived configuration will overwrite the previous one in "jndiName" configuration set.
    If you just want to obtain the previously loaded configuration, and you are sure to depend on the same class loader, please use getInstance(jndiName) method.

It's also possible to list all available configurations:

    List configList; // Will contain available configuration JNDI names (String objects).
    try {
        configList = XmlConfig.jndiList();
    }
    catch(XmlConfigException xce) {
        throw MyApplicationException("Cannot access configuration list: " + xce.getMessage());
    }
    ...

Low level JNDI Access

If a program wants to directly communicate with JNDI at a low level, he must specify the JEZXmlConfig factory for JNDI initial context.

It could be done by two ways:

  1. By adding options "-Djava.naming.factory.initial=net.sourceforge.jezxmlconfig.jndi.spi.XmlConfigInitCtxFactory"
    and "-Djava.naming.provider.url=jezxmlconfig:/DATA/myConfRootDir" on command line, and simply getting JNDI initial context:
        Context ctx;
        try {
            ctx = new InitialContext();
        }
        catch(NamingException ne) {
            throw MyApplicationException("Cannot get JNDI initial context (" + ne.getClass().getName() +
                                         "): " + ne.getMessage());
        }   
  2. By specifying properties when getting JNDI initial context:

        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "net.sourceforge.jezxmlconfig.jndi.spi.XmlConfigInitCtxFactory");
        env.put(Context.PROVIDER_URL, "jezxmlconfig:/DATA/myConfRootDir");
        Context ctx;
        try {
            ctx = new InitialContext(env);
        }
        catch(NamingException ne) {
            throw MyApplicationException("Cannot get JNDI initial context (" + ne.getClass().getName() +
                                         "): " + ne.getMessage());
        }   

Then, he can make any JNDI operation on retreived context.

For example:

    XmlConfig config;
    try {
        config = (XmlConfig)ctx.lookup("foo.xml");
    }
    catch(NamingException ne) {
        throw MyApplicationException("Cannot access 'foo.xml' configuration (" +
                                     ne.getClass().getName() + "): " + ne.getMessage());
    }

In JNDI, each context corresponds to a subdirectory under servive provider root directory.

You can find below a table that describes actions made by JNDI methods on a particular context.

JNDI method Effect
lookup Retreive a configuration (XmlConfig object) or a subdirectory (java.naming.Context object).
lookupLink Same as lookup (symbolic links are not supported).
bind Put a new configuration in JEZXmlConfig JNDI service provider repository.
rebind Replace a configuration.
unbind Delete a configuration.
rename Rename or move a configuration.
Note: if you want to move configuration, you must ensure that all subdirectories of JEZXmlConfig JNDI service provider repository must be in same filesystem.
list List a context: i.e. List objects (configurations) and subcontexts (subdirectories) names and classes under the current context.
listBindings Same as list but also retreive objects.
createSubcontext Create a subcontext in current context: i.e. Create a subdirectory.
destroySubcontext Destroy a subcontext in current context: i.e. Delete a subdirectory.
getNameInNamespace Get absolute file path corresponding to current context. For example, if current context is "subdir" under root, return value will be "C:\DATA\myConfRootDir\subdir" under Windows, and "/DATA/myConfRootDir/subdir" under Unix.