added namespace handling to the node type registration. The ecore package namespace is used now for the node types of its classes.(https://bugs.eclipse.org/bugs/show_bug.cgi?id=245739) This is an important first step for mapping JCR namespaces to Ecore packages.

enhanced JCRM to allow adding many nodes without needing to specify it's name. (https://bugs.eclipse.org/bugs/show_bug.cgi?id=245450) Before this patch only one node with a fixed name "NewNode" could be added. The contribution increments the node name by one if it exists.

reviewed the first version of the JCRM tutorial

My employer inovex GmbH (http://www.inovex.de) contributes 2 person days to work on this project within working hours.

Ed Merks:

helps as a mentor for questions regarding the Eclipse foundation.

implemented a new feature request for EMF that I had (dynamic feature delegation)

answers a lot of my questions in the newsgroup

2007

My employer inovex GmbH (http://www.inovex.de) contributes 5 person days to work on this project within working hours.

Ed Merks:

helps as a mentor for questions regarding the Eclipse foundation.

answers a lot of my questions in the newsgroup

The ATL team contributed an initial meta model and transformation that will speed up ATL integration.

The Status

JCRM is not production ready at the moment. The current code base consists of prototypes that serve as a basis for concrete discussions about requirements and solutions. The prototypes are tested exclusively with the example library EMF model.

The Architecture

JCRM is based on a mapping between EMF and JCR. One part is responsible to map EMF type elements to JCR node type elements and the other part maps EMF objects to JCR nodes. The main advantage of that architecture is, that many EMF frameworks can now work on JCR node types and nodes. The following JCRM tool prototypes are just examples how this mapping can be used. More ideas JCR tools or Eclipse projects are certainly welcome. They will be logged in the ideas section of this wiki. The most interesting ideas will be provided to the community for priorization and for validation the use cases. After that the JCRM team (at the moment it's just me - Sandro) will work on it.

Mapping of type elements

The Tooling

EMF Class Editor

The class editor is based on the EMF Ecore model. This model is generated from the node types in the repository.

Jackrabbit XML Nodetype Editor

This editor saves the content in the XML format that can be used for Jackrabbit node type registration. It completely supports drag 'n drop, copy & paste and undo/redo. The editor is basically the sample model editor of EMF enhanced with the JCRM type mapping semantics. You can use it to create a new XML file for the node type registration in Jackrabbit and you can also read the node types from the repository save it and manipulate it with this editor.

Jackrabbit CND Editor

This text editor validates the content against the Jackrabbit CND format. It also contains minimal code completion. For this editor the XText framework of oAW is used. The JCRM type mapping framework makes it possible to use the JCR node type structure in the grammar for the XText editor. This way the CND file can get generated from the node types in the repository on the fly.
At the moment only a small subset of the XText features are used in this editor.

Domain Model Editor

The domain model editor is a sample tool that is generated from EMF. It usually has a XML backend. Just the backend configuration changes it to be an editor of JCR nodes.

Every change in the model immediately changes the underlaying repository. E.g. if you add a "Book" object in that editor node.addNode("NewNode","Book") is called. As soon as you save the editor session.save() is called.

The API

Navigation and Modification

The API uses the classes generated by EMF and the JCRM type mapping. At runtime you can see the JCRM object mapping at work. You can find more detailed descriptions in the comments.

publicstaticvoid main(String[] args){// Create an EMF resource set to hold the resources.
ResourceSet resourceSet =new ResourceSetImpl();// Register the appropriate EMF resource factory to handle the file // extension "mydomainmodel"
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("mydomainmodel", new JRRMIResourceFactoryImpl());// Register the EMF package to ensure it is available during loading.
resourceSet.getPackageRegistry().put(MyDomainModelPackage.eNS_URI,
MyDomainModelPackage.eINSTANCE);try{// Create an EMF resource using the registered "mydomainmodel" extension
Resource resource = resourceSet.createResource(URI.createURI("http:///My.mydomainmodel"));// Create a domain object. The constructor is initially generated by EMF to be protected// but you can make it public if you want.
Library library = MyDomainModelFactory.eINSTANCE.createLibrary();// In contrast to the JCR specification EMF generally allows to have many root nodes// hence the content of the resource is designed to be a list in EMF even though the // JCRM Framework needs only one element in the content.// JCRM puts the root object containing the corresponding root node of the repository// in the content of the resource where it can get retrieved. "root" is the type// of the rootObject that corresponds to the node type of the root node.
root rootObject =(root) resource.getContents().get(0);// As soon as an object is added to it's containing object it also get it's// node injected. In this case the library object is added to the "bases" // feature of the rootObject. That calls addNode() on the// node that is contained within the rootObject. The resulting node// is then injected in the library object.
rootObject.getBases().add(library);// Every domain object contains it's corresponding node and every modification // is delegated to the node. In this case setName...("MyLibraryNameByAPI") actually// calls nodeOfTheObject.setProperty("name","MyLibraryNameByAPI")// This way the objects don't need to have own copies of the property content.// At the moment there is not something like a detached mode where you can// modify properties without the object having an injected node.
library.setName("MyLibraryNameByAPI");
library.setNodeName("MyLibraryNodeNameByAPI");Book aBook = MyDomainModelFactory.eINSTANCE.createBook();
library.getBooks().add(aBook);Writer aWriter = MyDomainModelFactory.eINSTANCE.createGuideBookWriter();
aBook.setAuthor(aWriter);
aWriter.setName("aGuidBookWritersNameByAPI");
aBook.setTitle("Book-TitleByAPI");
aBook.setPages("200ByAPI");// that calls session.save() and persists the changes made above.
resource.save(System.out, null);}catch(IOException exception){
exception.printStackTrace();}}

Locking

publicstaticvoid main(String[] args){// Create an EMF resource set to hold the resources.
ResourceSet resourceSet =new ResourceSetImpl();// Register the appropriate EMF resource factory to handle the file extension "mydomainmodel"
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("mydomainmodel",
new JRRMIResourceFactoryImpl());// Register the EMF package to ensure it is available during loading.
resourceSet.getPackageRegistry().put(MyDomainModelPackage.eNS_URI, MyDomainModelPackage.eINSTANCE);try{// Create a library object to lock it.
Resource resource = resourceSet.createResource(URI.createURI("http:///My.mydomainmodel"));
Library library = MyDomainModelFactory.eINSTANCE.createLibrary();
root rootObject =(root) resource.getContents().get(0);
rootObject.getBases().add(library);
library.setNodeName("Testlibrary for locking");
library.setName("Testlibrary for locking");// Make this library lockable.
library.setLockable(true);
resource.save(System.out, null);// The JSR-170 javadoc says about the session scoped lock:// If isSessionScoped is true then this lock will expire upon the expiration of the // current session.boolean sessionScoped =true;// The JSR-170 javadoc says about the deep lock:// If isDeep is true then the lock applies to this node and all its descendant nodes; boolean deep =true;// Apply the lock to the library.
library.lock(deep, sessionScoped);try{// This method tries to change the library in an other session.// But that throws an Exception as the library is locked.
changeLibraryNameInANewSession();}catch(Throwable t){// Exception is throws as we try to modify a locked node.
t.printStackTrace();}// Unlock the library.
library.unlock();// Changing the library in an other session is possible now.
changeLibraryNameInANewSession();// that calls session.save() and persists the changes made above.
resource.save(System.out, null);}catch(IOException exception){
exception.printStackTrace();}}privatestaticvoid changeLibraryNameInANewSession(){
ResourceSet secondResourceSet =new ResourceSetImpl();
secondResourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("mydomainmodel",
new JRRMIResourceFactoryImpl());// Register the EMF package to ensure it is available during loading.
secondResourceSet.getPackageRegistry().put(MyDomainModelPackage.eNS_URI, MyDomainModelPackage.eINSTANCE);
Resource secondResource = secondResourceSet.createResource(URI.createURI("http:///My2nd.mydomainmodel"));
root secondRootObject =(root) secondResource.getContents().get(0);
EList<base> rootChildren = secondRootObject.getBases();// retrieve the last library
Library libraryFromSecondResource =(Library) rootChildren.get(rootChildren.size()-1);
libraryFromSecondResource.setName("not settable as the node is locked");}

Ideas

Values

simplicity

transparency

no dependency on JCR implementations

FAQ

What's the relationship between JCR Management and Jackrabbit JCR-OCM?

One part of JCR Management has the same goal as JCR-OCM - exposing node data and operations to domain models - but JCRM uses an MDSD approach based on Eclipse Modeling Framework (EMF). This makes it depending less on reflection and using more generated classes instead. It will delegate as many calls on JCR node data as possible to minimize copying node data to domain model objects. Additionally JCR Management also has many other goals. But find out the details of JCR-OCM and check out the source code and the documentation.

Try it out

Installation

Jackrabbit:

JCRM expects the currently latest Jackrabbit version remotely at "//localhost:1099/jackrabbit.repository". Please make sure it's available there.

Registering the domain model and create the tooling

"src-gen/library.jrxmlntmodel" is the xml file that has been used to register the domain model as Jackrabbit XML.

"src-gen/library.nodetype" is a native JCRM model that serves as a basis for transformations into other models.

Right-Click at "workflow/loadModelFromRepository.oaw" choose "Run As/oAW Workflow" to create a new EMF model from the native JCR node types and your own node types that are currently registered to your repository. This basically puts your domain model in the context of the node types of your repository.

"src-gen/loadedLibrary.ecore" is the Ecore model that contains the domain model, the native JCR node types in Ecore format and the annotations that map the Ecore elements to node types elements.

"src-gen/loadedlibraryNodeTypes.nodetype" is a native JCRM model that serves as a basis for transformations into other models.

"src-gen/loadedLibrary.jrcnd" is a generated file that contains all node types in the Jackrabbit CND format.

Press "Finish" and you will see how the EClasses (node types) relate to each other in the Diagram

Configure JCRM for the use as API and as domain model editor

Copy the loadedLibrary from the src-gen folder to the model folder to avoid that it is overwritten by subsequent calls on loadModelFromRepository.oaw

To make JCRM aware of changes in the model instance, all domain model classes need to extend from JCRM's JCRMNode class instead of Object or EMF's EObject. As now basically all domain model classes inherit from the "Base" class (nt:base) we only need to make the Base class inherit from JCRMNode. To do this you can do the following steps:

Open the mode/loadedLibrary.ecore file

Right-Click at the root node of the tree and choose "Load Resource..."

Right-Click at loadedLibrary.ecore and create a new "Eclipse Modeling Framework/EMF model" (loadedLibrary.genmodel) based on the Ecore model.

Click throuch to the 4th page and press "Load".

At the "Package Selection" page check "myDomainModel" as "Root packages" and "Domainmodel" as "Referenced generator models". Then you can finish the dialog.

Open the properties editor for the root element of the genmodel and change the "Model/Feature Delegation Field" to "Reflective". That delegates all calls on the model to the JCRM framework. Later an other value instead of "Reflective" will be used which will provide more flexibility.

Right-Click at the root element of the genmodel and select "Generate All". That generates the model classes, initial source code for a test class and the tree based editor for the model.

Add org.eclipse.emf.jcrm.userdependencies to the list of the plug-in dependencies in the META-INF/MANIFEST.MF file. Make sure the "reexport" option is selected.

Add the "org.eclipse.emf.ecore.extension_parser" extension to the list of extensions in the plugin.xml file (it's the same editor as the manifest.mf file.

Use "mydomainmodel" in the type field. That declarates that every resource that ends with *.mydomainmodel will handled by the JCR backend we declare in the class field

In the class field add the Jackrabbit RMI Resource Factory class "org.eclipse.emf.jcrm.jr.rmi.JRRMIResourceFactoryImpl". Now the JCRM class <--> node type mapping, the object <--> node mapping and the backend (resource) configuration are finished.

Starting a test case using the API

To test the JCRM API you can replace the Demo.tests/src/myDomainModel.tests.MyDomainModelExample.main() method with the one from http://wiki.eclipse.org/JCR_Management#The_API . If you run it as an application within Eclipse you need to add "-Xmx512m" to the VM arguments in the "Arguments" tab of the run configuration. This should add some nodes and give you a quick example of the JCRM runtime API.

Starting the domain model editor

In the main menu of your current (2nd) Eclipse instance choose "Run/Run Configurations"

Create a new Run Configuration for "Eclipse Application"

Give it a name like "Start Editor" if you want

In the "Arguments" tab enter "-Xmx512m" in the "VM arguments" field

Press "Run" to start a new Eclipse instance open a new simple project like that:

In the new Eclipse instance (the 3rd):

Click at "File/New/Project...".

Choose "General/Project".

Give it a name like "Editor".

Now you can create the domain model editor by using the new file wizard:

Right click at your project and choose "New/Other...".

Choose "Example EMF Model Creation Wizards/MyDomainModel".

Give it an arbitrary name.

Choose an arbitrary Model Object in the following page and finish the dialog.

Now the editor opens and you can edit your Jackrabbit backed model.

The properties editor can be opened by right clicking on a model element and choosing "Show Properties View".