Last publication:23/02/2018at 2:43 PMCédricDamioli
Repository plugin
This plugin provides low-level modeling and storing of any kind of information (content, document, page, user, ...) as a light layer above JCR (JSR 170).
Apache Jackrabbit is used for the JCR implementation.
Repository plugin allows to read, write, browse and query AmetysObject.
An AmetysObject is a hierarchical entity defined by the following mandatory properties:
an id in the following syntax scheme://uri.
a name.
a path.
a parent (null for the parent's root).
a parent path (for performance purpose).
On top of that, an AmetysObject can be a:
TraversableAmetysObject for creating and accessing children.
MetadataAwareAmetysObject for providing metadata support.
VersionableAmetysObject for supporting versioning features.
LockableAmetysObject for supporting locking features.
JCRAmetysObject which is a MetadataAwareAmetysObject with JCR node access.
Most of the time an AmetysObject is backed by one and only one JCR node with a specific node type but it can also be virtual (typically used for dynamic Pages).
By default, the Ametys repository contains only one node which is the Ametys root node and is by definition traversable for accessing and creating children.
You must distinguish JCR repository from Ametys repository (the former is a sub-set of the JCR repository with virtual feature).
Only a few node type are registered (ametys:root by default) for exposing node as AmetysObject.
Child nodes with unregistered node types are therefore not returned when calling TraversableAmetysObject#getChildren(), they are simply ignored.
Theses two repositories can be browsed using the repository workspace accessible with the following URL:
http://localhost:8080/_repository/
Because access to this data must be secured, a HTTP BASIC authentication with the same credentials as in the admin workspace is used.
CMS massively uses this plugin for managing CMS business AmetysObject like Content or Page.
Changelog
TODO insert here changelog from JIRA.
Components
Repository plugin comes with two Avalon components.
AmetysObjectResolver component
AmetysObjectResolver is a Avalon component which provides:
Ametys namespaces and nodetypes registration.
AmetysObject access by path or id.
AmetysObject search using JCR XPath query ( only for static AmetysObject, not virtual ones).
Resolving an AmetysObject or querying the repository will open and provide one JCR session. Therefore, saving changes must be done manually using the wrapped node or the provided session.
See #usage for seeing it in action.
UnlockScheduler component
UnlockScheduler is an Avalon component for automatically unlocking LockableAmetysObject after a given period.
The thread can be disable using a configuration parameter and the period can be tweaked using also a configuration parameter expressed in hours.
Extension point
JCR Repository single extension point
This extension point provides the javax.jcr.Repository instance as an Avalon component.
Default implementation creates a JackrabbitRepository instance using as the repository home a configuration parameter and as the repository configuration the file WEB-INF/param/repository.xml.
org.ametys.plugins.repository.provider.JNDIRepository can also be used if the Repository instance is created by the sevlet engine and shared between multiple contexts via JNDI.
SessionFactory single extension point
This extension point is responsible for managing javax.jcr.Session objects.
Default implementation uses the JCR Repository extension point for creating sessions and also an Apache commons pool in order to reuse session for performance purposes.
Sessions are returned to the pool when logout() method is called or at the end of an HTTP request.
Calling getSession() multiple times returns a new Session each time.
AmetysObjectFactory extension point
This extension point allows to register many kind of AmetysObjectFactory in order to create and manage AmetysObject.
See AmetysObjectFactory for creating your own AmetysObject and AmetysObjectFactory.
Usage
Accessing the JCR Repository
Oops !
Copy to clipboard failed. Open the code and copy it manually.
But the right way to retrieve a JCR Session is to use the SessionFactory (for benefit from pool implementation):
Oops !
Copy to clipboard failed. Open the code and copy it manually.
SessionFactory sessionFactory = (SessionFactory) manager.lookup(SessionFactory.ROLE);
Session session = sessionFactory.getSession();
try
{
...
}
finally
{
// Returns the session to the pool
session.logout();
}
SessionFactory sessionFactory = (SessionFactory) manager.lookup(SessionFactory.ROLE);
Session session = sessionFactory.getSession();
try
{
...
}
finally
{
// Returns the session to the pool
session.logout();
}
SessionFactory sessionFactory = (SessionFactory) manager.lookup(SessionFactory.ROLE);
Session session = sessionFactory.getSession();
try
{
...
}
finally
{
// Returns the session to the pool
session.logout();
}
Warning
Ensure that the session is logout when it is not needed anymore for avoiding pool exhaustion when your are in a thread environment.
Indeed in a HTTP request environment sessions opened are returned to the pool at the end of the request thanks to a J2EE request listener.
Access and manage AmetysObject
In the following examples, four AmetysObjectFactory are already registered:
ametys:root node type with root id (built-in).
feed:feeds node type with feeds id (for creating TraversableAmetysObject for feeds root).
feed:feed node type with feed id (for creating TraversableAmetysObject for feed).
feed:item node type with item id (for creating MyItem instances which implements JCRAmetysObject).
See AmetysObjectFactory for more information on creating and registering AmetysObjectFactory.
Retrieve root AmetysObject
Oops !
Copy to clipboard failed. Open the code and copy it manually.
AmetysObjectResolver resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
// By path
AmetysObject ametysObjectRoot = resolver.resolve("/");
// By a different path
ametysObjectRoot = resolver.resolve("");
// By id
ametysObjectRoot = resolver.resolveById("root://d6d2e760-55d3-11de-8f6c-0002a5d5c51b");
AmetysObjectResolver resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
// By path
AmetysObject ametysObjectRoot = resolver.resolve("/");
// By a different path
ametysObjectRoot = resolver.resolve("");
// By id
ametysObjectRoot = resolver.resolveById("root://d6d2e760-55d3-11de-8f6c-0002a5d5c51b");
AmetysObjectResolver resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
// By path
AmetysObject ametysObjectRoot = resolver.resolve("/");
// By a different path
ametysObjectRoot = resolver.resolve("");
// By id
ametysObjectRoot = resolver.resolveById("root://d6d2e760-55d3-11de-8f6c-0002a5d5c51b");
Access feeds and items
Oops !
Copy to clipboard failed. Open the code and copy it manually.
// Automatic cast
TraversableAmetysObject myFeed = resolver.resolve("/myfeeds/myfeed");
// Child direct access by path
MyItem myItem01 = resolver.resolve("/myfeeds/myfeed/item01");
// Child direct access by id
myItem01 = resolver.resolve("myitem://d40843c0-55d5-11de-927f-0002a5d5c51b");
// Child access by parent navigation
myItem01 = myFeed.getChild("item01");
// Retrieve parent
myFeed = myItem01.getParent();
// Automatic cast
TraversableAmetysObject myFeed = resolver.resolve("/myfeeds/myfeed");
// Child direct access by path
MyItem myItem01 = resolver.resolve("/myfeeds/myfeed/item01");
// Child direct access by id
myItem01 = resolver.resolve("myitem://d40843c0-55d5-11de-927f-0002a5d5c51b");
// Child access by parent navigation
myItem01 = myFeed.getChild("item01");
// Retrieve parent
myFeed = myItem01.getParent();
// Automatic cast
TraversableAmetysObject myFeed = resolver.resolve("/myfeeds/myfeed");
// Child direct access by path
MyItem myItem01 = resolver.resolve("/myfeeds/myfeed/item01");
// Child direct access by id
myItem01 = resolver.resolve("myitem://d40843c0-55d5-11de-927f-0002a5d5c51b");
// Child access by parent navigation
myItem01 = myFeed.getChild("item01");
// Retrieve parent
myFeed = myItem01.getParent();
Create and modify an item
Oops !
Copy to clipboard failed. Open the code and copy it manually.
TraversableAmetysObject myFeed = resolver.resolve("/myfeeds/myfeed");
// Create a new item
MyItem myItem02 = myFeed.createChild("item02", "feed:item");
// Save changes (use the session because a newly created node cannot be save directly)
myItem02.getNode().getSession().save();
MyItem myItem01 = myFeed.getChild("item01");
// Access JCR node and change title
myItem01.getNode().setProperty("title", "Item 01");
// Save changes
myItem01.getNode().save();
TraversableAmetysObject myFeed = resolver.resolve("/myfeeds/myfeed");
// Create a new item
MyItem myItem02 = myFeed.createChild("item02", "feed:item");
// Save changes (use the session because a newly created node cannot be save directly)
myItem02.getNode().getSession().save();
MyItem myItem01 = myFeed.getChild("item01");
// Access JCR node and change title
myItem01.getNode().setProperty("title", "Item 01");
// Save changes
myItem01.getNode().save();
TraversableAmetysObject myFeed = resolver.resolve("/myfeeds/myfeed");
// Create a new item
MyItem myItem02 = myFeed.createChild("item02", "feed:item");
// Save changes (use the session because a newly created node cannot be save directly)
myItem02.getNode().getSession().save();
MyItem myItem01 = myFeed.getChild("item01");
// Access JCR node and change title
myItem01.getNode().setProperty("title", "Item 01");
// Save changes
myItem01.getNode().save();
Query and child navigation
Oops !
Copy to clipboard failed. Open the code and copy it manually.
// Query example
AmetysObjectIterable<MyItem> myItemIterator = resolver.query("//element(*, feed:item)");
for (MyItem myItem : myItemIterator)
{
System.out.println(myItem.getName());
}
// The same with children browsing
myItemIterator = resolver.resolve("/myfeeds/myfeed").getChildren();
for (MyItem myItem : myItemIterator)
{
System.out.println(myItem.getName());
}
// Query example
AmetysObjectIterable<MyItem> myItemIterator = resolver.query("//element(*, feed:item)");
for (MyItem myItem : myItemIterator)
{
System.out.println(myItem.getName());
}
// The same with children browsing
myItemIterator = resolver.resolve("/myfeeds/myfeed").getChildren();
for (MyItem myItem : myItemIterator)
{
System.out.println(myItem.getName());
}
// Query example
AmetysObjectIterable<MyItem> myItemIterator = resolver.query("//element(*, feed:item)");
for (MyItem myItem : myItemIterator)
{
System.out.println(myItem.getName());
}
// The same with children browsing
myItemIterator = resolver.resolve("/myfeeds/myfeed").getChildren();
for (MyItem myItem : myItemIterator)
{
System.out.println(myItem.getName());
}
Using a MetadataAwareAmetysObject
Oops !
Copy to clipboard failed. Open the code and copy it manually.
MetadataAwareAmetysObject myObject = resolver.resolve("/myobject");
// Retrieve composite metadata holder
CompositeMetadata metadata = myObject.getMetadataHolder();
// Set a date property
metadata.setMetadata("updatedAt", new Date());
// Set a multiple string property
metadata.setMetadata("authors", new String[] {"bob", "john"});
// Creating a binary metadata
BinaryMetadata binaryMetadata = metadata.getBinaryMetadata("thumbnail", true);
binaryMetadata.setFilename("thumbnail.jpg");
binaryMetadata.setMimeType("image/jpeg");
binaryMetadata.setLastModified(new Date());
binaryMetadata.setInputStream(new FileInputStream("/tmp/thumb.jpg"));
// Creating a rich text metadata
RichText richText = metadata.getRichText("body", true);
richText.setMimeType("text/xml");
richText.setEncoding("UTF-8");
richText.setLastModified(new Date());
richText.setInputStream(new ByteArrayInputStream("<?xml version='1.0' encoding='UTF-8'?><document><para>body</para></document>".getBytes("UTF-8")));
// Creating a composite metadata
CompositeMetadata videoMetadata = metadata.getCompositeMetadata("address", true);
videoMetadata.setMetadata("street", "Beverly Drive");
videoMetadata.setMetadata("zipcode", "90210");
videoMetadata.setMetadata("city", "Beverly Hills");
videoMetadata.setMetadata("state", "California");
MetadataAwareAmetysObject myObject = resolver.resolve("/myobject");
// Retrieve composite metadata holder
CompositeMetadata metadata = myObject.getMetadataHolder();
// Set a date property
metadata.setMetadata("updatedAt", new Date());
// Set a multiple string property
metadata.setMetadata("authors", new String[] {"bob", "john"});
// Creating a binary metadata
BinaryMetadata binaryMetadata = metadata.getBinaryMetadata("thumbnail", true);
binaryMetadata.setFilename("thumbnail.jpg");
binaryMetadata.setMimeType("image/jpeg");
binaryMetadata.setLastModified(new Date());
binaryMetadata.setInputStream(new FileInputStream("/tmp/thumb.jpg"));
// Creating a rich text metadata
RichText richText = metadata.getRichText("body", true);
richText.setMimeType("text/xml");
richText.setEncoding("UTF-8");
richText.setLastModified(new Date());
richText.setInputStream(new ByteArrayInputStream("<?xml version='1.0' encoding='UTF-8'?><document><para>body</para></document>".getBytes("UTF-8")));
// Creating a composite metadata
CompositeMetadata videoMetadata = metadata.getCompositeMetadata("address", true);
videoMetadata.setMetadata("street", "Beverly Drive");
videoMetadata.setMetadata("zipcode", "90210");
videoMetadata.setMetadata("city", "Beverly Hills");
videoMetadata.setMetadata("state", "California");
MetadataAwareAmetysObject myObject = resolver.resolve("/myobject");
// Retrieve composite metadata holder
CompositeMetadata metadata = myObject.getMetadataHolder();
// Set a date property
metadata.setMetadata("updatedAt", new Date());
// Set a multiple string property
metadata.setMetadata("authors", new String[] {"bob", "john"});
// Creating a binary metadata
BinaryMetadata binaryMetadata = metadata.getBinaryMetadata("thumbnail", true);
binaryMetadata.setFilename("thumbnail.jpg");
binaryMetadata.setMimeType("image/jpeg");
binaryMetadata.setLastModified(new Date());
binaryMetadata.setInputStream(new FileInputStream("/tmp/thumb.jpg"));
// Creating a rich text metadata
RichText richText = metadata.getRichText("body", true);
richText.setMimeType("text/xml");
richText.setEncoding("UTF-8");
richText.setLastModified(new Date());
richText.setInputStream(new ByteArrayInputStream("<?xml version='1.0' encoding='UTF-8'?><document><para>body</para></document>".getBytes("UTF-8")));
// Creating a composite metadata
CompositeMetadata videoMetadata = metadata.getCompositeMetadata("address", true);
videoMetadata.setMetadata("street", "Beverly Drive");
videoMetadata.setMetadata("zipcode", "90210");
videoMetadata.setMetadata("city", "Beverly Hills");
videoMetadata.setMetadata("state", "California");
Using a VersionableAmetysObject
Oops !
Copy to clipboard failed. Open the code and copy it manually.
VersionableAmetysObject myObject = resolver.resolve("/myobject");
// Create a new version
myObject.checkpoint();
// Mark current version as 'validated' label
myObject.addLabel("validated", true);
// Access the 1.2 version
myObject.switchToRevision("1.2");
// Go back to the last version ('validated' label in our example)
myObject.switchToLabel("validated");
VersionableAmetysObject myObject = resolver.resolve("/myobject");
// Create a new version
myObject.checkpoint();
// Mark current version as 'validated' label
myObject.addLabel("validated", true);
// Access the 1.2 version
myObject.switchToRevision("1.2");
// Go back to the last version ('validated' label in our example)
myObject.switchToLabel("validated");
VersionableAmetysObject myObject = resolver.resolve("/myobject");
// Create a new version
myObject.checkpoint();
// Mark current version as 'validated' label
myObject.addLabel("validated", true);
// Access the 1.2 version
myObject.switchToRevision("1.2");
// Go back to the last version ('validated' label in our example)
myObject.switchToLabel("validated");
Using a LockableAmetysObject
Oops !
Copy to clipboard failed. Open the code and copy it manually.
LockableAmetysObject myObject = resolver.resolve("/myobject");
// Test if current object is locked
boolean isLocked = myObject.isLocked();
if (isLocked)
{
// Lock current object
myObject.lock();
...
// Unlock current object
myObject.unlock();
}
else
{
// Retrieve current lock owner
System.out.println("Current lock owner: " + myObject.getLockOwner());
}
LockableAmetysObject myObject = resolver.resolve("/myobject");
// Test if current object is locked
boolean isLocked = myObject.isLocked();
if (isLocked)
{
// Lock current object
myObject.lock();
...
// Unlock current object
myObject.unlock();
}
else
{
// Retrieve current lock owner
System.out.println("Current lock owner: " + myObject.getLockOwner());
}
LockableAmetysObject myObject = resolver.resolve("/myobject");
// Test if current object is locked
boolean isLocked = myObject.isLocked();
if (isLocked)
{
// Lock current object
myObject.lock();
...
// Unlock current object
myObject.unlock();
}
else
{
// Retrieve current lock owner
System.out.println("Current lock owner: " + myObject.getLockOwner());
}