Integration of Jackrabbit OCM and Spring (updated version)

March 26 20097 Commented

Categorized Under: Engroup, Java, Object Oriented and Design, Technology, Testing

In the previous post Jackrabbit OCM and Spring, I have outlined the way of integrating between Jacckrabbit OCM (jackrabbit 1.5.0-snapshot) and Spring. The time flies help us have more JCR knowledge, jackrabbit api becomes more stable and our Engroup ECM implementation is changed. Instead of creating the total new JCR node types for our domain classes in the previous version, we extends our node types base on the standard JCR node types as nt:file, nt:folder. In addition, our unit test is re-written for simpleness and make higher coverage rate.
You can download our example in example zip file with note that this example is the cut-off version of Engroup ECM API that limits handling simple file, folder instances. Because Engroup runs on Osgi platform, and we already
packaged our libraries as Osgi bundles. To reduce our efforts in editing this cut-off version, we use our libraries
at http://engroup.sourceforge.net/maven2/ instead the libraries in standard maven repository. It will takes your time to download Engroup libraries at the first time.

Basic Jackrabbit OCM and Spring configuration

<bean id="repository"
  class="org.springmodules.jcr.jackrabbit.RepositoryFactoryBean">
    <property name="configuration"
      value="classpath:jackrabbit-repo-test.xml" />
    <property name="homeDir" value="." />
 </bean>
 <bean id="jcrSessionFactory"
  class="org.springmodules.jcr.jackrabbit.ocm.JackrabbitSessionFactory">
     <property name="repository" ref="repository" />
     <property name="credentials">
         <bean class="javax.jcr.SimpleCredentials">
             <constructor-arg index="0" value="superuser" />
             <!-- create the credentials using a bean factory -->
             <constructor-arg index="1">
                 <bean factory-bean="password" factory-method="toCharArray" />
             </constructor-arg>
         </bean>
     </property>
     <property name="skipExistingNamespaces" value="true"/>
     <property name="namespaces">
         <props>
             <prop key="engroup">http://esofthead.com/engroup</prop>
         </props>
     </property>
     <property name="nodeTypes2Import" value="custom_nodetypes_test.xml" />
 </bean>
 <!-- create the password to return it as a char[] -->
 <bean id="password" class="java.lang.String">
     <constructor-arg index="0" value="superuser" />
 </bean>
 <bean id="jcrMappingTemplate"
  class="org.springmodules.jcr.jackrabbit.ocm.JcrMappingTemplate">
     <constructor-arg index="0" ref="jcrSessionFactory" />
     <constructor-arg index="1" ref="jcrMappingDescriptor" />
 </bean>



Declaring annotation classes

<bean id="jcrMappingDescriptor"
  class="org.apache.jackrabbit.ocm.mapper.impl.annotation.AnnotationMapperImpl">
    <constructor-arg index="0">
        <list>
            <value>com.engroup.module.ecm.domain.Content</value>
            <value>com.engroup.module.ecm.domain.File</value>
            <value>com.engroup.module.ecm.domain.Folder</value>
            <value>com.engroup.module.ecm.domain.Resource</value>
        </list>
     </constructor-arg>
 </bean>



Transaction support

 <bean id="jcrTransactionManager"
  class="org.springmodules.jcr.jackrabbit.LocalTransactionManager">
     <property name="sessionFactory" ref="jcrSessionFactory" />
 </bean>
 <bean id="baseTransactionProxy"
  class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
  abstract="true">
     <property name="transactionManager">
         <ref bean="jcrTransactionManager" />
     </property>
     <property name="transactionAttributes">
         <props>
             <prop key="save*">PROPAGATION_REQUIRED</prop>
             <prop key="*">PROPAGATION_REQUIRED,readonly</prop>
         </props>
     </property>
 </bean>
 <bean id="internalFileSystemService"
  class="com.engroup.module.ecm.service.impl.FileSystemServiceImpl">
     <property name="jcrTemplate" ref="jcrMappingTemplate" />
 </bean>
 <bean id="fileSystemService" parent="baseTransactionProxy">
     <qualifier value="proxy" />
     <property name="proxyInterfaces">
         <value>
             com.engroup.module.ecm.service.FileSystemService
         </value>
      </property>
      <property name="target">
          <ref bean="internalFileSystemService" />
      </property>
      <property name="transactionAttributes">
          <props>
              <prop key="*">PROPAGATION_REQUIRED</prop>
          </props>
      </property>
 </bean>



Custom JCR node types

