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