Apache Sling Overview

Author: Adam Constabaris
Organization: Carolina Digital Library and Archive

Apache Sling

An open source content repository.

http://incubator.apache.org/sling

Sling Blurb

Based on the REST architectural style, it uses an OSGi container backed by a Java Content Repository to provide storage, presentation, and management of, well, whatever.

Wait, what? And "Incubator?"

Incubator?

How Stable is this?

From a technical perspective, Sling's fairly stable and has at least one full-fledged "release" under its belt. The main issue is that most of the developers work for the same company (Day Software). The Apache Software Foundation requires projects to establish that they have a sustainable, distributed community of committers, and that's why it's still in incubation. Since the project's getting more attention though, I don't expect it to be too long before it "graduates."

REST

REpresentational State Transfer

An architectural style for distributed systems. Application data and state is represented as a set of addressable resources which present a uniform interface that allows transfers of state (e.g. reading and updating of the resource's state). The (quasi-canonical) example of a RESTful architecture is the web, where "resources" have URIs and the "uniform interface" is HTTP.

REST (cont.)

An HTTP request consists of a path, a verb, and some headers. The "verbs" of HTTP are the methods; GET is the simplest:

GET / HTTP/1.1
Host: localhost

This fetches the resource at / and sends it back as the body of the HTTP response. GET, by definition, should never change the state of the resource on the server.

The POST Method

A POST request involves sending a request body along with the URI, and usually involves a few headers that tell the server how to understand the request body.

POST /funk.cgi HTTP/1.1
Host: localhost
Content-type: application/x-www-form-urlencoded
Content-length: 30

name=Smedley&occupation=Bosun

POST (cont.)

It's up to the server to figure out what that means, although the official definition is something like "please stash these things as subordinates of /funk.cgi." POST is in practice used as the generic method to use when a resource needs to change on the server.

POST is not idempotent (the result of calling it more than once with the same values may produce different results than calling it just once).

Other HTTP Methods

Most browsers only support GET and POST as the "method" attribute on a form tag, but there are at least two other commonly used methods out there.

PUT [uri] means "stash the body of this request at [uri] (whether or not there was something already there). It's different from POST in that it is idempotent. It's intended as a counterpart to GET.

DELETE [uri] means pzackly what it says.

REST (finally)

The main contention of RESTafarians is that for most cases, a framework like HTTP (addressable resources + "verbs" + standard ways to transmit metadata) is all you need to build a distributed application. Sling may be seen as an attempt to provide some data for this contention, at least for the case where the distributed application is a content repository.

JCR

Java Content Repository (a.k.a JSR-170)

A (Java-based) standard for content repositories; think "filesystem with metadata" and you won't be too far off. "Hierarchical object storage" maybe gets closer to the heart of the matter.

Specific JCR implementations may provide features such as content versioning and full-text indexing.

JCR Basics

JCR stores nodes which have types and properties, and may in addition have child nodes. Every node has at least one primary type that define(s) the structure of the node's properties and children.

In addition, nodes may have mixin types which are used to add extra behavior (e.g. mix:versionable is a mixin type; you add it to a node to have the repository store previous versions of the node).

Querying JCR

The JCR spec allows for querying a repository using (among other things) an XPath syntax (since the overall structure of a repository is roughly like that of an XML document, with nodes and properties roughly mapping on to elements and attributes. The implementation bundled with Sling provides full-text indexing (with Lucene) for a decent range of content types.

JCR Content Modelling

Apart from the builtin node types (e.g. nt:folder, nt:file), you can define your own. This feature can be used to constrain the properties and children a node may (or must) have and also specify the types of those properties (dates vs. numbers, e.g. -- the default type of a property is string).

OSGi

formerly "Open Services Gateway initiative"

A module system for Java that allows building applications as a set of reloadable and strongly encapsulated services. Traditional Java application servers use a fairly monolithic approach to application deployment. OSGi "bundles" run inside an OSGi "container"

OSGi (cont.)

The OSGi container manages relations among bundles, which are simply JAR files that contain extra metadata indicating what services they require and which they provide. See the OSGi metadata overview from Spring for a friendly and implementation-neutral introduction.

Sling Highlights

Sling Highlights (cont.)

  • add new scripting languages
  • add and update modules while running
  • deploy entire applications in one operation

Sling Highlights (cont.)

Being a REST framework, Sling is oriented around resources, which usually map into JCR nodes.

The sling:resourceType property specifies a content type and is the basis of content modeling and specialized content handling in Sling.

An Example

First, create a new content (resource) type (authentication info is omitted in all curl examples) [*]

curl -X MKCOL http://localhost:8080/apps
curl -X MKCOL http://localhost:8080/apps/review
curl -X MKCOL http://localhost:8080/apps/review/movie

/apps is where Sling will look for scripts; so this gives us a home for special content handlers for the review/movie resource type.

Example (cont.)

Add some content with the new resource type

curl -X MKCOL http://localhost:8080/reviews
curl -F"sling:resourceType=review/movie" \
     -F"title=Plan 9 From Outer Space" \
     -F"rating=5.3/10" http://localhost:8080/reviews/*

This is equivalent to POSTing a form to /reviews/* with the parameters sling:resourceType, title, and rating as indicated.

Example (cont.)

The * in the URI of the last command indicates that Sling is to make up the URI for the new resource; it will typically base this off of the title parameter we submitted, so the new node will be available at something like

http://localhost:8080/reviews/plan_9_from_outer

Sling's default POST handler servlet does all the work here, including handling node name "collisions."

Example (cont.)

A Conceptual Break

The default POST servlet passes through HTTP parameters to become the properties of the new resource, which makes maintaining content via forms relatively straightforward.

see Sling Cheat Sheet and this short reference to POST servlet parameters for more info.

Example (cont.)

Updates

To update a bit of metadata on a node, you can simply POST to its URI with the new metadata, e.g.

curl -F"rating=5.9/10" \
 http://localhost:8080/reviews/plan_9_from_outer

The other properties will not be touched.

Example (cont.)

Default Content Rendering

If we browse to the new resource's URI, we will see the default (plain text) rendering. Add .html to the end to see the default HTML rendering (or .json to see the default JSON output!)

These default handlers will be used unless they are specifically overridden (e.g. by installing scripts to the URI we created above).

Example (cont.)

Create a file named html.esp (.esp => ECMAScript)

<html>
 <head>
  <title><%= currentNode.title %></title>
 </head>
 <body>
  <h1><%= currentNode.title %></h1>
  <p><i>Rating : </i> <%= currentNode.rating %></p>
 </body>
</html>

Example (cont.)

Install the new file as the HTML renderer for the review/movie resource type.

curl -X PUT -d @html.esp \
http://localhost:8080/apps/review/movie/html.esp

Sling stores your script at a special part of the repository, and from now on when it gets requests for [some resource's uri where the resource's type is review/movie].html, it will use this script to render the response.

Example (cont.)

Scripts can be installed for HTTP verbs (POST.esp) and extensions (html.esp), and are themselves interpreted according to their extensions: .esp for server side ECMAScript, .jsp for Java Server Pages, .rb for (JRuby), etc. The examples don't show it, but they can modify the repository.

Scripts run in an environment provided by Sling and have access to the repository, current request and response, as well as to OSGi services that "advertise" themselves.

Static Content

Not everything needs to go through the content modeling system. For example, to install jQuery, you can simply PUT the jQuery file and Sling will do the right thing.

curl -X MKCOL http://localhost:8080/js
curl -T jquery-1.2.6.min.js \
 http://localhost:8080/js/jquery-1.2.6.js

It will thereafter serve that file with the correct MIME type, etc.

The Shiny OSGi Future

OSGi module = a JAR + some metadata

OSGi modules run inside a container and typically present themselves as services. Modules can be aware of the container and can also dynamically reconfigure themselves based on other services in the container. All of Sling's functionality is available to user-defined modules.

OSGi Example

(Mostly Conceptual)

Suppose we need a facility for transforming XML documents. The best way to add this feature is to build an OSGi bundle and have it expose the transformation functions as a service. The bundle can either store the XSLT files internally, or it can automatically load them into Sling's repository when it is deployed.

OSGi Example (cont.)

The steps involved in creating a no-frills module manually look like this:

Bundles may also specify an "Activator," which has a method the container can call when it starts the bundle; typically, activators run setup tasks (e.g. compiling stylesheets).

OSGi Example(cont.)

package edu.unc.lib.dl;

import javax.jcr.*
import javax.xml.transform.*;

public interface XSLTService {
   public String runTransform(Node node) throws TransformerException;
}

OSGi Example (cont.)

Imagine that we have an implementation of the above interface and that we have all the OSGi metadata correctly specified.

Bundles are sort of an exception to the "curl is my UI" maxim; the most obvious (but not the only) way to deploy a bundle is to log into Sling's management console and upload it, then use the console to start it, stop it, set its properties, etc.

OSGi Example (cont).

To invoke the above-defined service in an .esp:

<% service = sling.getService(Packages.edu.unc.lib.dl.XSLTService); %>
<%= service.runTransform(currentNode) %>

The first line looks up the service, and the second simply outputs the result of running the transformation on the current node to the response.

OSGi Example (cont.)

It's possible to do the above without a script -- as a matter of fact, scripts are converted into servlets by Sling.

The trick is to use OSGi metadata that lets Sling know our bundle contains a servlet and that it wants to handle certain resource types. Then it just has to look up the XSLTService in the OSGi container.

The details are beyond the scope of this slide set, though.

What Can Go in a Bundle?

  • Arbitrary services (not related to JCR/HTTP)
  • Servlets
  • Sling Extensions (e.g. new scripting engines)
  • Libraries
  • Scripts
  • Static content

Building Bundles

Much of the OSGi metadata drudgery is relieved by Maven plugins, some written for the Apache Felix OSGi container, while others are provided by the Sling developers. The Sling plugin also provides a facility for deploying your bundle into a running Sling repository.

WebDAV

Sling speaks WebDAV (roughly, a set of extensions to HTTP that allow for two-way communication), so a Sling repository can also be 'mounted' as a WebDAV share and treated in much the same way as a network drive. This approach really only work with parts of the JCR repository that consist of nt::folder and nt:file nodes, since that's how WebDAV 'thinks'.

URL Conventions

/apps or /libs are where application logic components (e.g. scripts) go.

/path/to/node.N.json where N is an integer will retrieve a JSON object specifying the contents of the node and its children to a depth of N (i.e. this is how you can browse your repository using Javascript). If omitted entirely, N = 1.

Deployment Options

Most of the above examples use the "launchpad" application provided with Sling, which is an all-in-one bundle (servlet container, Sling classes, JCR repository); Sling can also be deployed into a traditional servlet container, and it can also make use of an externally defined JCR instance.

Auth[nz]

Authentication and authorization in Sling are a little primitive at the moment (it delegates to whatever scheme the underlying JCR instance uses, so you're limited to whatever it provides). Since Sling operations all take place over HTTP, it should be possible to use a web server (e.g. Apache httpd) to help deal with the situation (and provide https support).

There has been some discussion among the main developers about whether this state of affairs should continue.

Statelessness and Auth[nz]

REST as implemented in HTTP tends to be stateless; for example, it doesn't use cookies and clients need to re-authenticate on every request. Sling isn't necessarily good for applications that need to be stateful (although you could implement a 'shopping cart' as a resource if you need to =), but provided your authn/z isn't too expensive, this class of application isn't as big as it might seem.

Notes and Further Reading

[*]MKCOL ("make collection") is a WebDAV "verb" that creates a folder.