blob: 32751a00ce99394798cf6ee43685e022f6dcae8f [file] [log] [blame]
/*
* 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;
}
}
}