CAMEL-11296: camel-maven-plugin:validate - Allow to detect duplicate route ids


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

Branch: refs/heads/master
Commit: a724619a0622b1747a146138d7fbe0243f9a3bf1
Parents: 76ec4bb
Author: Claus Ibsen <davscl...@apache.org>
Authored: Sun May 21 10:33:41 2017 +0200
Committer: Claus Ibsen <davscl...@apache.org>
Committed: Sun May 21 10:40:57 2017 +0200

----------------------------------------------------------------------
 .../apache/camel/parser/RouteBuilderParser.java |  39 ++++++
 .../org/apache/camel/parser/XmlRouteParser.java |   6 +-
 .../camel/parser/xml/DuplicateRouteIdsTest.java |   2 +-
 .../src/main/docs/camel-maven-plugin.adoc       |   9 +-
 .../org/apache/camel/maven/ValidateMojo.java    | 132 ++++++++++++++++++-
 5 files changed, 178 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/a724619a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RouteBuilderParser.java
----------------------------------------------------------------------
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 dd15697..d38e1d6 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
@@ -24,6 +24,7 @@ import java.util.List;
 
 import org.apache.camel.parser.helper.CamelJavaParserHelper;
 import org.apache.camel.parser.model.CamelEndpointDetails;
+import org.apache.camel.parser.model.CamelRouteDetails;
 import org.apache.camel.parser.model.CamelSimpleExpressionDetails;
 import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.ASTNode;
 import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.Expression;
@@ -270,6 +271,44 @@ public final class RouteBuilderParser {
         }
     }
 
+    /**
+     * Parses the java source class to discover Camel routes with id's 
assigned.
+     *
+     * @param clazz                   the java source class
+     * @param baseDir                 the base of the source code
+     * @param fullyQualifiedFileName  the fully qualified source code file name
+     * @param routes                  list to add discovered and parsed routes
+     */
+    public static void parseRouteBuilderRouteIds(JavaClassSource clazz, String 
baseDir, String fullyQualifiedFileName,
+                                                 List<CamelRouteDetails> 
routes) {
+
+        MethodSource<JavaClassSource> method = 
CamelJavaParserHelper.findConfigureMethod(clazz);
+        if (method != null) {
+            List<ParserResult> expressions = 
CamelJavaParserHelper.parseCamelConsumerUris(method, true, false);
+            for (ParserResult result : expressions) {
+                // route ids is assigned in java dsl using the routeId method
+                if (result.isParsed() && "routeId".equals(result.getNode())) {
+                    String fileName = fullyQualifiedFileName;
+                    if (fileName.startsWith(baseDir)) {
+                        fileName = fileName.substring(baseDir.length() + 1);
+                    }
+
+                    CamelRouteDetails detail = new CamelRouteDetails();
+                    detail.setFileName(fileName);
+                    detail.setClassName(clazz.getQualifiedName());
+                    detail.setMethodName("configure");
+                    int line = findLineNumber(fullyQualifiedFileName, 
result.getPosition());
+                    if (line > -1) {
+                        detail.setLineNumber("" + line);
+                    }
+                    detail.setRouteId(result.getElement());
+
+                    routes.add(detail);
+                }
+            }
+        }
+    }
+
     private static CamelEndpointDetails 
