View Javadoc

1   package org.apache.continuum.builder.distributed.executor;
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.builder.utils.ContinuumBuildConstant;
23  import org.apache.continuum.dao.BuildDefinitionDao;
24  import org.apache.continuum.dao.BuildResultDao;
25  import org.apache.continuum.dao.ProjectDao;
26  import org.apache.continuum.dao.ProjectScmRootDao;
27  import org.apache.continuum.distributed.transport.slave.SlaveBuildAgentTransportClient;
28  import org.apache.continuum.model.project.ProjectScmRoot;
29  import org.apache.continuum.model.repository.LocalRepository;
30  import org.apache.continuum.taskqueue.PrepareBuildProjectsTask;
31  import org.apache.continuum.utils.ContinuumUtils;
32  import org.apache.continuum.utils.ProjectSorter;
33  import org.apache.continuum.utils.build.BuildTrigger;
34  import org.apache.maven.continuum.ContinuumException;
35  import org.apache.maven.continuum.configuration.ConfigurationService;
36  import org.apache.maven.continuum.model.project.BuildDefinition;
37  import org.apache.maven.continuum.model.project.BuildResult;
38  import org.apache.maven.continuum.model.project.Project;
39  import org.apache.maven.continuum.model.scm.ChangeFile;
40  import org.apache.maven.continuum.model.scm.ChangeSet;
41  import org.apache.maven.continuum.model.scm.ScmResult;
42  import org.apache.maven.continuum.project.ContinuumProjectState;
43  import org.apache.maven.continuum.store.ContinuumStoreException;
44  import org.codehaus.plexus.taskqueue.Task;
45  import org.codehaus.plexus.taskqueue.execution.TaskExecutionException;
46  import org.codehaus.plexus.util.StringUtils;
47  import org.slf4j.Logger;
48  import org.slf4j.LoggerFactory;
49  
50  import java.net.MalformedURLException;
51  import java.net.URL;
52  import java.util.ArrayList;
53  import java.util.Date;
54  import java.util.HashMap;
55  import java.util.List;
56  import java.util.Map;
57  
58  public class DistributedBuildProjectTaskExecutor
59      implements DistributedBuildTaskExecutor
60  {
61      private static final Logger log = LoggerFactory.getLogger( DistributedBuildProjectTaskExecutor.class );
62  
63      private String buildAgentUrl;
64  
65      private long startTime;
66  
67      private long endTime;
68  
69      /**
70       * @plexus.requirement
71       */
72      private ProjectDao projectDao;
73  
74      /**
75       * @plexus.requirement
76       */
77      private ProjectScmRootDao projectScmRootDao;
78  
79      /**
80       * @plexus.requirement
81       */
82      private BuildDefinitionDao buildDefinitionDao;
83  
84      /**
85       * @plexus.requirement
86       */
87      private BuildResultDao buildResultDao;
88  
89      /**
90       * @plexus.requirement
91       */
92      private ConfigurationService configurationService;
93  
94      public void setBuildAgentUrl( String buildAgentUrl )
95      {
96          this.buildAgentUrl = buildAgentUrl;
97      }
98  
99      public String getBuildAgentUrl()
100     {
101         return buildAgentUrl;
102     }
103 
104     public void executeTask( Task task )
105         throws TaskExecutionException
106     {
107         PrepareBuildProjectsTask prepareBuildTask = (PrepareBuildProjectsTask) task;
108 
109         try
110         {
111             SlaveBuildAgentTransportClient client = new SlaveBuildAgentTransportClient( new URL( buildAgentUrl ), "",
112                                                                                         configurationService.getSharedSecretPassword() );
113 
114             log.info( "initializing buildContext for projectGroupId=" + prepareBuildTask.getProjectGroupId() );
115             List<Map<String, Object>> buildContext = initializeBuildContext(
116                 prepareBuildTask.getProjectsBuildDefinitionsMap(), prepareBuildTask.getBuildTrigger(),
117                 prepareBuildTask.getScmRootAddress(), prepareBuildTask.getProjectScmRootId() );
118 
119             startTime = System.currentTimeMillis();
120             client.buildProjects( buildContext );
121             endTime = System.currentTimeMillis();
122         }
123         catch ( MalformedURLException e )
124         {
125             log.error( "Invalid URL " + buildAgentUrl + ", not building" );
126             throw new TaskExecutionException( "Invalid URL " + buildAgentUrl, e );
127         }
128         catch ( Exception e )
129         {
130             log.error( "Error occurred while building task", e );
131             endTime = System.currentTimeMillis();
132             createResult( prepareBuildTask, ContinuumUtils.throwableToString( e ) );
133         }
134     }
135 
136     private List<Map<String, Object>> initializeBuildContext( Map<Integer, Integer> projectsAndBuildDefinitions,
137                                                               BuildTrigger buildTrigger, String scmRootAddress,
138                                                               int scmRootId )
139         throws ContinuumException
140     {
141         List<Map<String, Object>> buildContext = new ArrayList<Map<String, Object>>();
142 
143         try
144         {
145             ProjectScmRoot scmRoot = projectScmRootDao.getProjectScmRoot( scmRootId );
146 
147             List<Project> projects = projectDao.getProjectsWithDependenciesByGroupId(
148                 scmRoot.getProjectGroup().getId() );
149             List<Project> sortedProjects = ProjectSorter.getSortedProjects( projects, null );
150 
151             for ( Project project : sortedProjects )
152             {
153                 if ( !projectsAndBuildDefinitions.containsKey( project.getId() ) )
154                 {
155                     continue;
156                 }
157 
158                 int buildDefinitionId = projectsAndBuildDefinitions.get( project.getId() );
159                 BuildDefinition buildDef = buildDefinitionDao.getBuildDefinition( buildDefinitionId );
160                 BuildResult buildResult = buildResultDao.getLatestBuildResultForProject( project.getId() );
161 
162                 Map<String, Object> context = new HashMap<String, Object>();
163 
164                 context.put( ContinuumBuildConstant.KEY_PROJECT_GROUP_ID, project.getProjectGroup().getId() );
165                 context.put( ContinuumBuildConstant.KEY_PROJECT_GROUP_NAME, project.getProjectGroup().getName() );
166                 context.put( ContinuumBuildConstant.KEY_SCM_ROOT_ID, scmRootId );
167                 context.put( ContinuumBuildConstant.KEY_SCM_ROOT_ADDRESS, scmRootAddress );
168                 context.put( ContinuumBuildConstant.KEY_PROJECT_ID, project.getId() );
169                 context.put( ContinuumBuildConstant.KEY_PROJECT_NAME, project.getName() );
170                 context.put( ContinuumBuildConstant.KEY_PROJECT_VERSION, project.getVersion() );
171                 context.put( ContinuumBuildConstant.KEY_EXECUTOR_ID, project.getExecutorId() );
172                 context.put( ContinuumBuildConstant.KEY_PROJECT_BUILD_NUMBER, project.getBuildNumber() );
173                 context.put( ContinuumBuildConstant.KEY_SCM_URL, project.getScmUrl() );
174                 context.put( ContinuumBuildConstant.KEY_PROJECT_STATE, project.getState() );
175                 if ( buildResult != null )
176                 {
177                     context.put( ContinuumBuildConstant.KEY_LATEST_UPDATE_DATE, new Date(
178                         buildResult.getLastChangedDate() ) );
179                 }
180 
181                 LocalRepository localRepo = project.getProjectGroup().getLocalRepository();
182 
183                 if ( localRepo != null )
184                 {
185                     // CONTINUUM-2391
186                     context.put( ContinuumBuildConstant.KEY_LOCAL_REPOSITORY, localRepo.getName() );
187                 }
188                 else
189                 {
190                     context.put( ContinuumBuildConstant.KEY_LOCAL_REPOSITORY, "" );
191                 }
192 
193                 if ( project.getScmUsername() == null )
194                 {
195                     context.put( ContinuumBuildConstant.KEY_SCM_USERNAME, "" );
196                 }
197                 else
198                 {
199                     context.put( ContinuumBuildConstant.KEY_SCM_USERNAME, project.getScmUsername() );
200                 }
201 
202                 if ( project.getScmPassword() == null )
203                 {
204                     context.put( ContinuumBuildConstant.KEY_SCM_PASSWORD, "" );
205                 }
206                 else
207                 {
208                     context.put( ContinuumBuildConstant.KEY_SCM_PASSWORD, project.getScmPassword() );
209                 }
210 
211                 if ( project.getScmTag() != null )
212                 {
213                     context.put( ContinuumBuildConstant.KEY_SCM_TAG, project.getScmTag() );
214                 }
215                 else
216                 {
217                     context.put( ContinuumBuildConstant.KEY_SCM_TAG, "" );
218                 }
219 
220                 context.put( ContinuumBuildConstant.KEY_BUILD_DEFINITION_ID, buildDefinitionId );
221                 String buildDefinitionLabel = buildDef.getDescription();
222                 if ( StringUtils.isEmpty( buildDefinitionLabel ) )
223                 {
224                     buildDefinitionLabel = buildDef.getGoals();
225                 }
226                 context.put( ContinuumBuildConstant.KEY_BUILD_DEFINITION_LABEL, buildDefinitionLabel );
227 
228                 context.put( ContinuumBuildConstant.KEY_BUILD_FILE, buildDef.getBuildFile() );
229 
230                 if ( buildDef.getGoals() == null )
231                 {
232                     context.put( ContinuumBuildConstant.KEY_GOALS, "" );
233                 }
234                 else
235                 {
236                     context.put( ContinuumBuildConstant.KEY_GOALS, buildDef.getGoals() );
237                 }
238 
239                 if ( buildDef.getArguments() == null )
240                 {
241                     context.put( ContinuumBuildConstant.KEY_ARGUMENTS, "" );
242                 }
243                 else
244                 {
245                     context.put( ContinuumBuildConstant.KEY_ARGUMENTS, buildDef.getArguments() );
246                 }
247                 context.put( ContinuumBuildConstant.KEY_TRIGGER, buildTrigger.getTrigger() );
248 
249                 if ( buildTrigger.getTrigger() == ContinuumProjectState.TRIGGER_FORCED )
250                 {
251                     context.put( ContinuumBuildConstant.KEY_USERNAME, buildTrigger.getTriggeredBy() );
252                 }
253                 else
254                 {
255                     context.put( ContinuumBuildConstant.KEY_USERNAME, buildDef.getSchedule().getName() );
256                 }
257 
258                 context.put( ContinuumBuildConstant.KEY_BUILD_FRESH, buildDef.isBuildFresh() );
259                 context.put( ContinuumBuildConstant.KEY_ALWAYS_BUILD, buildDef.isAlwaysBuild() );
260                 context.put( ContinuumBuildConstant.KEY_OLD_SCM_CHANGES, getOldScmChanges( project.getId(),
261                                                                                            buildDefinitionId ) );
262                 context.put( ContinuumBuildConstant.KEY_BUILD_AGENT_URL, buildAgentUrl );
263                 context.put( ContinuumBuildConstant.KEY_MAX_JOB_EXEC_TIME,
264                              buildDef.getSchedule().getMaxJobExecutionTime() );
265 
266                 buildContext.add( context );
267             }
268 
269             return buildContext;
270         }
271         catch ( ContinuumStoreException e )
272         {
273             throw new ContinuumException( "Error while initializing build context", e );
274         }
275     }
276 
277     private void createResult( PrepareBuildProjectsTask task, String error )
278         throws TaskExecutionException
279     {
280         try
281         {
282             ProjectScmRoot scmRoot = projectScmRootDao.getProjectScmRootByProjectGroupAndScmRootAddress(
283                 task.getProjectGroupId(), task.getScmRootAddress() );
284 
285             if ( scmRoot.getState() == ContinuumProjectState.UPDATING )
286             {
287                 scmRoot.setState( ContinuumProjectState.ERROR );
288                 scmRoot.setError( error );
289                 projectScmRootDao.updateProjectScmRoot( scmRoot );
290             }
291             else
292             {
293                 Map<Integer, Integer> map = task.getProjectsBuildDefinitionsMap();
294                 for ( Integer projectId : map.keySet() )
295                 {
296                     int buildDefinitionId = map.get( projectId );
297                     Project project = projectDao.getProject( projectId );
298                     BuildDefinition buildDef = buildDefinitionDao.getBuildDefinition( buildDefinitionId );
299                     BuildResult latestBuildResult = buildResultDao.
300                         getLatestBuildResultForBuildDefinition( projectId, buildDefinitionId );
301                     if ( latestBuildResult == null ||
302                         ( latestBuildResult.getStartTime() >= startTime && latestBuildResult.getEndTime() > 0 &&
303                             latestBuildResult.getEndTime() < endTime ) || latestBuildResult.getStartTime() < startTime )
304                     {
305                         BuildResult buildResult = new BuildResult();
306                         buildResult.setBuildDefinition( buildDef );
307                         buildResult.setError( error );
308                         buildResult.setState( ContinuumProjectState.ERROR );
309                         buildResult.setTrigger( task.getBuildTrigger().getTrigger() );
310                         buildResult.setUsername( task.getBuildTrigger().getTriggeredBy() );
311                         buildResult.setStartTime( startTime );
312                         buildResult.setEndTime( endTime );
313 
314                         buildResultDao.addBuildResult( project, buildResult );
315                     }
316                 }
317 
318             }
319         }
320         catch ( ContinuumStoreException e )
321         {
322             throw new TaskExecutionException( "Error while creating result", e );
323         }
324     }
325 
326     private List<Map<String, Object>> getOldScmChanges( int projectId, int buildDefinitionId )
327         throws ContinuumStoreException
328     {
329         List<Map<String, Object>> scmChanges = new ArrayList<Map<String, Object>>();
330 
331         BuildResult oldBuildResult = buildResultDao.getLatestBuildResultForBuildDefinition( projectId,
332                                                                                             buildDefinitionId );
333 
334         if ( oldBuildResult != null )
335         {
336             ScmResult scmResult = getOldScmResults( projectId, oldBuildResult.getBuildNumber(),
337                                                     oldBuildResult.getEndTime() );
338 
339             scmChanges = getScmChanges( scmResult );
340         }
341 
342         return scmChanges;
343     }
344 
345     private List<Map<String, Object>> getScmChanges( ScmResult scmResult )
346     {
347         List<Map<String, Object>> scmChanges = new ArrayList<Map<String, Object>>();
348 
349         if ( scmResult != null && scmResult.getChanges() != null )
350         {
351             for ( Object obj : scmResult.getChanges() )
352             {
353                 ChangeSet changeSet = (ChangeSet) obj;
354 
355                 Map<String, Object> map = new HashMap<String, Object>();
356                 if ( StringUtils.isNotEmpty( changeSet.getAuthor() ) )
357                 {
358                     map.put( ContinuumBuildConstant.KEY_CHANGESET_AUTHOR, changeSet.getAuthor() );
359                 }
360                 else
361                 {
362                     map.put( ContinuumBuildConstant.KEY_CHANGESET_AUTHOR, "" );
363                 }
364                 if ( StringUtils.isNotEmpty( changeSet.getComment() ) )
365                 {
366                     map.put( ContinuumBuildConstant.KEY_CHANGESET_COMMENT, changeSet.getComment() );
367                 }
368                 else
369                 {
370                     map.put( ContinuumBuildConstant.KEY_CHANGESET_COMMENT, "" );
371                 }
372                 if ( changeSet.getDateAsDate() != null )
373                 {
374                     map.put( ContinuumBuildConstant.KEY_CHANGESET_DATE, changeSet.getDateAsDate() );
375                 }
376                 map.put( ContinuumBuildConstant.KEY_CHANGESET_FILES, getScmChangeFiles( changeSet.getFiles() ) );
377                 scmChanges.add( map );
378             }
379         }
380 
381         return scmChanges;
382     }
383 
384     private List<Map<String, String>> getScmChangeFiles( List<ChangeFile> files )
385     {
386         List<Map<String, String>> scmChangeFiles = new ArrayList<Map<String, String>>();
387 
388         if ( files != null )
389         {
390             for ( ChangeFile changeFile : files )
391             {
392                 Map<String, String> map = new HashMap<String, String>();
393 
394                 if ( StringUtils.isNotEmpty( changeFile.getName() ) )
395                 {
396                     map.put( ContinuumBuildConstant.KEY_CHANGEFILE_NAME, changeFile.getName() );
397                 }
398                 else
399                 {
400                     map.put( ContinuumBuildConstant.KEY_CHANGEFILE_NAME, "" );
401                 }
402                 if ( StringUtils.isNotEmpty( changeFile.getRevision() ) )
403                 {
404                     map.put( ContinuumBuildConstant.KEY_CHANGEFILE_REVISION, changeFile.getRevision() );
405                 }
406                 else
407                 {
408                     map.put( ContinuumBuildConstant.KEY_CHANGEFILE_REVISION, "" );
409                 }
410                 if ( StringUtils.isNotEmpty( changeFile.getStatus() ) )
411                 {
412                     map.put( ContinuumBuildConstant.KEY_CHANGEFILE_STATUS, changeFile.getStatus() );
413                 }
414                 else
415                 {
416                     map.put( ContinuumBuildConstant.KEY_CHANGEFILE_STATUS, "" );
417                 }
418                 scmChangeFiles.add( map );
419             }
420         }
421         return scmChangeFiles;
422     }
423 
424     private ScmResult getOldScmResults( int projectId, long startId, long fromDate )
425         throws ContinuumStoreException
426     {
427         List<BuildResult> results = buildResultDao.getBuildResultsForProjectFromId( projectId, startId );
428 
429         ScmResult res = new ScmResult();
430 
431         if ( results != null && results.size() > 0 )
432         {
433             for ( BuildResult result : results )
434             {
435                 ScmResult scmResult = result.getScmResult();
436 
437                 if ( scmResult != null )
438                 {
439                     List<ChangeSet> changes = scmResult.getChanges();
440 
441                     if ( changes != null )
442                     {
443                         for ( ChangeSet changeSet : changes )
444                         {
445                             if ( changeSet.getDate() < fromDate )
446                             {
447                                 continue;
448                             }
449                             if ( !res.getChanges().contains( changeSet ) )
450                             {
451                                 res.addChange( changeSet );
452                             }
453                         }
454                     }
455                 }
456             }
457         }
458 
459         return res;
460     }
461 }