hudson.maven.MavenUtil.java Source code

Java tutorial

Introduction

Here is the source code for hudson.maven.MavenUtil.java

Source

/*
 * The MIT License
 * 
 * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package hudson.maven;

import hudson.AbortException;
import hudson.model.BuildListener;
import hudson.model.TaskListener;
import hudson.model.AbstractProject;
import hudson.tasks.Maven.MavenInstallation;
import hudson.tasks.Maven.ProjectWithMaven;

import org.apache.maven.embedder.MavenEmbedderException;
import org.apache.maven.embedder.MavenEmbedderLogger;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuildingException;
import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Logger;

/**
 * @author Kohsuke Kawaguchi
 */
public class MavenUtil {
    /**
     * @deprecated
     *      Use {@link #createEmbedder(TaskListener, File, String, Properties)}  
     *      or other overloaded versions that infers maven home.
     */
    public static MavenEmbedder createEmbedder(TaskListener listener, String profiles)
            throws MavenEmbedderException, IOException {
        return createEmbedder(listener, (File) null, profiles);
    }

    /**
     * This version tries to infer mavenHome by looking at a project.
     *
     * @see #createEmbedder(TaskListener, File, String)
     */
    public static MavenEmbedder createEmbedder(TaskListener listener, AbstractProject<?, ?> project,
            String profiles) throws MavenEmbedderException, IOException {
        MavenInstallation m = null;
        if (project instanceof ProjectWithMaven)
            m = ((ProjectWithMaven) project).inferMavenInstallation();

        return createEmbedder(listener, m != null ? m.getHomeDir() : null, profiles);
    }

    public static MavenEmbedder createEmbedder(TaskListener listener, File mavenHome, String profiles)
            throws MavenEmbedderException, IOException {
        return createEmbedder(listener, mavenHome, profiles, new Properties());
    }

    public static MavenEmbedder createEmbedder(TaskListener listener, File mavenHome, String profiles,
            Properties systemProperties) throws MavenEmbedderException, IOException {
        return createEmbedder(listener, mavenHome, profiles, systemProperties, null);
    }

    public static MavenEmbedder createEmbedder(TaskListener listener, File mavenHome, String profiles,
            Properties systemProperties, String privateRepository) throws MavenEmbedderException, IOException {
        return createEmbedder(listener, mavenHome, profiles, systemProperties, privateRepository, null);
    }

    /**
     * Creates a fresh {@link MavenEmbedder} instance.
     *
     * @param listener
     *      This is where the log messages from Maven will be recorded.
     * @param mavenHome
     *      Directory of the Maven installation. We read {@code conf/settings.xml}
     *      from here. Can be null.
     * @param profiles
     *      Profiles to activate/deactivate. Can be null.
     * @param systemProperties
     *      The system properties that the embedded Maven sees. See {@link MavenEmbedder#setSystemProperties(Properties)}.
     * @param privateRepository
     *      Optional private repository to use as the local repository.
     * @param alternateSettings
     *      Optional alternate settings.xml file.
     */
    public static MavenEmbedder createEmbedder(TaskListener listener, File mavenHome, String profiles,
            Properties systemProperties, String privateRepository, File alternateSettings)
            throws MavenEmbedderException, IOException {
        MavenEmbedder maven = new MavenEmbedder(mavenHome);

        ClassLoader cl = MavenUtil.class.getClassLoader();
        maven.setClassLoader(new MaskingClassLoader(cl));
        EmbedderLoggerImpl logger = new EmbedderLoggerImpl(listener);
        if (debugMavenEmbedder)
            logger.setThreshold(MavenEmbedderLogger.LEVEL_DEBUG);
        maven.setLogger(logger);

        {
            Enumeration<URL> e = cl.getResources("META-INF/plexus/components.xml");
            while (e.hasMoreElements()) {
                URL url = e.nextElement();
                LOGGER.fine("components.xml from " + url);
            }
        }
        // make sure ~/.m2 exists to avoid http://www.nabble.com/BUG-Report-tf3401736.html
        File m2Home = new File(MavenEmbedder.userHome, ".m2");
        m2Home.mkdirs();
        if (!m2Home.exists())
            throw new AbortException(
                    "Failed to create " + m2Home + "\nSee https://hudson.dev.java.net/cannot-create-.m2.html");

        if (privateRepository != null)
            maven.setLocalRepositoryDirectory(new File(privateRepository));

        maven.setProfiles(profiles);

        if (alternateSettings != null)
            maven.setAlternateSettings(alternateSettings);

        maven.setSystemProperties(systemProperties);
        maven.start();

        return maven;
    }

