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

org.kantega.reststop.maven.AbstractReststopRunMojo Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2018 Kantega AS
 *
 * Licensed 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.kantega.reststop.maven;

import org.apache.commons.io.FileUtils;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.DependencyManagement;
import org.apache.maven.model.Model;
import org.apache.maven.model.building.*;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.graph.Exclusion;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.DependencyResolutionException;
import org.eclipse.jetty.maven.plugin.JettyWebAppContext;
import org.eclipse.jetty.maven.plugin.ServerSupport;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.websocket.jsr356.server.ServerContainer;
import org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter;
import org.kantega.reststop.classloaderutils.BuildSystem;
import org.kantega.reststop.classloaderutils.PluginClassLoader;
import org.kantega.reststop.classloaderutils.PluginInfo;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.List;

import static java.util.Arrays.asList;

/**
 *
 */
public abstract class AbstractReststopRunMojo extends AbstractReststopMojo {

    @Parameter(required = true)
    protected String applicationName;

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

    @Parameter(defaultValue =  "${basedir}/src/config")
    protected File configDir;


    @Parameter(defaultValue = "${project.build.directory}/reststop/temp")
    private File tempDirectory;


    @Parameter(defaultValue = "${project.build.testOutputDirectory}/reststopPort.txt")
    protected File reststopPortFile;

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

    @Parameter(defaultValue = "${basedir}/src/main/webapp")
    protected File webAppSourceDirectory;

    @Parameter(defaultValue = "${project.build.outputDirectory}")
    protected File classesDirectory;

    @Component
    private ModelBuilder modelBuilder;

    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {

        File war;
        if("war".equals(mavenProject.getPackaging())) {
            war = webAppSourceDirectory;
        } else {
            war = resolveArtifactFile(warCoords);
        }


        startJetty(war);


    }

    private void startJetty(File war) throws MojoExecutionException {
        try {


            System.setProperty("reststopPluginDir", mavenProject.getBasedir().getAbsolutePath());

            int port = this.port;
            if(port != 0) {
                port = nextAvailablePort(port);
            }


            Server server = new Server(port);
            ServerSupport.configureDefaultConfigurationClasses(server);

            mavenProject.setContextValue("jettyServer", server);

            JettyWebAppContext context = new JettyWebAppContext();

            List serverClasses = asList(
                    "org.eclipse.aether.",
                    "com.sun.codemodel.",
                    "com.jcraft.",
                    "org.apache.commons.",
                    "org.apache.ant.",
                    "org.apache.http.",
                    "org.apache.maven.",
                    "org.codehaus.plexus.",
                    "org.sonatype.plexus.",
                    "org.eclipse.jgit.",
                    "org.twdata.",
                    "com.googlecode.");
            serverClasses.forEach(context::addServerClass);
            getLog().info("Added system classes: " + serverClasses);

            context.setWar(war.getAbsolutePath());
            context.setContextPath(contextPath);
            context.getServletContext().setAttribute("pluginsXml", createPluginXmlDocument(false));
            context.setInitParameter("pluginConfigurationDirectory", configDir.getAbsolutePath());
            context.setInitParameter("applicationName", applicationName);
            context.setInitParameter("org.eclipse.jetty.websocket.jsr356", "false");

            customizeContext(context);


            File jettyTmpDir = new File(tempDirectory, war.getName());
            jettyTmpDir.mkdirs();
            context.setTempDirectory(jettyTmpDir);
            boolean deleteTempDirectory= jettyTmpDir.exists() && war.lastModified() > jettyTmpDir.lastModified();
            context.setPersistTempDirectory(!deleteTempDirectory);
            context.setThrowUnavailableOnStartupException(true);

            HandlerCollection handlers = new HandlerCollection(true);

            handlers.addHandler(new ShutdownHandler(context, server, getLog()));
            server.setHandler(handlers);

            server.start();


            ServerConnector connector = (ServerConnector) server.getConnectors()[0];
            int actualPort = connector.getLocalPort();

            mavenProject.getProperties().setProperty("reststopPort", Integer.toString(actualPort));
            String reststopPort = Integer.toString(actualPort);
            System.setProperty("reststopPort", reststopPort);
            FileUtils.writeStringToFile(reststopPortFile, reststopPort);

            handlers.addHandler(context);

            configureWebSocket(context);

            context.start();

            afterServerStart(server, actualPort);

        } catch (Exception e) {
            throw new MojoExecutionException("Failed starting Jetty ", e);
        }
    }

