OpenJPA
  1. OpenJPA
  2. OPENJPA-540

Classloading issue with WAS, Spring, and OpenJPA

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 1.0.0, 1.0.1, 1.0.2, 1.0.3, 1.1.0
    • Fix Version/s: 1.0.3, 1.1.0
    • Component/s: kernel
    • Labels:
      None

      Description

      The WASManagedRuntime class needs to dynamically load and process a couple of WebSphere-specific classes in order to interact with the WebSphere Transaction Manager. Currently, when these classes are loaded (ExtendedJTATransaction and SynchronizationCallback), there is some problem with the Method invocations that are invoked against the JNDI object that is looked up (java:comp/websphere/ExtendedJTATransaction). There seems to be a mismatch between the Interface classes that are loaded to obtain the Method objects and the actual object that is returned by the JNDI lookup. The callstack is similar to the following:

      Caused by: <openjpa-1.0.1-r420667:592145 nonfatal user error> org.apache.openjpa.persistence.InvalidStateException: An error occured reflecting WebSphere proprietary interfaces. Please ensure that you are running the application from within WebSphere Application Server (version 5.0.2 or newer).
      at org.apache.openjpa.ee.WASManagedRuntime$WASTransaction.getGlobalId(WASManagedRuntime.java:157)
      at org.apache.openjpa.ee.WASManagedRuntime$WASTransaction.getStatus(WASManagedRuntime.java:104)
      ... 49 more
      Caused by: java.lang.IllegalArgumentException
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:615)
      at org.apache.openjpa.ee.WASManagedRuntime$WASTransaction.getGlobalId(WASManagedRuntime.java:155)
      ... 50 more

      The call to getGlobalId in WASManagedRuntime is like this:

      byte[] rval = (byte[]) _getGlobalId.invoke(_extendedTransaction, null);

      Debugging this problem has narrowed it down to a classloading issue for this scenario of using WAS v6.1.0.13, Spring 2.5.2, and OpenJPA 1.0.x. I'll post more on the proposed solution shortly.

      Kevin

        Activity

        Hide
        Kevin Sutter added a comment -

        The classloading issue for this environment seems to be related to the loader returned by this invocation:

        ClassLoader loader = _conf.getClassResolverInstance()
        .getClassLoader(getClass(), null);

        The loader returned by the ClassResolverImpl (and there are no external ClassResolvers configured) is a Spring classloader:

        [3/18/08 14:36:26:281 PDT] 00000013 SystemOut O BJK: loader=org.springframework.instrument.classloading.SimpleInstrumentableClassLoader@17e217e2

        I'm not an expert with Spring, but it seems that this SimpleInstrumentableClassLoader uses OverridingClassLoader which calls getResourceAsStream, instruments the bytecodes, and then defines the class.

        Unfortunately, this returned Class along with the associated Method classes do not seem to be compatible with the eventual Reflective Method invocations (reference the IllegalArgumentException in the Issue description).

        All other classloaders that are available to us during this processing (this WASManagedRuntime classloader, the context classloader, and the jndi object's classloader) all seem to return a more "reasonable" classloader:

        [3/18/08 14:36:26:281 PDT] 00000013 SystemOut O BJK: this loader=
        com.ibm.ws.classloader.CompoundClassLoader@2de82de8

        The WebSphere compound classloader works much better and provides us with the appropriate Class and Method objects to complete this WAS, Spring, and OpenJPA scenario.

        I, personally, do not quite understand the logic in the ClassResolver code for obtaining or creating the appropriate classloader. There's a comment in this Impl that says we need to do this to be compliant with section 12.5 of the spec. The JPA spec only has 11 chapters, so this must be referring to some other spec. JDO, maybe? Since this part of the kernel, that very well could be.

        But, even a more basic question is why do we need to get the loader from this ClassResolver instance? It seems that if we are in the WASManagedRuntime class and we have found the WAS ExtendedJTATransaction object in JNDI, then why are we concerned about this ClassResolver? It would seem that a simple call to to getClass().getClassLoader() would suffice for the WebSphere interfaces.

        Since this WASManagedRuntime class is only used in the non-EJB3 environments (where the TransactionSynchronizationRegistry doesn't exist yet), making this type of change seems benign. And, the various test scenarios that we have for WebSphere, Spring, and OpenJPA seem to work (again), it seems like a safe path. I will post the proposed patch shortly. Any comments or suggestions will be appreciated.

        Thanks,
        Kevin

        Show
        Kevin Sutter added a comment - The classloading issue for this environment seems to be related to the loader returned by this invocation: ClassLoader loader = _conf.getClassResolverInstance() .getClassLoader(getClass(), null); The loader returned by the ClassResolverImpl (and there are no external ClassResolvers configured) is a Spring classloader: [3/18/08 14:36:26:281 PDT] 00000013 SystemOut O BJK: loader=org.springframework.instrument.classloading.SimpleInstrumentableClassLoader@17e217e2 I'm not an expert with Spring, but it seems that this SimpleInstrumentableClassLoader uses OverridingClassLoader which calls getResourceAsStream, instruments the bytecodes, and then defines the class. Unfortunately, this returned Class along with the associated Method classes do not seem to be compatible with the eventual Reflective Method invocations (reference the IllegalArgumentException in the Issue description). All other classloaders that are available to us during this processing (this WASManagedRuntime classloader, the context classloader, and the jndi object's classloader) all seem to return a more "reasonable" classloader: [3/18/08 14:36:26:281 PDT] 00000013 SystemOut O BJK: this loader= com.ibm.ws.classloader.CompoundClassLoader@2de82de8 The WebSphere compound classloader works much better and provides us with the appropriate Class and Method objects to complete this WAS, Spring, and OpenJPA scenario. I, personally, do not quite understand the logic in the ClassResolver code for obtaining or creating the appropriate classloader. There's a comment in this Impl that says we need to do this to be compliant with section 12.5 of the spec. The JPA spec only has 11 chapters, so this must be referring to some other spec. JDO, maybe? Since this part of the kernel, that very well could be. But, even a more basic question is why do we need to get the loader from this ClassResolver instance? It seems that if we are in the WASManagedRuntime class and we have found the WAS ExtendedJTATransaction object in JNDI, then why are we concerned about this ClassResolver? It would seem that a simple call to to getClass().getClassLoader() would suffice for the WebSphere interfaces. Since this WASManagedRuntime class is only used in the non-EJB3 environments (where the TransactionSynchronizationRegistry doesn't exist yet), making this type of change seems benign. And, the various test scenarios that we have for WebSphere, Spring, and OpenJPA seem to work (again), it seems like a safe path. I will post the proposed patch shortly. Any comments or suggestions will be appreciated. Thanks, Kevin
        Hide
        Kevin Sutter added a comment -

        The patch looks more complicated than it really is due to some formatting cleanup... The only real change is to skip the call to the ClassResolver to get the classloader and just use getClass().getClassLoader() instead.

        Show
        Kevin Sutter added a comment - The patch looks more complicated than it really is due to some formatting cleanup... The only real change is to skip the call to the ClassResolver to get the classloader and just use getClass().getClassLoader() instead.

          People

          • Assignee:
            Kevin Sutter
            Reporter:
            Kevin Sutter
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development