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 * $Id$
017 */
018
019package org.nuxeo.ecm.platform.audit.io;
020
021import java.io.IOException;
022import java.io.InputStream;
023import java.io.OutputStream;
024import java.io.Serializable;
025import java.util.ArrayList;
026import java.util.Collection;
027import java.util.Collections;
028import java.util.HashMap;
029import java.util.List;
030import java.util.Map;
031
032import org.apache.commons.logging.Log;
033import org.apache.commons.logging.LogFactory;
034import org.nuxeo.ecm.core.api.CoreInstance;
035import org.nuxeo.ecm.core.api.CoreSession;
036import org.nuxeo.ecm.core.api.DocumentModel;
037import org.nuxeo.ecm.core.api.DocumentNotFoundException;
038import org.nuxeo.ecm.core.api.DocumentRef;
039import org.nuxeo.ecm.core.api.IdRef;
040import org.nuxeo.ecm.core.api.NuxeoException;
041import org.nuxeo.ecm.core.io.DocumentTranslationMap;
042import org.nuxeo.ecm.platform.audit.api.LogEntry;
043import org.nuxeo.ecm.platform.audit.api.Logs;
044import org.nuxeo.ecm.platform.io.api.AbstractIOResourceAdapter;
045import org.nuxeo.ecm.platform.io.api.IOResources;
046import org.nuxeo.runtime.api.Framework;
047
048/**
049 * Adapter for import/export of audit logs.
050 *
051 * @author <a href="mailto:dm@nuxeo.com">Dragos Mihalache</a>
052 */
053public class IOAuditAdapter extends AbstractIOResourceAdapter {
054
055    private static final Log log = LogFactory.getLog(IOAuditAdapter.class);
056
057    private static final long serialVersionUID = -3661302796286246086L;
058
059    /**
060     * Should be overridden if IOLogEntryBase is subclassed.
061     *
062     * @return IOLogEntryBase instance that will know how to write and read log entries
063     */
064    protected IOLogEntryBase getLogEntryHelper() {
065        return new IOLogEntryBase();
066    }
067
068    @Override
069    public void setProperties(Map<String, Serializable> properties) {
070    }
071
072    /**
073     * Extract logs involving given documents.
074     * <p>
075     * The adapter properties will filter which logs must be taken into account.
076     */
077    @Override
078    public IOResources extractResources(String repo, Collection<DocumentRef> sources) {
079        if (sources == null || sources.isEmpty()) {
080            return null;
081        }
082        try (CoreSession session = CoreInstance.openCoreSessionSystem(repo)) {
083            Map<DocumentRef, List<LogEntry>> docLogs = new HashMap<DocumentRef, List<LogEntry>>();
084
085            Logs logService = Framework.getService(Logs.class);
086
087            for (DocumentRef docRef : sources) {
088                try {
089                    final String uuid;
090                    if (docRef.type() == DocumentRef.ID) {
091                        uuid = docRef.toString();
092                    } else {
093                        DocumentModel doc = session.getDocument(docRef);
094                        uuid = doc.getId();
095                    }
096
097                    List<LogEntry> logEntries = logService.getLogEntriesFor(uuid);
098
099                    docLogs.put(docRef, logEntries);
100                } catch (DocumentNotFoundException e) {
101                    List<LogEntry> emptyList = Collections.emptyList();
102                    docLogs.put(docRef, emptyList);
103                    continue;
104                }
105            }
106            return new IOAuditResources(docLogs);
107        }
108    }
109
110    @Override
111    public void getResourcesAsXML(OutputStream out, IOResources resources) {
112        if (!(resources instanceof IOAuditResources)) {
113            return;
114        }
115        IOAuditResources auditResources = (IOAuditResources) resources;
116
117        List<LogEntry> logEntries = new ArrayList<LogEntry>();
118
119        Map<DocumentRef, List<LogEntry>> docLogs = auditResources.getLogsMap();
120
121        Collection<List<LogEntry>> all = docLogs.values();
122        for (List<LogEntry> list : all) {
123            logEntries.addAll(list);
124        }
125
126        try {
127            IOLogEntryBase.write(logEntries, out);
128        } catch (IOException e) {
129            throw new NuxeoException("Cannot write logs", e);
130        }
131    }
132
133    @Override
134    public IOResources loadResourcesFromXML(InputStream stream) {
135        List<LogEntry> allEntries;
136        try {
137            allEntries = IOLogEntryBase.read(stream);
138        } catch (IOException e) {
139            throw new NuxeoException("Cannot read entries from " + stream);
140        }
141
142        // will put each log entry to its correspondent document ref
143        Map<DocumentRef, List<LogEntry>> docLogs = new HashMap<DocumentRef, List<LogEntry>>();
144        for (LogEntry logEntry : allEntries) {
145            DocumentRef docRef = new IdRef(logEntry.getDocUUID());
146
147            List<LogEntry> logEntries = docLogs.get(docRef);
148            if (logEntries == null) {
149                logEntries = new ArrayList<LogEntry>();
150                docLogs.put(docRef, logEntries);
151            }
152            logEntries.add(logEntry);
153        }
154
155        return new IOAuditResources(docLogs);
156    }
157
158    @Override
159    public void storeResources(IOResources newResources) {
160        if (!(newResources instanceof IOAuditResources)) {
161            return;
162        }
163        Logs logService = Framework.getService(Logs.class);
164        IOAuditResources auditResources = (IOAuditResources) newResources;
165        Map<DocumentRef, List<LogEntry>> docLogs = auditResources.getLogsMap();
166        for (Map.Entry<DocumentRef, List<LogEntry>> mapEntry : docLogs.entrySet()) {
167            DocumentRef docRef = mapEntry.getKey();
168            List<LogEntry> logs = mapEntry.getValue();
169            // need to set the given docRef - so transfer with the help of
170            // IOLogEntryBase (subclass eventually)
171            List<LogEntry> newLogs = IOLogEntryBase.translate(logs, docRef);
172            logService.addLogEntries(newLogs);
173        }
174    }
175
176    @Override
177    public IOResources translateResources(String repo, IOResources resources, DocumentTranslationMap map) {
178        if (map == null) {
179            return null;
180        }
181        if (!(resources instanceof IOAuditResources)) {
182            return resources;
183        }
184
185        IOAuditResources auditResources = (IOAuditResources) resources;
186        Map<DocumentRef, List<LogEntry>> newResourcesMap = new HashMap<DocumentRef, List<LogEntry>>();
187
188        for (Map.Entry<DocumentRef, List<LogEntry>> entry : auditResources.getLogsMap().entrySet()) {
189            DocumentRef oldRef = entry.getKey();
190            DocumentRef newRef = map.getDocRefMap().get(oldRef);
191            if (newRef == null) {
192                if (log.isErrorEnabled()) {
193                    log.error("newRef does not exist in translation map for " + oldRef);
194                }
195                continue;
196            }
197            List<LogEntry> docLogs = auditResources.getDocumentLogs(oldRef);
198
199            // need to set the given docRef - so transfer with the help of
200            // IOLogEntryBase (subclass eventually)
201            List<LogEntry> newLogs = IOLogEntryBase.translate(docLogs, newRef);
202            newResourcesMap.put(newRef, newLogs);
203        }
204
205        return new IOAuditResources(newResourcesMap);
206    }
207
208}