Apache Sling is a game changer for Java Content Repositories in two different ways:
- It allows developers to build web applications on top of JCRs (and thus exploiting the benefits of JCRs) much much more efficiently than it was possible before. Proof for this claim is given e.g. in the screencast "TheServerSide.com in 15 minutes" or by looking at the little amount of code needed for the Notes example application.
- Sling exposes a RESTful API for the underlying JCR. As a consequence, the content repository can easily be used from outside of the JVM. In fact, Sling really takes the J out of JCR.
I would like to discuss the second point in a bit more depth. At the moment we are seeing the arrival of a bunch of new (logical) storage technologies that aim to replace or complement the relational model. I discussed a number of them in the post "Your Data is Your Web Server", but there are two dominant aspects in my view:
- For connecting RESTful APIs are used instead of ODBC, JDBC or similar. REST is the new JDBC.
- There is no need for creating big upfront data structure. Instead, unstructured data can be stored ad hoc.
The Sling/JCR combo nicely fits into this and also adds some more (see bottom (*)). In this post I show hands-on how to use Sling's REST APIs from Ruby - mind you, that's MRI, not JRuby. If you have not done so, yet, install CRX Quickstart (which bundles Apache Jackrabbit and Sling). Just start it by double-clicking, no further customization is needed.
In order to make this a fun exercise I installed three Ruby gems: the brilliant Faker for producing fake data, a Flickr gem and the Ruby-JSON gem. Get them as well for following the examples (the usual "sudo gem install ..." will do). Due to Ruby's disappointing file upload capabilities please also install curl and put it on the path.
Writing and Reading
Writing to the repository is very simple. Just post the node properties. The URL where the POST goes to determines the path of the node (a * in the end denotes that a new node). Each node can be retrieved in JSON format by sending a GET request. For example a GET for /path/to/node.2.json will return /path/to/node and its sub trees two levels deep in JSON format. Using -1 instead of 2 will return the whole sub tree.
Let's put this together: the Ruby script below creates two nodes at /content/cr/hello and /content/cr/ruby, respectively (it also writes some node properties that get send along with the POST). In the next step, the tree at /content/cr is retrieved and parsed into native Ruby objects. In the last step we use these objects to produce some more child nodes like in the first step. So, here we are: no Java, all Ruby.
require 'net/http'require 'uri'require 'json/pure'require 'faker'Net::HTTP.post_form(URI.parse("http://localhost:7402/content/cr/hello"), {'name'=>'yay', 'body'=>Faker::Lorem.paragraphs(5)})Net::HTTP.post_form(URI.parse("http://localhost:7402/content/cr/ruby"), {'body'=>Faker::Lorem.paragraphs(5)})data = Net::HTTP.get_response(URI.parse("http://localhost:7402/content/cr.-1.json")).body# we convert the returned JSON data to native Ruby data structure - a hashnotes = JSON.parse(data)# iterate over nodes and produce some more fun datanotes.keys.each do |node_name| 5.times {Net::HTTP.post_form(URI.parse("http://localhost:7402/content/cr/#{node_name}/*"), {'name'=>Faker::Name.name , 'body'=>Faker::Company.catch_phrase()})}end

Binary Data
One excellent property of JCRs is that they handle binary data as well as non-binary data (ahm, "everything is content"). This is demonstrated by the simple script below. It downloads images from Flickr and posts them into the repository at /content/cr/pixxi.
All mechanisms are the same as with the non-binary data above. You will need to supply a Flickr API key to run the script (get it here.). As mentioned I use the command line tool curl for the file upload.(**)
require 'net/http'require 'uri'require 'flickr'flickr = Flickr.new(YOUR_API_KEY)# get the last 100 public photos...flickr.photos.each do |photo| uri = URI.parse(photo.source) Net::HTTP.start(uri.host) { |http| resp = http.get(uri.path) open("test/"+photo.filename, "wb") { |file| file.write(resp.body) } } # post the file to the repository system("curl -F "+photo.filename+"=@test/"+photo.filename+" localhost:7402/content/cr/pixxi/*") # clean up File.delete("test/"+photo.filename)end
After uploading the files each one has a url through which it can accessed in the browser:

Moreover, if you mounted the repository via WebDAV you can also manipulate the files in your local file system:

Just to quickly note it again in case you missed it: all these examples run with zero configuration on the repository side.
--
(*) Mainly due to the fact that JCRs have already been around for a while there are additional aspects that make Sling/JCR stand out from other REST/unstructured data storage technologies:
- JCRs have been running production systems for a long while. It is fair to assume that the technology is proven. Along these lines: there are a number of tools available to work with JCRs and four different open source implementations of the JCR spec.
- JCRs allow users to work with highly structured as well as unstructured data side-by-side and move between those two at will.
(**) Have a look at the post "File Uploads in Sling" if you want to dive deeper into file uploads.
