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