Monday, August 25, 2008

Why Services are Important

Many people choose OSGi because it provides them with far superior class loading functions than plain Java. Some even say that OSGi is class loaders on steroids. However, viewing OSGi from this perspective is like building a car that looks like a horse carriage with engine. Taking full advantage of OSGi requires the use of services, however, the service model is much more enigmatic for most people than class loading. A good friend of mine, Niclas Nilsson, who visited me last week told me a story about how people view software languages in a very subjective way. If the languages they are familiar with do not have a specific feature, then they have a tendency to view this feature as unimportant and consider them (unnecessary) syntactic sugar. For example, few Basic programmers missed objects in the nineties.
I think services are a bit like this. There are millions very experienced Java programmers that build impressive libraries and applications using class loaders as extension mechanisms. There is a wealth of knowledge and experience. In contrast, OSGi services implement a concept where one cannot find a comparable mechanism in plain Java, nor in other languages/environments.

Why are services then so important if so many applications can be built without them? Well, services are the best known way to decouple software components from each other. And, I assume that you are aware of the many advantages that decoupling gives you in almost any aspect of software development.

One of the most important aspects of services is that they significantly minimize class loading problems because they work with instances of objects, not with class names. Instances that are created by the provider, not the consumer. The reduction of the complexity is quite surprising. Simple example is Hibernate; most of the class loading problems in Hibernate are caused by the fact that the configuration is done in XML files. Working with Class instances and instances of those classes make most problems go away.

What kind of problems do you have with the class names? Well, first you have to configure it. This means that if you want to change the implementation, you must not only edit some (hard to read) XML or properties file, you must also make sure that the right JAR files are on your class path. In a service based system, you only install the proper bundle. Look at the 'interesting' world of logging in Java. Most logging subsystems handle their extension needs with class names. This requires having property or XML files in the right place and requires you to have the right logger implementation on your JAR path. In an OSGi world, the only thing you have to do is install an implementation of the log service and everybody uses that log service. As Richard Hall always says: 'The set of installed bundles is your configuration.'

Not only do services minimize configuration, they also significantly reduce the number of shared packages. In Class.forName based system you need to be able to see all possible implementation classes. In a service based system, you only need to see the package in which the service is defined. And the best package is one that is not shared.

To be honest, after working for many years with OSGi I really had to get used to the idea that one could also share packages with implementation code; before R4 OSGi was only intended to share service packages. It seemed such a bad idea.

Sharing implementation classes is almost always bad because it makes bundles extremely dependent on each other. The reason we added all those powerful class loading mechanisms was not for them to be used in new designs, their primary purpose was to ease conversion of existing applications.

And last not least: versioning. Class.forName must fundamental flaw is that the only parameter is a string for the class name. In complex systems there will be multiple classes with the same name. This confusion is completely absent in a service based system because service objects are already correctly wired up.

However, I guess there is no escape to Niclas' story. If you have never built a real system with services it is hard to see the advantages and it is easy consider them as an extra without a lot of value. But, maybe this blog can give you the push to start using services in real designs and find out how surprisingly powerful this very simple concept is. Let me know!

Peter Kriens

Tuesday, August 5, 2008

Classy Solutions to Tricky Proxies

Though the quest for reusable systems is the guiding principle in my life, I only recently start to understand the reason for some of the mechanisms in Spring. For example, I start to see that the whole Aspect Oriented Programming (AOP) business is not about aspect oriented programming, but it is about hammering an existing code base into its intended slot. Unfortunately, some of the slots are square while the code bases are quite round. Therefore, proxying is an important aspect of developing a system by making a round object look square, or vice versa.

The bad news is that OSGi is not making this easier. The OSGi framework was designed with green field applications in mind that would follow proper modularity rules and communicate through services. In a perfect OSGi world, bundles are highly cohesive and are coupled through their services. In the real world, class loaders are (ab)used for configuration and ad-hoc plugin systems. Some of those problems are quite complex.

Today, I was asked about the following problem.

A company is using Spring. One of their bundles referred to a service that another bundle had registered. Spring has decided that services are too jumpy, so they proxy the service object for their clients and add some mixins in the proxy that handle the situations when a service goes away (Spring also adds some other mixins).

