The Artima Developer Community
Sponsored Link

The Journal of Spontaneous Networking
Using Objects to Configure Jini Network Services
by John McClain
June 29, 2004

<<  Page 2 of 4  >>

Advertisement

Using Configuration

The Configuration class serves as a medium of conversation between a Jini service developer and the person deploying, or using, that software. The developer delegates certain aspects of the service to the deployer. The deployer then decides what values those objects should assume to achieve the goals of a particular deployment situation. The Configuration implementation provides the deployer with a set of tools to construct those objects.

That division of responsibilities is a good fit for an object-oriented (OO) language such as Java. The developer can use the Java type system to model the contract needed from each object, while the deployer gets to use the polymorphism of the Java programming language to construct instances that both implement the contract and meets the needs of a given deployment.

Configuration objects are created at runtime by an instance of the net.jini.config.Configuration interface from configuration entries. A developer defines the requirements each configuration entry must meet. A Configuration instance is then responsible for evaluating configuration entries to yield the objects that are �plugged into� the runtime Jini system. The deployer chooses what Configuration implementation to use, and defines the configuration entries. The config model, therefore, gives the deployer control over what object each configuration entry will yield.

The default implementation of Configuration is ConfigurationFile. That class allows for the construction of arbitrary Java objects based on a text file interpreted at runtime and defined in a Java-based language. The following section explains that Java-based configuration language, and provides examples of its use.

The following code snippet illustrates configuration-specific additions to a Jini service's code:


package org.jini.user.jmcclain.myservice;


     import net.jini.config.Configuration;
     import net.jini.config.ConfigurationProvider;
     import net.jini.config.ConfigurationException;
     import net.jini.config.NoSuchEntryException;
     ....

     class MyServiceImpl {
        private MyServiceImpl(String[] argv) throws  ConfigurationException {
           ....
           Configuration config =
               ConfigurationProvider.getInstance(argv,
                                                  getClass().getClassLoader());
           ....

A client would require similar code. Utilities that rely on a configuration object generally consume those configuration objects in their constructors.

The ConfigurationProvider class provides static methods that can be used to create a configuration. By delegating to ConfigurationProvider, we allow the deployer to use whatever configuration implementation he desires. By default ConfigurationFile will be used, but using Java's resource mechanism, deployers can arrange for another implementation to be used.

The argv String array is passed into ConfigurationProvider. How that array is parsed will vary between Configuration implementations. ConfigurationFile treats the first element as a URL to look for a ConfigurationFile source file. Any additional elements in the array override the contents of the source file. This example assumes that all of the program's command-line arguments are used to obtain the configuration. ConfigurationFile's override feature allows for simple values to be provided on the command line without having to edit the configuration source.

Configuration Entries

Conceptually, a Configuration instance is composed of configuration entries. When a program needs an object to be provided by the deployer, that program asks Configuration to evaluate one of those entries to obtain an object. Evaluating a configuration entry twice may or may not yield the same object. A service's developer must document what configuration entries her service uses, when those entries are evaluated, the requirements of those entry objects, and how those objects are used.

Configuration entries are identified by two strings: one denoting the component and one the name. The config model expects that every configuration entry used by a given software module (e.g. a client, service, or utility) shares the same component string, but has a distinct, hopefully descriptive, name string. In effect, the component string forms a name space and prevents collisions if two modules happen to use the same name. In Sun's contributed implementation of the JSK, the package name serves as the component string for clients and services, and the class name as the component string for utilities.

Once we have a Configuration, we can retrieve entries from it. Suppose MyService has a daemon thread that cleans up internal data structures, and we want to give the deployer control over that thread's priority. The following code snippet illustrates how a deployer might determine that daemon thread's priority:


....

       int reapingPriority = ((Integer)config.getEntry(
            "org.jini.user.jmcclain.myservice",   // component
            "reaperPriority",                     // name
             int.class,                            // type
             new Integer(Thread.NORM_PRIORITY))    // default value
            ).intValue();

           if ((reapingPriority < Thread.MIN_PRIORITY) ||
               (reapingPriority > Thread.MAX_PRIORITY))
           {
               throw new ConfigurationException("entry for component " +
                   "org.jini.user.jmcclain.myservice, name  reaperPriority " +
                   "must be between " + Thread.MIN_PRIORITY + " and " +
                   Thread.MAX_PRIORITY + ", has a value of " +
                   reapingPriority);
           }

           reaperThread = new ReaperThread();
           reaperThread.setPriority(reapingPriority);
           reaperThread.setDaemon(true);
           reaperThread.start();

           ....

The above example illustrates some important configuration principles:

A ConfigurationFile source file might look as follows:


   org.jini.user.jmcclain.myservice {
      reaperPriority = 6;
   }

If this file were named myservice.config, the command line might look like this:

   java -Djava.rmi.server.codebase=http://recycle/myservice-dl.jar \
        -jar myservice.jar                                         \
        myservice.config

In this case, the reaping thread would have a priority of 6. Because we provided a default when fetching reaperPriority, we could use an empty file:


   java -Djava.rmi.server.codebase=http://recycle/myservice-dl.jar \
        -jar myservice.jar                                         \
        empty.config

or no file:


   java -Djava.rmi.server.codebase=http://recycle/myservice-dl.jar \
        -jar myservice.jar

and still have a running service. In both cases, the reaping thread would have a priority of Thread.NORM_PRIORITY. We could also use a command-line override to obtain the effect of using myservice.config without specifying a file on the command line:


   java -Djava.rmi.server.codebase=http://recycle/myservice-dl.jar \
        -jar myservice.jar                                         \
        - org.jini.user.jmcclain.myservice.reaperPriority=6

Passing '-' as the first command-line argument to MyService indicates that there is no ConfigurationFile source file. Replacing '-' with empty.config would have a similar effect.

Finally we could use myservice.config but use an override to give the reaping thread some value besides 6:


   java -Djava.rmi.server.codebase=http://recycle/myservice-dl.jar \
        -jar myservice.jar                                         \
        myservice.config  org.jini.user.jmcclain.myservice.reaperPriority=4

In this case the reaping thread would have a priority of 4.

<<  Page 2 of 4  >>


Sponsored Links



Google
  Web Artima.com   
Copyright © 1996-2014 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us