Day JCR Cup 2008

Day's JCR Cup 2008 will start very soon. Leave your email address to get a notification and be the first to know!


Looking for the rules? See this post.




Yesterday I spoke at the at IDC's 4. Enterprise Content Management Conference 2008 in Mainz, Germany.


Please find the slide deck below



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.

package com.day.samples;

import javax.jcr.Node;
import javax.jcr.RepositoryException;

public interface HelloService {
  public String sayHello();
  public String getReversedNodePath(Node
    node) throws RepositoryException;
  public String getRepository();
  public void log(String text);
}

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:

package com.day.samples.impl;

import javax.jcr.Node;
import javax.jcr.RepositoryException;
import org.apache.sling.jcr.api.SlingRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.day.samples.HelloService;

public class HelloServiceImpl implements
  HelloService{

  private SlingRepository repository;
  private static final Logger log =
    LoggerFactory.getLogger(HelloServiceImpl.class);

  public String sayHello() {
    return ("Hello World!!"); 
  }	
	
  public String getReversedNodePath(Node node)
    throws RepositoryException {
    return new
      StringBuffer(node.getPath()).reverse().
        toString();
  }

  public String getRepository() {		
    return
      repository.getDescriptor(
      SlingRepository.REP_NAME_DESC);
  }	
	
  protected void bindRepository(SlingRepository
    repository) {
    this.repository = repository;
  }
	
  protected void unbindRepository(SlingRepository
    repository) {
    this.repository = null;
  }

  public void log(String text) {
    log.error(text);
  }
}

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:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: HelloWorld Plug-in
Bundle-SymbolicName: HelloWorld
Bundle-Version: 1.0.0
Import-Package: org.osgi.framework;version="1.3.0",
 org.apache.sling.jcr.api,org.slf4j
Export-Package: com.day.samples;version="1.0.0"
Private-Package: com.day.samples.impl
Service-Component: OSGI-INF/serviceComponents.xml

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:

<?xml version="1.0" encoding="UTF-8"?>
<components
  xmlns:scr="http://www.osgi.org/xmlns/scr/v1.0.0">
  <scr:component enabled="true" immediate="true"
    name="com.day.samples.impl.HelloServiceImpl">
    <scr:implementation
      class="com.day.samples.impl.HelloServiceImpl"/>
    <scr:service servicefactory="false">
      <scr:provide
        interface="com.day.samples.HelloService"/>
    </scr:service>
    <scr:property name="service.description"
      value="Say hello sample service"/>
    <scr:property name="service.vendor"
      value="Day"/>
    <scr:property name="service.pid"
      value="com.day.samples.impl.HelloServiceImpl"/>
    <scr:reference name="repository"
      interface="org.apache.sling.jcr.api.
        SlingRepository"
      cardinality="1..1" policy="static"
      bind="bindRepository"
      unbind="unbindRepository"/>
  </scr:component>
</components>

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:

<%
var service =
  sling.getService(
  Packages.com.day.samples.HelloService);
%>

<%= service.sayHallo() %>

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:

<%= service.getReversedNodePath(currentNode)%>

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.

The Apache Sling project has won a prize at the JAX Innovation Award, see Carsten Ziegeler's blog post about the ceremony. Congratulations to the Sling development team.

Happy Sling developers Felix Meschberger and Carsten Ziegeler

My recent activity in the Sling community has been centered around SLING-301, an enhancement request that adds Dojo classes to Sling readily available for every application developer. You might ask: “What is so great about Dojo, that it needs to be added to Sling?”. Let me answer this with a short 101 on using Dojo with regard to Sling.

Dojo is a Javascript framework that can be used for client-side and server-side Javascript programming and will provide a rich programming environment for Javascript programmers providing browser abstractions, a persistent storage API, a Comet client library, a widget library, a template system, a module system that supports lazy loading, a query framework, Javascript language enhancements such as easy class-based inheritance, enhanced for loops and many more. From my personal experience the Dojo Toolkit is a very well-engineered piece of software with good modularization and proper use of abstractions, which might be a reason why large web applications such as Bloglines or AOL webmail are using Dojo as the primary Javascript framework. This use of abstractions and modularizations is what makes Dojo so easily integrateable with Sling.

