0

We looking for a solution to track the state of a client POJO instance in a performant manner. What we expect is: every time a change is made on a POJO this state is made by using setters. We created an OGNL-based watching / event-bus and when change is made we will send proper OgnlChangeEvent to our event-bus.

So far we looked into AspectJ / cglib / object graph Diff solution but they all occupied too much CPU time. Our current solution is based on Spring MethodInterceptor and we are creating a new Proxy instance every time a Getter method is invoked.

At this point we are starting to look at code generation solutions and we stumbled on Byte Buddy. Is this direction is the right way to do so? Can we generate a new Class that will extend our client POJO State and notify about its OGNL prefix until setter method is invoked?

Rafael Winterhalter
  • 42,759
  • 13
  • 108
  • 192
Elad Hirsch
  • 294
  • 1
  • 16
  • Can you elaborate on the performance problem with some measurements? How many times you call a method, what's the method execution time without change tracking, what is the execution time with change tracking, and an overview of what you do in the change tracking part of your code? – Nándor Előd Fekete Mar 20 '16 at 23:50
  • I agree with @NándorElődFekete. It is hard to believe that AspectJ should be slower than Spring AOP. If so, you must be doing something wrong because AspectJ is very efficient. Maybe we could help if we saw your aspects. But if you are happy with ByteBuddy, maybe this comment is obsolete. I did not read a lot here lately because I was busy, so I am a bit late to comment here, maybe. – kriegaex Apr 17 '16 at 10:38

2 Answers2

1

Byte Buddy is a code generation tool and it is of course possible to implement such a solution. For creating a class that intercepts a setter, you would write code like:

new ByteBuddy()
  .subclass(UserPojo.class)
  .method(ElementMatchers.isSetter())
  .intercept(MethodDelegation.to(MyInterceptor.class)
             .andThen(SuperMethodCall.INSTANCE)
  .make();

Where you would write an interceptor like this:

public class MyInterceptor {
  public static void intercept(Object value) {
    // business logic comes here
  }
}

This way, you can add some code every time a setter is invoked which is triggered before the original code. You can also overload the intercept method with all primitive types to avoid the boxing for the argument. Byte Buddy figures out what to do for you.

I am however confused what you mean by performant. The above code turns out to me the same as creating a class like:

class UserClass {
  String value;
  void setValue(String value) {
    this.value = value;
  }
}

class InstrumentedUserClass extends UserClass {
  @Override
  void setValue(String value) {
    MyInterceptor.intercept(value);
    super.setValue(value);
  }
}

The performance is mainly influenced by the performance of what you do within the intercept method.

Finally, I do not understand how cglib does not work for you but using Spring - which is build on top of cglib - does work. I suspect that there is some problem with your interception logic you should look into.

Rafael Winterhalter
  • 42,759
  • 13
  • 108
  • 192
  • thanks :-) CGLIB is working for me as well as the spring wrapper solution. the main issue was and still the performance :-( – Elad Hirsch Mar 20 '16 at 10:26
1

I think that performance doesn't depend a lot on the bytecode instrumentation framework you use rather it depends on what you do in a method interceptor. Finally you will only know if you measure.

I don't know a lot about your use case, but in general I would ask myself:

  • What information do I really need?
  • What is the basic data to get that information from?

You should separate basic data collection from the interpretation of that data (the information). Usually the interpretation takes more time. Basic data is data that can not be derived from other data. E.g birthday is basic data while age is derived from birthday.

In a method interceptor I would

  • only collect the basic data like class name, method name and maybe parameters.
  • send this information to some kind of worker queue
  • let a background worker generate the information, log or persit it.

The background worker can e.g. interprete the method name to find out if it is a property accessor or not. Usually you do this using the BeanInfo that you get from an Introspector or at least the reflection api.

René Link
  • 48,224
  • 13
  • 108
  • 140
  • Thanks René ! the most consuming part was creating new Proxy for each getter method in order to continue getting the Ognl prefix. that's why i looked for byte instrumentation solution. – Elad Hirsch Mar 21 '16 at 16:54