001package org.nuxeo.ecm.automation.jaxrs.io.documents; 002 003import static org.nuxeo.ecm.core.api.security.SecurityConstants.BROWSE; 004import static org.nuxeo.ecm.core.api.security.SecurityConstants.EVERYONE; 005import static org.nuxeo.ecm.core.api.security.SecurityConstants.UNSUPPORTED_ACL; 006 007import java.io.IOException; 008import java.io.OutputStream; 009import java.lang.annotation.Annotation; 010import java.lang.reflect.Type; 011import java.util.ArrayList; 012import java.util.Arrays; 013import java.util.List; 014import java.util.Map; 015 016import javax.ws.rs.Produces; 017import javax.ws.rs.core.HttpHeaders; 018import javax.ws.rs.core.MediaType; 019import javax.ws.rs.ext.Provider; 020 021import org.codehaus.jackson.JsonEncoding; 022import org.codehaus.jackson.JsonGenerator; 023import org.nuxeo.ecm.core.api.DocumentModel; 024import org.nuxeo.ecm.core.api.DocumentRef; 025import org.nuxeo.ecm.core.api.security.ACE; 026import org.nuxeo.ecm.core.api.security.ACL; 027import org.nuxeo.ecm.core.api.security.ACP; 028import org.nuxeo.ecm.core.api.security.impl.ACPImpl; 029import org.nuxeo.ecm.core.security.SecurityService; 030import org.nuxeo.ecm.platform.tag.Tag; 031import org.nuxeo.ecm.platform.tag.TagService; 032import org.nuxeo.runtime.api.Framework; 033 034/** 035 * JSon writer that outputs a format ready to eat by elasticsearch. 036 * 037 * @since 5.9.3 038 */ 039@Provider 040@Produces({ JsonESDocumentWriter.MIME_TYPE }) 041// TODO: remove the use of JsonDocumentWriter - refactor the ES marshalling and use nuxeo-core-io marshalling instead 042@SuppressWarnings("deprecation") 043public class JsonESDocumentWriter extends JsonDocumentWriter { 044 045 public static final String MIME_TYPE = "application/json+esentity"; 046 047 @Override 048 public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { 049 return super.isWriteable(type, genericType, annotations, mediaType) && MIME_TYPE.equals(mediaType.toString()); 050 } 051 052 public void writeDoc(JsonGenerator jg, DocumentModel doc, String[] schemas, Map<String, String> contextParameters, 053 HttpHeaders headers) throws IOException { 054 055 jg.writeStartObject(); 056 writeSystemProperties(jg, doc); 057 writeSchemas(jg, doc, schemas); 058 writeContextParameters(jg, doc, contextParameters); 059 jg.writeEndObject(); 060 jg.flush(); 061 } 062 063 /** 064 * @since 7.2 065 */ 066 protected void writeSystemProperties(JsonGenerator jg, DocumentModel doc) throws IOException { 067 jg.writeStringField("ecm:repository", doc.getRepositoryName()); 068 jg.writeStringField("ecm:uuid", doc.getId()); 069 jg.writeStringField("ecm:name", doc.getName()); 070 jg.writeStringField("ecm:title", doc.getTitle()); 071 jg.writeStringField("ecm:path", doc.getPathAsString()); 072 jg.writeStringField("ecm:primaryType", doc.getType()); 073 DocumentRef parentRef = doc.getParentRef(); 074 if (parentRef != null) { 075 jg.writeStringField("ecm:parentId", parentRef.toString()); 076 } 077 jg.writeStringField("ecm:currentLifeCycleState", doc.getCurrentLifeCycleState()); 078 jg.writeStringField("ecm:versionLabel", doc.getVersionLabel()); 079 jg.writeBooleanField("ecm:isCheckedIn", !doc.isCheckedOut()); 080 jg.writeBooleanField("ecm:isProxy", doc.isProxy()); 081 jg.writeBooleanField("ecm:isVersion", doc.isVersion()); 082 jg.writeBooleanField("ecm:isLatestVersion", doc.isLatestVersion()); 083 jg.writeBooleanField("ecm:isLatestMajorVersion", doc.isLatestMajorVersion()); 084 jg.writeArrayFieldStart("ecm:mixinType"); 085 for (String facet : doc.getFacets()) { 086 jg.writeString(facet); 087 } 088 jg.writeEndArray(); 089 TagService tagService = Framework.getService(TagService.class); 090 if (tagService != null) { 091 jg.writeArrayFieldStart("ecm:tag"); 092 for (Tag tag : tagService.getDocumentTags(doc.getCoreSession(), doc.getId(), null, true)) { 093 jg.writeString(tag.getLabel()); 094 } 095 jg.writeEndArray(); 096 } 097 jg.writeStringField("ecm:changeToken", doc.getChangeToken()); 098 Long pos = doc.getPos(); 099 if (pos != null) { 100 jg.writeNumberField("ecm:pos", pos); 101 } 102 // Add a positive ACL only 103 SecurityService securityService = Framework.getService(SecurityService.class); 104 List<String> browsePermissions = new ArrayList<String>( 105 Arrays.asList(securityService.getPermissionsToCheck(BROWSE))); 106 ACP acp = doc.getACP(); 107 if (acp == null) { 108 acp = new ACPImpl(); 109 } 110 jg.writeArrayFieldStart("ecm:acl"); 111 outerloop: for (ACL acl : acp.getACLs()) { 112 for (ACE ace : acl.getACEs()) { 113 if (ace.isGranted() && ace.isEffective() && browsePermissions.contains(ace.getPermission())) { 114 jg.writeString(ace.getUsername()); 115 } 116 if (ace.isDenied() && ace.isEffective()) { 117 if (!EVERYONE.equals(ace.getUsername())) { 118 jg.writeString(UNSUPPORTED_ACL); 119 } 120 break outerloop; 121 } 122 } 123 } 124 125 jg.writeEndArray(); 126 Map<String, String> bmap = doc.getBinaryFulltext(); 127 if (bmap != null && !bmap.isEmpty()) { 128 for (Map.Entry<String, String> item : bmap.entrySet()) { 129 String value = item.getValue(); 130 if (value != null) { 131 jg.writeStringField("ecm:" + item.getKey(), value); 132 } 133 } 134 } 135 } 136 137 /** 138 * @since 7.2 139 */ 140 protected void writeSchemas(JsonGenerator jg, DocumentModel doc, String[] schemas) throws IOException { 141 if (schemas == null || (schemas.length == 1 && "*".equals(schemas[0]))) { 142 schemas = doc.getSchemas(); 143 } 144 for (String schema : schemas) { 145 writeProperties(jg, doc, schema, null); 146 } 147 } 148 149 /** 150 * @since 7.2 151 */ 152 protected void writeContextParameters(JsonGenerator jg, DocumentModel doc, Map<String, String> contextParameters) 153 throws IOException { 154 if (contextParameters != null && !contextParameters.isEmpty()) { 155 for (Map.Entry<String, String> parameter : contextParameters.entrySet()) { 156 jg.writeStringField(parameter.getKey(), parameter.getValue()); 157 } 158 } 159 } 160 161 @Override 162 public void writeDocument(OutputStream out, DocumentModel doc, String[] schemas, 163 Map<String, String> contextParameters) throws IOException { 164 writeDoc(factory.createJsonGenerator(out, JsonEncoding.UTF8), doc, schemas, contextParameters, headers); 165 } 166 167 public void writeESDocument(JsonGenerator jg, DocumentModel doc, String[] schemas, 168 Map<String, String> contextParameters) throws IOException { 169 writeDoc(jg, doc, schemas, contextParameters, null); 170 } 171}