As you might know, Sling comes with the Sling client library, a component that allows Javascript developers to access the underlying Java Content Repository by GETting a JSON representation of a node (or a tree of nodes) and POSTing updates to nodes through web forms. While this is convenient if you are developing small applications from scratch, you might want to use an existing Javascript Widget library instead to spare the hassle of implementing all widgets yourself and dealing with browser quirks. This is where Dojo comes into play. Dojo provides a large widget library (called Dijit) that contains components such as Combo-Boxes, Tree Controls or, in the (DojoX package) a complete Grid control that supports column and row spanning, sorting and customizable cell editors. All of these widgets use a modular Store API to access its data model and this Store API is independent of implementation, which allowed me to create specialized Stores that allow accessing the Content Repository in a transparent way. The Store API itself is modular, so there are widgets and stores that only require and provide read access, others have support for write access, others for identity and thus hierarchical storage. The Sling stores provide all of these aspects and can be used for all widgets as a consequence. In order to use the Store API, you have to follow this procedure:

a) Load the Dojo Bundle using the Sling console
b) Create an HTML page that imports the Dojo Javascript and CSS files:

 
<html>
<head>
  <title>Dojo and Sling</title>
  <style type="text/css">
  @import "../../../dijit/themes/tundra/tundra.css";
  @import "../../../dojo/resources/dojo.css";
  @import "../../../dijit/tests/css/dijitTests.css";
  @import "../../../dojox/grid/_grid/tundraGrid.css";
  </style>
  <script type="text/javascript"
  src="../../../dojo/dojo.js"
  djConfig="isDebug: true,
  parseOnLoad: true, usePlainJson: true"></script>
  <script type="text/javascript">
    dojo.require("dojo.parser");
    dojo.require("dijit.Tree");
    dojo.require("dojox.data.SlingNodeStore");
    dojo.require("dojox.data.SlingPropertyStore");
    dojo.require("dojox.grid._data.model");
  </script>
</head>

<body class="tundra">

Create the data store. You can do this declarative by using following HTML snippet in your page:

 
<div dojoType="dojox.data.SlingNodeStore"
url="/" jsId="nodeStore"></div>

Use the Data store, for instance in a Tree widget. Please note the query attribute of the div, which will take care that the tree only shows nodes with a primary type of either nt:folder or nt:file, making it a poor man’s file browser.

 
 <div dojoType="dijit.Tree"
  id="tree1" store="nodeStore"
  query="{ query: { 'jcr:primaryType' : ['nt:folder',
  'rep:root'] } }">
  </div>
 

If you would like to learn more about Dojo, head to the Dojo homepage and do not forget to take a look at the Dojo Feature Explorer provided by Dojo Campus, a blog providing well written tutorials on various aspects of Dojo.

Today the Google Summer of Code program has announced the accepted students for this year. There is one JCR-related project that got through: Janandith will work on integrating Sling and Scala. The mentor is Day's Bertrand Delacretaz (with a little help from Michael Dürig, who also wrote some posts on Scala and JCR on dev.day.com).

  • PlanetDay

    The latest posts on PlanetDay

  • Links on Daigg

  • Day developers on Ohloh

    ohloh profile for Alexander Klimetschek ohloh profile for Alexander Saar ohloh profile for angela ohloh profile for Bertrand Delacretaz ohloh profile for cziegeler ohloh profile for david.nuescheler ohloh profile for dpfister ohloh profile for fmeschbe ohloh profile for Jean-Michel Pittet ohloh profile for Jukka Zitting ohloh profile for Lars Trieloff ohloh profile for Marcel ohloh profile for mduerig ohloh profile for Roy T. Fielding ohloh profile for stefan guggisberg ohloh profile for Thomas Mueller ohloh profile for Tobias Bocanegra