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();
+ }
+
+
+}