ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Using PASX
Pages: 1, 2, 3

If the PASXService author chooses to stick with the XML tags defined by PASX and follows the Java Bean patterns for getting and setting Bean properties, then he or she may use a utility class called XMLBeanUtil. This class uses Bean introspection to match and assign properties of a Bean to the XML tags defined by PASX. This can make coding very easy. For example:



public void configure( Element config,
                       Context context, 
                       ServiceManager caller )
{
  xbu = new XMLBeanUtil( context );
  xbu.populate( config, this );
}

If a PASXService component is composed of a smaller, tightly integrated group of classes, the populate method may called multiple times. Consider this property:

<String name="lastName">Bushaw</String>

Given this XML fragment, the populate method could be used multiple times to give the lastName property to two different Beans:

Person father = new Person();
Person mother = new Person();
xbu.populate( config, father );
xbu.populate( config, mother );

When using an XML Schema and custom tags, one benefit is the validation of the expected configuration. Put another way, you can write an XML Schema so that if, for example, a <Host> tag is given, that the portNumber and ipAddress attributes must also be specified. The schema defining the PASX set of tags cannot do this, because it has no knowledge of the semantics needed by individual PASXService components. However, XMLBeanUtil can keep track of which Bean properties were set and which weren't, and it can be configured with a list of constraints about the properties. A PASXService class author may choose either method, and this choice is a matter of taste, need, and convenience.

XMLBeanUtil classifies Bean properties three ways: property, dependent, and compliment. The first classification is just a normal Bean property. The second type is a property that depends on another property (e.g. if you set a dependent property, you must also set the property on which it depends). The third type is a property that is a compliment of another property (e.g. if you do not set a compliment property you must set the property which compliments it). Finally, all three classifications of properties may be set as an aggregate if the Java type of the property is either List or Map. If designated as an aggregate and the property already has a value, the List or Map in the configuration is added to the current property. (If it wasn't designated as an aggregate, then the List or Map in the configuration would replace the property value).

The following configure method example demonstrates this use.

public void configure( Element config,
                       Context context,
                       ServiceManager caller )
{
  xbu = new XMLBeanUtil( context );
  xbu.addProperty( "hostName",
    "Host Name Of Mythical TCP Service", 
    true, false );
  xbu.addDependent( "portNumber",
    "Port Number Of Mythical TCP Service",
    "hostName", false );
  xbu.addCompliment( "serverFarm",
    "Back-end Server Farm", 
    "nameServers", true );
  xbu.addCompliment( "nameServers",
    "Name Servers To Resolve Against",
    "serverFarm", true );
  xbu.addProperty( "person", 
    "Person", true, false );
  xbu.addProperty( "binaryThing",
    "The binary input stream",
    true, false );
  xbu.addProperty( "props",
    "Example Protomatter Properties",
    true, false );
  xbu.addProperty( "xml",
    "An example JDOM XML Document",
    true, false ); 
  xbu.populate( config, this );
}

The description of the property passed to the addProperty, addDependent, and addCompliment methods is for use in error messages. The checkService method of the XMLBeanUtil class will check to see if the correct properties have been set. If they have not, then it will throw an exception and use the descriptions of the properties in the error text. For a complete example, consult the BeanServiceExample class.

Being the observant reader, you probably noticed that nowhere in any of these past examples was there any mention of JNDI. Even the name attribute of the <Service> tag said nothing about JNDI. So you are probably asking yourself how the naming part of PASX uses JNDI.

By default, PASX uses an in-memory flat JNDI service provider called PAS (which is from the underlying Protomatter package). This service provider is nothing more than a Hashtable with a JNDI interface. Through the use of URL naming, other JNDI service providers may be specified and even used together. Therefore, it would be possible to specify a URL from an RMI registry or an LDAP server (i.e. rmi://localhost/creditCardAuthorizer or ldap://foo.com/uid=littlek,dc=foo,dc=com).

The default JNDI action for using a service with JNDI is bind. However, using the optional action attribute, the actions of rebind or lookup may also be used. The bind action attempts to place the service into a JNDI directory, but will fail if the directory already contains an entry with the name specified. The rebind action is the same as the bind action except it will overwrite an existing entry if it already exists. With both the bind and rebind actions, PASX instantiates the PASXService object, but with the lookup action, the object is retrieved from the JNDI directory.

There are two other attributes on the <Service> tag that may be used: preRebind and postRebind. The preRebind attribute specifies a name, or URL, to be used to rebind the object into a JNDI directory before configuration has taken place. Likewise, the postRebind attribute does the same, except it occurs after the configuration step.

The combination of these features allows the configuration file to do some useful and powerful things. For instance, it is possible to deserialize an object from an LDAP server, configure it via XML, and then place it in-memory so it may be accessed by other objects. Consider an example where the expiration date for some business process is sitting in an LDAP server. The following markup pulls the expiration date from the LDAP server, configures it for the current time zone, and places it in memory:

<Service
  name="ldap://foo.com/cn=expirationDate,o=foo.com"
  class="my.UtilityDate"
  action="lookup"
  postRebind="pas:expirationDate">
  <Integer name="timeZone" value="-6"/>
</Service>

It is possible now for another service to reference this configured service using the <NamedService> tag. For example, consider a sales advertising service that needs to know when a sale has expired:

<Service
  name="pas:oriellySale"
  class="my.SalesAdBanner">
  <NamedService
    name="endDate"
    serviceName="pas:expirationDate"/>
</Service>

There is no magic here. The <NamedService> tag merely causes PASX to do a JNDI lookup, based on the serviceName attribute, and places the result in the Bean property named endDate. However, this manner of connecting one object to another at configuration time is very powerful. Once you get use to doing, it will probably change your class design style.

PASX contains an assortment of utilities for use with JNDI. These include state and object factories, for use in serializing and storing object state in directories, and two service providers, one of which is a tree-based, hierarchical namespace and the other which utilizes the Java classpath. Consult the JavaDoc for more information on them.

There is also an entire package of examples complete with source code, JavaDoc, and configuration files that accompanies PASX. These examples start with simple configuration examples and range all the way up to the mixed use of various JNDI directories. Just consult the org.pasx.examples JavaDoc.

Hopefully the advantages of the concepts presented here have convinced you that, at the very least, you need to consider your configuration needs and methodology the next time you sit down to write a program of any significant size. And while these concepts are a central theme, there are also other features in PASX which have not been presented here. PASX has many standard services and managers for accomplishing many tasks, and even a servlet framework from which to do management of services and managers.

Finally, there is the Potomac project. Potomac is a PASX-based collection of open source Java software. With Potomac, all you need to do is unzip or untar one file, and you are provided with all the necessary Java components for using PASX, such as Protomatter and JDOM, and some convenient startup scripts and configuration files for getting started.

Andrew Newton is an expert open source Java and XML developer.


Return to ONJava.com.