22

I am working on a project where there are a lot of objects that are created by a library, and I have no access to the creation process of these objects.

The following snippets serve as a good example to illustrate my problem.

Code:

public class Clazz {
    //The contents of Clazz are irrelevant
    //Clazz is NOT my class. I have no access to its internal structure.
    //However, I do have access to Clazz objects that were created elsewhere.
}

ExampleInterface is an interface that Clazz may or may not implement at compile time.

Code:

public interface ExampleInterface {
    public void run();
}

The following code is the problem that I am running into. Take note of the following:

  1. run() is only called when c is an instance of ExampleInterface.
  2. getRunConditions(Clazz c) and executeClazz(Clazz c) are both private methods in a class that I do not have access to.
  3. At compile time, Clazz will not contain a method named run().
  4. ExampleExecutor is not my class. I have no access to it in any way (I can't even get an instance of the class).

Code:

public class ExampleExecutor {
    public void executeClazz(Clazz c) {
        if ((c instanceof ExampleInterface) && getRunConditions(c)) {
            ExampleInterface ex = (ExampleInterface) c;
            ex.run();
        }
    }
}

Obviously the following method isn't syntactically possible, but it is what I am trying to achieve. Basically, if c doesn't already implement ExampleInterface, set c to implement ExampleInterface, and then provide the methods that must be overridden.

Take note of the following:

  1. extendInterface(Name of Interface) is made-up syntax I created in an attempt to illustrate my goal.
  2. run() must be defined here (at runtime).
  3. I cannot use a wrapper or proxy class as a solution. IE, the Clazz object must wind up implementing ExampleInterface, and I can't use a workaround. (refer to this link if you want to know why).

Code:

public void implementInterface(Clazz c) {
    if (!(c instanceof ExampleInterface)) {
        c.extendInterface(ExampleInterface {
            @Override
            public void run() {
                //code
            }
        });
    }
}

To clarify, the issue that I am running into is that I need to always know when run() is called in Clazz. If Clazz ever doesn't implement ExampleInterface, I can't know when run() should be called.

At the same time, I would also like to occasionally add support for run() when it by default isn't supported. Because I have no access to the creation of Clazz objects, I can't do this by implementing the interface myself.

Question: Put simply, is it possible to implement an interface (and provide the required methods) at runtime?

NOTE: While the only solution may require reflection (and if so please post it below), the library I am using has a security manager that blocks the use of all reflection. IE, a reflective solution may be of use to others in the future, but will be of no use to me.

Also, I don't mean just using a library in my own program. An already running host application (which is what the library I am using was made for) complies and then runs code that I write for it. If that application doesn't like any of the code that I provide (IE, conflicts with its security manager), the code is never even compiled.

Why I need to do this:

It has to do with the library that I am using. Because ExampleExecutor is a method that I don't have access to, and I can't control the creation of Clazz, I can't determine when run() is executed.

The reason why I need to know when run() is executed is because in actuality, run() is an event handler that is part of the library I am using.

For example: mouseClicked(CustomMouseEvent evt) might be a method that is part of the interface CustomMouseListener. Sometimes the instance of Clazz I am working with cares when the mouse is clicked (and therefore inherits CustomMouseListener), while other times it doesn't.

Unlike the Clazz instance, I always care if the mouse is clicked, and always need the event to be triggered.

In reality, the ExampleInterface would actually be the following:

public interface CustomMouseListener {
    public void mouseClicked(CustomMouseEvent evt);
    public void mousePressed(CustomMouseEvent evt);
    public void mouseReleased(CustomMouseEvent evt);
    //etc
}
Community
  • 1
  • 1
user3144349
  • 419
  • 1
  • 4
  • 12
  • 1
    Just to be clear, are you not asking about anonymous classes? http://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html – Ruan Mendes May 20 '14 at 20:25
  • You can create an instance of something that implements an interface at runtime with `new SomeInterface() { … }`, but it won't be an instance of anything else (except Object, and superinterfaces, of course). It sounds like you're *getting* an instance of some class, and just need to check whether it implements a particular interface, and if so, do something with it. Is that right? – Joshua Taylor May 20 '14 at 20:27
  • No and no. Refer to the other question I have linked, that will likely shed some light on what I am trying to accomplish. The code `if (c extends ExampleInterface) {` is already called by the internal library. I need to make sure that if Clazz doesn't implement `ExampleInterface` at compile time, I can force it to do so at runtime. – user3144349 May 20 '14 at 20:30
  • OK, would you be willing to create a new instance that dispatches class methods to the existing instance, and implements the interface methods? It looks like that might be the only realistic option… – Joshua Taylor May 20 '14 at 20:34
  • Why do you want to make an instance implement some interface? What would you gain? I still don't understand the use case. – Sotirios Delimanolis May 20 '14 at 20:38
  • Indeed, instead of attempting the impossible, the poster should consider what he wants to achieve with this. – Kayaman May 20 '14 at 20:39
  • I stated my reasoning in my above comment. – user3144349 May 20 '14 at 20:44
  • You're saying what you need to do. I'd like to know why you need to do it. If `Clazz` isn't a `ExampleInterface`, it shouldn't do what `ExampleInterface` says it can do. You seem to be breaking a lot of OO rules. – Sotirios Delimanolis May 20 '14 at 20:45
  • I don't know why that's relevant, but okay. It has to do with the library I am using. I'll give a very specific example in the original post. – user3144349 May 20 '14 at 20:46
  • This is not in unit test code, is it? Because it sounds a lot like what mocking frameworks (like EasyMock or Mockito) do when they create mock objects. I wonder if you can take advantage of them in some way. Although, if you can't use reflection, maybe not. – David Conrad May 20 '14 at 20:47
  • Is there some kind of licensing which doesn't allow you to decompile the class or change it? – TomasZ. May 20 '14 at 21:26

4 Answers4

6

You can use the java instrumentation API to (forcefully) adapt the class to the interface. This technique is usually used by APM, AOP frameworks, and profilers to inject logging and metrics measurement code into target classes at runtime. It is very unusual for applications to directly use this technique. It would be a big red flag in the least if I see this in production code.

Nonetheless,

Given these Clazz:

package com.sabertiger.example;

public class Clazz {
    public void purr(){
        System.out.println("Hello world");
    }

}

Interface

package com.sabertiger.example;

public interface ExampleInterface {
    void run();
}

Executor

package com.sabertiger.example;

public class ExampleExecutor {  
    public static void main(String[] args) {
        Clazz c=new Clazz();
        // Normally a ClassCastException
        ExampleInterface i=(ExampleInterface)(Object)(Clazz) c;
        i.run();
    }
}

A Normal run produces this error:

Exception in thread "main" java.lang.ClassCastException:
  com.sabertiger.example.Clazz cannot be cast to 
  com.sabertiger.example.ExampleInterface
    at com.sabertiger.example.ExampleExecutor.main(ExampleExecutor.java:7)

You can make it work by supplying the missing interface and implementation by transforming the class:

package com.sabertiger.instrumentation;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;

public class ExampleInterfaceAdapter implements ClassFileTransformer {

    public static void premain(String agentArgument, Instrumentation instrumentation) {
        // Add self to list of runtime transformations
        instrumentation.addTransformer(new ExampleInterfaceAdapter());
    }

    @Override
    // Modify only com.sabertiger.example.Clazz, return all other unmodified
    public byte[] transform(ClassLoader loader, String className,
            Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
            byte[] classfileBuffer) throws IllegalClassFormatException {

        if(className.matches("com/sabertiger/example/Clazz")) {
            return addExampleInterface(className, classfileBuffer );            
        } else {
            return classfileBuffer;
        }
    }

    // Uses javassist framework to add interface and new methods to target class
    protected byte[] addExampleInterface(String className, byte[] classBytecode) {
        CtClass clazz= null;
        try {
            ClassPool pool = ClassPool.getDefault();
            clazz = pool.makeClass(new java.io.ByteArrayInputStream(classBytecode));

            String src=
              "{         "+
              "  purr(); "+
              "}         ";

            //Add interface
            CtClass anInterface = pool.getCtClass("com.sabertiger.example.ExampleInterface");
            clazz.addInterface(anInterface);

            //Add implementation for run method
            CtMethod implementation = CtNewMethod.make(
                    CtClass.voidType,
                    "run",
                    new CtClass[0],
                    new CtClass[0],
                    src,
                    clazz);
            clazz.addMethod(implementation);

            classBytecode=clazz.toBytecode();
        } catch(Throwable e) {
            throw new Error("Failed to instrument class " + className, e);
        }
        return classBytecode;
    }

}

and the required MANIFEST.MF

Manifest-Version: 1.0
Premain-Class: com.sabertiger.instrumentation.ExampleInterfaceAdapter
Boot-Class-Path: javassist.jar

Pack everything into a jar to make it work:

jar -tf agent.jar
META-INF/MANIFEST.MF
com/sabertiger/instrumentation/ExampleInterfaceAdapter.class

Now we are able to pass Clazz to ExampleExecutor

java -javaagent:agent.jar -classpath ..\instrumentation\bin com.sabertiger.example.ExampleExecutor
Hello world
sabertiger
  • 428
  • 2
  • 7
5

The only way to do what you suggest is to use byte code Instrumentation. You can add an agent which changes the byte code of the clazz you want to modify before it is loaded.

The reason you need to do this at load time is that many JVMs will not allow you to change fields and some don't allow you to add methods after the class is loaded.

A simpler solution is to decompile the class, modify it and compile it again. Assuming the class can be decompiled this will save you a lot of time and effort.

the library I am using has a security manager that blocks the use of all reflection

This is an odd choice because you can put in place your own SecurityManager before calling the library and it can't prevent you from doing anything.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • Well, perhaps library isn't the best word. The code that I am writing is called from another application that is based around the library I am using. If that host application detects something in the code I provide it that it doesn't like (conflicts with the security manager), it won't compile and run any of my code. – user3144349 May 20 '14 at 21:05
  • @user3144349 If you can change the command line, you can do anything. If you can't change the command line, you are limited to what your framework allows you to do. That is the whole point of having a SecurityManager. ;) – Peter Lawrey May 20 '14 at 21:09
  • @user3144349 in that case, if the SecurityManager is written correctly you can't change the security model. BTW Reflection won't allow you to change code, you need lower level access to alter a class. e.g. instrumentation or change a class on the class path. – Peter Lawrey May 20 '14 at 21:14
  • I might have a solution, but I'm going to need to ask another question first. I'll report back shortly. – user3144349 May 20 '14 at 21:20
2

I don't think what you want is possible; there are Dynamic Proxies, but they use reflection, and it doesn't look likely that you have access to the classloader (where you could set your own that did on-the-fly bytecode manipulation).

Tassos Bassoukos
  • 16,017
  • 2
  • 36
  • 40
-2

Depending on your java version, you could use the lambda expression (with java 8).

The code would be relatively simple:

Clazz o = .... // Here you obtain your object through third party library
ExampleInterface yourInterface = o::run;
yourInterface.run();

Note that this only works for interface with one method. Both signatures (interface and Clazz) must match.

Jean-Simon Vaillant
  • 283
  • 1
  • 5
  • 14