001/*
002 * (C) Copyright 2006-2008 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 *     Stephane Lacoin (Nuxeo EP Software Engineer)
016 */
017
018package org.nuxeo.ecm.platform.audit.service;
019
020import java.util.Date;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024import java.util.Map.Entry;
025import java.util.Set;
026
027import javax.persistence.EntityManager;
028import javax.persistence.Query;
029
030import org.apache.commons.logging.Log;
031import org.apache.commons.logging.LogFactory;
032import org.nuxeo.ecm.platform.audit.api.FilterMapEntry;
033import org.nuxeo.ecm.platform.audit.api.LogEntry;
034import org.nuxeo.ecm.platform.audit.api.query.AuditQueryException;
035import org.nuxeo.ecm.platform.audit.api.query.DateRangeParser;
036import org.nuxeo.ecm.platform.audit.impl.LogEntryImpl;
037
038public class LogEntryProvider implements BaseLogEntryProvider {
039
040    private static final Log log = LogFactory.getLog(LogEntryProvider.class);
041
042    protected final EntityManager em;
043
044    private LogEntryProvider(EntityManager em) {
045        this.em = em;
046    }
047
048    public static LogEntryProvider createProvider(EntityManager em) {
049        return new LogEntryProvider(em);
050    }
051
052    protected void doPersist(LogEntry entry) {
053        // Set the log date in java right before saving to the database. We
054        // cannot set a static column definition to
055        // "TIMESTAMP DEFAULT CURRENT_TIMESTAMP" as MS SQL Server does not
056        // support the TIMESTAMP column type and generating a dynamic
057        // persistence configuration that would depend on the database is too
058        // complicated.
059        entry.setLogDate(new Date());
060        em.persist(entry);
061    }
062
063    protected List<?> doPublishIfEntries(List<?> entries) {
064        if (entries == null || entries.size() == 0) {
065            return entries;
066        }
067        Object entry = entries.get(0);
068        if (entry instanceof LogEntry) {
069            for (Object logEntry : entries) {
070                doPublish((LogEntry) logEntry);
071            }
072        }
073        return entries;
074    }
075
076    protected List<LogEntry> doPublish(List<LogEntry> entries) {
077        for (LogEntry entry : entries) {
078            doPublish(entry);
079        }
080        return entries;
081    }
082
083    protected LogEntry doPublish(LogEntry entry) {
084        if (entry.getExtendedInfos() != null) {
085            entry.getExtendedInfos().size(); // force lazy loading
086        }
087        return entry;
088    }
089
090    /*
091     * (non-Javadoc)
092     * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#addLogEntry(org
093     * .nuxeo.ecm.platform.audit.api.LogEntry)
094     */
095    @Override
096    public void addLogEntry(LogEntry entry) {
097        doPersist(entry);
098    }
099
100    public void addLogEntries(List<LogEntry> entries) {
101        for (LogEntry entry : entries) {
102            doPersist(entry);
103        }
104    }
105
106    @SuppressWarnings("unchecked")
107    public List<LogEntry> getLogEntriesFor(String uuid) {
108        if (log.isDebugEnabled()) {
109            log.debug("getLogEntriesFor() UUID=" + uuid);
110        }
111        Query query = em.createNamedQuery("LogEntry.findByDocument");
112        query.setParameter("docUUID", uuid);
113        return doPublish(query.getResultList());
114    }
115
116    @SuppressWarnings("unchecked")
117    @Deprecated
118    public List<LogEntry> getLogEntriesFor(String uuid, Map<String, FilterMapEntry> filterMap, boolean doDefaultSort) {
119        if (log.isDebugEnabled()) {
120            log.debug("getLogEntriesFor() UUID=" + uuid);
121        }
122
123        if (filterMap == null) {
124            filterMap = new HashMap<String, FilterMapEntry>();
125        }
126
127        StringBuilder queryStr = new StringBuilder();
128        queryStr.append(" FROM LogEntry log WHERE log.docUUID=:uuid ");
129
130        Set<String> filterMapKeySet = filterMap.keySet();
131        for (String currentKey : filterMapKeySet) {
132            FilterMapEntry currentFilterMapEntry = filterMap.get(currentKey);
133            String currentOperator = currentFilterMapEntry.getOperator();
134            String currentQueryParameterName = currentFilterMapEntry.getQueryParameterName();
135            String currentColumnName = currentFilterMapEntry.getColumnName();
136
137            if ("LIKE".equals(currentOperator)) {
138                queryStr.append(" AND log.").append(currentColumnName).append(" LIKE :").append(
139                        currentQueryParameterName).append(" ");
140            } else {
141                queryStr.append(" AND log.").append(currentColumnName).append(currentOperator).append(":").append(
142                        currentQueryParameterName).append(" ");
143            }
144        }
145
146        if (doDefaultSort) {
147            queryStr.append(" ORDER BY log.eventDate DESC");
148        }
149
150        Query query = em.createQuery(queryStr.toString());
151
152        query.setParameter("uuid", uuid);
153
154        for (String currentKey : filterMapKeySet) {
155            FilterMapEntry currentFilterMapEntry = filterMap.get(currentKey);
156            String currentOperator = currentFilterMapEntry.getOperator();
157            String currentQueryParameterName = currentFilterMapEntry.getQueryParameterName();
158            Object currentObject = currentFilterMapEntry.getObject();
159
160            if ("LIKE".equals(currentOperator)) {
161                query.setParameter(currentQueryParameterName, "%" + currentObject + "%");
162            } else {
163                query.setParameter(currentQueryParameterName, currentObject);
164            }
165        }
166
167        return doPublish(query.getResultList());
168    }
169
170    /*
171     * (non-Javadoc)
172     * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#getLogEntryByID (long)
173     */
174    public LogEntry getLogEntryByID(long id) {
175        if (log.isDebugEnabled()) {
176            log.debug("getLogEntriesFor() logID=" + id);
177        }
178        return doPublish(em.find(LogEntryImpl.class, id));
179    }
180
181    /*
182     * (non-Javadoc)
183     * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#nativeQueryLogs (java.lang.String, int, int)
184     */
185    @SuppressWarnings("unchecked")
186    public List<LogEntry> nativeQueryLogs(String whereClause, int pageNb, int pageSize) {
187        Query query = em.createQuery("from LogEntry log where " + whereClause);
188        if (pageNb > 1) {
189            query.setFirstResult((pageNb - 1) * pageSize);
190        }
191        query.setMaxResults(pageSize);
192        return doPublish(query.getResultList());
193    }
194
195    /*
196     * (non-Javadoc)
197     * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#nativeQuery(java .lang.String, int, int)
198     */
199    public List<?> nativeQuery(String queryString, int pageNb, int pageSize) {
200        Query query = em.createQuery(queryString);
201        if (pageNb > 1) {
202            query.setFirstResult((pageNb - 1) * pageSize);
203        }
204        query.setMaxResults(pageSize);
205        return doPublishIfEntries(query.getResultList());
206    }
207
208    /*
209     * (non-Javadoc)
210     * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#nativeQuery(java .lang.String, java.util.Map, int,
211     * int)
212     */
213    public List<?> nativeQuery(String queryString, Map<String, Object> params, int pageNb, int pageSize) {
214        if (pageSize <= 0) {
215            pageSize = 1000;
216        }
217        Query query = em.createQuery(queryString);
218        for (Entry<String, Object> en : params.entrySet()) {
219            query.setParameter(en.getKey(), en.getValue());
220        }
221        if (pageNb > 1) {
222            query.setFirstResult((pageNb - 1) * pageSize);
223        }
224        query.setMaxResults(pageSize);
225        return doPublishIfEntries(query.getResultList());
226    }
227
228    /*
229     * (non-Javadoc)
230     * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#queryLogs(java. lang.String[], java.lang.String)
231     */
232    @SuppressWarnings("unchecked")
233    public List<LogEntry> queryLogs(String[] eventIds, String dateRange) {
234        Date limit;
235        try {
236            limit = DateRangeParser.parseDateRangeQuery(new Date(), dateRange);
237        } catch (AuditQueryException aqe) {
238            aqe.addInfo("Wrong date range query. Query was " + dateRange);
239            throw aqe;
240        }
241
242        String queryStr = "";
243        if (eventIds == null || eventIds.length == 0) {
244            queryStr = "from LogEntry log" + " where log.eventDate >= :limit" + " ORDER BY log.eventDate DESC";
245        } else {
246            String inClause = "(";
247            for (String eventId : eventIds) {
248                inClause += "'" + eventId + "',";
249            }
250            inClause = inClause.substring(0, inClause.length() - 1);
251            inClause += ")";
252
253            queryStr = "from LogEntry log" + " where log.eventId in " + inClause + " AND log.eventDate >= :limit"
254                    + " ORDER BY log.eventDate DESC";
255        }
256
257        if (log.isDebugEnabled()) {
258            log.debug("queryLogs() =" + queryStr);
259        }
260        Query query = em.createQuery(queryStr);
261        query.setParameter("limit", limit);
262
263        return doPublish(query.getResultList());
264    }
265
266    /*
267     * (non-Javadoc)
268     * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#queryLogsByPage (java.lang.String[], java.lang.String,
269     * java.lang.String[], java.lang.String, int, int)
270     */
271    public List<LogEntry> queryLogsByPage(String[] eventIds, String dateRange, String[] categories, String path,
272            int pageNb, int pageSize) {
273        Date limit = null;
274        try {
275            limit = DateRangeParser.parseDateRangeQuery(new Date(), dateRange);
276        } catch (AuditQueryException aqe) {
277            aqe.addInfo("Wrong date range query. Query was " + dateRange);
278            throw aqe;
279        }
280        return queryLogsByPage(eventIds, limit, categories, path, pageNb, pageSize);
281    }
282
283    /*
284     * (non-Javadoc)
285     * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#queryLogsByPage (java.lang.String[], java.util.Date,
286     * java.lang.String[], java.lang.String, int, int)
287     */
288    @SuppressWarnings("unchecked")
289    public List<LogEntry> queryLogsByPage(String[] eventIds, Date limit, String[] categories, String path, int pageNb,
290            int pageSize) {
291        if (eventIds == null) {
292            eventIds = new String[0];
293        }
294        if (categories == null) {
295            categories = new String[0];
296        }
297
298        StringBuilder queryString = new StringBuilder();
299
300        queryString.append("from LogEntry log where ");
301
302        if (eventIds.length > 0) {
303            String inClause = "(";
304            for (String eventId : eventIds) {
305                inClause += "'" + eventId + "',";
306            }
307            inClause = inClause.substring(0, inClause.length() - 1);
308            inClause += ")";
309
310            queryString.append(" log.eventId IN ").append(inClause);
311            queryString.append(" AND ");
312        }
313        if (categories.length > 0) {
314            String inClause = "(";
315            for (String cat : categories) {
316                inClause += "'" + cat + "',";
317            }
318            inClause = inClause.substring(0, inClause.length() - 1);
319            inClause += ")";
320            queryString.append(" log.category IN ").append(inClause);
321            queryString.append(" AND ");
322        }
323
324        if (path != null && !"".equals(path.trim())) {
325            queryString.append(" log.docPath LIKE '").append(path).append("%'");
326            queryString.append(" AND ");
327        }
328
329        queryString.append(" log.eventDate >= :limit");
330        queryString.append(" ORDER BY log.eventDate DESC");
331
332        Query query = em.createQuery(queryString.toString());
333
334        query.setParameter("limit", limit);
335
336        if (pageNb > 1) {
337            query.setFirstResult((pageNb - 1) * pageSize);
338        }
339        query.setMaxResults(pageSize);
340
341        return doPublish(query.getResultList());
342    }
343
344    /*
345     * (non-Javadoc)
346     * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#removeEntries(java .lang.String, java.lang.String)
347     */
348    @Override
349    @SuppressWarnings("unchecked")
350    public int removeEntries(String eventId, String pathPattern) {
351        // TODO extended info cascade delete does not work using HQL, so we
352        // have to delete each
353        // entry by hand.
354        Query query = em.createNamedQuery("LogEntry.findByEventIdAndPath");
355        query.setParameter("eventId", eventId);
356        query.setParameter("pathPattern", pathPattern + "%");
357        int count = 0;
358        for (LogEntry entry : (List<LogEntry>) query.getResultList()) {
359            em.remove(entry);
360            count += 1;
361        }
362        if (log.isDebugEnabled()) {
363            log.debug("removed " + count + " entries from " + pathPattern);
364        }
365        return count;
366    }
367
368    /*
369     * (non-Javadoc)
370     * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#countEventsById (java.lang.String)
371     */
372    public Long countEventsById(String eventId) {
373        Query query = em.createNamedQuery("LogEntry.countEventsById");
374        query.setParameter("eventId", eventId);
375        return (Long) query.getSingleResult();
376    }
377
378    /*
379     * (non-Javadoc)
380     * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#findEventIds()
381     */
382    @SuppressWarnings("unchecked")
383    public List<String> findEventIds() {
384        Query query = em.createNamedQuery("LogEntry.findEventIds");
385        return (List<String>) query.getResultList();
386    }
387
388}