SideEffects are defined in SideEffect.
If you want to reproduce what’s explained in this tutorial, remember to depend on the Core Bootstrap artifact:
At runtime you will need the Core Runtime artifact too. See the Depend on Zest™ in your build tutorial for details.
A typed SideEffect is a Java class that implements the MixinType it can be used on:
public abstract class MailNotifySideEffect extends SideEffectOf<Confirmable> implements Confirmable { @Service private MailService mailer; @This private HasLineItems hasItems; @This private HasCustomer hasCustomer; @Override public void confirm() { StringBuilder builder = new StringBuilder(); builder.append( "An Order has been made.\n\n\n" ); builder.append( "Customer:" ); builder.append( hasCustomer.name().get() ); builder.append( "\n\nItems ordered:\n" ); for( LineItem item : hasItems.lineItems().get() ) { builder.append( item.name().get() ); builder.append( " : " ); builder.append( item.quantity().get() ); builder.append( "\n" ); } mailer.send( "sales@mycompany.com", builder.toString() ); } }
The MailNotifySideEffect is implemented as an abstract class, since we are not interested in the many other methods in the Confirmable interface. Extending the SideEffectOf is a convenience mechanism, instead of an explicit @SideEffectFor annotation on a private field, which can be used in rare occasions when you are not able to extend. This base class defines the next field, which is set up by the Zest™ runtime and points to the next fragment in the call stack. We can also see that the MailService, HasLineItems and HasCustomer are provided to the side-effect, which is done with dependency injection. Zest™ also supports dependency injection via constructors and methods.
It can be used as follows;
@SideEffects( MailNotifySideEffect.class ) public interface OrderEntity extends Order, HasSequenceNumber, HasCustomer, HasLineItems, Confirmable, EntityComposite { }
Methods of the SideEffect Fragment will be called after the Mixin invocation.
A generic SideEffect is a Java class that implements java.lang.reflect.InvocationHandler which allows it to be used on any arbitrary MixinType.
public class MyGenericSideEffect extends GenericSideEffect { @Override public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable { // Do whatever you need... try { // It is possible to obtain the returned values by using 'result' member; Object returnedValue = result.invoke( proxy, method, args ); } catch( NumberFormatException e ) { // And Exception will be thrown accordingly, in case you need to know. throw new IllegalArgumentException(); // But any thrown exceptions are ignored. } return 23; // Return values will also be ignored. } }
It can be used as follows;
@Concerns( MyGenericSideEffect.class ) public interface AnyMixinType { [...snip...] }
Methods of the SideEffect Fragment will be called before the Mixin invocation.
For generic SideEffects that should only trigger on methods with specific annotations or fulfilling some expression, add @AppliesTo annotation to the SideEffect class which points to either triggering annotation(s), or to AppliesToFilter implementation(s).
The SideEffect is invoked if one of the triggering annotations is found or one of the AppliesToFilter accepts the invocation. In other words the AppliesTo arguments are OR’ed.
Here is how the declaration goes ;
@AppliesTo( { MyAnnotation.class, MyAppliesToFilter.class } ) public class MyGenericSideEffect extends GenericSideEffect { [...snip...] }
And how to use the annotation ;
@Concerns( MyGenericSideEffect.class ) public interface AnyMixinType { @MyAnnotation void doSomething(); void doSomethingElse(); } [...snip...]
Here only the doSomething() method will see the SideEffect applied whereas the doSomethingElse() method won’t.
Finally here is how to implement an AppliesToFilter:
public class MyAppliesToFilter implements AppliesToFilter { public boolean appliesTo( Method method, Class<?> mixin, Class<?> compositeType, Class<?> modifierClass ) { boolean appliesTo = evaluate(method); // Do whatever you want return appliesTo; } [...snip...] private boolean evaluate( Method method ) { return true; }