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/09/20 07:54:11 UTC

[isis] branch v2 updated: ISIS-2158: adds new debugging presets

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 b2e054b  ISIS-2158: adds new debugging presets
b2e054b is described below

commit b2e054ba0bb4a51d6ee449c3f09f5e72692b7b79
Author: Andi Huber <ah...@apache.org>
AuthorDate: Fri Sep 20 09:53:51 2019 +0200

    ISIS-2158: adds new debugging presets
    
    - debug logging for the programming model and its refiners
    - also adds stubs for metamodel validation smoketesting with good and
    bad domain-objects
---
 .../services/metamodel/MetaModelService.java       |  12 ++
 .../java/org/apache/isis/config/IsisPresets.java   |  92 +-------------
 .../resources/presets/DebugDiscovery.properties    |  18 +++
 .../presets/DebugProgrammingModel.properties       |  18 +++
 .../resources/presets/DebugValidation.properties   |  18 +++
 .../MetaModelValidatorServiceDefault.java          |  15 ++-
 .../specloader/ProgrammingModelServiceDefault.java |  21 +++-
 .../specloader/SpecificationLoaderDefault.java     |   1 -
 .../isis/webapp/IsisWebAppContextListener.java     |   5 +-
 .../runtime/system/session/IsisSessionFactory.java |   3 -
 .../system/session/IsisSessionFactoryDefault.java  | 139 ++++++++++-----------
 .../testdomain/conf/Configuration_headless.java    |  15 ---
 .../apache/isis/testdomain/model/bad/BadTitle.java |  30 +++++
 .../bad/Configuration_usingInvalidDomain.java      |  38 ++++++
 .../model/good/Configuration_usingValidDomain.java |  38 ++++++
 .../testdomain/model/good/GoodDomainObject.java    |  15 +++
 .../isis/testdomain/model/good/ValidDomain.java    |  27 ++++
 .../domainmodel/SupportingMethodTest.java          |  88 +++++++++++++
 18 files changed, 403 insertions(+), 190 deletions(-)

diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/metamodel/MetaModelService.java b/core/applib/src/main/java/org/apache/isis/applib/services/metamodel/MetaModelService.java
index c11cfa7..b1e5778 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/metamodel/MetaModelService.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/metamodel/MetaModelService.java
@@ -93,6 +93,8 @@ public interface MetaModelService {
         private static final int IGNORE_ABSTRACT_CLASSES = 4;
         private static final int IGNORE_BUILT_IN_VALUE_TYPES = 8;
         private static final int IGNORE_MIXINS = 16;
+        
+        private static final String WILDCARD = "*";
 
         private final int mask;
         
@@ -131,6 +133,16 @@ public interface MetaModelService {
             return new Config(mask | x, packagePrefixes);
         }
 
+        public Config withPackagePrefixAny() {
+            val newPrefixes = _Sets.<String>newHashSet();
+            newPrefixes.add(WILDCARD);
+            return new Config(mask, newPrefixes);
+        }
+        
+        public boolean isPackagePrefixAny() {
+            return packagePrefixes.contains(WILDCARD);
+        }
+        
         /**
          * Returns a new {@code Config} with given {@code packagePrefix} added to the set of 
          * this {@code Config}'s packagePrefixes.
diff --git a/core/config/src/main/java/org/apache/isis/config/IsisPresets.java b/core/config/src/main/java/org/apache/isis/config/IsisPresets.java
index 124bf67..81a93e9 100644
--- a/core/config/src/main/java/org/apache/isis/config/IsisPresets.java
+++ b/core/config/src/main/java/org/apache/isis/config/IsisPresets.java
@@ -19,76 +19,13 @@
 package org.apache.isis.config;
 
 /**
- * Introduced to support Spring's {@code @PropertySource} annotation.
+ * Supports Spring's {@code @PropertySource} annotation.
  * 
  * @since 2.0
  *
  */
 public final class IsisPresets  {
 
-//    public static final String ISIS_PERSISTOR                   = "isis.persistor.";
-//    public static final String ISIS_PERSISTOR_DATANUCLEUS       = ISIS_PERSISTOR + "datanucleus.";
-//    public static final String ISIS_PERSISTOR_DATANUCLEUS_IMPL  = ISIS_PERSISTOR_DATANUCLEUS + "impl.";
-
-//    @RequiredArgsConstructor
-//    private static enum Providers {
-//
-//        H2InMemory(Providers::withH2InMemoryProperties),
-//        HsqlDbInMemory(Providers::withHsqlDbInMemoryProperties),
-//        DataNucleusAutoCreate(Providers::withDataNucleusProperties),
-//        IsisIntegTest(Providers::withIsisIntegTestProperties),
-//        NoTranslations(map->map.put("isis.services.translation.po.mode", "disable")),
-//        ;
-//
-//        private final Consumer<Map<String, String>> populator;
-//
-//        private static Map<String,String> withH2InMemoryProperties(final Map<String, String> map) {
-//            //map.put(ISIS_PERSISTOR_DATANUCLEUS_IMPL + "javax.jdo.option.ConnectionURL", "jdbc:h2:mem:test-" + UUID.randomUUID().toString());
-//            map.put(ISIS_PERSISTOR_DATANUCLEUS_IMPL + "javax.jdo.option.ConnectionURL", "jdbc:h2:mem:test");
-//            map.put(ISIS_PERSISTOR_DATANUCLEUS_IMPL + "javax.jdo.option.ConnectionDriverName", "org.h2.Driver");
-//            map.put(ISIS_PERSISTOR_DATANUCLEUS_IMPL + "javax.jdo.option.ConnectionUserName", "sa");
-//            map.put(ISIS_PERSISTOR_DATANUCLEUS_IMPL + "javax.jdo.option.ConnectionPassword", "");
-//
-//            return map;
-//        }
-//
-//        private static Map<String,String> withHsqlDbInMemoryProperties(final Map<String, String> map) {
-//            //map.put(ISIS_PERSISTOR_DATANUCLEUS_IMPL + "javax.jdo.option.ConnectionURL", "jdbc:hsqldb:mem:test-" + UUID.randomUUID().toString());
-//            map.put(ISIS_PERSISTOR_DATANUCLEUS_IMPL + "javax.jdo.option.ConnectionURL", "jdbc:hsqldb:mem:test");
-//            map.put(ISIS_PERSISTOR_DATANUCLEUS_IMPL + "javax.jdo.option.ConnectionDriverName", "org.hsqldb.jdbcDriver");
-//            map.put(ISIS_PERSISTOR_DATANUCLEUS_IMPL + "javax.jdo.option.ConnectionUserName", "sa");
-//            map.put(ISIS_PERSISTOR_DATANUCLEUS_IMPL + "javax.jdo.option.ConnectionPassword", "");
-//
-//            return map;
-//        }
-//
-//        private static Map<String,String> withDataNucleusProperties(final Map<String, String> map) {
-//
-//            // Don't do validations that consume setup time.
-//            map.put(ISIS_PERSISTOR_DATANUCLEUS_IMPL + "datanucleus.schema.autoCreateAll", "true");
-//            map.put(ISIS_PERSISTOR_DATANUCLEUS_IMPL + "datanucleus.schema.validateAll", "false");
-//
-//            // other properties as per WEB-INF/persistor_datanucleus.properties
-//            map.put(ISIS_PERSISTOR_DATANUCLEUS_IMPL + "datanucleus.persistenceByReachabilityAtCommit", "false");
-//            map.put(ISIS_PERSISTOR_DATANUCLEUS_IMPL + "datanucleus.identifier.case", "MixedCase");
-//            map.put(ISIS_PERSISTOR_DATANUCLEUS_IMPL + "datanucleus.cache.level2.type"  ,"none");
-//            map.put(ISIS_PERSISTOR_DATANUCLEUS_IMPL + "datanucleus.cache.level2.mode", "ENABLE_SELECTIVE");
-//
-//            return map;
-//        }
-//
-//        private static Map<String,String> withIsisIntegTestProperties(final Map<String, String> map) {
-//
-//            // automatically install any fixtures that might have been registered
-//            map.put(ISIS_PERSISTOR_DATANUCLEUS + "install-fixtures", "true");
-//            map.put(ISIS_PERSISTOR + "enforceSafeSemantics", "false");
-//            map.put("isis.services.eventbus.allowLateRegistration", "true");
-//
-//            return map;
-//        }
-//
-//    }
-
     public static final String NoTranslations = "classpath:/presets/NoTranslations.properties";
     
     public static final String H2InMemory = "classpath:/presets/H2InMemory.properties";
@@ -96,8 +33,9 @@ public final class IsisPresets  {
     public static final String DataNucleusAutoCreate = "classpath:/presets/DataNucleusAutoCreate.properties";
     
     public static final String DebugPersistence = "classpath:/presets/DebugPersistence.properties";
-    public static final String DebugDiscovery = "logging.level.org.apache.isis.config.registry.IsisBeanTypeRegistry=DEBUG";
-    
+    public static final String DebugDiscovery = "classpath:/presets/DebugDiscovery.properties";
+    public static final String DebugProgrammingModel = "classpath:/presets/DebugProgrammingModel.properties";
+    public static final String DebugValidation = "classpath:/presets/DebugValidation.properties";
     
     /**
      * @deprecated seems no longer required anyway
@@ -105,28 +43,6 @@ public final class IsisPresets  {
     @Deprecated
     public static final String IsisIntegTest = "classpath:/presets/IsisIntegTest.properties";
     
-
-
-//    public static class Factory implements PropertySourceFactory {
-//
-//        @Override
-//        public PropertySource<?> createPropertySource(
-//                String name,
-//                EncodedResource resource) throws IOException {
-//
-//            val map = new HashMap<String, String>(); 
-//            Providers.valueOf(name).populator.accept(map);
-//
-//            val widenedMap = new HashMap<String, Object>();
-//            widenedMap.putAll(map);
-//
-//            val propSource = new MapPropertySource(name, widenedMap);
-//
-//            return propSource;
-//        }
-//
-//    }
-
     /**
      * Use PROTOTYPING mode as the default. Does not override if the system-property 
      * 'PROTOTYPING' was already set.
diff --git a/core/config/src/main/resources/presets/DebugDiscovery.properties b/core/config/src/main/resources/presets/DebugDiscovery.properties
new file mode 100644
index 0000000..45a70de
--- /dev/null
+++ b/core/config/src/main/resources/presets/DebugDiscovery.properties
@@ -0,0 +1,18 @@
+#  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.
+
+logging.level.org.apache.isis.config.registry.IsisBeanTypeRegistry = DEBUG
diff --git a/core/config/src/main/resources/presets/DebugProgrammingModel.properties b/core/config/src/main/resources/presets/DebugProgrammingModel.properties
new file mode 100644
index 0000000..07313c8
--- /dev/null
+++ b/core/config/src/main/resources/presets/DebugProgrammingModel.properties
@@ -0,0 +1,18 @@
+#  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.
+
+logging.level.org.apache.isis.metamodel.specloader.ProgrammingModelServiceDefault = DEBUG
diff --git a/core/config/src/main/resources/presets/DebugValidation.properties b/core/config/src/main/resources/presets/DebugValidation.properties
new file mode 100644
index 0000000..7648c2b
--- /dev/null
+++ b/core/config/src/main/resources/presets/DebugValidation.properties
@@ -0,0 +1,18 @@
+#  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.
+
+logging.level.org.apache.isis.metamodel.specloader.MetaModelValidatorServiceDefault = DEBUG
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/MetaModelValidatorServiceDefault.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/MetaModelValidatorServiceDefault.java
index 40b388b..18f6e3c 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/MetaModelValidatorServiceDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/MetaModelValidatorServiceDefault.java
@@ -32,11 +32,12 @@ import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorComposit
 import org.apache.isis.metamodel.specloader.validator.MetaModelValidatorService;
 
 import lombok.val;
+import lombok.extern.log4j.Log4j2;
 
 /**
  * @since 2.0
  */
-@Singleton
+@Singleton @Log4j2
 public class MetaModelValidatorServiceDefault implements MetaModelValidatorService {
 
     @Override
@@ -55,7 +56,9 @@ public class MetaModelValidatorServiceDefault implements MetaModelValidatorServi
 
     private MetaModelValidator createMetaModelValidator() {
 
-        final String metaModelValidatorClassName =
+        log.debug("About to create the MetaModelValidator.");
+        
+        val metaModelValidatorClassName =
                 configuration.getString(
                         ReflectorConstants.META_MODEL_VALIDATOR_CLASS_NAME,
                         ReflectorConstants.META_MODEL_VALIDATOR_CLASS_NAME_DEFAULT);
@@ -71,6 +74,14 @@ public class MetaModelValidatorServiceDefault implements MetaModelValidatorServi
         }
 
         programmingModel.refineMetaModelValidator(mmValidatorComposite);
+        
+        if(log.isDebugEnabled()) {
+            
+            val refinersCount = metaModelRefiners.size();
+            
+            log.debug("MetaModelValidator created with {} refiners.", 
+                    refinersCount);    
+        }
 
         return mmValidator;
     }
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/ProgrammingModelServiceDefault.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/ProgrammingModelServiceDefault.java
index 0091703..93ba399 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/ProgrammingModelServiceDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/ProgrammingModelServiceDefault.java
@@ -28,7 +28,10 @@ import org.apache.isis.metamodel.progmodel.ProgrammingModelAbstract.DeprecatedPo
 import org.apache.isis.metamodel.progmodel.ProgrammingModelService;
 import org.apache.isis.metamodel.progmodels.dflt.ProgrammingModelFacetsJava8;
 
-@Singleton
+import lombok.val;
+import lombok.extern.log4j.Log4j2;
+
+@Singleton @Log4j2
 public class ProgrammingModelServiceDefault implements ProgrammingModelService {
 
     @Override
@@ -44,12 +47,24 @@ public class ProgrammingModelServiceDefault implements ProgrammingModelService {
             _Lazy.threadSafe(this::createProgrammingModel);
 
     private ProgrammingModel createProgrammingModel() {
+        
+        log.debug("About to create the ProgrammingModel.");
 
-        final DeprecatedPolicy deprecatedPolicy = DeprecatedPolicy.parse(configuration);
+        val deprecatedPolicy = DeprecatedPolicy.parse(configuration);
 
-        final ProgrammingModel programmingModel = new ProgrammingModelFacetsJava8(deprecatedPolicy);
+        val programmingModel = new ProgrammingModelFacetsJava8(deprecatedPolicy);
         ProgrammingModel.Util.includeFacetFactories(configuration, programmingModel);
         ProgrammingModel.Util.excludeFacetFactories(configuration, programmingModel);
+        
+        if(log.isDebugEnabled()) {
+            
+            val facetFactoryCount = programmingModel.getList().size();
+            val postProcessorCount = programmingModel.getPostProcessors().size();
+            
+            log.debug("ProgrammingModel created with {} factories and {} post-processors.", 
+                    facetFactoryCount, postProcessorCount);    
+        }
+        
         return programmingModel;
     }
 
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 351ffec..5f3620f 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
@@ -28,7 +28,6 @@ import javax.inject.Inject;
 import javax.inject.Singleton;
 
 import org.apache.isis.commons.internal.collections._Lists;
-import org.apache.isis.commons.internal.concurrent.ConcurrentTaskList;
 import org.apache.isis.commons.internal.context._Context;
 import org.apache.isis.config.IsisConfiguration;
 import org.apache.isis.config.internal._Config;
diff --git a/core/runtime-web/src/main/java/org/apache/isis/webapp/IsisWebAppContextListener.java b/core/runtime-web/src/main/java/org/apache/isis/webapp/IsisWebAppContextListener.java
index 0d10abc..3178d89 100644
--- a/core/runtime-web/src/main/java/org/apache/isis/webapp/IsisWebAppContextListener.java
+++ b/core/runtime-web/src/main/java/org/apache/isis/webapp/IsisWebAppContextListener.java
@@ -19,7 +19,6 @@
 package org.apache.isis.webapp;
 
 import javax.inject.Inject;
-import javax.inject.Singleton;
 import javax.servlet.ServletContext;
 import javax.servlet.ServletContextEvent;
 import javax.servlet.ServletContextListener;
@@ -50,9 +49,9 @@ import lombok.extern.log4j.Log4j2;
  */
 //@WebListener //[ahuber] to support Servlet 3.0 annotations @WebFilter, @WebListener or others 
 //with skinny war deployment requires additional configuration, so for now we disable this annotation
-@Log4j2 @Singleton
+@Log4j2 //@Singleton
 public class IsisWebAppContextListener implements ServletContextListener {
-
+    
     private @Inject ServiceRegistry serviceRegistry; // this dependency ensures Isis has been initialized/provisioned 
 
     // -- INTERFACE IMPLEMENTATION
diff --git a/core/runtime/src/main/java/org/apache/isis/runtime/system/session/IsisSessionFactory.java b/core/runtime/src/main/java/org/apache/isis/runtime/system/session/IsisSessionFactory.java
index 098de8d..184d82a 100644
--- a/core/runtime/src/main/java/org/apache/isis/runtime/system/session/IsisSessionFactory.java
+++ b/core/runtime/src/main/java/org/apache/isis/runtime/system/session/IsisSessionFactory.java
@@ -46,9 +46,6 @@ import org.apache.isis.security.authentication.manager.AuthenticationManager;
  */
 public interface IsisSessionFactory {
 
-    public void initServices();
-    public void destroyServicesAndShutdown();
-
     public IsisSession openSession(final AuthenticationSession authenticationSession);
     public void closeSession();
 
diff --git a/core/runtime/src/main/java/org/apache/isis/runtime/system/session/IsisSessionFactoryDefault.java b/core/runtime/src/main/java/org/apache/isis/runtime/system/session/IsisSessionFactoryDefault.java
index f68eeab..c8d523e 100644
--- a/core/runtime/src/main/java/org/apache/isis/runtime/system/session/IsisSessionFactoryDefault.java
+++ b/core/runtime/src/main/java/org/apache/isis/runtime/system/session/IsisSessionFactoryDefault.java
@@ -50,7 +50,6 @@ import org.apache.isis.metamodel.specloader.validator.MetaModelDeficiencies;
 import org.apache.isis.runtime.system.MessageRegistry;
 import org.apache.isis.runtime.system.context.IsisContext;
 import org.apache.isis.runtime.system.context.session.RuntimeEventService;
-import org.apache.isis.runtime.system.internal.InitialisationSession;
 import org.apache.isis.runtime.system.internal.IsisLocaleInitializer;
 import org.apache.isis.runtime.system.internal.IsisTimeZoneInitializer;
 import org.apache.isis.schema.utils.ChangesDtoUtils;
@@ -67,7 +66,7 @@ import lombok.extern.log4j.Log4j2;
  * a thread-local.
  *
  * <p>
- *     The class can in considered as analogous to (and is in many ways a wrapper for) a JDO
+ *     The class is considered as analogous to (and is in many ways a wrapper for) a JDO
  *     <code>PersistenceManagerFactory</code>.
  * </p>
  *
@@ -110,7 +109,7 @@ public class IsisSessionFactoryDefault implements IsisSessionFactory {
         runtimeEventService.fireAppPreMetamodel();
 
         val taskList = ConcurrentTaskList.named("IsisSessionFactoryDefault Concurrent Tasks");
-        
+
         taskList.addRunnable("SpecificationLoader.init()", ()->{
             // time to initialize...
             specificationLoader.init();
@@ -146,84 +145,85 @@ public class IsisSessionFactoryDefault implements IsisSessionFactory {
         runtimeEventService.fireAppPostMetamodel();
 
         //initServicesAndRunFixtures();
+        //doInSession(this::initServices);
     }
 
-    @Override
-    public void initServices() {
+    private void initServices() {
 
-        doInSession(()->{
+        //
+        // postConstructInSession
+        //
 
-            //
-            // postConstructInSession
-            //
+        //            IsisTransactionManagerJdoInternal transactionManager = getTransactionManagerElseFail();
+        //            transactionManager.executeWithinTransaction(serviceInitializer::postConstruct);
 
-            //            IsisTransactionManagerJdoInternal transactionManager = getTransactionManagerElseFail();
-            //            transactionManager.executeWithinTransaction(serviceInitializer::postConstruct);
+        //
+        // translateServicesAndEnumConstants
+        //
+        val titleService = serviceRegistry.lookupServiceElseFail(TitleService.class);
 
-            //
-            // translateServicesAndEnumConstants
-            //
-            val titleService = serviceRegistry.lookupServiceElseFail(TitleService.class);
-
-            final Stream<Object> domainServices = serviceRegistry
-                    .streamRegisteredBeansOfSort(BeanSort.MANAGED_BEAN)
-                    .map(BeanAdapter::getInstance)
-                    .filter(Bin::isCardinalityOne)
-                    .map(Bin::getSingleton)
-                    .map(Optional::get)
-                    ;
-
-            domainServices.forEach(domainService->{
-                final String unused = titleService.titleOf(domainService);
-                _Blackhole.consume(unused);
-            });
-
-
-            // (previously we took a protective copy to avoid a concurrent modification exception,
-            // but this is now done by SpecificationLoader itself)
-            for (final ObjectSpecification objSpec : IsisContext.getSpecificationLoader().currentSpecifications()) {
-                final Class<?> correspondingClass = objSpec.getCorrespondingClass();
-                if(correspondingClass.isEnum()) {
-                    final Object[] enumConstants = correspondingClass.getEnumConstants();
-                    for (Object enumConstant : enumConstants) {
-                        final String unused = titleService.titleOf(enumConstant);
-                        _Blackhole.consume(unused);
-                    }
-                }
-            }
+        final Stream<Object> domainServices = serviceRegistry
+                .streamRegisteredBeansOfSort(BeanSort.MANAGED_BEAN)
+                .map(BeanAdapter::getInstance)
+                .filter(Bin::isCardinalityOne)
+                .map(Bin::getSingleton)
+                .map(Optional::get)
+                ;
 
-            // as used by the Wicket UI
-            final TranslationService translationService = 
-                    serviceRegistry.lookupServiceElseFail(TranslationService.class);
+        domainServices.forEach(domainService->{
+            final String unused = titleService.titleOf(domainService);
+            _Blackhole.consume(unused);
+        });
 
-            final String context = IsisSessionFactory.class.getName();
-            final MessageRegistry messageRegistry = new MessageRegistry();
-            final List<String> messages = messageRegistry.listMessages();
-            for (String message : messages) {
-                translationService.translate(context, message);
-            }
 
-            // meta-model validation ...            
-            val mmDeficiencies = specificationLoader.validate().getDeficienciesIfAny(); 
-            if(mmDeficiencies!=null) {
-                // no need to use a higher level, such as error(...); the calling code will expose any metamodel
-                // validation errors in their own particular way.
-                if(log.isDebugEnabled()) {
-                    log.debug("Meta model invalid", mmDeficiencies.getValidationErrorsAsString());
+        // (previously we took a protective copy to avoid a concurrent modification exception,
+        // but this is now done by SpecificationLoader itself)
+        for (final ObjectSpecification objSpec : IsisContext.getSpecificationLoader().currentSpecifications()) {
+            final Class<?> correspondingClass = objSpec.getCorrespondingClass();
+            if(correspondingClass.isEnum()) {
+                final Object[] enumConstants = correspondingClass.getEnumConstants();
+                for (Object enumConstant : enumConstants) {
+                    final String unused = titleService.titleOf(enumConstant);
+                    _Blackhole.consume(unused);
                 }
-                _Context.putSingleton(MetaModelDeficiencies.class, mmDeficiencies);
             }
+        }
+
+        // as used by the Wicket UI
+        final TranslationService translationService = 
+                serviceRegistry.lookupServiceElseFail(TranslationService.class);
+
+        final String context = IsisSessionFactory.class.getName();
+        final MessageRegistry messageRegistry = new MessageRegistry();
+        final List<String> messages = messageRegistry.listMessages();
+        for (String message : messages) {
+            translationService.translate(context, message);
+        }
 
-        },
-                new InitialisationSession());
+        // meta-model validation ...            
+        val mmDeficiencies = specificationLoader.validate().getDeficienciesIfAny(); 
+        if(mmDeficiencies!=null) {
+            // no need to use a higher level, such as error(...); the calling code will expose any metamodel
+            // validation errors in their own particular way.
+            if(log.isDebugEnabled()) {
+                log.debug("Meta model invalid", mmDeficiencies.getValidationErrorsAsString());
+            }
+            _Context.putSingleton(MetaModelDeficiencies.class, mmDeficiencies);
+        }
 
     }
 
     @PreDestroy
-    @Override
-    public void destroyServicesAndShutdown() {
+    public void shutdown() {
         // call might originate from a different thread than main
-        shutdown();
+
+        // just in case we still have an open session, must also work if called from a different thread than 'main'
+        openSessions.forEach(IsisSession::close);
+        openSessions.clear();
+
+        runtimeEventService.fireAppPreDestroy();
+        authenticationManager.shutdown();
+        //specificationLoader.shutdown(); //[2112] lifecycle is managed by IoC
     }
 
     // -- 
@@ -301,17 +301,6 @@ public class IsisSessionFactoryDefault implements IsisSessionFactory {
         return authenticationManager;
     }
 
-    // -- HELPER
-
-    private void shutdown() {
 
-        // just in case we still have an open session, must also work if called from a different thread than 'main'
-        openSessions.forEach(IsisSession::close);
-        openSessions.clear();
-
-        runtimeEventService.fireAppPreDestroy();
-        authenticationManager.shutdown();
-        //specificationLoader.shutdown(); //[2112] lifecycle is managed by IoC
-    }
 
 }
diff --git a/examples/smoketests/src/main/java/org/apache/isis/testdomain/conf/Configuration_headless.java b/examples/smoketests/src/main/java/org/apache/isis/testdomain/conf/Configuration_headless.java
index ef5f34c..4d81653 100644
--- a/examples/smoketests/src/main/java/org/apache/isis/testdomain/conf/Configuration_headless.java
+++ b/examples/smoketests/src/main/java/org/apache/isis/testdomain/conf/Configuration_headless.java
@@ -57,43 +57,33 @@ public class Configuration_headless {
 
             @Override
             public TransactionId currentTransactionId() {
-                // TODO Auto-generated method stub
                 return null;
             }
 
             @Override
             public void flushTransaction() {
-                // TODO Auto-generated method stub
-                
             }
 
             @Override
             public TransactionState currentTransactionState() {
-                // TODO Auto-generated method stub
                 return null;
             }
 
             @Override
             public void executeWithinTransaction(Runnable task) {
-                // TODO Auto-generated method stub
-                
             }
 
             @Override
             public <T> T executeWithinTransaction(Supplier<T> task) {
-                // TODO Auto-generated method stub
                 return null;
             }
 
             @Override
             public void executeWithinNewTransaction(Runnable task) {
-                // TODO Auto-generated method stub
-                
             }
 
             @Override
             public <T> T executeWithinNewTransaction(Supplier<T> task) {
-                // TODO Auto-generated method stub
                 return null;
             }
 
@@ -106,20 +96,15 @@ public class Configuration_headless {
             
             @Override
             public void rollback(TransactionStatus status) throws TransactionException {
-                // TODO Auto-generated method stub
-                
             }
             
             @Override
             public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
-                // TODO Auto-generated method stub
                 return null;
             }
             
             @Override
             public void commit(TransactionStatus status) throws TransactionException {
-                // TODO Auto-generated method stub
-                
             }
         };
     }
diff --git a/examples/smoketests/src/main/java/org/apache/isis/testdomain/model/bad/BadTitle.java b/examples/smoketests/src/main/java/org/apache/isis/testdomain/model/bad/BadTitle.java
new file mode 100644
index 0000000..e7c91d9
--- /dev/null
+++ b/examples/smoketests/src/main/java/org/apache/isis/testdomain/model/bad/BadTitle.java
@@ -0,0 +1,30 @@
+package org.apache.isis.testdomain.model.bad;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.Nature;
+import org.apache.isis.applib.annotation.Title;
+
+@DomainObject(nature = Nature.VIEW_MODEL)
+public class BadTitle {
+    
+    @Title 
+    public void invalidTitleProvider() {
+        
+    }
+    
+    @Title 
+    public String invalidTitleProvider2() {
+        return null;
+    }
+    
+    public String title() {
+        return null;
+    }
+    
+    @Action 
+    public void disableSomething() {
+        
+    }
+    
+}
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
new file mode 100644
index 0000000..9ce372b
--- /dev/null
+++ b/examples/smoketests/src/main/java/org/apache/isis/testdomain/model/bad/Configuration_usingInvalidDomain.java
@@ -0,0 +1,38 @@
+/*
+ *  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.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
new file mode 100644
index 0000000..49a7504
--- /dev/null
+++ b/examples/smoketests/src/main/java/org/apache/isis/testdomain/model/good/Configuration_usingValidDomain.java
@@ -0,0 +1,38 @@
+/*
+ *  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.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/main/java/org/apache/isis/testdomain/model/good/GoodDomainObject.java b/examples/smoketests/src/main/java/org/apache/isis/testdomain/model/good/GoodDomainObject.java
new file mode 100644
index 0000000..ddcbe8f
--- /dev/null
+++ b/examples/smoketests/src/main/java/org/apache/isis/testdomain/model/good/GoodDomainObject.java
@@ -0,0 +1,15 @@
+package org.apache.isis.testdomain.model.good;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.Nature;
+
+@DomainObject(nature = Nature.VIEW_MODEL)
+public class GoodDomainObject {
+    
+    @Action 
+    public void disableSomething() {
+        
+    }
+    
+}
diff --git a/examples/smoketests/src/main/java/org/apache/isis/testdomain/model/good/ValidDomain.java b/examples/smoketests/src/main/java/org/apache/isis/testdomain/model/good/ValidDomain.java
new file mode 100644
index 0000000..163c5ff
--- /dev/null
+++ b/examples/smoketests/src/main/java/org/apache/isis/testdomain/model/good/ValidDomain.java
@@ -0,0 +1,27 @@
+/*
+ *  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.model.good;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class ValidDomain {
+
+}
diff --git a/examples/smoketests/src/test/java/org/apache/isis/testdomain/domainmodel/SupportingMethodTest.java b/examples/smoketests/src/test/java/org/apache/isis/testdomain/domainmodel/SupportingMethodTest.java
new file mode 100644
index 0000000..7de2394
--- /dev/null
+++ b/examples/smoketests/src/test/java/org/apache/isis/testdomain/domainmodel/SupportingMethodTest.java
@@ -0,0 +1,88 @@
+/*
+ *  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.applib.services.metamodel.MetaModelService;
+import org.apache.isis.config.IsisPresets;
+import org.apache.isis.integtestsupport.validate.ValidateDomainModel;
+import org.apache.isis.schema.metamodel.v1.DomainClassDto;
+import org.apache.isis.testdomain.conf.Configuration_headless;
+import org.apache.isis.testdomain.model.bad.Configuration_usingInvalidDomain;
+
+import lombok.val;
+
+@SpringBootTest(
+        classes = { 
+                Configuration_headless.class,
+                Configuration_usingInvalidDomain.class
+        }, 
+        properties = {
+                //IsisPresets.DebugProgrammingModel
+        })
+@TestPropertySource({
+    IsisPresets.DebugValidation,
+    IsisPresets.DebugProgrammingModel,
+    
+})
+class SupportingMethodTest {
+    
+    @Inject private MetaModelService metaModelService;
+    
+//    @BeforeAll
+//    static void beforeAll() {
+//        val typeMetaData = TypeMetaData.of(AValidDomainObject.class.getName());
+//        val typeRegistry = IsisBeanTypeRegistry.current();
+//        typeRegistry.isIoCManagedType(typeMetaData); // as a side-effect adds class to the meta model
+//    }
+
+    @Test //TODO under construction
+    void reservedPrefix_shouldBeAllowedForMembers() {
+           
+        
+        val config = new MetaModelService.Config()
+//              .withIgnoreNoop()
+//              .withIgnoreAbstractClasses()
+//              .withIgnoreBuiltInValueTypes()
+//              .withIgnoreInterfaces()
+                //.withPackagePrefix("*")
+                .withPackagePrefix("org.apache.isis.testdomain.")
+                ;
+        
+        val metamodelDto = metaModelService.exportMetaModel(config);
+
+        System.out.println("!!! listing MM");
+        
+        for (DomainClassDto domainClass : metamodelDto.getDomainClassDto()) {
+            System.out.println("dc: " + domainClass.getId());
+        }
+        
+        System.out.println("!!! ---");
+        
+        new ValidateDomainModel()
+        .run();
+        
+    }
+
+}