JBoss Community Archive (Read Only)

ModeShape 5

Defining custom node types

One of the important features of JCR is that it allows your applications to define and use custom node types, which can be used for either primary types or mixin types. This section talks about how to define and use the node types, property definitions, and child node definitions.

Compact Node Definition (CND)

The JCR 2.0 specification defines a file format called "Compact Node Definition", or CND. True to its namesake, the format does indeed make it possible to define node types in a very compact form. It supports Java-style comments, uses JCR-style namespace prefixes, and does not require whitespace or newlines around key characters (e.g., '[', ']', '>', ',', '(', ')', '=', and '<').

This documentation is a summary of the CND format. For the true specification of the format, see Section 25.2 in the JCR 2.0 specification.

Here is a small CND file that defines just a few of the 33 built-in node types, and hopefully gives you an example of what CND files look like:

<jcr='http://www.jcp.org/jcr/1.0'>
<nt='http://www.jcp.org/jcr/nt/1.0'>
<mix='http://www.jcp.org/jcr/mix/1.0'>

/* Every node type directly or indirectly extends from 'nt:base' */
[nt:base] abstract
  - jcr:primaryType (name) mandatory autocreated protected compute
  - jcr:mixinTypes (name) protected multiple compute

[nt:unstructured] orderable
  - * (undefined) multiple
  - * (undefined)
  + * (nt:base) = nt:unstructured sns version

[mix:created] mixin
  - jcr:created (date) protected
  - jcr:createdBy (string) protected

[nt:hierarchyNode] > mix:created abstract

// The 'nt:file' and 'nt:folder' node types allows applications to store
// files and directories as content inside a repository
[nt:file] > nt:hierarchyNode
  + jcr:content (nt:base) primary mandatory

[nt:folder] > nt:hierarchyNode
  + * (nt:hierarchyNode) version

[mix:referenceable] mixin
  - jcr:uuid (string) mandatory autocreated protected initialize

[mix:mimeType] mixin
  - jcr:mimeType (string)
  - jcr:encoding (string)

[mix:lastModified] mixin
  - jcr:lastModified (date)
  - jcr:lastModifiedBy (string)

[nt:resource] > mix:mimeType, mix:lastModified
  - jcr:data (binary) primary mandatory

Let's break apart the format into the different parts.

Declaring namespaces

A namespace is declared using the form:

< prefix = uri >

where prefix is the quoted or unquoted string literal used in the rest of the file as the prefix for the namespace, and uri is the quoted URI for the namespace. For readability, most people place each namespace declaration on a separate line near the top of the file, but that's not required.

Declaring node types

Each node type declaration is made up of several parts. The first is the specification of the node type's name and attributes, and is of the form:

[ prefixedName ] > supertypes attributes

where prefixedName is the name of the node type in prefixed notation (e.g., "nt:base", "nt:unstructured", "mix:lastModified", etc), supertypes is the optional comma-separated list of the prefixed names of the node type's supertypes, and attributes is the optional list of node type attributes:

Attribute Keywords

Description

orderable
ord
o

The node type supports orderable children. If absent, then orderable children are not supported.

mixin
mix
m

The node type is a mixin. If absent, the node type can be used as a primary type.

abstract
abs
a

The node type is abstract and cannot be directly used as nodes' primary type or mixin type. If absent, the node type is concrete.

noquery
nq
query
q

Specifies whether the node type can be queried. If 'noquery' or 'nq' are used, then the node type cannot be queried; if 'query' or 'q' are used, then the node type can be queried. If neither are specified, ModeShape assumes that the node type can be queried. If multiple are specified, the last one is used.

primaryitem
!

The string following this keyword specifies the prefixed name of the property or child (including same-name-sibling index if required) that will be considered the primary item , which is the child that can be navigated using a special method and allows a client to more easily navigate an unknown structure. If absent, the node type does not have a primary item.

After the attributes are listed the property definitions and child node definitions.

Property definitions

Each property definition begins with a '-' character and follows the form:

- prefixedName ( type ) = defaultValues attributes

where

  • prefixedName is the name of the property definition in prefix form

  • type is the case-insensitive name of the JCR property type, and is one of: STRING, BINARY, LONG, DOUBLE, DATE, BOOLEAN, NAME, PATH, REFERENCE, WEAKREFERENCE, URI, and DECIMAL

  • defaultValues is an optional comma-separated list of quoted string literals containing the string representation of the default values; multiple are allowed for multi-valued properties

  • attributes is the optional list of property definition attributes:

Attribute Keywords

Description

mandatory
man
m

The parent node must have at least least one property to which this property definition applies.

autocreated
aut
a

The property is automatically created when the node is created with the node type as primary type, or when the node type is added as a mixin to a node. If absent, the property is not auto-created.

protected
pro
p

The property to which this definition applies is protected, meaning it can be read but not modified by client applications. When absent, the property can be set by client applications.

multiple
mul
*

The property is multi-valued. If absent, the property is single-valued.

