This is an automated email from the ASF dual-hosted git repository. ahuber pushed a commit to branch ISIS-1976-rethink-object-adapters in repository https://gitbox.apache.org/repos/asf/isis.git
commit 8ba3a7cdc4ed556baf1db4e81b25f96bc6739443 Author: Andi Huber <ahu...@apache.org> AuthorDate: Mon Sep 10 13:24:19 2018 +0200 ISIS-1976: decouple lifecycle event posting from PersistenceSession Task-Url: https://issues.apache.org/jira/browse/ISIS-1976 --- .../system/persistence/PersistenceSession4.java | 97 ++----------- .../system/persistence/PersistenceSession5.java | 107 ++------------ .../system/persistence/PersistenceSession.java | 3 +- .../system/persistence/PersistenceSessionBase.java | 9 -- .../adaptermanager/ObjectAdapterContext.java | 32 ++++- ...ObjectAdapterContext_LifecycleEventSupport.java | 96 +++++++++++++ .../ObjectAdapterContext_ObjectCreation.java | 158 +++++++++++++++++++++ .../ObjectAdapterContext_ObjectReCreation.java | 72 ---------- 8 files changed, 298 insertions(+), 276 deletions(-) diff --git a/core/plugins/jdo-datanucleus-4/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession4.java b/core/plugins/jdo-datanucleus-4/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession4.java index 1acd10d..35230c5 100644 --- a/core/plugins/jdo-datanucleus-4/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession4.java +++ b/core/plugins/jdo-datanucleus-4/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession4.java @@ -441,95 +441,14 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { */ @Override public ObjectAdapter createTransientInstance(final ObjectSpecification objectSpec) { - return createInstance(objectSpec, Variant.TRANSIENT, null); + return objectAdapterContext.newInstance(objectSpec); } @Override public ObjectAdapter createViewModelInstance(final ObjectSpecification objectSpec, final String memento) { - return createInstance(objectSpec, Variant.VIEW_MODEL, memento); + return objectAdapterContext.recreateInstance(objectSpec, memento); } - private ObjectAdapter createInstance( - final ObjectSpecification spec, - final Variant variant, - final String memento) { - if (LOG.isDebugEnabled()) { - LOG.debug("creating {} instance of {}", variant, spec); - } - final Object pojo; - - if(variant == Variant.VIEW_MODEL) { - pojo = objectAdapterContext.recreateViewModel(spec, memento); - } else { - pojo = objectAdapterContext.instantiateAndInjectServices(spec); - } - - final ObjectAdapter adapter = adapterFor(pojo); - return initializePropertiesAndDoCallback(adapter); - } - - private ObjectAdapter initializePropertiesAndDoCallback(final ObjectAdapter adapter) { - - // initialize new object - final List<ObjectAssociation> fields = adapter.getSpecification().getAssociations(Contributed.EXCLUDED); - for (ObjectAssociation field : fields) { - field.toDefault(adapter); - } - final Object pojo = adapter.getObject(); - servicesInjector.injectServicesInto(pojo); - - CallbackFacet.Util.callCallback(adapter, CreatedCallbackFacet.class); - - if (Command.class.isAssignableFrom(pojo.getClass())) { - - // special case... the command object is created while the transaction is being started and before - // the event bus service is initialized (nb: we initialize services *within* a transaction). To resolve - // this catch-22 situation, we simply suppress the posting of this event for this domain class. - - // this seems the least unpleasant of the various options available: - // * we could have put a check in the EventBusService to ignore the post if not yet initialized; - // however this might hide other genuine errors - // * we could have used the thread-local in JdoStateManagerForIsis and the "skip(...)" hook in EventBusServiceJdo - // to have this event be skipped; but that seems like co-opting some other design - // * we could have the transaction initialize the EventBusService as a "special case" before creating the Command; - // but then do we worry about it being re-init'd later by the ServicesInitializer? - - // so, doing it this way is this simplest, least obscure. - - if(LOG.isDebugEnabled()) { - LOG.debug("Skipping postEvent for creation of Command pojo"); - } - - } else { - postLifecycleEventIfRequired(adapter, CreatedLifecycleEventFacet.class); - } - - return adapter; - } - - - // -- helper: postEvent - - @SuppressWarnings({ "unchecked", "rawtypes" }) - void postLifecycleEventIfRequired( - final ObjectAdapter adapter, - final Class<? extends LifecycleEventFacet> lifecycleEventFacetClass) { - final LifecycleEventFacet facet = adapter.getSpecification().getFacet(lifecycleEventFacetClass); - if(facet != null) { - final Class<? extends AbstractLifecycleEvent<?>> eventType = facet.getEventType(); - final Object instance = InstanceUtil.createInstance(eventType); - final Object pojo = adapter.getObject(); - postEvent((AbstractLifecycleEvent) instance, pojo); - } - } - - void postEvent(final AbstractLifecycleEvent<Object> event, final Object pojo) { - event.setSource(pojo); - eventBusService.post(event); - } - - - // -- fixture installation @@ -1021,7 +940,7 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { changedObjectsServiceInternal.enlistDeleting(adapter); CallbackFacet.Util.callCallback(adapter, RemovingCallbackFacet.class); - postLifecycleEventIfRequired(adapter, RemovingLifecycleEventFacet.class); + objectAdapterContext.postLifecycleEventIfRequired(adapter, RemovingLifecycleEventFacet.class); } @Override @@ -1083,7 +1002,7 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { adapter = objectAdapterContext.addRecreatedPojoToCache(originalOid, pojo); CallbackFacet.Util.callCallback(adapter, LoadedCallbackFacet.class); - postLifecycleEventIfRequired(adapter, LoadedLifecycleEventFacet.class); + objectAdapterContext.postLifecycleEventIfRequired(adapter, LoadedLifecycleEventFacet.class); } newAdapter = adapter; @@ -1130,7 +1049,7 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { // persisting // previously this was performed in the DataNucleusSimplePersistAlgorithm. CallbackFacet.Util.callCallback(adapter, PersistingCallbackFacet.class); - postLifecycleEventIfRequired(adapter, PersistingLifecycleEventFacet.class); + objectAdapterContext.postLifecycleEventIfRequired(adapter, PersistingLifecycleEventFacet.class); } else { // updating @@ -1162,7 +1081,7 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { objectAdapterContext.remapAsPersistent(adapter, persistentOid, this); CallbackFacet.Util.callCallback(adapter, PersistedCallbackFacet.class); - postLifecycleEventIfRequired(adapter, PersistedLifecycleEventFacet.class); + objectAdapterContext.postLifecycleEventIfRequired(adapter, PersistedLifecycleEventFacet.class); changedObjectsServiceInternal.enlistCreated(adapter); @@ -1171,7 +1090,7 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { // the callback and transaction.enlist are done in the preDirty callback // (can't be done here, as the enlist requires to capture the 'before' values) CallbackFacet.Util.callCallback(adapter, UpdatedCallbackFacet.class); - postLifecycleEventIfRequired(adapter, UpdatedLifecycleEventFacet.class); + objectAdapterContext.postLifecycleEventIfRequired(adapter, UpdatedLifecycleEventFacet.class); } Version versionIfAny = getVersionIfAny(pojo); @@ -1215,7 +1134,7 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { if(!wasAlreadyEnlisted) { // prevent an infinite loop... don't call the 'updating()' callback on this object if we have already done so CallbackFacet.Util.callCallback(adapter, UpdatingCallbackFacet.class); - postLifecycleEventIfRequired(adapter, UpdatingLifecycleEventFacet.class); + objectAdapterContext.postLifecycleEventIfRequired(adapter, UpdatingLifecycleEventFacet.class); } ensureRootObject(pojo); diff --git a/core/plugins/jdo-datanucleus-5/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession5.java b/core/plugins/jdo-datanucleus-5/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession5.java index 3441cf0..6255c81 100644 --- a/core/plugins/jdo-datanucleus-5/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession5.java +++ b/core/plugins/jdo-datanucleus-5/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession5.java @@ -21,8 +21,6 @@ package org.apache.isis.core.runtime.system.persistence; import static java.util.Objects.requireNonNull; import static org.apache.isis.commons.internal.base._Casts.uncheckedCast; -import java.lang.reflect.Array; -import java.lang.reflect.Modifier; import java.sql.Timestamp; import java.text.MessageFormat; import java.util.ArrayList; @@ -48,7 +46,6 @@ import org.datanucleus.identity.DatastoreIdImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.apache.isis.applib.events.lifecycle.AbstractLifecycleEvent; import org.apache.isis.applib.query.Query; import org.apache.isis.applib.services.bookmark.Bookmark; import org.apache.isis.applib.services.bookmark.BookmarkService; @@ -58,7 +55,6 @@ import org.apache.isis.applib.services.iactn.Interaction; import org.apache.isis.core.commons.authentication.AuthenticationSession; import org.apache.isis.core.commons.ensure.Assert; import org.apache.isis.core.commons.exceptions.IsisException; -import org.apache.isis.core.commons.factory.InstanceUtil; import org.apache.isis.core.metamodel.adapter.ObjectAdapter; import org.apache.isis.core.metamodel.adapter.ObjectAdapterByIdProvider; import org.apache.isis.core.metamodel.adapter.ObjectAdapterProvider; @@ -70,9 +66,6 @@ import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException; import org.apache.isis.core.metamodel.adapter.version.Version; import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacetUtils; import org.apache.isis.core.metamodel.facets.object.callbacks.CallbackFacet; -import org.apache.isis.core.metamodel.facets.object.callbacks.CreatedCallbackFacet; -import org.apache.isis.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacet; -import org.apache.isis.core.metamodel.facets.object.callbacks.LifecycleEventFacet; import org.apache.isis.core.metamodel.facets.object.callbacks.LoadedCallbackFacet; import org.apache.isis.core.metamodel.facets.object.callbacks.LoadedLifecycleEventFacet; import org.apache.isis.core.metamodel.facets.object.callbacks.PersistedCallbackFacet; @@ -91,8 +84,6 @@ import org.apache.isis.core.metamodel.services.container.query.QueryCardinality; import org.apache.isis.core.metamodel.spec.FreeStandingList; import org.apache.isis.core.metamodel.spec.ObjectSpecId; import org.apache.isis.core.metamodel.spec.ObjectSpecification; -import org.apache.isis.core.metamodel.spec.feature.Contributed; -import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation; import org.apache.isis.core.runtime.persistence.FixturesInstalledFlag; import org.apache.isis.core.runtime.persistence.NotPersistableException; import org.apache.isis.core.runtime.persistence.ObjectNotFoundException; @@ -439,96 +430,14 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { */ @Override public ObjectAdapter createTransientInstance(final ObjectSpecification objectSpec) { - return createInstance(objectSpec, Variant.TRANSIENT, null); + return objectAdapterContext.newInstance(objectSpec); } @Override public ObjectAdapter createViewModelInstance(final ObjectSpecification objectSpec, final String memento) { - return createInstance(objectSpec, Variant.VIEW_MODEL, memento); + return objectAdapterContext.recreateInstance(objectSpec, memento); } - private ObjectAdapter createInstance( - final ObjectSpecification spec, - final Variant variant, - final String memento) { - if (LOG.isDebugEnabled()) { - LOG.debug("creating {} instance of {}", variant, spec); - } - final Object pojo; - - if(variant == Variant.VIEW_MODEL) { - pojo = objectAdapterContext.recreateViewModel(spec, memento); - } else { - pojo = objectAdapterContext.instantiateAndInjectServices(spec); - - } - - final ObjectAdapter adapter = adapterFor(pojo); - return initializePropertiesAndDoCallback(adapter); - } - - private ObjectAdapter initializePropertiesAndDoCallback(final ObjectAdapter adapter) { - - // initialize new object - final List<ObjectAssociation> fields = adapter.getSpecification().getAssociations(Contributed.EXCLUDED); - for (ObjectAssociation field : fields) { - field.toDefault(adapter); - } - final Object pojo = adapter.getObject(); - servicesInjector.injectServicesInto(pojo); - - CallbackFacet.Util.callCallback(adapter, CreatedCallbackFacet.class); - - if (Command.class.isAssignableFrom(pojo.getClass())) { - - // special case... the command object is created while the transaction is being started and before - // the event bus service is initialized (nb: we initialize services *within* a transaction). To resolve - // this catch-22 situation, we simply suppress the posting of this event for this domain class. - - // this seems the least unpleasant of the various options available: - // * we could have put a check in the EventBusService to ignore the post if not yet initialized; - // however this might hide other genuine errors - // * we could have used the thread-local in JdoStateManagerForIsis and the "skip(...)" hook in EventBusServiceJdo - // to have this event be skipped; but that seems like co-opting some other design - // * we could have the transaction initialize the EventBusService as a "special case" before creating the Command; - // but then do we worry about it being re-init'd later by the ServicesInitializer? - - // so, doing it this way is this simplest, least obscure. - - if(LOG.isDebugEnabled()) { - LOG.debug("Skipping postEvent for creation of Command pojo"); - } - - } else { - postLifecycleEventIfRequired(adapter, CreatedLifecycleEventFacet.class); - } - - return adapter; - } - - - // -- helper: postEvent - - @SuppressWarnings({ "unchecked", "rawtypes" }) - void postLifecycleEventIfRequired( - final ObjectAdapter adapter, - final Class<? extends LifecycleEventFacet> lifecycleEventFacetClass) { - final LifecycleEventFacet facet = adapter.getSpecification().getFacet(lifecycleEventFacetClass); - if(facet != null) { - final Class<? extends AbstractLifecycleEvent<?>> eventType = facet.getEventType(); - final Object instance = InstanceUtil.createInstance(eventType); - final Object pojo = adapter.getObject(); - postEvent((AbstractLifecycleEvent) instance, pojo); - } - } - - void postEvent(final AbstractLifecycleEvent<Object> event, final Object pojo) { - event.setSource(pojo); - eventBusService.post(event); - } - - - // -- fixture installation @@ -1017,7 +926,7 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { changedObjectsServiceInternal.enlistDeleting(adapter); CallbackFacet.Util.callCallback(adapter, RemovingCallbackFacet.class); - postLifecycleEventIfRequired(adapter, RemovingLifecycleEventFacet.class); + objectAdapterContext.postLifecycleEventIfRequired(adapter, RemovingLifecycleEventFacet.class); } @Override @@ -1079,7 +988,7 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { adapter = objectAdapterContext.addRecreatedPojoToCache(originalOid, pojo); CallbackFacet.Util.callCallback(adapter, LoadedCallbackFacet.class); - postLifecycleEventIfRequired(adapter, LoadedLifecycleEventFacet.class); + objectAdapterContext.postLifecycleEventIfRequired(adapter, LoadedLifecycleEventFacet.class); } newAdapter = adapter; @@ -1126,7 +1035,7 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { // persisting // previously this was performed in the DataNucleusSimplePersistAlgorithm. CallbackFacet.Util.callCallback(adapter, PersistingCallbackFacet.class); - postLifecycleEventIfRequired(adapter, PersistingLifecycleEventFacet.class); + objectAdapterContext.postLifecycleEventIfRequired(adapter, PersistingLifecycleEventFacet.class); } else { // updating @@ -1158,7 +1067,7 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { objectAdapterContext.remapAsPersistent(adapter, persistentOid, this); CallbackFacet.Util.callCallback(adapter, PersistedCallbackFacet.class); - postLifecycleEventIfRequired(adapter, PersistedLifecycleEventFacet.class); + objectAdapterContext.postLifecycleEventIfRequired(adapter, PersistedLifecycleEventFacet.class); changedObjectsServiceInternal.enlistCreated(adapter); @@ -1167,7 +1076,7 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { // the callback and transaction.enlist are done in the preDirty callback // (can't be done here, as the enlist requires to capture the 'before' values) CallbackFacet.Util.callCallback(adapter, UpdatedCallbackFacet.class); - postLifecycleEventIfRequired(adapter, UpdatedLifecycleEventFacet.class); + objectAdapterContext.postLifecycleEventIfRequired(adapter, UpdatedLifecycleEventFacet.class); } Version versionIfAny = getVersionIfAny(pojo); @@ -1211,7 +1120,7 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement { if(!wasAlreadyEnlisted) { // prevent an infinite loop... don't call the 'updating()' callback on this object if we have already done so CallbackFacet.Util.callCallback(adapter, UpdatingCallbackFacet.class); - postLifecycleEventIfRequired(adapter, UpdatingLifecycleEventFacet.class); + objectAdapterContext.postLifecycleEventIfRequired(adapter, UpdatingLifecycleEventFacet.class); } ensureRootObject(pojo); diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession.java index 8f3967f..af0a2e8 100644 --- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession.java +++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSession.java @@ -53,6 +53,7 @@ extends void open(); void close(); boolean flush(); + void resolve(Object parent); IsisConfiguration getConfiguration(); IsisTransactionManager getTransactionManager(); @@ -166,6 +167,6 @@ extends Object lookup(Bookmark bookmark, FieldResetPolicy fieldResetPolicy); - void resolve(Object parent); + } diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSessionBase.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSessionBase.java index f597cc5..21f1e31 100644 --- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSessionBase.java +++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSessionBase.java @@ -28,7 +28,6 @@ import org.slf4j.LoggerFactory; import org.apache.isis.applib.services.clock.ClockService; import org.apache.isis.applib.services.command.CommandContext; import org.apache.isis.applib.services.command.spi.CommandService; -import org.apache.isis.applib.services.eventbus.EventBusService; import org.apache.isis.applib.services.factory.FactoryService; import org.apache.isis.applib.services.iactn.InteractionContext; import org.apache.isis.applib.services.metrics.MetricsService; @@ -67,7 +66,6 @@ abstract class PersistenceSessionBase implements PersistenceSession { protected final CommandService commandService; protected final InteractionContext interactionContext; - protected final EventBusService eventBusService ; protected final ChangedObjectsServiceInternal changedObjectsServiceInternal; protected final FactoryService factoryService; protected final MetricsService metricsService; @@ -122,7 +120,6 @@ abstract class PersistenceSessionBase implements PersistenceSession { this.commandContext = lookupService(CommandContext.class); this.commandService = lookupService(CommandService.class); this.interactionContext = lookupService(InteractionContext.class); - this.eventBusService = lookupService(EventBusService.class); this.changedObjectsServiceInternal = lookupService(ChangedObjectsServiceInternal.class); this.metricsService = lookupService(MetricsService.class); this.factoryService = lookupService(FactoryService.class); @@ -184,11 +181,6 @@ abstract class PersistenceSessionBase implements PersistenceSession { PERSISTENT } - protected enum Variant { - TRANSIENT, - VIEW_MODEL - } - protected enum State { NOT_INITIALIZED, OPEN, CLOSED } @@ -264,5 +256,4 @@ abstract class PersistenceSessionBase implements PersistenceSession { return new ToString(this).toString(); } - } diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext.java index efff6cc..d4ac548 100644 --- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext.java +++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext.java @@ -22,6 +22,8 @@ import java.util.Objects; import java.util.UUID; import java.util.function.Function; +import javax.annotation.Nullable; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,7 +38,7 @@ import org.apache.isis.core.metamodel.adapter.oid.ParentedCollectionOid; import org.apache.isis.core.metamodel.adapter.oid.RootOid; import org.apache.isis.core.metamodel.adapter.version.Version; import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy; -import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet; +import org.apache.isis.core.metamodel.facets.object.callbacks.LifecycleEventFacet; import org.apache.isis.core.metamodel.facets.propcoll.accessor.PropertyOrCollectionAccessorFacet; import org.apache.isis.core.metamodel.services.ServicesInjector; import org.apache.isis.core.metamodel.spec.ObjectSpecId; @@ -135,7 +137,8 @@ public class ObjectAdapterContext { private final ObjectAdapterContext_NewIdentifier newIdentifierMixin; private final ObjectAdapterContext_ObjectAdapterByIdProvider objectAdapterByIdProviderMixin; private final ObjectAdapterContext_DependencyInjection dependencyInjectionMixin; - private final ObjectAdapterContext_ObjectReCreation objectReCreationMixin; + private final ObjectAdapterContext_ObjectCreation objectReCreationMixin; + private final ObjectAdapterContext_LifecycleEventSupport lifecycleEventMixin; private ObjectAdapterContext( ServicesInjector servicesInjector, @@ -151,7 +154,8 @@ public class ObjectAdapterContext { this.newIdentifierMixin = new ObjectAdapterContext_NewIdentifier(this, persistenceSession); this.objectAdapterByIdProviderMixin = new ObjectAdapterContext_ObjectAdapterByIdProvider(this, persistenceSession, authenticationSession); this.dependencyInjectionMixin = new ObjectAdapterContext_DependencyInjection(this, persistenceSession); - this.objectReCreationMixin = new ObjectAdapterContext_ObjectReCreation(this, persistenceSession); + this.objectReCreationMixin = new ObjectAdapterContext_ObjectCreation(this, persistenceSession); + this.lifecycleEventMixin = new ObjectAdapterContext_LifecycleEventSupport(this, persistenceSession); this.persistenceSession = persistenceSession; this.servicesInjector = servicesInjector; @@ -347,10 +351,22 @@ public class ObjectAdapterContext { return mementoSupportMixin; } - // -- OBJECT RECREATION SUPPORT + // -- DOMAIN OBJECT CREATION SUPPORT + + public ObjectAdapter newInstance(ObjectSpecification objectSpec) { + return objectReCreationMixin.newInstance(objectSpec); + } + + public ObjectAdapter recreateInstance(ObjectSpecification objectSpec, @Nullable final String memento) { + return objectReCreationMixin.recreateInstance(objectSpec, memento); + } + + // -- LIFECYCLE EVENT SUPPORT - public Object recreateViewModel(final ObjectSpecification spec, final String memento) { - return objectReCreationMixin.recreateViewModel(spec, memento); + public void postLifecycleEventIfRequired( + final ObjectAdapter adapter, + final Class<? extends LifecycleEventFacet> lifecycleEventFacetClass) { + lifecycleEventMixin.postLifecycleEventIfRequired(adapter, lifecycleEventFacetClass); } // ------------------------------------------------------------------------------------------------ @@ -493,4 +509,8 @@ public class ObjectAdapterContext { + + + + } \ No newline at end of file diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext_LifecycleEventSupport.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext_LifecycleEventSupport.java new file mode 100644 index 0000000..217ca05 --- /dev/null +++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext_LifecycleEventSupport.java @@ -0,0 +1,96 @@ +/* + * 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.runtime.system.persistence.adaptermanager; + +import java.util.List; + +import javax.annotation.Nullable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.apache.isis.applib.events.lifecycle.AbstractLifecycleEvent; +import org.apache.isis.applib.services.command.Command; +import org.apache.isis.applib.services.eventbus.EventBusService; +import org.apache.isis.core.commons.factory.InstanceUtil; +import org.apache.isis.core.metamodel.adapter.ObjectAdapter; +import org.apache.isis.core.metamodel.facets.object.callbacks.CallbackFacet; +import org.apache.isis.core.metamodel.facets.object.callbacks.CreatedCallbackFacet; +import org.apache.isis.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacet; +import org.apache.isis.core.metamodel.facets.object.callbacks.LifecycleEventFacet; +import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet; +import org.apache.isis.core.metamodel.services.ServicesInjector; +import org.apache.isis.core.metamodel.spec.ObjectSpecification; +import org.apache.isis.core.metamodel.spec.feature.Contributed; +import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation; +import org.apache.isis.core.metamodel.specloader.SpecificationLoader; +import org.apache.isis.core.runtime.system.persistence.PersistenceSession; + +/** + * package private mixin for ObjectAdapterContext + * <p> + * Responsibility: provides life-cycle event post support + * </p> + * @since 2.0.0-M2 + */ +@SuppressWarnings("unused") +class ObjectAdapterContext_LifecycleEventSupport { + + + private static final Logger LOG = LoggerFactory.getLogger(ObjectAdapterContext_LifecycleEventSupport.class); + private final ObjectAdapterContext objectAdapterContext; + private final PersistenceSession persistenceSession; + private final ServicesInjector servicesInjector; + private final SpecificationLoader specificationLoader; + private final EventBusService eventBusService; + + + ObjectAdapterContext_LifecycleEventSupport(ObjectAdapterContext objectAdapterContext, + PersistenceSession persistenceSession) { + this.objectAdapterContext = objectAdapterContext; + this.persistenceSession = persistenceSession; + this.servicesInjector = persistenceSession.getServicesInjector(); + this.specificationLoader = servicesInjector.getSpecificationLoader(); + this.eventBusService = servicesInjector.lookupService(EventBusService.class); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + void postLifecycleEventIfRequired( + final ObjectAdapter adapter, + final Class<? extends LifecycleEventFacet> lifecycleEventFacetClass) { + final LifecycleEventFacet facet = adapter.getSpecification().getFacet(lifecycleEventFacetClass); + if(facet != null) { + final Class<? extends AbstractLifecycleEvent<?>> eventType = facet.getEventType(); + final Object instance = InstanceUtil.createInstance(eventType); + final Object pojo = adapter.getObject(); + postEvent((AbstractLifecycleEvent) instance, pojo); + } + } + + // -- HELPER + + private void postEvent(final AbstractLifecycleEvent<Object> event, final Object pojo) { + if(eventBusService!=null) { + event.setSource(pojo); + eventBusService.post(event); + } + } + + +} \ No newline at end of file diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext_ObjectCreation.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext_ObjectCreation.java new file mode 100644 index 0000000..b50a42e --- /dev/null +++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext_ObjectCreation.java @@ -0,0 +1,158 @@ +/* + * 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.runtime.system.persistence.adaptermanager; + +import java.util.List; + +import javax.annotation.Nullable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.apache.isis.applib.events.lifecycle.AbstractLifecycleEvent; +import org.apache.isis.applib.services.command.Command; +import org.apache.isis.applib.services.eventbus.EventBusService; +import org.apache.isis.core.commons.factory.InstanceUtil; +import org.apache.isis.core.metamodel.adapter.ObjectAdapter; +import org.apache.isis.core.metamodel.facets.object.callbacks.CallbackFacet; +import org.apache.isis.core.metamodel.facets.object.callbacks.CreatedCallbackFacet; +import org.apache.isis.core.metamodel.facets.object.callbacks.CreatedLifecycleEventFacet; +import org.apache.isis.core.metamodel.facets.object.callbacks.LifecycleEventFacet; +import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet; +import org.apache.isis.core.metamodel.services.ServicesInjector; +import org.apache.isis.core.metamodel.spec.ObjectSpecification; +import org.apache.isis.core.metamodel.spec.feature.Contributed; +import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation; +import org.apache.isis.core.metamodel.specloader.SpecificationLoader; +import org.apache.isis.core.runtime.system.persistence.PersistenceSession; + +/** + * package private mixin for ObjectAdapterContext + * <p> + * Responsibility: re-creates domain object instances + * </p> + * @since 2.0.0-M2 + */ +@SuppressWarnings("unused") +class ObjectAdapterContext_ObjectCreation { + + + private static final Logger LOG = LoggerFactory.getLogger(ObjectAdapterContext_ObjectCreation.class); + private final ObjectAdapterContext objectAdapterContext; + private final PersistenceSession persistenceSession; + private final ServicesInjector servicesInjector; + private final SpecificationLoader specificationLoader; + + ObjectAdapterContext_ObjectCreation(ObjectAdapterContext objectAdapterContext, + PersistenceSession persistenceSession) { + this.objectAdapterContext = objectAdapterContext; + this.persistenceSession = persistenceSession; + this.servicesInjector = persistenceSession.getServicesInjector(); + this.specificationLoader = servicesInjector.getSpecificationLoader(); + } + + public ObjectAdapter newInstance(ObjectSpecification objectSpec) { + return newInstance(objectSpec, Variant.TRANSIENT, null); + } + + public ObjectAdapter recreateInstance(ObjectSpecification objectSpec, @Nullable String memento) { + return newInstance(objectSpec, Variant.VIEW_MODEL, memento); + } + + // -- HELPER + + private enum Variant { + TRANSIENT, + VIEW_MODEL + } + + private ObjectAdapter newInstance(ObjectSpecification spec, Variant variant, String memento) { + if (LOG.isDebugEnabled()) { + LOG.debug("creating {} instance of {}", variant, spec); + } + final Object pojo; + + if(variant == Variant.VIEW_MODEL) { + pojo = recreateViewModel(spec, memento); + } else { + pojo = objectAdapterContext.instantiateAndInjectServices(spec); + + } + + final ObjectAdapter adapter = objectAdapterContext.getObjectAdapterProvider().adapterFor(pojo); + return initializePropertiesAndDoCallback(adapter); + } + + private Object recreateViewModel(final ObjectSpecification spec, final String memento) { + final ViewModelFacet facet = spec.getFacet(ViewModelFacet.class); + if(facet == null) { + throw new IllegalArgumentException("spec does not have ViewModelFacet; spec is " + spec.getFullIdentifier()); + } + + final Object viewModelPojo; + if(facet.getRecreationMechanism().isInitializes()) { + viewModelPojo = objectAdapterContext.instantiateAndInjectServices(spec); + facet.initialize(viewModelPojo, memento); + } else { + viewModelPojo = facet.instantiate(spec.getCorrespondingClass(), memento); + } + return viewModelPojo; + } + + private ObjectAdapter initializePropertiesAndDoCallback(final ObjectAdapter adapter) { + + // initialize new object + final List<ObjectAssociation> fields = adapter.getSpecification().getAssociations(Contributed.EXCLUDED); + for (ObjectAssociation field : fields) { + field.toDefault(adapter); + } + final Object pojo = adapter.getObject(); + servicesInjector.injectServicesInto(pojo); + + CallbackFacet.Util.callCallback(adapter, CreatedCallbackFacet.class); + + if (Command.class.isAssignableFrom(pojo.getClass())) { + + // special case... the command object is created while the transaction is being started and before + // the event bus service is initialized (nb: we initialize services *within* a transaction). To resolve + // this catch-22 situation, we simply suppress the posting of this event for this domain class. + + // this seems the least unpleasant of the various options available: + // * we could have put a check in the EventBusService to ignore the post if not yet initialized; + // however this might hide other genuine errors + // * we could have used the thread-local in JdoStateManagerForIsis and the "skip(...)" hook in EventBusServiceJdo + // to have this event be skipped; but that seems like co-opting some other design + // * we could have the transaction initialize the EventBusService as a "special case" before creating the Command; + // but then do we worry about it being re-init'd later by the ServicesInitializer? + + // so, doing it this way is this simplest, least obscure. + + if(LOG.isDebugEnabled()) { + LOG.debug("Skipping postEvent for creation of Command pojo"); + } + + } else { + objectAdapterContext.postLifecycleEventIfRequired(adapter, CreatedLifecycleEventFacet.class); + } + + return adapter; + } + + +} \ No newline at end of file diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext_ObjectReCreation.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext_ObjectReCreation.java deleted file mode 100644 index cb276ac..0000000 --- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/adaptermanager/ObjectAdapterContext_ObjectReCreation.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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.runtime.system.persistence.adaptermanager; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet; -import org.apache.isis.core.metamodel.services.ServicesInjector; -import org.apache.isis.core.metamodel.spec.ObjectSpecification; -import org.apache.isis.core.metamodel.specloader.SpecificationLoader; -import org.apache.isis.core.runtime.system.persistence.PersistenceSession; - -/** - * package private mixin for ObjectAdapterContext - * <p> - * Responsibility: re-creates domain object instances - * </p> - * @since 2.0.0-M2 - */ -@SuppressWarnings("unused") -class ObjectAdapterContext_ObjectReCreation { - - - private static final Logger LOG = LoggerFactory.getLogger(ObjectAdapterContext_ObjectReCreation.class); - private final ObjectAdapterContext objectAdapterContext; - private final PersistenceSession persistenceSession; - private final ServicesInjector servicesInjector; - private final SpecificationLoader specificationLoader; - - - ObjectAdapterContext_ObjectReCreation(ObjectAdapterContext objectAdapterContext, - PersistenceSession persistenceSession) { - this.objectAdapterContext = objectAdapterContext; - this.persistenceSession = persistenceSession; - this.servicesInjector = persistenceSession.getServicesInjector(); - this.specificationLoader = servicesInjector.getSpecificationLoader(); - } - - Object recreateViewModel(final ObjectSpecification spec, final String memento) { - final ViewModelFacet facet = spec.getFacet(ViewModelFacet.class); - if(facet == null) { - throw new IllegalArgumentException("spec does not have ViewModelFacet; spec is " + spec.getFullIdentifier()); - } - - final Object viewModelPojo; - if(facet.getRecreationMechanism().isInitializes()) { - viewModelPojo = objectAdapterContext.instantiateAndInjectServices(spec); - facet.initialize(viewModelPojo, memento); - } else { - viewModelPojo = facet.instantiate(spec.getCorrespondingClass(), memento); - } - return viewModelPojo; - } - -} \ No newline at end of file