Announcement Announcement Module
Collapse

Spring Dynamic Modules forum decommissioned in favor of Eclipse Gemini Blueprint

With the official first release of Eclipse Gemini Blueprint shipped, the migration of the Spring Dynamic Modules code base to the Eclipse Foundation, as part of the Gemini project, has been completed.

As such, this forum has been decommissioned in favour of the Eclipse Gemini forums.
See more
See less
Classloader issues with Spring DM 1.1.1 Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Classloader issues with Spring DM 1.1.1

    We are building an enterprise application client in Swing on an Equinox framework with application context supported by Spring Dynamic Modules. Our application runs very well under Spring DM 1.0.2, but after upgrading to Spring DM 1.1.1, we have not been able to resolve the classloader problems. Here is one scenario:

    1) Bundle A queries and RMI service for a result set of type X
    2) Bundle B exports type X
    3) Bundle A imports type X
    4) Upon receiving a result set of type X, the RMI classloader throws ClassNotFoundException, because it is not using the classloader space of Bundle A.
    5) As a test, instantiating type X just before the remote call within Bundle A is successful. Type X is visible from there.

    The problem here is that the default JVM implementation of RMIClassLoaderSpi is not compatible with the Spring DM 1.1.1 classloaders, and should not be allowed to run with it. Spring DM should use system property "java.rmi.server.RMIClassLoaderSpi" to override this classloader with a compatible implementation of RMIClassLoaderSpi. I have been able to resolve the above scenario by doing this, though my resolution is a hack because it delegates to the bundle classloaders on the basis of hardcoded package-to-bundle mappings.

    Another problem, which I have not resolved in any way, is the instantiation of org.springframework.jms.listener.DefaultMessageLis tenerContainer within bundle A. The simple method call new DefaultMessageListenerContainer() throws ClassNotFoundException for javax/jms/JMSException. The crippler is that I can call new javax.jms.JMSException() in bundle A directly before the failure, and that call succeeds. There is nothing missing for package javax.jms. Here are all the circumstances:

    1) The superclass of DefaultMessageListenerContainer is AbstractPollingMessageListenerContainer, which instantiates inner class MessageListenerContainerResourceFactory in its constructor.
    2) MessageListenerContainerResourceFactory declares several methods that throw JMSException

    At the failure point in bundle A:

    3) getClass().getClassLoader() returns the framework classloader hook.
    4) getClass().getClassLoader().loadClass("org.springf ramework.jms.listener.DefaultMessageListenerContai ner") succeeds
    5) getClass().getClassLoader().loadClass("javax.jms.J MSException") succeeds
    6) Expression DefaultMessageListenerContainer.class throws ClassNotFoundException for javax.jms.JMSException from org.eclipse.osgi.framework.internal.core.BundleLoa der.findClass(). The stack trace goes through the framework classloader hook mentioned in item #3.
    7) Thread.currentThread().getContextClassLoader() evaluates to org.springframework.osgi.context.internal.classloa der.ChainedClassLoader
    8) Thread.currentThread().getContextClassLoader().loa dClass("org.springframework.jms.listener.DefaultMe ssageListenerContainer") fails exactly the same as item #6 (for obvious reasons)
    9) Thread.currentThread().getContextClassLoader().loa dClass("javax.jms.JMSException") fails in the same way as item #6

    I'm not sure what the fundamental problem is here, but it is clear that the bundle classloader is not being honored by the Spring DM 1.1.1 thread context classloader. A proper context switch has been made into bundle A, all classes are visible to bundle A (as proven by trial), yet the instantiation fails. We need some way to force this ChainedClassLoader to stop looking for classes on the boot classpath, because they are not there (and we will absolutely not deploy these classes on the boot classpath for any reason whatsoever). If Spring DM 1.1.1 wants to successfully remove proxy weaving, its thread context classloader must learn to look in the calling bundle for classes. It is not the responsibility of the framework bootstrap to deliver class imports that have been properly declared--and can be properly loaded--by bundle A.

    For now, we need a workaround that really works. Can anyone offer a suggestion?

  • #2
    It seems there are two issues in this post and I'll try to answer them separately. I would recommend to post separate threads for each of them so they can be followed individually.

    Regarding the upgrade to 1.1.1, from 1.0.2 - the classloading issues are explained in the FAQ at: http://static.springframework.org/os...upgrade-to-1.1
    Serialization in general is problematic under OSGi simply because it conflicts with the modularity enforcement. RMI causes arbitrary (and potentially unknown) classes to be loaded and defined which can conflict with classes loaded by a module. The main problem is that the same class can be loaded by different classloaders which results in ClassCastExceptions (type differences).
    One solution to get around this is to use a DynamicImport which looses down the constrains between modules.

    The problem here is that the default JVM implementation of RMIClassLoaderSpi is not compatible with the Spring DM 1.1.1 classloaders, and should not be allowed to run with it. Spring DM should use system property "java.rmi.server.RMIClassLoaderSpi" to override this classloader with a compatible implementation of RMIClassLoaderSpi. I have been able to resolve the above scenario by doing this, though my resolution is a hack because it delegates to the bundle classloaders on the basis of hardcoded package-to-bundle mappings.
    Spring-DM doesn't addresses serialization under OSGi. I would argue that this is something outside Spring-DM itself, it's more of a serialization (in your case RMI) problem, separate from Spring or Spring-DM.
    However, feel free to raise an issue for it and we'll consider it.

    Comment


    • #3
      Regarding the JMS question:

      Another problem, which I have not resolved in any way, is the instantiation of org.springframework.jms.listener.DefaultMessageLis tenerContainer within bundle A. The simple method call new DefaultMessageListenerContainer() throws ClassNotFoundException for javax/jms/JMSException. The crippler is that I can call new javax.jms.JMSException() in bundle A directly before the failure, and that call succeeds.
      Based on your description, it's likely that there are multiple JMS class types loaded by the bundles. You might be able to load the JMS class, version A but Spring JMS classes might not since they might depend on version B which is not available.

      3) getClass().getClassLoader() returns the framework classloader hook
      What do you mean by "classloader hook" ?

      I'm not sure what the fundamental problem is here, but it is clear that the bundle classloader is not being honored by the Spring DM 1.1.1 thread context classloader. A proper context switch has been made into bundle A, all classes are visible to bundle A (as proven by trial), yet the instantiation fails. We need some way to force this ChainedClassLoader to stop looking for classes on the boot classpath, because they are not there (and we will absolutely not deploy these classes on the boot classpath for any reason whatsoever)
      Spring-DM does not use the boot classpath - based on your OSGi platform configuration, the boot classpath can be made available to your bundle or not (search the Equinox mailing list as this topic has been discussed many times).
      If I recall correctly, there was a change between 3.2 and 3.3 in the way Equinox delegated to the boot classpath.
      Additionally, note that Spring-DM doesn't uses the TCCL for class loading - it rather does all classloading inside the managed application context through the bundle classloader.
      Do you know who is using the TCCL?


      If Spring DM 1.1.1 wants to successfully remove proxy weaving, its thread context classloader must learn to look in the calling bundle for classes. It is not the responsibility of the framework bootstrap to deliver class imports that have been properly declared--and can be properly loaded--by bundle A.
      I think there is a misunderstanding here. I'm not sure what you mean by "removing proxy weaving" - Spring-DM doesn't try to do that, in fact it uses proxies internally.
      Spring-DM can manage the TCCL for OSGi services, from both the client and server side.
      The TCCL management is documented at:
      http://static.springframework.org/os...eploy:ext-libs
      and
      http://static.springframework.org/os...:singular:tccl

      I would recommend that you create two small bundles with just the JMS imports and if you encounter problems, post them so we can work them out.
      Additionally, what version of Spring-DM are you using for the JMS case? I would highly recommend using 1.1.x (if possible, try the 1.1.x branch, i.e. the upcoming 1.1.2, there are snapshots available in the maven repo - see http://www.springframework.org/osgi#repos).

      Comment

      Working...
      X