Skip to end of metadata
Go to start of metadata

Before your application can do anything with a JCR repository, it first has to find the javax.jcr.Repository instance and use it to establish a javax.jcr.Session. This page shows the ways of using the JCR API to do exactly this.

Getting a Repository

The JCR 2.0 specification defines two primary ways to obtain a Repository instance, and neither require your application to use any implementation-specific code.

Using JNDI

One of the more popular ways to find a Repository instance is to use JNDI, though this only works in environments like web servers or application servers that contain a JNDI implementation. It also assumes that a Repository instance has already been registered in JNDI; how this is done is specific to the environment.

For example, consider that our environment has registered our Repository in JNDI with the name "jcr". We can then simply use JNDI to obtain the instance using the JNDI API (in the "javax.naming" package):

Different environments require different techniques to obtain the javax.naming.Context object. The above example is what's used in the Tomcat web server. Some app servers allow you to directly look up components in JNDI using the InitialContext.

Using RepositoryFactory

The JCR 2.0 specification defined a new mechanism to find Repository instances without relying upon JNDI, and this works in any Java application.

Implementations that support this mechanism implement the javax.jcr.RepositoryFactory interface, and define a resource in their JARs that registers their implementation with the Java SE Service Loader facility.

The only thing a JCR client application needs to do is use the ServiceLoader facility to loop over all the RepositoryFactory implementations and ask each one to create (or obtain) a repository given a set of supplied parameters:

The parameters are implementation-specific, but you can keep your application independent of the JCR implementation by simply loading the properties from a resource file.

Getting a Session

Once you've gotten hold of your Repository instance, your application can connect to it by passing a set of credentials identifying the user and the name of the workspace that the user wishes to use. The repository will return a javax.jcr.Session instance that has the privileges awarded to that user per the credentials. The Session can be used to read, query, observe, or change repository content.

The Repository interface defines a login method that takes the users javax.jcr.Credentials object and the name of the workspace. The signature of this method is as follows:

The application can supply null values for either or both of the "credentials" and "workspaceName". When no credentials is provided, the repository can use an external mechanism to identify and authenticate the user. When no workspace name is provided, the repository chooses a default workspace.

For convenience, the Repository interface defines three other forms of login that take different combinations of the credentials and/or workspace name, but all are simple wrappers around the primary login(Credentials,String) method.

Credentials

The JCR API defines a javax.jcr.Credentials marker interface that is intended to encapsulate the information necessary to identify, authenticate, and authorize a particular user. JCR implementations can define their own implementations, or they can reuse the two concrete Credentials implementation classes:

  • javax.jcr.SimpleCredentials - A Credentials that identifies a user with a username and password.
  • javax.jcr.GuestCredentials - A Credentials that can be used to obtain an anonymous session.

Credentials don't need to be supplied, either. In such cases, the repository can use an external mechanism to authenticate and authorize the current user. Many implementations support JAAS, allowing the login context currently associated with the thread to perform the authentication.

Thread-safety

JCR sessions are intended to be lightweight, so creating them should be very fast. But they are not thread safe, so they shouldn't be used concurrently by multiple threads. Therefore, most applications should probably create a session to read, query or change repository content, and then quickly close that session. Web applications, for example, often will obtain a Session for each incoming request, use that session to process the request, and then will close the session.

Although not required by the specification, ModeShape 3 sessions are thread-safe and can be used by multiple threads. Care still needs to be used, however, as any transient state of a session used by multiple threads will be visible to all components using that session.

Long-running Sessions

One exception to the short-lived session pattern is that an application can only register listeners to the repository through a Session. When a session is closed, all listeners registered with that session are unbound. So if an application requires listeners to exist for long periods of time, the application will have to use a long-lived session.

Applications should use these long-lived sessions only to register listeners, and should never use the session to process any of the events. This is because the session is not thread-safe, and the listeners will often be notified on separate threads. Instead, listener implementations should enqueue any work to be done and return without using the listener's session. Then, have separate threads that take items from the queue, obtain a new Session, perform the work, and close the session.

Getting off the listener thread is generally a good practice for asynchronous listeners, even if the Session implementations are thread-safe. This is because implementations may serialize delivery of the events to all listeners, so one listener that takes a long time to process each event might cause a delay for the other listeners.

Some JCR implementations can operate in a mode where the repository remains open only as long as there is at least one session. As such, your application might need to use a long-running session just to keep the repository running.

Making and persisting changes

The Session can be used to make changes to the persistent workspace content. However, all changes are transient and visible only to that Session until the save() method is called on the Session. At that point, the transient changes are persisted to the workspace, and generally become immediately visible to all other sessions.

Visibility of changes

The specification gives a fair amount of freedom to implementations in how and when a Session sees the changes persisted by other Session}}s. Some implementations use a copy-on-read behavior, where the {{Session obtains a snapshot of each node it accesses, and any changes to those nodes will only be seen by the session if/when it refreshes it's cached information or if/when it saves its state. Other implementations use a different copy-on-write behavior, where the only content a session is sure to see unchanged are the transient changes it has made, and will immediately see all other changes persisted by other sessions.

Be sure to know how the implementation you're using works.

ModeShape 3.x uses the copy-on-write behavior. Note that this is different than ModeShape 2.x, which used copy-on-read.

Transactions

When using transactions, the changes made by a Session are not persisted on "save()", but as expected are persisted when the transaction commits. Note that only those saved changes will be committed as part of the transaction, so be sure to call "save()" prior to committing the transaction.

Logging out

An application is expected to close each session after it is no longer needed, and this is done using the logout() method. This will immediately discard any transient (but unsaved) changes made with that session, and will immediately unregister all listeners that were registered using the session.

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.