findEndpointByUri(List<CamelEndpointDetails> endpoints, String uri) {
         for (CamelEndpointDetails detail : endpoints) {
             if (uri.equals(detail.getEndpointUri())) {

http://git-wip-us.apache.org/repos/asf/camel/blob/a724619a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/XmlRouteParser.java
----------------------------------------------------------------------
diff --git 
a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/XmlRouteParser.java
 
b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/XmlRouteParser.java
index 25ed77a..364cf9a 100644
--- 
a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/XmlRouteParser.java
+++ 
b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/XmlRouteParser.java
@@ -154,15 +154,15 @@ public final class XmlRouteParser {
     }
 
     /**
-     * Parses the XML source to discover Camel routes.
+     * Parses the XML source to discover Camel routes with id's assigned.
      *
      * @param xml                     the xml file as input stream
      * @param baseDir                 the base of the source code
      * @param fullyQualifiedFileName  the fully qualified source code file name
      * @param routes                  list to add discovered and parsed routes
      */
-    public static void parseXmlRouteRoutes(InputStream xml, String baseDir, 
String fullyQualifiedFileName,
-                                           List<CamelRouteDetails> routes) 
throws Exception {
+    public static void parseXmlRouteRouteIds(InputStream xml, String baseDir, 
String fullyQualifiedFileName,
+                                             List<CamelRouteDetails> routes) 
throws Exception {
 
         // find all the endpoints (currently only <route> and within <route>)
         // try parse it as dom

http://git-wip-us.apache.org/repos/asf/camel/blob/a724619a/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/xml/DuplicateRouteIdsTest.java
----------------------------------------------------------------------
diff --git 
a/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/xml/DuplicateRouteIdsTest.java
 
b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/xml/DuplicateRouteIdsTest.java
index 1497a8d..1e2fe60 100644
--- 
a/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/xml/DuplicateRouteIdsTest.java
+++ 
b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/xml/DuplicateRouteIdsTest.java
@@ -39,7 +39,7 @@ public class DuplicateRouteIdsTest {
         InputStream is = new 
FileInputStream("src/test/resources/org/apache/camel/parser/xml/myduplicateroutes.xml");
         String fqn = 
"src/test/resources/org/apache/camel/camel/parser/xml/myduplicateroutes.xml";
         String baseDir = "src/test/resources";
-        XmlRouteParser.parseXmlRouteRoutes(is, baseDir, fqn, list);
+        XmlRouteParser.parseXmlRouteRouteIds(is, baseDir, fqn, list);
 
         for (CamelRouteDetails detail : list) {
             LOG.info(detail.getRouteId());

http://git-wip-us.apache.org/repos/asf/camel/blob/a724619a/tooling/maven/camel-maven-plugin/src/main/docs/camel-maven-plugin.adoc
----------------------------------------------------------------------
diff --git 
a/tooling/maven/camel-maven-plugin/src/main/docs/camel-maven-plugin.adoc 
b/tooling/maven/camel-maven-plugin/src/main/docs/camel-maven-plugin.adoc
index 01787e4..8da3da6 100644
--- a/tooling/maven/camel-maven-plugin/src/main/docs/camel-maven-plugin.adoc
+++ b/tooling/maven/camel-maven-plugin/src/main/docs/camel-maven-plugin.adoc
@@ -112,7 +112,11 @@ You cannot change Spring or OSGi Blueprint `<bean>` 
elements.
 
 == camel:validate
 
-For validating Camel endpoints in the source code:
+For validating the source code for mis configured Camel:
+
+- endpoint uris
+- simple expressions or predicates
+- duplicate route ids
 
 Then you can run the validate goal from the command line or from within your 
Java editor such as IDEA or Eclipse.
 
@@ -229,7 +233,8 @@ The maven plugin supports the following options which can 
be configured from the
 | excludes | | To filter the names of java and xml files to exclude files 
matching any of the given list of patterns (wildcard and regular expression). 
Multiple values can be separated by comma.
 | ignoreUnknownComponent | true | Whether to ignore unknown components.
 | ignoreIncapable | true | Whether to ignore incapable of parsing the endpoint 
uri or simple expression.
-| ignoreLenientProperties | true |  Whether to ignore components that uses 
lenient properties. When this is true, then the uri validation is stricter but 
would fail on properties that are not part of the component but in the uri 
because of using lenient properties. For example using the HTTP components to 
provide query parameters in the endpoint uri.
+| ignoreLenientProperties | true | Whether to ignore components that uses 
lenient properties. When this is true, then the uri validation is stricter but 
would fail on properties that are not part of the component but in the uri 
because of using lenient properties. For example using the HTTP components to 
provide query parameters in the endpoint uri.
+| duplicateRouteId | true | *Camel 2.20* Whether to validate for duplicate 
route ids. Route ids should be unique and if there are duplicates then Camel 
will fail to startup.
 | showAll | false | Whether to show all endpoints and simple expressions (both 
invalid and valid).
 |========================================
 

http://git-wip-us.apache.org/repos/asf/camel/blob/a724619a/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/ValidateMojo.java
----------------------------------------------------------------------
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 3c8eca2..f907607 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
@@ -34,6 +34,7 @@ import org.apache.camel.maven.helper.EndpointHelper;
 import org.apache.camel.parser.RouteBuilderParser;
 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.maven.model.Dependency;
 import org.apache.maven.model.Resource;
@@ -160,6 +161,15 @@ public class ValidateMojo extends AbstractExecMojo {
      */
     private boolean downloadVersion;
 
+    /**
+     * Whether to validate for duplicate route ids. Route ids should be unique 
and if there are duplicates
+     * then Camel will fail to startup.
+     *
+     * @parameter property="camel.duplicateRouteId"
+     *            default-value="true"
+     */
+    private boolean duplicateRouteId;
+
     // CHECKSTYLE:OFF
     @Override
     public void execute() throws MojoExecutionException, MojoFailureException {
@@ -200,6 +210,7 @@ public class ValidateMojo extends AbstractExecMojo {
 
         List<CamelEndpointDetails> endpoints = new ArrayList<>();
         List<CamelSimpleExpressionDetails> simpleExpressions = new 
ArrayList<>();
+        List<CamelRouteDetails> routeIds = new ArrayList<>();
         Set<File> javaFiles = new LinkedHashSet<File>();
         Set<File> xmlFiles = new LinkedHashSet<File>();
 
@@ -238,6 +249,7 @@ public class ValidateMojo extends AbstractExecMojo {
             if (matchFile(file)) {
                 try {
                     List<CamelEndpointDetails> fileEndpoints = new 
ArrayList<>();
+                    List<CamelRouteDetails> fileRouteIds = new ArrayList<>();
                     List<CamelSimpleExpressionDetails> fileSimpleExpressions = 
new ArrayList<>();
                     List<String> unparsable = new ArrayList<>();
 
@@ -250,10 +262,14 @@ public class ValidateMojo extends AbstractExecMojo {
                         JavaClassSource clazz = (JavaClassSource) out;
                         RouteBuilderParser.parseRouteBuilderEndpoints(clazz, 
baseDir, fqn, fileEndpoints, unparsable, includeTest);
                         
RouteBuilderParser.parseRouteBuilderSimpleExpressions(clazz, baseDir, fqn, 
fileSimpleExpressions);
+                        if (duplicateRouteId) {
+                            
RouteBuilderParser.parseRouteBuilderRouteIds(clazz, baseDir, fqn, fileRouteIds);
+                        }
 
                         // add what we found in this file to the total list
                         endpoints.addAll(fileEndpoints);
                         simpleExpressions.addAll(fileSimpleExpressions);
+                        routeIds.addAll(fileRouteIds);
 
                         // was there any unparsable?
                         if (logUnparseable && !unparsable.isEmpty()) {
@@ -272,6 +288,7 @@ public class ValidateMojo extends AbstractExecMojo {
                 try {
                     List<CamelEndpointDetails> fileEndpoints = new 
ArrayList<>();
                     List<CamelSimpleExpressionDetails> fileSimpleExpressions = 
new ArrayList<>();
+                    List<CamelRouteDetails> fileRouteIds = new ArrayList<>();
 
                     // parse the xml source code and find Camel routes
                     String fqn = file.getPath();
@@ -285,9 +302,17 @@ public class ValidateMojo extends AbstractExecMojo {
                     XmlRouteParser.parseXmlRouteSimpleExpressions(is, baseDir, 
fqn, fileSimpleExpressions);
                     is.close();
 
+                    if (duplicateRouteId) {
+                        // need a new stream
+                        is = new FileInputStream(file);
+                        XmlRouteParser.parseXmlRouteRouteIds(is, baseDir, fqn, 
fileRouteIds);
+                        is.close();
+                    }
+
                     // add what we found in this file to the total list
                     endpoints.addAll(fileEndpoints);
                     simpleExpressions.addAll(fileSimpleExpressions);
+                    routeIds.addAll(fileRouteIds);
                 } catch (Exception e) {
                     getLog().warn("Error parsing xml file " + file + " code 
due " + e.getMessage(), e);
                 }
@@ -484,18 +509,117 @@ public class ValidateMojo extends AbstractExecMojo {
             simpleSummary = String.format("Simple validation error: (%s = 
passed, %s = invalid)", ok, simpleErrors);
         }
 
-        if (failOnError && (endpointErrors > 0 || simpleErrors > 0)) {
-            throw new MojoExecutionException(endpointSummary + "\n" + 
simpleSummary);
-        }
-
         if (simpleErrors > 0) {
             getLog().warn(simpleSummary);
         } else {
             getLog().info(simpleSummary);
         }
+
+        int duplicateRouteIdErrors = 0;
+        if (duplicateRouteId) {
+
+            // filter out all non uniques
+            for (CamelRouteDetails detail : routeIds) {
+                // skip empty route ids
+                if (detail.getRouteId() == null || 
"".equals(detail.getRouteId())) {
+                    continue;
+                }
+                int count = countRouteId(routeIds, detail.getRouteId());
+                if (count > 1) {
+                    duplicateRouteIdErrors++;
+
+                    StringBuilder sb = new StringBuilder();
+                    sb.append("Duplicate route id validation error at: ");
+                    if (detail.getClassName() != null && 
detail.getLineNumber() != null) {
+                        // this is from java code
+                        sb.append(detail.getClassName());
+                        if (detail.getMethodName() != null) {
+                            sb.append(".").append(detail.getMethodName());
+                        }
+                        
sb.append("(").append(asSimpleClassName(detail.getClassName())).append(".java:");
+                        sb.append(detail.getLineNumber()).append(")");
+                    } else if (detail.getLineNumber() != null) {
+                        // this is from xml
+                        String fqn = 
stripRootPath(asRelativeFile(detail.getFileName()));
+                        if (fqn.endsWith(".xml")) {
+                            fqn = fqn.substring(0, fqn.length() - 4);
+                            fqn = asPackageName(fqn);
+                        }
+                        sb.append(fqn);
+                        
sb.append("(").append(asSimpleClassName(fqn)).append(".xml:");
+                        sb.append(detail.getLineNumber()).append(")");
+                    } else {
+                        sb.append(detail.getFileName());
+                    }
+                    sb.append("\n");
+                    sb.append("\n\t").append(detail.getRouteId());
+                    sb.append("\n\n");
+
+                    getLog().warn(sb.toString());
+                } else if (showAll) {
+                    StringBuilder sb = new StringBuilder();
+                    sb.append("Duplicate route id validation passed at: ");
+                    if (detail.getClassName() != null && 
detail.getLineNumber() != null) {
+                        // this is from java code
+                        sb.append(detail.getClassName());
+                        if (detail.getMethodName() != null) {
+                            sb.append(".").append(detail.getMethodName());
+                        }
+                        
sb.append("(").append(asSimpleClassName(detail.getClassName())).append(".java:");
+                        sb.append(detail.getLineNumber()).append(")");
+                    } else if (detail.getLineNumber() != null) {
+                        // this is from xml
+                        String fqn = 
stripRootPath(asRelativeFile(detail.getFileName()));
+                        if (fqn.endsWith(".xml")) {
+                            fqn = fqn.substring(0, fqn.length() - 4);
+                            fqn = asPackageName(fqn);
+                        }
+                        sb.append(fqn);
+                        
sb.append("(").append(asSimpleClassName(fqn)).append(".xml:");
+                        sb.append(detail.getLineNumber()).append(")");
+                    } else {
+                        sb.append(detail.getFileName());
+                    }
+                    sb.append("\n");
+                    sb.append("\n\t").append(detail.getRouteId());
+                    sb.append("\n\n");
+
+                    getLog().info(sb.toString());
+                }
+            }
+        }
+
+        String routeIdSummary = "";
+        if (duplicateRouteId) {
+            if (duplicateRouteIdErrors == 0) {
+                routeIdSummary = String.format("Duplicate route id validation 
success (%s = ids)", routeIds.size());
+            } else {
+                routeIdSummary = String.format("Duplicate route id validation 
error: (%s = ids, %s = duplicates)", routeIds.size(), duplicateRouteIdErrors);
+            }
+
+            if (duplicateRouteIdErrors > 0) {
+                getLog().warn(routeIdSummary);
+            } else {
+                getLog().info(routeIdSummary);
+            }
+        }
+
+        if (failOnError && (endpointErrors > 0 || simpleErrors > 0 || 
duplicateRouteIdErrors > 0)) {
+            throw new MojoExecutionException(endpointSummary + "\n" + 
simpleSummary + "\n" + routeIdSummary);
+        }
     }
     // CHECKSTYLE:ON
 
+    private static int countRouteId(List<CamelRouteDetails> details, String 
routeId) {
+        int answer = 0;
+        for (CamelRouteDetails detail : details) {
+            if (routeId.equals(detail.getRouteId())) {
+                answer++;
+            }
+        }
+        return answer;
+    }
+
     private static String findCamelVersion(MavenProject project) {
         Dependency candidate = null;
 

Reply via email to