001/* 002 * (C) Copyright 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 * Nicolas Chapurlat <nchapurlat@nuxeo.com> 018 */ 019 020package org.nuxeo.ecm.core.io.marshallers.json; 021 022import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE; 023import static org.nuxeo.ecm.core.io.marshallers.json.enrichers.AbstractJsonEnricher.ENTITY_ENRICHER_NAME; 024import static org.nuxeo.ecm.core.io.registry.MarshallingConstants.ENTITY_FIELD_NAME; 025 026import java.io.Closeable; 027import java.io.IOException; 028import java.lang.reflect.Type; 029import java.util.ArrayList; 030import java.util.Collection; 031import java.util.List; 032import java.util.Set; 033 034import org.apache.commons.lang3.reflect.TypeUtils; 035import org.nuxeo.ecm.core.io.marshallers.json.enrichers.Enriched; 036import org.nuxeo.ecm.core.io.registry.MarshallerRegistry; 037import org.nuxeo.ecm.core.io.registry.Writer; 038import org.nuxeo.ecm.core.io.registry.context.MaxDepthReachedException; 039import org.nuxeo.ecm.core.io.registry.context.RenderingContext; 040import org.nuxeo.ecm.core.io.registry.context.WrappedContext; 041 042import com.fasterxml.jackson.core.JsonGenerator; 043 044import io.opencensus.trace.Span; 045import io.opencensus.trace.Tracing; 046 047/** 048 * Base class to write Nuxeo Json entity. This class write the json object, the json "entity-type" property and enable 049 * all activated enrichers registered in the {@link MarshallerRegistry} and compatible with the marshalled Java type. 050 * 051 * @param <EntityType> The managed Java type. 052 * @since 7.2 053 */ 054public abstract class ExtensibleEntityJsonWriter<EntityType> extends AbstractJsonWriter<EntityType> { 055 056 /** 057 * The "entity-type" Json property value. 058 */ 059 private final String entityType; 060 061 /** 062 * The {@link Enriched} generic type parametrized with the given EntityType. 063 */ 064 private final Type genericType; 065 066 /** 067 * @param entityType The "entity-type" Json property value. 068 * @param entityClass The entity type. 069 */ 070 public ExtensibleEntityJsonWriter(String entityType, Class<EntityType> entityClass) { 071 super(); 072 this.entityType = entityType; 073 genericType = TypeUtils.parameterize(Enriched.class, entityClass); 074 } 075 076 @Override 077 public void write(EntityType entity, JsonGenerator jg) throws IOException { 078 Span span = Tracing.getTracer().getCurrentSpan(); 079 span.addAnnotation("json#write " + entityType); 080 jg.writeStartObject(); 081 List<Object> entityList = new ArrayList<>(); 082 entityList.add(this.entityType); 083 ctx.addParameterListValues(RenderingContext.RESPONSE_HEADER_ENTITY_TYPE_KEY, entityList); 084 jg.writeStringField(ENTITY_FIELD_NAME, entityType); 085 writeEntityBody(entity, jg); 086 try { 087 WrappedContext wrappedCtx = ctx.wrap().controlDepth(); 088 Set<String> enrichers = ctx.getEnrichers(entityType); 089 if (enrichers.size() > 0) { 090 boolean hasEnrichers = false; 091 Enriched<EntityType> enriched = null; 092 for (String enricherName : enrichers) { 093 span.addAnnotation("json#write " + entityType + ".enricher." + enricherName); 094 try (Closeable resource = wrappedCtx.with(ENTITY_ENRICHER_NAME, enricherName).open()) { 095 @SuppressWarnings("rawtypes") 096 Collection<Writer<Enriched>> writers = registry.getAllWriters(ctx, Enriched.class, 097 this.genericType, APPLICATION_JSON_TYPE); 098 for (@SuppressWarnings("rawtypes") 099 Writer<Enriched> writer : writers) { 100 if (!hasEnrichers) { 101 hasEnrichers = true; 102 jg.writeObjectFieldStart("contextParameters"); 103 enriched = new Enriched<>(entity); 104 } 105 OutputStreamWithJsonWriter out = new OutputStreamWithJsonWriter(jg); 106 writer.write(enriched, Enriched.class, this.genericType, APPLICATION_JSON_TYPE, out); 107 } 108 } 109 } 110 if (hasEnrichers) { 111 jg.writeEndObject(); 112 } 113 } 114 } catch (MaxDepthReachedException e) { 115 // do nothing, do not call enrichers 116 } 117 extend(entity, jg); 118 jg.writeEndObject(); 119 span.addAnnotation("json#write " + entityType + ".done"); 120 } 121 122 /** 123 * Implement this method to write the entity body. 124 * 125 * @param entity The Java entity. 126 * @param jg A {@link JsonGenerator} ready to write your entity as Json. 127 * @since 7.2 128 */ 129 protected abstract void writeEntityBody(EntityType entity, JsonGenerator jg) throws IOException; 130 131 /** 132 * Override this method to add additional property in the entity. This method is useful to override a marshaller 133 * implementing this class. 134 * 135 * @param entity The Java entity. 136 * @param jg A {@link JsonGenerator} ready to write your entity as Json. 137 * @since 7.2 138 */ 139 protected void extend(EntityType entity, JsonGenerator jg) throws IOException { 140 } 141 142}