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 2017/02/13 11:31:51 UTC

isis git commit: ISIS-1571: checks that every action collection parameter has a supporting choices or autoComplete facet.

Repository: isis
Updated Branches:
  refs/heads/maint-1.14.0 e97baf565 -> e402f2311


ISIS-1571: checks that every action collection parameter has a supporting choices or autoComplete facet.

Implementing this required moving the metamodel validation to be run within a session.  This results in potentially more object specs to validate, and in particular resulted in a validation failure that java.lang.Object and FreeStandingList had the same object type.  So this commit also incorporates a fix for that, by only comparing specs that are entities or view models.


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/e402f231
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/e402f231
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/e402f231

Branch: refs/heads/maint-1.14.0
Commit: e402f2311a776687ec653f5cf9270053fbd2f0bc
Parents: e97baf5
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Mon Feb 13 10:53:09 2017 +0000
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Mon Feb 13 10:53:09 2017 +0000

----------------------------------------------------------------------
 .../guides/_rgcfg_configuring-core.adoc         | 136 ++++++++++++-------
 .../guides/_rgsvc_api_EmailService.adoc         |   2 +-
 ...oicesForCollectionParameterFacetFactory.java | 125 +++++++++++++++++
 .../DomainObjectAnnotationFacetFactory.java     |  15 ++
 .../dflt/ProgrammingModelFacetsJava5.java       |   5 +-
 .../session/IsisSessionFactoryBuilder.java      |  53 +++++---
 6 files changed, 266 insertions(+), 70 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/e402f231/adocs/documentation/src/main/asciidoc/guides/_rgcfg_configuring-core.adoc
----------------------------------------------------------------------
diff --git a/adocs/documentation/src/main/asciidoc/guides/_rgcfg_configuring-core.adoc b/adocs/documentation/src/main/asciidoc/guides/_rgcfg_configuring-core.adoc
index 4386605..11382ad 100644
--- a/adocs/documentation/src/main/asciidoc/guides/_rgcfg_configuring-core.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/_rgcfg_configuring-core.adoc
@@ -240,7 +240,7 @@ Lazy initialization can speed up bootstrapping, useful while developing and runn
 | `true`,`false` +
 (depends)
 |If a domain object has been mapped to the specified JAXB `x-ro-domain-type`, then determines whether the result is pretty-printed or not. +
-+
+
 If no configuration property is available, then the defaults is determined by the xref:rgcfg.adoc#_rgcfg_deployment-types[deployment type]: production mode disables pretty printing, while prototype mode enables it.
 
 
@@ -249,7 +249,7 @@ If no configuration property is available, then the defaults is determined by th
 `email.` +
 `override.bcc` +
 | email address
-|(`1.13.3-SNAPSHOT`) intended to simplify testing, if specified then the email's `bcc` address will be that specified (rather than the email address(es) passed in as an argument to `EmailService#send(...)`).  +
+|(`1.14.0-SNAPSHOT`) intended to simplify testing, if specified then the email's `bcc` address will be that specified (rather than the email address(es) passed in as an argument to `EmailService#send(...)`).  +
 
 NB: note that the key is mis-spelt, (`isis.service.email` rather than `isis.services.email`)
 
