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 3875249322 ISIS-3295: fixes post-construct not called, when viewmodel new-ed up programmatically 3875249322 is described below commit 3875249322ab308921be2724845a4cf00a95c1ac Author: Andi Huber <ahu...@apache.org> AuthorDate: Fri Nov 25 09:39:32 2022 +0100 ISIS-3295: fixes post-construct not called, when viewmodel new-ed up programmatically --- .../applib/services/factory/FactoryService.java | 2 +- .../facets/object/viewmodel/ViewModelFacet.java | 7 +++ .../object/viewmodel/ViewModelFacetAbstract.java | 12 +++- .../factory/FactoryServiceDefault.java | 68 ++++++++++++++-------- 4 files changed, 61 insertions(+), 28 deletions(-) diff --git a/api/applib/src/main/java/org/apache/causeway/applib/services/factory/FactoryService.java b/api/applib/src/main/java/org/apache/causeway/applib/services/factory/FactoryService.java index 499833b7a4..81dee2ddc6 100644 --- a/api/applib/src/main/java/org/apache/causeway/applib/services/factory/FactoryService.java +++ b/api/applib/src/main/java/org/apache/causeway/applib/services/factory/FactoryService.java @@ -154,7 +154,7 @@ public interface FactoryService { * with injection points resolved, * post-construct called * and defaults applied. - * @param domainClass - must be a <i>Spring</i> managed type + * @param domainClass - must NOT be a <i>Spring</i> managed type * @throws IllegalArgumentException if domainClass is a <i>Spring</i> managed type, * or not recognized by the meta-model * @apiNote forces the domainClass to be added to the meta-model if not already diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/viewmodel/ViewModelFacet.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/viewmodel/ViewModelFacet.java index 73f8bc30ca..dc74b4778f 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/viewmodel/ViewModelFacet.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/viewmodel/ViewModelFacet.java @@ -20,6 +20,8 @@ package org.apache.causeway.core.metamodel.facets.object.viewmodel; import java.util.Optional; +import org.springframework.lang.Nullable; + import org.apache.causeway.applib.annotation.Nature; import org.apache.causeway.applib.services.bookmark.Bookmark; import org.apache.causeway.core.metamodel.facetapi.Facet; @@ -54,4 +56,9 @@ public interface ViewModelFacet extends Facet { */ Bookmark serializeToBookmark(ManagedObject managedObject); + /** + * For given view-model pojo resolves injection points, then calls post-construct method(s) if any. + */ + void initialize(@Nullable Object pojo); + } diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/viewmodel/ViewModelFacetAbstract.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/viewmodel/ViewModelFacetAbstract.java index 9ad17cf8c0..ddc2b2e157 100644 --- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/viewmodel/ViewModelFacetAbstract.java +++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/viewmodel/ViewModelFacetAbstract.java @@ -21,6 +21,8 @@ package org.apache.causeway.core.metamodel.facets.object.viewmodel; import java.lang.reflect.Method; import java.util.Optional; +import org.springframework.lang.Nullable; + import org.apache.causeway.applib.services.bookmark.Bookmark; import org.apache.causeway.commons.internal.base._Strings; import org.apache.causeway.core.metamodel.commons.CanonicalInvoker; @@ -74,11 +76,17 @@ implements ViewModelFacet { ? createViewmodel(spec) : createViewmodel(spec, bookmark); - getServiceInjector().injectServicesInto(viewModel.getPojo()); - invokePostConstructMethod(viewModel.getPojo()); + initialize(viewModel.getPojo()); return viewModel; } + @Override + public final void initialize(final @Nullable Object pojo) { + if(pojo==null) return; + getServiceInjector().injectServicesInto(pojo); + invokePostConstructMethod(pojo); + } + /** * Create default viewmodel instance (without any {@link Bookmark} available). */ diff --git a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/factory/FactoryServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/factory/FactoryServiceDefault.java index 37535df515..4a56c4a41a 100644 --- a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/factory/FactoryServiceDefault.java +++ b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/factory/FactoryServiceDefault.java @@ -61,7 +61,7 @@ public class FactoryServiceDefault implements FactoryService { @Override public <T> T getOrCreate(final @NonNull Class<T> requiredType) { - val spec = loadSpec(requiredType); + val spec = loadSpecElseFail(requiredType); if(spec.isInjectable()) { return get(requiredType); } @@ -77,19 +77,19 @@ public class FactoryServiceDefault implements FactoryService { @Override public <T> T detachedEntity(final @NonNull Class<T> domainClass) { - val entitySpec = loadSpec(domainClass); + val entitySpec = loadSpecElseFail(domainClass); if(!entitySpec.isEntity()) { throw _Exceptions.illegalArgument("Class '%s' is not an entity", domainClass.getName()); } - return _Casts.uncheckedCast(createObject(entitySpec)); + return createObject(domainClass, entitySpec); } @Override public <T> T detachedEntity(final @NonNull T entityPojo) { val entityClass = entityPojo.getClass(); - val spec = loadSpec(entityClass); + val spec = loadSpecElseFail(entityClass); if(!spec.isEntity()) { - throw _Exceptions.illegalArgument("Type '%s' is not recogniced as an entity type by the framework.", + throw _Exceptions.illegalArgument("Type '%s' is not recognized as an entity type by the framework.", entityClass); } objectLifecyclePublisher().onPostCreate(ManagedObject.entity(spec, entityPojo, Optional.empty())); return entityPojo; @@ -97,7 +97,7 @@ public class FactoryServiceDefault implements FactoryService { @Override public <T> T mixin(final @NonNull Class<T> mixinClass, final @NonNull Object mixee) { - val mixinSpec = loadSpec(mixinClass); + val mixinSpec = loadSpecElseFail(mixinClass); val mixinFacet = mixinSpec.getFacet(MixinFacet.class); if(mixinFacet == null) { throw _Exceptions.illegalArgument("Class '%s' is not a mixin", @@ -114,49 +114,67 @@ public class FactoryServiceDefault implements FactoryService { @Override public <T> T viewModel(final @NonNull T viewModelPojo) { val viewModelClass = viewModelPojo.getClass(); - val spec = loadSpec(viewModelClass); + val spec = loadSpecElseFail(viewModelClass); if(!spec.isViewModel()) { - throw _Exceptions.illegalArgument("Type '%s' is not recogniced as a ViewModel by the framework.", + throw _Exceptions.illegalArgument("Type '%s' is not recognized as a ViewModel by the framework.", viewModelClass); } + spec.viewmodelFacetElseFail().initialize(viewModelPojo); objectLifecyclePublisher().onPostCreate(ManagedObject.viewmodel(spec, viewModelPojo, Optional.empty())); return viewModelPojo; } @Override public <T> T viewModel(final @NonNull Class<T> viewModelClass, final @Nullable Bookmark bookmark) { - val spec = loadSpec(viewModelClass); - if(!spec.isViewModel()) { - throw _Exceptions.illegalArgument("Type '%s' is not recogniced as a ViewModel by the framework.", - viewModelClass); - } - val viewModelFacet = spec.viewmodelFacetElseFail(); - val viewModel = viewModelFacet.instantiate(spec, Optional.ofNullable(bookmark)); - objectLifecyclePublisher().onPostCreate(viewModel); - return _Casts.uncheckedCast(viewModel.getPojo()); + val spec = loadSpecElseFail(viewModelClass); + return createViewModelElseFail(viewModelClass, spec, Optional.ofNullable(bookmark)); } @Override public <T> T create(final @NonNull Class<T> domainClass) { - val spec = loadSpec(domainClass); - + val spec = loadSpecElseFail(domainClass); if(spec.isInjectable()) { throw _Exceptions.illegalArgument( - "Class '%s' is managed by IoC container, use get() instead", domainClass.getName()); + "Class '%s' is managed by Spring, use get() instead", domainClass.getName()); } - return _Casts.uncheckedCast(createObject(spec)); + if(spec.isViewModel()) { + return createViewModelElseFail(domainClass, spec, Optional.empty()); + } + if(spec.isEntity()) { + return detachedEntity(domainClass); + } + // fallback to generic object creation + return createObject(domainClass, spec); } // -- HELPER - private ObjectSpecification loadSpec(final @NonNull Class<?> type) { + private ObjectSpecification loadSpecElseFail(final @NonNull Class<?> type) { return specificationLoader.specForTypeElseFail(type); } - private Object createObject(final ObjectSpecification spec) { - // already handles injection and publishing + /** handles injection, post-construct and publishing */ + private <T> T createViewModelElseFail( + final @NonNull Class<T> viewModelClass, + final @NonNull ObjectSpecification objectSpecification, + final @NonNull Optional<Bookmark> bookmarkIfAny) { + return Optional.of(objectSpecification) + .filter(ObjectSpecification::isViewModel) + .<T>map(spec->{ + val viewModel = spec.viewmodelFacetElseFail().instantiate(spec, bookmarkIfAny); + objectLifecyclePublisher().onPostCreate(viewModel); + return _Casts.uncheckedCast(viewModel.getPojo()); + }) + .orElseThrow(()->_Exceptions.illegalArgument("Type '%s' is not recognized as a ViewModel by the framework.", + viewModelClass)); + } + + /** handles injection and publishing, but probably not post-construct */ + private <T> T createObject( + final @NonNull Class<?> type, + final @NonNull ObjectSpecification spec) { val domainObject = spec.createObject(); - return domainObject.getPojo(); + return _Casts.uncheckedCast(domainObject.getPojo()); } }