3

The Java OpenGL GL interface contains about 2000 methods, for debugging purposes I would like to wrap an instance and delegate calls to it while doing some logging. The logging code can be pushed to the same method in each case, so the task of writing out the method implementations looks like it could be automated. An example of what I am trying to do:

import javax.media.opengl.GL;

public class GLErrorLogger implements GL {
  private final GL backing;

  public GLErrorLogger(GL delegateToMe) {
    backing = delegateToMe;
  }

  private void checkErrorCode() {
    // Log frame and thread details depending on gl state
  }

  /**
   * Example of a method
   */
  @Override
  public int glGenLists(int arg0) {
    checkErrorCode();
    int retVal = backing.glGenLists(arg0);
    checkErrorCode();
    return retVal;
  }

  // rest of methods here...
}

In other words copy the method name and parameters (minus their types) into a call on the backing object, surround with calls to the logging method, and if there is a return type then assign the result to a variable of this type and return it at the end of the method.

I looked at creating a one shot eclipse code template to autogenerate the methods, but there wasn't an immediately obvious way to do pattern matching on the return type. Can anyone suggest a way to do this in Eclipse or any of its code generation tools to save me pulling out the regex toolkit?

MilesHampson
  • 2,069
  • 24
  • 43
  • How about using Aspecj4j and implement a cut? See http://stackoverflow.com/questions/8839077/how-to-use-aop-with-aspectj-for-logging – Rekin Aug 03 '12 at 07:49

2 Answers2

5

You might want to use an Aspect to create the necessary bytecode for you instead of producing all the source code. Take a look at the Traceing Aspect example here: Traceing Aspect Example.

As an Alternative, you can create a Java Dynamic Proxy, if you do not want to use AspectJ as Thrid party Library. Please refer to Dynamic Proxy Tutorial

  • 1
    Since `Proxy` is part of the JDK and quite simple I really recommend this solution. All other proposals so far require additional libs, additional overhead and *much* additional effort for a beginner (of that lib) for really very little benefit. – A.H. Aug 03 '12 at 08:08
  • Nice idea with the proxies, and it looks like I can still reflexively get the stack information when piping the calls through to the invocation handler method. – MilesHampson Aug 03 '12 at 08:09
  • @MilesHampson: Besides reading them when errors occur: What do you want to do with the stacktrace? – A.H. Aug 03 '12 at 08:11
  • 1
    Indeed you can - and it saves you a _LOT_ of boiler plate code :-). As for Dynamic Proxies vs. AspectJ, I completely agreed that the hurdle is higher for using AspectJ, but on the other Hand, I think AspectJ is more powerful, since you don't have to create the proxies manually. If all you want to do is tracing, stick to the dynamic proxies - if you are looking to seperate more of your corss cutting concerns from the sourcecode, I'd recommend AspectJ. Maybe solve you current Problem with Dynamic Proxies _AND_ take a look at AspactJ afterwards. – Jochen.Kohler Aug 03 '12 at 08:14
  • 1
    A note for anyone trying to proxy a large class like GL, I got hit by the 65k limit on method size when I switched my test code over to this type. Doesn't immediately make sense to me why (minfo.code.size() > 65535) {throw new IllegalArgumentException("code size limit exceeded"); is being called unless the ProxyGenerator is putting everything in MethodInfo . – MilesHampson Aug 03 '12 at 09:16
  • 1
    Finally got back to this, I ended up filing java bug 7189284 on the generation of the 65k+ static initialiser. Dynamic proxies are a great solution but if your interface happens to have many static variables then one of the other methods suggested here may be required. – MilesHampson Aug 06 '12 at 06:30
2

Use JDK proxies as suggested, or: use a Mock Framework like EasyMock or Mockito.

GL mock = EasyMock.createMock(GL.class);
EasyMock.expect(mock.someMethod()).andReturn(someValue);
// or, if you need to do more computing:
EasyMock.expect(mock.someOtherMethod()).andAnswer(new IAnswer<String>() {
        public String answer() throws Throwable {
            return "some value you calculate here";
        }
    });
EasyMock.replay(mock);

now you can use the mock Object for all methods you configured. See the EasyMock readme for more info.

Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588