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.service.ObjectPartType;
46 import org.collectionspace.services.common.vocabulary.RefNameUtils;
48 import org.jboss.resteasy.plugins.providers.multipart.InputPart;
49 import org.jboss.resteasy.plugins.providers.multipart.MultipartInput;
50 import org.jboss.resteasy.plugins.providers.multipart.MultipartOutput;
51 import org.nuxeo.ecm.core.api.DocumentModel;
52 import org.nuxeo.ecm.core.api.DocumentModelList;
53 import org.nuxeo.ecm.core.api.model.PropertyException;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56 import org.w3c.dom.Document;
59 * RemoteDocumentModelHandler
61 * $LastChangedRevision: $
66 public abstract class RemoteDocumentModelHandlerImpl<T, TL>
67 extends DocumentModelHandler<T, TL> {
69 private final Logger logger = LoggerFactory.getLogger(RemoteDocumentModelHandlerImpl.class);
72 public void setServiceContext(ServiceContext ctx) {
73 if(ctx instanceof MultipartServiceContext){
74 super.setServiceContext(ctx);
76 throw new IllegalArgumentException("setServiceContext requires instance of " +
77 MultipartServiceContext.class.getName());
82 public void completeUpdate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
83 DocumentModel docModel = wrapDoc.getWrappedObject();
84 //return at least those document part(s) that were received
85 Map<String, ObjectPartType> partsMetaMap = getServiceContext().getPartsMetadata();
86 MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext();
87 List<InputPart> inputParts = ctx.getInput().getParts();
88 for(InputPart part : inputParts){
89 String partLabel = part.getHeaders().getFirst("label");
90 ObjectPartType partMeta = partsMetaMap.get(partLabel);
91 // extractPart(docModel, partLabel, partMeta);
92 Map<String, Object> unQObjectProperties = extractPart(docModel, partLabel, partMeta);
93 addOutputPart(unQObjectProperties, partLabel, partMeta);
97 private void addOutputPart(Map<String, Object> unQObjectProperties, String schema, ObjectPartType partMeta)
99 Document doc = DocumentUtils.buildDocument(partMeta, schema,
100 unQObjectProperties);
101 if (logger.isDebugEnabled()) {
102 DocumentUtils.writeDocument(doc, System.out);
104 MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext();
105 ctx.addOutputPart(schema, doc, partMeta.getContent().getContentType());
110 * Extract paging info.
112 * @param commonsList the commons list
114 * @throws Exception the exception
116 protected TL extractPagingInfo(TL theCommonList, DocumentWrapper<DocumentModelList> wrapDoc)
118 AbstractCommonList commonList = (AbstractCommonList)theCommonList;
120 DocumentFilter docFilter = this.getDocumentFilter();
121 long pageSize = docFilter.getPageSize();
122 long pageNum = pageSize != 0 ? docFilter.getOffset() / pageSize : pageSize;
123 // set the page size and page number
124 commonList.setPageNum(pageNum);
125 commonList.setPageSize(pageSize);
126 DocumentModelList docList = wrapDoc.getWrappedObject();
127 // Set num of items in list. this is useful to our testing framework.
128 commonList.setItemsInPage(docList.size());
129 // set the total result size
130 commonList.setTotalItems(docList.totalSize());
132 return (TL)commonList;
137 public void extractAllParts(DocumentWrapper<DocumentModel> wrapDoc)
140 DocumentModel docModel = wrapDoc.getWrappedObject();
141 String[] schemas = docModel.getDeclaredSchemas();
142 Map<String, ObjectPartType> partsMetaMap = getServiceContext().getPartsMetadata();
143 for (String schema : schemas) {
144 ObjectPartType partMeta = partsMetaMap.get(schema);
145 if (partMeta == null) {
146 continue; // unknown part, ignore
148 Map<String, Object> unQObjectProperties = extractPart(docModel, schema, partMeta);
149 addOutputPart(unQObjectProperties, schema, partMeta);
154 public void fillAllParts(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
156 //TODO filling extension parts should be dynamic
157 //Nuxeo APIs lack to support stream/byte[] input, get/setting properties is
158 //not an ideal way of populating objects.
159 DocumentModel docModel = wrapDoc.getWrappedObject();
160 MultipartServiceContext ctx = (MultipartServiceContext) getServiceContext();
161 MultipartInput input = ctx.getInput();
162 if(input.getParts().isEmpty()){
163 String msg = "No payload found!";
164 logger.error(msg + "Ctx=" + getServiceContext().toString());
165 throw new BadRequestException(msg);
168 Map<String, ObjectPartType> partsMetaMap = getServiceContext().getPartsMetadata();
170 //iterate over parts received and fill those parts
171 List<InputPart> inputParts = input.getParts();
172 for(InputPart part : inputParts){
174 String partLabel = part.getHeaders().getFirst("label");
175 if (partLabel == null) {
176 String msg = "Part label is missing or empty!";
177 logger.error(msg + "Ctx=" + getServiceContext().toString());
178 throw new BadRequestException(msg);
181 //skip if the part is not in metadata
182 if(!partsMetaMap.containsKey(partLabel)){
185 ObjectPartType partMeta = partsMetaMap.get(partLabel);
186 fillPart(part, docModel, partMeta);
192 * fillPart fills an XML part into given document model
193 * @param part to fill
194 * @param docModel for the given object
195 * @param partMeta metadata for the object to fill
198 protected void fillPart(InputPart part, DocumentModel docModel, ObjectPartType partMeta)
200 InputStream payload = part.getBody(InputStream.class, null);
202 //check if this is an xml part
203 if(part.getMediaType().equals(MediaType.APPLICATION_XML_TYPE)){
205 Document document = DocumentUtils.parseDocument(payload);
206 //TODO: callback to handler if registered to validate the
208 Map<String, Object> objectProps = DocumentUtils.parseProperties(document);
209 docModel.setProperties(partMeta.getLabel(), objectProps);
215 * extractPart extracts an XML object from given DocumentModel
217 * @param schema of the object to extract
218 * @param partMeta metadata for the object to extract
221 protected Map<String, Object> extractPart(DocumentModel docModel, String schema, ObjectPartType partMeta)
223 Map<String, Object> result = null;
225 MediaType mt = MediaType.valueOf(partMeta.getContent().getContentType());
226 if (mt.equals(MediaType.APPLICATION_XML_TYPE)){
227 Map<String, Object> objectProps = docModel.getProperties(schema);
228 //unqualify properties before sending the doc over the wire (to save bandwidh)
229 //FIXME: is there a better way to avoid duplication of a collection?
230 Map<String, Object> unQObjectProperties = new HashMap<String, Object>();
231 Set<Entry<String, Object>> qualifiedEntries = objectProps.entrySet();
232 for(Entry<String, Object> entry : qualifiedEntries){
233 String unqProp = getUnQProperty(entry.getKey());
234 unQObjectProperties.put(unqProp, entry.getValue());
236 result = unQObjectProperties;
237 } //TODO: handle other media types
243 * @see org.collectionspace.services.nuxeo.client.java.DocumentModelHandler#getAuthorityRefs(org.collectionspace.services.common.document.DocumentWrapper, java.util.List)
246 public AuthorityRefList getAuthorityRefs(
247 DocumentWrapper<DocumentModel> docWrapper,
248 List<String> authRefFields) throws PropertyException {
249 AuthorityRefList authRefList = new AuthorityRefList();
251 DocumentModel docModel = docWrapper.getWrappedObject();
252 List<AuthorityRefList.AuthorityRefItem> list = authRefList.getAuthorityRefItem();
254 for (String field : authRefFields) {
255 String refName = (String) docModel.getPropertyValue(field);
259 RefNameUtils.AuthorityTermInfo termInfo = RefNameUtils
260 .parseAuthorityTermInfo(refName);
261 AuthorityRefList.AuthorityRefItem ilistItem = new AuthorityRefList.AuthorityRefItem();
262 ilistItem.setRefName(refName);
263 ilistItem.setAuthDisplayName(termInfo.inAuthority.displayName);
264 ilistItem.setItemDisplayName(termInfo.displayName);
265 ilistItem.setSourceField(field);
266 ilistItem.setUri(termInfo.getRelativeUri());
268 } catch (Exception e) {
269 // FIXME: Do we need to throw this Exception here?
270 if (logger.isDebugEnabled()) {
271 logger.debug("Caught exception in getAuthorityRefs", e);
275 } catch (PropertyException pe) {
276 String msg = "Attempted to retrieve value for invalid or missing authority field. "
277 + "Check authority field properties in tenant bindings.";
278 logger.warn(msg, pe);
280 } catch (Exception e) {
281 if (logger.isDebugEnabled()) {
282 logger.debug("Caught exception in getAuthorityRefs", e);
284 Response response = Response.status(
285 Response.Status.INTERNAL_SERVER_ERROR).entity(
286 "Failed to retrieve authority references").type(
287 "text/plain").build();
288 throw new WebApplicationException(response);