Jackrabbit with Spring
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.
Please can you send me the jar files required for this example
Narender, do you use Maven2?
Hi, I’m intressted in this too. A simple maven2 project would be great. Thanks.
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:
Probably, this is more than enough. Of course if you use other logger than log4j switch the implementation for slf4j.
Hope that helped.
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.
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)?
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.
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.
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!
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
* I added * (just correcting)
Vanessa!
Try moving all of the jackrabbit jars and dependencies to your webapps WEB-INF/lib.
It works!
Thanks again!
Hello,
It’s me again. How do I pass the user name e password to login by using the bean jcrSession?
Best regards,
Vanessa
Hi Vanessa! Do you still have the issue with passing the password?
(Sorry for the delay… I’ve been on short excursion)
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
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.
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
Sorry, here is the config I changed (on repository.xml)
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_”
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.
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
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
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
@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.
@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
I forgot to mention: I deploy the war in a JBoss 4.2.2.GA (not sure if this matters)
@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):
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).
@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
@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.
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.
@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.
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
@Paco Avila
Sorry for delay. Still in trouble with passing credentials?