Saturday, June 21, 2008

Jackbrabbit OCM and Spring

Content Repository API becomes more and more popular nowadays, however it is rather difficult in use because its takes time of learning and use. OCM (Object Content Mapping) module is the great tool to helps developers save a lot of time to develop the content-driven application and Spring is the great DI platform to hide most of complexity of initializing, creating and executing repository. We are developing the engroup ECM module base on Jackbrabbit OCM and Spring Module. During development, we look for help in many forums, websites but unfortunately we do not seek the full solution, we hope that this article provide the full example of using Jackbrabbit and Spring in the real application. Part of engroup ECM code base is included in attached file, it is developed base on jackrabbit 1.5 (snapshot version - you can get it at apache maven repository http://people.apache.org/maven-snapshot-repository/), spring modules 0.9 and the patch spring-ocm got at http://jira.springframework.org/browse/MOD-446

First, create the POJOs for JCR repository:

  @Node(jcrMixinTypes = “mix:versionable”)
  public class Content {
    @Field(uuid=true)
    protected String id;
    @Field(path=true)
    protected String path;
    @Field
    protected String name;
    …
  }

Note: if you want to create the POJO inherit the JCR fields of its parent class, you must use the extend property field of annotation Node like the following example:

  @Node(jcrMixinTypes = “mix:versionable”, extend = AbstractFile.class)
  public class File extends Content {
    @Field
    protected byte[] content;
    …
  }

The next step is creating the spring beans to init the repository, register nodes types and POJOs with repository. Here is the part of configuration file (you can see the full file in the attachment):

  • Initialize the repository:
        <bean id="repository" class="org.springmodules.jcr.jackrabbit.RepositoryFactoryBean">
          <property name="configuration" value="classpath:jackrabbit-repo.xml" />
          <property name="homeDir" value="file:/tmp/repository" />
        </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="nodeTypes2Import" value="nodetypes/custom_nodetypes.xml" />
        </bean>
  • Make the mapping between POJOs and annotation mapper
        <bean id="jcrMappingDescriptor" class="org.apache.jackrabbit.ocm.mapper.impl.annotation.AnnotationMapperImpl">
          <constructor-arg index="0">
            <!--Put all your POJOs in this list-->
            <list>
              <value>com.engroup.module.ecm.domain.AbstractFile</value>
              <value>com.engroup.module.ecm.domain.Content</value>
              <value>com.engroup.module.ecm.domain.File</value>
              <value>com.engroup.module.ecm.domain.Folder</value>
            </list>
          </constructor-arg>
        </bean>
  • Declare the Content Service Bean with transaction management support
    
     <bean id=”internalContentService”
        class=”com.engroup.module.ecm.service.impl.ContentServiceImpl”>
        <property name=”jcrTemplate” ref=”jcrMappingTemplate” />
     </bean>
     <bean id=”contentService” parent=”baseTransactionProxy”>
        <property name=”proxyInterfaces”>
          <value>com.engroup.module.ecm.service.ContentService</value>
        </property>
        <property name=”target”>
          <ref bean=”internalContentService” />
        </property>
        <property name=”transactionAttributes”>
          <props>
            <prop key=”*”>PROPAGATION_REQUIRED</prop>
          </props>
        </property>
      </bean>

Well, all configuration tasks are done. Now, you can access the content of repository by using POJOs. Thanks for spring modules that helps you reduce lot of code for initilizing and manage repository. The tasks later just be simply like you work with Hibernate entity, all works are done by Java code (Of course, you need to know a little advance knowledge of repository to customize data types etc for your needs). After I create the service, now it is time to write some little unit test to make sure all configurations are set properly :)

  @RunWith(UnitilsJUnit4TestClassRunner.class)
  public class ContentServiceTest {
    @SpringBean
    private ContentService<Content> contentService;

    @Test
    public void testSave() {
      File file = createFile();
      contentService.save(file);
      file = (File)contentService.findByPath("/nextss");
      Assert.assertThat(file.getPath(), is("/nextss"));
      Assert.assertThat(file.getFileType(), is(FileType.UNDEFINED));
      contentService.remove(file);
    }

    private File createFile() {
      File file = new File();
      file.setId("1");
      file.setPath("/nextss");
      file.setName("ABC");
      file.setFileType(FileType.UNDEFINED);
      file.setTitle("Test Exam");
      file.setLastModified(Calendar.getInstance().toString());
      file.setContent("Hello world".getBytes());
      file.setComment("AAA");
      return file;
    }
    ...
  }

I am happy when the unit test run well :). Hope it is the part complements with Jackrabbit OCM and Spring modules - OCM. Welcomes any comments from you.

Resources:

No comments: