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

io.reactiverse.vertx.maven.plugin.mojos.AbstractVertxMojo Maven / Gradle / Ivy

Go to download

A plugin to allow package, start, stop, run of Eclipse Vert.x applications

There is a newer version: 2.0.1
Show newest version
/*
 *
 *   Copyright (c) 2016-2018 Red Hat, Inc.
 *
 *   Red Hat 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 io.reactiverse.vertx.maven.plugin.mojos;

import io.reactiverse.vertx.maven.plugin.utils.MavenUtils;
import io.reactiverse.vertx.maven.plugin.utils.WebJars;
import org.apache.commons.lang3.ClassUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.lifecycle.LifecycleExecutor;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.BuildPluginManager;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.context.Context;
import org.codehaus.plexus.context.ContextException;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
import org.codehaus.plexus.util.StringUtils;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.repository.ArtifactRepository;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResolutionException;
import org.eclipse.aether.resolution.ArtifactResult;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * The base Mojo class that will be extended by the other plugin goals
 */
public abstract class AbstractVertxMojo extends AbstractMojo implements Contextualizable {

    private static final String LAUNCHER = "io.vertx.launcher.application.VertxApplication";
    private static final String LEGACY_LAUNCHER = "io.vertx.core.Launcher";

    /* ==== Maven deps ==== */

    /**
     * The Maven project which will define and configure the vertx-maven-plugin
     */
    @Parameter(defaultValue = "${project}", readonly = true)
    protected MavenProject project;

    /**
     * The project build output directory, defaults to ${project.build.directory} which will be target directory
     * of the project
     */
    @Parameter(defaultValue = "${project.build.directory}")
    protected String projectBuildDir;

    /**
     * The maven project classes directory, defaults to target/classes
     */
    @Parameter(defaultValue = "${project.build.outputDirectory}", required = true)
    File classesDirectory;

    /**
     * The maven artifact resolution session, which will be used to resolve Maven artifacts
     */
    @Parameter(defaultValue = "${repositorySystemSession}", readonly = true)
    protected DefaultRepositorySystemSession repositorySystemSession;

    /**
     * The list of remote repositories that will be used to resolve artifacts
     */
    @Parameter(alias = "remoteRepositories", defaultValue = "${project.remoteArtifactRepositories}", readonly = true)
    protected List remoteRepositories;


    /* ==== Maven Components ==== */

    /**
     * The Maven Session.
     */
    @Parameter(defaultValue = "${session}", readonly = true)
    protected MavenSession mavenSession;

    @Component
    protected BuildPluginManager buildPluginManager;

    @Component
    protected RepositorySystem repositorySystem;

    /**
     * The component used to execute the second Maven execution.
     */
    @Component
    protected LifecycleExecutor lifecycleExecutor;

    /**
     * Configure the packaging content.
     */
    @Parameter
    protected Archive archive;

    /* ==== Config ====  */

    /**
     * The verticle that will be the main entry point on to the vertx application, the same property will be used
     * as "Main-Verticle;" attribute value on the MANIFEST.MF
     */
    @Parameter(alias = "verticle", property = "vertx.verticle")
    protected String verticle;

    /**
     * The main launcher class that will be used when launching the Vert.X applications.
     */
    @Parameter(property = "vertx.launcher")
    protected String launcher;

    /**
     * Skip (globally) the processing made by this plugin. All mojos are impacted by this parameter.
     */
    @Parameter(property = "vertx.skip", defaultValue = "false")
    protected boolean skip;

    /**
     * The Plexus container.
     */
    protected PlexusContainer container;

    private List classPathElements;
    private VertxApplicationInfo applicationInfo;

    // Visible for testing
    public MavenProject getProject() {
        return project;
    }

    /**
     * Retrieves the Plexus container.
     *
     * @param context the context
     * @throws ContextException if the container cannot be retrieved.
     */
    @Override
    public void contextualize(Context context) throws ContextException {
        container = (PlexusContainer) context.get(PlexusConstants.PLEXUS_KEY);
    }

    /**
     * List of classpath elements (classes directory and dependencies).
     */
    public List getClassPathElements() throws MojoExecutionException {
        if (classPathElements == null) {
            classPathElements = new ArrayList<>();
            classPathElements.add(this.classesDirectory);
            classPathElements.addAll(extractArtifactPaths(this.project.getArtifacts()));
            getLog().debug("Classpath elements: " + classPathElements);
        }
        return classPathElements;
    }

    public VertxApplicationInfo getVertxApplicationInfo() throws MojoExecutionException {
        if (applicationInfo == null) {
            applicationInfo = computeApplicationInfo();
        }
        return applicationInfo;
    }

    private VertxApplicationInfo computeApplicationInfo() throws MojoExecutionException {
        String vertxVersion = null;
        boolean hasLauncherLegacyCliJar = false, hasLauncherApplicationJar = false;
        for (Artifact artifact : project.getArtifacts()) {
            if ("io.vertx".equals(artifact.getGroupId()))
                if ("vertx-core".equals(artifact.getArtifactId())) {
                    vertxVersion = artifact.getVersion();
                } else if ("vertx-launcher-legacy-cli".equals(artifact.getArtifactId())) {
                    hasLauncherLegacyCliJar = true;
                } else if ("vertx-launcher-application".equals(artifact.getArtifactId())) {
                    hasLauncherApplicationJar = true;
                }
        }
        if (vertxVersion == null) {
            throw new MojoExecutionException("Vert.x core not found, it should be a dependency of the project.");
        }
        if (vertxVersion.startsWith("4.")) {
            return computeVertx4ApplicationInfo();
        }
        if (vertxVersion.startsWith("5.")) {
            return computeVertx5ApplicationInfo(hasLauncherLegacyCliJar, hasLauncherApplicationJar);
        }
        throw new MojoExecutionException("Unsupported Vert.x version: " + vertxVersion);
    }

