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 <[email protected]> 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(); + } + }
