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