JCR Session Pooling Repository

1. Overview

Applications that interact with a Java Content Repository (JCR) often require efficient management of JCR sessions. Creating a new JCR session for every user request can introduce significant overhead, especially in public-facing applications with many concurrent users. Typically, only a small subset of users are actively making requests at any given time, and a JCR session is only needed during request processing.

To address this, you can use a pool of open JCR sessions shared among users. The application itself authenticates with the JCR repository and manages user accounts internally.

The HST Session Pools package uses the Apache Commons Pool library to provide object pooling for JCR sessions. In addition, the package offers MultipleRepository, LazyMultipleRepository, and BasicPoolingRepository components for advanced session pool management.

2. BasicPoolingRepository: Implementing javax.jcr.Repository

The HST Session Pooling package implements the javax.jcr.Repository interface, following a strategy similar to Commons DBCP. It decorates JCR sessions, allowing you to use the standard JCR API without needing to manage session pooling directly.

Both javax.sql.DataSource and javax.jcr.Repository serve as entry points for obtaining a connection or session. For example, DataSource#getConnection() is analogous to Repository#login().

By configuring the HST Session Pooling Component as a JNDI resource, you can use the standard JCR API to obtain pooled sessions. The following example demonstrates this approach:

Context initCtx = new InitialContext(); Context envCtx = initCtx.lookup("java:comp/env"); // retrieves the pooling repository by JNDI look up. Repository repository = (Repository) envCtx.lookup("jcr/repository"); Session jcrSession = repository.login(new SimpleCredentials("admin", "admin".toCharArray()); // use session // ... // returns the session to the pooling repository via the #logout() method. jcrSession.logout();

BasicPoolingRepository wrapping JCR repository and pooled session access

Diagram: The diagram illustrates BasicPoolingRepository as a central component implementing the javax.jcr.Repository interface. It wraps an internal repository and manages session creation and pooling. Clients interact with the pooling repository using the standard JCR API, retrieving sessions with login() and returning them with logout().

In this architecture, BasicPoolingRepository manages the internal repository and the session pool. Clients obtain a pooled JCR session using the login() method and return it to the pool by calling logout() on the session.

3. BasicPoolingRepository Configuration

You can configure BasicPoolingRepository components using a Spring Framework XML configuration file.

The default configuration for the session pooling repository is located in hst-core-2.xx.xx.jar!/org/hippoecm/hst/site/container/SpringComponentManager-jcr.xml:

<bean class="org.hippoecm.hst.core.jcr.pool.BasicPoolingRepository" init-method="initialize" destroy-method="close"> <!-- delegated JCR repository --> <property name="repositoryProviderClassName" value="${repositoryProviderClassName}" /> <property name="repositoryAddress" value="${default.repository.address}"/> <property name="defaultCredentialsUserID" value="${default.repository.user.name} ${repository.pool.user.name.separator} ${default.repository.pool.name}"/> <property name="defaultCredentialsUserIDSeparator" value="${repository.pool.user.name.separator}"/> <property name="defaultCredentialsPassword" value="${default.repository.password}"/> <property name="hstJmvEnabledUsers" ref="hstJmvEnabledUsers"/> <!-- Pool properties. Refer to the GenericObjectPool of commons-pool library. --> <property name="maxActive" value="${default.repository.maxActive}"/> <property name="maxIdle" value="${default.repository.maxIdle}"/> <property name="minIdle" value="0"/> <property name="initialSize" value="0"/> <property name="maxWait" value="10000"/> <property name="testOnBorrow" value="true"/> <property name="testOnReturn" value="false"/> <property name="testWhileIdle" value="false"/> <property name="timeBetweenEvictionRunsMillis" value="60000"/> <property name="numTestsPerEvictionRun" value="1"/> <property name="minEvictableIdleTimeMillis" value="300000"/> <property name="refreshOnPassivate" value="true"/> <property name="maxRefreshIntervalOnPassivate" value="${sessionPool.maxRefreshIntervalOnPassivate}"/> <property name="poolingCounter" ref="defaultPoolingCounter" /> <property name="maxTimeToLiveMillis" value="${default.repository.maxTimeToLiveMillis}"/> </bean>

Note: Variable expressions such as ${default.repository.address} must be defined in /WEB-INF/hst-config.properties or provided by the system in SpringComponentManager.properties.

The following table describes all configurable parameters:

