This is an automated email from the ASF dual-hosted git repository. ahuber pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/master by this push: new 836d8c9669 ISIS-3167: major work on EntityFacets new ae111b0b0d Merge remote-tracking branch 'origin/master' 836d8c9669 is described below commit 836d8c9669c3a34e136d1f5a9767e82ae240ac98 Author: Andi Huber <ahu...@apache.org> AuthorDate: Thu Sep 1 11:01:16 2022 +0200 ISIS-3167: major work on EntityFacets - introducing a new EntityState: NEW - purging legacy ManageObject implementations --- .../applib/services/repository/EntityState.java | 23 ++- .../facets/object/entity/EntityFacet.java | 54 ++++++- .../object/entity/_EntityFacetForTesting.java | 5 +- .../isis/core/metamodel/object/ManagedObject.java | 102 ++++++++---- .../core/metamodel/object/MmAssertionUtil.java | 26 +++ .../isis/core/metamodel/object/MmEntityUtil.java | 82 ++-------- .../isis/core/metamodel/object/MmSpecUtil.java | 47 +++--- .../isis/core/metamodel/object/Refetchable.java | 27 ++++ .../object/_ManagedObjectEntityAttached.java | 156 ++++++++++++++++++ ...tity.java => _ManagedObjectEntityDetached.java} | 56 ++++--- .../object/_ManagedObjectEntityHybrid.java | 144 +++++++++++++++++ .../core/metamodel/object/_ManagedObjectOther.java | 7 +- .../metamodel/object/_ManagedObjectSpecified.java | 24 ++- .../object/_ManagedObjectWithBookmark.java | 174 --------------------- .../object/_ManagedObjectWithEagerSpec.java | 85 ---------- .../metamodel/objectmanager/ObjectManager.java | 44 +----- .../detach/ObjectDetacher_builtinHandlers.java | 20 +-- .../identify/ObjectBookmarker_builtinHandlers.java | 54 +------ .../load/ObjectLoader_builtinHandlers.java | 14 +- .../query/ObjectBulkLoader_builtinHandlers.java | 7 +- .../refresh/ObjectRefresher_builtinHandlers.java | 20 +-- .../isis/core/metamodel/spec/Hierarchical.java | 5 + .../core/metamodel/spec/ObjectSpecification.java | 23 ++- .../specimpl/ObjectSpecificationAbstract.java | 35 +++++ .../title/TitleAnnotationFacetFactoryTest.java | 8 +- .../core/metamodel/object/ManagedObjectTest.java | 4 +- .../command/SchemaValueMarshallerDefault.java | 8 +- .../executor/MemberExecutorServiceDefault.java | 4 +- .../factory/FactoryServiceDefault.java | 8 +- .../repository/RepositoryServiceDefault.java | 2 +- .../handlers/DomainObjectInvocationHandler.java | 3 +- .../xmlsnapshot/XmlSnapshotBuilder.java | 2 +- .../xmlsnapshot/XmlSnapshotServiceDefault.java | 2 +- .../wkt/viewer/EventProviderAbstract.java | 2 +- .../graphql/viewer/source/ObjectTypeFactory.java | 2 +- .../graphql/viewer/source/QueryFieldFactory.java | 7 +- .../viewer/vaadin/ui/binding/BindingsVaa.java | 6 +- .../changetracking/JdoLifecycleListener.java | 29 ++-- .../jdo/datanucleus/changetracking/_Utils.java | 21 +-- .../entities/DnEntityStateProvider.java | 9 +- .../metamodel/facets/entity/JdoEntityFacet.java | 69 ++------ .../jpa/integration/entity/JpaEntityFacet.java | 49 ++---- .../DomainModelTest_usingGoodDomain.java | 4 +- .../testdomain/interact/SimulatedUiChoices.java | 10 +- .../testdomain/interact/SimulatedUiComponent.java | 2 +- .../domainmodel/jdo/DomainModelTest.java | 3 +- .../injecting/jdo/JdoEntityInjectingTest.java | 2 +- .../persistence/jpa/JpaBootstrappingTest.java | 6 +- ...JpaNonGeneratedStringIdEntityLifecycleTest.java | 2 + .../springdata/SpringDataJpaBootstrappingTest.java | 3 +- .../isis/testdomain/value/ValueSemanticsTest.java | 2 +- .../testdomain/value/ValueSemanticsTester.java | 4 +- .../publishing/PublishingTestFactoryJdo.java | 2 +- .../publishing/PublishingTestFactoryJpa.java | 2 +- .../interaction/DomainObjectTesterFactory.java | 9 +- .../binding/BindingConverterForManagedObject.java | 2 +- .../viewer/wicket/model/models/BooleanModel.java | 2 +- .../wicket/model/models/ManagedObjectModel.java | 3 +- .../wicket/model/util/PageParameterUtils.java | 2 +- .../components/tree/IsisToWicketTreeAdapter.java | 2 +- .../viewer/services/DeepLinkServiceWicket.java | 2 +- 61 files changed, 779 insertions(+), 754 deletions(-) diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/repository/EntityState.java b/api/applib/src/main/java/org/apache/isis/applib/services/repository/EntityState.java index 1da34628ae..2ca6ae644b 100644 --- a/api/applib/src/main/java/org/apache/isis/applib/services/repository/EntityState.java +++ b/api/applib/src/main/java/org/apache/isis/applib/services/repository/EntityState.java @@ -38,6 +38,12 @@ public enum EntityState { * the database. */ PERSISTABLE_ATTACHED, + + /** + * DN/JDO specific on pre-store. Is attached, has no OID yet. + */ + PERSISTABLE_NEW, + /** * Object with this state is an entity but that is detached from a * persistence session, in other words changes to the entity will <i>not</i> @@ -65,6 +71,12 @@ public enum EntityState { public boolean isAttached() { return this == PERSISTABLE_ATTACHED; } + /** + * DN/JDO specific on pre-store. Is attached, has no OID yet. + */ + public boolean isNew() { + return this == PERSISTABLE_NEW; + } /** * Object with this state is an entity but that is detached from a * persistence session, in other words changes to the entity will <i>not</i> @@ -95,9 +107,16 @@ public enum EntityState { * @apiNote 'removed' is only supported by JDO. */ public boolean isAttachedOrRemoved() { - return this == PERSISTABLE_ATTACHED - || this == PERSISTABLE_REMOVED; + return isAttached() + || isRemoved(); } + /** + * @apiNote 'new' is only supported by JDO. + */ + public boolean isAttachedOrNew() { + return isAttached() + || isNew(); + } } diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/entity/EntityFacet.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/entity/EntityFacet.java index 548ba3b4f1..f3537fb078 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/entity/EntityFacet.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/entity/EntityFacet.java @@ -19,15 +19,21 @@ package org.apache.isis.core.metamodel.facets.object.entity; import java.lang.reflect.Method; +import java.util.Optional; + +import org.springframework.lang.Nullable; import org.apache.isis.applib.query.Query; import org.apache.isis.applib.services.bookmark.Bookmark; import org.apache.isis.applib.services.repository.EntityState; import org.apache.isis.commons.collections.Can; +import org.apache.isis.commons.internal.exceptions._Exceptions; import org.apache.isis.core.config.beans.PersistenceStack; import org.apache.isis.core.metamodel.facetapi.Facet; import org.apache.isis.core.metamodel.facetapi.FacetHolder; import org.apache.isis.core.metamodel.object.ManagedObject; +import org.apache.isis.core.metamodel.object.MmSpecUtil; +import org.apache.isis.core.metamodel.spec.ObjectSpecification; /** * Indicates that this class is managed by a persistence context. @@ -35,9 +41,51 @@ import org.apache.isis.core.metamodel.object.ManagedObject; */ public interface EntityFacet extends Facet { - String identifierFor(Object pojo); + /** + * The {@link ObjectSpecification} of the entity type this + * facet is associated with. + */ + default ObjectSpecification getEntitySpecification() { + return (ObjectSpecification)getFacetHolder(); + } + + /** + * Optionally the stringified OID, + * based on whether the entity has one associated. + * @throws IllegalArgumentException if the pojo's class is not recognized + * by the persistence layer + */ + Optional<String> identifierFor(@Nullable Object pojo); + + /** + * Optionally the {@link Bookmark}, + * based on whether the entity has an OID associated. + * eg. it has not if not persisted yet + * @throws IllegalArgumentException if the pojo's class is not recognized + * by the persistence layer or does not exactly match the expected + */ + default Optional<Bookmark> bookmarkFor(final @Nullable Object pojo) { + return identifierFor(pojo) + .map(id->Bookmark.forLogicalTypeAndIdentifier( + MmSpecUtil.quicklyResolveObjectSpecificationFor( + getEntitySpecification(), + pojo.getClass()) + .getLogicalType(), + id)); + } + + default Bookmark bookmarkForElseFail(final @Nullable Object pojo) { + return bookmarkFor(pojo) + .orElseThrow(()->_Exceptions.noSuchElement("entity has no OID: %s", + getEntitySpecification().getLogicalType())); + } - ManagedObject fetchByIdentifier(Bookmark bookmark); + + /** + * Optionally the entity pojo corresponding to given {@link Bookmark}, + * based on whether could be found. + */ + Optional<Object> fetchByBookmark(Bookmark bookmark); Can<ManagedObject> fetchByQuery(Query<?> query); @@ -67,4 +115,6 @@ public interface EntityFacet extends Facet { return new _EntityFacetForTesting(persistenceStandard, facetHolder); } + + } diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/entity/_EntityFacetForTesting.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/entity/_EntityFacetForTesting.java index d4d1239f0b..97289b4ea2 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/entity/_EntityFacetForTesting.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/entity/_EntityFacetForTesting.java @@ -19,6 +19,7 @@ package org.apache.isis.core.metamodel.facets.object.entity; import java.lang.reflect.Method; +import java.util.Optional; import java.util.function.BiConsumer; import org.apache.isis.applib.query.Query; @@ -55,12 +56,12 @@ class _EntityFacetForTesting implements EntityFacet { } @Override - public String identifierFor(final Object pojo) { + public Optional<String> identifierFor(final Object pojo) { throw _Exceptions.unsupportedOperation(); } @Override - public ManagedObject fetchByIdentifier(final Bookmark bookmark) { + public Optional<Object> fetchByBookmark(final Bookmark bookmark) { throw _Exceptions.unsupportedOperation(); } diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/ManagedObject.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/ManagedObject.java index df2861b712..8997ef491a 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/ManagedObject.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/ManagedObject.java @@ -24,9 +24,8 @@ import java.util.function.Supplier; import org.springframework.lang.Nullable; import org.apache.isis.applib.services.bookmark.Bookmark; +import org.apache.isis.applib.services.repository.EntityState; import org.apache.isis.commons.collections.Can; -import org.apache.isis.commons.internal.assertions._Assert; -import org.apache.isis.commons.internal.collections._Collections; import org.apache.isis.commons.internal.exceptions._Exceptions; import org.apache.isis.core.metamodel.context.HasMetaModelContext; import org.apache.isis.core.metamodel.facets.object.icon.ObjectIcon; @@ -41,9 +40,9 @@ import lombok.val; import lombok.extern.log4j.Log4j2; /** - * Represents an instance of some element of the meta-model managed by the framework, - * that is <i>Spring</i> managed beans, persistence-stack provided entities, view-models - * or instances of value types. + * Represents an instance of some element of the meta-model recognized by the framework, + * that is <i>Spring</i> managed beans, persistence-stack provided entities, view-models, + * mixins or instances of value types. * * @since 2.0 {@index}} * @@ -325,6 +324,11 @@ extends */ Object getPojo(); + @NonNull + default EntityState getEntityState() { + return EntityState.NOT_PERSISTABLE; + } + /** * If the underlying domain object is a viewmodel, refreshes any referenced entities. * (Acts as a no-op otherwise.) @@ -434,23 +438,48 @@ extends final @Nullable Object pojo, final Optional<Bookmark> bookmarkIfKnown) { return pojo != null - ? bookmarkIfKnown.map(bookmark->bookmarked(spec, pojo, bookmark)) //FIXME - .orElseGet(()->new _ManagedObjectWithEagerSpec(spec, pojo)) //FIXME + ? new _ManagedObjectViewmodel(spec, pojo, bookmarkIfKnown) : empty(spec); } /** * ENTITY * @param spec - required * @param pojo - if <code>null</code> maps to {@link #empty(ObjectSpecification)} + * @param bookmark * @see ManagedObject.Specialization.TypePolicy#EXACT_TYPE_REQUIRED * @see ManagedObject.Specialization.BookmarkPolicy#IMMUTABLE * @see ManagedObject.Specialization.PojoPolicy#REFETCHABLE */ static ManagedObject entity( + final @NonNull ObjectSpecification spec, + final @Nullable Object pojo, + final @NonNull Optional<Bookmark> bookmarkIfKnown) { + if(pojo == null) { + return empty(spec); + } + val bookmarkIfAny = bookmarkIfKnown + .or(()->spec.entityFacetElseFail().bookmarkFor(pojo)); + if(bookmarkIfAny.isPresent()) { + return entityAttached(spec, pojo, bookmarkIfAny); + } else { + return entityDetached(spec, pojo); + } + } + //FIXME java-doc + static ManagedObject entityAttached( + final @NonNull ObjectSpecification spec, + final @NonNull Object pojo, + final @NonNull Optional<Bookmark> bookmarkIfKnown) { + return new _ManagedObjectEntityHybrid( + new _ManagedObjectEntityAttached(spec, pojo, bookmarkIfKnown)); + } + //FIXME java-doc + static ManagedObject entityDetached( final @NonNull ObjectSpecification spec, final @Nullable Object pojo) { return pojo != null - ? new _ManagedObjectWithEagerSpec(spec, pojo) //FIXME + ? new _ManagedObjectEntityHybrid( + new _ManagedObjectEntityDetached(spec, pojo)) : empty(spec); } /** @@ -502,23 +531,44 @@ extends * @param specLoader - required * @param pojo - required, required non-scalar */ - static ManagedObject wrapScalar( + static ManagedObject adaptScalar( final @NonNull SpecificationLoader specLoader, final @NonNull Object pojo) { if(pojo instanceof ManagedObject) { return (ManagedObject)pojo; } val spec = specLoader.specForType(pojo.getClass()).orElse(null); - return wrapScalarInternal(spec, pojo, Optional.empty()); + return adaptScalarInternal(spec, pojo, Optional.empty()); + } + + static ManagedObject adaptScalar( + final @NonNull ObjectSpecification guess, + final @Nullable Object pojo) { + if(pojo instanceof ManagedObject) { + return (ManagedObject)pojo; + } + return adaptScalarInternal(guess, pojo, Optional.empty()); } - private static ManagedObject wrapScalarInternal( - final @Nullable ObjectSpecification spec, + static ManagedObject identified( + final @NonNull ObjectSpecification spec, + final @Nullable Object pojo, + final @NonNull Bookmark bookmark) { + return adaptScalarInternal(spec, pojo, Optional.of(bookmark)); + } + + // -- HELPER + + /** + * spec and pojo don't need to be strictly in sync, we adapt if required + */ + private static ManagedObject adaptScalarInternal( + final @Nullable ObjectSpecification guess, final @NonNull Object pojo, final @NonNull Optional<Bookmark> bookmarkIfAny) { - _Assert.assertTrue(!_Collections.isCollectionOrArrayOrCanType(pojo.getClass()), - ()->String.format("is scalar %s", pojo.getClass())); + MmAssertionUtil.assertPojoIsScalar(pojo); + val spec = MmSpecUtil.quicklyResolveObjectSpecificationFor(guess, pojo.getClass()); val specialization = spec!=null ? Specialization.inferFrom(spec, pojo) @@ -534,7 +584,7 @@ extends case VIEWMODEL: return viewmodel(spec, pojo, bookmarkIfAny); case ENTITY: - return entity(spec, pojo); + return entity(spec, pojo, bookmarkIfAny); case MIXIN: return mixin(spec, pojo); case OTHER: @@ -549,18 +599,6 @@ extends // -- FACTORIES LEGACY - @Deprecated - static ManagedObject notBookmarked( - final ObjectSpecification spec, - final Object pojo) { - if(pojo instanceof ManagedObject) { - return (ManagedObject)pojo; - } - return !_Collections.isCollectionOrArrayOrCanType(pojo.getClass()) - ? wrapScalarInternal(spec, pojo, Optional.empty()) - : new _ManagedObjectWithEagerSpec(spec, pojo); - } - /** * Optimized for cases, when the pojo's specification is already available. * If {@code pojo} is an entity, automatically memoizes its bookmark. @@ -572,7 +610,9 @@ extends final @NonNull ObjectSpecification spec, final @Nullable Object pojo) { - MmAssertionUtil.assertPojoNotWrapped(pojo); + if(pojo instanceof ManagedObject) { + return (ManagedObject)pojo; + } //ISIS-2430 Cannot assume Action Param Spec to be correct when eagerly loaded //actual type in use (during runtime) might be a sub-class of the above, so re-adapt with hinting spec @@ -589,9 +629,7 @@ extends final @NonNull Object pojo, final @NonNull Bookmark bookmark) { - if(pojo!=null) { - _Assert.assertFalse(_Collections.isCollectionOrArrayOrCanType(pojo.getClass())); - } + MmAssertionUtil.assertPojoIsScalar(pojo); if(!spec.getCorrespondingClass().isAssignableFrom(pojo.getClass())) { throw _Exceptions.illegalArgument( @@ -602,7 +640,7 @@ extends spec.getCorrespondingClass(), pojo.getClass(), pojo.toString()); } MmAssertionUtil.assertPojoNotWrapped(pojo); - return _ManagedObjectWithEagerSpec.identified(spec, pojo, bookmark); + return ManagedObject.identified(spec, pojo, bookmark); } } diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmAssertionUtil.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmAssertionUtil.java index b31317dfd3..f32da3b83d 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmAssertionUtil.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmAssertionUtil.java @@ -24,6 +24,7 @@ import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; import org.apache.isis.commons.internal.assertions._Assert; +import org.apache.isis.commons.internal.collections._Collections; import org.apache.isis.commons.internal.exceptions._Exceptions; import org.apache.isis.core.metamodel.commons.ClassUtil; import org.apache.isis.core.metamodel.spec.ObjectSpecification; @@ -51,6 +52,23 @@ public class MmAssertionUtil { + "does not exaclty match %s%n", actualSpec, requiredSpec)); } + /** + * Guard against incompatible type. + */ + public @NonNull UnaryOperator<ObjectSpecification> assertTypeOf( + final @NonNull ObjectSpecification requiredSpec) { + return specUnderInvestigation -> { + _Assert.assertNotNull(specUnderInvestigation); + if(specUnderInvestigation.isOfTypeResolvePrimitive(requiredSpec)) { + return specUnderInvestigation; + } + throw _Exceptions.illegalArgument("Object has incompatible type %s, " + + "must be an instance of %s.", + specUnderInvestigation, + requiredSpec); + }; + } + /** * Guard against incompatible type. */ @@ -87,4 +105,12 @@ public class MmAssertionUtil { } } + public void assertPojoIsScalar(final @Nullable Object pojo) { + if(pojo==null) { + return; + } + _Assert.assertTrue(!_Collections.isCollectionOrArrayOrCanType(pojo.getClass()), + ()->String.format("is scalar %s", pojo.getClass())); + } + } diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmEntityUtil.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmEntityUtil.java index 1305817bb1..715c8b812f 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmEntityUtil.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmEntityUtil.java @@ -24,13 +24,10 @@ import java.util.function.UnaryOperator; import org.springframework.lang.Nullable; import org.apache.isis.applib.services.repository.EntityState; -import org.apache.isis.commons.functional.Try; import org.apache.isis.commons.internal.assertions._Assert; -import org.apache.isis.commons.internal.base._Casts; import org.apache.isis.commons.internal.exceptions._Exceptions; import org.apache.isis.core.config.beans.PersistenceStack; import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet; -import org.apache.isis.core.metamodel.objectmanager.load.ObjectLoader; import lombok.NonNull; import lombok.val; @@ -49,45 +46,28 @@ public final class MmEntityUtil { return Optional.empty(); } - val entityFacet = spec.getFacet(EntityFacet.class); - if(entityFacet==null) { - return Optional.empty(); - } - - return Optional.of(entityFacet.getPersistenceStack()); + return spec.entityFacet() + .map(EntityFacet::getPersistenceStack); } @NonNull public static EntityState getEntityState(final @Nullable ManagedObject adapter) { - if(ManagedObjects.isNullOrUnspecifiedOrEmpty(adapter)) { - return EntityState.NOT_PERSISTABLE; - } - val spec = adapter.getSpecification(); - val pojo = adapter.getPojo(); - - if(!spec.isEntity()) { - return EntityState.NOT_PERSISTABLE; - } - - val entityFacet = spec.getFacet(EntityFacet.class); - if(entityFacet==null) { - throw _Exceptions.unrecoverable("Entity types must have an EntityFacet"); - } - - return entityFacet.getEntityState(pojo); + return adapter!=null + ? adapter.getEntityState() + : EntityState.NOT_PERSISTABLE; } public static void persistInCurrentTransaction(final ManagedObject managedObject) { requiresEntity(managedObject); val spec = managedObject.getSpecification(); - val entityFacet = spec.getFacet(EntityFacet.class); + val entityFacet = spec.entityFacetElseFail(); entityFacet.persist(managedObject.getPojo()); } public static void destroyInCurrentTransaction(final ManagedObject managedObject) { requiresEntity(managedObject); val spec = managedObject.getSpecification(); - val entityFacet = spec.getFacet(EntityFacet.class); + val entityFacet = spec.entityFacetElseFail(); entityFacet.delete(managedObject.getPojo()); } @@ -126,53 +106,8 @@ public final class MmEntityUtil { return managedObject; } + @Deprecated public static ManagedObject refetch(final @Nullable ManagedObject managedObject) { - if(ManagedObjects.isNullOrUnspecifiedOrEmpty(managedObject)) { - return managedObject; - } - if(managedObject instanceof PackedManagedObject) { - ((PackedManagedObject)managedObject).unpack().forEach(MmEntityUtil::refetch); - return managedObject; - } - val entityState = MmEntityUtil.getEntityState(managedObject); - if(!entityState.isPersistable()) { - return managedObject; - } - if(!entityState.isDetached()) { - return managedObject; - } - - val spec = managedObject.getSpecification(); - val objectManager = managedObject.getObjectManager(); - - val reattached = ManagedObjects.bookmark(managedObject) - .map(bookmark-> - ObjectLoader.Request.of( - spec, - bookmark)) - .map(loadRequest->Try.call( - ()->objectManager.loadObject(loadRequest))) - .map(loadResult-> - // a valid scenario for entities: not found eg. after deletion, - // which will fail the load request - loadResult.isFailure() - ? ManagedObject.empty(managedObject.getSpecification()) - : loadResult.getValue().get() - ) - .orElse(managedObject); - - // handles deleted entities - if(ManagedObjects.isNullOrUnspecifiedOrEmpty(reattached)) { - // returns the 'emptied' ManagedObject from above - return reattached; - } - - val newState = MmEntityUtil.getEntityState(reattached); - _Assert.assertTrue(newState.isAttached()); - - _Casts.castTo(_ManagedObjectWithBookmark.class, managedObject) - .ifPresent(obj->obj.replacePojo(old->reattached.getPojo())); - return managedObject; } @@ -236,4 +171,5 @@ public final class MmEntityUtil { return adapter; } + } \ No newline at end of file diff --git a/viewers/commons/model/src/main/java/org/apache/isis/viewer/commons/model/binding/BindingConverterForManagedObject.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmSpecUtil.java similarity index 53% copy from viewers/commons/model/src/main/java/org/apache/isis/viewer/commons/model/binding/BindingConverterForManagedObject.java copy to core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmSpecUtil.java index 06b02e91c7..f3d40188eb 100644 --- a/viewers/commons/model/src/main/java/org/apache/isis/viewer/commons/model/binding/BindingConverterForManagedObject.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/MmSpecUtil.java @@ -16,34 +16,29 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.isis.viewer.commons.model.binding; +package org.apache.isis.core.metamodel.object; -import org.apache.isis.commons.internal.base._Casts; -import org.apache.isis.core.metamodel.object.ManagedObject; -import org.apache.isis.core.metamodel.object.MmUnwrapUtil; import org.apache.isis.core.metamodel.spec.ObjectSpecification; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor(staticName = "of") -public final class BindingConverterForManagedObject<T> -implements BindingConverter<ManagedObject, T> { - - @Getter private final ObjectSpecification valueSpecification; - - @Override - public ManagedObject toLeft(final T pojo) { - return ManagedObject.of(getValueSpecification(), pojo); - } - - @Override - public T toRight(final ManagedObject adapter) { - return _Casts.uncheckedCast(MmUnwrapUtil.single(adapter)); +import lombok.NonNull; +import lombok.experimental.UtilityClass; + +@UtilityClass +public final class MmSpecUtil { + + /** + * optimized for the case when a specification that probably matches is known in advance + * the result must be an instance of guess + */ + public ObjectSpecification quicklyResolveObjectSpecificationFor( + final @NonNull ObjectSpecification guess, + final @NonNull Class<?> requiredType) { + return guess.getCorrespondingClass().equals(requiredType) + // when successful guess + ? guess + // else lookup + : MmAssertionUtil.assertTypeOf(guess) + .apply(guess.getSpecificationLoader().specForTypeElseFail(requiredType)); } -} - - - - +} \ No newline at end of file diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/Refetchable.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/Refetchable.java new file mode 100644 index 0000000000..f00463b887 --- /dev/null +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/Refetchable.java @@ -0,0 +1,27 @@ +/* + * 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.object; + +/** don't expose outside this package */ +interface Refetchable { + + /** side-effect free for toString, equals and hashCode */ + Object peekAtPojo(); + +} diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEntityAttached.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEntityAttached.java new file mode 100644 index 0000000000..238e30a829 --- /dev/null +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEntityAttached.java @@ -0,0 +1,156 @@ +/* + * 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.object; + +import java.util.Optional; +import java.util.function.Supplier; + +import org.springframework.lang.Nullable; + +import org.apache.isis.applib.exceptions.unrecoverable.ObjectNotFoundException; +import org.apache.isis.applib.services.bookmark.Bookmark; +import org.apache.isis.applib.services.repository.EntityState; +import org.apache.isis.commons.internal.debug._Debug; +import org.apache.isis.commons.internal.debug.xray.XrayUi; +import org.apache.isis.commons.internal.exceptions._Exceptions; +import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet; +import org.apache.isis.core.metamodel.spec.ObjectSpecification; + +import lombok.NonNull; +import lombok.val; +import lombok.extern.log4j.Log4j2; + +/** + * (package private) specialization corresponding to a attached {@link Specialization#ENTITY} + * @see ManagedObject.Specialization#ENTITY + */ +@Log4j2 +final class _ManagedObjectEntityAttached +extends _ManagedObjectSpecified +implements Refetchable { + + private /*final*/ @Nullable Object pojo; + private final @NonNull Bookmark bookmark; + + _ManagedObjectEntityAttached( + final ObjectSpecification spec, + final Object pojo, + final @NonNull Optional<Bookmark> bookmarkIfKnown) { + super(ManagedObject.Specialization.ENTITY, spec); + this.pojo = assertCompliance(pojo); + this.bookmark = bookmarkIfKnown + .orElseGet(this::createBookmark); + } + + @Override + public Optional<Bookmark> getBookmark() { + return Optional.of(bookmark); + } + + @Override + public Optional<Bookmark> getBookmarkRefreshed() { + return getBookmark(); // no-op for entities + } + + @Override + public boolean isBookmarkMemoized() { + return true; + } + + @Override + public void refreshViewmodel(final Supplier<Bookmark> bookmarkSupplier) { + // no-op for entities + } + + @Override + public Object peekAtPojo() { + return pojo; + } + + @Override + public Object getPojo() { + + // refetch if required ... + + val entityFacet = entityFacet(); + + val entityState = entityFacet.getEntityState(pojo); + if(!entityState.isPersistable()) { + throw _Exceptions.illegalState("not persistable %s", getSpecification()); + } + if(!entityState.isDetached()) { + return pojo; // is attached + } + + // throws on deleted entity + val reattached = entityFacet.fetchByBookmark(bookmark) + .orElseThrow(()->{ + return new ObjectNotFoundException(""+bookmark);}); + + if(!entityFacet.getEntityState(reattached).isAttached()) { + throw _Exceptions.illegalState("entity not attached after refetch attempt %s", bookmark); + } + + return this.pojo = assertCompliance(reattached); + } + + @Override + public @NonNull EntityState getEntityState() { + val entityFacet = entityFacet(); + return entityFacet.getEntityState(pojo); + } + + // -- HELPER + + private EntityFacet entityFacet() { + return getSpecification().entityFacetElseFail(); + } + + @SuppressWarnings("deprecation") + private Bookmark createBookmark() { + val entityFacet = entityFacet(); + + // fail early when detached entities are detected + // should have been re-fetched at start of this request-cycle + if( +// && EntityUtil.getPersistenceStandard(managedObject) +// .map(PersistenceStandard::isJdo) +// .orElse(false) + !entityFacet.getEntityState(pojo).isAttached()) { + + _Debug.onCondition(XrayUi.isXrayEnabled(), ()->{ + _Debug.log("detached entity detected %s", pojo); + }); + + val msg = String.format( + "The persistence layer does not recognize given object %s, " + + "meaning the object has no identifier that associates it with the persistence layer. " + + "(most likely, because the object is detached, eg. was not persisted after being new-ed up)", + getSpecification()); + + // in case of the exception getting swallowed, also write a log + log.error(msg); + + throw _Exceptions.illegalArgument(msg); + } + + return entityFacet.bookmarkForElseFail(pojo); + } + +} \ No newline at end of file diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEntity.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEntityDetached.java similarity index 57% rename from core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEntity.java rename to core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEntityDetached.java index 91b8c9192c..299c14fd29 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEntity.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEntityDetached.java @@ -18,65 +18,63 @@ */ package org.apache.isis.core.metamodel.object; -import java.util.Optional; import java.util.function.Supplier; -import org.springframework.lang.Nullable; - import org.apache.isis.applib.services.bookmark.Bookmark; +import org.apache.isis.applib.services.repository.EntityState; +import org.apache.isis.commons.internal.assertions._Assert; +import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet; import org.apache.isis.core.metamodel.spec.ObjectSpecification; +import lombok.Getter; import lombok.NonNull; +import lombok.val; +import lombok.experimental.Accessors; /** - * (package private) specialization corresponding to {@link Specialization#ENTITY} + * (package private) specialization corresponding to a detached {@link Specialization#ENTITY} * @see ManagedObject.Specialization#ENTITY */ -final class _ManagedObjectEntity -extends _ManagedObjectSpecified { +final class _ManagedObjectEntityDetached +extends _ManagedObjectSpecified +implements Bookmarkable.NoBookmark, Refetchable { - private /*final*/ @Nullable Object pojo; - private final @NonNull Bookmark bookmark; + @Getter(onMethod_ = {@Override}) @Accessors(makeFinal = true) + private final @NonNull Object pojo; - _ManagedObjectEntity( + _ManagedObjectEntityDetached( final ObjectSpecification spec, - final Object pojo, - final @NonNull Bookmark bookmark) { + final Object pojo) { super(ManagedObject.Specialization.ENTITY, spec); + _Assert.assertTrue(spec.isEntity()); this.pojo = assertCompliance(pojo); - this.bookmark = bookmark; } @Override - public Optional<Bookmark> getBookmark() { - return Optional.of(bookmark); - } - - @Override - public Optional<Bookmark> getBookmarkRefreshed() { - return getBookmark(); // no-op for entities + public void refreshViewmodel(final Supplier<Bookmark> bookmarkSupplier) { + // no-op for other } @Override - public boolean isBookmarkMemoized() { - return true; + public String getTitle() { + return "detached entity object"; } @Override - public void refreshViewmodel(final Supplier<Bookmark> bookmarkSupplier) { - // no-op for entities + public Object peekAtPojo() { + return pojo; } @Override - public Object getPojo() { - // TODO refetch if required - return pojo; + public @NonNull EntityState getEntityState() { + val entityFacet = entityFacet(); + return entityFacet.getEntityState(pojo); } // -- HELPER -// private EntityFacet entityFacet() { -// return getSpecification().entityFacet().orElseThrow(); -// } + private EntityFacet entityFacet() { + return getSpecification().entityFacetElseFail(); + } } \ No newline at end of file diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEntityHybrid.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEntityHybrid.java new file mode 100644 index 0000000000..31da98859f --- /dev/null +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectEntityHybrid.java @@ -0,0 +1,144 @@ +/* + * 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.object; + +import java.util.Optional; +import java.util.function.Supplier; + +import org.apache.isis.applib.services.bookmark.Bookmark; +import org.apache.isis.applib.services.repository.EntityState; +import org.apache.isis.commons.functional.Either; +import org.apache.isis.commons.internal.assertions._Assert; + +import lombok.NonNull; +import lombok.Synchronized; +import lombok.val; +import lombok.extern.log4j.Log4j2; + +/** + * (package private) specialization corresponding to {@link Specialization#ENTITY} + * @see ManagedObject.Specialization#ENTITY + */ +@Log4j2 +final class _ManagedObjectEntityHybrid +extends _ManagedObjectSpecified +implements Refetchable { + + /** + * dynamically mutates from one to the other based on pojos persistent state; + * however the pojo reference must be kept identical + */ + private @NonNull Either<_ManagedObjectEntityDetached, _ManagedObjectEntityAttached> + eitherDetachedOrAttached; + + private EntityState entityState; + + _ManagedObjectEntityHybrid( + final @NonNull _ManagedObjectEntityDetached detached) { + super(ManagedObject.Specialization.ENTITY, detached.getSpecification()); + this.eitherDetachedOrAttached = Either.left(detached); + this.entityState = EntityState.PERSISTABLE_DETACHED; + } + + _ManagedObjectEntityHybrid( + final @NonNull _ManagedObjectEntityAttached attached) { + super(ManagedObject.Specialization.ENTITY, attached.getSpecification()); + this.eitherDetachedOrAttached = Either.right(attached); + this.entityState = EntityState.PERSISTABLE_ATTACHED; + } + + @Override + public Optional<Bookmark> getBookmark() { + return eitherDetachedOrAttached + .fold(ManagedObject::getBookmark, ManagedObject::getBookmark); + } + + @Override + public Optional<Bookmark> getBookmarkRefreshed() { + return eitherDetachedOrAttached + .fold(ManagedObject::getBookmarkRefreshed, ManagedObject::getBookmarkRefreshed); + } + + @Override + public boolean isBookmarkMemoized() { + return eitherDetachedOrAttached + .fold(ManagedObject::isBookmarkMemoized, ManagedObject::isBookmarkMemoized); + } + + @Override + public @NonNull EntityState getEntityState() { + + val entityState = eitherDetachedOrAttached + .fold(ManagedObject::getEntityState, ManagedObject::getEntityState); + + if(this.entityState!=entityState) { + log.debug("about to morph {} -> {}", this.entityState, entityState); + this.entityState = entityState; + reassessVariant(entityState, peekAtPojo()); + if(entityState.isAttached()) { + _Assert.assertTrue(eitherDetachedOrAttached.isRight()); + } else { + _Assert.assertTrue(eitherDetachedOrAttached.isLeft()); + } + } + + return entityState; + } + + @Override + public void refreshViewmodel(final Supplier<Bookmark> bookmarkSupplier) { + // no-op for entities + } + + @Override + public Object getPojo() { + val pojo = eitherDetachedOrAttached + .fold(ManagedObject::getPojo, ManagedObject::getPojo); + getEntityState(); // triggers reassessment + return pojo; + } + + // -- HELPER + + @Override + public Object peekAtPojo() { + return eitherDetachedOrAttached + .fold(Refetchable::peekAtPojo, Refetchable::peekAtPojo); + } + + @Synchronized + private void reassessVariant(final EntityState entityState, final Object pojo) { + if(eitherDetachedOrAttached.isLeft() + && entityState.isAttached()) { + // morph into attached + val bookmark = getSpecification().entityFacetElseFail().bookmarkFor(pojo); + eitherDetachedOrAttached = Either.right( + new _ManagedObjectEntityAttached(getSpecification(), pojo, bookmark)); + return; + } + if(eitherDetachedOrAttached.isRight() + && !entityState.isAttached()) { + // morph into detached + eitherDetachedOrAttached = Either.left( + new _ManagedObjectEntityDetached(getSpecification(), pojo)); + return; + } + } + +} \ No newline at end of file diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectOther.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectOther.java index 4618fad95a..17f71f1aeb 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectOther.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectOther.java @@ -21,6 +21,7 @@ package org.apache.isis.core.metamodel.object; import java.util.function.Supplier; import org.apache.isis.applib.services.bookmark.Bookmark; +import org.apache.isis.commons.internal.assertions._Assert; import org.apache.isis.core.metamodel.spec.ObjectSpecification; import lombok.Getter; @@ -42,9 +43,9 @@ implements Bookmarkable.NoBookmark { final ObjectSpecification spec, final Object pojo) { super(ManagedObject.Specialization.OTHER, spec); - //_Assert.assertTrue(spec.isOther()); //TODO later - //this.pojo = assertCompliance(pojo); //TODO later - this.pojo = pojo; + _Assert.assertTrue(!spec.isValue()); + _Assert.assertTrue(!spec.isEntityOrViewModel()); + this.pojo = assertCompliance(pojo); } @Override diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectSpecified.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectSpecified.java index 65d1d0871c..76a9f9efe7 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectSpecified.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectSpecified.java @@ -67,6 +67,9 @@ implements ManagedObject { if(specialization.getTypePolicy().isExactTypeRequired()) { MmAssertionUtil.assertExactType(specification, pojo); } + if(getSpecification().isEntityOrViewModel()) { + getServiceInjector().injectServicesInto(pojo); // might be redundant + } return pojo; } @@ -94,16 +97,22 @@ implements ManagedObject { if(!this.getSpecification().equals(other.getSpecification())) { return false; } - val canGetPojosWithoutSideeffect = !this.getSpecialization().getPojoPolicy().isRefetchable(); + val canGetPojosWithoutSideeffect = !getSpecialization().getPojoPolicy().isRefetchable(); if(canGetPojosWithoutSideeffect) { // expected to work for packed variant just fine, as it compares lists return Objects.equals(this.getPojo(), other.getPojo()); } - // objects are considered equal if their bookmarks match - _Assert.assertTrue(other.isBookmarkMemoized()); // guarantee no side-effects on other - return Objects.equals( - sideEffectFreeBookmark(), - other.getBookmark().orElseThrow(_Exceptions::unexpectedCodeReach)); + + if(this.isBookmarkMemoized() + && other.isBookmarkMemoized()) { + return Objects.equals( + sideEffectFreeBookmark(), + other.getBookmark().orElseThrow(_Exceptions::unexpectedCodeReach)); + } + + val a = (Refetchable) this; + val b = (Refetchable) this; + return Objects.equals(a.peekAtPojo(), b.peekAtPojo()); } @Override @@ -124,7 +133,7 @@ implements ManagedObject { getSpecification(), !getSpecialization().getPojoPolicy().isRefetchable() ? getPojo() // its safe to get pojo side-effect free - : !getSpecialization().getBookmarkPolicy().isNoBookmark() + : isBookmarkMemoized() ? String.format("(refetchable, %s)", sideEffectFreeBookmark()) : "(refetchable, suppressed to not cause side effects)"); } @@ -132,7 +141,6 @@ implements ManagedObject { // -- HELPER private Bookmark sideEffectFreeBookmark() { - _Assert.assertFalse(getSpecialization().getBookmarkPolicy().isNoBookmark()); _Assert.assertTrue(isBookmarkMemoized()); return getBookmark().orElseThrow(_Exceptions::unexpectedCodeReach); } diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectWithBookmark.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectWithBookmark.java deleted file mode 100644 index b928865fde..0000000000 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectWithBookmark.java +++ /dev/null @@ -1,174 +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.metamodel.object; - -import java.util.Objects; -import java.util.Optional; -import java.util.UUID; -import java.util.function.Supplier; -import java.util.function.UnaryOperator; - -import org.springframework.lang.Nullable; - -import org.apache.isis.applib.services.bookmark.Bookmark; -import org.apache.isis.commons.internal.base._Lazy; -import org.apache.isis.commons.internal.debug._XrayEvent; -import org.apache.isis.core.metamodel.context.MetaModelContext; -import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet; -import org.apache.isis.core.metamodel.spec.ObjectSpecification; - -import lombok.NonNull; -import lombok.val; - -abstract class _ManagedObjectWithBookmark -extends _ManagedObjectSpecifiedLegacy { - - protected final _Lazy<Optional<Bookmark>> bookmarkLazy = - _Lazy.threadSafe(()->bookmark(this)); - - protected _ManagedObjectWithBookmark(final Specialization specialization) { - super(specialization); - } - - @Override - public final Optional<Bookmark> getBookmark() { - return bookmarkLazy.get(); - } - - @Override - public final boolean isBookmarkMemoized() { - return bookmarkLazy.isMemoized(); - } - - @Override - public final Optional<Bookmark> getBookmarkRefreshed() { - // silently ignore invalidation, when the pojo is an entity - if(!getSpecification().isEntity()) { - bookmarkLazy.clear(); - } - return getBookmark(); - } - - private void replaceBookmark(final UnaryOperator<Bookmark> replacer) { - final Bookmark old = bookmarkLazy.isMemoized() - ? bookmarkLazy.get().orElse(null) - : null; - bookmarkLazy.clear(); - bookmarkLazy.set(Optional.ofNullable(replacer.apply(old))); - } - - // guards against non-identifiable objects; - // historically, we allowed non-identifiable to be handled by the objectManager, - // which as a fallback creates 'random' UUIDs - private Optional<Bookmark> bookmark(final @Nullable ManagedObject adapter) { - - if(ManagedObjects.isNullOrUnspecifiedOrEmpty(adapter) - || adapter.getSpecification().isValue() - || !ManagedObjects.isIdentifiable(adapter)) { - return Optional.empty(); - } - - return ManagedObjects.spec(adapter) - .map(ObjectSpecification::getMetaModelContext) - .map(MetaModelContext::getObjectManager) - .map(objectManager->objectManager.bookmarkObject(adapter)); - } - - // -- REFRESH OPTIMIZATION - - private UUID interactionIdDuringWhichRefreshed = null; - - @Override - public final void refreshViewmodel(final @Nullable Supplier<Bookmark> bookmarkSupplier) { - val spec = getSpecification(); - if(spec.isViewModel()) { - val viewModelFacet = spec.getFacet(ViewModelFacet.class); - if(viewModelFacet.containsEntities()) { - - val shouldRefresh = spec.getMetaModelContext().getInteractionProvider().getInteractionId() - .map(this::shouldRefresh) - .orElse(true); // if there is no current interaction, refresh regardless; unexpected state, might fail later - - if(!shouldRefresh) { - return; - } - - if(isBookmarkMemoized()) { - reloadViewmodelFromMemoizedBookmark(); - } else { - val bookmark = bookmarkSupplier!=null - ? bookmarkSupplier.get() - : null; - if(bookmark!=null) { - reloadViewmodelFromBookmark(bookmark); - } - } - } - } - } - - private boolean shouldRefresh(final @NonNull UUID interactionId) { - if(Objects.equals(this.interactionIdDuringWhichRefreshed, interactionId)) { - return false; // already refreshed within current interaction - } - this.interactionIdDuringWhichRefreshed = interactionId; - return true; - } - - /** - * Reload current viewmodel object from memoized bookmark, otherwise does nothing. - */ - private void reloadViewmodelFromMemoizedBookmark() { - val spec = getSpecification(); - if(isBookmarkMemoized() - && spec.isViewModel()) { - - val bookmark = getBookmark().get(); - val viewModelClass = spec.getCorrespondingClass(); - - val recreatedViewmodel = - getMetaModelContext().getFactoryService().viewModel(viewModelClass, bookmark); - - _XrayEvent.event("Viewmodel '%s' recreated from memoized bookmark.", viewModelClass.getName()); - - replacePojo(old->recreatedViewmodel); - } - } - - private void reloadViewmodelFromBookmark(final @NonNull Bookmark bookmark) { - val spec = getSpecification(); - if(spec.isViewModel()) { - val viewModelClass = spec.getCorrespondingClass(); - - val recreatedViewmodel = - getMetaModelContext().getFactoryService().viewModel(viewModelClass, bookmark); - - _XrayEvent.event("Viewmodel '%s' recreated from provided bookmark.", viewModelClass.getName()); - - replacePojo(old->recreatedViewmodel); - replaceBookmark(old->bookmark); - } - } - - /** - * Introduced, so we can re-fetch detached entity pojos in place. - */ - abstract void replacePojo(UnaryOperator<Object> replacer); - -} \ No newline at end of file diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectWithEagerSpec.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectWithEagerSpec.java deleted file mode 100644 index fdf49ed4f3..0000000000 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/object/_ManagedObjectWithEagerSpec.java +++ /dev/null @@ -1,85 +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.metamodel.object; - -import java.util.Optional; -import java.util.function.UnaryOperator; - -import org.springframework.lang.Nullable; - -import org.apache.isis.applib.services.bookmark.Bookmark; -import org.apache.isis.commons.internal.assertions._Assert; -import org.apache.isis.commons.internal.collections._Collections; -import org.apache.isis.core.metamodel.facets.object.title.TitleRenderRequest; -import org.apache.isis.core.metamodel.spec.ObjectSpecification; - -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NonNull; -import lombok.ToString; -import lombok.val; - -@Deprecated -@EqualsAndHashCode(of = "pojo", callSuper = false) -@ToString(of = {"specification", "pojo"}) //ISIS-2317 make sure toString() is without side-effects -@Getter -final class _ManagedObjectWithEagerSpec -extends _ManagedObjectWithBookmark { - - public static ManagedObject identified( - final @NonNull ObjectSpecification spec, - final @Nullable Object pojo, - final @NonNull Bookmark bookmark) { - - if(pojo!=null) { - _Assert.assertFalse(_Collections.isCollectionOrArrayOrCanType(pojo.getClass())); - } - - val managedObject = new _ManagedObjectWithEagerSpec(spec, pojo); - managedObject.bookmarkLazy.set(Optional.of(bookmark)); - return managedObject; - } - - _ManagedObjectWithEagerSpec( - final ObjectSpecification spec, - final Object pojo) { - super(ManagedObject.Specialization.inferFrom(spec, pojo)); - //_Assert.assertFalse(getSpecialization().isValue()); // VALUE already migrated - _Assert.assertFalse(getSpecialization().isService()); // SERVICE already migrated - _Assert.assertFalse(getSpecialization().isMixin()); // MIXIN already migrated - - this.specification = spec; - this.pojo = pojo; - } - - @NonNull private final ObjectSpecification specification; - @Nullable private /*final*/ Object pojo; - - @Override - public void replacePojo(final UnaryOperator<Object> replacer) { - pojo = assertCompliance(replacer.apply(pojo)); - } - - @Override - public final String getTitle() { - return _InternalTitleUtil.titleString( - TitleRenderRequest.forObject(this)); - } - -} \ No newline at end of file diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectManager.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectManager.java index 07b9b73210..165b141cfa 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectManager.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/ObjectManager.java @@ -27,7 +27,6 @@ import org.apache.isis.applib.services.bookmark.Bookmark; import org.apache.isis.commons.collections.Can; import org.apache.isis.commons.internal.base._NullSafe; import org.apache.isis.core.metamodel.context.MetaModelContext; -import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet; import org.apache.isis.core.metamodel.object.ManagedObject; import org.apache.isis.core.metamodel.objectmanager.create.ObjectCreator; import org.apache.isis.core.metamodel.objectmanager.detach.ObjectDetacher; @@ -115,26 +114,12 @@ public interface ObjectManager { // -- ADAPTING POJOS - enum EntityAdaptingMode { - MEMOIZE_BOOKMARK, - SKIP_MEMOIZATION; - public boolean isMemoize() { return this == MEMOIZE_BOOKMARK;} - } - /** * Not suitable for adapting a non-scalar * If {@code pojo} is an entity, automatically memoizes its bookmark. */ public default ManagedObject adapt(final @Nullable Object pojo) { - return adapt(pojo, ()->specForType(Object.class).orElseThrow(), EntityAdaptingMode.MEMOIZE_BOOKMARK); - } - - /** - * Not suitable for adapting a non-scalar. - * If {@code pojo} is an entity, memoizes its bookmark based on policy {@code bookmarking}. - */ - public default ManagedObject adapt(final @Nullable Object pojo, final EntityAdaptingMode bookmarking) { - return adapt(pojo, ()->specForType(Object.class).orElseThrow(), bookmarking); + return adapt(pojo, ()->specForType(Object.class).orElseThrow()); } /** @@ -143,8 +128,7 @@ public interface ObjectManager { */ public default ManagedObject adapt( final @Nullable Object pojo, - final @NonNull Supplier<ObjectSpecification> fallbackElementType, - final EntityAdaptingMode entityAdaptingMode) { + final @NonNull Supplier<ObjectSpecification> fallbackElementType) { if(pojo==null) { return ManagedObject.unspecified(); } @@ -158,11 +142,11 @@ public interface ObjectManager { return ManagedObject.unspecified(); } return spec.isScalar() - ? managedObjectEagerlyBookmarkedIfRequired(spec, pojo, entityAdaptingMode) + ? managedObjectEagerlyBookmarkedIfRequired(spec, pojo) : ManagedObject.packed( spec.getElementSpecification().orElseGet(fallbackElementType), _NullSafe.streamAutodetect(pojo) - .map(element->adapt(element, entityAdaptingMode)) + .map(element->adapt(element)) .collect(Can.toCan())); } @@ -190,7 +174,7 @@ public interface ObjectManager { // if actual type matches spec's, we assume, that we don't need to reload, // so this is a shortcut for performance reasons ? managedObjectEagerlyBookmarkedIfRequired( - proposedSpec, pojo, EntityAdaptingMode.MEMOIZE_BOOKMARK) + proposedSpec, pojo) // fallback, ignoring proposedSpec : adapt(pojo); return adapter; @@ -200,26 +184,14 @@ public interface ObjectManager { /** * {@link ManagedObject} factory, that in case of given pojo representing an entity - * and the entityAdaptingMode equals {@link EntityAdaptingMode#isMemoize()}, + * and the entityAdaptingMode equals {@link EntityAdaptingMode#isBookmarkable()}, * then tries to memoize its {@link Bookmark} eagerly * (otherwise its {@link Bookmark} is lazily resolved). */ private static ManagedObject managedObjectEagerlyBookmarkedIfRequired( final ObjectSpecification spec, - final Object pojo, - final EntityAdaptingMode entityAdaptingMode) { - - if(entityAdaptingMode.isMemoize() - && spec.isEntity()) { - val entityFacet = spec.getFacet(EntityFacet.class); - val state = entityFacet.getEntityState(pojo); - if(state.isAttached()) { - val id = entityFacet.identifierFor(pojo); - val bookmark = Bookmark.forLogicalTypeAndIdentifier(spec.getLogicalType(), id); - return ManagedObject.bookmarked(spec, pojo, bookmark); - } - } - return ManagedObject.notBookmarked(spec, pojo); + final Object pojo) { + return ManagedObject.adaptScalar(spec, pojo); } } diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/detach/ObjectDetacher_builtinHandlers.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/detach/ObjectDetacher_builtinHandlers.java index 39b9b71b53..b20094fd28 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/detach/ObjectDetacher_builtinHandlers.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/detach/ObjectDetacher_builtinHandlers.java @@ -18,9 +18,7 @@ */ package org.apache.isis.core.metamodel.objectmanager.detach; -import org.apache.isis.commons.internal.exceptions._Exceptions; import org.apache.isis.core.metamodel.context.MetaModelContext; -import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet; import org.apache.isis.core.metamodel.object.ManagedObject; import lombok.Data; @@ -41,7 +39,7 @@ final class ObjectDetacher_builtinHandlers { private MetaModelContext metaModelContext; @Override - public boolean isHandling(ManagedObject managedObject) { + public boolean isHandling(final ManagedObject managedObject) { if(managedObject==null || managedObject.getPojo()==null) { return true; @@ -51,7 +49,7 @@ final class ObjectDetacher_builtinHandlers { } @Override - public ManagedObject handle(ManagedObject managedObject) { + public ManagedObject handle(final ManagedObject managedObject) { return null; // noop } @@ -63,20 +61,16 @@ final class ObjectDetacher_builtinHandlers { private final MetaModelContext metaModelContext; @Override - public boolean isHandling(ManagedObject request) { + public boolean isHandling(final ManagedObject request) { val spec = request.getSpecification(); return spec.isEntity(); } @Override - public ManagedObject handle(ManagedObject request) { + public ManagedObject handle(final ManagedObject request) { val spec = request.getSpecification(); - val entityFacet = spec.getFacet(EntityFacet.class); - if(entityFacet==null) { - throw _Exceptions.illegalArgument( - "ObjectSpecification is missing an EntityFacet: %s", spec); - } + val entityFacet = spec.entityFacetElseFail(); Object detachedPojo = entityFacet.detach(request.getPojo()); @@ -92,13 +86,13 @@ final class ObjectDetacher_builtinHandlers { public static class DetachOther implements ObjectDetacher.Handler { @Override - public boolean isHandling(ManagedObject request) { + public boolean isHandling(final ManagedObject request) { // if no one else feels responsible, we do return true; } @Override - public ManagedObject handle(ManagedObject request) { + public ManagedObject handle(final ManagedObject request) { return request; } diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/identify/ObjectBookmarker_builtinHandlers.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/identify/ObjectBookmarker_builtinHandlers.java index ef74f4bff2..9130d1b88d 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/identify/ObjectBookmarker_builtinHandlers.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/identify/ObjectBookmarker_builtinHandlers.java @@ -26,10 +26,7 @@ import org.apache.isis.applib.services.bookmark.Oid; import org.apache.isis.applib.value.semantics.ValueSemanticsProvider; import org.apache.isis.commons.internal.base._Bytes; import org.apache.isis.commons.internal.base._Strings; -import org.apache.isis.commons.internal.debug._Debug; -import org.apache.isis.commons.internal.debug.xray.XrayUi; import org.apache.isis.commons.internal.exceptions._Exceptions; -import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet; import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet; import org.apache.isis.core.metamodel.object.ManagedObject; import org.apache.isis.core.metamodel.object.PackedManagedObject; @@ -37,7 +34,6 @@ import org.apache.isis.core.metamodel.objectmanager.identify.ObjectBookmarker.Ha import lombok.SneakyThrows; import lombok.val; -import lombok.extern.log4j.Log4j2; class ObjectBookmarker_builtinHandlers { @@ -63,67 +59,25 @@ class ObjectBookmarker_builtinHandlers { @Override public boolean isHandling(final ManagedObject managedObject) { - return managedObject.getSpecification().isInjectable(); + return managedObject.getSpecialization().isService(); } @Override public Bookmark handle(final ManagedObject managedObject) { - final String identifier = SERVICE_IDENTIFIER; - return Bookmark.forLogicalTypeAndIdentifier( - managedObject.getSpecification().getLogicalType(), - identifier); + return managedObject.getBookmark().orElseThrow(); } - } - @Log4j2 static class BookmarkForEntities implements Handler { @Override public boolean isHandling(final ManagedObject managedObject) { - return managedObject.getSpecification().isEntity(); + return managedObject.getSpecialization().isEntity(); } @Override public Bookmark handle(final ManagedObject managedObject) { - val spec = managedObject.getSpecification(); - val entityPojo = managedObject.getPojo(); - if(entityPojo==null) { - val msg = String.format("entity '%s' is null, cannot identify", managedObject); - throw _Exceptions.unrecoverable(msg); - } - val entityFacet = spec.getFacet(EntityFacet.class); - if(entityFacet==null) { - val msg = String.format("entity '%s' has no EntityFacet associated", managedObject); - throw _Exceptions.unrecoverable(msg); - } - - // fail early when detached entities are detected - // should have been re-fetched at start of this request-cycle - if(!managedObject.isBookmarkMemoized() -// && EntityUtil.getPersistenceStandard(managedObject) -// .map(PersistenceStandard::isJdo) -// .orElse(false) - && !entityFacet.getEntityState(entityPojo).isAttached()) { - - _Debug.onCondition(XrayUi.isXrayEnabled(), ()->{ - _Debug.log("detached entity detected %s", entityPojo); - }); - - val msg = String.format( - "The persistence layer does not recognize given object %s, " - + "meaning the object has no identifier that associates it with the persistence layer. " - + "(most likely, because the object is detached, eg. was not persisted after being new-ed up)", - managedObject); - - // in case of the exception getting swallowed, also write a log - log.error(msg); - - throw _Exceptions.illegalArgument(msg); - } - - val identifier = entityFacet.identifierFor(entityPojo); - return Bookmark.forLogicalTypeAndIdentifier(spec.getLogicalType(), identifier); + return managedObject.getBookmark().orElseThrow(); } } diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/load/ObjectLoader_builtinHandlers.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/load/ObjectLoader_builtinHandlers.java index 88581e4e1b..4b0116eb1f 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/load/ObjectLoader_builtinHandlers.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/load/ObjectLoader_builtinHandlers.java @@ -24,7 +24,6 @@ import org.apache.isis.commons.collections.Can; import org.apache.isis.commons.internal.exceptions._Exceptions; import org.apache.isis.commons.internal.ioc._ManagedBeanAdapter; import org.apache.isis.core.metamodel.context.MetaModelContext; -import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet; import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet; import org.apache.isis.core.metamodel.object.ManagedObject; @@ -179,15 +178,14 @@ final class ObjectLoader_builtinHandlers { public ManagedObject handle(final ObjectLoader.Request objectLoadRequest) { val spec = objectLoadRequest.getObjectSpecification(); - val entityFacet = spec.getFacet(EntityFacet.class); - if(entityFacet==null) { - throw _Exceptions.illegalArgument( - "ObjectSpecification is missing an EntityFacet: %s", spec); - } + val entityFacet = spec.entityFacetElseFail(); val bookmark = objectLoadRequest.getBookmark(); - val entity = entityFacet.fetchByIdentifier(bookmark); - return entity; + val entityPojoIfAny = entityFacet.fetchByBookmark(bookmark); + + return entityPojoIfAny + .map(entityPojo->ManagedObject.entity(spec, entityPojo, Optional.of(bookmark))) + .orElseGet(()->ManagedObject.empty(spec)); } } diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/query/ObjectBulkLoader_builtinHandlers.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/query/ObjectBulkLoader_builtinHandlers.java index 7eb33b8e98..e632428813 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/query/ObjectBulkLoader_builtinHandlers.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/query/ObjectBulkLoader_builtinHandlers.java @@ -21,7 +21,6 @@ package org.apache.isis.core.metamodel.objectmanager.query; import org.apache.isis.commons.collections.Can; import org.apache.isis.commons.internal.exceptions._Exceptions; import org.apache.isis.core.metamodel.context.MetaModelContext; -import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet; import org.apache.isis.core.metamodel.object.ManagedObject; import lombok.NonNull; @@ -85,11 +84,7 @@ final class ObjectBulkLoader_builtinHandlers { public Can<ManagedObject> handle(final ObjectBulkLoader.Request objectQuery) { val spec = objectQuery.getObjectSpecification(); - val entityFacet = spec.getFacet(EntityFacet.class); - if(entityFacet==null) { - throw _Exceptions.illegalArgument( - "ObjectSpecification is missing an EntityFacet: %s", spec.getCorrespondingClass()); - } + val entityFacet = spec.entityFacetElseFail(); val entities = entityFacet.fetchByQuery(objectQuery.getQuery()); val serviceInjector = metaModelContext.getServiceInjector(); diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/refresh/ObjectRefresher_builtinHandlers.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/refresh/ObjectRefresher_builtinHandlers.java index 9f6f80a93f..3cecee0d22 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/refresh/ObjectRefresher_builtinHandlers.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/objectmanager/refresh/ObjectRefresher_builtinHandlers.java @@ -18,9 +18,7 @@ */ package org.apache.isis.core.metamodel.objectmanager.refresh; -import org.apache.isis.commons.internal.exceptions._Exceptions; import org.apache.isis.core.metamodel.context.MetaModelContext; -import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet; import org.apache.isis.core.metamodel.object.ManagedObject; import lombok.Data; @@ -41,7 +39,7 @@ final class ObjectRefresher_builtinHandlers { private MetaModelContext metaModelContext; @Override - public boolean isHandling(ManagedObject managedObject) { + public boolean isHandling(final ManagedObject managedObject) { if(managedObject==null || managedObject.getPojo()==null) { return true; @@ -51,7 +49,7 @@ final class ObjectRefresher_builtinHandlers { } @Override - public Void handle(ManagedObject managedObject) { + public Void handle(final ManagedObject managedObject) { return null; // noop } @@ -61,20 +59,16 @@ final class ObjectRefresher_builtinHandlers { public static class RefreshEntity implements ObjectRefresher.Handler { @Override - public boolean isHandling(ManagedObject request) { + public boolean isHandling(final ManagedObject request) { val spec = request.getSpecification(); return spec.isEntity(); } @Override - public Void handle(ManagedObject request) { + public Void handle(final ManagedObject request) { val spec = request.getSpecification(); - val entityFacet = spec.getFacet(EntityFacet.class); - if(entityFacet==null) { - throw _Exceptions.illegalArgument( - "ObjectSpecification is missing an EntityFacet: %s", spec); - } + val entityFacet = spec.entityFacetElseFail(); entityFacet.refresh(request.getPojo()); @@ -90,13 +84,13 @@ final class ObjectRefresher_builtinHandlers { public static class RefreshOther implements ObjectRefresher.Handler { @Override - public boolean isHandling(ManagedObject request) { + public boolean isHandling(final ManagedObject request) { // if no one else feels responsible, we do return true; } @Override - public Void handle(ManagedObject request) { + public Void handle(final ManagedObject request) { return null; // noop } diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/Hierarchical.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/Hierarchical.java index 1915659ba5..c6225b2ffb 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/Hierarchical.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/Hierarchical.java @@ -45,6 +45,11 @@ public interface Hierarchical { */ boolean isOfType(ObjectSpecification other); + /** + * Same as {@link #isOfType(ObjectSpecification)}, except treating wrapper/primitive the same. + */ + boolean isOfTypeResolvePrimitive(ObjectSpecification other); + public static enum Depth { DIRECT, TRANSITIVE diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ObjectSpecification.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ObjectSpecification.java index 847ea7f3d7..a220b5adde 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ObjectSpecification.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ObjectSpecification.java @@ -440,13 +440,13 @@ extends default boolean isEntity() { return getBeanSort().isEntity() || (getBeanSort().isAbstract() - && lookupFacet(EntityFacet.class).isPresent()); + && entityFacet().isPresent()); } default boolean isViewModel() { return getBeanSort().isViewModel() || (getBeanSort().isAbstract() - && lookupFacet(ViewModelFacet.class).isPresent()); + && viewmodelFacet().isPresent()); } default boolean isEntityOrViewModel() { @@ -629,5 +629,24 @@ extends /** introduced for lookup optimization / allow memoization */ @SuppressWarnings("rawtypes") Optional<ValueFacet> valueFacet(); + @SuppressWarnings("rawtypes") + default ValueFacet valueFacetElseFail() { + return valueFacet().orElseThrow(()-> + _Exceptions.unrecoverable("Value type %s must have a ValueFacet", toString())); + } + + /** introduced for lookup optimization / allow memoization */ + Optional<EntityFacet> entityFacet(); + default EntityFacet entityFacetElseFail() { + return entityFacet().orElseThrow(()-> + _Exceptions.unrecoverable("Entity type %s must have an EntityFacet", toString())); + } + + /** introduced for lookup optimization / allow memoization */ + Optional<ViewModelFacet> viewmodelFacet(); + default ViewModelFacet viewmodelFacetElseFail() { + return viewmodelFacet().orElseThrow(()-> + _Exceptions.unrecoverable("ViewModel type %s must have a ViewModelFacet", toString())); + } } diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java index 76296c1606..af88318689 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java @@ -29,6 +29,8 @@ import java.util.stream.Stream; import javax.enterprise.inject.Vetoed; +import org.springframework.util.ClassUtils; + import org.apache.isis.applib.Identifier; import org.apache.isis.applib.annotation.Introspection.IntrospectionPolicy; import org.apache.isis.applib.id.LogicalType; @@ -60,6 +62,7 @@ import org.apache.isis.core.metamodel.facets.all.named.ObjectNamedFacet; import org.apache.isis.core.metamodel.facets.members.cssclass.CssClassFacet; import org.apache.isis.core.metamodel.facets.members.cssclassfa.CssClassFaFacet; import org.apache.isis.core.metamodel.facets.members.cssclassfa.CssClassFaFactory; +import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet; import org.apache.isis.core.metamodel.facets.object.icon.IconFacet; import org.apache.isis.core.metamodel.facets.object.icon.ObjectIcon; import org.apache.isis.core.metamodel.facets.object.immutable.ImmutableFacet; @@ -70,6 +73,7 @@ import org.apache.isis.core.metamodel.facets.object.parented.ParentedCollectionF import org.apache.isis.core.metamodel.facets.object.title.TitleFacet; import org.apache.isis.core.metamodel.facets.object.title.TitleRenderRequest; import org.apache.isis.core.metamodel.facets.object.value.ValueFacet; +import org.apache.isis.core.metamodel.facets.object.viewmodel.ViewModelFacet; import org.apache.isis.core.metamodel.interactions.InteractionContext; import org.apache.isis.core.metamodel.interactions.InteractionUtils; import org.apache.isis.core.metamodel.interactions.ObjectTitleContext; @@ -181,6 +185,8 @@ implements ObjectSpecification { private ObjectSpecification superclassSpec; private ValueFacet valueFacet; + private EntityFacet entityFacet; + private ViewModelFacet viewmodelFacet; private TitleFacet titleFacet; private IconFacet iconFacet; private NavigableParentFacet navigableParentFacet; @@ -394,6 +400,24 @@ implements ObjectSpecification { return Optional.ofNullable(valueFacet); } + @Override + public final Optional<EntityFacet> entityFacet() { + // deliberately don't memoize lookup misses, because could be too early + if(entityFacet==null) { + entityFacet = getFacet(EntityFacet.class); + } + return Optional.ofNullable(entityFacet); + } + + @Override + public final Optional<ViewModelFacet> viewmodelFacet() { + // deliberately don't memoize lookup misses, because could be too early + if(viewmodelFacet==null) { + viewmodelFacet = getFacet(ViewModelFacet.class); + } + return Optional.ofNullable(viewmodelFacet); + } + @Override public String getTitle(final TitleRenderRequest titleRenderRequest) { if (titleFacet != null) { @@ -466,6 +490,17 @@ implements ObjectSpecification { || otherClass.isAssignableFrom(thisClass); } + @Override + public boolean isOfTypeResolvePrimitive(final ObjectSpecification other) { + + val thisClass = ClassUtils.resolvePrimitiveIfNecessary(this.getCorrespondingClass()); + val otherClass = ClassUtils.resolvePrimitiveIfNecessary(other.getCorrespondingClass()); + + return thisClass == otherClass + || otherClass.isAssignableFrom(thisClass); + } + + // -- NAME, DESCRIPTION, PERSISTABILITY @Override diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/ident/title/TitleAnnotationFacetFactoryTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/ident/title/TitleAnnotationFacetFactoryTest.java index dc2e7c70d3..c0c13c4e43 100644 --- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/ident/title/TitleAnnotationFacetFactoryTest.java +++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/object/ident/title/TitleAnnotationFacetFactoryTest.java @@ -132,7 +132,7 @@ extends AbstractFacetFactoryJUnit4TestCase { } final Customer2 customer = new Customer2(); - val objectAdapter = ManagedObject.wrapScalar(specificationLoader, customer); + val objectAdapter = ManagedObject.adaptScalar(specificationLoader, customer); final String title = titleFacetViaTitleAnnotation.title(objectAdapter); assertThat(title, is("titleElement1. titleElement3,titleElement2")); @@ -198,9 +198,9 @@ extends AbstractFacetFactoryJUnit4TestCase { public void titleAnnotatedMethodsSomeOfWhichReturnNulls() throws Exception { { // check prerequisites - val wThree = ManagedObject.wrapScalar(specificationLoader, Integer.valueOf(3)); + val wThree = ManagedObject.adaptScalar(specificationLoader, Integer.valueOf(3)); assertEquals("3", wThree.getTitle()); - val pThree = ManagedObject.wrapScalar(specificationLoader, 3); + val pThree = ManagedObject.adaptScalar(specificationLoader, 3); assertEquals("3", pThree.getTitle()); } @@ -208,7 +208,7 @@ extends AbstractFacetFactoryJUnit4TestCase { .forTesting(Customer4.class, mockMethodRemover, facetedMethod)); final Customer4 customer = new Customer4(); - val objectAdapter = ManagedObject.wrapScalar(specificationLoader, customer); + val objectAdapter = ManagedObject.adaptScalar(specificationLoader, customer); assertThat(objectAdapter.getTitle(), is("titleElement1 titleElement3 titleElement5 3 this needs to be trimmed")); diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/object/ManagedObjectTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/object/ManagedObjectTest.java index 3bfdf8587a..02de16d75d 100644 --- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/object/ManagedObjectTest.java +++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/object/ManagedObjectTest.java @@ -84,7 +84,7 @@ class ManagedObjectTest { val emptyObject = ManagedObject.empty(spec); assertNotNull(emptyObject); - val presentObject = ManagedObject.wrapScalar(specLoader, 3); + val presentObject = ManagedObject.adaptScalar(specLoader, 3); assertEquals(Specialization.VALUE, presentObject.getSpecialization()); presentObject.assertCompliance(6); @@ -113,7 +113,7 @@ class ManagedObjectTest { val constructor = cls.getConstructor(_Constants.emptyClasses); val pojo = constructor.newInstance(_Constants.emptyObjects); - val presentObject = ManagedObject.wrapScalar(specLoader, pojo); + val presentObject = ManagedObject.adaptScalar(specLoader, pojo); assertEquals(Specialization.VIEWMODEL, presentObject.getSpecialization()); presentObject.assertCompliance(pojo); diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/SchemaValueMarshallerDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/SchemaValueMarshallerDefault.java index 357262c9de..3f8a406b1f 100644 --- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/SchemaValueMarshallerDefault.java +++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/SchemaValueMarshallerDefault.java @@ -146,13 +146,7 @@ extends SchemaValueMarshallerAbstract { ? fromTypedTuple(context, valueDto.getComposite()) : context.getSemantics().compose(ValueDecomposition.ofFundamental(valueDto)); - if(recoveredValueAsPojo==null) { - return ManagedObject.empty(context.getElementType()); - } - - val recoveredValue = recoveredValueAsPojo!=null - ? ManagedObject.of(elementSpec, recoveredValueAsPojo) - : ManagedObject.empty(context.getElementType()); + val recoveredValue = ManagedObject.value(elementSpec, recoveredValueAsPojo); return recoveredValue; } diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/executor/MemberExecutorServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/executor/MemberExecutorServiceDefault.java index 4cc45784e3..68692ac664 100644 --- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/executor/MemberExecutorServiceDefault.java +++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/executor/MemberExecutorServiceDefault.java @@ -62,7 +62,6 @@ import org.apache.isis.core.metamodel.object.MmUnwrapUtil; import org.apache.isis.core.metamodel.object.MmVisibilityUtil; import org.apache.isis.core.metamodel.object.PackedManagedObject; import org.apache.isis.core.metamodel.objectmanager.ObjectManager; -import org.apache.isis.core.metamodel.objectmanager.ObjectManager.EntityAdaptingMode; import org.apache.isis.core.metamodel.services.events.MetamodelEventService; import org.apache.isis.core.metamodel.services.ixn.InteractionDtoFactory; import org.apache.isis.core.metamodel.services.publishing.ExecutionPublisher; @@ -177,7 +176,7 @@ implements MemberExecutorService { val returnedPojo = priorExecution.getReturned(); val returnedAdapter = objectManager.adapt( - returnedPojo, owningAction::getElementType, EntityAdaptingMode.MEMOIZE_BOOKMARK); + returnedPojo, owningAction::getElementType); // sync DTO with result interactionDtoFactory @@ -279,6 +278,7 @@ implements MemberExecutorService { } val entityState = MmEntityUtil.getEntityState(resultAdapter); + //FIXME what to do with new state if(entityState.isDetached()) { // ensure that any still-to-be-persisted adapters get persisted to DB. getTransactionService().flushTransaction(); diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/factory/FactoryServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/factory/FactoryServiceDefault.java index 0211ddb2c6..0e0fb5cb0b 100644 --- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/factory/FactoryServiceDefault.java +++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/factory/FactoryServiceDefault.java @@ -33,7 +33,6 @@ import org.apache.isis.applib.annotation.PriorityPrecedence; import org.apache.isis.applib.services.bookmark.Bookmark; import org.apache.isis.applib.services.factory.FactoryService; import org.apache.isis.applib.services.iactnlayer.InteractionService; -import org.apache.isis.applib.services.inject.ServiceInjector; import org.apache.isis.commons.internal.base._Casts; import org.apache.isis.commons.internal.exceptions._Exceptions; import org.apache.isis.core.config.environment.IsisSystemEnvironment; @@ -56,7 +55,6 @@ public class FactoryServiceDefault implements FactoryService { @Inject InteractionService interactionService; // dependsOn @Inject private SpecificationLoader specificationLoader; - @Inject private ServiceInjector serviceInjector; @Inject private IsisSystemEnvironment isisSystemEnvironment; @Inject private Provider<ObjectLifecyclePublisher> objectLifecyclePublisherProvider; private ObjectLifecyclePublisher objectLifecyclePublisher() { return objectLifecyclePublisherProvider.get(); } @@ -95,8 +93,7 @@ public class FactoryServiceDefault implements FactoryService { throw _Exceptions.illegalArgument("Type '%s' is not recogniced as an entity type by the framework.", entityClass); } - serviceInjector.injectServicesInto(entityPojo); - objectLifecyclePublisher().onPostCreate(ManagedObject.of(spec, entityPojo)); + objectLifecyclePublisher().onPostCreate(ManagedObject.entityDetached(spec, entityPojo)); return entityPojo; } @@ -124,8 +121,7 @@ public class FactoryServiceDefault implements FactoryService { throw _Exceptions.illegalArgument("Type '%s' is not recogniced as a ViewModel by the framework.", viewModelClass); } - serviceInjector.injectServicesInto(viewModelPojo); - objectLifecyclePublisher().onPostCreate(ManagedObject.of(spec, viewModelPojo)); + objectLifecyclePublisher().onPostCreate(ManagedObject.viewmodel(spec, viewModelPojo, Optional.empty())); return viewModelPojo; } diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/repository/RepositoryServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/repository/RepositoryServiceDefault.java index 22dd0420c9..c605a71ac4 100644 --- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/repository/RepositoryServiceDefault.java +++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/repository/RepositoryServiceDefault.java @@ -97,7 +97,7 @@ public class RepositoryServiceDefault implements RepositoryService { if(ManagedObjects.isNullOrUnspecifiedOrEmpty(adapter)) { throw new PersistFailedException("Object not known to framework (unable to create/obtain an adapter)"); } - // only persist detached entities, otherwise skip + // only persist detached or new entities, otherwise skip val entityState = MmEntityUtil.getEntityState(adapter); if(!entityState.isPersistable() || entityState.isAttached()) { diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/DomainObjectInvocationHandler.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/DomainObjectInvocationHandler.java index 84ed080cfb..107e227938 100644 --- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/DomainObjectInvocationHandler.java +++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/DomainObjectInvocationHandler.java @@ -127,8 +127,7 @@ extends DelegatingInvocationHandlerDefault<T> { nsme); } - entityFacet = targetAdapter.getSpecification() - .getFacet(EntityFacet.class); + entityFacet = targetAdapter.getSpecification().entityFacet().orElse(null); this.mixeeAdapter = mixeeAdapter; } diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotBuilder.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotBuilder.java index ac1bae20da..75e698543a 100644 --- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotBuilder.java +++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotBuilder.java @@ -79,7 +79,7 @@ public class XmlSnapshotBuilder { } public XmlSnapshot build() { - final ManagedObject adapter = ManagedObject.wrapScalar(specificationLoader, domainObject); + final ManagedObject adapter = ManagedObject.adaptScalar(specificationLoader, domainObject); final XmlSnapshot snapshot = (schema != null) ? new XmlSnapshot(adapter, schema) : new XmlSnapshot(adapter); diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotServiceDefault.java index 669b1ae402..f8ec1e95f6 100644 --- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotServiceDefault.java +++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotServiceDefault.java @@ -86,7 +86,7 @@ public class XmlSnapshotServiceDefault implements XmlSnapshotService { */ @Override public XmlSnapshotService.Snapshot snapshotFor(final Object domainObject) { - final ManagedObject adapter = ManagedObject.wrapScalar(specificationLoader, domainObject); + final ManagedObject adapter = ManagedObject.adaptScalar(specificationLoader, domainObject); return new XmlSnapshot(adapter); } diff --git a/extensions/vw/fullcalendar/wicket/ui/src/main/java/org/apache/isis/extensions/fullcalendar/wkt/viewer/EventProviderAbstract.java b/extensions/vw/fullcalendar/wicket/ui/src/main/java/org/apache/isis/extensions/fullcalendar/wkt/viewer/EventProviderAbstract.java index 012cacff38..8715a7db0f 100644 --- a/extensions/vw/fullcalendar/wicket/ui/src/main/java/org/apache/isis/extensions/fullcalendar/wkt/viewer/EventProviderAbstract.java +++ b/extensions/vw/fullcalendar/wicket/ui/src/main/java/org/apache/isis/extensions/fullcalendar/wkt/viewer/EventProviderAbstract.java @@ -121,7 +121,7 @@ public abstract class EventProviderAbstract implements EventProvider { final Object dereferencedObject = dereference(commonContext, domainObjectPojo); val dereferencedManagedObject = - ManagedObject.wrapScalar(commonContext.getSpecificationLoader(), dereferencedObject); + ManagedObject.adaptScalar(commonContext.getSpecificationLoader(), dereferencedObject); val oid = ManagedObjects.bookmark(dereferencedManagedObject).orElse(null); if(oid!=null) { diff --git a/incubator/viewers/graphql/viewer/src/main/java/org/apache/isis/viewer/graphql/viewer/source/ObjectTypeFactory.java b/incubator/viewers/graphql/viewer/src/main/java/org/apache/isis/viewer/graphql/viewer/source/ObjectTypeFactory.java index d0b7c46a3e..4f15234a8c 100644 --- a/incubator/viewers/graphql/viewer/src/main/java/org/apache/isis/viewer/graphql/viewer/source/ObjectTypeFactory.java +++ b/incubator/viewers/graphql/viewer/src/main/java/org/apache/isis/viewer/graphql/viewer/source/ObjectTypeFactory.java @@ -414,7 +414,7 @@ public class ObjectTypeFactory { Class<?> domainObjectInstanceClass = domainObjectInstance.getClass(); ObjectSpecification specification = specificationLoader.loadSpecification(domainObjectInstanceClass); - ManagedObject owner = ManagedObject.of(specification, domainObjectInstance); + ManagedObject owner = ManagedObject.adaptScalar(specification, domainObjectInstance); ManagedObject managedObject = otom.get(owner); diff --git a/incubator/viewers/graphql/viewer/src/main/java/org/apache/isis/viewer/graphql/viewer/source/QueryFieldFactory.java b/incubator/viewers/graphql/viewer/src/main/java/org/apache/isis/viewer/graphql/viewer/source/QueryFieldFactory.java index 2c84794a57..d800229253 100644 --- a/incubator/viewers/graphql/viewer/src/main/java/org/apache/isis/viewer/graphql/viewer/source/QueryFieldFactory.java +++ b/incubator/viewers/graphql/viewer/src/main/java/org/apache/isis/viewer/graphql/viewer/source/QueryFieldFactory.java @@ -123,7 +123,7 @@ public class QueryFieldFactory { ObjectSpecification specification = specificationLoader .loadSpecification(domainObjectInstanceClass); - ManagedObject owner = ManagedObject.of(specification, domainObjectInstance); + ManagedObject owner = ManagedObject.adaptScalar(specification, domainObjectInstance); ActionInteractionHead actionInteractionHead = objectAction.interactionHead(owner); @@ -133,10 +133,7 @@ public class QueryFieldFactory { Object argumentValue = arguments.get(oap.getId()); ObjectSpecification elementType = oap.getElementType(); - if (argumentValue == null) - return ManagedObject.empty(elementType); - return ManagedObject.of(elementType, argumentValue); - + return ManagedObject.adaptScalar(elementType, argumentValue); }).collect(Can.toCan()); diff --git a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BindingsVaa.java b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BindingsVaa.java index d95e499217..389d0063bc 100644 --- a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BindingsVaa.java +++ b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BindingsVaa.java @@ -20,8 +20,6 @@ package org.apache.isis.incubator.viewer.vaadin.ui.binding; import java.util.function.UnaryOperator; -import org.springframework.lang.Nullable; - import com.vaadin.flow.component.HasValidation; import com.vaadin.flow.component.HasValue; import com.vaadin.flow.data.binder.Binder; @@ -30,6 +28,8 @@ import com.vaadin.flow.data.binder.Setter; import com.vaadin.flow.data.converter.Converter; import com.vaadin.flow.function.ValueProvider; +import org.springframework.lang.Nullable; + import org.apache.isis.commons.binding.Bindable; import org.apache.isis.commons.binding.Observable; import org.apache.isis.commons.internal.base._Casts; @@ -361,7 +361,7 @@ public final class BindingsVaa { //SETTER @Override public void accept(@NonNull final Bindable<ManagedObject> target, final V fieldValue) { - target.setValue(ManagedObject.of(valueSpec, fieldValue)); + target.setValue(ManagedObject.adaptScalar(valueSpec, fieldValue)); } diff --git a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/changetracking/JdoLifecycleListener.java b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/changetracking/JdoLifecycleListener.java index 410d455401..8be754c905 100644 --- a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/changetracking/JdoLifecycleListener.java +++ b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/changetracking/JdoLifecycleListener.java @@ -31,11 +31,11 @@ import javax.jdo.listener.StoreLifecycleListener; import org.datanucleus.enhancement.Persistable; +import org.apache.isis.commons.internal.assertions._Assert; import org.apache.isis.commons.internal.base._Casts; import org.apache.isis.core.metamodel.context.MetaModelContext; import org.apache.isis.core.metamodel.facets.object.publish.entitychange.EntityChangePublishingFacet; import org.apache.isis.core.metamodel.object.ManagedObject; -import org.apache.isis.core.metamodel.objectmanager.ObjectManager.EntityAdaptingMode; import org.apache.isis.core.metamodel.services.objectlifecycle.ObjectLifecyclePublisher; import org.apache.isis.persistence.jdo.datanucleus.entities.DnObjectProviderForIsis; @@ -89,7 +89,7 @@ DetachLifecycleListener, DirtyLifecycleListener, LoadLifecycleListener, StoreLif public void postLoad(final InstanceLifecycleEvent event) { log.debug("postLoad {}", ()->_Utils.debug(event)); final Persistable pojo = _Utils.persistableFor(event); - val entity = adaptEntityAndInjectServices(pojo, EntityAdaptingMode.MEMOIZE_BOOKMARK); + val entity = adaptEntity(pojo); objectLifecyclePublisher.onPostLoad(entity); @@ -101,10 +101,11 @@ DetachLifecycleListener, DirtyLifecycleListener, LoadLifecycleListener, StoreLif final Persistable pojo = _Utils.persistableFor(event); - /* Called either when an entity is initially persisted, or when an entity is updated; fires the appropriate + /* Called either when an entity is initially persisted, + * or when an entity is updated; fires the appropriate * lifecycle callback. So filter for those events when initially persisting. */ if(pojo.dnGetStateManager().isNew(pojo)) { - val entity = adaptEntity(pojo, EntityAdaptingMode.SKIP_MEMOIZATION); + val entity = adaptEntity(pojo); objectLifecyclePublisher.onPrePersist(entity); } } @@ -114,7 +115,7 @@ DetachLifecycleListener, DirtyLifecycleListener, LoadLifecycleListener, StoreLif log.debug("postStore {}", ()->_Utils.debug(event)); final Persistable pojo = _Utils.persistableFor(event); - val entity = adaptEntityAndInjectServices(pojo, EntityAdaptingMode.MEMOIZE_BOOKMARK); + val entity = adaptEntity(pojo); if(EntityChangePublishingFacet.isPublishingEnabled(entity.getSpecification())) { @@ -150,7 +151,7 @@ DetachLifecycleListener, DirtyLifecycleListener, LoadLifecycleListener, StoreLif } private final void doPreDirty(final Persistable pojo) { - val entity = adaptEntity(pojo, EntityAdaptingMode.MEMOIZE_BOOKMARK); + val entity = adaptEntity(pojo); objectLifecyclePublisher.onPreUpdate(entity, null); } @@ -164,7 +165,10 @@ DetachLifecycleListener, DirtyLifecycleListener, LoadLifecycleListener, StoreLif log.debug("preDelete {}", ()->_Utils.debug(event)); final Persistable pojo = _Utils.persistableFor(event); - val entity = adaptEntity(pojo, EntityAdaptingMode.SKIP_MEMOIZATION); + + _Assert.assertNotNull(pojo.dnGetObjectId()); + //val entity = adaptEntity(pojo, EntityAdaptingMode.NOT_YET_BOOKMARKABLE); + val entity = adaptEntity(pojo); objectLifecyclePublisher.onPreRemove(entity); } @@ -204,15 +208,8 @@ DetachLifecycleListener, DirtyLifecycleListener, LoadLifecycleListener, StoreLif // -- HELPER private ManagedObject adaptEntity( - final @NonNull Persistable pojo, - final @NonNull EntityAdaptingMode bookmarking) { - return _Utils.adaptEntity(metaModelContext, pojo, bookmarking); - } - - private ManagedObject adaptEntityAndInjectServices( - final @NonNull Persistable pojo, - final @NonNull EntityAdaptingMode bookmarking) { - return _Utils.adaptEntityAndInjectServices(metaModelContext, pojo, bookmarking); + final @NonNull Persistable pojo) { + return _Utils.adaptEntity(metaModelContext, pojo); } } diff --git a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/changetracking/_Utils.java b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/changetracking/_Utils.java index 6fa5f63e90..cd46eb93c0 100644 --- a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/changetracking/_Utils.java +++ b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/changetracking/_Utils.java @@ -29,7 +29,6 @@ import org.apache.isis.commons.internal.assertions._Assert; import org.apache.isis.core.metamodel.context.MetaModelContext; import org.apache.isis.core.metamodel.object.ManagedObject; import org.apache.isis.core.metamodel.object.ManagedObjects; -import org.apache.isis.core.metamodel.objectmanager.ObjectManager.EntityAdaptingMode; import lombok.NonNull; import lombok.val; @@ -64,40 +63,36 @@ final class _Utils { ManagedObject adaptEntity( final @NonNull MetaModelContext mmc, - final @NonNull Object entityPojo, - final @NonNull EntityAdaptingMode bookmarking) { + final @NonNull Object entityPojo) { val objectManager = mmc.getObjectManager(); - val entity = objectManager.adapt(entityPojo, bookmarking); + val entity = objectManager.adapt(entityPojo); _Assert.assertTrue(entity.getSpecification().isEntity()); return entity; } ManagedObject adaptNullableEntity( final @NonNull MetaModelContext mmc, - final @Nullable Object entityPojo, - final @NonNull EntityAdaptingMode bookmarking) { + final @Nullable Object entityPojo) { return entityPojo == null ? ManagedObject.unspecified() - : adaptEntity(mmc, entityPojo, bookmarking); + : adaptEntity(mmc, entityPojo); } ManagedObject adaptNullableAndInjectServices( final @NonNull MetaModelContext mmc, - final @Nullable Object entityPojo, - final @NonNull EntityAdaptingMode bookmarking) { + final @Nullable Object entityPojo) { return entityPojo == null ? ManagedObject.unspecified() - : adaptEntityAndInjectServices(mmc, entityPojo, bookmarking); + : adaptEntityAndInjectServices(mmc, entityPojo); } ManagedObject adaptEntityAndInjectServices( final @NonNull MetaModelContext mmc, - final @NonNull Object entityPojo, - final @NonNull EntityAdaptingMode bookmarking) { - return injectServices(mmc, adaptEntity(mmc, entityPojo, bookmarking)); + final @NonNull Object entityPojo) { + return injectServices(mmc, adaptEntity(mmc, entityPojo)); } diff --git a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/entities/DnEntityStateProvider.java b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/entities/DnEntityStateProvider.java index f1d894d971..0e2fa29a70 100644 --- a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/entities/DnEntityStateProvider.java +++ b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/entities/DnEntityStateProvider.java @@ -21,9 +21,8 @@ package org.apache.isis.persistence.jdo.datanucleus.entities; import java.lang.reflect.Method; import java.util.Set; -import org.springframework.lang.Nullable; - import org.datanucleus.enhancement.Persistable; +import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; import org.apache.isis.applib.services.repository.EntityState; @@ -76,7 +75,9 @@ public class DnEntityStateProvider implements JdoFacetContext { } val isPersistent = persistable.dnIsPersistent(); if(isPersistent) { - return EntityState.PERSISTABLE_ATTACHED; + return persistable.dnGetObjectId()!=null + ? EntityState.PERSISTABLE_ATTACHED + : EntityState.PERSISTABLE_NEW; } return EntityState.PERSISTABLE_DETACHED; } @@ -100,7 +101,7 @@ public class DnEntityStateProvider implements JdoFacetContext { } @Override - public EntityFacet createEntityFacet(final FacetHolder facetHolder, Class<?> entityClass) { + public EntityFacet createEntityFacet(final FacetHolder facetHolder, final Class<?> entityClass) { return new JdoEntityFacet(facetHolder, entityClass); } diff --git a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/metamodel/facets/entity/JdoEntityFacet.java b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/metamodel/facets/entity/JdoEntityFacet.java index ac2b3741e3..6865d76cd1 100644 --- a/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/metamodel/facets/entity/JdoEntityFacet.java +++ b/persistence/jdo/datanucleus/src/main/java/org/apache/isis/persistence/jdo/datanucleus/metamodel/facets/entity/JdoEntityFacet.java @@ -21,6 +21,7 @@ package org.apache.isis.persistence.jdo.datanucleus.metamodel.facets.entity; import java.lang.reflect.Method; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; @@ -46,8 +47,6 @@ import org.apache.isis.commons.collections.Can; import org.apache.isis.commons.internal.assertions._Assert; import org.apache.isis.commons.internal.base._NullSafe; import org.apache.isis.commons.internal.collections._Maps; -import org.apache.isis.commons.internal.debug._Debug; -import org.apache.isis.commons.internal.debug.xray.XrayUi; import org.apache.isis.commons.internal.exceptions._Exceptions; import org.apache.isis.core.config.beans.PersistenceStack; import org.apache.isis.core.metamodel.facetapi.FacetAbstract; @@ -56,7 +55,6 @@ import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet; import org.apache.isis.core.metamodel.object.ManagedObject; import org.apache.isis.core.metamodel.objectmanager.ObjectManager; import org.apache.isis.core.metamodel.services.objectlifecycle.ObjectLifecyclePublisher; -import org.apache.isis.core.metamodel.spec.ObjectSpecification; import org.apache.isis.core.runtime.idstringifier.IdStringifierService; import org.apache.isis.persistence.jdo.datanucleus.entities.DnEntityStateProvider; import org.apache.isis.persistence.jdo.metamodel.facets.object.persistencecapable.JdoPersistenceCapableFacetFactory; @@ -98,48 +96,22 @@ implements EntityFacet { } @Override - public String identifierFor(final Object pojo) { + public Optional<String> identifierFor(final Object pojo) { - if(pojo==null) { - throw _Exceptions.illegalArgument( - "The persistence layer cannot identify a pojo that is null (given type %s)", - entityClass.getName()); - } - - if(!isPersistableType(pojo.getClass())) { - throw _Exceptions.illegalArgument( - "The persistence layer does not recognize given type %s", - pojo.getClass().getName()); + if (!getEntityState(pojo).isAttached()) { + return Optional.empty(); } val pm = getPersistenceManager(); - var primaryKey = pm.getObjectId(pojo); - -// if(primaryKey==null) { -// pm.makePersistent(pojo); -// primaryKey = pm.getObjectId(pojo); -// } - - if(primaryKey==null) { - - _Debug.onCondition(XrayUi.isXrayEnabled(), ()->{ - _Debug.log("detached entity detected %s", pojo); - }); - - throw _Exceptions.illegalArgument( - "The persistence layer does not recognize given object of type %s, " - + "meaning the object has no identifier that associates it with the persistence layer. " - + "(most likely, because the object is detached, eg. was not persisted after being new-ed up)", - pojo.getClass().getName()); - } + var primaryKeyIfAny = pm.getObjectId(pojo); - return idStringifierService.enstringPrimaryKey(primaryKey.getClass(), primaryKey); + return Optional.ofNullable(primaryKeyIfAny) + .map(primaryKey-> + idStringifierService.enstringPrimaryKey(primaryKey.getClass(), primaryKey)); } - @Override - public ManagedObject fetchByIdentifier( - final @NonNull Bookmark bookmark) { + public Optional<Object> fetchByBookmark(final @NonNull Bookmark bookmark) { log.debug("fetchEntity; bookmark={}", bookmark); @@ -166,14 +138,7 @@ implements EntityFacet { throw e; } - if (entityPojo == null) { - throw new ObjectNotFoundException(""+bookmark); - } - - val actualEntitySpec = getSpecificationLoader().specForTypeElseFail(entityPojo.getClass()); - getServiceInjector().injectServicesInto(entityPojo); // might be redundant - //TODO integrate with entity change tracking - return ManagedObject.bookmarked(actualEntitySpec, entityPojo, bookmark); + return Optional.ofNullable(entityPojo); } private Map<Class<?>, Class<?>> primaryKeyClassByEntityClass = new ConcurrentHashMap<>(); @@ -209,11 +174,6 @@ implements EntityFacet { } } - private ObjectSpecification getEntitySpec() { - return getSpecificationLoader().specForType(entityClass) - .orElseThrow(() -> new IllegalStateException(String.format("Could not load specification for entity class '%s'", entityClass))); - } - @Override public Can<ManagedObject> fetchByQuery(final Query<?> query) { @@ -294,7 +254,7 @@ implements EntityFacet { if(pojo==null || !isPersistableType(pojo.getClass()) - || DnEntityStateProvider.entityState(pojo).isAttached()) { + || DnEntityStateProvider.entityState(pojo).isAttachedOrNew()) { return; // nothing to do } @@ -404,7 +364,8 @@ implements EntityFacet { private Can<ManagedObject> fetchWithinTransaction(final Supplier<List<?>> fetcher) { - val objectLifecyclePublisher = getFacetHolder().getServiceRegistry().lookupServiceElseFail(ObjectLifecyclePublisher.class); + val objectLifecyclePublisher = getFacetHolder().getServiceRegistry() + .lookupServiceElseFail(ObjectLifecyclePublisher.class); return getTransactionalProcessor().callWithinCurrentTransactionElseCreateNew( ()->_NullSafe.stream(fetcher.get()) @@ -413,7 +374,9 @@ implements EntityFacet { .getValue().orElseThrow(); } - private ManagedObject adopt(final ObjectLifecyclePublisher objectLifecyclePublisher, final Object fetchedObject) { + private ManagedObject adopt( + final ObjectLifecyclePublisher objectLifecyclePublisher, + final Object fetchedObject) { // handles lifecycle callbacks and injects services // ought not to be necessary, however for some queries it seems that the diff --git a/persistence/jpa/integration/src/main/java/org/apache/isis/persistence/jpa/integration/entity/JpaEntityFacet.java b/persistence/jpa/integration/src/main/java/org/apache/isis/persistence/jpa/integration/entity/JpaEntityFacet.java index adc42ad8dc..7c764ec931 100644 --- a/persistence/jpa/integration/src/main/java/org/apache/isis/persistence/jpa/integration/entity/JpaEntityFacet.java +++ b/persistence/jpa/integration/src/main/java/org/apache/isis/persistence/jpa/integration/entity/JpaEntityFacet.java @@ -28,8 +28,8 @@ import javax.persistence.metamodel.EntityType; import org.eclipse.persistence.exceptions.DescriptorException; import org.springframework.data.jpa.repository.JpaContext; +import org.springframework.lang.Nullable; -import org.apache.isis.applib.exceptions.unrecoverable.ObjectNotFoundException; import org.apache.isis.applib.query.AllInstancesQuery; import org.apache.isis.applib.query.NamedQuery; import org.apache.isis.applib.query.Query; @@ -45,7 +45,6 @@ import org.apache.isis.core.metamodel.facetapi.FacetAbstract; import org.apache.isis.core.metamodel.facetapi.FacetHolder; import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet; import org.apache.isis.core.metamodel.object.ManagedObject; -import org.apache.isis.core.metamodel.spec.ObjectSpecification; import org.apache.isis.core.runtime.idstringifier.IdStringifierService; import lombok.NonNull; @@ -80,32 +79,23 @@ public class JpaEntityFacet } @Override - public String identifierFor(final Object pojo) { + public Optional<String> identifierFor(final @Nullable Object pojo) { - if (pojo == null) { - throw _Exceptions.illegalArgument( - "The persistence layer cannot identify a pojo that is null (given type %s)", - entityClass.getName()); + if (!getEntityState(pojo).isAttached()) { + return Optional.empty(); } val entityManager = getEntityManager(); val persistenceUnitUtil = getPersistenceUnitUtil(entityManager); - val primaryKey = persistenceUnitUtil.getIdentifier(pojo); - - if (primaryKey == null) { - throw _Exceptions.illegalArgument( - "The persistence layer does not recognize given object of type %s, " - + "meaning the object has no identifier that associates it with the persistence layer. " - + "(most likely, because the object is detached, eg. was not persisted after being new-ed up)", - pojo.getClass().getName()); - } + val primaryKeyIfAny = persistenceUnitUtil.getIdentifier(pojo); - return idStringifierService.enstringPrimaryKey(getPrimaryKeyType(), primaryKey); + return Optional.ofNullable(primaryKeyIfAny) + .map(primaryKey-> + idStringifierService.enstringPrimaryKey(getPrimaryKeyType(), primaryKey)); } @Override - public ManagedObject fetchByIdentifier( - final @NonNull Bookmark bookmark) { + public Optional<Object> fetchByBookmark(final @NonNull Bookmark bookmark) { log.debug("fetchEntity; bookmark={}", bookmark); @@ -114,18 +104,7 @@ public class JpaEntityFacet val entityManager = getEntityManager(); val entityPojo = entityManager.find(entityClass, primaryKey); - - if (entityPojo == null) { - throw new ObjectNotFoundException("" + bookmark); - } - - final ObjectSpecification entitySpec = getEntitySpec(); - return ManagedObject.bookmarked(entitySpec, entityPojo, bookmark); - } - - private ObjectSpecification getEntitySpec() { - return getSpecificationLoader().specForType(entityClass) - .orElseThrow(() -> new IllegalStateException(String.format("Could not load specification for entity class '%s'", entityClass))); + return Optional.ofNullable(entityPojo); } private Class<?> getPrimaryKeyType() { @@ -164,10 +143,10 @@ public class JpaEntityFacet typedQuery.setMaxResults(range.getLimitAsInt()); } - val spec = getEntitySpec(); + val entitySpec = getEntitySpecification(); return Can.ofStream( typedQuery.getResultStream() - .map(entity -> ManagedObject.of(spec, entity))); + .map(entity -> ManagedObject.adaptScalar(entitySpec, entity))); } else if (query instanceof NamedQuery) { @@ -191,10 +170,10 @@ public class JpaEntityFacet .forEach((paramName, paramValue) -> namedQuery.setParameter(paramName, paramValue)); - val spec = getEntitySpec(); + val entitySpec = getEntitySpecification(); return Can.ofStream( namedQuery.getResultStream() - .map(entity -> ManagedObject.of(spec, entity))); + .map(entity -> ManagedObject.adaptScalar(entitySpec, entity))); } diff --git a/regressiontests/stable-domainmodel/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java b/regressiontests/stable-domainmodel/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java index 82a33d1151..cffab3a342 100644 --- a/regressiontests/stable-domainmodel/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java +++ b/regressiontests/stable-domainmodel/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_usingGoodDomain.java @@ -262,7 +262,7 @@ class DomainModelTest_usingGoodDomain { assertEquals("inherited title", titleService.titleOf(instance)); assertEquals("inherited icon", titleService.iconNameOf(instance)); - val domainObject = ManagedObject.of(spec, instance); + val domainObject = ManagedObject.adaptScalar(spec, instance); assertEquals("inherited title", domainObject.getTitle()); assertEquals("inherited icon", iconFacet.iconName(domainObject)); } @@ -538,7 +538,7 @@ class DomainModelTest_usingGoodDomain { val objectSpec = specificationLoader.specForTypeElseFail(ProperMemberSupport.class); val member = objectSpec.getMemberElseFail(memberId); - val sampleObject = ManagedObject.of(objectSpec, new ProperMemberSupport()); + val sampleObject = ManagedObject.adaptScalar(objectSpec, new ProperMemberSupport()); assertEquals(named, member.getFriendlyName(()->sampleObject)); assertEquals(described, member.getDescription(()->sampleObject).orElse(null)); diff --git a/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/SimulatedUiChoices.java b/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/SimulatedUiChoices.java index d747b60c54..9608666727 100644 --- a/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/SimulatedUiChoices.java +++ b/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/SimulatedUiChoices.java @@ -74,13 +74,9 @@ public class SimulatedUiChoices extends HasValueValidation { * @param choiceIndices */ public void simulateMultiChoiceSelect(final int ... choiceIndices) { - val newValuePojos = choiceBox.getValue() - .pickByIndex(choiceIndices) - .map(ManagedObject::getPojo); - val newValue = ManagedObject.of( - valueSpecification, - newValuePojos.toList()); - selectedItem.setValue(newValue); + val newValues = choiceBox.getValue() + .pickByIndex(choiceIndices); + selectedItem.setValue(ManagedObject.packed(valueSpecification, newValues)); } public ManagedObject getValue() { diff --git a/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/SimulatedUiComponent.java b/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/SimulatedUiComponent.java index c97fce05e6..4525ca2023 100644 --- a/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/SimulatedUiComponent.java +++ b/regressiontests/stable-interact/src/test/java/org/apache/isis/testdomain/interact/SimulatedUiComponent.java @@ -44,7 +44,7 @@ public class SimulatedUiComponent extends HasValueValidation { } public void simulateValueChange(final Object newValue) { - value.setValue(ManagedObject.of(valueSpec, newValue)); + value.setValue(ManagedObject.adaptScalar(valueSpec, newValue)); } public ManagedObject getValue() { diff --git a/regressiontests/stable-persistence-jdo/src/test/java/org/apache/isis/testdomain/domainmodel/jdo/DomainModelTest.java b/regressiontests/stable-persistence-jdo/src/test/java/org/apache/isis/testdomain/domainmodel/jdo/DomainModelTest.java index de7f9e704a..9a4a137e4d 100644 --- a/regressiontests/stable-persistence-jdo/src/test/java/org/apache/isis/testdomain/domainmodel/jdo/DomainModelTest.java +++ b/regressiontests/stable-persistence-jdo/src/test/java/org/apache/isis/testdomain/domainmodel/jdo/DomainModelTest.java @@ -33,7 +33,6 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import org.apache.isis.applib.services.metamodel.BeanSort; import org.apache.isis.applib.services.registry.ServiceRegistry; import org.apache.isis.core.config.presets.IsisPresets; -import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet; import org.apache.isis.core.metamodel.specloader.SpecificationLoader; import org.apache.isis.persistence.jdo.provider.metamodel.facets.object.datastoreidentity.JdoDatastoreIdentityFacet; import org.apache.isis.persistence.jdo.provider.metamodel.facets.object.persistencecapable.JdoPersistenceCapableFacet; @@ -95,7 +94,7 @@ class DomainModelTest { val entitySpec = specificationLoader.loadSpecification(JdoEntityMetaAnnotated.class); assertEquals(BeanSort.ENTITY, entitySpec.getBeanSort()); - assertNotNull(entitySpec.getFacet(EntityFacet.class)); + assertNotNull(entitySpec.entityFacetElseFail()); //@PersistenceCapable(identityType = IdentityType.DATASTORE) val persistenceCapableFacet = entitySpec.getFacet(JdoPersistenceCapableFacet.class); diff --git a/regressiontests/stable-persistence-jdo/src/test/java/org/apache/isis/testdomain/injecting/jdo/JdoEntityInjectingTest.java b/regressiontests/stable-persistence-jdo/src/test/java/org/apache/isis/testdomain/injecting/jdo/JdoEntityInjectingTest.java index fe8cc62357..46a3c06cb2 100644 --- a/regressiontests/stable-persistence-jdo/src/test/java/org/apache/isis/testdomain/injecting/jdo/JdoEntityInjectingTest.java +++ b/regressiontests/stable-persistence-jdo/src/test/java/org/apache/isis/testdomain/injecting/jdo/JdoEntityInjectingTest.java @@ -71,7 +71,7 @@ class JdoEntityInjectingTest extends IsisIntegrationTestAbstract { void init() { // given jdoTestFixtures.reinstall(()->kvStore.clear(JdoBook.class)); - assertInjectCountRange(3, 9); //TODO there is some injection redundancy + assertInjectCountRange(3, 12); //TODO there is some injection redundancy } diff --git a/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/JpaBootstrappingTest.java b/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/JpaBootstrappingTest.java index 1f409f9306..3589d7ae9e 100644 --- a/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/JpaBootstrappingTest.java +++ b/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/JpaBootstrappingTest.java @@ -29,7 +29,6 @@ import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.TestPropertySource; import org.springframework.transaction.PlatformTransactionManager; @@ -43,7 +42,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.isis.commons.collections.Can; import org.apache.isis.core.config.presets.IsisPresets; -import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet; import org.apache.isis.core.metamodel.specloader.SpecificationLoader; import org.apache.isis.testdomain.conf.Configuration_usingJpa; import org.apache.isis.testdomain.jdo.JdoTestFixtures; @@ -101,11 +99,11 @@ class JpaBootstrappingTest extends IsisIntegrationTestAbstract { void jpaEntities_shouldBeRecognisedAsSuch() { val productSpec = specLoader.loadSpecification(JpaProduct.class); assertTrue(productSpec.isEntity()); - assertNotNull(productSpec.getFacet(EntityFacet.class)); + assertNotNull(productSpec.entityFacetElseFail()); val inventorySpec = specLoader.loadSpecification(JpaInventory.class); assertTrue(inventorySpec.isEntity()); - assertNotNull(inventorySpec.getFacet(EntityFacet.class)); + assertNotNull(inventorySpec.entityFacetElseFail()); } @Test @Order(1) @Rollback(false) diff --git a/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/entitylifecycle/JpaNonGeneratedStringIdEntityLifecycleTest.java b/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/entitylifecycle/JpaNonGeneratedStringIdEntityLifecycleTest.java index 2cad44d6b7..f9405155ea 100644 --- a/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/entitylifecycle/JpaNonGeneratedStringIdEntityLifecycleTest.java +++ b/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/entitylifecycle/JpaNonGeneratedStringIdEntityLifecycleTest.java @@ -90,6 +90,8 @@ class JpaNonGeneratedStringIdEntityLifecycleTest { repository.persist(entity.getPojo()); + System.err.printf("entity %s%n", entity); + assertEquals( EntityState.PERSISTABLE_ATTACHED, MmEntityUtil.getEntityState(entity)); diff --git a/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/springdata/SpringDataJpaBootstrappingTest.java b/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/springdata/SpringDataJpaBootstrappingTest.java index b780d2dbe3..e9ec920458 100644 --- a/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/springdata/SpringDataJpaBootstrappingTest.java +++ b/regressiontests/stable-persistence-jpa/src/test/java/org/apache/isis/testdomain/persistence/jpa/springdata/SpringDataJpaBootstrappingTest.java @@ -42,7 +42,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.isis.applib.services.repository.RepositoryService; import org.apache.isis.core.config.presets.IsisPresets; -import org.apache.isis.core.metamodel.facets.object.entity.EntityFacet; import org.apache.isis.core.metamodel.specloader.SpecificationLoader; import org.apache.isis.testdomain.conf.Configuration_usingSpringDataJpa; import org.apache.isis.testdomain.jpa.springdata.Employee; @@ -111,7 +110,7 @@ class SpringDataJpaBootstrappingTest extends IsisIntegrationTestAbstract { void jpaEntities_shouldBeRecognisedAsSuch() { val productSpec = specLoader.loadSpecification(Employee.class); assertTrue(productSpec.isEntity()); - assertNotNull(productSpec.getFacet(EntityFacet.class)); + assertNotNull(productSpec.entityFacetElseFail()); } @Test @Order(1) @Rollback(false) diff --git a/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTest.java b/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTest.java index fa82d0061d..e2016a76af 100644 --- a/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTest.java +++ b/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTest.java @@ -205,7 +205,7 @@ class ValueSemanticsTest { val spec = specLoader.specForTypeElseFail(valueMixin.getClass()); val interaction = ActionInteraction - .start(ManagedObject.of(spec, valueMixin), "act", Where.ANYWHERE); + .start(ManagedObject.mixin(spec, valueMixin), "act", Where.ANYWHERE); val pendingParams = interaction .startParameterNegotiation() diff --git a/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTester.java b/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTester.java index e4c1db52e7..da62128771 100644 --- a/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTester.java +++ b/regressiontests/stable-value/src/test/java/org/apache/isis/testdomain/value/ValueSemanticsTester.java @@ -115,10 +115,10 @@ public class ValueSemanticsTester<T> { val command = interactionService.currentInteractionElseFail().getCommand(); val propInteraction = PropertyInteraction - .wrap(ManagedProperty.of(ManagedObject.of(objSpec, domainObject), prop, Where.OBJECT_FORMS)); + .wrap(ManagedProperty.of(ManagedObject.adaptScalar(objSpec, domainObject), prop, Where.OBJECT_FORMS)); propInteraction.modifyProperty(managedProp-> - ManagedObject.of(managedProp.getElementType(), newProperyValueProvider.apply(managedProp))); + ManagedObject.adaptScalar(managedProp.getElementType(), newProperyValueProvider.apply(managedProp))); probe.testCommand(context, command); }); diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/publishing/PublishingTestFactoryJdo.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/publishing/PublishingTestFactoryJdo.java index afdebc781d..b734d06fc5 100644 --- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/publishing/PublishingTestFactoryJdo.java +++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/publishing/PublishingTestFactoryJdo.java @@ -227,7 +227,7 @@ extends PublishingTestFactoryAbstract { val managedProperty = propertyInteraction.getManagedPropertyElseThrow(__->_Exceptions.noSuchElement()); val propertyModel = managedProperty.startNegotiation(); val propertySpec = managedProperty.getElementType(); - propertyModel.getValue().setValue(ManagedObject.of(propertySpec, "Book #2")); + propertyModel.getValue().setValue(ManagedObject.value(propertySpec, "Book #2")); propertyModel.submit(); }); diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/publishing/PublishingTestFactoryJpa.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/publishing/PublishingTestFactoryJpa.java index 3e58308d07..ca7c5402b9 100644 --- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/publishing/PublishingTestFactoryJpa.java +++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/publishing/PublishingTestFactoryJpa.java @@ -223,7 +223,7 @@ extends PublishingTestFactoryAbstract { val managedProperty = propertyInteraction.getManagedPropertyElseThrow(__->_Exceptions.noSuchElement()); val propertyModel = managedProperty.startNegotiation(); val propertySpec = managedProperty.getElementType(); - propertyModel.getValue().setValue(ManagedObject.of(propertySpec, "Book #2")); + propertyModel.getValue().setValue(ManagedObject.value(propertySpec, "Book #2")); propertyModel.submit(); }); diff --git a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/util/interaction/DomainObjectTesterFactory.java b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/util/interaction/DomainObjectTesterFactory.java index 09424fb3b2..f47b5fb5cd 100644 --- a/regressiontests/stable/src/main/java/org/apache/isis/testdomain/util/interaction/DomainObjectTesterFactory.java +++ b/regressiontests/stable/src/main/java/org/apache/isis/testdomain/util/interaction/DomainObjectTesterFactory.java @@ -625,7 +625,9 @@ public class DomainObjectTesterFactory { @SuppressWarnings("unchecked") static void updatePojo(final ManagedValue managedValue, final UnaryOperator replacer) { - managedValue.update(v->ManagedObject.of(v.getSpecification(), replacer.apply(v.getPojo()))); + managedValue.update(v->ManagedObject.adaptScalar( + v.getSpecification(), + replacer.apply(v.getPojo()))); } @SneakyThrows @@ -1128,7 +1130,10 @@ public class DomainObjectTesterFactory { protected Tester<T> init() { this.objectSpecification = specificationLoader.specForTypeElseFail(domainObjectType); - this.vm = ManagedObject.of(objectSpecification, factoryService.viewModel(domainObjectType)); + this.vm = ManagedObject.viewmodel( + objectSpecification, + factoryService.viewModel(domainObjectType), + Optional.empty()); return this; } diff --git a/viewers/commons/model/src/main/java/org/apache/isis/viewer/commons/model/binding/BindingConverterForManagedObject.java b/viewers/commons/model/src/main/java/org/apache/isis/viewer/commons/model/binding/BindingConverterForManagedObject.java index 06b02e91c7..13e480f34c 100644 --- a/viewers/commons/model/src/main/java/org/apache/isis/viewer/commons/model/binding/BindingConverterForManagedObject.java +++ b/viewers/commons/model/src/main/java/org/apache/isis/viewer/commons/model/binding/BindingConverterForManagedObject.java @@ -34,7 +34,7 @@ implements BindingConverter<ManagedObject, T> { @Override public ManagedObject toLeft(final T pojo) { - return ManagedObject.of(getValueSpecification(), pojo); + return ManagedObject.value(getValueSpecification(), pojo); } @Override diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/BooleanModel.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/BooleanModel.java index b903f15afb..2b0d5a5a88 100644 --- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/BooleanModel.java +++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/BooleanModel.java @@ -59,7 +59,7 @@ extends ChainingModel<Boolean> { @Override public void setObject(final Boolean value) { - val adaptedValue = ManagedObject.of( + val adaptedValue = ManagedObject.value( scalarModel().getScalarTypeSpec(), (value==null && isPrimitive) diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ManagedObjectModel.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ManagedObjectModel.java index eb204fc3df..bc0a884369 100644 --- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ManagedObjectModel.java +++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ManagedObjectModel.java @@ -27,7 +27,6 @@ import org.apache.isis.applib.annotation.BookmarkPolicy; import org.apache.isis.applib.id.LogicalType; import org.apache.isis.applib.services.bookmark.Bookmark; import org.apache.isis.commons.internal.base._Casts; -import org.apache.isis.commons.internal.collections._Collections; import org.apache.isis.core.metamodel.object.ManagedObject; import org.apache.isis.core.metamodel.object.ManagedObjects; import org.apache.isis.core.metamodel.object.PackedManagedObject; @@ -83,7 +82,7 @@ extends ModelAbstract<ManagedObject> { super.setObject(adapter); - if(_Collections.isCollectionOrArrayOrCanType(adapter.getPojo().getClass())) { + if(adapter instanceof PackedManagedObject) { setObjectCollection((PackedManagedObject)adapter); } else { memento = super.getMementoService().mementoForSingle(adapter); diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/util/PageParameterUtils.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/util/PageParameterUtils.java index a68acc0863..44075d5f0b 100644 --- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/util/PageParameterUtils.java +++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/util/PageParameterUtils.java @@ -229,7 +229,7 @@ public class PageParameterUtils { } if(objSpec.isValue()) { - return ManagedObject.of(objSpec, + return ManagedObject.value(objSpec, Facets.valueSerializerElseFail(objSpec, objSpec.getCorrespondingClass()) .fromEncodedString(Format.JSON, encoded)); } diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/IsisToWicketTreeAdapter.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/IsisToWicketTreeAdapter.java index 621dfb6fcb..e205c9a0a3 100644 --- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/IsisToWicketTreeAdapter.java +++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/IsisToWicketTreeAdapter.java @@ -259,7 +259,7 @@ class IsisToWicketTreeAdapter { this.commonContext = commonContext; this.factoryService = commonContext.lookupServiceElseFail(FactoryService.class); this.pojoToAdapter = pojo -> - ManagedObject.wrapScalar(commonContext.getSpecificationLoader(), pojo); + ManagedObject.adaptScalar(commonContext.getSpecificationLoader(), pojo); } private TreeAdapter wrappedTreeAdapter() { diff --git a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/services/DeepLinkServiceWicket.java b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/services/DeepLinkServiceWicket.java index bbfdde5944..db3751d98c 100644 --- a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/services/DeepLinkServiceWicket.java +++ b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/services/DeepLinkServiceWicket.java @@ -58,7 +58,7 @@ public class DeepLinkServiceWicket implements DeepLinkService { @Override public URI deepLinkFor(final Object domainObject) { - final ManagedObject objectAdapter = ManagedObject.wrapScalar(specificationLoader, domainObject); + final ManagedObject objectAdapter = ManagedObject.adaptScalar(specificationLoader, domainObject); final PageParameters pageParameters = PageParameterUtils.createPageParametersForObject(objectAdapter);