Creating a proxy is class loader magic. You take a set of interfaces (the mixins) and generate the byte codes for a class that implements all these interfaces. The implementation of the interface methods forwards the call to a handler object using the object, the method that was called and the arguments.

If you generate the byte codes for a class, you need to define this class in a class loader and this is where the modularity comes into play. In a traditional Java application you can just take the application class loader and define this new class in this class loader. The application class loader usually has full visibility. In a modular system, however, visibility is restricted to what you need to see. In OSGi, the imported packages are all a bundle can see (at least, when you want to work modular and not use dynamic imports buddy class loading).

The catch is that the proxy requires access to all the mixin classes. However, some of these mixin classes come from one bundle, and some come from another bundle. There is likely no class loader that can see both bundle class spaces unless that bundle specifically imports the correct interfaces. However, if a client bundle gets one of his objects proxified, then he has no knowledge of the mixins (that is the whole idea). It would be fundamentally wrong to import the interface classes while he is oblivious of them. However, the bundle that generates the proxy might know the mixin classes but it is unlikely it knows the classes of the target object. If the proxy generator was implemented as a service (as it should be) then there would even be 3 bundles involved: the proxy generator, the bundle needing the proxy, and the client bundle.

Spring solved this problem with a special class loader that chained the class loader from the client bundle together with the class loader of the Spring bundle. They made the assumption that the Spring bundle would have the proper imports for the mixin classes it would provide and the client bundle would have knowledge of the proxied object's classes.

So all should work fine? Well obviously not or I would not write this blog.

The generated proxy class can see the Spring bundle with the interface classes and it can see the all the classes that the client bundle can see. In the following picture, the client bundle imports the service interface from package p, but it does not import package q which is used in package p. If you create a proxy for a class in package p, it will need access to package q, and the client bundle can not provide this.



For example, assume the following case:

javax.swing.event.DocumentEvent de =
(DocumentEvent) Proxy.newProxyInstance(getClass().getClassLoader(),
new Class<>[]{DocumentEvent.class}, this );

The DocumentEvent is from the javax.swing.event package. However, it uses classes from the javax.swing.text package. Obviously the bnd tool correctly calculates the import for the DocumentEvent class. However, when the generated proxy class is instantiated, it will need access to all the classes that the DocumentEvent refers to: the super class, classes used as parameters in method calls, exceptions thrown, etc. These auxiliary classes are crucial for the DocumentEvent class, but they are irrelevant for the client bundle, unless these classes are actually used in the client bundle, where they would be picked up by bnd.

So, if the client bundle does not import javax.swing.text then you will get a ClassDefNotFoundError when you try to instantiate the proxy class. This error gets generated when the VM sees the reference to class in the javax.swing.text package and tries to load it through the proxy class' class loader (that is why this is an error, it happens deep down in the VM).

To be specific, this is exactly as it should be in a proper modular system. The client should not be required to import classes that it does not need. The onus is on the proxy generating code to do it right.

Fortunately, the solution is not that hard but it highlights how easy it is to make the wrong assumption, believe me, these Spring guys are quite clever.

Just take a step back and look at the problem.

Do we need visibility on the bundle level here? Look at the information given to the proxy generator: a set of mixin classes. It does not really matter from what bundle these interfaces classes come from; the class loader that we need must be able to see each of the interface classes class loaders. By definition, those class loaders can see the right class space.

So instead of chaining the bundle class loaders, the problem can be solved by having a class loader that searches each of the class loaders of the used mixin classes.

For the Advanced


The sketched model is too simplistic because there is the cost of a class loader per generated proxy. Even though enterprise developers seem to live in a wonderful world where memory is abundant, as an old embedded geezer such abuse of class loaders worries me. Though it is possible to use a single combined class loader by keep adding the required class loaders but this raises the question of life cycle. Such an ├╝ber class loader would pin an awful lot of bundle class loaders in memory and it will be very hard to not run into class space consistency problems because over time this class loader will see all class spaces. That is, this approach brings us back to all the problems of today's application servers.

I have not tried this, but the solution is likely to make the proxy generator service based. Not only will this make it easier to plugin different generators, it also means that the generator is bundle aware. It can then create a combining class loader for each bundle that uses the Proxy Generator service. This class loader will then be associated with the class space of that bundle and should therefore not be polluted with classes from other class spaces. In the Spring case, this would be the client bundle, not the spring bundle. The reason is, is that the spring bundle could be used from different class spaces.

