| /* |
| * 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. |
| */ |
| package org.apache.maven; |
| |
| import javax.inject.Inject; |
| import javax.inject.Named; |
| import javax.inject.Singleton; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.LinkedHashMap; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.function.Function; |
| import java.util.stream.Stream; |
| |
| import org.apache.maven.api.Session; |
| import org.apache.maven.api.model.Model; |
| import org.apache.maven.api.model.Prerequisites; |
| import org.apache.maven.api.model.Profile; |
| import org.apache.maven.artifact.ArtifactUtils; |
| import org.apache.maven.execution.BuildResumptionAnalyzer; |
| import org.apache.maven.execution.BuildResumptionDataRepository; |
| import org.apache.maven.execution.BuildResumptionPersistenceException; |
| 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.ProfileActivation; |
| import org.apache.maven.execution.ProjectActivation; |
| import org.apache.maven.execution.ProjectDependencyGraph; |
| import org.apache.maven.graph.GraphBuilder; |
| import org.apache.maven.graph.ProjectSelector; |
| import org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory; |
| import org.apache.maven.internal.aether.MavenChainedWorkspaceReader; |
| import org.apache.maven.internal.impl.DefaultSessionFactory; |
| import org.apache.maven.internal.impl.InternalSession; |
| import org.apache.maven.lifecycle.LifecycleExecutionException; |
| 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.model.superpom.SuperPomProvider; |
| import org.apache.maven.plugin.LegacySupport; |
| import org.apache.maven.project.MavenProject; |
| import org.apache.maven.project.ProjectBuilder; |
| import org.apache.maven.session.scope.internal.SessionScope; |
| import org.codehaus.plexus.PlexusContainer; |
| import org.codehaus.plexus.component.repository.exception.ComponentLookupException; |
| import org.eclipse.aether.RepositorySystemSession; |
| import org.eclipse.aether.RepositorySystemSession.CloseableSession; |
| import org.eclipse.aether.repository.WorkspaceReader; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.slf4j.helpers.MessageFormatter; |
| |
| import static java.util.stream.Collectors.toSet; |
| |
| /** |
| */ |
| @Named |
| @Singleton |
| public class DefaultMaven implements Maven { |
| private final Logger logger = LoggerFactory.getLogger(getClass()); |
| |
| protected ProjectBuilder projectBuilder; |
| |
| private LifecycleStarter lifecycleStarter; |
| |
| protected PlexusContainer container; |
| |
| private ExecutionEventCatapult eventCatapult; |
| |
| private LegacySupport legacySupport; |
| |
| private SessionScope sessionScope; |
| |
| private DefaultRepositorySystemSessionFactory repositorySessionFactory; |
| |
| private final GraphBuilder graphBuilder; |
| |
| private final BuildResumptionAnalyzer buildResumptionAnalyzer; |
| |
| private final BuildResumptionDataRepository buildResumptionDataRepository; |
| |
| private final SuperPomProvider superPomProvider; |
| |
| private final DefaultSessionFactory defaultSessionFactory; |
| |
| private final ProjectSelector projectSelector; |
| |
| @Inject |
| @SuppressWarnings("checkstyle:ParameterNumber") |
| public DefaultMaven( |
| ProjectBuilder projectBuilder, |
| LifecycleStarter lifecycleStarter, |
| PlexusContainer container, |
| ExecutionEventCatapult eventCatapult, |
| LegacySupport legacySupport, |
| SessionScope sessionScope, |
| DefaultRepositorySystemSessionFactory repositorySessionFactory, |
| @Named(GraphBuilder.HINT) GraphBuilder graphBuilder, |
| BuildResumptionAnalyzer buildResumptionAnalyzer, |
| BuildResumptionDataRepository buildResumptionDataRepository, |
| SuperPomProvider superPomProvider, |
| DefaultSessionFactory defaultSessionFactory) { |
| this.projectBuilder = projectBuilder; |
| this.lifecycleStarter = lifecycleStarter; |
| this.container = container; |
| this.eventCatapult = eventCatapult; |
| this.legacySupport = legacySupport; |
| this.sessionScope = sessionScope; |
| this.repositorySessionFactory = repositorySessionFactory; |
| this.graphBuilder = graphBuilder; |
| this.buildResumptionAnalyzer = buildResumptionAnalyzer; |
| this.buildResumptionDataRepository = buildResumptionDataRepository; |
| this.superPomProvider = superPomProvider; |
| this.defaultSessionFactory = defaultSessionFactory; |
| this.projectSelector = new ProjectSelector(); // if necessary switch to DI |
| } |
| |
| @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 (IOException 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(); |
| MavenChainedWorkspaceReader chainedWorkspaceReader = new MavenChainedWorkspaceReader(); |
| try (CloseableSession closeableSession = newCloseableSession(request, chainedWorkspaceReader)) { |
| MavenSession session = new MavenSession(closeableSession, request, result); |
| session.setSession(defaultSessionFactory.getSession(session)); |
| |
| sessionScope.seed(MavenSession.class, session); |
| sessionScope.seed(Session.class, session.getSession()); |
| sessionScope.seed(InternalSession.class, InternalSession.from(session.getSession())); |
| |
| legacySupport.setSession(session); |
| |
| return doExecute(request, session, result, chainedWorkspaceReader); |
| } finally { |
| sessionScope.exit(); |
| } |
| } |
| |
| private MavenExecutionResult doExecute( |
| MavenExecutionRequest request, |
| MavenSession session, |
| MavenExecutionResult result, |
| MavenChainedWorkspaceReader chainedWorkspaceReader) { |
| try { |
| afterSessionStart(session); |
| } catch (MavenExecutionException e) { |
| return addExceptionToResult(result, e); |
| } |
| |
| try { |
| WorkspaceReader reactorReader = container.lookup(WorkspaceReader.class, ReactorReader.HINT); |
| chainedWorkspaceReader.setReaders(Collections.singletonList(reactorReader)); |
| } catch (ComponentLookupException e) { |
| return addExceptionToResult(result, e); |
| } |
| |
| eventCatapult.fire(ExecutionEvent.Type.ProjectDiscoveryStarted, session, null); |
| |
| Result<? extends ProjectDependencyGraph> graphResult = buildGraph(session); |
| |
| if (graphResult.hasErrors()) { |
| return addExceptionToResult( |
| result, graphResult.getProblems().iterator().next().getException()); |
| } |
| |
| try { |
| session.setProjectMap(getProjectMap(session.getProjects())); |
| } catch (DuplicateProjectException e) { |
| return addExceptionToResult(result, e); |
| } |
| |
| try { |
| setupWorkspaceReader(session, chainedWorkspaceReader); |
| } catch (ComponentLookupException e) { |
| return addExceptionToResult(result, e); |
| } |
| try { |
| afterProjectsRead(session); |
| } catch (MavenExecutionException e) { |
| return addExceptionToResult(result, e); |
| } |
| |
| // |
| // 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); |
| |
| if (graphResult.hasErrors()) { |
| return addExceptionToResult( |
| result, graphResult.getProblems().iterator().next().getException()); |
| } |
| |
| try { |
| if (result.hasExceptions()) { |
| return result; |
| } |
| |
| result.setTopologicallySortedProjects(session.getProjects()); |
| |
| result.setProject(session.getTopLevelProject()); |
| |
| validatePrerequisitesForNonMavenPluginProjects(session.getProjects()); |
| |
| validateRequiredProfiles(session, request.getProfileActivation()); |
| if (session.getResult().hasExceptions()) { |
| return result; |
| } |
| |
| validateOptionalProfiles(session, request.getProfileActivation()); |
| |
| lifecycleStarter.execute(session); |
| |
| validateOptionalProjects(request, session); |
| validateOptionalProfiles(session, request.getProfileActivation()); |
| |
| if (session.getResult().hasExceptions()) { |
| addExceptionToResult(result, session.getResult().getExceptions().get(0)); |
| persistResumptionData(result, session); |
| return result; |
| } else { |
| session.getAllProjects().stream() |
| .filter(MavenProject::isExecutionRoot) |
| .findFirst() |
| .ifPresent(buildResumptionDataRepository::removeResumptionData); |
| } |
| } finally { |
| try { |
| afterSessionEnd(session); |
| } catch (MavenExecutionException e) { |
| return addExceptionToResult(result, e); |
| } |
| } |
| |
| return result; |
| } |
| |
| private void setupWorkspaceReader(MavenSession session, MavenChainedWorkspaceReader chainedWorkspaceReader) |
| throws ComponentLookupException { |
| // Desired order of precedence for workspace readers before querying the local artifact repositories |
| Set<WorkspaceReader> workspaceReaders = new LinkedHashSet<>(); |
| // 1) Reactor workspace reader |
| WorkspaceReader reactorReader = container.lookup(WorkspaceReader.class, ReactorReader.HINT); |
| workspaceReaders.add(reactorReader); |
| // 2) Repository system session-scoped workspace reader |
| for (WorkspaceReader repoWorkspaceReader : chainedWorkspaceReader.getReaders()) { |
| if (repoWorkspaceReader != null && repoWorkspaceReader != reactorReader) { |
| workspaceReaders.add(repoWorkspaceReader); |
| } |
| } |
| // 3) .. n) Project-scoped workspace readers |
| workspaceReaders.addAll(getProjectScopedExtensionComponents(session.getProjects(), WorkspaceReader.class)); |
| chainedWorkspaceReader.setReaders(workspaceReaders); |
| } |
| |
| private void afterSessionStart(MavenSession session) throws MavenExecutionException { |
| callListeners(session, AbstractMavenLifecycleParticipant::afterSessionStart); |
| } |
| |
| private void afterProjectsRead(MavenSession session) throws MavenExecutionException { |
| callListeners(session, AbstractMavenLifecycleParticipant::afterProjectsRead); |
| } |
| |
| private void afterSessionEnd(MavenSession session) throws MavenExecutionException { |
| callListeners(session, AbstractMavenLifecycleParticipant::afterSessionEnd); |
| } |
| |
| @FunctionalInterface |
| interface ListenerMethod { |
| void run(AbstractMavenLifecycleParticipant listener, MavenSession session) throws MavenExecutionException; |
| } |
| |
| private void callListeners(MavenSession session, ListenerMethod method) throws MavenExecutionException { |
| ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); |
| try { |
| for (AbstractMavenLifecycleParticipant listener : |
| getExtensionComponents(session.getProjects(), AbstractMavenLifecycleParticipant.class)) { |
| Thread.currentThread().setContextClassLoader(listener.getClass().getClassLoader()); |
| method.run(listener, session); |
| } |
| } finally { |
| Thread.currentThread().setContextClassLoader(originalClassLoader); |
| } |
| } |
| |
| private void persistResumptionData(MavenExecutionResult result, MavenSession session) { |
| boolean hasLifecycleExecutionExceptions = |
| result.getExceptions().stream().anyMatch(LifecycleExecutionException.class::isInstance); |
| |
| if (hasLifecycleExecutionExceptions) { |
| MavenProject rootProject = session.getAllProjects().stream() |
| .filter(MavenProject::isExecutionRoot) |
| .findFirst() |
| .orElseThrow(() -> new IllegalStateException("No project in the session is execution root")); |
| |
| buildResumptionAnalyzer.determineBuildResumptionData(result).ifPresent(resumption -> { |
| try { |
| buildResumptionDataRepository.persistResumptionData(rootProject, resumption); |
| result.setCanResume(true); |
| } catch (BuildResumptionPersistenceException e) { |
| logger.warn("Could not persist build resumption data", e); |
| } |
| }); |
| } |
| } |
| |
| /** |
| * Nobody should ever use this method. |
| * |
| * @deprecated If you use this method and your code is not in Maven Core, stop doing this. |
| */ |
| @Deprecated |
| public RepositorySystemSession newRepositorySession(MavenExecutionRequest request) { |
| return newCloseableSession(request, new MavenChainedWorkspaceReader()); |
| } |
| |
| private CloseableSession newCloseableSession(MavenExecutionRequest request, WorkspaceReader workspaceReader) { |
| return repositorySessionFactory |
| .newRepositorySessionBuilder(request) |
| .setWorkspaceReader(workspaceReader) |
| .build(); |
| } |
| |
| private void validateLocalRepository(MavenExecutionRequest request) throws IOException { |
| File localRepoDir = request.getLocalRepositoryPath(); |
| |
| logger.debug("Using local repository at {}", localRepoDir); |
| |
| localRepoDir.mkdirs(); |
| |
| if (!localRepoDir.isDirectory()) { |
| throw new IOException("Could not create local repository at " + localRepoDir); |
| } |
| } |
| |
| private <T> Collection<T> getExtensionComponents(Collection<MavenProject> projects, Class<T> role) { |
| Collection<T> foundComponents = new LinkedHashSet<>(); |
| |
| try { |
| foundComponents.addAll(container.lookupList(role)); |
| } catch (ComponentLookupException e) { |
| // this is just silly, lookupList should return an empty list! |
| logger.warn("Failed to lookup {}: {}", role, e.getMessage()); |
| } |
| |
| foundComponents.addAll(getProjectScopedExtensionComponents(projects, role)); |
| |
| return foundComponents; |
| } |
| |
| protected <T> Collection<T> getProjectScopedExtensionComponents(Collection<MavenProject> projects, Class<T> role) { |
| if (projects == null) { |
| return Collections.emptyList(); |
| } |
| |
| Collection<T> foundComponents = new LinkedHashSet<>(); |
| Collection<ClassLoader> scannedRealms = new HashSet<>(); |
| |
| Thread currentThread = Thread.currentThread(); |
| ClassLoader originalContextClassLoader = currentThread.getContextClassLoader(); |
| try { |
| for (MavenProject project : projects) { |
| ClassLoader projectRealm = project.getClassRealm(); |
| |
| if (projectRealm != null && scannedRealms.add(projectRealm)) { |
| currentThread.setContextClassLoader(projectRealm); |
| |
| try { |
| foundComponents.addAll(container.lookupList(role)); |
| } catch (ComponentLookupException e) { |
| // this is just silly, lookupList should return an empty list! |
| logger.warn("Failed to lookup {}: {}", role, e.getMessage()); |
| } |
| } |
| } |
| return foundComponents; |
| } finally { |
| currentThread.setContextClassLoader(originalContextClassLoader); |
| } |
| } |
| |
| private MavenExecutionResult addExceptionToResult(MavenExecutionResult result, Throwable e) { |
| if (!result.getExceptions().contains(e)) { |
| result.addException(e); |
| } |
| |
| return result; |
| } |
| |
| private void validatePrerequisitesForNonMavenPluginProjects(List<MavenProject> projects) { |
| for (MavenProject mavenProject : projects) { |
| if (!"maven-plugin".equals(mavenProject.getPackaging())) { |
| Prerequisites prerequisites = |
| mavenProject.getModel().getDelegate().getPrerequisites(); |
| if (prerequisites != null && prerequisites.getMaven() != null) { |
| logger.warn( |
| "The project {} uses prerequisites" |
| + " which is only intended for maven-plugin projects " |
| + "but not for non maven-plugin projects. " |
| + "For such purposes you should use the maven-enforcer-plugin. " |
| + "See https://maven.apache.org/enforcer/enforcer-rules/requireMavenVersion.html", |
| mavenProject.getId()); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Get all profiles that are detected in the projects, any parent of the projects, or the settings. |
| * @param session The Maven session |
| * @return A {@link Set} of profile identifiers, never {@code null}. |
| */ |
| private Set<String> getAllProfiles(MavenSession session) { |
| final Map<String, Model> superPomModels = new HashMap<>(); |
| final Set<MavenProject> projectsIncludingParents = new HashSet<>(); |
| for (MavenProject project : session.getProjects()) { |
| superPomModels.computeIfAbsent( |
| project.getModelVersion(), |
| v -> superPomProvider.getSuperModel(v).getDelegate()); |
| boolean isAdded = projectsIncludingParents.add(project); |
| MavenProject parent = project.getParent(); |
| while (isAdded && parent != null) { |
| isAdded = projectsIncludingParents.add(parent); |
| parent = parent.getParent(); |
| } |
| } |
| |
| final Stream<String> projectProfiles = projectsIncludingParents.stream() |
| .flatMap(p -> p.getModel().getDelegate().getProfiles().stream()) |
| .map(Profile::getId); |
| final Stream<String> settingsProfiles = |
| session.getSettings().getProfiles().stream().map(org.apache.maven.settings.Profile::getId); |
| final Stream<String> superPomProfiles = superPomModels.values().stream() |
| .flatMap(p -> p.getProfiles().stream()) |
| .map(Profile::getId); |
| |
| return Stream.of(projectProfiles, settingsProfiles, superPomProfiles) |
| .flatMap(Function.identity()) |
| .collect(toSet()); |
| } |
| |
| /** |
| * Check whether the required profiles were found in any of the projects we're building or the settings. |
| * @param session the Maven session. |
| * @param profileActivation the requested optional and required profiles. |
| */ |
| private void validateRequiredProfiles(MavenSession session, ProfileActivation profileActivation) { |
| final Set<String> allAvailableProfiles = getAllProfiles(session); |
| |
| final Set<String> requiredProfiles = new HashSet<>(); |
| requiredProfiles.addAll(profileActivation.getRequiredActiveProfileIds()); |
| requiredProfiles.addAll(profileActivation.getRequiredInactiveProfileIds()); |
| |
| // Check whether the required profiles were found in any of the projects we're building. |
| final Set<String> notFoundRequiredProfiles = requiredProfiles.stream() |
| .filter(rap -> !allAvailableProfiles.contains(rap)) |
| .collect(toSet()); |
| |
| if (!notFoundRequiredProfiles.isEmpty()) { |
| // Use SLF4J formatter for consistency with warnings reported by logger |
| final String message = MessageFormatter.format( |
| "The requested profiles {} could not be activated or deactivated because they do not" |
| + " exist.", |
| notFoundRequiredProfiles) |
| .getMessage(); |
| addExceptionToResult(session.getResult(), new MissingProfilesException(message)); |
| } |
| } |
| |
| /** |
| * Check whether any of the requested optional projects were not activated or deactivated. |
| * @param request the {@link MavenExecutionRequest}. |
| * @param session the {@link MavenSession}. |
| */ |
| private void validateOptionalProjects(MavenExecutionRequest request, MavenSession session) { |
| final ProjectActivation projectActivation = request.getProjectActivation(); |
| final Set<String> allOptionalSelectors = new HashSet<>(); |
| allOptionalSelectors.addAll(projectActivation.getOptionalActiveProjectSelectors()); |
| allOptionalSelectors.addAll(projectActivation.getRequiredActiveProjectSelectors()); |
| // We intentionally ignore the results of this method. |
| // As a side effect it will log the optional projects that could not be resolved. |
| projectSelector.getOptionalProjectsBySelectors(request, session.getAllProjects(), allOptionalSelectors); |
| } |
| |
| /** |
| * Check whether any of the requested optional profiles were not activated or deactivated. |
| * @param session the Maven session. |
| * @param profileActivation the requested optional and required profiles. |
| */ |
| private void validateOptionalProfiles(MavenSession session, ProfileActivation profileActivation) { |
| final Set<String> allAvailableProfiles = getAllProfiles(session); |
| |
| final Set<String> optionalProfiles = new HashSet<>(); |
| optionalProfiles.addAll(profileActivation.getOptionalActiveProfileIds()); |
| optionalProfiles.addAll(profileActivation.getOptionalInactiveProfileIds()); |
| |
| final Set<String> notFoundOptionalProfiles = optionalProfiles.stream() |
| .filter(rap -> !allAvailableProfiles.contains(rap)) |
| .collect(toSet()); |
| |
| if (!notFoundOptionalProfiles.isEmpty()) { |
| logger.info( |
| "The requested optional profiles {} could not be activated or deactivated because they do not" |
| + " exist.", |
| notFoundOptionalProfiles); |
| } |
| } |
| |
| 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) { |
| Result<? extends ProjectDependencyGraph> graphResult = graphBuilder.build(session); |
| for (ModelProblem problem : graphResult.getProblems()) { |
| if (problem.getSeverity() == ModelProblem.Severity.WARNING) { |
| logger.warn(problem.getMessage()); |
| } else { |
| logger.error(problem.getMessage()); |
| } |
| } |
| |
| if (!graphResult.hasErrors()) { |
| ProjectDependencyGraph projectDependencyGraph = graphResult.get(); |
| session.setProjects(projectDependencyGraph.getSortedProjects()); |
| session.setAllProjects(projectDependencyGraph.getAllProjects()); |
| session.setProjectDependencyGraph(projectDependencyGraph); |
| } |
| |
| return graphResult; |
| } |
| |
| @Deprecated |
| // 5 January 2014 |
| protected Logger getLogger() { |
| return logger; |
| } |
| } |