This part will show you how to configure and use eXo JCR in GateIn and standalone.

1.1. Introduction in eXoJCR

Java Content Repository API as well as other Java language related
standards is created within the Java Community Process http://jcp.org/ as
a result of collaboration of an expert group and the Java community. It is
known as JSR-170 (Java
Specification Request).

1.1.1. Data model

The main purpose of content repository is to maintain the data.
The heart of CR is the data model:

The main data storage abstraction of JCR's data model is a
workspace

Each repository should have one or more workspaces

The content is stored in a workspace as a hierarchy of
items

Each workspace has its own hierarchy of items

Figure 1.1. Item hierarchy

Node is intended to support the data hierarchy. It is of type
using namespaced names which allows the content to be structured in
accordance with standardized constraints. A node may be versioned
through an associated version graph (optional feature)

It is important to note that the data model for the interface (the
repository model) is rarely the same as the data models used by the
repository's underlying storage subsystems. The repository knows how to
make the client's changes persistent because that is part of the
repository configuration, rather than part of the application
programming task.

1.2. Why use JCR?

1.2.1. What is JCR?

JCR (Java Content Repository) is a java interface used to access
contents that are not only web contents, but also other hierarchically
stored data. The content is stored in a repository. The repository can be
a file system, a relational database or an XML document. The internal
structure of JCR data looks similar to an XML document, that means a
document tree with nodes and data, but with a small difference, in JCR the
data are stored in "property items".

Or better to cite the specification of JCR: "A content repository is
a high-level information management system that is a superset of
traditional data repositories."

1.2.2. Why use JCR?

How do you know the data of your website are stored? The images are
probably in a file system, the meta data are in some dedicated files -
maybe in XML - the text documents and pdfs are stored in different folders
with the meta data in an other place (a database?) and in a proprietary
structure. How do you manage to update these data and how do you manage
the access rights? If your boss asks you to manage different versions of
each document or not? The larger your website is, the more you need a
Content
Management Systems (CMS) which tackles all these issues.

These CMS solutions are sold by different vendors and each vendor
provides its own API for interfacing the proprietary content repository.
The developers have to deal with this and need to learn the
vendor-specific API. If in the future you wish to switch to a different
vendor, everything will be different and you will have a new
implementation, a new interface, etc.

JCR provides a unique java interface for interacting with both text
and binary data, for dealing with any kind and amount of meta data your
documents might have. JCR supplies methods for storing, updating, deleting
and retrieving your data, independent of the fact if this data is stored
in a RDBMS, in a file system or as an XML document - you just don't need
to care about. The JCR interface is also defined as classes and methods
for searching, versioning, access control, locking, and
observation.

Furthermore, an export and import functionality is specified so that
a switch to a different vendor is always possible.

1.2.3. What does eXo do?

eXo fully complies a JCR standard JSR 170; therefore with
eXo JCR you can use a vendor-independent API. It means that you could
switch any time to a different vendor. Using the standard lowers your
lifecycle cost and reduces your long term risk.

Of course eXo does not only offer JCR, but also the complete
solution for ECM (Enterprise Content Management) and for WCM (Web Content
Management).

1.2.4. Further Reading

In order to further understand the theory of JCR and the API, please
refer to some external documents about this standard:

1.3.2. How it works

eXo Repository Service is a standard eXo service and is a registered
IoC component, i.e. can be deployed in some eXo Containers (see Service configuration for details). The relationships between components
are shown in the picture below:

eXo Container: some subclasses of
org.exoplatform.container.ExoContainer (usually
org.exoplatform.container.StandaloneContainer or
org.exoplatform.container.PortalContainer) that holds a reference to
Repository Service.

Repository Service: contains
information about repositories. eXo JCR is able to manage many
Repositories.

Repository: Implementation of
javax.jcr.Repository. It holds references to one or more
Workspace(s).

Workspace: Container of a single rooted tree of
Items. (Note that here it is not exactly the same as
javax.jcr.Workspace as it is not a per Session object).

Usual JCR application use case includes two initial steps:

Obtaining Repository object by getting Repository Service from the current eXo
Container (eXo "native" way) or via JNDI lookup if eXo repository is
bound to the naming context using (see Service configuration for
details).

Creating javax.jcr.Session object that calls
Repository.login(..).

1.3.3. Workspace Data Model

The following diagram explains which components of eXo JCR
implementation are used in a data flow to perform operations specified in
JCR API

The Workspace Data Model can be split into 4 levels by data
isolation and value from the JCR model point of view.

Workspace Data Container:
implements physical data storage. It allows different types of backend
(like RDB, FS files, etc) to be used as a storage for JCR data. With
the main Data Container, other storages for persisted Property Values
can be configured and used.

Indexer: maintains workspace
data indexing for further queries.

Storage Level: Persistent
storages for:

JCR Data

Indexes (Apache Lucene)

Values (e.g., for BLOBs) if different from the main Data
Container

1.4. Advantages of eXo JCR

1.4.1. Advantages for application developers

Data repository and application are isolated from each other so
an application developer should not learn the details of particular
data storage's interfaces, but can need to concentrate on business
logic of a particular application built on the top of JCR.

Repositories can be simply exchanged between different
applications without changing the applications themselves. This is the
matter of the repository configuration.

Data storage types/ versions can be changed and also, different
types of data storages can be combined in one repository data model
(of course, the complexity and work of building interfaces between the
repository and its data storage don't disappear but these changes are
isolated in the repository and thus manageable from the point of view
of the customer).

1.4.2. Advantages for managers

Using a standardized repository for content management reduces
the risk of dependence on a particular software vendor and proprietary
API.

Costs for maintaining and developing a content repository based
custom application is significantly lower than developing and
supporting your own interfaces and maintaining your own data
repository applications (staff can be trained once, it is possible to
take help from the community and the third party consulters).

Thanks to flexible layered JCR API (see below), it is possible
to fit the legacy storage subsystem into new interfaces and decrease
the costs and the risk of losing data.

An extension to the API exists as we can see in the following layer
schema.

1.5. Compatibility Levels

The Java Content Repository specification JSR-170 has been split
into two compliance levels as well as a set of optional features.

Level 1 defines a read-only repository.

Level 2 defines methods for writing
content and bidirectional interaction with the repository.

eXo JCR supports JSR-170 level 1 and level 2 and all optional
features. The recent JSR-283 is not yet supported.

1.5.1. Level 1

Level 1 includes read-only functionality for very simple
repositories. It is useful to port an existing data repository and convert
it to a more advanced form step by step. JCR uses a well-known Session
abstraction to access the repository data (similar to the sessions we have
in OS, web, etc).

The features of level 1:

Initiating a session calling login method with the name of
desired workspace and client credentials. It involves some security
mechanisms (JAAS) to authenticate the client and in case the client is
authorized to use the data from a particular workspace, he can
retrieve the session with a workspace tied to it.

Using the obtained session, the client can retrieve data (items)
by traversing the tree, directly accessing a particular item
(requesting path or UUID) or traversing the query result. So an
application developer can choose the "best" form depending on the
content structure and desired operation.

Reading property values. All content of a repository is
ultimately accessed through properties and stored in property values
of predefined types (Boolean, Binary Data, Double, Long, String) and
special types Name, Reference, and Path. It is possible to read
property value without knowing its real name as a primary item.

Export to XML. Repository supports two XML/JCR data model
mappings: system and doc view. The system view provides complete XML
serialization without loss of information and is somewhat difficult
for a human to read. In contrast, the document view is well readable
but does not completely reflect the state of repository, it is used
for Xpath queries.

Query facility with Xpath syntax. Xpath, originally developed
for XML, suits the JCR data model as well because the JCR data model
is very close to XML's one. It is applied to JCR as it would be
applied to the document view of the serialized repository content,
returning a table of property names and content matching the
query.

Discovery of available node types. Every node should have only
one primary node type that defines names, types and other
characteristics of child nodes and properties. It also can have one or
more mixin data types that defines additional characteristics. Level 1
provides methods for discovering available in repository node types
and node types of a concrete node.

Transient namespace remapping. Item name can have prefix,
delimited by a single ':' (colon) character that indicates the
namespace of this name. It is patterned after XML namespaces, prefix
is mapped to URI to minimize names collisions. In Level 1, a prefix
can be temporary overridden by another prefix in the scope of a
session.

1.5.2. Level 2

In addition to the features of the Level 1, it also supports the
following major features:

Adding, moving, copying and removing items inside workspace and
moving, copying and cloning items between workspaces. The client can
also compare the persisted state of an item with its unsaved states
and either save the new state or discard it.

Modifying and writing value of properties. Property types are
checked and can be converted to the defined format.

Importing XML document into the repository as a tree of nodes
and properties. If the XML document is an export of JCR system view,
the content of repository can be completely restored. If this is not
the case, the document is interpreted as a document view and the
import procedure builds a tree of JCR nodes and properties that
matches the tree structure of the XML document.

Assigning node types to nodes. The primary node type is assigned
when adding a node. This can be done automatically based on the parent
node type definition and mixin node types.

1.5.3. Optional features

On the top of Level 1 or Level 2, a number of optional features are
defined for a more advanced repository functionality. This includes
functions such as Versioning, (JTA) Transactions, Query using SQL,
Explicit Locking and Content Observation. eXo JCR supports all optional
features.

1.6. Using JCR

1.6.1. Obtaining a Repository object

A javax.jcr.Repository object can be obtained by:

Using the eXo Container "native" mechanism. All Repositories
are kept with a single RepositoryService component. So it can be
obtained from eXo Container, described as the
following:

1.6.2. JCR Session common considerations

Remember that javax.jcr.Session is not a thread safe object.
Never try to share it between
threads.

Do not use System session
from the user related code
because a system session has unlimited
rights. Call ManageableRepository.getSystemSession()
from process related code
only.

Call Session.logout() explicitly to release resources assigned to the
session.

When designing your application, take care of the Session
policy inside your application. Two strategies are possible: Stateless (Session per business request)
and Stateful (Session per User)
or some mix.

1.6.3. JCR Application practices

1.6.3.1. Simplifying the management of a multi-workspace
application

(one-shot logout for all opened sessions)

Use org.exoplatform.services.jcr.ext.common.SessionProvider which
is responsible for caching/obtaining your JCR Sessions and closing all
opened sessions at once.

The SessionProvider is per-request or per-user object, depending
on your policy. Create it with your application before performing JCR
operations, use it to obtain the Sessions and close at the end of an
application session(request). See the following example:

1.6.3.2. Reusing SessionProvider

As shown above, creating the SessionProvider involves multiple
steps and you may not want to repeat them each time you need to get a
JCR session. In order to avoid all this plumbing code, we provide the
SessionProviderService whose goal is to help you to get a
SessionProvider object.

The org.exoplatform.services.jcr.ext.app.SessionProviderService
interface is defined as follows:

per-request style : keeps a single SessionProvider in a
static ThreadLocal variable

Always use null for the key.

For any implementation, your code should follow the following
sequence :

Call SessionProviderService.setSessionProvider(Object key,
SessionProvider sessionProvider) at the beginning of a business
request for Stateless application or application's session for
Statefull policy.

Call SessionProviderService.removeSessionProvider(Object key)
at the end of a business request for Stateless application or
application's session for Statefull policy.

1.7. Session Leak Detector

1.7.1. Introduction

The session leak detector is able to help you debug your application based on JCR when you suspect that you have a bug related to a wrong usage of JCR sessions. It works by creating a queue of weak references to JCR sessions and the queue is periodically cleaned. When a session reference is dequeued and is not cleared it is considered as a leaked session. Obviously what matters here is the time by which a session is stale known as max age. If the max age is too short period it will suspect that many sessions are leaked although they are not. The default max age value is configured at 2 minutes.

1.7.2. Activation

Setting the virtual machine system property exo.jcr.session.tracking.active to true activates the session detector with a default time period of 2 minutes.

You can set the max age with the virtual machine system property exo.jcr.jcr.session.tracking.maxage in seconds. The default value, if you don't override it is 120 (2 minutes).

We activate the session tracking and configure a maxage of 1 minute. Any JCR session older than 1 minute will cause an alert.

1.7.3. Report

Each detector execution starts with

Starting detector task

and ends with

Finished detector task

When a session is considered as leaked, debug information is printed on the console with a stack trace of the code that created the session in order to help you find out where the leaked session was created at runtime.

Example :

java.lang.Exception
at org.exoplatform.services.jcr.impl.core.SessionReference.<init>(SessionReference.java:113)
at org.exoplatform.services.jcr.impl.core.TrackedXASession.<init>(TrackedXASession.java:32)
at org.exoplatform.services.jcr.impl.core.SessionFactory.createSession(SessionFactory.java:128)
at org.exoplatform.services.jcr.impl.core.RepositoryImpl.getSystemSession(RepositoryImpl.java:314)
at org.exoplatform.services.jcr.impl.core.RepositoryImpl.getSystemSession(RepositoryImpl.java:71)
at org.exoplatform.services.jcr.ext.common.SessionProvider.getSession(SessionProvider.java:157)
at org.exoplatform.faq.service.impl.JCRDataStorage.getFAQServiceHome(JCRDataStorage.java:323)
...

In this Stacktrace we learn that the method org.exoplatform.faq.service.impl.JCRDataStorage.getFAQServiceHome has opened a session that seems to be leaked. You need to verify in the code if Session.logout() is properly called in all cases (calling it in finally clause usually resolves the issue).

1.8. JCR Service Extensions

1.8.1. Concept

eXo JCR supports observation
(JSR-170 8.3), which enables applications to register interest in events
that describe changes to a workspace, and then monitor and respond to
those events. The standard observation feature allows dispatching events
when persistent change to the workspace
is made.

eXo JCR also offers a proprietary Extension
Action which dispatches and fires an event upon each
transient session level change,
performed by a client. In other words, the event is triggered when a
client's program invokes some updating methods in a session or a
workspace (such as: Session.addNode(), Session.setProperty(),
Workspace.move() etc.

One important recommendation should be applied for an extension
action implementation. Each action will add its own execution time to
standard JCR methods (Session.addNode(), Session.setProperty(),
Workspace.move() etc.) execution time. As a consequence, it's necessary
to minimize Action.execute(Context) body execution time.

To make the rule, you can use the dedicated Thread in
Action.execute(Context) body for a custom logic. But if your application
logic requires the action to add items to a created/updated item and you
save these changes immediately after the JCR API method call is
returned, the suggestion with Thread is not applicable for you in this
case.

1.8.2. Implementation

Interceptor framework class diagram

1.8.3. Configuration

Add a SessionActionCatalog
service and an appropriate AddActionsPlugin (see the example below)
configuration to your eXo Container configuration. As usual, the plugin
can be configured as in-component-place, which is the case for a
Standalone Container or externally, which is a usual case for
Root/Portal Container configuration).

Each Action entry is exposed as
org.exoplatform.services.jcr.impl.ext.action. ActionConfiguration of actions collection of
org.exoplatform.services.jcr.impl.ext.action.AddActionsPlugin$ActionsConfig
(see an example below). The mandatory field named actionClassName is the fully qualified name of
org.exoplatform.services.command.action.Action implementation - the
command will be launched in case the current event matches the criteria. All other fields are criteria. The
criteria are *AND*ed together. In other words, for a particular item to
be listened to, it must meet ALL the criteria:

* workspace: the comma delimited
(ORed) list of workspaces

* eventTypes: a comma delimited
(ORed) list of event names (see below)
to be listened to. This is the only mandatory field, others are optional
and if they are missing they are interpreted as ANY.

* path - a comma delimited (ORed)
list of item absolute paths (or within
its subtree if isDeep is true, which is the default value)

* nodeTypes - a comma delimited
(ORed) list of the current NodeType.
Since version 1.6.1 JCR supports the functionalities of nodeType and
parentNodeType. This parameter has different semantics, depending on the
type of the current item and the operation performed. If the current item is a property it means the parent node type. If the current item is a node, the semantic depends on the event type: **
add node event: the node type of the
newly added node. ** add mixin event:
the newly added mixing node type of the current node. ** remove mixin event the removed mixin type of the
current node. ** other events: the
already assigned NodeType(s) of the current node (can be both primary
and mixin).

Content Application: all applications
may use JCR as a data storage. Some of them are generic and completely
decoupled from JCR API as interaction protocol hides Content storage nature
(like WebDav client), some partially decoupled (like Command framework
based), meaning that they do not use JCR API directly, and some (most part)
use JSR-170 directly.

Frameworks is a special kind of JCR
client that acts as an intermediate level between Content Repository and End
Client Application. There are Protocol
(WebDav, RMI or FTP servers for example) and Pattern (Command, Web(servlet), J2EE connector)
specific Frameworks. It is possible to build a multi-layered (in framework
sense) JCR application, for example Web application uses Web framework that
uses Command framework underneath.

1.10.1. Interfaces and methods

1.10.1.1. ExtendedNodeTypeManager

public static final int IGNORE_IF_EXISTS = 0;
public static final int FAIL_IF_EXISTS = 2;
public static final int REPLACE_IF_EXISTS = 4;
/**
* Return NodeType for a given InternalQName.
*
* @param qname nodetype name
* @return NodeType
* @throws NoSuchNodeTypeException if no nodetype found with the name
* @throws RepositoryException Repository error
*/
NodeType findNodeType(InternalQName qname) throws NoSuchNodeTypeException, RepositoryException;
/**
* Registers node type using value object.
*
* @param nodeTypeValue
* @param alreadyExistsBehaviour
* @throws RepositoryException
*/
NodeType registerNodeType(NodeTypeValue nodeTypeValue, int alreadyExistsBehaviour) throws RepositoryException;
/**
* Registers all node types using XML binding value objects from xml stream.
*
* @param xml a InputStream
* @param alreadyExistsBehaviour a int
* @throws RepositoryException
*/
NodeTypeIterator registerNodeTypes(InputStream xml, int alreadyExistsBehaviour, String contentType)
throws RepositoryException;
/**
* Gives the {@link NodeTypeManager}
*
* @throws RepositoryException if another error occurs.
*/
NodeTypeDataManager getNodeTypesHolder() throws RepositoryException;
/**
* Return <code>NodeTypeValue</code> for a given nodetype name. Used for
* nodetype update. Value can be edited and registered via
* <code>registerNodeType(NodeTypeValue nodeTypeValue, int alreadyExistsBehaviour)</code>
* .
*
* @param ntName nodetype name
* @return NodeTypeValue
* @throws NoSuchNodeTypeException if no nodetype found with the name
* @throws RepositoryException Repository error
*/
NodeTypeValue getNodeTypeValue(String ntName) throws NoSuchNodeTypeException, RepositoryException;
/**
* Registers or updates the specified <code>Collection</code> of
* <code>NodeTypeValue</code> objects. This method is used to register or
* update a set of node types with mutual dependencies. Returns an iterator
* over the resulting <code>NodeType</code> objects. <p/> The effect of the
* method is "all or nothing"; if an error occurs, no node types are
* registered or updated. <p/> Throws an
* <code>InvalidNodeTypeDefinitionException</code> if a
* <code>NodeTypeDefinition</code> within the <code>Collection</code> is
* invalid or if the <code>Collection</code> contains an object of a type
* other than <code>NodeTypeDefinition</code> . <p/> Throws a
* <code>NodeTypeExistsException</code> if <code>allowUpdate</code> is
* <code>false</code> and a <code>NodeTypeDefinition</code> within the
* <code>Collection</code> specifies a node type name that is already
* registered. <p/> Throws an
* <code>UnsupportedRepositoryOperationException</code> if this implementation
* does not support node type registration.
*
* @param values a collection of <code>NodeTypeValue</code>s
* @param alreadyExistsBehaviour a int
* @return the registered node types.
* @throws InvalidNodeTypeDefinitionException if a
* <code>NodeTypeDefinition</code> within the
* <code>Collection</code> is invalid or if the
* <code>Collection</code> contains an object of a type other than
* <code>NodeTypeDefinition</code>.
* @throws NodeTypeExistsException if <code>allowUpdate</code> is
* <code>false</code> and a <code>NodeTypeDefinition</code> within
* the <code>Collection</code> specifies a node type name that is
* already registered.
* @throws UnsupportedRepositoryOperationException if this implementation does
* not support node type registration.
* @throws RepositoryException if another error occurs.
*/
public NodeTypeIterator registerNodeTypes(List<NodeTypeValue> values, int alreadyExistsBehaviour)
throws UnsupportedRepositoryOperationException, RepositoryException;
/**
* Unregisters the specified node type.
*
* @param name a <code>String</code>.
* @throws UnsupportedRepositoryOperationException if this implementation does
* not support node type registration.
* @throws NoSuchNodeTypeException if no registered node type exists with the
* specified name.
* @throws RepositoryException if another error occurs.
*/
public void unregisterNodeType(String name) throws UnsupportedRepositoryOperationException, NoSuchNodeTypeException,
RepositoryException;
/**
* Unregisters the specified set of node types.<p/> Used to unregister a set
* of node types with mutual dependencies.
*
* @param names a <code>String</code> array
* @throws UnsupportedRepositoryOperationException if this implementation does
* not support node type registration.
* @throws NoSuchNodeTypeException if one of the names listed is not a
* registered node type.
* @throws RepositoryException if another error occurs.
*/
public void unregisterNodeTypes(String[] names) throws UnsupportedRepositoryOperationException,
NoSuchNodeTypeException, RepositoryException;

1.10.1.2. NodeTypeValue

The NodeTypeValue interface represents a simple container
structure used to define node types which are then registered through
the ExtendedNodeTypeManager.registerNodeType method. The implementation
of this interface does not contain any validation logic.

1.10.1.3. NodeDefinitionValue

The NodeDefinitionValue interface extends ItemDefinitionValue with
the addition of writing methods, enabling the characteristics of a child
node definition to be set, after that the NodeDefinitionValue is added
to a NodeTypeValue.

1.10.1.4. PropertyDefinitionValue

The PropertyDefinitionValue interface extends ItemDefinitionValue
with the addition of writing methods, enabling the characteristics of a
child property definition to be set, after that the
PropertyDefinitionValue is added to a NodeTypeValue.

Note that the existing data must be consistent before changing or
removing a existing definition . JCR does not
allow you to change the node type in the way in which the
existing data would be incompatible with a new node type. But if these
changes are needed, you can do it in several phases, consistently
changing the node type and the existing data.

For example:

Add a new residual property definition with name "downloadCount"
to the existing node type "myNodeType".

There are two limitations that do not allow us to make the task
with a single call of registerNodeType method.

Existing nodes of the type "myNodeType", which does not
contain properties "downloadCount" that conflicts with node type
what we need.

Registered node type "myNodeType" will not allow us to add
properties "downloadCount" because it has no such specific
properties.

1.11. Registry Service

1.11.1. Concept

The Registry Service is one of the key parts of the infrastructure
built around eXo JCR. Each JCR that is based on service, applications, etc
may have its own configuration, settings data and other data that have to
be stored persistently and used by the approptiate service or application.
( We call it "Consumer").

The service acts as a centralized collector (Registry) for such
data. Naturally, a registry storage is JCR based i.e. stored in some JCR
workspace (one per Repository) as an Item tree under /exo:registry node.

Despite the fact that the structure of the tree is well defined (see
the scheme below), it is not recommended for other services to manipulate
data using JCR API directly for better flexibility. So the Registry
Service acts as a mediator between a Consumer and its settings.

The proposed structure of the Registry Service storage is divided
into 3 logical groups: services, applications and users:

Each upper level eXo Service may store its configuration in eXo
Registry. At first, start from xml-config (in jar etc) and then from
Registry. In configuration file, you can add force-xml-configuration
parameter to component to ignore reading parameters initialization from
RegistryService and to use file instead:

As you can see it looks like a simple CRUD interface for the
RegistryEntry object which wraps registry data for some Consumer as a
Registry Entry. The Registry Service itself knows nothing about the
wrapping data, it is Consumer's responsibility to manage and use its data
in its own way.

To create an Entity Consumer you should know how to serialize the
data to some XML structure and then create a RegistryEntry from these data
at once or populate them in a RegistryEntry object (using
RegistryEntry(String entryName) constructor and then obtain and fill a DOM
document).

1.11.3. Configuration

RegistryService has two optional params: value parameter mixin-names and properties parameter locations. The mixin-names is used for adding
additional mixins to nodes exo:registry, exo:applications, exo:services,
exo:users and exo:groups of RegistryService. This allows the top level
applications to manage these nodes in special way. Locations is used to
mention where exo:registry is placed for each repository. The name of each
property is interpreted as a repository name and its value as a workspace
name (a system workspace by default).

1.13. Node Types and Namespaces

Support of node types and namespaces is required by the JSR-170
specification. Beyond the methods required by the specification, eXo JCR
has its own API extension for the Node type registration as well
as the ability to declaratively define node types in the Repository at the
start-up time.

1.13.1. Node Types definition

Your custom service can register some neccessary predefined node
types at the start-up time. The node definition should be placed in a
special XML file (see DTD below) and declared in the service's
configuration file thanks to eXo component plugin mechanism, described as
follows:

There are two types of registration. The first type is the
registration of node types in all created repositories, it is configured
in values-param with the name autoCreatedInNewRepository. The second type is
registration of node types in specified repository and it is configured in
values-param with the name of repository.

1.14.1. Related documents

1.14.2. Portal and Standalone configuration

Like other eXo services, eXo JCR can be configured and used in the
portal or embedded mode (as a service embedded in GateIn) and in
standalone mode.

In Embedded mode, JCR services are registered in the Portal
container and the second option is to use a Standalone container. The main
difference between these container types is that the first one is intended
to be used in a Portal (Web) environment, while the second one can be used
standalone (see the comprehensive page Service Configuration
for Beginners for more details).

Configuration that is set explicitly using
StandaloneContainer.addConfigurationURL(String url) or
StandaloneContainer.addConfigurationPath(String path) before
getInstance()

Configuration from $base:directory/exo-configuration.xml or
$base:directory/conf/exo-configuration.xml file. Where
$base:directory is either AS's home directory in case of J2EE AS
environment or just the current directory in case of a standalone
application.

/conf/exo-configuration.xml in the current classloader (e.g.
war, ear archive)

Configuration from
$service_jar_file/conf/portal/configuration.xml. WARNING: Don't rely
on some concrete jar's configuration if you have more than one jar
containing conf/portal/configuration.xml file. In this case choosing
a configuration is unpredictable.

max-backup-files : max number of backup files. This option lets you specify the number of stored backups.
Number of backups can't exceed this value. File which will exceed the limit will replace the oldest file.

Service configuration may be placed in
jar:/conf/standalone/exo-jcr-config.xml for standalone mode. For portal
mode, it is located in the portal web application
portal/WEB-INF/conf/jcr/repository-configuration.xml.

default-repository: The name of a
default repository (one returned by
RepositoryService.getRepository()).

repositories: The list of
repositories.

1.14.5. Repository configuration

name: The name of a
repository.

default-workspace: The name of a
workspace obtained using Session's login() or login(Credentials) methods
(ones without an explicit workspace name).

system-workspace: The name of
workspace where /jcr:system node is
placed.

security-domain: The name of a
security domain for JAAS authentication.

access-control: The name of an
access control policy. There can be 3 types: optional - ACL is created
on-demand(default), disable - no access control, mandatory - an ACL is
created for each added node(not supported yet).

authentication-policy: The name of
an authentication policy class.

workspaces: The list of
workspaces.

session-max-age: The time after
which an idle session will be removed (called logout). If session-max-age
is not set up, idle session will never be removed.

lock-remover-max-threads: Number of threads that
can serve LockRemover tasks. Default value is 1. Repository may have many
workspaces, each workspace have own LockManager. JCR supports Locks with
defined lifetime. Such a lock must be removed is it become expired. That
is what LockRemovers does. But LockRemovers is not an independent
timer-threads, its a task that executed each 30 seconds. Such a task is
served by ThreadPoolExecutor which may use different number of
threads.

auto-init-permissions: DEPRECATED
in JCR 1.9 (use initializer). Default permissions of the root node. It is
defined as a set of semicolon-delimited permissions containing a group of
space-delimited identities (user, group, etc, see Organization service
documentation for details) and the type of permission. For example, any
read; :/admin read;:/admin add_node;
:/admin set_property;:/admin remove means
that users from group admin have all
permissions and other users have only a 'read' permission.

