You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2019/12/23 09:53:07 UTC

[camel] 01/03: CAMEL-14311: Add validate configuration properties to camel-catalog.

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 4bcc61bc34ba284c4a575ff7f038e7948a33b899
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sun Dec 22 08:51:43 2019 +0100

    CAMEL-14311: Add validate configuration properties to camel-catalog.
---
 .../java/org/apache/camel/maven/ValidateMojo.java  |  84 +++++++++-
 .../ConfigurationPropertiesValidationResult.java   | 175 +++++++++++++++++++++
 2 files changed, 255 insertions(+), 4 deletions(-)

diff --git a/catalog/camel-report-maven-plugin/src/main/java/org/apache/camel/maven/ValidateMojo.java b/catalog/camel-report-maven-plugin/src/main/java/org/apache/camel/maven/ValidateMojo.java
index 8f17ace..ea10bd2 100644
--- a/catalog/camel-report-maven-plugin/src/main/java/org/apache/camel/maven/ValidateMojo.java
+++ b/catalog/camel-report-maven-plugin/src/main/java/org/apache/camel/maven/ValidateMojo.java
@@ -22,9 +22,11 @@ import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Properties;
 import java.util.Set;
 import java.util.stream.Collectors;
 
+import org.apache.camel.PropertyBindingException;
 import org.apache.camel.catalog.CamelCatalog;
 import org.apache.camel.catalog.DefaultCamelCatalog;
 import org.apache.camel.catalog.EndpointValidationResult;
@@ -37,6 +39,8 @@ 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.support.PatternHelper;
+import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.OrderedProperties;
 import org.apache.camel.util.StringHelper;
 import org.apache.maven.model.Dependency;
 import org.apache.maven.model.Resource;
