Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "JCR Management"

(Configure JCRM for the use as API and as domain model editor)
(Starting the domain model editor)
Line 373: Line 373:
 
# Create a new Run Configuration for "Eclipse Application"
 
# Create a new Run Configuration for "Eclipse Application"
 
# Give it a name like "Start Editor" if you want
 
# 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:
 
# Press "Run" to start a new Eclipse instance open a new simple project like that:
 
# In the new Eclipse instance (the 3rd):
 
# In the new Eclipse instance (the 3rd):
## Click at "File/New/Project...".
+
## Navigate to Window / Show View / Other / JCR Management / ConConFile. This opens the view that manages the ConCon file. This file contains all configured JCR connections.
## Choose "General/Project".
+
## Click on the link to open the file. It contains one preconfigured connection to a Jackrabbit 1.4 repository.
## Give it a name like "Editor".
+
## Navigate down to a Simple Credentials element
# Now you can create the domain model editor by using the new file wizard:
+
## If you right-click it you find a menu item for our library model. Clicking it opens the editor that we previously generated for our model. There you can edit your Jackrabbit backed EMF model.
## 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".
+

Revision as of 11:59, 11 September 2010

JCR Management (JCRM)

JCR Management will provide tooling and a JCR (http://en.wikipedia.org/wiki/Content_repository_API_for_Java) persistence framework for EMF with pluggable JCR implementations.


The Contributions

2008

  • Johan Gielstra:
  • 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.
  • Nick Boldt created the initial JCR Management (CVS, website, ...) setup at eclipse.org

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

There is a seperate page that explains how to map EMF type elements to JCR node 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.

ClassEditor.png

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.

JackrabbitXMLNodetypeEditor.png

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.

JCRMDomainModelEditor.png

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.

	public static void 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

	public static void 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();
		}
	}
 
	private static void 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");
	}


Versioning

	public static void 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"));
	  		Library library = MyDomainModelFactory.eINSTANCE.createLibrary();
	  		root rootObject = (root) resource.getContents().get(0);
	  		rootObject.getBases().add(library);
	  		library.setNodeName("MyVersionedLibrary1");
	  		library.setName("MyVersionedLibrary");
	  		// initial unversioned content
	  		resource.save(System.out,null);
 
	  		library.setVersionable(true);
	  		library.setName("MyLibraryVersion Zero");
	  		resource.save(System.out,null);
	  		// checkin the initial version
	  		// This makes the library read-only until checkout() is called.
	  		library.checkin();
 
	  		try {
				// The library is not changeable as it is not yet checked out.
				library.setName("New Text");
			} catch (Exception e) {
				//Trying to change versioned content that is not checked out throws an Exception.
			}
 
	  		library.checkout();
	  		library.setName("MyLibraryVersion One");
	  		resource.save(System.out,null);
	  		// Creates a second version.
	  		library.checkin();
 
	  		library.checkout();
	  		library.setName("MyLibraryVersion Two");
	  		resource.save(System.out,null);
	  		// Creates a third version.
	  		library.checkin();
 
	  		JCRMVersionHistory versionHistory = library.getJcrmVersionHistory();
	  		EList<JCRMVersion> allVersions = versionHistory.getAllVersions();
	  		for (JCRMVersion version : allVersions) {
				Date creationDate = version.getCreated();
				String versionName=version.getVersionName();
				System.out.println("Version: "+versionName+": created at: "+creationDate);
			}
 
	  		boolean removeExisting=true;
			library.restore("1.1", removeExisting);
			System.out.println(library.getName());
 
			// Non versioning use cases e.g. the generated EMF editor don't call
			// checkout()/checkin() before/after a model modification. 
			// Set versionable to false to not force checkin()/checkout() 
			// in these use cases later on.
			library.checkout();
			library.setVersionable(false);
 
	        // that calls session.save() and persists the changes made above.
	  		resource.save(System.out, null);
	  	}
	  	catch (IOException exception) {
	  		exception.printStackTrace();
	  	}
	  }

Tasks

  1. See Bugzilla for an overview of the ToDo's https://bugs.eclipse.org/bugs/buglist.cgi?short_desc_type=allwordssubstr&short_desc=&product=EMFT&component=JCR+Management&long_desc_type=allwordssubstr&long_desc=&order=Importance


Next Milestone

Create a first downloadable presentation of the project to show the potential of Eclipse modeling to the Jackrabbit community.

Ideas

  • Using Cedrics Compare editor inside the JCR Manager
http://www.eclipse.org/modeling/emft/?project=compare#compare