1.14.7. Workspace data container configuration

class: A workspace data container
class name.

properties: The list of properties
(name-value pairs) for the concrete Workspace data container.

Table 1.2.

trigger-events-for-descendants-on-rename

indicates if need to trigger events for descendants on
rename or not. It allows to increase performance on rename
operation but in same time Observation'll not notified, has
default value true

lazy-node-iterator-page-size

the page size for lazy iterator. Indicates how many nodes
can be retrieved from storage per request. The default value is
100

Expected number of ACL-elements in the Bloom-filter.
Default value 1000000. (See the note below)

Note

Bloom filters are not supported by all the cache implementations
so far only the inplementation for infinispan supports it.

value-storages: The list of value
storage plugins.

1.14.8. Value Storage plugin configuration (for data container):

Note

The value-storage element is optional. If you don't include it,
the values will be stored as BLOBs inside the database.

value-storage: Optional value
Storage plugin definition.

class: A value storage plugin class
name (attribute).

properties: The list of properties
(name-value pairs) for a concrete Value Storage plugin.

filters: The list of filters
defining conditions when this plugin is applicable.

1.14.9. Initializer configuration (optional)

class: Initializer implementation
class.

properties: The list of properties
(name-value pairs). Properties are supported.

root-nodetype: The node type for
root node initialization.

root-permissions: Default
permissions of the root node. It is defined as a set of
semicolon-delimited permissions containing a group of space-delimited
identities (user, group etc, see Organization service documentation for
details) and the type of permission. For example any read; :/admin read;:/admin add_node; :/admin set_property;:/admin remove means that
users from group admin have all
permissions and other users have only a 'read' permission.

1.14.13. Help application to prohibit the use of closed sessions

Products that use eXo JCR, sometimes missuse it since they continue
to use a session that has been closed through a method call on a node, a
property or even the session itself. To prevent bad practices we propose
three modes which are the folllowing:

If the system property
exo.jcr.prohibit.closed.session.usage has been
set to true, then a RepositoryException will be
thrown any time an application will try to access to a closed session.
In the stack trace, you will be able to know the call stack that
closes the session.

If the system property
exo.jcr.prohibit.closed.session.usage has not
been set and the system property
exo.product.developing has been set to
true, then a warning will be logged in the log
file with the full stack trace in order to help identifying the root
cause of the issue. In the stack trace, you will be able to know the
call stack that closes the session.

If none of the previous system properties have been set, then we
will ignore that the issue and let the application use the closed
session as it was possible before without doing anything in order to
allow applications to migrate step by step.

1.14.14. Help application to allow the use of closed datasources

Since usage of closed session affects usage of closed datasource
we propose three ways to resolve such kind of isses:

If the system property
exo.jcr.prohibit.closed.datasource.usage is
set to true (default value) then a SQLException will be
thrown any time an application will try to access to a closed datasource.
In the stack trace, you will be able to know the call stack that
closes the datasource.

If the system property
exo.jcr.prohibit.closed.datasource.usage is
set to false and the system property
exo.product.developing is set to
true, then a warning will be logged in the log
file with the full stack trace in order to help identifying the root
cause of the issue. In the stack trace, you will be able to know the
call stack that closes the datasource.

If the system property
exo.jcr.prohibit.closed.datasource.usage is
set to false and the system property
exo.product.developing is set to
false usage of closed datasource will be allowed
and nothing will be logged or thrown.

1.14.15. Getting the effective configuration at Runtime of all the
repositories

The effective configuration of all the repositories and their
workspaces can be known thanks to the method
getConfigurationXML() that is exposed through JMX at
the RepositoryServiceConfiguration level in case of a
PortalContainer the name of the related MBean will be
of type
exo:portal=${portal-container-name},service=RepositoryServiceConfiguration.
This method will give you the effective configuration in XML format that
has been really interpreted by the the JCR core. This could be helpful to
understand how your repositories/workspaces are configured especially if
you would like to overwrite the configuration for some reasons.

1.15. Multi-language support in eXo JCR RDB backend

Whenever relational database is used to store multilingual text data
of eXo Java Content Repository, it is necessary to adapt configuration in order to
support UTF-8 encoding. Here is a short HOWTO instruction for several
supported RDBMS with examples.

The configuration file you have to modify:
.../webapps/portal/WEB-INF/conf/jcr/repository-configuration.xml

Note

Datasource jdbcjcr used in examples can be
configured via InitialContextInitializer
component.

1.15.1. Oracle

In order to run multilanguage JCR on an Oracle backend Unicode
encoding for characters set should be applied to the database. Other
Oracle globalization parameters don't make any impact. The only property
to modify is NLS_CHARACTERSET.

We have tested NLS_CHARACTERSET =
AL32UTF8 and it works well for many European and
Asian languages.

Note

For DB2 v.8.x support change the property "dialect" to
db2v8.

1.15.3. MySQL

JCR MySQL-backend requires special dialect MySQL-UTF8
to be used for internationalization support. But the database default
charset should be latin1 to use limited index space effectively (1000
bytes for MyISAM engine, 767 for InnoDB). If database default charset is
multibyte, a JCR database initialization error is thrown concerning index
creation failure. In other words, JCR can work on any singlebyte default
charset of database, with UTF8 supported by MySQL server. But we have
tested it only on latin1 database default charset.

1.15.4. PostgreSQL

On PostgreSQL-backend, multilingual support can be enabled in different
ways:

Using the locale features of the operating system to provide
locale-specific collation order, number formatting, translated
messages, and other aspects. UTF-8 is widely used on Linux
distributions by default, so it can be useful in such case.

Providing a number of different character sets defined in the
PostgreSQL server, including multiple-byte character sets, to
support storing text of any languages, and providing character set
translation between client and server. We recommend to use UTF-8
database charset, it will allow any-to-any conversations and make
this issue transparent for the JCR.

Create database with UTF-8 encoding and use PgSQL dialect for
Workspace Container:

1.16. How to host several JCR instances on the same database
instance?

Frequently, a single database instance must be shared by several
other applications. But some of our customers have also asked for a way to
host several JCR instances in the same database instance. To fulfill this
need, we had to review our queries and scope them to the current schema;
it is now possible to have one JCR instance per DB schema instead of per DB
instance. To benefit of the work done for this feature you will need to
apply the configuration changes described below.

1.16.1. LockManager configuration

To enable this feature you need to replace
org.jboss.cache.loader.JDBCCacheLoader with
org.exoplatform.services.jcr.impl.core.lock.jbosscache.JDBCCacheLoader in
JBossCache configuration file.

1.16.2. HibernateService configuration

If you use HibernateService for JDBC connections management you will
need to specify explicitly the default schema by setting
"hibernate.default_schema" property in the configuration of
HibernateService.

1.17.2. Configuration parameters

The location of the index directory. This parameter is
mandatory. Up to 1.9, this parameter called "indexDir"

1.0

use-compoundfile

true

Advises lucene to use compound files for the index
files.

1.9

min-merge-docs

100

Minimum number of nodes in an index until segments are
merged.

1.9

volatile-idle-time

3

Idle time in seconds until the volatile index part is moved
to a persistent index even though minMergeDocs is not
reached.

1.9

max-merge-docs

Integer.MAX_VALUE

Maximum number of nodes in segments that will be merged.
The default value changed in JCR 1.9 to Integer.MAX_VALUE.

1.9

merge-factor

10

Determines how often segment indices are merged.

1.9

max-field-length

10000

The number of words that are fulltext indexed at most per
property.

1.9

cache-size

1000

Size of the document number cache. This cache maps uuids to
lucene document numbers

1.9

force-consistencycheck

false

Runs a consistency check on every startup. If false, a
consistency check is only performed when the search index detects
a prior forced shutdown.

1.9

auto-repair

true

Errors detected by a consistency check are automatically
repaired. If false, errors are only written to the log.

1.9

query-class

QueryImpl

Class name that implements the javax.jcr.query.Query
interface.This class must also extend from the class:
org.exoplatform.services.jcr.impl.core.query.AbstractQueryImpl.

1.9

document-order

true

If true and the query does not contain an 'order by'
clause, result nodes will be in document order. For better
performance when queries return a lot of nodes set to
'false'.

1.9

result-fetch-size

Integer.MAX_VALUE

The number of results when a query is executed. Default
value: Integer.MAX_VALUE (-> all).

1.9

excerptprovider-class

DefaultXMLExcerpt

The name of the class that implements
org.exoplatform.services.jcr.impl.core.query.lucene.ExcerptProvider
and should be used for the rep:excerpt() function in a
query.

1.9

support-highlighting

false

If set to true additional information is stored in the
index to support highlighting using the rep:excerpt()
function.

1.9

synonymprovider-class

none

The name of a class that implements
org.exoplatform.services.jcr.impl.core.query.lucene.SynonymProvider.
The default value is null (-> not set).

1.9

synonymprovider-config-path

none

The path to the synonym provider configuration file. This
path interpreted is relative to the path parameter. If there is a
path element inside the SearchIndex element, then this path is
interpreted and relative to the root path of the path. Whether
this parameter is mandatory or not, it depends on the synonym
provider implementation. The default value is null (-> not
set).

1.9

indexing-configuration-path

none

The path to the indexing configuration file.

1.9

indexing-configuration-class

IndexingConfigurationImpl

The name of the class that implements
org.exoplatform.services.jcr.impl.core.query.lucene.IndexingConfiguration.

1.9

force-consistencycheck

false

If setting to true, a consistency check is performed,
depending on the parameter forceConsistencyCheck. If setting to
false, no consistency check is performed on startup, even if a
redo log had been applied.

1.9

spellchecker-class

none

The name of a class that implements
org.exoplatform.services.jcr.impl.core.query.lucene.SpellChecker.

1.9

spellchecker-more-popular

true

If setting true, spellchecker returns only the suggest
words that are as frequent or more frequent than the checked word.
If setting false, spellchecker returns null (if checked word exit
in dictionary), or spellchecker will return most close suggest
word.

1.10

spellchecker-min-distance

0.55f

Minimal distance between checked word and proposed suggest
word.

1.10

errorlog-size

50(Kb)

The default size of error log file in Kb.

1.9

upgrade-index

false

Allows JCR to convert an existing index into the new
format. Also, it is possible to set this property via system
property, for example: -Dupgrade-index=true Indexes before JCR
1.12 will not run with JCR 1.12. Hence you have to run an
automatic migration: Start JCR with -Dupgrade-index=true. The old
index format is then converted in the new index format. After the
conversion the new format is used. On the next start, you don't
need this option anymore. The old index is replaced and a back
conversion is not possible - therefore better take a backup of the
index before. (Only for migrations from JCR 1.9 and
later.)

1.12

analyzer

org.apache.lucene.analysis.standard.StandardAnalyzer

Class name of a lucene analyzer to use for fulltext
indexing of text.

1.12

1.17.3. Global Search Index

1.17.3.1. Global Search Index Configuration

The global search index is configured in the above-mentioned
configuration file
(portal/WEB-INF/conf/jcr/repository-configuration.xml)
in the tag "query-handler".

In fact, when using Lucene, you should always use the same
analyzer for indexing and for querying, otherwise the results are
unpredictable. You don't have to worry about this, eXo JCR does this for
you automatically. If you don't like the StandardAnalyzer configured by
default, just replace it by your own.

If you don't have a handy QueryHandler, you should learn how
to create a customized Handler in 5 minutes.

1.17.3.2. Customized Search Indexes and Analyzers

By default Exo JCR uses the Lucene standard Analyzer to index
contents. This analyzer uses some standard filters in the method that
analyzes the content:

1.17.3.2.2. Creating the analyzer

The analyzer has to extends
org.apache.lucene.analysis.standard.StandardAnalyzer, and overload the
method

public TokenStream tokenStream(String fieldName, Reader reader)

to put your own filters. You can have a glance at the example analyzer attached to this article.

1.17.3.2.3. Creating the search index

Now, we have the analyzer, we have to write the SearchIndex,
which will use the analyzer. Your have to extends
org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex. You
have to write the constructor, to set the right analyzer, and the
method

public Analyzer getAnalyzer() {
return MyAnalyzer;
}

to return your analyzer. You can see the attached
SearchIndex.

Note

Since 1.12 version, we can set Analyzer directly in
configuration. So, creation new SearchIndex only for new Analyzer is
redundant.

1.17.3.2.4. Configuring your application to use your SearchIndex

In
portal/WEB-INF/conf/jcr/repository-configuration.xml,
you have to replace each

1.17.4.2.2. Indexing Boost Value

It is also possible to configure a boost value
for the nodes that match the index rule. The default boost value is
1.0. Higher boost values (a reasonable range is 1.0 - 5.0) will yield
a higher score value and appear as more relevant.

The indexing configuration also allows you to specify the type
of a node in the condition. Please note however that the type match
must be exact. It does not consider sub types of the specified node
type.

1.17.4.2.4. Exclusion from the Node Scope Index

Per default all configured properties are fulltext indexed if
they are of type STRING and included in the node scope index. A node
scope search finds normally all nodes of an index. That is, the select
jcr:contains(., 'foo') returns all nodes that have a string property
containing the word 'foo'. You can exclude explicitly a property from
the node scope index:

1.17.4.4. Property-Level Analyzers

1.17.4.4.1. Example

In this configuration section, you define how a property has to
be analyzed. If there is an analyzer configuration for a property,
this analyzer is used for indexing and searching of this property. For
example:

The configuration above means that the property "mytext" for the
entire workspace is indexed (and searched) with the Lucene
KeywordAnalyzer, and property "mytext2" with the WhitespaceAnalyzer.
Using different analyzers for different languages is particularly
useful.

The WhitespaceAnalyzer tokenizes a property, the KeywordAnalyzer
takes the property as a whole.

1.17.4.4.2. Characteristics of Node Scope Searches

When using analyzers, you may encounter an unexpected behavior
when searching within a property compared to searching within a node
scope. The reason is that the node scope always uses the global
analyzer.

Let's suppose that the property "mytext" contains the text :
"testing my analyzers" and that you haven't configured any analyzers
for the property "mytext" (and not changed the default analyzer in
SearchIndex).

If your query is for example:

xpath = "//*[jcr:contains(mytext,'analyzer')]"

This xpath does not return a hit in the node with the property
above and default analyzers.

Also a search on the node scope

xpath = "//*[jcr:contains(.,'analyzer')]"

won't give a hit. Realize that you can only set specific analyzers
on a node property, and that the node scope indexing/analyzing is always
done with the globally defined analyzer in the SearchIndex element.

Now, if you change the analyzer used to index the "mytext"
property above to

you
would get a hit because of the word stemming (analyzers -
analyzer).

The other search,

xpath = "//*[jcr:contains(.,'analyzer')]"

still would not give a result, since the node scope is indexed
with the global analyzer, which in this case does not take into
account any word stemming.

In conclusion, be aware that when using analyzers for specific
properties, you might find a hit in a property for some search text,
and you do not find a hit with the same search text in the node scope
of the property!

Note

Both index rules and index aggregates influence how content is
indexed in JCR. If you change the configuration, the existing
content is not automatically re-indexed according to the new rules.
You, therefore, have to manually re-index the content when you
change the configuration!

1.17.4.5. Advanced features

eXo JCR supports some advanced features, which are not specified
in JSR 170:

Get a text excerpt with highlighted
words that matches the query: ExcerptProvider.

In the example, Repository Service will read the configuration from
the file /conf/standalone/exo-jcr-config.xml.

But in some cases, it's required to change the configuration on the
fly. And know that the new one will be used. Additionally we wish not to
modify the original file.

In this case, we have to use the configuration persister feature
which allows to store the configuration in different locations.

1.18.2. Usage

On startup RepositoryServiceConfiguration
component checks if a configuration persister was configured. In that
case, it uses the provided ConfigurationPersister
implementation class to instantiate the persister object.

Each database software supports ANSI SQL standards but also has its
own specifics. So, each database has its own configuration in eXo JCR as a
database dialect parameter. If you need a more detailed configuration of the
database, it's possible to do that by editing the metadata SQL-script
files.

SQL-scripts you can obtain from jar-file
exo.jcr.component.core-XXX.XXX.jar:conf/storage/. They also can be found at
GitHub here.

In the next two tables correspondence between the scripts and
databases is shown.

Table 1.4. Single-database

MySQL DB

jcr-sjdbc.mysql.sql

MySQL DB with utf-8

jcr-sjdbc.mysql-utf8.sql

MySQL DB with MyISAM*

jcr-sjdbc.mysql-myisam.sql

MySQL DB with MyISAM and utf-8*

jcr-sjdbc.mysql-myisam-utf8.sql

PostgresSQL

jcr-sjdbc.pqsql.sql

Oracle DB

jcr-sjdbc.ora.sql

DB2

jcr-sjdbc.db2.sql

MS SQL Server

jcr-sjdbc.mssql.sql

Sybase

jcr-sjdbc.sybase.sql

HSQLDB

jcr-sjdbc.sql

Table 1.5. Multi-database

MySQL DB

jcr-mjdbc.mysql.sql

MySQL DB with utf-8

jcr-mjdbc.mysql-utf8.sql

MySQL DB with MyISAM*

jcr-mjdbc.mysql-myisam.sql

MySQL DB with MyISAM and utf-8*

jcr-mjdbc.mysql-myisam-utf8.sql

PostgresSQL

jcr-mjdbc.pqsql.sql

Oracle DB

jcr-mjdbc.ora.sql

DB2

jcr-mjdbc.db2.sql

MS SQL Server

jcr-mjdbc.mssql.sql

Sybase

jcr-mjdbc.sybase.sql

HSQLDB

jcr-mjdbc.sql

In case the non-ANSI node name is used, it's necessary to use a
database with MultiLanguage support. Some JDBC
drivers need additional parameters for establishing a Unicode friendly
connection. E.g. under mysql it's necessary to add an additional parameter
for the JDBC driver at the end of JDBC URL. For instance:
jdbc:mysql://exoua.dnsalias.net/portal?characterEncoding=utf8

There are preconfigured configuration files for HSQLDB. Look for these
files in /conf/portal and /conf/standalone folders of the jar-file
exo.jcr.component.core-XXX.XXX.jar or source-distribution
of eXo JCR implementation.

By default, the configuration files are located in service jars
/conf/portal/configuration.xml (eXo services including
JCR Repository Service) and exo-jcr-config.xml
(repositories configuration). In GateIn product, JCR is configured in
portal web application
portal/WEB-INF/conf/jcr/jcr-configuration.xml (JCR
Repository Service and related serivces) and repository-configuration.xml
(repositories configuration).

1.19.1. General recommendations for database configuration

Please note, that JCR requires at least READ_COMMITED isolation
level and other RDBMS configurations can cause some side-effects and
issues. So, please, make sure proper isolation level is configured on
database server side.

1.19.1.1. DB2 configuration

RDBMS reindexing feature use queries based on LIMIT and OFFSET
clauses which are not enabled by default. However, you can ensure
they are enabled by executing the following

$ db2set DB2_COMPATIBILITY_VECTOR=MYS
$ db2stop
$ db2start

Statistics is collected automatically
starting from DB2 Version 9, however it is needed to launch
statistics collection manually during the very first start,
otherwise it could be very long. You need to run 'RUNSTATS'
command

RUNSTATS ON TABLE <scheme>.<table> WITH DISTRIBUTION AND INDEXES ALL

for JCR_SITEM (or JCR_MITEM) and JCR_SVALUE (or JCR_MVALUE)
tables.

1.19.1.2. MySQL configuration

To prevent any consistency issues please ensure that InnoDB is
configured as default MySQL engine (instead of MyISAM by default)
before launching your application for the very first time otherwise
when the application will create the tables MyISAM will be used as
MySQL engine which is a non transactional engine that doesn't not
support integrity constraints so even if later you swicth to InnoDB
using an alter table all the integrity constraints would be missing
as they would have been removed at table creation table by
MyISAM.

MyISAM is not supported due to its lack of transaction support
and integrity check, so use it only if you don't expect any support
and if performances in read accesses are more important than the
consistency in your use-case. Therefore the dialects mysql-myisam
and mysql-myisam-utf8 are only dedicated to the community.

MySQL relies on collected statistics for keeping track of data
distribution in tables and for optimizing join statements, but you
can manually call 'ANALYZE'
to update statistics if needed. For example

ANALYZE TABLE JCR_SITEM, JCR_SVALUE

1.19.1.3. PostgreSQL configuration

Be aware, when using RDBMS for reindexing need to set
"enable_seqscan" to "off" or "default_statistics_target" at least
"50"

Though PostgreSQL server performs query optimization
automatically, you can manualy call 'ANALYZE'
command to collect statistics which can influence the performance.
For example

ANALYZE JCR_SITEM
ANALYZE JCR_SVALUE

1.19.1.4. MS SQL configuration

One more mandatory JCR requirement for underlying databases is
a case sensitive collation. Microsoft SQL Server both 2005 and 2008
customers must configure their server with collation corresponding
to personal needs and requirements, but obligatorily case sensitive.
For more information please refer to Microsoft SQL Server
documentation page "Selecting a SQL Server Collation" here.

MS SQL DB server's optimizer automatically processes queries
to increase performance. Optimization is based on statistical data
which is collected automatically, but you can manually call Transact-SQL
command 'UPDATE
STATISTICS' which in very few situations may increase
performance. For example

UPDATE STATISTICS JCR_SITEM
UPDATE STATISTICS JCR_SVALUE

1.19.1.5. Sybase configuration

Sybase DB Server optimizer automatically processes queries to
increase performance. Optimization is based on statistical data
which is collected automatically, but you can manually call Transact-SQL
command 'update
statistics' which in very few situations may increase
performance. For example

update statistics JCR_SITEM
update statistics JCR_SVALUE

1.19.1.6. Oracle configuration

Oracle DB automatically collects statistics to optimize
performance of queries, but you can manually call 'ANALYZE'
command to start collecting statistics immediately which may improve
performance. For example

1.19.2. Multi-database Configuration

You need to configure each workspace in a repository. You may have
each one on different remote servers as far as you need.

First of all configure the data containers in the
org.exoplatform.services.naming.InitialContextInitializer
service. It's the JNDI context initializer which registers (binds) naming
resources (DataSources) for data containers.

eXo JCR uses query hints only for Complex Query Oracle dialect. For
all other dialects this parameter is ignored.

1.19.6. Notes for Microsoft Windows users

The current configuration of eXo JCR uses Apache DBCP connection pool
(org.apache.commons.dbcp.BasicDataSourceFactory).
It's possible to set a big value for maxActive parameter in
configuration.xml. That means usage of lots of TCP/IP
ports from a client machine inside the pool (i.e. JDBC driver). As a
result, the data container can throw exceptions like "Address already in
use". To solve this problem, you have to configure the client's machine
networking software for the usage of shorter timeouts for opened TCP/IP
ports.

Microsoft Windows has MaxUserPort,
TcpTimedWaitDelay registry keys in the node
HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesTcpipParameters,
by default these keys are unset, set each one with values like
these:

1.20. External Value Storages

By default JCR Values are stored in the Workspace Data container
along with the JCR structure (i.e. Nodes and Properties). eXo JCR offers
an additional option of storing JCR Values separately from Workspace Data
container, which can be extremely helpful to keep Binary Large Objects
(BLOBs) for example.

Value storage configuration is a part of Repository configuration,
find more details there.

Tree-based storage is recommended for most of cases. If you run an
application on Amazon EC2 - the S3 option may be interesting for
architecture. Simple 'flat' storage is good in speed of creation/deletion
of values, it might be a compromise for a small storages.

id: The value storage unique
identifier, used for linking with properties stored in workspace
container.

path: A location where value files will
be stored.

Each file value storage can have the filter(s)
for incoming values. A filter can match values by property type
(property-type), property name
(property-name), ancestor path
(ancestor-path) and/or size of values stored
(min-value-size, in bytes). In code sample, we use a
filter with property-type and min-value-size only. I.e. storage for binary
values with size greater of 1MB. It's recommended to store properties with
large values in file value storage only.

Another example shows a value storage with different locations for
large files (min-value-size a 20Mb-sized filter). A
value storage uses ORed logic in the process of filter selection. That
means the first filter in the list will be asked first and if not matched
the next will be called etc. Here a value matches the 20 MB-sized filter
min-value-size and will be stored in the path
"data/20Mvalues", all other in "data/values".

1.20.3. Content Addressable Value storage (CAS) support

Note

Content-addressable storage, also referred to as associative
storage and abbreviated CAS, is a mechanism for storing information that
can be retrieved based on its content, not its storage location. It is
typically used for high-speed storage and retrieval of fixed content,
such as documents stored for compliance with government
regulations.

Content Addressable Value storage stores unique content once.
Different properties (values) with same content will be stored as one data
file shared between those values. We can tell the Value content will be
shared across some Values in storage and will be stored on one physical
file.

Storage size will be decreased for application which governs
potentially same data in the content.

Note

For example: if you have 100 different properties containing the
same data (e.g. mail attachment), the storage stores only one single
file. The file will be shared with all referencing properties.

If property Value changes, it is stored in an additional file.
Alternatively the file is shared with other values, pointing to the same
content.

The storage calculates Value content address each time the property
was changed. CAS write operations are much more expensive compared to the
non-CAS storages.

Content address calculation based on java.security.MessageDigest
hash computation and tested with MD5 and
SHA1 algorithms.

Note

CAS storage works most efficiently on data that does not change
often. For data that changes frequently, CAS is not as efficient as
location-based addressing.

CAS support can be enabled for Tree and
Simple File Value Storage types.

To enable CAS support, just configure it in JCR Repositories
configuration as we do for other Value Storages.

1.21. Workspace Data Container

Each Workspace of JCR has its own persistent storage to hold
workspace's items data. eXo Content Repository can be configured so that it
can use one or more workspaces that are logical units of the repository
content. Physical data storage mechanism is configured using mandatory
element container. The type of container is
described in the attribute class = fully
qualified name of
org.exoplatform.services.jcr.storage.WorkspaceDataContainer subclass
like

Note

Bloom filters are not supported by all the cache implementations so
far only the inplementation for infinispan supports it.

Bloom-filter used to avoid read nodes that definitely do not have
ACL. acl-bloomfilter-false-positive-probability and
acl-bloomfilter-elements-number used to
configure such filters. Bloom filters are not supported by all the cache
implementations so far only the inplementation for infinispan supports
it.

Workspace Data Container MAY support external storages for
javax.jcr.Value (which can be the case for BLOB values for example) using
the optional element value-storages. Data
Container will try to read or write Value using underlying value storage
plugin if the filter criteria (see below) match the current property.

1.23.2. Requirements

1.23.2.1. Environment requirements

Every node of cluster MUST have the same mounted Network File
System with the read and write permissions on it.

"/mnt/tornado" - path to the mounted Network File System (all
cluster nodes must use the same NFS).

Every node of cluster MUST use the same database.

The same Clusters on different nodes MUST have the same names
(e.g., if Indexer cluster in workspace production on the first node
has the name "production_indexer_cluster", then indexer clusters in
workspace production on all other nodes MUST have the same name
"production_indexer_cluster" ).

1.23.2.2. Configuration requirements

Configuration of every workspace in repository must contains of
such parts:

1.24. JBoss Cache configuration

This section will show you how to use and configure Jboss Cache in the
clustered environment. Also, you will know how to use a template-based
configuration offered by eXo JCR for JBoss Cache instances.

Each mentioned components uses instances of JBoss Cache product for
caching in clustered environment. So every element has its own transport
and has to be configured in a proper way. As usual, workspaces have
similar configuration but with different cluster-names and may-be some
other parameters. The simplest way to configure them is to define their
own configuration files for each component in each workspace:

But if there are few workspaces, configuring them in such a way can
be painful and hard-manageable. eXo JCR offers a template-based
configuration for JBoss Cache instances. You can have one template for
Lock Manager, one for Indexer and one for data container and use them in
all the workspaces, defining the map of substitution parameters in a main
configuration file. Just simply define ${jbosscache-<parameter
name>} inside xml-template and list correct value in JCR configuration
file just below "jbosscache-configuration", as shown:

