1 package org.apache.maven.archiva.web.action.reports;
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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
22 import java.io.ByteArrayInputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.StringReader;
26 import java.text.ParseException;
27 import java.util.ArrayList;
28 import java.util.Calendar;
29 import java.util.Collection;
30 import java.util.Date;
31 import java.util.List;
33 import java.util.TreeMap;
34 import javax.servlet.http.HttpServletRequest;
36 import com.opensymphony.xwork2.Preparable;
37 import org.apache.archiva.metadata.repository.stats.RepositoryStatistics;
38 import org.apache.archiva.metadata.repository.stats.RepositoryStatisticsManager;
39 import org.apache.commons.io.IOUtils;
40 import org.apache.commons.lang.StringUtils;
41 import org.apache.commons.lang.time.DateUtils;
42 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
43 import org.apache.maven.archiva.database.ArchivaDAO;
44 import org.apache.maven.archiva.database.Constraint;
45 import org.apache.maven.archiva.database.constraints.RangeConstraint;
46 import org.apache.maven.archiva.database.constraints.RepositoryProblemByGroupIdConstraint;
47 import org.apache.maven.archiva.database.constraints.RepositoryProblemByRepositoryIdConstraint;
48 import org.apache.maven.archiva.database.constraints.RepositoryProblemConstraint;
49 import org.apache.maven.archiva.database.constraints.UniqueFieldConstraint;
50 import org.apache.maven.archiva.model.RepositoryProblem;
51 import org.apache.maven.archiva.model.RepositoryProblemReport;
52 import org.apache.maven.archiva.reporting.ArchivaReportException;
53 import org.apache.maven.archiva.security.ArchivaRoleConstants;
54 import org.apache.maven.archiva.web.action.PlexusActionSupport;
55 import org.apache.struts2.interceptor.ServletRequestAware;
56 import org.codehaus.plexus.redback.rbac.Resource;
57 import org.codehaus.redback.integration.interceptor.SecureAction;
58 import org.codehaus.redback.integration.interceptor.SecureActionBundle;
59 import org.codehaus.redback.integration.interceptor.SecureActionException;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
64 * @plexus.component role="com.opensymphony.xwork2.Action" role-hint="generateReport" instantiation-strategy="per-lookup"
66 public class GenerateReportAction
67 extends PlexusActionSupport
68 implements SecureAction, ServletRequestAware, Preparable
70 public static final String ALL_REPOSITORIES = "All Repositories";
72 public static final String BLANK = "blank";
74 private static final String[] datePatterns =
75 new String[]{"MM/dd/yy", "MM/dd/yyyy", "MMMMM/dd/yyyy", "MMMMM/dd/yy", "dd MMMMM yyyy", "dd/MM/yy",
76 "dd/MM/yyyy", "yyyy/MM/dd", "yyyy-MM-dd", "yyyy-dd-MM", "MM-dd-yyyy", "MM-dd-yy"};
78 public static final String SEND_FILE = "send-file";
80 private Logger log = LoggerFactory.getLogger( GenerateReportAction.class );
83 * @plexus.requirement role-hint="jdo"
85 private ArchivaDAO dao;
90 private ArchivaConfiguration archivaConfiguration;
95 private RepositoryStatisticsManager repositoryStatisticsManager;
97 private HttpServletRequest request;
99 private String groupId;
101 private String repositoryId;
103 private int page = 1;
105 private int rowCount = 100;
107 private List<String> selectedRepositories = new ArrayList<String>();
109 private String startDate;
111 private String endDate;
113 private int numPages;
115 private Collection<String> repositoryIds;
117 private Map<String, List<RepositoryProblemReport>> repositoriesMap =
118 new TreeMap<String, List<RepositoryProblemReport>>();
120 private List<String> availableRepositories;
122 private List<RepositoryStatistics> repositoryStatistics = new ArrayList<RepositoryStatistics>();
124 private InputStream inputStream;
126 private boolean lastPage;
128 @SuppressWarnings("unchecked")
129 public void prepare()
131 repositoryIds = new ArrayList<String>();
132 repositoryIds.add( ALL_REPOSITORIES ); // comes first to be first in the list
133 repositoryIds.addAll( (List<String>) dao.query(
134 new UniqueFieldConstraint( RepositoryProblem.class.getName(), "repositoryId" ) ) );
136 availableRepositories = new ArrayList<String>();
138 // remove selected repositories in the option for the statistics report
139 availableRepositories.addAll( archivaConfiguration.getConfiguration().getManagedRepositoriesAsMap().keySet() );
140 for ( String repo : selectedRepositories )
142 if ( availableRepositories.contains( repo ) )
144 availableRepositories.remove( repo );
150 * Generate the statistics report.
152 * check whether single repo report or comparison report
153 * 1. if it is a single repository, get all the statistics for the repository on the specified date
154 * - if no date is specified, get only the latest
155 * (total page = 1 --> no pagination since only the most recent stats will be displayed)
156 * - otherwise, get everything within the date range (total pages = repo stats / rows per page)
157 * - required params: repository, startDate, endDate
159 * 2. if multiple repositories, get the latest statistics on each repository on the specified date
160 * - if no date is specified, use the current date endDate
161 * - required params: repositories, endDate
162 * - total pages = repositories / rows per page
164 * @return action result
166 public String generateStatistics()
170 // TODO: move to validation framework
171 addFieldError( "rowCount", "Row count must be larger than 10." );
179 if ( selectedRepositories.size() > 1 )
185 startDateInDF = getStartDateInDateFormat();
186 endDateInDF = getEndDateInDateFormat();
188 catch ( ParseException e )
190 addActionError( "Error parsing date(s)." );
194 if ( startDateInDF != null && endDateInDF != null && startDateInDF.after( endDateInDF ) )
196 addFieldError( "startDate", "Start Date must be earlier than the End Date" );
201 for ( String repo : selectedRepositories )
203 List<RepositoryStatistics> stats =
204 repositoryStatisticsManager.getStatisticsInRange( repo, startDateInDF, endDateInDF );
205 if ( stats.isEmpty() )
207 log.info( "No statistics available for repository '" + repo + "'." );
208 // TODO set repo's stats to 0
212 repositoryStatistics.add( stats.get( 0 ) );
215 else if ( selectedRepositories.size() == 1 )
217 repositoryId = selectedRepositories.get( 0 );
220 startDateInDF = getStartDateInDateFormat();
221 endDateInDF = getEndDateInDateFormat();
223 if ( startDateInDF != null && endDateInDF != null && startDateInDF.after( endDateInDF ) )
225 addFieldError( "startDate", "Start Date must be earlier than the End Date" );
229 List<RepositoryStatistics> stats =
230 repositoryStatisticsManager.getStatisticsInRange( repositoryId, startDateInDF, endDateInDF );
232 if ( stats.isEmpty() )
235 "No statistics available for repository. Repository might not have been scanned." );
239 int rowCount = getRowCount();
240 int extraPage = ( stats.size() % rowCount ) != 0 ? 1 : 0;
241 int totalPages = ( stats.size() / rowCount ) + extraPage;
242 numPages = totalPages;
244 int currentPage = getPage();
245 if ( currentPage > totalPages )
247 throw new ArchivaReportException( "The requested page exceeds the total number of pages." );
250 int start = rowCount * ( currentPage - 1 );
251 int end = ( start + rowCount ) - 1;
253 if ( end > stats.size() )
255 end = stats.size() - 1;
258 repositoryStatistics = stats.subList( start, end + 1 );
260 catch ( ParseException pe )
262 addActionError( pe.getMessage() );
268 addFieldError( "availableRepositories", "Please select a repository (or repositories) from the list." );
272 if ( repositoryStatistics.isEmpty() )
277 catch ( ArchivaReportException e )
279 addActionError( "Error encountered while generating report :: " + e.getMessage() );
287 * Export report to CSV.
289 * @return action result
291 public String downloadStatisticsReport()
296 selectedRepositories = parseSelectedRepositories();
297 List<RepositoryStatistics> repositoryStatistics = new ArrayList<RepositoryStatistics>();
299 StringBuffer input = null;
300 if ( selectedRepositories.size() > 1 )
304 startDateInDF = getStartDateInDateFormat();
305 endDateInDF = getEndDateInDateFormat();
307 catch ( ParseException e )
309 addActionError( "Error parsing date(s)." );
313 if ( startDateInDF != null && endDateInDF != null && startDateInDF.after( endDateInDF ) )
315 addFieldError( "startDate", "Start Date must be earlier than the End Date" );
319 input = new StringBuffer(
320 "Repository,Total File Count,Total Size,Artifact Count,Group Count,Project Count," +
321 "Plugins,Archetypes,Jars,Wars,Deployments,Downloads\n" );
324 for ( String repo : selectedRepositories )
326 List<RepositoryStatistics> stats =
327 repositoryStatisticsManager.getStatisticsInRange( repo, startDateInDF, endDateInDF );
328 if ( stats.isEmpty() )
330 log.info( "No statistics available for repository '" + repo + "'." );
331 // TODO set repo's stats to 0
335 // only the first one
336 RepositoryStatistics repositoryStats = stats.get( 0 );
337 repositoryStatistics.add( repositoryStats );
339 input.append( repo ).append( "," );
340 input.append( repositoryStats.getTotalFileCount() ).append( "," );
341 input.append( repositoryStats.getTotalArtifactFileSize() ).append( "," );
342 input.append( repositoryStats.getTotalArtifactCount() ).append( "," );
343 input.append( repositoryStats.getTotalGroupCount() ).append( "," );
344 input.append( repositoryStats.getTotalProjectCount() );//.append( "," );
346 // input.append( repositoryStats.getPluginCount() ).append( "," );
347 // input.append( repositoryStats.getArchetypeCount() ).append( "," );
348 // input.append( repositoryStats.getJarCount() ).append( "," );
349 // input.append( repositoryStats.getWarCount() ).append( "," );
350 // input.append( repositoryStats.getDeploymentCount() ).append( "," );
351 // input.append( repositoryStats.getDownloadCount() ).append( "\n" );
352 input.append( "\n" );
355 else if ( selectedRepositories.size() == 1 )
357 repositoryId = selectedRepositories.get( 0 );
360 startDateInDF = getStartDateInDateFormat();
361 endDateInDF = getEndDateInDateFormat();
363 if ( startDateInDF != null && endDateInDF != null && startDateInDF.after( endDateInDF ) )
365 addFieldError( "startDate", "Start Date must be earlier than the End Date" );
369 List<RepositoryStatistics> stats =
370 repositoryStatisticsManager.getStatisticsInRange( repositoryId, startDateInDF, endDateInDF );
371 if ( stats.isEmpty() )
373 addActionError( "No statistics available for repository. Repository might not have been scanned." );
377 input = new StringBuffer(
378 "Date of Scan,Total File Count,Total Size,Artifact Count,Group Count,Project Count," +
379 "Plugins,Archetypes,Jars,Wars,Deployments,Downloads\n" );
381 for ( RepositoryStatistics repositoryStats : stats )
383 input.append( repositoryStats.getScanStartTime() ).append( "," );
384 input.append( repositoryStats.getTotalFileCount() ).append( "," );
385 input.append( repositoryStats.getTotalArtifactFileSize() ).append( "," );
386 input.append( repositoryStats.getTotalArtifactCount() ).append( "," );
387 input.append( repositoryStats.getTotalGroupCount() ).append( "," );
388 input.append( repositoryStats.getTotalProjectCount() );//.append( "," );
390 // input.append( repositoryStats.getPluginCount() ).append( "," );
391 // input.append( repositoryStats.getArchetypeCount() ).append( "," );
392 // input.append( repositoryStats.getJarCount() ).append( "," );
393 // input.append( repositoryStats.getWarCount() ).append( "," );
394 // input.append( repositoryStats.getDeploymentCount() ).append( "," );
395 // input.append( repositoryStats.getDownloadCount() );
396 input.append( "\n" );
399 repositoryStatistics = stats;
401 catch ( ParseException pe )
403 addActionError( pe.getMessage() );
409 addFieldError( "availableRepositories", "Please select a repository (or repositories) from the list." );
413 if ( repositoryStatistics.isEmpty() )
418 // write output stream depending on single or comparison report
419 StringReader reader = new StringReader( input.toString() );
423 inputStream = new ByteArrayInputStream( IOUtils.toByteArray( reader ) );
425 catch ( IOException i )
427 addActionError( "Error occurred while generating CSV file." );
434 // hack for parsing the struts list passed as param in <s:url ../>
435 private List<String> parseSelectedRepositories()
437 List<String> pasedSelectedRepos = new ArrayList<String>();
439 for ( String repo : selectedRepositories )
441 String[] tokens = StringUtils.split( repo, ',' );
442 if ( tokens.length > 1 )
444 for ( String token : tokens )
446 pasedSelectedRepos.add( StringUtils.remove( StringUtils.remove( token, '[' ), ']' ).trim() );
451 pasedSelectedRepos.add( StringUtils.remove( StringUtils.remove( repo, '[' ), ']' ).trim() );
454 return pasedSelectedRepos;
457 private Date getStartDateInDateFormat()
458 throws ParseException
461 if ( startDate == null || "".equals( startDate ) )
463 startDateInDF = null;
467 startDateInDF = DateUtils.parseDate( startDate, datePatterns );
469 return startDateInDF;
472 private Date getEndDateInDateFormat()
473 throws ParseException
476 if ( endDate == null || "".equals( endDate ) )
482 endDateInDF = DateUtils.parseDate( endDate, datePatterns );
484 // add a day, since we don't inclue time and want the date to be inclusive
485 Calendar cal = Calendar.getInstance();
486 cal.setTime( endDateInDF );
487 cal.add( Calendar.DAY_OF_MONTH, 1 );
488 endDateInDF = cal.getTime();
494 public String execute()
497 if ( repositoryId == null )
499 addFieldError( "repositoryId", "You must provide a repository id." );
505 addFieldError( "rowCount", "Row count must be larger than 10." );
509 List<RepositoryProblem> problemArtifacts =
510 dao.getRepositoryProblemDAO().queryRepositoryProblems( configureConstraint() );
513 request.getRequestURL().substring( 0, request.getRequestURL().indexOf( request.getRequestURI() ) );
514 for ( RepositoryProblem problem : problemArtifacts )
516 RepositoryProblemReport problemArtifactReport = new RepositoryProblemReport( problem );
518 problemArtifactReport.setGroupURL( contextPath + "/browse/" + problem.getGroupId() );
519 problemArtifactReport.setArtifactURL(
520 contextPath + "/browse/" + problem.getGroupId() + "/" + problem.getArtifactId() );
522 List<RepositoryProblemReport> problemsList;
523 if ( repositoriesMap.containsKey( problemArtifactReport.getRepositoryId() ) )
525 problemsList = repositoriesMap.get( problemArtifactReport.getRepositoryId() );
529 problemsList = new ArrayList<RepositoryProblemReport>();
530 repositoriesMap.put( problemArtifactReport.getRepositoryId(), problemsList );
533 problemsList.add( problemArtifactReport );
536 // TODO: handling should be improved
537 if ( problemArtifacts.size() <= rowCount )
542 if ( problemArtifacts.isEmpty() && page == 1 )
552 private Constraint configureConstraint()
554 Constraint constraint;
557 new int[]{( page - 1 ) * rowCount, ( page * rowCount ) + 1}; // Add 1 to check if it's the last page or not.
559 if ( groupId != null && ( !groupId.equals( "" ) ) )
561 if ( repositoryId != null && ( !repositoryId.equals( "" ) && !repositoryId.equals( ALL_REPOSITORIES ) ) )
563 constraint = new RepositoryProblemConstraint( range, groupId, repositoryId );
567 constraint = new RepositoryProblemByGroupIdConstraint( range, groupId );
570 else if ( repositoryId != null && ( !repositoryId.equals( "" ) && !repositoryId.equals( ALL_REPOSITORIES ) ) )
572 constraint = new RepositoryProblemByRepositoryIdConstraint( range, repositoryId );
576 constraint = new RangeConstraint( range, "repositoryId" );
582 public SecureActionBundle getSecureActionBundle()
583 throws SecureActionException
585 SecureActionBundle bundle = new SecureActionBundle();
587 bundle.setRequiresAuthentication( true );
588 bundle.addRequiredAuthorization( ArchivaRoleConstants.OPERATION_ACCESS_REPORT, Resource.GLOBAL );
593 public Collection<String> getRepositoryIds()
595 return repositoryIds;
598 public void setServletRequest( HttpServletRequest request )
600 this.request = request;
603 public String getGroupId()
608 public void setGroupId( String groupId )
610 this.groupId = groupId;
613 public String getRepositoryId()
618 public void setRepositoryId( String repositoryId )
620 this.repositoryId = repositoryId;
628 public void setPage( int page )
633 public int getRowCount()
638 public void setRowCount( int rowCount )
640 this.rowCount = rowCount;
643 public void setRepositoriesMap( Map<String, List<RepositoryProblemReport>> repositoriesMap )
645 this.repositoriesMap = repositoriesMap;
648 public Map<String, List<RepositoryProblemReport>> getRepositoriesMap()
650 return repositoriesMap;
653 public List<String> getSelectedRepositories()
655 return selectedRepositories;
658 public void setSelectedRepositories( List<String> selectedRepositories )
660 this.selectedRepositories = selectedRepositories;
663 public List<String> getAvailableRepositories()
665 return availableRepositories;
668 public void setAvailableRepositories( List<String> availableRepositories )
670 this.availableRepositories = availableRepositories;
673 public String getStartDate()
678 public void setStartDate( String startDate )
680 this.startDate = startDate;
683 public String getEndDate()
688 public void setEndDate( String endDate )
690 this.endDate = endDate;
693 public List<RepositoryStatistics> getRepositoryStatistics()
695 return repositoryStatistics;
698 public void setRepositoryStatistics( List<RepositoryStatistics> repositoryStatistics )
700 this.repositoryStatistics = repositoryStatistics;
703 public boolean isLastPage()
708 public void setLastPage( boolean lastPage )
710 this.lastPage = lastPage;
713 public InputStream getInputStream()
718 public int getNumPages()
723 public void setRepositoryStatisticsManager( RepositoryStatisticsManager repositoryStatisticsManager )
725 this.repositoryStatisticsManager = repositoryStatisticsManager;