001    package org.apache.maven.lifecycle.internal;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     *  http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    import org.apache.maven.InternalErrorException;
023    import org.apache.maven.artifact.Artifact;
024    import org.apache.maven.execution.BuildFailure;
025    import org.apache.maven.execution.ExecutionEvent;
026    import org.apache.maven.execution.MavenExecutionRequest;
027    import org.apache.maven.execution.MavenSession;
028    import org.apache.maven.lifecycle.LifecycleExecutionException;
029    import org.apache.maven.lifecycle.LifecycleNotFoundException;
030    import org.apache.maven.lifecycle.LifecyclePhaseNotFoundException;
031    import org.apache.maven.lifecycle.MavenExecutionPlan;
032    import org.apache.maven.model.Plugin;
033    import org.apache.maven.plugin.InvalidPluginDescriptorException;
034    import org.apache.maven.plugin.MojoNotFoundException;
035    import org.apache.maven.plugin.PluginDescriptorParsingException;
036    import org.apache.maven.plugin.PluginNotFoundException;
037    import org.apache.maven.plugin.PluginResolutionException;
038    import org.apache.maven.plugin.descriptor.MojoDescriptor;
039    import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException;
040    import org.apache.maven.plugin.version.PluginVersionResolutionException;
041    import org.apache.maven.project.MavenProject;
042    import org.codehaus.plexus.classworlds.realm.ClassRealm;
043    import org.codehaus.plexus.component.annotations.Component;
044    import org.codehaus.plexus.component.annotations.Requirement;
045    import org.codehaus.plexus.logging.Logger;
046    
047    import java.util.Set;
048    
049    /**
050     * Common code that is shared by the LifecycleModuleBuilder and the LifeCycleWeaveBuilder
051     *
052     * @since 3.0
053     * @author Kristian Rosenvold
054     *         Builds one or more lifecycles for a full module
055     *         NOTE: This class is not part of any public api and can be changed or deleted without prior notice.
056     */
057    @Component( role = BuilderCommon.class )
058    public class BuilderCommon
059    {
060        @Requirement
061        private LifecycleDebugLogger lifecycleDebugLogger;
062    
063        @Requirement
064        private LifecycleExecutionPlanCalculator lifeCycleExecutionPlanCalculator;
065    
066        @Requirement
067        private ExecutionEventCatapult eventCatapult;
068    
069        @Requirement
070        private Logger logger;
071    
072    
073        public BuilderCommon()
074        {
075        }
076    
077        public BuilderCommon( LifecycleDebugLogger lifecycleDebugLogger,
078                              LifecycleExecutionPlanCalculator lifeCycleExecutionPlanCalculator, Logger logger )
079        {
080            this.lifecycleDebugLogger = lifecycleDebugLogger;
081            this.lifeCycleExecutionPlanCalculator = lifeCycleExecutionPlanCalculator;
082            this.logger = logger;
083        }
084    
085        public MavenExecutionPlan resolveBuildPlan( MavenSession session, MavenProject project, TaskSegment taskSegment,
086                                                    Set<Artifact> projectArtifacts )
087            throws PluginNotFoundException, PluginResolutionException, LifecyclePhaseNotFoundException,
088            PluginDescriptorParsingException, MojoNotFoundException, InvalidPluginDescriptorException,
089            NoPluginFoundForPrefixException, LifecycleNotFoundException, PluginVersionResolutionException,
090            LifecycleExecutionException
091        {
092            MavenExecutionPlan executionPlan =
093                lifeCycleExecutionPlanCalculator.calculateExecutionPlan( session, project, taskSegment.getTasks() );
094    
095            lifecycleDebugLogger.debugProjectPlan( project, executionPlan );
096    
097            if ( session.getRequest().isThreadConfigurationPresent() )
098            {
099                final Set<Plugin> unsafePlugins = executionPlan.getNonThreadSafePlugins();
100                if ( !unsafePlugins.isEmpty() )
101                {
102                    logger.warn( "*****************************************************************" );
103                    logger.warn( "* Your build is requesting parallel execution, but project      *" );
104                    logger.warn( "* contains the following plugin(s) that have goals not marked   *" );
105                    logger.warn( "* as @threadSafe to support parallel building.                  *" );
106                    logger.warn( "* While this /may/ work fine, please look for plugin updates    *" );
107                    logger.warn( "* and/or request plugins be made thread-safe.                   *" );
108                    logger.warn( "* If reporting an issue, report it against the plugin in        *" );
109                    logger.warn( "* question, not against maven-core                              *" );
110                    logger.warn( "*****************************************************************" );
111                    if ( logger.isDebugEnabled() )
112                    {
113                        final Set<MojoDescriptor> unsafeGoals = executionPlan.getNonThreadSafeMojos();
114                        logger.warn( "The following goals are not marked @threadSafe in " + project.getName() + ":" );
115                        for ( MojoDescriptor unsafeGoal : unsafeGoals )
116                        {
117                            logger.warn( unsafeGoal.getId() );
118                        }
119                    }
120                    else
121                    {
122                        logger.warn( "The following plugins are not marked @threadSafe in " + project.getName() + ":" );
123                        for ( Plugin unsafePlugin : unsafePlugins )
124                        {
125                            logger.warn( unsafePlugin.getId() );
126                        }
127                        logger.warn( "Enable debug to see more precisely which goals are not marked @threadSafe." );
128                    }
129                    logger.warn( "*****************************************************************" );
130                }
131            }
132    
133            return executionPlan;
134        }
135    
136        public void handleBuildError( final ReactorContext buildContext, final MavenSession rootSession,
137                                      final MavenSession currentSession, final MavenProject mavenProject, Exception e,
138                                      final long buildStartTime )
139        {
140            if ( e instanceof RuntimeException )
141            {
142                e = new InternalErrorException( "Internal error: " + e, e );
143            }
144    
145            buildContext.getResult().addException( e );
146    
147            long buildEndTime = System.currentTimeMillis();
148    
149            buildContext.getResult().addBuildSummary( new BuildFailure( mavenProject, buildEndTime - buildStartTime, e ) );
150    
151            eventCatapult.fire( ExecutionEvent.Type.ProjectFailed, currentSession, null, e );
152    
153            if ( MavenExecutionRequest.REACTOR_FAIL_NEVER.equals( rootSession.getReactorFailureBehavior() ) )
154            {
155                // continue the build
156            }
157            else if ( MavenExecutionRequest.REACTOR_FAIL_AT_END.equals( rootSession.getReactorFailureBehavior() ) )
158            {
159                // continue the build but ban all projects that depend on the failed one
160                buildContext.getReactorBuildStatus().blackList( mavenProject );
161            }
162            else if ( MavenExecutionRequest.REACTOR_FAIL_FAST.equals( rootSession.getReactorFailureBehavior() ) )
163            {
164                buildContext.getReactorBuildStatus().halt();
165            }
166            else
167            {
168                throw new IllegalArgumentException(
169                    "invalid reactor failure behavior " + rootSession.getReactorFailureBehavior() );
170            }
171        }
172    
173        public static void attachToThread( MavenProject currentProject )
174        {
175            ClassRealm projectRealm = currentProject.getClassRealm();
176            if ( projectRealm != null )
177            {
178                Thread.currentThread().setContextClassLoader( projectRealm );
179            }
180        }
181    
182        // Todo: I'm really wondering where this method belongs; smells like it should be on MavenProject, but for some reason
183        // it isn't ? This localization is kind-of a code smell.
184    
185        public static String getKey( MavenProject project )
186        {
187            return project.getGroupId() + ':' + project.getArtifactId() + ':' + project.getVersion();
188        }
189    
190    }