Home > java, open source, oss, spring > Jackrabbit with Spring

Jackrabbit with Spring

No Gravatar

I was searching for a way to use Jackrabbit with Spring. There are articles about this topic, but each of them suggests using spring-modules. Now, my problem with spring-modules jcr implementation is, that they depend on non-released or obsolete 3rd party JARs, like apravez.* and jeceira. Just take a look at the pom file, and you’ll see.

On the other hand I do not feel a need for jcrTemplate, jcrCallback or any other ORM flavor helpers. I just need a nice, easy integration of Jackrabbit, where I can easily inject JCR Session into any class with spring.

The JNDI way would do, but I do not want to use JNDI, I want to configure Jackrabbit from my spring beans.

Now with those premises, I came out with a quite easy setup, that follows below.

Spring configuration:



  
    
    
  

  
    
      
    
  

  
  

The configuration of Jackrabbit is defined in repository.xml file, – I just took the default repository.xml, the one that is created when running TransientReposiotry for the first time. Of course for better integration and configuration define your own.

There is one more configuration point and that could be seen in the XML from above. Spring bean jcrConfiguration defines where the repository.xml configuration is and the path to repository resources, – the second argument. For test purpose I just used /tmp/repository but for production you might set it to something like: repository

Do not forget to set up spring RequestContextListener or you’ll be missing session (and request) scope in spring configurations. In web.xml just define a listener:



  org.springframework.web.context.request.RequestContextListener

From code we’ll just going to use the jcrSession. Here is a small example for usage:

@Service
public class TopicService implements BeanFactoryAware {

  private BeanFactory beanFactory;

  public Session getJcrSession() {
    Session jcrSession =
	  (Session) beanFactory.getBean("jcrSession");
    return jcrSession;
  }

  @Override
  public void setBeanFactory(BeanFactory beanFactory)
      throws BeansException
  {
    this.beanFactory = beanFactory;
  }

  public void myFucntion() {
    Session session = getJcrSession();

    // here you do what you would do with JCR...
  }
}

As my service is singleton I inject Spring Bean Factory with the help of org.springframework.beans.factory.BeanFactoryAware mixin, so each time I call getJcrSession() I get a JCR session bind to HTTP session.

Well, that’s it. A simple configuration, easy usage.

  • Share/Bookmark

