Permit class loading after framework shutdown
---------------------------------------------
Key: FELIX-2128
URL: https://issues.apache.org/jira/browse/FELIX-2128
Project: Felix
Issue Type: Improvement
Components: Framework
Affects Versions: felix-2.0.3
Environment: Linux, JDK 6.
Reporter: Jesse Glick
Priority: Minor
In
http://hg.netbeans.org/core-main/raw-file/default/core.netigso/test/unit/src/org/netbeans/core/osgi/ActivatorTest.java
I have some unit tests which repeatedly launch Felix, start some bundles, shut
down, and repeat. On occasion - more reproducibly if calls to System.gc() and
System.runFinalization() are inserted into ActivatorTest.setUp - I get errors
like these (though the test still passes):
{noformat}
ERROR: JarContent: Unable to read bytes. (java.lang.IllegalStateException: zip
file closed)
java.lang.IllegalStateException: zip file closed
at java.util.zip.ZipFile.ensureOpen(ZipFile.java:403)
at java.util.zip.ZipFile.getEntry(ZipFile.java:148)
at java.util.jar.JarFile.getEntry(JarFile.java:206)
at org.apache.felix.framework.util.JarFileX.getEntry(JarFileX.java:77)
at
org.apache.felix.framework.cache.JarContent.getEntryAsBytes(JarContent.java:120)
at
org.apache.felix.framework.ModuleImpl$ModuleClassLoader.findClass(ModuleImpl.java:1746)
at
org.apache.felix.framework.ModuleImpl.findClassOrResourceByDelegation(ModuleImpl.java:723)
at org.apache.felix.framework.ModuleImpl.access$100(ModuleImpl.java:61)
at
org.apache.felix.framework.ModuleImpl$ModuleClassLoader.loadClass(ModuleImpl.java:1698)
at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2427)
at java.lang.Class.getMethod0(Class.java:2670)
at java.lang.Class.getMethod(Class.java:1603)
at
org.openide.util.WeakListenerImpl$ListenerReference.getRemoveMethod(WeakListenerImpl.java:610)
at
org.openide.util.WeakListenerImpl$ListenerReference.run(WeakListenerImpl.java:563)
at
org.openide.util.lookup.implspi.ActiveQueue$Impl.run(ActiveQueue.java:73)
at java.lang.Thread.run(Thread.java:619)
Feb 23, 2010 3:22:53 PM org.openide.util.lookup.implspi.ActiveQueue$Impl run
WARNING: null
java.lang.NoClassDefFoundError: org/openide/loaders/FolderListListener
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2427)
at java.lang.Class.getMethod0(Class.java:2670)
at java.lang.Class.getMethod(Class.java:1603)
at
org.openide.util.WeakListenerImpl$ListenerReference.getRemoveMethod(WeakListenerImpl.java:610)
at
org.openide.util.WeakListenerImpl$ListenerReference.run(WeakListenerImpl.java:563)
at
org.openide.util.lookup.implspi.ActiveQueue$Impl.run(ActiveQueue.java:73)
at java.lang.Thread.run(Thread.java:619)
Caused by: java.lang.ClassNotFoundException:
org.openide.loaders.FolderListListener
at
org.apache.felix.framework.ModuleImpl.findClassOrResourceByDelegation(ModuleImpl.java:779)
at org.apache.felix.framework.ModuleImpl.access$100(ModuleImpl.java:61)
at
org.apache.felix.framework.ModuleImpl$ModuleClassLoader.loadClass(ModuleImpl.java:1698)
at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
... 8 more
{noformat}
Here some code in a bundle has registered a special ReferenceQueue and is doing
some minor cleanup of recently finalized objects. Unfortunately running this
code block can trigger fresh class loading and JarContent throws an ISE when
trying to load from the now-closed JAR file. The situation is less likely to
come up in a real app than in a unit test but still possible - in case a bundle
is dynamically unloaded, or some cleanup tasks happen to run during JVM
shutdown.
The timing of class loading is not easily predictable: it will occur any time a
section of code is run for the first time. Even in the absence of apparent
threads, it is very hard to guarantee that no class loading will take place
after code ceases to be called externally, since overridden finalize() methods
and JVM shutdown hooks can be called passively at any time. The code in this
example could disable its RQ upon BundleActivator.stop if it were originally
written for use inside OSGi, but it is not.
I have come up with a patch to JarFileX which lets it load classes from
nominally closed JARs on an emergency basis. (This was implemented years ago in
the NetBeans module system.) To make it safer for the original JAR to be
recreated or deleted, especially on Windows with its mandatory file locks, a
temporary copy is made.
(Safest would be to copy the original JAR eagerly in close(), but this would
impose a huge performance penalty. Instead, the JAR is copied on demand only in
cases where an ISE would otherwise be thrown. It is possible for the JAR to be
modified/deleted after close() but before the next class load, in which case
the ISE will still occur; similarly if a SecurityManager prevents the copying,
etc.)
It is not clear to me from the OSGi spec whether it is permissible for the
bundle class loader to continue to function after framework shutdown (or
generally after a bundle moves into an unresolved state). The spec seems to say
that Bundle.loadClass should throw ISE, but this is different from performing
implicit class loading at the VM's request as part of running already-loaded
code. For what it's worth, 4.4.10 does say "all old exports must remain
available for existing bundles and future resolves until the refreshPackages
method is called or the Framework is restarted". While more permissive behavior
is very useful for situations like these, if it contradicts the spec, I might
suggest one or both of the following:
1. Enable emergency loading only with an optional Felix framework property.
Then, for example, unit tests which knew they would be starting and stopping
code which potentially left behind live threads or finalizer queues etc. could
set the property to avoid printing such exceptions.
2. At least report when close() was called to assist the user in debugging the
problem.
--
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.