COPY
VERSION
INITIALIZE
COMPUTE
IGNORE
ABORT

The specification for how the property is to be handled when the node is versioned. When absent, the default versioning is COPY.

< constraints

The quoted string literals containing regular expressions or exact matches for the values. When constraints are provided, every value must satisfy at least one constraint on the property definition. If absent, there are no constraints on the values.

queryops
qop

This keyword is followed by a quoted string literal containing the comma-separated operators that can be used in property comparison constraints against this property. If absent, all possible operators are used, and this is equivalent to specifying '=, <>, <, <=, >, >=, LIKE'.

nofulltext
not

Specifies whether the property value(s) should be considered when performing a full-text search. If absent, the values will be used in full-text searches.

noqueryorder
nqord

Specifies whether the property can be ordered in queries. If absent, the property can be used to order query results.

Child node definitions

Each child node definition begins with a '{+}' character and follows the form:

{+} prefixedName ( requiredTypes ) = defaultType attributes

where

  • prefixedName is the name of the property definition in prefix form

  • requiredTypes is optional comma-separated names of the required node types for the child node. Any child adhering to this child node definition must be instances of at least the required types listed here. If absent, the required type is assumed to be 'nt:base' (of which all nodes are instances).

  • defaultType is an optional name of the node type that should be used by default when creating the child node. If absent, the default type is assumed to be 'nt:unstructured'. The default type can always be overridden by clients using the Node.addNode(String,String) method.

  • attributes is the optional list of child node definition attributes:

Attribute Keywords

Description

mandatory
man
m

The parent node must have at least least one child to which this child node definition applies.

autocreated
aut
a

The child node is automatically created when the node is created with the node type as primary type, or when the node type is added as a mixin to a node. If absent, the child node is not auto-created.

protected
pro
p

The child to which this node definition type applies is protected, meaning it can be read but not modified by client applications. When absent, the property can be set by client applications. If absent, the child nodes can be read and removed from the parent node.

sns
*

There may be multiple child nodes with the same name to which this definition applies. Such child nodes will be distinguished with a same-name-sibling index. If absent, same-name-siblings are not allowed.

COPY
VERSION
INITIALIZE
COMPUTE
IGNORE
ABORT

The specification for how the child nodes to which this definition applies are to be handled when the parent node is versioned. When absent, the default versioning is COPY.

< constraints

The quoted string literals containing regular expressions or exact matches for the values. When constraints are provided, every value must satisfy at least one constraint on the property definition. If absent, there are no constraints on the values.

queryops
qop

This keyword is followed by a quoted string literal containing the comma-separated operators that can be used in property comparison constraints against this property. If absent, all possible operators are used, and this is equivalent to specifying '=, <>, <, <=, >, >=, LIKE'.

nofulltext
not

Specifies whether the property value(s) should be considered when performing a full-text search. If absent, the values will be used in full-text searches.

noqueryorder
nqord

Specifies whether the property can be ordered in queries. If absent, the property can be used to order query results.

CND example

Consider that we want to define a node type named 'ns:NodeType' that:

  • has two supertypes

  • is abstract

  • has orderable children

  • is a mixin

  • can be queried

  • has the ex:property property as the primary item

  • defines a property named ex:property that

    • has values of type STRING

    • is multi-valued (meaning it can have zero or more values)

    • is mandatory

    • will by default have two values 'default1' and 'default2'

    • will be auto-created as soon as the mixin is applied to a node if the node does not already have a property to which this definition can be assigned

    • is a protected property, meaning clients cannot change the value

    • is included in the version history when the node is checked in

    • has two constraints, which for string values are regular expressions or exact matches for the values

    • can be used in a query property comparison constraint using any of the specified operators

    • has values that are not included in full-text search queries

    • cannot be used to order query results

  • allows a child node that

    • has a name of 'ns:node'

    • has a primary type that is at least subtypes of 'ns:reqType1' and 'ns:reqType2'

    • unless specified will be given a primary type of 'ns:defaultType'

    • is mandatory

    • will be auto-created as soon as the mixin is applied to a node if the node does not already have a child node to which this definition can be assigned

    • is a protected child, meaning clients cannot remove the child node

    • allows multiple children with the same 'ns:node' name, using same-name-sibling indexes to distinguish between the different child nodes

    • is included in the version history when the parent node is checked in

Using the CND format where we already defined the 'ns' namespace prefix, we can define this node type as:

[ns:NodeType] > ns:ParentType1, ns:ParentType2 abstract orderable mixin query primaryitem ex:property
- ex:property (STRING) = 'default1', 'default2' mandatory autocreated protected multiple VERSION
  < '[.]*\d', 'constraint2' queryops '=, <>, <, <=, >, >=, LIKE' nofulltext noqueryorder
+ ns:node (ns:reqType1, ns:reqType2) = ns:defaultType mandatory autocreated protected sns VERSION

Note that we used the full-length keywords. We could use the mid-length keywords to make it a bit more readable:

