All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.camel.maven.RunMojo Maven / Gradle / Ivy

The newest version!
/*
 * 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.List;
import java.util.Properties;
import java.util.Set;

import javax.inject.Inject;

import org.apache.camel.util.CastUtils;
import org.apache.camel.util.IOHelper;
import org.apache.camel.util.StopWatch;
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.ArtifactResolutionRequest;
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.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.apache.maven.project.artifact.ProjectArtifact;
import org.codehaus.mojo.exec.AbstractExecMojo;
import org.codehaus.mojo.exec.ExecutableDependency;
import org.codehaus.mojo.exec.Property;

/**
 * Runs a CamelContext using any Spring configuration files found in META-INF/spring/*.xml, and
 * camel-*.xml and starting up the context.
 */
@Mojo(name = "run", defaultPhase = LifecyclePhase.PREPARE_PACKAGE,
      requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME)
public class RunMojo extends AbstractExecMojo {

    //language=properties
    private static final String LOG4J_TEMPLATE
            = """
                    appender.stdout.type = Console
                    appender.stdout.name = out
                    appender.stdout.layout.type = PatternLayout
                    appender.stdout.layout.pattern = %style{%d{yyyy-MM-dd HH:mm:ss.SSS}}{Dim} %highlight{%5p} %style{%pid}{Magenta} %style{---}{Dim} %style{[%15.15t]}{Dim} %style{%-40.40c}{Cyan} : %m%n
                    rootLogger.level = @@@LOGGING_LEVEL@@@
                    rootLogger.appenderRef.out.ref = out
                    """;

    // 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..

    /**
     * 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 built-in console logging (uses log4j), which does not require to add any logging dependency to
     * your project.
     *
     * However, the logging is fixed to log to the console, with a color style that is similar to Spring Boot.
     *
     * You can change the root logging level to: FATAL, ERROR, WARN, INFO, DEBUG, TRACE, OFF
     */
    @Parameter(property = "camel.loggingLevel", defaultValue = "OFF")
    protected String loggingLevel;

    /**
     * Whether to use Kamelet (camel-main-kamelet) when running, instead of Spring
     */
    @Parameter(property = "camel.useKamelet")
    protected Boolean useKamelet;

    /**
     * To run with a specific Camel Main profile (dev,test,prod)
     */
    @Parameter(property = "camel.profile")
    protected String profile;

    protected String extendedPluginDependencyArtifactId;

    protected final ArtifactResolver artifactResolver;

    private final ArtifactFactory artifactFactory;

    private final ArtifactMetadataSource metadataSource;

    @Parameter(property = "localRepository")
    private ArtifactRepository localRepository;

    @Parameter(property = "project.remoteArtifactRepositories")
    private List remoteRepositories;

    private final MavenProjectBuilder projectBuilder;

    @Parameter(property = "plugin.artifacts")
    private List pluginDependencies;

    /**
     * Whether to enable the tracer or not
     */
    @Parameter(property = "camel.trace")
    private boolean trace;

    /**
     * The main class to execute.
     */
    @Parameter(property = "camel.mainClass")
    private String mainClass;

    /**
     * The classpath based application context uri that spring want to get.
     */
    @Parameter(property = "camel.applicationContextUri")
    private String applicationContextUri;

    /**
     * The filesystem-based application context uri that spring want to get.
     */
    @Parameter(property = "camel.fileApplicationContextUri")
    private String fileApplicationContextUri;

    /**
     * The configureAdmin persistent id, it will be used when loading the camel context from blueprint.
     */
    @Parameter(property = "camel.configAdminPid")
    private String configAdminPid;

    /**
     * The configureAdmin persistent file name, it will be used when loading the camel context from blueprint.
     */
    @Parameter(property = "camel.configAdminFileName")
    private String configAdminFileName;

    /**
     * The class arguments.
     */
    @Parameter(property = "camel.arguments")
    private 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.
     */
    private Property[] systemProperties;

    /**
     * Deprecated; this is not needed anymore. Indicates if mojo should be kept running after the mainclass has
     * terminated. Useful for serverlike apps with deamonthreads.
     */
    @Parameter(property = "camel.keepAlive")
    private boolean keepAlive;

    /**
     * Indicates if the project dependencies should be used when executing the main class.
     */
    @Parameter(property = "camel.includeProjectDependencies", defaultValue = "true")
    private boolean includeProjectDependencies;

