]> source.dussan.org Git - archiva.git/commitdiff
Merged /archiva/trunk:r887040-888396
authorBrett Porter <brett@apache.org>
Tue, 8 Dec 2009 15:09:13 +0000 (15:09 +0000)
committerBrett Porter <brett@apache.org>
Tue, 8 Dec 2009 15:09:13 +0000 (15:09 +0000)
git-svn-id: https://svn.apache.org/repos/asf/archiva/branches/MRM-1025@888427 13f79535-47bb-0310-9956-ffa450edef68

1  2 
archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/reports/GenerateReportAction.java
archiva-modules/archiva-web/archiva-webapp/src/test/java/org/apache/maven/archiva/web/action/admin/repositories/ArchivaDAOStub.java
archiva-modules/archiva-web/archiva-webapp/src/test/java/org/apache/maven/archiva/web/action/reports/GenerateReportActionTest.java
archiva-modules/archiva-web/archiva-webapp/src/test/resources/org/apache/maven/archiva/web/action/reports/GenerateReportActionTest.xml
archiva-modules/plugins/repository-statistics/src/main/java/org/apache/archiva/metadata/repository/stats/DefaultRepositoryStatisticsManager.java

index a0484fe28fb25fa171b45047ad841e0025200f23,f38012909d1e2cdab9eee67a87ee0bfcd2ae3dc3..49724c86d53234972a31d5e05ee1fad4d6a16687
@@@ -187,75 -224,61 +187,75 @@@ public class GenerateReportActio
                  }
                  catch ( ParseException e )
                  {
 -                      addActionError( "Error parsing date(s)." );
 -                      return ERROR;
 +                    addActionError( "Error parsing date(s)." );
 +                    return ERROR;
                  }
 -                
 -                if( startDateInDF.after( endDateInDF ) )
 +
