This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel.git
commit 4482cbf0cd946ff3d6ccfe608d43aa76ea02fc88 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Thu Aug 30 09:05:04 2018 +0200 CAMEL-12598: Camel maven tooling validate to detect direct/send endpoints missing names, eg sending to none existing seda queue which has no consumers etc. --- .../apache/camel/parser/RouteBuilderParser.java | 71 ++++++++----------- .../camel/parser/model/CamelEndpointDetails.java | 44 ------------ .../camel/parser/java/MySedaRouteBuilder.java | 38 ++++++++++ .../parser/java/RoasterEndpointInjectTest.java | 14 ++-- .../parser/java/RoasterMySedaRouteBuilderTest.java | 51 ++++++++++++++ .../java/org/apache/camel/maven/ValidateMojo.java | 80 +++++++++++++++++++++- 6 files changed, 200 insertions(+), 98 deletions(-) diff --git a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RouteBuilderParser.java b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RouteBuilderParser.java index d7040aa..2fa973f 100644 --- a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RouteBuilderParser.java +++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RouteBuilderParser.java @@ -207,40 +207,34 @@ public final class RouteBuilderParser { unparsable.add(result.getElement()); } } else { - CamelEndpointDetails detail = findEndpointByUri(endpoints, result.getElement()); - if (detail != null) { - // its a consumer only - detail.setConsumerOnly(true); - } else { - String fileName = fullyQualifiedFileName; - if (fileName.startsWith(baseDir)) { - fileName = fileName.substring(baseDir.length() + 1); - } + String fileName = fullyQualifiedFileName; + if (fileName.startsWith(baseDir)) { + fileName = fileName.substring(baseDir.length() + 1); + } - detail = new CamelEndpointDetails(); - detail.setFileName(fileName); - detail.setClassName(clazz.getQualifiedName()); - detail.setMethodName(configureMethod.getName()); - detail.setEndpointInstance(null); - detail.setEndpointUri(result.getElement()); - int line = findLineNumber(clazz.toUnformattedString(), result.getPosition()); - if (line > -1) { - detail.setLineNumber("" + line); - } - int lineEnd = findLineNumber(clazz.toUnformattedString(), result.getPosition() + result.getLength()); - if (lineEnd > -1) { - detail.setLineNumberEnd("" + lineEnd); - } - detail.setAbsolutePosition(result.getPosition()); - int linePos = findLinePosition(clazz.toUnformattedString(), result.getPosition()); - if (linePos > -1) { - detail.setLinePosition(linePos); - } - detail.setEndpointComponentName(endpointComponentName(result.getElement())); - detail.setConsumerOnly(true); - detail.setProducerOnly(false); - endpoints.add(detail); + CamelEndpointDetails detail = new CamelEndpointDetails(); + detail.setFileName(fileName); + detail.setClassName(clazz.getQualifiedName()); + detail.setMethodName(configureMethod.getName()); + detail.setEndpointInstance(null); + detail.setEndpointUri(result.getElement()); + int line = findLineNumber(clazz.toUnformattedString(), result.getPosition()); + if (line > -1) { + detail.setLineNumber("" + line); + } + int lineEnd = findLineNumber(clazz.toUnformattedString(), result.getPosition() + result.getLength()); + if (lineEnd > -1) { + detail.setLineNumberEnd("" + lineEnd); } + detail.setAbsolutePosition(result.getPosition()); + int linePos = findLinePosition(clazz.toUnformattedString(), result.getPosition()); + if (linePos > -1) { + detail.setLinePosition(linePos); + } + detail.setEndpointComponentName(endpointComponentName(result.getElement())); + detail.setConsumerOnly(true); + detail.setProducerOnly(false); + endpoints.add(detail); } } // producer only @@ -251,17 +245,6 @@ public final class RouteBuilderParser { unparsable.add(result.getElement()); } } else { - CamelEndpointDetails detail = findEndpointByUri(endpoints, result.getElement()); - if (detail != null) { - if (detail.isConsumerOnly()) { - // its both a consumer and producer - detail.setConsumerOnly(false); - detail.setProducerOnly(false); - } else { - // its a producer only - detail.setProducerOnly(true); - } - } // the same endpoint uri may be used in multiple places in the same route // so we should maybe add all of them String fileName = fullyQualifiedFileName; @@ -269,7 +252,7 @@ public final class RouteBuilderParser { fileName = fileName.substring(baseDir.length() + 1); } - detail = new CamelEndpointDetails(); + CamelEndpointDetails detail = new CamelEndpointDetails(); detail.setFileName(fileName); detail.setClassName(clazz.getQualifiedName()); detail.setMethodName(configureMethod.getName()); diff --git a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/CamelEndpointDetails.java b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/CamelEndpointDetails.java index 1b23091..be46063 100644 --- a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/CamelEndpointDetails.java +++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/CamelEndpointDetails.java @@ -137,50 +137,6 @@ public class CamelEndpointDetails { } @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - CamelEndpointDetails that = (CamelEndpointDetails) o; - - if (!fileName.equals(that.fileName)) { - return false; - } - if (lineNumber != null ? !lineNumber.equals(that.lineNumber) : that.lineNumber != null) { - return false; - } - if (lineNumberEnd != null ? !lineNumberEnd.equals(that.lineNumberEnd) : that.lineNumberEnd != null) { - return false; - } - if (className != null ? !className.equals(that.className) : that.className != null) { - return false; - } - if (methodName != null ? !methodName.equals(that.methodName) : that.methodName != null) { - return false; - } - if (endpointInstance != null ? !endpointInstance.equals(that.endpointInstance) : that.endpointInstance != null) { - return false; - } - return endpointUri != null ? endpointUri.equals(that.endpointUri) : that.endpointUri != null; - } - - @Override - public int hashCode() { - int result = fileName.hashCode(); - result = 31 * result + (lineNumber != null ? lineNumber.hashCode() : 0); - result = 31 * result + (lineNumberEnd != null ? lineNumberEnd.hashCode() : 0); - result = 31 * result + (className != null ? className.hashCode() : 0); - result = 31 * result + (methodName != null ? methodName.hashCode() : 0); - result = 31 * result + (endpointInstance != null ? endpointInstance.hashCode() : 0); - result = 31 * result + (endpointUri != null ? endpointUri.hashCode() : 0); - return result; - } - - @Override public String toString() { return "CamelEndpointDetails[" + "fileName='" + fileName + '\'' diff --git a/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/MySedaRouteBuilder.java b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/MySedaRouteBuilder.java new file mode 100644 index 0000000..f43fa34 --- /dev/null +++ b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/MySedaRouteBuilder.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.camel.parser.java; + +import org.apache.camel.builder.RouteBuilder; + +public class MySedaRouteBuilder extends RouteBuilder { + + @Override + public void configure() throws Exception { + from("timer:hello?period={{timer.period}}").routeId("hello").routeGroup("hello-group") + .transform().method("myBean", "saySomething") + .filter(simple("${body} contains 'foo'")) + .to("seda:foo") + .end() + .to("stream:out"); + + from("seda:foo") + .to("log:foo"); + + from("seda:bar") + .to("log:bar"); + } +} diff --git a/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterEndpointInjectTest.java b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterEndpointInjectTest.java index 34bc0a4..d05fbdb 100644 --- a/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterEndpointInjectTest.java +++ b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterEndpointInjectTest.java @@ -64,11 +64,11 @@ public class RoasterEndpointInjectTest { Assert.assertEquals(10, details.get(2).getLinePosition()); // spans 2 lines - Assert.assertEquals("mock:foo?retainFirst=1", details.get(5).getEndpointUri()); - Assert.assertEquals("45", details.get(5).getLineNumber()); - Assert.assertEquals("46", details.get(5).getLineNumberEnd()); - Assert.assertEquals(1457, details.get(5).getAbsolutePosition()); - Assert.assertEquals(17, details.get(5).getLinePosition()); + Assert.assertEquals("mock:foo?retainFirst=1", details.get(6).getEndpointUri()); + Assert.assertEquals("45", details.get(6).getLineNumber()); + Assert.assertEquals("46", details.get(6).getLineNumberEnd()); + Assert.assertEquals(1457, details.get(6).getAbsolutePosition()); + Assert.assertEquals(17, details.get(6).getLinePosition()); List<ParserResult> list = CamelJavaParserHelper.parseCamelConsumerUris(method, true, true); for (ParserResult result : list) { @@ -82,8 +82,8 @@ public class RoasterEndpointInjectTest { } Assert.assertEquals(3, list.size()); - Assert.assertEquals(6, details.size()); - Assert.assertEquals("log:a", details.get(3).getEndpointUri()); + Assert.assertEquals(7, details.size()); + Assert.assertEquals("log:a", details.get(4).getEndpointUri()); } } diff --git a/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterMySedaRouteBuilderTest.java b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterMySedaRouteBuilderTest.java new file mode 100644 index 0000000..80376f4 --- /dev/null +++ b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterMySedaRouteBuilderTest.java @@ -0,0 +1,51 @@ +/** + * 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.camel.parser.java; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.apache.camel.parser.RouteBuilderParser; +import org.apache.camel.parser.model.CamelEndpointDetails; +import org.jboss.forge.roaster.Roaster; +import org.jboss.forge.roaster.model.source.JavaClassSource; +import org.junit.Assert; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RoasterMySedaRouteBuilderTest { + + private static final Logger LOG = LoggerFactory.getLogger(RoasterMySedaRouteBuilderTest.class); + + @Test + public void parse() throws Exception { + JavaClassSource clazz = (JavaClassSource) Roaster.parse(new File("src/test/java/org/apache/camel/parser/java/MySedaRouteBuilder.java")); + + List<CamelEndpointDetails> details = new ArrayList<>(); + RouteBuilderParser.parseRouteBuilderEndpoints(clazz, ".", "src/test/java/org/apache/camel/parser/java/MySedaRouteBuilder.java", details); + LOG.info("{}", details); + + Assert.assertEquals(7, details.size()); + Assert.assertEquals("32", details.get(1).getLineNumber()); + Assert.assertEquals("seda:foo", details.get(1).getEndpointUri()); + Assert.assertTrue(details.get(1).isConsumerOnly()); + Assert.assertFalse(details.get(1).isProducerOnly()); + } + +} diff --git a/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/ValidateMojo.java b/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/ValidateMojo.java index b2e88b9..de43041 100644 --- a/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/ValidateMojo.java +++ b/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/ValidateMojo.java @@ -23,6 +23,8 @@ import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; import org.apache.camel.catalog.CamelCatalog; import org.apache.camel.catalog.DefaultCamelCatalog; @@ -36,6 +38,7 @@ import org.apache.camel.parser.XmlRouteParser; import org.apache.camel.parser.model.CamelEndpointDetails; import org.apache.camel.parser.model.CamelRouteDetails; import org.apache.camel.parser.model.CamelSimpleExpressionDetails; +import org.apache.camel.util.StringHelper; import org.apache.maven.model.Dependency; import org.apache.maven.model.Resource; import org.apache.maven.plugin.MojoExecutionException; @@ -179,6 +182,15 @@ public class ValidateMojo extends AbstractExecMojo { */ private boolean duplicateRouteId; + /** + * Whether to validate for direct/seda endpoints not having matching pairs, such as producing to + * a non existing seda endpoint. + * + * @parameter property="camel.directOrSedaPairCheck" + * default-value="true" + */ + private boolean directOrSedaPairCheck; + // CHECKSTYLE:OFF @Override public void execute() throws MojoExecutionException, MojoFailureException { @@ -327,6 +339,7 @@ public class ValidateMojo extends AbstractExecMojo { } } + // endpoint uris int endpointErrors = 0; int unknownComponents = 0; int incapableErrors = 0; @@ -431,13 +444,13 @@ public class ValidateMojo extends AbstractExecMojo { endpointSummary = String.format("Endpoint validation error: (%s = passed, %s = invalid, %s = incapable, %s = unknown components, %s = deprecated options)", ok, endpointErrors, incapableErrors, unknownComponents, deprecatedOptions); } - if (endpointErrors > 0) { getLog().warn(endpointSummary); } else { getLog().info(endpointSummary); } + // simple int simpleErrors = validateSimple(catalog, simpleExpressions); String simpleSummary; if (simpleErrors == 0) { @@ -453,6 +466,20 @@ public class ValidateMojo extends AbstractExecMojo { getLog().info(simpleSummary); } + // endpoint pairs + int sedaDirectErrors = 0; + String sedaDirectSummary = ""; + if (directOrSedaPairCheck) { + long sedaDirectEndpoints = countEndpointPairs(endpoints, "direct") + countEndpointPairs(endpoints, "seda"); + sedaDirectErrors += validateEndpointPairs(endpoints, "direct") + validateEndpointPairs(endpoints, "seda"); + if (sedaDirectErrors == 0) { + sedaDirectSummary = String.format("Endpoint pair (seda/direct) validation success (%s = pairs)", sedaDirectEndpoints); + } else { + sedaDirectSummary = String.format("Endpoint pair (seda/direct) validation error: (%s = pairs, %s = non-pairs)", sedaDirectEndpoints, sedaDirectErrors); + } + } + + // route id int duplicateRouteIdErrors = validateDuplicateRouteId(routeIds); String routeIdSummary = ""; if (duplicateRouteId) { @@ -468,9 +495,56 @@ public class ValidateMojo extends AbstractExecMojo { } } - if (failOnError && (endpointErrors > 0 || simpleErrors > 0 || duplicateRouteIdErrors > 0)) { - throw new MojoExecutionException(endpointSummary + "\n" + simpleSummary + "\n" + routeIdSummary); + if (failOnError && (endpointErrors > 0 || simpleErrors > 0 || duplicateRouteIdErrors > 0) || sedaDirectErrors > 0) { + throw new MojoExecutionException(endpointSummary + "\n" + simpleSummary + "\n" + routeIdSummary + "\n" + sedaDirectSummary); + } + } + + private int countEndpointPairs(List<CamelEndpointDetails> endpoints, String scheme) { + int pairs = 0; + + Set<CamelEndpointDetails> consumers = endpoints.stream().filter(e -> e.isConsumerOnly() && e.getEndpointUri().startsWith(scheme + ":")).collect(Collectors.toSet()); + Set<CamelEndpointDetails> producers = endpoints.stream().filter(e -> e.isProducerOnly() && e.getEndpointUri().startsWith(scheme + ":")).collect(Collectors.toSet()); + + // find all pairs, eg consumers that has a producer (no need to check for producer that has a consumer) + for (CamelEndpointDetails c : consumers) { + boolean any = producers.stream().findAny().filter(e -> matchEndpointPath(c.getEndpointUri(), e.getEndpointUri())).isPresent(); + if (any) { + pairs++; + } + } + + return pairs; + } + + private int validateEndpointPairs(List<CamelEndpointDetails> endpoints, String scheme) { + int errors = 0; + + Set<CamelEndpointDetails> consumers = endpoints.stream().filter(e -> e.isConsumerOnly() && e.getEndpointUri().startsWith(scheme + ":")).collect(Collectors.toSet()); + Set<CamelEndpointDetails> producers = endpoints.stream().filter(e -> e.isProducerOnly() && e.getEndpointUri().startsWith(scheme + ":")).collect(Collectors.toSet()); + + // are there any consumers that do not have a producer pair + for (CamelEndpointDetails c : consumers) { + boolean any = producers.stream().findAny().filter(e -> matchEndpointPath(c.getEndpointUri(), e.getEndpointUri())).isPresent(); + if (!any) { + errors++; + } + } + // are there any producers that do not have a consumer pair + for (CamelEndpointDetails p : producers) { + boolean any = consumers.stream().findAny().filter(e -> matchEndpointPath(p.getEndpointUri(), e.getEndpointUri())).isPresent(); + if (!any) { + errors++; + } } + + return errors; + } + + private static boolean matchEndpointPath(String uri, String uri2) { + String p = uri.contains("?") ? StringHelper.before(uri, "?") : uri; + String p2 = uri2.contains("?") ? StringHelper.before(uri2, "?") : uri2; + return p.equals(p2); } private int validateSimple(CamelCatalog catalog, List<CamelSimpleExpressionDetails> simpleExpressions) {