001/* 002 * Copyright (c) 2006-2011 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 * $Id$ 013 */ 014package org.nuxeo.ecm.platform.audit.api; 015 016import java.io.Serializable; 017import java.sql.Timestamp; 018import java.util.ArrayList; 019import java.util.Calendar; 020import java.util.Collections; 021import java.util.HashMap; 022import java.util.Iterator; 023import java.util.List; 024import java.util.Map; 025 026import org.nuxeo.ecm.core.api.CoreSession; 027import org.nuxeo.ecm.core.api.DocumentModel; 028import org.nuxeo.ecm.core.api.SortInfo; 029import org.nuxeo.ecm.platform.audit.api.comment.CommentProcessorHelper; 030import org.nuxeo.ecm.platform.query.api.AbstractPageProvider; 031import org.nuxeo.ecm.platform.query.api.PageProvider; 032import org.nuxeo.ecm.platform.query.api.PageProviderDefinition; 033import org.nuxeo.ecm.platform.query.api.PredicateDefinition; 034import org.nuxeo.ecm.platform.query.api.PredicateFieldDefinition; 035import org.nuxeo.runtime.api.Framework; 036 037/** 038 * {@link PageProvider} implementation that returns {@link LogEntry} from Audit Service 039 * 040 * @author Tiry (tdelprat@nuxeo.com) 041 * @since 5.4.2 042 */ 043public class AuditPageProvider extends AbstractPageProvider<LogEntry> implements PageProvider<LogEntry> { 044 045 private static final long serialVersionUID = 1L; 046 047 protected String auditQuery; 048 049 protected Map<String, Object> auditQueryParams; 050 051 public static final String CORE_SESSION_PROPERTY = "coreSession"; 052 053 public static final String UICOMMENTS_PROPERTY = "generateUIComments"; 054 055 public String toString() { 056 buildAuditQuery(true); 057 StringBuffer sb = new StringBuffer(); 058 059 sb.append("\nquery : " + auditQuery); 060 sb.append("\nparams : "); 061 List<String> pNames = new ArrayList<String>(auditQueryParams.keySet()); 062 Collections.sort(pNames); 063 for (String name : pNames) { 064 sb.append("\n "); 065 sb.append(name); 066 sb.append(" : "); 067 sb.append(auditQueryParams.get(name).toString()); 068 } 069 070 return sb.toString(); 071 } 072 073 protected void preprocessCommentsIfNeeded(List<LogEntry> entries) { 074 Serializable preprocess = getProperties().get(UICOMMENTS_PROPERTY); 075 CoreSession session = (CoreSession) getProperties().get(CORE_SESSION_PROPERTY); 076 if (session != null && preprocess != null && "true".equalsIgnoreCase(preprocess.toString())) { 077 CommentProcessorHelper cph = new CommentProcessorHelper(session); 078 cph.processComments(entries); 079 } 080 } 081 082 @SuppressWarnings("unchecked") 083 @Override 084 public List<LogEntry> getCurrentPage() { 085 AuditReader reader = Framework.getService(AuditReader.class); 086 buildAuditQuery(true); 087 List<LogEntry> entries = (List<LogEntry>) reader.nativeQuery(auditQuery, auditQueryParams, 088 (int) getCurrentPageIndex() + 1, (int) getMinMaxPageSize()); 089 preprocessCommentsIfNeeded(entries); 090 return entries; 091 } 092 093 protected String getSortPart() { 094 StringBuffer sort = new StringBuffer(); 095 if (getSortInfos() != null && getSortInfos().size() > 0) { 096 sort.append(" ORDER BY "); 097 } 098 int index = 0; 099 for (SortInfo si : getSortInfos()) { 100 if (index > 0) { 101 sort.append(" , "); 102 } 103 sort.append(si.getSortColumn()); 104 if (si.getSortAscending()) { 105 sort.append(" ASC "); 106 } else { 107 sort.append(" DESC "); 108 } 109 index++; 110 } 111 return sort.toString(); 112 } 113 114 protected Object convertParam(Object param) { 115 if (param == null) { 116 return null; 117 } 118 // Hibernate does not like Calendar type 119 if (param instanceof Calendar) { 120 return new Timestamp(((Calendar) param).getTime().getTime()); 121 } 122 return param; 123 } 124 125 protected boolean isNonNullParam(Object[] val) { 126 if (val == null) { 127 return false; 128 } 129 for (Object v : val) { 130 if (v != null) { 131 if (v instanceof String) { 132 if (!((String) v).isEmpty()) { 133 return true; 134 } 135 } else if (v instanceof String[]) { 136 if (((String[]) v).length > 0) { 137 return true; 138 } 139 } else { 140 return true; 141 } 142 } 143 } 144 return false; 145 } 146 147 protected String getFixedPart() { 148 if (getDefinition().getWhereClause() == null) { 149 return null; 150 } else { 151 return getDefinition().getWhereClause().getFixedPart(); 152 } 153 } 154 155 protected boolean allowSimplePattern() { 156 return true; 157 } 158 159 protected void buildAuditQuery(boolean includeSort) { 160 PageProviderDefinition def = getDefinition(); 161 Object[] params = getParameters(); 162 163 if (def.getWhereClause() == null) { 164 // Simple Pattern 165 166 if (!allowSimplePattern()) { 167 throw new UnsupportedOperationException("This page provider requires a explicit Where Clause"); 168 } 169 170 String baseQuery = def.getPattern(); 171 172 Map<String, Object> qParams = new HashMap<String, Object>(); 173 for (int i = 0; i < params.length; i++) { 174 baseQuery = baseQuery.replaceFirst("\\?", ":param" + i); 175 qParams.put("param" + i, convertParam(params[i])); 176 } 177 178 if (includeSort) { 179 baseQuery = baseQuery + getSortPart(); 180 } 181 182 auditQuery = baseQuery; 183 auditQueryParams = qParams; 184 185 } else { 186 // Where clause based on DocumentModel 187 188 StringBuilder baseQuery = new StringBuilder("from LogEntry log "); 189 190 // manage fixed part 191 String fixedPart = getFixedPart(); 192 Map<String, Object> qParams = new HashMap<String, Object>(); 193 int idxParam = 0; 194 if (fixedPart != null && !fixedPart.isEmpty()) { 195 while (fixedPart.indexOf("?") > 0) { 196 fixedPart = fixedPart.replaceFirst("\\?", ":param" + idxParam); 197 qParams.put("param" + idxParam, convertParam(params[idxParam])); 198 idxParam++; 199 } 200 baseQuery.append(" where "); 201 baseQuery.append(fixedPart); 202 } 203 204 // manages predicates 205 DocumentModel searchDocumentModel = getSearchDocumentModel(); 206 if (searchDocumentModel != null) { 207 PredicateDefinition[] predicates = def.getWhereClause().getPredicates(); 208 int idxPredicate = 0; 209 210 for (PredicateDefinition predicate : predicates) { 211 212 // extract data from DocumentModel 213 PredicateFieldDefinition[] fieldDef = predicate.getValues(); 214 Object[] val = new Object[fieldDef.length]; 215 for (int fidx = 0; fidx < fieldDef.length; fidx++) { 216 if (fieldDef[fidx].getXpath() != null) { 217 val[fidx] = searchDocumentModel.getPropertyValue(fieldDef[fidx].getXpath()); 218 } else { 219 val[fidx] = searchDocumentModel.getProperty(fieldDef[fidx].getSchema(), 220 fieldDef[fidx].getName()); 221 } 222 } 223 224 if (!isNonNullParam(val)) { 225 // skip predicate where all values are null 226 continue; 227 } 228 229 if (idxPredicate > 0 || idxParam > 0) { 230 baseQuery.append(" AND "); 231 } else { 232 baseQuery.append(" where "); 233 } 234 235 baseQuery.append(predicate.getParameter()); 236 baseQuery.append(" "); 237 238 if (!predicate.getOperator().equalsIgnoreCase("BETWEEN")) { 239 // don't add the between operation for now 240 baseQuery.append(predicate.getOperator()); 241 } 242 243 if (predicate.getOperator().equalsIgnoreCase("IN")) { 244 baseQuery.append(" ("); 245 246 if (val[0] instanceof Iterable<?>) { 247 Iterable<?> vals = (Iterable<?>) val[0]; 248 Iterator<?> valueIterator = vals.iterator(); 249 250 while (valueIterator.hasNext()) { 251 Object v = valueIterator.next(); 252 qParams.put("param" + idxParam, convertParam(v)); 253 baseQuery.append(" :param" + idxParam); 254 idxParam++; 255 if (valueIterator.hasNext()) { 256 baseQuery.append(","); 257 } 258 } 259 } else if (val[0] instanceof Object[]) { 260 Object[] valArray = (Object[]) val[0]; 261 for (int i = 0; i < valArray.length; i++) { 262 Object v = valArray[i]; 263 qParams.put("param" + idxParam, convertParam(v)); 264 baseQuery.append(" :param" + idxParam); 265 idxParam++; 266 if (i < valArray.length - 1) { 267 baseQuery.append(","); 268 } 269 } 270 } 271 baseQuery.append(" ) "); 272 } else if (predicate.getOperator().equalsIgnoreCase("BETWEEN")) { 273 Object startValue = convertParam(val[0]); 274 Object endValue = null; 275 if (val.length > 1) { 276 endValue = convertParam(val[1]); 277 } 278 if (startValue != null && endValue != null) { 279 baseQuery.append(predicate.getOperator()); 280 baseQuery.append(" :param" + idxParam); 281 qParams.put("param" + idxParam, startValue); 282 idxParam++; 283 baseQuery.append(" AND :param" + idxParam); 284 qParams.put("param" + idxParam, endValue); 285 } else if (startValue == null) { 286 baseQuery.append("<="); 287 baseQuery.append(" :param" + idxParam); 288 qParams.put("param" + idxParam, endValue); 289 } else if (endValue == null) { 290 baseQuery.append(">="); 291 baseQuery.append(" :param" + idxParam); 292 qParams.put("param" + idxParam, startValue); 293 } 294 idxParam++; 295 } else { 296 baseQuery.append(" :param" + idxParam); 297 qParams.put("param" + idxParam, convertParam(val[0])); 298 idxParam++; 299 } 300 301 idxPredicate++; 302 } 303 } 304 305 if (includeSort) { 306 baseQuery.append(getSortPart()); 307 } 308 309 auditQuery = baseQuery.toString(); 310 auditQueryParams = qParams; 311 } 312 } 313 314 public void refresh() { 315 setCurrentPageOffset(0); 316 super.refresh(); 317 } 318 319 @SuppressWarnings("unchecked") 320 @Override 321 public long getResultsCount() { 322 if (resultsCount == AbstractPageProvider.UNKNOWN_SIZE) { 323 buildAuditQuery(false); 324 AuditReader reader = Framework.getService(AuditReader.class); 325 List<Long> res = (List<Long>) reader.nativeQuery("select count(log.id) " + auditQuery, auditQueryParams, 1, 326 20); 327 resultsCount = res.get(0).longValue(); 328 } 329 return resultsCount; 330 } 331}