@@ -51,7 +55,8 @@ import org.jboss.forge.roaster.model.JavaType;
 import org.jboss.forge.roaster.model.source.JavaClassSource;
 
 /**
- * Parses the source code and validates the Camel routes has valid endpoint uris and simple expressions.
+ * Parses the source code and validates the Camel routes has valid endpoint uris and simple expressions,
+ * and validates configuration files such as application.properties.
  */
 @Mojo(name = "validate", threadSafe = true)
 public class ValidateMojo extends AbstractExecMojo {
@@ -158,6 +163,13 @@ public class ValidateMojo extends AbstractExecMojo {
     @Parameter(property = "camel.directOrSedaPairCheck", defaultValue = "true")
     private boolean directOrSedaPairCheck;
 
+    /**
+     * Location of configuration files to validate. The default is application.properties
+     * Multiple values can be separated by comma and use ANT path style.
+     */
+    @Parameter(property = "camel.configurationFiles")
+    private String configurationFiles = "application.properties";
+
     // CHECKSTYLE:OFF
     @Override
     public void execute() throws MojoExecutionException, MojoFailureException {
@@ -195,6 +207,45 @@ public class ValidateMojo extends AbstractExecMojo {
             getLog().info("Validating using Camel version: " + catalog.getCatalogVersion());
         }
 
+        doExecuteRoutes(catalog);
+        doExecuteConfigurationFiles(catalog);
+    }
+
+    protected void doExecuteConfigurationFiles(CamelCatalog catalog) {
+        // TODO: implement me
+
+        Set<File> propertiesFiles = new LinkedHashSet<>();
+        List list = project.getResources();
+        for (Object obj : list) {
+            String dir = (String) obj;
+            findPropertiesFiles(new File(dir), propertiesFiles);
+        }
+        if (includeTest) {
+            list = project.getTestResources();
+            for (Object obj : list) {
+                String dir = (String) obj;
+                findPropertiesFiles(new File(dir), propertiesFiles);
+            }
+        }
+        for (File file : propertiesFiles) {
+            if (matchPropertiesFile(file)) {
+                InputStream is = null;
+                try {
+                    is = new FileInputStream(file);
+                    Properties prop = new OrderedProperties();
+                    prop.load(is);
+
+                    EndpointValidationResult result = catalog.validateConfigurationProperty(line);
+                } catch (Exception e) {
+                    getLog().warn("Error parsing file " + file + " code due " + e.getMessage(), e);
+                } finally {
+                    IOHelper.close(is);
+                }
+            }
+        }
+    }
+
+    protected void doExecuteRoutes(CamelCatalog catalog) throws MojoExecutionException, MojoFailureException {
         List<CamelEndpointDetails> endpoints = new ArrayList<>();
         List<CamelSimpleExpressionDetails> simpleExpressions = new ArrayList<>();
         List<CamelRouteDetails> routeIds = new ArrayList<>();
@@ -233,7 +284,7 @@ public class ValidateMojo extends AbstractExecMojo {
         }
 
         for (File file : javaFiles) {
-            if (matchFile(file)) {
+            if (matchRouteFile(file)) {
                 try {
                     List<CamelEndpointDetails> fileEndpoints = new ArrayList<>();
                     List<CamelRouteDetails> fileRouteIds = new ArrayList<>();
@@ -271,7 +322,7 @@ public class ValidateMojo extends AbstractExecMojo {
             }
         }
         for (File file : xmlFiles) {
-            if (matchFile(file)) {
+            if (matchRouteFile(file)) {
                 try {
                     List<CamelEndpointDetails> fileEndpoints = new ArrayList<>();
                     List<CamelSimpleExpressionDetails> fileSimpleExpressions = new ArrayList<>();
@@ -773,6 +824,19 @@ public class ValidateMojo extends AbstractExecMojo {
         return null;
     }
 
+    private void findPropertiesFiles(File dir, Set<File> propertiesFiles) {
+        File[] files = dir.isDirectory() ? dir.listFiles() : null;
+        if (files != null) {
+            for (File file : files) {
+                if (file.getName().endsWith(".properties")) {
+                    propertiesFiles.add(file);
+                } else if (file.isDirectory()) {
+                    findJavaFiles(file, propertiesFiles);
+                }
+            }
+        }
+    }
+
     private void findJavaFiles(File dir, Set<File> javaFiles) {
         File[] files = dir.isDirectory() ? dir.listFiles() : null;
         if (files != null) {
@@ -799,7 +863,19 @@ public class ValidateMojo extends AbstractExecMojo {
         }
     }
 
-    private boolean matchFile(File file) {
+    private boolean matchPropertiesFile(File file) {
+        for (String part : configurationFiles.split(",")) {
+            part = part.trim();
+            String fqn = stripRootPath(asRelativeFile(file.getAbsolutePath()));
+            boolean match = PatternHelper.matchPattern(fqn, part);
+            if (match) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean matchRouteFile(File file) {
         if (excludes == null && includes == null) {
             return true;
         }
diff --git a/core/camel-api/src/main/java/org/apache/camel/runtimecatalog/ConfigurationPropertiesValidationResult.java b/core/camel-api/src/main/java/org/apache/camel/runtimecatalog/ConfigurationPropertiesValidationResult.java
index 16a40d1..8138445 100644
--- a/core/camel-api/src/main/java/org/apache/camel/runtimecatalog/ConfigurationPropertiesValidationResult.java
+++ b/core/camel-api/src/main/java/org/apache/camel/runtimecatalog/ConfigurationPropertiesValidationResult.java
@@ -17,6 +17,9 @@
 package org.apache.camel.runtimecatalog;
 
 import java.io.Serializable;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.Map;
 
 /**
  * Details result of validating configuration properties (eg application.properties for camel-main).
@@ -38,4 +41,176 @@ public class ConfigurationPropertiesValidationResult extends PropertiesValidatio
     public String getValue() {
         return value;
     }
+
+    /**
+     * A human readable summary of the validation errors.
+     *
+     * @param includeHeader    whether to include a header
+     * @return the summary, or <tt>null</tt> if no validation errors
+     */
+    public String summaryErrorMessage(boolean includeHeader) {
+        return summaryErrorMessage(includeHeader, true, false);
+    }
+
+    /**
+     * A human readable summary of the validation errors.
+     *
+     * @param includeHeader    whether to include a header
+     * @param ignoreDeprecated whether to ignore deprecated options in use as an error or not
+     * @param includeWarnings  whether to include warnings as an error or not
+     * @return the summary, or <tt>null</tt> if no validation errors
+     */
+    public String summaryErrorMessage(boolean includeHeader, boolean ignoreDeprecated, boolean includeWarnings) {
+        boolean ok = isSuccess();
+
+        // special check if we should ignore deprecated options being used
+        if (ok && !ignoreDeprecated) {
+            ok = deprecated == null;
+        }
+
+        if (includeWarnings) {
+            if (unknownComponent != null) {
+                return "\tUnknown component: " + unknownComponent;
+            }
+        }
+
+        if (ok) {
+            return null;
+        }
+
+        // for each invalid option build a reason message
+        Map<String, String> options = new LinkedHashMap<>();
+        if (unknown != null) {
+            for (String name : unknown) {
+                if (unknownSuggestions != null && unknownSuggestions.containsKey(name)) {
+                    String[] suggestions = unknownSuggestions.get(name);
+                    if (suggestions != null && suggestions.length > 0) {
+                        String str = Arrays.asList(suggestions).toString();
+                        options.put(name, "Unknown option. Did you mean: " + str);
+                    } else {
+                        options.put(name, "Unknown option");
+                    }
+                } else {
+                    options.put(name, "Unknown option");
+                }
+            }
+        }
+        if (required != null) {
+            for (String name : required) {
+                options.put(name, "Missing required option");
+            }
+        }
+        if (deprecated != null) {
+            for (String name : deprecated) {
+                options.put(name, "Deprecated option");
+            }
+        }
+        if (invalidEnum != null) {
+            for (Map.Entry<String, String> entry : invalidEnum.entrySet()) {
+                String name = entry.getKey();
+                String[] choices = invalidEnumChoices.get(name);
+                String defaultValue = defaultValues != null ? defaultValues.get(entry.getKey()) : null;
+                String str = Arrays.asList(choices).toString();
+                String msg = "Invalid enum value: " + entry.getValue() + ". Possible values: " + str;
+                if (invalidEnumSuggestions != null) {
+                    String[] suggestions = invalidEnumSuggestions.get(name);
+                    if (suggestions != null && suggestions.length > 0) {
+                        str = Arrays.asList(suggestions).toString();
+                        msg += ". Did you mean: " + str;
+                    }
+                }
+                if (defaultValue != null) {
+                    msg += ". Default value: " + defaultValue;
+                }
+
+                options.put(entry.getKey(), msg);
+            }
+        }
+        if (invalidReference != null) {
+            for (Map.Entry<String, String> entry : invalidReference.entrySet()) {
+                boolean empty = isEmpty(entry.getValue());
+                if (empty) {
+                    options.put(entry.getKey(), "Empty reference value");
+                } else if (!entry.getValue().startsWith("#")) {
+                    options.put(entry.getKey(), "Invalid reference value: " + entry.getValue() + " must start with #");
+                } else {
+                    options.put(entry.getKey(), "Invalid reference value: " + entry.getValue());
+                }
+            }
+        }
+        if (invalidBoolean != null) {
+            for (Map.Entry<String, String> entry : invalidBoolean.entrySet()) {
+                boolean empty = isEmpty(entry.getValue());
+                if (empty) {
+                    options.put(entry.getKey(), "Empty boolean value");
+                } else {
+                    options.put(entry.getKey(), "Invalid boolean value: " + entry.getValue());
+                }
+            }
+        }
+        if (invalidInteger != null) {
+            for (Map.Entry<String, String> entry : invalidInteger.entrySet()) {
+                boolean empty = isEmpty(entry.getValue());
+                if (empty) {
+                    options.put(entry.getKey(), "Empty integer value");
+                } else {
+                    options.put(entry.getKey(), "Invalid integer value: " + entry.getValue());
+                }
+            }
+        }
+        if (invalidNumber != null) {
+            for (Map.Entry<String, String> entry : invalidNumber.entrySet()) {
+                boolean empty = isEmpty(entry.getValue());
+                if (empty) {
+                    options.put(entry.getKey(), "Empty number value");
+                } else {
+                    options.put(entry.getKey(), "Invalid number value: " + entry.getValue());
+                }
+            }
+        }
+        if (invalidMap != null) {
+            for (Map.Entry<String, String> entry : invalidMap.entrySet()) {
+                boolean empty = isEmpty(entry.getValue());
+                if (empty) {
+                    options.put(entry.getKey(), "Empty map key/value pair");
+                } else {
+                    options.put(entry.getKey(), "Invalid map key/value: " + entry.getValue());
+                }
+            }
+        }
+        if (invalidArray != null) {
+            for (Map.Entry<String, String> entry : invalidArray.entrySet()) {
+                boolean empty = isEmpty(entry.getValue());
+                if (empty) {
+                    options.put(entry.getKey(), "Empty array index/value pair");
+                } else {
+                    options.put(entry.getKey(), "Invalid array index/value: " + entry.getValue());
+                }
+            }
+        }
+
+        // build a table with the error summary nicely formatted
+        // lets use 24 as min length
+        int maxLen = 24;
+        for (String key : options.keySet()) {
+            maxLen = Math.max(maxLen, key.length());
+        }
+        String format = "%" + maxLen + "s    %s";
+
+        // build the human error summary
+        StringBuilder sb = new StringBuilder();
+        if (includeHeader) {
+            sb.append("Configuration properties error\n");
+            sb.append("---------------------------------------------------------------------------------------------------------------------------------------\n");
+            sb.append("\n");
+        }
+        sb.append("\n");
+        for (Map.Entry<String, String> option : options.entrySet()) {
+            String out = String.format(format, option.getKey(), option.getValue());
+            sb.append("\n\t").append(out);
+        }
+
+        return sb.toString();
+    }
+
 }