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 09:23:32 UTC
isis git commit: ISIS-1580: adds validators for JdoQueryAnnotation to
check the FROM and VARIABLES clauses
Repository: isis
Updated Branches:
refs/heads/maint-1.14.0 5e247641e -> e97baf565
ISIS-1580: adds validators for JdoQueryAnnotation to check the FROM and VARIABLES clauses
Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/e97baf56
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/e97baf56
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/e97baf56
Branch: refs/heads/maint-1.14.0
Commit: e97baf565cc34ca013789857d09f2d6d5fbad720
Parents: 5e24764
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Mon Feb 13 07:35:54 2017 +0000
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Mon Feb 13 09:23:23 2017 +0000
----------------------------------------------------------------------
.../guides/_rgcfg_configuring-core.adoc | 16 ++
.../validator/MetaModelValidatorVisiting.java | 2 +-
.../query/JdoQueryAnnotationFacetFactory.java | 168 ++++++++++++++++++-
.../JdoQueryAnnotationFacetFactoryTest.java | 101 +++++++++++
.../application/manifest/isis.properties | 6 +-
5 files changed, 290 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/isis/blob/e97baf56/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 3a6e1c2..4386605 100644
--- a/adocs/documentation/src/main/asciidoc/guides/_rgcfg_configuring-core.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/_rgcfg_configuring-core.adoc
@@ -577,6 +577,22 @@ If not, then a metamodel validation error will be triggered, meaning the app won
+
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/e97baf56/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/validator/MetaModelValidatorVisiting.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/validator/MetaModelValidatorVisiting.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/validator/MetaModelValidatorVisiting.java
index edbce71..e7d212a 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/validator/MetaModelValidatorVisiting.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/validator/MetaModelValidatorVisiting.java
@@ -23,7 +23,7 @@ import java.util.Collection;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
-public final class MetaModelValidatorVisiting extends MetaModelValidatorAbstract {
+public class MetaModelValidatorVisiting extends MetaModelValidatorAbstract {
public interface Visitor {
/**
http://git-wip-us.apache.org/repos/asf/isis/blob/e97baf56/core/metamodel/src/main/java/org/apache/isis/objectstore/jdo/metamodel/facets/object/query/JdoQueryAnnotationFacetFactory.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/objectstore/jdo/metamodel/facets/object/query/JdoQueryAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/objectstore/jdo/metamodel/facets/object/query/JdoQueryAnnotationFacetFactory.java
index d58a1f0..74ebc45 100644
--- a/core/metamodel/src/main/java/org/apache/isis/objectstore/jdo/metamodel/facets/object/query/JdoQueryAnnotationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/objectstore/jdo/metamodel/facets/object/query/JdoQueryAnnotationFacetFactory.java
@@ -18,17 +18,35 @@
*/
package org.apache.isis.objectstore.jdo.metamodel.facets.object.query;
+import java.util.List;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
import javax.jdo.annotations.Queries;
import javax.jdo.annotations.Query;
+import org.apache.isis.core.commons.config.IsisConfiguration;
import org.apache.isis.core.metamodel.facetapi.FacetHolder;
import org.apache.isis.core.metamodel.facetapi.FacetUtil;
import org.apache.isis.core.metamodel.facetapi.FeatureType;
+import org.apache.isis.core.metamodel.facetapi.MetaModelValidatorRefiner;
import org.apache.isis.core.metamodel.facets.Annotations;
import org.apache.isis.core.metamodel.facets.FacetFactoryAbstract;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+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;
+import org.apache.isis.objectstore.jdo.metamodel.facets.object.persistencecapable.JdoPersistenceCapableFacet;
+
+public class JdoQueryAnnotationFacetFactory extends FacetFactoryAbstract implements MetaModelValidatorRefiner {
+ public static final String ISIS_REFLECTOR_VALIDATOR_JDOQL_FROM_CLAUSE_KEY = "isis.reflector.validator.jdoqlFromClause";
+ public static final boolean ISIS_REFLECTOR_VALIDATOR_JDOQL_FROM_CLAUSE_DEFAULT = true;
-public class JdoQueryAnnotationFacetFactory extends FacetFactoryAbstract {
+ public static final String ISIS_REFLECTOR_VALIDATOR_JDOQL_VARIABLES_CLAUSE_KEY = "isis.reflector.validator.variablesFromClause";
+ public static final boolean ISIS_REFLECTOR_VALIDATOR_JDOQL_VARIABLES_CLAUSE_DEFAULT = true;
public JdoQueryAnnotationFacetFactory() {
super(FeatureType.OBJECTS_ONLY);
@@ -52,4 +70,152 @@ public class JdoQueryAnnotationFacetFactory extends FacetFactoryAbstract {
namedQueryAnnotation, facetHolder));
}
}
+
+ @Override
+ public void refineMetaModelValidator(
+ final MetaModelValidatorComposite metaModelValidator,
+ final IsisConfiguration configuration) {
+
+ final boolean validateFromClause = configuration.getBoolean(
+ ISIS_REFLECTOR_VALIDATOR_JDOQL_FROM_CLAUSE_KEY,
+ ISIS_REFLECTOR_VALIDATOR_JDOQL_FROM_CLAUSE_DEFAULT);
+ if (validateFromClause) {
+ final MetaModelValidator queryFromValidator = 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) {
+ final JdoQueryFacet facet = objectSpec.getFacet(JdoQueryFacet.class);
+ if(facet == null) {
+ return;
+ }
+ final List<JdoNamedQuery> namedQueries = facet.getNamedQueries();
+ for (final JdoNamedQuery namedQuery : namedQueries) {
+ final String query = namedQuery.getQuery();
+ final String fromClassName = from(query);
+ interpret(fromClassName, objectSpec, query, validationFailures);
+ }
+ }
+
+ void interpret(
+ final String fromClassName, final ObjectSpecification objectSpec,
+ final String query,
+ final ValidationFailures validationFailures) {
+
+ if(fromClassName == null) {
+ // may be an update statement, for example
+ return;
+ }
+
+ final String className = objectSpec.getCorrespondingClass().getName();
+ if(!getSpecificationLoader().loaded(fromClassName)) {
+ validationFailures.add(
+ "Error in JDOQL query on %s, class name for FROM clause not recognized (JDOQL : %s)",
+ className, query);
+ return;
+
+ }
+
+ if (Objects.equals(fromClassName, className)) {
+ return;
+ }
+ final ObjectSpecification fromSpec = getSpecificationLoader().loadSpecification(fromClassName);
+ List<ObjectSpecification> subclasses = fromSpec.subclasses();
+ if(subclasses.contains(objectSpec)) {
+ return;
+ }
+ validationFailures.add(
+ "Error in JDOQL query on %s, class name after FROM clause should be same as class name on which annotated, or one of its supertypes (JDOQL : %s)",
+ className, query);
+ }
+
+ });
+ metaModelValidator.add(queryFromValidator);
+ }
+
+ final boolean validateVariablesClause = configuration.getBoolean(
+ ISIS_REFLECTOR_VALIDATOR_JDOQL_VARIABLES_CLAUSE_KEY,
+ ISIS_REFLECTOR_VALIDATOR_JDOQL_VARIABLES_CLAUSE_DEFAULT);
+ if (validateVariablesClause) {
+ final MetaModelValidator queryFromValidator = 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) {
+ final String className = objectSpec.getCorrespondingClass().getName();
+ final JdoQueryFacet facet = objectSpec.getFacet(JdoQueryFacet.class);
+ if(facet == null) {
+ return;
+ }
+ final List<JdoNamedQuery> namedQueries = facet.getNamedQueries();
+ for (final JdoNamedQuery namedQuery : namedQueries) {
+ final String query = namedQuery.getQuery();
+ final String variablesClassName = variables(query);
+ interpret(variablesClassName, className, query, validationFailures);
+ }
+ }
+
+ void interpret(
+ final String variablesClassName, final String className,
+ final String query,
+ final ValidationFailures validationFailures) {
+
+ if(variablesClassName == null) {
+ return;
+ }
+
+ if(!getSpecificationLoader().loaded(variablesClassName)) {
+ validationFailures.add(
+ "Error in JDOQL query on %s, class name for VARIABLES clause not recognized (JDOQL : %s)",
+ className, query);
+ return;
+ }
+ ObjectSpecification objectSpecification = getSpecificationLoader()
+ .loadSpecification(variablesClassName);
+ JdoPersistenceCapableFacet persistenceCapableFacet = objectSpecification
+ .getFacet(JdoPersistenceCapableFacet.class);
+
+ if(persistenceCapableFacet == null) {
+ validationFailures.add(
+ "Error in JDOQL query on %s, class name for VARIABLES clause is not annotated as @PersistenceCapable (JDOQL : %s)",
+ className, query);
+ return;
+ }
+ }
+ });
+ metaModelValidator.add(queryFromValidator);
+ }
+ }
+
+ private final static Pattern fromPattern = Pattern.compile("SELECT.*?FROM[\\s]+([^\\s]+).*", Pattern.CASE_INSENSITIVE);
+
+ static String from(final String query) {
+ final Matcher matcher = fromPattern.matcher(query);
+ return matcher.matches() ? matcher.group(1) : null;
+ }
+
+ private final static Pattern variablesPattern = Pattern.compile(".*?VARIABLES[\\s]+([^\\s]+).*", Pattern.CASE_INSENSITIVE);
+ static String variables(final String query) {
+ final Matcher matcher = variablesPattern.matcher(query);
+ return matcher.matches() ? matcher.group(1) : null;
+ }
+
+
+
}
http://git-wip-us.apache.org/repos/asf/isis/blob/e97baf56/core/metamodel/src/test/java/org/apache/isis/objectstore/jdo/metamodel/facets/object/query/JdoQueryAnnotationFacetFactoryTest.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/test/java/org/apache/isis/objectstore/jdo/metamodel/facets/object/query/JdoQueryAnnotationFacetFactoryTest.java b/core/metamodel/src/test/java/org/apache/isis/objectstore/jdo/metamodel/facets/object/query/JdoQueryAnnotationFacetFactoryTest.java
new file mode 100644
index 0000000..01fca3e
--- /dev/null
+++ b/core/metamodel/src/test/java/org/apache/isis/objectstore/jdo/metamodel/facets/object/query/JdoQueryAnnotationFacetFactoryTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.objectstore.jdo.metamodel.facets.object.query;
+
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+public class JdoQueryAnnotationFacetFactoryTest {
+
+ public static class From_Test extends JdoQueryAnnotationFacetFactoryTest {
+
+ @Test
+ public void lower_case() throws Exception {
+
+ String query = "SELECT from domainapp.modules.simple.dom.impl.SimpleObject WHERE name.indexOf(:name) >= 0 ";
+
+ String name = JdoQueryAnnotationFacetFactory.from(query);
+
+ assertThat(name, is(equalTo("domainapp.modules.simple.dom.impl.SimpleObject")));
+ }
+
+ @Test
+ public void upper_case() throws Exception {
+
+ String query = "SELECT FROM domainapp.modules.simple.dom.impl.SimpleObject WHERE name.indexOf(:name) >= 0 ";
+
+ String name = JdoQueryAnnotationFacetFactory.from(query);
+
+ assertThat(name, is(equalTo("domainapp.modules.simple.dom.impl.SimpleObject")));
+ }
+
+ @Test
+ public void no_match() throws Exception {
+
+ String query = "UPDATE org.isisaddons.module.sessionlogger.dom.SessionLogEntry "
+ + " SET logoutTimestamp == :logoutTimestamp "
+ + " ,causedBy2 == :causedBy2 "
+ + " WHERE causedBy2 == null";
+
+ String name = JdoQueryAnnotationFacetFactory.from(query);
+
+ assertThat(name, is(nullValue()));
+ }
+
+ }
+
+ public static class Variables_Test extends JdoQueryAnnotationFacetFactoryTest {
+
+ @Test
+ public void lower_case() throws Exception {
+
+ String query = "SELECT FROM mydomain.Supplier WHERE this.products.contains(prod) && prod.name == 'Beans' variables mydomain.Product prod ";
+
+ String variables = JdoQueryAnnotationFacetFactory.variables(query);
+
+ assertThat(variables, is(equalTo("mydomain.Product")));
+ }
+
+ @Test
+ public void upper_case() throws Exception {
+
+ String query = "SELECT FROM mydomain.Supplier WHERE this.products.contains(prod) && prod.name == 'Beans' VARIABLES mydomain.Product prod ";
+
+ String variables = JdoQueryAnnotationFacetFactory.variables(query);
+
+ assertThat(variables, is(equalTo("mydomain.Product")));
+ }
+
+ @Test
+ public void no_match() throws Exception {
+
+ String query = "SELECT FROM domainapp.modules.simple.dom.impl.SimpleObject WHERE name.indexOf(:name) >= 0 ";
+
+ String variables = JdoQueryAnnotationFacetFactory.variables(query);
+
+ assertThat(variables, is(nullValue()));
+ }
+
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/isis/blob/e97baf56/example/application/simpleapp/application/src/main/resources/domainapp/application/manifest/isis.properties
----------------------------------------------------------------------
diff --git a/example/application/simpleapp/application/src/main/resources/domainapp/application/manifest/isis.properties b/example/application/simpleapp/application/src/main/resources/domainapp/application/manifest/isis.properties
index 9546cdb..d828d46 100644
--- a/example/application/simpleapp/application/src/main/resources/domainapp/application/manifest/isis.properties
+++ b/example/application/simpleapp/application/src/main/resources/domainapp/application/manifest/isis.properties
@@ -17,7 +17,6 @@
-
#################################################################################
#
# MetaModel
@@ -54,6 +53,11 @@ isis.reflector.validator.allowDeprecated=false
#
+# Whether to validate JDOQL clauses. If not specified, default is to validate.
+#
+#isis.reflector.validator.jdoqlFromClause=true
+
+#
# Whether to ignore or honour (at least some of the) deprecated annotations/method prefixes.
# If not specified, default is to honour
#