1.24.2. JGroups configuration

JGroups is used by JBoss Cache for network communications and
transport in a clustered environment. If property "jgroups-configuration"
is defined in component configuration, it will be injected into the JBoss
Cache instance on startup.

As mentioned above, each component (lock manager, data container and
query handler) for each workspace requires its own clustered environment.
In other words, they have their own clusters with unique names. By
default, each cluster should perform multi-casts on a separate port. This
configuration leads to much unnecessary overhead on cluster. That's why
JGroups offers multiplexer feature, providing ability to use one single
channel for set of clusters. This feature reduces network overheads and
increase performance and stability of application. To enable multiplexer
stack, you should define appropriate configuration file (upd-mux.xml is
pre-shipped one with eXo JCR) and set "jgroups-multiplexer-stack" to
"true".

It is now highly recommended to use the shared transport
instead of the multiplexer, to do so simply disable the
multiplexer stack in the configuration of each component by setting the
property jgroups-multiplexer-stack to
false then you will need to ensure that the format of
your jgroups configuration is not anymore a jgroups stack definitions but
a normal configuration and finally you will need to set the property
singleton_name of your JGroups configuration to a
unique name (this name must not be the same from one portal container to
another).

1.24.3. Allow to share JBoss Cache instances

A JBoss Cache instance is quite resource consuming and by default we
will have 3 JBoss Cache instances (one instance for the indexer, one for
the lock manager and one for the data container) for each workspace, so if
you intend to have a lot of workspaces it could make sense to decide to
share one JBoss Cache instance with several cache instances of the same
type (i.e. indexer, lock manager or data container). This feature is
disabled by default and can be enabled at component configuration level
(i.e. indexer configuration, lock manager configuration and/or data
container configuration) by setting the property "jbosscache-shareable" to
true as below:

<property name="jbosscache-shareable" value="true" />

Once enabled this feature will allow the JBoss Cache instance used
by the component to be re-used by another components of the same type
(i.e. indexer, lock manager or data container) with the exact same JBoss
Cache configuration (except the eviction configuration that cans be
different), which means that all the parameters of type
${jbosscache-<parameter name>} must be identical between the
components of same type of different workspaces. In other words, if we use
the same values for the parameters of type ${jbosscache-<parameter
name>} in each workspace, we will have only 3 JBoss Cache instances
(one instance for the indexer, one for the lock manager and one for the
data container) used whatever the total amount of workspaces
defined.

1.24.4. Shipped JBoss Cache configuration templates

eXo JCR implementation is shipped with ready-to-use JBoss Cache
configuration templates for JCR's components. They are situated in
application package in /conf/porta/ folder.

Note

To prevent any consistency issue regarding the lock data please
ensure that your cache loader is
org.exoplatform.services.jcr.impl.core.lock.jbosscache.JDBCCacheLoader
and that your database engine is transactional.

Table 1.7. Template variables

Variable

Description

jbosscache-cluster-name

cluster name (must be unique)

jbosscache-cl-cache.jdbc.table.name

the name of the table.

jbosscache-cl-cache.jdbc.table.create

can be true or false. Indicates whether to create the
table during startup. If true, the table is created if it
doesn't already exist. The default value is true.

jbosscache-cl-cache.jdbc.table.drop

can be true or false. Indicates whether to drop the table
during shutdown. The default value is true.

jbosscache-cl-cache.jdbc.table.primarykey

the name of the primary key for the table.

jbosscache-cl-cache.jdbc.fqn.column

FQN column name. The default value is 'fqn'.

jbosscache-cl-cache.jdbc.fqn.type

FQN column type. The default value is
'varchar(255)'.

jbosscache-cl-cache.jdbc.node.column

node contents column name. The default value is
'node'.

jbosscache-cl-cache.jdbc.node.type

node contents column type. The default value is 'blob'.
This type must specify a valid binary data type for the database
being used.

1.25.1. LockManagerImpl

LockManagerImpl is a simple implementation of LockManager, and also
faster than CacheableLockManager. It stores Lock objects in HashMap and
may also persist Locks if LockPersister is configured. LockManagerImpl
does not support replication in any way.

1.25.2. CacheableLockManagerImpl

CacheableLockManagerImpl stores Lock objects in JBoss-cache, so
Locks are replicable and affect on cluster, not only a single node. Also,
JBoss-cache has JDBCCacheLoader, so Locks will be stored to the
database.

Both of the implementations support to remove Expired Locks.
LockRemover separates threads, that periodically ask LockManager to remove
Locks that live so long. So, the timeout for LockRemover may be set as
follows, the default value is 30m.

1.25.2.2. Simple JbossCache Configuration

The first one is putting JbossCache configuraion file path to
CacheableLockManagerImpl.

Note

This configuration is not so good as you think. Because the
repository may contain many workspaces, and each workspace must
contain LockManager configuration, and LockManager configuration may
contain the JbossCache config file. So, the total configuration will
grow up. But it is useful if we want to have a single LockManager
with a special configuration.

<clustering mode="replication"
clusterName="JBoss-Cache-Lock-Cluster_Name"> - the cluster name
must be unique;

cache.jdbc.table.name must be unique
per datasource;

cache.jdbc.node.type and
cache.jdbc.fqn.type must be configured
according to your database.

Note

To prevent any consistency issue regarding the lock data
please ensure that your cache loader is
org.exoplatform.services.jcr.impl.core.lock.jbosscache.JDBCCacheLoader
and that your database engine is transactional.

1.25.2.3. Template JBossCache Configuration

The second one is using the template JBoss-cache configuration for
all LockManagers.

jbosscache-cl-cache.jdbc.fqn.column
and jbosscache-cl-cache.jdbc.node.type is
the same as cache.jdbc.fqn.type and cache.jdbc.node.type in
JBoss-Cache configuration. You can set those data types according
to your database type or set it as AUTO (or do not set at all) and
data type will be detected automatically.

As you see, jgroups-configuration is moved to separate the
configuration file - udp-mux.xml. In this case, the udp-mux.xml
file is a common JGroup configuration for all components
(QueryHandler, Cache, LockManager), but we can still create our
own configuration.

1.26. QueryHandler configuration

1.26.1. Indexing in clustered environment

JCR offers multiple indexing strategies. They include both for
standalone and clustered environments using the advantages of running in a
single JVM or doing the best to use all resources available in cluster.
JCR uses Lucene library as underlying search and indexing engine, but it
has several limitations that greatly reduce possibilities and limits the
usage of cluster advantages. That's why eXo JCR offers three strategies
that are suitable for it's own usecases. They are standalone, clustered
with shared index and clustered with local indexes. Each one has it's pros
and cons.

Stanadlone strategy provides a stack of indexes to achieve greater
performance within single JVM.

It combines in-memory buffer index directory with delayed
file-system flushing. This index is called "Volatile" and it is invoked in
searches also. Within some conditions volatile index is flushed to the
persistent storage (file system) as new index directory. This allows to
achieve great results for write operations.

Clustered implementation with local indexes is built upon same
strategy with volatile in-memory index buffer along with delayed flushing
on persistent storage.

As this implementation designed for clustered environment it has
additional mechanisms for data delivery within cluster. Actual text
extraction jobs done on the same node that does content operations (i.e.
write operation). Prepared "documents" (Lucene term that means block of
data ready for indexing) are replicated withing cluster nodes and
processed by local indexes. So each cluster instance has the same index
content. When new node joins the cluster it has no initial index, so it
must be created. There are some supported ways of doing this operation.
The simplest is to simply copy the index manually but this is not intended
for use. If no initial index found JCR uses automated sceneries. They are
controlled via configuration (see "index-recovery-mode" parameter)
offering full re-indexing from database or copying from another cluster
node.

For some reasons having a multiple index copies on each instance can
be costly. So shared index can be used instead (see diagram below).

This indexing strategy combines advantages of in-memory index along
with shared persistent index offering "near" real time search
capabilities. This means that newly added content is accessible via search
practically immediately. This strategy allows nodes to index data in their
own volatile (in-memory) indexes, but persistent indexes are managed by
single "coordinator" node only. Each cluster instance has a read access
for shared index to perform queries combining search results found in own
in-memory index also. Take in account that shared folder must be
configured in your system environment (i.e. mounted NFS folder). But this
strategy in some extremely rare cases can have a bit different volatile
indexes within cluster instances for a while. In a few seconds they will
be up2date.

The FQN of the class to use to indicate the policy to use
to manage the lucene indexes changes. This class must extend
org.exoplatform.services.jcr.impl.core.query.IndexerChangesFilter.
This must be set in cluster environment to define the clustering
strategy to adopt. To use the Shared Indexes Strategy, you can
set it to
org.exoplatform.services.jcr.impl.core.query.jbosscache.JBossCacheIndexChangesFilter.
I you prefer the Local Indexes Strategy, you can set it to
org.exoplatform.services.jcr.impl.core.query.jbosscache.LocalIndexChangesFilter.

jbosscache-configuration

template of JBoss-cache configuration for all
query-handlers in repository (search, cache, locks)

jgroups-configuration

This is the path to JGroups configuration that should not
be anymore jgroups' stack definitions but a normal jgroups
configuration format with the shared transport configured by
simply setting the jgroups property singleton_name to a unique
name (it must remain unique from one portal container to
another). This file is also pre-bundled with templates and is
recommended for use.

jgroups-multiplexer-stack

if set to true, it will indicate that the file
corresponding to the parameter jgroups-configuration is a
actually a file defining a set of jgroups multiplexer stacks. In
the XML tag jgroupsConfig within the jboss cache configuration,
you will then be able to set the name of the multiplexer stack
to use thanks to the attribute multiplexerStack. Please note
that the jgroups multiplexer has been deprecated by the jgroups
Team and has been replaced by the shared transport so it is
highly recommended to not use it anymore.

jbosscache-cluster-name

cluster name (must be unique)

max-volatile-time

max time to live for Volatile Index

rdbms-reindexing

indicate that need to use rdbms reindexing mechanism if
possible, the default value is true

reindexing-page-size

maximum amount of nodes which can be retrieved from
storage for re-indexing purpose, the default value is
100

index-recovery-mode

If the parameter has been set to
from-indexing, so a full indexing will be
automatically launched, if the parameter has been set to
from-coordinator (default behavior), the
index will be retrieved from coordinator

index-recovery-filter

Defines implementation class or classes of
RecoveryFilters, the mechanism of index synchronization for
Local Index strategy.

async-reindexing

Controls the process of re-indexing on JCR's startup. If
flag set, indexing will be launched asynchronously, without
blocking the JCR. Default is "false".

indexing-thread-pool-size

Define the total amount of indexing threads.

Note

If you use postgreSQL and the parameter rdbms-reindexing is set
to true, the performances of the queries used while indexing can be
improved by setting the parameter "enable_seqscan" to "off" or
"default_statistics_target" to at least "50" in the configuration of
your database. Then you need to restart DB server and make analyze of
the JCR_SVALUE (or JCR_MVALUE) table.

Note

If you use DB2 and the parameter rdbms-reindexing is set to
true, the performance of the queiries used while indexing can be
improved by making statisticks on tables by running "RUNSTATS ON TABLE
<scheme>.<table> WITH DISTRIBUTION AND INDEXES ALL" for
JCR_SITEM (or JCR_MITEM) and JCR_SVALUE (or JCR_MVALUE) tables.

1.26.2.2. Standalone strategy

When running JCR in standalone usually standalone indexing is used
also. Such parameters as "changesfilter-class", "jgroups-configuration"
and all the "jbosscache-*" must be skipped and not defined. Like the
configuration below.

1.26.2.3. Cluster-ready indexing strategies

For both cluster-ready implementations JBoss Cache, JGroups and
Changes Filter values must be defined. Shared index requires some kind
of remote or shared file system to be attached in a system (i.e. NFS,
SMB or etc). Indexing directory ("indexDir" value) must point to it.
Setting "changesfilter-class" to
"org.exoplatform.services.jcr.impl.core.query.jbosscache.JBossCacheIndexChangesFilter"
will enable shared index implementation.

In order to use cluster-ready strategy based on local indexes,
when each node has own copy of index on local file system, the following
configuration must be applied. Indexing directory must point to any
folder on local file system and "changesfilter-class" must be set to
"org.exoplatform.services.jcr.impl.core.query.jbosscache.LocalIndexChangesFilter".

1.26.2.3.1. Local Index Recovery Filters

Common usecase for all cluster-ready applications is a hot
joining and leaving of processing units. Node that is joining cluster
for the first time or node joining after some downtime, they all must
be in a synchronized state. When having a deal with shared value
storages, databases and indexes, cluster nodes are synchronized
anytime. But it's an issue when local index strategy used. If new node
joins cluster, having no index it is retrieved or recreated. Node can
be restarted also and thus index not empty. Usually existing index is
thought to be actual, but can be outdated. JCR offers a mechanism
called RecoveryFilters that will automatically retrieve index for the
joining node on startup. This feature is a set of filters that can be
defined via QueryHandler configuration:

If any one fires, the index is re-synchronized. Please take in
account, that DocNumberRecoveryFilter is used in cases when no filter
configured. So, if resynchronization should be blocked, or strictly
required on start, then ConfigurationPropertyRecoveryFilter can be
used.

org.exoplatform.services.jcr.impl.core.query.lucene.DummyRecoveryFilter:
always returns true, for cases when index must be force
resynchronized (recovered) each time;

org.exoplatform.services.jcr.impl.core.query.lucene.SystemPropertyRecoveryFilter
: return value of system property
"org.exoplatform.jcr.recoveryfilter.forcereindexing". So index
recovery can be controlled from the top without changing
documentation using system properties;

org.exoplatform.services.jcr.impl.core.query.lucene.ConfigurationPropertyRecoveryFilter
: return value of QueryHandler configuration property
"index-recovery-filter-forcereindexing". So index recovery can be
controlled from configuration separately for each workspace.
I.e:

org.exoplatform.services.jcr.impl.core.query.lucene.DocNumberRecoveryFilter
: checks number of documents in index on coordinator side and
self-side. Return true if differs. Advantage of this filter
comparing to other, it will skip reindexing for workspaces where
index wasn't modified. I.e. there is 10 repositories with 3
workspaces in each one. Only one is really heavily used in cluster
: frontend/production. So using this filter will only reindex
those workspaces that are really changed, without affecting other
indexes thus greatly reducing startup time.

1.26.2.4. JBoss-Cache template configuration

JBoss-Cache template configuration for query handler is about the
same for both clustered strategies.

1.26.3. Asynchronous reindexing

Managing a big set of data using JCR in production environment
sometimes requires special operations with Indexes, stored on File System.
One of those maintenance operations is a recreation of it. Also called
"re-indexing". There are various usecases when it's important to do. They
include hardware faults, hard restarts, data-corruption, migrations and
JCR updates that brings new features related to index. Usually index
re-creation requested on server's startup or in runtime.

1.26.3.1. On startup indexing

Common usecase for updating and re-creating the index is to stop
the server and manually remove indexes for workspaces requiring it. When
server will be started, missing indexes are automatically recovered by
re-indexing. JCR Supports direct RDBMS re-indexing, that usually is
faster than ordinary and can be configured via QueryHandler parameter
"rdbms-reindexing" set to "true" (for more information please refer to
"Query-handler configuration overview"). New feature to introduce is
asynchronous indexing on startup. Usually startup is blocked until
process is finished. Block can take any period of time, depending on
amount of data persisted in repositories. But this can be resolved by
using an asynchronous approaches of startup indexation. Saying briefly,
it performs all operations with index in background, without blocking
the repository. This is controlled by the value of "async-reindexing"
parameter in QueryHandler configuration. With asynchronous indexation
active, JCR starts with no active indexes present. Queries on JCR still
can be executed without exceptions, but no results will be returned
until index creation completed. Checking index state is possible via
QueryManagerImpl:

"OFFLINE" state means that index is currently re-creating. When
state changed, corresponding log event is printed. From the start of
background task index is switched to "OFFLINE", with following log event
:

Those two log lines indicates the end of process for workspace
given in brackets. Calling isOnline() as mentioned above, will also
return true.

1.26.3.2. Hot Asynchronous Workspace Reindexing via JMX

Some hard system faults, error during upgrades, migration issues
and some other factors may corrupt the index. Most likely end customers
would like the production systems to fix index issues in run-time,
without delays and restarts. Current versions of JCR supports "Hot
Asynchronous Workspace Reindexing" feature. It allows end-user (Service
Administrator) to launch the process in background without stopping or
blocking whole application by using any JMX-compatible console (see
screenshot below, "JConsole in action").

Server can continue working as expected while index is
recreated. This depends on the flag "allow queries", passed via JMX
interface to reindex operation invocation. If the flag set, then
application continues working. But there is one critical limitation the
end-users must be aware. Index is frozen while background task is
running. It meant that queries are performed on index present on the
moment of task startup and data written into repository after startup
won't be available through the search until process finished. Data added
during re-indexation is also indexed, but will be available only when
task is done. Briefly, JCR makes the snapshot of indexes on asynch task
startup and uses it for searches. When operation finished, stale indexes
replaced by newly created including newly added data. If flag "allow
queries" is set to false, then all queries will throw an exception while
task is running. Current state can be acquired using the following JMX
operation:

getHotReindexingState() - returns information about latest
invocation: start time, if in progress or finish time if
done.

1.26.3.3. Notices

First of all, can't launch Hot re-indexing via JMX if index is
already in offline mode. It means that index is currently is invoked in
some operations, like re-indexing at startup, copying in cluster to
another node or whatever. Another important this is Hot Asynchronous
Reindexing via JMX and "on startup" reindexing are completely different
features. So you can't get the state of startup reindexing using command
getHotReindexingState in JMX interface, but there are some common JMX
operations:

1.26.4. Advanced tuning

1.26.4.1. Lucene tuning

As mentioned above, JCR Indexing is based on Lucene indexing
library as underlying search engine. It uses Directories to store index
and manages access to index by Lock Factories. By default JCR
implementation uses optimal combination of Directory implementation and
Lock Factory implementation. When running on OS different from Windows,
NIOFSDirectory implementation used. And SimpleFSDirectory for Windows
stations. NativeFSLockFactory is an optimal solution for wide variety of
cases including clustered environment with NFS shared resources. But
those default can be overridden with the help of system properties.
There are two properties:
"org.exoplatform.jcr.lucene.store.FSDirectoryLockFactoryClass" and
"org.exoplatform.jcr.lucene.FSDirectory.class" that are responsible for
changing default behavior. First one defines implementation of abstract
Lucene LockFactory class and the second one sets implementation class
for FSDirectory instances. For more information please refer to Lucene
documentation. But be sure You know what You are changing. JCR allows
end users to change implementation classes of Lucene internals, but
doesn't guarantee it's stability and functionality.

1.29.2. Workspaces configuration requirements

Each mentioned below components uses instances of Infinispan Cache
product for caching in clustered environment. So every element has it's
own transport and has to be configured in a proper way. As usual,
workspaces have similar configuration. The simplest way to configure them
is to define their own configuration files for each component in each
workspace. There are several commons parameters.

JGroups is used by Infinispan Cache for network communications and
transport in a clustered environment. If property
"jgroups-configuration" is defined in component
configuration, it will be injected into the Infinispan Cache instance on
startup.

The another parameter is "infinispan-cluster-name".
This defines the name of the cluster. Needs to be the same for all nodes
in a cluster in order to find each other.

changesfilter-class - defines cluster-ready
index strategy based on Infinispan Cache, it can be either
org.exoplatform.services.jcr.impl.core.query.ispn.ISPNIndexChangesFilter
(for shared index) or
org.exoplatform.services.jcr.impl.core.query.ispn.LocalIndexChangesFilter
(for local index)

This is the path to JGroups configuration that should not
be anymore jgroups' stack definitions but a normal jgroups
configuration format with the shared transport configured by
simply setting the jgroups property singleton_name to a unique
name (it must remain unique from one portal container to
another). This file is also pre-bundled with templates and is
recommended for use.

infinispan-cluster-name

This defines the name of the cluster. Needs to be the
same for all nodes in a cluster in order to find each
other.

This is the path to JGroups configuration that should not
be anymore jgroups' stack definitions but a normal jgroups
configuration format with the shared transport configured by
simply setting the jgroups property singleton_name to a unique
name (it must remain unique from one portal container to
another). This file is also pre-bundled with templates and is
recommended for use.

infinispan-cluster-name

This defines the name of the cluster. Needs to be the
same for all nodes in a cluster in order to find each
other.

infinispan-cl-cache.jdbc.table.name

The table name.

infinispan-cl-cache.jdbc.id.column

The name of the column id.

infinispan-cl-cache.jdbc.data.column

The name of the column data.

infinispan-cl-cache.jdbc.timestamp.column

The name of the column timestamp.

infinispan-cl-cache.jdbc.id.type

The type of the column id.

infinispan-cl-cache.jdbc.data.type

The type of the column data.

infinispan-cl-cache.jdbc.timestamp.type

The type of the column timestamp.

infinispan-cl-cache.jdbc.table.drop

Can be set to true or false. Indicates whether to drop
the table at stop phase.

infinispan-cl-cache.jdbc.table.create

Can be set to true or false. Indicates whether to create
table at start phase. If true, the table is created if it does
not already exist.

This is the path to JGroups configuration that should not
be anymore jgroups' stack definitions but a normal jgroups
configuration format with the shared transport configured by
simply setting the jgroups property singleton_name to a unique
name (it must remain unique from one portal container to
another). This file is also pre-bundled with templates and is
recommended for use.

infinispan-cluster-name

This defines the name of the cluster. Needs to be the
same for all nodes in a cluster in order to find each
other.

1.30. RepositoryCreationService

RepositoryCreationService is the service which is used to create repositories
in runtime. The service can be used in a standalone or cluster
environment.

1.30.1. Dependencies

RepositoryConfigurationService depends to next components:

DBCreator -
DBCreator used to create new database for each unbinded
datasource.

Note

RPCService may not be configured - in this case,
RepositoryService will work as standalone service.

1.30.2. How it works

User executes reserveRepositoryName(String repositoryName) -
client-node calls coordinator-node to reserve repositoryName. If this
name is already reserved or repository with this name exist,
client-node will fetch RepositoryCreationException. If not Client will
get token string.

whan repository become created - user-node broadcast message to
all clusterNodes with RepositoryEntry, so each cluster node starts new
Repository.

There is two ways to create repositry: make it in single step - just
call createRepository(String backupId, RepositoryEntry); or reserve
repositoryName at first (reserveRepositoryName(String repositoryName)),
than create reserved repository (createRepository(String backupId,
RepositoryEntry rEntry, String token)).

1.30.5. Conclusions and restrictions

Each datasource in RepositoryEntry of new Repository must have
unbinded datasources. Thats mean, such datasource must have not
databases behind them. This restriction exists to avoid corruption of
existing repositories data.

RPCService is optional component, but without it,
RepositoryCreatorService can not communicate with other cluster-nodes
and works as standalone.

1.31. JCR Query Usecases

JCR supports two query languages - JCR and XPath. A query, whether
XPath or SQL, specifies a subset of nodes within a workspace, called the
result set. The result set constitutes all the nodes in the workspace that
meet the constraints stated in the query.

1.31.1.3. Scoring

The result returns a score for each row in the result set. The
score contains a value that indicates a rating of how well the result
node matches the query. A high value means a better matching than a low
value. This score can be used for ordering the result.

eXo JCR Scoring is a mapping of Lucene scoring. For a more
in-depth understanding, please study Lucene
documentation.

1.31.9.7. Escaping in LIKE Statements

Find all nodes with a mixin type 'mix:title' and whose property
'jcr:title' starts with 'P%ri'.

As you see "P%rison break" contains the symbol '%'. This symbol is
reserved for LIKE comparisons. So what can we do?

Within the LIKE pattern, literal instances of percent ("%") or
underscore ("_") must be escaped. The SQL ESCAPE clause allows the
definition of an arbitrary escape character within the context of a single
LIKE statement. The following example defines the backslash ' \' as escape
character:

SELECT * FROM mytype WHERE a LIKE 'foo\%' ESCAPE '\'

