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.nuxeo.client.java;
26 import java.io.InputStream;
27 import java.util.HashMap;
28 import java.util.List;
30 import java.util.Map.Entry;
33 import javax.ws.rs.WebApplicationException;
34 import javax.ws.rs.core.MediaType;
35 import javax.ws.rs.core.Response;
37 import org.collectionspace.services.jaxb.AbstractCommonList;
38 import org.collectionspace.services.common.authorityref.AuthorityRefList;
39 import org.collectionspace.services.common.context.MultipartServiceContext;
40 import org.collectionspace.services.common.context.ServiceContext;
41 import org.collectionspace.services.common.document.BadRequestException;
42 import org.collectionspace.services.common.document.DocumentUtils;
43 import org.collectionspace.services.common.document.DocumentWrapper;
44 import org.collectionspace.services.common.document.DocumentFilter;
45 import org.collectionspace.services.common.document.DocumentHandler.Action;
46 import org.collectionspace.services.common.service.ObjectPartType;
47 import org.collectionspace.services.common.vocabulary.RefNameUtils;
49 import org.jboss.resteasy.plugins.providers.multipart.InputPart;
50 import org.jboss.resteasy.plugins.providers.multipart.MultipartInput;
51 import org.jboss.resteasy.plugins.providers.multipart.MultipartOutput;
52 import org.nuxeo.ecm.core.api.DocumentModel;
53 import org.nuxeo.ecm.core.api.DocumentModelList;
54 import org.nuxeo.ecm.core.api.model.PropertyException;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57 import org.w3c.dom.Document;
60 * RemoteDocumentModelHandler
62 * $LastChangedRevision: $
67 public abstract class RemoteDocumentModelHandlerImpl<T, TL>
68 extends DocumentModelHandler<T, TL> {
71 private final Logger logger = LoggerFactory.getLogger(RemoteDocumentModelHandlerImpl.class);
74 * @see org.collectionspace.services.common.document.AbstractDocumentHandlerImpl#setServiceContext(org.collectionspace.services.common.context.ServiceContext)
77 public void setServiceContext(ServiceContext ctx) { //FIXME: Apply proper generics to ServiceContext<MultipartInput, MultipartOutput>
78 if(ctx instanceof MultipartServiceContext){
79 super.setServiceContext(ctx);
81 throw new IllegalArgumentException("setServiceContext requires instance of " +
82 MultipartServiceContext.class.getName());
87 * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#completeUpdate(org.collectionspace.services.common.document.DocumentWrapper)
90 public void completeUpdate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
91 DocumentModel docModel = wrapDoc.getWrappedObject();
92 //return at least those document part(s) that were received
93 Map<String, ObjectPartType> partsMetaMap = getServiceContext().getPartsMetadata();
94 MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext();
95 List<InputPart> inputParts = ctx.getInput().getParts();
96 for(InputPart part : inputParts){
97 String partLabel = part.getHeaders().getFirst("label");
98 ObjectPartType partMeta = partsMetaMap.get(partLabel);
99 // extractPart(docModel, partLabel, partMeta);
100 Map<String, Object> unQObjectProperties = extractPart(docModel, partLabel, partMeta);
101 addOutputPart(unQObjectProperties, partLabel, partMeta);
106 * Adds the output part.
108 * @param unQObjectProperties the un q object properties
109 * @param schema the schema
110 * @param partMeta the part meta
111 * @throws Exception the exception
113 private void addOutputPart(Map<String, Object> unQObjectProperties, String schema, ObjectPartType partMeta)
115 Document doc = DocumentUtils.buildDocument(partMeta, schema,
116 unQObjectProperties);
117 if (logger.isDebugEnabled() == true) {
118 logger.debug(DocumentUtils.xmlToString(doc));
120 MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext();
121 ctx.addOutputPart(schema, doc, partMeta.getContent().getContentType());
125 * Extract paging info.
127 * @param commonsList the commons list
129 * @throws Exception the exception
131 protected TL extractPagingInfo(TL theCommonList, DocumentWrapper<DocumentModelList> wrapDoc)
133 AbstractCommonList commonList = (AbstractCommonList)theCommonList;
135 DocumentFilter docFilter = this.getDocumentFilter();
136 long pageSize = docFilter.getPageSize();
137 long pageNum = pageSize != 0 ? docFilter.getOffset() / pageSize : pageSize;
138 // set the page size and page number
139 commonList.setPageNum(pageNum);
140 commonList.setPageSize(pageSize);
141 DocumentModelList docList = wrapDoc.getWrappedObject();
142 // Set num of items in list. this is useful to our testing framework.
143 commonList.setItemsInPage(docList.size());
144 // set the total result size
145 commonList.setTotalItems(docList.totalSize());
147 return (TL)commonList;
152 * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#extractAllParts(org.collectionspace.services.common.document.DocumentWrapper)
155 public void extractAllParts(DocumentWrapper<DocumentModel> wrapDoc)
158 DocumentModel docModel = wrapDoc.getWrappedObject();
159 String[] schemas = docModel.getDeclaredSchemas();
160 Map<String, ObjectPartType> partsMetaMap = getServiceContext().getPartsMetadata();
161 for (String schema : schemas) {
162 ObjectPartType partMeta = partsMetaMap.get(schema);
163 if (partMeta == null) {
164 continue; // unknown part, ignore
166 Map<String, Object> unQObjectProperties = extractPart(docModel, schema, partMeta);
167 addOutputPart(unQObjectProperties, schema, partMeta);
172 * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#fillAllParts(org.collectionspace.services.common.document.DocumentWrapper)
175 public void fillAllParts(DocumentWrapper<DocumentModel> wrapDoc, Action action) throws Exception {
177 //TODO filling extension parts should be dynamic
178 //Nuxeo APIs lack to support stream/byte[] input, get/setting properties is
179 //not an ideal way of populating objects.
180 DocumentModel docModel = wrapDoc.getWrappedObject();
181 MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext();
182 MultipartInput input = ctx.getInput();
183 if(input.getParts().isEmpty()){
184 String msg = "No payload found!";
185 logger.error(msg + "Ctx=" + getServiceContext().toString());
186 throw new BadRequestException(msg);
189 Map<String, ObjectPartType> partsMetaMap = getServiceContext().getPartsMetadata();
191 //iterate over parts received and fill those parts
192 List<InputPart> inputParts = input.getParts();
193 for(InputPart part : inputParts){
195 String partLabel = part.getHeaders().getFirst("label");
196 if (partLabel == null) {
197 String msg = "Part label is missing or empty!";
198 logger.error(msg + "Ctx=" + getServiceContext().toString());
199 throw new BadRequestException(msg);
202 //skip if the part is not in metadata
203 ObjectPartType partMeta = partsMetaMap.get(partLabel);
207 fillPart(part, docModel, partMeta, action);
213 * fillPart fills an XML part into given document model
214 * @param part to fill
215 * @param docModel for the given object
216 * @param partMeta metadata for the object to fill
219 protected void fillPart(InputPart part, DocumentModel docModel, ObjectPartType partMeta, Action action)
221 InputStream payload = part.getBody(InputStream.class, null);
223 // TODO for sub-docs - after we parse the doc, we need to look for elements that are configured as
224 // subitem lists, for this part (schema), pull them out, and set them aside for later processing.
226 //check if this is an xml part
227 if(part.getMediaType().equals(MediaType.APPLICATION_XML_TYPE)){
229 Document document = DocumentUtils.parseDocument(payload, partMeta);
230 //TODO: callback to handler if registered to validate the
232 Map<String, Object> objectProps = DocumentUtils.parseProperties(document.getFirstChild());
233 if(action==Action.UPDATE) {
234 this.filterReadOnlyPropertiesForPart(objectProps, partMeta);
236 docModel.setProperties(partMeta.getLabel(), objectProps);
242 * Filters out read only properties, so they cannot be set on update.
243 * TODO: add configuration support to do this generally
244 * @param objectProps the properties parsed from the update payload
245 * @param partMeta metadata for the object to fill
247 public void filterReadOnlyPropertiesForPart(
248 Map<String, Object> objectProps, ObjectPartType partMeta) {
249 // Currently a no-op, but can be overridden in Doc handlers.
253 * extractPart extracts an XML object from given DocumentModel
255 * @param schema of the object to extract
256 * @param partMeta metadata for the object to extract
259 protected Map<String, Object> extractPart(DocumentModel docModel, String schema, ObjectPartType partMeta)
261 return extractPart( docModel, schema, partMeta, null );
265 * extractPart extracts an XML object from given DocumentModel
267 * @param schema of the object to extract
268 * @param partMeta metadata for the object to extract
271 protected Map<String, Object> extractPart(
272 DocumentModel docModel, String schema, ObjectPartType partMeta,
273 Map<String, Object> addToMap)
275 Map<String, Object> result = null;
277 MediaType mt = MediaType.valueOf(partMeta.getContent().getContentType());
278 if (mt.equals(MediaType.APPLICATION_XML_TYPE)){
279 Map<String, Object> objectProps = docModel.getProperties(schema);
280 //unqualify properties before sending the doc over the wire (to save bandwidh)
281 //FIXME: is there a better way to avoid duplication of a collection?
282 Map<String, Object> unQObjectProperties =
283 (addToMap!=null)? addToMap:(new HashMap<String, Object>());
284 Set<Entry<String, Object>> qualifiedEntries = objectProps.entrySet();
285 for(Entry<String, Object> entry : qualifiedEntries){
286 String unqProp = getUnQProperty(entry.getKey());
287 unQObjectProperties.put(unqProp, entry.getValue());
289 result = unQObjectProperties;
290 } //TODO: handle other media types
296 * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#getAuthorityRefs(org.collectionspace.services.common.document.DocumentWrapper, java.util.List)
299 public AuthorityRefList getAuthorityRefs(
300 DocumentWrapper<DocumentModel> docWrapper,
301 List<String> authRefFields) throws PropertyException {
302 AuthorityRefList authRefList = new AuthorityRefList();
304 DocumentModel docModel = docWrapper.getWrappedObject();
305 List<AuthorityRefList.AuthorityRefItem> list = authRefList.getAuthorityRefItem();
307 for (String field : authRefFields) {
308 String refName = (String) docModel.getPropertyValue(field);
312 RefNameUtils.AuthorityTermInfo termInfo = RefNameUtils
313 .parseAuthorityTermInfo(refName);
314 AuthorityRefList.AuthorityRefItem ilistItem = new AuthorityRefList.AuthorityRefItem();
315 ilistItem.setRefName(refName);
316 ilistItem.setAuthDisplayName(termInfo.inAuthority.displayName);
317 ilistItem.setItemDisplayName(termInfo.displayName);
318 ilistItem.setSourceField(field);
319 ilistItem.setUri(termInfo.getRelativeUri());
321 } catch (Exception e) {
322 // FIXME: Do we need to throw this Exception here?
323 if (logger.isDebugEnabled()) {
324 logger.debug("Caught exception in getAuthorityRefs", e);
328 } catch (PropertyException pe) {
329 String msg = "Attempted to retrieve value for invalid or missing authority field. "
330 + "Check authority field properties in tenant bindings.";
331 logger.warn(msg, pe);
333 } catch (Exception e) {
334 if (logger.isDebugEnabled()) {
335 logger.debug("Caught exception in getAuthorityRefs", e);
337 Response response = Response.status(
338 Response.Status.INTERNAL_SERVER_ERROR).entity(
339 "Failed to retrieve authority references").type(
340 "text/plain").build();
341 throw new WebApplicationException(response);