jTransfo 0.10 released, introducing convert interceptors

jTransfo 0.10 is now available. It contains several improvements and a few bug fixes. For the full list of issues solved see the issue tracker.

The most important change introduced in this release is the introduction of convert interceptors. The convert interceptors are called around each jTransfo convert call. They allow you to do validations and cause side-effects on the conversions.

For example a converter could be used to automatically add “last modified” information in domain objects.

For example, assuming objects which can contain last update metadata extend AbstractChangeLoggedObject and that there is a spring service which fills the metadata, you can use the code below. When using the jtransfo-spring module, the interceptors are automatically added (you can order them using the @Order() annotation if needed).

@Component
public class MetadataUpdateConvertInterceptor implements ConvertInterceptor {
 
    @Autowired
    private MetaDataUpdater metaDataUpdater;
 
    @Override
    public <T> T convert(Object source, T target, boolean isTargetTo, ConvertSourceTarget next, String... tags) {
        T res = next.convert(source, target, isTargetTo, tags);
        if (res instanceof AbstractChangeLoggedObject) {
            metaDataUpdater.update((AbstractChangeLoggedObject) res);
        }
        return res;
    }
}

You can also use this mechanism to integrate bean validations. Using spring this could be done by including the following service:

@Component
public class BeanValidationConvertInterceptor implements ConvertInterceptor {
 
    @Autowired
    private SpringValidatorAdapter validator;
 
    @Override
    public <T> T convert(Object source, T target, boolean isTargetTo, ConvertSourceTarget next, String... tags) {
        T res = next.convert(source, target, isTargetTo, tags);
        if (!isTargetTo) { // only validate on the domain objects, not on transfer objects
            BindingResult bindingResult = new MapBindingResult(new HashMap(), "");
            validator.validate(res, bindingResult);
            if (bindingResult.hasErrors()) {
                Set<ConstraintViolation<?>> violations = new HashSet<ConstraintViolation<?>>();
                for (ObjectError error : bindingResult.getAllErrors()) {
                    violations.add(new ConstraintViolationImpl(error.getDefaultMessage(), error.getDefaultMessage(),
                            res.getClass(), res, res, res,
                            PathImpl.createPathFromString(error.getObjectName()),
                            null, null));
                }
                throw new MyConstraintViolationException(violations);
            }
 
        }
        return res;
    }
 
    private static class MyConstraintViolationException extends ConstraintViolationException {
 
        private MyConstraintViolationException(Set<ConstraintViolation<?>> constraintViolations) {
            super(constraintViolations);
        }
 
        @Override
        public String getMessage() {
            return toString();
        }
 
        @Override
        public String toString() {
            return "ConstraintViolationException " + this.getConstraintViolations();
        }
    }
 
}
Posted in Documentation, Release