ParameterDefaultDescription
repositoryProviderClassNameJcrHippoRepositoryProvider (in org.hippoecm.hst.core.jcr.pool)Class name of the JCR repository provider. The default provider creates a HippoRepository using HippoRepositoryFactory.
repositoryAddressJCR Repository URL used by the repository provider.
defaultCredentialsUserIDDefault username for establishing a JCR session.
defaultCredentialsUserIDSeparator@Separator between the repository username and pool-specific suffix. For example, "siteuser@default" uses "admin" as the repository username and "@default" as the suffix. The pool uses "admin" internally but distinguishes pools by the suffix. See the MultipleRepository section for details.
defaultCredentialsPasswordDefault password for establishing a JCR session.
maxActive100Maximum number of active JCR sessions in the pool, or negative for no limit.
maxIdle25Maximum number of idle JCR sessions in the pool, or negative for no limit.
minIdle0Minimum number of idle JCR sessions in the pool, or zero for none.
initialSize0Number of JCR sessions created when the pool starts.
maxWait-1 (indefinitely)Maximum milliseconds to wait for a session when none are available. -1 waits indefinitely.
validationQueryJCR query used to validate sessions before returning them. By default, the pool validates sessions using Session#isLive().
testOnBorrowtrueWhether to validate objects before borrowing from the pool. Invalid objects are dropped and another is borrowed.
testOnReturnfalseWhether to validate objects before returning them to the pool.
testWhileIdlefalseWhether idle objects are validated by the evictor thread. Invalid objects are dropped.
timeBetweenEvictionRunsMillis-1Milliseconds between idle object evictor runs. Non-positive disables the evictor thread.
numTestsPerEvictionRun3Number of objects examined per idle object evictor run.
minEvictableIdleTimeMillis180000Minimum time (ms) an object may sit idle before eligible for eviction.
refreshOnPassivatetrueWhether to refresh session objects when returned to the pool.
maxRefreshIntervalOnPassivate300000Minimum interval (ms) between session refreshes on passivation.
sessionsRefreshPendingTimeMillis0If set to a positive value, sessions refreshed before this timestamp are refreshed again.
keepChangesOnRefreshfalseWhether to refresh session objects using the keepChanges option.
whenExhaustedActionblockBehavior when the pool is exhausted:
- block: Wait for a session to become available, up to maxWait ms, then throw NoAvailableSessionException.
- fail: Immediately throw NoAvailableSessionException if no session is available.
- grow: Create a new session, ignoring maxActive.
poolingCounterInstance of DefaultPoolingCounterUsed for monitoring session pool usage via JMX Beans. JMX support is enabled by default.
maxTimeToLiveMillis1 hourMaximum lifetime (ms) for a pooled JCR session.

4. MultipleRepository Component

BasicPoolingRepository manages a single session pool. Some applications require multiple session pools for different purposes, such as one pool for binary resource access and another for content editing.

MultipleRepository enables you to access multiple session pools transparently through the standard JCR API. It delegates Repository API calls to the appropriate underlying BasicPoolingRepository based on the credentials provided.

The org.hippoecm.hst.core.jcr.pool.MultipleRepository interface extends javax.jcr.Repository. When you call login() with specific credentials, the component selects the corresponding session pool.

If two pools use the same username, you can differentiate them by adding a suffix separated by a delimiter (default: @). For example, use "siteuser@default" for one pool and "siteuser@binaries" for another.

Example configuration:

<bean id="javax.jcr.Repository" class="org.hippoecm.hst.core.jcr.pool.MultipleRepositoryImpl"> <!-- Delegating session pool repositories map --> <constructor-arg> <map> <entry key-ref="javax.jcr.Credentials.default"> <!-- A BasicPoolingRepository here --> <bean class="org.hippoecm.hst.core.jcr.pool.BasicPoolingRepository" init-method="initialize" destroy-method="close"> <!-- SNIP --> </bean> </entry> <!-- SNIP --> <entry key-ref="javax.jcr.Credentials.binaries"> <!-- A BasicPoolingRepository here --> <bean class="org.hippoecm.hst.core.jcr.pool.BasicPoolingRepository" init-method="initialize" destroy-method="close"> <!-- SNIP --> </bean> </entry> </map> </constructor-arg> <!-- The default credentials for login() without credentials argument --> <constructor-arg ref="javax.jcr.Credentials.default" /> </bean>

In this configuration, the first constructor argument is a map of session pools keyed by credentials. The second argument specifies the default credentials for calls to login() without parameters.

5. LazyMultipleRepository Component

While MultipleRepository is suitable for predefined session pools, some scenarios require session pools to be created on demand. LazyMultipleRepository supports this by creating internal session pools when first requested with new credentials.

By default, LazyMultipleRepository is configured in SpringComponentManager-jcr.xml as follows:

