4 Replies Latest reply on Aug 22, 2011 4:50 PM by Randall Hauch

    NPE after looking up for a repository with JNDI

    Tomasz Zarna Newbie

      At first I thought I'm seeing this, but Randal explained to me that I'm wrong and asked to start a new discussion here. So here is my problem:

       

      I've deployed modeshape and modeshape-rest.war (exploded) in Tomcat. Everything works fine, both apps are started, a test jsp page connects to my repo, but when I do GET on http://localhost:8080/modeshape-rest.war/resources I get the following exception:

      SEVERE: Servlet.service() for servlet Resteasy threw exception
      org.jboss.resteasy.spi.UnhandledException: java.lang.NullPointerException
      at org.jboss.resteasy.core.SynchronousDispatcher.handleApplicationException(SynchronousDispatcher.java:263)
      at org.jboss.resteasy.core.SynchronousDispatcher.handleException(SynchronousDispatcher.java:169)
      at org.jboss.resteasy.core.SynchronousDispatcher.handleInvokerException(SynchronousDispatcher.java:146)
      at org.jboss.resteasy.core.SynchronousDispatcher.getResponse(SynchronousDispatcher.java:456)
      at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:418)
      at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:111)
      at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:217)
      at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:159)
      at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
      at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
      at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
      at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
      at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
      at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
      at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
      at org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:861)
      at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:579)
      at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1584)
      at java.lang.Thread.run(Thread.java:619)
      Caused by: java.lang.NullPointerException
      at org.modeshape.web.jcr.spi.FactoryRepositoryProvider.getRepository(FactoryRepositoryProvider.java:71)
      at org.modeshape.web.jcr.spi.FactoryRepositoryProvider.getSession(FactoryRepositoryProvider.java:108)
      at org.modeshape.web.jcr.RepositoryFactory.getSession(RepositoryFactory.java:90)
      at org.modeshape.web.jcr.rest.AbstractHandler.getSession(AbstractHandler.java:40)
      at org.modeshape.web.jcr.rest.RepositoryHandler.getWorkspaces(RepositoryHandler.java:36)
      at org.modeshape.web.jcr.rest.JcrResources.getWorkspaces(JcrResources.java:186)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
      at java.lang.reflect.Method.invoke(Method.java:597)
      at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:124)
      at org.jboss.resteasy.core.ResourceMethod.invokeOnTarget(ResourceMethod.java:247)
      at org.jboss.resteasy.core.ResourceMethod.invoke(ResourceMethod.java:212)
      at org.jboss.resteasy.core.ResourceMethod.invoke(ResourceMethod.java:202)
      at org.jboss.resteasy.core.SynchronousDispatcher.getResponse(SynchronousDispatcher.java:441)
      ... 17 more

      snippet from my conf/context.xml :

      <Resource name="jcr/local" 
                 auth="Container"
                 type="javax.jcr.Repository"
                 factory="org.modeshape.jcr.JndiRepositoryFactory"
                 configFile="c:\apps\apache-tomcat-6.0.29\webapps\modeshape\WEB-INF\lib\modeshape-config.xml?repositoryName=ANJnb6arAB8c07rsXEnCtA"
                 repositoryName="ANJnb6arAB8c07rsXEnCtA" />

      test.jsp :

       

      <%
      Session sess = null;
      try {
          InitialContext initCtx = new InitialContext();
          Context envCtx = (Context) initCtx.lookup("java:comp/env");
          Repository repo = (Repository) envCtx.lookup("jcr/local");
          sess = repo.login(new SimpleCredentials("admin", "admin".toCharArray()));
          out.println("Primary node type name: "+sess.getRootNode().getPrimaryNodeType().getName());
      } catch (Exception ex) {
          ex.printStackTrace();
      } finally {
          if (sess != null) sess.logout();
      }
      %>

       

      web.xml for the modeshape-rest.war :

      <context-param>
      <param-name>org.modeshape.web.jcr.JCR_URL</param-name>     
      <param-value>jndi:jcr\local</param-value>
      </context-param>

      and finally an excerpt from the log :

      org.jboss.resteasy.core.SynchronousDispatcher 2010-08-31 16:36:35,474 -- DEBUG -- PathInfo: /resources
      org.modeshape.jcr.JcrRepositoryFactory 2010-08-31 16:36:35,615 -- DEBUG -- Could not load engine from URL: jndi:jcr\local

       

      I don't have access to Modeshape's source code, but I took a look at JcrRepositoryFactory and noticed that the JcrRepositoryFactory#getRepositoriesFromJndi,  which is probably called in my case, expects that the object under the  JNDI name is type of Repositories. While in fact I'd put there a  javax.jcr.Repository. Is that the reason why I receive the exception?

       

      How can I fix this?

        • 1. Re: NPE after looking up for a repository with JNDI
          Randall Hauch Master

          Tomasz Zarna wrote:

           

          I've deployed modeshape and modeshape-rest.war (exploded) in Tomcat.

           

          Which 'modeshape-rest.war' file did you deploy? We actually have two:

          1. a generic one that works in Tomcat (and maybe other app servers) from our binary distribution; and
          2. a version that is specific to JBoss AS, from our kit for JBoss AS

           

          I presume it was #1.

           

          The configuration that you used:

          <Resource name="jcr/local" 

                     auth="Container"
                     type="javax.jcr.Repository"
                     factory="org.modeshape.jcr.JndiRepositoryFactory"
                     configFile="c:\apps\apache-tomcat-6.0.29\webapps\modeshape\WEB-INF\lib\modeshape-config.xml"
                     repositoryName="ANJnb6arAB8c07rsXEnCtA" />

           

          defines a component that Tomcat will instantiate, call "getObjectInstance()" method on it (this method will returns the javax.jcr.Repository instance for your named repository), and will then register that javax.jcr.Repository instance in JNDI under "jcr/local".  Any other web applications deployed within that Tomcat instance can simply look up the javax.jcr.Repository instance in JNDI and start using it.

           

          One of your web apps you are deploying contains a simple JSP page:

          test.jsp :

           

          <%
          Session sess = null;
          try {
              InitialContext initCtx = new InitialContext();
              Context envCtx = (Context) initCtx.lookup("java:comp/env");
              Repository repo = (Repository) envCtx.lookup("jcr/local");
              sess = repo.login(new SimpleCredentials("admin", "admin".toCharArray()));
              out.println("Primary node type name: "+sess.getRootNode().getPrimaryNodeType().getName());
          } catch (Exception ex) {
              ex.printStackTrace();
          } finally {
              if (sess != null) sess.logout();
          }
          %>

           

          As you can see, this looks in JNDI under "jcr/local", finds the javax.jcr.Repository instance, and is able to establish a session. BTW, this is sort of a JCR-1.0-style way to look up and find your Repository instance.

           

          Anyway, so far so good. So why can't the ModeShape RESTful web application find it? It can't find it because it is using the JCR-2.0-style approach of using the RepositoryFactory. Here's the configuration in the web.xml file:

           

          The ModeShape RESTful web application then needs to look for a javax.jcr.Repository in JNDI. Unfortunately, the RESTful WAR file is doing it using the JCR-2.0-style approach of using the RepositoryFactory. This configuration:

           

          web.xml for the modeshape-rest.war :


          <context-param>
          <param-name>org.modeshape.web.jcr.JCR_URL</param-name>     
          <param-value>jndi:jcr\local</param-value>
          </context-param>

           

          This tells the web application to use the JCR 2.0 service loader mechanism to obtain a JCR 2.0 javax.jcr.RepositoryFactory instance. This factory is then used to find the ModeShape engine (specifically, an instance of org.modeshape.jcr.api.RepositoryFactory) at the supplied location in JNDI. The problem is that the object in JNDI "jcr/local" is a javax.jcr.Repository instance. Obviously it's not a org.modeshape.jcr.api.RepositoryFactory, so instead null is returned and thus the NPE.

           

          This is a (big) oversight on our part, resulting from an incomplete migration from the JCR 1.0-style to JCR 2.0-style repository lookup mechanism. So I will log a new defect to cover this. But, in the mean time, there is something you can do.  Actually, there are two approaches.

           

          Option 1: Make the RESTful web app use the JCR 1.0-style

           

          The RESTful web application's web.xml file as described in Section 11.2.2 of our Reference Guide. That section talks about this parameter:

           

            <!--
              This parameter provides the fully-qualified name of a class that implements     the o.m.web.jcr.spi.RepositoryProvider interface.  It is required     by the ModeShapeJcrDeployer that controls the lifecycle for the ModeShape REST server.   -->   <context-param>     <param-name>org.modeshape.web.jcr.REPOSITORY_PROVIDER</param-name>     <param-value>org.modeshape.web.jcr.spi.ModeShapeJcrRepositoryProvider</param-value>   </context-param>

           

          Instead of our JCR-2.0-style org.modeshape.web.jcr.spi.ModeShapeJcrRepositoryProvider class, you want to provide a different implementation of the org.modeshape.web.jcr.spi.RepositoryProvider interface. I've attached an implementation file that should work (though I didn't test it yet). This class needs to be compiled and placed onto the classpath in the RESTful WAR, and the web.xml needs to have a couple of changes:

           

              <!--

                  This parameter provides the fully-qualified name of a class that implements

                  the o.m.w.jcr.spi.RepositoryProvider interface.  It is required

                  by the ModeShapeJcrDeployer that controls the lifecycle for the ModeShape REST server.

              -->

              <context-param>

                  <param-name>org.modeshape.web.jcr.REPOSITORY_PROVIDER</param-name>

                  <param-value>org.modeshape.web.jcr.spi.JndiRepositoryProvider</param-value>

              </context-param>

              <!--

                  This parameter, specific to the JndiRepositoryProvider implementation, specifies

                  a name in JNDI where a javax.jcr.Repository instance can be found.

              -->

              <context-param>

                  <param-name>org.modeshape.web.jcr.JNDI_NAME</param-name>

                  <param-value>jcr/local</param-value>

              </context-param>

              <!--

                  This parameter, specific to the JndiRepositoryProvider implementation, specifies

                  a name of the javax.jcr.Repository instance.

              -->

              <context-param>

                  <param-name>org.modeshape.web.jcr.JCR_REPOSITORY_NAME</param-name>

                  <param-value>WhateverYourRepositoryNameIs</param-value>

              </context-param>

           

          Option 2: Register the ModeShape JCR engine into JNDI, for a JCR-2.0-style approach

           

          Instead of changing the RESTful WAR file, you can instead use a different JNDI Object Factory to put the org.modeshape.jcr.api.RepositoryFactory instance (our JcrEngine class implements this interface) into JNDI. This actually might be easier, since the existing org.modeshape.jcr.JndiRepositoryFactory class is starting up the engine. You could copy this class, and modify the last few lines of the class to return the JcrEngine instance (and not find the particular repository by name).  Of course, you'd have to change your other web apps to look in JNDI for the org.modeshape.jcr.api.RepositoryFactory instance, and use it to obtain your javax.jcr.Repository instance.

           

           

          Sorry that this doesn't work out of the box. We'll get this fixed (this week) and committed into trunk for our next release, probably using Option 2. But either of these approaches should work in the mean time.

           

          Let me know if you have any questions!

          • 2. Re: NPE after looking up for a repository with JNDI
            Tomasz Zarna Newbie

            I went with Option 1. Worked exactly as you explained. Thanks a lot!