dev@glassfish.java.net

Re: [v3] exporting all Metro packages for OSGi

From: Fabian Ritzmann <Fabian.Ritzmann_at_Sun.COM>
Date: Wed, 27 Aug 2008 15:47:08 +0300

On 27. Aug 2008, at 15:28, Richard S. Hall wrote:
> Fabian Ritzmann wrote:
>> On 27. Aug 2008, at 13:49, Sahoo wrote:
>>
>>> No, I still don't understand why we are seeing failures. My
>>> understanding is that Metro is a OSGi bundle and some code in that
>>> OSGi bundle is attempting to load another non-exported class from
>>> the same OSGi bundle using a variant of Class.forName() that does
>>> not take any classloader as argument. In such a case,
>>> Class.forName() uses caller's classloader, which is the
>>> classloader of the Metro bundle. Such a class loader should be
>>> able to load any class that's part of Metro bundle.
>>>
>>> There are two assumptions here:
>>> 1. Class being loaded is part of Metro OSGi bundle.
>>> 2. No classloader is passed to Class.forName().
>>>
>>> Is any of them wrong?
>>
>> Both assumptions are correct. I better leave it to Richard to
>> explain why Class.forName(String name) is not doing what we are
>> expecting.
>
> It is not my understanding of the situation, but I could be incorrect.
>
> I understood the situation to be something like this:
>
> Bundle A has a non-public service impl class. Bundle B tries to load
> that class using Class.forName() using a class loader that tries to
> load from all exported packages. Since Bundle A's class is not
> imported, Bundle B does not find it. Failure. However, if Bundle A
> exports the service impl class, then the class loader from Bundle B
> can find it. Success.
>
> I agree with Sahoo, if it is the same bundle (or you are using
> Bundle.loadClass()), you should be able to find the class no matter
> what, which is why I called into question this situation and started
> investigating it in the first place. But obviously the situation
> must be different than this, otherwise we wouldn't be seeing the
> issue.

The stack trace we were looking at was this:

Aug 25, 2008 4:00:46 PM
[com.sun.xml.ws.policy.privateutil.ServiceFinder] access$100
SEVERE: [failed to localize]
WSP_0025_SPI_FAIL_SERVICE_MSG
(com.sun.xml.ws.policy.spi.PolicyAssertionValidator, [failed to
localize]
WSP_0027_SERVICE_PROVIDER_NOT_FOUND
(com.sun.xml.ws.transport.tcp.wsit.TCPTransportPolicyValidator))
java.lang.ClassNotFoundException:
com.sun.xml.ws.transport.tcp.wsit.TCPTransportPolicyValidator
    at
org
.glassfish
.web.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1504)
    at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:374)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:247)
    at com.sun.xml.ws.policy.privateutil.ServiceFinder
$LazyIterator.next(ServiceFinder.java:378)
    at
com
.sun
.xml.ws.policy.privateutil.ServiceFinder.toArray(ServiceFinder.java:228)
    at com.sun.xml.ws.policy.privateutil.PolicyUtils
$ServiceProvider.load(PolicyUtils.java:445)
    at
com
.sun
.xml
.ws
.policy
.AssertionValidationProcessor
.<clinit>(AssertionValidationProcessor.java:55)


All the com.sun.xml.ws.policy and com.sun.xml.ws.transport classes as
well as the META-INF/services file are in the same bundle.

FWIW, it's quite possible that we will bump into service instantiation
scenarios across bundles sooner or later, but in that case I guess an
export is the only and proper solution anyway.

Fabian


>>> Fabian Ritzmann wrote:
>>>> Hi,
>>>>
>>>> Richard has had a look at some of the class loading issues we
>>>> have had with the Metro bundles. Here is my summary as I
>>>> understand it and some more comments and questions:
>>>>
>>>> Metro code is using Class.forName(String className) to
>>>> dynamically load classes. In an OSGi environment, these classes
>>>> usually can not be found unless the packages are explicitly
>>>> imported. We bumped into this issue when doing META-INF/services
>>>> look-ups but there are almost certainly other scenarios where
>>>> this will lead to ClassNotFoundExceptions. GlassFish implemented
>>>> the WebappClassLoader, which in addition to the OSGi classloaders
>>>> is loading all classes that are exported. That is why an export
>>>> of all classes solves the issue for us [1].
>>>>
>>>> Richard suggested a few approaches to solve this issue without
>>>> exporting everything:
>>>>
>>>> 1) Replace calls to Class.forName(String className) with
>>>> Bundle.loadClass(String name).
>>>>
>>>> 2) Create a ClassLoader wrapper for Bundle.loadClass.
>>>>
>>>> I believe 2) is already close to what we are doing with the
>>>> WebappClassLoader. 1) doesn't really work for us because Metro
>>>> libraries are used in a variety of environments, most of which we
>>>> don't have any control over, and we can't introduce dependencies
>>>> on OSGi.
>>>>
>>>> I believe another solution could be if we manually added the
>>>> packages in question to the Import-Package manifest header? I am
>>>> not very comfortable however with checking dozens of libraries
>>>> for their use of dynamically created classes. The risk that we
>>>> will be missing some package and it slips through testing is high.
>>>>
>>>>
>>>> Fabian
>>>>
>>>>
>>>> [1] Plus the bnd tool we are using is creating an import
>>>> statement for every package that is being exported, i.e. by
>>>> exporting everything we are making sure that all dynamically
>>>> loaded classes are getting imported as well.
>>