Developers Guide


Plug-in mechanisms
SPI plug-ins
Naming a class in services metadata
Architecture
Details on SPI-based plug-ins
AnalysisInventory
MetadataAccessorFactory
EventListener and EventDeserializer
PersistentStorage
Details on classes-based plug-ins
Job
Input/Result
Parameter
JUnit testing

Daily bread of every developer is an API. Soaplab2 API, including those classes and interfaces that are supposed to be extended, enhanced or re-implemented, are generated here.

Here, and on several other places, this document refers to the Soaplab2 API. If you are browsing your local copy of the Soaplab2 documentation, and the API links are broken, you need to generate the API first by calling an Ant task docs:
ant docs
But browsing the Soaplab2 source code is usually inevitable (e.g. the generated API does not include protected methods that could be quite helpful when sub-classing).

Many features in Soaplab2 are configurable by Java properties. When developing, you should be aware of the singleton class Config that provides access to all Soaplab2 properties and also defines their names. Because Soaplab2 configuration is equally important for Soaplab2 service providers, the Configuration Guide with all details is included in their documentation section.

The Soaplab2 design is driven by the idea of being flexible and extensible. Therefore, the core of the Soaplab2 architecture are the plug-in mechanisms:

Plug-in mechanisms

A plug-in mechanism (in contrast to a regular sub-classing) adds specific behaviour to the system without a need to change and recompile the existing classes. The Soaplab2 uses two such mechanisms:
SPI-based plug-ins
Naming a class in services metadata

SPI (Service Provider Interface) plug-ins

Java (starting from version 1.3) defines rules how to store and find various implementations of the same interface. It is part of the Jar File Specification, chapter Service Provider. A usual, almost reference, implementation of this Java part, also used by Soaplab2, is the Apache Commons Service Discovery library.

The Soaplab2 uses this mechanism to add behaviour shared by all Soaplab2 services (in contrast to the second mechanism, see below, that allows to add specific behaviour per service base). Here is a list of the available SPI plug-in hooks (described below in details):

