From 5b5e220a958a020946800e825be51d635dde01be Mon Sep 17 00:00:00 2001 From: Richard Millet Date: Fri, 6 Dec 2013 22:11:01 -0800 Subject: [PATCH] CSPACE-6110: Converting FileInputStream instances to ByteArrayInputStream instances for images less than 256k. --- .../services/client/BlobClient.java | 11 +++ .../services/client/BlobProxy.java | 15 +++- .../services/client/test/BlobScaleTest.java | 21 ++++- .../collectionspace-client.properties | 4 +- .../common/imaging/nuxeo/NuxeoBlobUtils.java | 79 +++++++++++++++---- .../client/java/NuxeoClientEmbedded.java | 2 + .../client/java/RepositoryJavaClientImpl.java | 8 +- 7 files changed, 117 insertions(+), 23 deletions(-) diff --git a/services/blob/client/src/main/java/org/collectionspace/services/client/BlobClient.java b/services/blob/client/src/main/java/org/collectionspace/services/client/BlobClient.java index 5788d17cf..392fdd371 100644 --- a/services/blob/client/src/main/java/org/collectionspace/services/client/BlobClient.java +++ b/services/blob/client/src/main/java/org/collectionspace/services/client/BlobClient.java @@ -16,6 +16,7 @@ */ package org.collectionspace.services.client; +import javax.ws.rs.PathParam; import javax.ws.rs.core.Response; import org.jboss.resteasy.client.ClientResponse; @@ -71,4 +72,14 @@ public class BlobClient extends AbstractCommonListPoxServiceClientImpl createBlobFromURI(String blobUri) { return getProxy().createBlobFromURI("".getBytes(), blobUri); } + + public ClientResponse getBlobContent(String csid) { + return getProxy().getBlobContent(csid); + } + + public ClientResponse getDerivativeContent( + @PathParam("csid") String csid, + @PathParam("derivativeTerm") String derivativeTerm) { + return getProxy().getDerivativeContent(csid, derivativeTerm); + } } diff --git a/services/blob/client/src/main/java/org/collectionspace/services/client/BlobProxy.java b/services/blob/client/src/main/java/org/collectionspace/services/client/BlobProxy.java index 7c3e38c55..b1c85d54f 100644 --- a/services/blob/client/src/main/java/org/collectionspace/services/client/BlobProxy.java +++ b/services/blob/client/src/main/java/org/collectionspace/services/client/BlobProxy.java @@ -1,8 +1,10 @@ package org.collectionspace.services.client; import javax.ws.rs.Consumes; +import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; +import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; @@ -27,5 +29,14 @@ public interface BlobProxy extends CollectionSpaceCommonListPoxProxy { @POST @Consumes("multipart/form-data") ClientResponse createBlobFromFormData(MultipartFormDataOutput formDataOutput); - -} + + @GET + @Path("{csid}/content") + ClientResponse getBlobContent(@PathParam("csid") String csid); + + @GET + @Path("{csid}/derivatives/{derivativeTerm}/content") + public ClientResponse getDerivativeContent( + @PathParam("csid") String csid, + @PathParam("derivativeTerm") String derivativeTerm); +} \ No newline at end of file diff --git a/services/blob/client/src/test/java/org/collectionspace/services/client/test/BlobScaleTest.java b/services/blob/client/src/test/java/org/collectionspace/services/client/test/BlobScaleTest.java index 0007f5631..53bd27c0f 100644 --- a/services/blob/client/src/test/java/org/collectionspace/services/client/test/BlobScaleTest.java +++ b/services/blob/client/src/test/java/org/collectionspace/services/client/test/BlobScaleTest.java @@ -32,6 +32,7 @@ public class BlobScaleTest extends BaseServiceTest { private static final int MAX_FONTSIZE = 60; private static final String IMAGES_TO_CREATE_PROP = "imagesToCreate"; private static final int DEFAULT_IMAGES_TO_CREATE = 1; + private static final int DEFAULT_IMAGES_TO_GET = 1024; private static final String GENERATED_IMAGES = "target/generated_images"; private static Random generator = new Random(System.currentTimeMillis()); @@ -70,6 +71,21 @@ public class BlobScaleTest extends BaseServiceTest { return result; } + @Test(dataProvider = "testName", dependsOnMethods = {"scaleTest"}) + public void scaleGETTest(String testName) throws MalformedURLException { + this.setupRead(); + String blobToGetID = getKnowResourceId(); + BlobClient client = new BlobClient(); + + for (int i = 0; i < DEFAULT_IMAGES_TO_GET; i++) { + ClientResponse res = client.getDerivativeContent(blobToGetID, "Thumbnail"); + assertStatusCode(res, testName); + } + + logger.debug(String.format("Performed %d GET operations on blob = %s.", + DEFAULT_IMAGES_TO_GET, blobToGetID)); + } + @Test(dataProvider = "testName") public void scaleTest(String testName) throws MalformedURLException { this.createDirectory(GENERATED_IMAGES); @@ -83,7 +99,7 @@ public class BlobScaleTest extends BaseServiceTest { URL url = jpegFile.toURI().toURL(); profiler.start(); - ClientResponse res = client.createBlobFromURI(url.toString()); + ClientResponse res = client.createBlobFromURI("http://farm6.static.flickr.com/5289/5688023100_15e00cde47_o.jpg");//url.toString()); try { profiler.stop(); assertStatusCode(res, testName); @@ -96,7 +112,8 @@ public class BlobScaleTest extends BaseServiceTest { + jpegFile.getAbsolutePath()); String csid = extractId(res); - allResourceIdsCreated.add(csid); + //allResourceIdsCreated.add(csid); + this.knownResourceId = csid; } finally { if (res != null) { res.releaseConnection(); diff --git a/services/client/src/main/resources/collectionspace-client.properties b/services/client/src/main/resources/collectionspace-client.properties index 332c68654..b1984bcfb 100644 --- a/services/client/src/main/resources/collectionspace-client.properties +++ b/services/client/src/main/resources/collectionspace-client.properties @@ -1,6 +1,6 @@ #url of the collectionspace server -cspace.url=http://localhost:8180/cspace-services/ -#cspace.url=http://nightly.collectionspace.org:8180/cspace-services/ +#cspace.url=http://localhost:8180/cspace-services/ +cspace.url=http://qa.collectionspace.org:8180/cspace-services/ #cspace.url=http://localhost:8200/cspace-services/ #for sockspy: diff --git a/services/common/src/main/java/org/collectionspace/services/common/imaging/nuxeo/NuxeoBlobUtils.java b/services/common/src/main/java/org/collectionspace/services/common/imaging/nuxeo/NuxeoBlobUtils.java index db7c407e8..d592abab1 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/imaging/nuxeo/NuxeoBlobUtils.java +++ b/services/common/src/main/java/org/collectionspace/services/common/imaging/nuxeo/NuxeoBlobUtils.java @@ -26,6 +26,7 @@ */ package org.collectionspace.services.common.imaging.nuxeo; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; @@ -49,7 +50,6 @@ import org.nuxeo.ecm.platform.picture.api.ImageInfo; import org.nuxeo.ecm.platform.picture.api.ImagingDocumentConstants; import org.nuxeo.ecm.platform.picture.api.ImagingService; import org.nuxeo.ecm.platform.picture.api.PictureView; - import org.nuxeo.ecm.platform.mimetype.MimetypeDetectionException; import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeRegistry; import org.nuxeo.ecm.platform.picture.api.adapters.PictureBlobHolder; @@ -58,10 +58,8 @@ import org.nuxeo.ecm.platform.filemanager.service.FileManagerService; import org.nuxeo.ecm.platform.filemanager.service.extension.FileImporter; import org.nuxeo.ecm.platform.filemanager.utils.FileManagerUtils; import org.nuxeo.ecm.platform.types.TypeManager; - import org.nuxeo.ecm.core.repository.RepositoryDescriptor; import org.nuxeo.ecm.core.repository.RepositoryManager; - import org.nuxeo.ecm.core.repository.RepositoryService; import org.nuxeo.ecm.core.storage.sql.BinaryManager; import org.nuxeo.ecm.core.storage.sql.DefaultBinaryManager; @@ -96,14 +94,13 @@ import org.nuxeo.ecm.core.api.ClientException; import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.api.DocumentRef; import org.nuxeo.ecm.core.event.EventServiceAdmin; - import org.nuxeo.ecm.core.schema.SchemaManager; import org.nuxeo.ecm.core.schema.types.Schema; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; //import org.nuxeo.ecm.core.repository.jcr.testing.RepositoryOSGITestCase; +import org.apache.commons.io.IOUtils; import org.collectionspace.services.client.PoxPayloadIn; import org.collectionspace.services.client.PoxPayloadOut; import org.collectionspace.services.common.FileUtils; @@ -136,6 +133,12 @@ public class NuxeoBlobUtils { private static final Logger logger = LoggerFactory .getLogger(NuxeoBlobUtils.class); + // + // A maximum byte size for the byte array used to hold an image. Images larger than this will + // be returned as FileInputStreams rather than ByteArrayInputStreams + // + private static final int MAX_IMAGE_BUFFER = 256 * 1024; // REM: 11/26/2013 - This should be set in a config/property file. + // // File name constants // @@ -441,6 +444,11 @@ public class NuxeoBlobUtils { static private BlobsCommon createBlobsCommon(DocumentModel documentModel, Blob nuxeoBlob) { + return createBlobsCommon(documentModel, nuxeoBlob, false); + } + + static private BlobsCommon createBlobsCommon(DocumentModel documentModel, + Blob nuxeoBlob, Boolean getContentFlag) { BlobsCommon result = new BlobsCommon(); if (documentModel != null) { @@ -448,14 +456,21 @@ public class NuxeoBlobUtils { result.setName(nuxeoBlob.getFilename()); result.setLength(Long.toString(nuxeoBlob.getLength())); result.setRepositoryId(documentModel.getId()); - MeasuredPartGroupList measuredPartGroupList = getDimensions( - documentModel, nuxeoBlob); - if (measuredPartGroupList != null) { - result.setMeasuredPartGroupList(measuredPartGroupList); + + // + // If getContentFlag is true then we're being asked for the blob's content, so we don't + // need the measurement info. Getting the measurement info requires a call to Nuxeo which in turn + // calls ImageMagick. + // + if (getContentFlag.booleanValue() == false) { + MeasuredPartGroupList measuredPartGroupList = getDimensions( + documentModel, nuxeoBlob); + if (measuredPartGroupList != null) { + result.setMeasuredPartGroupList(measuredPartGroupList); + } } // Check to see if a thumbnail preview was created by Nuxeo - // REM: 11/26/2013 - This looks like dead code? What are we looking for a thumbnail? if (documentModel.hasFacet(ThumbnailConstants.THUMBNAIL_FACET)) { String errorMsg = null; String thumbnailName = null; @@ -1185,6 +1200,38 @@ public class NuxeoBlobUtils { return result; } + // + // If the blob is not too big, we return a ByteArrayInputStream. Otherwise, we return Nuxeo's InputStream + // which is usually a FileInputStream. + // + static private InputStream getInputStream(BlobsCommon blobsCommon, Blob blob) { + InputStream result = null; + + try { + InputStream blobStream = blob.getStream(); // By default, the result will be whatever stream Nuxeo returns to us. + int blobSize = blobsCommon.getLength() != null ? Integer.parseInt(blobsCommon.getLength()) : 0; + if (blobSize > 0 && blobSize < MAX_IMAGE_BUFFER) { + byte[] bytes = IOUtils.toByteArray(blobStream); + blobStream.close(); // Close the InputStream that we got from Nuxeo since it's usually a FileInputStream -we definitely want FileInputStreams closed. + result = new ByteArrayInputStream(bytes); + } else { + result = blobStream; // The blob is too large to put into a ByteArrayStream. + } + } catch (Exception e) { + logger.error(String.format("Error getting the InputStream content for file %s.", blobsCommon.getName()), e); + if (result != null) { + try { + result.close(); + result = null; + } catch (Exception x) { + logger.debug(String.format("Exception encountered during InputStream cleanup of file %s", blobsCommon.getName()), x); + } + } + } + + return result; + } + /** * Gets the image. * @@ -1240,19 +1287,21 @@ public class NuxeoBlobUtils { // and an InputStream with the bits if the 'getContentFlag' is // set. // - BlobsCommon blobsCommon = createBlobsCommon(documentModel, docBlob); + BlobsCommon blobsCommon = createBlobsCommon(documentModel, docBlob, getContentFlag); result.setBlobsCommon(blobsCommon); if (getContentFlag == true) { InputStream remoteStream = null; if (isNonImageDerivative == false) { - remoteStream = docBlob.getStream(); // This will fail if the blob's file has been deleted. FileNotFoundException thrown. + //remoteStream = docBlob.getStream(); + remoteStream = getInputStream(blobsCommon, docBlob); // CSPACE-6110 - For small files, return a byte array instead of a file stream } else { remoteStream = getResource(DOCUMENT_PLACEHOLDER_IMAGE); outMimeType.append(MIME_JPEG); } - BufferedInputStream bufferedInputStream = new BufferedInputStream( - remoteStream); - result.setBlobInputStream(bufferedInputStream); +// BufferedInputStream bufferedInputStream = new BufferedInputStream( +// remoteStream); +// result.setBlobInputStream(bufferedInputStream); + result.setBlobInputStream(remoteStream); } } catch (Exception e) { if (logger.isErrorEnabled() == true) { diff --git a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/NuxeoClientEmbedded.java b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/NuxeoClientEmbedded.java index c2672e8b7..121abc2d5 100644 --- a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/NuxeoClientEmbedded.java +++ b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/NuxeoClientEmbedded.java @@ -420,6 +420,7 @@ public final class NuxeoClientEmbedded { } } + // REM 10/29/2013 - We may want to add this clause if (!TransactionHelper.isTransactionActive()) { boolean startTransaction = TransactionHelper.startTransaction(); if (startTransaction == false) { logger.warn("Could not start a Nuxeo transaction with the TransactionHelper class."); @@ -469,6 +470,7 @@ public final class NuxeoClientEmbedded { + repositoryInstances.size()); } } + //if (TransactionHelper.isTransactionActiveOrMarkedRollback()) TransactionHelper.commitOrRollbackTransaction(); } } diff --git a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RepositoryJavaClientImpl.java b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RepositoryJavaClientImpl.java index c764a3c33..cebbb47db 100644 --- a/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RepositoryJavaClientImpl.java +++ b/services/common/src/main/java/org/collectionspace/services/nuxeo/client/java/RepositoryJavaClientImpl.java @@ -1737,8 +1737,12 @@ public class RepositoryJavaClientImpl implements RepositoryClient