001/*
002 * Copyright (c) 2006-2012 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *     Nuxeo - initial API and implementation
011 *
012 */
013package org.nuxeo.ecm.platform.audit.api.document;
014
015import java.util.Calendar;
016import java.util.GregorianCalendar;
017import java.util.HashMap;
018import java.util.List;
019import java.util.Map;
020
021import org.nuxeo.ecm.core.api.CoreSession;
022import org.nuxeo.ecm.core.api.DocumentModel;
023import org.nuxeo.ecm.core.api.DocumentModelList;
024import org.nuxeo.ecm.core.api.IdRef;
025import org.nuxeo.ecm.core.api.event.DocumentEventTypes;
026import org.nuxeo.ecm.platform.audit.api.AuditReader;
027import org.nuxeo.ecm.platform.audit.api.FilterMapEntry;
028import org.nuxeo.ecm.platform.audit.api.LogEntry;
029import org.nuxeo.runtime.api.Framework;
030
031/**
032 * Audit log stores event related to the "live" DocumentModel. This means that when retrieving the Audit Log for a
033 * version or a proxy, we must merge part of the "live" document history with the history of the proxy or version. This
034 * helper class fetches the additional parameters that must be used to retrieve history of a version or of a proxy.
035 *
036 * @author <a href="mailto:tdelprat@nuxeo.com">Tiry</a>
037 */
038public class DocumentAuditHelper {
039
040    @SuppressWarnings({ "unchecked", "boxing" })
041    public static AdditionalDocumentAuditParams getAuditParamsForUUID(String uuid, CoreSession session) {
042
043        IdRef ref = new IdRef(uuid);
044        if (!session.exists(ref)) {
045            return null;
046        }
047        DocumentModel doc = session.getDocument(ref);
048        if (!doc.isProxy() && !doc.isVersion()) {
049            return null;
050        }
051        SourceDocumentResolver resolver = new SourceDocumentResolver(session, doc);
052        resolver.runUnrestricted();
053        if (resolver.sourceDocument == null) {
054            return null;
055        }
056        String targetUUID = resolver.sourceDocument.getId();
057        // now get from Audit Logs the creation date of
058        // the version / proxy
059        AuditReader reader = Framework.getLocalService(AuditReader.class);
060        FilterMapEntry filter = new FilterMapEntry();
061        filter.setColumnName("eventId");
062        filter.setOperator("=");
063        filter.setQueryParameterName("eventId");
064        filter.setObject(DocumentEventTypes.DOCUMENT_CREATED);
065        Map<String, FilterMapEntry> filters = new HashMap<String, FilterMapEntry>();
066        filters.put("eventId", filter);
067        List<LogEntry> entries = reader.getLogEntriesFor(uuid, filters, false);
068        AdditionalDocumentAuditParams result;
069        if (entries != null && entries.size() > 0) {
070            result = new AdditionalDocumentAuditParams();
071            result.maxDate = entries.get(0).getEventDate();
072            result.targetUUID = targetUUID;
073            result.eventId = entries.get(0).getId();
074        } else {
075            // we have no entry in audit log to get the maxDate
076            // fallback to repository timestamp
077            // this code is here only for compatibility so that it works before version events were added to
078            // the audit log
079            if (doc.getPropertyValue("dc:modified") == null) {
080                return null;
081            }
082            result = new AdditionalDocumentAuditParams();
083            Calendar estimatedDate = ((Calendar) doc.getPropertyValue("dc:modified"));
084
085            // We can not directly use the repo timestamp because Audit and VCS can be in separated DB
086            // => try to find the matching TS in Audit
087            StringBuilder queryString = new StringBuilder();
088            queryString.append("from LogEntry log where log.docUUID in (");
089            queryString.append("'" + targetUUID + "'");
090            if (doc.isVersion()) {
091                DocumentModelList proxies = session.getProxies(doc.getRef(), null);
092                for (DocumentModel proxy : proxies) {
093                    queryString.append(",'" + proxy.getId() + "'");
094                }
095            }
096            queryString.append(",'" + doc.getId() + "'");
097            queryString.append(") AND log.eventId IN (");
098            queryString.append("'" + DocumentEventTypes.DOCUMENT_CREATED + "'");
099            queryString.append(",'" + DocumentEventTypes.DOCUMENT_CHECKEDIN + "'");
100            queryString.append(") AND log.eventDate >= :minDate ");
101            queryString.append(" order by log.eventId asc");
102
103            estimatedDate.add(Calendar.MILLISECOND, -500);
104            Map<String, Object> params = new HashMap<String, Object>();
105            params.put("minDate", estimatedDate.getTime());
106
107            List<LogEntry> dateEntries = (List<LogEntry>) reader.nativeQuery(queryString.toString(),
108                    params, 0, 20);
109            if (dateEntries.size() > 0) {
110                result.targetUUID = targetUUID;
111                Calendar maxDate = new GregorianCalendar();
112                maxDate.setTime(dateEntries.get(0).getEventDate());
113                maxDate.add(Calendar.MILLISECOND, -500);
114                result.maxDate = maxDate.getTime();
115            } else {
116                // no other choice : use the VCS TS
117                // results may be truncated in some DB config
118                result.targetUUID = targetUUID;
119                result.maxDate = ((Calendar) doc.getPropertyValue("dc:modified")).getTime();
120            }
121        }
122        return result;
123    }
124
125}