@@ -258,7 +258,7 @@ NB: note that the key is mis-spelt, (`isis.service.email` rather than `isis.serv
 `email.` +
 `override.cc` +
 | email address
-|(`1.13.3-SNAPSHOT`) intended to simplify testing, if specified then the email's `cc` address will be that specified (rather than the email address(es) passed in as an argument to `EmailService#send(...)`).  +
+|(`1.14.0-SNAPSHOT`) intended to simplify testing, if specified then the email's `cc` address will be that specified (rather than the email address(es) passed in as an argument to `EmailService#send(...)`).  +
 
 NB: note that the key is mis-spelt, (`isis.service.email` rather than `isis.services.email`)
 
@@ -267,7 +267,7 @@ NB: note that the key is mis-spelt, (`isis.service.email` rather than `isis.serv
 `email.` +
 `override.to` +
 | email address
-|(`1.13.3-SNAPSHOT`) intended to simplify testing, if specified then the email's `to` address will be that specified (rather than the email address(es) passed in as an argument to `EmailService#send(...)`).  +
+|(`1.14.0-SNAPSHOT`) intended to simplify testing, if specified then the email's `to` address will be that specified (rather than the email address(es) passed in as an argument to `EmailService#send(...)`).  +
 
 NB: note that the key is mis-spelt, (`isis.service.email` rather than `isis.services.email`)
 
@@ -313,10 +313,11 @@ NB: note that the key is mis-spelt, (`isis.service.email` rather than `isis.serv
 
 | `isis.service.` +
 `email.` +
-`socketConnectionTimeout`
+`socket` +
+`ConnectionTimeout`
 |milliseconds +
 (2000)
-|(`1.13.3-SNAPSHOT`) The socket connection timeout
+|(`1.14.0-SNAPSHOT`) The socket connection timeout
 
 NB: note that the key is mis-spelt, (`isis.service.email` rather than `isis.services.email`)
 
@@ -326,7 +327,7 @@ NB: note that the key is mis-spelt, (`isis.service.email` rather than `isis.serv
 `socketTimeout`
 |milliseconds +
 (2000)
-|(`1.13.3-SNAPSHOT`) The socket timeout
+|(`1.14.0-SNAPSHOT`) The socket timeout
 
 NB: note that the key is mis-spelt, (`isis.service.email` rather than `isis.services.email`)
 
@@ -337,7 +338,7 @@ NB: note that the key is mis-spelt, (`isis.service.email` rather than `isis.serv
 `throwExceptionOnFail`
 |`true`,`false` +
 (`true`)
-|(`1.13.3-SNAPSHOT`) Whether to throw an exception if there the email cannot be sent (probably because of some misconfiguration).
+|(`1.14.0-SNAPSHOT`) Whether to throw an exception if there the email cannot be sent (probably because of some misconfiguration).
 
 This behaviour is (now) the default; the old behaviour (of just returning `false` from the `send()` method) can be re-enabled by setting this property to `false`.
 
@@ -363,7 +364,8 @@ FQCN (`_guava_`)
 
 | `isis.services.` +
 `eventbus.` +
-`allowLateRegistration` +
+`allowLate` +
+`Registration` +
 |`true`,`false` +
 (`false`)
 |whether a domain service can register with the xref:rgsvc.adoc#_rgsvc_api_EventBusService[`EventBusService`] after any events have posted. +
@@ -373,7 +375,8 @@ Since this almost certainly constitutes a bug in application code, by default th
 
 | `isis.services.` +
 `exceprecog.` +
-`logRecognizedExceptions` +
+`logRecognized` +
+`Exceptions` +
 |`true`,`false` +
 (`false`)
 |whether recognized exceptions should also be logged. +
@@ -383,8 +386,9 @@ This property logs the exception anyway, useful for debugging.
 
 
 | `isis.services.` +
-`ExceptionRecognizerComposite-` +
-`ForJdoObjectStore.` +
+`ExceptionRecognizer` +
+`CompositeFor` +
+`JdoObjectStore.` +
 `disable` +
 |`true`,`false` +
 (`false`)
@@ -417,8 +421,6 @@ By default this is enabled (no change in `1.13.0`).
 If the setting is changed to disabled then this may reduce application start-up times.
 
 
-
-
 |`isis.services.` +
 `publish.` +
 `objects`
@@ -441,7 +443,8 @@ If the setting is changed to disabled then this may reduce application start-up
 
 
 |`isis.services.` +
-`ServicesInstallerFromAnnotation.` +
+`ServicesInstaller` +
+`FromAnnotation.` +
 `packagePrefix`
 |fully qualified package names (CSV)
 |to search for domain services (including all subpackages).
@@ -462,6 +465,73 @@ See xref:ugbtb.adoc#_ugbtb_i18n[i18n support] to learn more about the translatio
 
 
 
+[[_rgcfg_configuring-core_metamodel-validation]]
+== MetaModel Validation
+
+
+.Other Core Configuration Properties
+[cols="2a,1,3a", options="header"]
+|===
+|Property
+|Value +
+(default value)
+|Description
+
+|`isis.reflector.validator`
+|`FQCN`
+|Custom implementation of `MetaModelValidator` (in the `org.apache.isis.core.metamodel.specloader.validator` package) +
+
+See xref:ugbtb.adoc#_ugbtb_programming-model_custom-validator[Custom Validator] to learn more.
+
+|`isis.reflector.validator.` +
+`allowDeprecated`
+|`true`,`false` +
+(`true`)
+| Whether deprecated annotations or naming conventions are tolerated or not.
+If not, then a metamodel validation error will be triggered, meaning the app won't boot (fail-fast). +
+
+See also `isis.reflector.facets.ignoreDeprecated`.
+
+|`isis.reflector.validator.` +
+`actionCollection` +
+`ParameterChoices`
+|`true`,`false` +
+(`true`)
+| (`1.14.0-SNAPSHOT`) Whether to check that collection action parameters have a corresponding choices or autoComplete facet. +
+
+In the current implementation such a facet is always required, so this configuration option has only been introduced as a feature flag in case it needs to be disabled for some reason.
+
+|`isis.reflector.validator.` +
+`jdoqlFromClause`
+|`true`,`false` +
+(`true`)
+| (`1.14.0-SNAPSHOT`) Whether to check that the class name in JDOQL `FROM` clause matches or is a supertype of the class on which it is annotated. +
+
+Only "SELECT" queries are validated; "UPDATE" queries etc are simply ignored.
+
+|`isis.reflector.validator.` +
+`jdoql` +
+`VariablesClause`
+|`true`,`false` +
+(`true`)
+| (`1.14.0-SNAPSHOT`) Whether to check that the class name in JDOQL `VARIABLES` clause is a recognized class. +
+
+Note that although JDOQL syntax supports multiple `VARIABLES` classes, currently the validator only checks the first class name found.
+
+|`isis.reflector.validator.` +
+`actionCollection` +
+`ParameterChoices`
+|`true`,`false` +
+(`true`)
+| (`1.14.0-SNAPSHOT`) Whether to check that the class name in JDOQL `VARIABLES` clause is a recognized class. +
+
+Note that although JDOQL syntax supports multiple `VARIABLES` classes, currently the validator only checks the first class name found.
+
+
+|===
+
+
+
 [[_rgcfg_configuring-core_other]]
 == Other Config Properties
 
@@ -482,7 +552,8 @@ See xref:ugbtb.adoc#_ugbtb_i18n[i18n support] to learn more about the translatio
 
 
 |`isis.persistor.` +
-`disableConcurrencyChecking`
+`disable` +
+`ConcurrencyChecking`
 |`true`,`false` +
 (`false`)
 | Disables concurrency checking globally.  +
@@ -522,8 +593,6 @@ See xref:ugbtb.adoc#_ugbtb_programming-model_finetuning[finetuning the programmi
 
 
 
-
-
 |`isis.reflector.facets.` +
 `exclude`
 |`FQCN`,`FQCN2`,...
@@ -562,37 +631,6 @@ See xref:ugbtb.adoc#_ugbtb_programming-model_layout-metadata-reader[Layout Metad
 
 
 
-|`isis.reflector.validator`
-|`FQCN`
-|Custom implementation of `MetaModelValidator` (in the `org.apache.isis.core.metamodel.specloader.validator` package) +
-
-See xref:ugbtb.adoc#_ugbtb_programming-model_custom-validator[Custom Validator] to learn more.
-
-|`isis.reflector.validator.` +
-`allowDeprecated`
-|`true`,`false` +
-(`true`)
-| Whether deprecated annotations or naming conventions are tolerated or not.
-If not, then a metamodel validation error will be triggered, meaning the app won't boot (fail-fast). +
-+
-See also `isis.reflector.facets.ignoreDeprecated`.
-
-|`isis.reflector.validator.` +
-`jdoqlFromClause`
-|`true`,`false` +
-(`true`)
-| (`1.14.0-SNAPSHOT`) Whether to check that the class name in JDOQL `FROM` clause matches or is a supertype of the class on which it is annotated. +
-+
-Only "SELECT" queries are validated; "UPDATE" queries etc are simply ignored.
-
-|`isis.reflector.validator.` +
-`jdoqlVariablesClause`
-|`true`,`false` +
-(`true`)
-| (`1.14.0-SNAPSHOT`) Whether to check that the class name in JDOQL `VARIABLES` clause is a recognized class. +
-+
-Note that although JDOQL syntax supports multiple `VARIABLES` classes, currently the validator only checks the first class name found.
-
 
 
 |`isis.viewers.` +

http://git-wip-us.apache.org/repos/asf/isis/blob/e402f231/adocs/documentation/src/main/asciidoc/guides/_rgsvc_api_EmailService.adoc
----------------------------------------------------------------------
diff --git a/adocs/documentation/src/main/asciidoc/guides/_rgsvc_api_EmailService.adoc b/adocs/documentation/src/main/asciidoc/guides/_rgsvc_api_EmailService.adoc
index 9215dd9..36ad337 100644
--- a/adocs/documentation/src/main/asciidoc/guides/_rgsvc_api_EmailService.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/_rgsvc_api_EmailService.adoc
@@ -66,7 +66,7 @@ For example, if you create a test email account on gmail, you can configure the
 where "xxx" is the gmail user account and "yyy" is its password
 
 
-In addition (as of `1.13.3-SNAPSHOT`), the following properties can be set:
+In addition (as of `1.14.0-SNAPSHOT`), the following properties can be set:
 
 * `isis.service.email.throwExceptionOnFail` +
 +

http://git-wip-us.apache.org/repos/asf/isis/blob/e402f231/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionChoicesForCollectionParameterFacetFactory.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionChoicesForCollectionParameterFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionChoicesForCollectionParameterFacetFactory.java
new file mode 100644
index 0000000..4d02bb4
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionChoicesForCollectionParameterFacetFactory.java
@@ -0,0 +1,125 @@
+/*
+ *  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.core.metamodel.facets.actions.action;
+
+import java.util.List;
+
+import org.apache.isis.core.commons.config.IsisConfiguration;
+import org.apache.isis.core.metamodel.facetapi.FeatureType;
+import org.apache.isis.core.metamodel.facetapi.MetaModelValidatorRefiner;
+import org.apache.isis.core.metamodel.facets.FacetFactoryAbstract;
+import org.apache.isis.core.metamodel.facets.param.autocomplete.ActionParameterAutoCompleteFacet;
+import org.apache.isis.core.metamodel.facets.param.choices.ActionParameterChoicesFacet;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.feature.Contributed;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter;
+import org.apache.isis.core.metamodel.specloader.specimpl.ObjectActionContributee;
+import org.apache.isis.core.metamodel.specloader.specimpl.ObjectActionMixedIn;
+import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidator;
+import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorComposite;
+import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorVisiting;
+import org.apache.isis.core.metamodel.specloader.validator.ValidationFailures;
+
+/**
+ * Ensures that every action that has a collection parameter has a choices facet for that parameter.
+ */
+public class ActionChoicesForCollectionParameterFacetFactory extends FacetFactoryAbstract
+            implements MetaModelValidatorRefiner {
+
+    public static final String ISIS_REFLECTOR_VALIDATOR_ACTION_COLLECTION_PARAMETER_CHOICES_KEY =
+                                                    "isis.reflector.validator.actionCollectionParameterChoices";
+    public static final boolean ISIS_REFLECTOR_VALIDATOR_ACTION_COLLECTION_PARAMETER_CHOICES_DEFAULT = true;
+
+    public ActionChoicesForCollectionParameterFacetFactory() {
+        super(FeatureType.ACTIONS_ONLY);
+    }
+
+    @Override
+    public void process(final ProcessMethodContext processMethodContext) {
+
+        // no-op here... just adding a validator
+
+    }
+
+    @Override
+    public void refineMetaModelValidator(final MetaModelValidatorComposite metaModelValidator, final IsisConfiguration configuration) {
+
+        final boolean doCheck = configuration.getBoolean(
+                ISIS_REFLECTOR_VALIDATOR_ACTION_COLLECTION_PARAMETER_CHOICES_KEY,
+                ISIS_REFLECTOR_VALIDATOR_ACTION_COLLECTION_PARAMETER_CHOICES_DEFAULT);
+
+        if(!doCheck) {
+            return;
+        }
+
+        final MetaModelValidator validator = new MetaModelValidatorVisiting(
+                new MetaModelValidatorVisiting.Visitor() {
+                    @Override
+                    public boolean visit(
+                            final ObjectSpecification objectSpec,
+                            final ValidationFailures validationFailures) {
+                        validate(objectSpec, validationFailures);
+                        return true;
+                    }
+
+                    private void validate(
+                            final ObjectSpecification objectSpec,
+                            final ValidationFailures validationFailures) {
+                        List<ObjectAction> objectActions = objectSpec.getObjectActions(Contributed.INCLUDED);
+                        for (ObjectAction objectAction : objectActions) {
+                            if(objectAction instanceof ObjectActionMixedIn || objectAction instanceof ObjectActionContributee) {
+                                // we'll report only the mixin or contributor
+                                continue;
+                            }
+                            List<ObjectActionParameter> parameters = objectAction.getParameters();
+                            for (int paramNum = 0; paramNum < parameters.size(); paramNum++) {
+                                ObjectActionParameter parameter = parameters.get(paramNum);
+                                if(parameter.getFeatureType() == FeatureType.ACTION_PARAMETER_COLLECTION) {
+                                    validate(objectSpec, objectAction, parameter, paramNum, validationFailures);
+                                }
+                            }
+                        }
+                    }
+
+                    private void validate(
+                            final ObjectSpecification objectSpec,
+                            final ObjectAction objectAction,
+                            final ObjectActionParameter parameter,
+                            final int paramNum,
+                            final ValidationFailures validationFailures) {
+                        final ActionParameterChoicesFacet choicesFacet =
+                                parameter.getFacet(ActionParameterChoicesFacet.class);
+                        final ActionParameterAutoCompleteFacet autoCompleteFacet =
+                                parameter.getFacet(ActionParameterAutoCompleteFacet.class);
+                        if(choicesFacet == null && autoCompleteFacet == null) {
+                            validationFailures.add(
+                                    "Collection action parameter found without supporting "
+                                    + "choices or autoComplete facet.  "
+                                    + "Class: %s action: %s parameter %d",
+                                    objectSpec.getFullIdentifier(), objectAction.getName(), paramNum);
+                        }
+                    }
+                });
+        metaModelValidator.add(validator);
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/e402f231/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java
index d35a85a..5910b4a 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java
@@ -521,6 +521,10 @@ public class DomainObjectAnnotationFacetFactory extends FacetFactoryAbstract
             @Override
             public boolean visit(final ObjectSpecification thisSpec, final ValidationFailures validationFailures) {
 
+                if(!isEntityOrViewModel(thisSpec)) {
+                    return true;
+                }
+
                 final Map<ObjectSpecId, ObjectSpecification> specById = Maps.newHashMap();
                 final Collection<ObjectSpecification> allSpecifications = getSpecificationLoader().allSpecifications();
                 for (final ObjectSpecification otherSpec : allSpecifications) {
@@ -528,6 +532,11 @@ public class DomainObjectAnnotationFacetFactory extends FacetFactoryAbstract
                     if(thisSpec == otherSpec) {
                         continue;
                     }
+
+                    if(!isEntityOrViewModel(otherSpec)) {
+                        continue;
+                    }
+
                     final ObjectSpecId objectSpecId = otherSpec.getSpecId();
                     if (objectSpecId == null) {
                         continue;
@@ -546,6 +555,12 @@ public class DomainObjectAnnotationFacetFactory extends FacetFactoryAbstract
 
                 return true;
             }
+
+            public boolean isEntityOrViewModel(final ObjectSpecification thisSpec) {
+                ViewModelFacet facet = thisSpec.getFacet(ViewModelFacet.class);
+                JdoPersistenceCapableFacet facet1 = thisSpec.getFacet(JdoPersistenceCapableFacet.class);
+                return facet != null || facet1 != null;
+            }
         }));
 
         metaModelValidator.add(publishedObjectValidator);

http://git-wip-us.apache.org/repos/asf/isis/blob/e402f231/core/metamodel/src/main/java/org/apache/isis/progmodels/dflt/ProgrammingModelFacetsJava5.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/progmodels/dflt/ProgrammingModelFacetsJava5.java b/core/metamodel/src/main/java/org/apache/isis/progmodels/dflt/ProgrammingModelFacetsJava5.java
index 0faa1c3..d6085cb 100644
--- a/core/metamodel/src/main/java/org/apache/isis/progmodels/dflt/ProgrammingModelFacetsJava5.java
+++ b/core/metamodel/src/main/java/org/apache/isis/progmodels/dflt/ProgrammingModelFacetsJava5.java
@@ -19,6 +19,7 @@ package org.apache.isis.progmodels.dflt;
 
 import org.apache.isis.core.commons.config.IsisConfiguration;
 import org.apache.isis.core.metamodel.facets.actions.action.ActionAnnotationFacetFactory;
+import org.apache.isis.core.metamodel.facets.actions.action.ActionChoicesForCollectionParameterFacetFactory;
 import org.apache.isis.core.metamodel.facets.actions.contributing.maxlenannot.MaxLengthFacetOnActionAnnotationFactory;
 import org.apache.isis.core.metamodel.facets.actions.contributing.paged.PagedFacetOnActionFactory;
 import org.apache.isis.core.metamodel.facets.actions.defaults.method.ActionDefaultsFacetViaMethodFactory;
@@ -523,7 +524,9 @@ public final class ProgrammingModelFacetsJava5 extends ProgrammingModelAbstract
         // should come near the end, after any facets that install PropertySetterFacet have run.
         addFactory(new DisabledFacetOnPropertyInferredFactory());
 
-        
+
+        addFactory(new ActionChoicesForCollectionParameterFacetFactory());
+
         addFactory(new AuditableFacetMarkerInterfaceFactory());
 
         addFactory(new FacetsFacetAnnotationFactory());

http://git-wip-us.apache.org/repos/asf/isis/blob/e402f231/core/runtime/src/main/java/org/apache/isis/core/runtime/system/session/IsisSessionFactoryBuilder.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/session/IsisSessionFactoryBuilder.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/session/IsisSessionFactoryBuilder.java
index 26a8ef9..3002b3f 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/session/IsisSessionFactoryBuilder.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/session/IsisSessionFactoryBuilder.java
@@ -171,29 +171,23 @@ public class IsisSessionFactoryBuilder {
 
 
             // time to initialize...
-            try {
-                // first, initial metamodel (may throw exception if invalid)
-                specificationLoader.init();
-                specificationLoader.validateAndAssert();
+            specificationLoader.init();
 
-
-            } catch (final MetaModelInvalidException ex) {
-                // 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", ex);
-                }
-                IsisContext.setMetaModelInvalidException(ex);
-            }
-
-
-            // the remaining functionality is done even if metamodel is valid.
-            // so that can still call isisSessionFactory#doInSession
+            // we need to do this before checking if the metamodel is valid.
+            //
+            // eg ActionChoicesForCollectionParameterFacetFactory metamodel validator requires a runtime...
+            // at o.a.i.core.metamodel.specloader.specimpl.ObjectActionContributee.getServiceAdapter(ObjectActionContributee.java:287)
+            // at o.a.i.core.metamodel.specloader.specimpl.ObjectActionContributee.determineParameters(ObjectActionContributee.java:138)
+            // at o.a.i.core.metamodel.specloader.specimpl.ObjectActionDefault.getParameters(ObjectActionDefault.java:182)
+            // at o.a.i.core.metamodel.facets.actions.action.ActionChoicesForCollectionParameterFacetFactory$1.validate(ActionChoicesForCollectionParameterFacetFactory.java:85)
+            // at o.a.i.core.metamodel.facets.actions.action.ActionChoicesForCollectionParameterFacetFactory$1.visit(ActionChoicesForCollectionParameterFacetFactory.java:76)
+            // at o.a.i.core.metamodel.specloader.validator.MetaModelValidatorVisiting.validate(MetaModelValidatorVisiting.java:47)
+            //
+            // also, required so that can still call isisSessionFactory#doInSession
             //
             // eg todoapp has a custom UserSettingsThemeProvider that is called when rendering any page
             // (including the metamodel invalid page)
-            //
-            // at org.apache.isis.core.runtime.system.session.IsisSessionFactory.doInSession(IsisSessionFactory.java:327)
+            // at o.a.i.core.runtime.system.session.IsisSessionFactory.doInSession(IsisSessionFactory.java:327)
             // at todoapp.webapp.UserSettingsThemeProvider.getActiveTheme(UserSettingsThemeProvider.java:36)
 
             authenticationManager.init(deploymentCategory);
@@ -204,6 +198,27 @@ public class IsisSessionFactoryBuilder {
             isisSessionFactory.constructServices();
 
 
+            isisSessionFactory.doInSession(
+                    new Runnable() {
+                        @Override
+                        public void run() {
+                            try {
+                                specificationLoader.validateAndAssert();
+
+                            } catch (final MetaModelInvalidException ex) {
+                                // 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", ex);
+                                }
+                                IsisContext.setMetaModelInvalidException(ex);
+                            }
+
+                        }
+                    }
+            );
+
+
         } catch (final IsisSystemException ex) {
             LOG.error("failed to initialise", ex);
             throw new RuntimeException(ex);