    public static ServerContainer configureWebSocket(ServletContextHandler context) throws ServletException
    {

       // Create Filter
        WebSocketUpgradeFilter filter = WebSocketUpgradeFilter.configureContext(context);

        // Create the Jetty ServerContainer implementation
        ServerContainer jettyContainer = new RedeployableServerContainer(filter.getConfiguration(),context.getServer().getThreadPool());
        context.addBean(jettyContainer, true);

        // Store a reference to the ServerContainer per javax.websocket spec 1.0 final section 6.4 Programmatic Server Deployment
        context.setAttribute(javax.websocket.server.ServerContainer.class.getName(),jettyContainer);

        return jettyContainer;
    }



    private int nextAvailablePort(int first) {
        int port = first;
        for(;;) {
            try {
                ServerSocket socket = new ServerSocket(port);
                socket.close();
                return port;
            } catch (IOException e) {
                port++;
            }
        }
    }

    private class ShutdownHandler extends AbstractHandler {
        private final JettyWebAppContext context;
        private final Server server;
        private final Log log;

        public ShutdownHandler(JettyWebAppContext context, Server server, Log log) {
            this.context = context;
            this.server = server;
            this.log = log;
        }

        @Override
        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {

            if("/shutdown".equals(target) && ! (server.isStopping() || server.isStopped())) {
                try {
                    log.info("Shutting down Jetty server");
                    new Thread() {
                        @Override
                        public void run() {
                            try {
                                context.stop();
                                server.stop();
                            } catch (Throwable e) {
                                org.eclipse.jetty.util.log.Log.getLogger(getClass()).ignore(e);
                            }
                        }
                    }.start();
                } catch (Exception e) {
                    throw new ServletException(e);
                }
                baseRequest.setHandled(true);
            }
        }
    }

    protected void registerBuildSystem() {
        BuildSystem.instance = new BuildSystem() {
            @Override
            public boolean needsRefresh(PluginClassLoader cl) {
                if(cl.getPluginInfo().getSourceDirectory() == null) {
                    return false;
                }

                File pomXml = new File(cl.getPluginInfo().getSourceDirectory(), "pom.xml");

                return cl.getCreationTime() < pomXml.lastModified();
            }

            @Override
            public PluginInfo refresh(PluginInfo pluginInfo) {


                Resolver resolver = new Resolver(repoSystem, repoSession, remoteRepos, getLog());

                try {
                    File pomFile = new File(pluginInfo.getSourceDirectory(), "pom.xml");

                    PluginInfo info = new PluginInfo();
                    info.setGroupId(pluginInfo.getGroupId());
                    info.setArtifactId(pluginInfo.getArtifactId());
                    info.setVersion(pluginInfo.getVersion());
                    info.setSourceDirectory(pluginInfo.getSourceDirectory());

                    Artifact pluginArtifact = resolver.resolveArtifact(info.getGroupId() +":" + info.getArtifactId() +":" + info.getVersion());
                    info.setFile(pluginArtifact.getFile());
                    resolver.resolveClasspaths(info, createCollectRequest(buildModel(pomFile)));
                    return info;
                } catch ( MojoFailureException | DependencyResolutionException | MojoExecutionException e) {
                    throw new RuntimeException(e);
                }
            }

            private CollectRequest createCollectRequest(Model model) {
                CollectRequest collectRequest = new CollectRequest();

                for (RemoteRepository repo : remoteRepos) {
                    collectRequest.addRepository(repo);
                }
                for (org.apache.maven.model.Dependency dependency : model.getDependencies()) {
                    collectRequest.addDependency(mavenDep2AetherDep(dependency));
                }
                DependencyManagement depm = model.getDependencyManagement();
                if(depm != null) {
                    for (org.apache.maven.model.Dependency dependency : depm.getDependencies()) {
                        collectRequest = collectRequest.addManagedDependency(mavenDep2AetherDep(dependency));
                    }
                }

                return collectRequest;
            }

            private org.eclipse.aether.graph.Dependency mavenDep2AetherDep(Dependency dependency) {
                Artifact artifact = new DefaultArtifact(dependency.getGroupId(), dependency.getArtifactId(), dependency.getClassifier(), dependency.getType(), dependency.getVersion());
                org.eclipse.aether.graph.Dependency aetherDep = new org.eclipse.aether.graph.Dependency(artifact, dependency.getScope());
                if(dependency.getExclusions() != null) {
                    List exclusions = new ArrayList<>();
                    dependency.getExclusions().forEach( e -> {
                        exclusions.add(new Exclusion(e.getGroupId(), e.getArtifactId(), "*", "*"));
                    });
                    aetherDep = aetherDep.setExclusions(exclusions);
                }
                return aetherDep;
            }

            private Model buildModel(File pomFile) {
                try {
                    DefaultModelBuildingRequest request = new DefaultModelBuildingRequest();
                    request.setModelSource(new FileModelSource(pomFile));
                    ModelBuildingResult build = modelBuilder.build(request);
                    return build.getEffectiveModel();
                } catch (ModelBuildingException e) {
                    throw  new RuntimeException(e);
                }
            }
        };
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy