Skip to content

Commit

Permalink
Merge pull request #1583 from apache/3409-mixin.eventtypes
Browse files Browse the repository at this point in the history
merging ... still WIP but has some fixes already
(CAUSEWAY-3409: Mixed in members might have the wrong domain-event type associated)
  • Loading branch information
andi-huber committed Apr 18, 2023
2 parents 26e2aef + f3fbc4d commit 434de49
Show file tree
Hide file tree
Showing 117 changed files with 5,824 additions and 6,378 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -66,6 +66,9 @@ component/objectstore/xml/tmp/objects/
component/objectstore/xml/xml/objects/
component/objectstore/xml/tmp/tests/

# backup files
*.bak

# log files
*.log

Expand Down
Expand Up @@ -119,24 +119,26 @@ public S getSource() {
* </p>
*/
@Getter
private Object mixedIn;
private Object mixee;

/**
* Not API - set by the framework.
*/
public void setMixedIn(final Object mixedIn) {
this.mixedIn = mixedIn;
public final void setMixee(final Object mixee) {
this.mixee = mixee;
}


/**
* The subject of the event, which will be either the
* {@link #getSource() source} for a regular action, or the
* {@link #getMixedIn() mixed-in} domain object for a mixin.
* {@link #getMixee() mixed-in} domain object for a mixin.
*/
public <T> T getSubject() {
val mixedIn = getMixedIn();
val mixedInElseSource = mixedIn != null ? mixedIn : getSource();
val mixee = getMixee();
val mixedInElseSource = mixee != null
? mixee
: getSource();
return _Casts.uncheckedCast(mixedInElseSource);
}

Expand Down Expand Up @@ -194,7 +196,7 @@ public boolean isExecuted() {
/**
* Not API, set by the framework.
*/
public void setEventPhase(final Phase phase) {
public final void setEventPhase(final Phase phase) {
this.eventPhase = phase;
}

Expand All @@ -208,7 +210,7 @@ public void setEventPhase(final Phase phase) {
/**
* Not API, set by the framework if the no-arg constructor is used.
*/
public void setIdentifier(final Identifier identifier) {
public final void setIdentifier(final Identifier identifier) {
this.identifier = identifier;
}

Expand All @@ -226,7 +228,7 @@ public void setIdentifier(final Identifier identifier) {
*
* @see #veto(String, Object...)
*/
public void hide() {
public final void hide() {
this.hidden = true;
}

Expand Down Expand Up @@ -275,7 +277,7 @@ public boolean isDisabled() {
* @see #disable(org.apache.causeway.applib.services.i18n.TranslatableString)
* @see #veto(String, Object...)
*/
public void disable(final String reason) {
public final void disable(final String reason) {
this.disabledReason = reason;
}

Expand All @@ -286,7 +288,7 @@ public void disable(final String reason) {
* @see #disable(java.lang.String)
* @see #veto(org.apache.causeway.applib.services.i18n.TranslatableString)
*/
public void disable(final TranslatableString reason) {
public final void disable(final TranslatableString reason) {
this.disabledReasonTranslatable = reason;
}

Expand Down Expand Up @@ -478,7 +480,7 @@ public Object get(final Object key) {
*
* Set user-data, for the use of a subsequent {@link #getEventPhase() phase}.
*/
public void put(final Object key, final Object value) {
public final void put(final Object key, final Object value) {
userData.put(key, value);
}

Expand Down
Expand Up @@ -139,12 +139,6 @@ public ActionDomainEvent(final S source) {
@Getter
private List<Class<?>> parameterTypes;

/**
* Populated only for mixins; holds the underlying domain object that the mixin contributes to.
*/
@Getter
private Object mixedIn;

/**
* The arguments being used to invoke the action.
*
Expand Down Expand Up @@ -208,14 +202,6 @@ public void setParameterNames(final List<String> parameterNames) {
public void setParameterTypes(final List<Class<?>> parameterTypes) {
this.parameterTypes = parameterTypes;
}
/**
* @apiNote : NOT API, set by the framework
*/
@Override
public void setMixedIn(final Object mixedIn) {
this.mixedIn = mixedIn;
}


private static final ToString<ActionDomainEvent<?>> toString = ObjectContracts.<ActionDomainEvent<?>>
toString("source", ActionDomainEvent::getSource)
Expand Down
@@ -0,0 +1,99 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.causeway.core.metamodel.facets;

import java.util.function.BiConsumer;

import org.apache.causeway.core.metamodel.facetapi.Facet;
import org.apache.causeway.core.metamodel.facetapi.FacetAbstract;
import org.apache.causeway.core.metamodel.facetapi.FacetHolder;
import org.apache.causeway.core.metamodel.spec.ObjectSpecification;

import lombok.NonNull;

/**
* Base for any event-type holding facets,
* while defining the facet-type is up to implementors.
*/
public abstract class DomainEventFacetAbstract<T>
extends FacetAbstract {

public static enum EventTypeOrigin {
/** {@link #eventType} originates from configured defaults */
DEFAULT,
/** {@link #eventType} originates from domain object annotation */
ANNOTATED_OBJECT,
/** {@link #eventType} originates from member annotation */
ANNOTATED_MEMBER;
public boolean isDefault() { return this==DEFAULT; }
public boolean isAnnotatedObject() { return this==ANNOTATED_OBJECT; }
public boolean isAnnotatedMember() { return this==ANNOTATED_MEMBER; }
}

private Class<? extends T> eventType;
private EventTypeOrigin eventTypeOrigin;

protected DomainEventFacetAbstract(
final Class<? extends Facet> facetType,
final Class<? extends T> eventType,
final EventTypeOrigin eventTypeOrigin,
final FacetHolder holder) {
super(facetType, holder);
this.eventType = eventType;
this.eventTypeOrigin = eventTypeOrigin;
}

public final Class<? extends T> getEventType() {
return eventType;
}

public final EventTypeOrigin getEventTypeOrigin() {
return eventTypeOrigin;
}

/** called during meta-model post-processing only */
protected final void updateEventType(
final Class<? extends T> eventType,
final EventTypeOrigin eventTypeOrigin) {
this.eventType = eventType;
this.eventTypeOrigin = eventTypeOrigin;
}

/**
* Called by meta-model post-processors, to honor domain object annotations on mixees.
* (required only, if this facet belongs to a mixed-in member)
*/
public void initWithMixee(final ObjectSpecification mixeeSpec) {}


@Override
public void visitAttributes(final BiConsumer<String, Object> visitor) {
super.visitAttributes(visitor);
visitor.accept("eventType", getEventType());
visitor.accept("eventTypeOrigin", getEventTypeOrigin().name());
}

@Override
public boolean semanticEquals(final @NonNull Facet other) {
return other instanceof DomainEventFacetAbstract
? this.getEventType() == ((DomainEventFacetAbstract<?>)other).getEventType()
: false;
}

}
Expand Up @@ -125,10 +125,10 @@ private <S> ActionDomainEvent<S> postEventForAction(
final Identifier identifier = facetHolder.getFeatureIdentifier();
event = newActionDomainEvent(eventType, identifier, source, arguments);

// copy over if have
head.getMixedIn()
// copy over if mixee is present
head.getMixee()
.ifPresent(mixedInAdapter->
event.setMixedIn(mixedInAdapter.getPojo()));
event.setMixee(mixedInAdapter.getPojo()));

if(objectAction != null) {
// should always be the case...
Expand Down Expand Up @@ -251,9 +251,9 @@ public <S, T> PropertyDomainEvent<S, T> postEventForProperty(
event = newPropertyDomainEvent(eventType, identifier, source, oldValue, newValue);

// copy over if have
head.getMixedIn()
.ifPresent(mixedInAdapter->
event.setMixedIn(mixedInAdapter.getPojo()));
head.getMixee()
.ifPresent(mixeeAdapter->
event.setMixee(mixeeAdapter.getPojo()));

}

Expand Down Expand Up @@ -343,9 +343,9 @@ public <S, T> CollectionDomainEvent<S, T> postEventForCollection(
event = newCollectionDomainEvent(eventType, phase, identifier, source);

// copy over if have
head.getMixedIn()
.ifPresent(mixedInAdapter->
event.setMixedIn(mixedInAdapter.getPojo()));
head.getMixee()
.ifPresent(mixeeAdapter->
event.setMixee(mixeeAdapter.getPojo()));

event.setEventPhase(phase);

Expand Down
Expand Up @@ -33,7 +33,7 @@ public abstract class SingleClassValueFacetAbstract

private final Class<?> value;

public SingleClassValueFacetAbstract(
protected SingleClassValueFacetAbstract(
final Class<? extends Facet> facetType,
final FacetHolder holder,
final Class<?> value) {
Expand Down
Expand Up @@ -23,7 +23,6 @@
import javax.inject.Inject;

import org.apache.causeway.applib.annotation.Action;
import org.apache.causeway.applib.events.domain.ActionDomainEvent;
import org.apache.causeway.applib.mixins.system.HasInteractionId;
import org.apache.causeway.core.config.progmodel.ProgrammingModelConstants;
import org.apache.causeway.core.metamodel.context.MetaModelContext;
Expand All @@ -33,22 +32,16 @@
import org.apache.causeway.core.metamodel.facets.actions.action.choicesfrom.ChoicesFromFacetForActionAnnotation;
import org.apache.causeway.core.metamodel.facets.actions.action.explicit.ActionExplicitFacetForActionAnnotation;
import org.apache.causeway.core.metamodel.facets.actions.action.hidden.HiddenFacetForActionAnnotation;
import org.apache.causeway.core.metamodel.facets.actions.action.invocation.ActionDomainEventFacetAbstract;
import org.apache.causeway.core.metamodel.facets.actions.action.invocation.ActionDomainEventFacetDefault;
import org.apache.causeway.core.metamodel.facets.actions.action.invocation.ActionDomainEventFacetForActionAnnotation;
import org.apache.causeway.core.metamodel.facets.actions.action.invocation.ActionInvocationFacetForDomainEventFromActionAnnotation;
import org.apache.causeway.core.metamodel.facets.actions.action.invocation.ActionInvocationFacetForDomainEventFromDefault;
import org.apache.causeway.core.metamodel.facets.actions.action.invocation.ActionDomainEventFacet;
import org.apache.causeway.core.metamodel.facets.actions.action.invocation.ActionInvocationFacetForDomainEvent;
import org.apache.causeway.core.metamodel.facets.actions.action.prototype.PrototypeFacetForActionAnnotation;
import org.apache.causeway.core.metamodel.facets.actions.action.semantics.ActionSemanticsFacetForActionAnnotation;
import org.apache.causeway.core.metamodel.facets.actions.action.typeof.TypeOfFacetForActionAnnotation;
import org.apache.causeway.core.metamodel.facets.actions.fileaccept.FileAcceptFacetForActionAnnotation;
import org.apache.causeway.core.metamodel.facets.members.layout.group.LayoutGroupFacetForActionAnnotation;
import org.apache.causeway.core.metamodel.facets.members.publish.command.CommandPublishingFacetForActionAnnotation;
import org.apache.causeway.core.metamodel.facets.members.publish.execution.ExecutionPublishingActionFacetForActionAnnotation;
import org.apache.causeway.core.metamodel.facets.object.domainobject.domainevents.ActionDomainEventDefaultFacetForDomainObjectAnnotation;
import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
import org.apache.causeway.core.metamodel.specloader.validator.MetaModelValidatorForAmbiguousMixinAnnotations;
import org.apache.causeway.core.metamodel.util.EventUtil;

import lombok.val;

Expand Down Expand Up @@ -111,63 +104,41 @@ void processInvocation(final ProcessMethodContext processMethodContext, final Op

val cls = processMethodContext.getCls();
val typeSpec = getSpecificationLoader().loadSpecification(cls);
if(typeSpec.isMixin()) {
/* don't process mixed in actions, as we would require the actual mixee
* (deferred to post processing) */
//TODO[CAUSEWAY-3409] breaks tests ... return;
}

val holder = processMethodContext.getFacetHolder();

//
// Set up ActionDomainEventFacet, which will act as the hiding/disabling/validating advisor
//


// search for @Action(domainEvent=...), else use the default event type
val actionDomainEventFacet =
actionIfAny
.map(Action::domainEvent)
.filter(domainEvent -> domainEvent != ActionDomainEvent.Default.class)
.map(domainEvent ->
(ActionDomainEventFacetAbstract)
new ActionDomainEventFacetForActionAnnotation(
defaultFromDomainObjectIfRequired(typeSpec, domainEvent), holder))
.orElse(
new ActionDomainEventFacetDefault(
defaultFromDomainObjectIfRequired(typeSpec, ActionDomainEvent.Default.class), holder)
);

if(EventUtil.eventTypeIsPostable(
actionDomainEventFacet.getEventType(),
ActionDomainEvent.Noop.class,
ActionDomainEvent.Default.class,
getConfiguration().getApplib().getAnnotation().getAction().getDomainEvent().isPostForDefault())) {
ActionDomainEventFacet
.createRegular(actionIfAny, typeSpec, holder)
.ifPresent(actionDomainEventFacet->{

addFacet(actionDomainEventFacet);
}

// replace the current actionInvocationFacet with one that will
// emit the appropriate domain event and then delegate onto the underlying
// replace the current actionInvocationFacet with one that will
// emit the appropriate domain event and then delegate onto the underlying

addFacet(
//TODO[CAUSEWAY-3409] we don't install those for the mixin case, if bailing out above
new ActionInvocationFacetForDomainEvent(
actionDomainEventFacet.getEventType(), actionDomainEventFacet.getEventTypeOrigin(),
actionMethod, typeSpec, returnSpec, holder));
});

addFacet(actionDomainEventFacet instanceof ActionDomainEventFacetForActionAnnotation
? new ActionInvocationFacetForDomainEventFromActionAnnotation(
actionDomainEventFacet.getEventType(), actionMethod, typeSpec, returnSpec, holder)
: new ActionInvocationFacetForDomainEventFromDefault(
actionDomainEventFacet.getEventType(), actionMethod, typeSpec, returnSpec, holder));

} finally {
processMethodContext.removeMethod(actionMethod.asMethodForIntrospection());
}
}

private static Class<? extends ActionDomainEvent<?>> defaultFromDomainObjectIfRequired(
final ObjectSpecification typeSpec,
final Class<? extends ActionDomainEvent<?>> actionDomainEventType) {

if (actionDomainEventType == ActionDomainEvent.Default.class) {
val typeFromDomainObject =
typeSpec.getFacet(ActionDomainEventDefaultFacetForDomainObjectAnnotation.class);
if (typeFromDomainObject != null) {
return typeFromDomainObject.getEventType();
}
}
return actionDomainEventType;
}

void processHidden(final ProcessMethodContext processMethodContext, final Optional<Action> actionIfAny) {
val facetedMethod = processMethodContext.getFacetHolder();

Expand Down

0 comments on commit 434de49

Please sign in to comment.