OpenXRI:ServerArchitecture
From I-names Development Wiki
This page describes the architecture and operation of the OpenXRI Authority Resolution Server. This is intended for developers; it is not a Howto on setting up an authority resolution service or community i-names.
Contents |
Architecture
The principal components that make up the OpenXRI Authority Resolution Server are:
- XRIServlet: The main entry point servlet that handles incoming requests.
- URIMapper: Parses incoming requests and extracts information needed for looking up authority information.
- Server: The core component responsible for retrieving or building a complete XRDS document.
- Store: Responsible for storing and retrieving authority data, e.g. service endpoints and Refs.
- Plugin: Handles requests which cannot be parsed by the URIMapper.
- Config: Holds configuration data for the XRIServlet, URIMapper, Server, Store and Plugin components.
- Factories: Provide a unified mechanism for constructing instances of the various other components.
The following diagram shows dependencies between these components:
Operation
The XRIServlet is the Java servlet which handles incoming requests to the Authority Resolution Server. It performs the following functions:
1. Parse the request using an URIMapper implementation.
2. If the request could not be parsed, hand off processing to a Plugin implementation.
3. Otherwise use a Server implementation to construct an XRDS and send it back.
The following diagram describes this process from a high-level perspective:
Components
URIMapper
An URIMapper is responsible for parsing incoming requests. It is given a HttpServletRequest object and extracts the following information:
- The segment that needs to be resolved
- Either a namespace of this segment OR
- The ID of the parent authority of this segment
URIMappers are also responsible for completing URIs, which can be used as endpoints of an authority resolution service endpoint.
All implementations must implement the URIMapper interface.
Currently the following URIMappers exist:
- FolderURIMapper: Uses a folder-like URI structure. This is the recommended implementation.
- QueryURIMapper: Uses URIs with a query string.
- SingleNamespaceURIMapper: Assumes a single, fixed namespace, which does not have to be encoded in the URI.
- NullURIMapper: Fails to handle all requests. Can be used if all request should be forwarded to the Plugin.
The following diagram shows interfaces and classes relevant to the URIMapper component:
Server
The Server is the core component and responsible for building or retrieving an XRDS document. While this is not required by the architecture, the Server is expected to access the Store component for retrieving data about the authority to be resolved. The Server implementations can use information from the URIMapper to determine which authority has to be resolved.
Implementing classes can choose to either implement the Server interface directly or to subclass AbstractServer, which handles the task of retrieving authority data from the Store. In the latter case, the implementing class only needs to add 'dynamic' parts to the XRDS, e.g. Status, Query and Expires elements.
The AbstractServer supports resolving multiple subsegments in a single request ("recursing resolution").
The AbstractServer also includes an experimental feature called Authority Handlers, which makes it possible to add dynamic, programmatic behaviour to individual authorities of the Store.
All implementations must implement the Server interface.
Currently the following Servers exist:
- BasicServer: Subclass of AbstractServer; can build a complete XRDS with all required elements; dynamically creates Status, Query and Expires elements
- TrustedServer: Subclass of BasicServer; can add a SAML signature to the XRDS if trusted resolution is requested
The following diagrams shows interfaces and classes relevant to the Server component:
Store
A Store is responsible for persisting 'static' authority data, for example service endpoints and Refs. This data is retrieved when the Server needs to construct an XRDS for an authority. The Store component is subdivided into various interfaces, which represent different capabilities of stores:
- Store and StoreLookup: These interfaces define core functionality and have to be implemented by every store. They include basic operations for creating and looking up authorities and subsegments. All other interfaces are optional.
- StoreBetterLookup: Defines advanced methods for getting information about authorities and subsegments out of a store. If a store implements this interface, the Server is able to automatically generate LocalIDs for an XRDS.
- StoreEditable: Defines methods for adding and deleting refs and services of an authority.
- StoreContext: Provides methods for associating context with subsegments and authorities. The context is a String and can include information such as registration date etc.
- StoreStatistics: Defines methods for getting statistics out of a store.
- ResettableStore: Defines a method for deleting all contents from a store.
Currently the following Store implementations exist:
- MemoryStore: Stores all authority and subsegment information using in-memory data structures. When the Store object is destroyed, all contained information is lost. The MemoryStore implements the following interfaces: Store, StoreLookup and ResettableStore
- FileStore: Stores all authority and subsegment information using the local file system. The base path can be configured as a parameter in the XRIServlet configuration file. The FileStore implements the following interfaces: Store, StoreLookup and ResettableStore
- DatabaseStore: Uses Hibernate to persist all authority and subsegment information in a relational database. This implementation covers all of the interfaces mentioned above.
- NullStore: Implements all interfaces mentioned above, but does not actually store anything.
The following diagram shows interfaces and classes relevant to the Store component:
Plugin
Plugins can be used to handle requests to the server which the URIMapper cannot parse.
All implementations must implement the Plugin interface.
Currently only a single Plugin exists:
- NullPlugin: Fails to handle all requests.
Planned future Plugins include an administration web interface and a SOAP webservice.
The following diagrams shows interfaces and classes relevant to the Plugin component:
Config
The Config component is used to configure the XRIServlet as well as other components. For the components URIMapper, Server, Store and Plugin, the Config component provides two pieces of information:
1. The name of the class to be instantiated
2. Parameters for the implementation
This information is used by the factories (see below), in order to instantiate and configure the URIMapper, Server, Store and Plugin implementations.
The ServerConfig interface defines methods for reading and writing configuration.
Currently, two implementations exist:
- PreferencesServerConfig: Uses the Java Preferences mechanism for reading and writing configuration.
- PropertiesServerConfig: Uses a properties file for reading and writing configuration.
The following diagrams shows interfaces and classes relevant to the Config component:
Factories
In order to instantiate and configure implementations for the URIMapper, Server, Store and Plugin components, the following factory classes are provided:
- URIMapperFactory
- ServerFactory
- StoreFactory
- PluginFactory
Factory objects follow a Singleton pattern, i.e. they can be obtained by calling a static method, e.g. StoreFactory::getInstance().
These classes provide methods for instantiating and configuring implementations of the respective components. To do so, the following steps are followed:
1. Obtain a class name (e.g. "org.openxri.server.db.DatabaseStore") from a ServerConfig object
2. Obtain parameters (in the form of a java.util.Properties object) from a ServerConfig object
3. Instantiate an object of the class, passing the Properties object as the single parameter to the constructor. This gives the implementation a chance to read its parameters.
4. Invoke the init() method on the object. This gives the implementation a chance to configure and initialize itself.
This process was designed so that is also possible to instantiate components using the Spring framework. This makes it possible to access the individual components from other applications. For example, one could write an application that accesses a Store object and performs various administrative operations.