    /**
     * Indicates if this plugin's dependencies should be used when executing the main class.
     * 

* 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") private boolean includePluginDependencies; /** * If provided, the {@link ExecutableDependency} identifies which of the plugin dependencies contains the executable * class. This will have the effect of only including plugin dependencies required by the identified * ExecutableDependency. *

* If includeProjectDependencies is set to true, all 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") private ExecutableDependency executableDependency; /** * Whether to interrupt/join and possibly stop the daemon threads upon quitting.
* If this is false, maven does nothing about the daemon threads. When maven has no more work to do, * the VM will normally terminate any remaining daemon threads. *

* 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. *

*/ @Parameter(property = "camel.cleanupDaemonThreads", defaultValue = "true") private boolean cleanupDaemonThreads; /** * This defines the number of milliseconds to wait for daemon threads to quit following their interruption.
* This is only taken into account if {@link #cleanupDaemonThreads} is true. A value <=0 means to * not timeout (i.e., wait indefinitely for threads to finish). Following a timeout, a warning will be logged. *

* Note: properly coded threads should 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 <= 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 may change in the future based on user feedback. *

*/ @Parameter(property = "camel.daemonThreadJoinTimeout", defaultValue = "15000") private 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 true and the * {@link #daemonThreadJoinTimeout} threshold has been reached for an uncooperative thread. If this is * false, 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 true 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 Timer fixed, vote for * this bug. */ @Parameter(property = "camel.stopUnresponsiveDaemonThreads", defaultValue = "15000") private boolean stopUnresponsiveDaemonThreads; private Properties originalSystemProperties; private String extraPluginDependencyArtifactId; @Inject public RunMojo( ArtifactResolver artifactResolver, ArtifactFactory artifactFactory, ArtifactMetadataSource metadataSource, MavenProjectBuilder projectBuilder) { this.artifactResolver = artifactResolver; this.artifactFactory = artifactFactory; this.metadataSource = metadataSource; this.projectBuilder = projectBuilder; } /** * 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 { 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 " + goal() + " -Dmaven.test.skip=true"); } boolean usingKameletMain; if (useKamelet != null) { // use configured value usingKameletMain = useKamelet; } else { // auto detect if we have blueprint usingKameletMain = detectKameletOnClassPath(); } // lets create the command line arguments to pass in... List args = new ArrayList<>(); if (trace) { args.add("-t"); } 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 (profile != null) { args.add("-profile"); args.add(profile); } if (mainClass == null && usingKameletMain) { mainClass = "org.apache.camel.main.KameletMain"; // must include plugin dependencies for kamelet extraPluginDependencyArtifactId = "camel-kamelet-main"; getLog().info("Using " + mainClass + " to initiate a CamelContext"); } else if (mainClass != null) { getLog().info("Using custom " + mainClass + " to initiate a CamelContext"); } else { // use spring by default getLog().info("Using org.apache.camel.spring.Main to initiate a CamelContext"); mainClass = "org.apache.camel.spring.Main"; } 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(); IsolatedThreadGroup threadGroup = new IsolatedThreadGroup(mainClass /* name */); if (useKamelet != null && usingKameletMain && !detectKameletOnClassPath()) { throw new MojoFailureException( "Cannot run Kamelet Main because camel-kamelet-main JAR is not available on classpath"); } final Thread bootstrapThread = new Thread(threadGroup, () -> { 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); } if (originalSystemProperties != null) { System.setProperties(originalSystemProperties); } synchronized (threadGroup) { if (threadGroup.uncaughtException != null) { throw new MojoExecutionException(null, threadGroup.uncaughtException); } } registerSourceRoots(); } protected String goal() { return "camel:run"; } /** * 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 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) { StopWatch watch = new StopWatch(); Set uncooperativeThreads = new HashSet<>(); // these were not responsive // to interruption for (Collection 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 - watch.taken(); 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 getActiveThreads(ThreadGroup threadGroup) { Thread[] threads = new Thread[threadGroup.activeCount()]; int numThreads = threadGroup.enumerate(threads); Collection result = new ArrayList<>(numThreads); for (int i = 0; i < threads.length && threads[i] != null; i++) { result.add(threads[i]); } // note: the 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); } } } private boolean detectKameletOnClassPath() { List deps = project.getCompileDependencies(); for (Dependency dep : deps) { if ("org.apache.camel".equals(dep.getGroupId()) && "camel-kamelet-main".equals(dep.getArtifactId())) { getLog().info("camel-kamelet-main detected on classpath"); return true; } } // maybe there are Kamelet YAML files List resources = project.getResources(); for (Resource res : resources) { File dir = new File(res.getDirectory()); File kamelets = new File(dir, "kamelets"); if (kamelets.exists() && kamelets.isDirectory()) { getLog().info("Kamelets YAML files detected in directory " + kamelets); return true; } } return false; } /** * Set up a classloader for the execution of the main class. * * @return the classloader * @throws MojoExecutionException */ private ClassLoader getClassLoader() throws MojoExecutionException, MojoFailureException { final List classpath = getClasspath(); final List classpathURLs = new ArrayList<>(classpath.size()); try { for (Artifact artifact : classpath) { File file = artifact.getFile(); if (file != null) { classpathURLs.add(file.toURI().toURL()); } } } catch (MalformedURLException e) { throw new MojoExecutionException("Error during setting up classpath", e); } if (logClasspath) { getLog().info("Classpath:"); for (URL url : classpathURLs) { getLog().info(" " + url.getFile()); } } return new URLClassLoader(classpathURLs.toArray(new URL[0])); } /** * @return the list of artifacts corresponding to the classpath to use when launching the application */ protected List getClasspath() throws MojoExecutionException, MojoFailureException { final List classpath = new ArrayList<>(); // project classpath must be first this.addRelevantProjectDependenciesToClasspath(classpath); // and extra plugin classpath this.addExtraPluginDependenciesToClasspath(classpath); // and plugin classpath last this.addRelevantPluginDependenciesToClasspath(classpath); if (!loggingLevel.equals("OFF")) { getLog().info("Using built-in logging level: " + loggingLevel); // and extra plugin classpath this.addConsoleLogDependenciesToClasspath(classpath); // setup logging which can only be done by copying log4j.properties to project output to be in classpath try { String out = LOG4J_TEMPLATE.replace("@@@LOGGING_LEVEL@@@", loggingLevel); IOHelper.writeText(out, new File(project.getBuild().getOutputDirectory() + "/log4j2.properties")); } catch (Exception e) { throw new MojoFailureException("Error configuring loggingLevel", e); } } return classpath; } /** * Add any relevant project dependencies to the classpath. Indirectly takes includePluginDependencies and * ExecutableDependency into consideration. * * @param classpath the list of artifacts representing the classpath to which artifacts should be * added * @throws MojoExecutionException */ private void addRelevantPluginDependenciesToClasspath(List classpath) throws MojoExecutionException { if (hasCommandlineArgs()) { arguments = parseCommandlineArgs(); } for (Artifact classPathElement : this.determineRelevantPluginDependencies()) { // 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"); classpath.add(classPathElement); } } /** * Add any relevant project dependencies to the classpath. Indirectly takes includePluginDependencies and * ExecutableDependency into consideration. * * @param classpath the list of artifacts representing the classpath to which artifacts should be * added * @throws MojoExecutionException */ private void addExtraPluginDependenciesToClasspath(List classpath) throws MojoExecutionException { if (extraPluginDependencyArtifactId == null && extendedPluginDependencyArtifactId == null) { return; } final Set 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"); classpath.add(artifact); // add the transient dependencies of this artifact Set deps = resolveExecutableDependencies(artifact, true); if (deps != null) { for (Artifact dep : deps) { getLog().debug("Adding extra plugin dependency artifact: " + dep.getArtifactId() + " to classpath"); classpath.add(dep); } } } } } /** * Adds the JARs needed for using the built-in logging to console */ private void addConsoleLogDependenciesToClasspath(List classpath) { Set artifacts = new HashSet<>(this.pluginDependencies); for (Artifact artifact : artifacts) { // add these loggers in the beginning so they are first if (artifact.getArtifactId().equals("jansi")) { // jansi for logging in color classpath.add(0, artifact); } else if (artifact.getGroupId().equals("org.apache.logging.log4j")) { // add log4j as this is needed classpath.add(0, artifact); } else if (artifact.getArtifactId().equals("camel-maven-plugin")) { // add ourselves classpath.add(0, artifact); } } } /** * Add any relevant project dependencies to the classpath. Takes includeProjectDependencies into consideration. * * @param classpath the list of artifacts representing the classpath to which artifacts should be * added * @throws MojoExecutionException */ private void addRelevantProjectDependenciesToClasspath(List classpath) throws MojoExecutionException { if (this.includeProjectDependencies) { getLog().debug("Project Dependencies will be included."); File mainClasses = new File(project.getBuild().getOutputDirectory()); getLog().debug("Adding to classpath : " + mainClasses); classpath.add( new ProjectArtifact(project) { @Override public File getFile() { return mainClasses; } }); Set dependencies = CastUtils.cast(project.getArtifacts()); // system scope dependencies are not returned by maven 2.0. See // MEXEC-17 dependencies.addAll(getAllNonTestScopedDependencies()); for (Artifact classPathElement : dependencies) { getLog().debug("Adding project dependency artifact: " + classPathElement.getArtifactId() + " to classpath"); classpath.add(classPathElement); } } else { getLog().debug("Project Dependencies will be excluded."); } } private Collection getAllNonTestScopedDependencies() throws MojoExecutionException { List 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 getAllDependencies() throws MojoExecutionException { List artifacts = new ArrayList<>(); for (Dependency dependency : project.getDependencies()) { 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 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 */ @Override protected Set determineRelevantPluginDependencies() throws MojoExecutionException { Set 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 resolveExecutableDependencies(Artifact executablePomArtifact, boolean ignoreFailures) throws MojoExecutionException { Set executableDependencies = null; try { MavenProject executableProject = this.projectBuilder.buildFromRepository(executablePomArtifact, this.remoteRepositories, this.localRepository); // get all the dependencies for the executable project List dependencies = executableProject.getDependencies(); // make Artifacts of all the dependencies Set 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 ArtifactResolutionRequest request = new ArtifactResolutionRequest() .setArtifact(executablePomArtifact) .setResolveRoot(false) .setArtifactDependencies(dependencyArtifacts) .setManagedVersionMap(Collections.emptyMap()) .setLocalRepository(localRepository) .setRemoteRepositories(remoteRepositories) .setCollectionFilter(new ScopeArtifactFilter(Artifact.SCOPE_RUNTIME)) .setListeners(Collections.emptyList()); ArtifactResolutionResult result = artifactResolver.resolve(request); 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, 0 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); } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy