<commit_before>/*
 * Copyright 2013 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.maven.plugin.AbstractMojo;
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.apache.maven.project.MavenProject;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.*;
import org.eclipse.aether.util.artifact.JavaScopes;
import org.eclipse.aether.util.filter.DependencyFilterUtils;
import org.eclipse.jetty.maven.plugin.JettyWebAppContext;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.kantega.reststop.classloaderutils.PluginInfo;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.*;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;

import static java.util.Arrays.asList;

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


    @Component
    protected RepositorySystem repoSystem;

    @Parameter(defaultValue ="${repositorySystemSession}" ,readonly = true)
    protected RepositorySystemSession repoSession;

    @Parameter(defaultValue = "${project.remoteProjectRepositories}")
    protected List<RemoteRepository> remoteRepos;

    @Parameter (defaultValue = "org.kantega.reststop:reststop-webapp:war:${plugin.version}")
    protected String warCoords;

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

    @Parameter(defaultValue = "${project.build.directory}/${project.build.finalName}.${project.packaging}")
    private File pluginJar;

    @Parameter(defaultValue = "${project}")
    protected MavenProject mavenProject;

    @Parameter
    protected List<Plugin> plugins;


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

        File war = resolveArtifactFile(warCoords);

        startJetty(war);


    }

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

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

            int port = nextAvailablePort(8080);

            mavenProject.getProperties().setProperty("reststopPort", Integer.toString(port));
            System.setProperty("reststopPort", Integer.toString(port));

            Server server = new Server(port);

            mavenProject.setContextValue("jettyServer", server);

            JettyWebAppContext context = new JettyWebAppContext();

            context.setWar(war.getAbsolutePath());
            context.getServletContext().setAttribute("pluginsXml", createPluginXmlDocument(false));

            customizeContext(context);

            tempDirectory.mkdirs();
            context.setTempDirectory(tempDirectory);
            context.setThrowUnavailableOnStartupException(true);

            HandlerCollection handlers = new HandlerCollection();

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

            server.start();

            afterServerStart(server, port);

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

    protected void customizeContext(JettyWebAppContext context) {

    }

    protected void afterServerStart(Server server, int port) throws MojoFailureException {

    }

    protected Document createPluginXmlDocument(boolean prod) throws MojoFailureException, MojoExecutionException {

        List<PluginInfo> pluginInfos = getPluginInfos();

        return buildPluginsDocument(prod, pluginInfos);


    }

    private Document buildPluginsDocument(boolean prod, List<PluginInfo> pluginInfos) throws MojoExecutionException {

        try {
            Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();

            Element pluginsElem = doc.createElement("plugins");

            doc.appendChild(pluginsElem);

            for (PluginInfo plugin : pluginInfos) {
                Element pluginElem = doc.createElement("plugin");
                pluginsElem.appendChild(pluginElem);

                List<PluginInfo> parents = plugin.getParents(pluginInfos);
                for (PluginInfo parent : parents) {
                    Element dependsElem = doc.createElement("depends-on");
                    pluginElem.appendChild(dependsElem);
                    dependsElem.setAttribute("groupId", parent.getGroupId());
                    dependsElem.setAttribute("artifactId", parent.getArtifactId());
                    dependsElem.setAttribute("version", parent.getVersion());

                }
                if(!plugin.getConfig().isEmpty()) {
                    Element configElem = doc.createElement("config");

                    for (String name : plugin.getConfig().stringPropertyNames()) {
                        Element propElem = doc.createElement("prop");
                        propElem.setAttribute("name", name);
                        propElem.setAttribute("value", plugin.getConfig().getProperty(name));
                        configElem.appendChild(propElem);
                    }

                    pluginElem.appendChild(configElem);
                }

                pluginElem.setAttribute("groupId", plugin.getGroupId());
                pluginElem.setAttribute("artifactId", plugin.getArtifactId());
                pluginElem.setAttribute("version", plugin.getVersion());
                if(!prod) {

                    if(plugin.getSourceDirectory() != null) {
                        pluginElem.setAttribute("sourceDirectory", plugin.getSourceDirectory().getAbsolutePath());
                    }


                    pluginElem.setAttribute("pluginFile", plugin.getFile().getAbsolutePath());
                    pluginElem.setAttribute("directDeploy", Boolean.toString(plugin.isDirectDeploy()));
                }


                List<String> scopes = prod ? Collections.singletonList(JavaScopes.RUNTIME) : asList(JavaScopes.TEST, JavaScopes.RUNTIME, JavaScopes.COMPILE);

                for(String scope : scopes) {

                    Element scopeElem = doc.createElement(scope);

                    pluginElem.appendChild(scopeElem);

                    for (org.kantega.reststop.classloaderutils.Artifact artifact : plugin.getClassPath(scope)) {
                        Element artifactElement = doc.createElement("artifact");
                        artifactElement.setAttribute("groupId", artifact.getGroupId());
                        artifactElement.setAttribute("artifactId", artifact.getArtifactId());
                        artifactElement.setAttribute("version", artifact.getVersion());

                        if(!prod) {
                            artifactElement.setAttribute("file", artifact.getFile().getAbsolutePath());
                        }

                        scopeElem.appendChild(artifactElement);
                    }


                }
            }
            return doc;
        } catch (ParserConfigurationException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }
    }

    private List<PluginInfo> getPluginInfos() throws MojoFailureException, MojoExecutionException {
        List<PluginInfo> pluginInfos = new ArrayList<>();

        for (Plugin plugin : getPlugins()) {
            PluginInfo info = plugin.asPluginInfo();
            pluginInfos.add(info);

            Artifact pluginArtifact = resolveArtifact(plugin.getCoords());

            info.setFile(pluginArtifact.getFile());

            for(String scope : asList(JavaScopes.TEST, JavaScopes.RUNTIME, JavaScopes.COMPILE)) {

                try {

                    ArtifactDescriptorResult descriptorResult = repoSystem.readArtifactDescriptor(repoSession, new ArtifactDescriptorRequest(pluginArtifact, remoteRepos, null));

                    CollectRequest collectRequest = new CollectRequest();

                    for (RemoteRepository repo : remoteRepos) {
                        collectRequest.addRepository(repo);
                    }
                    for (Dependency dependency : descriptorResult.getDependencies()) {
                        collectRequest.addDependency(dependency);
                    }

                    DependencyRequest dependencyRequest = new DependencyRequest(collectRequest, DependencyFilterUtils.classpathFilter(scope));

                    DependencyResult dependencyResult = repoSystem.resolveDependencies(repoSession, dependencyRequest);

                    if(!dependencyResult.getCollectExceptions().isEmpty()) {
                        throw new MojoFailureException("Failed resolving plugin dependencies", dependencyResult.getCollectExceptions().get(0));
                    }

                    for(ArtifactResult result : dependencyResult.getArtifactResults()) {
                        Artifact artifact = result.getArtifact();
                        org.kantega.reststop.classloaderutils.Artifact pa  = new org.kantega.reststop.classloaderutils.Artifact();
                        info.getClassPath(scope).add(pa);

                        pa.setGroupId(artifact.getGroupId());
                        pa.setArtifactId(artifact.getArtifactId());
                        pa.setVersion(artifact.getVersion());

                        pa.setFile(artifact.getFile());
                    }

                } catch (DependencyResolutionException | ArtifactDescriptorException e) {
                    throw new MojoFailureException("Failed resolving plugin dependencies", e);
                }


            }
        }

        validateTransitivePluginsMissing(pluginInfos);
        return pluginInfos;
    }

    private void validateTransitivePluginsMissing(List<PluginInfo> pluginInfos) throws MojoExecutionException, MojoFailureException {

        for (PluginInfo pluginInfo : pluginInfos) {

            Map<String, org.kantega.reststop.classloaderutils.Artifact> missing = new TreeMap<>();

            for (org.kantega.reststop.classloaderutils.Artifact dep : pluginInfo.getClassPath("compile")) {


                try {
                    JarFile jar = new JarFile(dep.getFile());
                    ZipEntry entry = jar.getEntry("META-INF/services/ReststopPlugin/");
                    boolean isPlugin = entry != null;
                    jar.close();

                    if(isPlugin && !isDeclaredPlugin(dep, pluginInfos)) {
                        missing.put(dep.getGroupIdAndArtifactId(), dep);
                        getLog().error("Plugin " + pluginInfo.getPluginId() +" depends on plugin " + dep.getPluginId() +" which must be declared as a <plugin>!");
                        String decl = String.format("\t<plugin>\n\t\t<groupId>%s</groupId>\n\t\t<artifactId>%s</artifactId>\n\t\t<version>%s</version>\n\t</plugin>", dep.getGroupId(), dep.getArtifactId(), dep.getVersion());
                        getLog().error("Please add the following to your <plugins> section:\n" + decl);
                    }


                } catch (IOException e) {
                    throw new MojoExecutionException(e.getMessage(), e);
                }

            }
            if(!missing.isEmpty()) {
                throw new MojoFailureException("Plugin " +pluginInfo.getPluginId() +" has a Maven <dependency> on "
                        + "one or more plugin artifacts which should be directly declared as a <plugin>: " + missing.values());
            }
        }

    }

    private boolean isDeclaredPlugin(org.kantega.reststop.classloaderutils.Artifact dep, List<PluginInfo> pluginInfos) {

        for(PluginInfo declared : pluginInfos) {
            if(declared.getGroupIdAndArtifactId().equals(dep.getGroupIdAndArtifactId())) {
                return true;
            }
        }
        return false;
    }

    protected List<Plugin> getPlugins() {
        List<Plugin> plugins = new ArrayList<>();

        if(this.plugins != null) {
            plugins.addAll(this.plugins);
        }


        return plugins;
    }


    private String getClasspath(List<org.apache.maven.artifact.Artifact> artifacts) {

        StringBuilder classpath = new StringBuilder();

        int c = 0;


        for (org.apache.maven.artifact.Artifact a : artifacts) {

            if (c > 0) {
                classpath.append(File.pathSeparatorChar);
            }
            c++;
            classpath.append(a.getFile().getAbsolutePath());


        }

        return classpath.toString();
    }

    private int nextAvailablePort(int first) {
        int port = first;
        for(;;) {
            try {
                ServerSocket socket = new ServerSocket(port);
                socket.close();
                return port;
            } catch (IOException e) {
                port++;
            }
        }
    }
    private File resolveArtifactFile(String coords) throws MojoFailureException, MojoExecutionException {
        return resolveArtifact(coords).getFile();
    }


    private Artifact resolveArtifact(String coords) throws MojoFailureException, MojoExecutionException {
        Artifact artifact;
        try
        {
            artifact = new DefaultArtifact(coords);
        }
        catch ( IllegalArgumentException e )
        {
            throw new MojoFailureException( e.getMessage(), e );
        }

        ArtifactRequest request = new ArtifactRequest();
        request.setArtifact( artifact );
        request.setRepositories( remoteRepos );

        getLog().info( "Resolving artifact " + artifact + " from " + remoteRepos );

        ArtifactResult result;
        try
        {
            result = repoSystem.resolveArtifact( repoSession, request );
        }
        catch ( ArtifactResolutionException e )
        {
            throw new MojoExecutionException( e.getMessage(), e );
        }

        getLog().info( "Resolved artifact " + artifact + " to " + result.getArtifact().getFile() + " from "
                + result.getRepository() );

        return result.getArtifact();
    }

    protected File getSourceDirectory(Plugin plugin) {
        String path = repoSession.getLocalRepositoryManager().getPathForLocalArtifact(new DefaultArtifact(plugin.getGroupId(), plugin.getArtifactId(), "sourceDir", plugin.getVersion()));

        File file = new File(repoSession.getLocalRepository().getBasedir(), path);
        try {
            return file.exists() ? new File(Files.readAllLines(file.toPath(), Charset.forName("utf-8")).get(0)) : null;
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    protected void addDevelopmentPlugins(List<Plugin> plugins) {
        {
            Plugin devConsolePlugin = new Plugin("org.kantega.reststop", "reststop-development-console", mavenProject.getVersion());
            plugins.add(devConsolePlugin);
            devConsolePlugin.setDirectDeploy(false);
        }

        for (Plugin plugin : plugins) {
            plugin.setDirectDeploy(false);
        }
        {
            Plugin developmentPlugin = new Plugin("org.kantega.reststop", "reststop-development-plugin", mavenProject.getVersion());
            plugins.add(developmentPlugin);
            developmentPlugin.setDirectDeploy(true);
        }


        for (Plugin plugin : plugins) {
            plugin.setSourceDirectory(getSourceDirectory(plugin));
        }
    }

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

        public ShutdownHandler(Server server, Log log) {
            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 {
                                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);
            }
        }
    }
}
<commit_msg>Plugin defined props should not leak into prod<commit_after>/*
 * Copyright 2013 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.maven.plugin.AbstractMojo;
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.apache.maven.project.MavenProject;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.*;
import org.eclipse.aether.util.artifact.JavaScopes;
import org.eclipse.aether.util.filter.DependencyFilterUtils;
import org.eclipse.jetty.maven.plugin.JettyWebAppContext;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.kantega.reststop.classloaderutils.PluginInfo;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.*;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;

import static java.util.Arrays.asList;

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


    @Component
    protected RepositorySystem repoSystem;

    @Parameter(defaultValue ="${repositorySystemSession}" ,readonly = true)
    protected RepositorySystemSession repoSession;

    @Parameter(defaultValue = "${project.remoteProjectRepositories}")
    protected List<RemoteRepository> remoteRepos;

    @Parameter (defaultValue = "org.kantega.reststop:reststop-webapp:war:${plugin.version}")
    protected String warCoords;

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

    @Parameter(defaultValue = "${project.build.directory}/${project.build.finalName}.${project.packaging}")
    private File pluginJar;

    @Parameter(defaultValue = "${project}")
    protected MavenProject mavenProject;

    @Parameter
    protected List<Plugin> plugins;


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

        File war = resolveArtifactFile(warCoords);

        startJetty(war);


    }

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

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

            int port = nextAvailablePort(8080);

            mavenProject.getProperties().setProperty("reststopPort", Integer.toString(port));
            System.setProperty("reststopPort", Integer.toString(port));

            Server server = new Server(port);

            mavenProject.setContextValue("jettyServer", server);

            JettyWebAppContext context = new JettyWebAppContext();

            context.setWar(war.getAbsolutePath());
            context.getServletContext().setAttribute("pluginsXml", createPluginXmlDocument(false));

            customizeContext(context);

            tempDirectory.mkdirs();
            context.setTempDirectory(tempDirectory);
            context.setThrowUnavailableOnStartupException(true);

            HandlerCollection handlers = new HandlerCollection();

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

            server.start();

            afterServerStart(server, port);

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

    protected void customizeContext(JettyWebAppContext context) {

    }

    protected void afterServerStart(Server server, int port) throws MojoFailureException {

    }

    protected Document createPluginXmlDocument(boolean prod) throws MojoFailureException, MojoExecutionException {

        List<PluginInfo> pluginInfos = getPluginInfos();

        return buildPluginsDocument(prod, pluginInfos);


    }

    private Document buildPluginsDocument(boolean prod, List<PluginInfo> pluginInfos) throws MojoExecutionException {

        try {
            Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();

            Element pluginsElem = doc.createElement("plugins");

            doc.appendChild(pluginsElem);

            for (PluginInfo plugin : pluginInfos) {
                Element pluginElem = doc.createElement("plugin");
                pluginsElem.appendChild(pluginElem);

                List<PluginInfo> parents = plugin.getParents(pluginInfos);
                for (PluginInfo parent : parents) {
                    Element dependsElem = doc.createElement("depends-on");
                    pluginElem.appendChild(dependsElem);
                    dependsElem.setAttribute("groupId", parent.getGroupId());
                    dependsElem.setAttribute("artifactId", parent.getArtifactId());
                    dependsElem.setAttribute("version", parent.getVersion());

                }
                if(!prod) {
                    if(!plugin.getConfig().isEmpty()) {
                        Element configElem = doc.createElement("config");

                        for (String name : plugin.getConfig().stringPropertyNames()) {
                            Element propElem = doc.createElement("prop");
                            propElem.setAttribute("name", name);
                            propElem.setAttribute("value", plugin.getConfig().getProperty(name));
                            configElem.appendChild(propElem);
                        }

                        pluginElem.appendChild(configElem);
                    }
                }

                pluginElem.setAttribute("groupId", plugin.getGroupId());
                pluginElem.setAttribute("artifactId", plugin.getArtifactId());
                pluginElem.setAttribute("version", plugin.getVersion());
                if(!prod) {

                    if(plugin.getSourceDirectory() != null) {
                        pluginElem.setAttribute("sourceDirectory", plugin.getSourceDirectory().getAbsolutePath());
                    }


                    pluginElem.setAttribute("pluginFile", plugin.getFile().getAbsolutePath());
                    pluginElem.setAttribute("directDeploy", Boolean.toString(plugin.isDirectDeploy()));
                }


                List<String> scopes = prod ? Collections.singletonList(JavaScopes.RUNTIME) : asList(JavaScopes.TEST, JavaScopes.RUNTIME, JavaScopes.COMPILE);

                for(String scope : scopes) {

                    Element scopeElem = doc.createElement(scope);

                    pluginElem.appendChild(scopeElem);

                    for (org.kantega.reststop.classloaderutils.Artifact artifact : plugin.getClassPath(scope)) {
                        Element artifactElement = doc.createElement("artifact");
                        artifactElement.setAttribute("groupId", artifact.getGroupId());
                        artifactElement.setAttribute("artifactId", artifact.getArtifactId());
                        artifactElement.setAttribute("version", artifact.getVersion());

                        if(!prod) {
                            artifactElement.setAttribute("file", artifact.getFile().getAbsolutePath());
                        }

                        scopeElem.appendChild(artifactElement);
                    }


                }
            }
            return doc;
        } catch (ParserConfigurationException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }
    }

    private List<PluginInfo> getPluginInfos() throws MojoFailureException, MojoExecutionException {
        List<PluginInfo> pluginInfos = new ArrayList<>();

        for (Plugin plugin : getPlugins()) {
            PluginInfo info = plugin.asPluginInfo();
            pluginInfos.add(info);

            Artifact pluginArtifact = resolveArtifact(plugin.getCoords());

            info.setFile(pluginArtifact.getFile());

            for(String scope : asList(JavaScopes.TEST, JavaScopes.RUNTIME, JavaScopes.COMPILE)) {

                try {

                    ArtifactDescriptorResult descriptorResult = repoSystem.readArtifactDescriptor(repoSession, new ArtifactDescriptorRequest(pluginArtifact, remoteRepos, null));

                    CollectRequest collectRequest = new CollectRequest();

                    for (RemoteRepository repo : remoteRepos) {
                        collectRequest.addRepository(repo);
                    }
                    for (Dependency dependency : descriptorResult.getDependencies()) {
                        collectRequest.addDependency(dependency);
                    }

                    DependencyRequest dependencyRequest = new DependencyRequest(collectRequest, DependencyFilterUtils.classpathFilter(scope));

                    DependencyResult dependencyResult = repoSystem.resolveDependencies(repoSession, dependencyRequest);

                    if(!dependencyResult.getCollectExceptions().isEmpty()) {
                        throw new MojoFailureException("Failed resolving plugin dependencies", dependencyResult.getCollectExceptions().get(0));
                    }

                    for(ArtifactResult result : dependencyResult.getArtifactResults()) {
                        Artifact artifact = result.getArtifact();
                        org.kantega.reststop.classloaderutils.Artifact pa  = new org.kantega.reststop.classloaderutils.Artifact();
                        info.getClassPath(scope).add(pa);

                        pa.setGroupId(artifact.getGroupId());
                        pa.setArtifactId(artifact.getArtifactId());
                        pa.setVersion(artifact.getVersion());

                        pa.setFile(artifact.getFile());
                    }

                } catch (DependencyResolutionException | ArtifactDescriptorException e) {
                    throw new MojoFailureException("Failed resolving plugin dependencies", e);
                }


            }
        }

        validateTransitivePluginsMissing(pluginInfos);
        return pluginInfos;
    }

    private void validateTransitivePluginsMissing(List<PluginInfo> pluginInfos) throws MojoExecutionException, MojoFailureException {

        for (PluginInfo pluginInfo : pluginInfos) {

            Map<String, org.kantega.reststop.classloaderutils.Artifact> missing = new TreeMap<>();

            for (org.kantega.reststop.classloaderutils.Artifact dep : pluginInfo.getClassPath("compile")) {


                try {
                    JarFile jar = new JarFile(dep.getFile());
                    ZipEntry entry = jar.getEntry("META-INF/services/ReststopPlugin/");
                    boolean isPlugin = entry != null;
                    jar.close();

                    if(isPlugin && !isDeclaredPlugin(dep, pluginInfos)) {
                        missing.put(dep.getGroupIdAndArtifactId(), dep);
                        getLog().error("Plugin " + pluginInfo.getPluginId() +" depends on plugin " + dep.getPluginId() +" which must be declared as a <plugin>!");
                        String decl = String.format("\t<plugin>\n\t\t<groupId>%s</groupId>\n\t\t<artifactId>%s</artifactId>\n\t\t<version>%s</version>\n\t</plugin>", dep.getGroupId(), dep.getArtifactId(), dep.getVersion());
                        getLog().error("Please add the following to your <plugins> section:\n" + decl);
                    }


                } catch (IOException e) {
                    throw new MojoExecutionException(e.getMessage(), e);
                }

            }
            if(!missing.isEmpty()) {
                throw new MojoFailureException("Plugin " +pluginInfo.getPluginId() +" has a Maven <dependency> on "
                        + "one or more plugin artifacts which should be directly declared as a <plugin>: " + missing.values());
            }
        }

    }

    private boolean isDeclaredPlugin(org.kantega.reststop.classloaderutils.Artifact dep, List<PluginInfo> pluginInfos) {

        for(PluginInfo declared : pluginInfos) {
            if(declared.getGroupIdAndArtifactId().equals(dep.getGroupIdAndArtifactId())) {
                return true;
            }
        }
        return false;
    }

    protected List<Plugin> getPlugins() {
        List<Plugin> plugins = new ArrayList<>();

        if(this.plugins != null) {
            plugins.addAll(this.plugins);
        }


        return plugins;
    }


    private String getClasspath(List<org.apache.maven.artifact.Artifact> artifacts) {

        StringBuilder classpath = new StringBuilder();

        int c = 0;


        for (org.apache.maven.artifact.Artifact a : artifacts) {

            if (c > 0) {
                classpath.append(File.pathSeparatorChar);
            }
            c++;
            classpath.append(a.getFile().getAbsolutePath());


        }

        return classpath.toString();
    }

    private int nextAvailablePort(int first) {
        int port = first;
        for(;;) {
            try {
                ServerSocket socket = new ServerSocket(port);
                socket.close();
                return port;
            } catch (IOException e) {
                port++;
            }
        }
    }
    private File resolveArtifactFile(String coords) throws MojoFailureException, MojoExecutionException {
        return resolveArtifact(coords).getFile();
    }


    private Artifact resolveArtifact(String coords) throws MojoFailureException, MojoExecutionException {
        Artifact artifact;
        try
        {
            artifact = new DefaultArtifact(coords);
        }
        catch ( IllegalArgumentException e )
        {
            throw new MojoFailureException( e.getMessage(), e );
        }

        ArtifactRequest request = new ArtifactRequest();
        request.setArtifact( artifact );
        request.setRepositories( remoteRepos );

        getLog().info( "Resolving artifact " + artifact + " from " + remoteRepos );

        ArtifactResult result;
        try
        {
            result = repoSystem.resolveArtifact( repoSession, request );
        }
        catch ( ArtifactResolutionException e )
        {
            throw new MojoExecutionException( e.getMessage(), e );
        }

        getLog().info( "Resolved artifact " + artifact + " to " + result.getArtifact().getFile() + " from "
                + result.getRepository() );

        return result.getArtifact();
    }

    protected File getSourceDirectory(Plugin plugin) {
        String path = repoSession.getLocalRepositoryManager().getPathForLocalArtifact(new DefaultArtifact(plugin.getGroupId(), plugin.getArtifactId(), "sourceDir", plugin.getVersion()));

        File file = new File(repoSession.getLocalRepository().getBasedir(), path);
        try {
            return file.exists() ? new File(Files.readAllLines(file.toPath(), Charset.forName("utf-8")).get(0)) : null;
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    protected void addDevelopmentPlugins(List<Plugin> plugins) {
        {
            Plugin devConsolePlugin = new Plugin("org.kantega.reststop", "reststop-development-console", mavenProject.getVersion());
            plugins.add(devConsolePlugin);
            devConsolePlugin.setDirectDeploy(false);
        }

        for (Plugin plugin : plugins) {
            plugin.setDirectDeploy(false);
        }
        {
            Plugin developmentPlugin = new Plugin("org.kantega.reststop", "reststop-development-plugin", mavenProject.getVersion());
            plugins.add(developmentPlugin);
            developmentPlugin.setDirectDeploy(true);
        }


        for (Plugin plugin : plugins) {
            plugin.setSourceDirectory(getSourceDirectory(plugin));
        }
    }

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

        public ShutdownHandler(Server server, Log log) {
            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 {
                                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);
            }
        }
    }
}

{'fraction_non_alphanumeric': 0.09854303911652953, 'fraction_numerical': 0.0010387338381215318, 'mean_word_length': 2.2673037420737696, 'pattern_counts': {'":': 0, '<': 63, '<?xml version=': 0, '>': 65, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 4}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5714258', 'n_tokens_mistral': 9240, 'n_tokens_neox': 8643, 'n_words': 1744}