←Older revision |
Revision as of 08:23, 8 November 2010 |
Line 1: |
Line 1: |
- | [[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 general purpose [[modular system]]s, when it is not easy to control thread management. If any part of application can create and destroy threads on the fly, it is hard to make sure the [[ThreadContextClassLoader]] behavior is specified properly. | + | [[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 general purpose [[modular system]]s, when it is not easy to control thread management. If any part of application can create and destroy threads on the fly, it is hard to make sure the [[ThreadContextClassLoader]] behavior is specified properly. |
| | | |
| 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. |
Line 11: |
Line 11: |
| 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. | | 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. | + | 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. | | 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 == | + | == The [[NbJUnit]] [[ClassLoader]] == |
| | | |
| I don't see much reasons why classes in real system shall be duplicated in a bundle and on classpath except in rare situations (like providing newer ''javac'' or ''JAX-WS'' than available in [[JDK]]). Thus the [[JUnit]] class duplication is an unusual situation. But hard to overcome if one relies on standard [[Ant]] [[JUnit]] task. | | I don't see much reasons why classes in real system shall be duplicated in a bundle and on classpath except in rare situations (like providing newer ''javac'' or ''JAX-WS'' than available in [[JDK]]). Thus the [[JUnit]] class duplication is an unusual situation. But hard to overcome if one relies on standard [[Ant]] [[JUnit]] task. |
| | | |
- | Thus I decided to write new ''isolating'' classloader. One that does not load anything from classpath (except own [[JUnit]] classes). By using this classloader as a parent for [[NetBeans]] or for [[Equinox]], one can make sure the system basically sees only one class and thus the best of ''both worlds'' again becomes beneficial. | + | Thus I decided to write new ''isolating'' [[ClassLoader]]. One that does not load anything from classpath (except own [[JUnit]] classes). By using this [[ClassLoader]] as a parent for [[NetBeans]] or for [[Equinox]], one can make sure the system basically sees only one class and thus the best of ''both worlds'' again becomes beneficial. |
| | | |
| Still my problems were not over. From time to time the system managed to load a class from classpath, in spite it was available in a bundle (and the classpath shall not be visible). It took me next few days to find out I have to use: | | Still my problems were not over. From time to time the system managed to load a class from classpath, in spite it was available in a bundle (and the classpath shall not be visible). It took me next few days to find out I have to use: |
Line 27: |
Line 27: |
| </source> | | </source> |
| | | |
- | Only then the [[Equinox]] stops loading classes from the classpath and really uses the ''isolating'' classloader. Tests started to almost work. | + | Only then the [[Equinox]] stops loading classes from the classpath and really uses the ''isolating'' [ClassLoader]]. Tests started to almost work. |
| | | |
| == Happy End == | | == Happy End == |