Sebastien Diot
Sebastien Diot

Reputation: 7359

How to merge multiple Java classes *at runtime*?

Say you have an application that defines an "Entity" (interface) called A, with an accompanying AImpl. And you have third-party plugins that extends the class A into B, C, .. Z (with BImpl, ...), each adding some state and functionality. You also have a "factory" in your application that creates instances of A.

I would like my "user" to load several plugins at the same time, say F, K and W, such that the factory would than create a "super A" instance that would be at the same time a F, K and W. Assuming that "interface" and "implementation" are strictly separated, as long as the "super A" implements the interface of F, K and W, it should not mater that it is neither a FImpl, KImpl or WImpl. So a kind of "runtime" multiple-inheritance.

To be precise, I would not have the source code of F, K or W, and the third-party developers should not need to know about each-others extensions.

So, is there some library that makes it easy to merge Java classes? Ideally with some way of managing "conflicts", for example two classes defining a private method with the same signature, or trying to override the same base-class method.

Note: I'm looking for something using bytecode manipulation, rather than composition. Composition prevents the "extending" classes of overriding methods of the base class, and use-up more memory.

[EDIT] As I have explained in my comment to @Gray, composition is not a good solution for a number of reasons. While you can use it, if you do it properly, you end-up with very many very small classes and interfaces, and a massive amount of glue code to make them look like just one single object. This also has, in addition to a large coding overhead resulting in decreased productivity, the effect that your application uses much more memory and is also a lot slower, due to the added indirections everywhere.

For an application that processes small and short DB transactions, composition might be perfect, but for an application that has to keep gigabytes of hot data in ram at all times, which is what I will be doing, this has a very real cost, both in development time, and memory and CPU requirement for the clients and servers.

A solution where you can code "naturally" (interface-first style of programming IS how I normally code anyway), with some limitation on what methods from the base-class you can extend, would be much "cheaper" than composition.

Upvotes: 1

Views: 1795

Answers (3)

Sebastien Diot
Sebastien Diot

Reputation: 7359

While it's not an easy-to-use library, the closest I found so far is this "recipe" to do it in ASM:

Merging class methods with ASM

But as the page says, it does not handle the corner cases, which is why having a library that does it would be easier.

Upvotes: 2

Gray
Gray

Reputation: 116888

You could do this with a proxy object which was injected with all of the F, K, and W plugins. This is easier to do if they are interfaces as your question implies. If not then you'll have do more magic like use cglib. The proxy docs here:

http://download.oracle.com/javase/1.5.0/docs/guide/reflection/proxy.html

Here's a code sample tuned from examples there:

public class MyProxy implements InvocationHandler {
    private F f; private K k; private W w;

    public static Object newInstance(F f, K k, W w) {
        return java.lang.reflect.Proxy.newProxyInstance(
            obj.getClass().getClassLoader(),
            new Class[] { F.class, K.class, W.class ],
            new MyProxy(f, k, w));
    }

    private MyProxy(F f, K k, W w) {
        this.f = f; this.k = k; this.w = w;
    }

    public Object invoke(Object proxy, Method m, Object[] args) throws Throwable{
        try {
            // pseudo code starts here
            if (f has method that matches m.getName() and args) {
                find method from f
                return fMethod.invoke(obj, args);
            } else (same thing for f and w ... ) {}
            throw some exception about missing method
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        } catch (Exception e) {
            throw new RuntimeException("unexpected invocation exception", e);
        }
    }
}

You could create a map of methods and for every call figure out if you want to call a method in some plugin. @DaveNewton is right that the trick is that what if two of them have the same method name with the same arguments.

Upvotes: 1

dMb
dMb

Reputation: 9337

This is not as trivial as you think or make it sound. But, yes, there are frameworks for this "plugin-based architecture." One of the more popular these days is this: http://www.osgi.org/Main/HomePage

Upvotes: 0

Related Questions