How can I track an objects changes with bytebuddy?

32 views Asked by At

I'm trying to write something similar to a Unit for Work for tracking changes, although it's very specific to some generated code.

I have the following ByteBuddy code

    private Class<? extends Group> getTrackedClass(GroupChangeTrackingProxy trackingProxy) {
        return new ByteBuddy()
                .subclass(Group.class)
                .method(ElementMatchers.isSetter())
                .intercept(InvocationHandlerAdapter.of(new GroupChangeTrackingProxy()))
                .make()
                .load(CustomerPlatformGroupsRepositoryImpl.class.getClassLoader())
                .getLoaded();
    }
public class GroupChangeTrackingProxy implements InvocationHandler {

    private final Logger log = LogManager.getLogger(GroupChangeTrackingProxy.class);
    private final GroupModifications target = new GroupModifications();
    private final AtomicBoolean tracking = new AtomicBoolean(false);

    public GroupModifications getTarget() {
        return target;
    }

    public void setTrack(boolean bool) {
        this.tracking.setPlain(bool);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {

        if (tracking.get()) {
            var match = ReflectionUtils.findMethod(GroupModifications.class, method.getName());
            if (match != null) {
                ReflectionUtils.invokeMethod(match, target, args);
                log.debug(
                        "tracking: '{}.{}({})'",
                        () -> proxy.getClass().getSimpleName(),
                        method::getName,
                        () -> args);
            }
        }
// RECURSIVE!!!!
        ReflectionUtils.invokeMethod(method, proxy, args); 

        return null; // should be void methods anyways
    }
}

We have to copy the returned information to our tracked proxy, and then start tracking

    private Group track(Group value) {
        var tracker = new GroupChangeTrackingProxy();
        var clazz = getTrackedClass(tracker);

        var tracked = createInstanceOf(clazz);
        this.copier.copy(value, tracked);
        tracker.setTrack(true);
        return tracked;
    }

the problem with this implementation is it ends up being a recursive call. I'm fine with avoiding springs ReflectionUtils and InvocationHandler but I can't find a way to get the method name from Byte Buddy.

How can I do this without the recursion problem?

1

There are 1 answers

0
xenoterracide On

I found the answer in this question.

Essentially it's these annotations

    public void invoke(
            @This Object proxy, @SuperMethod Method method, @AllArguments Object[] args) 

and then change how bytebuddy is set up

.intercept(MethodDelegation.to(new GroupChangeTrackingProxy()))