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