For Rave JCR content services sandbox project we need a way to easily get content in and out of the repository.

Content Import/Export

JCR already provides XML import and export, however, the format is not very human-friendly and no merge functionality is supported.

Feature list:

  • Support for JSON format content description ()
  • Resolve path-based reference type properties
  • CND import support
  • Import order and dependency resolution
  • Per-item import settings for node and property merge behavior

Out of scope:

  • ACLs / security
  • Same name sibling support

Bootstrap/Initialization

This feature builds on the Json-based import functionality with merge capabilities defined above. It adds automatic scanning of jars for content and cnds to import and updates a registry of initialize items that have been bootstrapped.

Feature list:

  • Registration of imported content
  • Bootstrap order based on dependencies between content modules/jar files
  • Optional version-based reloading

Implementation Documentation

A content module is a jar file that contains a module descriptor at /META-INF/rave/module.json. This module descriptor is a json file that describes the content that is packaged in this module and contains instructions for the bootstrapping system concerning the content to load. Here is an example:

{
  # module meta data: name must be unique within an application, version may be any string
  "name" : "foo",
  "version" : "1.0",
  # module dependencies refer to other modules by name which are loaded before the current one
  "dependencies" : [ "bar", "quz" ],

  # register namespace mappings
  "namespaces" : {
      "foo" : "http://foo.com/1.0"
  },

  # register node types
  "cnds" : {
    "foo" : {
      "file" : "foo.cnd",
      "reload" : false
    }
  },

  # bootstrap content described in json format to be loaded
  "contents" : {
    # the following will create the content from foo.json under node /testroot with whatever properties and subnodes
    # are defined in foo.json
    "foo" : {
      "file" : "foo.json",
      "parent" : "/testroot"
    },
    "bar" : {
      "file" : "bar.json",
      "parent" : "/testroot",
      "reload" : false,
      "importBehavior" : "merge",
      "workspace" : "myws"
    }
  },

  # plain jar resources to be imported as nt:folder and nt:file nodes
  "resources" : {
    "quux" : {
      "path" : "quux",
      "parent" : "/testroot"
    }
  }
}

A module has a name that must be unique in the application in order for dependency resolution between modules to work properly. It may have a version and it may have a list of dependencies, the values of which are the names of other content modules to be loaded before the current one.

Beyond this meta data there are four sections that describe how to bootstrap a repository. A module can register namespaces, cnds, content, and resources.

CNDs or Compact Nodetype Definitions are files that define JCR node types. Register them in the module descriptor in the "cnds" object. A cnd declaration consists of the path to the cnd file in the jar and an optional "reload" property of type boolean which is explained later.

The "contents" section registers json content descriptor files in the module, again using the "file" field to hold the path to the content file in the jar. Such a content file is a json representation of nodes and properties to be created in the repository. This json representation is in the following format:

{
  # name of a node to create or merge with
  "exampleNode" :
  {
    # optional primary node type, defaults to nt:unstructured
    "jcr:primaryType" : "nt:folder",
    # optional mixin node types as array
    "jcr:mixinTypes" : [ ],

    # properties are added as key value pairs, the name of the key being the name
    # of the property. The value is either the string, a number, a boolean, or an array for
    # multi-values
    "exampleProp": "value",
    "exampleMultivaluedProp": [ "value1", "value2" ],

    # reference properties start with jcr:reference and may be either paths or uuids
    "jcr:reference:exampleReference": "/test/content",

    # path properties start with jcr:path
    "jcr:path:examplePath": "/test/path",

    # name properties start with jcr:name
    "jcr:name:exampleName": "name",

    # sub nodes are defined as nested objects
    "exampleSubnode": {}
  }
}

The "parent" field determines where in the repository the content should be loaded and the optional "workspace" field determines in what alternative workspace (which will be created if it doesn't already exist). Content items also support the "reload" and "importBehavior". The "importBehavior" directive determines what happens when the node defined by the content file already exists. Valid values are "replace", "merge", and "skip". Replace: the old content is removed and replaced with the new content. Merge: an attempt is made to merge the new content with the old content. This will fail if incompatible primary types are encountered. Skip: no import will take place.

"resources" are paths to folders or files within the jar that should be bootstrapped as nt:folder and nt:file nodes respectively. This occurs recursively in the case of folders.

If cnd or content items are marked with the optional directive "reload" they are re-imported whenever the version of the module changes. In the case of CNDs a reregistration takes place. Note that reregistration of node types may fail due to incompatible changes to the node types. In the case of content and resource items the nodes are removed and reimported. Content items that are expected to merge with existing content do not support reloading: i.e. when "importBehavior" has the value "merge" no reloading can be done.

  • No labels