View Javadoc

1   package org.apache.continuum.buildagent.build.execution.maven.m2;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.artifact.manager.WagonManager;
23  import org.apache.maven.artifact.repository.ArtifactRepository;
24  import org.apache.maven.artifact.repository.ArtifactRepositoryFactory;
25  import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
26  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
27  import org.apache.maven.continuum.model.project.Project;
28  import org.apache.maven.continuum.model.project.ProjectDependency;
29  import org.apache.maven.continuum.model.project.ProjectDeveloper;
30  import org.apache.maven.continuum.model.project.ProjectNotifier;
31  import org.apache.maven.continuum.project.builder.ContinuumProjectBuildingResult;
32  import org.apache.maven.model.Dependency;
33  import org.apache.maven.model.Developer;
34  import org.apache.maven.model.Extension;
35  import org.apache.maven.model.Model;
36  import org.apache.maven.model.Notifier;
37  import org.apache.maven.model.Plugin;
38  import org.apache.maven.model.Profile;
39  import org.apache.maven.model.ReportPlugin;
40  import org.apache.maven.model.Scm;
41  import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
42  import org.apache.maven.profiles.DefaultProfileManager;
43  import org.apache.maven.profiles.ProfileManager;
44  import org.apache.maven.project.InvalidProjectModelException;
45  import org.apache.maven.project.MavenProject;
46  import org.apache.maven.project.MavenProjectBuilder;
47  import org.apache.maven.project.ProjectBuildingException;
48  import org.apache.maven.project.validation.ModelValidationResult;
49  import org.apache.maven.settings.MavenSettingsBuilder;
50  import org.apache.maven.settings.Mirror;
51  import org.apache.maven.settings.Proxy;
52  import org.apache.maven.settings.Server;
53  import org.apache.maven.settings.Settings;
54  import org.apache.maven.settings.io.xpp3.SettingsXpp3Writer;
55  import org.codehaus.plexus.PlexusConstants;
56  import org.codehaus.plexus.PlexusContainer;
57  import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
58  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
59  import org.codehaus.plexus.context.Context;
60  import org.codehaus.plexus.context.ContextException;
61  import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
62  import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
63  import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
64  import org.codehaus.plexus.util.StringUtils;
65  import org.codehaus.plexus.util.xml.Xpp3Dom;
66  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
67  import org.slf4j.Logger;
68  import org.slf4j.LoggerFactory;
69  
70  import java.io.File;
71  import java.io.IOException;
72  import java.io.StringWriter;
73  import java.util.ArrayList;
74  import java.util.List;
75  
76  /**
77   * @plexus.component role="org.apache.continuum.buildagent.build.execution.maven.m2.BuildAgentMavenBuilderHelper"
78   * role-hint="default"
79   */
80  public class DefaultBuildAgentMavenBuilderHelper
81      implements BuildAgentMavenBuilderHelper, Contextualizable, Initializable
82  {
83      private static final Logger log = LoggerFactory.getLogger( DefaultBuildAgentMavenBuilderHelper.class );
84  
85      /**
86       * @plexus.requirement
87       */
88      private MavenProjectBuilder projectBuilder;
89  
90      /**
91       * @plexus.requirement
92       */
93      private MavenSettingsBuilder mavenSettingsBuilder;
94  
95      /**
96       * @plexus.requirement
97       */
98      private ArtifactRepositoryFactory artifactRepositoryFactory;
99  
100     /**
101      * @plexus.requirement
102      */
103     private ArtifactRepositoryLayout defaultRepositoryLayout;
104 
105     private PlexusContainer container;
106 
107     public MavenProject getMavenProject( ContinuumProjectBuildingResult result, File file )
108     {
109         MavenProject project;
110 
111         try
112         {
113             //   TODO: This seems like code that is shared with DefaultMaven, so it should be moved to the project
114             //   builder perhaps
115 
116             Settings settings = getSettings();
117 
118             if ( log.isDebugEnabled() )
119             {
120                 writeSettings( settings );
121             }
122 
123             ProfileManager profileManager = new DefaultProfileManager( container, settings );
124 
125             project = projectBuilder.build( file, getRepository( settings ), profileManager, false );
126 
127             if ( log.isDebugEnabled() )
128             {
129                 writePom( project );
130                 writeActiveProfileStatement( project );
131             }
132 
133         }
134         catch ( ProjectBuildingException e )
135         {
136             StringBuffer messages = new StringBuffer();
137 
138             Throwable cause = e.getCause();
139 
140             if ( cause != null )
141             {
142                 while ( ( cause.getCause() != null ) && ( cause instanceof ProjectBuildingException ) )
143                 {
144                     cause = cause.getCause();
145                 }
146             }
147 
148             if ( e instanceof InvalidProjectModelException )
149             {
150                 InvalidProjectModelException ex = (InvalidProjectModelException) e;
151 
152                 ModelValidationResult validationResult = ex.getValidationResult();
153 
154                 if ( validationResult != null && validationResult.getMessageCount() > 0 )
155                 {
156                     for ( String valmsg : (List<String>) validationResult.getMessages() )
157                     {
158                         result.addError( ContinuumProjectBuildingResult.ERROR_VALIDATION, valmsg );
159                         messages.append( valmsg );
160                         messages.append( "\n" );
161                     }
162                 }
163             }
164 
165             if ( cause instanceof ArtifactNotFoundException )
166             {
167                 result.addError( ContinuumProjectBuildingResult.ERROR_ARTIFACT_NOT_FOUND, ( cause ).toString() );
168                 return null;
169             }
170 
171             result.addError( ContinuumProjectBuildingResult.ERROR_PROJECT_BUILDING, e.getMessage() );
172 
173             String msg = "Cannot build maven project from " + file + " (" + e.getMessage() + ").\n" + messages;
174 
175             file.delete();
176 
177             log.error( msg );
178 
179             return null;
180         }
181         // TODO catch all exceptions is bad
182         catch ( Exception e )
183         {
184             result.addError( ContinuumProjectBuildingResult.ERROR_PROJECT_BUILDING, e.getMessage() );
185 
186             String msg = "Cannot build maven project from " + file + " (" + e.getMessage() + ").";
187 
188             file.delete();
189 
190             log.error( msg );
191 
192             return null;
193         }
194 
195         // ----------------------------------------------------------------------
196         // Validate the MavenProject using some Continuum rules
197         // ----------------------------------------------------------------------
198 
199         // SCM connection
200         Scm scm = project.getScm();
201 
202         if ( scm == null )
203         {
204             result.addError( ContinuumProjectBuildingResult.ERROR_MISSING_SCM, getProjectName( project ) );
205 
206             log.error( "Missing 'scm' element in the " + getProjectName( project ) + " POM." );
207 
208             return null;
209         }
210 
211         String url = scm.getConnection();
212 
213         if ( StringUtils.isEmpty( url ) )
214         {
215             result.addError( ContinuumProjectBuildingResult.ERROR_MISSING_SCM_CONNECTION, getProjectName( project ) );
216 
217             log.error( "Missing 'connection' element in the 'scm' element in the " + getProjectName( project ) +
218                            " POM." );
219 
220             return null;
221         }
222 
223         return project;
224     }
225 
226     public void mapMetadataToProject( ContinuumProjectBuildingResult result, File metadata, Project continuumProject )
227     {
228         MavenProject mavenProject = getMavenProject( result, metadata );
229 
230         if ( mavenProject == null )
231         {
232             result.addError( ContinuumProjectBuildingResult.ERROR_UNKNOWN,
233                              "Can't load the maven project. Verify that your scm url is correct and remove/readd the project." );
234             return;
235         }
236 
237         mapMavenProjectToContinuumProject( result, mavenProject, continuumProject, false );
238     }
239 
240     public void mapMavenProjectToContinuumProject( ContinuumProjectBuildingResult result, MavenProject mavenProject,
241                                                    Project continuumProject, boolean groupPom )
242     {
243         if ( mavenProject == null )
244         {
245             result.addError( ContinuumProjectBuildingResult.ERROR_UNKNOWN, "The maven project is null." );
246             return;
247         }
248 
249         // ----------------------------------------------------------------------
250         // Name
251         // ----------------------------------------------------------------------
252 
253         continuumProject.setName( getProjectName( mavenProject ) );
254 
255         // ----------------------------------------------------------------------
256         // SCM Url
257         // ----------------------------------------------------------------------
258 
259         // TODO: Remove this: scm url shouldn't be null there
260         if ( StringUtils.isEmpty( continuumProject.getScmUrl() ) )
261         {
262             String scmUrl = getScmUrl( mavenProject );
263 
264             continuumProject.setScmUrl( scmUrl );
265 
266             if ( !"HEAD".equals( mavenProject.getScm().getTag() ) )
267             {
268                 continuumProject.setScmTag( mavenProject.getScm().getTag() );
269             }
270         }
271 
272         // ----------------------------------------------------------------------
273         // Version
274         // ----------------------------------------------------------------------
275 
276         continuumProject.setVersion( getVersion( mavenProject ) );
277 
278         // ----------------------------------------------------------------------
279         // GroupId
280         // ----------------------------------------------------------------------
281 
282         if ( !StringUtils.isEmpty( mavenProject.getGroupId() ) )
283         {
284             continuumProject.setGroupId( mavenProject.getGroupId() );
285         }
286         else
287         {
288             result.addError( ContinuumProjectBuildingResult.ERROR_MISSING_GROUPID );
289             return;
290         }
291 
292         // ----------------------------------------------------------------------
293         // artifactId
294         // ----------------------------------------------------------------------
295 
296         if ( !StringUtils.isEmpty( mavenProject.getArtifactId() ) )
297         {
298             continuumProject.setArtifactId( mavenProject.getArtifactId() );
299         }
300         else
301         {
302             result.addError( ContinuumProjectBuildingResult.ERROR_MISSING_ARTIFACTID );
303             return;
304         }
305 
306         // ----------------------------------------------------------------------
307         // Project Url
308         // ----------------------------------------------------------------------
309 
310         if ( !StringUtils.isEmpty( mavenProject.getUrl() ) )
311         {
312             continuumProject.setUrl( mavenProject.getUrl() );
313         }
314 
315         // ----------------------------------------------------------------------
316         // Developers
317         // ----------------------------------------------------------------------
318 
319         if ( mavenProject.getDevelopers() != null )
320         {
321             List<ProjectDeveloper> developers = new ArrayList<ProjectDeveloper>();
322 
323             for ( Developer d : (List<Developer>) mavenProject.getDevelopers() )
324             {
325                 ProjectDeveloper cd = new ProjectDeveloper();
326 
327                 cd.setScmId( d.getId() );
328 
329                 cd.setName( d.getName() );
330 
331                 cd.setEmail( d.getEmail() );
332 
333                 developers.add( cd );
334             }
335 
336             continuumProject.setDevelopers( developers );
337         }
338 
339         // ----------------------------------------------------------------------
340         // Parent
341         // ----------------------------------------------------------------------
342 
343         if ( mavenProject.getParent() != null )
344         {
345             MavenProject parentProject = mavenProject.getParent();
346 
347             ProjectDependency parent = new ProjectDependency();
348 
349             parent.setGroupId( parentProject.getGroupId() );
350 
351             parent.setArtifactId( parentProject.getArtifactId() );
352 
353             parent.setVersion( parentProject.getVersion() );
354 
355             continuumProject.setParent( parent );
356         }
357 
358         // ----------------------------------------------------------------------
359         // Dependencies
360         // ----------------------------------------------------------------------
361 
362         List<ProjectDependency> dependencies = new ArrayList<ProjectDependency>();
363 
364         for ( Dependency dependency : (List<Dependency>) mavenProject.getDependencies() )
365         {
366             ProjectDependency cd = new ProjectDependency();
367 
368             cd.setGroupId( dependency.getGroupId() );
369 
370             cd.setArtifactId( dependency.getArtifactId() );
371 
372             cd.setVersion( dependency.getVersion() );
373 
374             dependencies.add( cd );
375         }
376 
377         for ( Plugin dependency : (List<Plugin>) mavenProject.getBuildPlugins() )
378         {
379             ProjectDependency cd = new ProjectDependency();
380 
381             cd.setGroupId( dependency.getGroupId() );
382 
383             cd.setArtifactId( dependency.getArtifactId() );
384 
385             cd.setVersion( dependency.getVersion() );
386 
387             dependencies.add( cd );
388         }
389 
390         for ( ReportPlugin dependency : (List<ReportPlugin>) mavenProject.getReportPlugins() )
391         {
392             ProjectDependency cd = new ProjectDependency();
393 
394             cd.setGroupId( dependency.getGroupId() );
395 
396             cd.setArtifactId( dependency.getArtifactId() );
397 
398             cd.setVersion( dependency.getVersion() );
399 
400             dependencies.add( cd );
401         }
402 
403         for ( Extension dependency : (List<Extension>) mavenProject.getBuildExtensions() )
404         {
405             ProjectDependency cd = new ProjectDependency();
406 
407             cd.setGroupId( dependency.getGroupId() );
408 
409             cd.setArtifactId( dependency.getArtifactId() );
410 
411             cd.setVersion( dependency.getVersion() );
412 
413             dependencies.add( cd );
414         }
415 
416         continuumProject.setDependencies( dependencies );
417 
418         // ----------------------------------------------------------------------
419         // Notifiers
420         // ----------------------------------------------------------------------
421 
422         List<ProjectNotifier> userNotifiers = new ArrayList<ProjectNotifier>();
423 
424         if ( continuumProject.getNotifiers() != null )
425         {
426             for ( int i = 0; i < continuumProject.getNotifiers().size(); i++ )
427             {
428                 ProjectNotifier notifier = (ProjectNotifier) continuumProject.getNotifiers().get( i );
429 
430                 if ( notifier.isFromUser() )
431                 {
432                     ProjectNotifier userNotifier = new ProjectNotifier();
433 
434                     userNotifier.setType( notifier.getType() );
435 
436                     userNotifier.setEnabled( notifier.isEnabled() );
437 
438                     userNotifier.setConfiguration( notifier.getConfiguration() );
439 
440                     userNotifier.setFrom( notifier.getFrom() );
441 
442                     userNotifier.setRecipientType( notifier.getRecipientType() );
443 
444                     userNotifier.setSendOnError( notifier.isSendOnError() );
445 
446                     userNotifier.setSendOnFailure( notifier.isSendOnFailure() );
447 
448                     userNotifier.setSendOnSuccess( notifier.isSendOnSuccess() );
449 
450                     userNotifier.setSendOnWarning( notifier.isSendOnWarning() );
451 
452                     userNotifier.setSendOnScmFailure( notifier.isSendOnScmFailure() );
453 
454                     userNotifiers.add( userNotifier );
455                 }
456             }
457         }
458 
459         List<ProjectNotifier> notifiers = getNotifiers( result, mavenProject, continuumProject );
460         if ( notifiers != null )
461         {
462             continuumProject.setNotifiers( notifiers );
463         }
464 
465         for ( ProjectNotifier notifier : userNotifiers )
466         {
467             continuumProject.addNotifier( notifier );
468         }
469     }
470 
471     private String getScmUrl( MavenProject project )
472     {
473         return project.getScm().getConnection();
474     }
475 
476     private List<ProjectNotifier> getNotifiers( ContinuumProjectBuildingResult result, MavenProject mavenProject,
477                                                 Project continuumProject )
478     {
479         List<ProjectNotifier> notifiers = new ArrayList<ProjectNotifier>();
480 
481         if ( mavenProject.getCiManagement() != null && mavenProject.getCiManagement().getNotifiers() != null )
482         {
483             for ( Notifier projectNotifier : (List<Notifier>) mavenProject.getCiManagement().getNotifiers() )
484             {
485                 ProjectNotifier notifier = new ProjectNotifier();
486 
487                 if ( StringUtils.isEmpty( projectNotifier.getType() ) )
488                 {
489                     result.addError( ContinuumProjectBuildingResult.ERROR_MISSING_NOTIFIER_TYPE );
490                     return null;
491                 }
492 
493                 notifier.setType( projectNotifier.getType() );
494 
495                 if ( projectNotifier.getConfiguration() == null )
496                 {
497                     result.addError( ContinuumProjectBuildingResult.ERROR_MISSING_NOTIFIER_CONFIGURATION );
498                     return null;
499                 }
500 
501                 notifier.setConfiguration( projectNotifier.getConfiguration() );
502 
503                 notifier.setFrom( ProjectNotifier.FROM_PROJECT );
504 
505                 notifier.setSendOnSuccess( projectNotifier.isSendOnSuccess() );
506 
507                 notifier.setSendOnFailure( projectNotifier.isSendOnFailure() );
508 
509                 notifier.setSendOnError( projectNotifier.isSendOnError() );
510 
511                 notifier.setSendOnWarning( projectNotifier.isSendOnWarning() );
512 
513                 notifier.setSendOnScmFailure( false );
514 
515                 notifiers.add( notifier );
516             }
517         }
518 
519         return notifiers;
520     }
521 
522     private String getVersion( MavenProject project )
523     {
524         return project.getVersion();
525     }
526 
527     private Settings getSettings()
528         throws SettingsConfigurationException
529     {
530         try
531         {
532             return mavenSettingsBuilder.buildSettings( false );
533         }
534         catch ( IOException e )
535         {
536             throw new SettingsConfigurationException( "Error reading settings file", e );
537         }
538         catch ( XmlPullParserException e )
539         {
540             throw new SettingsConfigurationException( e.getMessage(), e.getDetail(), e.getLineNumber(),
541                                                       e.getColumnNumber() );
542         }
543     }
544 
545     private ArtifactRepository getRepository( Settings settings )
546     {
547         return artifactRepositoryFactory.createArtifactRepository( "local", "file://" + settings.getLocalRepository(),
548                                                                    defaultRepositoryLayout, null, null );
549     }
550 
551     public String getProjectName( MavenProject project )
552     {
553         String name = project.getName();
554 
555         if ( StringUtils.isEmpty( name ) )
556         {
557             return project.getId();
558         }
559 
560         return name;
561     }
562 
563     private void writeSettings( Settings settings )
564     {
565         StringWriter sWriter = new StringWriter();
566 
567         SettingsXpp3Writer settingsWriter = new SettingsXpp3Writer();
568 
569         try
570         {
571             settingsWriter.write( sWriter, settings );
572 
573             StringBuffer message = new StringBuffer();
574 
575             message.append( "\n************************************************************************************" );
576             message.append( "\nEffective Settings" );
577             message.append( "\n************************************************************************************" );
578             message.append( "\n" );
579             message.append( sWriter.toString() );
580             message.append( "\n************************************************************************************" );
581             message.append( "\n\n" );
582 
583             log.debug( message.toString() );
584         }
585         catch ( IOException e )
586         {
587             log.warn( "Cannot serialize Settings to XML.", e );
588         }
589     }
590 
591     private void writePom( MavenProject project )
592     {
593         StringBuffer message = new StringBuffer();
594 
595         Model pom = project.getModel();
596 
597         StringWriter sWriter = new StringWriter();
598 
599         MavenXpp3Writer pomWriter = new MavenXpp3Writer();
600 
601         try
602         {
603             pomWriter.write( sWriter, pom );
604 
605             message.append( "\n************************************************************************************" );
606             message.append( "\nEffective POM for project \'" ).append( project.getId() ).append( "\'" );
607             message.append( "\n************************************************************************************" );
608             message.append( "\n" );
609             message.append( sWriter.toString() );
610             message.append( "\n************************************************************************************" );
611             message.append( "\n\n" );
612 
613             log.debug( message.toString() );
614         }
615         catch ( IOException e )
616         {
617             log.warn( "Cannot serialize POM to XML.", e );
618         }
619     }
620 
621     private void writeActiveProfileStatement( MavenProject project )
622     {
623         List<Profile> profiles = project.getActiveProfiles();
624 
625         StringBuffer message = new StringBuffer();
626 
627         message.append( "\n" );
628 
629         message.append( "\n************************************************************************************" );
630         message.append( "\nActive Profiles for Project \'" ).append( project.getId() ).append( "\'" );
631         message.append( "\n************************************************************************************" );
632         message.append( "\n" );
633 
634         if ( profiles == null || profiles.isEmpty() )
635         {
636             message.append( "There are no active profiles." );
637         }
638         else
639         {
640             message.append( "The following profiles are active:\n" );
641 
642             for ( Profile profile : profiles )
643             {
644                 message.append( "\n - " ).append( profile.getId() ).append( " (source: " ).append(
645                     profile.getSource() ).append( ")" );
646             }
647 
648         }
649 
650         message.append( "\n************************************************************************************" );
651         message.append( "\n\n" );
652 
653         log.debug( message.toString() );
654     }
655 
656     public void contextualize( Context context )
657         throws ContextException
658     {
659         container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
660     }
661 
662     public void initialize()
663         throws InitializationException
664     {
665         try
666         {
667             Settings settings = getSettings();
668 
669             resolveParameters( settings );
670         }
671         catch ( Exception e )
672         {
673             throw new InitializationException( "Can't initialize '" + getClass().getName() + "'", e );
674         }
675     }
676 
677     /**
678      * @todo [BP] this might not be required if there is a better way to pass
679      * them in. It doesn't feel quite right.
680      * @todo [JC] we should at least provide a mapping of protocol-to-proxy for
681      * the wagons, shouldn't we?
682      */
683     private void resolveParameters( Settings settings )
684         throws ComponentLookupException, ComponentLifecycleException, SettingsConfigurationException
685     {
686         WagonManager wagonManager = (WagonManager) container.lookup( WagonManager.ROLE );
687 
688         try
689         {
690             Proxy proxy = settings.getActiveProxy();
691 
692             if ( proxy != null )
693             {
694                 if ( proxy.getHost() == null )
695                 {
696                     throw new SettingsConfigurationException( "Proxy in settings.xml has no host" );
697                 }
698 
699                 wagonManager.addProxy( proxy.getProtocol(), proxy.getHost(), proxy.getPort(), proxy.getUsername(),
700                                        proxy.getPassword(), proxy.getNonProxyHosts() );
701             }
702 
703             for ( Server server : (List<Server>) settings.getServers() )
704             {
705                 wagonManager.addAuthenticationInfo( server.getId(), server.getUsername(), server.getPassword(),
706                                                     server.getPrivateKey(), server.getPassphrase() );
707 
708                 wagonManager.addPermissionInfo( server.getId(), server.getFilePermissions(),
709                                                 server.getDirectoryPermissions() );
710 
711                 if ( server.getConfiguration() != null )
712                 {
713                     wagonManager.addConfiguration( server.getId(), (Xpp3Dom) server.getConfiguration() );
714                 }
715             }
716 
717             for ( Mirror mirror : (List<Mirror>) settings.getMirrors() )
718             {
719                 wagonManager.addMirror( mirror.getId(), mirror.getMirrorOf(), mirror.getUrl() );
720             }
721         }
722         finally
723         {
724             container.release( wagonManager );
725         }
726     }
727 }