Depends on your context, you could want to write custom JCR node types such as add description field into File class. Fairly easy to do so by declaring custom node types in custom_nodetypes_test.xml file:

<!--It is a must node type for OCM support-->
<nodeType name="ocm:discriminator" isMixin="true">
    <supertypes>
        <supertype>nt:base</supertype>
    </supertypes>
    <propertyDefinition name="ocm_classname" requiredType="String"
        autoCreated="false" mandatory="true" onParentVersion="COPY"
        protected="false" multiple="false" />
 </nodeType> 

 <nodeType name="engroup:hierarchyNode" isMixin="false"
  hasOrderableChildNodes="false" primaryItemName="">
     <supertypes>
         <supertype>nt:hierarchyNode</supertype>
     </supertypes>
     <propertyDefinition name="jcr:description" requiredType="String"
         autoCreated="false" mandatory="false" onParentVersion="COPY"
         protected="false" multiple="false" />
     <propertyDefinition name="jcr:owner" requiredType="String"
         autoCreated="false" mandatory="false" onParentVersion="COPY"
         protected="false" multiple="false" />
 </nodeType>



Writing unit test

Unit test is our standard and it is time to write some test cases to test Engroup ECM API. We will use the Spring
Test Framework to test our spring bean. One more concern is choosing appropriate Jackrabbit Persistent Manager for
testing. Jackrabbit supports various Persistent Manager such as File System, Database, InMemory. In our product, we
use Database Persistent Manager, but for testing purpose the InMemory Persistent Manager is the good choice for fast
and cleaning data after running test. You could configure it in Jackrabbit configuration file

<FileSystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem"/>
...Workspace section
    <PersistenceManager class="org.apache.jackrabbit.core.persistence.mem.InMemPersistenceManager">
        <param name="persistent" value="false"/>
    </PersistenceManager>
...Version section
    <PersistenceManager class="org.apache.jackrabbit.core.persistence.mem.InMemPersistenceManager">
        <param name="persistent" value="false"/>
    </PersistenceManager>

Write FileSystemService unit test:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/ecm-context-test.xml" })
public class FileSystemServiceTest {
    @Autowired
    @Qualifier("proxy")
    protected FileSystemService fileSystemService;
...
}



And add some test method:

@Test
public void testUpdateFileWithVersion() {
    Resource resource = new Resource();
    resource.setData(new ByteArrayInputStream("this is the content"
            .getBytes()));
    resource.setLastModified(Calendar.getInstance());
    resource.setMimeType("plain/text");
    File file = new File();
    file.setPath("/A");
    file.setResource(resource);
    fileSystemService.save(file);

    Resource resource2 = new Resource();
    resource2.setData(new ByteArrayInputStream("this is the content 2"
            .getBytes()));
    resource2.setLastModified(Calendar.getInstance());
    resource2.setMimeType("plain/x");
    file.setResource(resource2);
    fileSystemService.update(file);
    Collection<Version> versions = fileSystemService
            .getVersionHistory(ContentConstants.FILE_ROOT_PATH + "/A");
    Assert.assertEquals(2, versions.size());
    File latestFileVersion = fileSystemService.getFileByVersion(
            ContentConstants.FILE_ROOT_PATH + "/A", "1.0");
    Assert.assertEquals("plain/x", latestFileVersion.getResource()
            .getMimeType());
    fileSystemService.remove(file);
}

You could download the example, see more details how to integrate Jackrabbit OCM and Spring, run the test and provide your feedbacks if any.

Source code

