You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@continuum.apache.org by ba...@apache.org on 2015/05/04 03:42:06 UTC
svn commit: r1677500 - in /continuum/trunk:
continuum-api/src/main/java/org/apache/continuum/dao/
continuum-api/src/main/java/org/apache/maven/continuum/
continuum-core/src/main/java/org/apache/maven/continuum/
continuum-core/src/test/java/org/apache/m...
Author: batkinson
Date: Mon May 4 01:42:05 2015
New Revision: 1677500
URL: http://svn.apache.org/r1677500
Log:
[CONTINUUM-2760] Change API to prevent unbounded build results access
Also addressed:
[CONTINUUM-2746] Running build reports for a large number of builds can crash continuum
[CONTINUUM-2537] Allow navigation to build result details from a build report
Modified:
continuum/trunk/continuum-api/src/main/java/org/apache/continuum/dao/BuildResultDao.java
continuum/trunk/continuum-api/src/main/java/org/apache/maven/continuum/Continuum.java
continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/DefaultContinuum.java
continuum/trunk/continuum-core/src/test/java/org/apache/maven/continuum/DefaultContinuumTest.java
continuum/trunk/continuum-store/src/main/java/org/apache/continuum/dao/BuildResultDaoImpl.java
continuum/trunk/continuum-store/src/test/java/org/apache/maven/continuum/store/ContinuumStoreTest.java
continuum/trunk/continuum-webapp/src/main/java/org/apache/continuum/web/action/ViewBuildsReportAction.java
continuum/trunk/continuum-webapp/src/main/java/org/apache/maven/continuum/web/action/ProjectGroupAction.java
continuum/trunk/continuum-webapp/src/main/resources/localization/Continuum.properties
continuum/trunk/continuum-webapp/src/main/resources/struts.xml
continuum/trunk/continuum-webapp/src/main/webapp/WEB-INF/jsp/viewProjectBuildsReport.jsp
continuum/trunk/continuum-webapp/src/test/java/org/apache/continuum/web/action/ViewBuildsReportActionTest.java
continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-api/src/main/java/org/apache/maven/continuum/xmlrpc/ContinuumService.java
continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-backup/src/main/java/org/apache/maven/continuum/xmlrpc/backup/Backup.java
continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-client/src/main/java/org/apache/maven/continuum/xmlrpc/client/BuildResultsPurge.java
continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-client/src/main/java/org/apache/maven/continuum/xmlrpc/client/ContinuumXmlRpcClient.java
continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-client/src/main/java/org/apache/maven/continuum/xmlrpc/client/SampleClient.java
continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-server/src/main/java/org/apache/maven/continuum/xmlrpc/server/ContinuumServiceImpl.java
Modified: continuum/trunk/continuum-api/src/main/java/org/apache/continuum/dao/BuildResultDao.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-api/src/main/java/org/apache/continuum/dao/BuildResultDao.java?rev=1677500&r1=1677499&r2=1677500&view=diff
==============================================================================
--- continuum/trunk/continuum-api/src/main/java/org/apache/continuum/dao/BuildResultDao.java (original)
+++ continuum/trunk/continuum-api/src/main/java/org/apache/continuum/dao/BuildResultDao.java Mon May 4 01:42:05 2015
@@ -72,12 +72,12 @@ public interface BuildResultDao
/**
* Returns the list of build results between the fromdate and the buildResult defined by its toBuildResultId
*
- * @param projectId The project id
- * @param fromDate the from date
- * @param tobuildResultId the build result id
+ * @param projectId The project id
+ * @param fromResultId the from date
+ * @param toResultId the build result id
* @return the list of build results
*/
- List<BuildResult> getBuildResultsForProjectWithDetails( int projectId, long fromDate, int tobuildResultId );
+ List<BuildResult> getBuildResultsForProjectWithDetails( int projectId, int fromResultId, int toResultId );
/**
* Returns the number of build results in success since fromDate
@@ -88,9 +88,16 @@ public interface BuildResultDao
*/
long getNbBuildResultsInSuccessForProject( int projectId, long fromDate );
- List<BuildResult> getBuildResultsForProject( int projectId );
-
- List<BuildResult> getBuildResultsForProject( int projectId, long startIndex, long endIndex );
+ /**
+ * Looks up a range of build results for a project ordered from most recent to earliest.
+ *
+ * @param projectId id of project to fetch results for
+ * @param startIndex zero-based index indicating start of result interval
+ * @param endIndex zero-based index indicating boundary of result interval
+ * @param fullDetails whether to use a fetch plan that includes full build result details
+ * @return a list of results in the interval [start, end)
+ */
+ List<BuildResult> getBuildResultsForProject( int projectId, long startIndex, long endIndex, boolean fullDetails );
/**
* @param projectId
@@ -115,5 +122,5 @@ public interface BuildResultDao
List<BuildResult> getAllBuildsForAProjectByDate( int projectId );
List<BuildResult> getBuildResultsInRange( Date fromDate, Date toDate, int state, String triggeredBy,
- int projectGroupId );
+ int projectGroupId, int offset, int length );
}
Modified: continuum/trunk/continuum-api/src/main/java/org/apache/maven/continuum/Continuum.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-api/src/main/java/org/apache/maven/continuum/Continuum.java?rev=1677500&r1=1677499&r2=1677500&view=diff
==============================================================================
--- continuum/trunk/continuum-api/src/main/java/org/apache/maven/continuum/Continuum.java (original)
+++ continuum/trunk/continuum-api/src/main/java/org/apache/maven/continuum/Continuum.java Mon May 4 01:42:05 2015
@@ -108,7 +108,6 @@ public interface Continuum
void removeProject( int projectId )
throws ContinuumException;
-
/**
* @param projectId
* @throws ContinuumException
@@ -199,9 +198,6 @@ public interface Continuum
long getNbBuildResultsForProject( int projectId );
- Collection<BuildResult> getBuildResultsForProject( int projectId )
- throws ContinuumException;
-
Collection<BuildResult> getBuildResultsForProject( int projectId, int offset, int length )
throws ContinuumException;
@@ -212,7 +208,7 @@ public interface Continuum
throws ContinuumException;
List<BuildResult> getBuildResultsInRange( int projectGroupId, Date fromDate, Date toDate, int state,
- String triggeredBy );
+ String triggeredBy, int offset, int length );
// ----------------------------------------------------------------------
// Projects
@@ -300,7 +296,6 @@ public interface Continuum
boolean useCredentialsCache )
throws ContinuumException;
-
/**
* Add a Maven 2 project to the list of projects.
*
@@ -457,7 +452,6 @@ public interface Continuum
/**
* returns the default build definition for the project
- * <p/>
* 1) if project has default build definition, return that
* 2) otherwise return default build definition for parent project group
*
Modified: continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/DefaultContinuum.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/DefaultContinuum.java?rev=1677500&r1=1677499&r2=1677500&view=diff
==============================================================================
--- continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/DefaultContinuum.java (original)
+++ continuum/trunk/continuum-core/src/main/java/org/apache/maven/continuum/DefaultContinuum.java Mon May 4 01:42:05 2015
@@ -541,9 +541,10 @@ public class DefaultContinuum
}
public List<BuildResult> getBuildResultsInRange( int projectGroupId, Date fromDate, Date toDate, int state,
- String triggeredBy )
+ String triggeredBy, int offset, int length )
{
- return buildResultDao.getBuildResultsInRange( fromDate, toDate, state, triggeredBy, projectGroupId );
+ return buildResultDao.getBuildResultsInRange( fromDate, toDate, state, triggeredBy, projectGroupId, offset,
+ length );
}
// ----------------------------------------------------------------------
@@ -630,16 +631,22 @@ public class DefaultContinuum
project.getDependencies().clear();
projectDao.updateProject( project );
- Collection<BuildResult> buildResults = getBuildResultsForProject( projectId );
-
- for ( BuildResult br : buildResults )
- {
- br.setBuildDefinition( null );
- //Remove all modified dependencies to prevent SQL errors
- br.getModifiedDependencies().clear();
- buildResultDao.updateBuildResult( br );
- removeBuildResult( br );
+ // Remove build results in batches: the number of results could be very large
+ int batchSize = 100;
+ Collection<BuildResult> buildResults;
+ do
+ {
+ buildResults = buildResultDao.getBuildResultsForProject( projectId, 0, batchSize, true );
+ for ( BuildResult br : buildResults )
+ {
+ br.setBuildDefinition( null );
+ //Remove all modified dependencies to prevent SQL errors
+ br.getModifiedDependencies().clear();
+ buildResultDao.updateBuildResult( br );
+ removeBuildResult( br );
+ }
}
+ while ( buildResults != null && buildResults.size() > 0 );
File workingDirectory = getWorkingDirectory( projectId );
@@ -1145,11 +1152,9 @@ public class DefaultContinuum
{
//No previous build in success, Nothing to do
}
- long startTime = previousBuildResult == null ? 0 : previousBuildResult.getStartTime();
+ int lastSuccessId = previousBuildResult == null ? 0 : previousBuildResult.getId();
ArrayList<BuildResult> buildResults = new ArrayList<BuildResult>(
- buildResultDao.getBuildResultsForProjectWithDetails( projectId, startTime, buildResultId ) );
-
- Collections.reverse( buildResults );
+ buildResultDao.getBuildResultsForProjectWithDetails( projectId, lastSuccessId, buildResultId ) );
Iterator<BuildResult> buildResultsIterator = buildResults.iterator();
@@ -2809,16 +2814,10 @@ public class DefaultContinuum
return buildResultDao.getNbBuildResultsForProject( projectId );
}
- public Collection<BuildResult> getBuildResultsForProject( int projectId )
- throws ContinuumException
- {
- return buildResultDao.getBuildResultsForProject( projectId );
- }
-
public Collection<BuildResult> getBuildResultsForProject( int projectId, int offset, int length )
throws ContinuumException
{
- return buildResultDao.getBuildResultsForProject( projectId, offset, offset + length );
+ return buildResultDao.getBuildResultsForProject( projectId, offset, offset + length, false );
}
// ----------------------------------------------------------------------
Modified: continuum/trunk/continuum-core/src/test/java/org/apache/maven/continuum/DefaultContinuumTest.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-core/src/test/java/org/apache/maven/continuum/DefaultContinuumTest.java?rev=1677500&r1=1677499&r2=1677500&view=diff
==============================================================================
--- continuum/trunk/continuum-core/src/test/java/org/apache/maven/continuum/DefaultContinuumTest.java (original)
+++ continuum/trunk/continuum-core/src/test/java/org/apache/maven/continuum/DefaultContinuumTest.java Mon May 4 01:42:05 2015
@@ -280,7 +280,7 @@ public class DefaultContinuumTest
getBuildResultDao().addBuildResult( project, buildResult );
- Collection<BuildResult> brs = continuum.getBuildResultsForProject( project.getId() );
+ Collection<BuildResult> brs = continuum.getBuildResultsForProject( project.getId(), 0, 5 );
assertEquals( "Build result of project was not added", 1, brs.size() );
@@ -295,7 +295,7 @@ public class DefaultContinuumTest
}
catch ( ContinuumException expected )
{
- brs = continuum.getBuildResultsForProject( project.getId() );
+ brs = continuum.getBuildResultsForProject( project.getId(), 0, 5 );
assertEquals( "Build result of project was not removed", 0, brs.size() );
}
Modified: continuum/trunk/continuum-store/src/main/java/org/apache/continuum/dao/BuildResultDaoImpl.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-store/src/main/java/org/apache/continuum/dao/BuildResultDaoImpl.java?rev=1677500&r1=1677499&r2=1677500&view=diff
==============================================================================
--- continuum/trunk/continuum-store/src/main/java/org/apache/continuum/dao/BuildResultDaoImpl.java (original)
+++ continuum/trunk/continuum-store/src/main/java/org/apache/continuum/dao/BuildResultDaoImpl.java Mon May 4 01:42:05 2015
@@ -258,7 +258,7 @@ public class BuildResultDaoImpl
query.setFilter( "this.project.id == projectId && this.buildDefinition.id == buildDefinitionId "
+ "&& this.id < buildResultId" );
- query.setOrdering( "id descending" );
+ query.setOrdering( "this.id descending" );
Object[] params = new Object[3];
params[0] = projectId;
@@ -520,7 +520,7 @@ public class BuildResultDaoImpl
}
}
- public List<BuildResult> getBuildResultsForProjectWithDetails( int projectId, long fromDate, int tobuildResultId )
+ public List<BuildResult> getBuildResultsForProjectWithDetails( int projectId, int fromResultId, int toResultId )
{
PersistenceManager pm = getPersistenceManager();
@@ -535,29 +535,28 @@ public class BuildResultDaoImpl
Query query = pm.newQuery( extent );
- String parameters = "int projectId, long fromDate";
- String filter = "this.project.id == projectId && this.startTime > fromDate";
+ String parameters = "int projectId, int fromResultId";
+ String filter = "this.project.id == projectId && this.id > fromResultId";
- if ( tobuildResultId > 0 )
+ if ( toResultId > 0 )
{
- parameters += ", int buildResultId";
- filter += " && this.id < buildResultId";
+ parameters += ", int toResultId";
+ filter += " && this.id < toResultId";
}
- query.declareParameters( parameters );
+ query.declareParameters( parameters );
query.setFilter( filter );
-
- query.setOrdering( "this.id descending" );
+ query.setOrdering( "this.id ascending" );
List<BuildResult> result;
- if ( tobuildResultId > 0 )
+ if ( toResultId > 0 )
{
- result = (List<BuildResult>) query.execute( projectId, fromDate, tobuildResultId );
+ result = (List<BuildResult>) query.execute( projectId, fromResultId, toResultId );
}
else
{
- result = (List<BuildResult>) query.execute( projectId, fromDate );
+ result = (List<BuildResult>) query.execute( projectId, fromResultId );
}
result = (List<BuildResult>) pm.detachCopyAll( result );
@@ -572,15 +571,10 @@ public class BuildResultDaoImpl
}
}
- public List<BuildResult> getBuildResultsForProject( int projectId )
- {
- return getBuildResultsForProjectWithDetails( projectId, -1, -1 );
- }
-
- public List<BuildResult> getBuildResultsForProject( int projectId, long startIndex, long endIndex )
+ public List<BuildResult> getBuildResultsForProject( int projectId, long startIndex, long endIndex,
+ boolean fullDetails )
{
PersistenceManager pm = getPersistenceManager();
-
Transaction tx = pm.currentTransaction();
try
@@ -589,17 +583,17 @@ public class BuildResultDaoImpl
Extent extent = pm.getExtent( BuildResult.class, true );
+ if ( fullDetails )
+ {
+ pm.getFetchPlan().addGroup( BUILD_RESULT_WITH_DETAILS_FETCH_GROUP );
+ }
+
Query query = pm.newQuery( extent );
query.declareParameters( "int projectId" );
-
query.setFilter( "this.project.id == projectId" );
-
- if ( startIndex >= 0 )
- {
- query.setRange( startIndex, endIndex );
- }
query.setOrdering( "this.id descending" );
+ query.setRange( startIndex, endIndex );
List<BuildResult> result = (List<BuildResult>) query.execute( projectId );
@@ -692,41 +686,6 @@ public class BuildResultDaoImpl
}
}
- private int getPreviousBuildResultIdInSuccess( int projectId, int buildResultId )
- {
- PersistenceManager pm = getPersistenceManager();
-
- Transaction tx = pm.currentTransaction();
-
- try
- {
- tx.begin();
-
- Extent extent = pm.getExtent( BuildResult.class, true );
-
- Query query = pm.newQuery( extent );
-
- query.declareParameters( "int projectId, int buildResultId" );
-
- String filter = "this.id < buildResultId && this.state == " + ContinuumProjectState.OK +
- " && this.project.id == projectId";
-
- query.setFilter( filter );
-
- query.setResult( "max(this.id)" );
-
- int result = (Integer) query.execute( projectId, buildResultId );
-
- tx.commit();
-
- return result;
- }
- finally
- {
- rollback( tx );
- }
- }
-
public Set<Integer> resolveOrphanedInProgressResults( long ageCutoff )
throws ContinuumStoreException
{
@@ -763,13 +722,37 @@ public class BuildResultDaoImpl
public BuildResult getPreviousBuildResultInSuccess( int projectId, int buildResultId )
throws ContinuumStoreException
{
+ PersistenceManager pm = getPersistenceManager();
+
+ Transaction tx = pm.currentTransaction();
+
try
{
- return getBuildResult( getPreviousBuildResultIdInSuccess( projectId, buildResultId ) );
+ tx.begin();
+
+ Extent extent = pm.getExtent( BuildResult.class, true );
+
+ Query query = pm.newQuery( extent );
+
+ query.declareParameters( "int projectId, int buildResultId" );
+
+ String filter = "this.project.id == projectId"
+ + " && this.state == " + ContinuumProjectState.OK
+ + " && this.id < buildResultId";
+
+ query.setFilter( filter );
+ query.setOrdering( "this.id descending" );
+ query.setRange( 0, 1 );
+
+ List<BuildResult> results = (List<BuildResult>) query.execute( projectId, buildResultId );
+
+ tx.commit();
+
+ return results.size() > 0 ? results.get( 0 ) : null;
}
- catch ( NullPointerException e )
+ finally
{
- return null;
+ rollback( tx );
}
}
@@ -832,12 +815,10 @@ public class BuildResultDaoImpl
return null;
}
- @SuppressWarnings( "unchecked" )
public List<BuildResult> getBuildResultsInRange( Date fromDate, Date toDate, int state, String triggeredBy,
- int projectGroupId )
+ int projectGroupId, int offset, int length )
{
PersistenceManager pm = getPersistenceManager();
-
Transaction tx = pm.currentTransaction();
try
@@ -850,17 +831,88 @@ public class BuildResultDaoImpl
Query query = pm.newQuery( extent );
- String parameters = "";
- String filter = "";
+ InRangeQueryAttrs inRangeQueryAttrs =
+ new InRangeQueryAttrs( fromDate, toDate, state, triggeredBy, projectGroupId, query ).build();
+
+ String parameters = inRangeQueryAttrs.getParameters();
+ String filter = inRangeQueryAttrs.getFilter();
+ Map params = inRangeQueryAttrs.getParams();
+
+ query.declareParameters( parameters );
+ query.setFilter( filter );
+ query.setRange( offset, offset + length );
+
+ List<BuildResult> result = (List<BuildResult>) query.executeWithMap( params );
+
+ result = (List<BuildResult>) pm.detachCopyAll( result );
+
+ tx.commit();
+
+ return result;
+ }
+ finally
+ {
+ rollback( tx );
+ }
+
+ }
+
+ private class InRangeQueryAttrs
+ {
+ private Date fromDate;
+
+ private Date toDate;
+
+ private int state;
+
+ private String triggeredBy;
+
+ private int projectGroupId;
+
+ private Query query;
- Map params = new HashMap();
+ private String parameters;
- int ctr = 0;
+ private String filter;
+
+ private Map params;
+
+ public InRangeQueryAttrs( Date fromDate, Date toDate, int state, String triggeredBy, int projectGroupId,
+ Query query )
+ {
+ this.fromDate = fromDate;
+ this.toDate = toDate;
+ this.state = state;
+ this.triggeredBy = triggeredBy;
+ this.projectGroupId = projectGroupId;
+ this.query = query;
+ }
+
+ public String getParameters()
+ {
+ return parameters;
+ }
+
+ public String getFilter()
+ {
+ return filter;
+ }
+
+ public Map getParams()
+ {
+ return params;
+ }
+
+ public InRangeQueryAttrs build()
+ {
+ parameters = "";
+ filter = "";
+
+ params = new HashMap();
if ( state > 0 )
{
params.put( "state", state );
- ctr++;
parameters += "int state, ";
filter += "this.state == state && ";
}
@@ -868,7 +920,6 @@ public class BuildResultDaoImpl
if ( projectGroupId > 0 )
{
params.put( "projectGroupId", projectGroupId );
- ctr++;
parameters += "int projectGroupId, ";
filter += "this.project.projectGroup.id == projectGroupId && ";
}
@@ -876,7 +927,6 @@ public class BuildResultDaoImpl
if ( triggeredBy != null && !triggeredBy.equals( "" ) )
{
params.put( "triggeredBy", triggeredBy );
- ctr++;
query.declareImports( "import java.lang.String" );
parameters += "String triggeredBy, ";
filter += "this.username == triggeredBy && ";
@@ -885,7 +935,6 @@ public class BuildResultDaoImpl
if ( fromDate != null )
{
params.put( "fromDate", fromDate.getTime() );
- ctr++;
parameters += "long fromDate, ";
filter += "this.startTime >= fromDate && ";
}
@@ -897,7 +946,6 @@ public class BuildResultDaoImpl
cal.add( Calendar.DAY_OF_MONTH, 1 );
params.put( "toDate", cal.getTimeInMillis() );
- ctr++;
parameters += "long toDate";
filter += "this.startTime < toDate";
}
@@ -907,21 +955,7 @@ public class BuildResultDaoImpl
filter = filter.substring( 0, filter.length() - 3 );
parameters = parameters.substring( 0, parameters.length() - 2 );
}
-
- query.declareParameters( parameters );
- query.setFilter( filter );
-
- List<BuildResult> result = (List<BuildResult>) query.executeWithMap( params );
-
- result = (List<BuildResult>) pm.detachCopyAll( result );
-
- tx.commit();
-
- return result;
- }
- finally
- {
- rollback( tx );
+ return this;
}
}
}
Modified: continuum/trunk/continuum-store/src/test/java/org/apache/maven/continuum/store/ContinuumStoreTest.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-store/src/test/java/org/apache/maven/continuum/store/ContinuumStoreTest.java?rev=1677500&r1=1677499&r2=1677500&view=diff
==============================================================================
--- continuum/trunk/continuum-store/src/test/java/org/apache/maven/continuum/store/ContinuumStoreTest.java (original)
+++ continuum/trunk/continuum-store/src/test/java/org/apache/maven/continuum/store/ContinuumStoreTest.java Mon May 4 01:42:05 2015
@@ -1454,23 +1454,24 @@ public class ContinuumStoreTest
public void testGetBuildResultsInRange()
throws Exception
{
- List<BuildResult> results = buildResultDao.getBuildResultsInRange( null, null, 0, null, 0 );
+ int maxFetch = 5;
+ List<BuildResult> results = buildResultDao.getBuildResultsInRange( null, null, 0, null, 0, 0, maxFetch );
assertEquals( "check number of build results returned", 3, results.size() );
- results = buildResultDao.getBuildResultsInRange( null, null, 2, null, 0 );
+ results = buildResultDao.getBuildResultsInRange( null, null, 2, null, 0, 0, maxFetch );
assertEquals( "check number of build results returned with state == OK", 2, results.size() );
- results = buildResultDao.getBuildResultsInRange( null, null, 0, "user", 0 );
+ results = buildResultDao.getBuildResultsInRange( null, null, 0, "user", 0, 0, maxFetch );
assertEquals( "check number of build results returned with triggeredBy == user", 1, results.size() );
- results = buildResultDao.getBuildResultsInRange( null, null, 0, "schedule", 0 );
+ results = buildResultDao.getBuildResultsInRange( null, null, 0, "schedule", 0, 0, maxFetch );
assertEquals( "check number of build results returned with triggeredBy == schedule", 2, results.size() );
- results = buildResultDao.getBuildResultsInRange( null, null, 2, "schedule", 0 );
+ results = buildResultDao.getBuildResultsInRange( null, null, 2, "schedule", 0, 0, maxFetch );
assertEquals( "check number of build results returned with state == Ok and triggeredBy == schedule", 1,
results.size() );
- results = buildResultDao.getBuildResultsInRange( null, null, 3, "user", 0 );
+ results = buildResultDao.getBuildResultsInRange( null, null, 3, "user", 0, 0, maxFetch );
assertEquals( "check number of build results returned with state == Failed and triggeredBy == user", 0,
results.size() );
@@ -1478,16 +1479,17 @@ public class ContinuumStoreTest
cal.setTime( new Date( baseTime ) );
cal.add( Calendar.DAY_OF_MONTH, 1 );
- results = buildResultDao.getBuildResultsInRange( new Date( baseTime ), cal.getTime(), 0, null, 0 );
+ results = buildResultDao.getBuildResultsInRange( new Date( baseTime ), cal.getTime(), 0, null, 0, 0, maxFetch );
assertEquals( "check number of build results returned with startDate and endDate", 2, results.size() );
- results = buildResultDao.getBuildResultsInRange( new Date( baseTime ), new Date( baseTime ), 0, null, 0 );
+ results = buildResultDao.getBuildResultsInRange( new Date( baseTime ), new Date( baseTime ), 0, null, 0, 0,
+ maxFetch );
assertEquals( "check number of build results returned with the same startDate and endDate", 1, results.size() );
- results = buildResultDao.getBuildResultsInRange( null, null, 0, null, 1 );
+ results = buildResultDao.getBuildResultsInRange( null, null, 0, null, 1, 0, maxFetch );
assertEquals( "check number of build results returned with an existing group id", 3, results.size() );
- results = buildResultDao.getBuildResultsInRange( null, null, 0, null, 2 );
+ results = buildResultDao.getBuildResultsInRange( null, null, 0, null, 2, 0, maxFetch );
assertEquals( "check number of build results returned with non-existing group id", 0, results.size() );
}
// ----------------------------------------------------------------------
Modified: continuum/trunk/continuum-webapp/src/main/java/org/apache/continuum/web/action/ViewBuildsReportAction.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-webapp/src/main/java/org/apache/continuum/web/action/ViewBuildsReportAction.java?rev=1677500&r1=1677499&r2=1677500&view=diff
==============================================================================
--- continuum/trunk/continuum-webapp/src/main/java/org/apache/continuum/web/action/ViewBuildsReportAction.java (original)
+++ continuum/trunk/continuum-webapp/src/main/java/org/apache/continuum/web/action/ViewBuildsReportAction.java Mon May 4 01:42:05 2015
@@ -19,7 +19,6 @@ package org.apache.continuum.web.action;
* under the License.
*/
-import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.apache.maven.continuum.model.project.BuildResult;
@@ -28,25 +27,42 @@ import org.apache.maven.continuum.model.
import org.apache.maven.continuum.project.ContinuumProjectState;
import org.apache.maven.continuum.web.action.ContinuumActionSupport;
import org.apache.maven.continuum.web.exception.AuthorizationRequiredException;
-import org.apache.maven.continuum.web.model.ProjectBuildsSummary;
+import org.apache.struts2.interceptor.ServletResponseAware;
import org.codehaus.plexus.component.annotations.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-import java.io.ByteArrayInputStream;
+import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.StringReader;
+import java.io.Writer;
+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.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
@Component( role = com.opensymphony.xwork2.Action.class, hint = "projectBuildsReport", instantiationStrategy = "per-lookup" )
public class ViewBuildsReportAction
extends ContinuumActionSupport
+ implements ServletResponseAware
{
+ private static final Logger log = LoggerFactory.getLogger( ViewBuildsReportAction.class );
+
+ private static final int MAX_BROWSE_SIZE = 500;
+
+ private static final int EXPORT_BATCH_SIZE = 4000;
+
+ private static final int MAX_EXPORT_SIZE = 100000;
+
+ private static final String[] datePatterns =
+ new String[] { "MM/dd/yy", "MM/dd/yyyy", "MMMMM/dd/yyyy", "MMMMM/dd/yy", "dd MMMMM yyyy", "dd/MM/yy",
+ "dd/MM/yyyy", "yyyy/MM/dd", "yyyy-MM-dd", "yyyy-dd-MM", "MM-dd-yyyy", "MM-dd-yy" };
+
private int buildStatus;
private String triggeredBy = "";
@@ -57,25 +73,39 @@ public class ViewBuildsReportAction
private int projectGroupId;
- private int rowCount = 30;
+ private int rowCount = 25;
private int page = 1;
- private int numPages;
+ private int pageTotal;
private Map<Integer, String> buildStatuses;
private Map<Integer, String> projectGroups;
- private List<ProjectBuildsSummary> projectBuilds;
+ private Set<String> permittedGroups = new HashSet<String>();
- private InputStream inputStream;
+ private List<BuildResult> filteredResults = new ArrayList<BuildResult>();
- public static final String SEND_FILE = "send-file";
+ private HttpServletResponse rawResponse;
- private static final String[] datePatterns =
- new String[] { "MM/dd/yy", "MM/dd/yyyy", "MMMMM/dd/yyyy", "MMMMM/dd/yy", "dd MMMMM yyyy", "dd/MM/yy",
- "dd/MM/yyyy", "yyyy/MM/dd", "yyyy-MM-dd", "yyyy-dd-MM", "MM-dd-yyyy", "MM-dd-yy" };
+ public void setServletResponse( HttpServletResponse response )
+ {
+ this.rawResponse = response;
+ }
+
+ private boolean isAuthorized( String projectGroupName )
+ {
+ try
+ {
+ checkViewProjectGroupAuthorization( projectGroupName );
+ return true;
+ }
+ catch ( AuthorizationRequiredException authzE )
+ {
+ return false;
+ }
+ }
public void prepare()
throws Exception
@@ -87,16 +117,24 @@ public class ViewBuildsReportAction
buildStatuses.put( ContinuumProjectState.OK, "Ok" );
buildStatuses.put( ContinuumProjectState.FAILED, "Failed" );
buildStatuses.put( ContinuumProjectState.ERROR, "Error" );
+ buildStatuses.put( ContinuumProjectState.BUILDING, "Building" );
+ buildStatuses.put( ContinuumProjectState.CANCELLED, "Canceled" );
projectGroups = new LinkedHashMap<Integer, String>();
projectGroups.put( 0, "ALL" );
+ // TODO: Use these to limit results at the data layer
List<ProjectGroup> groups = getContinuum().getAllProjectGroups();
if ( groups != null )
{
for ( ProjectGroup group : groups )
{
- projectGroups.put( group.getId(), group.getName() );
+ String groupName = group.getName();
+ if ( isAuthorized( groupName ) )
+ {
+ projectGroups.put( group.getId(), groupName );
+ permittedGroups.add( groupName );
+ }
}
}
}
@@ -129,8 +167,8 @@ public class ViewBuildsReportAction
return REQUIRES_AUTHORIZATION;
}
- Date fromDate = null;
- Date toDate = null;
+ Date fromDate;
+ Date toDate;
try
{
@@ -149,42 +187,52 @@ public class ViewBuildsReportAction
return INPUT;
}
- if ( rowCount < 10 )
- {
- // TODO: move to validation framework
- addFieldError( "rowCount", "Row count should be at least 10." );
- return INPUT;
- }
-
- List<BuildResult> buildResults = getContinuum().getBuildResultsInRange( projectGroupId, fromDate, toDate,
- buildStatus, triggeredBy );
- projectBuilds = Collections.emptyList();
-
- if ( buildResults != null && !buildResults.isEmpty() )
- {
- projectBuilds = mapBuildResultsToProjectBuildsSummaries( buildResults );
-
- int extraPage = ( projectBuilds.size() % rowCount ) != 0 ? 1 : 0;
- numPages = ( projectBuilds.size() / rowCount ) + extraPage;
+ // Users can preview a limited number of records (use export for more)
+ int offset = 0;
+ List<BuildResult> results;
+ populating:
+ do
+ {
+ // Fetch a batch of records (may be filtered based on permissions)
+ results = getContinuum().getBuildResultsInRange( projectGroupId, fromDate, toDate, buildStatus, triggeredBy,
+ offset, MAX_BROWSE_SIZE );
+ offset += MAX_BROWSE_SIZE;
- if ( page > numPages )
+ for ( BuildResult result : results )
{
- addActionError(
- "Error encountered while generating project builds report :: The requested page exceeds the total number of pages." );
- return ERROR;
+ if ( permittedGroups.contains( result.getProject().getProjectGroup().getName() ) )
+ {
+ filteredResults.add( result );
+ }
+
+ if ( filteredResults.size() >= MAX_BROWSE_SIZE )
+ {
+ break populating; // Halt when we have filled a batch equivalent with results
+ }
}
+ }
+ while ( results.size() == MAX_BROWSE_SIZE ); // Keep fetching until batch is empty or incomplete
- int start = rowCount * ( page - 1 );
- int end = ( start + rowCount );
+ if ( filteredResults.size() == MAX_BROWSE_SIZE )
+ {
+ addActionMessage( "Results may have been limited due to size."
+ + " Refine your search criteria or try exporting." );
+ }
- if ( end > projectBuilds.size() )
- {
- end = projectBuilds.size();
- }
+ int resultSize = filteredResults.size();
+ pageTotal = resultSize / rowCount + ( resultSize % rowCount == 0 ? 0 : 1 );
- projectBuilds = projectBuilds.subList( start, end );
+ if ( page < 1 || page > pageTotal )
+ {
+ addActionError( "Specified page does not exist" );
+ return ERROR;
}
+ int pageStart = rowCount * ( page - 1 ), pageEnd = rowCount * page;
+
+ // Restrict results to just the page we will show
+ filteredResults = filteredResults.subList( pageStart, pageEnd > resultSize ? resultSize : pageEnd );
+
return SUCCESS;
}
@@ -203,8 +251,8 @@ public class ViewBuildsReportAction
return REQUIRES_AUTHORIZATION;
}
- Date fromDate = null;
- Date toDate = null;
+ Date fromDate;
+ Date toDate;
try
{
@@ -223,59 +271,101 @@ public class ViewBuildsReportAction
return INPUT;
}
- List<BuildResult> buildResults = getContinuum().getBuildResultsInRange( projectGroupId, fromDate, toDate,
- buildStatus, triggeredBy );
- List<ProjectBuildsSummary> builds = Collections.emptyList();
-
- StringBuffer input = new StringBuffer( "Project Group,Project Name,Build Date,Triggered By,Build Status\n" );
-
- if ( buildResults != null && !buildResults.isEmpty() )
+ try
{
- builds = mapBuildResultsToProjectBuildsSummaries( buildResults );
+ // First, build the output file
+ rawResponse.setContentType( "text/csv" );
+ rawResponse.addHeader( "Content-disposition", "attachment;filename=continuum_project_builds_report.csv" );
+ Writer output = rawResponse.getWriter();
- for ( ProjectBuildsSummary build : builds )
+ DateFormat dateTimeFormat = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZ" );
+
+ try
{
- input.append( build.getProjectGroupName() ).append( "," );
- input.append( build.getProjectName() ).append( "," );
+ // Write the header
+ output.append( "Group,Project,ID,Build#,Started,Duration,Triggered By,Status\n" );
- input.append( new Date( build.getBuildDate() ) ).append( "," );
+ // Build the output file by walking through the results in batches
+ int offset = 0, exported = 0;
+ List<BuildResult> results;
+ export:
+ do
+ {
+ results = getContinuum().getBuildResultsInRange( projectGroupId, fromDate, toDate, buildStatus,
+ triggeredBy, offset, EXPORT_BATCH_SIZE );
- input.append( build.getBuildTriggeredBy() ).append( "," );
+ offset += EXPORT_BATCH_SIZE; // Ensure we advance through results
- String status;
- switch ( build.getBuildState() )
- {
- case 2:
- status = "Ok";
- break;
- case 3:
- status = "Failed";
- break;
- case 4:
- status = "Error";
- break;
- case 6:
- status = "Building";
- break;
- case 7:
- status = "Checking Out";
- break;
- case 8:
- status = "Updating";
- break;
- default:
- status = "";
+ // Convert each build result to a line in the CSV file
+ for ( BuildResult result : results )
+ {
+
+ if ( !permittedGroups.contains( result.getProject().getProjectGroup().getName() ) )
+ {
+ continue;
+ }
+
+ exported += 1;
+
+ Project project = result.getProject();
+ ProjectGroup projectGroup = project.getProjectGroup();
+
+ // Decode status into human-readable form
+ String state;
+ switch ( result.getState() )
+ {
+ case 2:
+ state = "Ok";
+ break;
+ case 3:
+ state = "Failed";
+ break;
+ case 4:
+ state = "Error";
+ break;
+ case 6:
+ state = "Building";
+ break;
+ case 7:
+ state = "Checking Out";
+ break;
+ case 8:
+ state = "Updating";
+ break;
+ case 11:
+ state = "Canceled";
+ break;
+ default:
+ state = "";
+ }
+
+ String buildTime = dateTimeFormat.format( new Date( result.getStartTime() ) );
+ long buildDuration = ( result.getEndTime() - result.getStartTime() ) / 1000;
+
+ String formattedLine = String.format( "%s,%s,%s,%s,%s,%s,%s,%s\n",
+ projectGroup.getName(),
+ project.getName(),
+ result.getId(),
+ result.getBuildNumber(),
+ buildTime,
+ buildDuration,
+ result.getUsername(),
+ state );
+ output.append( formattedLine );
+
+ if ( exported >= MAX_EXPORT_SIZE )
+ {
+ log.warn( "build report export hit limit of {} records", MAX_EXPORT_SIZE );
+ break export;
+ }
+ }
}
- input.append( status );
- input.append( "\n" );
+ while ( results.size() == EXPORT_BATCH_SIZE );
+ }
+ finally
+ {
+ output.flush();
}
- }
-
- StringReader reader = new StringReader( input.toString() );
-
- try
- {
- inputStream = new ByteArrayInputStream( IOUtils.toByteArray( reader ) );
}
catch ( IOException e )
{
@@ -283,34 +373,7 @@ public class ViewBuildsReportAction
return ERROR;
}
- return SEND_FILE;
- }
-
- private List<ProjectBuildsSummary> mapBuildResultsToProjectBuildsSummaries( List<BuildResult> buildResults )
- {
- List<ProjectBuildsSummary> buildsSummary = new ArrayList<ProjectBuildsSummary>();
-
- for ( BuildResult buildResult : buildResults )
- {
- Project project = buildResult.getProject();
-
- // check if user is authorised to view build result
- if ( !isAuthorized( project.getProjectGroup().getName() ) )
- {
- continue;
- }
-
- ProjectBuildsSummary summary = new ProjectBuildsSummary();
- summary.setProjectGroupName( project.getProjectGroup().getName() );
- summary.setProjectName( project.getName() );
- summary.setBuildDate( buildResult.getStartTime() );
- summary.setBuildState( buildResult.getState() );
- summary.setBuildTriggeredBy( buildResult.getUsername() );
-
- buildsSummary.add( summary );
- }
-
- return buildsSummary;
+ return null;
}
private Date getStartDateInDateFormat()
@@ -394,26 +457,11 @@ public class ViewBuildsReportAction
return rowCount;
}
- public void setRowCount( int rowCount )
- {
- this.rowCount = rowCount;
- }
-
public Map<Integer, String> getBuildStatuses()
{
return buildStatuses;
}
- public Map<Integer, String> getProjectGroups()
- {
- return projectGroups;
- }
-
- public List<ProjectBuildsSummary> getProjectBuilds()
- {
- return projectBuilds;
- }
-
public int getPage()
{
return page;
@@ -424,26 +472,18 @@ public class ViewBuildsReportAction
this.page = page;
}
- public int getNumPages()
+ public int getPageTotal()
{
- return numPages;
+ return pageTotal;
}
- public InputStream getInputStream()
+ public List<BuildResult> getFilteredResults()
{
- return inputStream;
+ return filteredResults;
}
- private boolean isAuthorized( String projectGroupName )
+ public Map<Integer, String> getProjectGroups()
{
- try
- {
- checkViewProjectGroupAuthorization( projectGroupName );
- return true;
- }
- catch ( AuthorizationRequiredException authzE )
- {
- return false;
- }
+ return projectGroups;
}
-}
+}
\ No newline at end of file
Modified: continuum/trunk/continuum-webapp/src/main/java/org/apache/maven/continuum/web/action/ProjectGroupAction.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-webapp/src/main/java/org/apache/maven/continuum/web/action/ProjectGroupAction.java?rev=1677500&r1=1677499&r2=1677500&view=diff
==============================================================================
--- continuum/trunk/continuum-webapp/src/main/java/org/apache/maven/continuum/web/action/ProjectGroupAction.java (original)
+++ continuum/trunk/continuum-webapp/src/main/java/org/apache/maven/continuum/web/action/ProjectGroupAction.java Mon May 4 01:42:05 2015
@@ -529,11 +529,17 @@ public class ProjectGroupAction
project.setProjectGroup( newProjectGroup );
// CONTINUUM-1512
- Collection<BuildResult> results = getContinuum().getBuildResultsForProject( project.getId() );
- for ( BuildResult br : results )
+ int batchSize = 100;
+ Collection<BuildResult> results;
+ do
{
- getContinuum().removeBuildResult( br.getId() );
+ results = getContinuum().getBuildResultsForProject( project.getId(), 0, batchSize );
+ for ( BuildResult br : results )
+ {
+ getContinuum().removeBuildResult( br.getId() );
+ }
}
+ while ( results != null && results.size() > 0 );
getContinuum().updateProject( project );
}
Modified: continuum/trunk/continuum-webapp/src/main/resources/localization/Continuum.properties
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-webapp/src/main/resources/localization/Continuum.properties?rev=1677500&r1=1677499&r2=1677500&view=diff
==============================================================================
--- continuum/trunk/continuum-webapp/src/main/resources/localization/Continuum.properties (original)
+++ continuum/trunk/continuum-webapp/src/main/resources/localization/Continuum.properties Mon May 4 01:42:05 2015
@@ -1314,6 +1314,7 @@ projectBuilds.report.title=Continuum - P
projectBuilds.report.section.title=Project Builds Report
projectBuilds.report.button.reset=Reset
projectBuilds.report.project.group=Project Group
+projectBuilds.report.buildNumber=Build Number
projectBuilds.report.startDate=Start Date
projectBuilds.report.endDate=End Date
projectBuilds.report.triggeredBy=Triggered By
Modified: continuum/trunk/continuum-webapp/src/main/resources/struts.xml
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-webapp/src/main/resources/struts.xml?rev=1677500&r1=1677499&r2=1677500&view=diff
==============================================================================
--- continuum/trunk/continuum-webapp/src/main/resources/struts.xml (original)
+++ continuum/trunk/continuum-webapp/src/main/resources/struts.xml Mon May 4 01:42:05 2015
@@ -741,10 +741,6 @@
<action name="downloadProjectBuildsReport" class="projectBuildsReport" method="downloadBuildsReport">
<result name="input">/WEB-INF/jsp/viewProjectBuildsReport.jsp</result>
<result name="success">/WEB-INF/jsp/viewProjectBuildsReport.jsp</result>
- <result name="send-file" type="stream">
- <param name="contentType">%{contentType}</param>
- <param name="contentDisposition">attachment; filename="continuum_project_builds_report.csv"</param>
- </result>
</action>
</package>
Modified: continuum/trunk/continuum-webapp/src/main/webapp/WEB-INF/jsp/viewProjectBuildsReport.jsp
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-webapp/src/main/webapp/WEB-INF/jsp/viewProjectBuildsReport.jsp?rev=1677500&r1=1677499&r2=1677500&view=diff
==============================================================================
--- continuum/trunk/continuum-webapp/src/main/webapp/WEB-INF/jsp/viewProjectBuildsReport.jsp (original)
+++ continuum/trunk/continuum-webapp/src/main/webapp/WEB-INF/jsp/viewProjectBuildsReport.jsp Mon May 4 01:42:05 2015
@@ -39,7 +39,6 @@
$('#startDate').val('')
$('#endDate').val('')
$('#triggeredBy').val('')
- $('#rowCount').val('30')
$("#buildStatus option[value='0']").attr('selected', 'selected')
$("#projectGroupId option[value='0']").attr('selected', 'selected')
});
@@ -55,19 +54,20 @@
<body>
<h3><s:text name="projectBuilds.report.section.title"/></h3>
- <s:form name="generateReportForm" action="generateProjectBuildsReport">
- <c:if test="${!empty actionErrors || !empty errorMessages}">
- <tr><td>
- <div class="errormessage">
- <s:iterator value="actionErrors">
- <p><s:property/></p>
- </s:iterator>
- <c:forEach items="${errorMessages}" var="errorMessage">
- <p><c:out value="${errorMessage}"/></p>
- </c:forEach>
- </div>
- </td></tr>
- </c:if>
+ <s:form name="generateReportForm" action="generateProjectBuildsReport" method="GET">
+
+ <tr><td>
+ <s:if test="hasActionErrors()">
+ <div class="errormessage">
+ <s:actionerror/>
+ </div>
+ </s:if>
+ <s:if test="hasActionMessages()">
+ <div class="warningmessage">
+ <s:actionmessage/>
+ </div>
+ </s:if>
+ </td></tr>
<tr><td>
<div class="axial">
@@ -77,7 +77,6 @@
<s:textfield label="%{getText('projectBuilds.report.endDate')}" name="endDate" id="endDate" size="20"/>
<s:select label="%{getText('projectBuilds.report.buildStatus')}" name="buildStatus" id="buildStatus" list="buildStatuses"/>
<s:textfield label="%{getText('projectBuilds.report.triggeredBy')}" name="triggeredBy" id="triggeredBy" size="40"/>
- <s:textfield label="%{getText('projectBuilds.report.rowCount')}" name="rowCount" id="rowCount" size="10"/>
</table>
<div class="functnbar3">
<s:submit value="%{getText('projectBuilds.report.view')}" theme="simple" />
@@ -86,145 +85,101 @@
</div>
</td></tr>
</s:form>
-
- </p>
- <c:if test="${projectBuilds != null}">
<div id="h3">
<h3>Results</h3>
- <c:choose>
- <c:when test="${not empty projectBuilds}">
-
+ <s:if test="filteredResults.size() > 0">
<table>
<tr>
<td>
- <%-- Pagination --%>
- <c:set var="prevPageUrl">
- <s:url action="generateProjectBuildsReport">
- <s:param name="projectGroupId" value="%{#attr.projectGroupId}"/>
- <s:param name="triggeredBy" value="%{#attr.triggeredBy}"/>
- <s:param name="buildStatus" value="%{#attr.buildStatus}"/>
- <s:param name="rowCount" value="%{#attr.rowCount}"/>
- <s:param name="startDate" value="%{#attr.startDate}"/>
- <s:param name="endDate" value="%{#attr.endDate}"/>
- <s:param name="page" value="%{#attr.page - 1}"/>
- </s:url>
- </c:set>
- <c:set var="nextPageUrl">
- <s:url action="generateProjectBuildsReport">
- <s:param name="projectGroupId" value="%{#attr.projectGroupId}"/>
- <s:param name="triggeredBy" value="%{#attr.triggeredBy}"/>
- <s:param name="buildStatus" value="%{#attr.buildStatus}"/>
- <s:param name="rowCount" value="%{#attr.rowCount}"/>
- <s:param name="startDate" value="%{#attr.startDate}"/>
- <s:param name="endDate" value="%{#attr.endDate}"/>
- <s:param name="page" value="%{#attr.page + 1}"/>
- </s:url>
- </c:set>
- <c:choose>
- <c:when test="${page == 1}">
- <s:text name="projectBuilds.report.prev"/>
- </c:when>
- <c:otherwise>
- <a href="${prevPageUrl}">
- <s:text name="projectBuilds.report.prev"/>
- </a>
- </c:otherwise>
- </c:choose>
-
- <c:choose>
- <c:when test="${numPages > 11}">
- <c:choose>
- <c:when test="${(page - 5) < 0}">
- <c:set var="beginVal">0</c:set>
- <c:set var="endVal">10</c:set>
- </c:when>
- <c:when test="${(page + 5) > (numPages - 1)}">
- <c:set var="beginVal">${(numPages - 1) - 10}</c:set>
- <c:set var="endVal">${numPages - 1}</c:set>
- </c:when>
- <c:otherwise>
- <c:set var="beginVal">${page - 5}</c:set>
- <c:set var="endVal">${page + 5}</c:set>
- </c:otherwise>
- </c:choose>
- </c:when>
- <c:otherwise>
- <c:set var="beginVal">0</c:set>
- <c:set var="endVal">${numPages - 1}</c:set>
- </c:otherwise>
- </c:choose>
-
- <c:forEach var="i" begin="${beginVal}" end="${endVal}">
- <c:choose>
- <c:when test="${i != (page - 1)}">
- <c:set var="specificPageUrl">
- <s:url action="generateProjectBuildsReport">
- <s:param name="projectGroupId" value="%{#attr.projectGroupId}"/>
- <s:param name="triggeredBy" value="%{#attr.triggeredBy}"/>
- <s:param name="buildStatus" value="%{#attr.buildStatus}"/>
- <s:param name="rowCount" value="%{#attr.rowCount}"/>
- <s:param name="startDate" value="%{#attr.startDate}"/>
- <s:param name="endDate" value="%{#attr.endDate}"/>
- <s:param name="page" value="%{#attr.i + 1}"/>
- </s:url>
- </c:set>
- <a href="${specificPageUrl}">${i + 1}</a>
- </c:when>
- <c:otherwise>
- <b>${i + 1}</b>
- </c:otherwise>
- </c:choose>
- </c:forEach>
-
- <c:choose>
- <c:when test="${page == numPages}">
- <s:text name="projectBuilds.report.next"/>
- </c:when>
- <c:otherwise>
- <a href="${nextPageUrl}">
- <s:text name="projectBuilds.report.next"/>
- </a>
- </c:otherwise>
- </c:choose>
+
+ <%-- Pagination --%>
+ <s:url var="prevUrl" action="generateProjectBuildsReport">
+ <s:param name="projectGroupId" value="projectGroupId"/>
+ <s:param name="buildStatus" value="buildStatus"/>
+ <s:param name="triggeredBy" value="triggeredBy"/>
+ <s:param name="startDate" value="startDate"/>
+ <s:param name="endDate" value="endDate"/>
+ <s:param name="page" value="page - 1"/>
+ </s:url>
+ <s:url var="nextUrl" action="generateProjectBuildsReport">
+ <s:param name="projectGroupId" value="projectGroupId"/>
+ <s:param name="triggeredBy" value="triggeredBy"/>
+ <s:param name="buildStatus" value="buildStatus"/>
+ <s:param name="startDate" value="startDate"/>
+ <s:param name="endDate" value="endDate"/>
+ <s:param name="page" value="page + 1"/>
+ </s:url>
+
+ <s:if test="page <= 1">
+ <s:text name="projectBuilds.report.prev"/>
+ </s:if>
+ <s:else>
+ <s:a href="%{#prevUrl}"><s:text name="projectBuilds.report.prev"/></s:a>
+ </s:else>
+
+ <s:iterator var="page" begin="1" end="pageTotal">
+ <s:url var="pageUrl" action="generateProjectBuildsReport">
+ <s:param name="projectGroupId" value="projectGroupId"/>
+ <s:param name="triggeredBy" value="triggeredBy"/>
+ <s:param name="buildStatus" value="buildStatus"/>
+ <s:param name="startDate" value="startDate"/>
+ <s:param name="endDate" value="endDate"/>
+ <s:param name="page" value="#page"/>
+ </s:url>
+ <s:if test="page == #page">
+ <b><s:property value="#page"/></b>
+ </s:if>
+ <s:else>
+ <s:a href="%{#pageUrl}"><s:property value="#page"/></s:a>
+ </s:else>
+ </s:iterator>
+
+ <%-- Can not determine exact end of results due to filtering --%>
+ <s:if test="page >= pageTotal">
+ <s:text name="projectBuilds.report.next"/>
+ </s:if>
+ <s:else>
+ <s:a href="%{#nextUrl}"><s:text name="projectBuilds.report.next"/></s:a>
+ </s:else>
+
</td>
<td>
<%-- Export to CSV link --%>
<s:url id="downloadProjectBuildsReportUrl" action="downloadProjectBuildsReport" namespace="/">
- <s:param name="projectGroupId" value="%{#attr.projectGroupId}"/>
- <s:param name="triggeredBy" value="%{#attr.triggeredBy}"/>
- <s:param name="buildStatus" value="%{#attr.buildStatus}"/>
- <s:param name="startDate" value="%{#attr.startDate}"/>
- <s:param name="endDate" value="%{#attr.endDate}"/>
+ <s:param name="projectGroupId" value="projectGroupId"/>
+ <s:param name="triggeredBy" value="triggeredBy"/>
+ <s:param name="buildStatus" value="buildStatus"/>
+ <s:param name="startDate" value="startDate"/>
+ <s:param name="endDate" value="endDate"/>
</s:url>
<s:a href="%{downloadProjectBuildsReportUrl}">Export to CSV</s:a>
</td>
</tr>
</table>
- <s:set name="projectBuilds" value="projectBuilds" scope="request"/>
- <ec:table items="projectBuilds"
- var="projectBuild"
+ <s:set value="filteredResults" name="buildResults" scope="page"/>
+ <ec:table items="buildResults"
+ var="buildResult"
showExports="false"
showPagination="false"
showStatusBar="false"
sortable="false"
filterable="false">
<ec:row highlightRow="true">
- <ec:column property="projectGroupName" title="projectBuilds.report.projectGroup"/>
- <ec:column property="projectName" title="projectBuilds.report.project"/>
- <ec:column property="buildDate" title="projectBuilds.report.buildDate" cell="date"/>
- <ec:column property="buildTriggeredBy" title="projectBuilds.report.triggeredBy"/>
- <ec:column property="buildState" title="projectBuilds.report.buildStatus" cell="org.apache.maven.continuum.web.view.buildresults.StateCell"/>
+ <ec:column property="project.projectGroup.name" title="projectBuilds.report.projectGroup"/>
+ <ec:column property="project.name" title="projectBuilds.report.project"/>
+ <ec:column property="buildNumber" title="projectBuilds.report.buildNumber"/>
+ <ec:column property="startTime" title="projectBuilds.report.buildDate" cell="date"/>
+ <ec:column property="username" title="projectBuilds.report.triggeredBy"/>
+ <ec:column property="state" title="projectBuilds.report.buildStatus" cell="org.apache.maven.continuum.web.view.buildresults.StateCell"/>
</ec:row>
</ec:table>
- </c:when>
- <c:otherwise>
+ </s:if>
+ <s:else>
<s:text name="projectBuilds.report.noResult"/></p>
- </c:otherwise>
- </c:choose>
+ </s:else>
</div>
- </c:if>
</body>
</s:i18n>
</html>
Modified: continuum/trunk/continuum-webapp/src/test/java/org/apache/continuum/web/action/ViewBuildsReportActionTest.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-webapp/src/test/java/org/apache/continuum/web/action/ViewBuildsReportActionTest.java?rev=1677500&r1=1677499&r2=1677500&view=diff
==============================================================================
--- continuum/trunk/continuum-webapp/src/test/java/org/apache/continuum/web/action/ViewBuildsReportActionTest.java (original)
+++ continuum/trunk/continuum-webapp/src/test/java/org/apache/continuum/web/action/ViewBuildsReportActionTest.java Mon May 4 01:42:05 2015
@@ -105,7 +105,7 @@ public class ViewBuildsReportActionTest
public void testStartDateSameWithEndDate()
{
when( continuum.getBuildResultsInRange( anyInt(), any( Date.class ), any( Date.class ), anyInt(),
- anyString() ) ).thenReturn( buildResults );
+ anyString(), anyInt(), anyInt() ) ).thenReturn( buildResults );
action.setStartDate( "04/25/2010" );
action.setEndDate( "04/25/2010" );
@@ -118,7 +118,7 @@ public class ViewBuildsReportActionTest
public void testEndDateWithNoStartDate()
{
when( continuum.getBuildResultsInRange( anyInt(), any( Date.class ), any( Date.class ), anyInt(),
- anyString() ) ).thenReturn( buildResults );
+ anyString(), anyInt(), anyInt() ) ).thenReturn( buildResults );
action.setEndDate( "04/25/2010" );
String result = action.execute();
@@ -135,7 +135,7 @@ public class ViewBuildsReportActionTest
List<BuildResult> results = createBuildResult( cal.getTimeInMillis() );
when( continuum.getBuildResultsInRange( anyInt(), any( Date.class ), any( Date.class ), anyInt(),
- anyString() ) ).thenReturn( results );
+ anyString(), anyInt(), anyInt() ) ).thenReturn( results );
action.setProjectGroupId( 0 );
action.setBuildStatus( 0 );
action.setStartDate( "" );
Modified: continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-api/src/main/java/org/apache/maven/continuum/xmlrpc/ContinuumService.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-api/src/main/java/org/apache/maven/continuum/xmlrpc/ContinuumService.java?rev=1677500&r1=1677499&r2=1677500&view=diff
==============================================================================
--- continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-api/src/main/java/org/apache/maven/continuum/xmlrpc/ContinuumService.java (original)
+++ continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-api/src/main/java/org/apache/maven/continuum/xmlrpc/ContinuumService.java Mon May 4 01:42:05 2015
@@ -680,20 +680,24 @@ public interface ContinuumService
* Returns the project build result summary list.
*
* @param projectId The project id
+ * @param offset the zero-based offset to fetch from
+ * @param length the maximum number of results to fetch, starting from offset
* @return The build result list
* @throws Exception
*/
- List<BuildResultSummary> getBuildResultsForProject( int projectId )
+ List<BuildResultSummary> getBuildResultsForProject( int projectId, int offset, int length )
throws Exception;
/**
* Same method but compatible with standard XMLRPC
*
* @param projectId The project id
+ * @param offset the zero-based offset of result set to start from
+ * @param length the number of results to return, starting from the offset
* @return The build result list as RPC value
* @throws Exception
*/
- List<Object> getBuildResultsForProjectRPC( int projectId )
+ List<Object> getBuildResultsForProjectRPC( int projectId, int offset, int length )
throws Exception;
/**
Modified: continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-backup/src/main/java/org/apache/maven/continuum/xmlrpc/backup/Backup.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-backup/src/main/java/org/apache/maven/continuum/xmlrpc/backup/Backup.java?rev=1677500&r1=1677499&r2=1677500&view=diff
==============================================================================
--- continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-backup/src/main/java/org/apache/maven/continuum/xmlrpc/backup/Backup.java (original)
+++ continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-backup/src/main/java/org/apache/maven/continuum/xmlrpc/backup/Backup.java Mon May 4 01:42:05 2015
@@ -29,17 +29,7 @@ import org.apache.log4j.BasicConfigurato
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.maven.continuum.xmlrpc.client.ContinuumXmlRpcClient;
-import org.apache.maven.continuum.xmlrpc.project.BuildDefinition;
-import org.apache.maven.continuum.xmlrpc.project.BuildDefinitionTemplate;
-import org.apache.maven.continuum.xmlrpc.project.BuildResult;
-import org.apache.maven.continuum.xmlrpc.project.BuildResultSummary;
-import org.apache.maven.continuum.xmlrpc.project.Project;
-import org.apache.maven.continuum.xmlrpc.project.ProjectDependency;
-import org.apache.maven.continuum.xmlrpc.project.ProjectDeveloper;
-import org.apache.maven.continuum.xmlrpc.project.ProjectGroup;
-import org.apache.maven.continuum.xmlrpc.project.ProjectNotifier;
-import org.apache.maven.continuum.xmlrpc.project.ProjectSummary;
-import org.apache.maven.continuum.xmlrpc.project.Schedule;
+import org.apache.maven.continuum.xmlrpc.project.*;
import org.apache.maven.continuum.xmlrpc.scm.ChangeFile;
import org.apache.maven.continuum.xmlrpc.scm.ChangeSet;
import org.apache.maven.continuum.xmlrpc.scm.ScmResult;
@@ -415,17 +405,24 @@ public class Backup
endTag( "notifiers", true );
}
- List<BuildResultSummary> brs = client.getBuildResultsForProject( p.getId() );
- if ( brs != null && !brs.isEmpty() )
+ int batchSize = 100, offset = 0;
+ List<BuildResultSummary> brs;
+ do
{
- startTag( "buildResults", true );
- for ( BuildResultSummary brSummary : brs )
+ brs = client.getBuildResultsForProject( p.getId(), offset, batchSize );
+ if ( brs != null && !brs.isEmpty() )
{
- BuildResult br = client.getBuildResult( p.getId(), brSummary.getId() );
- backupBuildResult( br );
+ startTag( "buildResults", true );
+ for ( BuildResultSummary brSummary : brs )
+ {
+ BuildResult br = client.getBuildResult( p.getId(), brSummary.getId() );
+ backupBuildResult( br );
+ }
+ endTag( "buildResults", true );
}
- endTag( "buildResults", true );
+ offset += batchSize;
}
+ while ( brs != null && brs.size() == batchSize );
endTag( "project", true );
}
Modified: continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-client/src/main/java/org/apache/maven/continuum/xmlrpc/client/BuildResultsPurge.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-client/src/main/java/org/apache/maven/continuum/xmlrpc/client/BuildResultsPurge.java?rev=1677500&r1=1677499&r2=1677500&view=diff
==============================================================================
--- continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-client/src/main/java/org/apache/maven/continuum/xmlrpc/client/BuildResultsPurge.java (original)
+++ continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-client/src/main/java/org/apache/maven/continuum/xmlrpc/client/BuildResultsPurge.java Mon May 4 01:42:05 2015
@@ -30,7 +30,6 @@ import java.util.List;
/**
* Utility class to purge old build results.
- * <p/>
* The easiest way to use it is to change the exec plugin config in the pom to execute this class instead of
* SampleClient, change RETENTION_DAYS if desired, and type 'mvn clean install exec:exec'
*/
@@ -72,26 +71,37 @@ public class BuildResultsPurge
System.out.println( " Project [" + project.getId() + "] " + project.getName() );
- List<BuildResultSummary> results = client.getBuildResultsForProject( project.getId() );
+ int batchSize = 100, offset = 0;
+ List<BuildResultSummary> results;
- for ( BuildResultSummary brs : results )
+ do
{
+ int retained = 0;
+ results = client.getBuildResultsForProject( project.getId(), offset, batchSize );
- BuildResult br = client.getBuildResult( project.getId(), brs.getId() );
+ for ( BuildResultSummary brs : results )
+ {
- System.out.print( " Build Result [" + br.getId() + "] ended " + new Date( br.getEndTime() ) );
+ BuildResult br = client.getBuildResult( project.getId(), brs.getId() );
- if ( br.getEndTime() > 0 && br.getEndTime() < purgeDate )
- {
+ System.out.print( " Build Result [" + br.getId() + "] ended " + new Date( br.getEndTime() ) );
- client.removeBuildResult( br );
- System.out.println( " ...removed." );
- }
- else
- {
- System.out.println( " ...retained." );
+ if ( br.getEndTime() > 0 && br.getEndTime() < purgeDate )
+ {
+
+ client.removeBuildResult( br );
+ System.out.println( " ...removed." );
+ }
+ else
+ {
+ System.out.println( " ...retained." );
+ retained++;
+ }
}
+
+ offset += retained; // Only need to advance past items we keep
}
+ while ( results != null && results.size() == batchSize );
}
}
Modified: continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-client/src/main/java/org/apache/maven/continuum/xmlrpc/client/ContinuumXmlRpcClient.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-client/src/main/java/org/apache/maven/continuum/xmlrpc/client/ContinuumXmlRpcClient.java?rev=1677500&r1=1677499&r2=1677500&view=diff
==============================================================================
--- continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-client/src/main/java/org/apache/maven/continuum/xmlrpc/client/ContinuumXmlRpcClient.java (original)
+++ continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-client/src/main/java/org/apache/maven/continuum/xmlrpc/client/ContinuumXmlRpcClient.java Mon May 4 01:42:05 2015
@@ -386,10 +386,10 @@ public class ContinuumXmlRpcClient
return continuum.getBuildResult( projectId, buildId );
}
- public List<BuildResultSummary> getBuildResultsForProject( int projectId )
+ public List<BuildResultSummary> getBuildResultsForProject( int projectId, int offset, int length )
throws Exception
{
- return continuum.getBuildResultsForProject( projectId );
+ return continuum.getBuildResultsForProject( projectId, offset, length );
}
public int removeBuildResult( BuildResult br )
@@ -928,10 +928,10 @@ public class ContinuumXmlRpcClient
return continuum.getBuildResultRPC( projectId, buildId );
}
- public List<Object> getBuildResultsForProjectRPC( int projectId )
+ public List<Object> getBuildResultsForProjectRPC( int projectId, int offset, int length )
throws Exception
{
- return continuum.getBuildResultsForProjectRPC( projectId );
+ return continuum.getBuildResultsForProjectRPC( projectId, offset, length );
}
public Map<String, Object> getInstallationRPC( int installationId )
Modified: continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-client/src/main/java/org/apache/maven/continuum/xmlrpc/client/SampleClient.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-client/src/main/java/org/apache/maven/continuum/xmlrpc/client/SampleClient.java?rev=1677500&r1=1677499&r2=1677500&view=diff
==============================================================================
--- continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-client/src/main/java/org/apache/maven/continuum/xmlrpc/client/SampleClient.java (original)
+++ continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-client/src/main/java/org/apache/maven/continuum/xmlrpc/client/SampleClient.java Mon May 4 01:42:05 2015
@@ -53,7 +53,7 @@ public class SampleClient
// Test for [CONTINUUM-2641]: (test with distributed builds with multiple build agents or parallel builds with > 1 build queue)
// make sure to set the projectIds to the actual projectIds of your projects added in Continuum
- int projectIds[] = new int[]{2, 3, 4, 5, 6};
+ int projectIds[] = new int[] { 2, 3, 4, 5, 6 };
List<Thread> threads = new ArrayList<Thread>();
@@ -174,15 +174,20 @@ public class SampleClient
System.out.println( "Removing build results." );
System.out.println( "============================" );
- BuildResultSummary brs;
- List results = client.getBuildResultsForProject( ps.getId() );
- for ( Iterator i = results.iterator(); i.hasNext(); )
- {
- brs = (BuildResultSummary) i.next();
- System.out.print( "Removing build result (" + brs.getId() + ") - " );
- BuildResult br = client.getBuildResult( ps.getId(), brs.getId() );
- System.out.println( ( client.removeBuildResult( br ) == 0 ? "OK" : "Error" ) );
+
+ int batchSize = 100;
+ List<BuildResultSummary> results;
+ do
+ {
+ results = client.getBuildResultsForProject( ps.getId(), 0, batchSize );
+ for ( BuildResultSummary brs : results )
+ {
+ System.out.print( "Removing build result (" + brs.getId() + ") - " );
+ BuildResult br = client.getBuildResult( ps.getId(), brs.getId() );
+ System.out.println( ( client.removeBuildResult( br ) == 0 ? "OK" : "Error" ) );
+ }
}
+ while ( results != null && results.size() > 0 );
System.out.println( "Done." );
System.out.println();
Modified: continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-server/src/main/java/org/apache/maven/continuum/xmlrpc/server/ContinuumServiceImpl.java
URL: http://svn.apache.org/viewvc/continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-server/src/main/java/org/apache/maven/continuum/xmlrpc/server/ContinuumServiceImpl.java?rev=1677500&r1=1677499&r2=1677500&view=diff
==============================================================================
--- continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-server/src/main/java/org/apache/maven/continuum/xmlrpc/server/ContinuumServiceImpl.java (original)
+++ continuum/trunk/continuum-xmlrpc/continuum-xmlrpc-server/src/main/java/org/apache/maven/continuum/xmlrpc/server/ContinuumServiceImpl.java Mon May 4 01:42:05 2015
@@ -881,7 +881,7 @@ public class ContinuumServiceImpl
return populateBuildResult( continuum.getBuildResult( buildId ) );
}
- public List<BuildResultSummary> getBuildResultsForProject( int projectId )
+ public List<BuildResultSummary> getBuildResultsForProject( int projectId, int offset, int length )
throws ContinuumException
{
ProjectSummary ps = getProjectSummary( projectId );
@@ -889,7 +889,7 @@ public class ContinuumServiceImpl
List<BuildResultSummary> result = new ArrayList<BuildResultSummary>();
Collection<org.apache.maven.continuum.model.project.BuildResult> buildResults =
- continuum.getBuildResultsForProject( projectId );
+ continuum.getBuildResultsForProject( projectId, offset, length );
if ( buildResults != null )
{
for ( org.apache.maven.continuum.model.project.BuildResult buildResult : buildResults )
@@ -3471,10 +3471,10 @@ public class ContinuumServiceImpl
return serializeObject( this.getBuildResult( projectId, buildId ) );
}
- public List<Object> getBuildResultsForProjectRPC( int projectId )
+ public List<Object> getBuildResultsForProjectRPC( int projectId, int offset, int length )
throws Exception
{
- return serializeObject( this.getBuildResultsForProject( projectId ) );
+ return serializeObject( this.getBuildResultsForProject( projectId, offset, length ) );
}
public Map<String, Object> getInstallationRPC( int installationId )