Monday, August 19, 2013

Creating custom JCR node type

Sometimes it is required for application to have custom schema with application-specific namespace, node types and properties. Although David's model rule #1 is "Data First, Structure Later. Maybe", sometimes "maybe" becomes "must". So how I can create custom node type?

First of all, read carefully official JCR documentation: Node Types and Examples. ;-) And now let's implement it.

I. Create node type definition using CND descriptor. 

For simplicity we will put it into existing CQ namespace. (If you want to customize namespace, just define it alongside existing namespaces in CND file).

<cq  = 'http://www.day.com/jcr/cq/1.0'>
<sling = 'http://sling.apache.org/jcr/sling/1.0'>

//---- Custom Node Type ----

[cq:CustomNode] > nt:hierarchyNode, mix:title, mix:modified, mix:versionable
    orderable
    - * (undefined) multiple
    - * (undefined)
    + * (nt:base) = cq:CustomNode version

Here we define node type, which is hierarchical, contains jcr:created, jcr:modified properties and which supports versioning (mix:versionable). If your project is built using maven, put this file to the resources folder of your OSGI bundle. I use CQ-style and put it under CQ-INF/nodetypes folder.

II. Create OSGI listener which registers our node type during bundle activation.

Now you need to register newly created definition. For that you should invoke JCR API from the links above. In order to simplify things, we will register our node type during bundle activation, so we need to create an OSGI component.

package com.yvv.customnode.bundle;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.jackrabbit.api.JackrabbitNodeTypeManager;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.Session;
import java.io.InputStream;

@Component
public class CustomNodeTypeListener {

    private final Logger LOGGER = LoggerFactory.getLogger(CustomNodeTypeListener.class);
    public static final String NODE_TYPE_NAME = "cq:CustomNode";

    @Reference
    private org.apache.sling.jcr.api.SlingRepository repository;

    private Session session;

    protected void activate(ComponentContext context) throws Exception {
        session = repository.loginAdministrative(null);
        registerCustomNodeTypes(session);
    }

    protected void deactivate(ComponentContext componentContext) {
        if (session != null) {
            session.logout();
            session = null;
        }
    }

    public void registerCustomNodeTypes(Session session)
            throws Exception {
        JackrabbitNodeTypeManager manager = (JackrabbitNodeTypeManager) session.getWorkspace().getNodeTypeManager();
        if (manager.hasNodeType(NODE_TYPE_NAME)) {
            manager.unregisterNodeType(NODE_TYPE_NAME);
        }
        InputStream is = this.getClass().getClassLoader().getResourceAsStream("CQ-INF/nodetypes/cq-customnode.cnd");
        manager.registerNodeTypes(is, JackrabbitNodeTypeManager.TEXT_X_JCR_CND);
    }
}

III. Verify newly created node type.

Browse to http://localhost:4502/crx/explorer/nodetypes/index.jsp and under the tab "All registered node types" find cq:CustomNode, open it to view details.

IV. Create nodes using your custom type.

You can do it using i.e. curl command:
curl -D -u admin:admin -F"jcr:primaryType=cq:CustomNode" -F"title=some title" http://localhost:4502/content/customnode

2 comments:

  1. In case you are using eclipse, you can create complex CND files using cnd editor https://github.com/evra/cnd-editor . (Disclaimer: i'm the author and appreciate any feedback)

    ReplyDelete
  2. Appreciating the commitment you put into your blog
    and in depth information you present. It's awesome to come across a blog every once in a while that isn't the same out of
    date rehashed information. Fantastic read! I've bookmarked your site and
    I'm adding your RSS feeds to my Google account.


    my site; Payday Loans Vancouver Canada

    ReplyDelete