 |
ext:config - flexible JXTA configuration with ease.
Table of Contents
Abstract
The ext:config API is an optional JXTA extension that both eases and extends the core JXTA configuration principals. This is achieved through the combination of a comprehensive API implemented with optimal defaults encompassed with optional declarative models and UI constructs.
The principal ext:config constituents are:
Dependencies
At present, the required ext:config jars are the following:
- JXTA
- 3rd party
- jdom.jar
- jaxen.jar
- jaxen-jdom.jar
- saxpath.jar
note: work is underway to replace the afore mentioned 3rd party jars with the JAXP 1.3 equivalent at which time for JRE 1.5 runtimes will require no extra jars and JRE 1.4 runtimes will require only 1 extra jar.
These jars can be obtained the code below and additionally from the download and bootstrap process referenced in the resources section.
API (aka ext:config)
The ext:config API is the core of the ext:config package with both the profile and ext:config/ui tiers layering above, in succession. The principal API clases are:
- net.jxta.ext.config.AbstractConfigurator
- net.jxta.ext.config.Configurator
A number of supporting classes are also available that aid in the configuration management process.
The primary ext:config class a developer will use is AbstractConfigurator. This class implements the JXTA PlatformConfigurator interface and includes one abstract method:
public abstract PlatformConfig createPlatformConfig(PlatformConfigurator configurator)
throws ConfiguratorException;
AbstractConfigurator implementations are notified via the createPlatformConfig(PlatformConfigurator) during JXTA Platform startup process when configuration data is required either as missing or incomplete. It is at this time applications can inject configuration details into the process.
As such, the following is a complete JXTA configurator minus the requisite application specify configuration details:
import net.jxta.ext.config.AbstractConfigurator;
import net.jxta.exception.ConfiguratorException;
import net.jxta.impl.peergroup.PlatformConfigurator;
import net.jxta.impl.protocol.PlatformConfig;
public class MyConfigurator
extends AbstractConfigurator {
public PlatformConfig createPlatformConfig(PlatformConfigurator pc)
throws ConfiguratorException {
// xxx: application specific configuration logic
return pc.getPlatformConfig();
}
}
In order to override the default JXTA Platform configuration process you register your AbstractConfigurator implementation with the Platform as the configuration delegate:
MyConfigurator.register(MyConfigurator.class);
In order to manipulate the configuration process during each and every startup one can override the updatePlatformConfig(PlatformConfigurator) method:
public PlatformConfig updatePlatformConfig(PlatformConfigurator configurator)
throws ConfiguratorException
The corresponding AbstractConfigurator implementation is thusly notified each time just prior to JXTA Platform start up.
A number of resource management APIs exist which serve to transfer named resources into the resulting JXTA working directory. Resources can be of any form and are accessed via unique names which, in turn, are mapped to file names hosted under the afore mentioned directory. This way, a developer can scope application data to distinct application instances.
Following is a resource management example derived from MyJXTA:
addResource("profile.xml", "/net/jxta/myjxta/resources/profile.xml");
addResource("log4j.xml", "/net/jxta/myjxta/resources/log4j.xml");
With an AbstractConfigurator implementation in hand and registered with the JXTA Platform as the configuration delegate we now turn to the principal configuration class, aptly named Configurator.
The Configurator class serves to programatically create new and update existing PlatformConfig instances. As mentioned above, a Configurator can be managed via the AbstractConfigurator class or used standalone. For runtime environements, use of the AbstractConfigurator is encouraged as much of the file systems mechanics et al are managed by the ext:config framework. On the other hand, Configurator can be used to generate PlatformConfig objects for use elsewhere such as cluster configuration.
The lifecycle of a Configurator is typically quite short. Initially, a few key static constructs are invoked in order to establish a baseline Configurator instance. Firstly, a default PlatformConfig object is processed followed by the protected Profile.SEED profile. Lastly, the default JXTA_HOME is set to the .jxta directory hosted in the user's home directory, programmactically as follows:
setHome(new File(new File(System.getProperty("user.home")), ".jxta"));
note: Setting the JXTA_HOME via a static method has proven problematic and as such is currently under review. A likely refactor may include the JXTA_HOME as a constructor argument.
Upon instantiation, the Configurator object looks for a PlatformConfig in the specified JXTA_HOME and if found is processed to establish the relevant instance state. If a PlatformConfig is not found then a Profile by the name profile.xml hosted in the JXTA_HOME directory will be sought out and if found used to specify the relevant instance state. Failing to find
either of the PlatformConfig and profile.xml files in the specified JXTA_HOME the default profile Profile.DEFAULT (which is effectively Profile.EDGE) will be used to establish the instance state.
Lastly, the provided constructor arguments are accessed accordingly. None of the Configurator constructors throw exceptions so creating a new instance is relatively ensured. The principal constructors are as follows:
- Configurator(String name, String password)
- Configurator(Profile)
- Configurator(PlatformConfig)
note: Deprecated constructors are not included. In addition, a JXTA_HOME constructor may be added at a future time in order to address the static concern noted above.
The sole objective of the Configurator is to construct a viable PlatformConfig object. To this end there are two primary PlatformConfig generation methods:
- public PlatformConfig getPlatformConfig() throws ConfiguratorException
- public boolean save() throws ConfiguratorException
- public boolean save(File pc) ConfiguratorException
note: The save() method simply invokes save(new File(getHome(), "PlatformConfig"))
The ConfiguratorException objects are chained such that all causes are retrievable via respective iterators.
At this point war are now armed with the ability to construct a trivial configurator:
import net.jxta.ext.config.Configurator;
import net.jxta.exception.ConfiguratorException;
import java.util.Iterator;
...
Configurator c = new Configurator("usr", "pwd");
try {
c.save();
} catch (ConfiguratorException ce) {
for (Iterator c = ce.getCauses().iterator(); c.hasNext(); ) {
Throwable t = (Throwable)c.next();
System.out.println(t.getMesasge());
t.printStackTrace(System.out);
}
}
There you have it. Effectively JXTA configuration accomplished in what amounts to 4 lines of code. Alas, true life is typically not so simple. as such we next turn to the number of Configurator getter/setter methods that provide for ultimate JXTA configuration tuning. Before doing so, let's dive down into the PlatformConfig processing internals a bit.
The save methods actually invoke the getPlatformConfig() method. As such, the only real function of the save() methods are to persist a PlatformConfig, as XML, to the relevant file system. With that, our attention turns back to the getPlatformConfig() method. In short, the following steps are executed in order resulting in either a valid PlatformConfig object or a causal ConfiguratorException exception:
- normalization
- optimization
- validation
- PlatformConfig construction
Taking each of these in turn we start with normalization. Normalization starts out by filling in any blank entries with the relvant defaults. No exceptions or faults are generated during this process. wip: describe address macro expansion
Next up, the optimizer kicks in by iterating the current and recently normalized Configurator state through all registered Optimizers in succession. wip: detail Optimizer interface, registration
Following the resultant Configurator state is exercised against a series of internal heuristics that check for PlatformConfig viability. This process, alone, can throw a ConfiguratorException.
Upon passing the validity stage all that remains is PlatformConfig creation whereby the Configurator state is transferred into a representative PlatformConfig instance.
At this point we should step back and inventory the principal getter/setter methods that are available prior to PlatformConfig generation. Configurator hosts a significantly large number of prototypical getter/setter methods that can be logically grouped as follows:
- attributes (eg name, description, id, debug)
- security (user, password)
- services
- RendeZvous
- Relay
- Proxy
- network
- TCP
- HTTP
- others as implemented
- endpoint
- miscellaneous
All of the above are invokable but typical use case calls for only focussing on the relevant domains after instantiating the Configurator with the relevant context (see Profile).
wip: overview the above
Attributes ...
Security ...
Services ...
Network ...
Endpoint ...
Miscellaneous ...
Profile (aka ext:config/profile)
The ext:config/profile tier adds a declarative model to the afore mentioned API. The principal ext:config/profile class is:
- net.jxta.ext.config.Profile
The Profile class includes a number of profile definitions, including:
| name | description |
| Profile.EDGE | prototypical client |
| Profile.EDGE_TCP | |
| Profile.EDGE_HTTP | |
| Profile.SUPER | provisions rendezvous and relay services |
| Profile.SUPER_TCP | |
| Profile.SUPER_HTTP | |
| Profile.RENDEZVOUS | provisions rendezvous services |
| Profile.RENDEZVOUS_TCP | |
| Profile.RENDEZVOUS_HTTP | |
| Profile.RELAY | provisions relay service |
| Profile.RELAY_TCP | |
| Profile.RELAY_HTTP | |
| Profile.LOCAL | loopback; optimal for standalone development or demos |
For added flexibility, profiles can also be instantiated with either an URL or InputStream parameter the content of which conforms to the profile schema definition which can be found in both the Profile javadoc and net.jxta.ext.config.resources.profile.xsd file. This approach eases deployments by centrally managing application configuration meta data which is then accessed at application runtime.
Following is the prototypical edge profile which can serve as the basis for creating your specialized Profile:
<!DOCTYPE org.jxta:configuration>
<jxta>
<peer descriptor="edge"/>
<network>
<rendezVous>
<address>//:</address>
</rendezVous>
<relays>
<address>//:</address>
</relays>
</network>
<transport>
<tcp>
<address range="100">
<multicast>udp://224.0.1.85:1234</multicast>
</address>
<publicAddress exclusive="false"/>
</tcp>
<http>
<address range="100"/>
<publicAddress exclusive="false"/>
<proxy enabled="false"/>
</http>
</transport>
<service>
<relay>
<outgoing enabled="true"/>
</relay>
</service>
</jxta>
Assuming the above profile was copied to a file, say /tmp/myprofile.xml, and modified as needed one could instantiate the resulting specialized Profile as follows:
import net.jxta.ext.config.Profile;
import net.jxta.ext.config.ResourceNotFoundException;
import java.net.URL;
import java.net.MalformedURLException;
...
Profile p = null;
try {
p = new Profile(new URL("file:///tmp/myprofile.xml"));
} catch (MalformedURLException mue) {
mue.printStackTrace();
} catch (ResourceNotFoundException rnfe) {
rnfe.printStactTrace();
}
The edge profile does not include all possible configuration options as some services are not enabled. Further, defaults, when used, are not duplicated thereby simplifying overall profile usage. Following is a comprehensive breakdown of the principal configuration elements:
| peer | peer description | | | | |
| | descriptor | profile description | | | |
| | home | JXTA_HOME | | | |
| | trace | debug level | | | |
| | security | | | | |
| | proxy | | | | |
| network | subscribed services | | | | |
| | rendezVous | subscribed rendezVous services | | | |
| | | bootstrap | seeding URL | | |
| | | discovery | disallow usage of discovered rendezVous | | |
| | | address(es) | service URI | | |
| | relays | subscribed relays services | | | |
| | | bootstrap | seeding URL | | |
| | | discovery | disallow usage of discovered relays | | |
| | | address(es) | service URI | | |
| transport | peer address(es) | | | | |
| | tcp | TCP endpoint | | | |
| | | address | endpoint URI | | |
| | | range | port range | | |
| | | multicast | muticast URI | | |
| | | | enabled | multicast controller | |
| | | publicAddress | public endpoint URI | | |
| | | | enabled | public address controller | |
| | | proxy | proxy address | | |
| | http | HTTP endpoint | | | |
| | | address | endpoint URI | | |
| | | range | port range | | |
| | | publicAddress | public endpoint URI | | |
| | | proxy | proxy address | | |
| service | provisioned services | | | | |
| | rendezVous | provisioned rendezVous service | | | |
| | | enabled | rendezVous controller | | |
| | | autoStart | rendezVous provisioning policy in milliseconds | | |
| | | | enabled | autoStart controller | |
| | relay | provisioned relay service | | | |
| | | enabled | relay controller | | |
| | | queueSize | relay queue size | | |
| | | incoming | | | |
| | | | enabled | incoming controller | |
| | | | maximum | maximum messages | |
| | | | lease | lifetime in milliseconds | |
| | | outgoing | | | |
| | | | enabled | outgoing controller | |
| | | | maximum | maximum messages | |
| | | | lease | lifetime in milliseconds | |
| | endpoint | | | | |
| | | queueSize | message queue size | | |
| | proxy | | | | |
| | | enabled | proxy controller | | |
| configuration | | | | | |
| | optimizer(s) | configuration optimization | | | |
| | | class | optimizer class name | | |
| | | | property(s) | optimizer attributes | |
| | | | | name | value |
Bringing it all together, one can trivially instantiate a Configurator with a selected Profile instance as a constructor argument as follows:
Configurator c = new Configurator(Profile.EDGE);
The resulting Configurator can readily be manipulated by any of the available APIs. As such, profiles are used to declaratively instantiate an application Configurator via a configuration meta-model. The resulting Configurator instance can, in turn, be adjusted via any of the provided APIs. Using Profiles one can readily experiment with varying models without having to recompile a line of code. Lastly, profiles are used internally by the Configurator class during instantiation to set the relevant defaults.
UI (aka ext:config/ui)
Lastly, the ext:config/ui tier provides an extensible UI above the afore mentioned ext:config/profile tier.
wip ...
Just Code
Following are the steps necessary to compile and run the included functional code samples. The prerequisites are:
- J2SE (1.5 recommended0
- Ant
Obtain a working copy of the ext-config-lab.zip code samples and uncompress it accordingly followed by changing your working directory to that of the top-level jxta directory provided by the distribution. Running Ant with no arguments will display the usage message:
% ant
Buildfile: build.xml
usage:
[echo] % ant [option]
[echo] [exercise number]
[echo] all
[echo] clean
The excercises are contained under the src directory and organized as incremental and progressive code samples. The lib directory includes the necessary JXTA and ext:config libraries:
JXTA jars:
- bcprov-jdk14.jar
- javax.servlet.jar
- jxta.jar
- log4j.jar
- org.mortbay.jetty.jar
ext:config jars:
- jaxen-core.jar
- jaxen-jdom.jar
- jdom.jar
- jxtaext.jar
- saxpath.jar
note: the ext:config jars will likely soon be replaced with the JAXP 1.3 equivalents
To run an excercise simply provide the excercise directory as an Ant argument:
% ant 1
Buildfile: build.xml
http.proxy:
socks.proxy:
prepare:
1:
1.compile:
compile:
1.run:
run:
[java] create PlatformConfig with the following properties:
[java] name = MyPeerName
[java] description = MyPeerDescription
[java] principal = MyPeerPrincipal
[java] password = MyPeerPassWord
[java] saving PlatformConfig to: .../jxta/build/1/home
BUILD SUCCESSFUL
Total time: 9 seconds
Feel free to make changes to the exercises via your editor of choice and recompile and run them via Ant. You can always start over if needed by uncompressing the distribution file.
Have fun! Learn by doing!
Resources
- ext:config API - note: package net.jxta.ext.config
- ext-config-lab.zip
- JXTA Downloads
- JXTA Bootstrap
- JXTA jar dependencies
-- JamesTodd - 01 Feb 2005
|