However, the proxy generator must track any bundle that it depends on. That is, it must find out which bundle's export the interface classes and track their life cycle. If any of these bundles stop, it must clear the client's combining class loader and create a new one when needed. This will allow the classes from the stopped bundle to be garbage collected.

Peter Kriens


For the hard core aficionados, some code that demonstrates the problem:

First a base class that acts as activator. This base class calls a createProxy method in the start method. This is then later extended with a proxy generation method that fails and one that works ok.


package aQute.bugs.proxyloaderror;
import java.lang.reflect.*;
import javax.swing.event.*;
import org.osgi.framework.*;

public abstract class ProxyVisibility implements BundleActivator,
InvocationHandler {

public void start(BundleContext context) throws Exception {
try {
DocumentEvent de = createProxy(this, DocumentEvent.class);
System.out.println("Succesfully created proxy " + de.getClass());
} catch (Throwable e) {
System.out.println("Failed to create proxy" + e);
}
}

public void stop(BundleContext context) throws Exception {
// TODO Auto-generated method stub

}

public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}

abstract protected T createProxy(InvocationHandler handler,
Class primary, Class... remainder);

}


The following class uses the base activator but uses only the client bundle's class loader. Trying to create the proxy will therefore fail because this class loader can not see the javax.swing.text package.


package aQute.bugs.proxyloaderror;
import java.lang.reflect.*;

public class ProxyLoadError extends ProxyVisibility {

@SuppressWarnings("unchecked")
public T createProxy(InvocationHandler h,
Class primary, Class... others) {

Class parms[] = new Class[1 + others.length];
parms[0] = primary;
System.arraycopy(others, 0, parms, 1, others.length);

return (T) Proxy.newProxyInstance(getClass().getClassLoader(), parms,
h);
}
}


And at last, the solution. The following class extends the base activator and creates a proxy that uses a Combined Class Loader. This loader traverses all the class loaders of the mixin classes.


package aQute.bugs.proxyloaderror;
import java.lang.reflect.*;
import java.lang.reflect.Proxy;
import java.net.*;
import java.util.*;

public class ProxyLoadOk extends ProxyVisibility {

@SuppressWarnings("unchecked")
public T createProxy(InvocationHandler h, Class primary,
Class... others) {
CombinedLoader loader = new CombinedLoader();

Class parms[] = new Class[1 + others.length];
parms[0] = primary;
System.arraycopy(others, 0, parms, 1, others.length);
for (Class c : parms) {
loader.addLoader(c);
}

return (T) Proxy.newProxyInstance(loader, parms, h);
}

static class CombinedLoader extends ClassLoader {
Set loaders = new HashSet();

public void addLoader(ClassLoader loader) {
loaders.add(loader);
}

public void addLoader(Class clazz) {
addLoader(clazz.getClass().getClassLoader());
}

public Class findClass(String name) throws ClassNotFoundException {
for (ClassLoader loader : loaders) {
try {
return loader.loadClass(name);
} catch (ClassNotFoundException cnfe) {
// Try next
}
}
throw new ClassNotFoundException(name);
}

public URL getResource(String name) {
for (ClassLoader loader : loaders) {
URL url = loader.getResource(name);
if (url != null)
return url;
}
return null;
}
}

}

Monday, August 4, 2008

Sun Hires Richard Hall

It took sometime but yesterday it was official: Sun has hired Richard Hall in the Glassfish team! This is good news for Richard, who was looking for just a position like that, Sun, who can clearly use the expertise on OSGi that Richard as one of the foremost OSGi experts provides, Apache Felix because this means that Richard will be able to spend more time on this project, and also good news for the OSGi Alliance that will have one of its key expert group members represent Sun. Rarely is a change positive in so many ways.

I look forward to continue working with Richard in his new role. I hope that Richard's role will mean that Glassfish will become one of the key players in the next OSGi release, they clearly have the experience to match. Though it will be interesting to find out what happens in JSR 277 because Richard is currently an independent member. Will he continue to participate (though participate is a big word with the activity in the last year) or will he be able to actively work in that group (and get the long overdue EDR2 out)? That is still a question mark.

I wish Sun and Richard a very happy and fruitful cooperation and hope to be able to work with Richard for a long time.

Peter Kriens

Blog Archive