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.elasticsearch.audit.pageprovider; 015 016import java.io.IOException; 017import java.io.Serializable; 018import java.util.ArrayList; 019import java.util.List; 020 021import org.elasticsearch.action.search.SearchRequestBuilder; 022import org.elasticsearch.action.search.SearchResponse; 023import org.elasticsearch.search.SearchHit; 024import org.elasticsearch.search.SearchHits; 025import org.elasticsearch.search.sort.SortOrder; 026import org.nuxeo.ecm.core.api.CoreSession; 027import org.nuxeo.ecm.core.api.NuxeoException; 028import org.nuxeo.ecm.core.api.SortInfo; 029import org.nuxeo.ecm.platform.audit.api.LogEntry; 030import org.nuxeo.ecm.platform.audit.api.comment.CommentProcessorHelper; 031import org.nuxeo.ecm.platform.audit.service.AuditBackend; 032import org.nuxeo.ecm.platform.audit.service.NXAuditEventsService; 033import org.nuxeo.ecm.platform.query.api.AbstractPageProvider; 034import org.nuxeo.ecm.platform.query.api.PageProvider; 035import org.nuxeo.ecm.platform.query.api.PageProviderDefinition; 036import org.nuxeo.elasticsearch.audit.ESAuditBackend; 037import org.nuxeo.elasticsearch.audit.io.AuditEntryJSONReader; 038import org.nuxeo.runtime.api.Framework; 039 040public class ESAuditPageProvider extends AbstractPageProvider<LogEntry> implements PageProvider<LogEntry> { 041 042 private static final long serialVersionUID = 1L; 043 044 protected SearchRequestBuilder searchBuilder; 045 046 public static final String CORE_SESSION_PROPERTY = "coreSession"; 047 048 public static final String UICOMMENTS_PROPERTY = "generateUIComments"; 049 050 protected static String emptyQuery = "{ \"match_all\" : { }\n }"; 051 052 @Override 053 public String toString() { 054 buildAuditQuery(true); 055 StringBuffer sb = new StringBuffer(); 056 sb.append(searchBuilder.toString()); 057 return sb.toString(); 058 } 059 060 protected CoreSession getCoreSession() { 061 Object session = getProperties().get(CORE_SESSION_PROPERTY); 062 if (session != null && session instanceof CoreSession) { 063 return (CoreSession) session; 064 } 065 return null; 066 } 067 068 protected void preprocessCommentsIfNeeded(List<LogEntry> entries) { 069 Serializable preprocess = getProperties().get(UICOMMENTS_PROPERTY); 070 071 if (preprocess != null && "true".equalsIgnoreCase(preprocess.toString())) { 072 CoreSession session = getCoreSession(); 073 if (session != null) { 074 CommentProcessorHelper cph = new CommentProcessorHelper(session); 075 cph.processComments(entries); 076 } 077 } 078 } 079 080 @Override 081 public List<LogEntry> getCurrentPage() { 082 083 buildAuditQuery(true); 084 searchBuilder.setFrom((int) (getCurrentPageIndex() * pageSize)); 085 searchBuilder.setSize((int) getMinMaxPageSize()); 086 087 for (SortInfo sortInfo : getSortInfos()) { 088 searchBuilder.addSort(sortInfo.getSortColumn(), sortInfo.getSortAscending() ? SortOrder.ASC 089 : SortOrder.DESC); 090 } 091 092 SearchResponse searchResponse = searchBuilder.execute().actionGet(); 093 List<LogEntry> entries = new ArrayList<>(); 094 SearchHits hits = searchResponse.getHits(); 095 096 // set total number of hits ? 097 setResultsCount(hits.getTotalHits()); 098 099 for (SearchHit hit : hits) { 100 try { 101 entries.add(AuditEntryJSONReader.read(hit.getSourceAsString())); 102 } catch (IOException e) { 103 log.error("Error while reading Audit Entry from ES", e); 104 } 105 } 106 preprocessCommentsIfNeeded(entries); 107 108 long t0 = System.currentTimeMillis(); 109 110 CoreSession session = getCoreSession(); 111 if (session != null) { 112 // send event for statistics ! 113 fireSearchEvent(session.getPrincipal(), searchBuilder.toString(), entries, System.currentTimeMillis() - t0); 114 } 115 116 return entries; 117 } 118 119 protected boolean isNonNullParam(Object[] val) { 120 if (val == null) { 121 return false; 122 } 123 for (Object v : val) { 124 if (v != null) { 125 if (v instanceof String) { 126 if (!((String) v).isEmpty()) { 127 return true; 128 } 129 } else if (v instanceof String[]) { 130 if (((String[]) v).length > 0) { 131 return true; 132 } 133 } else { 134 return true; 135 } 136 } 137 } 138 return false; 139 } 140 141 protected String getFixedPart() { 142 if (getDefinition().getWhereClause() == null) { 143 return null; 144 } else { 145 String fixedPart = getDefinition().getWhereClause().getFixedPart(); 146 if (fixedPart == null || fixedPart.isEmpty()) { 147 fixedPart = emptyQuery; 148 } 149 return fixedPart; 150 } 151 } 152 153 protected boolean allowSimplePattern() { 154 return true; 155 } 156 157 protected ESAuditBackend getESBackend() { 158 NXAuditEventsService audit = (NXAuditEventsService) Framework.getRuntime().getComponent( 159 NXAuditEventsService.NAME); 160 AuditBackend backend = audit.getBackend(); 161 if (backend instanceof ESAuditBackend) { 162 return (ESAuditBackend) backend; 163 } 164 throw new NuxeoException( 165 "Unable to use ESAuditPageProvider if audit service is not configured to run with ElasticSearch"); 166 } 167 168 protected void buildAuditQuery(boolean includeSort) { 169 PageProviderDefinition def = getDefinition(); 170 Object[] params = getParameters(); 171 172 if (def.getWhereClause() == null) { 173 // Simple Pattern 174 175 if (!allowSimplePattern()) { 176 throw new UnsupportedOperationException("This page provider requires a explicit Where Clause"); 177 } 178 String baseQuery = getESBackend().expandQueryVariables(def.getPattern(), params); 179 searchBuilder = getESBackend().buildQuery(baseQuery, null); 180 } else { 181 // Where clause based on DocumentModel 182 String baseQuery = getESBackend().expandQueryVariables(getFixedPart(), params); 183 searchBuilder = getESBackend().buildSearchQuery(baseQuery, def.getWhereClause().getPredicates(), 184 getSearchDocumentModel()); 185 } 186 } 187 188 @Override 189 public void refresh() { 190 setCurrentPageOffset(0); 191 super.refresh(); 192 } 193 194 @Override 195 public long getResultsCount() { 196 return resultsCount; 197 } 198 199 @Override 200 public List<SortInfo> getSortInfos() { 201 202 // because ContentView can reuse PageProVider without redefining columns 203 // ensure compat for ContentView configured with JPA log.* sort syntax 204 List<SortInfo> sortInfos = super.getSortInfos(); 205 for (SortInfo si : sortInfos) { 206 if (si.getSortColumn().startsWith("log.")) { 207 si.setSortColumn(si.getSortColumn().substring(4)); 208 } 209 } 210 return sortInfos; 211 } 212 213}