Embedding A Jackrabbit Repository in an OSGi Framework
The Apache Sling project has provided an infrastructure for embedding and accessing JCR Repositories right from the start. With Jackrabbit 2 it is about time, that Jackrabbit itself becomes more OSGi friendly and provides many of the features which are currently part of Apache Sling.
This page is about the changes and enhancements proposed in the Jackrabbit 2 Bundle Sandbox.
Apache Sling JCR Bundles
Apache Sling currently provides the following JCR related bundles:
Bundle Current Version Jackrabbit Version Description
Bundle |
Current Version |
Jackrabbit Version |
Description |
Sling JCR API |
2.0.6 |
– |
Provides additional API to simplify use. Most notably contains the SlingRepository interface to ease configuration pain for bundles requiring administrative repository access. This bundle used to include and provide the JCR 1.0 API in the OSGi framework. This has been removed to allow the provision of the JCR 2.0 API as a proper bundle. |
Sling JCR Base |
2.0.4 |
1.5 |
Provides an abstract default implementation of the SlingRepository interface and includes the Jackrabbit RMI library at version 1.5 to access a repository over RMI. This abstract base implementation supports accessing repositories both through JNDI and RMI. |
Sling JCR Classloader |
3.0 |
|
Provides support to load classes from the repository. This bundle was based on the Jackrabbit Repository Classloader. Now it contains a stripped-down copy of the class loader with a few fixes. |
Sling Jackrabbit Client |
2.0.2 |
|
A concrete extension of the AbstractSlingRepository in the Sling JCR Base bundle. This extension is almost empty, since the basic support is already provided by the abstract class. |
Sling Jackrabbit Server |
2.0.4 |
1.6 |
Embeds Jackrabbit Core and most of its dependencies like Lucene, Derby, PDFBox, etc. |
Sling Jackrabbit Access Manager |
2.0.2 |
1.6 |
Supports Access Control Management in a Jackrabbit Repository using the Sling Features. |
Sling Jackrabbit User Manager |
2.0.2 |
1.6 |
Supports User Management in a Jackrabbit Repository using the Sling features. |
Note: The future of the Jackrabbit Repository Classloader library has not yet been decided upon. At the moment, we have a relatively stable release in the Commons subproject and Apache Sling has a stripped-down fork of that class loader. It will have to be discussed where we are heading with this beast and what the requirements of the Community will be around this.
Of these bundles, the Sling Jackrabbit Server bundle is probably the one which should be integrated in the Jackrabbit Core library. Over time I could imagine to also merge most of the Sling JCR Base and Sling JCR Client bundles into the Jackrabbit project.
Current State of OSGi Support in Jackrabbit
Currently many helper libraries of Jackrabbit are already setup as proper OSGi bundles. Most notably this includes the Jackrabbit API, JCR Commons, SPI, and SPI Commons libraries.
The major missing link is the Jackrabbit Core library, which is the actual implementation of the repository.
Extensions to Jackrabbit Core
The goal of turning the Jackrabbit Core library into an OSGi bundle is to preserve as much of the functionality of the current Sling Jackrabbit Server bundle as possible. Concretely this means:
- Build the Jackrabbit Core library as a bundle (obviously)
- Embed only as much dependencies as are really needed (goal: none)
- Support easy startup and access to the embedded repository instances
- Provide some degree of dynamic extensibility (at least provide the pluggability currently supported by the Sling Jackrabbit Server bundle)
Jackrabbit Core Bundle and Embedded Dependencies
The biggest issue when bundelizing the Jackrabbit Core bundle is the is the list of dependencies and extensibility.
The problem with dependencies is, that Jackrabbit Core has quite big number of dependencies. To launch a Jackrabbit repository a number of libraries have been collected and put together. Some of these libraries are available as OSGi bundles (e.g. the Jackrabbit helper libraries, the JCR 2.0 API, Derby) and some are not (yet) (e.g. Lucene, Concurrent, Commons Pool, Commons DBCP). Tika is special in that it will be available as a bundle starting with release 0.6.
The problem with extensibility is, some internal extension APIs of Jackrabbit Core are not yet separated from internal implementations. One example is the persistence manager interface. This makes it close to impossible to expose those APIs with OSGi Export-Package
without also requiring to exposing internal packages.
The current approach followed in the Jackrabbit 2 Bundle Sandbox is to embed libraries, which are not available as bundles and to not embed libraries available as bundles.
Easy Startup and Access
For easy startup and access the OSGi specification offers great functionalities: To startup an embedded repository instance, we need a location to store the filesystem data to and a location to access a configuration. Both properties can easily be provided through the OSGi Configuration Admin Service. This is what we do in the Sling Jackrabbit Server bundle and thus this is also implemented in the current approach.
Accessing an embedded repository is equally easy: As soon as a repository has been configured and started, it will be registered as an OSGi service and can be referenced like any other service using the service name. An embedded Jackrabbit Repository is registered under the two names implemented by Jackrabbit Core: javax.jcr.Jackrabbit
and org.apache.jackrabbit.api.JackrabbitRepository
.
The Jackrabbit Core bundle also contains an OSGi BundleActivator
which ensures configuration can be provided by the Configuration Admin Service and which also ensures an initial configuration to have a running repository as soon as the bundle is started.
As for the configuration file: The Jackrabbit Core library contains a default repository.xml file (OSGI-INF/repository_osgi.xml
) which is used as a configuration file if none is provided.
Extensibility
Jackrabbit configuration is the subject of a book of its own. Yet some things may be setup rather dynamically in the sense of extensible at runtime by the system administrator. Things in this area, we have at the moment in the Sling Jackrabbit Server bundle are a framework to extend login modules and a support to plugin extensible access controllers.
Both should, if possible, be migrated to the Jackrabbit Core bundle; possibly even enhancing it. Of course such extensibility points will have to be exported from the Jackrabbit Core bundle and as such will be placed into separate locations.
Pluggable LoginModule
The goal of a pluggable login module system is to provide a simple means to extend the functionality of the DefaultLoginModule
which is part of the internal API (but is one of those things, which cannot easily be exported without exposing internal implementation). One major use case of such pluggable login module functionality is to account for authentication mechanisms which require more than just the final SimpleCredentials
class but otherwise would benefit from all the Principal
resolution support of the DefaultLoginModule
.
The PluggableDefaultLoginModule
overwrites the following methods of the DefaultLoginModule
and the AbstractLoginModule
:
doInit
Locates a login module plugin which is capable of handling the credentials provided in the Repository.login
method.
commit
Allows the login module plugin to properly cleanup after a successful login attempt
abort
Allows the login module plugin to properly cleanup after a failed login attempt
supportsCredentials
Locates a login module plugin which is capable of handlig the credentials. If none can be found, the base class implementation is called.
getUserID
Overwrites the AbstractLoginModule
implementation to check whether one of the plugins can provide the user ID for the given credentials. If not the base class implementation is called.
getPrincipal
Overwrites the DefaultLoginModule
implementation to check whether one of the plugins can provide a Principal. If not the base class implementation is called.
getPrincipals
Calls base class implementation to get the base set of principals and the calls the currently used plugin to add further prinicipals and returns the completed set.
authenticate
Calls the authenticate method of the plugin handling the given credentials and returns true
. If no plugin can handle the credentials the result of calling the base class implementation is returned. If the plugin fails to authenticate the credentials for the principal a FailedLoginException
is thrown.
impersonate
Calls the impersonate method of the plugin handling the given credentials and returns true
or false
if the plugin handles impersonation. If no plugin can handle the credentials or the selected plugin does not handle impersonation the result of calling the base class implementation is returned. If impersonation either by the plugin or the base class implementation fails a FailedLoginException
is thrown.
Because login modules are created for each login request and may keep internal state while handling the login request, the service API for pluggable login modules defines a factory interface:
public interface LoginModulePluginFactory { /** * The name of the service under which to register the module factory. */ String SERVICE_NAME = "org.apache.jackrabbit.core.api.security.LoginModulePluginFactory"; /** * Returns a login module is capable of handling the login request for the * given <code>Credentials</code> instance or <code>null</code> if this * factory does not support the credentials. * * @param credentials The <code>Credentials</code> intended to be handled by * the {@link LoginModulePlugin}. */ LoginModulePlugin create(final Credentials credentials);
In the doInit
method the PluggableDefaultLoginModule
calls the create
method of all registered LoginModulePluginFactory
services for a LoginModulePlugin
. The first factory returning an non-null
instance "wins" and the instance is used throughout the login process.
The actual plugin API is here
It is important to understand, that the PluggableDefaultLoginModule
supports any number of LoginModuleFactoryPluginFactory
services and selects the one to use by querying them in turn. This allows for a number of login modules for different special cases: OpenID, Kerberos, SecurID, NTLM, etc. without having to reconfigure the repository – or even to have to restart Java VM to be able to add more login modules.
Pluggable Access Control Manager
TBD