    /**
     * Recursively resolves module POMs that are referenced from
     * the given {@link MavenProject} and parses them into
     * {@link MavenProject}s.
     *
     * @param rel
     *      Used to compute the relative path. Pass in "" to begin.
     * @param relativePathInfo
     *      Upon the completion of this method, this variable stores the relative path
     *      from the root directory of the given {@link MavenProject} to the root directory
     *      of each of the newly parsed {@link MavenProject}.
     *
     * @throws AbortException
     *      errors will be reported to the listener and the exception thrown.
     */
    public static void resolveModules(MavenEmbedder embedder, MavenProject project, String rel,
            Map<MavenProject, String> relativePathInfo, BuildListener listener)
            throws ProjectBuildingException, AbortException {

        File basedir = project.getFile().getParentFile();
        relativePathInfo.put(project, rel);

        List<MavenProject> modules = new ArrayList<MavenProject>();

        for (String modulePath : (List<String>) project.getModules()) {
            File moduleFile = new File(basedir, modulePath);
            if (moduleFile.exists() && moduleFile.isDirectory()) {
                moduleFile = new File(basedir, modulePath + "/pom.xml");
            }
            if (!moduleFile.exists())
                throw new AbortException(
                        moduleFile + " is referenced from " + project.getFile() + " but it doesn't exist");

            String relativePath = rel;
            if (relativePath.length() > 0)
                relativePath += '/';
            relativePath += modulePath;

            MavenProject child = embedder.readProject(moduleFile);
            resolveModules(embedder, child, relativePath, relativePathInfo, listener);
            modules.add(child);
        }

        project.setCollectedProjects(modules);
    }

    /**
     * When we run in Jetty during development, embedded Maven will end up
     * seeing some of the Maven class visible through Jetty, and this confuses it.
     *
     * <p>
     * Specifically, embedded Maven will find all the component descriptors
     * visible through Jetty, yet when it comes to loading classes, classworlds
     * still load classes from local realms created inside embedder.
     *
     * <p>
     * This classloader prevents this issue by hiding the component descriptor
     * visible through Jetty.
     */
    private static final class MaskingClassLoader extends ClassLoader {

        public MaskingClassLoader(ClassLoader parent) {
            super(parent);
        }

        public Enumeration<URL> getResources(String name) throws IOException {
            final Enumeration<URL> e = super.getResources(name);
            return new Enumeration<URL>() {
                URL next;

                public boolean hasMoreElements() {
                    fetch();
                    return next != null;
                }

                public URL nextElement() {
                    fetch();
                    URL r = next;
                    next = null;
                    return r;
                }

                private void fetch() {
                    while (next == null && e.hasMoreElements()) {
                        next = e.nextElement();
                        if (shouldBeIgnored(next))
                            next = null;
                    }
                }

                private boolean shouldBeIgnored(URL url) {
                    String s = url.toExternalForm();
                    if (s.contains("maven-plugin-tools-api"))
                        return true;
                    // because RemoteClassLoader mangles the path, we can't check for plexus/components.xml,
                    // which would have otherwise made the test cheaper.
                    if (s.endsWith("components.xml")) {
                        BufferedReader r = null;
                        try {
                            // is this designated for interception purpose? If so, don't load them in the MavenEmbedder
                            // earlier I tried to use a marker file in the same directory, but that won't work
                            r = new BufferedReader(new InputStreamReader(url.openStream()));
                            for (int i = 0; i < 2; i++) {
                                String l = r.readLine();
                                if (l != null && l.contains("MAVEN-INTERCEPTION-TO-BE-MASKED"))
                                    return true;
                            }
                        } catch (IOException _) {
                            // let whoever requesting this resource re-discover an error and report it
                        } finally {
                            IOUtils.closeQuietly(r);
                        }
                    }
                    return false;
                }
            };
        }
    }

    /**
     * If set to true, maximize the logging level of Maven embedder.
     */
    public static boolean debugMavenEmbedder = false;

    private static final Logger LOGGER = Logger.getLogger(MavenUtil.class.getName());
}