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