Hi folks (but probably mostly Oscar)...
Jeroen and I were looking to use the new EventBus support for feature,
specifically annotating an action using @PostsActionInvokedEvent with a
custom event...
public class CommunicationChannel {
public class RemovedEvent extends ActionInokedEvent {
...
public CommunicationChannel getSource() { ... }
public CommunicationChannel getReplacedBy() { ... }
...
}
@PostsActionInvokedEvent(CommunicationChannel.RemovedEvent.class)
public void remove(@Named("Replace with") @Optional
CommunicationChannel replaceWith) {
getContainer().remove(this);
}
...
}
and then in the subscriber:
public class AgreementRoleCommunicationChannels {
@Subscribe
public void on(CommunicationChannel.RemovedEvent ev) {
CommunicationChannel removed = ev.getSource();
CommunicationChannel replacementIfAny = ev.getReplacedBy()
List<AgreementRoleCommunicationChannel> impacted = ... query to
find those referencing "removed" ...
if(impacted.isEmpty()) {
return;
}
if(replacementIfAny == null) {
// veto
throw new RecoverableException("Provide a replacement");
}
for(AgreementRoleCommunicationChannel arcc: impacted) {
arcc.setCommunicationChannel(replacementIfAny);
}
}
...
}
The algorithm in the subscriber is to update the impacted ARCC objects so
that they use the replacement. If no replacement was provided, then the
action is vetoed.
As its stands this code fails because the query to find those referencing
the "removed" triggers a flush, which then causes a JDO referential
integrity exception; not what we wanted.
Still with me?
We thought of making the auto-flush behaviour configurable (and we might
still), however it seems that this use case highlights the fact that right
now our event bus only supports propagation of an event during the action
invocation, and there isn't yet a mechanism to propagate a validation event.
Put another way: really the veto logic ought to be done prior to executing
the action; analogous to how our "validateXxx" methods fire.
For that matter, it ought to be possible for a subscriber to disable (grey
out) or even hide an action on the object it subscribes to.
Haven't figured out what the syntax is for this. I don't really want the
developer to have to define multiple event types. One option is to
introduce a "mode" in the event, eg:
package org.apache.isis.applib.events;
public enum Mode
Hide, Disable, Validate, Execute;
}
and then have this as a member of ActionInvokedEvent. In the subscriber,
could then have:
public class AgreementRoleCommunicationChannels {
@Subscribe
public void on(CommunicationChannel.RemovedEvent ev) {
CommunicationChannel removed = ev.getSource();
CommunicationChannel replacementIfAny = ev.getReplacedBy()
Mode mode = ev.getMode();
switch(mode) {
case Validate:
List<AgreementRoleCommunicationChannel> impacted = ... query
to find those referencing "removed" ...
if(impacted.isEmpty()) {
return;
}
if(replacementIfAny == null) {
// veto
throw new RecoverableException("Provide a replacement");
}
ev.put("impacted", impacted);
break;
case Execute:
List<AgreementRoleCommunicationChannel> impacted =
ev.get("impacted");
for(AgreementRoleCommunicationChannel arcc: impacted) {
arcc.setCommunicationChannel(replacementIfAny);
}
}
}
}
Interested in opinions on this.
Cheers
Dan