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 2019/10/01 09:15:21 UTC

[isis] branch v2 updated: ISIS-2158: attempt to simplify some of the spec-loading code

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

ahuber pushed a commit to branch v2
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/v2 by this push:
     new ef3aa85  ISIS-2158: attempt to simplify some of the spec-loading code
ef3aa85 is described below

commit ef3aa85f1edfe4a73a1ca7f70c22daa9546789b7
Author: Andi Huber <ah...@apache.org>
AuthorDate: Tue Oct 1 11:15:09 2019 +0200

    ISIS-2158: attempt to simplify some of the spec-loading code
---
 .../apache/isis/commons/internal/base/_Timing.java |  5 ++
 .../specloader/SpecificationCacheDefault.java      | 91 ++++++++++------------
 .../specloader/SpecificationLoaderDefault.java     | 15 ++--
 .../specloader/SpecificationCacheDefaultTest.java  | 24 +++---
 .../testdomain/conf/Configuration_usingJdo.java    |  8 +-
 .../conf/Configuration_usingJdoAndShiro.java       |  6 --
 .../bad/Configuration_usingInvalidDomain.java      |  9 +--
 .../model/good/Configuration_usingValidDomain.java |  9 +--
 .../domainmodel/SpecloaderPerformanceTest.java     | 63 +++++++++++++++
 9 files changed, 131 insertions(+), 99 deletions(-)

