From f31b9f681b283e0b9a291fedb06262dc462d7669 Mon Sep 17 00:00:00 2001 From: Richard Millet Date: Fri, 26 Feb 2010 21:46:25 +0000 Subject: [PATCH] CSPACE-413: Initial CRUD+L performance suite. --- .../test/CollectionSpacePerformanceTest.java | 7 +- .../test/PerformanceTest.java | 287 +++++++++++------- .../client/CollectionObjectClient.java | 83 +++-- .../client/CollectionObjectProxy.java | 15 + .../CollectionObjectResource.java | 96 +++++- 5 files changed, 342 insertions(+), 146 deletions(-) diff --git a/services/PerformanceTests/src/test/java/org/collectionspace/services/PerformanceTests/test/CollectionSpacePerformanceTest.java b/services/PerformanceTests/src/test/java/org/collectionspace/services/PerformanceTests/test/CollectionSpacePerformanceTest.java index c2ee4de92..b887f20ff 100644 --- a/services/PerformanceTests/src/test/java/org/collectionspace/services/PerformanceTests/test/CollectionSpacePerformanceTest.java +++ b/services/PerformanceTests/src/test/java/org/collectionspace/services/PerformanceTests/test/CollectionSpacePerformanceTest.java @@ -27,6 +27,7 @@ package org.collectionspace.services.PerformanceTests.test; import java.util.ArrayList; +import java.util.Random; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; @@ -46,6 +47,9 @@ import org.jboss.resteasy.plugins.providers.multipart.MultipartInput; */ public abstract class CollectionSpacePerformanceTest { + protected final static String OBJECT_NUMBER = "objectNumber_"; + protected final static String OBJECT_NAME = "objectName_"; + /* * Package scoped methods. */ @@ -57,8 +61,7 @@ public abstract class CollectionSpacePerformanceTest { * @param identifier the identifier */ void fillCollectionObject(CollectionobjectsCommon co, String identifier) { - fillCollectionObject(co, "objectNumber-" + identifier, "objectName-" - + identifier); + fillCollectionObject(co, OBJECT_NUMBER + identifier, OBJECT_NAME + identifier); } /** diff --git a/services/PerformanceTests/src/test/java/org/collectionspace/services/PerformanceTests/test/PerformanceTest.java b/services/PerformanceTests/src/test/java/org/collectionspace/services/PerformanceTests/test/PerformanceTest.java index f93cf26ca..c3a1881c1 100644 --- a/services/PerformanceTests/src/test/java/org/collectionspace/services/PerformanceTests/test/PerformanceTest.java +++ b/services/PerformanceTests/src/test/java/org/collectionspace/services/PerformanceTests/test/PerformanceTest.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Date; +import java.util.Random; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; @@ -63,6 +64,7 @@ import org.collectionspace.services.CollectionObjectJAXBSchema; import org.collectionspace.services.client.CollectionObjectClient; import org.collectionspace.services.collectionobject.CollectionobjectsCommon; import org.collectionspace.services.collectionobject.CollectionobjectsCommonList; +import org.collectionspace.services.collectionobject.CollectionobjectsCommonList.CollectionObjectListItem; import org.collectionspace.services.IntakeJAXBSchema; import org.collectionspace.services.client.IntakeClient; @@ -82,121 +84,172 @@ import org.collectionspace.services.relation.RelationshipType; */ public class PerformanceTest extends CollectionSpacePerformanceTest { - final Logger logger = LoggerFactory.getLogger(PerformanceTest.class); - // - // Get clients for the CollectionSpace services - // - private RelationClient relationClient = new RelationClient(); - private IntakeClient intakeClient = new IntakeClient(); - private static int MAX_RECORDS = 100; - - private String createCollectionObject(CollectionObjectClient collectionObjectClient) { - String result = null; - // - // First create a CollectionObject - // - CollectionobjectsCommon co = new CollectionobjectsCommon(); - fillCollectionObject(co, createIdentifier()); - - // Next, create a part object - MultipartOutput multipart = new MultipartOutput(); - OutputPart commonPart = multipart.addPart(co, MediaType.APPLICATION_XML_TYPE); - commonPart.getHeaders().add("label", collectionObjectClient.getCommonPartName()); - // Make the create call and check the response - ClientResponse response = collectionObjectClient.create(multipart); - Assert.assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode()); - result = extractId(response); - - return result; - } - - @Test - public void createCollectionObjects() { - if (!isEnabled()) { - return; - } - CollectionObjectClient collectionObjectClient = new CollectionObjectClient(); - String[] coList = new String[MAX_RECORDS]; - - if (logger.isDebugEnabled()) { - logger.debug("Starting perf test..."); - } - Date startTime = new Date(); - for (int i = 0; i < MAX_RECORDS; i++) { - coList[i] = createCollectionObject(collectionObjectClient); - } - Date stopTime = new Date(); - if (logger.isDebugEnabled()) { - logger.debug("Created " + MAX_RECORDS + " CollectionObjects" - + " in " + (stopTime.getTime() - startTime.getTime()) / 1000.0 + " seconds."); - } - - startTime = new Date(); - for (int i = 0; i < MAX_RECORDS; i++) { - deleteCollectionObject(collectionObjectClient, coList[i]); - } - stopTime = new Date(); - if (logger.isDebugEnabled()) { - logger.debug("Deleted " + MAX_RECORDS + " CollectionObjects" - + " in " + (stopTime.getTime() - startTime.getTime()) / 1000.0 + " seconds."); - } - } - - private void deleteCollectionObject(CollectionObjectClient collectionObjectClient, - String resourceId) { - ClientResponse res = collectionObjectClient.delete(resourceId); - } - - @Test - public void relateCollectionObjectToIntake() { - if (!isEnabled()) { - return; - } - // - // First create a CollectionObject - // - CollectionObjectClient collectionObjectClient = new CollectionObjectClient(); - CollectionobjectsCommon co = new CollectionobjectsCommon(); - fillCollectionObject(co, createIdentifier()); - - // Next, create a part object - MultipartOutput multipart = new MultipartOutput(); - OutputPart commonPart = multipart.addPart(co, MediaType.APPLICATION_XML_TYPE); - commonPart.getHeaders().add("label", collectionObjectClient.getCommonPartName()); - // Make the create call and check the response - ClientResponse response = collectionObjectClient.create(multipart); - Assert.assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode()); - String collectionObjectCsid = extractId(response); - - - // Next, create an Intake object - IntakesCommon intake = new IntakesCommon(); - fillIntake(intake, createIdentifier()); - // Create the a part object - multipart = new MultipartOutput(); - commonPart = multipart.addPart(intake, MediaType.APPLICATION_XML_TYPE); - commonPart.getHeaders().add("label", intakeClient.getCommonPartName()); - // Make the call to create and check the response - response = intakeClient.create(multipart); - Assert.assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode()); - String intakeCsid = extractId(response); - - // Lastly, relate the two entities, by creating a new relation object - RelationsCommon relation = new RelationsCommon(); - fillRelation(relation, collectionObjectCsid, CollectionobjectsCommon.class.getSimpleName(), - intakeCsid, IntakesCommon.class.getSimpleName(), - RelationshipType.COLLECTIONOBJECT_INTAKE); - // Create the part and fill it with the relation object - multipart = new MultipartOutput(); - commonPart = multipart.addPart(relation, MediaType.APPLICATION_XML_TYPE); - commonPart.getHeaders().add("label", relationClient.getCommonPartName()); - // Make the call to crate - response = relationClient.create(multipart); - Assert.assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode()); - String relationCsid = extractId(response); - } - - /* - * Private Methods - */ + private static final int MAX_KEYWORDS = 10; + private static final int MAX_SEARCHES = 10; + final Logger logger = LoggerFactory + .getLogger(PerformanceTest.class); + // + // Get clients for the CollectionSpace services + // + private static int MAX_RECORDS = 10000; + + @Test + public void performanceTest() { + roundTripOverhead(10); + deleteCollectionObjects(); + String[] coList = this.createCollectionObjects(MAX_RECORDS); + this.searchCollectionObjects(MAX_RECORDS); +// this.deleteCollectionObjects(coList); + roundTripOverhead(10); + } + + private long roundTripOverhead(int numOfCalls) { + long result = 0; + CollectionObjectClient collectionObjectClient = new CollectionObjectClient(); + + long totalTime = 0; + for (int i = 0; i < numOfCalls; i++) { + Date startTime = new Date(); + collectionObjectClient.roundtrip(); + Date stopTime = new Date(); + totalTime = totalTime + (stopTime.getTime() - startTime.getTime()); + System.out.println("Overhead roundtrip time is: " + (stopTime.getTime() - startTime.getTime())); + } + + System.out.println("------------------------------------------------------------------------------"); + System.out.println("Client to server roundtrip overhead: " + (float)(totalTime / numOfCalls) / 1000); + System.out.println("------------------------------------------------------------------------------"); + System.out.println(""); + + return result; + } + + private void searchCollectionObjects(int numberOfObjects) { + CollectionObjectClient collectionObjectClient = new CollectionObjectClient(); + Random randomGenerator = new Random(System.currentTimeMillis()); + ClientResponse searchResults; + + long totalTime = 0; + long totalSearchResults = 0; + String keywords = ""; + String times = ""; + for (int numOfKeywords = 0; numOfKeywords < MAX_KEYWORDS; + numOfKeywords++, totalTime = 0, totalSearchResults = 0, times = "") { + keywords = keywords + " " + OBJECT_NAME + randomGenerator.nextInt(numberOfObjects); + for (int i = 0; i < MAX_SEARCHES; i++) { + Date startTime = new Date(); + searchResults = collectionObjectClient.keywordSearch(keywords); + Date stopTime = new Date(); + long time = stopTime.getTime() - startTime.getTime(); + times = times + " " + ((float)time / 1000); + totalTime = totalTime + time; + totalSearchResults = totalSearchResults + + searchResults.getEntity().getCollectionObjectListItem().size(); + } + if (logger.isDebugEnabled()) { + System.out.println("------------------------------------------------------------------------------"); + System.out.println("Searched Objects: " + numberOfObjects); + System.out.println("Number of keywords: " + numOfKeywords); + System.out.println("List of keywords: " + keywords); + System.out.println("Number of results: " + totalSearchResults / MAX_SEARCHES); + System.out.println("Result times: " + times); + System.out.println("Average Retreive time: " + (totalTime / MAX_SEARCHES) / 1000.0 + " seconds."); + System.out.println("------------------------------------------------------------------------------"); + } + } + return; + } + + private String createCollectionObject(CollectionObjectClient collectionObjectClient, + int identifier) { + String result = null; + // + // First create a CollectionObject + // + CollectionobjectsCommon co = new CollectionobjectsCommon(); + fillCollectionObject(co, Integer.toString(identifier)); + + // Next, create a part object + MultipartOutput multipart = new MultipartOutput(); + OutputPart commonPart = multipart.addPart(co, MediaType.APPLICATION_XML_TYPE); + commonPart.getHeaders().add("label", collectionObjectClient.getCommonPartName()); + // Make the create call and check the response + ClientResponse response = collectionObjectClient.create(multipart); + + int responseStatus = response.getStatus(); + if (logger.isDebugEnabled() == true) { + if (responseStatus != Response.Status.CREATED.getStatusCode()) + logger.debug("Status of call to create CollectionObject was: " + + responseStatus); + } + + Assert.assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode()); + result = extractId(response); + + return result; + } + + public String[] createCollectionObjects(int numberOfObjects) { + Random randomGenerator = new Random(System.currentTimeMillis()); + CollectionObjectClient collectionObjectClient = new CollectionObjectClient(); + String[] coList = new String[numberOfObjects]; + + int createdObjects = 0; + try { + Date startTime = new Date(); + for (int i = 0; i < numberOfObjects; i++, createdObjects++) { + coList[i] = createCollectionObject(collectionObjectClient, i + 1); + } + Date stopTime = new Date(); + if (logger.isDebugEnabled()) { + System.out.println("Created " + numberOfObjects + " CollectionObjects" + + " in " + (stopTime.getTime() - startTime.getTime())/1000.0 + " seconds."); + } + } catch (AssertionError e) { + System.out.println("FAILURE: Created " + createdObjects + " of " + numberOfObjects + + " before failing."); + Assert.assertTrue(false); + } + + return coList; + } + + private void deleteCollectionObject(CollectionObjectClient collectionObjectClient, + String resourceId) { + ClientResponse res = collectionObjectClient.delete(resourceId); + } + + public void deleteCollectionObjects(String[] arrayOfObjects) { + CollectionObjectClient collectionObjectClient = new CollectionObjectClient(); + + Date startTime = new Date(); + for (int i = 0; i < arrayOfObjects.length; i++) { + deleteCollectionObject(collectionObjectClient, arrayOfObjects[i]); + } + + Date stopTime = new Date(); + if (logger.isDebugEnabled()) { + System.out.println("Deleted " + arrayOfObjects.length + " CollectionObjects" + + " in " + (stopTime.getTime() - startTime.getTime())/1000.0 + " seconds."); + } + } + + public void deleteCollectionObjects() { + CollectionObjectClient collectionObjectClient = new CollectionObjectClient(); + ClientResponse commonsList; + commonsList = collectionObjectClient.readList(); + List coListItems = commonsList.getEntity().getCollectionObjectListItem(); + + Date startTime = new Date(); + for (CollectionObjectListItem i:coListItems) { + deleteCollectionObject(collectionObjectClient, i.getCsid()); + } + Date stopTime = new Date(); + + if (logger.isDebugEnabled()) { + System.out.println("Deleted " + coListItems.size() + " CollectionObjects" + + " in " + (stopTime.getTime() - startTime.getTime())/1000.0 + " seconds."); + } + } + } diff --git a/services/collectionobject/client/src/main/java/org/collectionspace/services/client/CollectionObjectClient.java b/services/collectionobject/client/src/main/java/org/collectionspace/services/client/CollectionObjectClient.java index e2796443b..a342a9580 100644 --- a/services/collectionobject/client/src/main/java/org/collectionspace/services/client/CollectionObjectClient.java +++ b/services/collectionobject/client/src/main/java/org/collectionspace/services/client/CollectionObjectClient.java @@ -26,11 +26,17 @@ */ package org.collectionspace.services.client; +import javax.ws.rs.GET; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; import org.collectionspace.services.collectionobject.CollectionobjectsCommonList; import org.collectionspace.services.common.context.ServiceContext; +import org.collectionspace.services.common.query.IQueryManager; import org.jboss.resteasy.client.ProxyFactory; import org.jboss.resteasy.plugins.providers.RegisterBuiltin; import org.jboss.resteasy.client.ClientResponse; @@ -39,15 +45,11 @@ import org.jboss.resteasy.plugins.providers.multipart.MultipartOutput; import org.jboss.resteasy.spi.ResteasyProviderFactory; /** - * A CollectionObjectClient. - - * @version $Revision:$ + * The Class CollectionObjectClient. */ public class CollectionObjectClient extends AbstractServiceClientImpl { - /** - * - */ + /** The collection object proxy. */ private CollectionObjectProxy collectionObjectProxy; /* (non-Javadoc) @@ -58,9 +60,7 @@ public class CollectionObjectClient extends AbstractServiceClientImpl { } /** - * - * Default constructor for CollectionObjectClient class. - * + * Instantiates a new collection object client. */ public CollectionObjectClient() { ResteasyProviderFactory factory = ResteasyProviderFactory.getInstance(); @@ -69,7 +69,7 @@ public class CollectionObjectClient extends AbstractServiceClientImpl { } /** - * allow to reset proxy as per security needs + * Sets the proxy. */ public void setProxy() { if(useAuth()){ @@ -82,46 +82,79 @@ public class CollectionObjectClient extends AbstractServiceClientImpl { } /** - * @return - * @see org.collectionspace.hello.client.CollectionObjectProxy#readList() + * Read list. + * + * @return the client response< collectionobjects common list> */ public ClientResponse readList() { return collectionObjectProxy.readList(); } + + /** + * Roundtrip. + * + * This is an intentionally empty method that is used for performance test + * to get a rough time estimate of the client to server response-request overhead. + * + * @return the client response< response> + */ + public ClientResponse roundtrip() { + return collectionObjectProxy.roundtrip(); + } + + /** + * Keyword search. + * + * @param keywords the keywords + * + * @return the client response< collectionobjects common list> + */ + public ClientResponse keywordSearch(String keywords) { + return collectionObjectProxy.keywordSearch(keywords); + } + /** - * @param csid - * @return - * @see org.collectionspace.hello.client.CollectionObjectProxy#getCollectionObject(java.lang.String) + * Read. + * + * @param csid the csid + * + * @return the client response< multipart input> */ public ClientResponse read(String csid) { return collectionObjectProxy.read(csid); } /** - * @param collectionobject - * @return - * @see org.collectionspace.hello.client.CollectionObjectProxy#create(org.collectionspace.services.collectionobject.CollectionobjectsCommon) + * Creates the. + * + * @param multipart the multipart + * + * @return the client response< response> */ public ClientResponse create(MultipartOutput multipart) { return collectionObjectProxy.create(multipart); } /** - * @param csid - * @param collectionobject - * @return - * @see org.collectionspace.hello.client.CollectionObjectProxy#updateCollectionObject(java.lang.Long, org.collectionspace.services.collectionobject.CollectionobjectsCommon) + * Update. + * + * @param csid the csid + * @param multipart the multipart + * + * @return the client response< multipart input> */ public ClientResponse update(String csid, MultipartOutput multipart) { return collectionObjectProxy.update(csid, multipart); } /** - * @param csid - * @return - * @see org.collectionspace.hello.client.CollectionObjectProxy#deleteCollectionObject(java.lang.Long) + * Delete. + * + * @param csid the csid + * + * @return the client response< response> */ public ClientResponse delete(String csid) { return collectionObjectProxy.delete(csid); diff --git a/services/collectionobject/client/src/main/java/org/collectionspace/services/client/CollectionObjectProxy.java b/services/collectionobject/client/src/main/java/org/collectionspace/services/client/CollectionObjectProxy.java index e6fcb8ec1..9928ef42c 100644 --- a/services/collectionobject/client/src/main/java/org/collectionspace/services/client/CollectionObjectProxy.java +++ b/services/collectionobject/client/src/main/java/org/collectionspace/services/client/CollectionObjectProxy.java @@ -7,9 +7,13 @@ import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; +import javax.ws.rs.QueryParam; import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import org.collectionspace.services.common.query.IQueryManager; import org.collectionspace.services.collectionobject.CollectionobjectsCommonList; import org.jboss.resteasy.client.ClientResponse; @@ -28,6 +32,17 @@ public interface CollectionObjectProxy { @Produces({"application/xml"}) ClientResponse readList(); + @GET + @Path("/roundtrip") + @Produces({"application/xml"}) + ClientResponse roundtrip(); + + @GET + @Path("/search") + @Produces({"application/xml"}) + ClientResponse keywordSearch( + @QueryParam(IQueryManager.SEARCH_TYPE_KEYWORDS) String keywords); + //(C)reate @POST ClientResponse create(MultipartOutput multipart); diff --git a/services/collectionobject/service/src/main/java/org/collectionspace/services/collectionobject/CollectionObjectResource.java b/services/collectionobject/service/src/main/java/org/collectionspace/services/collectionobject/CollectionObjectResource.java index 449668b17..6785a488e 100644 --- a/services/collectionobject/service/src/main/java/org/collectionspace/services/collectionobject/CollectionObjectResource.java +++ b/services/collectionobject/service/src/main/java/org/collectionspace/services/collectionobject/CollectionObjectResource.java @@ -73,15 +73,24 @@ import org.collectionspace.services.relation.RelationsCommonList; import org.collectionspace.services.relation.RelationsCommon; +/** + * The Class CollectionObjectResource. + */ @Path("/collectionobjects") @Consumes("multipart/mixed") @Produces("multipart/mixed") public class CollectionObjectResource extends AbstractCollectionSpaceResourceImpl { + /** The Constant serviceName. */ static final public String serviceName = "collectionobjects"; + + /** The logger. */ final Logger logger = LoggerFactory.getLogger(CollectionObjectResource.class); + /* (non-Javadoc) + * @see org.collectionspace.services.common.AbstractCollectionSpaceResourceImpl#getVersionString() + */ @Override protected String getVersionString() { /** The last change revision. */ @@ -89,11 +98,17 @@ public class CollectionObjectResource return lastChangeRevision; } + /* (non-Javadoc) + * @see org.collectionspace.services.common.AbstractCollectionSpaceResourceImpl#getServiceName() + */ @Override public String getServiceName() { return serviceName; } + /* (non-Javadoc) + * @see org.collectionspace.services.common.AbstractCollectionSpaceResourceImpl#createDocumentHandler(org.collectionspace.services.common.context.ServiceContext) + */ @Override public DocumentHandler createDocumentHandler(ServiceContext ctx) throws Exception { DocumentHandler docHandler = ctx.getDocumentHandler(); @@ -107,6 +122,13 @@ public class CollectionObjectResource return docHandler; } + /** + * Creates the collection object. + * + * @param input the input + * + * @return the response + */ @POST public Response createCollectionObject(MultipartInput input) { try { @@ -135,6 +157,13 @@ public class CollectionObjectResource } } + /** + * Gets the collection object. + * + * @param csid the csid + * + * @return the collection object + */ @GET @Path("{csid}") public MultipartOutput getCollectionObject( @@ -185,6 +214,13 @@ public class CollectionObjectResource return result; } + /** + * Gets the collection object list. + * + * @param ui the ui + * + * @return the collection object list + */ @GET @Produces("application/xml") public CollectionobjectsCommonList getCollectionObjectList(@Context UriInfo ui) { @@ -209,6 +245,14 @@ public class CollectionObjectResource return collectionObjectList; } + /** + * Update collection object. + * + * @param csid the csid + * @param theUpdate the the update + * + * @return the multipart output + */ @PUT @Path("{csid}") public MultipartOutput updateCollectionObject( @@ -254,6 +298,13 @@ public class CollectionObjectResource return result; } + /** + * Delete collection object. + * + * @param csid the csid + * + * @return the response + */ @DELETE @Path("{csid}") public Response deleteCollectionObject(@PathParam("csid") String csid) { @@ -292,6 +343,14 @@ public class CollectionObjectResource } + /** + * Gets the intakes common list. + * + * @param ui the ui + * @param csid the csid + * + * @return the intakes common list + */ @GET @Path("{csid}/intakes") @Produces("application/xml") @@ -333,13 +392,46 @@ public class CollectionObjectResource } return result; - } + } + + /** + * Roundtrip. + * + * This is an intentionally empty method used for getting a rough time estimate + * of the overhead required for a client->server request/response cycle. + * + * @return the response + */ + @GET + @Path("/roundtrip") + @Produces("application/xml") + public Response roundtrip() { + Response result = null; + + if (logger.isDebugEnabled()) { + logger.debug("------------------------------------------------------------------------------"); + logger.debug("Client to server roundtrip called."); + logger.debug("------------------------------------------------------------------------------"); + logger.debug(""); + } + result = Response.status(HttpResponseCodes.SC_OK).build(); + + return result; + } + //FIXME: Replace this "search" resource with a keyword "kw" query parameter + /** + * Keywords search collection objects. + * + * @param keywords the keywords + * + * @return the collectionobjects common list + */ @GET @Path("/search") @Produces("application/xml") - public CollectionobjectsCommonList keywordsSearchCollectionObjects(@Context UriInfo ui, + public CollectionobjectsCommonList keywordsSearchCollectionObjects( @QueryParam(IQueryManager.SEARCH_TYPE_KEYWORDS) String keywords) { CollectionobjectsCommonList collectionObjectList = new CollectionobjectsCommonList(); try { -- 2.47.3