Issue 93740 - Current thread context class loader on UNO callback threads
Summary: Current thread context class loader on UNO callback threads
Status: UNCONFIRMED
Alias: None
Product: General
Classification: Code
Component: code (show other issues)
Version: OOo 2.4.0
Hardware: All All
: P3 Trivial (vote)
Target Milestone: ---
Assignee: AOO issues mailing list
QA Contact:
URL:
Keywords: needhelp
Depends on:
Blocks:
 
Reported: 2008-09-11 00:01 UTC by uga
Modified: 2014-02-25 19:18 UTC (History)
3 users (show)

See Also:
Issue Type: DEFECT
Latest Confirmation in: ---
Developer Difficulty: ---


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description uga 2008-09-11 00:01:51 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
Comment 1 thorsten.martens 2008-09-11 08:56:30 UTC
TM->JL: please have a look.
Comment 2 joachim.lingner 2008-09-11 11:51:12 UTC
.
Comment 3 Stephan Bergmann 2008-09-11 12:09:26 UTC
@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).
Comment 4 Stephan Bergmann 2008-09-11 12:10:22 UTC
.
Comment 5 uga 2008-09-11 16:25:37 UTC
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

Comment 6 Rob Weir 2013-07-30 02:43:00 UTC
Reset assignee on issues not touched by assignee in more than 1000 days.