BigONotation
BigONotation

Reputation: 4538

Setter not delegated to ByteBuddy interceptor

UPDATE: the interceptor is indeed called. However, if I set a breakpoint in the intercepting method, the breakpoint is not triggered (although the method is indeed called). If I set the breakpoint in a method called from the interceptor, then it is triggered (because the breakpoint was not triggered in the first case, this is what made me think initially that the interceptor was not called)

I am trying to use ByteBuddy in order to implement a proxy a class for tracking all changes on an Entity as follows:

public class EntityProxyGenerator{

    public static <T extends Entity> T createProxy(T entity) throws NoSuchMethodException, InstantiationException, IllegalAccessException,
            InvocationTargetException {
        EntityChangesInterceptor interceptor = new EntityChangesInterceptor(entity);

        _class = entity.getClass();

        Class Proxy =
                new ByteBuddy()
                        .subclass(_class)
                        .method(ElementMatchers.isSetter())
                        .intercept(MethodDelegation.to(interceptor))
                        .make()
                        .load(EntityProxyGenerator.class.getClassLoader())
                        .getLoaded();

        return (T) Proxy.getDeclaredConstructor().newInstance();

    }
}

And EntityChangesInterceptor is implemented as follows:

public class EntityChangesInterceptor<T extends Entity> {
    private final T original;

    public EntityChangesInterceptor(T original) {
        this.original = original;
    }

    public boolean isValueObject(Object object) {
        Class<?> class_ = object.getClass();
        if (class_ == String.class
                || class_ == Integer.class
                || class_ == Double.class
                || class_ == Timestamp.class
                || class_ == Instant.class) {
            return true;
        }
        return false;
    }

    boolean isPropertyGetter(Method method, Object[] args) {
        return method.getName().startsWith("get") && args.length == 0;
    }

    boolean isPropertySetter(Method method, Object[] args) {
        return method.getName().startsWith("set") && args.length == 1;
    }

    @RuntimeType
    public Object intercept(@Origin Method method, @AllArguments Object[] args) throws Throwable {
        try {
            if (isPropertySetter(method, args)) {
                if (isValueObject(args[0])) {
                    String propertyName = method.getName().substring(3);
                    String getter = "get" + propertyName;
                    Object oldValue = MethodUtils.invokeMethod(original, getter, null);
                    Object newValue = args[0];
                    ValueChange valueChange = new ValueChange(propertyName, oldValue, newValue);
                    Object callResult = method.invoke(original, args);
                    original.addPropertyChange(valueChange);
                    return callResult;
                }
            }
            return method.invoke(original, args);
        } finally {
            // do your completion logic here
        }
    }
}

The proxy is created correctly, however whenever I try to call a setter on the proxy class, EntityChangesInterceptor.intercept is never called.

If I change the proxy class implementation so that it intercepts getters as follows, then everything works fine:

Class Proxy = new ByteBuddy()
              .subclass(_class)
              .method(ElementMatchers.isGetter()) // note isGetter() instead of isSetter()
              .intercept(MethodDelegation.to(interceptor))
              .make()
              .load(EntityProxyGenerator.class.getClassLoader())
              .getLoaded();

Upvotes: 0

Views: 159

Answers (1)

Adrian
Adrian

Reputation: 51

Making isValueObject private does the trick.

Upvotes: 1

Related Questions