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 public List<LogEntry> getLogEntriesFor(String uuid) { 110 if (log.isDebugEnabled()) { 111 log.debug("getLogEntriesFor() UUID=" + uuid); 112 } 113 Query query = em.createNamedQuery("LogEntry.findByDocument"); 114 query.setParameter("docUUID", uuid); 115 return doPublish(query.getResultList()); 116 } 117 118 @SuppressWarnings("unchecked") 119 @Deprecated 120 public List<LogEntry> getLogEntriesFor(String uuid, Map<String, FilterMapEntry> filterMap, boolean doDefaultSort) { 121 if (log.isDebugEnabled()) { 122 log.debug("getLogEntriesFor() UUID=" + uuid); 123 } 124 125 if (filterMap == null) { 126 filterMap = new HashMap<String, FilterMapEntry>(); 127 } 128 129 StringBuilder queryStr = new StringBuilder(); 130 queryStr.append(" FROM LogEntry log WHERE log.docUUID=:uuid "); 131 132 Set<String> filterMapKeySet = filterMap.keySet(); 133 for (String currentKey : filterMapKeySet) { 134 FilterMapEntry currentFilterMapEntry = filterMap.get(currentKey); 135 String currentOperator = currentFilterMapEntry.getOperator(); 136 String currentQueryParameterName = currentFilterMapEntry.getQueryParameterName(); 137 String currentColumnName = currentFilterMapEntry.getColumnName(); 138 139 if ("LIKE".equals(currentOperator)) { 140 queryStr.append(" AND log.").append(currentColumnName).append(" LIKE :").append( 141 currentQueryParameterName).append(" "); 142 } else { 143 queryStr.append(" AND log.").append(currentColumnName).append(currentOperator).append(":").append( 144 currentQueryParameterName).append(" "); 145 } 146 } 147 148 if (doDefaultSort) { 149 queryStr.append(" ORDER BY log.eventDate DESC"); 150 } 151 152 Query query = em.createQuery(queryStr.toString()); 153 154 query.setParameter("uuid", uuid); 155 156 for (String currentKey : filterMapKeySet) { 157 FilterMapEntry currentFilterMapEntry = filterMap.get(currentKey); 158 String currentOperator = currentFilterMapEntry.getOperator(); 159 String currentQueryParameterName = currentFilterMapEntry.getQueryParameterName(); 160 Object currentObject = currentFilterMapEntry.getObject(); 161 162 if ("LIKE".equals(currentOperator)) { 163 query.setParameter(currentQueryParameterName, "%" + currentObject + "%"); 164 } else { 165 query.setParameter(currentQueryParameterName, currentObject); 166 } 167 } 168 169 return doPublish(query.getResultList()); 170 } 171 172 /* 173 * (non-Javadoc) 174 * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#getLogEntryByID (long) 175 */ 176 public LogEntry getLogEntryByID(long id) { 177 if (log.isDebugEnabled()) { 178 log.debug("getLogEntriesFor() logID=" + id); 179 } 180 return doPublish(em.find(LogEntryImpl.class, id)); 181 } 182 183 /* 184 * (non-Javadoc) 185 * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#nativeQueryLogs (java.lang.String, int, int) 186 */ 187 @SuppressWarnings("unchecked") 188 public List<LogEntry> nativeQueryLogs(String whereClause, int pageNb, int pageSize) { 189 Query query = em.createQuery("from LogEntry log where " + whereClause); 190 if (pageNb > 1) { 191 query.setFirstResult((pageNb - 1) * pageSize); 192 }else if(pageNb == 0){ 193 log.warn("Requested pageNb equals 0 but page index start at 1. Will fallback to fetch the first page"); 194 } 195 query.setMaxResults(pageSize); 196 return doPublish(query.getResultList()); 197 } 198 199 /* 200 * (non-Javadoc) 201 * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#nativeQuery(java .lang.String, int, int) 202 */ 203 public List<?> nativeQuery(String queryString, int pageNb, int pageSize) { 204 Query query = em.createQuery(queryString); 205 if (pageNb > 1) { 206 query.setFirstResult((pageNb - 1) * pageSize); 207 } 208 query.setMaxResults(pageSize); 209 return doPublishIfEntries(query.getResultList()); 210 } 211 212 /* 213 * (non-Javadoc) 214 * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#nativeQuery(java .lang.String, java.util.Map, int, 215 * int) 216 */ 217 public List<?> nativeQuery(String queryString, Map<String, Object> params, int pageNb, int pageSize) { 218 if (pageSize <= 0) { 219 pageSize = 1000; 220 } 221 Query query = em.createQuery(queryString); 222 for (Entry<String, Object> en : params.entrySet()) { 223 query.setParameter(en.getKey(), en.getValue()); 224 } 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#queryLogs(java. lang.String[], java.lang.String) 235 */ 236 @SuppressWarnings("unchecked") 237 public List<LogEntry> queryLogs(String[] eventIds, String dateRange) { 238 Date limit; 239 try { 240 limit = DateRangeParser.parseDateRangeQuery(new Date(), dateRange); 241 } catch (AuditQueryException aqe) { 242 aqe.addInfo("Wrong date range query. Query was " + dateRange); 243 throw aqe; 244 } 245 246 String queryStr = ""; 247 if (eventIds == null || eventIds.length == 0) { 248 queryStr = "from LogEntry log" + " where log.eventDate >= :limit" + " ORDER BY log.eventDate DESC"; 249 } else { 250 String inClause = "("; 251 for (String eventId : eventIds) { 252 inClause += "'" + eventId + "',"; 253 } 254 inClause = inClause.substring(0, inClause.length() - 1); 255 inClause += ")"; 256 257 queryStr = "from LogEntry log" + " where log.eventId in " + inClause + " AND log.eventDate >= :limit" 258 + " ORDER BY log.eventDate DESC"; 259 } 260 261 if (log.isDebugEnabled()) { 262 log.debug("queryLogs() =" + queryStr); 263 } 264 Query query = em.createQuery(queryStr); 265 query.setParameter("limit", limit); 266 267 return doPublish(query.getResultList()); 268 } 269 270 /* 271 * (non-Javadoc) 272 * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#queryLogsByPage (java.lang.String[], java.lang.String, 273 * java.lang.String[], java.lang.String, int, int) 274 */ 275 public List<LogEntry> queryLogsByPage(String[] eventIds, String dateRange, String[] categories, String path, 276 int pageNb, int pageSize) { 277 Date limit = null; 278 try { 279 limit = DateRangeParser.parseDateRangeQuery(new Date(), dateRange); 280 } catch (AuditQueryException aqe) { 281 aqe.addInfo("Wrong date range query. Query was " + dateRange); 282 throw aqe; 283 } 284 return queryLogsByPage(eventIds, limit, categories, path, pageNb, pageSize); 285 } 286 287 /* 288 * (non-Javadoc) 289 * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#queryLogsByPage (java.lang.String[], java.util.Date, 290 * java.lang.String[], java.lang.String, int, int) 291 */ 292 @SuppressWarnings("unchecked") 293 public List<LogEntry> queryLogsByPage(String[] eventIds, Date limit, String[] categories, String path, int pageNb, 294 int pageSize) { 295 if (eventIds == null) { 296 eventIds = new String[0]; 297 } 298 if (categories == null) { 299 categories = new String[0]; 300 } 301 302 StringBuilder queryString = new StringBuilder(); 303 304 queryString.append("from LogEntry log where "); 305 306 if (eventIds.length > 0) { 307 String inClause = "("; 308 for (String eventId : eventIds) { 309 inClause += "'" + eventId + "',"; 310 } 311 inClause = inClause.substring(0, inClause.length() - 1); 312 inClause += ")"; 313 314 queryString.append(" log.eventId IN ").append(inClause); 315 queryString.append(" AND "); 316 } 317 if (categories.length > 0) { 318 String inClause = "("; 319 for (String cat : categories) { 320 inClause += "'" + cat + "',"; 321 } 322 inClause = inClause.substring(0, inClause.length() - 1); 323 inClause += ")"; 324 queryString.append(" log.category IN ").append(inClause); 325 queryString.append(" AND "); 326 } 327 328 if (path != null && !"".equals(path.trim())) { 329 queryString.append(" log.docPath LIKE '").append(path).append("%'"); 330 queryString.append(" AND "); 331 } 332 333 queryString.append(" log.eventDate >= :limit"); 334 queryString.append(" ORDER BY log.eventDate DESC"); 335 336 Query query = em.createQuery(queryString.toString()); 337 338 query.setParameter("limit", limit); 339 340 if (pageNb > 1) { 341 query.setFirstResult((pageNb - 1) * pageSize); 342 } 343 query.setMaxResults(pageSize); 344 345 return doPublish(query.getResultList()); 346 } 347 348 /* 349 * (non-Javadoc) 350 * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#removeEntries(java .lang.String, java.lang.String) 351 */ 352 @Override 353 @SuppressWarnings("unchecked") 354 public int removeEntries(String eventId, String pathPattern) { 355 // TODO extended info cascade delete does not work using HQL, so we 356 // have to delete each 357 // entry by hand. 358 Query query = em.createNamedQuery("LogEntry.findByEventIdAndPath"); 359 query.setParameter("eventId", eventId); 360 query.setParameter("pathPattern", pathPattern + "%"); 361 int count = 0; 362 for (LogEntry entry : (List<LogEntry>) query.getResultList()) { 363 em.remove(entry); 364 count += 1; 365 } 366 if (log.isDebugEnabled()) { 367 log.debug("removed " + count + " entries from " + pathPattern); 368 } 369 return count; 370 } 371 372 /* 373 * (non-Javadoc) 374 * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#countEventsById (java.lang.String) 375 */ 376 public Long countEventsById(String eventId) { 377 Query query = em.createNamedQuery("LogEntry.countEventsById"); 378 query.setParameter("eventId", eventId); 379 return (Long) query.getSingleResult(); 380 } 381 382 /* 383 * (non-Javadoc) 384 * @see org.nuxeo.ecm.platform.audit.service.LogEntryProvider#findEventIds() 385 */ 386 @SuppressWarnings("unchecked") 387 public List<String> findEventIds() { 388 Query query = em.createNamedQuery("LogEntry.findEventIds"); 389 return (List<String>) query.getResultList(); 390 } 391 392}