If you build web applications with Apache Sling or CRX Quickstart you might want to extend the core functionality, e.g. for starting a background process or connecting to some legacy system.
Fortunately, this is easy as Sling is based on OSGi. OSGi (Open Services Gateway Initiative) defines an architecture for developing and deploying modular applications and libraries. As such, extending Sling with application-specific components ("bundles" as they are called in OSGi lingo) essentially means creating a bundle. In this post I will walk you through the complete process. All code and configuration is attached to this post as a deployable bundle.
One disclaimer before we start, though: if have no clue at all about OSGi you should get some basic understanding from TheServerSide, or JavaWorld (the TSS article uses the terms "repository" and "node" in a generic way, not for JCR repositories and nodes as in this post).
Once you have a basic notion what a bundle is you're good to go.
Create a bundle
A bundle is a jar file plus some meta information. Let us start with a simple service interface class. In OSGi development it is common practice to use interfaces and implementing classes.
This is really just a plain old Java interface, nothing OSGi- or Sling-specific to be seen here, just some JCR classes are imported. I chose these four methods because they provide some insights how to develop OSGi bundles on top of Sling. However, they are not "required" in the sense that e.g. EJB 2 would require a certain structure of your classes. The implementing class looks like:
The implementation imports the SlingRepository class and a logger class that comes with Sling. In order to compile this class you will need to have the Sling jars on your classpath. If you use CRX Quickstart find them in launchpad/felix/bundleXX.
For the bundle description we will need a descriptor file called MANIFEST.MF located in META-INF and contains:
Note that the packages that are used in the implementing class above must be imported. Also, the packages that shall be exposed from our bundle to other bundles must be marked as "exported". Further, there must be a reference to the second needed descriptor file OSGI-INF/serviceComponents.xml. This file contains:
This file configures which implementation of the interface shall be used. It also configures the "injection" of the repository variable into the HelloServiceImpl class (highlighted). The "setter" is configured in the "bind" attribute. HelloServiceImpl must implement this method.
For creating an OSGi bundle you just need to compile these classes and package them in a jar file together with the descriptors (the complete bundle is attached).
The Sling console
The Sling console is a web application that comes with Sling and CRX Quickstart that allows you (among other things) to deploy new bundles. It is located at http://localhost:7402/system/console/list. Bundle details can be seen by clicking on a bundle name. This reveals e.g. the bundle version and the exported packages. For example, note that the org.slf4j package imported by HelloServiceImpl is exported by a bundle named "Sling-OSGi LogService Implementation".
Upload your bundle jar file, press "Install or Update" and "Refresh Packages". Your bundle should now show up in the list of bundles. If it has not been started start it yourself by pressing "start".
Accessing the service
In order to be useful the deployed OSGi service should be accessible from esp templates. That works like this:
The HelloService also contains a method getReversedNodePath(Node node) that takes a JCR node as a parameter (it diabolically returns the node's path as a reversed string). For passing the currently processed node to this method use:
The method log(String text) in HelloService writes into Sling's log file. This can come in handy for debugging purposes. Note again that the logger package needs to be imported in MANIFEST.MF in order to be visible.
In the method getRepository() the name of the current repository is returned. This method illustrates how to access the repository from within the bundle and thus perform manipulations or queries on the repository.
Final remarks
If you care to look at Sling's source code to learn more you will notice the lack of configuration files like serviceComponents.xml. The reason for this is that the Sling develpers use a Maven plugin that automatically generates these artifacts. You can still find them in the generated jar files, of course.
To learn more have a look at the sample applications that come with Sling. Find them in samples/simple-demo and samples/webloader. The webloader demo implements a background service that fills the repository with publicly available files.