org.apache.maven.DefaultMaven.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.maven.DefaultMaven.java

Source

package org.apache.maven;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF 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.
 */

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;

import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.execution.DefaultMavenExecutionResult;
import org.apache.maven.execution.ExecutionEvent;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenExecutionResult;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.ProjectDependencyGraph;
import org.apache.maven.graph.GraphBuilder;
import org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory;
import org.apache.maven.lifecycle.internal.ExecutionEventCatapult;
import org.apache.maven.lifecycle.internal.LifecycleStarter;
import org.apache.maven.model.building.ModelProblem;
import org.apache.maven.model.building.Result;
import org.apache.maven.plugin.LegacySupport;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuilder;
import org.apache.maven.repository.LocalRepositoryNotAccessibleException;
import org.apache.maven.session.scope.internal.SessionScope;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.logging.Logger;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.repository.WorkspaceReader;
import org.eclipse.aether.util.repository.ChainedWorkspaceReader;

import com.google.common.collect.Iterables;

/**
 * @author Jason van Zyl
 */
@Component(role = Maven.class)
public class DefaultMaven implements Maven {

    @Requirement
    private Logger logger;

    @Requirement
    protected ProjectBuilder projectBuilder;

    @Requirement
    private LifecycleStarter lifecycleStarter;

    @Requirement
    protected PlexusContainer container;

    @Requirement
    private ExecutionEventCatapult eventCatapult;

    @Requirement
    private LegacySupport legacySupport;

    @Requirement
    private SessionScope sessionScope;

    @Requirement
    private DefaultRepositorySystemSessionFactory repositorySessionFactory;

    @Requirement(hint = GraphBuilder.HINT)
    private GraphBuilder graphBuilder;

    @Override
    public MavenExecutionResult execute(MavenExecutionRequest request) {
        MavenExecutionResult result;

        try {
            result = doExecute(request);
        } catch (OutOfMemoryError e) {
            result = addExceptionToResult(new DefaultMavenExecutionResult(), e);
        } catch (RuntimeException e) {
            //TODO Hack to make the cycle detection the same for the new graph builder
            if (e.getCause() instanceof ProjectCycleException) {
                result = addExceptionToResult(new DefaultMavenExecutionResult(), e.getCause());
            } else {
                result = addExceptionToResult(new DefaultMavenExecutionResult(),
                        new InternalErrorException("Internal error: " + e, e));
            }
        } finally {
            legacySupport.setSession(null);
        }

        return result;
    }

    //
    // 1) Setup initial properties.
    //
    // 2) Validate local repository directory is accessible.
    //
    // 3) Create RepositorySystemSession.
    //
    // 4) Create MavenSession.
    //
    // 5) Execute AbstractLifecycleParticipant.afterSessionStart(session)
    //
    // 6) Get reactor projects looking for general POM errors
    //
    // 7) Create ProjectDependencyGraph using trimming which takes into account --projects and reactor mode.
    // This ensures that the projects passed into the ReactorReader are only those specified.
    //
    // 8) Create ReactorReader with the getProjectMap( projects ). NOTE that getProjectMap(projects) is the code that
    // checks for duplicate projects definitions in the build. Ideally this type of duplicate checking should be
    // part of getting the reactor projects in 6). The duplicate checking is conflated with getProjectMap(projects).
    //
    // 9) Execute AbstractLifecycleParticipant.afterProjectsRead(session)
    //
    // 10) Create ProjectDependencyGraph without trimming (as trimming was done in 7). A new topological sort is
    // required after the execution of 9) as the AbstractLifecycleParticipants are free to mutate the MavenProject
    // instances, which may change dependencies which can, in turn, affect the build order.
    //
    // 11) Execute LifecycleStarter.start()
    //
    @SuppressWarnings("checkstyle:methodlength")
    private MavenExecutionResult doExecute(MavenExecutionRequest request) {
        request.setStartTime(new Date());

        MavenExecutionResult result = new DefaultMavenExecutionResult();

        try {
            validateLocalRepository(request);
        } catch (LocalRepositoryNotAccessibleException e) {
            return addExceptionToResult(result, e);
        }

        //
        // We enter the session scope right after the MavenSession creation and before any of the
        // AbstractLifecycleParticipant lookups
        // so that @SessionScoped components can be @Injected into AbstractLifecycleParticipants.
        //
        sessionScope.enter();
        try {
            DefaultRepositorySystemSession repoSession = (DefaultRepositorySystemSession) newRepositorySession(
                    request);
            MavenSession session = new MavenSession(container, repoSession, request, result);

            sessionScope.seed(MavenSession.class, session);

            legacySupport.setSession(session);

            return doExecute(request, session, result, repoSession);
        } finally {
            sessionScope.exit();
        }
    }

    private MavenExecutionResult doExecute(MavenExecutionRequest request, MavenSession session,
            MavenExecutionResult result, DefaultRepositorySystemSession repoSession) {
        try {
            for (AbstractMavenLifecycleParticipant listener : getLifecycleParticipants(
                    Collections.<MavenProject>emptyList())) {
                listener.afterSessionStart(session);
            }
        } catch (MavenExecutionException e) {
            return addExceptionToResult(result, e);
        }

        eventCatapult.fire(ExecutionEvent.Type.ProjectDiscoveryStarted, session, null);

        Result<? extends ProjectDependencyGraph> graphResult = buildGraph(session, result);

        if (graphResult.hasErrors()) {
            return addExceptionToResult(result,
                    Iterables.toArray(graphResult.getProblems(), ModelProblem.class)[0].getException());
        }

        try {
            session.setProjectMap(getProjectMap(session.getProjects()));
        } catch (DuplicateProjectException e) {
            return addExceptionToResult(result, e);
        }

        WorkspaceReader reactorWorkspace;
        try {
            reactorWorkspace = container.lookup(WorkspaceReader.class, ReactorReader.HINT);
        } catch (ComponentLookupException e) {
            return addExceptionToResult(result, e);
        }

        //
        // Desired order of precedence for local artifact repositories
        //
        // Reactor
        // Workspace
        // User Local Repository
        //
        repoSession.setWorkspaceReader(
                ChainedWorkspaceReader.newInstance(reactorWorkspace, repoSession.getWorkspaceReader()));

        repoSession.setReadOnly();

        ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
        try {
            for (AbstractMavenLifecycleParticipant listener : getLifecycleParticipants(session.getProjects())) {
                Thread.currentThread().setContextClassLoader(listener.getClass().getClassLoader());

                listener.afterProjectsRead(session);
            }
        } catch (MavenExecutionException e) {
            return addExceptionToResult(result, e);
        } finally {
            Thread.currentThread().setContextClassLoader(originalClassLoader);
        }

        //
        // The projects need to be topologically after the participants have run their afterProjectsRead(session)
        // because the participant is free to change the dependencies of a project which can potentially change the
        // topological order of the projects, and therefore can potentially change the build order.
        //
        // Note that participants may affect the topological order of the projects but it is
        // not expected that a participant will add or remove projects from the session.
        //

        graphResult = buildGraph(session, result);

        if (graphResult.hasErrors()) {
            return addExceptionToResult(result,
                    Iterables.toArray(graphResult.getProblems(), ModelProblem.class)[0].getException());
        }

        try {
            if (result.hasExceptions()) {
                return result;
            }

            result.setTopologicallySortedProjects(session.getProjects());

            result.setProject(session.getTopLevelProject());

            lifecycleStarter.execute(session);

            validateActivatedProfiles(session.getProjects(), request.getActiveProfiles());

            if (session.getResult().hasExceptions()) {
                return addExceptionToResult(result, session.getResult().getExceptions().get(0));
            }
        } finally {
            try {
                afterSessionEnd(session.getProjects(), session);
            } catch (MavenExecutionException e) {
                return addExceptionToResult(result, e);
            }
        }

        return result;
    }

    private void afterSessionEnd(Collection<MavenProject> projects, MavenSession session)
            throws MavenExecutionException {
        ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
        try {
            for (AbstractMavenLifecycleParticipant listener : getLifecycleParticipants(projects)) {
                Thread.currentThread().setContextClassLoader(listener.getClass().getClassLoader());

                listener.afterSessionEnd(session);
            }
        } finally {
            Thread.currentThread().setContextClassLoader(originalClassLoader);
        }
    }

    public RepositorySystemSession newRepositorySession(MavenExecutionRequest request) {
        return repositorySessionFactory.newRepositorySession(request);
    }

    private void validateLocalRepository(MavenExecutionRequest request)
            throws LocalRepositoryNotAccessibleException {
        File localRepoDir = request.getLocalRepositoryPath();

        logger.debug("Using local repository at " + localRepoDir);

        localRepoDir.mkdirs();

        if (!localRepoDir.isDirectory()) {
            throw new LocalRepositoryNotAccessibleException("Could not create local repository at " + localRepoDir);
        }
    }

    private Collection<AbstractMavenLifecycleParticipant> getLifecycleParticipants(
            Collection<MavenProject> projects) {
        Collection<AbstractMavenLifecycleParticipant> lifecycleListeners = new LinkedHashSet<>();

        ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
        try {
            try {
                lifecycleListeners.addAll(container.lookupList(AbstractMavenLifecycleParticipant.class));
            } catch (ComponentLookupException e) {
                // this is just silly, lookupList should return an empty list!
                logger.warn("Failed to lookup lifecycle participants: " + e.getMessage());
            }

            Collection<ClassLoader> scannedRealms = new HashSet<>();

            for (MavenProject project : projects) {
                ClassLoader projectRealm = project.getClassRealm();

                if (projectRealm != null && scannedRealms.add(projectRealm)) {
                    Thread.currentThread().setContextClassLoader(projectRealm);

                    try {
                        lifecycleListeners.addAll(container.lookupList(AbstractMavenLifecycleParticipant.class));
                    } catch (ComponentLookupException e) {
                        // this is just silly, lookupList should return an empty list!
                        logger.warn("Failed to lookup lifecycle participants: " + e.getMessage());
                    }
                }
            }
        } finally {
            Thread.currentThread().setContextClassLoader(originalClassLoader);
        }

        return lifecycleListeners;
    }

    private MavenExecutionResult addExceptionToResult(MavenExecutionResult result, Throwable e) {
        if (!result.getExceptions().contains(e)) {
            result.addException(e);
        }

        return result;
    }

    private void validateActivatedProfiles(List<MavenProject> projects, List<String> activeProfileIds) {
        Collection<String> notActivatedProfileIds = new LinkedHashSet<>(activeProfileIds);

        for (MavenProject project : projects) {
            for (List<String> profileIds : project.getInjectedProfileIds().values()) {
                notActivatedProfileIds.removeAll(profileIds);
            }
        }

        for (String notActivatedProfileId : notActivatedProfileIds) {
            logger.warn("The requested profile \"" + notActivatedProfileId
                    + "\" could not be activated because it does not exist.");
        }
    }

    private Map<String, MavenProject> getProjectMap(Collection<MavenProject> projects)
            throws DuplicateProjectException {
        Map<String, MavenProject> index = new LinkedHashMap<>();
        Map<String, List<File>> collisions = new LinkedHashMap<>();

        for (MavenProject project : projects) {
            String projectId = ArtifactUtils.key(project.getGroupId(), project.getArtifactId(),
                    project.getVersion());

            MavenProject collision = index.get(projectId);

            if (collision == null) {
                index.put(projectId, project);
            } else {
                List<File> pomFiles = collisions.get(projectId);

                if (pomFiles == null) {
                    pomFiles = new ArrayList<>(Arrays.asList(collision.getFile(), project.getFile()));
                    collisions.put(projectId, pomFiles);
                } else {
                    pomFiles.add(project.getFile());
                }
            }
        }

        if (!collisions.isEmpty()) {
            throw new DuplicateProjectException("Two or more projects in the reactor"
                    + " have the same identifier, please make sure that <groupId>:<artifactId>:<version>"
                    + " is unique for each project: " + collisions, collisions);
        }

        return index;
    }

    private Result<? extends ProjectDependencyGraph> buildGraph(MavenSession session, MavenExecutionResult result) {
        Result<? extends ProjectDependencyGraph> graphResult = graphBuilder.build(session);
        for (ModelProblem problem : graphResult.getProblems()) {
            if (problem.getSeverity() == ModelProblem.Severity.WARNING) {
                logger.warn(problem.toString());
            } else {
                logger.error(problem.toString());
            }
        }

        if (!graphResult.hasErrors()) {
            ProjectDependencyGraph projectDependencyGraph = graphResult.get();
            session.setProjects(projectDependencyGraph.getSortedProjects());
            session.setAllProjects(projectDependencyGraph.getSortedProjects());
            session.setProjectDependencyGraph(projectDependencyGraph);
        }

        return graphResult;
    }

    @Deprecated
    // 5 January 2014
    protected Logger getLogger() {
        return logger;
    }
}