Java tutorial
/* $This file is distributed under the terms of the license in /doc/license.txt$ */ package edu.cornell.mannlib.vitro.webapp.searchengine; import static edu.cornell.mannlib.vitro.webapp.utils.developer.Key.SEARCH_DELETIONS_ENABLE; import static edu.cornell.mannlib.vitro.webapp.utils.developer.Key.SEARCH_ENGINE_ENABLE; import static edu.cornell.mannlib.vitro.webapp.utils.developer.Key.SEARCH_INDEX_ENABLE; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.regex.Pattern; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchInputDocument; import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchQuery; import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchResponse; import edu.cornell.mannlib.vitro.webapp.utils.developer.DeveloperSettings; import edu.cornell.mannlib.vitro.webapp.utils.developer.Key; import edu.cornell.mannlib.vitro.webapp.utils.developer.loggers.StackTraceUtility; import edu.cornell.mannlib.vitro.webapp.utils.searchengine.Formatter; /** * Logging the SearchEngine, for the Developer's panel. */ public abstract class SearchEngineLogger implements AutoCloseable { private static final Log log = LogFactory.getLog(SearchEngineLogger.class); // ---------------------------------------------------------------------- // Factory // ---------------------------------------------------------------------- public static SearchEngineLogger doAdd(SearchInputDocument[] docs) { if (isEnabled(SEARCH_INDEX_ENABLE)) { return new AddLogger(Arrays.asList(docs)); } else { return new DisabledLogger(); } } public static SearchEngineLogger doAdd(Collection<SearchInputDocument> docs) { if (isEnabled(SEARCH_INDEX_ENABLE)) { return new AddLogger(docs); } else { return new DisabledLogger(); } } public static SearchEngineLogger doDeleteById(String[] ids) { if (isEnabled(SEARCH_DELETIONS_ENABLE)) { return new DeleteIdsLogger(Arrays.asList(ids)); } else { return new DisabledLogger(); } } public static SearchEngineLogger doDeleteById(Collection<String> ids) { if (isEnabled(SEARCH_DELETIONS_ENABLE)) { return new DeleteIdsLogger(ids); } else { return new DisabledLogger(); } } public static SearchEngineLogger doDeleteByQuery(String query) { if (isEnabled(SEARCH_DELETIONS_ENABLE)) { return new DeleteQueryLogger(query); } else { return new DisabledLogger(); } } public static SearchEngineLogger doQuery(SearchQuery query) { if (isEnabled(SEARCH_ENGINE_ENABLE)) { return new QueryLogger(query); } else { return new DisabledLogger(); } } public static SearchEngineLogger doCountQuery() { if (isEnabled(SEARCH_ENGINE_ENABLE)) { return new CountQueryLogger(); } else { return new DisabledLogger(); } } private static boolean isEnabled(Key enableKey) { return log.isInfoEnabled() && DeveloperSettings.getInstance().getBoolean(enableKey); } // ---------------------------------------------------------------------- // Abstract instance // ---------------------------------------------------------------------- private final long startTime; public SearchEngineLogger() { this.startTime = System.currentTimeMillis(); } protected float elapsedSeconds() { long endTime = System.currentTimeMillis(); return (endTime - startTime) / 1000.0F; } @Override public void close() { try { writeToLog(); } catch (Exception e) { log.error("Failed to write log record", e); } } @SuppressWarnings("unused") public void setSearchResponse(SearchResponse response) { throw new UnsupportedOperationException( this.getClass().getSimpleName() + " does not support setSearchResponse()"); } protected abstract void writeToLog(); // ---------------------------------------------------------------------- // Concrete sub-classes // ---------------------------------------------------------------------- private static class AddLogger extends SearchEngineLogger { private final List<SearchInputDocument> docs; private final boolean passesRestrictions; AddLogger(Collection<SearchInputDocument> docs) { this.docs = restrictDocsByUriOrName(docs); this.passesRestrictions = passesDocumentRestriction() && this.docs.size() > 0; } private List<SearchInputDocument> restrictDocsByUriOrName(Collection<SearchInputDocument> rawDocs) { String restriction = DeveloperSettings.getInstance() .getString(Key.SEARCH_INDEX_URI_OR_NAME_RESTRICTION); if (restriction.isEmpty()) { return new ArrayList<>(rawDocs); } List<SearchInputDocument> list = new ArrayList<>(); for (SearchInputDocument doc : rawDocs) { if (passesUriOrNameRestriction(doc, restriction)) { list.add(doc); } } return list; } private boolean passesUriOrNameRestriction(SearchInputDocument doc, String restriction) { try { return Pattern.matches(restriction, Formatter.format(doc)); } catch (Exception e) { log.warn("Failed to test URI or Name restriction: '" + restriction + "'", e); return true; } } private boolean passesDocumentRestriction() { String restriction = DeveloperSettings.getInstance().getString(Key.SEARCH_INDEX_DOCUMENT_RESTRICTION); if (!restriction.isEmpty()) { try { return Pattern.matches(restriction, docContents()); } catch (Exception e) { log.warn("Failed to test document restriction: '" + restriction + "'", e); } } return true; } @Override public void writeToLog() { if (!passesRestrictions) { return; } if (showDocumentContents()) { log.info(String.format("%8.3f added %d documents: \n%s", elapsedSeconds(), docs.size(), docContents())); } else { log.info(String.format("%8.3f added %d documents: \n%s", elapsedSeconds(), docs.size(), docUris())); } } private boolean showDocumentContents() { return DeveloperSettings.getInstance().getBoolean(Key.SEARCH_INDEX_SHOW_DOCUMENTS); } private String docUris() { StringBuilder sb = new StringBuilder(); for (SearchInputDocument doc : docs) { sb.append(Formatter.getValueFromField(doc, "URI")).append(" - ") .append(Formatter.getValueFromField(doc, "nameRaw")).append("\n"); } return sb.toString(); } private String docContents() { StringBuilder sb = new StringBuilder(); for (SearchInputDocument doc : docs) { sb.append(Formatter.format(doc)); } return sb.toString(); } } private static class DeleteIdsLogger extends SearchEngineLogger { private final List<String> ids; DeleteIdsLogger(Collection<String> ids) { this.ids = new ArrayList<>(ids); } @Override public void writeToLog() { log.info(String.format("%8.3f deleted these %d search documents: %s", elapsedSeconds(), ids.size(), StringUtils.join(ids, ", "))); } } private static class DeleteQueryLogger extends SearchEngineLogger { private final String query; DeleteQueryLogger(String query) { this.query = query; } @Override public void writeToLog() { log.info(String.format("%8.3f delete documents as found by this query: %s\n", elapsedSeconds(), query)); } } public static class QueryLogger extends SearchEngineLogger { private final SearchQuery query; private final StackTraceUtility stackTrace; private final boolean passesRestrictions; private SearchResponse response; QueryLogger(SearchQuery query) { this.query = query; this.stackTrace = new StackTraceUtility(InstrumentedSearchEngineWrapper.class, true); this.passesRestrictions = passesQueryRestriction() && passesStackRestriction(); log.debug("QueryLogger: query=" + query + ", passes=" + passesRestrictions); } private boolean passesStackRestriction() { return stackTrace.passesStackRestriction( DeveloperSettings.getInstance().getString(Key.SEARCH_ENGINE_STACK_RESTRICTION)); } private boolean passesQueryRestriction() { String restriction = DeveloperSettings.getInstance().getString(Key.SEARCH_ENGINE_QUERY_RESTRICTION); if (StringUtils.isEmpty(restriction)) { return true; } try { return Pattern.matches(restriction, Formatter.format(query)); } catch (Exception e) { log.warn("Failed to test query restriction: '" + restriction + "'", e); return true; } } @Override public void setSearchResponse(SearchResponse response) { this.response = response; } @Override public void writeToLog() { if (!passesRestrictions) { return; } String results = (showSearchResults()) ? Formatter.format(response) : " returned " + response.getResults().size() + " results.\n"; String trace = stackTrace.format(showStackTrace()); log.info(String.format("%8.3f %s%s%s", elapsedSeconds(), Formatter.format(query), results, trace)); } private boolean showSearchResults() { return DeveloperSettings.getInstance().getBoolean(Key.SEARCH_ENGINE_ADD_RESULTS); } private boolean showStackTrace() { return DeveloperSettings.getInstance().getBoolean(Key.SEARCH_ENGINE_ADD_STACK_TRACE); } } public static class CountQueryLogger extends SearchEngineLogger { private final StackTraceUtility stackTrace; private final boolean passesRestrictions; private long count; CountQueryLogger() { this.stackTrace = new StackTraceUtility(InstrumentedSearchEngineWrapper.class, true); this.passesRestrictions = passesQueryRestriction() && passesStackRestriction(); log.debug("CountQueryLogger: passes=" + passesRestrictions); } private boolean passesStackRestriction() { return stackTrace.passesStackRestriction( DeveloperSettings.getInstance().getString(Key.SEARCH_ENGINE_STACK_RESTRICTION)); } /** Only passes if there is no restriction. */ private boolean passesQueryRestriction() { String restriction = DeveloperSettings.getInstance().getString(Key.SEARCH_ENGINE_QUERY_RESTRICTION); return StringUtils.isEmpty(restriction); } @Override public void setSearchResponse(SearchResponse response) { this.count = response.getResults().getNumFound(); } @Override public void writeToLog() { if (!passesRestrictions) { return; } String results = "Document count query found " + count + " documents.\n"; String trace = stackTrace.format(showStackTrace()); log.info(String.format("%8.3f %s%s", elapsedSeconds(), results, trace)); } private boolean showStackTrace() { return DeveloperSettings.getInstance().getBoolean(Key.SEARCH_ENGINE_ADD_STACK_TRACE); } } private static class DisabledLogger extends SearchEngineLogger { @Override public void setSearchResponse(SearchResponse response) { // Does nothing. } @Override protected void writeToLog() { // Does nothing. } } }