View Javadoc

1   package org.apache.continuum.web.action;
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.commons.io.IOUtils;
23  import org.apache.commons.lang.StringUtils;
24  import org.apache.commons.lang.time.DateUtils;
25  import org.apache.maven.continuum.model.project.BuildResult;
26  import org.apache.maven.continuum.model.project.Project;
27  import org.apache.maven.continuum.model.project.ProjectGroup;
28  import org.apache.maven.continuum.project.ContinuumProjectState;
29  import org.apache.maven.continuum.web.action.ContinuumActionSupport;
30  import org.apache.maven.continuum.web.exception.AuthorizationRequiredException;
31  import org.apache.maven.continuum.web.model.ProjectBuildsSummary;
32  
33  import java.io.ByteArrayInputStream;
34  import java.io.IOException;
35  import java.io.InputStream;
36  import java.io.StringReader;
37  import java.text.ParseException;
38  import java.util.ArrayList;
39  import java.util.Collections;
40  import java.util.Date;
41  import java.util.LinkedHashMap;
42  import java.util.List;
43  import java.util.Map;
44  
45  /**
46   * @plexus.component role="com.opensymphony.xwork2.Action" role-hint="projectBuildsReport"
47   */
48  public class ViewBuildsReportAction
49      extends ContinuumActionSupport
50  {
51      private int buildStatus;
52  
53      private String triggeredBy = "";
54  
55      private String startDate = "";
56  
57      private String endDate = "";
58  
59      private int projectGroupId;
60  
61      private int rowCount = 30;
62  
63      private int page = 1;
64  
65      private int numPages;
66  
67      private Map<Integer, String> buildStatuses;
68  
69      private Map<Integer, String> projectGroups;
70  
71      private List<ProjectBuildsSummary> projectBuilds;
72  
73      private InputStream inputStream;
74  
75      public static final String SEND_FILE = "send-file";
76  
77      private static final String[] datePatterns =
78          new String[]{"MM/dd/yy", "MM/dd/yyyy", "MMMMM/dd/yyyy", "MMMMM/dd/yy", "dd MMMMM yyyy", "dd/MM/yy",
79              "dd/MM/yyyy", "yyyy/MM/dd", "yyyy-MM-dd", "yyyy-dd-MM", "MM-dd-yyyy", "MM-dd-yy"};
80  
81      public void prepare()
82          throws Exception
83      {
84          super.prepare();
85  
86          buildStatuses = new LinkedHashMap<Integer, String>();
87          buildStatuses.put( 0, "ALL" );
88          buildStatuses.put( ContinuumProjectState.OK, "Ok" );
89          buildStatuses.put( ContinuumProjectState.FAILED, "Failed" );
90          buildStatuses.put( ContinuumProjectState.ERROR, "Error" );
91  
92          projectGroups = new LinkedHashMap<Integer, String>();
93          projectGroups.put( 0, "ALL" );
94  
95          List<ProjectGroup> groups = getContinuum().getAllProjectGroups();
96          if ( groups != null )
97          {
98              for ( ProjectGroup group : groups )
99              {
100                 projectGroups.put( group.getId(), group.getName() );
101             }
102         }
103     }
104 
105     public String init()
106     {
107         try
108         {
109             checkViewReportsAuthorization();
110         }
111         catch ( AuthorizationRequiredException authzE )
112         {
113             addActionError( authzE.getMessage() );
114             return REQUIRES_AUTHORIZATION;
115         }
116 
117         // action class was called from the Menu; do not generate report first
118         return SUCCESS;
119     }
120 
121     public String execute()
122     {
123         try
124         {
125             checkViewReportsAuthorization();
126         }
127         catch ( AuthorizationRequiredException authzE )
128         {
129             addActionError( authzE.getMessage() );
130             return REQUIRES_AUTHORIZATION;
131         }
132 
133         Date fromDate = null;
134         Date toDate = null;
135 
136         try
137         {
138             fromDate = getStartDateInDateFormat();
139             toDate = getEndDateInDateFormat();
140         }
141         catch ( ParseException e )
142         {
143             addActionError( "Error parsing date(s): " + e.getMessage() );
144             return ERROR;
145         }
146 
147         if ( fromDate != null && toDate != null && fromDate.after( toDate ) )
148         {
149             addFieldError( "startDate", "Start Date must be earlier than the End Date" );
150             return INPUT;
151         }
152 
153         if ( rowCount < 10 )
154         {
155             // TODO: move to validation framework
156             addFieldError( "rowCount", "Row count should be at least 10." );
157             return INPUT;
158         }
159 
160         List<BuildResult> buildResults = getContinuum().getBuildResultsInRange( projectGroupId, fromDate, toDate,
161                                                                                 buildStatus, triggeredBy );
162         projectBuilds = Collections.emptyList();
163 
164         if ( buildResults != null && !buildResults.isEmpty() )
165         {
166             projectBuilds = mapBuildResultsToProjectBuildsSummaries( buildResults );
167 
168             int extraPage = ( projectBuilds.size() % rowCount ) != 0 ? 1 : 0;
169             numPages = ( projectBuilds.size() / rowCount ) + extraPage;
170 
171             if ( page > numPages )
172             {
173                 addActionError(
174                     "Error encountered while generating project builds report :: The requested page exceeds the total number of pages." );
175                 return ERROR;
176             }
177 
178             int start = rowCount * ( page - 1 );
179             int end = ( start + rowCount );
180 
181             if ( end > projectBuilds.size() )
182             {
183                 end = projectBuilds.size();
184             }
185 
186             projectBuilds = projectBuilds.subList( start, end );
187         }
188 
189         return SUCCESS;
190     }
191 
192     /*
193     * Export Builds Report to .csv
194     */
195     public String downloadBuildsReport()
196     {
197         try
198         {
199             checkViewReportsAuthorization();
200         }
201         catch ( AuthorizationRequiredException authzE )
202         {
203             addActionError( authzE.getMessage() );
204             return REQUIRES_AUTHORIZATION;
205         }
206 
207         Date fromDate = null;
208         Date toDate = null;
209 
210         try
211         {
212             fromDate = getStartDateInDateFormat();
213             toDate = getEndDateInDateFormat();
214         }
215         catch ( ParseException e )
216         {
217             addActionError( "Error parsing date(s): " + e.getMessage() );
218             return ERROR;
219         }
220 
221         if ( fromDate != null && toDate != null && fromDate.after( toDate ) )
222         {
223             addFieldError( "startDate", "Start Date must be earlier than the End Date" );
224             return INPUT;
225         }
226 
227         List<BuildResult> buildResults = getContinuum().getBuildResultsInRange( projectGroupId, fromDate, toDate,
228                                                                                 buildStatus, triggeredBy );
229         List<ProjectBuildsSummary> builds = Collections.emptyList();
230 
231         StringBuffer input = new StringBuffer( "Project Group,Project Name,Build Date,Triggered By,Build Status\n" );
232 
233         if ( buildResults != null && !buildResults.isEmpty() )
234         {
235             builds = mapBuildResultsToProjectBuildsSummaries( buildResults );
236 
237             for ( ProjectBuildsSummary build : builds )
238             {
239                 input.append( build.getProjectGroupName() ).append( "," );
240                 input.append( build.getProjectName() ).append( "," );
241 
242                 input.append( new Date( build.getBuildDate() ) ).append( "," );
243 
244                 input.append( build.getBuildTriggeredBy() ).append( "," );
245 
246                 String status;
247                 switch ( build.getBuildState() )
248                 {
249                     case 2:
250                         status = "Ok";
251                         break;
252                     case 3:
253                         status = "Failed";
254                         break;
255                     case 4:
256                         status = "Error";
257                         break;
258                     case 6:
259                         status = "Building";
260                         break;
261                     case 7:
262                         status = "Checking Out";
263                         break;
264                     case 8:
265                         status = "Updating";
266                         break;
267                     default:
268                         status = "";
269                 }
270                 input.append( status );
271                 input.append( "\n" );
272             }
273         }
274 
275         StringReader reader = new StringReader( input.toString() );
276 
277         try
278         {
279             inputStream = new ByteArrayInputStream( IOUtils.toByteArray( reader ) );
280         }
281         catch ( IOException e )
282         {
283             addActionError( "Error occurred while generating CSV file." );
284             return ERROR;
285         }
286 
287         return SEND_FILE;
288     }
289 
290     private List<ProjectBuildsSummary> mapBuildResultsToProjectBuildsSummaries( List<BuildResult> buildResults )
291     {
292         List<ProjectBuildsSummary> buildsSummary = new ArrayList<ProjectBuildsSummary>();
293 
294         for ( BuildResult buildResult : buildResults )
295         {
296             Project project = buildResult.getProject();
297 
298             // check if user is authorised to view build result
299             if ( !isAuthorized( project.getProjectGroup().getName() ) )
300             {
301                 continue;
302             }
303 
304             ProjectBuildsSummary summary = new ProjectBuildsSummary();
305             summary.setProjectGroupName( project.getProjectGroup().getName() );
306             summary.setProjectName( project.getName() );
307             summary.setBuildDate( buildResult.getStartTime() );
308             summary.setBuildState( buildResult.getState() );
309             summary.setBuildTriggeredBy( buildResult.getUsername() );
310 
311             buildsSummary.add( summary );
312         }
313 
314         return buildsSummary;
315     }
316 
317     private Date getStartDateInDateFormat()
318         throws ParseException
319     {
320         Date date = null;
321 
322         if ( !StringUtils.isEmpty( startDate ) )
323         {
324             date = DateUtils.parseDate( startDate, datePatterns );
325         }
326 
327         return date;
328     }
329 
330     private Date getEndDateInDateFormat()
331         throws ParseException
332     {
333         Date date = null;
334 
335         if ( !StringUtils.isEmpty( endDate ) )
336         {
337             date = DateUtils.parseDate( endDate, datePatterns );
338         }
339 
340         return date;
341     }
342 
343     public int getBuildStatus()
344     {
345         return this.buildStatus;
346     }
347 
348     public void setBuildStatus( int buildStatus )
349     {
350         this.buildStatus = buildStatus;
351     }
352 
353     public int getProjectGroupId()
354     {
355         return this.projectGroupId;
356     }
357 
358     public void setProjectGroupId( int projectGroupId )
359     {
360         this.projectGroupId = projectGroupId;
361     }
362 
363     public String getTriggeredBy()
364     {
365         return this.triggeredBy;
366     }
367 
368     public void setTriggeredBy( String triggeredBy )
369     {
370         this.triggeredBy = triggeredBy;
371     }
372 
373     public String getStartDate()
374     {
375         return this.startDate;
376     }
377 
378     public void setStartDate( String startDate )
379     {
380         this.startDate = startDate;
381     }
382 
383     public String getEndDate()
384     {
385         return this.endDate;
386     }
387 
388     public void setEndDate( String endDate )
389     {
390         this.endDate = endDate;
391     }
392 
393     public int getRowCount()
394     {
395         return rowCount;
396     }
397 
398     public void setRowCount( int rowCount )
399     {
400         this.rowCount = rowCount;
401     }
402 
403     public Map<Integer, String> getBuildStatuses()
404     {
405         return buildStatuses;
406     }
407 
408     public Map<Integer, String> getProjectGroups()
409     {
410         return projectGroups;
411     }
412 
413     public List<ProjectBuildsSummary> getProjectBuilds()
414     {
415         return projectBuilds;
416     }
417 
418     public int getPage()
419     {
420         return page;
421     }
422 
423     public void setPage( int page )
424     {
425         this.page = page;
426     }
427 
428     public int getNumPages()
429     {
430         return numPages;
431     }
432 
433     public InputStream getInputStream()
434     {
435         return inputStream;
436     }
437 
438     private boolean isAuthorized( String projectGroupName )
439     {
440         try
441         {
442             checkViewProjectGroupAuthorization( projectGroupName );
443             return true;
444         }
445         catch ( AuthorizationRequiredException authzE )
446         {
447             return false;
448         }
449     }
450 }