From 82a60d5ef438dfcf59eb8b7c9a9cd5ad3558ed9c Mon Sep 17 00:00:00 2001 From: Michael Ritter Date: Thu, 4 Jan 2024 18:13:16 -0700 Subject: [PATCH] DRYD-1250: UCB Blob contributions (#372) Co-authored-by: Richard Millet --- .../bundles/nuxeo-binary-metadata-9.10.jar | Bin 0 -> 44036 bytes .../services/blob/BlobResource.java | 165 ++++--- .../blob/nuxeo/BlobDocumentModelHandler.java | 1 + services/common/pom.xml | 5 + .../services/common/blob/BlobOutput.java | 10 + .../common/imaging/nuxeo/NuxeoBlobUtils.java | 434 +++++++++--------- 6 files changed, 326 insertions(+), 289 deletions(-) create mode 100644 3rdparty/nuxeo/nuxeo-server/9.10-HF30/bundles/nuxeo-binary-metadata-9.10.jar diff --git a/3rdparty/nuxeo/nuxeo-server/9.10-HF30/bundles/nuxeo-binary-metadata-9.10.jar b/3rdparty/nuxeo/nuxeo-server/9.10-HF30/bundles/nuxeo-binary-metadata-9.10.jar new file mode 100644 index 0000000000000000000000000000000000000000..dfa81772ef31944bde94f0401c6b77b77e3e5155 GIT binary patch literal 44036 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≦-6DXMw<<$erwq9$uXF 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 queryParams,String keywords) { // return (CommonList) super.search(queryParams, keywords); // } - + private CommonList getDerivativeList(ServiceContext ctx, String csid) throws Exception { CommonList result = null; - + BlobInput blobInput = new BlobInput(); blobInput.setDerivativeListRequested(true); BlobUtil.setBlobInput(ctx, blobInput); @@ -117,21 +117,21 @@ public class BlobResource extends NuxeoBasedResource { // blobInput instance // result = BlobUtil.getBlobInput(ctx).getDerivativeList(); - + return result; } - + public InputStream getBlobContent(ServiceContext ctx, - String csid, - String derivativeTerm, + String csid, + String derivativeTerm, StringBuffer outMimeType) throws CSWebApplicationException { InputStream result = null; - + try { BlobInput blobInput = BlobUtil.getBlobInput(ctx); blobInput.setDerivativeTerm(derivativeTerm); blobInput.setContentRequested(true); - + PoxPayloadOut response = this.get(csid, ctx); if (logger.isDebugEnabled() == true) { logger.debug(response.toString()); @@ -140,7 +140,7 @@ public class BlobResource extends NuxeoBasedResource { // The result of a successful get should have put the results in the // blobInput instance // - + String mimeType = blobInput.getMimeType(); if (mimeType != null) { outMimeType.append(mimeType); // blobInput's mime type was set on call to "get" above by the doc handler @@ -149,7 +149,7 @@ public class BlobResource extends NuxeoBasedResource { } catch (Exception e) { throw bigReThrow(e, ServiceMessages.CREATE_FAILED); } - + if (result == null) { String errMsg = String.format("Index failed. Could not get the contents for the Blob with CSID = '%s'.", csid); @@ -157,10 +157,52 @@ public class BlobResource extends NuxeoBasedResource { Response.Status.INTERNAL_SERVER_ERROR).entity(errMsg).type("text/plain").build(); throw new CSWebApplicationException(response); } - + return result; } - + + /** + * Create a response for the Blob content. Depending on how the Blob is stored by Nuxeo, this may or may not support + * the Range HTTP header. If a Blob is backed by a File it will support Range, otherwise we can only use the input + * stream which RESTEasy won't apply the header to. + * + * @param ctx the service context + * @param csid the csid of the blob to get + * @return the http response builder for the Blob + * @throws CSWebApplicationException if the Blob or its content can't be retrieved + */ + private ResponseBuilder getBlobContentResponse(ServiceContext ctx, String csid) { + ResponseBuilder response = null; + + try { + BlobInput blobInput = BlobUtil.getBlobInput(ctx); + blobInput.setDerivativeTerm(null); + blobInput.setContentRequested(true); + + // refresh the BlobInput + PoxPayloadOut poxPayloadOut = this.get(csid, ctx); + + if (blobInput.getBlobFile() != null) { + response = Response.ok(blobInput.getBlobFile(), blobInput.getMimeType()); + } else if (blobInput.getContentStream() != null) { + response = Response.ok(blobInput.getContentStream(), blobInput.getMimeType()); + } + } catch (Exception e) { + throw bigReThrow(e, ServiceMessages.GET_FAILED); + } + + if (response == null) { + String errMsg = String.format("Index failed. Could not get the contents for the Blob with CSID = '%s'.", + csid); + Response errorResponse = Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity(errMsg) + .type(MediaType.TEXT_PLAIN).build(); + throw new CSWebApplicationException(errorResponse); + } + + return response; + } + /* * This method can replace the 'createBlob' -specifically, this JAX-RS technique can replace the call to * the BlobInput.createBlobFile() method. In theory, this should reduce by 1 the number of times we need to copy @@ -175,15 +217,15 @@ public class BlobResource extends NuxeoBasedResource { @Context HttpServletRequest req, @QueryParam(BlobClient.BLOB_URI_PARAM) String blobUri, MultipartFormDataInput partFormData) { - Response response = null; + Response response = null; try { InputStream fileStream = null; String preamble = partFormData.getPreamble(); System.out.println("Preamble type is:" + preamble); - + Map> partsMap = partFormData.getFormDataMap(); List fileParts = partsMap.get("file"); - + for (InputPart part : fileParts) { String mediaType = part.getMediaType().toString(); @@ -191,23 +233,23 @@ public class BlobResource extends NuxeoBasedResource { fileStream = part.getBody(InputStream.class, null); FileUtilities.createTmpFile(fileStream, getServiceName() + "_"); } - + ResponseBuilder rb = Response.ok(); rb.entity("Goodbye, world!"); response = rb.build(); } catch (Exception e) { throw bigReThrow(e, ServiceMessages.CREATE_FAILED); } - + return response; - } - + } + @POST @Consumes("multipart/form-data") @Produces("application/xml") public Response createBlob(@Context HttpServletRequest req, @QueryParam(BlobClient.BLOB_URI_PARAM) String blobUri) { - Response response = null; + Response response = null; try { ServiceContext ctx = createServiceContext(); BlobInput blobInput = BlobUtil.getBlobInput(ctx); @@ -216,20 +258,20 @@ public class BlobResource extends NuxeoBasedResource { } catch (Exception e) { throw bigReThrow(e, ServiceMessages.CREATE_FAILED); } - + return response; } - + @POST @Override public Response create( - @Context ResourceMap resourceMap, + @Context ResourceMap resourceMap, @Context UriInfo ui, String xmlPayload) { Response response = null; MultivaluedMap queryParams = ui.getQueryParameters(); String blobUri = queryParams.getFirst(BlobClient.BLOB_URI_PARAM); - + try { if (blobUri != null) { // If we were passed a URI than try to create a blob from it ServiceContext ctx = createServiceContext(); @@ -243,10 +285,10 @@ public class BlobResource extends NuxeoBasedResource { } catch (Exception e) { throw bigReThrow(e, ServiceMessages.CREATE_FAILED); } - + return response; - } - + } + /** * If there is no explicit setting in the Blobs service binding, we'll ask the HTTP client to cache blobs for 1 full day. * @param ctx @@ -256,7 +298,7 @@ public class BlobResource extends NuxeoBasedResource { @Override protected CacheControl getCacheControl(ServiceContext ctx, String cacheKey) { CacheControl result = null; - + if (cacheKey.matches(DERIVATIVES_REGEX)) { Pattern p = Pattern.compile(DERIVATIVES_REGEX); Matcher m = p.matcher(cacheKey); @@ -264,16 +306,16 @@ public class BlobResource extends NuxeoBasedResource { cacheKey = String.format("%s%s*%s", m.group(1), m.group(2), m.group(4)); // Converts something like this "GET/blobs/*/derivatives/Medium/content" into this "GET/blobs/*/derivatives/*/content" } } - + result = super.getCacheControl(ctx, cacheKey); if (result == null) { result = new CacheControl(); result.setMaxAge(DEFAULT_MAX_CACHE_AGE); } - + return result; } - + @GET @Path("{csid}/content") public Response getBlobContent( @@ -281,18 +323,15 @@ public class BlobResource extends NuxeoBasedResource { @Context Request jaxRsRequest, @Context UriInfo uriInfo) { Response result = null; - ServiceContext ctx = null; - + ServiceContext ctx = null; + try { ctx = createServiceContext(jaxRsRequest, uriInfo); BlobsCommon blobsCommon = getBlobsCommon(csid); - StringBuffer mimeType = new StringBuffer(); - InputStream contentStream = getBlobContent(ctx, csid, null /*derivative term*/, mimeType /*will get set*/); - - Response.ResponseBuilder responseBuilder = Response.ok(contentStream, mimeType.toString()); + + Response.ResponseBuilder responseBuilder = getBlobContentResponse(ctx, csid); setCacheControl(ctx, responseBuilder); - responseBuilder = responseBuilder.header("Content-Disposition","inline;filename=\"" - + blobsCommon.getName() +"\""); + responseBuilder.header("Content-Disposition","inline;filename=\"" + blobsCommon.getName() + "\""); result = responseBuilder.build(); } catch (Exception e) { throw bigReThrow(e, ServiceMessages.CREATE_FAILED); @@ -300,18 +339,18 @@ public class BlobResource extends NuxeoBasedResource { return result; } - + private BlobsCommon getBlobsCommon(String csid) throws Exception { BlobsCommon result = null; - + ServiceContext ctx = createServiceContext(); PoxPayloadOut ppo = this.get(csid, ctx); PayloadPart blobsCommonPart = ppo.getPart(BlobClient.SERVICE_COMMON_PART_NAME); result = (BlobsCommon)blobsCommonPart.getBody(); - + return result; } - + /* * Publish the blob content. */ @@ -323,45 +362,45 @@ public class BlobResource extends NuxeoBasedResource { @PathParam("csid") String csid) { Response result = null; ServiceContext ctx = null; - + try { - ctx = createServiceContext(); + ctx = createServiceContext(); BlobsCommon blobsCommon = getBlobsCommon(csid); StringBuffer mimeType = new StringBuffer(); - InputStream contentStream = getBlobContent(ctx, csid, null /*derivative term*/, mimeType /*will get set*/); - result = PublicItemUtil.publishToRepository((PublicitemsCommon)null, resourceMap, uriInfo, + InputStream contentStream = getBlobContent(ctx, csid, null /*derivative term*/, mimeType /*will get set*/); + result = PublicItemUtil.publishToRepository((PublicitemsCommon)null, resourceMap, uriInfo, getRepositoryClient(ctx), ctx, contentStream, blobsCommon.getName()); } catch (Exception e) { throw bigReThrow(e, ServiceMessages.PUT_FAILED); } - + return result; } - + @POST @Path("{csid}/derivatives/{derivativeTerm}/content/publish") public Response publishDerivativeContent( @Context ResourceMap resourceMap, - @Context UriInfo uriInfo, + @Context UriInfo uriInfo, @PathParam("csid") String csid, @PathParam("derivativeTerm") String derivativeTerm) { Response result = null; ServiceContext ctx = null; - + try { ctx = createServiceContext(); BlobsCommon blobsCommon = getBlobsCommon(csid); StringBuffer mimeType = new StringBuffer(); InputStream contentStream = getBlobContent(ctx, csid, derivativeTerm, mimeType); - result = PublicItemUtil.publishToRepository((PublicitemsCommon)null, resourceMap, uriInfo, + result = PublicItemUtil.publishToRepository((PublicitemsCommon)null, resourceMap, uriInfo, getRepositoryClient(ctx), ctx, contentStream, blobsCommon.getName()); } catch (Exception e) { throw bigReThrow(e, ServiceMessages.CREATE_FAILED); } - + return result; } - + @GET @Path("{csid}/derivatives/{derivativeTerm}/content") public Response getDerivativeContent( @@ -371,7 +410,7 @@ public class BlobResource extends NuxeoBasedResource { @Context UriInfo uriInfo) { Response result = null; ServiceContext ctx = null; - + try { // Setup the call to get the blob derivative ctx = createServiceContext(jaxRsRequest, uriInfo); @@ -391,13 +430,13 @@ public class BlobResource extends NuxeoBasedResource { return result; } - + @GET @Path("{csid}/derivatives/{derivativeTerm}") public String getDerivative(@PathParam("csid") String csid, @PathParam("derivativeTerm") String derivativeTerm) { PoxPayloadOut result = null; - + ensureCSID(csid, READ); try { ServiceContext ctx = createServiceContext(); @@ -412,10 +451,10 @@ public class BlobResource extends NuxeoBasedResource { } catch (Exception e) { throw bigReThrow(e, ServiceMessages.READ_FAILED, csid); } - + return result.toXML(); } - + @GET @Path("{csid}/derivatives") public CommonList getDerivatives(@PathParam("csid") String csid) { @@ -433,8 +472,8 @@ public class BlobResource extends NuxeoBasedResource { } catch (Exception e) { throw bigReThrow(e, ServiceMessages.READ_FAILED, csid); } - + return result; } - + } diff --git a/services/blob/service/src/main/java/org/collectionspace/services/blob/nuxeo/BlobDocumentModelHandler.java b/services/blob/service/src/main/java/org/collectionspace/services/blob/nuxeo/BlobDocumentModelHandler.java index a9475b49a..dc0f4d981 100644 --- a/services/blob/service/src/main/java/org/collectionspace/services/blob/nuxeo/BlobDocumentModelHandler.java +++ b/services/blob/service/src/main/java/org/collectionspace/services/blob/nuxeo/BlobDocumentModelHandler.java @@ -166,6 +166,7 @@ extends NuxeoDocumentModelHandler { if (getContentFlag == true) { if (blobOutput != null) { blobInput.setContentStream(blobOutput.getBlobInputStream()); + blobInput.setBlobFile(blobOutput.getBlobFile()); } else { blobInput.setContentStream(null); } diff --git a/services/common/pom.xml b/services/common/pom.xml index 73ff2f71d..b72827120 100644 --- a/services/common/pom.xml +++ b/services/common/pom.xml @@ -234,6 +234,11 @@ provided + + org.nuxeo.binary.metadata + nuxeo-binary-metadata + ${nuxeo.platform.version} + org.nuxeo.lib.runtime nuxeo-runtime-launcher diff --git a/services/common/src/main/java/org/collectionspace/services/common/blob/BlobOutput.java b/services/common/src/main/java/org/collectionspace/services/common/blob/BlobOutput.java index 6acb2a327..a2c9043f7 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/blob/BlobOutput.java +++ b/services/common/src/main/java/org/collectionspace/services/common/blob/BlobOutput.java @@ -1,5 +1,6 @@ package org.collectionspace.services.common.blob; +import java.io.File; import java.io.InputStream; import org.collectionspace.services.blob.BlobsCommon; @@ -7,6 +8,7 @@ public class BlobOutput { private String mimeType; private BlobsCommon blobsCommon; private InputStream blobInputStream; + private File blobFile; public BlobsCommon getBlobsCommon() { return blobsCommon; @@ -26,4 +28,12 @@ public class BlobOutput { public void setMimeType(String mimeType) { this.mimeType = mimeType; } + + public void setBlobFile(File file) { + this.blobFile = file; + } + + public File getBlobFile() { + return this.blobFile; + } } 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 905c379b8..2b638550d 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 @@ -1,4 +1,4 @@ -/** +/** * NuxeoImageUtils.java * * {Purpose of This Class} @@ -59,6 +59,7 @@ import org.nuxeo.ecm.core.api.blobholder.BlobHolder; import org.nuxeo.ecm.core.api.blobholder.DocumentBlobHolder; import org.nuxeo.ecm.core.api.impl.blob.FileBlob; import org.nuxeo.ecm.core.api.model.Property; +import org.nuxeo.binary.metadata.api.BinaryMetadataService; import org.nuxeo.ecm.core.api.Blob; import org.nuxeo.ecm.core.api.ClientException; import org.nuxeo.ecm.core.api.DocumentModel; @@ -104,7 +105,7 @@ import org.collectionspace.services.config.service.ListResultField; */ class CSpaceFileBlob extends FileBlob { /** - * + * */ private static final long serialVersionUID = 1L; @@ -118,23 +119,22 @@ class CSpaceFileBlob extends FileBlob { * The Class NuxeoBlobUtils. */ public class NuxeoBlobUtils { - + /** The Constant logger. */ - private static final Logger logger = LoggerFactory - .getLogger(NuxeoBlobUtils.class); - + private static final Logger logger = LoggerFactory.getLogger(NuxeoBlobUtils.class); + private static final String VIEWS_PROPERTY = "picture:views"; private static final String TITLE_PROPERTY = "title"; private static final String FILENAME_PROPERTY = "filename"; - // - // 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. - + /** + * 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 + *

+ * REM: 11/26/2013 - This should be set in a config/property file. + */ + private static final int MAX_IMAGE_BUFFER = 256 * 1024; - // // File name constants // @@ -143,7 +143,7 @@ public class NuxeoBlobUtils { public static final String DOCUMENT_PLACEHOLDER_IMAGE = "documentImage.jpg"; public static final String DOCUMENT_MISSING_PLACEHOLDER_IMAGE = "documentImageMissing.jpg"; - + public static final String DOCUMENT_PLACEHOLDER_CSV = "documentCSV.jpg"; public static final String DOCUMENT_PLACEHOLDER_DOC = "documentDOC.jpg"; public static final String DOCUMENT_PLACEHOLDER_DOCX = "documentDOC.jpg"; @@ -174,8 +174,7 @@ public class NuxeoBlobUtils { * hard coded. */ public static final String DERIVATIVE_ORIGINAL = "Original"; - public static final String DERIVATIVE_ORIGINAL_TAG = DERIVATIVE_ORIGINAL - + "_"; + public static final String DERIVATIVE_ORIGINAL_TAG = DERIVATIVE_ORIGINAL + "_"; public static final String DERIVATIVE_ORIGINAL_JPEG = "OriginalJpeg"; public static final String DERIVATIVE_ORIGINAL_JPEG_TAG = DERIVATIVE_ORIGINAL_JPEG + "_"; @@ -263,17 +262,17 @@ public class NuxeoBlobUtils { return item; } - + static public String getSanizitedFilename(File srcFile) throws Exception { return getSanizitedFilename(srcFile.getName()); } - + /* * Valid Nuxeo file names are a subset of *nix and Windows filenames, so we need to check. */ static public String getSanizitedFilename(String fileName) throws Exception { String result = fileName; - + if (fileName != null && fileName.matches(NUXEO_FILENAME_VALID_STRING) == false) { String fixedString = fileName.replaceAll(NUXEO_FILENAME_BAD_CHARS, "_"); // Replace "bad" chars with underscore character if (fixedString.matches(NUXEO_FILENAME_VALID_STRING) == true) { @@ -283,7 +282,7 @@ public class NuxeoBlobUtils { throw new Exception(errMsg); } } - + if (result != null && logger.isDebugEnabled() == true) { if (result.equals(fileName) == false) { logger.debug(String.format("The file name '%s' was sanizitized to '%s'.", fileName, result)); @@ -293,9 +292,9 @@ public class NuxeoBlobUtils { return result; } - static private void handleGenericBlobs(DocumentBlobHolder docBlobHolder, CommonList commonList, String uri) { + private static void handleGenericBlobs(DocumentBlobHolder docBlobHolder, CommonList commonList, String uri) { int total = 0; - List docBlobs = docBlobHolder.getBlobs(); + List docBlobs = docBlobHolder.getBlobs(); // List blobListItems = result.getBlobListItem(); HashMap item = null; for (Blob blob : docBlobs) { @@ -311,8 +310,8 @@ public class NuxeoBlobUtils { commonList.setTotalItems(total); commonList.setItemsInPage(total); } - - static private void handlePictureViewBlobs(PictureBlobHolder pictureBlobHolder, + + private static void handlePictureViewBlobs(PictureBlobHolder pictureBlobHolder, CoreSessionInterface repoSession, String repositoryId, CommonList commonList, @@ -359,111 +358,97 @@ public class NuxeoBlobUtils { } else { handleGenericBlobs(docBlobHolder, commonList, uri); } - + return commonList; } - /* - * [dublincore, uid, picture, iptc, common, image_metadata] - */ - static private Map getMetadata(Blob nuxeoBlob) - throws Exception { - ImagingService service = Framework.getService(ImagingService.class); - Map metadataMap = service.getImageMetadata(nuxeoBlob); - return metadataMap; - } + public static boolean isBlobAnImage(Blob input) { + boolean result = false; + + FileImporter importer = getFileManagerService().getPluginByName("Imageplugin"); + + String normalizedMimeType = getMimeService().getMimetypeEntryByMimeType(input.getMimeType()).getNormalized(); + if (importer.isEnabled() && (importer.matches(normalizedMimeType) || importer.matches(input.getMimeType()))) { + result = true; + } + + return result; + } + + /* + * [dublincore, uid, picture, iptc, common, image_metadata] + */ + private static Map getMetadata(Blob nuxeoBlob) throws Exception { + BinaryMetadataService binaryMetadataService = Framework.getService(BinaryMetadataService.class); + return binaryMetadataService.readMetadata(nuxeoBlob, false); + } - private static String[] imageTypes = {"jpeg", "bmp", "gif", "png", "tiff", "octet-stream"}; - static private boolean isImageMedia(Blob nuxeoBlob) { - boolean result = false; - - String mimeType = nuxeoBlob.getMimeType(); - if (mimeType != null) { - mimeType = mimeType.toLowerCase().trim(); - String[] parts = mimeType.split("/"); // split strings like "application/xml" into an array of two strings - if (parts.length == 2) { - for (String type : imageTypes) { - if (parts[1].equalsIgnoreCase(type)) { - result = true; - break; - } - } - } - } - - return result; - } - static private MeasuredPartGroupList getDimensions( DocumentModel documentModel, Blob nuxeoBlob) { MeasuredPartGroupList result = null; - - if (isImageMedia(nuxeoBlob) == true) try { - ImagingService service = Framework.getService(ImagingService.class); - ImageInfo imageInfo = service.getImageInfo(nuxeoBlob); - Map metadataMap = getMetadata(nuxeoBlob); - if (imageInfo != null) { - // - // Create a timestamp to add to all the image's dimensions - // - String valueDate = GregorianCalendarDateTimeUtils - .timestampUTC(); - - result = new MeasuredPartGroupList(); - List measuredPartGroupList = - (result).getMeasuredPartGroup(); - // - // Create a new measured part for the "image" - // - MeasuredPartGroup mpGroup = new MeasuredPartGroup(); - mpGroup.setMeasuredPart(PART_IMAGE); - mpGroup.setDimensionSummary(PART_SUMMARY); - mpGroup.setDimensionSubGroupList(new DimensionSubGroupList()); - List dimensionSubGroupList = mpGroup.getDimensionSubGroupList() - .getDimensionSubGroup(); + if (isBlobAnImage(nuxeoBlob)) { + try { + ImagingService service = Framework.getService(ImagingService.class); + ImageInfo imageInfo = service.getImageInfo(nuxeoBlob); + Map metadataMap = getMetadata(nuxeoBlob); - // - // Set the width - // - DimensionSubGroup widthDimension = new DimensionSubGroup(); - widthDimension.setDimension(WIDTH); - widthDimension.setMeasurementUnit(UNIT_PIXELS); - widthDimension.setValue(intToBigDecimal(imageInfo.getWidth())); - widthDimension.setValueDate(valueDate); - dimensionSubGroupList.add(widthDimension); - // - // Set the height - // - DimensionSubGroup heightDimension = new DimensionSubGroup(); - heightDimension.setDimension(HEIGHT); - heightDimension.setMeasurementUnit(UNIT_PIXELS); - heightDimension - .setValue(intToBigDecimal(imageInfo.getHeight())); - heightDimension.setValueDate(valueDate); - dimensionSubGroupList.add(heightDimension); - // - // Set the depth - // - DimensionSubGroup depthDimension = new DimensionSubGroup(); - depthDimension.setDimension(DEPTH); - depthDimension.setMeasurementUnit(UNIT_BITS); - depthDimension.setValue(intToBigDecimal(imageInfo.getDepth())); - depthDimension.setValueDate(valueDate); - dimensionSubGroupList.add(depthDimension); - // - // Now set out result - // - measuredPartGroupList.add(mpGroup); - } else { - if (logger.isWarnEnabled() == true) { - logger.warn("Could not synthesize a dimension list of the blob: " - + documentModel.getName()); + if (imageInfo != null) { + // + // Create a timestamp to add to all the image's dimensions + // + String valueDate = GregorianCalendarDateTimeUtils.timestampUTC(); + + result = new MeasuredPartGroupList(); + List measuredPartGroupList = (result).getMeasuredPartGroup(); + + // + // Create a new measured part for the "image" + // + MeasuredPartGroup mpGroup = new MeasuredPartGroup(); + mpGroup.setMeasuredPart(PART_IMAGE); + mpGroup.setDimensionSummary(PART_SUMMARY); + mpGroup.setDimensionSubGroupList(new DimensionSubGroupList()); + List dimensionSubGroupList = + mpGroup.getDimensionSubGroupList().getDimensionSubGroup(); + + // + // Set the width + // + DimensionSubGroup widthDimension = new DimensionSubGroup(); + widthDimension.setDimension(WIDTH); + widthDimension.setMeasurementUnit(UNIT_PIXELS); + widthDimension.setValue(intToBigDecimal(imageInfo.getWidth())); + widthDimension.setValueDate(valueDate); + dimensionSubGroupList.add(widthDimension); + // + // Set the height + // + DimensionSubGroup heightDimension = new DimensionSubGroup(); + heightDimension.setDimension(HEIGHT); + heightDimension.setMeasurementUnit(UNIT_PIXELS); + heightDimension.setValue(intToBigDecimal(imageInfo.getHeight())); + heightDimension.setValueDate(valueDate); + dimensionSubGroupList.add(heightDimension); + // + // Set the depth + // + DimensionSubGroup depthDimension = new DimensionSubGroup(); + depthDimension.setDimension(DEPTH); + depthDimension.setMeasurementUnit(UNIT_BITS); + depthDimension.setValue(intToBigDecimal(imageInfo.getDepth())); + depthDimension.setValueDate(valueDate); + dimensionSubGroupList.add(depthDimension); + // + // Now set out result + // + measuredPartGroupList.add(mpGroup); + } else { + logger.warn("Could not synthesize a dimension list of the blob: {}", documentModel.getName()); } + } catch (Exception e) { + logger.warn("Could not extract image information for blob: {}", documentModel.getName(), e); } - } catch (Exception e) { - logger.warn("Could not extract image information for blob: " - + documentModel.getName(), e); } return result; @@ -481,7 +466,7 @@ public class NuxeoBlobUtils { Blob nuxeoBlob) { return createBlobsCommon(ctx, documentModel, nuxeoBlob, false); } - + static private BlobsCommon createBlobsCommon(ServiceContext ctx, DocumentModel documentModel, Blob nuxeoBlob, Boolean getContentFlag) { BlobsCommon result = new BlobsCommon(); @@ -503,7 +488,7 @@ public class NuxeoBlobUtils { } } result.setDigest(digest); - + // // 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 @@ -559,29 +544,29 @@ public class NuxeoBlobUtils { } return blob; } - + /** * Gets the type service. Not in use, but please keep for future reference - * + * * @return the type service * @throws ClientException * the client exception */ private static TypeManager getTypeService() throws ClientException { TypeManager typeService = null; - + try { typeService = Framework.getService(TypeManager.class); } catch (Exception e) { throw new ClientException(e); } - + return typeService; } /** * Create a temporary Nuxeo FileBlob instance for processing. Nuxeo will clean this up for us. - * + * * @param file * @return * @throws Exception @@ -592,14 +577,14 @@ public class NuxeoBlobUtils { /** * Gets Nuxeo's file manager service. - * + * * @return the file manager service * @throws ClientException * the client exception */ private static FileManager getFileManager() throws ClientException { FileManager result = null; - + try { result = Framework.getService(FileManager.class); } catch (Exception e) { @@ -607,20 +592,20 @@ public class NuxeoBlobUtils { logger.error(msg, e); throw new ClientException("msg", e); } - + return result; } - + /** * Gets Nuxeo's file manager service. - * + * * @return the file manager service * @throws ClientException * the client exception */ private static FileManagerService getFileManagerService() throws ClientException { FileManagerService result = null; - + try { result = (FileManagerService)getFileManager(); } catch (Exception e) { @@ -628,54 +613,54 @@ public class NuxeoBlobUtils { logger.error(msg, e); throw new ClientException("msg", e); } - + return result; - } - - + } + + static private CoreSessionInterface getRepositorySession(ServiceContext ctx, RepositoryClient repositoryClient) { - CoreSessionInterface result = null; + CoreSessionInterface result = null; NuxeoRepositoryClientImpl nuxeoClient = (NuxeoRepositoryClientImpl)repositoryClient; - + try { result = nuxeoClient.getRepositorySession(ctx); } catch (Exception e) { logger.error("Could not get a repository session to the Nuxeo repository", e); } - + return result; } - + static private void releaseRepositorySession(ServiceContext ctx, RepositoryClient repositoryClient, CoreSessionInterface repoSession) throws TransactionException { NuxeoRepositoryClientImpl nuxeoClient = (NuxeoRepositoryClientImpl)repositoryClient; nuxeoClient.releaseRepositorySession(ctx, repoSession); } - + static private MimetypeRegistry getMimeService() throws ClientException { MimetypeRegistry result = null; - + try { result = Framework.getService(MimetypeRegistry.class); } catch (Exception e) { throw new ClientException(e); } - + return result; } - + private static DocumentModel createDocumentFromBlob( CoreSessionInterface repoSession, - Blob inputStreamBlob, - String blobLocation, - boolean overwrite, - String blobName, + Blob inputStreamBlob, + String blobLocation, + boolean overwrite, + String blobName, boolean useNuxeoAdaptors) throws Exception { DocumentModel result = null; - - boolean createdFromAdaptor = false; + + boolean createdFromAdaptor = false; if (useNuxeoAdaptors == true) try { // // Use Nuxeo's high-level create method which looks for plugin adapters that match the MIME type. For example, @@ -689,25 +674,25 @@ public class NuxeoBlobUtils { logger.warn(String.format("Tried but failed to use Nuxeo import adaptor to download '%s'. Falling back to generic file importer", blobName)); } - + if (createdFromAdaptor == false) { // // User Nuxeo's default file importer/adapter explicitly. This avoids specialized functionality from happening like // image derivative creation. // String digestAlgorithm = getFileManager().getDigestAlgorithm(); // Only call this because we seem to need some way of initializing Nuxeo's FileManager with a call. - + FileManagerService fileManagerService = getFileManagerService(); inputStreamBlob = checkMimeType(inputStreamBlob, blobName); FileImporter defaultFileImporter = fileManagerService.getPluginByName("DefaultFileImporter"); result = defaultFileImporter.create( - repoSession.getCoreSession(), inputStreamBlob, blobLocation, overwrite, blobName, getTypeService()); + repoSession.getCoreSession(), inputStreamBlob, blobLocation, overwrite, blobName, getTypeService()); } - + return result; } - + static public BlobsCommon createBlobInRepository( ServiceContext ctx, RepositoryClient repositoryClient, @@ -722,19 +707,19 @@ public class NuxeoBlobUtils { repoSession = getRepositorySession(ctx, repositoryClient); repoSessionCleanup = true; } - + try { // We'll store the blob inside the workspace directory of the calling service String nuxeoWspaceId = ctx.getRepositoryWorkspaceId(); DocumentRef nuxeoWspace = new IdRef(nuxeoWspaceId); DocumentModel blobLocation = repoSession.getDocument(nuxeoWspace); - + Blob inputStreamBlob = new FileBlob(inputStream); // creates a temp file to hold the stream DocumentModel documentModel = createDocumentFromBlob( repoSession, - inputStreamBlob, - blobLocation.getPathAsString(), - false, + inputStreamBlob, + blobLocation.getPathAsString(), + false, blobName, useNuxeoAdaptors); result = createBlobsCommon(ctx, documentModel, inputStreamBlob); // Now create the metadata about the Nuxeo blob document @@ -746,13 +731,13 @@ public class NuxeoBlobUtils { releaseRepositorySession(ctx, repositoryClient, repoSession); } } - + return result; } - + /** * Creates the picture. - * + * * @param ctx * the ctx * @param repoSession @@ -760,7 +745,7 @@ public class NuxeoBlobUtils { * @param filePath * the file path * @return the string - * @throws Exception + * @throws Exception */ public static BlobsCommon createBlobInRepository( ServiceContext ctx, @@ -786,11 +771,11 @@ public class NuxeoBlobUtils { logger.debug(String.format("The file '%s''s name has characters that Nuxeo can't deal with. Rather than renaming the file, we created a new temp file at '%s'", originalFile.getName(), targetFile.getAbsolutePath())); } - } - + } + DocumentModel wspaceDoc = repoSession.getDocument(nuxeoWspace); DocumentModel newBlobFolder = NuxeoUtils.createFolder(wspaceDoc, UUID.randomUUID().toString()); - + result = createBlobInRepository(ctx, repoSession, newBlobFolder, @@ -807,7 +792,7 @@ public class NuxeoBlobUtils { if (targetFile.equals(originalFile) == false) { result.setName(originalFile.getName()); } - + } catch (Exception e) { logger.error("Could not create image blob.", e); throw e; @@ -824,24 +809,24 @@ public class NuxeoBlobUtils { return result; } - + /* * Find out if this document's blob/file-contents are allowed to be purged. For instance, we currently - * only want to allow the purging the contents of Nuxeo "Picture" documents. + * only want to allow the purging the contents of Nuxeo "Picture" documents. */ static private boolean isPurgeAllowed(DocumentModel docModel) { boolean result = false; - + if (docModel.hasFacet(ImagingDocumentConstants.PICTURE_FACET) == true) { result = true; // Yes, delete/purge the original content } - + return result; } - + /** * Creates the image blob. - * + * * @param nuxeoSession * the nuxeo session * @param blobLocation @@ -877,10 +862,10 @@ public class NuxeoBlobUtils { // If the requester wants us to generate only derivatives, we need to purge/clear the original image file if (purgeOriginal == true && isPurgeAllowed(documentModel) == true) { - + // Empty the document model's "content" property -this does not delete the actual file/blob //documentModel.setPropertyValue("file:content", (Serializable) null); - + if (documentModel.hasFacet(ImagingDocumentConstants.PICTURE_FACET)) { // // We're going to use the "source" property field of the Dublin Core schema as a way of indicating to @@ -897,14 +882,14 @@ public class NuxeoBlobUtils { } documentModel.setProperty(CommonAPI.NUXEO_DUBLINCORE_SCHEMANAME, CommonAPI.NUXEO_DUBLINCORE_SOURCE, CommonAPI.URL_SOURCED_PICTURE); - + // Now with no content, the derivative listener wants to update the derivatives. So to // prevent the listener, we remove the "Picture" facet from the document //NuxeoUtils.removeFacet(documentModel, ImagingDocumentConstants.PICTURE_FACET); // Removing this facet ensures the original derivatives are unchanged. // Now that we've emptied the document model's content field, we can add back the Picture facet //NuxeoUtils.addFacet(documentModel, ImagingDocumentConstants.PICTURE_FACET); } - + //nuxeoSession.saveDocument(documentModel); // Next, we need to remove the actual file from Nuxeo's data directory // Blob blob = docBlobHolder.getBlob(); @@ -915,7 +900,7 @@ public class NuxeoBlobUtils { // boolean deleteSuccess = NuxeoUtils.deleteFileOfBlob(docBlobHolder.getBlob()); // } } - + // // Persist/save any changes. // @@ -950,16 +935,16 @@ public class NuxeoBlobUtils { // } // } // } - + public static InputStream getResource(String resourceName) { InputStream result = null; - + try { result = ServiceMain.getInstance().getResourceAsStream(resourceName); } catch (FileNotFoundException e) { logger.error("Missing Services resource: " + resourceName, e); } - + return result; } @@ -968,21 +953,21 @@ public class NuxeoBlobUtils { String repositoryId, StringBuffer outMimeType) throws TransactionException, DocumentNotFoundException { BlobOutput result = null; - + boolean repoSessionCleanup = false; CoreSessionInterface repoSession = (CoreSessionInterface)ctx.getCurrentRepositorySession(); if (repoSession == null) { repoSession = getRepositorySession(ctx, repositoryClient); repoSessionCleanup = true; } - + try { result = getBlobOutput(ctx, repoSession, repositoryId, null, true, outMimeType); if (outMimeType.length() == 0) { BlobsCommon blobsCommon = result.getBlobsCommon(); String mimeType = blobsCommon.getMimeType(); outMimeType.append(mimeType); - } + } } catch (DocumentException e) { if (logger.isDebugEnabled()) { logger.debug(e.getMessage(), e); @@ -994,17 +979,17 @@ public class NuxeoBlobUtils { releaseRepositorySession(ctx, repositoryClient, repoSession); } } - + 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; - + if (blob != null) { try { InputStream blobStream = blob.getStream(); // By default, the result will be whatever stream Nuxeo returns to us. @@ -1025,16 +1010,16 @@ public class NuxeoBlobUtils { } catch (Exception x) { logger.debug(String.format("Exception encountered during InputStream cleanup of file %s", blobsCommon.getName()), x); } - } + } } } - + return result; } - + static public Set getPictureViewNameSet(CoreSessionInterface repoSession, String repositoryId) throws PropertyException { Set result = null; - + DocumentModel docModel = repoSession.getDocument(new IdRef(repositoryId)); Collection views = docModel.getProperty(VIEWS_PROPERTY).getChildren(); if (views != null) { @@ -1043,13 +1028,13 @@ public class NuxeoBlobUtils { result.add((String)property.getValue(TITLE_PROPERTY)); } } - + return result; } - + /** * Gets the image. - * + * * @param repoSession * the repo session * @param repositoryId @@ -1057,7 +1042,7 @@ public class NuxeoBlobUtils { * @param derivativeTerm * the derivative term * @return the image - * @throws DocumentNotFoundException + * @throws DocumentNotFoundException */ static public BlobOutput getBlobOutput(ServiceContext ctx, CoreSessionInterface repoSession, @@ -1103,16 +1088,16 @@ public class NuxeoBlobUtils { isNonImageDerivative = true; } } - + if (docBlob == null && logger.isWarnEnabled()) { String msg = String.format("Could not retrieve document blob from Nuxeo document ID='%s' type='%s' name='%s'", repositoryId, documentModel.getType(), documentModel.getName()); - + if (docBlob == null && docBlobHolder instanceof PictureBlobHolder && derivativeTerm != null) { msg = String.format("Could not retrieve image blob for derivative '%s' of Picture document with Nuxeo ID='%s' and name='%s'", derivativeTerm, repositoryId, documentModel.getName()); } - + if (msg != null) { logger.warn(msg); } @@ -1171,10 +1156,8 @@ public class NuxeoBlobUtils { } outMimeType.append(MIME_JPEG); } -// BufferedInputStream bufferedInputStream = new BufferedInputStream( -// remoteStream); -// result.setBlobInputStream(bufferedInputStream); result.setBlobInputStream(remoteStream); + result.setBlobFile(docBlob.getFile()); } } catch (DocumentNotFoundException d) { throw d; @@ -1187,7 +1170,7 @@ public class NuxeoBlobUtils { return result; } - + } /* @@ -1196,31 +1179,31 @@ public class NuxeoBlobUtils { */ /* - * - * + * + * * MultiviewPictureAdapter org.nuxeo.ecm.platform.picture.api.adapters * PictureResourceAdapter pictureResourceAdapter = (PictureResourceAdapter) * documentModel.getAdapter(PictureResourceAdapter.class); String thumbnailPath * = pictureResourceAdapter.getViewXPath("Thumbnail"); - * + * * Map blobHolderProps = docBlobHolder.getProperties(); * String filePath = docBlobHolder.getFilePath(); List docBlobs = * docBlobHolder.getBlobs(); - * + * * stream = new FileInputStream(fileUploadHolder.getTempFile()); - * + * * public String addFile(InputStream fileUpload, String fileName) fileName = * FileUtils.getCleanFileName(fileName); DocumentModel currentDocument = * navigationContext.getCurrentDocument(); String path = * currentDocument.getPathAsString(); Blob blob = * FileUtils.createSerializableBlob(fileUpload, fileName, null); - * + * * DocumentModel createdDoc = getFileManagerService().createDocumentFromBlob( * documentManager, blob, path, true, fileName); * eventManager.raiseEventsOnDocumentSelected(createdDoc); - * + * * protected FileManager fileManager; - * + * * protected FileManager getFileManagerService() throws ClientException { if * (fileManager == null) { try { fileManager = * Framework.getService(FileManager.class); } catch (Exception e) { @@ -1237,7 +1220,7 @@ public class NuxeoBlobUtils { * repositoryManager.getDescriptor(repositoryName); DefaultBinaryManager * binaryManager = new DefaultBinaryManager( * SQLRepository.getDescriptor(descriptor))); - * + * * File storageDir = binaryManager.getStorageDir(); SQLBlob blob = (SQLBlob) * doc.getPropertyValue("schema:blobField"); File file = * binaryManager.getFileForDigest( blob.getBinary().getDigest(), false); @@ -1245,49 +1228,48 @@ public class NuxeoBlobUtils { /* * RepositoryInstance.getStreamURI() - * + * * String getStreamURI(String blobPropertyId) throws ClientException - * + * * Returns an URI identifying the stream given the blob property id. This method * should be used by a client to download the data of a blob property. - * + * * The blob is fetched from the repository and the blob stream is registered * against the streaming service so the stream will be available remotely * through stream service API. - * + * * After the client has called this method, it will be able to download the * stream using streaming server API. - * + * * Returns: an URI identifying the remote stream Throws: ClientException */ /* * A blob contains usually large data. - * + * * Document fields holding Blob data are by default fetched in a lazy manner. - * + * * A Blob object hides the data source and it also describes data properties * like the encoding or mime-type. - * + * * The encoding is used to decode Unicode text content that was stored in an * encoded form. If not encoding is specified, the default java encoding is * used. The encoding is ignored for binary content. - * + * * When retrieving the content from a document, it will be returned as source * content instead of returning the content bytes. - * + * * The same is true when setting the content for a document: you set a content * source and not directly the content bytes. Ex: - * + * * File file = new File("/tmp/index.html"); FileBlob fb = new FileBlob(file); * fb.setMimeType("text/html"); fb.setEncoding("UTF-8"); // this specifies that * content bytes will be stored as UTF-8 document.setProperty("file", "content", * fb); - * - * + * + * * Then you may want to retrieve the content as follow: - * + * * Blob blob = document.getProperty("file:content"); htmlDoc = blob.getString(); * // the content is decoded from UTF-8 into a java string */ - -- 2.47.3