XPath does not have any specification for defining escape symbols, so
we must use the default escape character (' \').

1.31.9.7.1. Repository structure

The repository contains mix:title nodes, where jcr:title can have
different values.

1.31.9.13.2. Query Execution

SQL

In SQL you have to use the keyword TIMESTAMP for date comparisons. Otherwise, the date
would be interpreted as a string. The date has to be surrounded by single
quotes (TIMESTAMP 'datetime') and in the ISO standard format:
YYYY-MM-DDThh:mm:ss.sTZD ( http://en.wikipedia.org/wiki/ISO_8601
and well explained in a W3C note http://www.w3.org/TR/NOTE-datetime).

You will see that it can be a date only (YYYY-MM-DD) but also a
complete date and time with a timezone designator (TZD).

Compared to the SQL format, you have to use the keyword xs:dateTime and surround the datetime by extra
brackets: xs:dateTime('datetime'). The actual format of the datetime also
conforms with the ISO date standard.

1.31.9.17. Child Node Constraint

Find all nodes with the primary type 'nt:folder' that are children of
node by path "/root1/root2". Only find children, do not find further
descendants.

1.31.9.17.1. Repository Structure

The repository is filled by "nt:folder" nodes. The nodes are placed
in a multilayer tree.

root

folder1 (nt:folder)

folder2 (nt:folder)

folder3 (nt:folder) // This node we want to
find

folder4 (nt:folder) // This node is not child
but a descendant of '/folder1/folder2/'.

folder5 (nt:folder) // This node we want to
find

1.31.9.17.2. Query Execution

SQL

The use of "%" in the LIKE statement includes any string, therefore
there is a second LIKE statement that excludes that the string contains
"/". This way child nodes are included but descendant nodes are
excluded.

1.31.9.21. Ordering by Score

Select all nodes with the mixin type 'mix:title' containing any word
from the set {'brown','fox','jumps'}. Then, sort result by the score in
ascending node. This way nodes that match better the query statement are
ordered at the last positions in the result list.

1.31.9.21.1. Info

SQL and XPath queries support both score constructions jcr:score and
jcr:score()

SELECT * FROM nt:base ORDER BY jcr:score [ASC|DESC]
SELECT * FROM nt:base ORDER BY jcr:score()[ASC|DESC]
//element(*,nt:base) order by jcr:score() [descending]
//element(*,nt:base) order by @jcr:score [descending]

Do not use "ascending" combined with jcr:score in XPath. The
following XPath statement may throw an exception:

... order by jcr:score() ascending

Do not set any ordering specifier - ascending is default:

... order by jcr:score()

1.31.9.21.2. Repository Structure

The repository contains mix:title nodes, where the jcr:description
has different values.

1.31.9.25. Ignoring Accent Symbols. New Analyzer Setting.

In this example, we will create new Analyzer, set it in QueryHandler
configuration, and make query to check it.

Standard analyzer does not normalize accents like é,è,à. So, a word
like 'tréma' will be stored to index as 'tréma'. But if we want to normalize
such symbols or not? We want to store 'tréma' word as 'trema'.

There is two ways of setting up new Analyzer (no matter standarts or
our):

1.31.9.26. Finding nt:file node by content of child jcr:content node

The node type nt:file represents a file. It requires a single child
node, called jcr:content. This node type represents images and other binary
content in a JCRWiki entry. The node type of jcr:conent is nt:resource which
represents the actual content of a file.

Find node with the primary type is 'nt:file' and which whose
'jcr:content' child node contains "cats".

Normally, we can't find nodes (in our case) using just JCR SQL or
XPath queries. But we can configure indexing so that nt:file aggregates
jcr:content child node.

Now the content of 'nt:file' and 'jcr:content' ('nt:resource') nodes
are concatenated in a single Lucene document. Then, we can make a fulltext
search query by content of 'nt:file'; this search includes the content of
child 'jcr:content' node.

1.31.9.29. Regular Expression as Property Name in Indexing Rules

In this example, we want to configure indexind in the next way. All
properties of nt:unstructured nodes must be excluded from search, except
properties whoes names ends with 'Text' string. First of all, add rules to
indexing-configuration.xml:

Also, remember that we can make indexing rules, as in the example
below:

Let's write rule for all nodes with primary node type
'nt:unstructed' where property 'rule' equal to "excerpt" string. For those
nodes, we will exclude property "title" from high-lighting and set "text"
property as highlightable. Indexing-configuration.xml must containt the
next rule:

1.31.9.33.1. Repository structure:

Repository contains many nt:file nodes"

root

baseFile (nt:file)

jcr:content (nt:resource) jcr:data="Similarity is
determined by looking up terms that are common to nodes. There
are some conditions that must be met for a term to be considered. This is required
to limit the number possibly relevant terms. Only terms with at least 4 characters are
considered. Only terms that
occur at least 2 times in the source node are considered. Only
terms that occur in at least
5 nodes are considered."

target1 (nt:file)

jcr:content (nt:resource) jcr:data="Similarity is
determined by looking up terms that are common to nodes."

1.32.1. Bi-directional RangeIterator (since 1.9)

Note

Bi-directional NodeIterator is not
supported in two cases:

SQL query: select * from nt:base

XPath query: //* .

TwoWayRangeIterator interface:

/**
* Skip a number of elements in the iterator.
*
* @param skipNum the non-negative number of elements to skip
* @throws java.util.NoSuchElementException if skipped past the first element
* in the iterator.
*/
public void skipBack(long skipNum);

1.32.3. SynonymSearch (since 1.9)

Searching with synonyms is integrated in the jcr:contains() function
and uses the same syntax as synonym searches in Google. If a search term
is prefixed by a tilde symbol ( ~ ), also synonyms of the search term are
taken into consideration. For example:

/**
* <code>SynonymProvider</code> defines an interface for a component that
* returns synonyms for a given term.
*/
public interface SynonymProvider {
/**
* Initializes the synonym provider and passes the file system resource to
* the synonym provider configuration defined by the configuration value of
* the <code>synonymProviderConfigPath</code> parameter. The resource may be
* <code>null</code> if the configuration parameter is not set.
*
* @param fsr the file system resource to the synonym provider
* configuration.
* @throws IOException if an error occurs while initializing the synonym
* provider.
*/
public void initialize(InputStream fsr) throws IOException;
/**
* Returns an array of terms that are considered synonyms for the given
* <code>term</code>.
*
* @param term a search term.
* @return an array of synonyms for the given <code>term</code> or an empty
* array if no synonyms are known.
*/
public String[] getSynonyms(String term);
}

1.32.4. High-lighting (Since 1.9)

An ExcerptProvider retrieves text excerpts for a node in the query
result and marks up the words in the text that match the query
terms.

By default highlighting words matched the query is disabled because
this feature requires that additional information is written to the search
index. To enable this feature, you need to add a configuration parameter
to the query-handler element in your jcr configuration file to enable
it.

<param name="support-highlighting" value="true"/>

Additionally, there is a parameter that controls the format of the
excerpt created. In JCR 1.9, the default is set to
org.exoplatform.services.jcr.impl.core.query.lucene.DefaultHTMLExcerpt.
The configuration parameter for this setting is:

The above code searches for nodes that contain the word
exoplatform and then gets the value of the Title property and an excerpt
for each result node.

It is also possible to use a relative path in the call
Row.getValue() while the query statement still remains the same. Also,
you may use a relative path to a string property. The returned value
will then be an excerpt based on string value of the property.

Both available excerpt provider will create fragments of about 150
characters and up to 3 fragments.

In SQL, the function is called excerpt() without the rep prefix,
but the column in the RowIterator will nonetheless be labled
rep:excerpt(.)!

1.32.5. SpellChecker

The lucene based query handler implementation supports a pluggable
spell checker mechanism. By default, spell checking is not available and
you have to configure it first. See parameter spellCheckerClass on page
Search Configuration. JCR
currently provides an implementation class , which uses the lucene-spellchecker
to contribute . The dictionary is derived from the fulltext indexed
content of the workspace and updated periodically. You can configure the
refresh interval by picking one of the available inner classes of
org.exoplatform.services.jcr.impl.core.query.lucene.spell.LuceneSpellChecker:

OneMinuteRefreshInterval

FiveMinutesRefreshInterval

ThirtyMinutesRefreshInterval

OneHourRefreshInterval

SixHoursRefreshInterval

TwelveHoursRefreshInterval

OneDayRefreshInterval

For example, if you want a refresh interval of six hours, the class
name is:
org.exoplatform.services.jcr.impl.core.query.lucene.spell.LuceneSpellChecker$SixHoursRefreshInterval.
If you use
org.exoplatform.services.jcr.impl.core.query.lucene.spell.LuceneSpellChecker,
the refresh interval will be one hour.

The spell checker dictionary is stored as a lucene index under
"index-dir"/spellchecker. If it does not
exist, a background thread will create it on startup. Similarly, the
dictionary refresh is also done in a background thread to not block
regular queries.

1.32.5.1. How do I use it?

You can spell check a fulltext statement either with an XPath or a
SQL query:

1.32.6. Similarity (Since 1.12)

Starting with version, 1.12 JCR allows you to search for nodes that
are similar to an existing node.

Similarity is determined by looking up terms that are common to
nodes. There are some conditions that must be met for a term to be
considered. This is required to limit the number possibly relevant
terms.

Only terms with at least 4 characters are considered.

Only terms that occur at least 2 times in the source node are
considered.

Only terms that occur in at least 5 nodes are considered.

Note: The similarity functionality requires that the support
Hightlighting is enabled. Please make sure that you have the following
parameter set for the query handler in your workspace.xml.

<param name="support-highlighting" value="true"/>

The functions are called rep:similar() (in XPath) and similar() (in
SQL) and have two arguments:

relativePath: a relative path to a descendant node or . for the
current node. absoluteStringPath: a string literal that contains the path
to the node for which to find similar nodes.

Warning

Finds nt:resource nodes, which are similar to node by path
/parentnode/node.txt/jcr:content.

1.33. Fulltext Search And Affecting Settings

1.33.1. Property content indexing

Each property of a node (if it is indexable) is processed with
Lucene analyzer and stored in Lucene index. That's called indexing of a
property. After that we can perform a fulltext search among these indexed
properties.

1.33.2. Lucene Analyzers

The sense of analyzers is to transform all strings stored in the
index in a well-defined condition. The same analyzer(s) is/are used when
searching in order to adapt the query string to the index reality.

Therefore, performing the same query using different analyzers can
return different results.

Now, let's see how the same string is transformed by different
analyzers.

Now change the default analyzer to
org.apache.lucene.analysis.StopAnalyzer. Fill repository again (new
Analyzer must process nodes properties) and run the same query again. It
will return nothing, because stop words like "the" will be excluded from
parsed string set.

1.34. JCR API Extensions

eXo JCR implementation offers new extended feature beyond JCR
specification. Sometimes one JCR Node has hundreds or
even thousands of child nodes. This situation is highly not recommended
for content repository data storage, but sometimes it occurs. JCR Team
is pleased to announce new feature that will help to have a deal with
huge child lists. They can be iterated in a "lazy" manner now giving
improvement in term of performance and RAM usage.

1.34.1. API and usage

Lazy child nodes iteration feature is accessible via extended
interface org.exoplatform.services.jcr.core.ExtendedNode, the inheritor
of javax.jcr.Node. It provides a new single method shown below:

/**
* Returns a NodeIterator over all child Nodes of this Node. Does not include properties
* of this Node. If this node has no child nodes, then an empty iterator is returned.
*
* @return A NodeIterator over all child Nodes of this <code>Node</code>.
* @throws RepositoryException If an error occurs.
*/
public NodeIterator getNodesLazily() throws RepositoryException;

From the view of end-user or client application, getNodesLazily()
works similar to JCR specified getNodes() returning NodeIterator. "Lazy"
iterator supports the same set of features as an ordinary NodeIterator,
including skip() and excluding remove() features. "Lazy" implementation
performs reading from DB by pages. Each time when it has no more
elements stored in memory, it reads next set of items from persistent
layer. This set is called "page". Must admit that getNodesLazily feature
fully supports session and transaction changes log, so it's a
functionally-full analogue of specified getNodes() operation. So when
having a deal with huge list of child nodes, getNodes() can be simply
and safely substituted with getNodesLazily().

JCR gives an experimental opportunity to replace all getNodes()
invocations with getNodesLazily() calls. It handles a boolean system
property named "org.exoplatform.jcr.forceUserGetNodesLazily" that
internally replaces one call with another, without any code changes. But
be sure using it only for development purposes. This feature can be used
with top level products using eXo JCR to perform a quick compatibility
and performance tests without changing any code. This is not recommended
to be used as a production solution.

1.34.2. Configuration

In order to enable add the
"-Dorg.exoplatform.jcr.forceUserGetNodesLazily=true" to the java system
properties.

The "lazy" iterator reads the child nodes "page" after "page" into
the memory. In this context, a "page" is a set of nodes that is read at
once. The size of the page is by default 100 nodes and can be configured
though workspace container configuration using
"lazy-node-iterator-page-size" parameter. For example:

1.34.3. Implementation notices

Current "lazy" child nodes iterator supports caching, when pages
are cached atomically in safe and optimized way. Cache is always kept in
consistent state using invalidation if child list changed. Take in
account the following difference in getNodes and getNodesLazily.
Specification defined getNodes method reads whole list of nodes, so
child items added after invocation will never be in results.
GetNodesLazily doesn't acquire full list of nodes, so child items added
after iterator creation can be found in result. So getNodesLazily can
represent some kind of "real-time" results. But it is highly depend on
numerous conditions and should not be used as a feature, it more likely
implementation specific issue typical for "lazy-pattern".

1.35. WebDAV

The WebDAV protocol enables you to use the third party tools to
communicate with hierarchical content servers via HTTP. It is possible to
add and remove documents or a set of documents from a path on the server.
DeltaV is an extension of the WebDav protocol that allows managing
document versioning. Locking guarantees protection against multiple access
when writing resources. The ordering support allows changing the position
of the resource in the list and sort the directory to make the directory
tree viewed conveniently. The full-text search makes it easy to find the
necessary documents. You can search by using two languages: SQL and
XPATH.

In eXo JCR, we plug in the WebDAV layer - based on the code taken
from the extension modules of the reference implementation - on the top of
our JCR implementation so that it is possible to browse a workspace using
the third party tools (it can be Windows folders or Mac ones as well as a
Java WebDAV client, such as DAVExplorer or IE using File->Open as a Web
Folder).

Now WebDav is an extension of the REST service. To get the WebDav
server ready, you must deploy the REST application. Then, you can access
any workspaces of your repository by using the following URL:

When accessing the WebDAV server with the
URLhttp://localhost:8080/rest/jcr/repository/production, you
might also use "collaboration" (instead of "production") which is the
default workspace in eXo products. You will be asked to enter your login
and password. Those will then be checked by using the organization service
that can be implemented thanks to an InMemory (dummy) module or a DB
module or an LDAP one and the JCR user session will be created with the
correct JCR Credentials.

Note

If you try the "in ECM" option, add "@ecm" to the user's password.
Alternatively, you may modify jaas.conf by adding the domain=ecm option as follows:

1.35.1. Configuration

<component>
<key>org.exoplatform.services.jcr.webdav.WebDavServiceImpl</key>
<type>org.exoplatform.services.jcr.webdav.WebDavServiceImpl</type>
<init-params>
<!-- default node type which is used for the creation of collections -->
<value-param>
<name>def-folder-node-type</name>
<value>nt:folder</value>
</value-param>
<!-- default node type which is used for the creation of files -->
<value-param>
<name>def-file-node-type</name>
<value>nt:file</value>
</value-param>
<!-- if MimeTypeResolver can't find the required mime type,
which conforms with the file extension, and the mimeType header is absent
in the HTTP request header, this parameter is used
as the default mime type-->
<value-param>
<name>def-file-mimetype</name>
<value>application/octet-stream</value>
</value-param>
<!-- This parameter indicates one of the three cases when you update the content of the resource by PUT command.
In case of "create-version", PUT command creates the new version of the resource if this resource exists.
In case of "replace" - if the resource exists, PUT command updates the content of the resource and its last modification date.
In case of "add", the PUT command tries to create the new resource with the same name (if the parent node allows same-name siblings).-->
<value-param>
<name>update-policy</name>
<value>create-version</value>
<!--value>replace</value -->
<!-- value>add</value -->
</value-param>
<!--
This parameter determines how service responds to a method that attempts to modify file content.
In case of "checkout-checkin" value, when a modification request is applied to a checked-in version-controlled resource, the request is automatically preceded by a checkout and followed by a checkin operation.
In case of "checkout" value, when a modification request is applied to a checked-in version-controlled resource, the request is automatically preceded by a checkout operation.
-->
<value-param>
<name>auto-version</name>
<value>checkout-checkin</value>
<!--value>checkout</value -->
</value-param>
<!--
This parameter is responsible for managing Cache-Control header value which will be returned to the client.
You can use patterns like "text/*", "image/*" or wildcard to define the type of content.
-->
<value-param>
<name>cache-control</name>
<value>text/xml,text/html:max-age=3600;image/png,image/jpg:max-age=1800;*/*:no-cache;</value>
</value-param>
<!--
This parameter determines the absolute path to the folder icon file, which is shown
during WebDAV view of the contents
-->
<value-param>
<name>folder-icon-path</name>
<value>/absolute/path/to/file</value>
</value-param>
<!--
This parameter is responsible for untrusted user agents definition.
Content-type headers of listed here user agents should be
ignored and MimeTypeResolver should be explicitly used instead
-->
<values-param>
<name>untrusted-user-agents</name>
<value>Microsoft Office Core Storage Infrastructure/1.0</value>
</values-param>
</init-params>
</component>

1.35.4. Restrictions

There are some restrictions for WebDAV in different Operating
systems.

1.35.4.1. Windows 7

When you try to set up a web folder by “adding a network location”
or “map a network drive” through My Computer, you can get an error
message saying that either “The folder you entered does not appear to be
valid. Please choose another” or “Windows cannot access… Check the
spelling of the name. Otherwise, there might be…”. These errors may
appear when you are using SSL or non-SSL.

1.35.4.2. Microsoft Office 2010

If you have Microsoft Office 2010 applications or Microsoft Office
2007 applications installed on a client computer. From that client
computer, you try to access an Office file that is stored on a web
server that is configured for Basic authentication. The connection
between your computer and the web server does not use Secure Sockets
Layer (SSL). When you try to open or to download the file, you
experience the following symptoms:

The Office file does not open or download.

You do not receive a Basic authentication password prompt
when you try to open or to download the file.

You do not receive an error message when you try to open the
file. The associated Office application starts. However, the
selected file does not open.

To enable Basic authentication on the client computer, follow
these steps:

Click Start, type regedit in the Start Search box, and then
press Enter.

Locate and then click the following registry subkey:

HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\Common\Internet

On the Edit menu, point to New, and then click DWORD
Value.

Type BasicAuthLevel, and then press Enter.

Right-click BasicAuthLevel, and then click Modify.

In the Value data box, type 2, and then click OK.

1.36. FTP

The JCR-FTP Server represents the standard eXo service, operates as an
FTP server with an access to a content stored in JCR repositories in the
form of nt:file/nt:folder nodes or their
successors. The client of an executed Server can be any FTP client. The FTP
server is supported by a standard configuration which can be changed as
required.

1.36.1.2. data-min-port & data-max-port

These two parameters indicate the minimal and maximal values of
the range of ports, used by the server. The usage of the additional data
channel is required by the FTP - protocol, which is used to transfer the
contents of files and the listing of catalogues. This range of ports
should be free from listening by other server-programs.

1.36.1.14. replace-char

Defines the character that will be used to replace the forbidden
characters.

1.37. eXo JCR Backup Service

Note

Restore of system workspace is not supported only as part of
restoring of whole repository.

1.37.1. Concept

The main purpose of that feature is to restore data in case of
system faults and repository crashes. Also, the backup results may be used
as a content history.

The concept is based on the export of a workspace unit in the Full,
or Full + Incrementals model. A repository workspace can be backup and
restored using a combination of these modes. In all cases, at least one
Full (initial) backup must be executed to mark a starting point of the
backup history. An Incremental backup is not a complete image of the
workspace. It contains only changes for some period. So it is not possible
to perform an Incremental backup without an initial Full backup.

The Backup service may operate as a hot-backup process at runtime on
an in-use workspace. It's a case when the Full + Incrementals model should
be used to have a guaranty of data consistency during restoration. An
Incremental will be run starting from the start point of the Full backup
and will contain changes that have occured during the Full backup,
too.

A restore operation is a mirror of
a backup one. At least one Full backup should be restored to obtain a
workspace corresponding to some points in time. On the other hand,
Incrementals may be restored in the order of creation to reach a required
state of a content. If the Incremental contains the same data as the Full
backup (hot-backup), the changes will be applied again as if they were
made in a normal way via API calls.

According to the model there are several modes for backup
logic:

Full backup only : Single
operation, runs once

Full + Incrementals : Start
with an initial Full backup and then keep incrementals changes in one
file. Run until it is stopped.

Full + Incrementals(periodic) :
Start with an initial Full backup and then keep incrementals with
periodic result file rotation. Run until it is stopped.

1.37.2. How it works

1.37.2.1. Implementation details

Full backup/restore is implemented using the JCR SysView
Export/Import. Workspace data will be exported into Sysview XML data
from root node.

Restoring is implemented, using the special eXo JCR API feature: a
dynamic workspace creation. Restoring of the workspace Full backup will
create one new workspace in the repository. Then, the SysView XML data
will be imported as the root node.

Incremental backup is implemented using the eXo JCR ChangesLog
API. This API allows to record each JCR API call as atomic entries in a
changelog. Hence, the Incremental backup uses a listener that collects
these logs and stores them in a file.

Restoring an incremental backup consists in applying the collected
set of ChangesLogs to a workspace in the correct order.

Note

Incremental backup is an experimental feture and not supported,
so it must be used with a lot of caution.

1.37.2.2. Work basics

The work of Backup is based on the BackupConfig configuration and
the BackupChain logical unit.

BackupConfig describes the backup operation chain that will be
performed by the service. When you intend to work with it, the
configuration should be prepared before the backup is started.

The configuration contains such values as:

Types of full and incremental
backup (fullBackupType, incrementalBackupType): Strings
with full names of classes which will cover the type
functional.

Incremental period: A period
after that a current backup will be stopped and a new one will be
started in seconds (long).

Target repository and workspace
names: Strings with described names

Destination directory for
result files: String with a path to a folder where operation result
files will be stored.

BackupChain is a unit performing the backup process and it covers
the principle of initial Full backup execution and manages Incrementals
operations. BackupChain is used as a key object for accessing current
backups during runtime via BackupManager. Each BackupJob performs a
single atomic operation - a Full or Incremental process. The result of
that operation is data for a Restore. BackupChain can contain one or
more BackupJobs. But at least the initial Full job is always there. Each
BackupJobs has its own unique number which means its Job order in the
chain, the initial Full job always has the number 0.

Backup process, result data and file
location

To start the backup process, it's necessary to create the
BackupConfig and call the BackupManager.startBackup(BackupConfig)
method. This method will return BackupChain created according to the
configuration. At the same time, the chain creates a BackupChainLog
which persists BackupConfig content and BackupChain operation states to
the file in the service working directory (see Configuration).

When the chain starts the work and the initial BackupJob starts,
the job will create a result data file using the destination directory
path from BackupConfig. The destination directory will contain a
directory with an automatically created name using the pattern
repository_workspace-timestamp where timestamp is current time in the
format of yyyyMMdd_hhmmss (E.g. db1_ws1-20080306_055404). The directory
will contain the results of all Jobs configured for execution. Each Job
stores the backup result in its own file with the name
repository_workspace-timestamp.jobNumber. BackupChain saves each state
(STARTING, WAITING, WORKING, FINISHED) of its Jobs in the
BackupChainLog, which has a current result full file path.

BackupChain log file and job result files are a whole and
consistent unit, that is a source for a Restore.

Note

BackupChain log contains absolute paths to job result files.
Don't move these files to another location.

Restore requirements

As mentioned before a Restore operation is a mirror of a Backup.
The process is a Full restore of a root node with restoring an
additional Incremental backup to reach a desired workspace state.
Restoring of the workspace Full backup will create a new workspace in
the repository using given RepositoyEntry of existing repository and
given (preconfigured) WorkspaceEntry for a new target workspace. A
Restore process will restore a root node from the SysView XML
data.

Note

The target workspace should not be in the repository. Otherwise,
a BackupConfigurationException exception will be thrown.

Finally, we may say that Restore is a process of a new Workspace
creation and filling it with a Backup content. In case you already have
a target Workspace (with the same name) in a Repository, you have to
configure a new name for it. If no target workspace exists in the
Repositor, you may use the same name as the Backup one.

1.37.3. Configuration

As an optional extension, the Backup service is not enabled by
default. You need to enable it via
configuration.

default-incremental-job-period : The period
between incremetal flushes (in seconds). Default is 3600
seconds.

full-backup-type : The FQN of
the full backup job class; Must implement
org.exoplatform.services.jcr.ext.backup.BackupJob. By default :
org.exoplatform.services.jcr.ext.backup.impl.rdbms.FullBackupJob
used. Please, notice that file-system based implementation
org.exoplatform.services.jcr.ext.backup.impl.fs.FullBackupJob is
deprecated and not recommended for use.

1.37.4. RDBMS backup

RDBMS backup It is the lastest, currently supportedm used by default
and recommended implementation of full backup job for BackupManager
service. It is useful in case when database is used to store data.

Brings such advantages:

fast: backup takes only several minutes to perform full backup
of repository with 1 million rows in tables;

atomic restore: restore process into existing
workspace/repository with same configuration is atomic, it means you
don’t loose the data when restore failed, the original data
remains;

cluster aware: it is possible to make backup/restore in
cluster environment into existing workspace/repository with same
configuration;

consistence backup: all threads make waiting until backup is
finished and then continue to work, so, there are no data
modification during backup process;

1.37.5. Usage

1.37.5.1. Performing a Backup

In the following example, we create a BackupConfig bean for the
Full + Incrementals mode, then we ask the BackupManager to start the
backup process.

1.37.6. Scheduling (experimental)

The Backup service has an additional feature that can be useful for
a production level backup implementation. When you need to organize a
backup of a repository, it's necessary to have a tool which will be able
to create and manage a cycle of Full and Incremental backups in periodic
manner.

The service has internal BackupScheduler which can run a
configurable cycle of BackupChains as if they have been executed by a user
during some period of time. I.e. BackupScheduler is a user-like daemon
which asks the BackupManager to start or stop backup operations.

backupConfig: A ready configuration which will be given to the
BackupManager.startBackup() method

startDate: The date and time of the backup start

stopDate: The date and time of the backup stop

chainPeriod: A period after which a current BackupChain will be
stopped and a new one will be started in seconds

incrementalPeriod: If it is greater than 0, it will be used to
override the same value in backupConfig.

// geting the scheduler from the BackupManager
BackupScheduler scheduler = backup.getScheduler();
// schedule backup using a ready configuration (Full + Incrementals) to run from startTime
// to stopTime. Full backuop will be performed every 24 hours (BackupChain lifecycle),
// incremental will rotate result files every 3 hours.
scheduler.schedule(config, startTime, stopTime, 3600 * 24, 3600 * 3);
// it's possible to run the scheduler for an uncertain period of time (i.e. without stop time).
// schedule backup to run from startTime till it will be stopped manually
// also there, the incremental will rotate result files as it configured in BackupConfig
scheduler.schedule(config, startTime, null, 3600 * 24, 0);
// to unschedule backup simply call the scheduler with the configuration describing the
// already planned backup cycle.
// the scheduler will search in internal tasks list for task with repository and
// workspace name from the configuration and will stop that task.
scheduler.unschedule(config);

When the BackupScheduler starts the scheduling, it uses the internal
Timer with startDate for the first (or just once) execution. If
chainPeriod is greater than 0, then the task is repeated with this value
used as a period starting from startDate. Otherwise, the task will be
executed once at startDate time. If the scheduler has stopDate, it will
stop the task ( the chain cycle) after stopDate. And the last parameter
incrementalPeriod will be used instead of the same from BackupConfig if
its values are greater than 0.

Starting each task (BackupScheduler.schedule(...)), the scheduler
creates a task file in the service working directory (see Configuration, backup-dir) which describes the task
backup configuration and periodic values. These files will be used at the
backup service start (JVM start) to reinitialize BackupScheduler for
continuous task scheduling. Only tasks that don't have a stopDate or a
stopDate not expired will be reinitialized.

There is one notice about BackupScheduler task reinitialization in
the current implementation. It comes from the BackupScheduler nature and
its implemented behaviour. As the scheduler is just a virtual user which
asks the BackupManager to start or stop backup operations, it isn't able
to reinitialize each existing BackupChain before the service (JVM) is
stopped. But it's possible to start a new operation with the same
configuration via BackupManager (that was configured before and stored in
a task file).

This is a main detail of the BackupScheduler which should be taken
into suggestion of a backup operation design now. In case of
reinitialization, the task will have new time values for the backup
operation cycle as the chainPeriod and incrementalPeriod will be applied
again. That behaviour may be changed in the future.

1.37.9. Backup set portability

The Backup log is stored during the Backup operation into two
different locations: backup-dir directory of BackupService to support
interactive operations via Backup API (e.g. console) and backup set files
for portability (e.g. on another server).

GateIn uses context /portal/rest, therefore you need to use
http://host:port/portal/rest/ instread of http://host:port/rest/

GateIn uses form authentication, so first you need to login (url to
form authentication is http://host:port/portal/login) and then perform
requests.

The service
org.exoplatform.services.jcr.ext.backup.server.HTTPBackupAgent is
REST-based front-end to service
org.exoplatform.services.jcr.ext.backup.BackupManager. HTTPBackupAgent is
representation BackupManager to creation backup, restore, getting status
of current or completed backup/restore, etc.

The backup client is http client for HTTPBackupAgent.

1.38.1. HTTPBackupAgent

The HTTPBackupAgent is based on REST (see details about the REST Framework).

In case, if you will restore backup in same workspace (so you will
drop previous workspace), you need configure
RepositoryServiceConfiguration in order to save the changes of the
repository configuration. For example

1.38.3.14. Restoring workspace from backup set and remove exists
workspace

Restore to workspace "backup" and remove fully (will be removed
content from db, value storage, index) exists workspace, for restore
need the <backup_set_path> (<backup_set_path> is path to
backup set folder on server side) of completed backup and path to file
with workspace configuration:

1.38.3.16. Restoring workspace with original configuation and remove exists
workspace

Restore to workspace "backup" with original configuration of
workspace (the original configuration was stored in backup set) and
remove fully (will be removed content from db, value storage, index)
exists workspace, for restore need the <backup_id> of completed
backup:

1.38.3.17. Restoring workspace from backup set with original
configuation

Restore to workspace "backup" with original configuration of
workspace (the original configuration was stored in backup set), for
restore need the <backup_set_path> (<backup_set_path> is
path to backup set folder on server side) of completed backup:

1.38.3.18. Restoring workspace from backup set with original configuation
and remove exists workspace

Restore to workspace "backup" and remove fully (will be removed
content from db, value storage, index) exists workspace with original
configuration of workspace (the original configuration was stored in
backup set), for restore need the <backup_set_path>
(<backup_set_path> is path to backup set folder on server side) of
completed backup:

1.38.3.21. Restoring repository from backup set

Restore to repository "repository", for restore need the
<backup_set_path> (<backup_set_path> is path to backup set
folder on server side) of completed backup and path to file with
repository configuration:

1.38.3.22. Restoring repository from backup set and remove exists
repository

Restore to repository "repository" and remove fully (will be
removed content from db, value storage, index) exists repository, for
restore need the <backup_set_path> (<backup_set_path> is
path to backup set folder on server side) of completed backup and path
to file with repository configuration:

1.38.3.24. Restoring repository with original configuation and remove exists
repository

Restore to repository "repository" with original configuration of
repository (the original configuration was stored in backup set) and
remove fully (will be removed content from db, value storage, index)
exists repository, for restore need the <backup_id> of completed
backup:

1.38.3.25. Restoring repository from backup set with original
configuation

Restore to repository "repository" with original configuration of
repository (the original configuration was stored in backup set), for
restore need the <backup_set_path> (<backup_set_path> is
path to backup set folder on server side) of completed backup:

1.38.3.26. Restoring repository from backup set with original configuation
and remove exists repository

Restore to repository "repository" and remove fully (will be
removed content from db, value storage, index) exists repository with
original configuration of repository (the original configuration was
stored in backup set), for restore need the <backup_set_path>
(<backup_set_path> is path to backup set folder on server side) of
completed backup:

1.38.4.4. Deleting the workspace "backup" and close opened sessions on this
workspace

1.38.4.5. Restoring the workspace "backup"

Delete/clean the database for workspace "backup" : When we use "single-db", then we
will run the SQL queries for clean database :

delete from JCR_SREF where NODE_ID in (select ID from JCR_SITEM where CONTAINER_NAME = 'backup')
delete from JCR_SVALUE where PROPERTY_ID in (select ID from JCR_SITEM where CONTAINER_NAME = 'backup')
delete from JCR_SITEM where CONTAINER_NAME='backup'

1.39. How to backup the data of your JCR using an external backup tool in 3 steps?

1.39.1. Step 1: Suspend the Repository

To keep all the data of your repository consistent, you have to
suspend it which means that all the working threads will be suspended until
the resume operation is performed. Indexes will be flushed during
the suspend operation.

You can suspend your repository by calling the suspend method
on the MBean of the RepositorySuspendController corresponding to
your repository as shown below:

The result of the suspend method will be "suspended" if everything worked
well otherwise you should get "undefined" which means that at least one component has not been
suspended successfully, in that case you can check the log file to understand what happens.

1.39.2. Step 2: Backup the data

Now we can backup the data manually or using third party softwares. We will
need to backup:

The database content

The Lucene indexes

The value storages content (if configured)

1.39.3. Step 3: Resume the Repository

Once the backup is done, we need to call the resume operation to resume
all the working threads that have been previously suspended. The expected return result is "online".

1.40. eXo JCR statistics

This section will show you how to get and manage all statistics provided by eXo JCR.

1.40.1. Statistics on the Database Access Layer

In order to have a better idea of the time spent into the database
access layer, it can be interesting to get some statistics on that part of
the code, knowing that most of the time spent into eXo JCR is mainly the
database access. This statistics will then allow you to identify without
using any profiler what is normally slow in this layer, which could help
to fix the problem quickly.

In case you use
org.exoplatform.services.jcr.impl.storage.jdbc.optimisation.CQJDBCWorkspaceDataContainer
or
org.exoplatform.services.jcr.impl.storage.jdbc.JDBCWorkspaceDataContainer
as WorkspaceDataContainer, you can get statistics on the
time spent into the database access layer. The database access layer (in
eXo JCR) is represented by the methods of the interface
org.exoplatform.services.jcr.storage.WorkspaceStorageConnection,
so for all the methods defined in this interface, we can have the
following figures:

The minimum time spent into the method.

The maximum time spent into the method.

The average time spent into the method.

The total amount of time spent into the method.

The total amount of time the method has been called.

Those figures are also available globally for all the methods which
gives us the global behavior of this layer.

If you want to enable the statistics, you just need to set the JVM
parameter called
JDBCWorkspaceDataContainer.statistics.enabled to
true. The corresponding CSV file is
StatisticsJDBCStorageConnection-${creation-timestamp}.csv
for more details about how the csv files are managed, please refer to the
section dedicated to the statistics manager.

The format of each column header is ${method-alias}-${metric-alias}.
The metric alias are described in the statistics manager section.

The name of the category of statistics corresponding to these
statistics is JDBCStorageConnection, this name is
mostly needed to access to the statistics through JMX.

Table 1.46. Method Alias

global

This is the alias for all the methods.

getItemDataById

This is the alias for the method
getItemData(String identifier).

getItemDataByNodeDataNQPathEntry

This is the alias for the method
getItemData(NodeData parentData, QPathEntry
name).

getChildNodesData

This is the alias for the method
getChildNodesData(NodeData parent).

getChildNodesCount

This is the alias for the method
getChildNodesCount(NodeData parent).

getChildPropertiesData

This is the alias for the method
getChildPropertiesData(NodeData
parent).

listChildPropertiesData

This is the alias for the method
listChildPropertiesData(NodeData
parent).

getReferencesData

This is the alias for the method
getReferencesData(String
nodeIdentifier).

commit

This is the alias for the method
commit().

addNodeData

This is the alias for the method add(NodeData
data).

addPropertyData

This is the alias for the method add(PropertyData
data).

updateNodeData

This is the alias for the method update(NodeData
data).

updatePropertyData

This is the alias for the method
update(PropertyData data).

deleteNodeData

This is the alias for the method delete(NodeData
data).

deletePropertyData

This is the alias for the method
delete(PropertyData data).

renameNodeData

This is the alias for the method rename(NodeData
data).

rollback

This is the alias for the method
rollback().

isOpened

This is the alias for the method
isOpened().

close

This is the alias for the method
close().

1.40.2. Statistics on the JCR API accesses

In order to know exactly how your application uses eXo JCR, it can
be interesting to register all the JCR API accesses in order to easily
create real life test scenario based on pure JCR calls and also to tune
your eXo JCR to better fit your requirements.

In order to allow you to specify the configuration which part of eXo
JCR needs to be monitored without applying any changes in your code
and/or building anything, we choose to rely on the Load-time Weaving
proposed by AspectJ.

To enable this feature, you will have to add in your classpath the
following jar files:

By default, the configuration will collect statistics on all the
methods of the internal interfaces
org.exoplatform.services.jcr.core.ExtendedSession and
org.exoplatform.services.jcr.core.ExtendedNode, and
the JCR API interface javax.jcr.Property. To add
and/or remove some interfaces to monitor, you have two configuration files
to change that are bundled into the jar
exo.jcr.component.statistics-X.Y.Z.jar, which are
conf/configuration.xml and
META-INF/aop.xml.

The file content below is the content of
conf/configuration.xml that you will need to modify
to add and/or remove the full qualified name of the interfaces to monitor,
into the list of parameter values of the init param called
targetInterfaces.

The file content below is the content of
META-INF/aop.xml that you will need to modify to
add and/or remove the full qualified name of the interfaces to monitor,
into the expression filter of the pointcut called
JCRAPIPointcut. As you can see below, by default only
JCR API calls from the exoplatform packages are took into account, don't
hesistate to modify this filter to add your own package names.

The corresponding CSV files are of type
Statistics${interface-name}-${creation-timestamp}.csv
for more details about how the csv files are managed, please refer to the
section dedicated to the statistics manager.

The format of each column header is ${method-alias}-${metric-alias}.
The method alias will be of type ${method-name}(list of parameter types
separeted by ; to be compatible with the CSV format).

The metric alias are described in the statistics manager
section.

The name of the category of statistics corresponding to these
statistics is the simple name of the monitored interface (e.g.
ExtendedSession for org.exoplatform.services.jcr.core.ExtendedSession),
this name is mostly needed to access to the statistics through JMX.

Please note that this feature will affect the performances of eXo
JCR so it must be used with caution.

1.40.3. Statistics Manager

The statistics manager manages all the statistics provided by eXo
JCR, it is responsible of printing the data into the CSV files and also
exposing the statistics through JMX and/or Rest.

The statistics manager will create all the CSV files for each
category of statistics that it manages, the format of those files is
Statistics${category-name}-${creation-timestamp}.csv.
Those files will be created into the user directory if it is possible
otherwise it will create them into the temporary directory. The format of
those files is CSV (i.e. Comma-Seperated Values), one new
line will be added regularily (every 5 seconds by default) and one last
line will be added at JVM exit. Each line, will be composed of the 5
figures described below for each method and globaly for all the
methods.

Table 1.47. Metric Alias

Min

The minimum time spent into the method expressed in
milliseconds.

Max

The maximum time spent into the method expressed in
milliseconds.

Total

The total amount of time spent into the method expressed
in milliseconds.

Avg

The average time spent into the method expressed in
milliseconds.

Times

The total amount of times the method has been
called.

You can disable the persistence of the statistics by setting the
JVM parameter called
JCRStatisticsManager.persistence.enabled to
false, by default, it is set to
true. You can aslo define the period of time between
each record (i.e. line of data into the file) by setting the JVM parameter
called JCRStatisticsManager.persistence.timeout to
your expected value expressed in milliseconds, by default it is set to
5000.

You can also access to the statistics thanks to JMX, the available
methods are the following:

Table 1.48. JMX Methods

getMin

Give the minimum time spent into the method corresponding
to the given category name and statistics name. The expected
arguments are the name of the category of statistics (e.g.
JDBCStorageConnection) and the name of the expected method or
global for the global value.

getMax

Give the maximum time spent into the method corresponding
to the given category name and statistics name. The expected
arguments are the name of the category of statistics (e.g.
JDBCStorageConnection) and the name of the expected method or
global for the global value.

getTotal

Give the total amount of time spent into the method
corresponding to the given category name and statistics name.
The expected arguments are the name of the category of
statistics (e.g. JDBCStorageConnection) and the name of the
expected method or global for the global value.

getAvg

Give the average time spent into the method corresponding
to the given category name and statistics name. The expected
arguments are the name of the category of statistics (e.g.
JDBCStorageConnection) and the name of the expected method or
global for the global value.

getTimes

Give the total amount of times the method has been called
corresponding to the given ,category name and statistics name.
The expected arguments are the name of the category of
statistics (e.g. JDBCStorageConnection) and the name of the
expected method or global for the global value.

reset

Reset the statistics for the given category name and
statistics name. The expected arguments are the name of the
category of statistics (e.g. JDBCStorageConnection) and the name
of the expected method or global for the global value.

resetAll

Reset all the statistics for the given category name. The
expected argument is the name of the category of statistics
(e.g. JDBCStorageConnection).

The full name of the related MBean is
exo:service=statistic, view=jcr.

1.41. Checking and repairing repository integrity and consistency

Warning

It is highly recommended to back up your data before repairing
inconsistencies (either automatically or maually). It is also recommended
to store the results of queries that check the data consistency. This may
be useful for the support team in case of deeper restoration
process.

Production and any systems may have faults in some days. They may be
caused by hardware and/or software problems, human faults during updates and
in many other circumstances. It is important to check integrity and
consistency of the system if it is not backed up or stale, or it takes the
recovery process much time. The eXo JCR implementation offers an innovative
JMX-based complex checking tool. Running inspection, this tool checks every
major JCR component, such as persistent data layer and index. The persistent
layer includes JDBC Data Container and Value Storage if they are configured.
The database is verified using the set of complex specialized
domain-specific queries. The Value Storage tool checks the existence and
access to each file. Index verification contains two-way pass cycle,
existence of each node in the index checks on persistent layer along with
opposite direction, when each node from Data Container is validated in the
index. Access to the checking tool is exposed via the JMX interface
(RepositoryCheckController MBean) with the
following operations available:

Among the list of known inconsistencies described in the next section,
see below what can be checked and repaired automatically:

An item has no parent node: Properties will be
removed and the root UUID will be assigned in case of nodes.

A node has a single valued property with nothing declared
in the VALUE table: This property will be removed if it is not
required by primary type of its node.

A node has no primary type property: This node
and the whole subtree will be removed if it is not required by primary
type of its parent.

Value record has no related property record:
Value record will be removed from database.

An item is its own parent: Properties will be
removed and root UUID will be assigned in case of nodes.

Several versions of same item: All earlier
records with earlier versions will be removed from ITEM table.

Reference properties without reference records:
The property will be removed if it is not required by the primary type
of its node.

A node is marked as locked in the lockmanager's table but
not in ITEM table or the opposite: All lock inconsistencies
will be removed from both tables.

Note

The only inconsistency that cannot be fixed automatically is
Corrupted VALUE records. Both
STORAGE_DESC and DATA fields
contain not null value. Since there is no way to determinate
which value is valid: either on the file system or in the database.

The list of ValueStorage inconsistencies which can be checked and
repaired automatically:

Property's value is stored in the File System but the
content is missing: A new empty file corresponding to this
value will be created.

The list of SearchIndex inconsistencies which can be checked. To
repair them we need to reindex the content completely, what also can be done
using JMX:

Not indexed document

Document indexed more than one
time

Document corresponds to removed
node

Table 1.50. Repair methods

Operation

Description

repairDataBase()

Repair DB incosistencies declared above

repairValueStorage()

Repair ValueStorage incosistencies declared above

All tool activities are stored in a file, which can be found in app
directory by the report-<repository
name>-dd-MMM-yy-HH-mm.txt name.

1.41.1. Recommendations on how to fix corrupted JCR manually

Here are examples of corrupted JCR and ways to eliminate them:

Note

It is assumed that queries for single and multi DB
configurations are different only in the
JCR_xITEM table name, otherwise queries will be
explicitly introduced.

In some examples, you will be asked to replace some
identificators with the corresponding value. That basically means that
you need to insert values, from each row of result of query executed
during the issue detection stage to the corresponding place. The
explicit explanation of what to do will be introduced in case
replacing is needed to be fulfilled in other way.

Items have no parent
nodes.

To detect this issue, you need to execute the following
query:

select * from JCR_SITEM I where NOT EXISTS(select * from JCR_SITEM P where P.ID = I.PARENT_ID)

Fix description: Assign root as parent node to be able to
delete this node later if it is not needed anymore.

Node considered to be locked in the
lockmanager data, is not locked according to the JCR data or the
opposite situation.

To detect this issue, you need to:

First, get all locked nodes IDs in repository, mentioned
in the JCR_xITEM table by executing a query:

select distinct PARENT_ID from JCR_SITEM where I_CLASS=2 and (NAME='[http://www.jcp.org/jcr/1.0]lockOwner' or NAME='[http://www.jcp.org/jcr/1.0]lockIsDeep')

Then compare it to nodes IDs from the LockManager's
table.

JBC:

Note

During comparing results, be aware that for single
DB configurations you need to cut off ID prefix
representing workspace name for results obtained from
the JCR_xITEM table.

Though a single lock table is usually used for the
whole repository, it is possible to configure separate
DB lock tables for each workspace. In this case, you
need to execute queries for each table to obtain
information over repository.

Non shareable:

Select fqn from ${LOCK_TABLE} where parent='/$LOCKS'

Shareable:

Replace ${REPOSITORY_NAME} with the
corresponding value:

select fqn from ${LOCK_TABLE} where parent like '/${REPOSITORY_NAME}%/$LOCKS/'

ISPN:

Note

For ISPN lock tables which are defined for each
workspace separately, you must execute queries for all
lock tables to obtain information over
repository.

To get all set of locked node IDs in repository,
you must execute the following query for each
workspace.

select id from ${LOCK_TABLE}

Fix description: Remove inconsistent lock entries and
properties. Remove entries in LOCK_TABLE
that have no corresponding properties in
JCR_xITEM table and remove the
JCR_xITEM properties that have no
corresponding entries in LOCK_TABLE.

delete from JCR_SITEM where PARENT_ID='${ID}' and (NAME = '[http://www.jcp.org/jcr/1.0]lockIsDeep' or NAME = '[http://www.jcp.org/jcr/1.0]lockOwner')

Replace ${ID} and ${FQN} with
the corresponding node ID and FQN:

JBC:

delete from ${LOCK_TABLE} where fqn = '${FQN}'

ISPN:

Execute the following query for each workspace:

delete from ${LOCK_TABLE} where id = '${ID}'

A property's value is stored in the file
system, but its content is missing.

This cannot be checked via simple SQL queries.

1.42. JTA

eXo JCR supports the Java Transaction API out of the box. If a
TransactionService has been defined (refer to the
section about the TransactionService for more details) at session save, it
checks if a global transaction is active and if so, it automatically
enrolles the JCR session in the global transaction. If you intend to use a
managed data source, you will have to configure the service
DataSourceProvider (for more details please refer to
the corresponding section).

1.43. The JCA Resource Adapter

eXo JCR supports J2EE Connector Architecture
1.5, thus If you would like to delegate the JCR Session lifecycle to your
application server, you can use the JCA Resource Adapter for eXo JCR if
your application server supports JCA 1.5. This adapter only supports XA
Transaction, in other words you cannot use it for local transactions.
Since the JCR Sessions have not been designed to be shareable, the session
pooling is simply not covered by the adapter.

1.43.1. The SessionFactory

The equivalent of the
javax.resource.cci.ConnectionFactory in JCA
terminology is
org.exoplatform.connectors.jcr.adapter.SessionFactory
in the context of eXo JCR, the resource that you will get thanks to a JNDI
lookup is of type SessionFactory and provides the
following methods:

/**
* Get a JCR session corresponding to the repository
* defined in the configuration and the default workspace.
* @return a JCR session corresponding to the criteria
* @throws RepositoryException if the session could not be created
*/
Session getSession() throws RepositoryException;
/**
* Get a JCR session corresponding to the repository
* defined in the configuration and the default workspace, using
* the given user name and password.
* @param userName the user name to use for the authentication
* @param password the password to use for the authentication
* @return a JCR session corresponding to the criteria
* @throws RepositoryException if the session could not be created
*/
Session getSession(String userName, String password) throws RepositoryException;
/**
* Get a JCR session corresponding to the repository
* defined in the configuration and the given workspace.
* @param workspace the name of the expected workspace
* @return a JCR session corresponding to the criteria
* @throws RepositoryException if the session could not be created
*/
Session getSession(String workspace) throws RepositoryException;
/**
* Get a JCR session corresponding to the repository
* defined in the configuration and the given workspace, using
* the given user name and password.
* @param workspace the name of the expected workspace
* @param userName the user name to use for the authentication
* @param password the password to use for the authentication
* @return a JCR session corresponding to the criteria
* @throws RepositoryException if the session could not be created
*/
Session getSession(String workspace, String userName, String password) throws RepositoryException;

1.43.2. Configuration

Table 1.51. Configuration Properties

PortalContainer

In case of the portal mode, if no portal container can be
found in the context of the request, the adapter will use the
value of this parameter to get the name of the expected portal
container to create the JCR sessions. In case of a standalone
mode, this parameter is not used. This parameter is optional, by
default the default portal container will be used.

Repository

The repository name used to create JCR sessions. This
parameter is optional, by default the current repository will be
used.

1.43.3. Deployment

In case of the standalone mode where the JCR and its dependencies
are not provided, you will need to deploy the whole ear file corresponding
to the artifactId exo.jcr.ear and groupId
org.exoplatform.jcr, the rar file is embedded into
the ear file. In case the JCR and its dependencies are provided like when
you use it with gateIn for example, you will need to deploy only the rar
file corresponding to the artifactId
exo.jcr.connectors.jca and groupId
org.exoplatform.jcr.

Then you will need to configure the connector itself, for example
for JBoss AS, you need to create in your deploy directory a file of type
*-ds.xml (jcr-ds.xml for example) with the following
content:

1.44. Access Control

1.44.1. Standard Action Permissions

The JCR specification (JSR 170) does not have many requirements
about Access Control. It only requires the implementation of the
Session.checkPermission(String absPath, String actions) method. This
method checks if a current session has permissions to perform some actions
on absPath:

absPath : The string representation of a JCR absolute
path.

actions : eXo JCR interprets this string as a comma separated
the list of individual action names, such as the 4 types defined in
JSR 170 :

add_node : Permission to
add a node.

set_property : Permission
to set a property.

remove : Permission to
remove an item (node or property).

read : Permission to
retrieve a node or read a property value.

For example :

session.checkPermission("/Groups/organization",
"add_node,set_property") will check if the session is allowed to add a
child node to "organization" and to modify its properties. If one of
the two permissions is denied, an AccessDeniedException is
thrown.

session.checkPermission("/Groups/organization/exo:name",
"read,set_property") will check if the session is allowed to read and
change the "exo:name" property of the "organization" node.

session.checkPermission("/Groups/organization/exo:name",
"remove") will check if the session allowed to remove "exo:name"
property or node.

1.44.2. eXo Access Control

The JSR170 specification does not define how permissions are managed
or checked. So eXo JCR has implemented its own proprietary extension to
manage and check permissions on nodes. In essence, this extension uses an
Access
Control List (ACL) policy model applied to eXo Organization model
(see eXo Platform Organization Service).

1.44.2.1. Principal and Identity

At the heart of eXo Access Control, is the notion of the identity concept. Access to JCR is made through
sessions acquired against a repository. Sessions can be authenticated
through the standard (but optional) repository login mechanism. Each
session is associated with a principal.
The principal is an authenticated user or group that may act on JCR
data. The identity is a string identifying this group or user.'

There are 3 reserved identities that have special meanings in eXo
JCR:

system : represents a
principal for system sessions, typically used for administrative
purposes. System session has full access (all permissions) to all
nodes; therefore be careful when working with system
sessions.

1.44.2.2. ACL

An access control list (ACL) is a list of permissions attached to
an object. An ACL specifies which users, groups or system processes are
granted access to JCR nodes, as well as what operations are allowed to
be performed on given objects.

eXo JCR Access Control is based on two facets applied to nodes
:

Privilegeable : Means that
the user or group (also called principal) needs the appropriate
privileges to access to this node. The privileges are defined as
(positive) permissions that are granted to users or groups.

Ownable : The node has an
owner. The owner has always
full access (all permissions) to
the node, independent of the privilegeable facet.

1.44.2.2.1. Privilegeable

A privilegeable node defines the permissions required for
actions on this node. For this purpose, it contains an ACL.

A privilegeable node can have multiple exo:permissions values.
The type of these values is the eXo JCR specific Permission type. The
Permission type contains a list of ACL.

The possible values are corresponding to JCR standard
actions:

read: The node or its
properties can be read.

remove: The node or its
properties can be removed.

add_node : Child nodes can
be added to this node.

set_property : The node's
properties can be modified, added or removed.

1.44.2.2.2. Ownable

An ownable node defines an owner identity. The owner has always full
privileges. These privileges are independent of the
permissions set by exo:permissions. At JCR level, the ownership is
implemented by an exo:owneable mixin.
This mixin holds an owner property.

The exo:owner property value contains exactly one identity
string value. There might be a long list of different permissions for
different identities (user or groups). All permissions are always
positive permissions; denials are not possible. When checking a
permission of an action, it's therefore perfectly sufficient that the
principal of a session belongs to the groups to which the concerned
action is granted.

1.44.2.2.3. ACL Inheritance

To grant or deny access to a node, eXo JCR applies a privilege
resolving logic at node access time.

If a node is privilegeable, the
node's ACL is used exclusively. If the ACL does not match the
principal's identity, the principal has no access (except the owner of
the node).

Non-privilegeable nodes inherit permissions from their parent
node. If the parent node is not privilegeable either, the resolving
logic looks further up the node hierarchy and stops with the first
privilegeable ancestor of the current node. All nodes potentially
inherit from the workspace root
node.

The owner of a node is inherited in accordance with the same
logic: If the node has no owner, the owner information of the closest
owneable ancestor is inherited.

This inheritance is implemented by browsing up the node's
hierarchy. At access time, if the node does not have owner or
permissions, the system looks up into the node's ancestor hierarchy
for the first ACL.

1.44.2.2.4. Default ACL of the root node

When no matching ACL is found in the ancestor hierarchy, the
system may end up looking at the root node's ACL. As ACL are optional,
even for the root node, if the root node has no ACL, the following
rule is ultimately applied to resolve privileges:

any identity (any
authenticated session) is granted all permissions

1.44.2.3. Notes

Access Control nodetypes are not
extendible: The access control mechanism works for exo:owneable and exo:privilegeable nodetypes only, not for their
subtypes! So you cannot extend those nodetypes.

Autocreation: By default, newly
created nodes are neither exo:privilegeable nor exo:owneable but it is possible to configure the
repository to auto-create exo:privilegeable or/and exo:owneable thanks to eXo's JCR interceptors
extension (see JCR
Extensions)

OR-based Privilege Inheritance:
Note, that eXo's Access Control implementation supports a privilege
inheritance that follows a strategy of either...or/ and has only an
ALLOW privilege mechanism (there is no DENY feature). This means that a
session is allowed to perform some operations on some nodes if its
identity has an appropriate permission assigned to this node. Only if
there is no exo:permission property assigned to the node itself, the
permissions of the node's ancestors are used.

1.44.2.4. Example

1.44.2.4.1. XML Example

In the following example, you see a node named "Politics" which
contains two nodes named "Cats" and "Dogs".

Note

These examples are exported from eXo DMS using the \"document
view\" representation of JCR. Each value of a multi-value property
is separated by a whitespace, each whitespace is escaped by
x0020.

The "Politics" node is exo:owneable and exo:privilegeable. It has both an exo:owner property and an exo:permissions property. There is an exo:owner="root" property so that the user root
is the owner. In the exo:permissions value, you can see the ACL that
is a list of access controls. In this example, the group *:/platform/administrators has all rights on
this node (remember that the "*"
means any kind of membership). any
means that any users also have the read permission.s

As you see in the jcr:mixinTypes property, the "Cats" node is
exo:owneable and there is an
exo:owner="marry" property so that
the user marry is the owner. The "Cats" node is not exo:privilegeable and has no exo:permissions. In this case, we can see
the inheritance mechanism here is
that the "Cats" node has the same permissions as "Politics"
node.

Finally, the "Dogs" node is also a child node of "Politics".
This node is notexo:owneable and inherits the owner of the
"Politics" node (which is the user root). Otherwise, "Dogs" is
exo:privilegeable and therefore, it
has its own exo:permissions. That
means only the users having a "manager" role in the group
"/organization" and the user "root" have the rights to access this
node.

1.44.2.4.2. Inheritance Examples

Here is an example showing the accessibility of two nodes (to
show inheritance) for two sample users named manager and user:

The "+" symbol means that there is a child node
"exo:owneable".

1.44.2.4.3. Permission validation

This session describes how permission is validated for different
JCR actions.

The "identity" parameter is a user or a group name. The
permissions are the literal strings of the standard action permissions
(add_node, set_property, remove, read).

1.45. Access Control Extension

An extended Access Control system consists of:

Specifically configured custom ExtendedAccessManager which is called by eXo JCR
internals to check if user's Session (user) has some privilege to
perform some operation or not.

The Action sets a thread local
InvocationContext at runtime, the
InvocationContext instance is then used by the ExtendedAccessManager in
handling permissions of the current Session.

InvocationContext is a collection
of properties which reflect the state of a current Session. At present,
it contains: the type of the current operation on Session (event),
current Item (javax.jcr.Item) on which this operation is performed and
the current eXo Container

1.45.1. Prerequisites

1.45.2. Access Context Action

SetAccessControlContextAction implements Action and may be called by
SessionActionInterceptor as a reaction of some events - usually before
writing methods and after reading (getNode(), getProperty() etc). This
SetAccessControlContextAction calls the
AccessManager.setContext(InvocationContext context) method which sets the
ThreadLocal invocation context for the current call.

1.45.3. The Invocation Context

The InvocationContext contains the
current Item, the previous Item, the current ExoContainer and the current
EventType is like below:

public class InvocationContext extends HashMap implements Context {
/**
* @return The related eXo container.
*/
public final ExoContainer getContainer()
/**
* @return The current item.
*/
public final Item getCurrentItem()
/**
* @return The previous item before the change.
*/
public final Item getPreviousItem()
/**
* @return The type of the event.
*/
public final int getEventType()
}

1.45.4. Custom Extended Access Manager

By default, all Workspaces share an AccessManager instance, created
by RepositoryService at the startup (DefaultAccessManagerImpl) which
supports default access control policy as described in the Access Control section. Custom Access Control
policy can be applied to certain Workspace configuring access-manager element inside workspace as follows:

When implementing AccessManager, hasPermission() method has to be
overriden so it uses the current invocation context at its discretion. For
instance, it may get the current node's metadata and make a decision if
the current User has appropriate permissions. Use Invocation Context's
runtime properties to make a decision about current Session's privileges
(see the Example below)

Simplified Sequence diagram for the Session.getNode() method (as an
Example):

1.45.5. Example of a custom Access Manager

The sample CustomAccessManagerImpl below extends the default access
manager and uses some DecisionMakingService in the overloaded
hasPermission method to find out if a current user has permission to use
current item, event type, user and some
parameter of AccessManager. To make this Access manager work, it is
necessary to configure it in jcr configuration as mentioned in Custom Extended Access Manager and
SetAccessControlContextAction should be configured in the way mentioned in
Access Context Action.

1.46. Link Producer Service

Link Producer Service - a simple service, which generates an .lnk
file, that is compatible with the Microsoft link file format. It is an
extension of the REST Framework library and is included into the WebDav
service. On dispatching a GET request the service generates the content of
an .lnk file, which points to a JCR resource via WebDav.

When using JCR the resource can be addressed by WebDav reference
(href) like
http://host:port/rest/jcr/repository/workspace/somenode/somefile.extention
, the link servlet must be called for this resource by several hrefs, like
http://localhost:8080/rest/lnkproducer/openit.lnk?path=/repository/workspace/somenode/somefile.extention

Please note, that when using the portal mode the REST servlet is
available using a reference (href) like
http://localhost:8080/portal/rest/...

The name of the .lnk file can be any. But for the best compatibility
it must be the same as the name of the JCR resource.

Here is a step by step sample of a use case of the link producer... At
first, type valid reference to the resource, using the link producer in your
browser's adress field:

Internet Explorer will give a dialog window requesting to Open a file
or to Save it. Click on the Open button

In Windows system an .lnk file will be downloaded and opened with the
application which is registered to open the files, which are pointed to by
the .lnk file. In case of a .doc file, Windows opens Microsoft Office Word
which will try to open a remote file (test0000.doc). Maybe it will be
necessary to enter USERNAME and PASSWORD.

Next, you will be able to edit the file in Microsoft Word.

The Link Producer is necessary for opening/editing and then saving the
remote files in Microsoft Office Word, without any further updates.

Also the Link Producer can be referenced to from an HTML page. If page
contains code like

1.47. Binary Values Processing

Processing binary large object (BLOB) is very important in eXo JCR, so this section focuses on explaining how to
do it.

1.47.1. Configuration

Binary large object (BLOB) properties can be stored in two ways in
the eXo JCR: in the database with items information or in an external
storage on host file system. These options can be configured at workspace
in the repository configuration file (repository-configuration.xml in
portal and exo-jcr-config.xml in standalone mode). The database storage
can't be completely disabled.

The first case is optimal for most of cases which you do not use
very large values or/and do not have too many BLOBs. The configuration of
the BLOBs size and BLOBs quantity in a repository depend on your database
features and hardware.

The second case is to use an external values storage. The storage
can be located on a built-in hard disk or on an attached storage. But in
any cases, you should access to the storage as if it was a regular
file(s). The external value storage is optional and can be enabled in a
database configuration.

Note

eXo JCR Repository service configuration basics is discussed in
Configuration

1.47.2. Usage

In both of the cases, a developer can set/update the binary Property
via Node.setProperty(String, InputStream), Property.setValue(InputStream)
as described in the spec JSR-170. Also, there is the setter with a ready
Value object (obtainer from ValueFactory.createValue(InputStream)).

An example of a specification usage.

// Set the property value with given stream content.
Property binProp = node.setProperty("BinData", myDataStream);
// Get the property value stream.
InputStream binStream = binProp.getStream();
// You may change the binary property value with a new Stream, all data will be replaced
// with the content from the new stream.
Property updatedBinProp = node.setProperty("BinData", newDataStream);
// Or update an obtained property
updatedBinProp.setValue(newDataStream);
// Or update using a Value object
updatedBinProp.setValue(ValueFactory.createValue(newDataStream));
// Get the updated property value stream.
InputStream newStream = updatedBinProp.getStream();

But if you need to update the property sequentially and with partial
content, you have no choice but to edit the whole data stream outside and
get it back to the repository each time. In case of really large-sized
data, the application will be stuck and the productivity will decrease a
lot. JCR stream setters will also check constraints and perform common
validation each time.

There is a feature of the eXo JCR extension that can be used for
binary values partial writing without frequent session level calls. The
main idea is to use a value object obtained from the property as the
storage of the property content while writing/reading during
runtime.

According to the spec JSR-170, Value interface provides the state of
property that can't be changed (edited). The eXo JCR core provides
ReadableBinaryValue and EditableBinaryValue interfaces which themselves
extend JCR Value. The interfaces allow the user to partially read and
change a value content.

ReadableBinaryValue value can be casted from any value, i.e. String,
Binary, Date etc.

// get the property value of type PropertyType.STRING
ReadableBinaryValue extValue = (ReadableBinaryValue) node.getProperty("LargeText").getValue();
// read 200 bytes to a destStream from the position 1024 in the value content
OutputStream destStream = new FileOutputStream("MyTextFile.txt");
extValue.read(destStream, 200, 1024);

But EditableBinaryValue can be applied only to properties of type
PropertyType.BINARY. In other cases, a cast to EditableBinaryValue will
fail.

After the value has been edited, the EditableBinaryValue value can
be applied to the property using the standard setters
(Property.setValue(Value), Property.setValues(Value),
Node.setProperty(String, Value) etc.). Only after the EditableBinaryValue
has been set to the property, it can be obtained in this session by
getters (Property.getValue(), Node.getProperty(String) etc.).

The user can obtain an EditableBinaryValue instance and fill it with
data in an interaction manner (or any other appropriated to the targets)
and return (set) the value to the property after the content will be
done.

A practical example of the iterative usage. In this example, the
value is updated with data from the sequence of streams and after the
update is done, the value will be applied to the property and be visible
during the session.

Update with length bytes from the specified stream to this value
data at a position. If the position is lower than 0, the IOException
exception will be thrown. If the position is higher than the current Value
length, the Value length will be increased at first to the size of
position and length bytes will be added after the position.

Set the length of the Value in bytes to the specified size. If the
size is lower than 0, the IOException exception will be thrown. This
operation can be used to extend or truncat the Value size. This method is
used internally in the update operation in case of extending the size to
the given position.

void setLength(long size) throws IOException;

An application can perform JCR binary operations more flexibly and
will have less I/O and CPU usage using these methods.

*EXPERIMENTAL*

Read-only WorkspaceStorageConnection is experimental feature and
not currently handled in JCR. Actually, such connections didn't prove
their performance, so JCR Core doesn't use them.

Storage connection might also be reused. This means reuse of
physical resource (e.g. JDBC Connection) allocated by one connection
in another. This feature is used in a data manager for saving
ordinary and system changes on the system Workspace. But the reuse
is an optional feature and it can work, otherwise a new connection
will open.

When checking Same-Name Siblings (SNS) existence, JCR Core can
use new connection or not. This is defined via Workspace Data
Container configuration and retrieved by using a special
method.

boolean isCheckSNSNewConnection();

Container initialization is only based on a configuration. After
the container has been created, it's not possible to change parameters.
Configuration consists of implementation class and set of properties and
Value Storages configuration.

1.49.1.2. Value storages

Container provides optional special mechanism for Value storing.
It's possible to configure external Value Storages via container
configuration (available only via configuration). Value Storage works as
fully independent pluggable storage. All required parameters storage
obtains from its configuration. Some storages are possible for one
container. Configuration describes such parameters as
ValueStoragePluginimplementation class, set of implementation specific
properties and filters. The filters declares criteria for Value matching
to the storage. Only matched Property Values will be stored. So, in
common case, the storage might contains only the part of the Workspace
content. Value Storages are very useful for BLOB storing. E.g. storing
on the File System instead of a database.

Container obtains Values Storages from ValueStoragePluginProvider
component. Provider acts as a factory of Value channels
(ValueIOChannel). Channel provides all CRUD operation for Value Storage
respecting the transaction manner of work (how it can be possible due to
implementation specifics of the storages).

1.49.1.3. Lifecycle

Container is used for read and write operations by data manager.
Read operations (getters) uses connection once and close it on the
finally. Write operations performs in commit method as a sequence of
creating/ updating calls and final commit (or rollback on error). Writes
uses one connection (or two - another for system workspace) per commit
call. One connection guaranties transaction support for write
operations. Commit or rollback should free/clean all resources consumed
by the container (connection).

1.49.1.4. Value storage lifecycle

Value storage is used from the container inside. Reads are related
to a container reads. Writes are commit-related. Container (connection)
implementation should use transaction capabilities of the storages in
the same way as for other operations.

1.49.2. Requirements

Connection creation and reuse should be a thread safe operation.
Connection provides CRUD operations support on the storage.

Persist changes and closes connection. It can be database
transaction commit for instance etc.

void commit() throws IllegalStateException, RepositoryException;

Refuse persistent changes and closes connection. It can be
database transaction rollback for instance etc.

void rollback() throws IllegalStateException, RepositoryException;

All methods throw IllegalStateException if connection is closed.
UnsupportedOperationException if the method is not supported (e.g. JCR
Level 1 implementation etc). RepositoryException if some errors occur
during preparation, validation or persistence.

1.49.2.3. State operations

Return true if connection can be used.

boolean isOpened();

1.49.2.4. Validation of write operations

Container has to care about storage consistency (JCR constraints)
on write operations: (InvalidItemStateException should be thrown
according the spec). At least, the following checks should be
performed:

1.49.2.5. Consistency of save

The container (connection) should implement consistency of Commit
(Rollback) in transaction manner. I.e.
If a set of operations was performed before the future Commit and another next operation fails. It should be
possible to rollback applied changes using Rollback command.

1.49.3. Value storages API

1.49.3.1. Storages provider:

Container implementation obtains Values Storages option via
ValueStoragePluginProvider component. Provider acts as a factory of
Value channels (ValueIOChannel) and has two methods for this
purpose:

Return ValueIOChannel matched this property and
valueOrderNumer. Null will be returned if no channel matches.

There is also method for consistency check, but this method
doesn't used anywhere and storage implementations has it empty.

1.49.3.2. Value storage plugin

Provider implementation should use ValueStoragePlugin abstract
class as a base for all storage implementations. Plugin provides support
for provider implementation methods. Plugin's methods should be
implemented:

Initialize this plugin. Used at start time in
ValueStoragePluginProvider.

1.50. How to implement Workspace Data Container

Start a new implementation project pom.xml with
org.exoplatform.jcr parent. It is not required, but will ease the development.

Update sources of JCR Core and read JavaDoc on org.exoplatform.services.jcr.storage.WorkspaceDataContainer
and org.exoplatform.services.jcr.storage.WorkspaceStorageConnection
interfaces. They are the main part for the implemenation.

Create WorkspaceStorageConnection dummy implementation
class. It's freeform class, but to be close to the eXo JCR, check how
to implement JDBC ( org.exoplatform.services.jcr.impl.storage.jdbc.JDBCStorageConnection.
Take in account usage of ValueStoragePluginProvider in both
implementations.Value storage is an useful option for production
versions. But leave it to the end of implementation work.

Create the connection implementation unit tests to play TTD.
(optional, but takes many benefits for the process)

Implement CRUD starting from the read to write etc. Test the
methods by using the external implementation ways of data read/write
in your backend.

When all methods of the connection done start WorkspaceDataContainer. Container class is very
simple, it's like a factory for the connections only.

Care about container reuseConnection(WorkspaceStorageConnection)
method logic. For some backends, it cab be same as openConnection(),
but for some others, it's important to reuse physical backend
connection, e.g. to be in the same transaction - see JDBC
container.

It's almost ready to use in data manager. Start another test and
go on.

When the container will be ready to run as JCR persistence storage
(e.g. for this level testing), it should be configured in Repository
configuration.

Assuming that our new implementation class name is org.project.jcr.impl.storage.MyWorkspaceDataContainer.

Important

After a sequence of write and/or delete operations on the storage
channel, the channel should be committed (or rolled back on an error).
See ValueIOChannel.commit() and
ValueIOChannel.rollback() and how those
methods are used in JDBC container.

1.51. DBCleanService

It is a special service for data removal from database. The section
shortly describes the principles of work DBCleaner under all
databases.

It is special service for data removal from database. The article
shortly describes the principles of work DBCleaner under all
databases

1.51.1. Methods of DBCleanService

Note

Code that invokes the methods of DBCleanService must have
JCRRuntimePermissions.MANAGE_REPOSITORY_PERMISSION permission.

1.51.3. Need to clean the whole repository

In case of single-db all workspaces will be processed simultaneously
as in case of single workspace multi-db configuration. For multi-db every
workspace will be processed separately as in case of single workspace
multi-db configuration.

1.52. JCR Performance Tuning Guide

This section will show you possible ways of improving JCR

It is intended to GateIn Administrators and those who wants to use
JCR features.

1.52.1. JCR Performance and Scalability

1.52.1.1. Cluster configuration

EC2 network: 1Gbit

Servers hardware:

7.5 GB memory

4 EC2 Compute Units (2 virtual cores with 2 EC2 Compute
Units each)

850 GB instance storage (2×420 GB plus 10 GB root
partition)

64-bit platform

I/O Performance: High

API name: m1.large

Note

NFS and statistics (cacti snmp) server were located on one
physical server.

1.52.1.2. JCR Clustered Performance

Benchmark test using webdav (Complex read/write load test
(benchmark)) with 20K same file. To obtain per-operation results we have
used custom output from the testscase threads to CSV file.

Read operation:

Warm-up iterations: 100

Run iterations: 2000

Background writing threads: 25

Reading threads: 225

Table 1.59.

Nodes count

tps

Responses >2s

Responses >4s

1

523

6.87%

1.27%

2

1754

0.64%

0.08%

3

2388

0.49%

0.09%

4

2706

0.46%

0.1%

Read operaion with more threads:

Warm-up iterations: 100

Run iterations: 2000

Background writing threads: 50

Reading threads: 450

Table 1.60.

Nodes count

tps

Responses >2s

Responses >4s

1

116

?

?

2

1558

6.1%

0.6%

3

2242

3.1%

0.38%

4

2756

2.2%

0.41%

1.52.2. Performance Tuning Guide

1.52.2.1. JBoss AS Tuning

You can use maxThreads parameter to
increase maximum amount of threads that can be launched in AS instance.
This can improve performance if you need a high level of concurrency.
also you can use -XX:+UseParallelGC java directory to use
parallel garbage collector.

Tip

Beware of setting maxThreads too big,
this can cause OutOfMemoryError. We've
got it with maxThreads=1250 on such machine:

7.5 GB memory

4 EC2 Compute Units (2 virtual cores with 2 EC2 Compute
Units each)

850 GB instance storage (2×420 GB plus 10 GB root
partition)

64-bit platform

I/O Performance: High

API name: m1.large

java -Xmx 4g

1.52.2.2. JCR Cache Tuning

Cache size

JCR-cluster implementation is built using JBoss Cache as
distributed, replicated cache. But there is one particularity related to
remove action in it. Speed of this operation depends on the actual size
of cache. As many nodes are currently in cache as much time is needed to
remove one particular node (subtree) from it.

Eviction

Manipulations with eviction wakeUpInterval
value doestn't affect on performance. Performance results with values
from 500 up to 3000 are approximately equal.

Transaction Timeout

Using short timeout for long transactions such as Export/Import,
removing huge subtree defined timeout may cause
TransactionTimeoutException.

1.52.2.3. Clustering

For performance it is better to have loadbalacer, DB server and
shared NFS on different computers. If in some reasons you see that one
node gets more load than others you can decrease this load using load
value in load balancer.

JGroups configuration

It's recommended to use "multiplexer stack" feature present in
JGroups. It is set by default in eXo JCR and offers higher performance
in cluster, using less network connections also. If there are two or
more clusters in your network, please check that they use different
ports and different cluster names.

Write performance in cluster

Exo JCR implementation uses Lucene indexing engine to provide
search capabilities. But Lucene brings some limitations for write
operations: it can perform indexing only in one thread. Thats why write
performance in cluster is not higher than in singleton environment. Data
is indexed on coordinator node, so increasing write-load on cluster may
lead to ReplicationTimeout exception. It occurs because writing threads
queue in the indexer and under high load timeout for replication to
coordinator will be exceeded.

Taking in consideration this fact, it is recommended to exceed
replTimeout value in cache configurations in case
of high write-load.

Replication timeout

Some operations may take too much time. So if you get
ReplicationTimeoutException try
increasing replication timeout:

1.52.2.4. JVM parameters

PermGen space size

If you intend to use Infinispan, you will have to increase the
PermGen size to at least 256 Mo due to the latest versions of JGroups
that are needed by Infinispan (please note that Infinspan is only
dedicated to the community for now, no support will be provided). In
case, you intend to use JBoss Cache, you can keep on using JGroups
2.6.13.GA which means that you don't need to increase the PermGen
size.

eXo Kernel is the basis of all eXo platform products and modules.
Any component available in eXo Platform is managed by the Exo Container,
our micro container responsible for gluing the services through dependency
injection

Therefore, each product is composed of a set of services and plugins
registered to the container and configured by XML configuration
files.

The Kernel module also contains a set of very low level
services.

2.1. ExoContainer info

ExoContainer is the main IoC kernel object. The container is
responsible for loading services/components.

2.1.1. Container hierarchy

Behavior is like class loaders

A child container sees parent container components

Extensively used in eXo Platform

2.2. Service Configuration for Beginners

This section provides you the basic knowledge
about modes, services and containers. You will find out where the service
configuration files should be placed, and you will also see the overriding
mechanism of configurations.

Finally, you will understand how the container
creates the services one after the other and what Inversion of
Control really means.

2.2.1. Requirements

By reading this article you are already glancing at the heart of eXo
Kernel.

Even you will read in this article to open the directory
"exo-tomcat", you may have installed GateIn on any application server,
just replace "exo-tomcat" by your folder name.

Note

If you only installed Gatein or eXo Platform, the folder paths are a slightly different.
You wil need to replace exo-tomcat with your tomcat home directory.

2.2.2. Services

Nearly everything could be considered a service! To get a better
idea, let's look into the exo-tomcat/lib folder where
you find all deployed jar files.

For example you find services for databases, caching, ldap and
ftp:

exo.core.component.database-2.1.3.jar

exo.kernel.component.cache-2.0.5.jar

exo.core.component.organization.ldap-2.1.3.jar

exo.jcr.component.ftp-1.10.1.jar

Of course, there are many more services, in fact a lot of these jar
files are services. To find out you have to open the jar file and then
look into its /conf or
/conf/portal directory. Only if there is a file named
configuration.xml, you are sure to have found a
service.

Note

Why are there 2 different places to look for the
configuration.xml? Because the /conf directory is
used by the RootContainer and the
/conf/portal directory is used by the
PortalContainer. Later you will see more details
about these containers.

Interface - Implementation It's
important to get the idea that you separate the interface and
implementation for a service. That is a good concept to reduce
dependencies on specific implementations. This concept is well known for
JDBC. If you use standard JDBC (=interface), you can connect any database
(=implementation) to your application. In a similar way any service in eXo
is defined by a java interface and may have many different
implementations. The service implementation is then
injected by a container into the
application.

Singleton Each service has to be
implemented as a singleton,
which means that each service is created only once - in one single
instance.

Service = Component You always read
about services, and you imagine a service as a large application which
does big things, but that's not true, a service can be just a little
component that reads or transforms a document,
therefore the term component is often used instead of service - so bear in
mind: a service and a component can safely be considered to be
the same thing.

2.2.3. Configuration File

The jar file of a service should contain a default configuration,
you find this configuration in the configuration.xml file which comes with
the jar. A configuration file can specify several services, as well as
there can be several services in one jar file.

For example open the
exo.kernel.component.cache-2.0.5.jar file and inside
this jar open /conf/portal/configuration.xml. You will see:

Here you will note that a service is specified between the
<component> tags. Each service has got a key,
which defines the kind of service. As you imagine the content of the
<key> tag matches the qualified
java interface name
(org.exoplatform.services.cache.CacheService) of
the service. The specific implementation class of the
CacheService is defined in the
<type> tag.

Parameters You have already opened
some configuration files and seen that there are more than just
<key> and <type>
tags. You can provide your service with init parameters. The parameters
can be simple parameters, properties, or object-params. There are also
plugins and they are special because the container
calls the setters of your service in order to inject
your plugin in your service (called setter injection)
see Service
Configuration in Detail. In general your service is free to use
init parameters, they are not required.

If you ever need to create your own service, the minimum is to
create an empty interface, an empty class and a constructor for your class
- that's all. Ok, you also should put your class and the interface in a
jar file and add a default configuration file.

2.2.4. Execution Modes

One important thing to understand concerns execution modes. There
are only two modes:

Portal mode: The service runs embedded in the GateIn. In
this mode a PortalContainer is used.

Standalone mode: The service runs without the portal. For
example, the JCR service can run standalone, and also the eXo Portlet
Container. This mode is used by eXo developers for unit tests. As the
name suggests a StandaloneContainer is
used.

2.2.5. Containers

Among the classes you see in this directory, you only will be
interested in these three container types:

RootContainer: This is a base container. This container plays an
important role during startup, but you should not use it
directly.

PortalContainer: Created at the startup of the portal web
application (in the init() method of the PortalController
servlet)

StandaloneContainer: A context independent eXo Container. The
StandaloneContainer is also used for unit
tests.

Use only one container Even if
there are several container types you always use exactly one. The
RootContainer is never directly used and it depends on the execution mode
if you use the PortalContainer or the StandaloneContainer. You will ask
how to find out the execution mode in my application and how to manage
these two modes. It's easy, you don't have to worry about it because the
ExoContainerContext class provides a static method that allows you to get
the right container from anywhere (see info box).

PicoContainer All containers
inherit from the ExoContainer class which itself inherits from a
PicoContainer. PicoContainer is a framework
which allows eXo to apply the IoC (Inversion of Control)
principles. The precise implementations of any service is unknown at
compile time. Various implementations can be used, eXo supplies different
implementations but they also may be delivered by other vendors. The
decision which service to use during runtime is made in configuration
files.

These configuration files are read by the container, the container
adds all services to a list or more exactly a java HashTable. It's
completely correct to suppose that the configuration.xml you already saw
plays an important role. But there are more places where a configuration
for a service can be defined as you see in the next section.

Note

"In your java code you have to use

ExoContainer myContainer = ExoContainerContext.getCurrentContainer();

in order to access to the current container. It doesn't greatly matter
to your application if the current container is a
PortalContainer or a
StandaloneContainer. Once you have your container
you may access to any service registered in this container using

You easily realize that MyService.class is the
name of the service interface.

2.2.6. Configuration Retrieval

The configuration you find inside the jar file is considered as the
default configuration. If you want to override this default configuration
you can do it in different places outside the jar. When the container
finds several configurations for the same service, the configuration which
is found later replaces completely the one found previously. Let's call
this the configuration override mechanism.

2.2.6.1. RootContainer

As both containers, PortalContainer and StandaloneContainer,
depend on the RootContainer, we will start by looking into this
one.

External RootContainer configuration,
to be found at
exo-tomcat/exo-conf/configuration.xml

Note

Naturally you always have to replace
exo-tomcat by your own folder name. In case of
a Java Standalone application you have to use the
user.dir JVM system property value.

HashTable The
RootContainer creates a java
HashTable which contains key-value pairs for the
services. The qualified interface name of each service is used as key
for the hashtable. Hopefully you still remember that the
<key> tag of the configuration file
contains the interface name? The value of each hashtable pair is an
object that contains the service configuration (yes, this means the
whole structure between the <component>
tags of your configuration.xml file).

The RootContainer runs over all jar files
you find in exo-tomcat/lib and looks if there is a
configuration file at /conf/configuration.xml, the
services configured in this file are added to the hashtable. That way -
at the end of this process - the default configurations for all services
are stored in the hashtable.

Note

What happens if the same service - recognized by the same
qualified interface name - is configured in different jars? As the
service only can exist one time the configuration of the jar found
later overrides the previous configuration. You know that the loading
order of the jars is unpredictable
you must not depend on this.

If you wish to provide your own configurations for one or several
services, you can do it in a general configuration file that has to be
placed at exo-tomcat/exo-conf/configuration.xml. Do
not search for such a file on your computer - you won't find one,
because this option is not used in the default installation. Here again
the same rule applies: The posterior configuration replaces
the previous one.

The further configuration retrieval depends on the container
type.

2.2.6.2. PortalContainer

The PortalContainer takes the hashtable filled by the
RootContainer and continues to look in some more places. Here you get
the opportunity to replace RootContainer configurations by those which
are specific to your portal. Again, the configurations are overridden
whenever necessary.

In short PortalContainer configurations are retrieved in the
following lookup sequence :

External configuration for services of a named portal, it will
be found at
exo-tomcat/exo-conf/portal/$portal_name/configuration.xml
(as of GateIn)

You see, here the
/conf/portal/configuration.xml file of each jar
enters the game, they are searched at first. Next, there is nearly
always a configuration.xml in the portal.war file (or in the portal
webapp folder), you find this file at
/WEB-INF/conf/configuration.xml. If you open it,
you will find a lot of import statements that point to other
configuration files in the same portal.war (or portal webapp).

Multiple Portals Be aware that
you might set up several different portals ("admin", "mexico", etc.),
and each of these portals will use a different PortalContainer. And each
of these PortalContainers can be configured separately. As of GateIn
you also will be able to provide configurations from outside the
jars and wars or webapps. Put a configuration file in
exo-tomcat/exo-conf/portal/$portal_name/configuration.xml
where $portal_name is the name of the portal you
want to configure for . But normally you only have one portal which is
called "portal" so you use
exo-tomcat/exo-conf/portal/portal/configuration.xml.

Note

GateIn you can override the external configuration
location with the system property exo.conf.dir.
If the property exists its value will be used as path to the eXo
configuration directory, that means this is an alternative to
exo-tomcat/exo-conf. Just put this property in
the command line: java
-Dexo.conf.dir=/path/to/exo/conf or use eXo.bat or eXo.sh.
In this particular use case, you have no need to use any prefixes in
your configuration file to import other files. For example, if your
configuration file is
exo-tomcat/exo-conf/portal/PORTAL_NAME/configuration.xml
and you want to import the configuration file
exo-tomcat/exo-conf/portal/PORTAL_NAME/mySubConfDir/myConfig.xml,
you can do it by adding
<import>mySubConfDir/myConfig.xml</import>
to your configuration file.

Note

Under JBoss application server
exo-conf will be looked up in directory described
by JBoss System property jboss.server.config.url.
If the property is not found or empty
exo-jboss/exo-conf will be asked (since kernel
2.0.4).

2.2.6.3. StandaloneContainer

In the same way as the PortalContainer the StandaloneContainer
takes over the configuration of the RootContainer.
After that our configuration gets a little bit more tricky because
standalone containers can be initialized using an URL. This URL contains
a link to an external configuration. As you probably never need a
standalone configuration you can safely jump over the remaining
confusing words of this section.

After taking over RootContainer's configuration, there are three
cases which depend on the URL initialization, :

Independent configuration by
URL No other configuration file is taken in
consideration. The configuration provided by the URL is used without
any default configs. That means that the container creates a new
empty hashtable and not any bit of previous configuration is used.
Apply the following code to do this:

StandaloneContainer.setConfigurationURL(containerConf);

Additional configuration by
URL The StandaloneContainer is initialized very similar
to the PortalContainer, but the last step is slightly different. A
configuration file that is provided by the URL is used to replace
some of the service configurations.The code looks like this:

External configuration for
StandaloneContainer services, it will be
found at $user_home/exo-configuration.xml.
If $user_home/exo-configuration.xml doesn't
exist and the StandaloneContainer instance
obtained with the dedicated configuration classloader the
container will try to retrieve the resource
conf/exo-configuration.xml within the given
classloader (user_home is your home directory like "C:/Documents
and Settings/Smith").

2.2.7. Service instantiation

As you have already learned the services are all singletons, so that
the container creates only one single instance of each container. The
services are created by calling the constructors (called
constructor injection). If there are only
zero-arguments constructors (Foo public Foo(){}) there are no
problems to be expected. That's easy.

You see this service depends on two other services. In order to be
able to call this constructor the container first needs a
ListenerService and a
DatabaseService. Therefore these services must be
instantiated before BaseOrganizationService,
because BaseOrganizationService depends on
them.

For this purpose the container first looks at the constructors of
all services and creates a matrix of service dependencies in order to call
the services in a proper order. If for any reason there are
interdependencies or circular dependencies you will get a java
Exception. In this way the dependencies
are injected by the container.

Note

What happens if one service has more than one constructor? The
container always tries first to use the constructor with a maximum number
of arguments, if this is not possible the container continues step by step
with constructors that have less arguments until arriving at the
zero-argument constructor (if there is any).

2.2.8. Miscellaneous

2.2.8.1. Startable interface

Your service can implement the startable
interface which defines a start() and a
stop() method. These methods are called by the
container at the beginning and the end of the container's lifecycle.
This way the lifecycle of your service is managed by the
container.

2.2.8.2. Inversion of Control

Retrospection. Do you remember
your last project where you had some small components and several larger
services? How was this organized? Some services had their own
configuration files, others had static values in the source code. Most
components were probably tightly coupled to the main application, or you
called static methods whenever you needed a service in your java class.
Presumably you even copied the source code of an earlier project in
order to adapt the implementation to your needs. In short:

Each of your service had a proprietary configuration
mechanism.

The service lifecycles were managed inside of each service or
were arbitrary.

The dependencies between your services were
implementation-dependent and tightly coupled in your source
code.

New Approach. You have seen that
eXo uses the Inversion of Control (IoC) pattern
which means that the control of the services is given to an independent
outside entity, in this case a container. Now the
container takes care of everything:

The configuration is injected by external
configuration files.

The lifecycle is managed from outside,
because the constructors are called by the container. You can
achieve an even finer lifecycle management if you use the startable
interface.

The dependencies are injected by the
service instantiation process.

Dependency Injection. You also saw
two types of dependency injections:

2.2.8.3. More Containers

There are two more Containers called
RepositoryContainer and
WorkspaceContainer. These are specificities of
eXo JCR, for the sake of simplicity. You don't need them.

2.2.8.4. Single Implementation Services

In some case the developer of a service does not expect that there
will be several implementations for his service. Therefore he does not
create an interface. In this case the configuration looks like
this:

2.2.8.5. Configuration properties

Since kernel 2.0.7 and 2.1, it is possible to use system
properties in literal values of component configuration meta data. Thus
it is possible to resolve properties at runtime instead of providing a
value at packaging time.

2.2.8.6. Configuration Logging

In case you need to solve problems with your service
configuration, you have to know from which JAR/WAR causes your troubles.
Add the JVM system property
org.exoplatform.container.configuration.debug to
your eXo.bat or eXo.sh file (exo-tomcat/bin/).

set EXO_CONFIG_OPTS="-Dorg.exoplatform.container.configuration.debug"

If this property is set the container configuration manager
reports during startup the configuration retrieval process to the
standard output (System.out).

If you wish to see a examples of service configurations you should
study the Core. Where you find descriptions of
some eXo's core services. Finally you might wish to read more about PicoContainer.

2.3. Service Configuration in Detail

This section shows you how to set up a sample service with some
configurations and how to access the configuration parameters. The
later sections describe all details of the configuration file (parameters,
object-params, plugins, imports, and more). It also shows how to access the
configuration values. You may consider this document as a reference, but you can also use this document as a
tutorial and read it from the beginning
to the end.

2.3.1. Requirements

You should have read and understood Service Configuration
for Beginners. Obviously you should know java and xml. We are
working with examples that are created for teaching reasons only and you
will see extracts from the eXo Products default installation. When reading
this article, you do not forget that the terms service and component are
interchangeable in eXo Products.

2.3.2. Sample Service

2.3.2.1. Java Class

Imagine that you are working for a publishing company called "La
Verdad" that is going to use eXo platform. Your boss asks you be able to
calculate the number of sentences of an article.

You remember in eXo product everything is a service so you decide to create a simple class.
In the future, you want to be able to plug different implementations of
your service, so that you should define an interface that defines your service.

You should already have prepared your working environment, where
you have a base folder (let's call it our service base folder). If you
wish to try out this example create this class in the
com/laverdad/services/ArticleStatsService subfolder.

2.3.2.2. First configuration file

When creating a service, you also should declare its existence to
the Container, therefore you create a
first simple configuration file. Copy the following code to a file
called "configuration.xml" and place this file in a /conf subdirectory
of your service base folder. As you already know the container looks for
a "/conf/configuration.xml" file in each jar-file.

Note

You are correctly using the namespace of the configuration
schema ( http://www.exoplatform.org/xml/ns/kernel_1_2.xsd).
Most of the configuration schema is explained in this article,
therefore you do not need to open and understand the schema. For
backward compatibility it is not necessary to declare the
schema.

When eXo kernel reads a configuration, it loads the file from
the kernel jar using the classloader and does not use an internet
connection to resolve the file.

2.3.2.3. Init Parameters

You see your service has a configuration file, but you wonder how
the file can gain access to its configuration. Imagine that you
are asked to implement two different calculation methods: fast and
exact.

You create one init parameter containing the calculation methods.
For the exact method, you wish to configure more details for the
service. Let's enhance the word service configuration file:

Note

When configuring your service, you are totally free. You can provide as many value-param, property-param, and properties as you wish, and you can give them any
names or values. You only must respect the xml structure.

Now let's see how our service can read this configuration. The
implementation of the calcSentences() method serves just as a simple
example. It's up to your imagination to implement the exact
method.

If you test this sample in standalone mode, you need to put all
jars of eXo Kernel in your buildpath, furthermore picoContainer is
needed.

2.3.3. Parameters

2.3.3.1. Value-Param

There is an value-param example:

<component>
<key>org.exoplatform.portal.config.UserACL</key>
<type>org.exoplatform.portal.config.UserACL</type>
<init-params>
...
<value-param>
<name>access.control.workspace</name>
<description>groups with memberships that have the right to access the User Control Workspace</description>
<value>*:/platform/administrators,*:/organization/management/executive-board</value>
</value-param>
...
</component>

You see here an object-param is
being used to pass the parameters inside an object (actually a java
bean). It consists of a name, a
description and exactly one object. The object defines the type and a number of fields.

The passed object is LDAPConnectionConfig which is a classic
java bean. It contains all fields and
also the appropriate getters and setters (not listed here). You also can
provide default values. The container creates a new instance of your
bean and calls all setters whose values are configured in the
configuration file.

2.3.3.4. Collection

You also can use java collections to configure your service. In
order to see an example, let's open the
database-organization-configuration.xml file. This file defines a
default user organization (users, groups, memberships/roles) of your
portal. They use component-plugins which are explained later. You wil
see that object-param is used again.

There are two collections: The first collection is an ArrayList. This ArrayList contains only one
value, but there could be more. The only value is an object which
defines the field of the NewUserConfig$JoinGroup bean.

You see the values of the HashSet are set one by one by the
container, and it's the responsibility of the bean to add these values
to its HashSet.

The JoinGroup object is just an inner class and implements a bean
of its own. It can be accessed like any other inner class using
NewUserConfig.JoinGroup.

2.3.4. External Plugin

The External Plugin allows you to add configuration on the
fly.

As you have carefully read Service Configuration
for Beginners you know that normally newer configurations always replaces previous configurations. An external
plugin allows you to add configuration
without replacing previous configurations.

That can be interesting if you adapt a service configuration for
your project-specific needs (country, language, branch, project,
etc.).

Let's have a look at the configuration of the TaxonomyPlugin of the
CategoriesService:

The <target-component>
defines the service for which the plugin is defined. The configuration is
injected by the container using a method that is defined in <set-method>. The method has exactly one
argument of the type
org.exoplatform.services.cms.categories.impl.TaxonomyPlugin:

The content of <init-params>
corresponds to the structure of the TaxonomyPlugin object.

Note

You can configure the component CategoriesService using the
addTaxonomyPlugin as often as you wish, you can also call
addTaxonomyPlugin in different configuration files. The method
addTaxonomyPlugin is then called several times, everything else depends
on the implementation of the method.

2.3.5. Import

The import tag allows to import other configuration files using URLs
that are configuration manager specific, for more details about what are
the supported URLs please refer to the next section about the
configuration manager.

2.3.6. System properties

Since kernel 2.0.7 and 2.1, it is possible to use system properties
in literal values of component configuration meta data. This makes it
possible to resolve properties at runtime instead of providing a value at
packaging time.

See below an example of a configuration file based on system
properties:

As these are system properties you use the -D command: java -DconnectionUrl=jdbc:hsqldb:file:../temp/data/exodb
-DdriverClass=org.hsqldb.jdbcDriver Or better use the
parameters of eXo.bat / eXo.sh when you start GateIn: set
EXO_OPTS="-DconnectionUrl=jdbc:hsqldb:file:../temp/data/exodb
-DdriverClass=org.hsqldb.jdbcDriver"

2.3.7. Understanding the prefixes supported by the configuration
manager

The configuration manager allows you to find files using URL with
special prefixes that we describe in details below.

war: try to find the file using
the Servlet Context of your
portal.war or any web applications defined as
PortalContainerConfigOwner, so for example in
case of the portal.war if the URL is
war:/conf/common/portlet-container-configuration.xml
it will try to get the file from
portal.war/WEB-INF/conf/common/portlet-container-configuration.xml.

jar or classpath: you can use this prefix to find a
file that is accessible using the ClassLoader.
For example jar:/conf/my-file.xml will be
understood as try to find conf/my-file.xml from
the ClassLoader.

file: this prefix will indicate
the configuration manager that it needs to interprete the URL as an
absolute path. For example
file:///path/to/my/file.xml will be understood as
an obsolute path.

Without prefixes: it will be understood as
a relative path from the parent directory of the
last processed configuration file. For example, if the configuration
manager is processing the file corresonding to the URL
file:///path/to/my/configuration.xml and in this
file you import dir/to/foo.xml, the configuration
manager will try to get the file from
file:///path/to/my/dir/to/foo.xml. Please note
that it works also for other perfixes. In case you use the
configuration manager in a component to get a file like the example
below, it will depend on the mode and will be relative to the
following directories:

In standalone mode: it will be a relative path to where it
can find the file exo-configuration.xml
knowing that the file is first checked in the user
directory, if it cannot be found there, it will check
in the exo configuration directory and if it
still cannot be found it will try to find
conf/exo-configuration.xml in the
ClassLoader.

In portal mode: it will be a relative path to the
exo configuration directory in case of the
RootContainer (assuming that a file configuration.xml exists there
otherwise it would be hard to know) and from
${exo-configuration-directory}/portal/${portal-container-name}
in case of the PortalContainer (assuming that a file
configuration.xml exists there otherwise it would be hard to
know).

Note

For more details about the exo configuration directory please
refer to the Configuration
Retrieval section.

2.4. Container Configuration

GateIn uses PicoContainer, which implements the Inversion of
Control (IoC) design pattern. All eXo containers inherit from a
PicoContainer. There are mainly two eXo containers used, each of them can
provide one or several services. Each container service is delivered in a
JAR file. This JAR file may contain a default configuration. The use of
default configurations is recommended and most services provide it.

When a Pico Container searches for services and its configurations,
each configurable service may be reconfigured to override default values or
set additional parameters. If the service is configured in two or more
places the configuration override mechanism will be used.

The variables that are supported, are System properties and
variables that are specific to your portal container, see next sections
for more details.

2.4.2. Understanding how configuration files are loaded

GateIn uses PicoContainer, which implements the Inversion of
Control (IoC) design pattern. All eXo containers inherit from a
PicoContainer. There are mainly two eXo containers used, each of them can
provide one or several services. Each container service is delivered in a
JAR file. This JAR file may contain a default configuration. The use of
default configurations is recommended and most of services provide
it.

When a Pico Container searches for services and its configurations,
each configurable service may be reconfigured to override default values
or set additional parameters. If the service is configured in two or more
places, the configuration override mechanism will be used.

2.4.2.1. Configuration Retrieval

The container performs the following steps to make eXo Container
configuration retrieval, depending on the container type.

2.4.2.1.1. Configuration retrieval order for the
PortalContainer

The container is initialized by looking into different
locations. This container is used by portal applications.
Configurations are overloaded in the following lookup sequence:

Configuration from added URL
containerConf will override only services
configured in the file

if configuration URL not initialized at all, it will be
found at $AS_HOME/exo-configuration.xml.
If $AS_HOME/exo-configuration.xml doesn't
exist the container will try find it at
$AS_HOME/exo-conf/exo-configuration.xml
location and if it's still not found and the
StandaloneContainer instance obtained with the
dedicated configuration ClassLoader the
container will try to retrieve the resource
conf/exo-configuration.xml within the
given ClassLoader.

2.4.2.1.3. General notes about the configuration retrieval

Note

$AS_HOME - application server home
directory, or user.dir JVM system property
value in case of Java Standalone application. The application server
home is:

For Jonas, the value of the variable
${jonas.base}.

For Jetty, the value of the variable
${jetty.home}.

For Websphere, the value of the variable
${was.install.root}.

For Weblogic, the value of the variable
${wls.home}.

For Glassfish, the value of the variable
${com.sun.aas.instanceRoot}.

For Tomcat, the value of the variable
${catalina.home}.

For JBoss AS, the value of the variable
${jboss.server.config.url} if the exo-conf
directory can be found there otherwise it will be the value of
the variable ${jboss.home.dir}.

Note

$PORTAL_NAME - portal web application
name.

Note

External configuration location can be overridden with System
property exo.conf.dir. If the property exists,
its value will be used as path to eXo configuration directory, i.e.
to $AS_HOME/exo-conf alternative. E.g. put
property in command line java
-Dexo.conf.dir=/path/to/exo/conf. In this
particular use case, you do not need to use any prefix to import
other files. For instance, if your configuration file is
$AS_HOME/exo-conf/portal/PORTAL_NAME/configuration.xml
and you want to import the configuration file
$AS_HOME/exo-conf/portal/PORTAL_NAME/mySubConfDir/myConfig.xml,
you can do it by adding
<import>mySubConfDir/myConfig.xml</import>
to your configuration file.

Note

The name of the configuration folder that is by default
"exo-conf", can be changed thanks to the System
property exo.conf.dir.name.

Note

The search looks for a configuration file in each JAR/WAR
available from the classpath using the current thread context
classloader. During the search these configurations are added to a
set. If the service was configured previously and the current JAR
contains a new configuration of that service the latest (from the
current JAR/WAR) will replace the previous one. The last one will be
applied to the service during the services start phase.

Warning

Take care to have no dependencies between configurations from
JAR files (/conf/portal/configuration.xml and
/conf/configuration.xml) since we have no way
to know in advance the loading order of those configurations. In
other words, if you want to overload some configuration located in
the file /conf/portal/configuration.xml of a
given JAR file, you must not do it from the file
/conf/portal/configuration.xml of another JAR
file but from another configuration file loaded after configurations
from JAR files
/conf/portal/configuration.xml.

After the processing of all configurations available in system,
the container will initialize it and start each service in order of
the dependency injection (DI).

The user/developer should be careful when configuring the same
service in different configuration files. It's recommended to
configure a service in its own JAR only. Or, in case of a portal
configuration, strictly reconfigure the services in portal WAR files
or in an external configuration.

There are services that can be (or should be) configured more
than one time. This depends on business logic of the service. A
service may initialize the same resource (shared with other services)
or may add a particular object to a set of objects (shared with other
services too). In the first case, it's critical who will be the last,
i.e. whose configuration will be used. In the second case, it's no
matter who is the first and who is the last (if the parameter objects
are independent).

2.4.2.1.4. Getting the effective configuration at Runtime

The effective configuration of the StandaloneContainer,
RootContainer and/or PortalContainer can be known thanks to the method
getConfigurationXML() that is exposed through JMX
at the container's level. This method will give you the effective
configuration in XML format that has been really interpreted by the
kernel. This could be helpful to understand how a given component or
plugin has been initialized.

2.4.2.2. Advanced concepts for the
PortalContainers

Since eXo JCR 1.12, we added a set of new features that have been
designed to extend portal applications such as GateIn.

2.4.2.2.1. Add new configuration files from a WAR file

A ServletContextListener called
org.exoplatform.container.web.PortalContainerConfigOwner
has been added in order to notify the application that a given web
application provides some configuration to the portal container, and
this configuration file is the file
WEB-INF/conf/configuration.xml available in the
web application itself.

If your war file contains some configuration to add to the
PortalContainer simply add the following lines in your
web.xml file.

2.4.2.2.2. Creating your PortalContainers from a WAR
file

A ServletContextListener called
org.exoplatform.container.web.PortalContainerCreator
has been added in order to create the current portal containers that
have been registered. We assume that all the web applications have
already been loaded before calling
PortalContainerCreator.contextInitialized[.]

Note

In GateIn, the PortalContainerCreator is
already managed by the file
starter.war/ear.

2.4.2.2.3. Defining a PortalContainer with its
dependencies and its settings

Now we can define precisely a portal container and its
dependencies and settings thanks to the
PortalContainerDefinition that currently contains the
name of the portal container, the name of the rest context, the name
of the realm, the web application dependencies ordered by loading
priority (i.e. the first dependency must be loaded at first and so
on..) and the settings.

To be able to define a PortalContainerDefinition,
we need to ensure first of all that a
PortalContainerConfig has been defined at the
RootContainer level, see an example below:

Indicates whether the unregistered webapps have to be
ignored. If a webapp has not been registered as a dependency
of any portal container, the application will use the value of
this parameter to know what to do:

If it is set to false, this
webapp will be considered by default as a dependency of
all the portal containers.

If it is set to true, this
webapp won't be considered by default as a dependency of
any portal container, it will be simply ignored.

This field is optional and by default this
parameter is set to false.

default.portal.definition

The definition of the default portal container. This
field is optional. The expected type is
org.exoplatform.container.definition.PortalContainerDefinition
that is described below. Allow the parameters defined in this
default PortalContainerDefinition will be the
default values.

Note

All the value of the parameters marked with a (*) can be
defined thanks to System properties like any values in configuration
files but also thanks to variables loaded by the
PropertyConfigurator. For example in GateIn by
default, it would be all the variables defined in the file
configuration.properties.

A new PortalContainerDefinition can be defined at
the RootContainer level thanks to an external plugin,
see an example below:

Table 2.2. Descriptions of the fields of a
PortalContainerDefinition when it is used to define a
new portal container

name (*)

The name of the portal container. This field is
mandatory .

restContextName (*)

The name of the context name of the rest web
application. This field is optional. The default value will be
defined at the PortalContainerConfig
level.

realmName (*)

The name of the realm. This field is optional. The
default value will be defined at the
PortalContainerConfig level.

dependencies

All the dependencies of the portal container ordered by
loading priority. This field is optional. The default value
will be defined at the PortalContainerConfig
level. The dependencies are in fact the list of the context
names of the web applications from which the portal container
depends. This field is optional. The dependency order is
really crucial since it will be interpreted the same way by
several components of the platform. All those components, will
consider the 1st element in the list less important than the
second element and so on. It is currently used
to:

Know the loading order of all the
dependencies.

If we have several
PortalContainerConfigOwner

The ServletContext of all the
PortalContainerConfigOwner will be
unified, if we use the unified
ServletContext
(PortalContainer.getPortalContext())
to get a resource, it will try to get the resource
in the ServletContext of the most
important PortalContainerConfigOwner
(i.e. last in the dependency list) and if it cans
find it, it will try with the second most important
PortalContainerConfigOwner and so
on.

The ClassLoader of all the
PortalContainerConfigOwner will be
unified, if we use the unified
ClassLoader
(PortalContainer.getPortalClassLoader())
to get a resource, it will try to get the resource
in the ClassLoader of the most
important PortalContainerConfigOwner
(i.e. last in the dependency list) and if it can
find it, it will try with the second most important
PortalContainerConfigOwner and so
on.

settings

A java.util.Map of internal parameters
that we would like to tie the portal container. Those
parameters could have any type of value. This field is
optional. If some internal settings are defined at the
PortalContainerConfig level, the two maps of
settings will be merged. If a setting with the same name is
defined in both maps, it will keep the value defined at the
PortalContainerDefinition level.

externalSettingsPath

The path of the external properties file to load as
default settings to the portal container. This field is
optional. If some external settings are defined at the
PortalContainerConfig level, the two maps of
settings will be merged. If a setting with the same name is
defined in both maps, it will keep the value defined at the
PortalContainerDefinition level. The external
properties files can be either of type "properties" or of type
"xml". The path will be interpreted as follows:

The path doesn't contain any prefix of type
"classpath:", "jar:" or "file:", we assume that the file
could be externalized so we apply the following
rules:

A file exists at
${exo-conf-dir}/portal/${portalContainerName}/${externalSettingsPath},
we will load this file.

No file exists at the previous path, we then
assume that the path cans be interpreted by the
ConfigurationManager.

The path contains a prefix, we then assume that
the path cans be interpreted by the
ConfigurationManager.

Table 2.3. Descriptions of the fields of a
PortalContainerDefinition when it is used to define
the default portal container

name (*)

The name of the portal container. This field is
optional. The default portal name will be:

If this field is not empty, then the default value
will be the value of this field.

If this field is empty and the value of the
parameter default.portal.container
is not empty, then the default value will be the value
of the parameter.

If this field and the parameter
default.portal.container are both
empty, the default value will be
"portal".

restContextName (*)

The name of the context name of the rest web
application. This field is optional. The default value wil
be:

If this field is not empty, then the default value
will be the value of this field.

If this field is empty and the value of the
parameter default.rest.context is
not empty, then the default value will be the value of
the parameter.

If this field and the parameter
default.rest.context are both
empty, the default value will be
"rest".

realmName (*)

The name of the realm. This field is optional. The
default value wil be:

If this field is not empty, then the default value
will be the value of this field.

If this field is empty and the value of the
parameter default.realm.name is not
empty, then the default value will be the value of the
parameter.

If this field and the parameter
default.realm.name are both empty,
the default value will be
"exo-domain".

dependencies

All the dependencies of the portal container ordered by
loading priority. This field is optional. If this field has a
non empty value, it will be the default list of
dependencies.

settings

A java.util.Map of internal parameters
that we would like to tie the default portal container. Those
parameters could have any type of value. This field is
optional.

externalSettingsPath

The path of the external properties file to load as
default settings to the default portal container. This field
is optional. The external properties files can be either of
type "properties" or of type "xml". The path will be
interpreted as follows:

The path doesn't contain any prefix of type
"classpath:", "jar:" or "file:", we assume that the file
could be externalized so we apply the following
rules:

A file exists at
${exo-conf-dir}/portal/${externalSettingsPath},
we will load this file.

No file exists at the previous path, we then
assume that the path cans be interpreted by the
ConfigurationManager.

The path contains a prefix, we then assume that
the path cans be interpreted by the
ConfigurationManager.

Note

All the value of the parameters marked with a (*) can be
defined thanks to System properties like any values in configuration
files but also thanks to variables loaded by the
PropertyConfigurator. For example in GateIn by
default, it would be all the variables defined in the file
configuration.properties.

Internal and external settings are both optional, but if we give
a non empty value for both the application will merge the settings. If
the same setting name exists in both settings, we apply the following
rules:

The value of the external setting is
null, we ignore the value.

The value of the external setting is not
null and the value of the internal setting is
null, the final value will be the external
setting value that is of type String.

Both values are not null, we will have to
convert the external setting value into the target type which is
the type of the internal setting value, thanks to the static
method valueOf(String), the following
sub-rules are then applied:

The method cannot be found, the final value will be the
external setting value that is of type
String.

The method can be found and the external setting value
is an empty String, we ignore the external
setting value.

The method can be found and the external setting value
is not an empty String but the method call
fails, we ignore the external setting value.

The method can be found and the external setting value
is not an empty String and the method call
succeeds, the final value will be the external setting value
that is of type of the internal setting value.

2.4.2.2.4. PortalContainer settings

We can inject the value of the portal container settings into
the portal container configuration files thanks to the variables which
name start with "portal.container.", so to get
the value of a setting called "foo", just use the
following syntax ${portal.container.foo}. You can
also use internal variables, such as:

Table 2.4. Definition of the internal variables

portal.container.name

Gives the name of the current portal container.

portal.container.rest

Gives the context name of the rest web application of
the current portal container.

In the properties file corresponding to the external settings,
you can reuse variables previously defined (in the external settings
or in the internal settings) to create a new variable. In this case,
the prefix "portal.container." is not needed, see
an example below:

my-var1=value 1
my-var2=value 2
complex-value=${my-var1}-${my-var2}

In the external and internal settings, you can also use create
variables based on value of System paramaters. The System parameters
can either be defined at launch time or thanks to the
PropertyConfigurator (see next section for more
details). See an example below:

temp-dir=${java.io.tmpdir}${file.separator}my-temp

However, for the internal settings, you can use System
parameters only to define settings of type
java.lang.String.

It cans be also very usefull to define a generic variable in the
settings of the default portal container, the value of this variable
will change according to the current portal container. See below an
example:

my-generic-var=value of the portal container "${name}"

If this variable is defined at the default portal container
level, the value of this variable for a portal container called
"foo" will be value of the portal
container "foo".

It is possible to use component-plugin elements
in order to dynamically change a PortalContainerDefinition. In the
example below, we add the dependency foo to the default
portal container and to the portal containers called
foo1 and foo2:

<external-component-plugins>
<!-- The full qualified name of the PortalContainerConfig -->
<target-component>org.exoplatform.container.definition.PortalContainerConfig</target-component>
<component-plugin>
<!-- The name of the plugin -->
<name>Change PortalContainer Definitions</name>
<!-- The name of the method to call on the PortalContainerConfig in order to register the changes on the PortalContainerDefinitions -->
<set-method>registerChangePlugin</set-method>
<!-- The full qualified name of the PortalContainerDefinitionChangePlugin -->
<type>org.exoplatform.container.definition.PortalContainerDefinitionChangePlugin</type>
<init-params>
<value-param>
<name>apply.default</name>
<value>true</value>
</value-param>
<values-param>
<name>apply.specific</name>
<value>foo1</value>
<value>foo2</value>
</values-param>
<object-param>
<name>change</name>
<object type="org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependencies">
<!-- The list of name of the dependencies to add -->
<field name="dependencies">
<collection type="java.util.ArrayList">
<value>
<string>foo</string>
</value>
</collection>
</field>
</object>
</object-param>
</init-params>
</component-plugin>
</external-component-plugins>

Table 2.5. Descriptions of the fields of a
PortalContainerDefinitionChangePlugin

apply.all (*)

Indicates whether the changes have to be applied to all
the portal containers or not. The default value of this field
is false. This field is a
ValueParam and is not mandatory.

apply.default (*)

Indicates whether the changes have to be applied to the
default portal container or not. The default value of this
field is false. This field is a
ValueParam and is not mandatory.

apply.specific (*)

A set of specific portal container names to which we
want to apply the changes. This field is a
ValuesParam and is not mandatory.

Rest of the expected parameters

The rest of the expected paramaters are
ObjectParam of type
PortalContainerDefinitionChange. Those
parameters are in fact the list of changes that we want to
apply to one or several portal containers. If the list of
changes is empty, the component plugin will be ignored. The
supported implementations of PortalContainerDefinitionChange
are described later in this section.

Note

All the value of the parameters marked with a (*) can be
defined thanks to System properties like any values in configuration
files but also thanks to variables loaded by the
PropertyConfigurator. For example in GateIn by
default, it would be all the variables defined in the file
configuration.properties.

To identify the portal containers to which the changes have to
be applied, we use the follwing algorithm:

The parameter apply.all has been set to
true. The corresponding changes will be applied to
all the portal containers. The other parameters will be
ignored.

The parameter apply.default has been set to
true and the parameter
apply.specific is null. The
corresponding changes will be applied to the default portal
container only.

The parameter apply.default has been set to
true and the parameter
apply.specific is not null. The
corresponding changes will be applied to the default portal
container and the given list of specific portal containers.

The parameter apply.default has been set to
false or has not been set and the parameter
apply.specific is null. The
corresponding changes will be applied to the default portal
container only.

The parameter apply.default has been set to
false or has not been set and the parameter
apply.specific is not null. The
corresponding changes will be applied to the given list of
specific portal containers.

2.4.2.2.5.1. The existing implementations of
PortalContainerDefinitionChange

The modifications that can be applied to a
PortalContainerDefinition must be a class of type
PortalContainerDefinitionChange. The product proposes
out of the box some implementations that we describe in the next sub
sections.

2.4.2.2.5.1.1. AddDependencies

This modification adds a list of dependencies at the end of
the list of dependencies defined into the
PortalContainerDefinition. The full qualified name
is
org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependencies.

Table 2.6. Descriptions of the fields of an
AddDependencies

dependencies

A list of String corresponding
to the list of name of the dependencies to add. If the
value of this field is empty, the change will be
ignored.

See an example below, that will add foo at
the end of the dependency list of the default portal
container:

<external-component-plugins>
<!-- The full qualified name of the PortalContainerConfig -->
<target-component>org.exoplatform.container.definition.PortalContainerConfig</target-component>
<component-plugin>
<!-- The name of the plugin -->
<name>Change PortalContainer Definitions</name>
<!-- The name of the method to call on the PortalContainerConfig in order to register the changes on the PortalContainerDefinitions -->
<set-method>registerChangePlugin</set-method>
<!-- The full qualified name of the PortalContainerDefinitionChangePlugin -->
<type>org.exoplatform.container.definition.PortalContainerDefinitionChangePlugin</type>
<init-params>
<value-param>
<name>apply.default</name>
<value>true</value>
</value-param>
<object-param>
<name>change</name>
<object type="org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependencies">
<!-- The list of name of the dependencies to add -->
<field name="dependencies">
<collection type="java.util.ArrayList">
<value>
<string>foo</string>
</value>
</collection>
</field>
</object>
</object-param>
</init-params>
</component-plugin>
</external-component-plugins>

2.4.2.2.5.1.2. AddDependenciesBefore

This modification adds a list of dependencies before a given
target dependency defined into the list of dependencies of the
PortalContainerDefinition. The full qualified name
is
org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependenciesBefore.

Table 2.7. Descriptions of the fields of an
AddDependenciesBefore

dependencies

A list of String corresponding
to the list of name of the dependencies to add. If the
value of this field is empty, the change will be
ignored.

target

The name of the dependency before which we would
like to add the new dependencies. If this field is
null or the target dependency cannot be
found in the list of dependencies defined into the
PortalContainerDefinition, the new
dependencies will be added in first position to the
list.

See an example below, that will add foo
before foo2 in the dependency list of the default
portal container:

<external-component-plugins>
<!-- The full qualified name of the PortalContainerConfig -->
<target-component>org.exoplatform.container.definition.PortalContainerConfig</target-component>
<component-plugin>
<!-- The name of the plugin -->
<name>Change PortalContainer Definitions</name>
<!-- The name of the method to call on the PortalContainerConfig in order to register the changes on the PortalContainerDefinitions -->
<set-method>registerChangePlugin</set-method>
<!-- The full qualified name of the PortalContainerDefinitionChangePlugin -->
<type>org.exoplatform.container.definition.PortalContainerDefinitionChangePlugin</type>
<init-params>
<value-param>
<name>apply.default</name>
<value>true</value>
</value-param>
<object-param>
<name>change</name>
<object type="org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependenciesBefore">
<!-- The list of name of the dependencies to add -->
<field name="dependencies">
<collection type="java.util.ArrayList">
<value>
<string>foo</string>
</value>
</collection>
</field>
<!-- The name of the target dependency -->
<field name="target">
<string>foo2</string>
</field>
</object>
</object-param>
</init-params>
</component-plugin>
</external-component-plugins>

2.4.2.2.5.1.3. AddDependenciesAfter

This modification adds a list of dependencies after a given
target dependency defined into the list of dependencies of the
PortalContainerDefinition. The full qualified name
is
org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependenciesAfter.

Table 2.8. Descriptions of the fields of an
AddDependenciesAfter

dependencies

A list of String corresponding
to the list of name of the dependencies to add. If the
value of this field is empty, the change will be
ignored.

target

The name of the dependency after which we would
like to add the new dependencies. If this field is
null or the target dependency cannot be
found in the list of dependencies defined into the
PortalContainerDefinition, the new
dependencies will be added in last position to the
list.

See an example below, that will add foo after
foo2 in the dependency list of the default portal
container:

<external-component-plugins>
<!-- The full qualified name of the PortalContainerConfig -->
<target-component>org.exoplatform.container.definition.PortalContainerConfig</target-component>
<component-plugin>
<!-- The name of the plugin -->
<name>Change PortalContainer Definitions</name>
<!-- The name of the method to call on the PortalContainerConfig in order to register the changes on the PortalContainerDefinitions -->
<set-method>registerChangePlugin</set-method>
<!-- The full qualified name of the PortalContainerDefinitionChangePlugin -->
<type>org.exoplatform.container.definition.PortalContainerDefinitionChangePlugin</type>
<init-params>
<value-param>
<name>apply.default</name>
<value>true</value>
</value-param>
<object-param>
<name>change</name>
<object type="org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependenciesAfter">
<!-- The list of name of the dependencies to add -->
<field name="dependencies">
<collection type="java.util.ArrayList">
<value>
<string>foo</string>
</value>
</collection>
</field>
<!-- The name of the target dependency -->
<field name="target">
<string>foo2</string>
</field>
</object>
</object-param>
</init-params>
</component-plugin>
</external-component-plugins>

2.4.2.2.5.1.4. AddSettings

This modification adds new settings to a
PortalContainerDefinition. The full qualified name
is
org.exoplatform.container.definition.PortalContainerDefinitionChange$AddSettings.

Table 2.9. Descriptions of the fields of an
AddSettings

settings

A map of <String,
Object> corresponding to the settings to
add. If the value of this field is empty, the change will
be ignored.

See an example below, that will add the settings
string and stringX to the settings
of the default portal container:

<external-component-plugins>
<!-- The full qualified name of the PortalContainerConfig -->
<target-component>org.exoplatform.container.definition.PortalContainerConfig</target-component>
<component-plugin>
<!-- The name of the plugin -->
<name>Change PortalContainer Definitions</name>
<!-- The name of the method to call on the PortalContainerConfig in order to register the changes on the PortalContainerDefinitions -->
<set-method>registerChangePlugin</set-method>
<!-- The full qualified name of the PortalContainerDefinitionChangePlugin -->
<type>org.exoplatform.container.definition.PortalContainerDefinitionChangePlugin</type>
<init-params>
<value-param>
<name>apply.default</name>
<value>true</value>
</value-param>
<object-param>
<name>change</name>
<object type="org.exoplatform.container.definition.PortalContainerDefinitionChange$AddSettings">
<!-- The settings to add to the to the portal containers -->
<field name="settings">
<map type="java.util.HashMap">
<entry>
<key>
<string>string</string>
</key>
<value>
<string>value1</string>
</value>
</entry>
<entry>
<key>
<string>stringX</string>
</key>
<value>
<string>value1</string>
</value>
</entry>
</map>
</field>
</object>
</object-param>
</init-params>
</component-plugin>
</external-component-plugins>

2.4.2.2.6. Disable dynamically a portal container

It is possible to use component-plugin elements
in order to dynamically disable one or several portal containers. In
the example below, we disable the portal container named
foo:

<external-component-plugins>
<!-- The full qualified name of the PortalContainerConfig -->
<target-component>org.exoplatform.container.definition.PortalContainerConfig</target-component>
<component-plugin>
<!-- The name of the plugin -->
<name>Disable a PortalContainer</name>
<!-- The name of the method to call on the PortalContainerConfig in order to register the changes on the PortalContainerDefinitions -->
<set-method>registerDisablePlugin</set-method>
<!-- The full qualified name of the PortalContainerDefinitionDisablePlugin -->
<type>org.exoplatform.container.definition.PortalContainerDefinitionDisablePlugin</type>
<init-params>
<!-- The list of the name of the portal containers to disable -->
<values-param>
<name>names</name>
<value>foo</value>
</values-param>
</init-params>
</component-plugin>
</external-component-plugins>

Table 2.10. Descriptions of the fields of a
PortalContainerDefinitionDisablePlugin

names (*)

The list of the name of the portal containers to
disable.

Note

All the value of the parameters marked with a (*) can be
defined thanks to System properties like any values in configuration
files but also thanks to variables loaded by the
PropertyConfigurator. For example in GateIn by
default, it would be all the variables defined in the file
configuration.properties.

To prevent any accesses to a web application corresponding to
PortalContainer that has been disabled, you need to
make sure that the following Http Filter (or a sub class of it) has
been added to your web.xml in first position as below:

2.4.3.2. Properties URL init param

The properties URL init param allow to load an external file by
specifying its URL. Both property and XML format are supported, see the
javadoc of the java.util.Properties
class for more information. When a property file is loaded the various
property declarations are loaded in the order in which the properties
are declared sequentially in the file.

In the properties file corresponding to the external properties,
you can reuse variables before defining to create a new variable. In
this case, the prefix "portal.container." is not
needed, see an example below:

my-var1=value 1
my-var2=value 2
complex-value=${my-var1}-${my-var2}

2.4.3.3. System Property configuration of the properties URL

It is possible to replace the properties URL init param by a
system property that overwrites it. The name of that property is
exo.properties.url.

2.4.4. Variable Syntaxes

All the variables that we described in the previous sections can be
defined thanks to 2 possible syntaxes which are
${variable-name} or
${variable-name:default-value}. The first syntax
doesn't define any default value so if the variable has not be set the
value will be ${variable-name} to indicate that it
could not be resolved. The second syntax allows you to define the default
value after the semi colon so if the variable has not be set the value
will be the given default value.

2.4.5. Runtime configuration profiles

The kernel configuration is able to handle configuration profiles at
runtime (as opposed to packaging time).

2.4.5.1. Profiles activation

An active profile list is obtained during the boot of the root
container and is composed of the system property
exo.profiles sliced according the "," delimiter and
also a server specific profile value (tomcat for tomcat, jboss for
jboss, etc...).

2.4.5.2. Profiles configuration

Profiles are configured in the configuration files of the eXo
kernel.

2.4.5.2.1. Profiles definition

Profile activation occurs at XML to configuration object
unmarshalling time. It is based on an "profile" attribute that is
present on some of the XML element of the configuration files. To
enable this, the kernel configuration schema has been upgraded to
kernel_1_1.xsd. The configuration is based on the following
rules:

Any kernel element with the no profiles
attribute will create a configuration object

Any kernel element having a profiles
attribute containing at least one of the active profiles will
create a configuration object

Any kernel element having a profiles
attribute matching none of the active profile will not create a
configuration object

Resolution of duplicates (such as two components with same
type) is left up to the kernel

2.4.5.2.2. Profiles capable configuration elements

A configuration element is profiles capable
when it carries a profiles element.

2.4.5.2.2.1. Component element

The component element declares a component when activated. It
will shadow any element with the same key declared before in the
same configuration file:

The container passed is the container to which the component is
related. This contract is often used to setup a thread local based
context that will be demarcated by a request.

For instance in the GateIn portal context, a component request
life cycle is triggered for user requests. Another example is the
initial data import in GateIn that demarcates using callbacks made to
that interface.

2.4.6.2. Request life cycle

The RequestLifeCycle class has several statics
methods that are used to schedule the component request life cycle of
components. Its main responsability is to perform scheduling while
respecting the constraint to execute the request life cycle of a
component only once even if it can be scheduled several times.

2.4.6.2.1. Scheduling a component request life cycle

2.4.6.2.2. Scheduling a container request life cycle

Scheduling a container triggers the component request life cyle
of all the components that implement the interface
ComponentRequestLifeCycle. If one of the component has
already been scheduled before and then that component will not be
scheduled again. When the local value is true, then the looked
components will be those of the container, when it is false then the
scheduler will also look at the components in the ancestor
containers.

2.4.6.3. When request life cycle is triggered

2.4.6.3.1. Portal request life cycle

Each portal request triggers the life cycle of the associated
portal container.

2.4.6.3.2. JMX request Life Cycle

When a JMX bean is invoked, the request life cycle of the
container to which it belongs it scheduled. Indeed JMX is an entry
point of the system that may need component to have a request life
cycle triggered.

2.4.7. Thread Context Holder

2.4.7.1. Thread Context Holder contract

A thread context holder defines a component that holds variables
of type ThreadLocal whose value is required by the
component to work normally and cannot be recovered. This component is
mainly used when we want to do a task asynchronously, in that case to
ensure that the task will be executed in the same conditions as if it
would be executed synchronously we need to transfer the thread context
from the original thread to the executor thread.

public interface ThreadContextHolder
{
/**
* Gives the value corresponding to the context of the thread
* @return a new instance of {@link ThreadContext} if there are some
* valuable {@link ThreadLocal} variables to share otherwise <code>null</code>
* is expected
*/
ThreadContext getThreadContext();
}

Note

This interface must be used with caution, only the most
important components that have ThreadLocal
variables whose value cannot be recovered should implement this
interface.

2.4.7.2. Thread Context Handler

To be able to transfer the values of all the
ThreadLocal variables (provided thanks to a
ThreadContext instance) of all the registered components of type
ThreadContextHolder, you can simply use a thread
context handler as below:

////////////////////////////////////////////////////////
// Steps to be done in the context of the initial thread
////////////////////////////////////////////////////////
// Create a new instance of ThreadContextHandler for a given ExoContainer
ThreadContextHandler handler = new ThreadContextHandler(container);
// Stores into memory the current values of all the Thread Local variables
// of all the registered ThreadContextHolder of the eXo container.
handler.store();
...
////////////////////////////////////////////////////////
// Steps to be done in the context of the executor thread
////////////////////////////////////////////////////////
try {
// Pushes values stored into memory into all the Thread Local variables
// of all the registered ThreadContextHolder of the eXo Container
handler.push();
...
} finally {
// Restores all the Thread Local variables of all the registered ThreadContextHolder
// of the eXo Container
handler.restore();
}

2.5. Inversion Of Control

The services are not responsible for the instantiation of the
components on which they depend.

This architecture provides a loosely coupled design where the
implementation of dependant services can be transparently
exchanged.

This pattern has several names:

Hollywood principle: "don't call me, I will call you"

Inversion of Control

Dependency injection

2.5.1. How

Don't let the object create itself the instances of the object that
it references. This job is delegated to the container (assembler in the
picture).

2.5.2. Injection

There are two ways to inject a dependency:

Using a constructor:

public ServiceA(ServiceB serviceB)

Using setter methods:

public void setServiceB(ServiceB serviceB)

When a client service can not be stored in the container then the
service locator pattern is used:

2.5.3. Side effects

Ease Unit test (use of Mock objects)

Ease Maintainability

Ease Refactoring

Component reuse ( POJOs != EJBs)

2.6. Services Wiring

The container package is responsible of building a hierarchy of
containers. Each service will then be registered in one container or the
other according to the XML configuration file it is defined in. It is
important to understand that there can be several PortalContainer
instances that all are children of the RootContainer.

The behavior of the hierarchy is similar to a class loader one,
hence when you will lookup a service that depends on another one, the
container will look for it in the current container and if it cannot be
found, then it will look in the parent container. That way you can load
all the reusable business logic components in the same container (here the
RootContainer) and differentiate the service implementation from one
portal instance to the other by just loading different service
implementations in two sibling PortalContainers.

Therefore, if you look at the Portal Container as a service
repository for all the business logic in a portal instance, then you
understand why several PortalContainers allows you to manage several
portals (each one deployed as a single war) in the same server by just
changing XML configuration files.

The default configuration XML files are packaged in the service jar.
There are three configuration.xml files, one for each container type. In
that XML file, we define the list of services and their init parameters
that will be loaded in the corresponding container.

2.6.1. Portal Instance

As there can be several portal container instances per JVM. it is
important to be able to configure the loaded services per instance.
Therefore all the default configuration files located in the service impl
jar can be overridden from the portal war. For more information refer to
Service
Configuration for Beginners.

2.6.2. Introduction to the XML schema of the configuration.xml
file

After deploying you find the configuration.xml file in
webapps/portal/WEB-INF/conf Use component registration tags. Let's look at
the key tag that defines the interface and the type tag that defines the
implementation. Note that the key tag is not mandatory, but it improves
performance.

Register plugins that can act as listeners or external plugin to
bundle some plugin classes in other jar modules. The usual example is the
hibernate service to which we can add hbm mapping files even if those are
deployed in an other maven artifact.

In that sample we target the HibernateService and we will call its
addPlugin() method with an argument of the type AddHibernateMappingPlugin.
That object will first have been filled with the init parameters.

Therefore, it is possible to define services that will be able to
receive plugins without implementing any framework interface.

Another example of use is the case of listeners as in the following
code where a listener is added to the OrganisationService and will be
called each time a new user is created:

In the previous XML configuration, we refer the organization service
and we will call its method addListenerPlugin with an object of type
PortalUserEventListenerImpl. Each time a new user will be created (apart
the predefined ones in the list above) methods of the
PortalUserEventListenerImpl will be called by the service.

As you can see, there are several types of init parameters, from a
simple value param which binds a key with a value to a more complex object
mapping that fills a JavaBean with the info defined in the XML.

Many other examples exist such as for the Scheduler Service where
you can add a job with a simple XML configuration or the JCR Service where
you can add a NodeType from your own configuration.xml file.

2.6.3. Configuration retrieval and log of this retrieval

When the RootContainer is starting the configuration retrieval looks
for configuration files in each jar available from the classpath at jar
path /conf/portal/configuration.xml and from each war at path
/WEB-INF/conf/configuration.xml. These configurations are added to a set.
If a component was configured in a previous jar and the current jar
contains a new configuration of that component the latest (from the
current jar) will replace the previous configuration.

After the processing of all configurations available on the system
the container will initialize it and start each component in order of the
dependency injection (DI).

So, in general the user/developer should be careful when configuring
the same components in different configuration files. It's recommended to
configure service in its own jar only. Or, in case of a portal
configuration, strictly reconfigure the component in portal files.

But, there are components that can be (or should be) configured more
than one time. This depends on the business logic of the component. A
component may initialize the same resource (shared with other players) or
may add a particular object to a set of objects (shared with other players
too). In the first case it's critical who will be the last, i.e. whose
configuration will be used. In second case it doesn't matter who is the
first and who is the last (if the parameter objects are
independent).

In case of problems with configuration of component it's important
to know from which jar/war it comes. For that purpose user/developer can
set JVM system property org.exoplatform.container.configuration.debug, in
command line:

java -Dorg.exoplatform.container.configuration.debug ...

With that property container configuration manager will report
configuration adding process to the standard output (System.out).

2.7. Component Plugin Priority

Since kernel version 2.0.6 it is possible to setup order of loading
for ComponentPlugin. Use the ' priority'
tag to define plugin's load priority. By default all plugins get priority '0'; they will be loaded in the container's
natural way. If you want one plugin to be loaded later than the others then
just set priority for it higher than
zero.

2.8.1. What is the ListenerService ?

Inside eXo, an event mechanism allows to trigger and listen to
events under specific conditions. This mechanism is used in several places
in eXo such as login/logout time.

2.8.2. How does it work?

Listeners must be subclasses of
org.exoplatform.services.listener.Listener registered by the
ListenerService.

2.8.2.1. Registering a listener

To register a listener, you need to call the addListener()
method.

/**
* This method is used to register a {@link Listener} to the events of the same
* name. It is similar to addListener(listener.getName(), listener)
*
* @param listener the listener to notify any time an even of the same name is
* triggered
*/
public void addListener(Listener listener)
/**
* This method is used to register a new {@link Listener}. Any time an
* event of the given event name has been triggered, the {@link Listener} will be
* notified.
* This method will:
* <ol>
* <li>Check if it exists a list of listeners that have been registered for the
* given event name, create a new list if no list exists</li>
* <li>Add the listener to the list</li>
* </ol>
* @param eventName The name of the event to listen to
* @param listener The Listener to notify any time the event with the given
* name is triggered
*/
public void addListener(String eventName, Listener listener)

By convention, we use the listener name as the name of the event
to listen to.

2.8.2.2. Triggering an event

To trigger an event, an application can call one of the
broadcast() methods of ListenerService.

/**
* This method is used to broadcast an event. This method should: 1. Check if
* there is a list of listener that listen to the event name. 2. If there is a
* list of listener, create the event object with the given name , source and
* data 3. For each listener in the listener list, invoke the method
* onEvent(Event)
*
* @param <S> The type of the source that broadcast the event
* @param <D> The type of the data that the source object is working on
* @param name The name of the event
* @param source The source object instance
* @param data The data object instance
* @throws Exception
*/
public <S, D> void broadcast(String name, S source, D data) throws Exception {
...
}
/**
* This method is used when a developer want to implement his own event object
* and broadcast the event. The method should: 1. Check if there is a list of
* listener that listen to the event name. 2. If there is a list of the
* listener, For each listener in the listener list, invoke the method
* onEvent(Event)
*
* @param <T> The type of the event object, the type of the event object has
* to be extended from the Event type
* @param event The event instance
* @throws Exception
*/
public <T extends Event> void broadcast(T event) throws Exception {
...
}

The boadcast() methods retrieve the name of the event and find the
registered listeners with the same name and call the method onEvent() on
each listener found.

Each listener is a class that extends
org.exoplatform.services.listener.Listener, as you can see below:

public abstract class Listener<S, D> extends BaseComponentPlugin {
/**
* This method should be invoked when an event with the same name is
* broadcasted
*/
public abstract void onEvent(Event<S, D> event) throws Exception;
}

Warning

As you can see we use generics to limit the source of the event
to the type 'S' and the data of the event to the type 'D', so we
expect that listeners implement the method onEvent() with the
corresponding types

Each listener is also a ComponentPlugin with a name and a
description, in other words, the name of the listener will be the name
given in the configuration file, for more details see the next
section.

2.8.2.3. Asynchnous mode

Some events may take a lot of time and you don't necessary need to
process them immediately, in that particular case, you can decide to
make your listener asynchronous. To do so it's very simple, just mark
your Listener implementation with the annotation
@Asynchronous.

2.8.3. How to configure a listener?

All listeners are in fact a ComponentPlugin so it must be configured
as below:

<?xml version="1.0" encoding="ISO-8859-1"?>
<configuration>
...
<external-component-plugins>
<!-- The full qualified name of the ListenerService -->
<target-component>org.exoplatform.services.listener.ListenerService</target-component>
<component-plugin>
<!-- The name of the listener that is also the name of the target event -->
<name>${name-of-the-target-event}</name>
<!-- The name of the method to call on the ListenerService in order to register the Listener -->
<set-method>addListener</set-method>
<!-- The full qualified name of the Listener -->
<type>${the-FQN-of-the-listener}</type>
</component-plugin>
</external-component-plugins>
</configuration>

2.8.4. Concrete Example

The org.exoplatform.services.security.ConversationRegistry uses the
ListenerService to notify that a user has just signed in or just left the
application. For example, when a new user signs in, the following code is
called:

This code will in fact create a new Event which name is
"exo.core.security.ConversationRegistry.register", which source is the
current instance of ConversationRegistry and which data is the given
state. The ListenerService will call the method
onEvent(Event<ConversationRegistry, ConversationState> event) on all
the listeners which name is
"exo.core.security.ConversationRegistry.register".

In the example below, we define a Listener that will listen the
event "exo.core.security.ConversationRegistry.register".

<?xml version="1.0" encoding="ISO-8859-1"?>
<configuration>
...
<external-component-plugins>
<!-- The full qualified name of the ListenerService -->
<target-component>org.exoplatform.services.listener.ListenerService</target-component>
<component-plugin>
<!-- The name of the listener that is also the name of the target event -->
<name>exo.core.security.ConversationRegistry.register</name>
<!-- The name of the method to call on the ListenerService in order to register the Listener -->
<set-method>addListener</set-method>
<!-- The full qualified name of the Listener -->
<type>org.exoplatform.forum.service.AuthenticationLoginListener</type>
</component-plugin>
</external-component-plugins>
</configuration>
...

2.9. Initial Context Binder

Initial Context Binder is responsible for binding references at
runtime, persisting in file and automatically rebinding. Java temp directory
is used to persist references in bind-references.xml file by default. In
case when need to definde special file it can be done by add parameter to
InitialContextInitializer
configuration.

2.10. Job Scheduler Service

Job scheduler defines a job to execute a given number of times during a given period. It is a service that is in charge of unattended background executions, commonly known for historical reasons as batch processing. It is used to create and run jobs automatically and continuously, to schedule event-driven jobs and reports.

2.10.1. Where is Job Scheduler Service used in eXo Products?

Job Scheduler Service is widely used in many eXo products such as Social, DMS, WCM, eXo Knowledge and eXo Collaboration.

In eXo products, Job Schedulers are used to do some tasks as below:

Automatically send notification, such as task/event reminder in the Calendar application of eXo Collaboration.

Automatically save chat messages from Openfire Server to History in the Chat application of eXo Collaboration.

Inactivate topics in the Forum application of eXo Knowledge.

Calculate the number of active and online users in the Forum application of eXo Knowledge.

Automatically collect RSS items from various RSS resources to post to the activity stream of users and spaces in eXo Social.

Automatically send Newsletters to users in WCM.

Also, it is used in Schedule lifecycle in DMS.

By using Job Scheduler Service in eXo kernel, many kinds of job can be configured to run, such as, addPeriodJob, addCronJob, addGlobalJobListener, addJobListener and many more. Just write a job (a class implements Job interface of quartz library and configures plug-in for JobSchedulerService and you're done.

2.10.2. How does Job Scheduler work?

Jobs are scheduled to run when a given Trigger occurs. Triggers can
be created with nearly any combination of the following directives:

at a certain time of day (to the millisecond)

on certain days of the week

on certain days of the month

on certain days of the year

not on certain days listed within a registered Calendar (such as
business holidays)

repeated a specific number of times

repeated until a specific time/date

repeated indefinitely

repeated with a delay interval

Jobs are given names by their creator and can also be organized into
named groups. Triggers may also be given names and placed into groups, in
order to easily organize them within the scheduler. Jobs can be added to
the scheduler once, but registered with multiple Triggers. Within a J2EE
environment, Jobs can perform their work as part of a distributed (XA)
transaction.

(Source: quartz-scheduler.org)

2.10.2.1. How can Job Scheduler Service be used in Kernel?

Kernel leverages Quartz for its scheduler
service and wraps org.quartz.Scheduler in
org.exoplatform.services.scheduler.impl.QuartzSheduler
for easier service wiring and configuration like any other services. To
work with Quartz in Kernel, you will mostly work with
org.exoplatform.services.scheduler.JobSchedulerService
(implemented by
org.exoplatform.services.scheduler.impl.JobSchedulerServiceImpl.

To use JobSchedulerService, you can
configure it as a component in the configuration.xml. Because
JobSchedulerService requires
QuartzSheduler and
QueueTasks, you also have to configure these two
components.

All jobs are required to implement the method execute from org.quartz.Job interface. This method will be called whenever a job is performed. With DumbJob, you just use logging to see that it will work. By looking at the terminal, you will see the the log message: "DumbJob is executing..."

2.10.2.2.2. Job configuration

After defining the "job", the only next step is to configure it by using external-component-plugin configuration for org.exoplatform.services.scheduler.JobSchedulerService. You can use these methods below for setting component plugin:

public void addPeriodJob(ComponentPlugin plugin) throws Exception;

The component plugin for this method must be the type of org.exoplatform.services.scheduler.PeriodJob. This type of job is used to perform actions that are executed in a period of time. You have to define when this job is performed, when it ends, when it performs the first action, how many times it is executed and the period of time to perform the action. See the configuration sample below to understand more clearly:

The component plugin for this method must be the type of org.exoplatform.services.scheduler.CronJob. This type of job is used to perform actions at specified time with Unix 'cron-like' definitions. The plugin uses "expression" field for specifying the 'cron-like' definitions to execute the job. This is considered as the most powerful and flexible job to define when it will execute. For example, at 12pm every day => "0 0 12 * * ?"; or at 10:15am every Monday, Tuesday, Wednesday, Thursday and Friday => "0 15 10 ? * MON-FRI". To see more about Cron expression, please refer to this article: