Latest Posts

Archives [+]

Categories [+]

Authors [+]

Archive for July 2008

    Posted by Michael Marth JUL 31, 2008

    Posted in ask the community and jcr Add comment

    Recently, I blogged about Brix, which is a new open source cms based on Apache Wicket and Jackrabbit. Brix has been developed by the Inertia Beverage Group. Inertia's Chief Strategy Officer Paul Mabray sat down with the dev team and passed on my questions about JCR and Jackrabbit:

    Paul Mabray: So tell me a bit about each of you - your background, hobbies, etc.

    Matej Knopp: Hi, I am Matej. I’m a key independent consultant from Slovakia. My number one project for Inertia is working on Brix and enjoying every minute of it. I’m also an Apache Wicket enthusiast and committer. Obviously I have a severe lack of free time.

    Paul: Do you do anything else?

    Matej: Whatever little time I find away from the keyboard I try to read as much as I can.

    Patrick Angeles: I guess, aside from you Paul, I am the veteran at Inertia. I am the VP of Technology and lead our SaaS development for a software solution that connects wineries to their customers. I’ve been in tech for over 10 years working for big companies like Bankers Trust and Acquire Systems. Food, wine and my family are the only things I have time to do outside of work.

    Igor Vaynberg: I’m the Principal Software Engineer at Inertia, residing in sunny Sacramento, California. I’m the guy your brought in to help build the next-generation enterprise web application that help will more easily bridge the gap between wineries and their customers.

    Paul: Tell me more

    Igor: Well, my love for writing software was sparked when I received a Sinclair Z80 on my tenth birthday and I’ve been doing it ever since. I do have the same hobbies as you such as playing with my kids, taking my beautiful wife to fancy dinners - that should get me points at home - and fragging my friends at Halo. I also still find time to work on software. My number one outlet is the Apache Wicket framework - I really believe to be the best way to build web applications.

    Paul: And I am the founder of Inertia and now Chief Strategy Officer in charge of product development and business development. Just like Patrick I only have time for the family outside of work. But I love what I do and when this team brought forth the concept to make Brix open source, I was 200% in support of the initiative.

    Igor/Patrick: COME ON

    Paul: OK, I frag a few people on Halo when I get a spare minute.

    Paul: So Michael asked us a question - Brix uses Apache Jackrabbit as its content repository. Back in the days when you made this decision what potential choices were you considering and what influenced your decision towards Jackrabbit?

    Igor: From the start, one of our major requirements was the ability for our users to edit content using their favorite desktop editor. We picked SVN and JCR as two possible solutions: SVN with its local check-in/check-out, and JCR with its WebDAV support. We started off with SVN because it had more powerful versioning features, such as merging. But we quickly ran into features we needed that were missing and would be too expensive to implement ourselves: referential integrity and ASLv2 java connector library being the two that jump to mind (we were planning to open-source Brix from the start under ASLv2).

    Patrick: That is why we switched to JCR. Jackrabbit seemed to be the most actively developed and complete JCR implementation, and since it was already released under ASLv2 it was not a very hard choice. A bonus for us is that two of our Brix developers are also Apache committers, and since Inertia is not shy about contributing to open source we saw an opportunity to improve Jackrabbit and give back to its community as we went along.

    Q: Did your expectations regarding the JCR come true or did you have to overcome some difficulties you did not expect? Were there any pleasant or not so pleasant surprises after working with Jackrabbit for a while?

    Matej: The most pleasant surprise was how much functionality Jackrabbit/JCR brought to the table: indexing, referential integrity, WebDAV, workspaces, versioning, and the list keeps going on. The API is consistent and easy to use.

    Igor: Don’t forget our experience with Jackrabbit/JCR was not perfect. It seems that the modus operandi of the JCR community is to use a repository with a set small number of workspaces, which did not mesh well with our needs. Brix makes an extensive use of JCR workspaces for multi-site support, site snapshots, and publishing workflow. This means that we needed to create and delete workspaces on the fly as well as have an easy way to search across them via some index.

    The first problem we ran into was that workspace creation event was not replicated across the Jackrabbit cluster. We have already patched Jackrabbit to support this feature, the patch is available in Jackrabbit’s issue tracker under JCR-1677.

    Patrick: The second, and more severe, problem was the way Jackrabbit manages database connections. Currently each Jackrabbit workspace keeps an open connection to the database. We are planning to host over 300 client sites on Brix in the near future. Brix’s publishing workflow requires 3 workspaces per site: development, staging, and production. Without even counting workspaces created for snapshots, we are up to 900 workspaces and thus 900 open database connections. This simply does not scale. Unfortunately, this does not look like a high priority for the Jackrabbit community, because seeminly the most common usecase is a repository with a small number of workspaces. Nonetheless, we are working on a patch that would allow for connection pooling. A first pass of the patch is available in the Jackrabbit issue tracker under JCR-1456.

    Q: One often discussed aspect of building content management systems on top of JCRs is the question if object-content-mapping should be used to wrap collections of JCR nodes into application-level objects. What is your view on this question?

    Igor: Brix’s object model is very simple, so using some kind of object-content mapping would have been overkill for us. We do have a wrapper for the JCR api, but that was meant for other things (see below).

    Q: In Brix you have wrapped JCR nodes and sessions in Brix' own wrapper classes. In the wrappers API calls are intercepted and events are generated. Why was this design decision made rather than using JCRs native Observation mechanisms?

    Patrick: Our JCR API wrapper is two levels deep.

    The bottom layer is an event layer. It provides events for before and after a change, as well as an ability to queue the events in a list and batch process them later. We also group and normalize the events so it is possible to know what is about to be changed before the change is applied. JCR’s native observation mechanism is limited to only observing changes that have already happened.

    Matej: The top wrapper has two basic jobs:

    Translate JCR’s checked exception model into unchecked while providing a pluggable and centralized way for exception handling strategies. In OOP JCR code is sprinkled among many classes, the checked exception model becomes extremely viral. Also, since a lot of exceptions are unrecoverable from Brix’s perspective there is very little point of having them be checked.

    The second job of this wrapper is to allow node wrapping. Instead of using a full blown and complex object-content-mapping system we provide a simple mechanism for having nodes wrapped based on their brix:nodeType property. This makes it possible to write code like this:

    Node n = session.getRootNode().getNode("page.html");
    PageNode page = (PageNode) n;
    page.setTitle("Page Title");
    page.setMarkup("Page Content");
    

    Q: Brix' WebDAV server has the nice feature that uploaded resources are automatically converted into Brix artifacts. How does that work?

    Igor: This is done by intercepting node events using the aforementioned wrapper and modifying the nodes before they are saved based on rules defined in Brix. There is also some magic to handle varying WebDAV client behaviors, for example: when a new file is created via Coda it will first create an Untitled file and immediately rename it to its given name.

    Q:If you had one wish regarding JCR and the JCR community, what would it be?

    Patrick: We would wish the JCR community a very long and productive future.

    Paul: I hope that the JCR community sees Brix as useful piece of software and contributes to its health and success.

    Posted by Jean-Michel Pittet JUL 31, 2008

    Posted in cms and communique Comments 6

    Here's a brief update on Communique 5's delivery: we are in excellent shape and the quality gated approach is paying of for our customers.

    As it was laid out in a previous post there are two iterations we need to go through before we release CQ5: the tech preview phase and the beta program. I am very happy to report that the tech preview has been successfully completed. By now, the beta program has started. I'm excited to see the shine in the developers eyes when they are allowed to take this baby into their hands.

    The customer who completed the tech preview with us is now in production with CQ5. There are about 80 editors producing content already. This number is expected to increase into several hundred very soon. This is a fantastic success, but there are also other ("softer") factors that we have experienced during the tech preview project. Both, the editors as well as the developers perceive working with CQ5 to be very pleasant and exciting compared to the competing products they knew. I am extremely happy about this feedback as it goes to show that our efforts on user experience and software architecture were well invested.

    There is also another interesting by-product of the beta program: by the time the program is finished most of our service and support team will have real-life experience with CQ5 from real-life customers in production. So from day one of the product launch there will be experienced service and support resources available from Day.

    Posted by Michael Marth JUL 30, 2008

    Posted in sling Comment 1

    One topic that periodically comes up is how to integrate data that resides relational databases with a Sling application. The use case might be that there is legacy data that cannot be migrated but needs to displayed in a new (Sling-based) web application. In this post I would like to discuss some aspects and possible strategies to accomplish this goal.

    One essential question for determining a fitting integration strategy is if the db rows shall be exposed as resources (in the REST sense). Let us start with the case that they shall not be exposed as resources. In such a scenario a db row does not have its own URL. A typical Sling application in such a scenario would expose the JCR-based content as resources, but might add data from the db to the presentation of the resources.

    On a technical level this could be implemented by creating an OSGi bundle that uses JDBC to connect to the database. The bundle would then be called by the rendering script through an OSGi service interface (as described here).

    However, this approach cannot be used if the db rows shall be exposed as resources and have their own URL.

    For data that changes on low frequencies one can create a JCR node for each db row. This requires the ability to keep the db and the JCR in sync. The JCR nodes could contain the primary key of the db data. Synching can be achieved by any programm capable of JCR and JDBC.

    One seemingly obvious way to avoid having to synch is to create a dummy node that represents all of the db data. However, the resulting URLs would not be resource-oriented at all since you need to pass the resource identifier as a request parameter.

    The full monty and best solution for making your db URLs RESTful is to create a Sling ResourceProvider. This would be an OSGi bundle that claims a part of the URL space in Sling and maps the URLs to resources of whatever kind. In the Sling samples there is an example implementation for file systems. In this example a tree of the local file system is mapped onto the URL space and all files are given Sling resource types. As such, the normal mechanisms for rendering requests are used. The same can be achieved with db rows, of course.

    It should be noted that there is a generic problem when the relational data needs to be mapped onto the hierarchical URL space. You could go the brute force way of using /content/tablename/id. However, if your data is somehow categorized using many-to-one relationships consider "denormalizing" the data (mentally - you do not need to actually change the tables). This would yield nicer URLs like /content/category1/category2/data (e.g. like /content/wagons/mercedes/e-class).

    Posted by Michael Marth JUL 28, 2008

    Posted in cms and jcr Comments 2

    David Nuescheler has a slide that compares JCR-based applications to an iceberg (see e.g here, first presentation, slides 18 and 19). No, not in the sense that it will sink the Titanic, but in the sense that the visible part is only a small fraction. Well, proving things that are hidden by definition is often a bit tricky. In the case of JCR-based apps one can refer to the traffic on the Jackrabbit user list or the number of Jackrabbit downloads.

    But once in a while one can actually look at a previously hidden JCR application: a couple of days ago the JCR-based CMS "Brix" has been open-sourced. It is based on Wicket and Jackrabbit. From what I understand its origins are in online wine sales (note to self: next time ask for a content sample before posting).

    In their own words:

    Using Apache Wicket as the technology to serve the content makes it very easy to embed custom, stateful Wicket components into any CMS page, allowing rich integration with existing Wicket web application. Using Apache Jackrabbit allows Brix to easily integrate full text search, versioning, and WebDav access.

    Thanks for making this available, guys.

    Posted by Michael Marth JUL 24, 2008

    Posted in crx and screencast Comments 2

    Here's a 3 minute screencast about Day's JCR Connector for MS Sharepoint (which makes available Sharepoint servers as JCR-compliant repositories). If you need your Java apps to retrieve content from your corporation's Sharepoint servers: this is for you.



    Posted by Lars Trieloff JUL 22, 2008

    Posted in sling and tutorial Comments 8

    My approach to learning a new technology (or-relearning something that changed several times since I last spend time with it) is writing documentation. As I wanted to write a small Sling application using the newly released Apache Sling 1.0, I decided to create a one-page documentation of the Script-resolution process in Sling. This is the frontside of my cheat sheet. It shows you how to get from HTTP request to content node, from content node to resource type, from resource type to script and what scripting variables are available.

    On the flipside of the cheat sheet I documented all the hidden, but powerful request parameters you can use when dealing with the SlingPostServlet, the default handler for all POST requests that gives you endless options for creating, modifying, deleting, copying and moving nodes in your repository.

    Click on the thumbnails to get a high-resolution PDF version of either side (3,7 and 1,7 MB) or download the PDF version (300 KB) here.

    Posted by Michael Marth JUL 15, 2008

    Posted in dynamic languages, jcr, rest and sling Comments 3

    Apache Sling is a game changer for Java Content Repositories in two different ways:

    1. 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.
    2. 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:

    (**) Have a look at the post "File Uploads in Sling" if you want to dive deeper into file uploads.

    Posted by David Nuescheler JUL 14, 2008

    Posted in agile, jcr and sling Comments 2

    In case you could not make it to this year's Jazoon: here's the two presentations I gave on Agile Java Web Development (together with Bertrand Delacretaz) and Microsoft Sharepoint and Java Application Development, respectively.

    In the Sharepoint presentation I discussed various integration options between Java-based development and Sharepoint and gave a demo on JCR-based integration.

    The second presentation covers various considerations about JST-based development of web apps (like security and accessibility). It is a follow-up to demos that were also covered in the screencasts First Steps with CRX Quickstart and TheServerSide.com in 15 minutes.



    Posted by Michael Duerig JUL 14, 2008

    Posted in dynamic languages and jcr Add comment

    Recently I read Michael Marth's article Extending JCR with JRuby. Michael shows how to extend existing JCR interfaces with JRuby. He adds a method getAllNodes to JCR's Node interface. The getAllNodes method returns the nodes of the whole sub-tree of the current node. He further adds a method to Repository which makes using the getDescriptor method more convenient.

    In this article I show how the same can be done with Scala. In contrast to JRuby, Scala is a statically typed language. Re-opening existing classes is not directly possible. However, using implicit definitions and the Pimp my Library pattern, a similar effect can be achieved while fully maintaining type safety.

    First we add two functions to Node. The childNodes function returns a Scala Iterator over the direct child nodes of a node. This function is just a wrapper around the NodeIterator returned by getNodes. This is necessary since the allChildNodes function needs the richer functionality provided by Scala's iterators.

    implicit def richNode(node: Node) = new RichNode(node)
    
    class RichNode(node: Node) {
      
      def childNodes = new Iterator[Node] {
        val it = node.getNodes
        def hasNext = it.hasNext
        def next = it.nextNode
      }
    
      def allChildNodes: Iterator[Node] = {
        (for(n <- childNodes) 
           yield n.allChildNodes)
             .foldLeft[Iterator[Node]](childNodes)(_ ++ _)
      }
    
    }
    

    The for expression in allChildNodes recursively retrieves all child nodes of the direct child nodes of a node. The returned iterators are subsequently concatenated together by passing them to the foldLeft function together with the direct child nodes and the concatenation operator ++.

    We can now use

    for (n <- node.allChildNodes)
      println(n.getName)
    

    to print the names of all nodes in a given node's sub-tree. When Scala cannot find the allChildNodes function on Node, it automatically converts the node to an instance of a RichNode as long as the implicit conversion function richNode is in scope.

    We can now extend Repository in a similar way:

    implicit def richRepository(repository: Repository) = new {
      def descriptor(name: String) = {
        if (repository.getDescriptor(name) == java.lang.Boolean.toString(true))
          true
        else if (repository.getDescriptor(name) == java.lang.Boolean.toString(false))
          false
        else 
           repository.getDescriptor(name)   
      }
    }
    

    Note the use of an anonymous class instead of explicitly declaring a RichRepository class. (Quiz: is it possible to use an anonymous class instead of RichNode in the first example too? How?)

    We can now check for observation support like so:

    if (repository.descriptor(Repository.OPTION_OBSERVATION_SUPPORTED) == true)
      println(Repository.OPTION_OBSERVATION_SUPPORTED)
    

    Although it might look strange, explicitly comparing to true is necessary here. The reason is, that the return type of the descriptor function is not a Boolean but rather the least upper bound of the types in all branches of the if-else statement. The least upper bound for types Boolean and String is the type Any.

    Posted by Michael Marth JUL 10, 2008

    Posted in sling and tutorial Comments 8

    Here's another little example application that demonstrates how to use Apache Sling (and its underlying Java Content Repository). The app is called "Notes". It allows users to save a written note and attach a zip file to it. Notes can be edited which creates a new version (old versions remain in the repository). They can also be commented upon in a hierarchical way (i.e. comments can get comments again). Comments are also versioned. Finally, you can search through existing notes.

    The app is packaged with CRX Quickstart's Package Manager. In order to install it just fire up the Package Manager and select Upload Package. This will install the scripts at /apps/notes as well as some initial content. After that just hit http://localhost:7402/content/notes.html.

    Notes is designed to show off a number of JCR/Sling features like hierarchical storage, binary data storage, search, versioning and Ajax (via JSON). The latter will be visible in the search results page: the Quick View link that appears behind a search result retrieves the comments thread on the fly using Sling's built-in JSON renderer (try search term "jcr"). Comment versioning will become apparent when you edit an existing comment: there will be a new link "old versions".

    A quick tour of the code

    The front page is rendered by html.jsp. In there, child nodes are retrieved and rendered directly (by accessing their properties) The real Sling way is shown in the thread view (file thread.jsp): child nodes are rendered by using sling.include and the selector "detail". For this, the file detail.jsp is invoked recursively down the comment tree.

    Posting new threads and comments is handled by POST.jsp. I decided to implement this a) for instructive reasons and b) because I wanted all nodes to be versionable (so I add the mixins mix:versionable and mix:referenceable to new nodes).

    search.jsp demonstrates how to perform a search over the content repository and how to retrieve and use nodes via JSON. The URL /path/to/node.-1.json will rerurn the full sub tree below a node.

    I gave up on my attempt to write a css file. The one that is included is from bluerobot.com. If you want to delete the initial content make sure that there is still a node left at /content/notes. This is assumed by the app (like many other things). In fact, this is not to be considered a real app, but just a sample to get you started with content-centric apps on Sling. Hence, it includes some sub-optimal aspects (feel free to fix them...): node UUIDs are exposed as anchors, node names are ugly and file uploads are not mime-type aware (there is probably more).

    Posted by Michael Marth JUL 07, 2008

    Posted in sling and tutorial Comment 1

    In the JCR Cup support forum there was an interesting question regarding user-generated content. In particular it was asked how users could upload content in Apache Sling. Here's some information about that:

    Basically, just using a form with the POST method and the proper encoding will already do the job:

    <form action="/content/firststeps/uploads/*" method="POST" enctype="multipart/form-data"><h2>Name</h2><p><input type="text" name="title" /></p><p><input type="file" name="myfile" /></p><p><input type="submit"></p></form>

    Posting this form will create a new node below /content/firststeps/uploads that contains the included file "myfile" as a child.

    Further options that can be used are available and documented in the SlingFileUploadHandler.java's comments:

    * Simple example: * <xmp> *   <form action="/home/admin" method="POST" enctype="multipart/form-data"> *     <input type="file" name="./portrait" /> *   </form> * </xmp> * * this will create a nt:file node below "/home/admin" if the node type of * "admin" is (derived from) nt:folder, a nt:resource node otherwise. * <p/> * * Filename example: * <xmp> *   <form action="/home/admin" method="POST" enctype="multipart/form-data"> *     <input type="file" name="./*" /> *   </form> * </xmp> * * same as above, but uses the filename of the uploaded file as name for the * new node. * <p/> * * Type hint example: * <xmp> *   <form action="/home/admin" method="POST" enctype="multipart/form-data"> *     <input type="file" name="./portrait" /> *     <input type="hidden" name="./portrait@TypeHint" value="my:file" /> *   </form> * </xmp> * * this will create a new node with the type my:file below admin. if the hinted * type extends from nt:file an intermediate file node is created otherwise * directly a resource node.

    Case closed? Not quite. What if you would want to do some fancy processing of the incoming request, say, check if it contains a virus. In that case you need to implement the file /apps/firststeps/POST.jsp (note the capitalized file name) yourself in order to overwrite Sling's default handling of POST requests. It could look like this:

    <%@page import="javax.jcr.Session"%><%@page import="javax.jcr.Node"%><%@page import="java.io.ByteArrayInputStream"%><%@page import="java.util.Calendar"%><%@taglib prefix="sling" uri="http://sling.apache.org/taglibs/sling/1.0"%><sling:defineObjects/><%// get the root nodeSession mySession = slingRequest.getResourceResolver().adaptTo(Session.class);Node root = mySession.getRootNode();// create a new node of type nt:fileNode myNewNode = root.getNode("content").addNode("mynewnode", "nt:file");Node contentNode = myNewNode.addNode("jcr:content", "nt:resource");// set the mandatory propertiescontentNode.setProperty("jcr:data", new ByteArrayInputStream(request.getParameter("myfile").getBytes()));contentNode.setProperty("jcr:lastModified", Calendar.getInstance());contentNode.setProperty("jcr:mimeType", "mymimetype");mySession.save();%>

    This jsp creates a new node of type nt:file at /content/mynewnode and stores the incoming file in the property jcr:data of the child node jcr:content. It also needs to write the required properties jcr:lastModified and jcr:mimeType. In order to do this a session is required which is retrieved from the request object. Also note the necessary <sling:defineObjects/> tag.

    The jsp essentially does the same as the default handler does. However, you could also do something completely different as well. For example, you could store incoming files below a different folder or add other custom properties or child nodes.

    JCR EventListeners

    There is also another way of dealing with incoming files in a custom manner: one can use the first simple HTML form and have a JCR EventListener listen for incoming files. Once the listener gets an event it can do whatever custom logic it requires.

    If you are new to JCR Observations you might first want to look at O'Reilly's tutorial on advanced JCR where JCR Versioning and Observations are introduced.

    EventListeners usually run in a separate JVM, so the first thing it must do is connect to the repository. If you want to do this via RMI and use CRX make sure that RMI connections are enabled. In CRX Quickstart RMI is configured in crx-quickstart\server\runtime\0\_crx\WEB-INF.web.xml. The default is switched off (I believe). Uncomment the RMI section below the part

        <!--        RMI configuration    -->

    and restart your server. Afterwards register an event listener like this by running the code in a seperate JVM (the full code is attached):

    public class MyUploadListener {  public static void main(String[] args) throws MalformedURLException, ClassCastException, RemoteException, NotBoundException, LoginException, RepositoryException, InterruptedException {    ClientRepositoryFactory factory = new ClientRepositoryFactory();    Repository repository = factory.getRepository("//localhost:1234/crx");    Session session = repository.login(new SimpleCredentials("admin", "admin".toCharArray()));    ObservationManager observationManager = session.getWorkspace().getObservationManager();    String[] types = {"nt:file"};    observationManager.addEventListener(new EventListener() {      public void onEvent(EventIterator eventIterator) {        while (eventIterator.hasNext()) {          Event event = eventIterator.nextEvent();          if (event.getType() == Event.NODE_ADDED) {       try {              System.out.println("new upload: " + event.getPath());       } catch (RepositoryException e) {         e.printStackTrace();       }     } }      }    },    Event.NODE_ADDED,"/content/firststeps/uploads",true,null,types,false);        while(true) {      System.out.println("I am still here");        Thread.sleep(5000);    }  }}

    The code above is a straight forward EventListener that listens for added nodes of type nt:file at path /content/firststeps/uploads. However, note the clunky endless loop in the end. It is required for not losing the connection to the repository.

    This ugly endless loop and enabling RMI can be avoided by registering the EventListener within an OSGi bundle that is deployed in Sling's OSGi container. When an OSGi service gets started the container calls the activate method. In there we can register the EventListener like this:

    public class ListenerServiceImpl implements ListenerService {  /** @scr.reference */  private SlingRepository repository;  private static final Logger log = LoggerFactory.getLogger(ListenerServiceImpl.class);  public void log(String text) {    log.error(text);  }  protected void activate(ComponentContext context) {    try {      Session session = repository.loginAdministrative(null);...    } catch (Exception e) {      log("cannot start");    }}

    Note that in this case the session is retrived through

    Session session =  repository.loginAdministrative(null);

    so we are logged in as admin without the need for an RMI connection. Apart from that the JCR code is the same as in the example above. A full tutorial on how to write and deploy OSGi bundles with Sling can be found here and here. The Maven pom.xml and the Java code for the complete bundle is also attached to this post.

    A crucial difference between the methods involving EventListeners and those that do not is that EventListeners operate on an admin session (or similar technical user). If you use the POST requests the code is executed on behalf of the logged in user. Depending on the circumstances this might be beneficial or not. For example, if you intend to move incoming files to a folder where only admin has write access you need to use the EventListener.

    Posted by Kia Puhm JUL 03, 2008

    Posted in announcements Comment 1

    I am happy to announce Day KB, the new source of Day product information and answers to common technical questions.

    KB is a Wiki space that contains questions and their answers to common issues users run into when developing or authoring with our products. The basis for the existing content came out of Daycare tickets (that were reviewed and scrubbed to remove private information). New content will be added regularly by KB moderators. Please provide your ideas for new topics and articles, plus any feedback to the Day KB moderators at kb@day.com

    Go to dev.day.com/kb, for the open access CRX space. From there, Day customers can log in to the Communiqué space with their existing docs.day.com user ID and password.

    Posted by Michael Marth JUL 03, 2008

    Posted in jcr and tools Comment 1

    There is a new kid on the block of (explorer-like) JCR tools: Craig Ching is currently developing a web browser-based application to visualize and edit contents of a Java Content Repository. The application is built upon the Dojo Toolkit and Apache Sling. It will probably make it into the standard Sling releases at some point (see the discussion on the mailing list).

    Sling JCR Explorer is not quite ready for production use, but you can find a demo version here (today the site seems to be down, unfortunately). The nice use of Dojo's AJAX capabilities makes it indeed a worth a look.

    I welcome this latest addition to the JCR tools arsenal as I do strongly believe that a good set of developer tools is essential for further adoption of JCR. In this context I have compiled a list of JCR tools for browsing repositories that I am aware of. I refrained from doing proper reviews since I am not really impartial. Please find the list below and leave a comment In case I missed a tool.

    Web-based tools

    CRX Explorer

    The tool I spend most of my time with is Day's CRX Explorer that comes bundled with CRX and CRX Quickstart. Rumor has it that CRX Explorer might make its way into the Jackrabbit project.

    JCR-Explorer

    Hendrik Beck has developed a similar and feature-rich web-based application named JCR-Explorer. One particular useful feature is the ability to observe ongoing changes in the repository (leveraging JCR's observation features).

     

    Hendrik used JSF (JBoss RichFaces) to build JCR-Explorer. It is open-sourced under the Apache License.

    Eclipse-based tools

    Day's Eclipse plugin

    Day has provided an Eclipse plugin to connect to and manipulate Jackrabbit or CRX-based Content Repositories. The plugin can easily be installed through Eclipse's Update Manager. See the plugin's home page for instructions.

     

    Nodes can be edited and changes be commited one by one. A full list of features is available in Eclipse's Help section once the plugion is installed (for the Catch-22 fans: there is also a section on how to install).

    JCR Browser

    Sandro Böhme has also written an Eclipse Plugin for viewing (but not editing) JCRs and open-sourced it under the Apache License.

    In the project's own words:


    Visualizes the content of a JSR 170 compliant Java content repository in Eclipse. It focuses on reliability by keeping the scope on displaying the content.

    JCR Viewer

    A second Eclipse Plugin focused on simply displaying nodes is Shailesh Mangal's appropriately named JCR Viewer. It seems to have been a by-product of developing the test management tool Zephyr which is based on JCR.

    The binaries are available from his blog.

    Others

    JCR Controller

    Jacco van Weert has developed a repository browser as a Java Web Start application (as such it sits somewhere inbetween web-based and Eclipse-based solutions).

    JCR Controller is open sourced (LGPL license) and available at Sourceforge. To see it in action check out the project's home page.

    Posted by Michael Marth JUL 02, 2008

    Posted in crx, crx quickstart, screencast and tutorial Add comment

    Before Sling was released there was a change in the way Sling finds application files (e.g. .jst or .esp). While this made things easier it also broke the examples I presented in the screencasts here and here. So here is some update information on how to fix this:

    The first screencast's code is included as an example application with CRX Quickstart. So if you just stick to the example app structure you will be fine.

    For the second screencast we started out with a directory like this (leaving out the image folder):

    tss
    |-index.html
    |-admin
      |-index.html
    |-post
      |-index.html
    |-thread
      |-index.html

    and ended up (after the code was changed in the course of the screencast) with:

    tss
    |-index.jst
    |-admin
      |-index.jst
    |-post
      |-index.jst
    |-thread
      |-index.jst

    Now, in the current version the final directory structure must be:

    tss
    |-html.jst (this was the home page)
    |-admin.jst
    |-post.jst
    |-thread.jst

    So, for example in the first step I rename tss/admin/index.html to tss/admin/index.jst. Now, it should be renamed and moved to tss/admin.jst and so on. For clarity, I have attached the new resulting code to this post.

    Posted by Michael Marth JUL 02, 2008

    Posted in atom, atompub and jcr Add comment

    Mule Galaxy 1.0 has been released 2 days ago. It is (in their own words)...

    an open source governance platform with an integrated registry/repository. It includes versioning, lifecycle management, dependency management and policy enforcement features which enable you to effectively govern your applications and services

    Galaxy uses JCR (Apache Jackrabbit) internally and Atom Publishing Protocol for custom integrations. As such it constitutes a nice example for JCR and APP complementing each other.

    Another comment that is only slightly related: I just came across Galaxy developer Dan Diephouse's presentations on Effective RESTful services and AtomPub. Excellent stuff (and slide 40 of the first one made me lol).

    Posted by Michael Marth JUL 02, 2008

    Posted in dynamic languages Add comment

    Working on a JRuby script that accesses a Java Content Repository I was missing a method on javax.jcr.Node that would return the whole subtree of child nodes. Ruby and the JRuby implementation allow to re-open classes and add new code. In that way one can easily extend even core classes (or interfaces as in this case):

    JavaUtilities.extend_proxy("javax.jcr.Node") do
      def getAllNodes
    	all = Array.new
    	children = self.getNodes
    	children.each {|child| all.push(child);
    	             all.push(child.getAllNodes)}
    	all.flatten
      end
    end
    

    This extends regular JCR nodes with a method getAllNodes that returns a flat array of all child nodes. Speaking of fixing issues one might have with existing objects: recently, I was unhappy with the fact that repository.getDescriptor( Repository.OPTION_OBSERVATION_SUPPORTED) returns a String instead of a Boolean because it makes me write code like:

    if (repository.getDescriptor(
      Repository.OPTION_OBSERVATION_SUPPORTED)
      .equals("true"))
    

    The fix is obviously:

    JavaUtilities.extend_proxy("javax.jcr.Repository") do
      def getRealDescriptor(name)
        if self.getDescriptor(name) == "true" ||
          self.getDescriptor(name) == "false"
          self.getDescriptor(name) == "true"
        else
          self.getDescriptor(name)
        end		
      end
    end
    

    (Actually I wanted to use alias_method to redefine getDescriptor instead of defining a new method, but could not figure out how to do this on an interface. If you know it, please leave a comment.)

    Posted by Michael Marth JUL 02, 2008

    Posted in apache and open Add comment

    Following up to my statement that Day is closely related with the open source community I have compiled some facts to support this claim in this post. Broadly speaking Day's support for open source software and open standards can be divided into four areas:

    1. Day Developers are OSS Developers

    Day's developers are have contributed to a wide range of open source projects (either as part of their employment, in their spare time or before joining Day). The numbers are:

    2. Work at the Apache Software Foundation

    Day's developers contribute to the Apache Software Foundation (ASF) in a number of ways:

    • Day's Chief Scientist Dr. Roy T. Fielding is co-founder of the ASF and its former chairman.
    • Day developer Bertrand Delacretaz has been elected as one of nine members of the ASF's Board of Directors.
    • The ASF currently has 267 formally elected members of the nonprofit foundation. Membership is by invitation only. Candidates for membership are proposed by existing members, and voted upon by the existing membership. At the moment of those 267 members ten are Day employees.
    • Officers of The Apache Software Foundation oversee the day-to-day operations of the Foundation. The V.P.s are assigned to specific projects of the ASF. Currently, there are three V.Ps. that are Day employees: Roy T. Fielding (Apache HTTP Server), Jukka Zitting (Apache Jackrabbit) and Carsten Ziegeler (Apache Excalibur)

    3. Open Source Projects

    Day's commercial products for content management and content infrastructure are in their core based on two open source projects: the content repository Apache Jackrabbit and the web framework Apache Sling.

    Day's developers have contributed to Jackrabbit's code base and also actively take part in the community (especially on Jackrabbit's mailing lists). As a company Day offers commercial services for Jackrabbit like consulting or implementation of customer-specific features.

    Apache Sling is a web framework built upon the REST architectural style. In November 2007 Sling was open sourced. Previously, it was an internal project at Day. Hence, a very large part of the code base has so far been written by Day's developers.

    Although Sling is a young project it has already won an award at Jax's innovation awards. Moreover, the OSGi console for managing OSGi bundles that was developed within the Sling project has been handed over to Apache's OSGi project Felix.

    4. Open Standards

    Open source software and open standards are related. Therefore, Day's employees actively contribute to the specification of open standards:

    • Day's CTO David Nüscheler has led the specification JSR-170 for Java Content Repositories (JCR). JCRs have gained broad acceptance in the open source community: there are four different open source implementation of the standard already. David is currently working on the successor JSR-283.
    • Chief Scientist Roy Fielding authored the standards for URIs (RFC 3986/STD 66) and HTTP (RFC 1945, 2068, 2616), and also contributed to early HTML (RFC 1866).

    In this context one should also mention Roy's work on the REST architectural style. It is not a standard in itself, but serves as a blueprint for information architectures that are based on open standards.

    Posted by Michael Marth JUL 01, 2008

    Posted in sling Add comment

    Hugo Schottmann has posted on his blog a video of David Nüscheler's presentation about Apache Sling at Webtuesday in Zurich.