On 11/25/13, 22:13 , Karl Rieb wrote:
Thanks for the quick response!  We have managed to come up with a work-around 
for our problem.  I will document it here in case someone else comes across the 
same issue we had, although I did have some questions (see last paragraph of 
e-mail).

I created a test environment to reproduce the bug that avoided spawning any 
threads; I made necessary calls within the Activator.  Using this test 
environment, I was able to narrow down the issue to an embedded dependency 
being used by our bundle.  Tracing through the code, I saw the embedded 
dependency dynamically adds a Java Security provider if one is not already 
present.  The code looks something like:

     if( Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null ) {
         Security.addProvider(new BouncyCastleProvider());
     }

This happens upon loading one of the classes in the embedded dependency.  Now 
this is pure educated guessing on my part, but I think the problem arises 
because the BouncyCastleProvider class is being loaded by the current bundle's 
ClassLoader, but getting added statically to a class, java.security.Security, 
that is handled by one of the core/boot felix ClassLoaders (since it is a base 
JVM class).  So now this class, java.security.Security, that is accessible by 
all the bundles has a reference to a class, BouncyCastleProvider, that is tied 
to a specific bundle's bootloader.  When I do the uninstall and re-install of 
the bundle, the above code never adds a new provider because it can still see 
the old one it set previously (this is why I think the java.security.Security 
class is being loaded outside of the bundle's ClassLoader).

Now when the newly re-installed bundle makes some crypto calls that require the 
BouncyCastle provider, some classes get loaded 
(sun.reflect.ConstructorAccessorImpl, specifically) using the old bundle's 
ClassLoader.  This triggers the NullPointerException because the old 
ClassLoader is disposed.  Again, purely an educated guess.

WORKAROUND:  The workaround we set in place was to add the BouncyCastleProvider 
as a Java Security provider in our bootstrapping bundle that starts before all 
our other bundles.  This bundle removes any existing BouncyCastleProvider then 
adds one.  When the bootstrapping bundle is stopped, it removes the 
BouncyCastleProvider.  This ensures the BouncyCastle provider always has a 
valid ClassLoader since it is only ever available as long as our bootstrapping 
bundle is active and started.  Additionally, this lets us managed the version 
of BouncyCastleProvider we want to use.

Note: we had a strict requirement that prevented us from being able to restart 
our OSGi framework.  A simpler work-around was to configure the boot delegation 
property for Felix:

   -Dorg.osgi.framework.bootdelegation="sun.*"

In either case, the issue no longer appears in our test environment using 
either fix mentioned above.  The boot delegation fix makes me a bit nervous 
since presumably you are still referencing an old ClassLoader.

Is there is a recommended practice for using the Java Crypto API in OSGi with 
custom providers?  Should providers be added statically outside of Felix?  
Should the Java Crypto APIs be avoided?  Or is boot delegation meant to be set 
when using these types of frameworks?

The tricky aspect here is that the embedded library automatically adds the provider when someone loads a class. It seems you'd have to modify the library to be aware that it has active/inactive states and it can only provide the crypto algorithm when it is active. There is no other way that I can think of because a non-activated bundle cannot get any events and it really isn't possible for the bundle to learn that it is being uninstalled (or refresh).

A slight different way to do it would be an extender pattern, where the provider bundles could offer their provider objects and the extender bundle could provide them when the bundle is resolved and remove them when the bundle is uninstalled/refreshed.

There is no real way around this issue, you cannot pass objects around from bundles and then not clean up after them after an uninstall/refresh.

-> richard


-Karl


________________________________________
From: Richard S. Hall [[email protected]]
Sent: Saturday, November 23, 2013 3:25 PM
To: [email protected]
Subject: Re: Stale BundleWiringImpl (ClassLoader) used for 
sun.reflect.ConstructorAccessorImpl

On 11/23/13, 13:29 , Karl Rieb wrote:
Hi,

We are using felix framwork 3.0.4 and are having problems when one of our 
bundle upgrades.  We noticed the same issue also occurs if we uninstall, then 
later re-install the bundle.

The bundle has an embedded dependency (e.g. a non-OSGi jar) that makes a call to 
MessageDigest.getInstance("MD4").  When the bundle is first installed, This 
internal call goes through without a problem.  But after upgrading the bundle or 
uninstalling and re-installing it, we start to get the following exceptions:

   Caused by: java.security.NoSuchAlgorithmException: Error constructing 
implementation (algorithm: MD4, provider: BC, class: 
org.bouncycastle.jce.provider.JDKMessageDigest$MD4)
          at java.security.Provider$Service.newInstance(Provider.java:1262)
          at sun.security.jca.GetInstance.getInstance(GetInstance.java:236)
          at sun.security.jca.GetInstance.getInstance(GetInstance.java:164)
          at java.security.Security.getImpl(Security.java:695)
          at java.security.MessageDigest.getInstance(MessageDigest.java:159)
          at rpc.security.ntlm.Responses.ntlmHash(Responses.java:186)
          at rpc.security.ntlm.Responses.ntlmv2Hash(Responses.java:203)
          at rpc.security.ntlm.Responses.getLMv2Response(Responses.java:102)
          at 
rpc.security.ntlm.NtlmAuthentication.createType3(NtlmAuthentication.java:270)
          ... 23 more
Caused by: java.lang.NullPointerException
          at 
org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1432)
          at 
