Commons Digester
  1. Commons Digester
  2. DIGESTER-23

Digester uses wrong ClassLoader in a web-application context

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 1.0
    • Fix Version/s: None
    • Labels:
      None
    • Environment:

      Operating System: other
      Platform: Other

      Description

      I'm cross posting this on Struts-dev and Commmons.

      I attempted to incorporate the commons-digester_1.0 into my struts-
      based application. A quick note on how I'm using it: In one of
      my actions, I 'digest' an xml file. The digester.jar,
      collections.jar, and beanutils.jar are all placed in 'WEB-INF/lib'
      for the web-app in question.

      The difficulty that I ran into is that the Digester could not find
      the classes that needed to be instantiated. I found a solution by
      comparing the struts and commons versions of ObjectCreateRule. The
      struts-ObjectCreateRule locates a class using the current-class'
      ClassLoader, while the commons-ObjectCreateRule first tries to use
      the context-ClassLoader that is set in the currentThread.

      struts.digester.ObjectCreateRule (line 151):
      // Instantiate the new object and push it on the context stack
      Class clazz = Class.forName(realClassName);

      commons.digester.ObjectCreateRule (line 156):

      // Check to see if the context class loader is set,
      // and if so, use it, as it may be set in server-side
      // environments and Class.forName() may cause issues
      ClassLoader ctxLoader =
      Thread.currentThread().getContextClassLoader();
      if (ctxLoader == null)

      { clazz = Class.forName(realClassName); }

      else

      { clazz = ctxLoader.loadClass(realClassName); }

      In the commons.digester.ObjectCreateRule, I made the following
      changes, to enforce using the class' ClassLoader:

      ClassLoader ctxLoader =
      Thread.currentThread().getContextClassLoader();
      - if (ctxLoader == null) {
      + if (true) { + System.out.println("OBJECTCREATERULE IS USING Class.forName()..."); clazz = Class.forName(realClassName); } else { clazz = ctxLoader.loadClass(realClassName); }

      And this worked for me. I understand (more or less) the intent behind
      the code in commons-ObjectCreateRule, but I'm not enough of an expert
      in ClassLoaders to suggest a good solution. Craig, can you help out
      on this one? Perhaps, there's something I need to do that does not
      involve changing the commons-digester1.0 at all?

      1. ASF.LICENSE.NOT.GRANTED--patchfile.txt
        3 kB
        Gidado-Yisa Immanuel
      2. ASF.LICENSE.NOT.GRANTED--patchfile.txt
        3 kB
        Gidado-Yisa Immanuel

        Activity

        Hide
        Gidado-Yisa Immanuel added a comment -

        Created an attachment (id=334)
        Patches for 2 classes: Digest.java and ObjectCreateRule.java

        Show
        Gidado-Yisa Immanuel added a comment - Created an attachment (id=334) Patches for 2 classes: Digest.java and ObjectCreateRule.java
        Hide
        Gidado-Yisa Immanuel added a comment -

        I found a workaround for the problem that I was having that
        allows me to use commons-digester 1.0 as is: Before I call
        the 'Digester.parse()' method, I save the current
        ContextClassLoader (CCL), then set the CCL to null. After
        I've called 'parse()', I set the CCL back to the previously
        saved ClassLoader:

        try

        { ClassLoader cl = Thread.currentThread.getContextClassLoader(); Thread.currentThread().setContextClassLoader(null); digester.parse(); Thread.currentThread().setContextClassLoader(cl); }

        However, this is probably not very portable code across
        servlet containers (works in Tomcat 3.2.2, though). So I
        created a patch which defaults to using the calling-class'
        ClassLoader in ObjectCreateRule. The user will be able
        to set the default ClassLoader by setting a boolean property
        in the Digester. I'm posting the patch in jakarta-commons.

        Rey, thanks for the insight/code; although I didn't use it
        (so as stay simple), I'm making a mental note of it.

        Show
        Gidado-Yisa Immanuel added a comment - I found a workaround for the problem that I was having that allows me to use commons-digester 1.0 as is: Before I call the 'Digester.parse()' method, I save the current ContextClassLoader (CCL), then set the CCL to null. After I've called 'parse()', I set the CCL back to the previously saved ClassLoader: try { ClassLoader cl = Thread.currentThread.getContextClassLoader(); Thread.currentThread().setContextClassLoader(null); digester.parse(); Thread.currentThread().setContextClassLoader(cl); } However, this is probably not very portable code across servlet containers (works in Tomcat 3.2.2, though). So I created a patch which defaults to using the calling-class' ClassLoader in ObjectCreateRule. The user will be able to set the default ClassLoader by setting a boolean property in the Digester. I'm posting the patch in jakarta-commons. Rey, thanks for the insight/code; although I didn't use it (so as stay simple), I'm making a mental note of it.
        Hide
        Gidado-Yisa Immanuel added a comment -

        Created an attachment (id=335)
        Use this patch: Patches for 2 classes

        Show
        Gidado-Yisa Immanuel added a comment - Created an attachment (id=335) Use this patch: Patches for 2 classes
        Hide
        Gidado-Yisa Immanuel added a comment -

        Previous patch had debug code in it, use patch "07/18/01 07:59"

        Show
        Gidado-Yisa Immanuel added a comment - Previous patch had debug code in it, use patch "07/18/01 07:59"
        Hide
        Craig McClanahan added a comment -

        For the commons version of digester (in source CVS repository, not yet a
        released version), you now have two ways to accomplish what you're after:

        • Call digester.setUseContextClassLoader(false) to make the class loader
          used to load Digester itself the first choice.
        • Call digester.setClassLoader() to set the class loader that Digester
          will use to load application classes, no matter what setting is set
          for the "useContextClassLoader" property.

        These changes will be released as part of Commons Digester 1.1.

        Show
        Craig McClanahan added a comment - For the commons version of digester (in source CVS repository, not yet a released version), you now have two ways to accomplish what you're after: Call digester.setUseContextClassLoader(false) to make the class loader used to load Digester itself the first choice. Call digester.setClassLoader() to set the class loader that Digester will use to load application classes, no matter what setting is set for the "useContextClassLoader" property. These changes will be released as part of Commons Digester 1.1.

          People

          • Assignee:
            Unassigned
            Reporter:
            Gidado-Yisa Immanuel
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development