You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2021/06/18 15:04:52 UTC

[isis] 01/01: ISIS-2751: fixes ModuleWithFixturesService

This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch ISIS-2751
in repository https://gitbox.apache.org/repos/asf/isis.git

commit ad27a281a603e9d5b0913a9c5b86ab6feddb7ebc
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Fri Jun 18 15:44:56 2021 +0100

    ISIS-2751: fixes ModuleWithFixturesService
    
    to return sequenced
---
 .../SpringServiceInjectOrderTest.java              | 212 +++++++++++++--------
 .../applib/modules/ModuleWithFixturesService.java  |  90 ++++++---
 2 files changed, 195 insertions(+), 107 deletions(-)

diff --git a/regressiontests/stable-bootstrapping/src/test/java/org/apache/isis/testdomain/bootstrapping/SpringServiceInjectOrderTest.java b/regressiontests/stable-bootstrapping/src/test/java/org/apache/isis/testdomain/bootstrapping/SpringServiceInjectOrderTest.java
index 436acf6..3527d32 100644
--- a/regressiontests/stable-bootstrapping/src/test/java/org/apache/isis/testdomain/bootstrapping/SpringServiceInjectOrderTest.java
+++ b/regressiontests/stable-bootstrapping/src/test/java/org/apache/isis/testdomain/bootstrapping/SpringServiceInjectOrderTest.java
@@ -53,11 +53,14 @@ import org.apache.isis.testdomain.conf.Configuration_headless;
 import lombok.Getter;
 import lombok.val;
 
