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 }else if(pageNb == 0){ 191 log.warn("Requested pageNb equals 0 but page index start at 1. Will fallback to fetch the first page"); 192 } 193 query.setMaxResults(pageSize); 194 return doPublish(query.getResultList()); 195 } 196 197 /* 198 * (non-Javadoc) 199 * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#nativeQuery(java .lang.String, int, int) 200 */ 201 public List<?> nativeQuery(String queryString, int pageNb, int pageSize) { 202 Query query = em.createQuery(queryString); 203 if (pageNb > 1) { 204 query.setFirstResult((pageNb - 1) * pageSize); 205 } 206 query.setMaxResults(pageSize); 207 return doPublishIfEntries(query.getResultList()); 208 } 209 210 /* 211 * (non-Javadoc) 212 * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#nativeQuery(java .lang.String, java.util.Map, int, 213 * int) 214 */ 215 public List<?> nativeQuery(String queryString, Map<String, Object> params, int pageNb, int pageSize) { 216 if (pageSize <= 0) { 217 pageSize = 1000; 218 } 219 Query query = em.createQuery(queryString); 220 for (Entry<String, Object> en : params.entrySet()) { 221 query.setParameter(en.getKey(), en.getValue()); 222 } 223 if (pageNb > 1) { 224 query.setFirstResult((pageNb - 1) * pageSize); 225 } 226 query.setMaxResults(pageSize); 227 return doPublishIfEntries(query.getResultList()); 228 } 229 230 /* 231 * (non-Javadoc) 232 * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#queryLogs(java. lang.String[], java.lang.String) 233 */ 234 @SuppressWarnings("unchecked") 235 public List<LogEntry> queryLogs(String[] eventIds, String dateRange) { 236 Date limit; 237 try { 238 limit = DateRangeParser.parseDateRangeQuery(new Date(), dateRange); 239 } catch (AuditQueryException aqe) { 240 aqe.addInfo("Wrong date range query. Query was " + dateRange); 241 throw aqe; 242 } 243 244 String queryStr = ""; 245 if (eventIds == null || eventIds.length == 0) { 246 queryStr = "from LogEntry log" + " where log.eventDate >= :limit" + " ORDER BY log.eventDate DESC"; 247 } else { 248 String inClause = "("; 249 for (String eventId : eventIds) { 250 inClause += "'" + eventId + "',"; 251 } 252 inClause = inClause.substring(0, inClause.length() - 1); 253 inClause += ")"; 254 255 queryStr = "from LogEntry log" + " where log.eventId in " + inClause + " AND log.eventDate >= :limit" 256 + " ORDER BY log.eventDate DESC"; 257 } 258 259 if (log.isDebugEnabled()) { 260 log.debug("queryLogs() =" + queryStr); 261 } 262 Query query = em.createQuery(queryStr); 263 query.setParameter("limit", limit); 264 265 return doPublish(query.getResultList()); 266 } 267 268 /* 269 * (non-Javadoc) 270 * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#queryLogsByPage (java.lang.String[], java.lang.String, 271 * java.lang.String[], java.lang.String, int, int) 272 */ 273 public List<LogEntry> queryLogsByPage(String[] eventIds, String dateRange, String[] categories, String path, 274 int pageNb, int pageSize) { 275 Date limit = null; 276 try { 277 limit = DateRangeParser.parseDateRangeQuery(new Date(), dateRange); 278 } catch (AuditQueryException aqe) { 279 aqe.addInfo("Wrong date range query. Query was " + dateRange); 280 throw aqe; 281 } 282 return queryLogsByPage(eventIds, limit, categories, path, pageNb, pageSize); 283 } 284 285 /* 286 * (non-Javadoc) 287 * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#queryLogsByPage (java.lang.String[], java.util.Date, 288 * java.lang.String[], java.lang.String, int, int) 289 */ 290 @SuppressWarnings("unchecked") 291 public List<LogEntry> queryLogsByPage(String[] eventIds, Date limit, String[] categories, String path, int pageNb, 292 int pageSize) { 293 if (eventIds == null) { 294 eventIds = new String[0]; 295 } 296 if (categories == null) { 297 categories = new String[0]; 298 } 299 300 StringBuilder queryString = new StringBuilder(); 301 302 queryString.append("from LogEntry log where "); 303 304 if (eventIds.length > 0) { 305 String inClause = "("; 306 for (String eventId : eventIds) { 307 inClause += "'" + eventId + "',"; 308 } 309 inClause = inClause.substring(0, inClause.length() - 1); 310 inClause += ")"; 311 312 queryString.append(" log.eventId IN ").append(inClause); 313 queryString.append(" AND "); 314 } 315 if (categories.length > 0) { 316 String inClause = "("; 317 for (String cat : categories) { 318 inClause += "'" + cat + "',"; 319 } 320 inClause = inClause.substring(0, inClause.length() - 1); 321 inClause += ")"; 322 queryString.append(" log.category IN ").append(inClause); 323 queryString.append(" AND "); 324 } 325 326 if (path != null && !"".equals(path.trim())) { 327 queryString.append(" log.docPath LIKE '").append(path).append("%'"); 328 queryString.append(" AND "); 329 } 330 331 queryString.append(" log.eventDate >= :limit"); 332 queryString.append(" ORDER BY log.eventDate DESC"); 333 334 Query query = em.createQuery(queryString.toString()); 335 336 query.setParameter("limit", limit); 337 338 if (pageNb > 1) { 339 query.setFirstResult((pageNb - 1) * pageSize); 340 } 341 query.setMaxResults(pageSize); 342 343 return doPublish(query.getResultList()); 344 } 345 346 /* 347 * (non-Javadoc) 348 * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#removeEntries(java .lang.String, java.lang.String) 349 */ 350 @Override 351 @SuppressWarnings("unchecked") 352 public int removeEntries(String eventId, String pathPattern) { 353 // TODO extended info cascade delete does not work using HQL, so we 354 // have to delete each 355 // entry by hand. 356 Query query = em.createNamedQuery("LogEntry.findByEventIdAndPath"); 357 query.setParameter("eventId", eventId); 358 query.setParameter("pathPattern", pathPattern + "%"); 359 int count = 0; 360 for (LogEntry entry : (List<LogEntry>) query.getResultList()) { 361 em.remove(entry); 362 count += 1; 363 } 364 if (log.isDebugEnabled()) { 365 log.debug("removed " + count + " entries from " + pathPattern); 366 } 367 return count; 368 } 369 370 /* 371 * (non-Javadoc) 372 * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#countEventsById (java.lang.String) 373 */ 374 public Long countEventsById(String eventId) { 375 Query query = em.createNamedQuery("LogEntry.countEventsById"); 376 query.setParameter("eventId", eventId); 377 return (Long) query.getSingleResult(); 378 } 379 380 /* 381 * (non-Javadoc) 382 * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#findEventIds() 383 */ 384 @SuppressWarnings("unchecked") 385 public List<String> findEventIds() { 386 Query query = em.createNamedQuery("LogEntry.findEventIds"); 387 return (List<String>) query.getResultList(); 388 } 389 390}