The usage of the SPI plug-ins is straightforward:

  1. The directory src/etc/config/services/META-INF/services contains a file for each plug-in, named by the same name as an interface that the plug-in implements. For example, file org.soaplab.services.storage.PersistentStorage is for the plug-in dealing with storing results.

    Write your own implementation of a selected interface and put the name of such class into this file (each class name on a separate line). For example, the persistent storage file may have this content:

    org.soaplab.services.storage.FileStorage
    org.soaplab.services.storage.DBStorage
           
    Which means that there are two mechanisms how and where to store Soaplab2 results: one is a local file system (implemented by the class org.soaplab.services.storage.FileStorage) and the second one stores results in a database (implemented by the class org.soaplab.services.storage.DBStorage).

  2. Run the Soaplab2's Ant task config (or the default compile - which includes the config, as well):
    ant config
           
    It creates a jar file services-config.jar in the build/lib directory. When this jar file - and, of course, your own implementation class - are found on the CLASSPATH, your plug-in starts to work.

    There are few properties that can be set to influence this Ant task (but usually there is no need to change them):

    services.config.dir
    contains a directory name with plug-in files (default value is <soaplab-home>/src/etc/config/services/META-INF/services)
    services.config.jar
    names the resulting jar file (default value is services-config.jar)
    released.date
    contains a date that is put in the created jar file as a MANIFEST property Released-Date (default value is the today's date).

Naming a class in services metadata

Another plug-in mechanism is designed for individual services. Each service has its own metadata and they can contain class names (or, in some cases, a convenient synonyms of class names). In some point, the Soaplab2 makes instances of these classes and they serve particular purposes for this particular service.

There are the following uses for this kind of plug-in mechanism (again, the details below):

So far, we described how to name a class in service metadata. But there is also one other place where to name a class: a place that is dictated by the used protocol.

All web applications (and the Web Services are just kinds of web applications) are "servlets" and they require to specify what class should be instantiated when a request for a particular web application comes to a servlet container (such as the Apache Tomcat server). Different Java toolkits for Web Services require to specify it in different places. For example, the JAX-WS toolkit uses configuration files web.xml and sun-jaxws.xml. Or, the old Axis 1 needs it in server-config.wsdd file.

In Soaplab2, creating or changing these files is part of the service deployment, and therefore, it is described in the documentation for service providers. Few details are also mentioned below, in the section What to do in order to add a new protocol.

Architecture

The architecture image shows the major parts of the Soaplab2 infrastructure, and the colours indicate where and how it can be extended.

The yellow boxes are places where the SPI mechanism looks for plug-ins. (There is one more SPI place that is not shown here - because of clarity - the EventDeserializer interface.)

The khaki boxes show implementation classes discovered by the SPI mechanism. These are, therefore, the real plug-ins. The picture shows only those that are distributed as a part of the Soaplab2 package - but they may be others, from other projects (after all, that's why it is called a plug-in mechanism, isn't it).

The cyan boxes are the core of extending by "naming a class". The focus points here are interfaces AnalysisService and AnalysisListService - they represents what the end-users (Soaplab2 clients) really see, and what functionality they may expect from Soaplab2. They are instantiated by the "Protocol layer": depending on the protocol used, they can handle different Web Service protocols.

The Job interface is the place where the major work happens. For example, there is a class (inheriting from the AbstractJob) representing command-line tools (aka AppLab), and another class representing not-that-old Gowlab.

Architecture guided tour

The requests from clients come using a particular protocol. Because Soaplab2 was designed to be primarily a Web service, the usual protocol is SOAP/HTTP. But even this protocol can be supported by various Java toolkits, such as JAX-WS or Apache Axis. Also, other protocols can be added without changing the rest of the picture (HTML REST jumps into mind first). There are few things dependent on the chosen protocol (e.g. how to find under which name a service was invoked, what is the service name that the client used) - those are accessible through the org.soaplab.services.protocol.ProtocolToolkit interface. Therefore, each Soaplab2 service is represented by a class implementing the org.soaplab.share.Analysis interface and the org.soaplab.services.protocol.ProtocolToolkit interface.

What to do in order to add a new protocol?

First, you need a class that understands how to interpret (read, un-marshal) incoming requests, and how to create (write, marshal) results, both complying with the chosen protocol. This class also needs to provide all functionality defined in the org.soaplab.share.Analysis interface.

The usual way to achieve this is to sub-class the class org.soaplab.services.AnalysisService. An example of that can be found in org.soaplab.services.protocol.jaxws.AnalysisService, a class used for the JAX-WS toolkit. Another example (using rather delegation pattern than a direct inheritance) is in org.soaplab.services.protocol.axis1.AnalysisService.

Second, you need to deploy your new class (or classes) in order to be recognised by your Web server. Which usually mean to specify your class name in some, protocol/toolkit specific configuration file. Soaplab2 provides support for JAX-WS and, for backward compatibility with Soaplab1, the old Apache Axis. Both protocols have own Ant task that does the deployment:

ant jaxdeploy
ant axis1deploy
More details about deployment is in the section for service providers.

What is actually available to the clients?

Soaplab2 core functionality can be expressed by two interfaces: the Analysis interface and the AnalysisList interface. The former one can be also extended by type-safe interface of so-called derived services (not yet implemented, not shown in the picture).

The AnalysisList interface is implemented by the AnalysisListService class (or by its sub-classes, depending on which protocol is used) which uses an AnalysisInventoryProvider to get a list of provided services. The inventory provider relies on the SPI mechanism to get one or more lists (it merges them together). The default provider reads the lists of available services from XML files. An explanation how to add your own list, possibly in another format, is below.

The AnalysisService class uses the same lists each time a service request comes in order to check whether such service is still available. It allows to add or remove new services on-the-fly, without restarting your servlet engine. Just by editing the XML file with a list of available services makes it happen.

Well, this is correct for the JAX-WS implementation (which is supposed to be the most usual one). For Axis-1 protocol, however, it is not enough. A full deployment Ant's task must be run every time you add a new service.
Service metadata

Each service needs metadata. It accesses them using the MetadataAccessor interface. A default implementation of this interface is the class org.soaplab.services.metadata.MetadataAccessorXML that reads it, as the name suggests, from the XML files. Instances of this class are created by the factory, and this factory can be replaced by the SPI plug-in. Details how to make a service reading its metadata from a different source are below.

Jobs - The main raison d'etre of each service

When a service gets a request to really do something (like starting a command-line analysis, fetching a web page, or submitting a task to a grid infrastructure) it asks JobManager to create an instance of a Job. Some related requests share, however, the same Job instance (typically, for example, one invocation of a command-line analysis is one Job, but it can contain more requests, such as starting the job, asking for its status, and requesting its results).

For each job, the job manager creates a Reporter, an inter-mediator between jobs and their results and states, and an EventManager, another inter-mediator connecting job events with their listeners.

The ultimate worker, however, is the Job. Each service can have its own class implementing the Job interface, but usually a whole group of services use the same Job class. The name of this class is taken from service metadata, or from service list metadata (it is called there a module - which should not be confused with service category that rather classifies services semantically).

Soaplab2 provides a convenient parent for Jobs, an AbstractJob, or - for jobs dealing with command-line tools - a CmdLineJob. These classes do most of the job, except the run method. They should be sub-classed whenever implementing a new Job.

Once again: writing a Job implementation is the most powerful way how to extend Soaplab2. All the SPI plug-ins and other classes are here to allow tweaking but new Jobs really do new things.

And the rest...

Remaining pieces of Soaplab2 infrastructure take care about storing persistently analysis results (see below the SPI PersistentStorage plug-in) and about propagating job events to interested listeners (see below the SPI EventListener plug-in).


Details on SPI-based plug-ins

AnalysisInventory

The AnalysisInventory interface allows to add new lists of available services.

It is not, however, needed for cases when a list of services is split across more than one file - such case can be already handled by the org.soaplab.services.DefaultAnalysisInventory class. This class reads list of available analyses (services) from an XML file whose name is given in a property applist (see more about configuration properties in the Configuration Guide). The applist property, can actually contain more than one file name (or, the same property can be used several times, with several different file names).

Therefore, use this SPI plug-in, and create your own class implementing the org.soaplab.services.AnalysisInventory interface, when you have a list of services in a different format, or if you wish to keep it in a different kind of storage (such as a database).

The AnalysisInventory interface allows to identify each list by adding a (reasonably) unique identifier to it, called a list authority. If not empty, the list authority becomes part of the service name.

Standard format of an analysis list

The class org.soaplab.services.DefaultAnalysisInventory understands lists in XML format that follow the SoaplabList.dtd where:

category
Is a group of semantically related services.

app
Is a service (an analysis) that belongs to a category.
To Be Done: I still need to find how to express that a service can belong to more than one category.
name
A name (unique within category) of this service/analysis.

xml
A name of an XML file with service metadata. More about service metadata elsewhere.
To Be Done: I still need to find how to express when service metadata are not in a file but, for example, in a relation database.
module
Is a class name that represents a Job for this service. More about Jobs here. Or it is a synonym for a class name. For example, all services fetching web pages have module gowlab, and all services starting command-line analyses have module sowa (which, by the way, means, historically, "SOaplab Without Applab").

If it is missing, the module attribute is looked for in the individual service metadata. If neither there can be found, the default synonym sowa is used.

desc
Is a short, often just one-line, description of this service. The more detailed description can be put in the individual service metadata.

An example of such list is in <soaplab-home>/metadata/DefaultApplications.xml.

MetadataAccessorFactory

Again, as have been said above: Each service needs metadata. When you are developing a Job class you can access metadata through MetadataAccessor interface. Description what you can get from it, however, requires a separate guide (not yet written). This SPI plug-in is more about how to allow each service to read its metadata in its own format and from its own resource.

The SPI plug-in mechanism is usually used to bring together more classes implementing the given interface. In this case, however, only one class is used (and if there are more found only the first one is used). This class is a factory, implementing interface MetadataAccessorFactory with only one method - getInstance(String serviceName) - that returns a metadata accessor for the given service.

The default implementation is class org.soaplab.services.metadata.DefaultMetadataAccessorFactory that finds service metadata in the service list (see the xml attribute in the standard list format), or in the configuration properties "metadata.dir" and/or ".metadata.file" (see details in Configuration guide). In fact, this class just delegates the task to the MetadataAccessorXML but that is only an implementation detail.

EventListener and EventDeserializer

A running job changes its state and the user may be interested in being notified about these changes. A prominent change in the job lifecycle is, of course, when the job terminates (either by an error or by a user request or by its own nature).

There are two mechanisms how to deliver such notifications: client-pull or server-push. They differ in a way how the events are being exchanged but both share the same format of the events themselves.

From the Soaplab2 infrastructure perspective, both delivery and - up to certain extent - format of events are hidden in individual event listeners. An Event Listener is a class implementing the EventListener interface. Any number of such listeners can be added using the SPI plug-in mechanism. (There are, for example, plans to have a listener in the Soaplab2 GUI - called Dashboard - that will display events coming from all jobs as HTML pages.)

If you implement your own event listener you decide which events to listen to. The interface assumes several default event types (of course, you are free to ignore any of them) - but they can be extended. If you do so, you also need to write a class that can convert these new events from their XML representation back to their Java instances. Such deserializer is added to the system by another SPI plug-in point - an interface EventDeserializer (this plug-in is not shown in the Architecture picture above). Where is then event serializer? Well, it is part of the EventListener interface - see there method toXML().

Existing event types

The picture is taken from the standard Life Sciences Analysis Engine specification which Soaplab2 follows. Only the names of Java classes representing individual events are more Java-like (e.g. the main event is the class AnalysisEvent).

The XML representation of the events follows the AnalysisEvent.dtd.

The details of individual events are described in their API.


You can see (static) examples of all standard events by running:

build/run/run-any-client org.soaplab.services.testing.TestEvents

The default event listener provided by Soaplab2 is class org.soaplab.services.events.DefaultEventListener that simply records events into a log file. For example:

2006-11-15 19:45:18,555 641  [main] DEBUG EventListener - [grid.helloworld]-576da760.10eed26e2d1.-8000 UNKNOWN --> CREATED
2006-11-15 19:45:18,604 690  [main] DEBUG EventListener - [grid.helloworld]-576da760.10eed26e2d1.-8000 CREATED --> RUNNING
2006-11-15 19:45:18,609 695  [main] DEBUG EventListener - [grid.helloworld]-576da760.10eed26e2d1.-8000 RUNNING --> COMPLETED
The Configuration Guide has some details how it can be configured.

PersistentStorage

This SPI plug-in allows to deal with the analysis results. Primarily meant to store them and retrieve on request later, it can also play a role of the result visualisers. The interface PersistentStorage has a boolean method isSink(). When it returns true the methods retrieving results are supposed to return nothing - exactly what a visualiser would do.

The default storage is in a local file system, implementing by the class org.soaplab.services.storage.FileStorage. A database storage is going to be added soon.

The existing contents and cleaning can be done by a command-line program ExploreStorage. For details, see Administration Tools, or start the on-line help by typing:

build/run/run-any-client org.soaplab.admin.ExploreStorage -help

Details on classes-based plug-ins

Several places can be extended by developer's own classes. Their names are (usually) in service metadata. A developer needs:

The best practise is to extend an existing class rather than develop from the scratch. See examples below.

Job - org.soaplab.services.Job

The most crucial extension. It brings the whole new functionality to a service or to a group of services.

A full example of such class-based plug-in is a Job that submits tasks to a computational grid. It is represented by the class org.bioinfogrid.services.GridJob. This project is in a separate CVS module bioinfogrid-plugin (in the same repository as Soaplab2, at SourceForge).

When writing a new Job implementation, consider to inherit either from the AbstractJob or from the CmdLineJob.

Input/Result - org.soaplab.services.adaptor.DataAdaptor

Yet to be written... for now, check the API

Parameter - org.soaplab.services.cmdline.Parameter

Yet to be written... for now, check the API

JUnit testing

For junit testing, there is an Ant's task test-junit:
ant test-junit
The tests included in this junit testing task are all Java sources defined in the directory tree within src/test/java/ that contain "Test" in their names.

The test-junit task compiles test classes and run the tests. The tests reports are stored in the directory test-reports.

Soaplab2 uses junit version 4 (and above). The corresponding junit.jar is downloaded by usual maven procedure (as any other dependencies) and stored in your local maven repository. However, it may not be (yet) visible to the testing tasks. If that happens, the following message (and instructions what to do) are being displayed:

ant test-junit
...
junit-present:
     [echo]
     [echo] ================================= WARNING ================================
     [echo] Junit library is not present in a correct directory. Tests not executed.
     [echo] The library (junit.jar) is, however, in your local Maven repository.
     [echo] Therefore:
     [echo]     Put it in your ${ANT_HOME}/lib directory.
     [echo]     Or, put in your ${HOME}/.ant/lib directory.
     [echo]     Or start ant with -lib <your junit library>.
     [echo] ==========================================================================
     [echo]
Sometimes it is more convenient to run only some tests (because the other tests may take too long). It can be done by compiling first the test classes (by the Ant's task compile-tests) and running a script with the name of the class containing the wanted tests. For example:
ant compile-tests
build/run/run-junit-test org.soaplab.clients.RepeatabilityTests
JUnit version 4.3.1
.
Time: 1.352

OK (1 test)

Last modified: Fri May 16 14:18:40 2008