Showing posts with label osgi. Show all posts
Showing posts with label osgi. Show all posts

2012-02-22

Java Module Systems The EDT and ClassLoader Issues

As mentioned in Java Module Systems SwingWorker, Runnable, Thread and ClassLoader Issues, where invokeLater and invokeAndWait are discussed, the EDT is a separate thread with its own context classloader, and this is most likely the system classloader. It is important to understand this.

If you create a user interface in a modular system, such as the NetBeans RCP, upon a user action logic will run on the EDT, and if in that logic you access classes by name, create new instances of them, or perform casts and those classes could possibly be in more than one module, you will run into classloader collision issues. I will restate here that this also affects EventQueue.invokeLater and invokeAndWait.

Along with collision issues, you need to also understand that certain class access may be blocked depending on the classloader being used once you set the thread context classloader. Imagine you have some classes your current UI components classloader can see; they are part of its dependencies. Those dependencies classloaders may not have access to each other. If you use a classloader which does not have access to some classes to set the thread context classloader, then access will be blocked to those classes, and your logic will not work.

Suppose there is a button and it has an action listener attached to it. It could be written as:
public class MyActionListener implements ActionListener {
public void actionPerformed(ActionEvent evt){
ClassLoader oldCl = Thread.currentThread().getContextClassLoader();
try {
//here we are actually taking the current instances classloader
//which could be slightly better than
//MyActionListener.class.getClassLoader()
Thread.currentThread()
.setContextClassLoader(getClass().getClassLoader());
//create instances, cast, load classes by name
}finally{
Thread.currentThread().setContextClassLoader(oldCl);
}
}
}


The above could make an assumption it is the only possible some.specificpackage.MyActionListener to be within any module in the system at one time were it to use MyActionListener.class.getClassLoader(), and if that were not to be the case, you would need to get creative in the manner in which you get a classloader into this object.

The code does slightly guard against that since it uses getClass().getClassLoader() versus the static MyActionListener.class.getClassLoader() which would unequivocally infer it is expected to be the only one in the system per the way the class and classloader are being accessed. This means the instances classloader will be used, and if the instance was created using a specific classloader, and it is correct, then the code will simply work. If the classloader is not correct, then the logic which created the instance will need to be fixed.

This is another subtle way in which classloader issues can creep into the domain of concurrency and Java modular systems. You can use this information to ensure you have protected your modules logic from being broken by other modules at runtime which you probably will not have tested against.

Remember, if you use 3rd party libraries in a module, or you are writing one, it is highly probable and definitely possible others will too, and you may use the same ones. Whether the library is the same version or not, unless it is accessed in the context of a shared module classloader, i.e. the library is itself in a module versus a simple JAR dependency and all other modules use it this way, it can cause collision and access issues.

2012-02-10

Java Module Systems, SwingWorker, Runnable, Thread and Class Loader Issues

People often have a hard time getting used to a Java module systems constraints on class loaders and dealing with compile time versus runtime dependencies. There are a lot of issues just around getting the dependencies organized.

Often overlooked is how threading impacts class loaders. NetBeans modules, when "they" create a thread for you, will generally set the context class loader to the current class loader. However, in your own code you need to handle this.

Note Runnable is mentioned above. This does not necessarily consider the cases of EventQueue.invokeLater and invokeAndWait as those things are generally cases where the object instances have been realized, and class loading is no longer in the picture, or at least they should be, as those things should be short and to the point as they happen on the EDT, but it focuses on Runnable when given to the Thread constructor.

However, this could become a source of issues, and if creating new object instances in the Runnable, and it is ever a possibility those classes could be included in more than one module in the targeted system, then you should probably go ahead and deal with a class loader change out as your code will be running on the EDT.

Either way, the reason this becomes important is class access and class collision.

Access deals with the visibility or permissions a given class loader gives the calling code. Calling code may not have access to a certain class, and thus the current class loader should not be the one to load classes, but should instead delegate that to another module which has access to not only the public API but the private API of the module itself. That would be the one loading the classes the caller can access or the public API of the called module.

Collision is a more subtle thing to deal with. Imagine one module has class mine.Foo and another module does as well. Even though these modules may not call or touch each other and thus not collide with each other under normal calling conditions, they may very well do that in a thread without setting the context class loader as those classes will have to be loaded some how, and here the system class loader will be used which will access all the class loaders, and that means either an arbitrary class must be chosen or the first one found, and in nearly all conditions this is not what you want.

In the NetBeans module system this will cause an error as the NetBeans developers do not want this to be a surprise and thus hide crashes. Others may load them; I'm not sure, so if anyone knows how various module systems handle this please comment. Either way, unless you give Java and the module system more guidance, you will run into strange and unusual problems.

Given a class, the following should really be used:

public class MyClass {

Runnable r = new Runnable(){
public void run(){
ClassLoader oldCl = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(MyClass.class.getClassLoader());
}finally{
Thread.currentThread().setContextClassLoader(oldCl);
}
}
};

Thread t = new Thread(){
public void run(){
ClassLoader oldCl = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(MyClass.class.getClassLoader());
}finally{
Thread.currentThread().setContextClassLoader(oldCl);
}
}
};

SwingWorker<Void, Void> = new SwingWorker<Void, Void>{
public Void doInBackGround(){
ClassLoader oldCl = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(MyClass.class.getClassLoader());
}finally{
Thread.currentThread().setContextClassLoader(oldCl);
}
return null;
}
};

}


This does a couple things at various runtimes. When the code runs in a regular Java application with no module system, the system class loader will always be used. Next, when run in a modular application, the current caller, or current module will use its class loader in the threaded logic. Finally, in both cases, the loader is reset to whatever it was before; for throw away threads, that is probably not a big deal, but what if your code is using a thread pool?

This is a basic convention to follow for safety and refactorability. Safety as noted in access and collision. If your code is used in a modular system, and does not follow the above, it will crash as soon as you add the same classes to other modules. Refactorability per the fact that if more than one module later adds a different version of a class, or the same version different class loader, then the application will start to crash until fixed, and if the person tracking down the issue has to spend time finding and/or fixing it, they can't focus on refactoring code to work with the same classes in different ways.

Another thing to keep in mind is there are times when you may need to pass a specific class other than your own for some block of code to pin-point an exact class loader. Imagine some code you are calling will need classes which your class loader does not have access as the module being called does not expose them as part of its public API.

So, there are times when you have to think beyond the current class loader, but the good thing is no matter which class works in the modular system, the code will continue to work if used outside of one in a standard Java application.

The one major time this differs are libraries written to load and use classes dynamically. MyBatis is a good example. In such cases, the current thread context class loader should be passed along. The idea being the caller is telling you which class loaders to use. This only applies to threads however as the module wrapping the called code will have configured the context class loader as needed for the current thread.

The MyBatis folks were working to address this issue the last time I was on the lists. Someone using it in such a context now should comment if able.

I hope this will help projects (open source and not) write Java code which is compatible in both modular and non-modular applications.