001/* 002 * (C) Copyright 2015-2020 Nuxeo (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.enrichers; 021 022import static com.fasterxml.jackson.core.JsonToken.END_OBJECT; 023import static com.fasterxml.jackson.core.JsonToken.FIELD_NAME; 024 025import java.io.IOException; 026import java.lang.reflect.Type; 027 028import javax.ws.rs.core.MediaType; 029 030import org.apache.logging.log4j.LogManager; 031import org.apache.logging.log4j.Logger; 032import org.nuxeo.ecm.core.io.marshallers.json.AbstractJsonWriter; 033import org.nuxeo.ecm.core.io.marshallers.json.ExtensibleEntityJsonWriter; 034 035import com.fasterxml.jackson.core.JsonGenerator; 036import com.fasterxml.jackson.core.JsonParser; 037import com.fasterxml.jackson.databind.ObjectMapper; 038import com.fasterxml.jackson.databind.util.TokenBuffer; 039 040/** 041 * Base class to write {@link ExtensibleEntityJsonWriter}'s enricher. 042 * 043 * @param <EntityType> The Java type whose the generated JSON will be enriched. 044 * @since 7.2 045 */ 046public abstract class AbstractJsonEnricher<EntityType> extends AbstractJsonWriter<Enriched<EntityType>> { 047 048 private static final Logger log = LogManager.getLogger(AbstractJsonEnricher.class); 049 050 public static final String ENTITY_ENRICHER_NAME = "_EntityEnricherName"; 051 052 private final String name; 053 054 protected static final ObjectMapper MAPPER = new ObjectMapper(); 055 056 public AbstractJsonEnricher(String name) { 057 this.name = name; 058 } 059 060 @Override 061 public boolean accept(Class<?> clazz, Type genericType, MediaType mediatype) { 062 return name.equals(ctx.<String> getParameter(ENTITY_ENRICHER_NAME)); 063 } 064 065 @Override 066 public void write(Enriched<EntityType> enrichable, JsonGenerator jg) { 067 try (TokenBuffer tb = new TokenBuffer(MAPPER, false)) { 068 // Write to a temporary output in case of exception during write() 069 tb.writeStartObject(); 070 write(tb, enrichable.getEntity()); 071 tb.writeEndObject(); 072 tb.flush(); 073 // Add the complete, well-formed content to the real output 074 try (JsonParser parser = tb.asParser()) { 075 parser.nextToken(); // ignoring START_OBJECT 076 while (parser.nextToken() == FIELD_NAME) { 077 jg.copyCurrentStructure(parser); 078 } 079 if (parser.currentToken() != END_OBJECT) { 080 log.error("Enricher: {} failed on current token: {}, output to write: {}", name::toString, 081 parser::currentToken, () -> safeReadBuffer(tb)); 082 } 083 } 084 } catch (Exception e) { 085 if (e instanceof InterruptedException) { // NOSONAR 086 Thread.currentThread().interrupt(); 087 throw new RuntimeException("interrupted", e); // NOSONAR 088 } else { 089 // TODO collect exception and return it to the caller 090 log.info("Enricher: {} failed", name, e); 091 } 092 } 093 } 094 095 protected String safeReadBuffer(TokenBuffer tb) { 096 try { 097 return MAPPER.readTree(tb.asParser()); 098 } catch (IOException e) { 099 return "malformed content could not be retrieved"; 100 } 101 } 102 103 /** 104 * When implementing this method, the provided {@link JsonGenerator} expect you write a field name and a field value 105 * (or many). 106 * 107 * @param jg The {@link JsonGenerator} to use. 108 * @param enriched The enriched entity. 109 * @since 7.2 110 */ 111 public abstract void write(JsonGenerator jg, EntityType enriched) throws IOException; 112 113}