| /* |
| * 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.maven; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileReader; |
| import java.io.InputStream; |
| import java.io.LineNumberReader; |
| 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.catalog.CamelCatalog; |
| import org.apache.camel.catalog.ConfigurationPropertiesValidationResult; |
| import org.apache.camel.catalog.DefaultCamelCatalog; |
| import org.apache.camel.catalog.EndpointValidationResult; |
| import org.apache.camel.catalog.LanguageValidationResult; |
| import org.apache.camel.catalog.lucene.LuceneSuggestionStrategy; |
| import org.apache.camel.catalog.maven.MavenVersionManager; |
| 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.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; |
| import org.apache.maven.plugin.MojoExecutionException; |
| import org.apache.maven.plugin.MojoFailureException; |
| import org.apache.maven.plugins.annotations.Mojo; |
| import org.apache.maven.plugins.annotations.Parameter; |
| import org.apache.maven.project.MavenProject; |
| import org.codehaus.mojo.exec.AbstractExecMojo; |
| import org.jboss.forge.roaster.Roaster; |
| 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, and validates |
| * configuration files such as application.properties. |
| */ |
| @Mojo(name = "validate", threadSafe = true) |
| public class ValidateMojo extends AbstractExecMojo { |
| |
| /** |
| * The maven project. |
| */ |
| @Parameter(property = "project", required = true, readonly = true) |
| protected MavenProject project; |
| |
| /** |
| * Whether to fail if invalid Camel endpoints was found. By default the plugin logs the errors at WARN level |
| */ |
| @Parameter(property = "camel.failOnError", defaultValue = "false") |
| private boolean failOnError; |
| |
| /** |
| * Whether to log endpoint URIs which was un-parsable and therefore not possible to validate |
| */ |
| @Parameter(property = "camel.logUnparseable", defaultValue = "false") |
| private boolean logUnparseable; |
| |
| /** |
| * Whether to include Java files to be validated for invalid Camel endpoints |
| */ |
| @Parameter(property = "camel.includeJava", defaultValue = "true") |
| private boolean includeJava; |
| |
| /** |
| * Whether to include XML files to be validated for invalid Camel endpoints |
| */ |
| @Parameter(property = "camel.includeXml", defaultValue = "true") |
| private boolean includeXml; |
| |
| /** |
| * Whether to include test source code |
| */ |
| @Parameter(property = "camel.includeTest", defaultValue = "false") |
| private boolean includeTest; |
| |
| /** |
| * To filter the names of java and xml files to only include files matching any of the given list of patterns |
| * (wildcard and regular expression). Multiple values can be separated by comma. |
| */ |
| @Parameter(property = "camel.includes") |
| private String includes; |
| |
| /** |
| * 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. |
| */ |
| @Parameter(property = "camel.excludes") |
| private String excludes; |
| |
| /** |
| * Whether to ignore unknown components |
| */ |
| @Parameter(property = "camel.ignoreUnknownComponent", defaultValue = "true") |
| private boolean ignoreUnknownComponent; |
| |
| /** |
| * Whether to ignore incapable of parsing the endpoint uri |
| */ |
| @Parameter(property = "camel.ignoreIncapable", defaultValue = "true") |
| private boolean ignoreIncapable; |
| |
| /** |
| * Whether to ignore deprecated options being used in the endpoint uri |
| */ |
| @Parameter(property = "camel.ignoreDeprecated", defaultValue = "true") |
| private boolean ignoreDeprecated; |
| |
| /** |
| * 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. |
| */ |
| @Parameter(property = "camel.ignoreLenientProperties", defaultValue = "true") |
| private boolean ignoreLenientProperties; |
| |
| /** |
| * Whether to show all endpoints and simple expressions (both invalid and valid). |
| */ |
| @Parameter(property = "camel.showAll", defaultValue = "false") |
| private boolean showAll; |
| |
| /** |
| * Whether to allow downloading Camel catalog version from the internet. This is needed if the project uses a |
| * different Camel version than this plugin is using by default. |
| */ |
| @Parameter(property = "camel.downloadVersion", defaultValue = "true") |
| 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", defaultValue = "true") |
| private boolean duplicateRouteId; |
| |
| /** |
| * Whether to validate direct/seda endpoints sending to non existing consumers. |
| */ |
| @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 wildcard pattern matching. |
| */ |
| @Parameter(property = "camel.configurationFiles") |
| private String configurationFiles = "application.properties"; |
| |
| // CHECKSTYLE:OFF |
| @Override |
| public void execute() throws MojoExecutionException, MojoFailureException { |
| CamelCatalog catalog = new DefaultCamelCatalog(); |
| // add activemq as known component |
| catalog.addComponent("activemq", "org.apache.activemq.camel.component.ActiveMQComponent"); |
| // enable did you mean |
| catalog.setSuggestionStrategy(new LuceneSuggestionStrategy()); |
| // enable loading other catalog versions dynamically |
| catalog.setVersionManager(new MavenVersionManager()); |
| // use custom class loading |
| catalog.getJSonSchemaResolver().setClassLoader(ValidateMojo.class.getClassLoader()); |
| // enable caching |
| catalog.enableCache(); |
| |
| String detectedVersion = findCamelVersion(project); |
| if (detectedVersion != null) { |
| getLog().info("Detected Camel version used in project: " + detectedVersion); |
| } |
| |
| if (downloadVersion) { |
| String catalogVersion = catalog.getCatalogVersion(); |
| String version = findCamelVersion(project); |
| if (version != null && !version.equals(catalogVersion)) { |
| // the project uses a different Camel version so attempt to load it |
| getLog().info("Downloading Camel version: " + version); |
| boolean loaded = catalog.loadVersion(version); |
| if (!loaded) { |
| getLog().warn("Error downloading Camel version: " + version); |
| } |
| } |
| } |
| |
| if (catalog.getLoadedVersion() != null) { |
| getLog().info("Validating using downloaded Camel version: " + catalog.getLoadedVersion()); |
| } else { |
| getLog().info("Validating using Camel version: " + catalog.getCatalogVersion()); |
| } |
| |
| doExecuteRoutes(catalog); |
| doExecuteConfigurationFiles(catalog); |
| } |
| |
| protected void doExecuteConfigurationFiles(CamelCatalog catalog) throws MojoExecutionException { |
| // TODO: implement me |
| |
| Set<File> propertiesFiles = new LinkedHashSet<>(); |
| List list = project.getResources(); |
| for (Object obj : list) { |
| Resource dir = (Resource) obj; |
| findPropertiesFiles(new File(dir.getDirectory()), propertiesFiles); |
| } |
| if (includeTest) { |
| list = project.getTestResources(); |
| for (Object obj : list) { |
| Resource dir = (Resource) obj; |
| findPropertiesFiles(new File(dir.getDirectory()), propertiesFiles); |
| } |
| } |
| |
| List<ConfigurationPropertiesValidationResult> results = new ArrayList<>(); |
| |
| for (File file : propertiesFiles) { |
| if (matchPropertiesFile(file)) { |
| InputStream is = null; |
| try { |
| is = new FileInputStream(file); |
| Properties prop = new OrderedProperties(); |
| prop.load(is); |
| |
| // validate each line |
| for (String name : prop.stringPropertyNames()) { |
| String value = prop.getProperty(name); |
| if (value != null) { |
| String text = name + "=" + value; |
| ConfigurationPropertiesValidationResult result = catalog.validateConfigurationProperty(text); |
| // only include lines that camel can accept (as there may be non camel properties too) |
| if (result.isAccepted()) { |
| // try to find line number |
| int lineNumber = findLineNumberInPropertiesFile(file, name); |
| if (lineNumber != -1) { |
| result.setLineNumber(lineNumber); |
| } |
| results.add(result); |
| result.setText(text); |
| result.setFileName(file.getName()); |
| } |
| } |
| } |
| } catch (Exception e) { |
| getLog().warn("Error parsing file " + file + " code due " + e.getMessage(), e); |
| } finally { |
| IOHelper.close(is); |
| } |
| } |
| } |
| |
| int configurationErrors = 0; |
| int unknownComponents = 0; |
| int incapableErrors = 0; |
| int deprecatedOptions = 0; |
| for (ConfigurationPropertiesValidationResult result : results) { |
| int deprecated = result.getDeprecated() != null ? result.getDeprecated().size() : 0; |
| deprecatedOptions += deprecated; |
| |
| boolean ok = result.isSuccess() && !result.hasWarnings(); |
| if (!ok && ignoreUnknownComponent && result.getUnknownComponent() != null) { |
| // if we failed due unknown component then be okay if we should ignore that |
| unknownComponents++; |
| ok = true; |
| } |
| if (!ok && ignoreIncapable && result.getIncapable() != null) { |
| // if we failed due incapable then be okay if we should ignore that |
| incapableErrors++; |
| ok = true; |
| } |
| if (ok && !ignoreDeprecated && deprecated > 0) { |
| ok = false; |
| } |
| |
| if (!ok) { |
| if (result.getUnknownComponent() != null) { |
| unknownComponents++; |
| } else if (result.getIncapable() != null) { |
| incapableErrors++; |
| } else { |
| configurationErrors++; |
| } |
| |
| StringBuilder sb = new StringBuilder(); |
| sb.append("Configuration validation error at: "); |
| sb.append("(").append(result.getFileName()); |
| if (result.getLineNumber() > 0) { |
| sb.append(":").append(result.getLineNumber()); |
| } |
| sb.append(")"); |
| sb.append("\n\n"); |
| String out = result.summaryErrorMessage(false, ignoreDeprecated, true); |
| sb.append(out); |
| sb.append("\n\n"); |
| |
| getLog().warn(sb.toString()); |
| } else if (showAll) { |
| StringBuilder sb = new StringBuilder(); |
| sb.append("Configuration validation passed at: "); |
| sb.append(result.getFileName()); |
| if (result.getLineNumber() > 0) { |
| sb.append(":").append(result.getLineNumber()); |
| } |
| sb.append("\n"); |
| sb.append("\n\t").append(result.getText()); |
| sb.append("\n\n"); |
| |
| getLog().info(sb.toString()); |
| } |
| } |
| String configurationSummary; |
| if (configurationErrors == 0) { |
| int ok = results.size() - configurationErrors - incapableErrors - unknownComponents; |
| configurationSummary = String.format("Configuration validation success: (%s = passed, %s = invalid, %s = incapable, %s = unknown components, %s = deprecated options)", |
| ok, configurationErrors, incapableErrors, unknownComponents, deprecatedOptions); |
| } else { |
| int ok = results.size() - configurationErrors - incapableErrors - unknownComponents; |
| configurationSummary = String.format("Configuration validation error: (%s = passed, %s = invalid, %s = incapable, %s = unknown components, %s = deprecated options)", |
| ok, configurationErrors, incapableErrors, unknownComponents, deprecatedOptions); |
| } |
| if (configurationErrors > 0) { |
| getLog().warn(configurationSummary); |
| } else { |
| getLog().info(configurationSummary); |
| } |
| |
| if (failOnError && (configurationErrors > 0)) { |
| throw new MojoExecutionException(configurationSummary + "\n"); |
| } |
| } |
| |
| private int findLineNumberInPropertiesFile(File file, String name) { |
| name = name.trim(); |
| // try to find the line number |
| try (LineNumberReader r = new LineNumberReader(new FileReader(file))) { |
| String line = r.readLine(); |
| while (line != null) { |
| int pos = line.indexOf('='); |
| if (pos > 0) { |
| line = line.substring(0, pos); |
| } |
| line = line.trim(); |
| if (line.equals(name)) { |
| return r.getLineNumber(); |
| } |
| line = r.readLine(); |
| } |
| } catch (Exception e) { |
| // ignore |
| } |
| |
| return -1; |
| } |
| |
| protected void doExecuteRoutes(CamelCatalog catalog) throws MojoExecutionException, MojoFailureException { |
| List<CamelEndpointDetails> endpoints = new ArrayList<>(); |
| List<CamelSimpleExpressionDetails> simpleExpressions = new ArrayList<>(); |
| List<CamelRouteDetails> routeIds = new ArrayList<>(); |
| Set<File> javaFiles = new LinkedHashSet<>(); |
| Set<File> xmlFiles = new LinkedHashSet<>(); |
| |
| // find all java route builder classes |
| if (includeJava) { |
| List list = project.getCompileSourceRoots(); |
| for (Object obj : list) { |
| String dir = (String) obj; |
| findJavaFiles(new File(dir), javaFiles); |
| } |
| if (includeTest) { |
| list = project.getTestCompileSourceRoots(); |
| for (Object obj : list) { |
| String dir = (String) obj; |
| findJavaFiles(new File(dir), javaFiles); |
| } |
| } |
| } |
| // find all xml routes |
| if (includeXml) { |
| List list = project.getResources(); |
| for (Object obj : list) { |
| Resource dir = (Resource) obj; |
| findXmlFiles(new File(dir.getDirectory()), xmlFiles); |
| } |
| if (includeTest) { |
| list = project.getTestResources(); |
| for (Object obj : list) { |
| Resource dir = (Resource) obj; |
| findXmlFiles(new File(dir.getDirectory()), xmlFiles); |
| } |
| } |
| } |
| |
| for (File file : javaFiles) { |
| if (matchRouteFile(file)) { |
| try { |
| List<CamelEndpointDetails> fileEndpoints = new ArrayList<>(); |
| List<CamelRouteDetails> fileRouteIds = new ArrayList<>(); |
| List<CamelSimpleExpressionDetails> fileSimpleExpressions = new ArrayList<>(); |
| List<String> unparsable = new ArrayList<>(); |
| |
| // parse the java source code and find Camel RouteBuilder classes |
| String fqn = file.getPath(); |
| String baseDir = "."; |
| JavaType out = Roaster.parse(file); |
| // we should only parse java classes (not interfaces and enums etc) |
| if (out instanceof JavaClassSource) { |
| 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()) { |
| for (String uri : unparsable) { |
| getLog().warn("Cannot parse endpoint uri " + uri + " in java file " + file); |
| } |
| } |
| } |
| } catch (Exception e) { |
| getLog().warn("Error parsing java file " + file + " code due " + e.getMessage(), e); |
| } |
| } |
| } |
| for (File file : xmlFiles) { |
| if (matchRouteFile(file)) { |
| 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(); |
| String baseDir = "."; |
| |
| InputStream is = new FileInputStream(file); |
| XmlRouteParser.parseXmlRouteEndpoints(is, baseDir, fqn, fileEndpoints); |
| is.close(); |
| // need a new stream |
| is = new FileInputStream(file); |
| 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); |
| } |
| } |
| } |
| |
| // endpoint uris |
| int endpointErrors = 0; |
| int unknownComponents = 0; |
| int incapableErrors = 0; |
| int deprecatedOptions = 0; |
| for (CamelEndpointDetails detail : endpoints) { |
| getLog().debug("Validating endpoint: " + detail.getEndpointUri()); |
| EndpointValidationResult result = catalog.validateEndpointProperties(detail.getEndpointUri(), ignoreLenientProperties); |
| int deprecated = result.getDeprecated() != null ? result.getDeprecated().size() : 0; |
| deprecatedOptions += deprecated; |
| |
| boolean ok = result.isSuccess() && !result.hasWarnings(); |
| if (!ok && ignoreUnknownComponent && result.getUnknownComponent() != null) { |
| // if we failed due unknown component then be okay if we should ignore that |
| unknownComponents++; |
| ok = true; |
| } |
| if (!ok && ignoreIncapable && result.getIncapable() != null) { |
| // if we failed due incapable then be okay if we should ignore that |
| incapableErrors++; |
| ok = true; |
| } |
| if (ok && !ignoreDeprecated && deprecated > 0) { |
| ok = false; |
| } |
| |
| if (!ok) { |
| if (result.getUnknownComponent() != null) { |
| unknownComponents++; |
| } else if (result.getIncapable() != null) { |
| incapableErrors++; |
| } else { |
| endpointErrors++; |
| } |
| |
| StringBuilder sb = new StringBuilder(); |
| sb.append("Endpoint 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\n"); |
| String out = result.summaryErrorMessage(false, ignoreDeprecated, true); |
| sb.append(out); |
| sb.append("\n\n"); |
| |
| getLog().warn(sb.toString()); |
| } else if (showAll) { |
| StringBuilder sb = new StringBuilder(); |
| sb.append("Endpoint 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(result.getUri()); |
| sb.append("\n\n"); |
| |
| getLog().info(sb.toString()); |
| } |
| } |
| String endpointSummary; |
| if (endpointErrors == 0) { |
| int ok = endpoints.size() - endpointErrors - incapableErrors - unknownComponents; |
| endpointSummary = String.format("Endpoint validation success: (%s = passed, %s = invalid, %s = incapable, %s = unknown components, %s = deprecated options)", |
| ok, endpointErrors, incapableErrors, unknownComponents, deprecatedOptions); |
| } else { |
| int ok = endpoints.size() - endpointErrors - incapableErrors - unknownComponents; |
| 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) { |
| int ok = simpleExpressions.size() - simpleErrors; |
| simpleSummary = String.format("Simple validation success: (%s = passed, %s = invalid)", ok, simpleErrors); |
| } else { |
| int ok = simpleExpressions.size() - simpleErrors; |
| simpleSummary = String.format("Simple validation error: (%s = passed, %s = invalid)", ok, simpleErrors); |
| } |
| if (simpleErrors > 0) { |
| getLog().warn(simpleSummary); |
| } else { |
| 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); |
| } |
| if (sedaDirectErrors > 0) { |
| getLog().warn(sedaDirectSummary); |
| } else { |
| getLog().info(sedaDirectSummary); |
| } |
| } |
| |
| // route id |
| int duplicateRouteIdErrors = validateDuplicateRouteId(routeIds); |
| 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) || 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 producers that has a consumer (no need to check for opposite) |
| for (CamelEndpointDetails p : producers) { |
| boolean any = consumers.stream().anyMatch(c -> matchEndpointPath(p.getEndpointUri(), c.getEndpointUri())); |
| 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 producers that do not have a consumer pair |
| for (CamelEndpointDetails detail : producers) { |
| boolean none = consumers.stream().noneMatch(c -> matchEndpointPath(detail.getEndpointUri(), c.getEndpointUri())); |
| if (none) { |
| errors++; |
| |
| StringBuilder sb = new StringBuilder(); |
| sb.append("Endpoint pair (seda/direct) 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.getEndpointUri()); |
| sb.append("\n\n\t\t\t\t").append(endpointPathSummaryError(detail)); |
| sb.append("\n\n"); |
| |
| getLog().warn(sb.toString()); |
| } else if (showAll) { |
| StringBuilder sb = new StringBuilder(); |
| sb.append("Endpoint pair (seda/direct) 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.getEndpointUri()); |
| sb.append("\n\n"); |
| |
| getLog().info(sb.toString()); |
| } |
| } |
| |
| // NOTE: are there any consumers that do not have a producer pair |
| // You can have a consumer which you send to from outside a Camel route such as via ProducerTemplate |
| |
| return errors; |
| } |
| |
| private static String endpointPathSummaryError(CamelEndpointDetails detail) { |
| String uri = detail.getEndpointUri(); |
| String p = uri.contains("?") ? StringHelper.before(uri, "?") : uri; |
| String path = StringHelper.after(p, ":"); |
| return path + "\t" + "Sending to non existing " + detail.getEndpointComponentName() + " queue name"; |
| } |
| |
| private static boolean matchEndpointPath(String uri, String uri2) { |
| String p = uri.contains("?") ? StringHelper.before(uri, "?") : uri; |
| String p2 = uri2.contains("?") ? StringHelper.before(uri2, "?") : uri2; |
| p = p.trim(); |
| p2 = p2.trim(); |
| return p.equals(p2); |
| } |
| |
| private int validateSimple(CamelCatalog catalog, List<CamelSimpleExpressionDetails> simpleExpressions) { |
| int simpleErrors = 0; |
| for (CamelSimpleExpressionDetails detail : simpleExpressions) { |
| LanguageValidationResult result; |
| boolean predicate = detail.isPredicate(); |
| if (predicate) { |
| getLog().debug("Validating simple predicate: " + detail.getSimple()); |
| result = catalog.validateLanguagePredicate(null, "simple", detail.getSimple()); |
| } else { |
| getLog().debug("Validating simple expression: " + detail.getSimple()); |
| result = catalog.validateLanguageExpression(null, "simple", detail.getSimple()); |
| } |
| if (!result.isSuccess()) { |
| simpleErrors++; |
| |
| StringBuilder sb = new StringBuilder(); |
| sb.append("Simple 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"); |
| String[] lines = result.getError().split("\n"); |
| for (String line : lines) { |
| sb.append("\n\t").append(line); |
| } |
| sb.append("\n"); |
| |
| getLog().warn(sb.toString()); |
| } else if (showAll) { |
| StringBuilder sb = new StringBuilder(); |
| sb.append("Simple 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(result.getText()); |
| sb.append("\n\n"); |
| |
| getLog().info(sb.toString()); |
| } |
| } |
| return simpleErrors; |
| } |
| |
| private int validateDuplicateRouteId(List<CamelRouteDetails> routeIds) { |
| 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()); |
| } |
| } |
| } |
| return duplicateRouteIdErrors; |
| } |
| // 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; |
| |
| List list = project.getDependencies(); |
| for (Object obj : list) { |
| Dependency dep = (Dependency) obj; |
| if ("org.apache.camel".equals(dep.getGroupId())) { |
| if ("camel-core".equals(dep.getArtifactId())) { |
| // favor camel-core |
| candidate = dep; |
| break; |
| } else { |
| candidate = dep; |
| } |
| } |
| } |
| if (candidate != null) { |
| return candidate.getVersion(); |
| } |
| |
| 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) { |
| for (File file : files) { |
| if (file.getName().endsWith(".java")) { |
| javaFiles.add(file); |
| } else if (file.isDirectory()) { |
| findJavaFiles(file, javaFiles); |
| } |
| } |
| } |
| } |
| |
| private void findXmlFiles(File dir, Set<File> xmlFiles) { |
| File[] files = dir.isDirectory() ? dir.listFiles() : null; |
| if (files != null) { |
| for (File file : files) { |
| if (file.getName().endsWith(".xml")) { |
| xmlFiles.add(file); |
| } else if (file.isDirectory()) { |
| findXmlFiles(file, xmlFiles); |
| } |
| } |
| } |
| } |
| |
| 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; |
| } |
| |
| // exclude take precedence |
| if (excludes != null) { |
| for (String exclude : excludes.split(",")) { |
| exclude = exclude.trim(); |
| // try both with and without directory in the name |
| String fqn = stripRootPath(asRelativeFile(file.getAbsolutePath())); |
| boolean match = PatternHelper.matchPattern(fqn, exclude) || PatternHelper.matchPattern(file.getName(), exclude); |
| if (match) { |
| return false; |
| } |
| } |
| } |
| |
| // include |
| if (includes != null) { |
| for (String include : includes.split(",")) { |
| include = include.trim(); |
| // try both with and without directory in the name |
| String fqn = stripRootPath(asRelativeFile(file.getAbsolutePath())); |
| boolean match = PatternHelper.matchPattern(fqn, include) || PatternHelper.matchPattern(file.getName(), include); |
| if (match) { |
| return true; |
| } |
| } |
| // did not match any includes |
| return false; |
| } |
| |
| // was not excluded nor failed include so its accepted |
| return true; |
| } |
| |
| private String asRelativeFile(String name) { |
| String answer = name; |
| |
| String base = project.getBasedir().getAbsolutePath(); |
| if (name.startsWith(base)) { |
| answer = name.substring(base.length()); |
| // skip leading slash for relative path |
| if (answer.startsWith(File.separator)) { |
| answer = answer.substring(1); |
| } |
| } |
| return answer; |
| } |
| |
| private String stripRootPath(String name) { |
| // strip out any leading source / resource directory |
| |
| List list = project.getCompileSourceRoots(); |
| for (Object obj : list) { |
| String dir = (String) obj; |
| dir = asRelativeFile(dir); |
| if (name.startsWith(dir)) { |
| return name.substring(dir.length() + 1); |
| } |
| } |
| list = project.getTestCompileSourceRoots(); |
| for (Object obj : list) { |
| String dir = (String) obj; |
| dir = asRelativeFile(dir); |
| if (name.startsWith(dir)) { |
| return name.substring(dir.length() + 1); |
| } |
| } |
| List resources = project.getResources(); |
| for (Object obj : resources) { |
| Resource resource = (Resource) obj; |
| String dir = asRelativeFile(resource.getDirectory()); |
| if (name.startsWith(dir)) { |
| return name.substring(dir.length() + 1); |
| } |
| } |
| resources = project.getTestResources(); |
| for (Object obj : resources) { |
| Resource resource = (Resource) obj; |
| String dir = asRelativeFile(resource.getDirectory()); |
| if (name.startsWith(dir)) { |
| return name.substring(dir.length() + 1); |
| } |
| } |
| |
| return name; |
| } |
| |
| private static String asPackageName(String name) { |
| return name.replace(File.separator, "."); |
| } |
| |
| private static String asSimpleClassName(String className) { |
| int dot = className.lastIndexOf('.'); |
| if (dot > 0) { |
| return className.substring(dot + 1); |
| } else { |
| return className; |
| } |
| } |
| } |