001package org.nuxeo.elasticsearch.audit.io;
002
003import java.io.IOException;
004import java.io.Serializable;
005import java.util.Map;
006
007import org.apache.commons.logging.Log;
008import org.apache.commons.logging.LogFactory;
009import org.codehaus.jackson.JsonGenerationException;
010import org.codehaus.jackson.JsonGenerator;
011import org.codehaus.jackson.map.JsonMappingException;
012import org.codehaus.jackson.map.JsonSerializer;
013import org.codehaus.jackson.map.ObjectMapper;
014import org.codehaus.jackson.map.SerializerProvider;
015import org.codehaus.jackson.map.module.SimpleModule;
016import org.elasticsearch.common.jackson.core.JsonProcessingException;
017import org.joda.time.DateTime;
018import org.joda.time.format.ISODateTimeFormat;
019import org.nuxeo.ecm.core.api.impl.blob.AbstractBlob;
020import org.nuxeo.ecm.platform.audit.api.ExtendedInfo;
021import org.nuxeo.ecm.platform.audit.api.LogEntry;
022
023public class AuditEntryJSONWriter {
024
025    protected static final Log log = LogFactory.getLog(AuditEntryJSONWriter.class);
026
027    public static void asJSON(JsonGenerator jg, LogEntry logEntry) throws IOException {
028        ObjectMapper objectMapper = new ObjectMapper();
029        SimpleModule module = new SimpleModule("esAuditJson", org.codehaus.jackson.Version.unknownVersion());
030        module.addSerializer(Map.class, new MapEntrySerializer());
031        module.addSerializer(AbstractBlob.class, new BinaryBlobEntrySerializer());
032        objectMapper.registerModule(module);
033        jg.setCodec(objectMapper);
034
035        jg.writeStartObject();
036        jg.writeStringField("entity-type", "logEntry");
037
038        writeField(jg, "category", logEntry.getCategory());
039        writeField(jg, "principalName", logEntry.getPrincipalName());
040        writeField(jg, "comment", logEntry.getComment());
041        writeField(jg, "docLifeCycle", logEntry.getDocLifeCycle());
042        writeField(jg, "docPath", logEntry.getDocPath());
043        writeField(jg, "docType", logEntry.getDocType());
044        writeField(jg, "docUUID", logEntry.getDocUUID());
045        writeField(jg, "eventId", logEntry.getEventId());
046        writeField(jg, "repositoryId", logEntry.getRepositoryId());
047        jg.writeStringField("eventDate", ISODateTimeFormat.dateTime().print(new DateTime(logEntry.getEventDate())));
048        jg.writeNumberField("id", logEntry.getId());
049        jg.writeStringField("logDate", ISODateTimeFormat.dateTime().print(new DateTime(logEntry.getLogDate())));
050        Map<String, ExtendedInfo> extended = logEntry.getExtendedInfos();
051        jg.writeObjectFieldStart("extended");
052        for (String key : extended.keySet()) {
053            ExtendedInfo ei = extended.get(key);
054            if (ei != null && ei.getSerializableValue() != null) {
055                Serializable value = ei.getSerializableValue();
056                if (value instanceof String) {
057                    String strValue = (String) value;
058                    if (isJsonContent(strValue)) {
059                        jg.writeFieldName(key);
060                        jg.writeRawValue(strValue);
061                    } else {
062                        jg.writeStringField(key, strValue);
063                    }
064                } else {
065                    try {
066                        jg.writeObjectField(key, ei.getSerializableValue());
067                    } catch (JsonMappingException e) {
068                        log.error("No Serializer found.", e);
069                    }
070                }
071            } else {
072                jg.writeNullField(key);
073            }
074        }
075        jg.writeEndObject();
076
077        jg.writeEndObject();
078        jg.flush();
079    }
080
081    /**
082     * Helper method used to determine if a String field is actually nested JSON
083     *
084     * @since 7.4
085     */
086    protected static boolean isJsonContent(String value) {
087        if (value != null) {
088            value = value.trim();
089            if (value.startsWith("{") && value.endsWith("}")) {
090                return true;
091            } else if (value.startsWith("[") && value.endsWith("]")) {
092                return true;
093            }
094        }
095        return false;
096    }
097
098    protected static void writeField(JsonGenerator jg, String name, String value) throws IOException {
099        if (value == null) {
100            jg.writeNullField(name);
101        } else {
102            jg.writeStringField(name, value);
103        }
104    }
105
106    static class MapEntrySerializer extends JsonSerializer<Map> {
107
108        @Override
109        public void serialize(Map map, JsonGenerator jgen, SerializerProvider provider) throws IOException,
110                JsonProcessingException {
111            jgen.writeStartObject();
112            for (Object key : map.keySet()) {
113                jgen.writeObjectField((String) key, map.get(key));
114            }
115            jgen.writeEndObject();
116        }
117    }
118
119    static class BinaryBlobEntrySerializer extends JsonSerializer<AbstractBlob> {
120
121        @Override
122        public void serialize(AbstractBlob blob, JsonGenerator jgen, SerializerProvider provider)
123                throws JsonGenerationException, IOException {
124            // Do not serizalize
125            jgen.writeNull();
126        }
127    }
128
129}