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}