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

net.unit8.waitt.mojo.AbstractRunMojo Maven / Gradle / Ivy

package net.unit8.waitt.mojo;

import net.unit8.waitt.api.*;
import net.unit8.waitt.api.configuration.Feature;
import net.unit8.waitt.api.configuration.Server;
import net.unit8.waitt.api.configuration.WebappConfiguration;
import net.unit8.waitt.mojo.component.ArtifactResolver;
import net.unit8.waitt.mojo.component.ServerProvider;
import net.unit8.waitt.mojo.configuration.ExtraWebapp;
import net.unit8.waitt.mojo.configuration.ServerSpec;
import net.unit8.waitt.mojo.log.WaittLogHandler;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuilder;
import org.apache.maven.project.ProjectBuildingRequest;
import org.apache.maven.project.ProjectBuildingResult;
import org.apache.maven.repository.RepositorySystem;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.util.DirectoryScanner;
import org.fusesource.jansi.AnsiConsole;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

import static java.util.logging.Level.*;

/**
 * @author kawasima
 */
@SuppressWarnings("unchecked")
public abstract class AbstractRunMojo extends AbstractMojo {
    private static final String[] WELLKNOWN_DOCROOT = {"src/main/webapp", "WebContent"};

    @Parameter
    private int port;

    @Parameter(defaultValue = "8080")
    private int startPort;

    @Parameter(defaultValue = "9000")
    private int endPort;

    @Parameter(defaultValue = "")
    private String contextPath;

    @Parameter(defaultValue = "")
    private String path;

    @Parameter
    private List servers;

    @Parameter
    private List features;

    @Parameter
    protected File docBase;

    @Component
    protected ProjectBuilder projectBuilder;

    @Parameter(defaultValue = "${project}", required = true, readonly = true)
    protected MavenProject project;

    @Parameter(defaultValue = "${session}", required = true, readonly = true)
    protected MavenSession session;

    @Component
    protected RepositorySystem repositorySystem;

    @Component
    protected ArtifactResolver artifactResolver;

    @Component
    protected ServerProvider serverProvider;


    private final List serverMonitors = new ArrayList();
    private final List logListeners = new ArrayList();
    private final List extraWebapps = new ArrayList();
    private final List webappDecorators = new ArrayList();


    /**
     * Start embedded server.
     *
     * @throws MojoExecutionException
     * @throws MojoFailureException
     */
    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        AnsiConsole.systemInstall();
        artifactResolver.setProject(project);
        artifactResolver.setSession(session);
        initLogger();

        if (docBase == null)
            docBase = scanDocBase(new File("."));
        WebappConfiguration webappConfig = new WebappConfiguration();
        webappConfig.setApplicationName(project.getName());
        webappConfig.setBaseDirectory(docBase);
        webappConfig.setPackages(PackageScanner.scan(new File(project.getBuild().getSourceDirectory())));
        webappConfig.setSourceDirectory(new File(project.getBuild().getSourceDirectory()));

        ClassRealm waittRealm = (ClassRealm) Thread.currentThread().getContextClassLoader();

        ServerSpec serverSpec = serverProvider.selectServer(servers, waittRealm, session.getSettings().getInteractiveMode());
        EmbeddedServer embeddedServer = serverSpec.getEmbeddedServer();

        if (port == 0)
            scanPort();
        embeddedServer.setPort(port);

        if (contextPath == null || contextPath.equals("/"))
            contextPath = "";
        embeddedServer.setBaseDir(".");

        loadFeature(waittRealm, webappConfig);

