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