This website uses IntenseDebate comments, but they are not currently loaded because either your browser doesn't support JavaScript, or they didn't load fast enough.

  1. narender
    December 5th, 2008 at 11:47 | #1

    Please can you send me the jar files required for this example

  2. December 5th, 2008 at 14:46 | #2

    Narender, do you use Maven2?

  3. December 5th, 2008 at 15:18 | #3

    Hi, I’m intressted in this too. A simple maven2 project would be great. Thanks.

  4. December 5th, 2008 at 15:48 | #4

    Hi!

    Okay, right now I’m bit out of time, so I just give you the relevant dependencies from my POM file, and afterwards I’ll try to isolate an example and put it up somewhere.

    So the deps:

    <!-- spring -->
    <dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-context</artifactId>
    	<version>2.5.6</version>
    </dependency>
    <dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-beans</artifactId>
    	<version>2.5.6</version>
    </dependency>
    <dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-core</artifactId>
    	<version>2.5.6</version>
    </dependency>
    <dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-aop</artifactId>
    	<version>2.5.6</version>
    </dependency>
    <dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-aspects</artifactId>
    	<version>2.5.6</version>
    </dependency>
    <dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-web</artifactId>
    	<version>2.5.6</version>
    </dependency>
    <dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-jdbc</artifactId>
    	<version>2.5.6</version>
    </dependency>
    <dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-orm</artifactId>
    	<version>2.5.6</version>
    </dependency>
    
    <dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-test</artifactId>
    	<version>2.5.6</version>
    	<scope>test</scope>
    </dependency>
    
    <!-- jcr, jackrabbit -->
    <dependency>
    	<groupId>org.apache.jackrabbit</groupId>
    	<artifactId>jackrabbit-core</artifactId>
    	<version>1.4.6</version>
    </dependency>
    <dependency>
    	<groupId>javax.jcr</groupId>
    	<artifactId>jcr</artifactId>
    	<version>1.0</version>
    </dependency>
    <dependency>
    	<groupId>org.slf4j</groupId>
    	<artifactId>slf4j-api</artifactId>
    	<version>1.5.5</version>
    </dependency>
    <dependency>
    	<groupId>org.slf4j</groupId>
    	<artifactId>slf4j-log4j12</artifactId>
    	<version>1.5.5</version>
    </dependency>
    

    Probably, this is more than enough. Of course if you use other logger than log4j switch the implementation for slf4j.

    Hope that helped.

  5. surjit
    December 7th, 2008 at 20:55 | #5

    I m getting the following error while i try to run myFunction from a JUnit test:
    Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘jcrConfiguration’ defined in class path resource [applicationContext.xml]: No matching factory method found: factory method ‘create’

    Can you let me know what i missed.

  6. December 11th, 2008 at 17:59 | #6

    surjit,
    do you have the constructor argument set?
    Can you find the org.apache.jackrabbit.core.RepositoryImpl class? Does that class have the method with following signature: public static RepositoryImpl create( org.apache.jackrabbit.core.config.RepositoryConfig config)?

  7. Vanessa
    April 15th, 2009 at 22:38 | #7

    Hi, can you help me, please?
    I’m getting the following error:
    Caused by: java.lang.ClassCastException: org.apache.jackrabbit.core.XASessionImpl cannot be cast to javax.jcr.Session

    I need to use the jackrabbit jars on my app?
    I’d like it to be independent of jack’s impl.

    Thanks.

  8. April 16th, 2009 at 00:01 | #8

    Vanessa!

    As far as you use Jackrabbit as a JCR implementation you should include Jackrabbit JARs. Of course you don’t have to use Jackrabbit API directly, using JCR API should be enough.

    http://jackrabbit.apache.org/api/1.5/org/apache/jackrabbit/core/XASessionImpl.html
    does implement javax.jcr.Session.

    My hints:
    - version clash
    - multiple versions

    If you provide some details, I might have better hints.

  9. Vanessa
    April 16th, 2009 at 20:12 | #9

    Yeah, I read that XASession does implement javax.jcr.Session. That’s I’m not getting it! :-)

    I have the following jars in Tomcat’s (6.0.18) lib folder:
    jcr-1.0.jar
    jackrabbit-core-1.5.3.jar
    jackrabbit-text-extractors-1.5.0.jar
    jackrabbit-spi-1.5.0.jar
    jackrabbit-api-1.5.0.jar
    log4j-1.2.14.jar
    commons-io-1.4.jar
    concurrent-1.3.4.jar
    commons-collections-3.1.jar
    lucene-core-2.3.2.jar

    In my application I put just the jcr-1.0.jar, among other jar files.
    These commons-something with different versions could be the cause?

    This is the java code:

    import javax.jcr.Session;


    public synchronized void listener(UploadEvent event) throws Exception {

    Session session = getJcrSession();
    String user = session.getUserID();
    System.out.println(“——— ” + user);
    session.logout();
    }

    And the error is:
    log4j:ERROR A “org.apache.log4j.ConsoleAppender” object is not assignable to a “org.apache.log4j.Appender” variable.
    log4j:ERROR The class “org.apache.log4j.Appender” was loaded by
    log4j:ERROR [org.apache.catalina.loader.StandardClassLoader@d1e604] whereas object of type
    log4j:ERROR “org.apache.log4j.ConsoleAppender” was loaded by [WebappClassLoader
    delegate: false
    repositories:
    /WEB-INF/classes/
    ----------> Parent Classloader:
    org.apache.catalina.loader.StandardClassLoader@d1e604
    ].
    log4j:ERROR Could not instantiate appender named “stdout”.
    log4j:ERROR A “org.apache.log4j.RollingFileAppender” object is not assignable to a “org.apache.log4j.Appender” variable.
    log4j:ERROR The class “org.apache.log4j.Appender” was loaded by
    log4j:ERROR [org.apache.catalina.loader.StandardClassLoader@d1e604] whereas object of type
    log4j:ERROR “org.apache.log4j.RollingFileAppender” was loaded by [WebappClassLoader
    delegate: false
    repositories:
    /WEB-INF/classes/
    ----------> Parent Classloader:
    org.apache.catalina.loader.StandardClassLoader@d1e604
    ].
    log4j:ERROR Could not instantiate appender named “R”.
    16/04/2009 14:44:55 com.sun.faces.lifecycle.ApplyRequestValuesPhase execute
    WARNING: /paginas/documento/cadDocumento.xhtml @89,67 fileUploadListener=”#{documentoEnviaRMB.listener}”: java.lang.ClassCastException: org.apache.jackrabbit.core.XASessionImpl cannot be cast to javax.jcr.Session
    javax.faces.el.EvaluationException: /paginas/documento/cadDocumento.xhtml @89,67 fileUploadListener=”#{documentoEnviaRMB.listener}”: java.lang.ClassCastException: org.apache.jackrabbit.core.XASessionImpl cannot be cast to javax.jcr.Session
    at com.sun.facelets.el.LegacyMethodBinding.invoke(LegacyMethodBinding.java:73)
    at org.richfaces.component.UIFileUpload.broadcast(UIFileUpload.java:158)
    at org.ajax4jsf.component.AjaxViewRoot.processEvents(AjaxViewRoot.java:317)
    at org.ajax4jsf.component.AjaxViewRoot.broadcastEvents(AjaxViewRoot.java:290)
    at org.ajax4jsf.component.AjaxViewRoot.processPhase(AjaxViewRoot.java:249)
    at org.ajax4jsf.component.AjaxViewRoot.processDecodes(AjaxViewRoot.java:405)
    at com.sun.faces.lifecycle.ApplyRequestValuesPhase.execute(ApplyRequestValuesPhase.java:78)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:100)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:265)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.myfaces.webapp.filter.ExtensionsFilter.doFilter(ExtensionsFilter.java:147)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.ajax4jsf.webapp.BaseXMLFilter.doXmlFilter(BaseXMLFilter.java:154)
    at org.ajax4jsf.webapp.BaseFilter.handleRequest(BaseFilter.java:260)
    at org.ajax4jsf.webapp.BaseFilter.processUploadsAndHandleRequest(BaseFilter.java:331)
    at org.ajax4jsf.webapp.BaseFilter.doFilter(BaseFilter.java:493)
    ERROR [http-8080-3] (BaseXMLFilter.java:157) – Exception in the filter chain
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    .
    .
    .
    Caused by: java.lang.ClassCastException: org.apache.jackrabbit.core.XASessionImpl cannot be cast to javax.jcr.Session
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
    at gov.cnmp.cnmpvirtual.visao.managedbean.DocumentoEnviaRMB.getJcrSession(DocumentoEnviaRMB.java:72)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
    at gov.cnmp.cnmpvirtual.visao.managedbean.DocumentoEnviaRMB.listener(DocumentoEnviaRMB.java:57)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:433)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:286)
    at org.apache.el.parser.AstValue.invoke(AstValue.java:172)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:845)
    at org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:276)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
    at com.sun.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:68)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
    at com.sun.facelets.el.LegacyMethodBinding.invoke(LegacyMethodBinding.java:69)
    at java.lang.Thread.run(Thread.java:619)
    … 31 more
    Caused by: javax.faces.el.EvaluationException: /paginas/documento/cadDocumento.xhtml @89,67 fileUploadListener=”#{documentoEnviaRMB.listener}”: java.lang.ClassCastException: org.apache.jackrabbit.core.XASessionImpl cannot be cast to javax.jcr.Session
    16/04/2009 14:44:55 com.sun.faces.lifecycle.Phase doPhase
    at com.sun.facelets.el.LegacyMethodBinding.invoke(LegacyMethodBinding.java:73)

    Many thanks for your help!

  10. Vanessa
    April 16th, 2009 at 22:49 | #10

    Hello again.

    I add the jackrabbit-core-1.5.3.jar in my app and got the following error:

    java.lang.LinkageError: loader constraint violation: when resolving overridden method “org.apache.jackrabbit.core.RepositoryImpl.login(Ljavax/jcr/Credentials;Ljava/lang/String;)Ljavax/jcr/Session;” the class loader (instance of org/apache/catalina/loader/WebappClassLoader) of the current class, org/apache/jackrabbit/core/RepositoryImpl, and its superclass loader (instance of org/apache/catalina/loader/StandardClassLoader), have different Class objects for the type javax/jcr/Session used in the signature

  11. Vanessa
    April 16th, 2009 at 22:53 | #11

    * I added * (just correcting)

  12. April 17th, 2009 at 15:29 | #12

    Vanessa!

    Try moving all of the jackrabbit jars and dependencies to your webapps WEB-INF/lib.

  13. Vanessa
    April 17th, 2009 at 18:52 | #13

    It works!

    Thanks again!

  14. Vanessa
    April 28th, 2009 at 18:57 | #14

    Hello,
    It’s me again. How do I pass the user name e password to login by using the bean jcrSession?

    Best regards,
    Vanessa

  15. May 6th, 2009 at 00:17 | #15

    Hi Vanessa! Do you still have the issue with passing the password?

    (Sorry for the delay… I’ve been on short excursion)

  16. Vanessa
    May 13th, 2009 at 21:38 | #16

    Hello Lóránd,

    I’ve already solved the password issue. I found out how to configure the bean attributes. I was easy. :-)

    Now I’m with a new problem. Maybe you could help me.

    I’m using this beans configuration on a local mysql repository, and it works fine. But when I try to run the application on a different machine (same network), I got a “Connection refused” error. I can access mysql jackrabbit schema directly, but not from inside my application. Do I need to configure something like a transaction manager?

    Thanks a lot for you help and attention.
    Regards,
    Vanessa

  17. May 13th, 2009 at 23:52 | #17

    Vanessa! I’m a bit confused. Do you have a remote JCR repository or only a remote MySQL?

    But for sure transaction manager should have nothing to do with this issue.

  18. Vanessa
    May 14th, 2009 at 18:11 | #18

    Hello Lóránd,

    I have just a remote MySQL. MySQL is running on a machine over the network and my application is on another machine.

    I just changed the following conf:

    And changed MySQL to accept the connection. So I can access MySQL using a simple JSP that access the tables directly, but I cannot do it using the repository beans.

    Thanks,
    Vanessa

  19. Vanessa
    May 14th, 2009 at 18:14 | #19

    Sorry, here is the config I changed (on repository.xml)

  20. Vanessa
    May 14th, 2009 at 18:27 | #20

    Can’t send you the config. Don’t know why it sends an all blank reply.

    One last try:

    PersistenceManager class=”org.apache.jackrabbit.core.persistence.bundle.MySqlPersistenceManager”
    param name=”url” value=”jdbc:mysql://p0022:3306/jackrabbit”/
    param name=”user” value=”jack”
    param name=”password” value=”pass”
    param name=”schema” value=”mysql”
    param name=”schemaObjectPrefix” value=”xxx_doc_”

  21. May 14th, 2009 at 19:21 | #21

    Vanessa, I would need the log of jackrabbit, but with out it my guess would be: your mysql is not configured to accept connections through the network from your host.

  22. Vanessa
    May 18th, 2009 at 20:07 | #22

    Hello Lóránd,

    Yeah, it was something like this. I just couldn’t figure out what was wrong on MySQL configuration. I changed my repository to Oracle and it worked just fine!

    Thanks anyway.
    Best regards,
    Vanessa

  23. Vanessa
    May 28th, 2009 at 22:13 | #23

    Hello Lóránd,

    It’s me again. Hope it’s the last time I need to bother you. :-)
    Now I’m trying to use a Transaction Manager, because I need a XA transaction to save things on jackrabbit and on another data source (through hibernate).
    I’m using your configuration to inject beans, and all code samples I found over the internet are using Spring Modules, which I don’t want to use.
    Have you an idea how can I configure a manager transaction? Maybe using something like Atomikos. I’m using tomcat 6.

    Regards,
    Vanessa

  24. Paco Avila
    July 30th, 2009 at 14:54 | #24

    I’ve tries this configuration but “beanFactory” in getJcrSession() always return null. Threre is any tip on this? This is my application-content.xml -> http://pastebin.com/m27bc07f3

  25. July 30th, 2009 at 16:49 | #25

    @Paco Avila

    Paco, a short checklist:
    – is jackrabbit configuration ok? (/tmp/repository, repository.xml)
    – repository.xml loads through classpath?
    – the caller side is manged through bean factory?

    If that does not help, drop another note with details.

  26. Paco Avila
    July 31st, 2009 at 09:33 | #26

    @Lóránd Somogyi
    Yes, Jackrabbit configuration is ok. The repository is created. Here is a very simple eclipse & maven project which reproduce this issue: http://rapidshare.com/files/262044081/test_jcr.zip.html

  27. Paco Avila
    July 31st, 2009 at 09:34 | #27

    I forgot to mention: I deploy the war in a JBoss 4.2.2.GA (not sure if this matters)

  28. July 31st, 2009 at 10:43 | #28

    @Paco Avila
    I’ve checked your project. According to your configuration your TopicService is *not* beanfactory managed. On the other hand in SimpleServlet you created a *new* instance of TopicService, so even if you would have it managed by spring this would be a new instance.

    The solution:
    1. In application-context.xml register the service (you can do this with javaconfig too, but this is the old fashion way):

    	<bean name="myService" class="org.monkiki.TopicService" />
    

    2. In SimpleServlet make sure you use the managed instance. For example:

    	public void doGet(HttpServletRequest request, HttpServletResponse response)
    			throws ServletException, IOException {
    		try {
    			WebApplicationContext wac = WebApplicationContextUtils
    						.getWebApplicationContext(
    						request.getSession().getServletContext()
    						);
    			TopicService ts = (TopicService) wac.getBean("myService");
    			ts.myFunction();
    		} catch (Exception e) {
    			log.error(e.getMessage(), e);
    		}
    	}
    

    This setup now works for me. (JBoss and Tomcat too).

  29. Paco Avila
    July 31st, 2009 at 11:23 | #29

    @Lóránd Somogyi
    Thanks! Now there is no NPE thrown.

    But I have another issue: session.getUserID() is returning “anonymous” in place of the web authenticated user name. Do you have any idea why is this happening?

    Thanks again :)

  30. August 9th, 2009 at 22:47 | #30

    @Paco Avila
    I’m not getting it. From your code you are authenticating the user with spring. So in your spring container you have a loged in user. But you do not pass the same user to jackrabbit… If you would like to do that, you would need to pass the user to Jackrabbit too.

    Usually you would not do that. Same as with any database access: one use it with a single user/role, which is different from the container user. In that case you would handle privileges with the container (spring) and not within the database, – or in this case Jackrabbit.

  31. Paco Avila
    August 31st, 2009 at 14:19 | #31

    Well, not the same thing: in jackrabbit you usually add access info to nodes and implement and AccessManager so the JCR framework can decide if an user have permissions to read a node.

    If you make a XPath search in JCR, the system will show only the matches nodes where the user who launched the query have access.

  32. August 31st, 2009 at 17:56 | #32

    @Paco Avila
    In that case you need to pass the user/password to the jackrabbit configuration, and make sure those credentials are the same in Jackrabbit. – IMO in cases like that service authorization, – or at least part of it, – should come from Jackrabbit.

  33. Paco Avila
    September 1st, 2009 at 12:56 | #33

    Yes, this is the objetive but is complicated for me to archieve :) I don’t know how to pass the authentication credentials to the jackrabbit session :(

  34. September 28th, 2009 at 07:26 | #34

    @Paco Avila
    Sorry for delay. Still in trouble with passing credentials?

  1. No trackbacks yet.