how to config to support multiple workspance for spingmodules jcr?
When i study the springmodules jcr project and souce,i dont found how to config it to support multiple workspace?
Any suggestion?
Thx!
Multiple Workspaces with JCR
If you only have one workspace (or some fixed number of them) in your app, you can use dependency injection to manage them all (in your applicationContext.xml files, you'd create a JcrSessionFactory for each repsoitory/workspace/credentials combination, and create a corresponding JcrDaoSupport subclass for each of those JcrSessionFactory beans.
In my case, I've got a workspace for each user and that means I can't create them until the application is running and I have user accounts created. The system I'm working on solves the problem of how you do SpringModules JCR based access to workspaces created dynamically, while the application is running. I'm still working on my solution to this problem. When I confirm that it works for my app, I'm going to write a generalized example and some documentation, as I have time.
The way I've got it set up, there's two hierarchies: a repository-based hierarchy and a workspace-based hierarchy. The repository-based hierarchy is intended to be the "visible" outer layer of the system; this is where I implement an interface that is used by other parts of my application. The workspace-based hierarchy is used to actually perform the work of accessing Nodes in the repository's workspaces and is only used by the repository-based class hierarchy. Here's more detailed descriptions:
Class: AbstractJcrDao
Extends: Object
Implements: <none>
Description:
This class has several properties that will be used by subclasses: systemRepository, systemCredentials, requireTransactionalSession. All three can be set by dependency injection. In my app, I have only one user/pass, so this is a good place for me to keep the Credentials; if you have multiple users for your repository, you may want to put the credentials elsewhere. There are setters and getters for each of the properties. There is also a protected createWorkspace( String name ) method that subclasses can use to create their needed workspaces.
The createWorkspace( String name ) method works by first creating a JcrSessionFactory with the repository, default workspace, and system credentials. It calls jcrTemplate.setAllowCreate( !requireTransactionalSession ) to enforce the the transactional requirement (which I need in my app). I then call jcrTemplate.execute( new JcrCallback() {...}, true ); and in the callback create the new workspace. Since creating a workspace is implementation dependent, I may move this to a utility interface that can be implemented for each JCR package I work with, but right now I'm only using Jackrabbit so this isn't a priority for me. Actually, Costin, if you read this, that would be a useful hierarchy to have someplace in the SpringModule's JCR package, possibly an addition to JcrUtils()?
Class: JcrWorkspaceDao
Extends: JcrDaoSupport
Implements: <none>
Description:
This is my other abstract class. While AbstractJcrDao and its hierarchy is concerned with access to the entire repository, this hierarchy focuses on a single Workspace; most of the actual work takes place in this hierarchy. Currently it provides two functions: checkWorkspace() and logNodeTree( Node n ). The former method simply tries to create a new session by calling getJcrTemplate().execute( new JcrCallback() { ... }, true ); and simply logging the fact that the instance has been created for the particular workspace. The latter method simply walks a a Node hierarchy and logs the names of the nodes in the hierarchy; this is a useful tool for ensuring the workspace contains the node tree you think it does.
Class: Jcr<DataType>Dao
Extends: AbstractJcrDao
Implements: <DataType>Dao
Description:
In my system, I've got one workspace per user, with several types of data stored in each workspace; therefore, I have one class of this sort for each of those data types. The interface that it implements defines the action methods that can be called, much as you would expect for a database DAO; in fact, you should be able to replace the Jcr-based DAO with a JDBC-based DAO and the rest of your code won't notice because only your Spring dependency injection file had to change. This class implements those action methods (addSomething( ... ), findSomething( ... ), deleteSomething( ... ), etc.). Each of these action methods is delegated to a subclass of JcrWorkspaceDao which is created by the Jcr<DataType>WorkspaceDao getWorkspaceDao( String name ) method in this class.
Jcr<DataType>WorkspaceDao getWorkspaceDao( String name ) works by creating a JcrSessionFactory with the repository and credentials from the abstract superclass, and the workspace name given to it by the action method. I then create a JcrTemplate with the session factory, and call jcrTemplate.setAllowCreate( !isTransactionRequired() ) to ensure that I get transactional sessions when I need them (at runtime and in Integration testing, but not during Unit testing). With the JcrTemplate, I create a new Jcr<DataType>WorkspaceDao instance (the JcrTemplate is passed to the constructor). If the workspace specified by the session doesn't exist, the constructor throws a DataAccessResourceFailureException (from the org.springframework.dao package). I check the cause of the exception to ensure it really is a NoSuchWorkspaceException (from the javax.jcr package). If it is, I call the superclass createWorkspace() method for the workspace name. Assuming that succeeds, I try creating the new Jcr<DataType>WorkspaceDao instance again (this time I don't try to catch any exceptions). I then call the workspaceDao.initialize() method to create required or default Nodes in the new workspace.
Class: Jcr<DataType>WorkspaceDao
Extends: JcrWorkspaceDao
Implements: <none>
Description:
This is where the real work of this contraption is done. All the major interactions with the JCR Session objects take place here. The only constructor requires a JcrTemplate, which is passed to the superclass's setJcrTemplate() method (in JcrDaoSupport) before calling the superclass afterPropertiesSet() method (also in JcrDaoSupport). The purpose with these first two calls is to simulate the dependency injection workflow. I then call the checkWorkspace() method in the immediate superclass, JcrWorkspaceDao, to ensure the workspace has been created. If the workspace doesn't exist, the JcrSessionFactory will fail to create a session and throw an exception which is allowed to fall back to the caller to be processed as described above.
There's also an initialize() method to do initial setup in a newly created workspace for this type of data. All the other methods are action methods that actually add, delete, search, and edit Nodes in the workspace.
---
So far, things are looking good. Workspaces get created on the fly as they are needed, and they are initialized. I'm having trouble with actually creating Nodes and setting properties in my action methods, but this seems to be an issue I'm having with Jackrabbit, not SpringModule's JCR package. Hopefully I'll get it worked out in the next day or two and be able to confirm that this setup actually does what its intended to do.
Mark,can you post some code?
Mark,thank you very much!
Great idea!
Can you post some code?
Thx!