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) {

Reply via email to