Grails
  1. Grails
  2. GRAILS-5592

"java.lang.IllegalStateException: Illegal class loader binding" when trying to access jndi: resources

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 1.2-RC1, 1.2-RC2
    • Fix Version/s: 1.2.1
    • Component/s: None
    • Labels:
      None
    • Environment:
      Windows XP
      Grails 1.2-RC2

      Description

      The sample attached uses Drools classes in a very basic way. Drools tries to access an xml file under /WEB-INF/changeset.xml.

      The following stack-trace is generated, when using 'grails run-app' (this is not happening when using 'grails run-war' or with Tomcat plugin version 1.2-M4):
      java.lang.RuntimeException: Unable to parse ChangeSet
      at org.drools.agent.impl.KnowledgeAgentImpl.getChangeSet(KnowledgeAgentImpl.java:223)
      at org.drools.agent.impl.KnowledgeAgentImpl.applyChangeSet(KnowledgeAgentImpl.java:109)
      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.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoCachedMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:229)
      at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:52)
      at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:40)
      at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:117)
      at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
      ....................
      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:293)
      at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:849)
      at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
      at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:454)
      at java.lang.Thread.run(Thread.java:619)
      Caused by: java.lang.IllegalStateException: Illegal class loader binding
      at org.apache.naming.resources.DirContextURLStreamHandler.get(DirContextURLStreamHandler.java:223)
      at org.apache.naming.resources.DirContextURLStreamHandler.openConnection(DirContextURLStreamHandler.java:88)
      at java.net.URL.openConnection(URL.java:945)
      at org.drools.io.impl.UrlResource.getLastModified(UrlResource.java:125)
      at org.drools.io.impl.UrlResource.getInputStream(UrlResource.java:73)
      at org.drools.io.impl.UrlResource.getReader(UrlResource.java:80)
      at org.drools.agent.impl.KnowledgeAgentImpl.getChangeSet(KnowledgeAgentImpl.java:221)

        Issue Links

          Activity

          Hide
          Mark Thomas added a comment -

          Building the WAR and running this under Tomcat trunk (Tomcat 7 development code base) gives:
          exception: java.net.MalformedURLException: Path WEB-INF/changeset.xml does not start with a "/" character

          Something in the application is doing calling ServletContext.getResource() or ServletContext.getResourceAsStream and passing "WEB-INF/changeset.xml" rather than "/WEB-INF/changeset.xml".

          The Servlet spec mandates that the leading / character is included.

          It may be co-incidental that this is the same resource that is triggering the error reported above. If I turn off strict spec compliance, the app works.

          I'll do some more tests running the WAR file under the embedded Tomcat code base Grails uses (it is Tomcat 6 based) and see if I can reproduce the issue.

          Regardless of the results of my tests with Tomcat embedded, the app is not spec compliant and needs to be fixed.

          Show
          Mark Thomas added a comment - Building the WAR and running this under Tomcat trunk (Tomcat 7 development code base) gives: exception: java.net.MalformedURLException: Path WEB-INF/changeset.xml does not start with a "/" character Something in the application is doing calling ServletContext.getResource() or ServletContext.getResourceAsStream and passing "WEB-INF/changeset.xml" rather than "/WEB-INF/changeset.xml". The Servlet spec mandates that the leading / character is included. It may be co-incidental that this is the same resource that is triggering the error reported above. If I turn off strict spec compliance, the app works. I'll do some more tests running the WAR file under the embedded Tomcat code base Grails uses (it is Tomcat 6 based) and see if I can reproduce the issue. Regardless of the results of my tests with Tomcat embedded, the app is not spec compliant and needs to be fixed.
          Hide
          Mark Thomas added a comment -

          Digging into this some more, my guess is that the custom Loader used by Grails is missing this call (from Tomcat;s standard loader):

          // Binding the Webapp class loader to the directory context
          DirContextURLStreamHandler.bind((ClassLoader) classLoader,
          this.container.getResources());

          Show
          Mark Thomas added a comment - Digging into this some more, my guess is that the custom Loader used by Grails is missing this call (from Tomcat;s standard loader): // Binding the Webapp class loader to the directory context DirContextURLStreamHandler.bind((ClassLoader) classLoader, this.container.getResources());
          Show
          Graeme Rocher added a comment - Hi Mark, we already have that line. See line 304: http://svn.grails.codehaus.org/browse/grails/trunk/grails-plugins/grails-tomcat/trunk/src/groovy/org/grails/tomcat/TomcatServer.groovy?r=9221#l304 Any other ideas?
          Hide
          Mark Thomas added a comment -

          Thanks for the link. That helps considerably.

          The root cause is that Tomcat expects each web application to have a dedicated class loader. Referring to the standard Tomcat class loader hierarchy [1], common must not equal WebappN.

          Some additional debug logging in Tomcat embedded shows that Grails is using the same class loader for both common and the web app. This is likely to introduce subtle bugs, particularly on reload but also anywhere where things are keyed on class loader (logging, resources, jndi, ...)

          The class loader doesn't have to do any more than just delegate to it's parent but it must be a separate object.

          Mark

          [1] http://tomcat.apache.org/tomcat-6.0-doc/class-loader-howto.html

          Show
          Mark Thomas added a comment - Thanks for the link. That helps considerably. The root cause is that Tomcat expects each web application to have a dedicated class loader. Referring to the standard Tomcat class loader hierarchy [1] , common must not equal WebappN. Some additional debug logging in Tomcat embedded shows that Grails is using the same class loader for both common and the web app. This is likely to introduce subtle bugs, particularly on reload but also anywhere where things are keyed on class loader (logging, resources, jndi, ...) The class loader doesn't have to do any more than just delegate to it's parent but it must be a separate object. Mark [1] http://tomcat.apache.org/tomcat-6.0-doc/class-loader-howto.html
          Hide
          Graeme Rocher added a comment -

          So how does one specify the common classloader so that Tomcat uses it?

          Show
          Graeme Rocher added a comment - So how does one specify the common classloader so that Tomcat uses it?
          Hide
          Mark Thomas added a comment -

          In the embedded use case the common class loader is likely to be the same class loader as is used for the main app. You want to create a trivial class loader implementation that just delegates to its parent and use that as the web app class loader.

          Show
          Mark Thomas added a comment - In the embedded use case the common class loader is likely to be the same class loader as is used for the main app. You want to create a trivial class loader implementation that just delegates to its parent and use that as the web app class loader.
          Hide
          Graeme Rocher added a comment -
          Show
          Graeme Rocher added a comment - Fixed by tomcat plugin change http://fisheye.codehaus.org/changelog/grails/?cs=9295

            People

            • Assignee:
              Graeme Rocher
              Reporter:
              Andrei Stefan
            • Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Development