ThreadContextClassLoader
From APIDesign
Line 3: | Line 3: | ||
Things get even more complicated as each [[NetBeans Runtime Container|modular runtime container]] may have different idea what [[ThreadContextClassLoader]] shall do. I know what I am talking about, I have spend last month trying to reimplement [[Netbinox]] [[OSGi]] container to provide [[ThreadContextClassLoader]] which is friendly to [[NetBeans]] modules as well as bundles comming from [[Equinox]] world. | Things get even more complicated as each [[NetBeans Runtime Container|modular runtime container]] may have different idea what [[ThreadContextClassLoader]] shall do. I know what I am talking about, I have spend last month trying to reimplement [[Netbinox]] [[OSGi]] container to provide [[ThreadContextClassLoader]] which is friendly to [[NetBeans]] modules as well as bundles comming from [[Equinox]] world. | ||
- | + | The need for enhanced [[ThreadContextClassLoader]] is clear as can be seen summary about [[eclipse:Context_Class_Loader_Enhancements|Equinox class loader enhancements]]. It summarizes the need as well as the solution taken by [[Equinox]] guys. Everything is built around '''buddies''' - e.g. each module can define its own '''buddy''' loading policy and let others to register is '''buddies'''. | |
- | The | + | The [[NetBeans]] ways is slightly different. It knows packages of all ''enabled'' modules and the [[ThreadContextClassLoader]] can load any classes from any of their packages (even non-public, e.g. not exported). It is build around observation that it does not make much sense in a running application to have the same implementation module loaded twice. |
- | === [[NetBeans]] | + | == Both of the Worlds? == |
+ | |||
+ | It is easy to create a union behavior that will support both ways. And I did it quite easily. If the class is not found the [[NetBeans]] way, then [[Netbinox]] tries the classical [[Equinox]] way. This guarantees that if the class is present in the system and either belongs to enabled module or it is accessible via '''buddy''' policy, it will be loaded. | ||
+ | |||
+ | Everything looked fine, until I started to execute existing tests in this new system. The testing framework (built around [[JUnit]] just like [[NetBeans]] [[NbJUnit]]), has the classes visible twice. Once on application classpath and once in the available bundles. I claimed that I can successfully find a class, if it can be found just a paragraph ago. Yes, I can, but I cannot guarantee it will be the right one. The problem is [[NetBeans]] [[ThreadContextClassLoader]] by default delegate to parent classloader first (e.g. the class is usually loaded from the application classpath), while the [[Equinox]] [[ThreadContextClassLoader]] prefers the bundle version. | ||
+ | |||
+ | When there are two version of a class visible (like in case of unit testing), having best of both worlds is a way to hell. It leads to randomness and bunch of ''ClassCastException''s. | ||
+ | |||
+ | == The [[NbJUnit]] ClassLoader == | ||
+ | |||
+ | [[TBD]] |
Revision as of 19:33, 7 November 2010
Java provides way to associate a special class loader with each of executing threads. This is handy, as often the executing class needs access to more than classes visible by its own classloader. However it is also accompanied with problems. Especially in modular systems. When it is not easy to control thread management (e.g. application can create and destroy threads on the fly), it is hard to make sure the ThreadContextClassLoader is specified properly.
Things get even more complicated as each modular runtime container may have different idea what ThreadContextClassLoader shall do. I know what I am talking about, I have spend last month trying to reimplement Netbinox OSGi container to provide ThreadContextClassLoader which is friendly to NetBeans modules as well as bundles comming from Equinox world.
The need for enhanced ThreadContextClassLoader is clear as can be seen summary about Equinox class loader enhancements. It summarizes the need as well as the solution taken by Equinox guys. Everything is built around buddies - e.g. each module can define its own buddy loading policy and let others to register is buddies.
The NetBeans ways is slightly different. It knows packages of all enabled modules and the ThreadContextClassLoader can load any classes from any of their packages (even non-public, e.g. not exported). It is build around observation that it does not make much sense in a running application to have the same implementation module loaded twice.
Both of the Worlds?
It is easy to create a union behavior that will support both ways. And I did it quite easily. If the class is not found the NetBeans way, then Netbinox tries the classical Equinox way. This guarantees that if the class is present in the system and either belongs to enabled module or it is accessible via buddy policy, it will be loaded.
Everything looked fine, until I started to execute existing tests in this new system. The testing framework (built around JUnit just like NetBeans NbJUnit), has the classes visible twice. Once on application classpath and once in the available bundles. I claimed that I can successfully find a class, if it can be found just a paragraph ago. Yes, I can, but I cannot guarantee it will be the right one. The problem is NetBeans ThreadContextClassLoader by default delegate to parent classloader first (e.g. the class is usually loaded from the application classpath), while the Equinox ThreadContextClassLoader prefers the bundle version.
When there are two version of a class visible (like in case of unit testing), having best of both worlds is a way to hell. It leads to randomness and bunch of ClassCastExceptions.