Values

  • simplicity
  • transparency
  • no dependency on JCR implementations

FAQ

  1. 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 currently expects Jackrabbit version 1.4 remotely at "//localhost:1099/jackrabbit.repository". Please make sure it's available there.

  • Eclipse:

JCRM is tested with the Eclipse Gallileo modeling package

  • JCRM:

- Check out all modules from org.eclipse.emf/org.eclipse.emf.jcrm/plugins as plugins using this repository connection:

   Server: dev.eclipse.org
   Repository Path: /cvsroot/modeling
   User: anonymous
   Password: (leave blank)
   Connection Type: pserver
   Checkout As: Empty EMF Project

- In the "org.eclipse.emf.jcrm.conversion" plugin in the source folder you find the general build.xml file. It is used to build all projects.

  1. Right-Click it and choose "Run As/Ant Build..."
  2. In the "JRE" tab choose "Run in the same JRE as the workspace"
  3. Press "Run"

- If there are no errors anymore you successfully checked out JCRM.

  • In case the dmodel project doesn't compile, please regenerate it by opening model/domainmodel.genmodel, right-clicking at the root node and choosing "Generate All"

Running JCRM

  1. In the main menu choose "Run/Run Configurations"
  2. Create a new Run Configuration for "Eclipse Application"
  3. Give it a name like "Start JCRM" if you want
  4. In the "Arguments" tab make sure there is "-Xmx512m" in the "VM arguments" field
  5. Press "Run" to start a new Eclipse instance
  6. You can also start your Jackrabbit server now

This puts all plugin projects that have been checked out (and maybe modified) into the new Eclipse instance.

Creating a demo domain model

  1. In the new Eclipse instance create a new JCR Management project using the "New Project..." wizard. You can give it a name like "Demo" if you want.
  2. Paste the example library EMF model as library.ecore into the generated "model" folder. I tested JCRM basically with this model but the current features should also work with other models.

Registering the domain model and create the tooling

Now the fun part starts as the tooling can be generated.

  1. Right-Click at "workflow/registerModelToRepository.oaw" choose "Run As/oAW Workflow" to register your Ecore model to the Jackrabbit repository.
    1. "src-gen/library.jrxmlntmodel" is the xml file that has been used to register the domain model as Jackrabbit XML.
    2. "src-gen/library.nodetype" is a native JCRM model that serves as a basis for transformations into other models.
  2. 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.
    1. "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. The JCR namespaces are mapped to packages.
    2. "src-gen/loadedLibrarySinglePackage.ecore" is the same Ecore model like loadedLibrary.ecore. It is just not seperated into packages to make it easier to create a complete class diagram from that.
    3. Right-Click at "loadedLibrarySinglePackage.ecore/Initialize ecore_diagram Diagram file"
    4. 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

  1. Copy the loadedLibrary from the src-gen folder to the model folder to avoid that it is overwritten by subsequent calls on loadModelFromRepository.oaw
  2. Right-Click at loadedLibrary.ecore and create a new "Eclipse Modeling Framework/EMF model" (loadedLibrary.genmodel) based on the Ecore model.
    1. Click throuch to the 4th page and press "Load".
    2. At the "Package Selection" page press "Select all" and check both available models in the "Referenced generator models" section. Then you can finish the dialog.
  3. Open the properties editor for the root element of the genmodel and change the "Model/Feature Delegation Field" to "Dynamic". That delegates all calls on the model to the JCRM framework.
  4. 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.
  5. 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.
  6. Add the following extension to the plugin.xml file:
  <extension point="org.eclipse.emf.jcrm.ejcrmnode.conCon4FileExtension">
     <connectionConfiguration
           ConConURI="defaultRMIConnection"
           extension="library">
     </connectionConfiguration>
  </extension>

Starting a test case using the API

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

  1. In the main menu of your current (2nd) Eclipse instance choose "Run/Run Configurations"
  2. Create a new Run Configuration for "Eclipse Application"
  3. Give it a name like "Start Editor" if you want
  4. Press "Run" to start a new Eclipse instance open a new simple project like that:
  5. In the new Eclipse instance (the 3rd):
    1. Navigate to Window / Show View / Other / JCR Management / ConConFile. This opens the view that manages the ConCon file. This file contains all configured JCR connections.
    2. Click on the link to open the file. It contains one preconfigured connection to a Jackrabbit 1.4 repository.
    3. Navigate down to a Simple Credentials element
    4. If you right-click it you find a menu item for our library model. Clicking it opens the editor that we previously generated for our model. There you can edit your Jackrabbit backed EMF model.

Back to the top