Share and Enjoy:
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • BarraPunto
  • Bitacoras.com
  • blinkbits
  • BlinkList
  • blogmarks
  • BlogMemes
  • BlogMemes Cn
  • BlogMemes Fr
  • BlogMemes Jp
  • BlogMemes Sp
  • Blogosphere News
  • Blogsvine
  • blogtercimlap
  • Book.mark.hu
  • Bumpzee
  • co.mments
  • connotea
  • De.lirio.us
  • Design Float
  • DotNetKicks
  • DZone
  • eKudos
  • email
  • Fark
  • Faves
  • feedmelinks
  • Fleck
  • Furl
  • GeenRedactie
  • Global Grind
  • Gwar
  • Haohao
  • HealthRanker
  • Hemidemi
  • Identi.ca
  • IndianPad
  • Internetmedia
  • kick.ie
  • Kirtsy
  • laaik.it
  • Leonaut
  • LinkaGoGo
  • LinkArena
  • LinkedIn
  • Linkter
  • Live
  • Ma.gnolia
  • Meneame
  • MisterWong
  • MisterWong.DE
  • muti
  • MyShare
  • MySpace
  • N4G
  • Netvibes
  • Netvouz
  • NewsVine
  • NuJIJ
  • Ping.fm
  • PlugIM
  • Pownce
  • ppnow
  • Print
  • Propeller
  • Ratimarks
  • Rec6
  • Reddit
  • SalesMarks
  • Scoopeo
  • scuttle
  • Segnalo
  • Shadows
  • Simpy
  • Slashdot
  • Smarking
  • Socialogs
  • SphereIt
  • Spurl
  • StumbleUpon
  • Symbaloo
  • Taggly
  • TailRank
  • Technorati
  • ThisNext
  • Tipd
  • Tumblr
  • TwitThis
  • Upnews
  • Webnews.de
  • Webride
  • Wikio
  • Wikio FR
  • Wikio IT
  • Wists
  • Wykop
  • Xerpi
  • Yahoo! Buzz
  • YahooMyWeb
  • Yigg

Related posts:

  1. Jackbrabbit OCM and Spring Content Repository API becomes more and more popular nowadays, however...
  2. Testing Spring beans against database with DbUnit This is another post of testing series we wrote so...
  3. Using Spring Integration in Osgi platform How to use Spring Integration in Osgi platform. This post...
  4. Object caching using Spring, Ehcache Caching is quite important to boost the application performance. There...
  5. Develop Restful application with RESTeasy Web service is complicated, to understand web service you must...

7 Responses to “Integration of Jackrabbit OCM and Spring (updated version)”

  1. Crakegrorma says:

    Great site this blog.esofthead.com and I am really pleased to see you have what I am actually looking for here and this this post is exactly what I am interested in. I shall be pleased to become a regular visitor :)

  2. admin says:

    Thank you for your kind comment :) . I am happy that it helps you in work.

  3. greg_larson says:

    Thanks for this extremely helpful demo project!
    I am curious about one part of the project:
    In the domain package, there are Tag and TagExample classes as well as a tags[] property in the File class. These are not used in the test package — can you explain how they would be used (I would imagine for searching) and provide a small example?

  4. admin says:

    Larson,
    You are right to state about the purpose of tag classes. It is used to for searching documents (You see the result at the screenshots http://www.flickr.com/photos/37821412@N04/3697666512/sizes/o/in/set-72157621082360806/). However, document type is one of many contents we would like to support tag (a CRM Lead could be tagged or a project is also be tagged etc), to make loose coupling with Document module , other modules need to tag resources and Tag module, we use Spring Integration and ActiveMQ (Follows publish-subscribe pattern) and we send message contains tag resource to Tag module in asynchronous way. Search content base tag is simply retrieving information from database only whether tag module keeps information of tag and resource information.

    However, I guess that you need to seek the solutions to search JCR node match contents (and/or its attributes), as our result like this http://www.flickr.com/photos/37821412@N04/3749630828/sizes/o/in/set-72157621082360806/ ? I worked with Jackrabbit deep in source code and I admitted that searching in jackrabbit ocm is not easy than using core api. If you fall into this issue, you could post more detail your problems, I will give a hand if I can :)

  5. greg_larson says:

    Yes, searching the JCR node contents/attributes for matches is exactly what I have in mind. It is still early in my project so this part is not required yet, but it will be coming soon. I will make a start on it and also get familiar with the jackrabbit ocm source code. Thanks for your help so far!

  6. Paco Avila says:

    Very goog article! An now my “problem”: I wish to propagate the web user name to the jackrabbit session space. So, I don’t want to provide the “credentials” into the “jcrSessionFactory” bean configuration. But when I access to the repository, the session.getUserID() is returning “anonymous”. There is an additional configuration for this?

    Thanks in advance.

  7. admin says:

    Jackrabbit allow user integrate JAAS with its repository. However, you must write custom JAAS Login Module, it is not belong Spring section. Try to Security section in jackrabbit configuration file:

    < Security appName=”Jackrabbit” >
    <SecurityManager
    class=”org.apache.jackrabbit.core.security.simple.SimpleSecurityManager”
    workspaceName=”security”>

    To do your request, you must create your own implementation to replace SimpleLoginModule, SimpleSecurityManager, SimpleAccessManager also create jcrSessionFactory instance and access repository by yourself (not hard-code ‘credentials’ in spring configuration file).

Leave a Reply