This chapter is divided into 2 main topics that allow you to follow easily:

  • Basic configuration

    Instructions on basic configurations related to JCR, persister and JDBC Data Container.

  • Advanced configuration

    Instructions on advanced configurations regarding to Search, LockManager, QueryHandler, Cluster, RepositoryCreationService, TransactionService and External value storages.

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

The JCR service configuration looks like:


<component>
  <key>org.exoplatform.services.jcr.RepositoryService</key>
  <type>org.exoplatform.services.jcr.impl.RepositoryServiceImpl</type>
</component>
<component>
  <key>org.exoplatform.services.jcr.config.RepositoryServiceConfiguration</key>
  <type>org.exoplatform.services.jcr.impl.config.RepositoryServiceConfigurationImpl</type>
  <init-params>
(1)    <value-param>
      <name>conf-path</name>
      <description>JCR repositories configuration file</description>
      <value>war:/conf/jcr/repository-configuration.xml</value>
    </value-param>
(2)    <value-param>
      <name>max-backup-files</name>
      <value>5</value>
    </value-param>
(3)    <properties-param>
      <name>working-conf</name>
      <description>working-conf</description>
      <property name="persister-class-name" value="org.exoplatform.services.jcr.impl.config.JDBCConfigurationPersister" />
      <property name="source-name" value="${gatein.jcr.datasource.name}${container.name.suffix}"/>
      <property name="dialect" value="${gatein.jcr.datasource.dialect}"/>
    </properties-param>
  </init-params>
</component>
1

conf-path: A path to a RepositoryService JCR Configuration.

2

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

3

working-conf: This is optional. See JCR configuration persister for more details. If there is not a working-conf, the persister will be disabled.

The JCR Core implementation contains a persister which stores the repository configuration in the related database using JDBC calls - org.exoplatform.services.jcr.impl.config.JDBCConfigurationPersister. The implementation will create and use table JCR_CONFIG in the provided database. But the developer can implement his own persister for his particular usecase.

In this section you will learn how to configure all basic configuration by configuring the Repository, Workspace, Value storage plugin, Initializer, Cache, Query Handler and Lock Manager of JCR .

The JCR configurations are defined in an .xml file. See the following DTD file to understand the expected format of the JCR configuration.


<!ELEMENT repository-service (repositories)>
<!ATTLIST repository-service default-repository NMTOKEN #REQUIRED>
<!ELEMENT repositories (repository)>
<!ELEMENT repository (security-domain,access-control,session-max-age,authentication-policy,workspaces)>
<!ATTLIST repository
  default-workspace NMTOKEN #REQUIRED
  name NMTOKEN #REQUIRED
  system-workspace NMTOKEN #REQUIRED