org.apache.felix.framework.BundleWiringImpl.access$400(BundleWiringImpl.java:72)
          at 
org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.loadClass(BundleWiringImpl.java:1843)
          at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
          at java.security.Provider$Service.getImplClass(Provider.java:1279)
          at java.security.Provider$Service.newInstance(Provider.java:1237)

Using a debugger, we traced through this call and found that the 
NullPointerException gets triggered when trying to load the 
sun.reflect.ConstructorAccessorImpl class for creating a new instance of 
org.bouncycastle.jce.provider.JDKMessageDigest$MD4.  Note that the classloader 
has no problems finding org.bouncycastle.jce.provider.JDKMessageDigest$MD4, it 
only has an issue with sun.reflect.ConstructorAccessorImpl.

Stepping through the BundleWiringImpl calls, we noticed that the 
NullPointerException occurs because the BundleWiringImpl instance has been 
disposed (e.g. it is stale).  The revision of the BundleWiringImpl confirms 
this since it matches the revision of the original bundle install (when we 
first start up the felix container).  For example, the revision will be 
something like [25] when the currently running bundle revision is actually 
[25.2] or [27] (depending if we do upgrades or uninstall/re-install).

All the other non-sun classes required by the bundle go against the correct 
version of BundleWiringImpl except for sun.reflect.ConstructorAccessorImpl.  
Some how, no matter how many times we uninstall and re-install the bundle, the 
sun.reflect.ConstructorAccessorImpl class always gets loaded by the same stale 
instance of BundleWiringImpl (e.g. [25]).  I imagine there must be some sort of 
caching going on somewhere that is keeping a reference to this old classloader, 
but I don't understand where or how.
The framework isn't caching it, could be in some class loader somewhere.
But still, this would indicate that when you uninstall and refresh, that
you aren't really refreshing everything. In most cases, someone has left
a thread running somewhere that continues to use the stale class loader.

Also, you could try on a new framework version, but if it is a runaway
thread, it probably won't help.

-> richard


Note that we are able to avoid this issue if we set:

    org.osgi.framework.bootdelegation=sun.*

Note that just setting 'sun.reflect.*' does not work.  We tried using dynamic 
imports for all sun packages, but this did not work:

    <DynamicImport-Package>sun.*</DynamicImport-Package>

Unfortunately we can not easily restart the Felix container to set the boot 
delegation property.  Is there a way to avoid this class loading issue without 
requiring a restart of Felix?

-Karl
This electronic message contains information which may be confidential or 
privileged. The information is intended for the use of the individual or entity 
named above. If you are not the intended recipient, be aware that any 
disclosure, copying, distribution or use of the contents of this information is 
prohibited. If you have received this electronic transmission in error, please 
notify us by e-mail at ([email protected]) immediately.


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

This electronic message contains information which may be confidential or 
privileged. The information is intended for the use of the individual or entity 
named above. If you are not the intended recipient, be aware that any 
disclosure, copying, distribution or use of the contents of this information is 
prohibited. If you have received this electronic transmission in error, please 
notify us by e-mail at ([email protected]) immediately.


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to