001/* 002 * Copyright (c) 2006-2015 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 * 009 * Contributors: 010 * Bogdan Stefanescu 011 */ 012package org.nuxeo.ecm.automation.jaxrs.io.documents; 013 014import java.io.IOException; 015import java.io.OutputStream; 016import java.lang.annotation.Annotation; 017import java.lang.reflect.Type; 018import java.util.Calendar; 019import java.util.List; 020import java.util.Map; 021 022import javax.servlet.ServletRequest; 023import javax.servlet.http.HttpServletRequest; 024import javax.ws.rs.Produces; 025import javax.ws.rs.WebApplicationException; 026import javax.ws.rs.core.Context; 027import javax.ws.rs.core.HttpHeaders; 028import javax.ws.rs.core.MediaType; 029import javax.ws.rs.core.MultivaluedMap; 030import javax.ws.rs.ext.MessageBodyWriter; 031import javax.ws.rs.ext.Provider; 032 033import org.apache.commons.logging.Log; 034import org.apache.commons.logging.LogFactory; 035import org.codehaus.jackson.JsonEncoding; 036import org.codehaus.jackson.JsonFactory; 037import org.codehaus.jackson.JsonGenerationException; 038import org.codehaus.jackson.JsonGenerator; 039import org.joda.time.DateTime; 040import org.joda.time.format.ISODateTimeFormat; 041import org.nuxeo.common.utils.StringUtils; 042import org.nuxeo.ecm.automation.core.util.DateTimeFormat; 043import org.nuxeo.ecm.automation.core.util.JSONPropertyWriter; 044import org.nuxeo.ecm.automation.io.services.enricher.ContentEnricherService; 045import org.nuxeo.ecm.automation.io.services.enricher.HeaderDocEvaluationContext; 046import org.nuxeo.ecm.automation.io.services.enricher.RestEvaluationContext; 047import org.nuxeo.ecm.core.api.DocumentModel; 048import org.nuxeo.ecm.core.api.Lock; 049import org.nuxeo.ecm.core.api.PropertyException; 050import org.nuxeo.ecm.core.api.model.DocumentPart; 051import org.nuxeo.ecm.core.api.model.Property; 052import org.nuxeo.ecm.core.api.model.PropertyNotFoundException; 053import org.nuxeo.ecm.core.io.download.DownloadService; 054import org.nuxeo.ecm.core.io.marshallers.json.document.DocumentModelJsonWriter; 055import org.nuxeo.ecm.core.io.registry.MarshallerHelper; 056import org.nuxeo.ecm.core.io.registry.MarshallerRegistry; 057import org.nuxeo.ecm.core.schema.utils.DateParser; 058import org.nuxeo.ecm.platform.web.common.vh.VirtualHostHelper; 059import org.nuxeo.ecm.webengine.jaxrs.coreiodelegate.JsonCoreIODelegate; 060import org.nuxeo.runtime.api.Framework; 061 062/** 063 * @deprecated this JAX-RS marshaller was migrated to {@link DocumentModelJsonWriter}. To use it in JAX-RS, use 064 * {@link JsonCoreIODelegate} to forward JAX-RS marshalling to core io. To use it your code, please refer to 065 * {@link MarshallerRegistry} service or use {@link MarshallerHelper}. 066 */ 067@Deprecated 068@Provider 069@Produces({ "application/json+nxentity", "application/json" }) 070public class JsonDocumentWriter implements MessageBodyWriter<DocumentModel> { 071 072 public static final String DOCUMENT_PROPERTIES_HEADER = "X-NXDocumentProperties"; 073 074 private static final Log log = LogFactory.getLog(JsonDocumentWriter.class); 075 076 @Context 077 JsonFactory factory; 078 079 @Context 080 protected HttpHeaders headers; 081 082 @Context 083 private HttpServletRequest servletRequest; 084 085 @Override 086 public long getSize(DocumentModel arg0, Class<?> arg1, Type arg2, Annotation[] arg3, MediaType arg4) { 087 return -1L; 088 } 089 090 @Override 091 public boolean isWriteable(Class<?> arg0, Type arg1, Annotation[] arg2, MediaType arg3) { 092 return DocumentModel.class.isAssignableFrom(arg0); 093 } 094 095 @Override 096 public void writeTo(DocumentModel doc, Class<?> arg1, Type arg2, Annotation[] arg3, MediaType arg4, 097 MultivaluedMap<String, Object> arg5, OutputStream out) throws IOException, WebApplicationException { 098 try { 099 // schema names: dublincore, file, ... or * 100 List<String> props = headers.getRequestHeader(DOCUMENT_PROPERTIES_HEADER); 101 String[] schemas = null; 102 if (props != null && !props.isEmpty()) { 103 schemas = StringUtils.split(props.get(0), ',', true); 104 } 105 writeDocument(out, doc, schemas); 106 } catch (IOException e) { 107 log.error("Failed to serialize document", e); 108 throw e; 109 } 110 } 111 112 public void writeDocument(OutputStream out, DocumentModel doc, String[] schemas) throws IOException { 113 writeDocument(out, doc, schemas, null); 114 } 115 116 /** 117 * @since 5.6 118 */ 119 public void writeDocument(OutputStream out, DocumentModel doc, String[] schemas, 120 Map<String, String> contextParameters) throws IOException { 121 writeDocument(factory.createJsonGenerator(out, JsonEncoding.UTF8), doc, schemas, contextParameters, headers, 122 servletRequest); 123 } 124 125 public static void writeDocument(JsonGenerator jg, DocumentModel doc, String[] schemas, ServletRequest request) 126 throws IOException { 127 writeDocument(jg, doc, schemas, null, request); 128 } 129 130 /** 131 * @since 5.6 132 */ 133 public static void writeDocument(JsonGenerator jg, DocumentModel doc, String[] schemas, 134 Map<String, String> contextParameters, ServletRequest request) throws IOException { 135 writeDocument(jg, doc, schemas, contextParameters, null, request); 136 } 137 138 /** 139 * @param jg a ready to user JSON generator 140 * @param doc the document to serialize 141 * @param schemas an array of schemas that must be serialized in the properties map 142 * @param contextParameters 143 * @param headers 144 * @param request the ServletRequest. If null blob URLs won't be generated. 145 * @since 5.7.3 146 */ 147 public static void writeDocument(JsonGenerator jg, DocumentModel doc, String[] schemas, 148 Map<String, String> contextParameters, HttpHeaders headers, ServletRequest request) throws IOException { 149 jg.writeStartObject(); 150 jg.writeStringField("entity-type", "document"); 151 jg.writeStringField("repository", doc.getRepositoryName()); 152 jg.writeStringField("uid", doc.getId()); 153 jg.writeStringField("path", doc.getPathAsString()); 154 jg.writeStringField("type", doc.getType()); 155 jg.writeStringField("state", doc.getRef() != null ? doc.getCurrentLifeCycleState() : null); 156 jg.writeStringField("parentRef", doc.getParentRef() != null ? doc.getParentRef().toString() : null); 157 jg.writeStringField("versionLabel", doc.getVersionLabel()); 158 jg.writeBooleanField("isCheckedOut", doc.isCheckedOut()); 159 Lock lock = doc.getLockInfo(); 160 if (lock != null) { 161 jg.writeStringField("lockOwner", lock.getOwner()); 162 jg.writeStringField("lockCreated", ISODateTimeFormat.dateTime().print(new DateTime(lock.getCreated()))); 163 } 164 jg.writeStringField("title", doc.getTitle()); 165 try { 166 Calendar cal = (Calendar) doc.getPropertyValue("dc:modified"); 167 if (cal != null) { 168 jg.writeStringField("lastModified", DateParser.formatW3CDateTime(cal.getTime())); 169 } 170 } catch (PropertyNotFoundException e) { 171 // ignore 172 } 173 174 if (schemas != null && schemas.length > 0) { 175 jg.writeObjectFieldStart("properties"); 176 if (schemas.length == 1 && "*".equals(schemas[0])) { 177 // full document 178 for (String schema : doc.getSchemas()) { 179 writeProperties(jg, doc, schema, request); 180 } 181 } else { 182 for (String schema : schemas) { 183 writeProperties(jg, doc, schema, request); 184 } 185 } 186 jg.writeEndObject(); 187 } 188 189 jg.writeArrayFieldStart("facets"); 190 for (String facet : doc.getFacets()) { 191 jg.writeString(facet); 192 } 193 jg.writeEndArray(); 194 jg.writeStringField("changeToken", doc.getChangeToken()); 195 196 jg.writeObjectFieldStart("contextParameters"); 197 if (contextParameters != null && !contextParameters.isEmpty()) { 198 for (Map.Entry<String, String> parameter : contextParameters.entrySet()) { 199 jg.writeStringField(parameter.getKey(), parameter.getValue()); 200 } 201 } 202 203 writeRestContributions(jg, doc, headers, request); 204 jg.writeEndObject(); 205 206 jg.writeEndObject(); 207 jg.flush(); 208 } 209 210 /** 211 * @param jg 212 * @param doc 213 * @param headers 214 * @throws IOException 215 * @throws JsonGenerationException 216 * @since 5.7.3 217 */ 218 protected static void writeRestContributions(JsonGenerator jg, DocumentModel doc, HttpHeaders headers, 219 ServletRequest request) throws JsonGenerationException, IOException { 220 ContentEnricherService rcs = Framework.getLocalService(ContentEnricherService.class); 221 RestEvaluationContext ec = new HeaderDocEvaluationContext(doc, headers, request); 222 rcs.writeContext(jg, ec); 223 } 224 225 protected static void writeProperties(JsonGenerator jg, DocumentModel doc, String schema, ServletRequest request) 226 throws IOException { 227 DocumentPart part = doc.getPart(schema); 228 if (part == null) { 229 return; 230 } 231 String prefix = part.getSchema().getNamespace().prefix; 232 if (prefix == null || prefix.length() == 0) { 233 prefix = schema; 234 } 235 prefix = prefix + ":"; 236 237 String blobUrlPrefix = null; 238 if (request != null) { 239 DownloadService downloadService = Framework.getService(DownloadService.class); 240 blobUrlPrefix = VirtualHostHelper.getBaseURL(request) + downloadService.getDownloadUrl(doc, null, null) 241 + "/"; 242 } 243 244 for (Property p : part.getChildren()) { 245 jg.writeFieldName(prefix + p.getField().getName().getLocalName()); 246 writePropertyValue(jg, p, blobUrlPrefix); 247 } 248 } 249 250 /** 251 * Converts the value of the given core property to JSON format. The given filesBaseUrl is the baseUrl that can be 252 * used to locate blob content and is useful to generate blob urls. 253 */ 254 public static void writePropertyValue(JsonGenerator jg, Property prop, String filesBaseUrl) 255 throws PropertyException, JsonGenerationException, IOException { 256 JSONPropertyWriter.writePropertyValue(jg, prop, DateTimeFormat.W3C, filesBaseUrl); 257 } 258 259}