+=======
+        >>>>>>>4dd5fb2460(ISIS-2751:fixes ModuleWithFixturesService)
+
 @SpringBootTest(
-        classes = { 
+        classes = {
                 Configuration_headless.class,
                 SpringServiceInjectOrderTest.TestConfig.class,
-                
+
                 SpringServiceInjectOrderTest.Average.class,
                 SpringServiceInjectOrderTest.Excellent.class,
                 SpringServiceInjectOrderTest.Good.class,
@@ -68,89 +71,19 @@ import lombok.val;
                 // "logging.level.ObjectSpecificationAbstract=TRACE"
         })
 @TestPropertySource({
-    IsisPresets.SilenceMetaModel,
-    IsisPresets.SilenceProgrammingModel,
-    IsisPresets.UseLog4j2Test
+        IsisPresets.SilenceMetaModel,
+        IsisPresets.SilenceProgrammingModel,
+        IsisPresets.UseLog4j2Test
 })
 class SpringServiceInjectOrderTest {
 
-    @Configuration
-    static class TestConfig {
-    }
-
-    interface Rating {
-        int getRating();
-    }
-    
-    @Service
-    @Order(PriorityPrecedence.EARLY)
-    @Qualifier("tallest")
-    @Named("withExcellentName")
-    static class Excellent implements Rating {
-
-        @Override
-        public int getRating() {
-            return 1;
-        }
-    }
-
-    @Service
-    @Priority(PriorityPrecedence.MIDPOINT)
-    @Qualifier("tall")
-    @Named("withGoodName")
-    static class Good implements Rating {
+    @Inject
+    DummyService dummyService;
+    @Inject
+    ServiceInjector serviceInjector;
+    @Inject
+    OrderComparator orderComparator;
 
-        @Override
-        public int getRating() {
-            return 2;
-        }
-    }
-
-    @Service
-    @Order(PriorityPrecedence.LAST)
-    @Qualifier("middle")
-    @Named("withAverageName")
-    static class Average implements Rating {
-     
-        @Override
-        public int getRating() {
-            return 3;
-        }
-    }
-    
-    @Service
-    static class DummyService {
-        @Inject @Getter MessageService messageService;
-        @Inject @Getter List<Rating> ratings;
-        @Inject @Getter Rating primaryRating;
-        @Inject @Getter Rating someArbitraryRating;
-        @Inject @Getter @Qualifier("tallest") Rating qualifiedRating1;
-        @Inject @Getter @Qualifier("tall") Rating qualifiedRating2;
-        @Inject @Getter @Qualifier("middle") Rating qualifiedRating3;
-        @Inject @Getter Rating tallest;
-        @Inject @Getter Rating mostExcellentName;
-
-        // this doesn't bootstrap, because matching is done using the service's @Qualifier, not the service's @Name
-        // @Inject @Getter @Qualifier("mostExcellentName") Rating namedRating1;
-    }
-    
-    @DomainObject
-    static class DummyObject {
-        @Inject @Getter MessageService messageService;
-        @Inject @Getter List<Rating> ratings;
-        @Inject @Getter Rating someArbitraryRating;
-        @Inject @Getter @Qualifier("tallest") Rating qualifiedRating1;
-        @Inject @Getter @Qualifier("tall") Rating qualifiedRating2;
-        @Inject @Getter @Qualifier("middle") Rating qualifiedRating3;
-        @Inject @Getter Rating tallest;
-        @Inject @Getter Rating mostExcellentName;
-    }
-    
-    
-    @Inject DummyService dummyService;
-    @Inject ServiceInjector serviceInjector;
-    @Inject OrderComparator orderComparator;
-    
     @BeforeEach
     void beforeEach() {
 
@@ -160,7 +93,7 @@ class SpringServiceInjectOrderTest {
     void defaultOrdering_shouldConsiderAnnotations() throws IOException {
         assertTrue(orderComparator instanceof AnnotationAwareOrderComparator);
     }
-    
+
     @Test
     void injectionOnServices_shouldFollowOrder() throws IOException {
 
@@ -188,7 +121,7 @@ class SpringServiceInjectOrderTest {
         // does NOT match field name to @Qualifier
         assertThat(dummyService.getMostExcellentName().getRating(), is(equalTo(2)));  // rather than '1'... so defaulted to @Primary
     }
-    
+
     @Test
     void injectionOnObjects_shouldFollowOrder() throws IOException {
 
@@ -221,4 +154,117 @@ class SpringServiceInjectOrderTest {
 
     }
 
+
+    interface Rating {
+        int getRating();
+    }
+
+    @Configuration
+    static class TestConfig {
+    }
+
+    @Service
+    @Order(PriorityPrecedence.EARLY)
+    @Qualifier("tallest")
+    @Named("withExcellentName")
+    static class Excellent implements Rating {
+
+        @Override
+        public int getRating() {
+            return 1;
+        }
+    }
+
+    @Service
+    @Priority(PriorityPrecedence.MIDPOINT)
+    @Qualifier("tall")
+    @Named("withGoodName")
+    static class Good implements Rating {
+
+        @Override
+        public int getRating() {
+            return 2;
+        }
+    }
+
+    @Service
+    @Order(PriorityPrecedence.LAST)
+    @Qualifier("middle")
+    @Named("withAverageName")
+    static class Average implements Rating {
+
+        @Override
+        public int getRating() {
+            return 3;
+        }
+    }
+
+    @Service
+    static class DummyService {
+        @Inject
+        @Getter
+        MessageService messageService;
+        @Inject
+        @Getter
+        List<Rating> ratings;
+        @Inject
+        @Getter
+        Rating primaryRating;
+        @Inject
+        @Getter
+        Rating someArbitraryRating;
+        @Inject
+        @Getter
+        @Qualifier("tallest")
+        Rating qualifiedRating1;
+        @Inject
+        @Getter
+        @Qualifier("tall")
+        Rating qualifiedRating2;
+        @Inject
+        @Getter
+        @Qualifier("middle")
+        Rating qualifiedRating3;
+        @Inject
+        @Getter
+        Rating tallest;
+        @Inject
+        @Getter
+        Rating mostExcellentName;
+
+        // this doesn't bootstrap, because matching is done using the service's @Qualifier, not the service's @Name
+        // @Inject @Getter @Qualifier("mostExcellentName") Rating namedRating1;
+    }
+
+    @DomainObject
+    static class DummyObject {
+        @Inject
+        @Getter
+        MessageService messageService;
+        @Inject
+        @Getter
+        List<Rating> ratings;
+        @Inject
+        @Getter
+        Rating someArbitraryRating;
+        @Inject
+        @Getter
+        @Qualifier("tallest")
+        Rating qualifiedRating1;
+        @Inject
+        @Getter
+        @Qualifier("tall")
+        Rating qualifiedRating2;
+        @Inject
+        @Getter
+        @Qualifier("middle")
+        Rating qualifiedRating3;
+        @Inject
+        @Getter
+        Rating tallest;
+        @Inject
+        @Getter
+        Rating mostExcellentName;
+    }
+
 }
diff --git a/testing/fixtures/applib/src/main/java/org/apache/isis/testing/fixtures/applib/modules/ModuleWithFixturesService.java b/testing/fixtures/applib/src/main/java/org/apache/isis/testing/fixtures/applib/modules/ModuleWithFixturesService.java
index de2b122..364f5f9 100644
--- a/testing/fixtures/applib/src/main/java/org/apache/isis/testing/fixtures/applib/modules/ModuleWithFixturesService.java
+++ b/testing/fixtures/applib/src/main/java/org/apache/isis/testing/fixtures/applib/modules/ModuleWithFixturesService.java
@@ -18,18 +18,15 @@
  */
 package org.apache.isis.testing.fixtures.applib.modules;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.annotation.Priority;
-import javax.inject.Inject;
-import javax.inject.Named;
-
+import lombok.Data;
+import lombok.extern.log4j.Log4j2;
+import lombok.val;
+import org.apache.isis.applib.annotation.PriorityPrecedence;
+import org.apache.isis.core.metamodel.facets.Annotations;
+import org.apache.isis.subdomains.spring.applib.service.BeanDescriptor;
+import org.apache.isis.subdomains.spring.applib.service.ContextBeans;
+import org.apache.isis.subdomains.spring.applib.service.SpringBeansService;
+import org.apache.isis.testing.fixtures.applib.fixturescripts.FixtureScript;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.ConfigurableApplicationContext;
 import org.springframework.context.annotation.Configuration;
@@ -38,16 +35,11 @@ import org.springframework.context.event.ContextRefreshedEvent;
 import org.springframework.context.event.EventListener;
 import org.springframework.stereotype.Service;
 
-import org.apache.isis.applib.annotation.PriorityPrecedence;
-import org.apache.isis.core.metamodel.facets.Annotations;
-import org.apache.isis.subdomains.spring.applib.service.BeanDescriptor;
-import org.apache.isis.subdomains.spring.applib.service.ContextBeans;
-import org.apache.isis.subdomains.spring.applib.service.SpringBeansService;
-import org.apache.isis.testing.fixtures.applib.fixturescripts.FixtureScript;
-
-import lombok.Data;
-import lombok.val;
-import lombok.extern.log4j.Log4j2;
+import javax.annotation.Priority;
+import javax.inject.Inject;
+import javax.inject.Named;
+import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * @since 2.x {@index}
@@ -95,10 +87,14 @@ public class ModuleWithFixturesService {
     }
 
     public List<ModuleWithFixturesDescriptor> modules() {
+        val beans = springBeansService.beans();
+        val modules = modulesWithin(beans);
+        return sequenced(modules);
+    }
 
+    static List<ModuleWithFixturesDescriptor> modulesWithin(Map<String, ContextBeans> beans) {
         final List<ModuleWithFixturesDescriptor> descriptors = new ArrayList<>();
-        final Map<String, ContextBeans> contexts = springBeansService.beans();
-        for (Map.Entry<String, ContextBeans> contextEntry : contexts.entrySet()) {
+        for (Map.Entry<String, ContextBeans> contextEntry : beans.entrySet()) {
             final String contextId = contextEntry.getKey();
             final ContextBeans contextBeans = contextEntry.getValue();
             final ConfigurableApplicationContext context = contextBeans.getContext();
@@ -150,6 +146,52 @@ public class ModuleWithFixturesService {
         return descriptors;
     }
 
+
+    static List<ModuleWithFixturesDescriptor> sequenced(List<ModuleWithFixturesDescriptor> modules) {
+        val remaining = new ArrayList<>(modules);
+        val sequenced = new ArrayList<ModuleWithFixturesDescriptor>();
+
+        val moduleByName = new LinkedHashMap<String, ModuleWithFixturesDescriptor>();
+        modules.forEach(module -> {
+            moduleByName.put(module.getBeanName(), module);
+        });
+
+        while(!remaining.isEmpty()) {
+            ModuleWithFixturesDescriptor added = addNextModule(sequenced, remaining, moduleByName);
+            if (added == null) {
+                throw new IllegalStateException(String.format(
+                        "Unable to determine next module.\nfound = %s\nremaining = %s",
+                        beanNamesOf(sequenced), beanNamesOf(remaining)));
+            }
+            remaining.remove(added);
+        }
+
+        return sequenced;
+    }
+
+    static List<String> beanNamesOf(ArrayList<ModuleWithFixturesDescriptor> result) {
+        return result.stream().map(ModuleWithFixturesDescriptor::getBeanName).collect(Collectors.toList());
+    }
+
+    static ModuleWithFixturesDescriptor addNextModule(
+            final List<ModuleWithFixturesDescriptor> result,
+            final List<ModuleWithFixturesDescriptor> remaining,
+            final LinkedHashMap<String, ModuleWithFixturesDescriptor> moduleByName) {
+
+        for (ModuleWithFixturesDescriptor module : remaining) {
+            val numDependenciesNotYetEncountered =
+                    module.getDependenciesByName().keySet().stream()
+                            .map(moduleByName::get)
+                            .filter(dependency -> !result.contains(dependency)) // ignore if already known about
+                            .count();
+            if(numDependenciesNotYetEncountered == 0) {
+                result.add(module);
+                return module;
+            }
+        }
+        return null;
+    }
+
     @EventListener(ContextRefreshedEvent.class)
     public void onContextRefreshed(ContextRefreshedEvent event) {
         log.info("onContextRefreshed");