-                 if ( startDateInDF.after( endDateInDF ) )
++                if ( startDateInDF != null && endDateInDF != null && startDateInDF.after( endDateInDF ) )
                  {
                      addFieldError( "startDate", "Start Date must be earlier than the End Date" );
                      return INPUT;
                  }
 -                
 +
                  // multiple repos
 -                generateReportForMultipleRepos(repoContentStatsDao, startDateInDF, endDateInDF, true);                
 +                for ( String repo : selectedRepositories )
 +                {
 +                    List<RepositoryStatistics> stats =
 +                        repositoryStatisticsManager.getStatisticsInRange( repo, startDateInDF, endDateInDF );
 +                    if ( stats.isEmpty() )
 +                    {
 +                        log.info( "No statistics available for repository '" + repo + "'." );
 +                        // TODO set repo's stats to 0
 +                        continue;
 +                    }
 +
 +                    repositoryStatistics.add( stats.get( 0 ) );
 +                }
              }
              else if ( selectedRepositories.size() == 1 )
 -            {   
 -                limits.setCurrentPage( getPage() );
 -                limits.setPerPageCount( getRowCount() );
 -                
 -                selectedRepo = selectedRepositories.get( 0 );
 +            {
 +                repositoryId = selectedRepositories.get( 0 );
                  try
 -                {      
 -                      startDateInDF = getStartDateInDateFormat();                     
 -                      endDateInDF = getEndDateInDateFormat();
 -                       
 -                      if( startDateInDF.after( endDateInDF ) )
 +                {
 +                    startDateInDF = getStartDateInDateFormat();
 +                    endDateInDF = getEndDateInDateFormat();
 +
-                     if ( startDateInDF.after( endDateInDF ) )
++                    if ( startDateInDF != null && endDateInDF != null && startDateInDF.after( endDateInDF ) )
                      {
 -                          addFieldError( "startDate", "Start Date must be earlier than the End Date" );
 -                          return INPUT;
 +                        addFieldError( "startDate", "Start Date must be earlier than the End Date" );
 +                        return INPUT;
                      }
 -                      
 -                    List<RepositoryContentStatistics> contentStats = repoContentStatsDao.queryRepositoryContentStatistics( 
 -                           new RepositoryContentStatisticsByRepositoryConstraint( selectedRepo, startDateInDF, endDateInDF ) );
 -                    
 -                    if( contentStats == null || contentStats.isEmpty() )
 -                    {   
 -                        addActionError( "No statistics available for repository. Repository might not have been scanned." );
 +
 +                    List<RepositoryStatistics> stats =
 +                        repositoryStatisticsManager.getStatisticsInRange( repositoryId, startDateInDF, endDateInDF );
 +
 +                    if ( stats.isEmpty() )
 +                    {
 +                        addActionError(
 +                            "No statistics available for repository. Repository might not have been scanned." );
                          return ERROR;
 -                    }   
 -                    
 -                    limits.setTotalCount( contentStats.size() );                    
 -                    int extraPage = ( limits.getTotalCount() % limits.getPerPageCount() ) != 0 ? 1 : 0;
 -                    int totalPages = ( limits.getTotalCount() / limits.getPerPageCount() ) + extraPage;                    
 -                    limits.setCountOfPages( totalPages );
 -                    
 -                    repositoryStatistics = generator.generateReport( contentStats, selectedRepo, startDateInDF, endDateInDF, limits );
 -                }
 -                catch ( ObjectNotFoundException oe )
 -                {
 -                    addActionError( oe.getMessage() );
 -                    return ERROR;
 -                }
 -                catch ( ArchivaDatabaseException de )
 -                {
 -                    addActionError( de.getMessage() );
 -                    return ERROR;
 +                    }
 +
 +                    int rowCount = getRowCount();
 +                    int extraPage = ( stats.size() % rowCount ) != 0 ? 1 : 0;
 +                    int totalPages = ( stats.size() / rowCount ) + extraPage;
 +                    numPages = totalPages;
 +
 +                    int currentPage = getPage();
 +                    if ( currentPage > totalPages )
 +                    {
 +                        throw new ArchivaReportException( "The requested page exceeds the total number of pages." );
 +                    }
 +
 +                    int start = rowCount * ( currentPage - 1 );
 +                    int end = ( start + rowCount ) - 1;
 +
 +                    if ( end > stats.size() )
 +                    {
 +                        end = stats.size() - 1;
 +                    }
 +
 +                    repositoryStatistics = stats.subList( start, end + 1 );
                  }
                  catch ( ParseException pe )
                  {
              addActionError( "Error encountered while generating report :: " + e.getMessage() );
              return ERROR;
          }
 -        
 +
          return SUCCESS;
      }
 -      
 +
      /**
       * Export report to CSV.
 -     * 
 -     * @return
 +     *
 +     * @return action result
       */
      public String downloadStatisticsReport()
 -    {   
 -        try
 +    {
 +        Date startDateInDF;
 +        Date endDateInDF;
 +
 +        selectedRepositories = parseSelectedRepositories();
 +        List<RepositoryStatistics> repositoryStatistics = new ArrayList<RepositoryStatistics>();
 +
 +        StringBuffer input = null;
 +        if ( selectedRepositories.size() > 1 )
          {
 -              Date startDateInDF = null;
 -            Date endDateInDF = null;
 -            
 -            selectedRepositories = parseSelectedRepositories();
 -            repositoryStatistics = new ArrayList<RepositoryStatistics>();
 -            
 -            RepositoryContentStatisticsDAO repoContentStatsDao = dao.getRepositoryContentStatisticsDAO();            
 -            if( selectedRepositories.size() > 1 )
 -            {   
 -                try
 -                {      
 -                      startDateInDF = getStartDateInDateFormat();                     
 -                      endDateInDF = getEndDateInDateFormat();
 -                }
 -                catch ( ParseException e )
 +            try
 +            {
 +                startDateInDF = getStartDateInDateFormat();
 +                endDateInDF = getEndDateInDateFormat();
 +            }
 +            catch ( ParseException e )
 +            {
 +                addActionError( "Error parsing date(s)." );
 +                return ERROR;
 +            }
 +
-             if ( startDateInDF.after( endDateInDF ) )
++            if ( startDateInDF != null && endDateInDF != null && startDateInDF.after( endDateInDF ) )
 +            {
 +                addFieldError( "startDate", "Start Date must be earlier than the End Date" );
 +                return INPUT;
 +            }
 +
++            input = new StringBuffer(
++                "Repository,Total File Count,Total Size,Artifact Count,Group Count,Project Count," +
++                    "Plugins,Archetypes,Jars,Wars,Deployments,Downloads\n" );
++
 +            // multiple repos
 +            for ( String repo : selectedRepositories )
 +            {
 +                List<RepositoryStatistics> stats =
 +                    repositoryStatisticsManager.getStatisticsInRange( repo, startDateInDF, endDateInDF );
 +                if ( stats.isEmpty() )
                  {
 -                      addActionError( "Error parsing date(s)." );
 -                      return ERROR;
 +                    log.info( "No statistics available for repository '" + repo + "'." );
 +                    // TODO set repo's stats to 0
 +                    continue;
                  }
 -                
 -                if( startDateInDF.after( endDateInDF ) )
 +
 +                // only the first one
 +                RepositoryStatistics repositoryStats = stats.get( 0 );
 +                repositoryStatistics.add( repositoryStats );
 +
-                 input = new StringBuffer(
-                     "Repository,Total File Count,Total Size,Artifact Count,Group Count,Project Count," +
-                         "Plugins,Archetypes,Jars,Wars,Deployments,Downloads\n" );
 +                input.append( repo ).append( "," );
 +                input.append( repositoryStats.getTotalFileCount() ).append( "," );
 +                input.append( repositoryStats.getTotalArtifactFileSize() ).append( "," );
 +                input.append( repositoryStats.getTotalArtifactCount() ).append( "," );
 +                input.append( repositoryStats.getTotalGroupCount() ).append( "," );
-                 input.append( repositoryStats.getTotalProjectCount() ).append( "," );
++                input.append( repositoryStats.getTotalProjectCount() );//.append( "," );
 +                // TODO
 +//                input.append( repositoryStats.getPluginCount() ).append( "," );
 +//                input.append( repositoryStats.getArchetypeCount() ).append( "," );
 +//                input.append( repositoryStats.getJarCount() ).append( "," );
 +//                input.append( repositoryStats.getWarCount() ).append( "," );
 +//                input.append( repositoryStats.getDeploymentCount() ).append( "," );
 +//                input.append( repositoryStats.getDownloadCount() ).append( "\n" );
 +                input.append( "\n" );
 +            }
 +        }
 +        else if ( selectedRepositories.size() == 1 )
 +        {
 +            repositoryId = selectedRepositories.get( 0 );
 +            try
 +            {
 +                startDateInDF = getStartDateInDateFormat();
 +                endDateInDF = getEndDateInDateFormat();
 +
-                 if ( startDateInDF.after( endDateInDF ) )
++                if ( startDateInDF != null && endDateInDF != null && startDateInDF.after( endDateInDF ) )
                  {
                      addFieldError( "startDate", "Start Date must be earlier than the End Date" );
                      return INPUT;
                  }
 -                
 -             // multiple repos
 -                generateReportForMultipleRepos( repoContentStatsDao, startDateInDF, endDateInDF, false );
 -            }
 -            else if ( selectedRepositories.size() == 1 )
 -            {   
 -                selectedRepo = selectedRepositories.get( 0 );
 -                try
 -                {                 
 -                      startDateInDF = getStartDateInDateFormat();
 -                      endDateInDF = getEndDateInDateFormat();
 -                      
 -                      if( startDateInDF.after( endDateInDF ) )
 -                    {
 -                          addFieldError( "startDate", "Start Date must be earlier than the End Date" );
 -                          return INPUT;
 -                    }
 -                      
 -                    List<RepositoryContentStatistics> contentStats = repoContentStatsDao.queryRepositoryContentStatistics( 
 -                           new RepositoryContentStatisticsByRepositoryConstraint( selectedRepo, startDateInDF, endDateInDF ) );
 -                                        
 -                    if( contentStats == null || contentStats.isEmpty() )
 -                    {   
 -                        addActionError( "No statistics available for repository. Repository might not have been scanned." );
 -                        return ERROR;
 -                    }   
 -                    
 -                    repositoryStatistics = generator.generateReport( contentStats, selectedRepo, startDateInDF, endDateInDF, false );                    
 -                }
 -                catch ( ObjectNotFoundException oe )
 -                {
 -                    addActionError( oe.getMessage() );
 -                    return ERROR;
 -                }
 -                catch ( ArchivaDatabaseException de )
 +
 +                List<RepositoryStatistics> stats =
 +                    repositoryStatisticsManager.getStatisticsInRange( repositoryId, startDateInDF, endDateInDF );
 +                if ( stats.isEmpty() )
                  {
 -                    addActionError( de.getMessage() );
 +                    addActionError( "No statistics available for repository. Repository might not have been scanned." );
                      return ERROR;
                  }
 -                catch ( ParseException pe )
 +
 +                input = new StringBuffer(
 +                    "Date of Scan,Total File Count,Total Size,Artifact Count,Group Count,Project Count," +
 +                        "Plugins,Archetypes,Jars,Wars,Deployments,Downloads\n" );
 +
 +                for ( RepositoryStatistics repositoryStats : stats )
                  {
 -                      addActionError( pe.getMessage() );
 -                      return ERROR;
 +                    input.append( repositoryStats.getScanStartTime() ).append( "," );
 +                    input.append( repositoryStats.getTotalFileCount() ).append( "," );
 +                    input.append( repositoryStats.getTotalArtifactFileSize() ).append( "," );
 +                    input.append( repositoryStats.getTotalArtifactCount() ).append( "," );
 +                    input.append( repositoryStats.getTotalGroupCount() ).append( "," );
-                     input.append( repositoryStats.getTotalProjectCount() ).append( "," );
++                    input.append( repositoryStats.getTotalProjectCount() );//.append( "," );
 +                    // TODO
 +//                input.append( repositoryStats.getPluginCount() ).append( "," );
 +//                input.append( repositoryStats.getArchetypeCount() ).append( "," );
 +//                input.append( repositoryStats.getJarCount() ).append( "," );
 +//                input.append( repositoryStats.getWarCount() ).append( "," );
 +//                input.append( repositoryStats.getDeploymentCount() ).append( "," );
 +//                input.append( repositoryStats.getDownloadCount() );
 +                    input.append( "\n" );
                  }
 +
 +                repositoryStatistics = stats;
              }
 -            else
 +            catch ( ParseException pe )
              {
 -                addFieldError( "availableRepositories", "Please select a repository (or repositories) from the list." );
 -                return INPUT;
 -            } 
 -            
 -            if( repositoryStatistics.isEmpty() )
 -            {
 -                return BLANK;
 -            }            
 +                addActionError( pe.getMessage() );
 +                return ERROR;
 +            }
          }
 -        catch ( ArchivaReportException e )
 +        else
          {
 -            addActionError( "Error encountered while generating report :: " + e.getMessage() );
 -            return ERROR;
 -        }    
 -        
 -        // write output stream depending on single or comparison report              
 -        StringBuffer input = getInput();        
 +            addFieldError( "availableRepositories", "Please select a repository (or repositories) from the list." );
 +            return INPUT;
 +        }
 +
 +        if ( repositoryStatistics.isEmpty() )
 +        {
 +            return BLANK;
 +        }
 +
 +        // write output stream depending on single or comparison report
          StringReader reader = new StringReader( input.toString() );
 -        
 +
          try
          {
 -              inputStream = new ByteArrayInputStream( IOUtils.toByteArray( reader ) );
 +            inputStream = new ByteArrayInputStream( IOUtils.toByteArray( reader ) );
          }
          catch ( IOException i )
 -        {     
 -              addActionError( "Error occurred while generating CSV file." );
 -              return ERROR;
 +        {
 +            addActionError( "Error occurred while generating CSV file." );
 +            return ERROR;
          }
 -        
 -      return SEND_FILE;
 +
 +        return SEND_FILE;
      }
 -    
 +
      // hack for parsing the struts list passed as param in <s:url ../>
      private List<String> parseSelectedRepositories()
 -    {           
 +    {
          List<String> pasedSelectedRepos = new ArrayList<String>();
 -     
 -        for( String repo : selectedRepositories )
 -        {   
 +
 +        for ( String repo : selectedRepositories )
 +        {
              String[] tokens = StringUtils.split( repo, ',' );
 -            if( tokens.length > 1 )
 +            if ( tokens.length > 1 )
              {
 -                for( int i = 0; i < tokens.length; i++ )
 -                {   
 -                    pasedSelectedRepos.add( StringUtils.remove( StringUtils.remove( tokens[i], '[' ), ']' ).trim() );
 +                for ( String token : tokens )
 +                {
 +                    pasedSelectedRepos.add( StringUtils.remove( StringUtils.remove( token, '[' ), ']' ).trim() );
                  }
              }
              else
          Date startDateInDF;
          if ( startDate == null || "".equals( startDate ) )
          {
--            startDateInDF = getDefaultStartDate();
++            startDateInDF = null;
          }
          else
          {
          Date endDateInDF;
          if ( endDate == null || "".equals( endDate ) )
          {
--            endDateInDF = getDefaultEndDate();
++            endDateInDF = null;
          }
          else
          {
              endDateInDF = DateUtils.parseDate( endDate, datePatterns );
 +
 +            // add a day, since we don't inclue time and want the date to be inclusive
 +            Calendar cal = Calendar.getInstance();
 +            cal.setTime( endDateInDF );
 +            cal.add( Calendar.DAY_OF_MONTH, 1 );
 +            endDateInDF = cal.getTime();
          }
 -        
 +
          return endDateInDF;
      }
 -    
 -    private StringBuffer getInput()
 -    {
 -        StringBuffer input = null;
 -        
 -        if( selectedRepositories.size() == 1 )
 -        {             
 -              input = new StringBuffer( "Date of Scan,Total File Count,Total Size,Artifact Count,Group Count,Project Count," +
 -                              "Plugins,Archetypes,Jars,Wars,Deployments,Downloads\n" );
 -              
 -              for( RepositoryStatistics stats : repositoryStatistics )
 -              {
 -                      input.append( stats.getDateOfScan() ).append( "," );
 -                      input.append( stats.getFileCount() ).append( "," );
 -                      input.append( stats.getTotalSize() ).append( "," );
 -                      input.append( stats.getArtifactCount() ).append( "," );
 -                      input.append( stats.getGroupCount() ).append( "," );
 -                      input.append( stats.getProjectCount() ).append( "," );
 -                      input.append( stats.getPluginCount() ).append( "," );
 -                      input.append( stats.getArchetypeCount() ).append( "," );
 -                      input.append( stats.getJarCount() ).append( "," );
 -                      input.append( stats.getWarCount() ).append( "," );
 -                      input.append( stats.getDeploymentCount() ).append( "," );
 -                      input.append( stats.getDownloadCount() ).append( "\n" );
 -              }               
 -        }            
 -        else if( selectedRepositories.size() > 1 )
 -        {
 -              input = new StringBuffer( "Repository,Total File Count,Total Size,Artifact Count,Group Count,Project Count," +
 -                                      "Plugins,Archetypes,Jars,Wars,Deployments,Downloads\n" );
 -                      
 -                      for( RepositoryStatistics stats : repositoryStatistics )
 -                      {
 -                              input.append( stats.getRepositoryId() ).append( "," );
 -                              input.append( stats.getFileCount() ).append( "," );
 -                              input.append( stats.getTotalSize() ).append( "," );
 -                              input.append( stats.getArtifactCount() ).append( "," );
 -                              input.append( stats.getGroupCount() ).append( "," );
 -                              input.append( stats.getProjectCount() ).append( "," );
 -                              input.append( stats.getPluginCount() ).append( "," );
 -                              input.append( stats.getArchetypeCount() ).append( "," );
 -                              input.append( stats.getJarCount() ).append( "," );
 -                              input.append( stats.getWarCount() ).append( "," );
 -                              input.append( stats.getDeploymentCount() ).append( "," );
 -                              input.append( stats.getDownloadCount() ).append( "\n" );
 -                      }
 -        }
 -        
 -        return input;
 -    }
 -    
 -    private Date getDefaultStartDate()
 -    {
 -        Calendar cal = Calendar.getInstance();
 -        cal.clear();
 -        cal.set( 1900, 1, 1, 0, 0, 0 );
 -        
 -        return cal.getTime();
 -    }
 -    
 -    private Date getDefaultEndDate()
 -    {
 -        return Calendar.getInstance().getTime();
 -    }
 -    
 +
-     private Date getDefaultStartDate()
-     {
-         Calendar cal = Calendar.getInstance();
-         cal.clear();
-         cal.set( 1900, 1, 1, 0, 0, 0 );
-         return cal.getTime();
-     }
-     private Date getDefaultEndDate()
-     {
-         return Calendar.getInstance().getTime();
-     }
      public String execute()
          throws Exception
 -    {   
 -        if( repositoryId == null )
 +    {
 +        if ( repositoryId == null )
          {
 -            addFieldError( "repositoryId", "You must provide a repository id.");            
 +            addFieldError( "repositoryId", "You must provide a repository id." );
              return INPUT;
          }
 -        
 -        if( rowCount < 10 )
 +
 +        if ( rowCount < 10 )
          {
              addFieldError( "rowCount", "Row count must be larger than 10." );
              return INPUT;
          throws SecureActionException
      {
          SecureActionBundle bundle = new SecureActionBundle();
 -    
 +
          bundle.setRequiresAuthentication( true );
          bundle.addRequiredAuthorization( ArchivaRoleConstants.OPERATION_ACCESS_REPORT, Resource.GLOBAL );
 -    
 +
          return bundle;
      }
 -    
 -    private void addToList( RepositoryProblemReport repoProblemReport )
 +
-     private void addToList( RepositoryProblemReport repoProblemReport )
-     {
-         List<RepositoryProblemReport> problemsList = null;
-         if ( repositoriesMap.containsKey( repoProblemReport.getRepositoryId() ) )
-         {
-             problemsList = (List<RepositoryProblemReport>) repositoriesMap.get( repoProblemReport.getRepositoryId() );
-         }
-         else
-         {
-             problemsList = new ArrayList<RepositoryProblemReport>();
-             repositoriesMap.put( repoProblemReport.getRepositoryId(), problemsList );
-         }
-         problemsList.add( repoProblemReport );
-     }
 +    public Collection<String> getRepositoryIds()
      {
 -        List<RepositoryProblemReport> problemsList = null;
 -        
 -        if ( repositoriesMap.containsKey( repoProblemReport.getRepositoryId() ) )
 -        {
 -            problemsList = ( List<RepositoryProblemReport> ) repositoriesMap.get( repoProblemReport.getRepositoryId() );
 -        }
 -        else
 -        {
 -            problemsList = new ArrayList<RepositoryProblemReport>();
 -            repositoriesMap.put( repoProblemReport.getRepositoryId(), problemsList );
 -        }
 -        
 -        problemsList.add( repoProblemReport );
 +        return repositoryIds;
      }
  
      public void setServletRequest( HttpServletRequest request )
      {
          this.repositoryStatistics = repositoryStatistics;
      }
 -    
 -    public int getReposSize()
 -    {
 -        return reposSize;
 -    }
  
 -    public void setReposSize( int reposSize )
 +    public boolean isLastPage()
      {
 -        this.reposSize = reposSize;
 +        return lastPage;
      }
  
 -    public String getSelectedRepo()
 +    public void setLastPage( boolean lastPage )
      {
 -        return selectedRepo;
 +        this.lastPage = lastPage;
      }
  
 -    public void setSelectedRepo( String selectedRepo )
 +    public InputStream getInputStream()
      {
 -        this.selectedRepo = selectedRepo;
 +        return inputStream;
      }
  
 -    public DataLimits getLimits()
 +    public int getNumPages()
      {
 -        return limits;
 +        return numPages;
      }
 -    public void setLimits( DataLimits limits )
 -    {
 -        this.limits = limits;
 -    }
 -    
 -    public InputStream getInputStream()
 -      return inputStream;
++    public void setRepositoryStatisticsManager( RepositoryStatisticsManager repositoryStatisticsManager )
+     {
++        this.repositoryStatisticsManager = repositoryStatisticsManager;
+     }
  }
index 3466be16d8f18d5e323a7a489915e85f10b86aaa,2e9b97e5813ed6060f61d93d0e81e30abfeaa715..973da10bbd7cee195ce20455415207401431c15a
@@@ -1,17 -1,23 +1,5 @@@
  package org.apache.maven.archiva.web.action.admin.repositories;
  
--import java.io.Serializable;
 -import java.util.ArrayList;
--import java.util.List;
--
 -import junit.framework.Assert;
--import org.apache.maven.archiva.configuration.ArchivaConfiguration;
--import org.apache.maven.archiva.database.ArchivaDAO;
--import org.apache.maven.archiva.database.ArtifactDAO;
 -import org.apache.maven.archiva.database.ProjectModelDAO;
 -import org.apache.maven.archiva.database.RepositoryContentStatisticsDAO;
--import org.apache.maven.archiva.database.RepositoryProblemDAO;
--import org.apache.maven.archiva.database.SimpleConstraint;
--import org.apache.maven.archiva.database.constraints.UniqueArtifactIdConstraint;
 -import org.apache.maven.archiva.database.constraints.UniqueFieldConstraint;
--import org.apache.maven.archiva.database.constraints.UniqueGroupIdConstraint;
--import org.apache.maven.archiva.database.constraints.UniqueVersionConstraint;
 -import org.apache.maven.archiva.model.RepositoryContentStatistics;
--
  /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * under the License.
   */
  
++import java.io.Serializable;
++import java.util.List;
++
++import org.apache.maven.archiva.configuration.ArchivaConfiguration;
++import org.apache.maven.archiva.database.ArchivaDAO;
++import org.apache.maven.archiva.database.ArtifactDAO;
++import org.apache.maven.archiva.database.RepositoryProblemDAO;
++import org.apache.maven.archiva.database.SimpleConstraint;
++import org.apache.maven.archiva.database.constraints.UniqueArtifactIdConstraint;
++import org.apache.maven.archiva.database.constraints.UniqueFieldConstraint;
++import org.apache.maven.archiva.database.constraints.UniqueGroupIdConstraint;
++import org.apache.maven.archiva.database.constraints.UniqueVersionConstraint;
++
  /**
   * Stub class for Archiva DAO to avoid having to set up a database for tests.
   *
@@@ -49,6 -57,12 +50,10 @@@ public class ArchivaDAOStu
  
      private List<String> artifacts;
  
 -    private RepositoryContentStatisticsDAO repositoryContentStatisticsDAO;
 -
+     private List<String> repositoryIds;
+     private RepositoryProblemDAO repositoryProblemDAO;
      public List<?> query( SimpleConstraint constraint )
      {
          if ( constraint instanceof UniqueVersionConstraint )
          {
              return artifacts;
          }
 -        else
 -        {
 -            Assert.assertEquals( RepositoryContentStatistics.class, constraint.getResultClass() );
 -
 -            List<RepositoryContentStatistics> stats = new ArrayList<RepositoryContentStatistics>();
 -            for ( String repo : configuration.getConfiguration().getManagedRepositoriesAsMap().keySet() )
 -            {
 -                RepositoryContentStatistics statistics = new RepositoryContentStatistics();
 -                statistics.setRepositoryId( repo );
 -                stats.add( statistics );
 -            }
 -            return stats;
 -        }
+         else if ( constraint instanceof UniqueFieldConstraint )
+         {
+             return repositoryIds;
+         }
 +        throw new UnsupportedOperationException();
      }
  
      public Object save( Serializable obj )
          return artifactDao;
      }
  
 -    public ProjectModelDAO getProjectModelDAO()
 -    {
 -        return projectDao;
 -    }
 -
      public RepositoryProblemDAO getRepositoryProblemDAO()
      {
-         throw new UnsupportedOperationException( "method not implemented for stub" );
+         return repositoryProblemDAO;
      }
  
 -    public RepositoryContentStatisticsDAO getRepositoryContentStatisticsDAO()
 -    {
 -        return repositoryContentStatisticsDAO;
 -    }
 -
      public void setArtifactDao( ArtifactDAO artifactDao )
      {
          this.artifactDao = artifactDao;
index 0000000000000000000000000000000000000000,82103db48887a262121158ce4882ef61cf14025b..46c258f2f9549154f0ae6a7d35db509a877fb46f
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,535 +1,643 @@@
 -import org.apache.maven.archiva.database.RepositoryContentStatisticsDAO;
+ package org.apache.maven.archiva.web.action.reports;
+ /*
+  * Licensed to the Apache Software Foundation (ASF) under one
+  * or more contributor license agreements.  See the NOTICE file
+  * distributed with this work for additional information
+  * regarding copyright ownership.  The ASF licenses this file
+  * to you under the Apache License, Version 2.0 (the
+  * "License"); you may not use this file except in compliance
+  * with the License.  You may obtain a copy of the License at
+  *
+  *   http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing,
+  * software distributed under the License is distributed on an
+  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  * KIND, either express or implied.  See the License for the
+  * specific language governing permissions and limitations
+  * under the License.
+  */
+ import java.io.IOException;
+ import java.util.ArrayList;
+ import java.util.Arrays;
+ import java.util.Collections;
++import java.util.Date;
+ import java.util.List;
+ import com.meterware.servletunit.ServletRunner;
+ import com.meterware.servletunit.ServletUnitClient;
+ import com.opensymphony.xwork2.Action;
++import org.apache.archiva.metadata.repository.stats.RepositoryStatistics;
++import org.apache.archiva.metadata.repository.stats.RepositoryStatisticsManager;
+ import org.apache.commons.io.IOUtils;
+ import org.apache.maven.archiva.database.ArchivaDAO;
+ import org.apache.maven.archiva.database.ArchivaDatabaseException;
 -import org.apache.maven.archiva.database.constraints.RepositoryContentStatisticsByRepositoryConstraint;
+ import org.apache.maven.archiva.database.RepositoryProblemDAO;
+ import org.apache.maven.archiva.database.constraints.RangeConstraint;
 -import org.apache.maven.archiva.model.RepositoryContentStatistics;
+ import org.apache.maven.archiva.database.constraints.RepositoryProblemByGroupIdConstraint;
+ import org.apache.maven.archiva.database.constraints.RepositoryProblemByRepositoryIdConstraint;
+ import org.apache.maven.archiva.database.constraints.RepositoryProblemConstraint;
 -import org.apache.maven.archiva.web.action.admin.repositories.RepositoryContentStatisticsDAOStub;
+ import org.apache.maven.archiva.model.RepositoryProblem;
+ import org.apache.maven.archiva.model.RepositoryProblemReport;
+ import org.apache.maven.archiva.web.action.admin.repositories.ArchivaDAOStub;
 -        RepositoryContentStatisticsDAOStub dao =
 -            (RepositoryContentStatisticsDAOStub) lookup( RepositoryContentStatisticsDAO.class, "jdo" );
 -        dao.setStats( Collections.<RepositoryContentStatistics>emptyList() );
+ import org.codehaus.plexus.spring.PlexusInSpringTestCase;
+ import org.easymock.MockControl;
+ /**
+  * Test the GenerationReportAction. Note that we are testing for <i>current</i> behaviour, however there are several
+  * instances below where other behaviour may actually be more appropriate (eg the error handling, download stats should
+  * never forward to HTML page, etc). This is also missing tests for various combinations of paging at this point.
+  */
+ public class GenerateReportActionTest
+     extends PlexusInSpringTestCase
+ {
+     private GenerateReportAction action;
+     private static final String SNAPSHOTS = "snapshots";
+     private static final String INTERNAL = "internal";
+     private RepositoryProblemDAO repositoryProblemDAO;
+     private MockControl repositoryProblemDAOControl;
+     private static final String GROUP_ID = "groupId";
+     private static final String URL = "http://localhost/reports/generateReport.action";
++    private RepositoryStatisticsManager repositoryStatisticsManager;
++
++    private MockControl repositoryStatisticsManagerControl;
++
+     @Override
+     protected void setUp()
+         throws Exception
+     {
+         super.setUp();
+         ArchivaDAOStub archivaDAOStub = (ArchivaDAOStub) lookup( ArchivaDAO.class, "jdo" );
+         archivaDAOStub.setRepositoryIds( Arrays.asList( "repo1", "repo2" ) );
+         repositoryProblemDAOControl = MockControl.createControl( RepositoryProblemDAO.class );
+         repositoryProblemDAO = (RepositoryProblemDAO) repositoryProblemDAOControl.getMock();
+         archivaDAOStub.setRepositoryProblemDAO( repositoryProblemDAO );
+         action = (GenerateReportAction) lookup( Action.class, "generateReport" );
++
++        repositoryStatisticsManagerControl = MockControl.createControl( RepositoryStatisticsManager.class );
++        repositoryStatisticsManager = (RepositoryStatisticsManager) repositoryStatisticsManagerControl.getMock();
++        action.setRepositoryStatisticsManager( repositoryStatisticsManager );
+     }
+     private void prepareAction( List<String> selectedRepositories, List<String> availableRepositories )
+     {
+         action.setSelectedRepositories( selectedRepositories );
+         action.prepare();
+         assertEquals( Arrays.asList( GenerateReportAction.ALL_REPOSITORIES, "repo1", "repo2" ),
+                       action.getRepositoryIds() );
+         assertEquals( availableRepositories, action.getAvailableRepositories() );
+     }
+     public void testGenerateStatisticsInvalidRowCount()
+     {
++        repositoryStatisticsManagerControl.replay();
+         prepareAction( Collections.singletonList( INTERNAL ), Collections.singletonList( SNAPSHOTS ) );
+         action.setRowCount( 0 );
+         String result = action.generateStatistics();
+         assertEquals( Action.INPUT, result );
+         assertTrue( action.hasFieldErrors() );
++        repositoryStatisticsManagerControl.verify();
+     }
+     public void testGenerateStatisticsInvalidEndDate()
+     {
++        repositoryStatisticsManagerControl.replay();
+         prepareAction( Collections.singletonList( INTERNAL ), Collections.singletonList( SNAPSHOTS ) );
+         action.setStartDate( "2009/12/12" );
+         action.setEndDate( "2008/11/11" );
+         String result = action.generateStatistics();
+         assertEquals( Action.INPUT, result );
+         assertTrue( action.hasFieldErrors() );
++        repositoryStatisticsManagerControl.verify();
+     }
+     public void testGenerateStatisticsMalformedEndDate()
+     {
++        repositoryStatisticsManagerControl.replay();
+         prepareAction( Collections.singletonList( INTERNAL ), Collections.singletonList( SNAPSHOTS ) );
+         action.setEndDate( "This is not a date" );
+         String result = action.generateStatistics();
+         // TODO: should be an input error
+         assertEquals( Action.ERROR, result );
+         assertTrue( action.hasActionErrors() );
++        repositoryStatisticsManagerControl.verify();
+     }
+     public void testGenerateStatisticsInvalidEndDateMultiRepo()
+     {
++        repositoryStatisticsManagerControl.replay();
+         prepareAction( Arrays.asList( SNAPSHOTS, INTERNAL ), Collections.<String>emptyList() );
+         action.setStartDate( "2009/12/12" );
+         action.setEndDate( "2008/11/11" );
+         String result = action.generateStatistics();
+         assertEquals( Action.INPUT, result );
+         assertTrue( action.hasFieldErrors() );
++        repositoryStatisticsManagerControl.verify();
+     }
+     public void testGenerateStatisticsMalformedEndDateMultiRepo()
+     {
++        repositoryStatisticsManagerControl.replay();
+         prepareAction( Arrays.asList( SNAPSHOTS, INTERNAL ), Collections.<String>emptyList() );
+         action.setEndDate( "This is not a date" );
+         String result = action.generateStatistics();
+         // TODO: should be an input error
+         assertEquals( Action.ERROR, result );
+         assertTrue( action.hasActionErrors() );
++        repositoryStatisticsManagerControl.verify();
+     }
+     public void testGenerateStatisticsNoRepos()
+     {
++        repositoryStatisticsManagerControl.replay();
+         prepareAction( Collections.<String>emptyList(), Arrays.asList( SNAPSHOTS, INTERNAL ) );
+         String result = action.generateStatistics();
+         assertEquals( Action.INPUT, result );
+         assertTrue( action.hasFieldErrors() );
++        repositoryStatisticsManagerControl.verify();
+     }
+     public void testGenerateStatisticsSingleRepo()
+     {
++        repositoryStatisticsManagerControl.expectAndReturn(
++            repositoryStatisticsManager.getStatisticsInRange( INTERNAL, null, null ),
++            Collections.singletonList( createDefaultStats() ) );
++
++        repositoryStatisticsManagerControl.replay();
+         prepareAction( Collections.singletonList( INTERNAL ), Collections.singletonList( SNAPSHOTS ) );
+         String result = action.generateStatistics();
+         assertSuccessResult( result );
++        repositoryStatisticsManagerControl.verify();
+     }
+     public void testGenerateStatisticsSingleRepoNoStats()
+     {
 -        RepositoryContentStatisticsDAOStub dao =
 -            (RepositoryContentStatisticsDAOStub) lookup( RepositoryContentStatisticsDAO.class, "jdo" );
 -        dao.setStats( Collections.<RepositoryContentStatistics>emptyList() );
++        repositoryStatisticsManagerControl.expectAndReturn(
++            repositoryStatisticsManager.getStatisticsInRange( INTERNAL, null, null ), Collections.<Object>emptyList() );
++        repositoryStatisticsManagerControl.replay();
+         prepareAction( Collections.singletonList( INTERNAL ), Collections.singletonList( SNAPSHOTS ) );
+         String result = action.generateStatistics();
+         assertEquals( Action.ERROR, result );
+         assertTrue( action.hasActionErrors() );
++
++        repositoryStatisticsManagerControl.verify();
+     }
+     public void testGenerateStatisticsOvershotPages()
+     {
++        repositoryStatisticsManagerControl.expectAndReturn(
++            repositoryStatisticsManager.getStatisticsInRange( INTERNAL, null, null ),
++            Collections.singletonList( createDefaultStats() ) );
++        repositoryStatisticsManagerControl.replay();
+         action.setPage( 2 );
+         prepareAction( Collections.singletonList( INTERNAL ), Collections.singletonList( SNAPSHOTS ) );
+         String result = action.generateStatistics();
+         assertEquals( Action.ERROR, result );
+         assertTrue( action.hasActionErrors() );
++        repositoryStatisticsManagerControl.verify();
+     }
+     public void testGenerateStatisticsMultipleRepoNoResults()
+     {
 -        RepositoryContentStatisticsDAOStub dao =
 -            (RepositoryContentStatisticsDAOStub) lookup( RepositoryContentStatisticsDAO.class, "jdo" );
 -        RepositoryContentStatistics stats = dao.queryRepositoryContentStatistics(
 -            new RepositoryContentStatisticsByRepositoryConstraint( SNAPSHOTS ) ).get( 0 );
 -
++        repositoryStatisticsManagerControl.expectAndReturn(
++            repositoryStatisticsManager.getStatisticsInRange( SNAPSHOTS, null, null ),
++            Collections.<Object>emptyList() );
++        repositoryStatisticsManagerControl.expectAndReturn(
++            repositoryStatisticsManager.getStatisticsInRange( INTERNAL, null, null ), Collections.<Object>emptyList() );
++        repositoryStatisticsManagerControl.replay();
+         prepareAction( Arrays.asList( SNAPSHOTS, INTERNAL ), Collections.<String>emptyList() );
+         String result = action.generateStatistics();
+         assertEquals( GenerateReportAction.BLANK, result );
+         assertFalse( action.hasActionErrors() );
+         assertFalse( action.hasActionMessages() );
+         assertFalse( action.hasFieldErrors() );
++
++        repositoryStatisticsManagerControl.verify();
+     }
+     public void testGenerateStatisticsMultipleRepo()
+     {
++        repositoryStatisticsManagerControl.expectAndReturn(
++            repositoryStatisticsManager.getStatisticsInRange( SNAPSHOTS, null, null ),
++            Collections.singletonList( createDefaultStats() ) );
++        repositoryStatisticsManagerControl.expectAndReturn(
++            repositoryStatisticsManager.getStatisticsInRange( INTERNAL, null, null ),
++            Collections.singletonList( createDefaultStats() ) );
++
++        repositoryStatisticsManagerControl.replay();
+         prepareAction( Arrays.asList( SNAPSHOTS, INTERNAL ), Collections.<String>emptyList() );
+         String result = action.generateStatistics();
+         assertSuccessResult( result );
++        repositoryStatisticsManagerControl.verify();
+     }
+     public void testDownloadStatisticsSingleRepo()
+         throws IOException, ArchivaDatabaseException
+     {
++        Date date = new Date();
++        repositoryStatisticsManagerControl.expectAndReturn(
++            repositoryStatisticsManager.getStatisticsInRange( SNAPSHOTS, null, null ),
++            Collections.singletonList( createStats( date ) ) );
++        repositoryStatisticsManagerControl.replay();
++
+         prepareAction( Arrays.asList( SNAPSHOTS ), Arrays.asList( INTERNAL ) );
+         String result = action.downloadStatisticsReport();
+         assertEquals( GenerateReportAction.SEND_FILE, result );
+         assertFalse( action.hasActionErrors() );
+         assertFalse( action.hasFieldErrors() );
 -                stats.getWhenGathered() + ",0,0,0,0,0,1,0,1,1,0,0\n", IOUtils.toString( action.getInputStream() ) );
++//        assertEquals(
++//            "Date of Scan,Total File Count,Total Size,Artifact Count,Group Count,Project Count,Plugins,Archetypes,Jars,Wars,Deployments,Downloads\n" +
++//                date + ",0,0,0,0,0,1,0,1,1,0,0\n", IOUtils.toString( action.getInputStream() ) );
+         assertEquals(
+             "Date of Scan,Total File Count,Total Size,Artifact Count,Group Count,Project Count,Plugins,Archetypes,Jars,Wars,Deployments,Downloads\n" +
 -    private void assertMultiRepoCsvResult()
 -        throws IOException
++                date + ",0,0,0,0,0\n", IOUtils.toString( action.getInputStream() ) );
++        repositoryStatisticsManagerControl.verify();
+     }
+     public void testDownloadStatisticsMultipleRepos()
+         throws IOException, ArchivaDatabaseException
+     {
++        repositoryStatisticsManagerControl.expectAndReturn(
++            repositoryStatisticsManager.getStatisticsInRange( SNAPSHOTS, null, null ),
++            Collections.singletonList( createDefaultStats() ) );
++        repositoryStatisticsManagerControl.expectAndReturn(
++            repositoryStatisticsManager.getStatisticsInRange( INTERNAL, null, null ),
++            Collections.singletonList( createDefaultStats() ) );
++        repositoryStatisticsManagerControl.replay();
+         prepareAction( Arrays.asList( SNAPSHOTS, INTERNAL ), Collections.<String>emptyList() );
+         String result = action.downloadStatisticsReport();
+         assertEquals( GenerateReportAction.SEND_FILE, result );
+         assertFalse( action.hasActionErrors() );
+         assertFalse( action.hasFieldErrors() );
+         assertMultiRepoCsvResult();
++        repositoryStatisticsManagerControl.verify();
+     }
 -        assertEquals(
 -            "Repository,Total File Count,Total Size,Artifact Count,Group Count,Project Count,Plugins,Archetypes,Jars,Wars,Deployments,Downloads\n" +
 -                "snapshots,0,0,0,0,0,1,0,1,1,0,0\n" + "internal,0,0,0,0,0,1,0,1,1,0,0\n",
 -            IOUtils.toString( action.getInputStream() ) );
++    public void testDownloadStatisticsMalformedEndDateMultiRepo()
+     {
 -    public void testDownloadStatisticsMalformedEndDateMultiRepo()
++        repositoryStatisticsManagerControl.replay();
++        prepareAction( Arrays.asList( SNAPSHOTS, INTERNAL ), Collections.<String>emptyList() );
++
++        action.setEndDate( "This is not a date" );
++        String result = action.downloadStatisticsReport();
++
++        // TODO: should be an input error
++        assertEquals( Action.ERROR, result );
++        assertTrue( action.hasActionErrors() );
++        repositoryStatisticsManagerControl.verify();
+     }
 -        prepareAction( Arrays.asList( SNAPSHOTS, INTERNAL ), Collections.<String>emptyList() );
++    public void testDownloadStatisticsMalformedEndDateSingleRepo()
+     {
 -        RepositoryContentStatisticsDAOStub dao =
 -            (RepositoryContentStatisticsDAOStub) lookup( RepositoryContentStatisticsDAO.class, "jdo" );
 -        dao.setStats( Collections.<RepositoryContentStatistics>emptyList() );
++        repositoryStatisticsManagerControl.replay();
++        prepareAction( Arrays.asList( SNAPSHOTS ), Arrays.asList( INTERNAL ) );
+         action.setEndDate( "This is not a date" );
+         String result = action.downloadStatisticsReport();
+         // TODO: should be an input error
+         assertEquals( Action.ERROR, result );
+         assertTrue( action.hasActionErrors() );
++        repositoryStatisticsManagerControl.verify();
+     }
+     public void testDownloadStatisticsInvalidEndDateMultiRepo()
+     {
++        repositoryStatisticsManagerControl.replay();
+         prepareAction( Arrays.asList( SNAPSHOTS, INTERNAL ), Collections.<String>emptyList() );
+         action.setStartDate( "2009/12/12" );
+         action.setEndDate( "2008/11/11" );
+         String result = action.downloadStatisticsReport();
+         assertEquals( Action.INPUT, result );
+         assertTrue( action.hasFieldErrors() );
++        repositoryStatisticsManagerControl.verify();
+     }
+     public void testDownloadStatisticsInvalidEndDateSingleRepo()
+     {
++        repositoryStatisticsManagerControl.replay();
+         prepareAction( Arrays.asList( SNAPSHOTS ), Arrays.asList( INTERNAL ) );
+         action.setStartDate( "2009/12/12" );
+         action.setEndDate( "2008/11/11" );
+         String result = action.downloadStatisticsReport();
+         assertEquals( Action.INPUT, result );
+         assertTrue( action.hasFieldErrors() );
++        repositoryStatisticsManagerControl.verify();
+     }
+     public void testDownloadStatisticsSingleRepoNoStats()
+     {
 -        RepositoryContentStatisticsDAOStub dao =
 -            (RepositoryContentStatisticsDAOStub) lookup( RepositoryContentStatisticsDAO.class, "jdo" );
 -        dao.setStats( Collections.<RepositoryContentStatistics>emptyList() );
++        repositoryStatisticsManagerControl.expectAndReturn(
++            repositoryStatisticsManager.getStatisticsInRange( INTERNAL, null, null ), Collections.<Object>emptyList() );
++        repositoryStatisticsManagerControl.replay();
+         prepareAction( Collections.singletonList( INTERNAL ), Collections.singletonList( SNAPSHOTS ) );
+         String result = action.downloadStatisticsReport();
+         assertEquals( Action.ERROR, result );
+         assertTrue( action.hasActionErrors() );
++        repositoryStatisticsManagerControl.verify();
+     }
+     public void testDownloadStatisticsNoRepos()
+     {
++        repositoryStatisticsManagerControl.replay();
+         prepareAction( Collections.<String>emptyList(), Arrays.asList( SNAPSHOTS, INTERNAL ) );
+         String result = action.downloadStatisticsReport();
+         assertEquals( Action.INPUT, result );
+         assertTrue( action.hasFieldErrors() );
++        repositoryStatisticsManagerControl.verify();
+     }
+     public void testDownloadStatisticsMultipleRepoNoResults()
+     {
++        repositoryStatisticsManagerControl.expectAndReturn(
++            repositoryStatisticsManager.getStatisticsInRange( SNAPSHOTS, null, null ),
++            Collections.<Object>emptyList() );
++        repositoryStatisticsManagerControl.expectAndReturn(
++            repositoryStatisticsManager.getStatisticsInRange( INTERNAL, null, null ), Collections.<Object>emptyList() );
++        repositoryStatisticsManagerControl.replay();
+         prepareAction( Arrays.asList( SNAPSHOTS, INTERNAL ), Collections.<String>emptyList() );
+         String result = action.downloadStatisticsReport();
+         assertEquals( GenerateReportAction.BLANK, result );
+         assertFalse( action.hasActionErrors() );
+         assertFalse( action.hasActionMessages() );
+         assertFalse( action.hasFieldErrors() );
++        repositoryStatisticsManagerControl.verify();
+     }
+     public void testDownloadStatisticsMultipleRepoInStrutsFormat()
+         throws IOException
+     {
++        repositoryStatisticsManagerControl.expectAndReturn(
++            repositoryStatisticsManager.getStatisticsInRange( SNAPSHOTS, null, null ),
++            Collections.singletonList( createDefaultStats() ) );
++        repositoryStatisticsManagerControl.expectAndReturn(
++            repositoryStatisticsManager.getStatisticsInRange( INTERNAL, null, null ),
++            Collections.singletonList( createDefaultStats() ) );
++        repositoryStatisticsManagerControl.replay();
+         prepareAction( Arrays.asList( SNAPSHOTS, INTERNAL ), Collections.<String>emptyList() );
+         action.setSelectedRepositories( Collections.singletonList( "[" + SNAPSHOTS + "],[" + INTERNAL + "]" ) );
+         String result = action.downloadStatisticsReport();
+         assertEquals( GenerateReportAction.SEND_FILE, result );
+         assertFalse( action.hasActionErrors() );
+         assertFalse( action.hasFieldErrors() );
+         assertMultiRepoCsvResult();
++        repositoryStatisticsManagerControl.verify();
+     }
+     public void testHealthReportSingleRepo()
+         throws Exception
+     {
+         RepositoryProblem problem1 = createProblem( GROUP_ID, "artifactId", INTERNAL );
+         RepositoryProblem problem2 = createProblem( GROUP_ID, "artifactId-2", INTERNAL );
+         repositoryProblemDAOControl.expectAndReturn( repositoryProblemDAO.queryRepositoryProblems(
+             new RepositoryProblemByRepositoryIdConstraint( new int[]{0, 101}, INTERNAL ) ),
+                                                      Arrays.asList( problem1, problem2 ) );
+         repositoryProblemDAOControl.replay();
+         action.setRepositoryId( INTERNAL );
+         ServletRunner sr = new ServletRunner();
+         ServletUnitClient sc = sr.newClient();
+         action.setServletRequest( sc.newInvocation( URL ).getRequest() );
+         prepareAction( Collections.<String>emptyList(), Arrays.asList( SNAPSHOTS, INTERNAL ) );
+         String result = action.execute();
+         assertSuccessResult( result );
+         RepositoryProblemReport problemReport1 = createProblemReport( problem1 );
+         RepositoryProblemReport problemReport2 = createProblemReport( problem2 );
+         assertEquals( Collections.singleton( INTERNAL ), action.getRepositoriesMap().keySet() );
+         assertEquals( Arrays.asList( problemReport1, problemReport2 ), action.getRepositoriesMap().get( INTERNAL ) );
+         repositoryProblemDAOControl.verify();
+     }
+     public void testHealthReportInvalidRowCount()
+         throws Exception
+     {
+         repositoryProblemDAOControl.replay();
+         action.setRowCount( 0 );
+         action.setRepositoryId( INTERNAL );
+         ServletRunner sr = new ServletRunner();
+         ServletUnitClient sc = sr.newClient();
+         action.setServletRequest( sc.newInvocation( URL ).getRequest() );
+         prepareAction( Collections.<String>emptyList(), Arrays.asList( SNAPSHOTS, INTERNAL ) );
+         String result = action.execute();
+         assertEquals( Action.INPUT, result );
+         assertFalse( action.hasActionErrors() );
+         assertTrue( action.hasFieldErrors() );
+         repositoryProblemDAOControl.verify();
+     }
+     public void testHealthReportAllRepos()
+         throws Exception
+     {
+         RepositoryProblem problem1 = createProblem( GROUP_ID, "artifactId", INTERNAL );
+         RepositoryProblem problem2 = createProblem( GROUP_ID, "artifactId-2", SNAPSHOTS );
+         repositoryProblemDAOControl.expectAndReturn(
+             repositoryProblemDAO.queryRepositoryProblems( new RangeConstraint( new int[]{0, 101} ) ),
+             Arrays.asList( problem1, problem2 ) );
+         repositoryProblemDAOControl.replay();
+         action.setRepositoryId( GenerateReportAction.ALL_REPOSITORIES );
+         ServletRunner sr = new ServletRunner();
+         ServletUnitClient sc = sr.newClient();
+         action.setServletRequest( sc.newInvocation( URL ).getRequest() );
+         prepareAction( Collections.<String>emptyList(), Arrays.asList( SNAPSHOTS, INTERNAL ) );
+         String result = action.execute();
+         assertSuccessResult( result );
+         RepositoryProblemReport problemReport1 = createProblemReport( problem1 );
+         RepositoryProblemReport problemReport2 = createProblemReport( problem2 );
+         assertEquals( Arrays.asList( INTERNAL, SNAPSHOTS ),
+                       new ArrayList<String>( action.getRepositoriesMap().keySet() ) );
+         assertEquals( Arrays.asList( problemReport1 ), action.getRepositoriesMap().get( INTERNAL ) );
+         assertEquals( Arrays.asList( problemReport2 ), action.getRepositoriesMap().get( SNAPSHOTS ) );
+         repositoryProblemDAOControl.verify();
+     }
+     public void testHealthReportSingleRepoByCorrectGroupId()
+         throws Exception
+     {
+         RepositoryProblem problem1 = createProblem( GROUP_ID, "artifactId", INTERNAL );
+         RepositoryProblem problem2 = createProblem( GROUP_ID, "artifactId-2", INTERNAL );
+         repositoryProblemDAOControl.expectAndReturn( repositoryProblemDAO.queryRepositoryProblems(
+             new RepositoryProblemConstraint( new int[]{0, 101}, GROUP_ID, INTERNAL ) ),
+                                                      Arrays.asList( problem1, problem2 ) );
+         repositoryProblemDAOControl.replay();
+         action.setGroupId( GROUP_ID );
+         action.setRepositoryId( INTERNAL );
+         ServletRunner sr = new ServletRunner();
+         ServletUnitClient sc = sr.newClient();
+         action.setServletRequest( sc.newInvocation( URL ).getRequest() );
+         prepareAction( Collections.<String>emptyList(), Arrays.asList( SNAPSHOTS, INTERNAL ) );
+         String result = action.execute();
+         assertSuccessResult( result );
+         RepositoryProblemReport problemReport1 = createProblemReport( problem1 );
+         RepositoryProblemReport problemReport2 = createProblemReport( problem2 );
+         assertEquals( Collections.singleton( INTERNAL ), action.getRepositoriesMap().keySet() );
+         assertEquals( Arrays.asList( problemReport1, problemReport2 ), action.getRepositoriesMap().get( INTERNAL ) );
+         repositoryProblemDAOControl.verify();
+     }
+     public void testHealthReportSingleRepoByCorrectGroupIdAllRepositories()
+         throws Exception
+     {
+         RepositoryProblem problem1 = createProblem( GROUP_ID, "artifactId", INTERNAL );
+         RepositoryProblem problem2 = createProblem( GROUP_ID, "artifactId-2", SNAPSHOTS );
+         repositoryProblemDAOControl.expectAndReturn( repositoryProblemDAO.queryRepositoryProblems(
+             new RepositoryProblemByGroupIdConstraint( new int[]{0, 101}, GROUP_ID ) ),
+                                                      Arrays.asList( problem1, problem2 ) );
+         repositoryProblemDAOControl.replay();
+         action.setGroupId( GROUP_ID );
+         action.setRepositoryId( GenerateReportAction.ALL_REPOSITORIES );
+         ServletRunner sr = new ServletRunner();
+         ServletUnitClient sc = sr.newClient();
+         action.setServletRequest( sc.newInvocation( URL ).getRequest() );
+         prepareAction( Collections.<String>emptyList(), Arrays.asList( SNAPSHOTS, INTERNAL ) );
+         String result = action.execute();
+         assertSuccessResult( result );
+         RepositoryProblemReport problemReport1 = createProblemReport( problem1 );
+         RepositoryProblemReport problemReport2 = createProblemReport( problem2 );
+         assertEquals( Arrays.asList( INTERNAL, SNAPSHOTS ),
+                       new ArrayList<String>( action.getRepositoriesMap().keySet() ) );
+         assertEquals( Arrays.asList( problemReport1 ), action.getRepositoriesMap().get( INTERNAL ) );
+         assertEquals( Arrays.asList( problemReport2 ), action.getRepositoriesMap().get( SNAPSHOTS ) );
+         repositoryProblemDAOControl.verify();
+     }
+     public void testHealthReportSingleRepoByIncorrectGroupId()
+         throws Exception
+     {
+         repositoryProblemDAOControl.expectAndReturn( repositoryProblemDAO.queryRepositoryProblems(
+             new RepositoryProblemConstraint( new int[]{0, 101}, "not.it", INTERNAL ) ),
+                                                      Collections.<Object>emptyList() );
+         repositoryProblemDAOControl.replay();
+         action.setGroupId( "not.it" );
+         action.setRepositoryId( INTERNAL );
+         ServletRunner sr = new ServletRunner();
+         ServletUnitClient sc = sr.newClient();
+         action.setServletRequest( sc.newInvocation( URL ).getRequest() );
+         prepareAction( Collections.<String>emptyList(), Arrays.asList( SNAPSHOTS, INTERNAL ) );
+         String result = action.execute();
+         assertEquals( GenerateReportAction.BLANK, result );
+         assertFalse( action.hasActionErrors() );
+         assertFalse( action.hasFieldErrors() );
+         repositoryProblemDAOControl.verify();
+     }
++    private void assertMultiRepoCsvResult()
++        throws IOException
++    {
++//        assertEquals(
++//            "Repository,Total File Count,Total Size,Artifact Count,Group Count,Project Count,Plugins,Archetypes,Jars,Wars,Deployments,Downloads\n" +
++//                "snapshots,0,0,0,0,0,1,0,1,1,0,0\n" + "internal,0,0,0,0,0,1,0,1,1,0,0\n",
++        assertEquals(
++            "Repository,Total File Count,Total Size,Artifact Count,Group Count,Project Count,Plugins,Archetypes,Jars,Wars,Deployments,Downloads\n" +
++                "snapshots,0,0,0,0,0\n" + "internal,0,0,0,0,0\n", IOUtils.toString( action.getInputStream() ) );
++    }
++
+     private RepositoryProblemReport createProblemReport( RepositoryProblem problem )
+     {
+         RepositoryProblemReport problemReport = new RepositoryProblemReport( problem );
+         problemReport.setGroupURL( "http://localhost/browse/" + problem.getGroupId() );
+         problemReport.setArtifactURL( problemReport.getGroupURL() + "/" + problem.getArtifactId() );
+         return problemReport;
+     }
+     private RepositoryProblem createProblem( String groupId, String artifactId, String repoId )
+     {
+         RepositoryProblem problem = new RepositoryProblem();
+         problem.setRepositoryId( repoId );
+         problem.setGroupId( groupId );
+         problem.setArtifactId( artifactId );
+         return problem;
+     }
+     public void testHealthReportNoRepositoryId()
+         throws Exception
+     {
+         prepareAction( Collections.<String>emptyList(), Arrays.asList( SNAPSHOTS, INTERNAL ) );
+         String result = action.execute();
+         assertEquals( Action.INPUT, result );
+         assertTrue( action.hasFieldErrors() );
+     }
+     private void assertSuccessResult( String result )
+     {
+         assertEquals( Action.SUCCESS, result );
+         assertFalse( action.hasActionErrors() );
+         assertFalse( action.hasFieldErrors() );
+     }
++
++    private RepositoryStatistics createDefaultStats()
++    {
++        return createStats( new Date() );
++    }
++
++    private RepositoryStatistics createStats( Date date )
++    {
++        RepositoryStatistics stats = new RepositoryStatistics();
++        stats.setScanStartTime( date );
++        return stats;
++    }
+ }
index 0000000000000000000000000000000000000000,dc68e12af815d01802e1d5168870d1d427b46aa3..44882614c909102bc9e5e815ba24e8e1c3bc570e
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,49 +1,39 @@@
 -        <requirement>
 -          <role>org.apache.maven.archiva.database.RepositoryContentStatisticsDAO</role>
 -          <role-hint>jdo</role-hint>
 -        </requirement>
+ <!--
+   ~ Licensed to the Apache Software Foundation (ASF) under one
+   ~ or more contributor license agreements.  See the NOTICE file
+   ~ distributed with this work for additional information
+   ~ regarding copyright ownership.  The ASF licenses this file
+   ~ to you under the Apache License, Version 2.0 (the
+   ~ "License"); you may not use this file except in compliance
+   ~ with the License.  You may obtain a copy of the License at
+   ~
+   ~   http://www.apache.org/licenses/LICENSE-2.0
+   ~
+   ~ Unless required by applicable law or agreed to in writing,
+   ~ software distributed under the License is distributed on an
+   ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+   ~ KIND, either express or implied.  See the License for the
+   ~ specific language governing permissions and limitations
+   ~ under the License.
+   -->
+ <component-set>
+   <components>
+     <component>
+       <role>org.apache.maven.archiva.database.ArchivaDAO</role>
+       <role-hint>jdo</role-hint>
+       <implementation>org.apache.maven.archiva.web.action.admin.repositories.ArchivaDAOStub</implementation>
+       <requirements>
+         <requirement>
+           <role>org.apache.maven.archiva.database.ArtifactDAO</role>
+           <role-hint>jdo</role-hint>
+         </requirement>
 -    <component>
 -      <role>org.apache.maven.archiva.database.RepositoryContentStatisticsDAO</role>
 -      <role-hint>jdo</role-hint>
 -      <implementation>org.apache.maven.archiva.web.action.admin.repositories.RepositoryContentStatisticsDAOStub
 -      </implementation>
 -    </component>
+       </requirements>
+     </component>
+     <component>
+       <role>org.apache.maven.archiva.database.ArtifactDAO</role>
+       <role-hint>jdo</role-hint>
+       <implementation>org.apache.maven.archiva.web.action.admin.repositories.ArtifactDAOStub</implementation>
+     </component>
+   </components>
+ </component-set>
index 9fc14104f9ea4b7f07e77c2e39688a04f00863c2,0000000000000000000000000000000000000000..b47e1166fa10db3c06a83e86fd963fcbdcb38a91
mode 100644,000000..100644
--- /dev/null
@@@ -1,126 -1,0 +1,127 @@@
-                 if ( !date.before( startTime ) && !date.after( endTime ) )
 +package org.apache.archiva.metadata.repository.stats;
 +
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *   http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied.  See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +
 +import java.text.DateFormat;
 +import java.text.ParseException;
 +import java.text.SimpleDateFormat;
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.Date;
 +import java.util.List;
 +
 +import org.apache.archiva.metadata.repository.MetadataRepository;
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
 +
 +/**
 + * @plexus.component role="org.apache.archiva.metadata.repository.stats.RepositoryStatisticsManager" role-hint="default"
 + */
 +public class DefaultRepositoryStatisticsManager
 +    implements RepositoryStatisticsManager
 +{
 +    private static final Logger log = LoggerFactory.getLogger( DefaultRepositoryStatisticsManager.class );
 +
 +    /**
 +     * @plexus.requirement
 +     */
 +    private MetadataRepository metadataRepository;
 +
 +    static final DateFormat SCAN_TIMESTAMP = new SimpleDateFormat( "yyyyMMdd.HHmmss.SSS" );
 +
 +    public RepositoryStatistics getLastStatistics( String repositoryId )
 +    {
 +        // TODO: consider a more efficient implementation that directly gets the last one from the content repository
 +        List<String> scans = metadataRepository.getMetadataFacets( repositoryId, RepositoryStatistics.FACET_ID );
 +        Collections.sort( scans );
 +        if ( !scans.isEmpty() )
 +        {
 +            String name = scans.get( scans.size() - 1 );
 +            return (RepositoryStatistics) metadataRepository.getMetadataFacet( repositoryId,
 +                                                                               RepositoryStatistics.FACET_ID, name );
 +        }
 +        else
 +        {
 +            return null;
 +        }
 +    }
 +
 +    public void addStatisticsAfterScan( String repositoryId, RepositoryStatistics repositoryStatistics )
 +    {
 +        // In the future, instead of being tied to a scan we might want to record information in the fly based on
 +        // events that are occurring. Even without these totals we could query much of the information on demand based
 +        // on information from the metadata content repository. In the mean time, we lock information in at scan time.
 +        // Note that if new types are later discoverable due to a code change or new plugin, historical stats will not
 +        // be updated and the repository will need to be rescanned.
 +
 +        // TODO, populate these and also a count per artifact type
 +        // populate total artifact count from content repository
 +//        repositoryStatistics.setTotalArtifactCount(  );
 +        // populate total size from content repository
 +//        repositoryStatistics.setTotalArtifactFileSize(  );
 +        // populate total group count from content repository
 +//        repositoryStatistics.setTotalGroupCount(  );
 +        // populate total project count from content repository
 +//        repositoryStatistics.setTotalProjectCount(  );
 +
 +        metadataRepository.addMetadataFacet( repositoryId, RepositoryStatistics.FACET_ID,
 +                                             SCAN_TIMESTAMP.format( repositoryStatistics.getScanStartTime() ),
 +                                             repositoryStatistics );
 +    }
 +
 +    public void deleteStatistics( String repositoryId )
 +    {
 +        metadataRepository.removeMetadataFacets( repositoryId, RepositoryStatistics.FACET_ID );
 +    }
 +
 +    public List<RepositoryStatistics> getStatisticsInRange( String repositoryId, Date startTime, Date endTime )
 +    {
 +        List<RepositoryStatistics> results = new ArrayList<RepositoryStatistics>();
 +        List<String> list = metadataRepository.getMetadataFacets( repositoryId, RepositoryStatistics.FACET_ID );
 +        Collections.sort( list, Collections.reverseOrder() );
 +        for ( String name : list )
 +        {
 +            try
 +            {
 +                Date date = SCAN_TIMESTAMP.parse( name );
++                if ( ( startTime == null || !date.before( startTime ) ) &&
++                    ( endTime == null || !date.after( endTime ) ) )
 +                {
 +                    RepositoryStatistics stats =
 +                        (RepositoryStatistics) metadataRepository.getMetadataFacet( repositoryId,
 +                                                                                    RepositoryStatistics.FACET_ID,
 +                                                                                    name );
 +                    results.add( stats );
 +                }
 +            }
 +            catch ( ParseException e )
 +            {
 +                log.error( "Invalid scan result found in the metadata repository: " + e.getMessage() );
 +                // continue and ignore this one
 +            }
 +        }
 +        return results;
 +    }
 +
 +    public void setMetadataRepository( MetadataRepository metadataRepository )
 +    {
 +        this.metadataRepository = metadataRepository;
 +    }
 +}