diff --git a/core/commons/src/main/java/org/apache/isis/commons/internal/base/_Timing.java b/core/commons/src/main/java/org/apache/isis/commons/internal/base/_Timing.java
index 1ed5f3d..7aa6995 100644
--- a/core/commons/src/main/java/org/apache/isis/commons/internal/base/_Timing.java
+++ b/core/commons/src/main/java/org/apache/isis/commons/internal/base/_Timing.java
@@ -100,6 +100,11 @@ public final class _Timing {
         public long getNanos() {
             return stopped ? t1 - t0 : System.nanoTime() - t0 ;
         }
+        
+        @Override
+        public String toString() {
+            return String.format(Locale.US, "%.2f ms", getMillis());
+        }
 
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationCacheDefault.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationCacheDefault.java
index df19632..647602a 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationCacheDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationCacheDefault.java
@@ -30,6 +30,8 @@ import org.apache.isis.metamodel.facets.object.objectspecid.ObjectSpecIdFacet;
 import org.apache.isis.metamodel.spec.ObjectSpecId;
 import org.apache.isis.metamodel.spec.ObjectSpecification;
 
+import lombok.AccessLevel;
+import lombok.Getter;
 import lombok.val;
 
 
@@ -48,7 +50,9 @@ import lombok.val;
 class SpecificationCacheDefault<T extends ObjectSpecification> {
 
     private final Map<String, T> specByClassName = _Maps.newHashMap();
-    private Map<ObjectSpecId, String> classNameBySpecId;
+    private final Map<ObjectSpecId, String> classNameBySpecId = _Maps.newHashMap();
+    
+    @Getter(AccessLevel.PACKAGE) private boolean initialized = false; //package scoped JUnit support
 
     public T get(final String className) {
         return specByClassName.get(className);
@@ -57,17 +61,19 @@ class SpecificationCacheDefault<T extends ObjectSpecification> {
     public T computeIfAbsent(
             final String className, 
             final Function<? super String, T> mappingFunction) {
-        return specByClassName.computeIfAbsent(className, mappingFunction);
-    }
-
-    public void put(final String className, final T spec) {
-        specByClassName.put(className, spec);
-        recache(spec);
+        
+        T spec = specByClassName.get(className);
+        if(spec==null) {
+            spec = mappingFunction.apply(className);
+            internalPut(spec);
+        }
+        return spec;
     }
 
-
     public void clear() {
+        initialized = false;
         specByClassName.clear();
+        classNameBySpecId.clear();
     }
 
     /** @returns thread-safe defensive copy */
@@ -85,8 +91,7 @@ class SpecificationCacheDefault<T extends ObjectSpecification> {
         return className != null ? specByClassName.get(className) : null;
     }
 
-    synchronized void init() {
-        val specById = _Maps.<ObjectSpecId, T>newHashMap();
+    void init() {
         val cachedSpecifications = _Lists.<T>newArrayList();
         for(;;) {
             val newSpecifications = snapshotSpecs();
@@ -94,64 +99,46 @@ class SpecificationCacheDefault<T extends ObjectSpecification> {
             if(newSpecifications.isEmpty()) {
                 break;
             }
-            for (val objSpec : newSpecifications) {
-                val objectSpecId = objSpec.getSpecId();
-                if (objectSpecId == null) {
-                    continue;
-                }
-                specById.put(objectSpecId, objSpec);
+            for (val spec : newSpecifications) {
+                internalPut(spec);
             }
             cachedSpecifications.addAll(newSpecifications);
         }
-
-        internalInit(specById);
+        initialized = true;
     }
 
-    void internalInit(final Map<ObjectSpecId, T> specById) {
-        val classNameBySpecId = _Maps.<ObjectSpecId, String>newHashMap();
-        val specByClassName = _Maps.<String, T>newHashMap();
-
-        for (val objectSpecId : specById.keySet()) {
-            val objectSpec = specById.get(objectSpecId);
-            val className = objectSpec.getCorrespondingClass().getName();
-            classNameBySpecId.put(objectSpecId, className);
-            specByClassName.put(className, objectSpec);
+    private void internalPut(T spec) {
+        if(spec==null) {
+            return;
         }
-        this.classNameBySpecId = classNameBySpecId;
-        this.specByClassName.clear();
-        this.specByClassName.putAll(specByClassName);
+        val className = spec.getCorrespondingClass().getName();
+        val specId = spec.getSpecId();
+        specByClassName.put(className, spec);
+        if (specId == null) {
+            return;
+        }
+        classNameBySpecId.put(specId, className);
     }
 
     public T remove(String typeName) {
-        T removed = specByClassName.remove(typeName);
-        if(removed != null) {
-            if(removed.containsDoOpFacet(ObjectSpecIdFacet.class)) {
-                // umm.  It turns out that anonymous inner classes (eg org.estatio.dom.WithTitleGetter$ToString$1)
-                // don't have an ObjectSpecId; hence the guard.
-                ObjectSpecId specId = removed.getSpecId();
-                classNameBySpecId.remove(specId);
-            }
+        final T removed = specByClassName.remove(typeName);
+        if(hasUsableSpecId(removed)) {
+            val specId = removed.getSpecId();
+            classNameBySpecId.remove(specId);
         }
         return removed;
     }
 
-    /**
-     * @param spec
-     */
     public void recache(T spec) {
-        if(!isInitialized()) {
-            // JRebel plugin might call this before we are actually up and running;
-            // just ignore.
-            return;
+        if(hasUsableSpecId(spec)) {
+            classNameBySpecId.put(spec.getSpecId(), spec.getCorrespondingClass().getName());
         }
-        if(!spec.containsDoOpFacet(ObjectSpecIdFacet.class)) {
-            return;
-        }
-        classNameBySpecId.put(spec.getSpecId(), spec.getCorrespondingClass().getName());
     }
-
-    boolean isInitialized() {
-        return classNameBySpecId != null;
+    
+    private boolean hasUsableSpecId(T spec) {
+        // umm.  It turns out that anonymous inner classes (eg org.estatio.dom.WithTitleGetter$ToString$1)
+        // don't have an ObjectSpecId; hence the guard.
+        return spec!=null && spec.containsDoOpFacet(ObjectSpecIdFacet.class);
     }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationLoaderDefault.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationLoaderDefault.java
index e10c8aa..53b1aa3 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationLoaderDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/SpecificationLoaderDefault.java
@@ -29,6 +29,7 @@ import javax.inject.Inject;
 import org.springframework.stereotype.Service;
 
 import org.apache.isis.commons.internal.base._Casts;
+import org.apache.isis.commons.internal.base._Timing;
 import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.config.IsisConfiguration;
 import org.apache.isis.config.registry.IsisBeanTypeRegistry;
@@ -125,14 +126,13 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
      */
     @Override
     public void init() {
-
+        
         if (log.isDebugEnabled()) {
             log.debug("initialising {}", this);
         }
-
-        // wire subcomponents into each other
-        //facetProcessor.setServicesInjector(servicesInjector);
-
+        
+        val stopWatch = _Timing.now();
+        
         // initialize subcomponents
         programmingModel.init();
         facetProcessor.init();
@@ -209,7 +209,8 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
             introspect(cachedSpecifications, IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
         }
 
-        log.info("init() - done");
+        stopWatch.stop();
+        log.info("init() - took " + stopWatch);
 
     }
 
@@ -277,7 +278,7 @@ public class SpecificationLoaderDefault implements SpecificationLoader {
             // umm.  It turns out that anonymous inner classes (eg org.estatio.dom.WithTitleGetter$ToString$1)
             // don't have an ObjectSpecId; hence the guard.
             if(spec.containsDoOpFacet(ObjectSpecIdFacet.class)) {
-                ObjectSpecId specId = spec.getSpecId();
+                val specId = spec.getSpecId();
                 if (cache.getByObjectType(specId) == null) {
                     cache.recache(spec);
                 }
diff --git a/core/metamodel/src/test/java/org/apache/isis/metamodel/specloader/SpecificationCacheDefaultTest.java b/core/metamodel/src/test/java/org/apache/isis/metamodel/specloader/SpecificationCacheDefaultTest.java
index 9996ebb..14ac36a 100644
--- a/core/metamodel/src/test/java/org/apache/isis/metamodel/specloader/SpecificationCacheDefaultTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/metamodel/specloader/SpecificationCacheDefaultTest.java
@@ -18,8 +18,6 @@
  */
 package org.apache.isis.metamodel.specloader;
 
-import java.util.Map;
-
 import org.jmock.Expectations;
 import org.jmock.auto.Mock;
 import org.junit.After;
@@ -27,7 +25,6 @@ import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 
-import org.apache.isis.commons.internal.collections._Maps;
 import org.apache.isis.metamodel.spec.ObjectSpecId;
 import org.apache.isis.metamodel.spec.ObjectSpecification;
 import org.apache.isis.unittestsupport.jmocking.JUnitRuleMockery2;
@@ -60,8 +57,15 @@ public class SpecificationCacheDefaultTest {
             allowing(customerSpec).getCorrespondingClass();
             will(returnValue(Customer.class));
 
+            allowing(customerSpec).getSpecId();
+            will(returnValue(ObjectSpecId.of("CUS")));
+            
             allowing(orderSpec).getCorrespondingClass();
             will(returnValue(Order.class));
+            
+            allowing(orderSpec).getSpecId();
+            will(returnValue(ObjectSpecId.of("ORD")));
+            
         }});
     }
 
@@ -81,7 +85,7 @@ public class SpecificationCacheDefaultTest {
     @Test
     public void get_whenCached() {
         final String customerClassName = Customer.class.getName();
-        specificationCache.put(customerClassName, customerSpec);
+        specificationCache.computeIfAbsent(customerClassName, __->customerSpec);
 
         final ObjectSpecification objectSpecification = specificationCache.get(customerClassName);
 
@@ -91,8 +95,8 @@ public class SpecificationCacheDefaultTest {
 
     @Test
     public void allSpecs_whenCached() {
-        specificationCache.put(Customer.class.getName(), customerSpec);
-        specificationCache.put(Order.class.getName(), orderSpec);
+        specificationCache.computeIfAbsent(Customer.class.getName(), __->customerSpec);
+        specificationCache.computeIfAbsent(Order.class.getName(), __->orderSpec);
 
         val allSpecs = specificationCache.snapshotSpecs();
 
@@ -106,11 +110,9 @@ public class SpecificationCacheDefaultTest {
 
     @Test
     public void getByObjectType_whenSet() {
-        Map<ObjectSpecId, ObjectSpecification> specByObjectType = _Maps.newHashMap();
-        specByObjectType.put(ObjectSpecId.of("CUS"), customerSpec);
-
-        specificationCache.internalInit(specByObjectType);
-        final ObjectSpecification objectSpec = specificationCache.getByObjectType(ObjectSpecId.of("CUS"));
+        specificationCache.init();
+        specificationCache.computeIfAbsent(Customer.class.getName(), __->customerSpec);
+        val objectSpec = specificationCache.getByObjectType(ObjectSpecId.of("CUS"));
 
         assertSame(objectSpec, customerSpec);
     }
diff --git a/examples/smoketests/src/main/java/org/apache/isis/testdomain/conf/Configuration_usingJdo.java b/examples/smoketests/src/main/java/org/apache/isis/testdomain/conf/Configuration_usingJdo.java
index 9d80eff..6bd9065 100644
--- a/examples/smoketests/src/main/java/org/apache/isis/testdomain/conf/Configuration_usingJdo.java
+++ b/examples/smoketests/src/main/java/org/apache/isis/testdomain/conf/Configuration_usingJdo.java
@@ -22,15 +22,12 @@ import javax.inject.Singleton;
 
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.ComponentScan;
-import org.springframework.context.annotation.ComponentScan.Filter;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.FilterType;
 import org.springframework.context.annotation.Import;
 import org.springframework.context.annotation.PropertySource;
 import org.springframework.context.annotation.PropertySources;
 
 import org.apache.isis.config.IsisPresets;
-import org.apache.isis.config.beans.IsisBeanScanInterceptorForSpring;
 import org.apache.isis.config.beans.WebAppConfigBean;
 import org.apache.isis.extensions.fixtures.IsisBootFixtures;
 import org.apache.isis.jdo.IsisBootDataNucleus;
@@ -48,10 +45,7 @@ import org.apache.isis.testdomain.jdo.JdoTestDomainModule;
 @ComponentScan(
         basePackageClasses= {               
                 JdoTestDomainModule.class
-        },
-        includeFilters= {
-                @Filter(type = FilterType.CUSTOM, classes= {IsisBeanScanInterceptorForSpring.class})}
-        )
+        })
 @PropertySources({
     @PropertySource("classpath:/org/apache/isis/testdomain/conf/isis-non-changing.properties"),
     @PropertySource("classpath:/org/apache/isis/testdomain/jdo/isis-persistence.properties"),
diff --git a/examples/smoketests/src/main/java/org/apache/isis/testdomain/conf/Configuration_usingJdoAndShiro.java b/examples/smoketests/src/main/java/org/apache/isis/testdomain/conf/Configuration_usingJdoAndShiro.java
index 7a8f479..ab111cc 100644
--- a/examples/smoketests/src/main/java/org/apache/isis/testdomain/conf/Configuration_usingJdoAndShiro.java
+++ b/examples/smoketests/src/main/java/org/apache/isis/testdomain/conf/Configuration_usingJdoAndShiro.java
@@ -22,15 +22,12 @@ import javax.inject.Singleton;
 
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.ComponentScan;
-import org.springframework.context.annotation.ComponentScan.Filter;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.FilterType;
 import org.springframework.context.annotation.Import;
 import org.springframework.context.annotation.PropertySource;
 import org.springframework.context.annotation.PropertySources;
 
 import org.apache.isis.config.IsisPresets;
-import org.apache.isis.config.beans.IsisBeanScanInterceptorForSpring;
 import org.apache.isis.config.beans.WebAppConfigBean;
 import org.apache.isis.extensions.fixtures.IsisBootFixtures;
 import org.apache.isis.extensions.secman.api.SecurityModuleConfig;
@@ -53,9 +50,6 @@ import org.apache.isis.testdomain.jdo.JdoTestDomainModule;
 @ComponentScan(
         basePackageClasses= {               
                 JdoTestDomainModule.class
-        },
-        includeFilters= {
-                @Filter(type = FilterType.CUSTOM, classes= {IsisBeanScanInterceptorForSpring.class})
         })
 @PropertySources({
     @PropertySource("classpath:/org/apache/isis/testdomain/conf/isis-non-changing.properties"),
diff --git a/examples/smoketests/src/main/java/org/apache/isis/testdomain/model/bad/Configuration_usingInvalidDomain.java b/examples/smoketests/src/main/java/org/apache/isis/testdomain/model/bad/Configuration_usingInvalidDomain.java
index 9ce372b..ab6c45e 100644
--- a/examples/smoketests/src/main/java/org/apache/isis/testdomain/model/bad/Configuration_usingInvalidDomain.java
+++ b/examples/smoketests/src/main/java/org/apache/isis/testdomain/model/bad/Configuration_usingInvalidDomain.java
@@ -19,20 +19,13 @@
 package org.apache.isis.testdomain.model.bad;
 
 import org.springframework.context.annotation.ComponentScan;
-import org.springframework.context.annotation.ComponentScan.Filter;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.FilterType;
-
-import org.apache.isis.config.beans.IsisBeanScanInterceptorForSpring;
 
 @Configuration
 @ComponentScan(
         basePackageClasses= {               
                 Configuration_usingInvalidDomain.class
-        },
-        includeFilters= {
-                @Filter(type = FilterType.CUSTOM, classes= {IsisBeanScanInterceptorForSpring.class})}
-        )
+        })
 public class Configuration_usingInvalidDomain {
 
 }
diff --git a/examples/smoketests/src/main/java/org/apache/isis/testdomain/model/good/Configuration_usingValidDomain.java b/examples/smoketests/src/main/java/org/apache/isis/testdomain/model/good/Configuration_usingValidDomain.java
index 49a7504..3917cf9 100644
--- a/examples/smoketests/src/main/java/org/apache/isis/testdomain/model/good/Configuration_usingValidDomain.java
+++ b/examples/smoketests/src/main/java/org/apache/isis/testdomain/model/good/Configuration_usingValidDomain.java
@@ -19,20 +19,13 @@
 package org.apache.isis.testdomain.model.good;
 
 import org.springframework.context.annotation.ComponentScan;
-import org.springframework.context.annotation.ComponentScan.Filter;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.FilterType;
-
-import org.apache.isis.config.beans.IsisBeanScanInterceptorForSpring;
 
 @Configuration
 @ComponentScan(
         basePackageClasses= {               
                 Configuration_usingValidDomain.class
-        },
-        includeFilters= {
-                @Filter(type = FilterType.CUSTOM, classes= {IsisBeanScanInterceptorForSpring.class})}
-        )
+        })
 public class Configuration_usingValidDomain {
 
 }
diff --git a/examples/smoketests/src/test/java/org/apache/isis/testdomain/domainmodel/SpecloaderPerformanceTest.java b/examples/smoketests/src/test/java/org/apache/isis/testdomain/domainmodel/SpecloaderPerformanceTest.java
new file mode 100644
index 0000000..dab5cfe
--- /dev/null
+++ b/examples/smoketests/src/test/java/org/apache/isis/testdomain/domainmodel/SpecloaderPerformanceTest.java
@@ -0,0 +1,63 @@
+/*
+ *  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.testdomain.domainmodel;
+
+import javax.inject.Inject;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.TestPropertySource;
+
+import org.apache.isis.config.IsisPresets;
+import org.apache.isis.integtestsupport.validate.ValidateDomainModel;
+import org.apache.isis.metamodel.specloader.SpecificationLoader;
+import org.apache.isis.testdomain.Smoketest;
+import org.apache.isis.testdomain.conf.Configuration_headless;
+import org.apache.isis.testdomain.model.good.Configuration_usingValidDomain;
+
+import lombok.val;
+
+@Smoketest
+@SpringBootTest(
+        classes = { 
+                Configuration_headless.class,
+                Configuration_usingValidDomain.class
+        }, 
+        properties = {
+                "isis.reflector.introspector.mode=FULL",
+                "isis.reflector.validator.explicitObjectType=FALSE", // does not override any of the imports
+        })
+@TestPropertySource({
+    IsisPresets.DebugValidation,
+    IsisPresets.DebugProgrammingModel,
+    
+})
+class SpecloaderPerformanceTest {
+    
+    @Inject private SpecificationLoader specificationLoader;
+    
+    //@Test under constr.
+    void repeatedSpecloading() {
+           
+        specificationLoader.shutdown();
+        specificationLoader.init();
+    }
+    
+
+}