Skip to topic | Skip to bottom

Start of topic | Skip to actions

Implementaion Design


  • Internally, the registry adopts a message-passing metaphore and a component based approach. Soap messages are transformed by Axis into Java objects. Messages are handled by components we refer to as handlers. Handlers are typically designed to process messages of a same category, such as the messages of the UDDI publish interface, of the UDDI inquiry interface, or messages related to metadata. Such handlers in fact would correspond to the business logic implementing some ports of the wsdl interface of the registry. This has a number of benefits:
    • Ease of maintenance: code for a handler for a given interface will be well scoped.
    • Separate compilation and flexible deployment: we aim at being able to compile separately components, and possibly deploy only a subset of them in some specific configuration. For instance: we should be able to deploy just the uddi interfaces, or just the uddi and metadata interfaces. This obviously requires component code not to make reference to other component code!

  • Some OO patterns have been adopted: specifically, the visitor pattern is used to structure all messages and their associated handlers. Each message of a given category will implement a given interface, e.g. PublishMessage and PublishProcessable for messages of the UDDI publish interface. In particular, such messages will implement a method accept for the handler handling messages of such category. Handlers will be visitors offering a method process for each of messages in the interface.
    • Some default handlers are provided for each interface. In particular, a handler "not implemented" which will return an error for each message it is passed. When developing a new handler, it is therefore natural to subclass such a "not implemented" handler, which will ensure consistent error messages for all non implemented methods. This is far better that returning a null value since it becomes very hard to know where this value was generated.

  • We have adopted a message passing approach so that messages can be delegated to other components simply by passing them. Currently, our handling of messages is asymmetric (is this something we should revise?). A message is passed to a handler using the visitor pattern approach, i.e. by calling process on the handler, with the message as argument. However, the returned result (if any) is not passed as a message in the same way. Instead, results are stored using the setResult method (in AbstractMessage), and the input message is returned with its result field set.

  • Given this approach, when an error situation is reached in the business logic, say because an input is incorrect, we do not raise an exception in the handler, instead we said the error field, using the setError method (in AbstractMessage).

  • A source of inefficiency is copying objects (and in particular deep copies). We aim to avoid these by making sure that once an object is created by the axis container, we do not copy it again. When methods have to be added to these objects' classes, we subclass the classes generated by axis, and we need to let axis know that the class to be used when deserialing an object is the new one. (This is done by defining the entry for the data type in the config/deploy.wsdd file).

The structure explained

package comment the grimoires distribution the server side implementation of all interfaces implementation of uddiv2 interfaces implementation of metadata interfaces implementation of wsdl-related interface implementation of damls interfaces implementation of topics interfaces backend related matters configuration classes an XML client tester

Each implementation XXX of an interface attempts to follow a same structure:

In XXX comment
messages message definitions and associated visitors
datamodel data type definitions
handlers implementation of handlers
api implementation of APIs (currently server ties)

Saver, Loader and RDQLGenerator visitors

Grimoires uses the visitor pattern to save data objects to the RDF store, load data objects back from the store and generating RDQL queries from query data objects. Using this pattern means that we do not have to alter the data objects themselves, which are generated by Axis. Additionally, savers, loaders and query generators can be chosen to match the backend store. Currently this pattern is implemented for the metadata API implementations but the UDDIv2 APIs still use the old views design (where the data objects contain the load, save and generate RDQL methods).

The interfaces for the savers, loaders and RDQL generators are %JAVADOC{}%, %JAVADOC{}% and %JAVADOC{}% respectively. Each has an base implementation in the same package called AbstractSaver?, AbstractLoader? and AbstractRDQLGenerator?. These throw a StoreException? (for save and load) or RDFException (for RDQL generation) reporting that the object given as parameter cannot be saved/loaded/used to generate RDQL.

The Abstract classes should be overridden in the handlers of each API. For example, there is a MetadataSaver?, MetadataLoader? and MetadataRDQLGenerator?. These override the save, load and generate methods for each type object to be saved, loaded or used in generation.

Using the save and load methods is fairly straightforward. To save, call saveToStore on a saver passing the Model to store to and the object to store. To load, call loadFromStore with an empty (default constructed) object to be initialised, the Model to load from and the Resource identifying the stored object details.

Generating RDQL is only slightly more complex. Each method for generating RDQL queries, named generateRdqlQuery, takes as arguments the object to generate the query from and a QueryDetails? object. QueryDetails? stores the list of statements, constraints, namespaces etc. that make up the query and is manipulated by the generate method. The client would usually create a new QueryDetails? using the default constructor when generating a query. The generate method returns a GenerationResults? object that contains the QueryDetails? and the name of the variable that is used to identify the object to be returned by the query. The GenerationResults? object can be processed using passing the Model to query, the GenerationResults? and the variable name that the client is interested in.



-- WeijianFang - 17 May 2005

to top

You are here: Grimoires > SoftwareReleases > GrimoiresRelease101 > ImplementationDesign

to top

Copyright 2004 by the University of Southampton