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