001/* 002 * (C) Copyright 2015-2017 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 <[email protected]> 018 */ 019 020package org.nuxeo.ecm.platform.audit.io; 021 022import static org.nuxeo.ecm.core.io.registry.reflect.Instantiations.SINGLETON; 023import static org.nuxeo.ecm.core.io.registry.reflect.Priorities.REFERENCE; 024import static org.nuxeo.ecm.platform.audit.api.BuiltinLogEntryData.LOG_CATEGORY; 025import static org.nuxeo.ecm.platform.audit.api.BuiltinLogEntryData.LOG_COMMENT; 026import static org.nuxeo.ecm.platform.audit.api.BuiltinLogEntryData.LOG_DOC_LIFE_CYCLE; 027import static org.nuxeo.ecm.platform.audit.api.BuiltinLogEntryData.LOG_DOC_PATH; 028import static org.nuxeo.ecm.platform.audit.api.BuiltinLogEntryData.LOG_DOC_TYPE; 029import static org.nuxeo.ecm.platform.audit.api.BuiltinLogEntryData.LOG_DOC_UUID; 030import static org.nuxeo.ecm.platform.audit.api.BuiltinLogEntryData.LOG_EVENT_DATE; 031import static org.nuxeo.ecm.platform.audit.api.BuiltinLogEntryData.LOG_EVENT_ID; 032import static org.nuxeo.ecm.platform.audit.api.BuiltinLogEntryData.LOG_EXTENDED; 033import static org.nuxeo.ecm.platform.audit.api.BuiltinLogEntryData.LOG_ID; 034import static org.nuxeo.ecm.platform.audit.api.BuiltinLogEntryData.LOG_LOG_DATE; 035import static org.nuxeo.ecm.platform.audit.api.BuiltinLogEntryData.LOG_PRINCIPAL_NAME; 036import static org.nuxeo.ecm.platform.audit.api.BuiltinLogEntryData.LOG_REPOSITORY_ID; 037 038import java.io.IOException; 039import java.io.Serializable; 040import java.util.Date; 041import java.util.List; 042import java.util.Map; 043import java.util.Map.Entry; 044import java.util.stream.Collectors; 045 046import org.apache.commons.lang3.ClassUtils; 047import org.joda.time.DateTime; 048import org.joda.time.format.DateTimeFormatter; 049import org.joda.time.format.ISODateTimeFormat; 050import org.nuxeo.ecm.core.io.marshallers.json.ExtensibleEntityJsonWriter; 051import org.nuxeo.ecm.core.io.marshallers.json.enrichers.AbstractJsonEnricher; 052import org.nuxeo.ecm.core.io.registry.reflect.Setup; 053import org.nuxeo.ecm.platform.audit.api.ExtendedInfo; 054import org.nuxeo.ecm.platform.audit.api.LogEntry; 055 056import com.fasterxml.jackson.core.JsonGenerator; 057 058/** 059 * Convert {@link LogEntry} to Json. 060 * <p> 061 * This marshaller is enrichable: register class implementing {@link AbstractJsonEnricher} and managing {@link LogEntry} 062 * . 063 * </p> 064 * <p> 065 * This marshaller is also extensible: extend it and simply override 066 * {@link ExtensibleEntityJsonWriter#extend(Object, JsonGenerator)}. 067 * </p> 068 * <p> 069 * Format is: 070 * 071 * <pre> 072 * {@code 073 * { 074 * "entity-type":"logEntry", 075 * "category": "LOG_ENTRY_CATEGORY", 076 * "principalName": "LOG_ENTRY_PRINCIPAL", 077 * "comment": "LOG_ENTRY_COMMENT", 078 * "docLifeCycle": "DOC_LIFECYCLE", 079 * "docPath": "DOC_PATH", 080 * "docType": "DOC_TYPE", 081 * "docUUID": "DOC_UUID", 082 * "eventId": "EVENT_ID", 083 * "repositoryId": "REPO_ID", 084 * "eventDate": "LOG_EVENT_DATE", 085 * "logDate": "LOG_DATE" 086 * <-- contextParameters if there are enrichers activated 087 * <-- additional property provided by extend() method 088 * } 089 * </pre> 090 * </p> 091 * @since 7.2 092 */ 093@Setup(mode = SINGLETON, priority = REFERENCE) 094public class LogEntryJsonWriter extends ExtensibleEntityJsonWriter<LogEntry> { 095 096 public static final String ENTITY_TYPE = "logEntry"; 097 098 public LogEntryJsonWriter() { 099 super(ENTITY_TYPE, LogEntry.class); 100 } 101 102 @Override 103 protected void writeEntityBody(LogEntry logEntry, JsonGenerator jg) throws IOException { 104 jg.writeNumberField(LOG_ID, logEntry.getId()); 105 jg.writeStringField(LOG_CATEGORY, logEntry.getCategory()); 106 jg.writeStringField(LOG_PRINCIPAL_NAME, logEntry.getPrincipalName()); 107 jg.writeStringField(LOG_COMMENT, logEntry.getComment()); 108 jg.writeStringField(LOG_DOC_LIFE_CYCLE, logEntry.getDocLifeCycle()); 109 jg.writeStringField(LOG_DOC_PATH, logEntry.getDocPath()); 110 jg.writeStringField(LOG_DOC_TYPE, logEntry.getDocType()); 111 jg.writeStringField(LOG_DOC_UUID, logEntry.getDocUUID()); 112 jg.writeStringField(LOG_EVENT_ID, logEntry.getEventId()); 113 jg.writeStringField(LOG_REPOSITORY_ID, logEntry.getRepositoryId()); 114 DateTimeFormatter dateTime = ISODateTimeFormat.dateTime(); 115 jg.writeStringField(LOG_EVENT_DATE, dateTime.print(new DateTime(logEntry.getEventDate()))); 116 jg.writeStringField(LOG_LOG_DATE, dateTime.print(new DateTime(logEntry.getLogDate()))); 117 writeExtendedInfos(jg, logEntry); 118 } 119 120 protected void writeExtendedInfos(JsonGenerator jg, LogEntry logEntry) throws IOException { 121 Map<String, ExtendedInfo> extended = logEntry.getExtendedInfos(); 122 jg.writeObjectFieldStart(LOG_EXTENDED); 123 for (String key : extended.keySet()) { 124 ExtendedInfo ei = extended.get(key); 125 if (ei != null && ei.getSerializableValue() != null) { 126 writeExtendedInfo(jg, key, ei.getSerializableValue()); 127 } else { 128 jg.writeNullField(key); 129 } 130 } 131 jg.writeEndObject(); 132 } 133 134 protected void writeExtendedInfo(JsonGenerator jg, String key, Serializable value) throws IOException { 135 Class<?> clazz = value.getClass(); 136 if (Long.class.isAssignableFrom(clazz)) { 137 jg.writeNumberField(key, (Long) value); 138 } else if (Integer.class.isAssignableFrom(clazz)) { 139 jg.writeNumberField(key, (Integer) value); 140 } else if (Double.class.isAssignableFrom(clazz)) { 141 jg.writeNumberField(key, (Double) value); 142 } else if (Date.class.isAssignableFrom(clazz)) { 143 jg.writeStringField(key, ISODateTimeFormat.dateTime().print(new DateTime(value))); 144 } else if (String.class.isAssignableFrom(clazz)) { 145 jg.writeStringField(key, (String) value); 146 } else if (Boolean.class.isAssignableFrom(clazz)) { 147 jg.writeBooleanField(key, (Boolean) value); 148 } else if (clazz.isArray() || List.class.isAssignableFrom(clazz)) { 149 jg.writeObjectField(key, value); 150 } else if (Map.class.isAssignableFrom(clazz)) { 151 @SuppressWarnings("unchecked") 152 Map<String, Serializable> map = // 153 ((Map<String, Serializable>) value).entrySet() 154 .stream() 155 // keep primitive values 156 // blobs can be in WorkflowVariables for instance 157 .filter(entry -> entry.getValue() != null 158 && ClassUtils.isPrimitiveOrWrapper( 159 entry.getValue().getClass())) 160 .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); 161 jg.writeObjectField(key, map); 162 } else { 163 // mainly blobs 164 jg.writeStringField(key, value.toString()); 165 } 166 } 167 168}