This is an automated email from the ASF dual-hosted git repository. danhaywood pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/isis.git
commit b9adf68d3cb78424c6c081a95d79911d5692d07c Author: danhaywood <d...@haywood-associates.co.uk> AuthorDate: Wed Nov 21 22:35:23 2018 +0100 ISIS-2043: adds domainEvents for @DomainObject --- .../org/apache/isis/applib/annotation/Action.java | 6 -- .../apache/isis/applib/annotation/Collection.java | 6 -- .../isis/applib/annotation/DomainObject.java | 81 ++++++++++++++++++++++ .../apache/isis/applib/annotation/Property.java | 6 -- .../action/ActionAnnotationFacetFactory.java | 22 ++++-- .../CollectionAnnotationFacetFactory.java | 27 ++++++-- .../DomainObjectAnnotationFacetFactory.java | 54 +++++++++++++++ ...EventDefaultFacetForDomainObjectAnnotation.java | 54 +++++++++++++++ ...EventDefaultFacetForDomainObjectAnnotation.java | 53 ++++++++++++++ ...EventDefaultFacetForDomainObjectAnnotation.java | 53 ++++++++++++++ .../property/PropertyAnnotationFacetFactory.java | 65 ++++++++++++++--- .../simple/subscribers/SimpleObjectListener.java | 4 ++ 12 files changed, 395 insertions(+), 36 deletions(-) diff --git a/core/applib/src/main/java/org/apache/isis/applib/annotation/Action.java b/core/applib/src/main/java/org/apache/isis/applib/annotation/Action.java index d1178eb..4ac31f7 100644 --- a/core/applib/src/main/java/org/apache/isis/applib/annotation/Action.java +++ b/core/applib/src/main/java/org/apache/isis/applib/annotation/Action.java @@ -63,12 +63,6 @@ public @interface Action { * <p> * This subclass must provide a no-arg constructor; the fields are set reflectively. * </p> - * - * <p> - * Only domain services should be registered as subscribers; only domain services are guaranteed to be instantiated and - * resident in memory. The typical implementation of a domain service subscriber is to identify the impacted entities, - * load them using a repository, and then to delegate to the event to them. - * </p> */ Class<? extends ActionDomainEvent<?>> domainEvent() default ActionDomainEvent.Default.class; diff --git a/core/applib/src/main/java/org/apache/isis/applib/annotation/Collection.java b/core/applib/src/main/java/org/apache/isis/applib/annotation/Collection.java index 50b8f95..444881d 100644 --- a/core/applib/src/main/java/org/apache/isis/applib/annotation/Collection.java +++ b/core/applib/src/main/java/org/apache/isis/applib/annotation/Collection.java @@ -54,12 +54,6 @@ public @interface Collection { * </pre> * * <p> - * Only domain services should be registered as subscribers; only domain services are guaranteed to be instantiated and - * resident in memory. The typical implementation of a domain service subscriber is to identify the impacted entities, - * load them using a repository, and then to delegate to the event to them. - * </p> - * - * <p> * This subclass must provide a no-arg constructor; the fields are set reflectively. * </p> */ diff --git a/core/applib/src/main/java/org/apache/isis/applib/annotation/DomainObject.java b/core/applib/src/main/java/org/apache/isis/applib/annotation/DomainObject.java index 97a9512..abce74b 100644 --- a/core/applib/src/main/java/org/apache/isis/applib/annotation/DomainObject.java +++ b/core/applib/src/main/java/org/apache/isis/applib/annotation/DomainObject.java @@ -24,6 +24,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.apache.isis.applib.services.eventbus.ActionDomainEvent; +import org.apache.isis.applib.services.eventbus.CollectionDomainEvent; import org.apache.isis.applib.services.eventbus.ObjectCreatedEvent; import org.apache.isis.applib.services.eventbus.ObjectLoadedEvent; import org.apache.isis.applib.services.eventbus.ObjectPersistedEvent; @@ -31,6 +33,7 @@ import org.apache.isis.applib.services.eventbus.ObjectPersistingEvent; import org.apache.isis.applib.services.eventbus.ObjectRemovingEvent; import org.apache.isis.applib.services.eventbus.ObjectUpdatedEvent; import org.apache.isis.applib.services.eventbus.ObjectUpdatingEvent; +import org.apache.isis.applib.services.eventbus.PropertyDomainEvent; import org.apache.isis.applib.services.publish.PublisherService; /** @@ -246,4 +249,82 @@ public @interface DomainObject { * </p> */ Class<? extends ObjectRemovingEvent<?>> removingLifecycleEvent() default ObjectRemovingEvent.Default.class; + + + /** + * Indicates that an invocation of <i>any</i> action of the domain object (that do not themselves specify their own + * <tt>@Action(domainEvent=...)</tt> should be posted to the + * {@link org.apache.isis.applib.services.eventbus.EventBusService event bus} using the specified custom + * (subclass of) {@link org.apache.isis.applib.services.eventbus.ActionDomainEvent}. + * + * <p>For example: + * </p> + * + * <pre> + * @DomainObject(actionDomainEvent=SomeObject.GenericActionDomainEvent.class) + * public class SomeObject{ + * public static class GenericActionDomainEvent extends ActionDomainEvent<Object> { ... } + * + * public void changeStartDate(final Date startDate) { ...} + * ... + * } + * </pre> + * + * <p> + * This will result in all actions as a more specific type to use) to emit this event. + * </p> + * <p> + * This subclass must provide a no-arg constructor; the fields are set reflectively. + * It must also use <tt>Object</tt> as its generic type. This is to allow mixins to also emit the same event. + * </p> + */ + Class<? extends ActionDomainEvent<?>> actionDomainEvent() default ActionDomainEvent.Default.class; + + /** + * Indicates that changes to <i>any</i> property of the domain object (that do not themselves specify their own + * <tt>@Property(domainEvent=...)</tt> should be posted to the + * {@link org.apache.isis.applib.services.eventbus.EventBusService event bus} using the specified custom + * (subclass of) {@link org.apache.isis.applib.services.eventbus.PropertyDomainEvent}. + * + * <p>For example: + * </p> + * + * <pre> + * @DomainObject(propertyDomainEvent=SomeObject.GenericPropertyDomainEvent.class) + * public class SomeObject{ + * + * public LocalDate getStartDate() { ...} + * } + * </pre> + * + * <p> + * This subclass must provide a no-arg constructor; the fields are set reflectively. + * It must also use <tt>Object</tt> as its generic type. This is to allow mixins to also emit the same event. + * </p> + */ + Class<? extends PropertyDomainEvent<?,?>> propertyDomainEvent() default PropertyDomainEvent.Default.class; + + /** + * Indicates that changes to <i>any</i> collection of the domain object (that do not themselves specify their own + * <tt>@Collection(domainEvent=...)</tt> should be posted to the + * {@link org.apache.isis.applib.services.eventbus.EventBusService event bus} using a custom (subclass of) + * {@link org.apache.isis.applib.services.eventbus.CollectionDomainEvent}. + * + * <p>For example: + * </p> + * <pre> + * @DomainObject(collectionDomainEvent=Order.GenericCollectionDomainEvent.class) + * public class Order { + * + * public SortedSet<OrderLine> getLineItems() { ...} + * } + * </pre> + * + * <p> + * This subclass must provide a no-arg constructor; the fields are set reflectively. + * It must also use <tt>Object</tt> as its generic type. This is to allow mixins to also emit the same event. + * </p> + */ + Class<? extends CollectionDomainEvent<?,?>> collectionDomainEvent() default CollectionDomainEvent.Default.class; + } \ No newline at end of file diff --git a/core/applib/src/main/java/org/apache/isis/applib/annotation/Property.java b/core/applib/src/main/java/org/apache/isis/applib/annotation/Property.java index ed91f9e..7d248d5 100644 --- a/core/applib/src/main/java/org/apache/isis/applib/annotation/Property.java +++ b/core/applib/src/main/java/org/apache/isis/applib/annotation/Property.java @@ -61,12 +61,6 @@ public @interface Property { * </pre> * * <p> - * Only domain services should be registered as subscribers; only domain services are guaranteed to be instantiated - * and resident in memory. The typical implementation of a domain service subscriber is to identify the impacted - * entities, load them using a repository, and then to delegate to the event to them. - * </p> - * - * <p> * This subclass must provide a no-arg constructor; the fields are set reflectively. * </p> */ diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactory.java index b26586d..255f7c0 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactory.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactory.java @@ -85,6 +85,7 @@ import org.apache.isis.core.metamodel.facets.actions.semantics.ActionSemanticsFa import org.apache.isis.core.metamodel.facets.all.hide.HiddenFacet; import org.apache.isis.core.metamodel.facets.members.disabled.DisabledFacet; import org.apache.isis.core.metamodel.facets.members.order.annotprop.MemberOrderFacetForActionAnnotation; +import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.ActionDomainEventDefaultFacetForDomainObjectAnnotation; import org.apache.isis.core.metamodel.services.ServicesInjector; import org.apache.isis.core.metamodel.spec.ObjectSpecification; import org.apache.isis.core.metamodel.specloader.CollectionUtils; @@ -172,19 +173,19 @@ public class ActionAnnotationFacetFactory extends FacetFactoryAbstract // search for @ActionInteraction(value=...) if(actionInteraction != null) { - actionDomainEventType = actionInteraction.value(); + actionDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, actionInteraction.value()); actionDomainEventFacet = new ActionDomainEventFacetForActionInteractionAnnotation( actionDomainEventType, servicesInjector, getSpecificationLoader(), holder); } else // search for @Action(domainEvent=...) - if(action != null && action.domainEvent() != null) { - actionDomainEventType = action.domainEvent(); + if(action != null) { + actionDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, action.domainEvent()); actionDomainEventFacet = new ActionDomainEventFacetForActionAnnotation( actionDomainEventType, servicesInjector, getSpecificationLoader(), holder); } else // else use default event type { - actionDomainEventType = ActionDomainEvent.Default.class; + actionDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, ActionDomainEvent.Default.class); actionDomainEventFacet = new ActionDomainEventFacetDefault( actionDomainEventType, servicesInjector, getSpecificationLoader(), holder); } @@ -238,6 +239,19 @@ public class ActionAnnotationFacetFactory extends FacetFactoryAbstract } } + private static Class<? extends ActionDomainEvent<?>> defaultFromDomainObjectIfRequired( + final ObjectSpecification typeSpec, + final Class<? extends ActionDomainEvent<?>> actionDomainEventType) { + if (actionDomainEventType == ActionDomainEvent.Default.class) { + final ActionDomainEventDefaultFacetForDomainObjectAnnotation typeFromDomainObject = + typeSpec.getFacet(ActionDomainEventDefaultFacetForDomainObjectAnnotation.class); + if (typeFromDomainObject != null) { + return typeFromDomainObject.getEventType(); + } + } + return actionDomainEventType; + } + void processHidden(final ProcessMethodContext processMethodContext) { final Method method = processMethodContext.getMethod(); final FacetHolder holder = processMethodContext.getFacetHolder(); diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactory.java index 55e1fc1..1832bda 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactory.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactory.java @@ -70,9 +70,11 @@ import org.apache.isis.core.metamodel.facets.collections.collection.typeof.TypeO import org.apache.isis.core.metamodel.facets.collections.modify.CollectionAddToFacet; import org.apache.isis.core.metamodel.facets.collections.modify.CollectionRemoveFromFacet; import org.apache.isis.core.metamodel.facets.members.disabled.DisabledFacet; +import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.CollectionDomainEventDefaultFacetForDomainObjectAnnotation; import org.apache.isis.core.metamodel.facets.propcoll.accessor.PropertyOrCollectionAccessorFacet; import org.apache.isis.core.metamodel.facets.propcoll.notpersisted.NotPersistedFacet; import org.apache.isis.core.metamodel.services.ServicesInjector; +import org.apache.isis.core.metamodel.spec.ObjectSpecification; import org.apache.isis.core.metamodel.specloader.CollectionUtils; import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorComposite; import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorForDeprecatedAnnotation; @@ -105,6 +107,9 @@ public class CollectionAnnotationFacetFactory extends FacetFactoryAbstract imple void processModify(final ProcessMethodContext processMethodContext) { final Method method = processMethodContext.getMethod(); + + final Class<?> cls = processMethodContext.getCls(); + final ObjectSpecification typeSpec = getSpecificationLoader().loadSpecification(cls); final FacetHolder holder = processMethodContext.getFacetHolder(); final PropertyOrCollectionAccessorFacet getterFacet = holder.getFacet(PropertyOrCollectionAccessorFacet.class); @@ -145,21 +150,21 @@ public class CollectionAnnotationFacetFactory extends FacetFactoryAbstract imple // search for @CollectionInteraction(value=...) if(collectionInteraction != null) { - collectionDomainEventType = collectionInteraction.value(); + collectionDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, collectionInteraction.value()); collectionDomainEventFacet = collectionInteractionValidator.flagIfPresent( new CollectionDomainEventFacetForCollectionInteractionAnnotation( collectionDomainEventType, servicesInjector, getSpecificationLoader(), holder), processMethodContext); } else // search for @Collection(domainEvent=...) - if(collection != null && collection.domainEvent() != null) { - collectionDomainEventType = collection.domainEvent(); + if(collection != null) { + collectionDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, collection.domainEvent()); collectionDomainEventFacet = new CollectionDomainEventFacetForCollectionAnnotation( collectionDomainEventType, servicesInjector, getSpecificationLoader(), holder); } else // else use default event type { - collectionDomainEventType = CollectionDomainEvent.Default.class; + collectionDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, CollectionDomainEvent.Default.class); collectionDomainEventFacet = new CollectionDomainEventFacetDefault( collectionDomainEventType, servicesInjector, getSpecificationLoader(), holder); } @@ -237,6 +242,20 @@ public class CollectionAnnotationFacetFactory extends FacetFactoryAbstract imple } + private static Class<? extends CollectionDomainEvent<?,?>> defaultFromDomainObjectIfRequired( + final ObjectSpecification typeSpec, + final Class<? extends CollectionDomainEvent<?,?>> collectionDomainEventType) { + if (collectionDomainEventType == CollectionDomainEvent.Default.class) { + final CollectionDomainEventDefaultFacetForDomainObjectAnnotation typeFromDomainObject = + typeSpec.getFacet(CollectionDomainEventDefaultFacetForDomainObjectAnnotation.class); + if (typeFromDomainObject != null) { + return typeFromDomainObject.getEventType(); + } + } + return collectionDomainEventType; + } + + void processHidden(final ProcessMethodContext processMethodContext) { final Method method = processMethodContext.getMethod(); final FacetHolder holder = processMethodContext.getFacetHolder(); diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java index 2a35bba..35808b6 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java @@ -34,6 +34,8 @@ import org.apache.isis.applib.annotation.Nature; import org.apache.isis.applib.annotation.ObjectType; import org.apache.isis.applib.annotation.PublishedObject; import org.apache.isis.applib.services.HasTransactionId; +import org.apache.isis.applib.services.eventbus.ActionDomainEvent; +import org.apache.isis.applib.services.eventbus.CollectionDomainEvent; import org.apache.isis.applib.services.eventbus.ObjectCreatedEvent; import org.apache.isis.applib.services.eventbus.ObjectLoadedEvent; import org.apache.isis.applib.services.eventbus.ObjectPersistedEvent; @@ -41,6 +43,7 @@ import org.apache.isis.applib.services.eventbus.ObjectPersistingEvent; import org.apache.isis.applib.services.eventbus.ObjectRemovingEvent; import org.apache.isis.applib.services.eventbus.ObjectUpdatedEvent; import org.apache.isis.applib.services.eventbus.ObjectUpdatingEvent; +import org.apache.isis.applib.services.eventbus.PropertyDomainEvent; import org.apache.isis.core.commons.config.IsisConfiguration; import org.apache.isis.core.commons.lang.Nullable; import org.apache.isis.core.metamodel.facetapi.Facet; @@ -68,6 +71,9 @@ import org.apache.isis.core.metamodel.facets.object.domainobject.autocomplete.Au import org.apache.isis.core.metamodel.facets.object.domainobject.autocomplete.AutoCompleteFacetForDomainObjectAnnotation; import org.apache.isis.core.metamodel.facets.object.domainobject.choices.ChoicesFacetForDomainObjectAnnotation; import org.apache.isis.core.metamodel.facets.object.domainobject.choices.ChoicesFacetFromBoundedAnnotation; +import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.ActionDomainEventDefaultFacetForDomainObjectAnnotation; +import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.CollectionDomainEventDefaultFacetForDomainObjectAnnotation; +import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.PropertyDomainEventDefaultFacetForDomainObjectAnnotation; import org.apache.isis.core.metamodel.facets.object.domainobject.editing.ImmutableFacetForDomainObjectAnnotation; import org.apache.isis.core.metamodel.facets.object.domainobject.objectspecid.ObjectSpecIdFacetForDomainObjectAnnotation; import org.apache.isis.core.metamodel.facets.object.domainobject.objectspecid.ObjectSpecIdFacetForJdoPersistenceCapableAnnotation; @@ -124,6 +130,7 @@ public class DomainObjectAnnotationFacetFactory extends FacetFactoryAbstract processObjectType(processClassContext); processNature(processClassContext); processLifecycleEvents(processClassContext); + processDomainEvents(processClassContext); } @@ -391,7 +398,21 @@ public class DomainObjectAnnotationFacetFactory extends FacetFactoryAbstract processLifecycleEventRemoving(domainObject, holder); processLifecycleEventUpdated(domainObject, holder); processLifecycleEventUpdating(domainObject, holder); + } + + private void processDomainEvents(final ProcessClassContext processClassContext) { + + final Class<?> cls = processClassContext.getCls(); + final DomainObject domainObject = Annotations.getAnnotation(cls, DomainObject.class); + if(domainObject == null) { + return; + } + final FacetHolder holder = processClassContext.getFacetHolder(); + + processDomainEventAction(domainObject, holder); + processDomainEventProperty(domainObject, holder); + processDomainEventCollection(domainObject, holder); } private void processLifecycleEventCreated(final DomainObject domainObject, final FacetHolder holder) { @@ -513,6 +534,39 @@ public class DomainObjectAnnotationFacetFactory extends FacetFactoryAbstract } } + private void processDomainEventAction(final DomainObject domainObject, final FacetHolder holder) { + final Class<? extends ActionDomainEvent<?>> domainEvent = domainObject.actionDomainEvent(); + + if(domainEvent != ActionDomainEvent.Default.class) { + final ActionDomainEventDefaultFacetForDomainObjectAnnotation facet = + new ActionDomainEventDefaultFacetForDomainObjectAnnotation( + holder, domainEvent, getSpecificationLoader()); + FacetUtil.addFacet(facet); + } + } + + private void processDomainEventProperty(final DomainObject domainObject, final FacetHolder holder) { + final Class<? extends PropertyDomainEvent<?,?>> domainEvent = domainObject.propertyDomainEvent(); + + if(domainEvent != PropertyDomainEvent.Default.class) { + final PropertyDomainEventDefaultFacetForDomainObjectAnnotation facet = + new PropertyDomainEventDefaultFacetForDomainObjectAnnotation( + holder, domainEvent, getSpecificationLoader()); + FacetUtil.addFacet(facet); + } + } + + private void processDomainEventCollection(final DomainObject domainObject, final FacetHolder holder) { + final Class<? extends CollectionDomainEvent<?,?>> domainEvent = domainObject.collectionDomainEvent(); + + if(domainEvent != CollectionDomainEvent.Default.class) { + final CollectionDomainEventDefaultFacetForDomainObjectAnnotation facet = + new CollectionDomainEventDefaultFacetForDomainObjectAnnotation( + holder, domainEvent, getSpecificationLoader()); + FacetUtil.addFacet(facet); + } + } + // ////////////////////////////////////// @Override diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/ActionDomainEventDefaultFacetForDomainObjectAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/ActionDomainEventDefaultFacetForDomainObjectAnnotation.java new file mode 100644 index 0000000..2bb8aef --- /dev/null +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/ActionDomainEventDefaultFacetForDomainObjectAnnotation.java @@ -0,0 +1,54 @@ +/* + * 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.isis.core.metamodel.facets.object.domainobject.domainevents; + +import org.apache.isis.applib.services.eventbus.ActionDomainEvent; +import org.apache.isis.core.metamodel.facetapi.Facet; +import org.apache.isis.core.metamodel.facetapi.FacetHolder; +import org.apache.isis.core.metamodel.facets.SingleClassValueFacetAbstract; +import org.apache.isis.core.metamodel.facets.actions.action.invocation.ActionDomainEventFacet; +import org.apache.isis.core.metamodel.specloader.SpecificationLoader; + +/** + * This does <i>NOT</i> implement {@link ActionDomainEventFacet}, rather it is to record the default type to use + * for any actions as a fallback/default. + */ +public class ActionDomainEventDefaultFacetForDomainObjectAnnotation + extends SingleClassValueFacetAbstract { + + + private final Class<? extends ActionDomainEvent<?>> eventType; + public Class<? extends ActionDomainEvent<?>> getEventType() { + return eventType; + } + + static Class<? extends Facet> type() { + return ActionDomainEventDefaultFacetForDomainObjectAnnotation.class; + } + + public ActionDomainEventDefaultFacetForDomainObjectAnnotation( + final FacetHolder holder, + final Class<? extends ActionDomainEvent<?>> value, + final SpecificationLoader specificationLoader) { + super(type(), holder, value, specificationLoader); + this.eventType = value; + } + +} diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/CollectionDomainEventDefaultFacetForDomainObjectAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/CollectionDomainEventDefaultFacetForDomainObjectAnnotation.java new file mode 100644 index 0000000..627b09d --- /dev/null +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/CollectionDomainEventDefaultFacetForDomainObjectAnnotation.java @@ -0,0 +1,53 @@ +/* + * 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.isis.core.metamodel.facets.object.domainobject.domainevents; + +import org.apache.isis.applib.services.eventbus.CollectionDomainEvent; +import org.apache.isis.core.metamodel.facetapi.Facet; +import org.apache.isis.core.metamodel.facetapi.FacetHolder; +import org.apache.isis.core.metamodel.facets.SingleClassValueFacetAbstract; +import org.apache.isis.core.metamodel.facets.actions.action.invocation.ActionDomainEventFacet; +import org.apache.isis.core.metamodel.specloader.SpecificationLoader; + +/** + * This does <i>NOT</i> implement {@link ActionDomainEventFacet}, rather it is to record the default type to use + * for any actions as a fallback/default. + */ +public class CollectionDomainEventDefaultFacetForDomainObjectAnnotation + extends SingleClassValueFacetAbstract { + + private final Class<? extends CollectionDomainEvent<?, ?>> eventType; + public Class<? extends CollectionDomainEvent<?, ?>> getEventType() { + return eventType; + } + + static Class<? extends Facet> type() { + return CollectionDomainEventDefaultFacetForDomainObjectAnnotation.class; + } + + public CollectionDomainEventDefaultFacetForDomainObjectAnnotation( + final FacetHolder holder, + final Class<? extends CollectionDomainEvent<?,?>> value, + final SpecificationLoader specificationLoader) { + super(type(), holder, value, specificationLoader); + this.eventType = value; + } + +} diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/PropertyDomainEventDefaultFacetForDomainObjectAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/PropertyDomainEventDefaultFacetForDomainObjectAnnotation.java new file mode 100644 index 0000000..4688bbd --- /dev/null +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/PropertyDomainEventDefaultFacetForDomainObjectAnnotation.java @@ -0,0 +1,53 @@ +/* + * 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.isis.core.metamodel.facets.object.domainobject.domainevents; + +import org.apache.isis.applib.services.eventbus.PropertyDomainEvent; +import org.apache.isis.core.metamodel.facetapi.Facet; +import org.apache.isis.core.metamodel.facetapi.FacetHolder; +import org.apache.isis.core.metamodel.facets.SingleClassValueFacetAbstract; +import org.apache.isis.core.metamodel.facets.actions.action.invocation.ActionDomainEventFacet; +import org.apache.isis.core.metamodel.specloader.SpecificationLoader; + +/** + * This does <i>NOT</i> implement {@link ActionDomainEventFacet}, rather it is to record the default type to use + * for any actions as a fallback/default. + */ +public class PropertyDomainEventDefaultFacetForDomainObjectAnnotation + extends SingleClassValueFacetAbstract { + + private final Class<? extends PropertyDomainEvent<?, ?>> eventType; + public Class<? extends PropertyDomainEvent<?, ?>> getEventType() { + return eventType; + } + + static Class<? extends Facet> type() { + return PropertyDomainEventDefaultFacetForDomainObjectAnnotation.class; + } + + public PropertyDomainEventDefaultFacetForDomainObjectAnnotation( + final FacetHolder holder, + final Class<? extends PropertyDomainEvent<?,?>> value, + final SpecificationLoader specificationLoader) { + super(type(), holder, value, specificationLoader); + this.eventType = value; + } + +} diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java index 7f919f7..47db77a 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java @@ -19,18 +19,37 @@ package org.apache.isis.core.metamodel.facets.properties.property; -import org.apache.isis.applib.annotation.*; +import java.lang.reflect.Method; + +import javax.annotation.Nullable; + +import org.apache.isis.applib.annotation.Disabled; +import org.apache.isis.applib.annotation.Hidden; +import org.apache.isis.applib.annotation.Mandatory; +import org.apache.isis.applib.annotation.MaxLength; +import org.apache.isis.applib.annotation.MustSatisfy; +import org.apache.isis.applib.annotation.NotPersisted; +import org.apache.isis.applib.annotation.Optional; +import org.apache.isis.applib.annotation.PostsPropertyChangedEvent; +import org.apache.isis.applib.annotation.Property; +import org.apache.isis.applib.annotation.PropertyInteraction; +import org.apache.isis.applib.annotation.RegEx; import org.apache.isis.applib.services.HasTransactionId; import org.apache.isis.applib.services.eventbus.PropertyChangedEvent; import org.apache.isis.applib.services.eventbus.PropertyDomainEvent; import org.apache.isis.core.commons.config.IsisConfiguration; -import org.apache.isis.core.metamodel.facetapi.*; +import org.apache.isis.core.metamodel.facetapi.Facet; +import org.apache.isis.core.metamodel.facetapi.FacetHolder; +import org.apache.isis.core.metamodel.facetapi.FacetUtil; +import org.apache.isis.core.metamodel.facetapi.FeatureType; +import org.apache.isis.core.metamodel.facetapi.MetaModelValidatorRefiner; import org.apache.isis.core.metamodel.facets.Annotations; import org.apache.isis.core.metamodel.facets.FacetFactoryAbstract; import org.apache.isis.core.metamodel.facets.FacetedMethod; import org.apache.isis.core.metamodel.facets.actions.command.CommandFacet; import org.apache.isis.core.metamodel.facets.all.hide.HiddenFacet; import org.apache.isis.core.metamodel.facets.members.disabled.DisabledFacet; +import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.PropertyDomainEventDefaultFacetForDomainObjectAnnotation; import org.apache.isis.core.metamodel.facets.objectvalue.fileaccept.FileAcceptFacet; import org.apache.isis.core.metamodel.facets.objectvalue.mandatory.MandatoryFacet; import org.apache.isis.core.metamodel.facets.objectvalue.maxlen.MaxLengthFacet; @@ -50,7 +69,18 @@ import org.apache.isis.core.metamodel.facets.properties.property.mandatory.Manda import org.apache.isis.core.metamodel.facets.properties.property.mandatory.MandatoryFacetInvertedByOptionalAnnotationOnProperty; import org.apache.isis.core.metamodel.facets.properties.property.maxlength.MaxLengthFacetForMaxLengthAnnotationOnProperty; import org.apache.isis.core.metamodel.facets.properties.property.maxlength.MaxLengthFacetForPropertyAnnotation; -import org.apache.isis.core.metamodel.facets.properties.property.modify.*; +import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertyClearFacetForDomainEventFromDefault; +import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertyClearFacetForDomainEventFromPropertyAnnotation; +import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertyClearFacetForDomainEventFromPropertyInteractionAnnotation; +import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertyClearFacetForPostsPropertyChangedEventAnnotation; +import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertyDomainEventFacetAbstract; +import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertyDomainEventFacetDefault; +import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertyDomainEventFacetForPropertyAnnotation; +import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertyDomainEventFacetForPropertyInteractionAnnotation; +import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertySetterFacetForDomainEventFromDefault; +import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertySetterFacetForDomainEventFromPropertyAnnotation; +import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertySetterFacetForDomainEventFromPropertyInteractionAnnotation; +import org.apache.isis.core.metamodel.facets.properties.property.modify.PropertySetterFacetForPostsPropertyChangedEventAnnotation; import org.apache.isis.core.metamodel.facets.properties.property.mustsatisfy.MustSatisfySpecificationFacetForMustSatisfyAnnotationOnProperty; import org.apache.isis.core.metamodel.facets.properties.property.mustsatisfy.MustSatisfySpecificationFacetForPropertyAnnotation; import org.apache.isis.core.metamodel.facets.properties.property.notpersisted.NotPersistedFacetForNotPersistedAnnotationOnProperty; @@ -62,14 +92,12 @@ import org.apache.isis.core.metamodel.facets.properties.publish.PublishedPropert import org.apache.isis.core.metamodel.facets.properties.update.clear.PropertyClearFacet; import org.apache.isis.core.metamodel.facets.properties.update.modify.PropertySetterFacet; import org.apache.isis.core.metamodel.services.ServicesInjector; +import org.apache.isis.core.metamodel.spec.ObjectSpecification; import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorComposite; import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorForConflictingOptionality; import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorForDeprecatedAnnotation; import org.apache.isis.core.metamodel.util.EventUtil; -import javax.annotation.Nullable; -import java.lang.reflect.Method; - public class PropertyAnnotationFacetFactory extends FacetFactoryAbstract implements MetaModelValidatorRefiner { private final MetaModelValidatorForDeprecatedAnnotation postsPropertyChangedEventValidator = new MetaModelValidatorForDeprecatedAnnotation(PostsPropertyChangedEvent.class); @@ -108,6 +136,9 @@ public class PropertyAnnotationFacetFactory extends FacetFactoryAbstract impleme void processModify(final ProcessMethodContext processMethodContext) { final Method method = processMethodContext.getMethod(); + + final Class<?> cls = processMethodContext.getCls(); + final ObjectSpecification typeSpec = getSpecificationLoader().loadSpecification(cls); final FacetedMethod holder = processMethodContext.getFacetHolder(); final PropertyOrCollectionAccessorFacet getterFacet = holder.getFacet(PropertyOrCollectionAccessorFacet.class); @@ -136,21 +167,21 @@ public class PropertyAnnotationFacetFactory extends FacetFactoryAbstract impleme // search for @PropertyInteraction(value=...) if(propertyInteraction != null) { - propertyDomainEventType = propertyInteraction.value(); + propertyDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, propertyInteraction.value()); propertyDomainEventFacet = propertyInteractionValidator.flagIfPresent( new PropertyDomainEventFacetForPropertyInteractionAnnotation( propertyDomainEventType, getterFacet, servicesInjector, getSpecificationLoader(), holder), processMethodContext); } else // search for @Property(domainEvent=...) - if(property != null && property.domainEvent() != null) { - propertyDomainEventType = property.domainEvent(); + if(property != null) { + propertyDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, property.domainEvent()); propertyDomainEventFacet = new PropertyDomainEventFacetForPropertyAnnotation( propertyDomainEventType, getterFacet, servicesInjector, getSpecificationLoader(), holder); } else // else use default event type { - propertyDomainEventType = PropertyDomainEvent.Default.class; + propertyDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, PropertyDomainEvent.Default.class); propertyDomainEventFacet = new PropertyDomainEventFacetDefault( propertyDomainEventType, getterFacet, servicesInjector, getSpecificationLoader(), holder); } @@ -228,6 +259,20 @@ public class PropertyAnnotationFacetFactory extends FacetFactoryAbstract impleme } } + private static Class<? extends PropertyDomainEvent<?,?>> defaultFromDomainObjectIfRequired( + final ObjectSpecification typeSpec, + final Class<? extends PropertyDomainEvent<?,?>> propertyDomainEventType) { + if (propertyDomainEventType == PropertyDomainEvent.Default.class) { + final PropertyDomainEventDefaultFacetForDomainObjectAnnotation typeFromDomainObject = + typeSpec.getFacet(PropertyDomainEventDefaultFacetForDomainObjectAnnotation.class); + if (typeFromDomainObject != null) { + return typeFromDomainObject.getEventType(); + } + } + return propertyDomainEventType; + } + + void processHidden(final ProcessMethodContext processMethodContext) { final Method method = processMethodContext.getMethod(); diff --git a/example/application/simpleapp/module-simple/src/main/java/domainapp/modules/simple/subscribers/SimpleObjectListener.java b/example/application/simpleapp/module-simple/src/main/java/domainapp/modules/simple/subscribers/SimpleObjectListener.java new file mode 100644 index 0000000..c377c55 --- /dev/null +++ b/example/application/simpleapp/module-simple/src/main/java/domainapp/modules/simple/subscribers/SimpleObjectListener.java @@ -0,0 +1,4 @@ +package domainapp.modules.simple.subscribers; + +public class SimpleObjectListener { +}