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();
+ }
+
}