Apache OpenOffice (AOO) Bugzilla – Issue 93740
Current thread context class loader on UNO callback threads
Last modified: 2014-02-25 19:18:33 UTC
(this issue might be related to issue 80100 and mail thread http://www.mail-archive.com/dev@api.openoffice.org/msg07637.html) Highlights -------------------------------- - Observation: OpenOffice.org uses parent class loader (sun.misc.Launcher$AppClassLoader) as current thread's context class loader. - Problem 1: This breaks the definition of context class loader: http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Thread.html#getContextClassLoader() - Problem 2: It also breaks compatibility with major Java libraries such as Springframework, TrueZip and JGoodies - Solution: OpenOffice.org should assign extension class loader (com.sun.star.lib.unoloader.UnoClassLoader) as current thread's context class loader. A more detailed description of the problem follows. Summary -------------------------------- To make sure everybody (including myself) is clear on the issue here is a little history. OpenOffice.org 2.4 introduced a new class loader architecture. In the new architecture each extension gets its own class loader and all extensions share a parent class loader. The shared parent class loader (sun.misc.Launcher$AppClassLoader) provides access to standard JRE classes, standard OpenOffice.org libraries (UNO) and the libraries specified in the class path of the active JRE (Tools => Options... => OpenOffice.org => Java => Class Path...). The extension-local class loader (com.sun.star.lib.unoloader.UnoClassLoader) provides access to extension classes and libraries bundled with the extension. All this makes sense, as the new architecture helps avoid collisions among different versions of the same libraries bundled by different extensions, and provides a means to hot deploy/undeploy extensions. Tomcat and other application servers, for example, use this approach, only for them an extension is a J2EE application. The problem -------------------------------- At certain point in time an extension might want to load a class path resource (icons, Swing look'n'feel, property file, etc). Usually that resource is bundled with the extension and therefore must be loaded by the extension-local class loader. The extension starts by acquiring an instance of a class loader. There are several ways to get a class loader, but two of them stand out: 1. someClass.getClassLoader() - this class loader is the one that loaded the class object someClass. If someClass belongs to the extension and the resource is in the same bundle (usually even the same JAR) as someClass, then the class loader acquired through this method will succeed in finding that resource. 2. Thread.currentThread().getContextClassLoader() - this class loader "is provided by the creator of the thread for use by code running in this thread when loading classes and resources", see http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Thread.html#getContextClassLoader(). According to this definition, this method should also succeed in loading the resources. But! Read on... The issue -------------------------------- Let's take a look at threads running in Java-based extensions. Every extension has one or more entry points that are called by OpenOffice.org through UNO API. These could be methods of XAsyncJob, XModifyListener, etc. Let's call them callbacks. Because it is OpenOffice.org that calls into callbacks it is OpenOffice.org process that creates the threads. Thus, OpenOffice.org is "the creator" in the description above. Now, as it turns out the context class loader set by OpenOffice.org is the parent class loader (sun.misc.Launcher$AppClassLoader). Consequently, any attempt to load an extension-specific resource using that class loader fail. At first sight the solution is simple. Let's all use the first method and avoid the second one. Well, turns out it's not simple at all. Many popular Java libraries, such as Springframework, TrueZip and JGoodies use the second method to load their resources. So we cannot bundle them with the extension. We have to put them in the parent classpath to make sure they get loaded by the parent class loader. But that defeats the whole purpose of the OXT extensions. The idea was that one should be able to bundle their UNO code and dependent libraries into a single OXT file and install it in OOo. But wait there is another solution. Why don't we set our own context class loader in every callback method. This actually works. However, the problematic point here is "every callback method". How many callbacks can an extension have? Does a developer have to remember to reload the context class loader in every listener, every async job? This is not a clean solution. In fact it's plain ugly. Right now for me, this is a temporary workaround. Proposed fix -------------------------------- I think that OpenOffice.org should set the extension-specific class loader (UnoClassLoader) in all threads that call into the extension code. This is what Tomcat (and I'm sure GlassFish, JBoss, Jetty) does (only they call it WebAppClassLoader), and this is what makes Springframework and others work well in those environments. Thanks, Yegor
TM->JL: please have a look.
.
@uga: The only solution that works well is to have the extension code explicitly set the context class loader where necessary. The UNO framework cannot (easily) do this automatically (set the extension's class loader as context class loader around every call into the extension's code), as calls into extension code can be direct Java-to-Java calls (we would need to introduce additional proxy objects, which would come at a non-negligible cost).
Hi sb, I agree that it may be hard to implement, although I think the affected code will be confined to UNO jars only (unoil.jar, ridl.jar, jurt.jar, jug.jar). However, let's point out that there is one OpenOffice.org and there are potentially hundreds of extensions for it written in Java. By not providing it in OpenOffice.org we introduce a major road-block to extension developers. I am confident that a majority will abandon their project blaming it on bugs in OpenOffice.org even before you get a chance to explain that they have to add the line Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader()); to every method in every listener and every callback class such as XAsyncJob. I am lucky to be paid for writing OpenOffice.org extensions, so I'm not going anywhere :) So, let's not rush marking this issue RESOLVED (WONTFIX) and make an attempt to fix it. It was an unintended side-effect or the new class loader architecture (which is otherwise simply fantastic!) and deserves some attention. I have gone through several mail threads where people hit this problem but everyone has their own explanation, which indicates that the issue is not obvious and developers get confused. I myself spent hours in Eclipse debugger to narrow down the problem. It is not documented (or at least not indexed by google) and due to misunderstanding from developers I keep finding wrong solutions, such as adding dependent libraries to the root classpath or changing the way I load resources which is not possible when I use 3rd party libraries, such as Spring. Relevant mail threads: http://extensions.openoffice.org/servlets/ReadMsg?list=dev&msgNo=912 http://www.mail-archive.com/dev@api.openoffice.org/msg07637.html http://www.openoffice.org/issues/show_bug.cgi?id=75767 http://www.openoffice.org/servlets/ReadMsg?list=dev&msgNo=19584
Reset assignee on issues not touched by assignee in more than 1000 days.