You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2020/01/24 10:22:00 UTC

[isis] 02/02: ISIS-2158: adds general purpose FactoryService.getOrCreate(requiredType)

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

commit a9675c8945038d8ffcbdeaeeb28b4f256da4dbf4
Author: Andi Huber <ah...@apache.org>
AuthorDate: Fri Jan 24 11:21:47 2020 +0100

    ISIS-2158: adds general purpose FactoryService.getOrCreate(requiredType)
    
    - fixes the tree demo
---
 .../applib/services/factory/FactoryService.java    | 77 ++++++++++------------
 .../ApplicationFeatureRepositoryDefaultTest.java   | 22 +++----
 .../factory/FactoryServiceDefault.java             | 30 +++++++--
 .../subdomains/excel/testing/ExcelFixture2.java    |  6 +-
 .../components/tree/IsisToWicketTreeAdapter.java   |  2 +-
 5 files changed, 77 insertions(+), 60 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/factory/FactoryService.java b/api/applib/src/main/java/org/apache/isis/applib/services/factory/FactoryService.java
index 4214b3f..fab230d 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/factory/FactoryService.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/factory/FactoryService.java
@@ -23,18 +23,31 @@ import java.util.NoSuchElementException;
 
 import javax.annotation.Nullable;
 
-import org.apache.isis.applib.services.repository.RepositoryService;
 import org.apache.isis.core.commons.exceptions.IsisException;
 
 public interface FactoryService {
 
     /**
+     * Carefree general purpose factory method, to automatically get or create an instance of
+     * {@code requiredType}. 
+     * <p>
+     * Maps onto one of the specialized factory methods {@link #get(Class)} or {@link #create(Class)} 
+     * based on the type's meta-data.
+     * @param <T>
+     * @param requiredType
+     * @return
+     * @throws NoSuchElementException if result is empty
+     * @throws IsisException if instance creation failed
+     */
+    <T> T getOrCreate(Class<T> requiredType);
+    
+    /**
      * Gets an instance (possibly shared or independent) of the specified {@code requiredType}, 
      * with injection points resolved 
      * and any life-cycle callback processed.
      * 
      * @param <T>
-     * @param requiredType
+     * @param requiredType - only applicable to IoC container managed types
      * @return (non-null), an instance of {@code requiredType}, if available and unique
      * (i.e. not multiple candidates found with none marked as primary)
      * 
@@ -48,36 +61,44 @@ public interface FactoryService {
     <T> T get(Class<T> requiredType);
     
     /**
-     * Creates a new detached entity instance, with injection points resolved.
+     * Creates a new detached entity instance, with injection points resolved
+     * and defaults applied.
      * @param <T>
-     * @param domainClass
+     * @param domainClass - only applicable to entity types
      * @return
+     * @throws IllegalArgumentException if domainClass is not an entity type  
      * @apiNote forces the domainClass to be added to the meta-model if not already
      */
     <T> T detachedEntity(Class<T> domainClass);
 
     /**
-     * Creates a new Mixin instance.
+     * Creates a new Mixin instance, with injection points resolved.
      * @param <T>
      * @param mixinClass
      * @param mixedIn
      * @return
+     * @throws IllegalArgumentException if mixinClass is not a mixin type
      * @apiNote forces the mixinClass to be added to the meta-model if not already
      */
     <T> T mixin(Class<T> mixinClass, Object mixedIn);
 
     /**
-     * Creates a new ViewModel instance, and initializes according to the given {@code mementoStr} 
+     * Creates a new ViewModel instance, with injection points resolved, 
+     * and initialized according to the given {@code mementoStr} 
      * @param viewModelClass
      * @param mementoStr - ignored if {@code null}
+     * @throws IllegalArgumentException if viewModelClass is not a viewmodel type
      * @apiNote forces the viewModelClass to be added to the meta-model if not already
      * @since 2.0
      */
     <T> T viewModel(Class<T> viewModelClass, @Nullable String mementoStr);
 
     /**
-     * Creates a new ViewModel instance 
+     * Creates a new ViewModel instance, 
+     * with injection points resolved
+     * and defaults applied. 
      * @param viewModelClass
+     * @throws IllegalArgumentException if viewModelClass is not a viewmodel type
      * @apiNote forces the viewModelClass to be added to the meta-model if not already
      * @since 2.0
      */
@@ -85,42 +106,16 @@ public interface FactoryService {
         return viewModel(viewModelClass, /*mementoStr*/null);
     }
 
-    // -- DEPRECATIONS
-    
     /**
-     * Creates a new instance of the specified class, but does not persist it.
-     *
-     * <p>
-     * It is recommended that the object be initially instantiated using
-     * this method, though the framework will also handle the case when
-     * the object is simply <i>new()</i>ed up.  The benefits of using
-     * {@link #instantiate(Class)} are:
-     * </p>
-     *
-     * <ul>
-     * <li>any services will be injected into the object immediately
-     *     (otherwise they will not be injected until the framework
-     *     becomes aware of the object, typically when it is
-     *     {@link RepositoryService#persist(Object) persist}ed</li>
-     * <li>the default value for any properties (usually as specified by
-     *     <tt>default<i>Xxx</i>()</tt> supporting methods) will (since 2.0) be
-     *     used</li>
-     * <li>the <tt>created()</tt> callback will not be called.
-     * </ul>
-     *
-     * <p>
-     * The corollary is: if your code never uses <tt>default<i>Xxx</i>()</tt>
-     * supporting methods or the <tt>created()</tt> callback, then you can
-     * alternatively just <i>new()</i> up the object rather than call this
-     * method.
-     * </p>
-     * @deprecated with semantic changes since 2.0 previous behavior is no longer guaranteed, 
-     * instead consider use of {@link #detachedEntity(Class)} if applicable
+     * Creates a new instance of the specified class, 
+     * with injection points resolved
+     * and defaults applied.
+     * @param domainClass - not applicable to IoC container managed types
+     * @throws IllegalArgumentException if domainClass is not an IoC container managed type
+     * @apiNote forces the domainClass to be added to the meta-model if not already
+     * @since 2.0
      */
-    @Deprecated
-    default <T> T instantiate(Class<T> domainClass) {
-        return detachedEntity(domainClass);
-    }
+    <T> T create(Class<T> domainClass);
 
 
 }
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefaultTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefaultTest.java
index 9d66edf..9f93871 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefaultTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureRepositoryDefaultTest.java
@@ -180,43 +180,43 @@ public class ApplicationFeatureRepositoryDefaultTest {
             // then
             final Sequence sequence = context.sequence("loadSequence");
             context.checking(new Expectations() {{
-                oneOf(mockFactoryService).instantiate(ApplicationFeature.class);
+                oneOf(mockFactoryService).create(ApplicationFeature.class);
                 inSequence(sequence);
                 will(returnValue(new ApplicationFeature(ApplicationFeatureId.newClass(Bar.class.getName()))));
 
-                oneOf(mockFactoryService).instantiate(ApplicationFeature.class);
+                oneOf(mockFactoryService).create(ApplicationFeature.class);
                 inSequence(sequence);
                 will(returnValue(new ApplicationFeature(ApplicationFeatureId.newMember(Bar.class.getName(), "someProperty"))));
 
-                oneOf(mockFactoryService).instantiate(ApplicationFeature.class);
+                oneOf(mockFactoryService).create(ApplicationFeature.class);
                 inSequence(sequence);
                 will(returnValue(new ApplicationFeature(ApplicationFeatureId.newMember(Bar.class.getName(), "someCollection"))));
 
-                oneOf(mockFactoryService).instantiate(ApplicationFeature.class);
+                oneOf(mockFactoryService).create(ApplicationFeature.class);
                 inSequence(sequence);
                 will(returnValue(new ApplicationFeature(ApplicationFeatureId.newMember(Bar.class.getName(), "someAction"))));
 
-                oneOf(mockFactoryService).instantiate(ApplicationFeature.class);
+                oneOf(mockFactoryService).create(ApplicationFeature.class);
                 inSequence(sequence);
                 will(returnValue(new ApplicationFeature(ApplicationFeatureId.newPackage("org.isisaddons.module.security.dom.feature"))));
 
-                oneOf(mockFactoryService).instantiate(ApplicationFeature.class);
+                oneOf(mockFactoryService).create(ApplicationFeature.class);
                 inSequence(sequence);
                 will(returnValue(new ApplicationFeature(ApplicationFeatureId.newPackage("org.isisaddons.module.security.dom"))));
 
-                oneOf(mockFactoryService).instantiate(ApplicationFeature.class);
+                oneOf(mockFactoryService).create(ApplicationFeature.class);
                 inSequence(sequence);
                 will(returnValue(new ApplicationFeature(ApplicationFeatureId.newPackage("org.isisaddons.module.security"))));
 
-                oneOf(mockFactoryService).instantiate(ApplicationFeature.class);
+                oneOf(mockFactoryService).create(ApplicationFeature.class);
                 inSequence(sequence);
                 will(returnValue(new ApplicationFeature(ApplicationFeatureId.newPackage("org.isisaddons.module"))));
 
-                oneOf(mockFactoryService).instantiate(ApplicationFeature.class);
+                oneOf(mockFactoryService).create(ApplicationFeature.class);
                 inSequence(sequence);
                 will(returnValue(new ApplicationFeature(ApplicationFeatureId.newPackage("org.isisaddons"))));
 
-                oneOf(mockFactoryService).instantiate(ApplicationFeature.class);
+                oneOf(mockFactoryService).create(ApplicationFeature.class);
                 inSequence(sequence);
                 will(returnValue(new ApplicationFeature(ApplicationFeatureId.newPackage("org"))));
             }});
@@ -285,7 +285,7 @@ public class ApplicationFeatureRepositoryDefaultTest {
             final ApplicationFeature newlyCreatedParent = new ApplicationFeature();
 
             context.checking(new Expectations() {{
-                allowing(mockFactoryService).instantiate(ApplicationFeature.class);
+                allowing(mockFactoryService).create(ApplicationFeature.class);
                 will(returnValue(newlyCreatedParent));
             }});
 
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 8450470..8c54b3d 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
@@ -48,6 +48,7 @@ import static org.apache.isis.core.commons.internal.base._With.requires;
 import static org.apache.isis.core.commons.internal.reflection._Reflect.Filter.paramAssignableFrom;
 import static org.apache.isis.core.commons.internal.reflection._Reflect.Filter.paramCount;
 
+import lombok.NonNull;
 import lombok.val;
 
 @Service
@@ -64,14 +65,23 @@ public class FactoryServiceDefault implements FactoryService {
     @Inject private ObjectManager objectManager;
 
     @Override
-    public <T> T get(final Class<T> requiredType) {
+    public <T> T getOrCreate(@NonNull Class<T> requiredType) {
+        val spec = specificationLoader.loadSpecification(requiredType);
+        if(spec.isManagedBean()) {
+            return get(requiredType);
+        }
+        return create(requiredType);
+    }
+    
+    @Override
+    public <T> T get(@NonNull Class<T> requiredType) {
         return isisSystemEnvironment.getIocContainer()
                 .get(requiredType)
                 .orElseThrow(_Exceptions::noSuchElement);
     }
 
     @Override
-    public <T> T detachedEntity(final Class<T> domainClass) {
+    public <T> T detachedEntity(@NonNull Class<T> domainClass) {
         val spec = specificationLoader.loadSpecification(domainClass);
         if(!spec.isEntity()) {
             throw _Exceptions.illegalArgument("Class '%s' is not an entity", domainClass.getName());
@@ -80,7 +90,7 @@ public class FactoryServiceDefault implements FactoryService {
     }
     
     @Override
-    public <T> T mixin(final Class<T> mixinClass, final Object mixedIn) {
+    public <T> T mixin(@NonNull Class<T> mixinClass, @NonNull Object mixedIn) {
         val objectSpec = specificationLoader.loadSpecification(mixinClass);
         val mixinFacet = objectSpec.getFacet(MixinFacet.class);
         if(mixinFacet == null) {
@@ -109,7 +119,7 @@ public class FactoryServiceDefault implements FactoryService {
     }
 
     @Override
-    public <T> T viewModel(Class<T> viewModelClass, String mementoStr) {
+    public <T> T viewModel(@NonNull Class<T> viewModelClass, String mementoStr) {
         requires(viewModelClass, "viewModelClass");
 
         val spec = specificationLoader.loadSpecification(viewModelClass);
@@ -125,6 +135,16 @@ public class FactoryServiceDefault implements FactoryService {
         return _Casts.uncheckedCast(viewModel);
     }
     
+    @Override
+    public <T> T create(@NonNull Class<T> domainClass) {
+        val spec = specificationLoader.loadSpecification(domainClass);
+        if(spec.isManagedBean()) {
+            throw _Exceptions.illegalArgument(
+                    "Class '%s' is managed by IoC container, use get() instead", domainClass.getName());
+        }
+        return _Casts.uncheckedCast(createObject(spec));
+    }
+    
     // -- HELEPR    
     
     private Object createObject(ObjectSpecification spec) {
@@ -134,4 +154,6 @@ public class FactoryServiceDefault implements FactoryService {
     }
 
 
+
+
 }
diff --git a/subdomains/excel/testing/src/main/java/org/apache/isis/subdomains/excel/testing/ExcelFixture2.java b/subdomains/excel/testing/src/main/java/org/apache/isis/subdomains/excel/testing/ExcelFixture2.java
index e6b6aa0..0afed88 100644
--- a/subdomains/excel/testing/src/main/java/org/apache/isis/subdomains/excel/testing/ExcelFixture2.java
+++ b/subdomains/excel/testing/src/main/java/org/apache/isis/subdomains/excel/testing/ExcelFixture2.java
@@ -16,10 +16,10 @@ import org.apache.isis.applib.services.factory.FactoryService;
 import org.apache.isis.applib.services.registry.ServiceRegistry;
 import org.apache.isis.applib.services.repository.RepositoryService;
 import org.apache.isis.applib.value.Blob;
-import org.apache.isis.testing.fixtures.applib.fixturescripts.FixtureScript;
-import org.apache.isis.testing.fixtures.applib.fixturescripts.FixtureScripts;
 import org.apache.isis.subdomains.excel.applib.dom.ExcelService;
 import org.apache.isis.subdomains.excel.applib.dom.WorksheetSpec;
+import org.apache.isis.testing.fixtures.applib.fixturescripts.FixtureScript;
+import org.apache.isis.testing.fixtures.applib.fixturescripts.FixtureScripts;
 
 import lombok.Getter;
 import lombok.Setter;
@@ -121,7 +121,7 @@ public class ExcelFixture2 extends FixtureScript {
             @Override
             public T create() {
                 final T importLine =
-                        factoryService.instantiate(rowClass);
+                        factoryService.getOrCreate(rowClass);
                 // allow the line to interact with the calling fixture
                 if(importLine instanceof FixtureAwareRowHandler<?>) {
                     final FixtureAwareRowHandler<?> farh = (FixtureAwareRowHandler<?>) importLine;
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 34b1ab4..63093e6 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
@@ -261,7 +261,7 @@ class IsisToWicketTreeAdapter {
             }
             try {
                 ensureInit(); // in case we were de-serialzed
-                return wrappedTreeAdapter = factoryService.instantiate(treeAdapterClass);
+                return wrappedTreeAdapter = factoryService.getOrCreate(treeAdapterClass);
             } catch (Exception e) {
                 throw new RuntimeException("failed to instantiate tree adapter", e);
             }