Proposal: Getter-/Setter-Interception via Annotation

149 views
Skip to first unread message

Jonas Pöhler

unread,
Mar 18, 2021, 12:17:41 PM3/18/21
to Ebean ORM
Hello,

I would like to propose a feature addition to Ebean, which would make a couple of useful opportunities available to the developer. With this post I am trying get some feedback, if it would be viable to implement this and if there maybe is the requirement from other forum user for such a feature. For us this addition would solve a couple problems we had for a while now.

Proposal: PreSetter- and PostGetter-Handlers via Annotation

An interface to intercept getters and setters on the Ebean-user-side would be helpful to implement a wide variety of features as demonstrated in the following (stripped of Getters/Setters and some other things for better readability).

public WhiteSpaceTrimmer implements PreSetterHandler {

    @Override
    public Object handle(EntityBean parent, String propertyName, Object oldValue, Object newValue) {
        if (newValue instanceof String) {
            return ((String) newValue).trim();
        }
        return newValue
    }

}

public OwnerHandler implements PostGetterHandler {

    @Override
    public Object handle(EntityBean parent, String propertyName, Object value) {
        if (value instanceof OwnerAware) {
            ((OwnerAware) value).setOwner(parent, propertyName);
        }
        return value;
    }

}

public ReadOnlyHandler implements PostGetterHandler {

    @Override
    public Object handle(EntityBean parent, String propertyName, Object value) {
        if (value instanceof EntityBean) {
            DB.getBeanState(value).setReadOnly(true);
        }
        return value;
    }

}

@PostGetter(ReadOnlyHandler.class)
public @interface Immutable {}

@Entity
@PreSetter(WhiteSpaceTrimmer.class)
public ExampleModel {

    private Integer countOfSomething;

    private String postalCode;

    @PreSetter({}) // Do not trim whitespaces here
    private String keepWhiteSpace;

    @Immutable
    @OneToOne
    private Log log;

    @PostGetter(OwnerHandler.class)
    @DbJson
    private DataStore dataStore;

}


There already was an implementation in Ebean which made things like these possible, but not quite as easy: https://github.com/ebean-orm/ebean/issues/1198. That feature got remove, it seemed like there was nobody really using it (https://groups.google.com/g/ebean/c/qxyeKbUrnCI/m/26XTzWz6BAAJ). Also it was implemented directly inside the EntityBeanIntercept (EBI) which meant additional runtime overhead for initializing lists of handlers inside the EBI for every EntityBean. I suggest that if an implementation like this would be done again, this should be done in the ebean-agent. That way the change would be least invasive (no additional code in the EBI) and code only gets called if the property of bean is explicitly annotated with the annotation, meaning, that it would also not break any existing API.

Motiviation:

Q: Why would we like to have something like this?
A: As demonstrated with the example, we have a couple of usecases like data-normalization, advanced relation tracking and programming security. Since all these things are not directly Ebean's responsibility, this would be the least invasive way of providing the opportunity to implement such things.

Q: Why don't we just implement it directly in the getters/setters?
A: We in particular are using Project Lombok to generate getters and setters at compile-time thus avoiding rather verbose models and instead try to do as much as possible with annotations. This is similar to Kotlin, where getters and setters are implicitly generated by the compiler itself. And for plain Java there is an advantage too, because the proposal includes the possibility to annotate entities with these annotations, to intercept every property. Implementing this behavior inside the getters and setters could get really repetitive and possibly error prone.

If this feature is desirable for Ebean, I would offer to implement it and add some documentation for it, so that it doesn't stay a feature that nobody uses after all, because it's simply unknown.
I'm also open to discussion on different approaches to achieve a similar result and if there are any questions I haven't answered yet that might be needed to understand this.

All the best
Jonas

Jens

unread,
Mar 19, 2021, 12:03:48 PM3/19/21
to Ebean ORM
Hi,

technically this is aspect oriented programming (AOP). As you already use Lombok, you might be fine with using something like AspectJ at compile time as well to weave different aspects into your entity classes. With AspectJ you can  easily define code that should execute before, after, around method body code and also define code that should run when a method has returned or has thrown an exception. Together with some sort of a query language you can select the target classes and methods you want to apply an aspect to, so you don't have to add annotations to your real code at all.

-- J.
Reply all
Reply to author
Forward
0 new messages