in core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/DomainEventHelper.java [60:422]
public record DomainEventHelper(
@NonNull MetamodelEventService metamodelEventService) {
public static DomainEventHelper ofServiceRegistry(final ServiceRegistry serviceRegistry) {
return new DomainEventHelper(serviceRegistry.lookupServiceElseFail(MetamodelEventService.class));
}
// -- postEventForAction
// variant using eventType and no existing event
public @Nullable ActionDomainEvent<?> postEventForAction(
final AbstractDomainEvent.Phase phase,
final @NonNull Class<? extends ActionDomainEvent<?>> eventType,
final ObjectAction objectAction,
final FacetHolder facetHolder,
final InteractionHead head,
final Can<ManagedObject> argumentAdapters,
final @Nullable Object resultPojo) {
return postEventForAction(phase, uncheckedCast(eventType), /*existingEvent*/null,
objectAction, facetHolder,
head, argumentAdapters, resultPojo);
}
// variant using existing event and not eventType (is derived from event)
public void postEventForAction(
final AbstractDomainEvent.Phase phase,
final @NonNull ActionDomainEvent<?> existingEvent,
final ObjectAction objectAction,
final FacetHolder facetHolder,
final InteractionHead head,
final Can<ManagedObject> argumentAdapters,
final @Nullable Object resultPojo) {
postEventForAction(phase,
uncheckedCast(existingEvent.getClass()), existingEvent, objectAction, facetHolder,
head, argumentAdapters, resultPojo);
}
private @Nullable <S> ActionDomainEvent<S> postEventForAction(
final AbstractDomainEvent.Phase phase,
final Class<? extends ActionDomainEvent<S>> eventType,
final ActionDomainEvent<S> existingEvent,
final ObjectAction objectAction,
final FacetHolder facetHolder,
final InteractionHead head,
final Can<ManagedObject> argumentAdapters,
final @Nullable Object resultPojo) {
_Assert.assertTypeIsInstanceOf(eventType, ActionDomainEvent.class);
try {
final ActionDomainEvent<S> event;
if (existingEvent != null && phase.isExecuted()) {
// reuse existing event from the executing phase
event = existingEvent;
} else {
// all other phases, create a new event
final S source = uncheckedCast(MmUnwrapUtils.single(head.target()));
final Object[] arguments = MmUnwrapUtils.multipleAsArray(argumentAdapters);
final Identifier identifier = facetHolder.getFeatureIdentifier();
event = newActionDomainEvent(eventType, identifier, source, arguments);
// copy over if mixee is present
if (event != null) {
head.getMixee()
.ifPresent(mixedInAdapter->
event.setMixee(mixedInAdapter.getPojo()));
}
if(objectAction != null) { // should always be the case...
if (event != null) {
event.setSemantics(objectAction.getSemantics());
}
var parameters = objectAction.getParameters();
var parameterNames = parameters.stream()
.map(ObjectActionParameter::getCanonicalFriendlyName)
.collect(_Lists.toUnmodifiable());
final List<Class<?>> parameterTypes = parameters.stream()
.map(ObjectActionParameter::getElementType)
.map(ObjectSpecification::getCorrespondingClass)
.collect(_Lists.toUnmodifiable());
if (event != null) {
event.setParameterNames(parameterNames);
event.setParameterTypes(parameterTypes);
}
}
}
if (event != null) {
event.setEventPhase(phase);
if(phase.isExecuted()) {
event.setReturnValue(resultPojo);
}
metamodelEventService.fireActionDomainEvent(event);
}
return event;
} catch (Exception e) {
throw new UnrecoverableException(e);
}
}
static @Nullable <S> ActionDomainEvent<S> newActionDomainEvent(
final Class<? extends ActionDomainEvent<S>> type,
final Identifier identifier,
final S source,
final Object... arguments)
throws IllegalArgumentException,
SecurityException {
var constructors = _Reflect.getPublicConstructors(type);
var noArgConstructor = constructors
.filter(paramCount(0))
.getFirst().orElse(null);
if(noArgConstructor!=null) {
final ActionDomainEvent<S> ade = EventObjectBase.getInstanceWithSource(type, source).orElseThrow();
ade.setIdentifier(identifier);
ade.setArguments(asList(arguments));
return ade;
}
var oneArgConstructor = constructors
.filter(paramCount(1)
.and(paramAssignableFrom(0, source.getClass())))
.getFirst().orElse(null);
if(oneArgConstructor!=null) {
final Object event = invokeConstructor(oneArgConstructor, source);
final ActionDomainEvent<S> ade = uncheckedCast(event);
ade.setIdentifier(identifier);
ade.setArguments(asList(arguments));
return ade;
}
var threeArgConstructor = constructors
.filter(paramCount(3)
.and(paramAssignableFrom(0, source.getClass()))
.and(paramAssignableFrom(1, Identifier.class))
.and(paramAssignableFrom(2, Object[].class))
)
.getFirst()
.orElse(null);
if(threeArgConstructor!=null) {
var event = invokeConstructor(threeArgConstructor, source, identifier, arguments);
return uncheckedCast(event);
}
log.error("Unable to locate constructor of ActionDomainEvent subclass.\n* event's class name : {}\n* source's class name: {}\n* identifier : {}\n", type.getName(), source.getClass().getName(), identifier.memberLogicalName());
return null;
}
// same as in ActionDomainEvent's constructor.
private static List<Object> asList(final Object[] arguments) {
return arguments != null
? Arrays.asList(arguments)
: Collections.emptyList();
}
// -- postEventForProperty, newPropertyInteraction
public <S, T> PropertyDomainEvent<S, T> postEventForProperty(
final AbstractDomainEvent.Phase phase,
final Class<? extends PropertyDomainEvent<S, T>> eventType,
final PropertyDomainEvent<S, T> existingEvent,
final FacetHolder facetHolder,
final InteractionHead head,
final T oldValue,
final T newValue) {
_Assert.assertTypeIsInstanceOf(eventType, PropertyDomainEvent.class);
try {
final PropertyDomainEvent<S, T> event;
if(existingEvent != null && phase.isExecuted()) {
// reuse existing event from the executing phase
event = existingEvent;
} else {
// all other phases, create a new event
final S source = uncheckedCast(MmUnwrapUtils.single(head.target()));
final Identifier identifier = facetHolder.getFeatureIdentifier();
event = newPropertyDomainEvent(eventType, identifier, source, oldValue, newValue);
// copy over if have
head.getMixee()
.ifPresent(mixeeAdapter->
event.setMixee(mixeeAdapter.getPojo()));
}
event.setEventPhase(phase);
// just in case the actual new value held by the object is different from that applied
setEventNewValue(event, newValue);
metamodelEventService.firePropertyDomainEvent(event);
return event;
} catch (Exception e) {
throw new UnrecoverableException(e);
}
}
private static <S,T> void setEventNewValue(final PropertyDomainEvent<S, T> event, final T newValue) {
event.setNewValue(newValue);
}
static <S,T> PropertyDomainEvent<S,T> newPropertyDomainEvent(
final @NonNull Class<? extends PropertyDomainEvent<S, T>> type,
final @NonNull Identifier identifier,
final S source,
final T oldValue,
final T newValue) throws NoSuchMethodException, SecurityException, IllegalArgumentException {
var constructors = _Reflect.getPublicConstructors(type);
var noArgonstructor = constructors
.filter(paramCount(0))
.getFirst().orElse(null);
if(noArgonstructor != null) {
final PropertyDomainEvent<S, T> pde = EventObjectBase.getInstanceWithSource(type, source).orElseThrow();
pde.setIdentifier(identifier);
pde.setOldValue(oldValue);
pde.setNewValue(newValue);
return pde;
}
var oneArgConstructor = constructors
.filter(paramCount(1)
.and(paramAssignableFrom(0, source.getClass())))
.getFirst().orElse(null);
if(oneArgConstructor != null) {
final Object event = invokeConstructor(oneArgConstructor, source);
final PropertyDomainEvent<S, T> pde = uncheckedCast(event);
pde.setIdentifier(identifier);
pde.setOldValue(oldValue);
pde.setNewValue(newValue);
return pde;
}
// else
var fourArgConstructor = constructors
.filter(paramCount(4)
.and(paramAssignableFrom(0, source.getClass()))
.and(paramAssignableFrom(1, Identifier.class))
.and(paramAssignableFromValue(2, oldValue))
.and(paramAssignableFromValue(3, newValue))
).getFirst().orElse(null);
if(fourArgConstructor != null) {
var event = invokeConstructor(fourArgConstructor, source, identifier, oldValue, newValue);
return uncheckedCast(event);
}
// else
throw new NoSuchMethodException(type.getName()+".<init>(...)");
}
// -- postEventForCollection, newCollectionDomainEvent
public <S, T> CollectionDomainEvent<S, T> postEventForCollection(
final AbstractDomainEvent.Phase phase,
final Class<? extends CollectionDomainEvent<S, T>> eventType,
final FacetHolder facetHolder,
final InteractionHead head) {
_Assert.assertTypeIsInstanceOf(eventType, CollectionDomainEvent.class);
try {
final CollectionDomainEvent<S, T> event;
final S source = uncheckedCast(MmUnwrapUtils.single(head.target()));
final Identifier identifier = facetHolder.getFeatureIdentifier();
event = newCollectionDomainEvent(eventType, phase, identifier, source);
// copy over if have
head.getMixee()
.ifPresent(mixeeAdapter->
event.setMixee(mixeeAdapter.getPojo()));
event.setEventPhase(phase);
metamodelEventService.fireCollectionDomainEvent(event);
return event;
} catch (Exception e) {
throw new UnrecoverableException(e);
}
}
<S, T> CollectionDomainEvent<S, T> newCollectionDomainEvent(
final Class<? extends CollectionDomainEvent<S, T>> type,
final AbstractDomainEvent.Phase phase,
final Identifier identifier,
final S source)
throws NoSuchMethodException, SecurityException,
IllegalArgumentException {
var constructors = _Reflect.getPublicConstructors(type);
var noArgConstructor = constructors
.filter(paramCount(0))
.getFirst().orElse(null);
if(noArgConstructor != null) {
final CollectionDomainEvent<S, T> cde = EventObjectBase.getInstanceWithSource(type, source).orElseThrow();
cde.setIdentifier(identifier);
return cde;
}
var oneArgConstructor = constructors
.filter(paramCount(1)
.and(paramAssignableFrom(0, source.getClass())))
.getFirst().orElse(null);
if(oneArgConstructor != null) {
final Object event = invokeConstructor(oneArgConstructor, source);
final CollectionDomainEvent<S, T> cde = uncheckedCast(event);
cde.setIdentifier(identifier);
return cde;
}
// else
// search for constructor accepting source, identifier
var twoArgConstructor = constructors
.filter(paramCount(2)
.and(paramAssignableFrom(0, source.getClass()))
.and(paramAssignableFrom(1, Identifier.class))
)
.getFirst().orElse(null);
if(twoArgConstructor != null) {
var event = invokeConstructor(twoArgConstructor, source, identifier);
return uncheckedCast(event);
}
// else
throw new NoSuchMethodException(type.getName()+".<init>(...)");
}
private static <T> T invokeConstructor(
final @NonNull Constructor<T> constructor,
final Object... args){
try {
return constructor.newInstance(args);
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
throw _Exceptions.unrecoverable(e,
"failed to invoke constructor %s", constructor);
}
}
}