/* * Autopsy Forensic Browser * * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier <at> sleuthkit <dot> org * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.sleuthkit.autopsy.keywordsearch; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.IOException; import java.util.logging.FileHandler; import java.util.logging.Level; import java.util.logging.Logger; import java.util.logging.SimpleFormatter; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.core.RuntimeProperties; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.keywordsearch.KeywordSearchResultFactory.BlackboardResultWriter; /** * Wrapper over KeywordSearch Solr server singleton. The class also provides * some global types and property change support on the server events. */ public class KeywordSearch { private static Server server; //we want a custom java.util.logging.Logger here for a reason //a separate logger from framework logs private static final Logger TIKA_LOGGER = Logger.getLogger("Tika"); //NON-NLS private static final org.sleuthkit.autopsy.coreutils.Logger logger = org.sleuthkit.autopsy.coreutils.Logger.getLogger(Case.class.getName()); public enum QueryType { LITERAL, REGEX }; public static final String NUM_FILES_CHANGE_EVT = "NUM_FILES_CHANGE_EVT"; //NON-NLS private static PropertyChangeSupport changeSupport = new PropertyChangeSupport(KeywordSearch.class); /** * Get an instance of KeywordSearch server to execute queries on Content, * getting extracted text, performing searches, etc. * * @return singleton instance of KeywordSearch server */ public static synchronized Server getServer() { if (server == null) { server = new Server(); } return server; } static { try { final int MAX_TIKA_LOG_FILES = 3; FileHandler tikaLogHandler = new FileHandler(PlatformUtil.getUserDirectory().getAbsolutePath() + "/var/log/tika.log", //NON-NLS 0, MAX_TIKA_LOG_FILES); tikaLogHandler.setFormatter(new SimpleFormatter()); tikaLogHandler.setEncoding(PlatformUtil.getLogFileEncoding()); TIKA_LOGGER.addHandler(tikaLogHandler); //do not forward to the parent autopsy logger TIKA_LOGGER.setUseParentHandlers(false); } catch (IOException | SecurityException ex) { logger.log(Level.SEVERE, "Error setting up tika logging", ex); //NON-NLS } } // don't instantiate private KeywordSearch() { throw new AssertionError(); } static Logger getTikaLogger() { return TIKA_LOGGER; } public static void addNumIndexedFilesChangeListener(PropertyChangeListener l) { changeSupport.addPropertyChangeListener(NUM_FILES_CHANGE_EVT, l); } public static void removeNumIndexedFilesChangeListener(PropertyChangeListener l) { changeSupport.removePropertyChangeListener(l); } public static void fireNumIndexedFilesChange(Integer oldNum, Integer newNum) { try { changeSupport.firePropertyChange(NUM_FILES_CHANGE_EVT, oldNum, newNum); } catch (Exception e) { logger.log(Level.SEVERE, "KeywordSearch listener threw exception", e); //NON-NLS MessageNotifyUtil.Notify.show(NbBundle.getMessage(KeywordSearch.class, "KeywordSearch.moduleErr"), NbBundle.getMessage(KeywordSearch.class, "KeywordSearch.fireNumIdxFileChg.moduleErr.msg"), MessageNotifyUtil.MessageType.ERROR); } } /** * Listener to create/open and close Solr cores when cases are * created/opened and closed. */ static class CaseChangeListener implements PropertyChangeListener { @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())) { if (null != evt.getOldValue()) { /* * A case is being closed. */ Case closedCase = (Case) evt.getOldValue(); try { BlackboardResultWriter.stopAllWriters(); /* * TODO (AUT-2084): The following code * KeywordSearch.CaseChangeListener gambles that any * BlackboardResultWriters (SwingWorkers) will complete * in less than roughly two seconds */ Thread.sleep(2000); server.closeCore(); } catch (Exception ex) { logger.log(Level.SEVERE, String.format("Failed to close core for %s", closedCase.getCaseDirectory()), ex); //NON-NLS if (RuntimeProperties.coreComponentsAreActive()) { MessageNotifyUtil.Notify.error(NbBundle.getMessage(KeywordSearch.class, "KeywordSearch.closeCore.notification.msg"), ex.getMessage()); } } } if (null != evt.getNewValue()) { /* * A case is being created/opened. */ Case openedCase = (Case) evt.getNewValue(); try { server.openCoreForCase(openedCase); } catch (Exception ex) { logger.log(Level.SEVERE, String.format("Failed to open or create core for %s", openedCase.getCaseDirectory()), ex); //NON-NLS if (RuntimeProperties.coreComponentsAreActive()) { MessageNotifyUtil.Notify.error(NbBundle.getMessage(KeywordSearch.class, "KeywordSearch.openCore.notification.msg"), ex.getMessage()); } } } } } } }