>
<!ELEMENT security-domain (#PCDATA)>
<!ELEMENT access-control (#PCDATA)>
<!ELEMENT session-max-age (#PCDATA)>
<!ELEMENT authentication-policy (#PCDATA)>
<!ELEMENT workspaces (workspace+)>
<!ELEMENT workspace (container,initializer,cache,query-handler)>
<!ATTLIST workspace name NMTOKEN #REQUIRED>
<!ELEMENT container (properties,value-storages)>
<!ATTLIST container class NMTOKEN #REQUIRED>
<!ELEMENT value-storages (value-storage+)>
<!ELEMENT value-storage (properties,filters)>
<!ATTLIST value-storage class NMTOKEN #REQUIRED>
<!ELEMENT filters (filter+)>
<!ELEMENT filter EMPTY>
<!ATTLIST filter property-type NMTOKEN #REQUIRED>
<!ELEMENT initializer (properties)>
<!ATTLIST initializer class NMTOKEN #REQUIRED>
<!ELEMENT cache (properties)>
<!ATTLIST cache 
  enabled NMTOKEN #REQUIRED
  class NMTOKEN #REQUIRED
>
<!ELEMENT query-handler (properties)>
<!ATTLIST query-handler class NMTOKEN #REQUIRED>
<!ELEMENT access-manager (properties)>
<!ATTLIST access-manager class NMTOKEN #REQUIRED>
<!ELEMENT lock-manager (time-out,persister)>
<!ELEMENT time-out (#PCDATA)>
<!ELEMENT persister (properties)>
<!ELEMENT properties (property+)>
<!ELEMENT property EMPTY>

The elements in the above configuration file are detailed in the following sections:

JCR Service can use multiple Repositories and each repository can have multiple Workspaces.

Repositories configuration parameters support human-readable formats of values. They are all case-insensitive:

  • Number formats: K, KB - kilobytes; M, MB - megabytes; G, GB - gigabytes; T,TB - terabytes. For example, 100.5 - digit 100.5, 200k - 200 Kbytes, 4m - 4 Mbytes, 1.4G - 1.4 Gbytes, 10T - 10 Tbytes.

  • Time format endings: ms - milliseconds; m - minutes; h - hours; d - days; w - weeks.

  • No ending - seconds. Examples: 500ms - 500 milliseconds; 20 - 20 seconds; 30m - 30 minutes; 12h - 12 hours; 5d - 5 days; 4w - 4 weeks.

Repository service configuration

The service configuration is located at platform-extension/WEB-INF/conf/jcr/platform-extension/repository-configuration.xml in the portal web application.

Repository configuration

Note

See RepositoryCreationService if you want to learn how to create repositories in runtime.

Workspace configuration

The service configuration is located at repository-configuration.xml in the web application.

Workspace data container configuration

The service configuration is located at repository-configuration.xml in the web application. This file can be found in various locations.

  • class: A Query Handler class name.

  • properties: The list of properties (name-value pairs) for a Query Handler (indexDir).

Note

See QueryHandler configuration for advanced configuration of QueryHandler.

The service configuration is located at repository-configuration.xml in the web application. The file can be found in various locations.

  • time-out: Time after which the unused global lock will be removed.

  • persister: A class for storing lock information for future use. For example, remove lock after jcr restart.

  • path: A lock folder. Each workspace has its own one.

Note

JCR allows using persister to store configuration. In this section, you will understand how to use and configure JCR persister.

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.

The configuration file is located in portal/WEB-INF/conf/jcr/jcr-configuration.xml in the portal web application.

Configuration with persister:

If you want to customize, you can implement ConfigurationPersister interface as follows:

/**

   * Init persister.
   * Used by RepositoryServiceConfiguration on init. 
   * @return - config data stream
   */
  void init(PropertiesParam params) throws RepositoryConfigurationException;
  
  /**
   * Read config data.
   * @return - config data stream
   */
  InputStream read() throws RepositoryConfigurationException;
  
  /**
   * Create table, write data.
   * @param confData - config data stream
   */
  void write(InputStream confData) throws RepositoryConfigurationException;
  
  /**
   * Tell if the config exists.
   * @return - flag
   */
  boolean hasConfig() throws RepositoryConfigurationException;
  

The current configuration of JCR uses Apache DBCP connection pool (org.apache.commons.dbcp.BasicDataSourceFactory). It is possible to set a big value for maxActive parameter in configuration.xml. That means lots of TCP/IP ports from a client machine inside the pool are used, such as JDBC driver. As the 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 using 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 as follows:

  • "TcpTimedWaitDelay"=dword:0000001e, sets TIME_WAIT parameter to 30 seconds (default value is "240").

  • "MaxUserPort"=dword:00001b58, sets the maximum of open ports to 7000 or higher (default value is "5000").

A sample registry file is below:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters]
"MaxUserPort"=dword:00001b58
"TcpTimedWaitDelay"=dword:0000001e

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 is the JNDI context initializer which registers (binds) naming resources (DataSources) for data containers.

For example, the configuration for two data containers (jdbcjcr - local HSQLDB, jdbcjcr1 - remote MySQL) is as follows :


<component>
    <key>org.exoplatform.services.naming.InitialContextInitializer</key>
    <type>org.exoplatform.services.naming.InitialContextInitializer</type>
    <component-plugins>
      <component-plugin>
        <name>bind.datasource</name>
        <set-method>addPlugin</set-method>
        <type>org.exoplatform.services.naming.BindReferencePlugin</type>
        <init-params>
          <value-param>
            <name>bind-name</name>
            <value>jdbcjcr</value>
          </value-param>
          <value-param>
            <name>class-name</name>
            <value>javax.sql.DataSource</value>
          </value-param>
          <value-param>
            <name>factory</name>
            <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
          </value-param>
          <properties-param>
            <name>ref-addresses</name>
(1)            <description>ref-addresses</description>
(2)            <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
(3)            <property name="url" value="jdbc:hsqldb:file:target/temp/data/portal"/>
(4)            <property name="username" value="sa"/>
            <property name="password" value=""/>
          </properties-param>
        </init-params>
      </component-plugin>
      <component-plugin>
        <name>bind.datasource</name>
        <set-method>addPlugin</set-method>
        <type>org.exoplatform.services.naming.BindReferencePlugin</type>
        <init-params>
          <value-param>
            <name>bind-name</name>
            <value>jdbcjcr1</value>
          </value-param>
          <value-param>
            <name>class-name</name>
            <value>javax.sql.DataSource</value>
          </value-param>
          <value-param>
            <name>factory</name>
            <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
          </value-param>
          <properties-param>
            <name>ref-addresses</name>
            <description>ref-addresses</description>
            <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://exoua.dnsalias.net/jcr"/>
            <property name="username" value="exoadmin"/>
(5)            <property name="password" value="exo12321"/>
(6)            <property name="maxActive" value="50"/>
(7)            <property name="maxIdle" value="5"/>
            <property name="initialSize" value="5"/>
          </properties-param>
        </init-params>
      </component-plugin>
    <component-plugins>
    <init-params>
      <value-param>
        <name>default-context-factory</name>
        <value>org.exoplatform.services.naming.SimpleContextFactory</value>
      </value-param>
    </init-params>
</component>
1

driverClassName, e.g. "org.hsqldb.jdbcDriver", "com.mysql.jdbc.Driver", "org.postgresql.Driver"

2

url, e.g. "jdbc:hsqldb:file:target/temp/data/portal", "jdbc:mysql://exoua.dnsalias.net/jcr"

3

username, e.g. "sa", "exoadmin"

4

password, e.g. "", "exo12321"

5

maxActive, e.g. 50

6

maxIdle, e.g. 5

7

initialSize, e.g. 5

There are also some other connection pool configuration parameters (org.apache.commons.dbcp.BasicDataSourceFactory) according to Apache DBCP configuration.

When the data container configuration is done, you can configure the repository service. Each workspace will be configured for its own data container.

For example (two workspaces ws - jdbcjcr, ws1 - jdbcjcr1):


<workspaces>
  <workspace name="ws" auto-init-root-nodetype="nt:unstructured">
    <container class="org.exoplatform.services.jcr.impl.storage.jdbc.optimisation.CQJDBCWorkspaceDataContainer">
(1)    <properties>
(2)      <property name="source-name" value="jdbcjcr"/>
(3)      <property name="dialect" value="hsqldb"/>
(4)      <property name="multi-db" value="true"/>
(5)      <property name="max-buffer-size" value="200K"/>
      <property name="swap-directory" value="target/temp/swap/ws"/>   
    </properties>
    </container>
    <cache enabled="true">
      <properties>
        <property name="max-size" value="10K"/><!-- 10Kbytes -->
        <property name="live-time" value="30m"/><!-- 30 min -->
      </properties>
    </cache>
    <query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">
    <properties>
      <property name="index-dir" value="target/temp/index"/>
    </properties>
    </query-handler>
    <lock-manager>
    <time-out>15m</time-out><!-- 15 min -->
    <persister class="org.exoplatform.services.jcr.impl.core.lock.FileSystemLockPersister">
      <properties>
      <property name="path" value="target/temp/lock/ws"/>
      </properties>
    </persister>
    </lock-manager>
  </workspace>
  <workspace name="ws1" auto-init-root-nodetype="nt:unstructured">
    <container class="org.exoplatform.services.jcr.impl.storage.jdbc.optimisation.CQJDBCWorkspaceDataContainer">
    <properties>
      <property name="source-name" value="jdbcjcr1"/>
      <property name="dialect" value="mysql"/>
      <property name="multi-db" value="true"/>
      <property name="max-buffer-size" value="200K"/>
      <property name="swap-directory" value="target/temp/swap/ws1"/>   
    </properties>
    </container>
    <cache enabled="true">
      <properties>
        <property name="max-size" value="10K"/>
        <property name="live-time" value="5m"/>
      </properties>
    </cache>
    <query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">
    <properties>
      <property name="index-dir" value="target/temp/index"/>
    </properties>
    </query-handler>
    <lock-manager>
    <time-out>15m</time-out><!-- 15 min -->
    <persister class="org.exoplatform.services.jcr.impl.core.lock.FileSystemLockPersister">
      <properties>
      <property name="path" value="target/temp/lock/ws1"/>
      </properties>
    </persister>
    </lock-manager>
  </workspace>
</workspaces>
    
1

source-name: A javax.sql.DataSource name configured in InitialContextInitializer component.

2

dialect: A database dialect, one of "hsqldb", "mysql", "mysql-utf8", "pgsql", "oracle", "oracle-oci", "mssql", "sybase", "derby", "db2", "db2v8" or "auto" for dialect autodetection;

3

multi-db: Enable multi-database container with this parameter (set value "true");

4

max-buffer-size: A threshold (in bytes) after which a javax.jcr. Value content will be swapped to a file in a temporary storage. For example: swap for pending changes.

5

swap-directory: A path in the file system used to swap the pending changes.

In this way, you have configured two workspaces which will be persisted in two different database (ws in HSQLDB, ws1 in MySQL).

Note

The repository configuration parameters supports human-readable formats of values (for example: 200K - 200 Kbytes, 30m - 30 minutes, etc)

It is simpler to configure a single-database data container. You have to configure one naming resource.

For example (embedded mode for jdbcjcr data container):


<external-component-plugins>
<target-component>org.exoplatform.services.naming.InitialContextInitializer</target-component>
<component-plugin>
    <name>bind.datasource</name>
    <set-method>addPlugin</set-method>
    <type>org.exoplatform.services.naming.BindReferencePlugin</type>
    <init-params>
      <value-param>
        <name>bind-name</name>
        <value>jdbcjcr</value>
      </value-param>
      <value-param>
        <name>class-name</name>
        <value>javax.sql.DataSource</value>
      </value-param>
      <value-param>
        <name>factory</name>
        <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
      </value-param>
      <properties-param>
        <name>ref-addresses</name>
        <description>ref-addresses</description>
        <property name="driverClassName" value="org.postgresql.Driver"/>
        <property name="url" value="jdbc:postgresql://exoua.dnsalias.net/portal"/>
        <property name="username" value="exoadmin"/>
        <property name="password" value="exo12321"/>
        <property name="maxActive" value="50"/>
        <property name="maxIdle" value="5"/>
        <property name="initialSize" value="5"/>
      </properties-param>
    </init-params>
</component-plugin>
</external-component-plugins>

And configure repository workspaces in repositories configuration with this one database. Parameter "multi-db" must be switched off (set value "false").

For example: two workspaces ws - jdbcjcr, and ws1 - jdbcjcr:


<workspaces>
  <workspace name="ws" auto-init-root-nodetype="nt:unstructured">
    <container class="org.exoplatform.services.jcr.impl.storage.jdbc.optimisation.CQJDBCWorkspaceDataContainer">
    <properties>
      <property name="source-name" value="jdbcjcr"/>
      <property name="dialect" value="pgsql"/>
      <property name="multi-db" value="false"/>
      <property name="max-buffer-size" value="200K"/>
      <property name="swap-directory" value="target/temp/swap/ws"/>
    </properties>
    </container>
    <cache enabled="true">
    <properties>
      <property name="max-size" value="10K"/>
      <property name="live-time" value="30m"/>
    </properties>
    </cache>
    <query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">
    <properties>
      <property name="index-dir" value="../temp/index"/>
    </properties>
    </query-handler>
    <lock-manager>
    <time-out>15m</time-out>
    <persister class="org.exoplatform.services.jcr.impl.core.lock.FileSystemLockPersister">
      <properties>
      <property name="path" value="target/temp/lock/ws"/>
      </properties>
    </persister>
    </lock-manager>
  </workspace>
  <workspace name="ws1" auto-init-root-nodetype="nt:unstructured">
    <container class="org.exoplatform.services.jcr.impl.storage.jdbc.optimisation.CQJDBCWorkspaceDataContainer">
    <properties>
      <property name="source-name" value="jdbcjcr"/>
      <property name="dialect" value="pgsql"/>
      <property name="multi-db" value="false"/>
      <property name="max-buffer-size" value="200K"/>
      <property name="swap-directory" value="target/temp/swap/ws1"/>
    </properties>
    </container>
    <cache enabled="true">
    <properties>
      <property name="max-size" value="10K"/>
      <property name="live-time" value="5m"/>
    </properties>
    </cache>
    <lock-manager>
    <time-out>15m</time-out>
    <persister class="org.exoplatform.services.jcr.impl.core.lock.FileSystemLockPersister">
      <properties>
      <property name="path" value="target/temp/lock/ws1"/>
      </properties>
    </persister>
    </lock-manager>
  </workspace>
</workspaces>

In this way, you have configured two workspaces which will be persisted in one database (PostgreSQL).

Configuration without DataSource

Repository configuration without using the javax.sql.DataSource bounded in JNDI.

This case may be usable if you have a dedicated JDBC driver implementation with special features like XA transactions, statements/connections pooling and so on:


<workspace name="ws" auto-init-root-nodetype="nt:unstructured">
<container class="org.exoplatform.services.jcr.impl.storage.jdbc.optimisation.CQJDBCWorkspaceDataContainer">
<properties>
  <property name="dialect" value="hsqldb"/>
  <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
  <property name="url" value="jdbc:hsqldb:file:target/temp/data/portal"/>
  <property name="username" value="su"/>
  <property name="password" value=""/> 
......
Q1. How to use Lucene spellchecker?
Q2. How can I affect spellchecker results?
Q3. Does Help application prohibit the use of closed sessions?
Q4. Does Help application allow the use of closed datasources?
Q5. How to get the effective configuration at Runtime of all the repositories?
Q1.

How to use Lucene spellchecker?

You simply do the following steps:

  • Enable the Lucene spellchecker in the JCR QueryHandler configuration:

    
    <query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">
        <properties>
            ...
            <property name="spellchecker-class" value="org.exoplatform.services.jcr.impl.core.query.lucene.spell.LuceneSpellChecker$FiveSecondsRefreshInterval"/>
            ...
        </properties>
    </query-handler>
  • Execute query with rep:spellcheck function and word that is checked:

    Query query = qm.createQuery("select rep:spellcheck() from nt:base where " +
    
      "jcr:path = '/' and spellcheck('word that is checked')", Query.SQL);
      RowIterator rows = query.execute().getRows();
  • Fetch a result:

    Row r = rows.nextRow();
    
      Value v = r.getValue("rep:spellcheck()");

If there is no any result, this means there is no suggestion, so word is correct or spellcheckers dictionary does not contain any words like the checked word.

Q2.

How can I affect spellchecker results?

There are two parameters in the JCR QueryHandler configuration:

  • Minimal distance between checked word and proposed suggestion:

  • Search for more popular suggestions:

    
    <query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">
        <properties>
            ...
            <property name="spellchecker-class" value="org.exoplatform.services.jcr.impl.core.query.lucene.spell.LuceneSpellChecker$FiveSecondsRefreshInterval" />
            <property name="spellchecker-more-popular" value="false" />
            <property name="spellchecker-min-distance" value="0.55" />
            ...
        </properties>
    </query-handler>

Minimal distance is counted as Levenshtein distance between checked word and spellchecker suggestion.

The MorePopular parameter affects in the following way:

If "morePopular" is disabled:

  • If the proposed word exists in the directory: no suggestion given.

  • If the proposed word does not exist in the directory: propose the closed word.

If "morePopular" is enabled:

  • No matter word exists or not, checker will propose the closed word that is more popular than the checked word.

Q3.

Does Help application prohibit the use of closed sessions?

Products that use 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 following modes:

  • 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 is trying 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 issue and let the application use the closed session as before without doing anything to allow applications to migrate step by step.

Q4.

Does Help application allow the use of closed datasources?

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

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

Q5.

How to get 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(). This method 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 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.

  • Search configuration

    Details of Search configuration, including XML parameters, global search index and indexing tuning.

  • LockManager configuration

    Instructions on how to configure LockManager which is used to store Lock objects.

  • QueryHandler configuration

    Details of Indexing in clustered environment, query-handler parameters, cluster-ready indexing strategies, Asynchronous reindexing and Lucene tuning.

  • Configure JCR in cluster

    Requirements related to environment and configuration, instructions on how to configure JBoss Cache and stop a node properly in the cluster environment.

  • RepositoryCreationService

    Overview of dependencies and how RepositoryCreationService works, details of its configuration and interface.

  • TransactionService

    Details of existing TransactionService implementations and JBoss TransactionService.

  • External Value Storages

    Details of Tree File Value Storage, Simple File Value Storage and Content Addressable Value storage support.

Search is an important function in JCR, so it is quite necessary for you to know how to configure the JCR Search tool. Before going deeper into the JCR Search tool, you need to learn about the .xml configuration file and its parameters as follows.

XML Configuration

This is the JCR index configuration under the repository-configuration.xml file which can be found in various locations.


<repository-service default-repository="db1">
  <repositories>
    <repository name="db1" system-workspace="ws" default-workspace="ws">
       ....
      <workspaces>
        <workspace name="ws">
       ....
          <query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">
            <properties>
              <property name="index-dir" value="${java.io.tmpdir}/temp/index/db1/ws" />
              <property name="synonymprovider-class" value="org.exoplatform.services.jcr.impl.core.query.lucene.PropertiesSynonymProvider" />
              <property name="synonymprovider-config-path" value="/synonyms.properties" />
              <property name="indexing-configuration-path" value="/indexing-configuration.xml" />
              <property name="query-class" value="org.exoplatform.services.jcr.impl.core.query.QueryImpl" />
            </properties>
          </query-handler>
        ... 
        </workspace>
     </workspaces>
    </repository>        
  </repositories>
</repository-service>

Configuration parameters

Followings are parameters of JCR index configuration:

ParameterDefaultDescription
index-dirnoneThe location of the index directory. This parameter is mandatory.
use-compoundfiletrueAdvise Lucene to use compound files for the index files.
min-merge-docs100Minimum number of nodes in an index until segments are merged.
volatile-idle-time3Idle time in seconds until the volatile index part is moved to a persistent index even though minMergeDocs is not reached.
max-merge-docsInteger.MAX_VALUEMaximum number of nodes in segments that will be merged.
merge-factor10Determine how often segment indices are merged.
max-field-length10000The number of words that are fulltext indexed at most per property.
cache-size1000Size of the document number cache. This cache maps uuids to Lucene document numbers.
force-consistencycheckfalseRun a consistency check on every startup. If false, a consistency check is only performed when the search index detects a prior forced shutdown.
auto-repairtrueErrors detected by a consistency check are automatically repaired. If false, errors are only written to the log.
query-classQueryImplClass name that implements the javax.jcr.query.Query interface. This class must also extend from the org.exoplatform.services.jcr.impl.core.query.AbstractQueryImpl class.
document-ordertrueIf 'true' is set 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 this parameter to 'false'.
result-fetch-sizeInteger.MAX_VALUEThe number of results when a query is executed. The default value is Integer.MAX_VALUE.
excerptprovider-classDefaultXMLExcerptThe 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.
support-highlightingfalseIf set to true additional information is stored in the index to support highlighting using the rep:excerpt() function.
synonymprovider-classnoneThe name of a class that implements org.exoplatform.services.jcr.impl.core.query.lucene.SynonymProvider. The default value is null (not set).
synonymprovider-config-pathnoneThe path to the synonym provider configuration file. This path is interpreted relatively 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.
indexing-configuration-pathnoneThe path to the indexing configuration file.
indexing-configuration-classIndexingConfigurationImplThe name of the class that implements org.exoplatform.services.jcr.impl.core.query.lucene.IndexingConfiguration.
force-consistencycheckfalseIf "true" is set, a consistency check is performed, depending on the forceConsistencyCheck parameter. If setting to false, no consistency check is performed on startup, even if a redo log had been applied.
spellchecker-classnoneThe name of a class that implements org.exoplatform.services.jcr.impl.core.query.lucene.SpellChecker.
spellchecker-more-populartrueIf "true" is set, spellchecker returns only the suggest words that are as frequent or more frequent than the checked word. If "false" set, spellchecker returns null (if checked word exit in dictionary), or spellchecker will return the most close suggested word.
spellchecker-min-distance0.55fMinimal distance between checked word and the proposed suggested word.
errorlog-size50(Kb)The default size of error log file in Kb.
upgrade-indexfalseAllow JCR to convert an existing index into the new format. 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 do not need this option anymore. As the old index is replaced and a back conversion is not possible, you should take a backup of the index before.
analyzerorg.apache.lucene.analysis.standard.StandardAnalyzerClass name of a lucene analyzer to use for fulltext indexing of text.

The global search index is configured in the above-mentioned configuration file (repository-configuration.xml which can be found in various locations) in the query-handler tag.


<query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">

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

If you do not have a handy QueryHandler, you can learn how to create a customized QueryHandler in the QueryHandler configuration section.

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

public TokenStream tokenStream(String fieldName, Reader reader) {

    StandardTokenizer tokenStream = new StandardTokenizer(reader, replaceInvalidAcronym);
    tokenStream.setMaxTokenLength(maxTokenLength);
    TokenStream result = new StandardFilter(tokenStream);
    result = new LowerCaseFilter(result);
    result = new StopFilter(result, stopSet);
    return result;
  }

For specific cases, you may wish to use additional filters like ISOLatin1AccentFilter, which replaces accented characters in the ISO Latin 1 character set (ISO-8859-1) by their unaccented equivalents.

In order to use a different filter, you have to create a new analyzer, and a new search index to use the analyzer. You put it in a jar, which is deployed with your application.

Create a filter

The ISOLatin1AccentFilter is not present in the current Lucene version used by eXo. You can use the attached file. You can also create your own filter with the relevant method as follows:

public final Token next(final Token reusableToken) throws java.io.IOException

This method defines how chars are read and used by the filter.

Create an analyzer

The analyzer has to extend org.apache.lucene.analysis.standard.StandardAnalyzer, and overload the following method to put your own filters.

public TokenStream tokenStream(String fieldName, Reader reader)

You can have a glance at the example analyzer attached to this article.

Configure Platform to use your analyzer

In repository-configuration.xml which can be found in various locations, you have to add the analyzer parameter to each query-handler config:


<query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">
    <properties>
      ...
      <property name="analyzer" value="org.exoplatform.services.jcr.impl.core.MyAnalyzer"/>
      ...
    </properties>
</query-handler>

When you start eXo, your SearchIndex will start to index content with the specified filters.

Create a search index

You have had the analyzer, so you now need to write the SearchIndex, which will use the analyzer. You have to extend org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex. You have to write the constructor to set the right analyzer and the following method to return your analyzer.

public Analyzer getAnalyzer() {

    return MyAnalyzer;
}

You can see the attached SearchIndex.

Configure Platform to use your SearchIndex

In repository-configuration.xml which can be found in various locations, you have to replace each:


<query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">

with your own class


<query-handler class="mypackage.indexation.MySearchIndex">

Property content indexing

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

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.



Note

StandardAnalyzer is the default analyzer in JCR search engine but it does not use stop words.

You can assign your analyzer as described in Search Configuration.

How are different properties indexed?

Different properties are indexed in different ways that defines if it can be searched like fulltext by property or not.

Only two property types are indexed as fulltext searcheable: STRING and BINARY.


For example, you have the jcr:data property (it is BINARY). It is stored well, but you will never find any string with query like:

SELECT * FROM nt:resource WHERE CONTAINS(jcr:data, 'some string')

BINARY is not searchable by fulltext search on the exact property, but the next query will return result if the node has searched data.

SELECT * FROM nt:resource WHERE CONTAINS( * , 'some string')

Fulltext search query examples

Different analyzers in action

First of all, fill repository by nodes with mixin type 'mix:title' and different values of jcr:description property.

Let's see analyzers effect closer. In the first case, the base JCR settings is used, so as mentioned above, the string "The quick brown fox jumped over the lazy dogs" will be transformed to set {[the] [quick] [brown] [fox] [jumped] [over] [the] [lazy] [dogs] }

// make SQL query

QueryManager queryManager = workspace.getQueryManager();
String sqlStatement = "SELECT * FROM mix:title WHERE CONTAINS(jcr:description, 'the')";
// create query
Query query = queryManager.createQuery(sqlStatement, Query.SQL);
// execute query and fetch result
QueryResult result = query.execute();

NodeIterator will return "document1".

Now change the default analyzer to org.apache.lucene.analysis.StopAnalyzer. Fill the repository (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.

The default search index implementation in JCR allows you to control which properties of a node are indexed. You also can define different analyzers for different nodes.

The configuration parameter is called indexingConfiguration and its default value is not set. This means all properties of a node are indexed.

If you wish to configure the indexing behavior, you need to add a parameter to the query-handler element in your configuration file.


<property name="indexing-configuration-path" value="/indexing_configuration.xml"/>

Index configuration path can indicate any file located on the file system, in the jar or war files.

Node scope limit

To optimize the index size, you can limit the node scope so that only certain properties of a node type are indexed.

With the below configuration, only properties named Text are indexed for nodes of type nt:unstructured. This configuration also applies to all nodes whose type extends from nt:unstructured.


<?xml version="1.0"?> <!DOCTYPE configuration SYSTEM "http://www.exoplatform.org/dtd/indexing-configuration-1.0.dtd">
<configuration xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
  <index-rule nodeType="nt:unstructured">
    <property>Text</property>
  </index-rule>
</configuration>

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.


<?xml version="1.0"?> <!DOCTYPE configuration SYSTEM "http://www.exoplatform.org/dtd/indexing-configuration-1.0.dtd">
<configuration xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
  <index-rule nodeType="nt:unstructured" boost="2.0">
    <property>Text</property>
  </index-rule>
</configuration>

If you do not wish to boost the complete node but only certain properties, you can also provide a boost value for the listed properties:


<?xml version="1.0"?> <!DOCTYPE configuration SYSTEM "http://www.exoplatform.org/dtd/indexing-configuration-1.0.dtd">
<configuration xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
  <index-rule nodeType="nt:unstructured">
    <property boost="3.0">Title</property>
    <property boost="1.5">Text</property>
  </index-rule>
</configuration>

Conditional index rules

You may also add a condition to the index rule and have multiple rules with the same nodeType. The first index rule that matches will apply and all remain ones are ignored:


<?xml version="1.0"?> <!DOCTYPE configuration SYSTEM "http://www.exoplatform.org/dtd/indexing-configuration-1.0.dtd">
<configuration xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
  <index-rule nodeType="nt:unstructured"
              boost="2.0"
              condition="@priority = 'high'">
    <property>Text</property>
  </index-rule>
  <index-rule nodeType="nt:unstructured">
    <property>Text</property>
  </index-rule>
</configuration>

In the above example, the first rule only applies if the nt:unstructured node has a priority property with a value 'high'. The condition syntax supports only the equals operator and a string literal.

You may also refer properties in the condition that are not on the current node:


<?xml version="1.0"?> <!DOCTYPE configuration SYSTEM "http://www.exoplatform.org/dtd/indexing-configuration-1.0.dtd">
<configuration xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
  <index-rule nodeType="nt:unstructured"
              boost="2.0"
              condition="ancestor::*/@priority = 'high'">
    <property>Text</property>
  </index-rule>
  <index-rule nodeType="nt:unstructured"
              boost="0.5"
              condition="parent::foo/@priority = 'low'">
    <property>Text</property>
  </index-rule>
  <index-rule nodeType="nt:unstructured"
              boost="1.5"
              condition="bar/@priority = 'medium'">
    <property>Text</property>
  </index-rule>
  <index-rule nodeType="nt:unstructured">
    <property>Text</property>
  </index-rule>
</configuration>

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


<?xml version="1.0"?>
<!DOCTYPE configuration SYSTEM "http://www.exoplatform.org/dtd/indexing-configuration-1.0.dtd">
<configuration xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
  <index-rule nodeType="nt:unstructured"
              boost="2.0"
              condition="element(*, nt:unstructured)/@priority = 'high'">
    <property>Text</property>
  </index-rule>
</configuration>

Exclusion from the node scope index

All configured properties of each default value 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:


<?xml version="1.0"?>
<!DOCTYPE configuration SYSTEM "http://www.exoplatform.org/dtd/indexing-configuration-1.0.dtd">
<configuration xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
  <index-rule nodeType="nt:unstructured">
    <property nodeScopeIndex="false">Text</property>
  </index-rule>
</configuration>

Sometimes it is useful to include the contents of descendant nodes into a single node to easier search on content that is scattered across multiple nodes.

JCR allows you to define indexed aggregates, basing on relative path patterns and primary node types.

The following example creates an indexed aggregate on nt:file that includes the content of the jcr:content node:


<?xml version="1.0"?> <!DOCTYPE configuration SYSTEM "http://www.exoplatform.org/dtd/indexing-configuration-1.0.dtd">
<configuration xmlns:jcr="http://www.jcp.org/jcr/1.0"
               xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
  <aggregate primaryType="nt:file">
    <include>jcr:content</include>
  </aggregate>
</configuration>

You can also restrict the included nodes to a certain type:


<?xml version="1.0"?> <!DOCTYPE configuration SYSTEM "http://www.exoplatform.org/dtd/indexing-configuration-1.0.dtd">
<configuration xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
  <aggregate primaryType="nt:file">
    <include primaryType="nt:resource">jcr:content</include>
  </aggregate>
</configuration>

You may also use the asterisk (*) to match all child nodes:


<?xml version="1.0"?> <!DOCTYPE configuration SYSTEM "http://www.exoplatform.org/dtd/indexing-configuration-1.0.dtd">
<configuration xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
  <aggregate primaryType="nt:file">http://wiki.exoplatform.com/xwiki/bin/edit/JCR/Search+Configuration
    <include primaryType="nt:resource">*</include>
  </aggregate>
</configuration>

If you wish to include nodes up to a certain depth below the current node, you can add multiple include elements. For example, the nt:file node may contain a complete XML document under jcr:content:


<?xml version="1.0"?> <!DOCTYPE configuration SYSTEM "http://www.exoplatform.org/dtd/indexing-configuration-1.0.dtd">
<configuration xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
  <aggregate primaryType="nt:file">
    <include>*</include>
    <include>*/*</include>
    <include>*/*/*</include>
  </aggregate>
</configuration>

Example

In this configuration section, you will 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:


<?xml version="1.0"?> <!DOCTYPE configuration SYSTEM "http://www.exoplatform.org/dtd/indexing-configuration-1.0.dtd">
<configuration xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
  <analyzers> 
        <analyzer class="org.apache.lucene.analysis.KeywordAnalyzer">
            <property>mytext</property>
        </analyzer>
        <analyzer class="org.apache.lucene.analysis.WhitespaceAnalyzer">
            <property>mytext2</property>
        </analyzer>
  </analyzers> 
</configuration>

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.

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 "mytext" property contains the "testing my analyzers" text and that you have not configured any analyzers for the "mytext" property (and not changed the default analyzer in SearchIndex).

For example, if your query is as follows:

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')]"

will not 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


<analyzer class="org.apache.lucene.analysis.Analyzer.GermanAnalyzer">
    <property>mytext</property>
</analyzer>

and you do the same search again, then for

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

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.

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

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

  • Search a term and its synonyms: SynonymSearch.

  • Search similar nodes: SimilaritySearch.

  • Check spelling of a full text query statement: SpellChecker.

  • Define index aggregates and rules: IndexingConfiguration.

In general, LockManager stores Lock objects, so it can give a Lock object or can release it.

Also, LockManager is responsible for removing Locks that live too long. This parameter may be configured with "time-out" property.

JCR provides two basic implementations of LockManager:

In this article, we will mostly mention about CacheableLockManagerImpl.

You can enable LockManager by adding lock-manager-configuration to workspace-configuration.

For example:


<workspace name="ws">
   ...
   <lock-manager class="org.exoplatform.services.jcr.impl.core.lock.jbosscache.CacheableLockManagerImpl">
      <properties>
         <property name="time-out" value="15m" />
         ...
      </properties>
   </lock-manager>               
   ...
</workspace>

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


<properties>
   <property name="time-out" value="10m" />
   ...
</properties>

Before going deeper into the QueryHandler configuration, you need to learn about the concept of Indexing in clustered environment.

Indexing in clustered environment

JCR offers multiple indexing strategies. They include both strategies 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 is why JCR offers three strategies that are suitable for its own usecases. They are standalone, clustered with shared index and clustered with local indexes. Each one has its pros and cons.

Standalone 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 are done on the same node that does content operations (for example: write operation). Prepared "documents" (Lucene term that means block of data ready for indexing) are replicated within 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 is found, JCR will use the automated scenarios. They are controlled via configuration (see the 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 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 into account that shared folder must be configured in your system environment (for example: mounted NFS folder). However, this strategy in some extremely rare cases may have a bit different volatile indexes within cluster instances for a while. In a few seconds they will be up to date.

See more about Search Configuration.

See the following sample configuration:


<workspace name="ws">
   <query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">
      <properties>
         <property name="index-dir" value="shareddir/index/db1/ws" />
         <property name="changesfilter-class"
            value="org.exoplatform.services.jcr.impl.core.query.jbosscache.JBossCacheIndexChangesFilter" />
         <property name="jbosscache-configuration" value="jbosscache-indexer.xml" />
         <property name="jgroups-configuration" value="udp-mux.xml" />
         <property name="jgroups-multiplexer-stack" value="true" />
         <property name="jbosscache-cluster-name" value="JCR-cluster-indexer-ws" />
         <property name="max-volatile-time" value="60" />
         <property name="rdbms-reindexing" value="true" />
         <property name="reindexing-page-size" value="1000" />
         <property name="index-recovery-mode" value="from-coordinator" />
         <property name="index-recovery-filter" value="org.exoplatform.services.jcr.impl.core.query.lucene.DocNumberRecoveryFilter" />
         <property name="indexing-thread-pool-size" value="16" />
      </properties>
   </query-handler>
</workspace>

Property nameDescription
index-dirPath to index.
changesfilter-classThe FQN of the class is to indicate the policy of managing 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 which needs to be adopted. To use the Shared Indexes Strategy, you can set it to org.exoplatform.services.jcr.impl.core.query.jbosscache.JBossCacheIndexChangesFilter. It is recommended you set the Local Indexes Strategy to org.exoplatform.services.jcr.impl.core.query.jbosscache.LocalIndexChangesFilter.
jbosscache-configurationTemplate of JBoss-cache configuration for all query-handlers in repository.
jgroups-configurationThis 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 setting the jgroups property singleton_name to a unique name (it must remain to be unique from one portal container to another). This file is also pre-bundled with templates and is recommended for use.
jgroups-multiplexer-stackIf the parameter value is set to "true", it will indicate that the file corresponding to the jgroups-configuration parameter is 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 multiplexerStack the attribute. Please note that the jgroups multiplexer has been deprecated by the jgroups Team and has been replaced with the shared transport so it is highly recommended you not use it anymore.
jbosscache-cluster-nameCluster name which must be unique.
max-volatile-timeMax time to live for Volatile Index.
rdbms-reindexingIndicate that it is needed to use RDBMS re-indexing mechanism if possible. The default value is "true".
reindexing-page-sizeThe maximum amount of nodes which can be retrieved from storage for re-indexing purpose. The default value is "100".
index-recovery-modeIf the parameter has been set to from-indexing, 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-filterDefine implementation class or classes of RecoveryFilters, the mechanism of index synchronization for Local Index strategy.
async-reindexingControl the process of re-indexing on JCR's startup. If a flag is set, indexing will be launched asynchronously without blocking the JCR. Its default value is "false".
indexing-thread-pool-sizeDefine the total amount of indexing threads.

Note

  • If you use postgreSQL and the rdbms-reindexing parameter is set to "true", the performance of the queries used while indexing can be improved by setting the 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.

  • If you use DB2 and the rdbms-reindexing parameter is set to "true", the performance of the queries used while indexing can be improved by making statistics 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.

For both cluster-ready implementations JBoss Cache, JGroups and Changes Filter values must be defined. Shared index requires some types of remote or shared file systems to be attached in a system (for example, NFS and SMB).

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.


<workspace name="ws">
   <query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">
      <properties>
         <property name="index-dir" value="/mnt/nfs_drive/index/db1/ws" />
         <property name="changesfilter-class"
            value="org.exoplatform.services.jcr.impl.core.query.jbosscache.JBossCacheIndexChangesFilter" />
         <property name="jbosscache-configuration" value="jbosscache-indexer.xml" />
         <property name="jgroups-configuration" value="udp-mux.xml" />
         <property name="jgroups-multiplexer-stack" value="false" />
         <property name="jbosscache-cluster-name" value="JCR-cluster-indexer" />
         <property name="max-volatile-time" value="60" />
         <property name="rdbms-reindexing" value="true" />
         <property name="reindexing-page-size" value="1000" />
         <property name="index-recovery-mode" value="from-coordinator" />
         <property name="jbosscache-shareable" value="true" />
      </properties>
   </query-handler>
</workspace>

To use cluster-ready strategy based on local indexes, the following configuration must be applied when each node has its own copy of index on local file system. 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".


<workspace name="ws">
   <query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">
      <properties>
         <property name="index-dir" value="/mnt/nfs_drive/index/db1/ws" />
         <property name="changesfilter-class"
            value="org.exoplatform.services.jcr.impl.core.query.jbosscache.LocalIndexChangesFilter" />
         <property name="jbosscache-configuration" value="jbosscache-indexer.xml" />
         <property name="jgroups-configuration" value="udp-mux.xml" />
         <property name="jgroups-multiplexer-stack" value="false" />
         <property name="jbosscache-cluster-name" value="JCR-cluster-indexer" />
         <property name="max-volatile-time" value="60" />
         <property name="rdbms-reindexing" value="true" />
         <property name="reindexing-page-size" value="1000" />
         <property name="index-recovery-mode" value="from-coordinator" />
         <property name="jbosscache-shareable" value="true" />
      </properties>
   </query-handler>
</workspace>

Local Index Recovery Filters

Common usecase for all cluster-ready applications is a hot joining and leaving of processing units. All nodes that are joining cluster for the first time or after some downtime must be in a synchronized state.

When having a deal with shared value storages, databases and indexes, cluster nodes are synchronized anytime. However it is an issue when local index strategy is used. If the new node joins cluster having no index, it will be retrieved or recreated. Node can be restarted also and thus index is 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:


<property name="index-recovery-filter" value="org.exoplatform.services.jcr.impl.core.query.lucene.DocNumberRecoveryFilter" />

Filter number is not limited so they can be combined:


<property name="index-recovery-filter" value="org.exoplatform.services.jcr.impl.core.query.lucene.DocNumberRecoveryFilter" />
<property name="index-recovery-filter" value="org.exoplatform.services.jcr.impl.core.query.lucene.SystemPropertyRecoveryFilter" />

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

This feature uses the standard index recovery mode defined by previously described parameter (can be "from-indexing" or "from-coordinator" (default value)).


<property name="index-recovery-mode" value="from-coordinator" />

There are couple implementations of filters:

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 or "re-indexing". There are various usecases when re-indexing is 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.

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 the server is started, missing indexes are automatically recovered by re-indexing.

JCR Supports direct RDBMS re-indexing, that is usually faster than ordinary and can be configured via the rdbms-reindexing QueryHandler parameter set to "true" (Refer to the Query-handler configuration overview for more information).

Another new feature is the asynchronous indexing on startup. Usually the startup is blocked until the process is finished. Block can take any period of time, depending on amount of data persisted in repositories. However, this can be resolved by using an asynchronous approach of startup indexation. In brief, 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 the index creation has been completed. Checking index state is possible via QueryManagerImpl:

boolean online = ((QueryManagerImpl)Workspace.getQueryManager()).getQueryHandeler().isOnline();

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

[INFO] Setting index OFFLINE (repository/production[system]).

When the process has been finished, two events are logged:

[INFO] Created initial index for 143018 nodes (repository/production[system]).
[INFO] Setting index ONLINE (repository/production[system]).

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

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. The current version of JCR supports "Hot Asynchronous Workspace Reindexing" feature. It allows end-user (Service Administrator) to launch the process in background without stopping or blocking the whole application by using any JMX-compatible console (see the "JConsole in action" screenshot below).

The server can continue working as expected while index is re-created. This depends on the flag "allow queries", passed via JMX interface to re-index operation invocation. If the flag is set, the application continues working. However, there is one critical limitation that the end-users must be aware. If the index is frozen while background task is running, it means queries are performed on index present on the moment of task startup and data written into repository after startup will not be available through the search until the 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 the operation is finished, the stale indexes are replaced with the new creation, including newly added data. If the "allow queries" flag is set to "false", all queries will throw an exception while the task is running. The current state can be acquired using the following JMX operation:

Environment requirements

Configuration requirements

Configuration of every workspace in repository must contain the following parts:

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 JCR for JBoss Cache instances.

For indexer, lock manager and data container

Each mentioned component 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 maybe some other parameters. The simplest way to configure them is to define their own configuration files for each component in each workspace:


<property name="jbosscache-configuration" value="${gatein.jcr.index.cache.config}"/>

However, if there are few workspaces, configuring them in such a way can be painful and hard to manage JCR which 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 values in the JCR configuration file just below "jbosscache-configuration", as shown:

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.


<property name="jgroups-configuration" value="${gatein.jcr.jgroups.config}" />

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 is why JGroups offers multiplexer feature, providing the ability to use one single channel for a 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 JCR) and set "jgroups-multiplexer-stack" to "true".


<property name="jgroups-configuration" value="jar:/conf/portal/udp-mux.xml" />
<property name="jgroups-multiplexer-stack" value="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. 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).


<property name="jgroups-configuration" value="jar:/conf/portal/udp-mux.xml" />
<property name="jgroups-multiplexer-stack" value="false" />

Allow sharing JBoss Cache instances

A JBoss Cache instance is quite resource consuming and there are three JBoss Cache instances by default (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 (for example: indexer, lock manager or data container). This feature is disabled by default and can be enabled at component configuration level (for example: 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 (for example: indexer, lock manager or data container) with the same JBoss Cache configuration (except the eviction configuration that can be different). This means all the parameters of type ${jbosscache-<parameter name>} must be identical between the components of same type of different workspaces. In other words, if you use the same values for the parameters of type ${jbosscache-<parameter name>} in each workspace, you 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.

Shipped JBoss Cache configuration templates

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.

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

Dependencies

RepositoryConfigurationService depends on the next components:

  • DBCreator which is used to create new database for each unbinded datasource.

  • BackupManager which is used to create repository from backup.

  • RPCService which is used for communicating between cluster-nodes.

    Note

    RPCService may not be configured. In this case, RepositoryService will work as a standalone service.

How it works

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

The following code shows all methods proposed by RepositoryCreationService that is used to create a new repository:

public interface RepositoryCreationService

{
   /**
    * Reserves, validates and creates repository in a simplified form.
    * 
    * @param rEntry - repository Entry - note that datasource must not exist.
    * @param backupId - backup id
    * @param creationProps - storage creation properties 
    * @throws RepositoryConfigurationException
    *          if some exception occurred during repository creation or repository name is absent in reserved list
    * @throws RepositoryCreationServiceException
    *          if some exception occurred during repository creation or repository name is absent in reserved list
    */
   void createRepository(String backupId, RepositoryEntry rEntry, StorageCreationProperties creationProps)
      throws RepositoryConfigurationException, RepositoryCreationException;
   /**
    * Reserves, validates and creates repository in a simplified form. 
    * 
    * @param rEntry - repository Entry - note that datasource must not exist.
    * @param backupId - backup id
    * @throws RepositoryConfigurationException
    *          if some exception occurred during repository creation or repository name is absent in reserved list
    * @throws RepositoryCreationServiceException
    *          if some exception occurred during repository creation or repository name is absent in reserved list
    */
   void createRepository(String backupId, RepositoryEntry rEntry) throws RepositoryConfigurationException,
      RepositoryCreationException;
   /**
    * Reserve repository name to prevent repository creation with same name from other place in same time
    * via this service.
    * 
    * @param repositoryName - repositoryName
    * @return repository token. Anyone obtaining a token can later create a repository of reserved name.
    * @throws RepositoryCreationServiceException if can't reserve name
    */
   String reserveRepositoryName(String repositoryName) throws RepositoryCreationException;
   /**
    * Creates repository, using token of already reserved repository name. 
    * Good for cases, when repository creation should be delayed or made asynchronously in dedicated thread. 
    * 
    * @param rEntry - repository entry - note, that datasource must not exist
    * @param backupId - backup id
    * @param rToken - token
    * @param creationProps - storage creation properties
    * @throws RepositoryConfigurationException
    *          if some exception occurred during repository creation or repository name is absent in reserved list
    * @throws RepositoryCreationServiceException
    *          if some exception occurred during repository creation or repository name is absent in reserved list
    */
   void createRepository(String backupId, RepositoryEntry rEntry, String rToken, StorageCreationProperties creationProps)
      throws RepositoryConfigurationException, RepositoryCreationException;
   /**
    * Creates  repository, using token of already reserved repository name. Good for cases, when repository creation should be delayed or 
    * made asynchronously in dedicated thread. 
    * 
    * @param rEntry - repository entry - note, that datasource must not exist
    * @param backupId - backup id
    * @param rToken - token
    * @throws RepositoryConfigurationException
    *          if some exception occurred during repository creation or repository name is absent in reserved list
    * @throws RepositoryCreationServiceException
    *          if some exception occurred during repository creation or repository name is absent in reserved list
    */
   void createRepository(String backupId, RepositoryEntry rEntry, String rToken)
      throws RepositoryConfigurationException, RepositoryCreationException;
   /**
    * Remove previously created repository. 
    * 
    * @param repositoryName - the repository name to delete
    * @param forceRemove - force close all opened sessions 
    * @throws RepositoryCreationServiceException
    *          if some exception occurred during repository removing occurred
    */
   void removeRepository(String repositoryName, boolean forceRemove) throws RepositoryCreationException;
}

TransactionServices provides access to the TransactionManager and the UserTransaction (See JTA specification for details).


JCR proposes out of the box several implementations, they all implement the abstract class org.exoplatform.services.transaction.impl.AbstractTransactionService. This main class implement the biggest part of all the methods proposed by the TransactionService. For each sub-class of AbstractTransactionService, you can set the transaction timeout by configuration using the value parameter timeout that is expressed in seconds.

JOTM in standalone mode

To use JOTM as TransactionManager in standalone mode, simply add the following component configuration:


<component>
    <key>org.exoplatform.services.transaction.TransactionService</key>
    <type>org.exoplatform.services.transaction.impl.jotm.TransactionServiceJotmImpl</type>
    <!-- Uncomment the lines below if you want to set default transaction timeout that is expressed in seconds -->
    <!--init-params>
        <value-param>
            <name>timeout</name>
            <value>60</value>
        </value-param>
    </init-params-->
</component>

Generic TransactionService

If you intend to use JBoss Cache, you can use a generic TransactionService based on its TransactionManagerLookup which is able to automatically find the TransactionManager of several Application Servers thanks to a set of JNDI lookups. This generic TransactionService covers mainly the TransactionManager lookups, the UserTransaction is actually simply the TransactionManager instance that has been wrapped. See the configuration example as below:


<!-- Configuration of the TransactionManagerLookup -->
<component>
  <key>org.jboss.cache.transaction.TransactionManagerLookup</key>
  <type>org.jboss.cache.transaction.GenericTransactionManagerLookup</type>
</component>
<!-- Configuration of the TransactionService -->
<component>
  <key>org.exoplatform.services.transaction.TransactionService</key>
  <type>org.exoplatform.services.transaction.jbosscache.GenericTransactionService</type>
  <!-- Uncomment the lines below if you want to set default transaction timeout that is expressed in seconds -->
  <!--init-params>
     <value-param>
        <name>timeout</name>
        <value>60</value>
     </value-param>
  </init-params-->
</component>

Specific GenericTransactionService for JBoss Cache and Arjuna

If you intend to use JBoss Cache with Arjuna, you can use a more specific GenericTransactionService. It is mostly interesting in case you want to use the real UserTransaction. See the configuration example as below:


<!-- Configuration of the TransactionManagerLookup -->
<component>
  <key>org.jboss.cache.transaction.TransactionManagerLookup</key>
  <type>org.jboss.cache.transaction.JBossStandaloneJTAManagerLookup</type>
</component>
<!-- Configuration of the TransactionService -->
<component>
  <key>org.exoplatform.services.transaction.TransactionService</key>
  <type>org.exoplatform.services.transaction.jbosscache.JBossTransactionsService</type>
  <!-- Uncomment the lines below if you want to set default transaction timeout that is expressed in seconds -->
  <!--init-params>
     <value-param>
        <name>timeout</name>
        <value>60</value>
     </value-param>
  </init-params-->
</component>

A very specific TransactionService for JBoss AS

If you intend to use JBoss AS with JBoss Cache, you can use a very specific TransactionService for JBoss AS. See the configuration example as below:


<component>
    <key>org.exoplatform.services.transaction.TransactionService</key>
    <type>org.exoplatform.services.transaction.impl.jboss.JBossTransactionService</type>
    <!-- Uncomment the lines below if you want to set default transaction timeout that is expressed in seconds -->
    <!--init-params>
        <value-param>
            <name>timeout</name>
            <value>60</value>
        </value-param>
    </init-params-->
</component>

TransactionsEssentials in standalone mode.

To use TransactionsEssentials, simply add the following component configuration:


<component>
  <key>org.exoplatform.services.transaction.TransactionService</key>
  <type>org.exoplatform.services.transaction.impl.atomikos.TransactionsEssentialsTransactionService</type>
  <!-- Uncomment the lines below if you want to set default transaction timeout that is expressed in seconds -->
  <!--init-params>
     <value-param>
        <name>timeout</name>
        <value>60</value>
     </value-param>
  </init-params-->
</component>

By default JCR Values are stored in the Workspace Data container along with the JCR structure (for example: Nodes and Properties). 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 instance.

Value storage configuration is a part of Repository configuration. See more details here.

Tree-based storage is recommended for most of cases. Simple 'flat' storage is good in speed of creation/deletion of values, it might be a compromise for a small storage.

Holds Values in tree-like FileSystem files. The path property points to the root directory to store the files.

This is a recommended type of external storage, it can contain a large amount of files limited only by disk/volume free space.

A disadvantage is that it is a higher time on Value deletion due to unused tree-nodes remove.


<value-storage id="Storage #1" class="org.exoplatform.services.jcr.impl.storage.value.fs.TreeFileValueStorage">
    <properties>
        <property name="path" value="data/values"/>
    </properties>
    <filters>
        <filter property-type="Binary" min-value-size="1M"/>
    </filters>

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. For example, storage for binary values with size greater of 1MB. It is 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. Here is a value which matches with the min-value-size 20 MB-sized filter and will be stored in the "data/20Mvalues" path, all others in "data/values".


<value-storages>
    <value-storage id="Storage #1" class="org.exoplatform.services.jcr.impl.storage.value.fs.TreeFileValueStorage">
        <properties>
            <property name="path" value="data/20Mvalues"/>
        </properties>
        <filters>
            <filter property-type="Binary" min-value-size="20M"/>
        </filters>
    <value-storage>
    <value-storage id="Storage #2" class="org.exoplatform.services.jcr.impl.storage.value.fs.TreeFileValueStorage">
        <properties>
            <property name="path" value="data/values"/>
        </properties>
        <filters>
            <filter property-type="Binary" min-value-size="1M"/>
        </filters>
    <value-storage>
<value-storages>

JCR supports Content-addressable storage feature for Values storing.

Content Addressable Value storage stores unique content once. Different properties (values) with the same content will be stored as one data file shared between those values. You 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.

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 is changed. CAS write operations are much more expensive compared to the non-CAS storages.

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

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 you do for other Value Storages.


<workspaces>
    <workspace name="ws">
        <container class="org.exoplatform.services.jcr.impl.storage.jdbc.optimisation.CQJDBCWorkspaceDataContainer">
        <properties>
            <property name="source-name" value="jdbcjcr"/>
            <property name="dialect" value="oracle"/>
            <property name="multi-db" value="false"/>
            <property name="update-storage" value="false"/>
            <property name="max-buffer-size" value="200k"/>
            <property name="swap-directory" value="target/temp/swap/ws"/>
        </properties>
        <value-storages>
<!------------------- here ----------------------->
        <value-storage id="ws" class="org.exoplatform.services.jcr.impl.storage.value.fs.CASableTreeFileValueStorage">
            <properties>
(1)                <property name="path" value="target/temp/values/ws"/>
(2)                <property name="digest-algo" value="MD5"/>
(3)                <property name="vcas-type" value="org.exoplatform.services.jcr.impl.storage.value.cas.JDBCValueContentAddressStorageImpl"/>
(4)                <property name="jdbc-source-name" value="jdbcjcr"/>
                <property name="jdbc-dialect" value="oracle"/>
            </properties>
            <filters>
                <filter property-type="Binary"/>
            </filters>
        </value-storage>
    </value-storages>
1

digest-algo: Digest hash algorithm (MD5 and SHA1 were tested).

2

vcas-type: Value CAS internal data type, JDBC backed is currently implemented org.exoplatform.services.jcr.impl.storage.value.cas.JDBCValueContentAddressStorageImpl.

3

jdbc-source-name: the JDBCValueContentAddressStorageImpl specific parameter, database will be used to save CAS metadata. It is simple to use the same as in workspace container.

4

jdbc-dialect: the JDBCValueContentAddressStorageImpl specific parameter, database dialect. It is simple to use the same as in workspace container.

Copyright © 2009-2012. All rights reserved. eXo Platform SAS