        try {
            embeddedServer.start();
            ClassRealm webappRealm = new ClassRealm(serverSpec.getClassRealm().getWorld(), "Application", ClassLoader.getSystemClassLoader());
            webappRealm.setParentRealm(serverSpec.getClassRealm());
            Set classpathUrls = resolveClasspaths();
            for (URL url : classpathUrls) {
                webappRealm.addURL(url);
            }

            for (ServerMonitor serverMonitor : serverMonitors) {
                serverMonitor.init(embeddedServer);
            }
            embeddedServer.setWebappDecorators(webappDecorators);
            embeddedServer.setMainContext(contextPath, docBase.getAbsolutePath(), webappRealm);
            for (ExtraWebapp extraWebapp : extraWebapps) {
                extraWebapp.getRealm().setParentRealm(serverSpec.getClassRealm());
                embeddedServer.addContext("/_" + extraWebapp.getName(), extraWebapp.getWarPath(), extraWebapp.getRealm());
            }

            for (ServerMonitor serverMonitor : serverMonitors) {
                serverMonitor.start(embeddedServer);
            }
            path = path == null ? "" : path;

            afterStart();
            embeddedServer.await();
        } catch (Exception e) {
            throw new MojoExecutionException("Fail to start server", e);
        } finally {
            embeddedServer.stop();
        }
    }

    abstract protected void afterStart() throws IOException;

    /**
     * Read artifacts.
     *
     * @param subDirectory a sub directory
     * @param artifacts artifacts
     * @param classpathFiles classpaths
     * @throws MojoExecutionException
     */
    private void readArtifacts(String subDirectory, List artifacts, List classpathFiles)
            throws MojoExecutionException {
        File modulePom = (subDirectory == null || subDirectory.isEmpty()) ? new File("pom.xml") : new File(subDirectory, "pom.xml");

        try {
            ProjectBuildingRequest request = session.getProjectBuildingRequest()
                    .setProcessPlugins(false)
                    .setResolveDependencies(true);

            ProjectBuildingResult result = projectBuilder.build(modulePom, request);
            MavenProject subProject = result.getProject();

            if ("war".equals(subProject.getPackaging())) {
                docBase = (subDirectory == null || subDirectory.isEmpty()) ?
                        scanDocBase(new File(".")) :
                        scanDocBase(new File(subDirectory));
            }
            for (Artifact dependency : subProject.getArtifacts()) {
                String scope = dependency.getScope();
                if (Artifact.SCOPE_COMPILE.equals(scope)
                        || Artifact.SCOPE_RUNTIME.equals(scope)
                        || Artifact.SCOPE_SYSTEM.equals(scope)) {
                    artifacts.add(dependency);
                }
            }
            classpathFiles.add(new File(subProject.getBuild().getOutputDirectory()));
        } catch (Exception e) {
            throw new MojoExecutionException("module(" + subDirectory + ") build failure", e);
        }
    }

    /**
     * Load features.
     *
     * @param waittRealm The realm of this plugin
     * @param config     The configuration of web application
     */
    private void loadFeature(ClassRealm waittRealm, WebappConfiguration config) {
        if (features == null) return;
        for (Feature feature : features) {
            String type = feature.getType();
            if (type == null) {
                type = "jar";
            }
            Artifact artifact = repositorySystem.createArtifact(feature.getGroupId(), feature.getArtifactId(), feature.getVersion(), type);
            ClassRealm realm = artifactResolver.resolve(artifact, waittRealm);
            config.getFeatures().add(feature);

            if ("war".equals(artifact.getType())) {
                String name = artifact.getArtifactId();
                if (name.startsWith("waitt-")) {
                    name = name.substring("waitt-".length());
                }
                extraWebapps.add(new ExtraWebapp(name, artifact.getFile().getAbsolutePath(), realm));
            } else {
                ServiceLoader serverMonitorLoader = ServiceLoader.load(ServerMonitor.class, realm);
                for (ServerMonitor serverMonitor : serverMonitorLoader) {
                    if (serverMonitor instanceof ConfigurableFeature) {
                        ((ConfigurableFeature) serverMonitor).config(config);
                    }
                    serverMonitors.add(serverMonitor);
                }

                ServiceLoader logListenerLoader = ServiceLoader.load(LogListener.class, realm);
                for (LogListener logListener : logListenerLoader) {
                    if (logListener instanceof ConfigurableFeature) {
                        ((ConfigurableFeature) logListener).config(config);
                    }
                    logListeners.add(logListener);
                }
                ServiceLoader webappDecoratorLoader = ServiceLoader.load(WebappDecorator.class, realm);
                for (WebappDecorator webappDecorator : webappDecoratorLoader) {
                    if (webappDecorator instanceof ConfigurableFeature) {
                        ((ConfigurableFeature) webappDecorator).config(config);
                    }
                    webappDecorators.add(webappDecorator);
                }
            }
        }
    }

    /**
     * Initialize logger.
     */
    private void initLogger() {
        Logger logger = Logger.getLogger("");
        logger.setLevel(ALL);
        for (Handler handler : logger.getHandlers()) {
            logger.removeHandler(handler);
        }

        logger.addHandler(new WaittLogHandler(getLog()));
        logger.addHandler(new Handler() {
            @Override
            public void publish(LogRecord record) {
                if (record.getLoggerName() != null && record.getLoggerName().startsWith("sun.awt."))
                    return;
                Level lv = record.getLevel();
                for (LogListener logListener : logListeners) {
                    if (Arrays.asList(ALL, CONFIG, FINE, FINER, FINEST).contains(lv)) {
                        logListener.debug(record.getMessage(), record.getThrown());
                    } else if (lv.equals(INFO)) {
                        logListener.info(record.getMessage(), record.getThrown());
                    } else if (lv.equals(WARNING)) {
                        logListener.warn(record.getMessage(), record.getThrown());
                    } else if (lv.equals(SEVERE)) {
                        logListener.error(record.getMessage(), record.getThrown());
                    }
                }
            }

            @Override
            public void flush() {

            }

            @Override
            public void close() throws SecurityException {
            }
        });
    }

    /**
     * Resolve dependencies as classpath.
     *
     * @return find classpath urls
     * @throws MojoExecutionException
     */
    private Set resolveClasspaths() throws MojoExecutionException {
        List artifacts = new ArrayList();
        List classpathFiles = new ArrayList();

        if (project.getModel().getModules().isEmpty()) {
            readArtifacts("", artifacts, classpathFiles);
        } else {
            for (String module : project.getModel().getModules()) {
                readArtifacts(module, artifacts, classpathFiles);
            }
        }
        Set classpathUrls = new HashSet();
        Set uniqueArtifacts = new HashSet();

        try {
            for (File classpathFile : classpathFiles) {
                URL url = classpathFile.toURI().toURL();
                classpathUrls.add(url);
            }

            for (URL url : ((URLClassLoader) Thread.currentThread().getContextClassLoader()).getURLs()) {
                if (url.toString().contains("/org/ow2/asm/")
                        || url.toString().contains("/waitt-maven-plugin/")
                        || url.toString().contains("/net/sourceforge/cobertura/")) {
                    classpathUrls.add(url);
                }
            }
            for (Artifact artifact : artifacts) {
                if ("provided".equals(artifact.getScope()))
                    continue;

                String versionlessKey = ArtifactUtils.versionlessKey(artifact);
                if (!uniqueArtifacts.contains(versionlessKey)) {
                    classpathUrls.add(artifact.getFile().toURI().toURL());
                    uniqueArtifacts.add(versionlessKey);
                }
            }
            return classpathUrls;
        } catch (MalformedURLException e) {
            throw new MojoExecutionException("Error during setting up classpath", e);
        }
    }

    /**
     * Scan a base directory.
     *
     * @param baseDir
     * @return document
     * @throws MojoExecutionException
     */
    protected File scanDocBase(File baseDir) throws MojoExecutionException {
        for (String dirStr : WELLKNOWN_DOCROOT) {
            File docBase = new File(baseDir, dirStr);
            if (docBase.isDirectory()) {
                return docBase;
            }
        }

        DirectoryScanner scanner = new DirectoryScanner();
        scanner.setBasedir(baseDir);
        scanner.setIncludes(new String[]{"**/web.xml"});
        scanner.addDefaultExcludes();
        scanner.scan();
        for (String path : scanner.getIncludedFiles()) {
            File webxml = new File(baseDir, path);
            if ("WEB-INF".equals(webxml.getParentFile().getName())) {
                getLog().info("Found webapp root = " + webxml);
                return webxml.getParentFile().getParentFile();
            }
        }

        File dummy = new File(baseDir, "target/dummy_webapp");
        if (!dummy.exists() && !dummy.mkdirs())
            throw new MojoExecutionException("Can't create webapp directory");
        return dummy;
    }

    /**
     * Scan an available port.
     */
    protected void scanPort() {
        for (int p = startPort; p <= endPort; p++) {
            try {
                Socket sock = new Socket("localhost", p);
                sock.close();
            } catch (IOException e) {
                port = p;
                return;
            }
        }
        throw new RuntimeException("Can't find available port from " + startPort + " to " + endPort);
    }

    public int getPort() {
        return port;
    }

    public String getContextPath() {
        return contextPath;
    }

    public String getPath() {
        return path;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy