blob: 60de492146ca412a36589cdd70dfd2b296cbcbc9 [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.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import org.apache.camel.util.CastUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter;
import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Exclusion;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectBuilder;
import org.apache.maven.project.artifact.MavenMetadataSource;
import org.codehaus.mojo.exec.AbstractExecMojo;
import org.codehaus.mojo.exec.ExecutableDependency;
import org.codehaus.mojo.exec.Property;
/**
* Runs a CamelContext using OSGi Blueprint.
*/
@Mojo(name = "run", defaultPhase = LifecyclePhase.PREPARE_PACKAGE,
requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME)
public class KarafRunMojo extends AbstractExecMojo {
// this code is based on a copy-and-paste of maven-exec-plugin
//
// If we could avoid the mega-cut-n-paste it would really really help!
// ideally all I wanna do is auto-default 2 values!
// namely the main and the command line arguments..
/**
* The maven project.
*
* @parameter property="project"
* @required
* @readonly
*/
@Parameter(property = "project", required = true, readonly = true)
protected MavenProject project;
/**
* Sets the time duration (seconds) that the application will run for before terminating. A value <= 0 will run
* forever.
*/
@Parameter(property = "camel.duration", defaultValue = "-1")
protected String duration;
/**
* Sets the idle time duration (seconds) duration that the application can be idle before terminating. A value <= 0
* will run forever.
*/
@Parameter(property = "camel.durationIdle", defaultValue = "-1")
protected String durationIdle;
/**
* Sets the duration of maximum number of messages that the application will process before terminating.
*/
@Parameter(property = "camel.duration.maxMessages", defaultValue = "-1")
protected String durationMaxMessages;
/**
* Whether to log the classpath when starting
*/
@Parameter(property = "camel.logClasspath", defaultValue = "false")
protected boolean logClasspath;
/**
* Whether to use Blueprint when running, instead of Spring
*/
@Parameter(property = "camel.useBlueprint")
protected Boolean useBlueprint;
/**
* Whether to enable the tracer or not
*/
@Parameter(property = "camel.trace")
protected boolean trace;
/**
* The main class to execute.
*/
@Parameter(property = "camel.mainClass")
protected String mainClass;
/**
* The basedPackages that spring java config want to gets.
*/
@Parameter(property = "camel.basedPackages")
protected String basedPackages;
/**
* The configClasses that spring java config want to gets.
*/
@Parameter(property = "camel.configClasses")
protected String configClasses;
/**
* The classpath based application context uri that spring want to gets.
*/
@Parameter(property = "camel.applicationContextUri")
protected String applicationContextUri;
/**
* The filesystem based application context uri that spring want to gets.
*/
@Parameter(property = "camel.fileApplicationContextUri")
protected String fileApplicationContextUri;
/**
* The configureAdmin persistent id, it will be used when loading the camel context from blueprint.
*/
@Parameter(property = "camel.configAdminPid")
protected String configAdminPid;
/**
* The configureAdmin persistent file name, it will be used when loading the camel context from blueprint.
*/
@Parameter(property = "camel.configAdminFileName")
protected String configAdminFileName;
/**
* To watch the directory for file changes which triggers a live reload of the Camel routes on-the-fly.
*/
@Parameter(property = "camel.fileWatcherDirectory")
protected String fileWatcherDirectory;
/**
* The class arguments.
*/
@Parameter(property = "camel.arguments")
protected String[] arguments;
/**
* A list of system properties to be passed. Note: as the execution is not forked, some system properties required
* by the JVM cannot be passed here. Use MAVEN_OPTS or the exec:exec instead. See the user guide for more
* information.
*/
protected Property[] systemProperties;
/**
* Deprecated; this is not needed anymore. Indicates if mojo should be kept running after the mainclass terminates.
* Usefull for serverlike apps with deamonthreads.
*/
@Parameter(property = "camel.keepAlive")
protected boolean keepAlive;
/**
* Indicates if the project dependencies should be used when executing the main class.
*/
@Parameter(property = "camel.includeProjectDependencies", defaultValue = "true")
protected boolean includeProjectDependencies;
/**
* Indicates if this plugin's dependencies should be used when executing the main class.
* <p/>
* This is useful when project dependencies are not appropriate. Using only the plugin dependencies can be
* particularly useful when the project is not a java project. For example a mvn project using the csharp plugins
* only expects to see dotnet libraries as dependencies.
*/
@Parameter(property = "camel.includePluginDependencies", defaultValue = "false")
protected boolean includePluginDependencies;
/**
* If provided the ExecutableDependency identifies which of the plugin dependencies contains the executable class.
* This will have the affect of only including plugin dependencies required by the identified ExecutableDependency.
* <p/>
* If includeProjectDependencies is set to <code>true</code>, all of the project dependencies will be included on
* the executable's classpath. Whether a particular project dependency is a dependency of the identified
* ExecutableDependency will be irrelevant to its inclusion in the classpath.
*/
@Parameter(property = "camel.executableDependency")
protected ExecutableDependency executableDependency;
/**
* Whether to interrupt/join and possibly stop the daemon threads upon quitting. <br/>
* If this is <code>false</code>, maven does nothing about the daemon threads. When maven has no more work to do,
* the VM will normally terminate any remaining daemon threads.
* <p>
* In certain cases (in particular if maven is embedded), you might need to keep this enabled to make sure threads
* are properly cleaned up to ensure they don't interfere with subsequent activity. In that case, see
* {@link #daemonThreadJoinTimeout} and {@link #stopUnresponsiveDaemonThreads} for further tuning.
* </p>
*/
@Parameter(property = "camel.cleanupDaemonThreads", defaultValue = "true")
protected boolean cleanupDaemonThreads;
/**
* This defines the number of milliseconds to wait for daemon threads to quit following their interruption.<br/>
* This is only taken into account if {@link #cleanupDaemonThreads} is <code>true</code>. A value &lt;=0 means to
* not timeout (i.e. wait indefinitely for threads to finish). Following a timeout, a warning will be logged.
* <p>
* Note: properly coded threads <i>should</i> terminate upon interruption but some threads may prove problematic: as
* the VM does interrupt daemon threads, some code may not have been written to handle interruption properly. For
* example java.util.Timer is known to not handle interruptions in JDK &lt;= 1.6. So it is not possible for us to
* infinitely wait by default otherwise maven could hang. A sensible default value has been chosen, but this default
* value <i>may change</i> in the future based on user feedback.
* </p>
*/
@Parameter(property = "camel.daemonThreadJoinTimeout", defaultValue = "15000")
protected long daemonThreadJoinTimeout;
/**
* Wether to call {@link Thread#stop()} following a timing out of waiting for an interrupted thread to finish. This
* is only taken into account if {@link #cleanupDaemonThreads} is <code>true</code> and the
* {@link #daemonThreadJoinTimeout} threshold has been reached for an uncooperative thread. If this is
* <code>false</code>, or if {@link Thread#stop()} fails to get the thread to stop, then a warning is logged and
* Maven will continue on while the affected threads (and related objects in memory) linger on. Consider setting
* this to <code>true</code> if you are invoking problematic code that you can't fix. An example is
* {@link java.util.Timer} which doesn't respond to interruption. To have <code>Timer</code> fixed, vote for
* <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6336543">this bug</a>.
*/
@Parameter(property = "camel.stopUnresponsiveDaemonThreads", defaultValue = "15000")
protected boolean stopUnresponsiveDaemonThreads;
protected String extendedPluginDependencyArtifactId;
@Component
private ArtifactResolver artifactResolver;
@Component
private ArtifactFactory artifactFactory;
@Component
private ArtifactMetadataSource metadataSource;
@Parameter(property = "localRepository")
private ArtifactRepository localRepository;
@Parameter(property = "project.remoteArtifactRepositories")
private List remoteRepositories;
@Component
private MavenProjectBuilder projectBuilder;
@Parameter(property = "plugin.artifacts")
private List<Artifact> pluginDependencies;
private Properties originalSystemProperties;
private String extraPluginDependencyArtifactId;
protected List<String> parseArguments() throws MojoFailureException {
String skip = System.getProperties().getProperty("maven.test.skip");
if (skip == null || "false".equals(skip)) {
// lets log a INFO about how to skip tests if you want to so you can run faster
getLog().info("You can skip tests from the command line using: mvn camel:run -Dmaven.test.skip=true");
}
boolean usingBlueprintMain;
if (useBlueprint != null) {
// use configured value
usingBlueprintMain = useBlueprint;
} else {
// auto detect if we have blueprint
usingBlueprintMain = detectBlueprintOnClassPathOrBlueprintXMLFiles();
}
// lets create the command line arguments to pass in...
List<String> args = new ArrayList<>();
if (trace) {
args.add("-t");
}
if (fileWatcherDirectory != null) {
args.add("-watch");
args.add(fileWatcherDirectory);
}
if (applicationContextUri != null) {
args.add("-ac");
args.add(applicationContextUri);
} else if (fileApplicationContextUri != null) {
args.add("-fa");
args.add(fileApplicationContextUri);
}
if (!duration.equals("-1")) {
args.add("-d");
args.add(duration);
}
if (!durationIdle.equals("-1")) {
args.add("-di");
args.add(durationIdle);
}
if (!durationMaxMessages.equals("-1")) {
args.add("-dm");
args.add(durationMaxMessages);
}
if (arguments != null) {
args.addAll(Arrays.asList(arguments));
}
if (usingBlueprintMain) {
mainClass = "org.apache.camel.blueprint.Main";
// set the configAdmin pid
if (configAdminPid != null) {
args.add("-pid");
args.add(configAdminPid);
}
// set the configAdmin pFile
if (configAdminFileName != null) {
args.add("-pf");
args.add(configAdminFileName);
}
extraPluginDependencyArtifactId = "camel-blueprint-main";
getLog().info("Using org.apache.camel.blueprint.Main to initiate a CamelContext");
}
return args;
}
/**
* Execute goal.
*
* @throws MojoExecutionException execution of the main class or one of the threads it generated failed.
* @throws MojoFailureException something bad happened...
*/
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
List<String> args = parseArguments();
arguments = new String[args.size()];
args.toArray(arguments);
if (getLog().isDebugEnabled()) {
StringBuilder msg = new StringBuilder("Invoking: ");
msg.append(mainClass);
msg.append(".main(");
for (int i = 0; i < arguments.length; i++) {
if (i > 0) {
msg.append(", ");
}
msg.append(arguments[i]);
}
msg.append(")");
getLog().debug(msg);
}
final ClassLoader loader = getClassLoader();
KarafRunMojo.IsolatedThreadGroup threadGroup = new KarafRunMojo.IsolatedThreadGroup(mainClass /* name */);
final Thread bootstrapThread = new Thread(threadGroup, new Runnable() {
public void run() {
try {
beforeBootstrapCamel();
getLog().info("Starting Camel ...");
Method main = Thread.currentThread().getContextClassLoader()
.loadClass(mainClass).getMethod("main", String[].class);
main.invoke(null, new Object[] { arguments });
afterBootstrapCamel();
} catch (Exception e) { // just pass it on
// let it be printed so end users can see the exception on the console
getLog().error("*************************************");
getLog().error("Error occurred while running main from: " + mainClass);
getLog().error(e);
getLog().error("*************************************");
Thread.currentThread().getThreadGroup().uncaughtException(Thread.currentThread(), e);
}
}
}, mainClass + ".main()");
bootstrapThread.setContextClassLoader(loader);
setSystemProperties();
bootstrapThread.start();
joinNonDaemonThreads(threadGroup);
// It's plausible that spontaneously a non-daemon thread might be
// created as we try and shut down,
// but it's too late since the termination condition (only daemon
// threads) has been triggered.
if (keepAlive) {
getLog().warn("Warning: keepAlive is now deprecated and obsolete. Do you need it? Please comment on MEXEC-6.");
waitFor(0);
}
if (cleanupDaemonThreads) {
terminateThreads(threadGroup);
try {
threadGroup.destroy();
} catch (IllegalThreadStateException e) {
getLog().warn("Couldn't destroy threadgroup " + threadGroup, e);
}
}
if (originalSystemProperties != null) {
System.setProperties(originalSystemProperties);
}
synchronized (threadGroup) {
if (threadGroup.uncaughtException != null) {
throw new MojoExecutionException(null, threadGroup.uncaughtException);
}
}
registerSourceRoots();
}
/**
* Allows plugin extensions to do custom logic before bootstrapping Camel.
*/
protected void beforeBootstrapCamel() throws Exception {
// noop
}
/**
* Allows plugin extensions to do custom logic after bootstrapping Camel.
*/
protected void afterBootstrapCamel() throws Exception {
// noop
}
class IsolatedThreadGroup extends ThreadGroup {
Throwable uncaughtException; // synchronize access to this
IsolatedThreadGroup(String name) {
super(name);
}
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
if (throwable instanceof ThreadDeath) {
return; // harmless
}
boolean doLog = false;
synchronized (this) {
// only remember the first one
if (uncaughtException == null) {
uncaughtException = throwable; // will be reported
// eventually
} else {
doLog = true;
}
}
if (doLog) {
getLog().warn("an additional exception was thrown", throwable);
}
}
}
private void joinNonDaemonThreads(ThreadGroup threadGroup) {
boolean foundNonDaemon;
do {
foundNonDaemon = false;
Collection<Thread> threads = getActiveThreads(threadGroup);
for (Thread thread : threads) {
if (thread.isDaemon()) {
continue;
}
foundNonDaemon = true; // try again; maybe more threads were
// created while we were busy
joinThread(thread, 0);
}
} while (foundNonDaemon);
}
private void joinThread(Thread thread, long timeoutMsecs) {
try {
getLog().debug("joining on thread " + thread);
thread.join(timeoutMsecs);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // good practice if don't throw
getLog().warn("interrupted while joining against thread " + thread, e); // not
// expected!
}
// generally abnormal
if (thread.isAlive()) {
getLog().warn("thread " + thread + " was interrupted but is still alive after waiting at least "
+ timeoutMsecs + "msecs");
}
}
private void terminateThreads(ThreadGroup threadGroup) {
long startTime = System.currentTimeMillis();
Set<Thread> uncooperativeThreads = new HashSet<>(); // these were not responsive
// to interruption
for (Collection<Thread> threads = getActiveThreads(threadGroup);
!threads.isEmpty();
threads = getActiveThreads(threadGroup), threads
.removeAll(uncooperativeThreads)) {
// Interrupt all threads we know about as of this instant (harmless
// if spuriously went dead (! isAlive())
// or if something else interrupted it ( isInterrupted() ).
for (Thread thread : threads) {
getLog().debug("interrupting thread " + thread);
thread.interrupt();
}
// Now join with a timeout and call stop() (assuming flags are set
// right)
for (Thread thread : threads) {
if (!thread.isAlive()) {
continue; // and, presumably it won't show up in
// getActiveThreads() next iteration
}
if (daemonThreadJoinTimeout <= 0) {
joinThread(thread, 0); // waits until not alive; no timeout
continue;
}
long timeout = daemonThreadJoinTimeout - (System.currentTimeMillis() - startTime);
if (timeout > 0) {
joinThread(thread, timeout);
}
if (!thread.isAlive()) {
continue;
}
uncooperativeThreads.add(thread); // ensure we don't process
// again
if (stopUnresponsiveDaemonThreads) {
getLog().warn("thread " + thread + " will be Thread.stop()'ed");
thread.stop();
} else {
getLog().warn("thread " + thread
+ " will linger despite being asked to die via interruption");
}
}
}
if (!uncooperativeThreads.isEmpty()) {
getLog().warn("NOTE: "
+ uncooperativeThreads.size()
+ " thread(s) did not finish despite being asked to "
+ " via interruption. This is not a problem with exec:java, it is a problem with the running code."
+ " Although not serious, it should be remedied.");
} else {
int activeCount = threadGroup.activeCount();
if (activeCount != 0) {
// TODO this may be nothing; continue on anyway; perhaps don't
// even log in future
Thread[] threadsArray = new Thread[1];
threadGroup.enumerate(threadsArray);
if (getLog().isDebugEnabled()) {
getLog().debug("strange; " + activeCount + " thread(s) still active in the group "
+ threadGroup + " such as " + threadsArray[0]);
}
}
}
}
private Collection<Thread> getActiveThreads(ThreadGroup threadGroup) {
Thread[] threads = new Thread[threadGroup.activeCount()];
int numThreads = threadGroup.enumerate(threads);
Collection<Thread> result = new ArrayList<>(numThreads);
for (int i = 0; i < threads.length && threads[i] != null; i++) {
result.add(threads[i]);
}
// note: result should be modifiable
return result;
}
/**
* Pass any given system properties to the java system properties.
*/
private void setSystemProperties() {
if (systemProperties != null) {
originalSystemProperties = System.getProperties();
for (Property systemProperty : systemProperties) {
String value = systemProperty.getValue();
System.setProperty(systemProperty.getKey(), value == null ? "" : value);
}
}
}
/**
* Set up a classloader for the execution of the main class.
*
* @return the classloader
* @throws MojoExecutionException
*/
private ClassLoader getClassLoader() throws MojoExecutionException {
Set<URL> classpathURLs = new LinkedHashSet<>();
// project classpath must be first
this.addRelevantProjectDependenciesToClasspath(classpathURLs);
// and extra plugin classpath
this.addExtraPluginDependenciesToClasspath(classpathURLs);
// and plugin classpath last
this.addRelevantPluginDependenciesToClasspath(classpathURLs);
if (logClasspath) {
getLog().info("Classpath:");
for (URL url : classpathURLs) {
getLog().info(" " + url.getFile().toString());
}
}
return new URLClassLoader(classpathURLs.toArray(new URL[classpathURLs.size()]));
}
/**
* Add any relevant project dependencies to the classpath. Indirectly takes includePluginDependencies and
* ExecutableDependency into consideration.
*
* @param path classpath of {@link java.net.URL} objects
* @throws MojoExecutionException
*/
private void addRelevantPluginDependenciesToClasspath(Set<URL> path) throws MojoExecutionException {
if (hasCommandlineArgs()) {
arguments = parseCommandlineArgs();
}
try {
Iterator<Artifact> iter = this.determineRelevantPluginDependencies().iterator();
while (iter.hasNext()) {
Artifact classPathElement = iter.next();
// we must skip org.osgi.core, otherwise we get a
// java.lang.NoClassDefFoundError: org.osgi.vendor.framework property not set
if (classPathElement.getArtifactId().equals("org.osgi.core")) {
if (getLog().isDebugEnabled()) {
getLog().debug("Skipping org.osgi.core -> " + classPathElement.getGroupId() + "/"
+ classPathElement.getArtifactId() + "/" + classPathElement.getVersion());
}
continue;
}
getLog().debug("Adding plugin dependency artifact: " + classPathElement.getArtifactId()
+ " to classpath");
path.add(classPathElement.getFile().toURI().toURL());
}
} catch (MalformedURLException e) {
throw new MojoExecutionException("Error during setting up classpath", e);
}
}
/**
* Add any relevant project dependencies to the classpath. Indirectly takes includePluginDependencies and
* ExecutableDependency into consideration.
*
* @param path classpath of {@link java.net.URL} objects
* @throws MojoExecutionException
*/
private void addExtraPluginDependenciesToClasspath(Set<URL> path) throws MojoExecutionException {
if (extraPluginDependencyArtifactId == null && extendedPluginDependencyArtifactId == null) {
return;
}
try {
Set<Artifact> artifacts = new HashSet<>(this.pluginDependencies);
for (Artifact artifact : artifacts) {
if (artifact.getArtifactId().equals(extraPluginDependencyArtifactId)
|| artifact.getArtifactId().equals(extendedPluginDependencyArtifactId)) {
getLog().debug("Adding extra plugin dependency artifact: " + artifact.getArtifactId()
+ " to classpath");
path.add(artifact.getFile().toURI().toURL());
// add the transient dependencies of this artifact
Set<Artifact> deps = resolveExecutableDependencies(artifact, true);
if (deps != null) {
for (Artifact dep : deps) {
getLog().debug("Adding extra plugin dependency artifact: " + dep.getArtifactId()
+ " to classpath");
path.add(dep.getFile().toURI().toURL());
}
}
}
}
} catch (MalformedURLException e) {
throw new MojoExecutionException("Error during setting up classpath", e);
}
}
/**
* Add any relevant project dependencies to the classpath. Takes includeProjectDependencies into consideration.
*
* @param path classpath of {@link java.net.URL} objects
* @throws MojoExecutionException
*/
private void addRelevantProjectDependenciesToClasspath(Set<URL> path) throws MojoExecutionException {
if (this.includeProjectDependencies) {
try {
getLog().debug("Project Dependencies will be included.");
URL mainClasses = new File(project.getBuild().getOutputDirectory()).toURI().toURL();
getLog().debug("Adding to classpath : " + mainClasses);
path.add(mainClasses);
Set<Artifact> dependencies = CastUtils.cast(project.getArtifacts());
// system scope dependencies are not returned by maven 2.0. See
// MEXEC-17
dependencies.addAll(getAllNonTestScopedDependencies());
Iterator<Artifact> iter = dependencies.iterator();
while (iter.hasNext()) {
Artifact classPathElement = iter.next();
getLog().debug("Adding project dependency artifact: " + classPathElement.getArtifactId()
+ " to classpath");
File file = classPathElement.getFile();
if (file != null) {
path.add(file.toURI().toURL());
}
}
} catch (MalformedURLException e) {
throw new MojoExecutionException("Error during setting up classpath", e);
}
} else {
getLog().debug("Project Dependencies will be excluded.");
}
}
private Collection<Artifact> getAllNonTestScopedDependencies() throws MojoExecutionException {
List<Artifact> answer = new ArrayList<>();
for (Artifact artifact : getAllDependencies()) {
// do not add test artifacts
if (!artifact.getScope().equals(Artifact.SCOPE_TEST)) {
answer.add(artifact);
}
}
return answer;
}
// generic method to retrieve all the transitive dependencies
private Collection<Artifact> getAllDependencies() throws MojoExecutionException {
List<Artifact> artifacts = new ArrayList<>();
for (Iterator<?> dependencies = project.getDependencies().iterator(); dependencies.hasNext();) {
Dependency dependency = (Dependency) dependencies.next();
String groupId = dependency.getGroupId();
String artifactId = dependency.getArtifactId();
VersionRange versionRange;
try {
versionRange = VersionRange.createFromVersionSpec(dependency.getVersion());
} catch (InvalidVersionSpecificationException e) {
throw new MojoExecutionException("unable to parse version", e);
}
String type = dependency.getType();
if (type == null) {
type = "jar";
}
String classifier = dependency.getClassifier();
boolean optional = dependency.isOptional();
String scope = dependency.getScope();
if (scope == null) {
scope = Artifact.SCOPE_COMPILE;
}
Artifact art = this.artifactFactory.createDependencyArtifact(groupId, artifactId, versionRange,
type, classifier, scope, null, optional);
if (scope.equalsIgnoreCase(Artifact.SCOPE_SYSTEM)) {
art.setFile(new File(dependency.getSystemPath()));
}
List<String> exclusions = new ArrayList<>();
for (Exclusion exclusion : dependency.getExclusions()) {
exclusions.add(exclusion.getGroupId() + ":" + exclusion.getArtifactId());
}
ArtifactFilter newFilter = new ExcludesArtifactFilter(exclusions);
art.setDependencyFilter(newFilter);
artifacts.add(art);
}
return artifacts;
}
/**
* Determine all plugin dependencies relevant to the executable. Takes includePlugins, and the executableDependency
* into consideration.
*
* @return a set of Artifact objects. (Empty set is returned if there are no relevant plugin
* dependencies.)
* @throws MojoExecutionException
*/
private Set<Artifact> determineRelevantPluginDependencies() throws MojoExecutionException {
Set<Artifact> relevantDependencies;
if (this.includePluginDependencies) {
if (this.executableDependency == null) {
getLog().debug("All Plugin Dependencies will be included.");
relevantDependencies = new HashSet<>(this.pluginDependencies);
} else {
getLog().debug("Selected plugin Dependencies will be included.");
Artifact executableArtifact = this.findExecutableArtifact();
Artifact executablePomArtifact = this.getExecutablePomArtifact(executableArtifact);
relevantDependencies = this.resolveExecutableDependencies(executablePomArtifact, false);
}
} else {
getLog().debug("Only Direct Plugin Dependencies will be included.");
PluginDescriptor descriptor = (PluginDescriptor) getPluginContext().get("pluginDescriptor");
try {
relevantDependencies = artifactResolver
.resolveTransitively(MavenMetadataSource
.createArtifacts(this.artifactFactory,
descriptor.getPlugin().getDependencies(),
null, null, null),
this.project.getArtifact(),
Collections.emptyMap(),
this.localRepository,
this.remoteRepositories,
metadataSource,
new ScopeArtifactFilter(Artifact.SCOPE_RUNTIME),
Collections.emptyList())
.getArtifacts();
} catch (Exception ex) {
throw new MojoExecutionException(
"Encountered problems resolving dependencies of the plugin "
+ "in preparation for its execution.",
ex);
}
}
return relevantDependencies;
}
/**
* Get the artifact which refers to the POM of the executable artifact.
*
* @param executableArtifact this artifact refers to the actual assembly.
* @return an artifact which refers to the POM of the executable artifact.
*/
private Artifact getExecutablePomArtifact(Artifact executableArtifact) {
return this.artifactFactory.createBuildArtifact(executableArtifact.getGroupId(), executableArtifact
.getArtifactId(), executableArtifact.getVersion(), "pom");
}
/**
* Examine the plugin dependencies to find the executable artifact.
*
* @return an artifact which refers to the actual executable tool (not a POM)
* @throws MojoExecutionException
*/
@Override
protected Artifact findExecutableArtifact() throws MojoExecutionException {
// ILimitedArtifactIdentifier execToolAssembly =
// this.getExecutableToolAssembly();
Artifact executableTool = null;
for (Artifact pluginDep : this.pluginDependencies) {
if (this.executableDependency.matches(pluginDep)) {
executableTool = pluginDep;
break;
}
}
if (executableTool == null) {
throw new MojoExecutionException(
"No dependency of the plugin matches the specified executableDependency."
+ " Specified executableToolAssembly is: "
+ executableDependency.toString());
}
return executableTool;
}
private Set<Artifact> resolveExecutableDependencies(Artifact executablePomArtifact, boolean ignoreFailures)
throws MojoExecutionException {
Set<Artifact> executableDependencies = null;
try {
MavenProject executableProject = this.projectBuilder.buildFromRepository(executablePomArtifact,
this.remoteRepositories,
this.localRepository);
// get all of the dependencies for the executable project
List<Dependency> dependencies = executableProject.getDependencies();
// make Artifacts of all the dependencies
Set<Artifact> dependencyArtifacts
= MavenMetadataSource.createArtifacts(this.artifactFactory, dependencies,
null, null, null);
// not forgetting the Artifact of the project itself
dependencyArtifacts.add(executableProject.getArtifact());
// resolve runtime dependencies transitively to obtain a comprehensive list of assemblies
ArtifactResolutionResult result = artifactResolver.resolveTransitively(dependencyArtifacts,
executablePomArtifact,
Collections.emptyMap(),
this.localRepository,
this.remoteRepositories,
metadataSource, new ScopeArtifactFilter(Artifact.SCOPE_RUNTIME),
Collections.emptyList());
executableDependencies = CastUtils.cast(result.getArtifacts());
} catch (Exception ex) {
if (ignoreFailures) {
getLog().debug("Ignoring maven resolving dependencies failure " + ex.getMessage());
} else {
throw new MojoExecutionException(
"Encountered problems resolving dependencies of the executable "
+ "in preparation for its execution.",
ex);
}
}
return executableDependencies;
}
/**
* Stop program execution for nn millis.
*
* @param millis the number of millis-seconds to wait for, <code>0</code> stops program forever.
*/
private void waitFor(long millis) {
Object lock = new Object();
synchronized (lock) {
try {
lock.wait(millis);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // good practice if don't throw
getLog().warn("Spuriously interrupted while waiting for " + millis + "ms", e);
}
}
}
private boolean detectBlueprintOnClassPathOrBlueprintXMLFiles() {
List<Dependency> deps = project.getCompileDependencies();
for (Dependency dep : deps) {
if ("org.apache.camel".equals(dep.getGroupId()) && "camel-blueprint".equals(dep.getArtifactId())) {
getLog().info("camel-blueprint detected on classpath");
return true;
}
}
// maybe there is blueprint XML files
List<Resource> resources = project.getResources();
for (Resource res : resources) {
File dir = new File(res.getDirectory());
File xml = new File(dir, "OSGI-INF/blueprint");
if (xml.exists() && xml.isDirectory()) {
getLog().info("OSGi Blueprint XML files detected in directory " + xml);
return true;
}
}
return false;
}
}