    private VertxApplicationInfo computeVertx4ApplicationInfo() throws MojoExecutionException {
        String mainVerticle = StringUtils.isBlank(verticle) ? null : verticle.trim();
        String mainClass = StringUtils.isBlank(launcher) ? LEGACY_LAUNCHER : launcher.trim();
        boolean isVertxLauncher = isMainClassInstanceOfLauncher(mainClass, LEGACY_LAUNCHER, getClassPathElements());
        if (isVertxLauncher) {
            if (mainVerticle == null) {
                throw new MojoExecutionException("Invalid configuration, the element `verticle` (`vertx.verticle` property) is required.");
            }
        }
        return new VertxApplicationInfo(mainClass, mainVerticle, isVertxLauncher, isVertxLauncher);
    }

    private VertxApplicationInfo computeVertx5ApplicationInfo(boolean hasLauncherLegacyCliJar, boolean hasLauncherApplicationJar) throws MojoExecutionException {
        String mainVerticle = StringUtils.isBlank(verticle) ? null : verticle.trim();
        String mainClass;
        boolean isVertxLauncher, isLegacyVertxLauncher;
        if (StringUtils.isBlank(launcher)) {
            if (hasLauncherLegacyCliJar && hasLauncherApplicationJar) {
                throw new MojoExecutionException("Invalid configuration, the element `launcher` (`vertx.launcher` property) is required (both `io.vertx:vertx-launcher-application` and `io.vertx:vertx-launcher-legacy-cli` dependencies are present).");
            }
            if (hasLauncherApplicationJar) {
                mainClass = LAUNCHER;
                isVertxLauncher = true;
                isLegacyVertxLauncher = false;
            } else if (hasLauncherLegacyCliJar) {
                mainClass = LEGACY_LAUNCHER;
                isVertxLauncher = true;
                isLegacyVertxLauncher = true;
            } else {
                throw new MojoExecutionException("Invalid configuration, you must add the `io.vertx:vertx-launcher-application` or `io.vertx:vertx-launcher-legacy-cli` dependency to the project.");
            }
        } else {
            mainClass = launcher.trim();
            if (isMainClassInstanceOfLauncher(mainClass, LAUNCHER, getClassPathElements())) {
                isVertxLauncher = true;
                isLegacyVertxLauncher = false;
            } else if (isMainClassInstanceOfLauncher(mainClass, LEGACY_LAUNCHER, getClassPathElements())) {
                isVertxLauncher = true;
                isLegacyVertxLauncher = true;
            } else {
                isVertxLauncher = false;
                isLegacyVertxLauncher = false;
            }
        }
        if (isVertxLauncher) {
            if (mainVerticle == null) {
                throw new MojoExecutionException("Invalid configuration, the element `verticle` (`vertx.verticle` property) is required.");
            }
        }
        return new VertxApplicationInfo(mainClass, mainVerticle, isVertxLauncher, isLegacyVertxLauncher);
    }

    private static boolean isMainClassInstanceOfLauncher(String mainClassName, String launcherClassName, List classPathElements) throws MojoExecutionException {
        if (launcherClassName.equals(mainClassName)) {
            return true;
        }
        URL[] classPathUrls = new URL[classPathElements.size()];
        for (int i = 0; i < classPathElements.size(); i++) {
            try {
                classPathUrls[i] = classPathElements.get(i).toURI().toURL();
            } catch (MalformedURLException e) {
                throw new MojoExecutionException("Invalid classpath element: " + classPathElements.get(i), e);
            }
        }
        try (URLClassLoader classLoader = new URLClassLoader(classPathUrls)) {
            Class mainClass = classLoader.loadClass(mainClassName);
            for (Class superClass : ClassUtils.getAllSuperclasses(mainClass)) {
                if (launcherClassName.equals(superClass.getName())) {
                    return true;
                }
            }
            return false;
        } catch (IOException e) {
            throw new MojoExecutionException("Failure while inspecting main class hierarchy", e);
        } catch (ClassNotFoundException e) {
            throw new MojoExecutionException("Could not find main class: " + mainClassName);
        }
    }

    private Set extractArtifactPaths(Set artifacts) throws MojoExecutionException {
        try {
            Set files = new LinkedHashSet<>();
            for (Artifact e : artifacts) {
                if (e.getScope().equals("compile") || e.getScope().equals("runtime")) {
                    if (e.getType().equals("jar")) {
                        if (!WebJars.isWebJar(e.getFile())) {
                            File resolvedArtifact = resolveArtifact(MavenUtils.asMavenCoordinates(e));
                            if (resolvedArtifact != null) {
                                files.add(resolvedArtifact.getAbsoluteFile());
                            }
                        }
                    }
                }
            }
            return files;
        } catch (IOException | ArtifactResolutionException ex) {
            throw new MojoExecutionException("Unable to extract artifact paths", ex);
        }
    }

    private File resolveArtifact(String artifact) throws ArtifactResolutionException {
        ArtifactRequest artifactRequest = new ArtifactRequest();
        artifactRequest.setArtifact(new DefaultArtifact(artifact));
        ArtifactResult artifactResult = repositorySystem.resolveArtifact(repositorySystemSession, artifactRequest);
        if (artifactResult.isResolved()) {
            getLog().debug("Resolved: " + artifactResult.getArtifact().getArtifactId());
            return artifactResult.getArtifact().getFile();
        }
        getLog().warn("Unable to resolve artifact: " + artifact);
        return null;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy