View Javadoc

1   package org.apache.maven.continuum.project.builder.maven;
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.continuum.dao.LocalRepositoryDao;
23  import org.apache.continuum.dao.ProjectGroupDao;
24  import org.apache.continuum.dao.ScheduleDao;
25  import org.apache.continuum.model.repository.LocalRepository;
26  import org.apache.maven.continuum.builddefinition.BuildDefinitionService;
27  import org.apache.maven.continuum.builddefinition.BuildDefinitionServiceException;
28  import org.apache.maven.continuum.configuration.ConfigurationService;
29  import org.apache.maven.continuum.execution.maven.m2.MavenBuilderHelper;
30  import org.apache.maven.continuum.execution.maven.m2.MavenTwoBuildExecutor;
31  import org.apache.maven.continuum.model.project.BuildDefinition;
32  import org.apache.maven.continuum.model.project.BuildDefinitionTemplate;
33  import org.apache.maven.continuum.model.project.Project;
34  import org.apache.maven.continuum.model.project.ProjectGroup;
35  import org.apache.maven.continuum.model.project.Schedule;
36  import org.apache.maven.continuum.project.builder.AbstractContinuumProjectBuilder;
37  import org.apache.maven.continuum.project.builder.ContinuumProjectBuilder;
38  import org.apache.maven.continuum.project.builder.ContinuumProjectBuilderException;
39  import org.apache.maven.continuum.project.builder.ContinuumProjectBuildingResult;
40  import org.apache.maven.continuum.store.ContinuumStoreException;
41  import org.apache.maven.project.MavenProject;
42  import org.codehaus.plexus.util.StringUtils;
43  
44  import java.io.File;
45  import java.net.MalformedURLException;
46  import java.net.URL;
47  import java.util.ArrayList;
48  import java.util.List;
49  
50  /**
51   * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
52   * @version $Id: MavenTwoContinuumProjectBuilder.java 1391353 2012-09-28 07:50:49Z brett $
53   * @plexus.component role="org.apache.maven.continuum.project.builder.ContinuumProjectBuilder" role-hint="maven-two-builder"
54   */
55  public class MavenTwoContinuumProjectBuilder
56      extends AbstractContinuumProjectBuilder
57      implements ContinuumProjectBuilder
58  {
59      public static final String ID = "maven-two-builder";
60  
61      private static final String POM_PART = "/pom.xml";
62  
63      /**
64       * @plexus.requirement
65       */
66      private LocalRepositoryDao localRepositoryDao;
67  
68      /**
69       * @plexus.requirement
70       */
71      private MavenBuilderHelper builderHelper;
72  
73      /**
74       * @plexus.requirement
75       */
76      private ScheduleDao scheduleDao;
77  
78      /**
79       * @plexus.requirement
80       */
81      private BuildDefinitionService buildDefinitionService;
82  
83      /**
84       * @plexus.configuration
85       */
86      private List<String> excludedPackagingTypes = new ArrayList<String>();
87  
88      private Project rootProject;
89  
90      /**
91       * @plexus.requirement
92       */
93      private ProjectGroupDao projectGroupDao;
94  
95      // ----------------------------------------------------------------------
96      // AbstractContinuumProjectBuilder Implementation
97      // ----------------------------------------------------------------------
98      public ContinuumProjectBuildingResult buildProjectsFromMetadata( URL url, String username, String password )
99          throws ContinuumProjectBuilderException
100     {
101         return buildProjectsFromMetadata( url, username, password, true, false );
102     }
103 
104     public ContinuumProjectBuildingResult buildProjectsFromMetadata( URL url, String username, String password,
105                                                                      boolean loadRecursiveProjects,
106                                                                      boolean checkoutInSingleDirectory )
107         throws ContinuumProjectBuilderException
108     {
109         try
110         {
111             return buildProjectsFromMetadata( url, username, password, loadRecursiveProjects,
112                                               buildDefinitionService.getDefaultMavenTwoBuildDefinitionTemplate(),
113                                               checkoutInSingleDirectory );
114         }
115         catch ( BuildDefinitionServiceException e )
116         {
117             throw new ContinuumProjectBuilderException( e.getMessage(), e );
118         }
119     }
120 
121     public ContinuumProjectBuildingResult buildProjectsFromMetadata( URL url, String username, String password,
122                                                                      boolean loadRecursiveProjects,
123                                                                      BuildDefinitionTemplate buildDefinitionTemplate,
124                                                                      boolean checkoutInSingleDirectory )
125         throws ContinuumProjectBuilderException
126     {
127         return buildProjectsFromMetadata( url, username, password, loadRecursiveProjects, buildDefinitionTemplate, checkoutInSingleDirectory, -1 );
128     }
129 
130     public ContinuumProjectBuildingResult buildProjectsFromMetadata( URL url, String username, String password,
131                                                                      boolean loadRecursiveProjects,
132                                                                      BuildDefinitionTemplate buildDefinitionTemplate,
133                                                                      boolean checkoutInSingleDirectory,
134                                                                      int projectGroupId )
135         throws ContinuumProjectBuilderException
136     {
137         // ----------------------------------------------------------------------
138         // We need to roll the project data into a file so that we can use it
139         // ----------------------------------------------------------------------
140 
141         ContinuumProjectBuildingResult result = new ContinuumProjectBuildingResult();
142 
143         try
144         {
145             ProjectGroup projectGroup = null;
146             if ( projectGroupId > 0 )
147             {
148                 projectGroup = projectGroupDao.getProjectGroupWithBuildDetailsByProjectGroupId( projectGroupId );
149             }
150 
151             readModules( url, result, projectGroup, username, password, null, loadRecursiveProjects, buildDefinitionTemplate,
152                          checkoutInSingleDirectory );
153         }
154         catch ( BuildDefinitionServiceException e )
155         {
156             throw new ContinuumProjectBuilderException( e.getMessage(), e );
157         }
158         catch ( ContinuumStoreException e )
159         {
160             throw new ContinuumProjectBuilderException( e.getMessage(), e );
161         }
162         return result;
163     }
164 
165     // ----------------------------------------------------------------------
166     //
167     // ----------------------------------------------------------------------
168 
169     private void readModules( URL url, ContinuumProjectBuildingResult result, ProjectGroup projectGroup, String username,
170                               String password, String scmUrl, boolean loadRecursiveProjects,
171                               BuildDefinitionTemplate buildDefinitionTemplate, boolean checkoutInSingleDirectory )
172         throws ContinuumProjectBuilderException, BuildDefinitionServiceException
173     {
174 
175         MavenProject mavenProject;
176 
177         File pomFile = null;
178 
179         try
180         {
181             pomFile = createMetadataFile( result, url, username, password );
182 
183             if ( result.hasErrors() )
184             {
185                 return;
186             }
187 
188             mavenProject = builderHelper.getMavenProject( result, pomFile );
189 
190             if ( result.hasErrors() )
191             {
192                 return;
193             }
194         }
195         finally
196         {
197             if ( pomFile != null && pomFile.exists() )
198             {
199                 pomFile.delete();
200             }
201         }
202         log.debug( "projectGroup " + projectGroup );
203 
204         if ( projectGroup == null )
205         {
206             projectGroup = buildProjectGroup( mavenProject, result );
207 
208             // project groups have the top lvl build definition which is the default build defintion for the sub
209             // projects
210             log.debug( "projectGroup != null" + ( projectGroup != null ) );
211             if ( projectGroup != null )
212             {
213                 List<BuildDefinition> buildDefinitions = getBuildDefinitions( buildDefinitionTemplate,
214                                                                               loadRecursiveProjects,
215                                                                               mavenProject.getBuild().getDefaultGoal() );
216                 for ( BuildDefinition buildDefinition : buildDefinitions )
217                 {
218                     buildDefinition = persistBuildDefinition( buildDefinition );
219 
220                     projectGroup.addBuildDefinition( buildDefinition );
221                 }
222             }
223         }
224         if ( result.getProjectGroups().isEmpty() )
225         {
226             result.addProjectGroup( projectGroup );
227         }
228 
229         if ( !excludedPackagingTypes.contains( mavenProject.getPackaging() ) )
230         {
231             Project continuumProject = new Project();
232 
233             /*
234             We are interested in having the scm username and password being passed into this method be taken into
235             account during project mapping so make sure we set it to the continuum project instance.
236              */
237             if ( username != null && StringUtils.isNotEmpty( username ) )
238             {
239                 continuumProject.setScmUsername( username );
240 
241                 if ( password != null && StringUtils.isNotEmpty( password ) )
242                 {
243                     continuumProject.setScmPassword( password );
244                 }
245             }
246 
247             continuumProject.setCheckedOutInSingleDirectory( checkoutInSingleDirectory );
248 
249             // New project
250             builderHelper.mapMavenProjectToContinuumProject( result, mavenProject, continuumProject, true );
251 
252             if ( result.hasErrors() )
253             {
254                 log.info(
255                     "Error adding project: Unknown error mapping project " + url + ": " + result.getErrorsAsString() );
256                 return;
257             }
258 
259             // Rewrite scmurl from the one found in added project due to a bug in scm url resolution
260             // for projects that doesn't have module name != artifactId
261             if ( StringUtils.isNotEmpty( scmUrl ) )
262             {
263                 continuumProject.setScmUrl( scmUrl );
264             }
265             else
266             {
267                 scmUrl = continuumProject.getScmUrl();
268             }
269 
270             if ( !"HEAD".equals( mavenProject.getScm().getTag() ) )
271             {
272                 continuumProject.setScmTag( mavenProject.getScm().getTag() );
273             }
274 
275             // CONTINUUM-2563
276             // Don't create if the project has a build definition template assigned to it already
277             if ( !loadRecursiveProjects && buildDefinitionTemplate.equals( getDefaultBuildDefinitionTemplate() ) )
278             {
279                 List<BuildDefinition> buildDefinitions = projectGroup.getBuildDefinitions();
280                 for ( BuildDefinition buildDefinition : buildDefinitions )
281                 {
282                     if ( buildDefinition.isDefaultForProject() )
283                     {
284                         // create a default build definition at the project level
285                         BuildDefinition projectBuildDef = buildDefinitionService.cloneBuildDefinition( buildDefinition );
286                         projectBuildDef.setDefaultForProject( true );
287 
288                         String arguments = projectBuildDef.getArguments().replace( "--non-recursive", "" );
289                         arguments = arguments.replace( "-N", "" );
290                         arguments = arguments.trim();
291 
292                         // add build definition only if it differs
293                         if ( !projectBuildDef.getArguments().equals( arguments ) )
294                         {
295                             log.info( "Adding default build definition for project '" + continuumProject.getName() +
296                                           "' without '--non-recursive' flag." );
297 
298                             projectBuildDef.setArguments( arguments );
299                             continuumProject.addBuildDefinition( projectBuildDef );
300                         }
301 
302                         break;
303                     }
304                 }
305             }
306 
307             result.addProject( continuumProject, MavenTwoBuildExecutor.ID );
308 
309             if ( checkoutInSingleDirectory && rootProject == null )
310             {
311                 rootProject = continuumProject;
312                 result.setRootProject( rootProject );
313             }
314         }
315 
316         List<String> modules = mavenProject.getModules();
317 
318         String prefix = url.toExternalForm();
319 
320         String suffix = "";
321 
322         int i = prefix.indexOf( '?' );
323 
324         int lastSlash;
325 
326         if ( i != -1 )
327         {
328             suffix = prefix.substring( i );
329 
330             lastSlash = prefix.lastIndexOf( "/", i );
331         }
332         else
333         {
334             lastSlash = prefix.lastIndexOf( "/" );
335         }
336 
337         prefix = prefix.substring( 0, lastSlash );
338         if ( loadRecursiveProjects )
339         {
340             for ( String module : modules )
341             {
342                 if ( StringUtils.isNotEmpty( module ) )
343                 {
344                     String urlString = prefix + "/" + module + POM_PART + suffix;
345 
346                     URL moduleUrl;
347 
348                     try
349                     {
350                         urlString = StringUtils.replace( urlString, '\\', '/' );
351                         moduleUrl = new URL( urlString );
352                     }
353                     catch ( MalformedURLException e )
354                     {
355                         log.debug( "Error adding project module: Malformed URL " + urlString, e );
356                         result.addError( ContinuumProjectBuildingResult.ERROR_MALFORMED_URL, urlString );
357                         continue;
358                     }
359 
360                     String moduleScmUrl = "";
361 
362                     String modulePath = StringUtils.replace( new String( module ), '\\', '/' );
363 
364                     // check if module is relative
365                     if ( modulePath.indexOf( "../" ) != -1 )
366                     {
367                         int depth = StringUtils.countMatches( StringUtils.substring( modulePath, 0,
368                                                                                      modulePath.lastIndexOf( '/' ) +
369                                                                                          1 ), "/" );
370 
371                         String baseUrl = "";
372                         for ( int j = 1; j <= depth; j++ )
373                         {
374                             scmUrl = StringUtils.chompLast( new String( scmUrl ), "/" );
375                             baseUrl = StringUtils.substring( scmUrl, 0, scmUrl.lastIndexOf( '/' ) );
376                         }
377                         moduleScmUrl = baseUrl + "/" + StringUtils.substring( modulePath, modulePath.lastIndexOf(
378                             "../" ) + 3 );
379                     }
380                     else
381                     {
382                         scmUrl = StringUtils.chompLast( scmUrl, "/" );
383                         moduleScmUrl = scmUrl + "/" + modulePath;
384                     }
385                     // we are in recursive loading mode
386                     readModules( moduleUrl, result, projectGroup, username, password, moduleScmUrl, true,
387                                  buildDefinitionTemplate, checkoutInSingleDirectory );
388                 }
389             }
390         }
391     }
392 
393     private BuildDefinition persistBuildDefinition( BuildDefinition buildDefinition )
394         throws BuildDefinitionServiceException
395     {
396         buildDefinition = buildDefinitionService.addBuildDefinition( buildDefinition );
397         if ( buildDefinition.getSchedule() == null )
398         {
399             try
400             {
401                 Schedule schedule = scheduleDao.getScheduleByName(
402                     ConfigurationService.DEFAULT_SCHEDULE_NAME );
403 
404                 buildDefinition.setSchedule( schedule );
405             }
406             catch ( ContinuumStoreException e )
407             {
408                 log.warn( "Can't get default schedule.", e );
409             }
410         }
411         return buildDefinition;
412     }
413 
414     private List<BuildDefinition> getBuildDefinitions( BuildDefinitionTemplate template, boolean loadRecursiveProjects,
415                                                        String defaultGoal )
416         throws ContinuumProjectBuilderException, BuildDefinitionServiceException
417     {
418         List<BuildDefinition> buildDefinitions = new ArrayList<BuildDefinition>();
419         boolean defaultSet = false;
420         for ( BuildDefinition buildDefinition : template.getBuildDefinitions() )
421         {
422             buildDefinition = buildDefinitionService.cloneBuildDefinition( buildDefinition );
423 
424             if ( !defaultSet && buildDefinition.isDefaultForProject() )
425             {
426                 defaultSet = true;
427 
428                 //CONTINUUM-1296
429                 if ( StringUtils.isNotEmpty( defaultGoal ) )
430                 {
431                     buildDefinition.setGoals( defaultGoal );
432                 }
433             }
434             else
435             {
436                 buildDefinition.setDefaultForProject( false );
437             }
438 
439             // due to CONTINUUM-1207 CONTINUUM-1436 user can do what they want with arguments
440             // we must remove if exists --non-recursive or -N
441             if ( !loadRecursiveProjects )
442             {
443                 if ( StringUtils.isEmpty( buildDefinition.getArguments() ) )
444                 {
445                     // strange for a mvn build 
446                     log.info( "build definition '" + buildDefinition.getId() + "' has empty args" );
447                 }
448                 else
449                 {
450                     String arguments = buildDefinition.getArguments().replace( "--non-recursive", "" );
451                     arguments = arguments.replace( "-N", "" );
452                     arguments = arguments.trim();
453                     buildDefinition.setArguments( arguments );
454                 }
455             }
456             buildDefinition.setTemplate( false );
457             buildDefinitions.add( buildDefinition );
458         }
459         return buildDefinitions;
460 
461     }
462 
463     private ProjectGroup buildProjectGroup( MavenProject mavenProject, ContinuumProjectBuildingResult result )
464     {
465         ProjectGroup projectGroup = new ProjectGroup();
466 
467         // ----------------------------------------------------------------------
468         // Group id
469         // ----------------------------------------------------------------------
470 
471         if ( StringUtils.isEmpty( mavenProject.getGroupId() ) )
472         {
473             result.addError( ContinuumProjectBuildingResult.ERROR_MISSING_GROUPID );
474 
475             return null;
476         }
477 
478         projectGroup.setGroupId( mavenProject.getGroupId() );
479 
480         // ----------------------------------------------------------------------
481         // Name
482         // ----------------------------------------------------------------------
483 
484         String name = mavenProject.getName();
485 
486         if ( StringUtils.isEmpty( name ) )
487         {
488             name = mavenProject.getGroupId();
489         }
490 
491         projectGroup.setName( name );
492 
493         // ----------------------------------------------------------------------
494         // Description
495         // ----------------------------------------------------------------------
496 
497         projectGroup.setDescription( mavenProject.getDescription() );
498 
499         // ----------------------------------------------------------------------
500         // Local Repository
501         // ----------------------------------------------------------------------
502 
503         try
504         {
505             LocalRepository repository = localRepositoryDao.getLocalRepositoryByName( "DEFAULT" );
506 
507             projectGroup.setLocalRepository( repository );
508         }
509         catch ( ContinuumStoreException e )
510         {
511             log.warn( "Can't get default repository.", e );
512         }
513 
514         return projectGroup;
515     }
516 
517 
518     public BuildDefinitionTemplate getDefaultBuildDefinitionTemplate()
519         throws ContinuumProjectBuilderException
520     {
521         try
522         {
523             return buildDefinitionService.getDefaultMavenTwoBuildDefinitionTemplate();
524         }
525         catch ( BuildDefinitionServiceException e )
526         {
527             throw new ContinuumProjectBuilderException( e.getMessage(), e );
528         }
529     }
530 
531 }