001/* 002 * (C) Copyright 2006-2007 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 * 018 * 019 * $Id: $ 020 */ 021package org.nuxeo.ecm.platform.audit.io; 022 023import java.io.ByteArrayInputStream; 024import java.io.ByteArrayOutputStream; 025import java.io.IOException; 026import java.io.InputStream; 027import java.io.OutputStream; 028import java.text.DateFormat; 029import java.text.ParseException; 030import java.text.SimpleDateFormat; 031import java.util.ArrayList; 032import java.util.Date; 033import java.util.Iterator; 034import java.util.List; 035 036import org.apache.commons.beanutils.BeanUtils; 037import org.apache.commons.io.IOUtils; 038import org.apache.commons.logging.Log; 039import org.apache.commons.logging.LogFactory; 040import org.dom4j.Document; 041import org.dom4j.DocumentException; 042import org.dom4j.DocumentFactory; 043import org.dom4j.Element; 044import org.dom4j.io.OutputFormat; 045import org.dom4j.io.SAXReader; 046import org.dom4j.io.XMLWriter; 047import org.nuxeo.ecm.core.api.DocumentRef; 048import org.nuxeo.ecm.core.api.NuxeoException; 049import org.nuxeo.ecm.platform.audit.api.AuditLogger; 050import org.nuxeo.ecm.platform.audit.api.LogEntry; 051import org.nuxeo.runtime.api.Framework; 052 053/** 054 * Audit log entry importer/exporter. 055 * <p> 056 * Could be overridden to externalize additional information of a redefined LogEntry. 057 * 058 * @author DM 059 */ 060// FIXME: design issue - this is a utility class (only static methods) with no subclasses (misleading name). 061public class IOLogEntryBase { 062 063 private static final Log log = LogFactory.getLog(IOLogEntryBase.class); 064 065 public static final String DOCUMENT_TAG = "documentLogs"; 066 067 public static final String LOGENTRY_TAG = "logEntry"; 068 069 public static void write(List<LogEntry> logEntries, OutputStream out) throws IOException { 070 Document jdoc = writeDocument(logEntries); 071 writeXML(jdoc, out); 072 } 073 074 private static Document writeDocument(List<LogEntry> logEntries) { 075 Document document = DocumentFactory.getInstance().createDocument(); 076 document.setName("logEntries"); 077 078 Element rootElement = document.addElement(DOCUMENT_TAG); 079 for (LogEntry logEntry : logEntries) { 080 Element logEntryElement = rootElement.addElement(LOGENTRY_TAG); 081 writeLogEntry(logEntryElement, logEntry); 082 } 083 084 return document; 085 } 086 087 /** 088 * Could be overridden to put other (additional) data. 089 */ 090 protected static void writeLogEntry(Element logEntryElement, LogEntry logEntry) { 091 logEntryElement.addAttribute("category", logEntry.getCategory()); 092 logEntryElement.addAttribute("comment", logEntry.getComment()); 093 logEntryElement.addAttribute("docLifeCycle", logEntry.getDocLifeCycle()); 094 logEntryElement.addAttribute("docPath", logEntry.getDocPath()); 095 logEntryElement.addAttribute("docType", logEntry.getDocType()); 096 logEntryElement.addAttribute("docUUID", logEntry.getDocUUID()); 097 logEntryElement.addAttribute("repoId", logEntry.getRepositoryId()); 098 099 String creationDate = getDateFormat().format(logEntry.getEventDate()); 100 logEntryElement.addAttribute("creationDate", creationDate); 101 logEntryElement.addAttribute("eventId", logEntry.getEventId()); 102 logEntryElement.addAttribute("principalName", logEntry.getPrincipalName()); 103 } 104 105 public static List<LogEntry> read(InputStream in) throws IOException { 106 Document jDoc = loadXML(in); 107 return readDocument(jDoc); 108 } 109 110 /** 111 * Will translate from a jdoc to a list of LogEntry objects. 112 */ 113 @SuppressWarnings({ "unchecked" }) 114 protected static List<LogEntry> readDocument(Document doc) { 115 List<LogEntry> logEntries = new ArrayList<LogEntry>(); 116 117 AuditLogger audit = Framework.getService(AuditLogger.class); 118 119 Element rootElement = doc.getRootElement(); 120 Iterator<Element> it = rootElement.elementIterator(); 121 while (it.hasNext()) { 122 Element logEntryElement = it.next(); 123 124 LogEntry logEntry = readLogEntry(audit, logEntryElement); 125 logEntries.add(logEntry); 126 } 127 128 return logEntries; 129 } 130 131 /** 132 * Could be overridden to get other (additional) data. 133 * 134 * @param logEntryElement 135 */ 136 protected static LogEntry readLogEntry(AuditLogger audit, Element logEntryElement) { 137 LogEntry logEntry = audit.newLogEntry(); 138 139 logEntry.setCategory(logEntryElement.attributeValue("category")); 140 logEntry.setComment(logEntryElement.attributeValue("comment")); 141 logEntry.setDocLifeCycle(logEntryElement.attributeValue("docLifeCycle")); 142 logEntry.setDocPath(logEntryElement.attributeValue("docPath")); 143 logEntry.setDocType(logEntryElement.attributeValue("docType")); 144 logEntry.setDocUUID(logEntryElement.attributeValue("docUUID")); 145 logEntry.setRepositoryId(logEntryElement.attributeValue("repoId")); 146 147 try { 148 Date creationDate = getDateFormat().parse(logEntryElement.attributeValue("creationDate")); 149 logEntry.setEventDate(creationDate); 150 } catch (ParseException e) { 151 log.error(e, e); 152 } 153 logEntry.setEventId(logEntryElement.attributeValue("eventId")); 154 logEntry.setPrincipalName(logEntryElement.attributeValue("principalName")); 155 156 return logEntry; 157 } 158 159 /** 160 * Specifies date-string conversion. 161 */ 162 protected static DateFormat getDateFormat() { 163 return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 164 } 165 166 protected static void writeXML(Document doc, OutputStream out) throws IOException { 167 OutputFormat format = OutputFormat.createPrettyPrint(); 168 XMLWriter writer = new XMLWriter(out, format); 169 writer.write(doc); 170 } 171 172 private static Document loadXML(InputStream in) throws IOException { 173 try { 174 // the SAXReader is closing the stream so that we need to copy the 175 // content somewhere 176 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 177 IOUtils.copy(in, baos); 178 return new SAXReader().read(new ByteArrayInputStream(baos.toByteArray())); 179 } catch (DocumentException e) { 180 IOException ioe = new IOException("Failed to read log entry " + ": " + e.getMessage()); 181 ioe.setStackTrace(e.getStackTrace()); 182 throw ioe; 183 } 184 } 185 186 public static List<LogEntry> translate(List<LogEntry> docLogs, DocumentRef newRef) { 187 List<LogEntry> newList = new ArrayList<LogEntry>(); 188 for (LogEntry logEntry : docLogs) { 189 LogEntry newLogEntry = translate(logEntry, newRef); 190 newList.add(newLogEntry); 191 } 192 return newList; 193 } 194 195 /** 196 * Should be overridden if log data structure is changed. 197 */ 198 private static LogEntry translate(LogEntry logEntry, DocumentRef newRef) { 199 LogEntry newLogEntry; 200 try { 201 newLogEntry = (LogEntry) BeanUtils.cloneBean(logEntry); 202 } catch (ReflectiveOperationException e) { 203 throw new NuxeoException("cannot clone bean " + logEntry, e); 204 } 205 newLogEntry.setDocUUID(newRef); 206 return newLogEntry; 207 } 208 209}