[ns:NodeType] > ns:ParentType1, ns:ParentType2 abs ord mix q ! ex:property
- ex:property (STRING) = 'default1', 'default2' man aut pro mul VERSION
  < '[.]*\d', 'constraint2' qop '=, <>, <, <=, >, >=, LIKE' nof nqord
+ ns:node (ns:reqType1, ns:reqType2) = ns:defaultType man aut pro sns VERSION

We can make it even more compact by using the shortest keywords:

[ns:NodeType] > ns:ParentType1, ns:ParentType2 a o m q ! ex:property
- ex:property (STRING) = 'default1', 'default2' m a p * VERSION
  < '[.]*\d', 'constraint2' qop '=, <>, <, <=, >, >=, LIKE' nof nqord
+ ns:node (ns:reqType1, ns:reqType2) = ns:defaultType m a p * VERSION

Again, this example uses every possible attribute for the node type, the property definition, and the child node definition. Often the default attributes will suffice, making the node definitions even more compact and readable.

Built-in node types

The JCR specification defines 33 node types that are built-in and available for use without applications having to register them. In fact, most JCR implementations will not allow applications to re-register or modify any of the built-in node types. These standard built-in node types and those node types defined in all ModeShape repositories are defined in Built-in node types.

Registering custom node types

While the JCR 2.0 specification uses the CND format within the specification, the only standard API for registering custom node type definitions is to use the programmatic API.

ModeShape provides non-standard a way for clients to register node types by reading a stream containing a CND file. For details, see Registering custom node types.

The standard programmatic API uses mutable template objects that can be created and registered using the javax.jcr.nodetype.NodeTypeManager. Here's some Java code that registers the 'ns:NodeType' node type definition we used earlier:

// Get the node type manager ...
javax.jcr.nodetype.NodeTypeManager mgr = session.getWorkspace().getNodeTypeManager();

// Create a template for the node type ...
NodeTypeTemplate type = mgr.createNodeTypeTemplate();
type.setName("ns:NodeType");
type.setDeclaredSuperTypeNames(new String[]{"ns:ParentType1","ns:ParentType2"});
type.setAbstract(true);
type.setOrderableChildNodes(true);
type.setMixin(true);
type.setQueryable(true);
type.setPrimaryItemName("ex:property");

// Create a template for the property definition ...
PropertyDefinitionTemplate propDefn = mgr.createPropertyDefinitionTemplate();
propDefn.setName("ex:property");
propDefn.setRequiredType(PropertyType.STRING);
ValueFactory valueFactory = session.getValueFactory();
Value[] defaultValues = {valueFactory.createValue("default1"),valueFactory.createValue("default2")};
propDefn.setDefaultValues(defaultValues);
propDefn.setMandatory(true);
propDefn.setAutoCreated(true);
propDefn.setProtected(true);
propDefn.setMultiple(true);
propDefn.setOnParentVersion(OnParentVersionAction.VERSION);
propDefn.setValueConstraints(new String[]{"[.]*\\d","constraint2"});
String[] queryOps = {QueryObjectModelConstants.JCR_OPERATOR_EQUAL_TO,
    QueryObjectModelConstants.JCR_OPERATOR_NOT_EQUAL_TO,
    QueryObjectModelConstants.JCR_OPERATOR_LESS_THAN,
    QueryObjectModelConstants.JCR_OPERATOR_LESS_THAN_OR_EQUAL_TO,
    QueryObjectModelConstants.JCR_OPERATOR_GREATER_THAN,
    QueryObjectModelConstants.JCR_OPERATOR_GREATER_THAN_OR_EQUAL_TO,
    QueryObjectModelConstants.JCR_OPERATOR_LIKE,
};
propDefn.setAvailableQueryOperators(queryOps);
propDefn.setFullTextSearchable(false);
propDefn.setQueryOrderable(false);
type.getPropertyDefinitionTemplates().add(propDefn);

// Create a template for the child node definition ...
NodeDefinitionTemplate childDefn = mgr.createNodeDefinitionTemplate();
childDefn.setName("ns:node");
childDefn.setRequiredPrimaryTypeNames(new String[]{"ns:reqType1","ns:reqType2"});
childDefn.setDefaultPrimaryTypeName("ns:defaultType");
childDefn.setMandatory(true);
childDefn.setAutoCreated(true);
childDefn.setProtected(true);
childDefn.setSameNameSiblings(true);
childDefn.setOnParentVersion(OnParentVersionAction.VERSION);
type.getNodeDefinitionTemplates().add(childDefn);

// Now register our node type template ...
NodeTypeDefinition[] nodeTypes = new NodeTypeDefinition[]{type};
mgr.registerNodeTypes(nodeTypes, true);

As you can see, even registering a simple node type requires quite a bit of code.

JBoss.org Content Archive (Read Only), exported from JBoss Community Documentation Editor at 2020-03-11 12:12:19 UTC, last content change 2016-04-04 10:08:53 UTC.