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