<bean id="javax.jcr.Repository" class="org.hippoecm.hst.core.jcr.pool.LazyMultipleRepositoryImpl"> <!-- Delegating session pool repositories map --> <constructor-arg> <map> <entry key-ref="javax.jcr.Credentials.default"> <!-- A BasicPoolingRepository here --> <bean class="org.hippoecm.hst.core.jcr.pool.BasicPoolingRepository" init-method="initialize" destroy-method="close"> <!-- SNIP --> </bean> </entry> <!-- SNIP --> <entry key-ref="javax.jcr.Credentials.binaries"> <!-- A BasicPoolingRepository here --> <bean class="org.hippoecm.hst.core.jcr.pool.BasicPoolingRepository" init-method="initialize" destroy-method="close"> <!-- SNIP --> </bean> </entry> </map> </constructor-arg> <!-- The default credentials for login() without credentials argument --> <constructor-arg ref="javax.jcr.Credentials.default" /> <!-- Default configuration map for on-demand BasicPoolingRepository creation --> <constructor-arg> <map key-type="java.lang.String" value-type="java.lang.String"> <entry key="repositoryProviderClassName" value="${repositoryProviderClassName}" /> <entry key="repositoryAddress" value="${default.repository.address}"/> <!-- SNIP --> <entry key="maxActive" value="${disposable.repository.maxActive}"/> <!-- SNIP --> </map> </constructor-arg> <property name="timeBetweenEvictionRunsMillis" value="${disposable.global.repository.timeBetweenEvictionRunsMillis}"/> <property name="disposableUserIDPattern" value=".*;disposable"/> </bean>

LazyMultipleRepository introduces two additional properties:

ParameterDefaultDescription
timeBetweenEvictionRunsMillis0Interval (ms) for the evictor thread to run and remove unused session pools. A session pool is considered unused if it has zero idle and zero active sessions. The evictor runs only if this value is positive.
disposableUserIDPatternRegular expression used to match repository credential usernames during eviction. Only session pools matching this pattern can be evicted. If not set, no session pool is evicted.

6. JNDI Resources

Note: As of version 17.0.0, RMI and related parameters (repository-address, start-remote-server) are no longer supported. BasicPoolingRepositoryFactory and MultiplePoolingRepositoryFactory are also no longer supported.

Two implementations of javax.naming.spi.ObjectFactory are available: BasicPoolingRepositoryFactory and MultiplePoolingRepositoryFactory.

For example, in Tomcat, you can configure a JCR session pool as a JNDI resource in the application context descriptor:

<Context> <Resource name="jcr/repository" auth="Container" type="javax.jcr.Repository" factory="org.hippoecm.hst.core.jcr.pool.BasicPoolingRepositoryFactory" repositoryAddress="rmi://127.0.0.1:1099/hipporepository" defaultCredentialsUserID="admin" defaultCredentialsPassword="admin" maxActive="250" maxIdle="50" initialSize="0" maxWait="10000" testOnBorrow="true" testOnReturn="false" testWhileIdle="false" timeBetweenEvictionRunsMillis="60000" minEvictableIdleTimeMillis="60000" /> </Resource> </Context>

This configuration sets up a BasicPoolingRepository session pool.

To configure a MultipleRepository with multiple session pools, use MultiplePoolingRepositoryFactory and separate each pool's properties with commas:

<Context> <Resource name="jcr/repository" auth="Container" type="javax.jcr.Repository" factory="org.hippoecm.hst.core.jcr.pool.MultiplePoolingRepositoryFactory" repositoryAddress="rmi://127.0.0.1:1099/hipporepository, rmi://127.0.0.1:1099/hipporepository" defaultCredentialsUserID="admin, editor" defaultCredentialsPassword="admin, editor" maxActive="250, 250" maxIdle="50, 50" initialSize="0, 0" maxWait="10000, 10000" testOnBorrow="true, true" testOnReturn="false, false" testWhileIdle="false, false" timeBetweenEvictionRunsMillis="60000, 60000" minEvictableIdleTimeMillis="60000, 60000" /> </Resource> </Context>

To use these JNDI resources, declare them in your web.xml:

<resource-ref> <description>JCR Repository</description> <res-ref-name>jcr/repository</res-ref-name> <res-type>javax.jcr.Repository</res-type> <res-auth>Container</res-auth> </resource-ref>

You can then access the session pool in JSP pages or Java classes:

<%@ page language="java" import="javax.jcr.*, javax.naming.*" %> <% Context initCtx = new InitialContext(); Context envCtx = (Context) initCtx.lookup("java:comp/env"); // look up the session pool Repository repository = (Repository) envCtx.lookup("jcr/repository"); // borrow a JCR session with login() method. Session jcrSession = repository.login(new SimpleCredentials("admin", "admin".toCharArray()); // do something ... // ... // return the JCR session to the pool with logout() method. jcrSession.logout(); %>
Share Feedback
Page: /build/web-application/jcr-session-pooling-repository
Section: Build
Category *
JCR Session Pooling Repository | Bloomreach Content Documentation