2 * This document is a part of the source code and related artifacts
3 * for CollectionSpace, an open source collections management system
4 * for museums and related institutions:
6 * http://www.collectionspace.org
7 * http://wiki.collectionspace.org
9 * Copyright 2009 University of California at Berkeley
11 * Licensed under the Educational Community License (ECL), Version 2.0.
12 * You may not use this file except in compliance with this License.
14 * You may obtain a copy of the ECL 2.0 License at
16 * https://source.collectionspace.org/collection-space/LICENSE.txt
18 * Unless required by applicable law or agreed to in writing, software
19 * distributed under the License is distributed on an "AS IS" BASIS,
20 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 * See the License for the specific language governing permissions and
22 * limitations under the License.
24 package org.collectionspace.services.blob.nuxeo;
26 import org.collectionspace.services.blob.BlobsCommon;
27 import org.collectionspace.services.nuxeo.client.java.DocHandlerBase;
28 import org.collectionspace.services.client.BlobClient;
29 import org.collectionspace.services.client.PayloadOutputPart;
30 import org.collectionspace.services.client.PoxPayloadIn;
31 import org.collectionspace.services.client.PoxPayloadOut;
32 import org.collectionspace.services.common.blob.BlobInput;
33 import org.collectionspace.services.common.blob.BlobOutput;
34 import org.collectionspace.services.common.blob.BlobUtil;
35 import org.collectionspace.services.common.context.ServiceContext;
36 import org.collectionspace.services.common.document.DocumentUtils;
37 import org.collectionspace.services.common.document.DocumentWrapper;
38 import org.collectionspace.services.common.imaging.nuxeo.NuxeoBlobUtils;
39 import org.collectionspace.services.config.service.ListResultField;
40 import org.collectionspace.services.config.service.ObjectPartType;
41 import org.collectionspace.services.jaxb.BlobJAXBSchema;
42 import org.collectionspace.services.nuxeo.client.java.CommonList;
45 import org.collectionspace.services.nuxeo.client.java.RepositoryInstanceInterface;
46 import org.nuxeo.ecm.core.api.ClientException;
47 import org.nuxeo.ecm.core.api.DocumentModel;
48 import org.nuxeo.ecm.core.api.IdRef;
49 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
53 import java.util.List;
56 import javax.ws.rs.core.MultivaluedMap;
58 import org.dom4j.Element;
61 * The Class BlobDocumentModelHandler.
63 public class BlobDocumentModelHandler
64 extends DocHandlerBase<BlobsCommon> {
67 private final Logger logger = LoggerFactory.getLogger(BlobDocumentModelHandler.class);
69 //==============================================================================
71 private String getDerivativePathBase(DocumentModel docModel) {
72 return getServiceContextPath() + docModel.getName() + "/" +
73 BlobInput.URI_DERIVATIVES_PATH + "/";
76 private BlobsCommon getCommonPartProperties(DocumentModel docModel) throws Exception {
77 String label = getServiceContext().getCommonPartLabel();
78 BlobsCommon result = new BlobsCommon();
80 result.setData((String)
81 docModel.getProperty(label, BlobJAXBSchema.data));
82 result.setDigest((String)
83 docModel.getProperty(label, BlobJAXBSchema.digest));
84 result.setEncoding((String)
85 docModel.getProperty(label, BlobJAXBSchema.encoding));
86 result.setLength((String)
87 docModel.getProperty(label, BlobJAXBSchema.length));
88 result.setMimeType((String)
89 docModel.getProperty(label, BlobJAXBSchema.mimeType));
90 result.setName((String)
91 docModel.getProperty(label, BlobJAXBSchema.name));
92 result.setRepositoryId((String)
93 docModel.getProperty(label, BlobJAXBSchema.repositoryId));
94 result.setUri(getServiceContextPath() + docModel.getName() + "/" +
95 BlobInput.URI_CONTENT_PATH);
100 private void setCommonPartProperties(DocumentModel documentModel,
101 BlobsCommon blobsCommon) throws ClientException {
103 String schemaName = getServiceContext().getCommonPartLabel();
104 PayloadOutputPart outputPart = new PayloadOutputPart(schemaName, blobsCommon);
105 Element element = outputPart.asElement();
106 Map<String, Object> propertyMap = DocumentUtils.parseProperties(schemaName, element, getServiceContext());
107 documentModel.setProperties(schemaName, propertyMap);
108 } catch (Exception e) {
109 throw new ClientException(e);
113 private void extractMetadata(String nuxeoImageID, String metadataLabel) {
114 Map<String, ObjectPartType> partsMetaMap = getServiceContext().getPartsMetadata();
115 ObjectPartType partMeta = partsMetaMap.get(metadataLabel);
117 if (partMeta != null) {
118 RepositoryInstanceInterface repoSession = this.getRepositorySession();
119 if (nuxeoImageID != null && nuxeoImageID.isEmpty() == false) try {
120 IdRef documentRef = new IdRef(nuxeoImageID);
121 DocumentModel docModel = repoSession.getDocument(documentRef);
122 Map<String, Object> unQObjectProperties = extractPart(docModel, metadataLabel);
123 if (unQObjectProperties != null) {
124 addOutputPart(unQObjectProperties, metadataLabel, partMeta);
126 } catch (Exception e) {
127 logger.warn("Metadata extraction failed: " + e.getMessage());
130 logger.warn("Metadata extraction failed: Could not find tenant binding for schema type = " + metadataLabel);
135 * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#extractAllParts(org.collectionspace.services.common.document.DocumentWrapper)
138 public void extractAllParts(DocumentWrapper<DocumentModel> wrapDoc)
140 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
141 BlobInput blobInput = BlobUtil.getBlobInput(ctx); // the blobInput was set by the Blob JAX-RS resource code and put into the service context
142 RepositoryInstanceInterface repoSession = this.getRepositorySession();
143 DocumentModel docModel = wrapDoc.getWrappedObject();
144 BlobsCommon blobsCommon = this.getCommonPartProperties(docModel);
145 String blobRepositoryId = blobsCommon.getRepositoryId(); //cache the value to pass to the blob retriever
147 // We're being asked for a list of blob derivatives, not the payload for a blob record. FIXME: REM - This should be handled in a class called DerivativeDocumentHandler (need to create).
149 if (blobInput.isDerivativeListRequested() == true) {
150 List<ListResultField> resultsFields = getListItemsArray();
151 CommonList blobsCommonList = NuxeoBlobUtils.getBlobDerivatives( //FIXME: REM - Need to replace "NuxeoImageUtils" with something more general like "BlobUtils" since we may support other blob types.
152 repoSession, blobRepositoryId, resultsFields, getDerivativePathBase(docModel));
153 // ctx.setProperty(BlobInput.BLOB_DERIVATIVE_LIST_KEY, blobsCommonList);
154 blobInput.setDerivativeList(blobsCommonList);
155 return; //FIXME: REM - Don't like this exit point. Perhaps derivatives should be a sub-resource with its own DerivativeDocumentHandler doc handler?
158 String derivativeTerm = blobInput.getDerivativeTerm();
159 Boolean getContentFlag = blobInput.isContentRequested();
161 // If we're being asked for either the content of the blob, the content of a derivative, or the payload for a derivative then
162 // fall into this block of code. Otherwise, we'll just call our parent to deal with a plain-old-blob payload.
164 if (derivativeTerm != null || getContentFlag == true) {
165 StringBuffer mimeTypeBuffer = new StringBuffer();
166 BlobOutput blobOutput = NuxeoBlobUtils.getBlobOutput(ctx, repoSession,
167 blobRepositoryId, derivativeTerm, getContentFlag, mimeTypeBuffer);
168 if (getContentFlag == true) {
169 if (blobOutput != null) {
170 blobInput.setContentStream(blobOutput.getBlobInputStream());
172 // If we can't find the blob's content, we'll return a "missing document" image
173 blobInput.setContentStream(NuxeoBlobUtils.getResource(NuxeoBlobUtils.DOCUMENT_MISSING_PLACEHOLDER_IMAGE));
174 mimeTypeBuffer.append(NuxeoBlobUtils.MIME_JPEG);
178 if (derivativeTerm != null) {
179 // reset 'blobsCommon' if we have a derivative request
180 blobsCommon = blobOutput.getBlobsCommon();
181 blobsCommon.setUri(getDerivativePathBase(docModel) +
182 derivativeTerm + "/" + BlobInput.URI_CONTENT_PATH);
185 String mimeType = mimeTypeBuffer.toString();
186 if (mimeType != null && !mimeType.isEmpty()) { // MIME type for derivatives might be different from original
187 blobInput.setMimeType(mimeType);
188 blobsCommon.setMimeType(mimeType);
190 blobInput.setMimeType(blobsCommon.getMimeType()); // Set the MIME type to the one in blobsCommon
193 blobsCommon.setRepositoryId(null); //hide the repository id from the GET results payload since it is private
194 this.setCommonPartProperties(docModel, blobsCommon);
195 // finish extracting the other parts by calling the parent
197 extractMetadata(blobRepositoryId, NuxeoBlobUtils.SCHEMA_IMAGE_METADATA);
198 extractMetadata(blobRepositoryId, NuxeoBlobUtils.SCHEMA_IPTC);
202 // Hide the Nuxeo repository ID of the Nuxeo blob since this is private
204 docModel.setProperty(ctx.getCommonPartLabel(), BlobJAXBSchema.repositoryId, null);
205 super.extractAllParts(wrapDoc);
209 public void fillAllParts(DocumentWrapper<DocumentModel> wrapDoc, Action action) throws Exception {
210 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
211 BlobInput blobInput = BlobUtil.getBlobInput(ctx); // The blobInput should have been put into the context by the Blob or Media resource
212 if (blobInput != null && blobInput.getBlobFile() != null) {
213 boolean purgeOriginal = false;
214 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
215 String purgeOriginalStr = queryParams.getFirst(BlobClient.BLOB_PURGE_ORIGINAL);
216 if (purgeOriginalStr != null && purgeOriginalStr.isEmpty() == false) { // Find our if the caller wants us to purge/delete the original
217 purgeOriginal = true;
220 // If blobInput has a file then we just received a multipart/form-data file post or a URI query parameter
222 DocumentModel documentModel = wrapDoc.getWrappedObject();
223 RepositoryInstanceInterface repoSession = this.getRepositorySession();
225 BlobsCommon blobsCommon = NuxeoBlobUtils.createBlobInRepository(ctx, repoSession, blobInput, purgeOriginal, true);
226 blobInput.setBlobCsid(documentModel.getName()); //Assumption here is that the documentModel "name" field is storing a CSID
228 PoxPayloadIn input = ctx.getInput();
230 // If the input payload is null, then we're creating a new blob from a post or a uri. This means there
231 // is no "input" payload for our framework to process. Therefore we need to synthesize a payload from
232 // the BlobsCommon instance we just filled out.
235 PoxPayloadOut output = new PoxPayloadOut(BlobClient.SERVICE_PAYLOAD_NAME);
236 PayloadOutputPart commonPart = new PayloadOutputPart(BlobClient.SERVICE_COMMON_PART_NAME, blobsCommon);
237 output.addPart(commonPart);
238 input = new PoxPayloadIn(output.toXML());
241 // At this point, we've created a blob document in the Nuxeo repository. Usually, we use the blob to create and instance of BlobsCommon and use
242 // that to populate the resource record. However, since the "input" var is not null the requester provided their own resource record data
243 // so we'll use it rather than deriving one from the blob.
244 logger.warn("A resource record payload was provided along with the actually blob binary file. This payload is usually derived from the blob binary. Since a payload was provided, we're creating the resource record from the payload and not from the corresponding blob binary." +
245 " The data in blob resource record fields may not correspond completely with the persisted blob binary file.");
249 super.fillAllParts(wrapDoc, action);