diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanel.java index 9152df26e8..bec42f0dc1 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanel.java @@ -66,7 +66,7 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { private static final String INVALID_INDEXING_SERVER_PORT_MSG = NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.validationErrMsg.invalidIndexingServerPort"); private static final String INVALID_SOLR4_SERVER_PORT_MSG = NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.validationErrMsg.invalidSolr4ServerPort"); private static final String SOLR_SERVER_NOT_CONFIGURED_MSG = NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.validationErrMsg.solrNotConfigured"); - private static final String INVALID_ZK_SERVER_HOST_MSG = NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.validationErrMsg.invalidZkServerHostName"); + private static final String INVALID_ZK_SERVER_HOST_MSG = NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.validationErrMsg.invalidZkServerHostName"); private static final String INVALID_ZK_SERVER_PORT_MSG = NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.validationErrMsg.invalidZkServerPort"); private static final String SOLR8_HOST_NAME_OR_IP_PROMPT = NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.tbSolr8Hostname.toolTipText"); private static final String SOLR8_PORT_PROMPT = NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.tbSolr8Port.toolTipText"); @@ -129,7 +129,7 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { tbSolr4Port.getDocument().putProperty("statusIcon", lbTestSolr); tbZkHostname.getDocument().putProperty("statusIcon", lbTestSolr); tbZkPort.getDocument().putProperty("statusIcon", lbTestSolr); - + tbMsgHostname.getDocument().putProperty("statusIcon", lbTestMessageService); tbMsgPort.getDocument().putProperty("statusIcon", lbTestMessageService); tbMsgUsername.getDocument().putProperty("statusIcon", lbTestMessageService); @@ -151,10 +151,10 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { textBoxes.add(tbSolr4Port); textBoxes.add(tbZkHostname); textBoxes.add(tbZkPort); - + // as the user enters Solr 8 settings, we fill in the ZK settings with the embedded Solr 8 ZK connection info. tbSolr8Hostname.getDocument().addDocumentListener(new MyDocumentListener()); - + addDocumentListeners(textBoxes, textBoxChangedListener); goodIcon = new ImageIcon(ImageUtilities.loadImage("org/sleuthkit/autopsy/images/good.png", false)); badIcon = new ImageIcon(ImageUtilities.loadImage("org/sleuthkit/autopsy/images/bad.png", false)); @@ -570,7 +570,7 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { * Enables/disables the multi-user settings, based upon input provided * * @param textFields The text fields to enable/disable. - * @param enabled True means enable, false means disable. + * @param enabled True means enable, false means disable. */ private static void enableMultiUserComponents(Collection textFields, boolean enabled) { for (JTextField textField : textFields) { @@ -668,13 +668,13 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { int port = Integer.parseInt(tbSolr8Port.getText().trim()); kwsService.tryConnect(tbSolr8Hostname.getText().trim(), port); } - + // test Solr 4 conenctivity if (!tbSolr4Port.getText().trim().isEmpty() && !tbSolr4Hostname.getText().trim().isEmpty()) { int port = Integer.parseInt(tbSolr4Port.getText().trim()); kwsService.tryConnect(tbSolr4Hostname.getText().trim(), port); } - + // test ZooKeeper connectivity (ZK settings are mandatory) if (tbZkPort.getText().trim().isEmpty() || tbZkHostname.getText().trim().isEmpty()) { lbTestSolr.setIcon(badIcon); @@ -770,7 +770,7 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { if (portNumberIsValid(solr4ServerPort)) { tbSolr4Port.setText(solr4ServerPort); } - + // if there are existing valid ZK settings, use those String zkServerPort = UserPreferences.getZkServerPort().trim(); if (portNumberIsValid(zkServerPort)) { @@ -781,7 +781,7 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { tbZkHostname.setText(zkServerHost); return; } - + // If there are no previous Solr 4 settings, use Solr 8 settings // to fill in the ZK settings with the embedded Solr 8 ZK connection info. if (solr4ServerHost.isEmpty() && !indexingServerHost.isEmpty()) { @@ -789,14 +789,14 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { tbZkPort.setText(zkServerPort); // gets default ZK port, which is Solr port number + 1000 return; } - + // If there are existing Solr 4 settings and no Solr 8 settings, // pre-populate the ZK settings with the Solr 4 embedded ZK settings. if (!solr4ServerHost.isEmpty() && indexingServerHost.isEmpty()) { tbZkHostname.setText(solr4ServerHost); tbZkPort.setText(zkServerPort); // gets default ZK port, which is Solr port number + 1000 return; - } + } } /** @@ -813,28 +813,28 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { } /** - * Tests whether or not values have been entered in all of the mandatory Solr settings - * text fields. Test optional settings for completeness. + * Tests whether or not values have been entered in all of the mandatory + * Solr settings text fields. Test optional settings for completeness. * * @return True or false. */ private boolean solrFieldsArePopulated() { - + // either Solr 8 or/and Solr 4 seetings must be specified boolean solrConfigured = false; - + // check if Solr 8 settings are set if (!tbSolr8Hostname.getText().trim().isEmpty() && !tbSolr8Port.getText().trim().isEmpty()) { solrConfigured = true; } - + // check if Solr 4 settings are set if (!tbSolr4Hostname.getText().trim().isEmpty() && !tbSolr4Port.getText().trim().isEmpty()) { solrConfigured = true; } - + // ZK settings are mandatory return (solrConfigured && !tbZkHostname.getText().trim().isEmpty() && !tbZkPort.getText().trim().isEmpty()); @@ -858,8 +858,6 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { boolean isPwSet = (tbMsgPassword.getPassword().length != 0); return (isUserSet == isPwSet); } - - void store() { boolean prevSelected = UserPreferences.getIsMultiUserModeEnabled(); @@ -872,11 +870,11 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { boolean multiUserCasesEnabled = cbEnableMultiUser.isSelected(); UserPreferences.setIsMultiUserModeEnabled(multiUserCasesEnabled); - + CaseDbConnectionInfo info = null; - + if (multiUserCasesEnabled == true) { - + // Check if aplication restart is required. boolean needsRestart = false; // don't check if entring multi user data for the first time @@ -918,7 +916,7 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { UserPreferences.setMessageServiceConnectionInfo(msgServiceInfo); } catch (UserPreferencesException ex) { logger.log(Level.SEVERE, "Error saving messaging service connection info", ex); //NON-NLS - } + } UserPreferences.setIndexingServerHost(tbSolr8Hostname.getText().trim()); String solr8port = tbSolr8Port.getText().trim(); @@ -929,7 +927,7 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { UserPreferences.setSolr4ServerPort(tbSolr4Port.getText().trim()); UserPreferences.setZkServerHost(tbZkHostname.getText().trim()); UserPreferences.setZkServerPort(tbZkPort.getText().trim()); - + if (needsRestart) { SwingUtilities.invokeLater(() -> { JOptionPane.showMessageDialog(this, @@ -941,10 +939,11 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { } // trigger changes to whether or not user can use multi user settings for central repository - if (prevSelected != multiUserCasesEnabled || !areCaseDbConnectionEqual(prevConn, info)) + if (prevSelected != multiUserCasesEnabled || !areCaseDbConnectionEqual(prevConn, info)) { GlobalSettingsPanel.onMultiUserChange(this, prevSelected, multiUserCasesEnabled); + } } - + private boolean isRestartRequired() { // if ZK was previously configured if (!UserPreferences.getZkServerHost().isEmpty()) { @@ -953,32 +952,29 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { || !(tbZkPort.getText().trim().equals(UserPreferences.getZkServerPort()))) { return true; } - } + } return false; } private static boolean arePropsEqual(Object a, Object b) { if (a == null || b == null) { return (a == null && b == null); - } - else { + } else { return a.equals(b); } } - - private static boolean areCaseDbConnectionEqual(CaseDbConnectionInfo a, CaseDbConnectionInfo b) { + + private static boolean areCaseDbConnectionEqual(CaseDbConnectionInfo a, CaseDbConnectionInfo b) { if (a == null || b == null) { return (a == null && b == null); } - return - arePropsEqual(a.getDbType(), b.getDbType()) && - arePropsEqual(a.getHost(), b.getHost()) && - arePropsEqual(a.getPassword(), b.getPassword()) && - arePropsEqual(a.getPort(), b.getPort()) && - arePropsEqual(a.getUserName(), b.getUserName()); - } - + return arePropsEqual(a.getDbType(), b.getDbType()) + && arePropsEqual(a.getHost(), b.getHost()) + && arePropsEqual(a.getPassword(), b.getPassword()) + && arePropsEqual(a.getPort(), b.getPort()) + && arePropsEqual(a.getUserName(), b.getUserName()); + } /** * Validates that the form is filled out correctly for our usage. @@ -1062,7 +1058,7 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { * @return True or false. */ boolean indexingServerSettingsAreValid() { - + String solr8Port = tbSolr8Port.getText().trim(); if (!solr8Port.isEmpty() && !portNumberIsValid(solr8Port)) { // if the port is specified, it has to be valid @@ -1075,34 +1071,34 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { // if the port is specified, it has to be valid tbOops.setText(INVALID_SOLR4_SERVER_PORT_MSG); return false; - } - + } + // either Solr 8 or/and Solr 4 seetings must be specified boolean solrConfigured = false; - + // check if Solr 8 settings are set if (!tbSolr8Hostname.getText().trim().isEmpty() && !tbSolr8Port.getText().trim().isEmpty()) { solrConfigured = true; } - + // check if Solr 4 settings are set if (!tbSolr4Hostname.getText().trim().isEmpty() && !tbSolr4Port.getText().trim().isEmpty()) { solrConfigured = true; } - + if (!solrConfigured) { tbOops.setText(SOLR_SERVER_NOT_CONFIGURED_MSG); return false; } - + // ZK settings are mandatory if (tbZkHostname.getText().trim().isEmpty()) { tbOops.setText(INVALID_ZK_SERVER_HOST_MSG); - return false; + return false; } - + // ZK settings are mandatory String zkPort = tbZkPort.getText().trim(); if (zkPort.isEmpty() || !portNumberIsValid(zkPort)) { diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Installer.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Installer.java index 8119015320..0fd7eb4af0 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Installer.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Installer.java @@ -49,7 +49,7 @@ class Installer extends ModuleInstall { server.start(); } catch (SolrServerNoPortException ex) { logger.log(Level.SEVERE, "Failed to start Keyword Search server: ", ex); //NON-NLS - if (ex.getPortNumber() == server.getCurrentSolrServerPort()) { + if (ex.getPortNumber() == server.getLocalSolrServerPort()) { reportPortError(ex.getPortNumber()); } else { reportStopPortError(ex.getPortNumber()); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java index 4d702a0d23..e1503ea3ef 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2019 Basis Technology Corp. + * Copyright 2011-2020 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -73,7 +73,6 @@ import org.apache.solr.common.SolrInputDocument; import org.apache.solr.common.util.NamedList; import org.openide.modules.InstalledFileLocator; import org.openide.modules.Places; -import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; @@ -84,6 +83,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.ModuleSettings; import org.sleuthkit.autopsy.coreutils.PlatformUtil; +import org.sleuthkit.autopsy.coreutils.ThreadUtils; import org.sleuthkit.autopsy.healthmonitor.HealthMonitor; import org.sleuthkit.autopsy.healthmonitor.TimingMetric; import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException; @@ -249,8 +249,7 @@ public class Server { private HttpSolrClient localSolrServer = null; private SOLR_VERSION localServerVersion = SOLR_VERSION.SOLR8; // start local Solr 8 by default - // A reference to the Solr server we are currently connected to for the Case. - // This could be a local or remote server. + // A reference to the remote/network running Solr instance. private HttpSolrClient remoteSolrServer; private Collection currentCollection; @@ -385,11 +384,11 @@ public class Server { serverAction.addPropertyChangeListener(l); } - int getCurrentSolrServerPort() { + int getLocalSolrServerPort() { return localSolrServerPort; } - int getCurrentSolrStopPort() { + int getLocalSolrStopPort() { return localSolrStopPort; } @@ -1869,13 +1868,12 @@ public class Server { private HttpSolrClient queryClient = null; private SolrClient indexingClient = null; - public final int maxBufferSize; - public final List buffer; + private final int maxBufferSize; + private final List buffer; private final Object bufferLock; private ScheduledThreadPoolExecutor periodicTasksExecutor = null; private static final long PERIODIC_BATCH_SEND_INTERVAL_MINUTES = 10; - private static final long EXECUTOR_TERMINATION_WAIT_SECS = 30; private Collection(String name, Case theCase, Index index) throws TimeoutException, InterruptedException, SolrServerException, IOException { this.name = name; @@ -1905,7 +1903,7 @@ public class Server { logger.log(Level.INFO, "Using Solr document queue size = {0}", maxBufferSize); //NON-NLS buffer = new ArrayList<>(maxBufferSize); periodicTasksExecutor = new ScheduledThreadPoolExecutor(1, new ThreadFactoryBuilder().setNameFormat("periodic-batched-document-task-%d").build()); //NON-NLS - periodicTasksExecutor.scheduleWithFixedDelay(new SentBatchedDocumentsTask(), PERIODIC_BATCH_SEND_INTERVAL_MINUTES, PERIODIC_BATCH_SEND_INTERVAL_MINUTES, TimeUnit.MINUTES); + periodicTasksExecutor.scheduleWithFixedDelay(new SendBatchedDocumentsTask(), PERIODIC_BATCH_SEND_INTERVAL_MINUTES, PERIODIC_BATCH_SEND_INTERVAL_MINUTES, TimeUnit.MINUTES); } /** @@ -1914,7 +1912,7 @@ public class Server { * if the buffer is not full, we want to periodically send the batched documents * so that users are able to see them in their keyword searches. */ - private final class SentBatchedDocumentsTask implements Runnable { + private final class SendBatchedDocumentsTask implements Runnable { @Override public void run() { @@ -1933,7 +1931,6 @@ public class Server { try { // send the cloned list to Solr - logger.log(Level.INFO, "Periodic batched document update"); //NON-NLS sendBufferedDocs(clone); } catch (KeywordSearchModuleException ex) { logger.log(Level.SEVERE, "Periodic batched document update failed", ex); //NON-NLS @@ -1979,17 +1976,21 @@ public class Server { } private void commit() throws SolrServerException { - + + List clone; synchronized (bufferLock) { - try { - // we do a manual commit after ingest is complete, so I - // think there is no need to clone the buffer - sendBufferedDocs(buffer); - } catch (KeywordSearchModuleException ex) { - throw new SolrServerException(NbBundle.getMessage(this.getClass(), "Server.commit.exception.msg"), ex); - } + // Make a clone and release the lock, so that we don't + // hold other ingest threads + clone = buffer.stream().collect(toList()); + buffer.clear(); } - + + try { + sendBufferedDocs(clone); + } catch (KeywordSearchModuleException ex) { + throw new SolrServerException(NbBundle.getMessage(this.getClass(), "Server.commit.exception.msg"), ex); + } + try { //commit and block indexingClient.commit(true, true); @@ -2107,14 +2108,7 @@ public class Server { try { // stop the periodic batch update task. If the task is already running, // allow it to finish. - periodicTasksExecutor.shutdown(); - try { - while (!periodicTasksExecutor.awaitTermination(EXECUTOR_TERMINATION_WAIT_SECS, TimeUnit.SECONDS)) { - logger.log(Level.WARNING, "Waited at least {0} seconds for periodic KWS task executor to shut down, continuing to wait", EXECUTOR_TERMINATION_WAIT_SECS); //NON-NLS - } - } catch (InterruptedException ex) { - logger.log(Level.SEVERE, "Unexpected interrupt while stopping periodic KWS task executor", ex); //NON-NLS - } + ThreadUtils.shutDownTaskExecutor(periodicTasksExecutor); // We only unload cores for "single-user" cases. if (this.caseType == CaseType.MULTI_USER_CASE) { diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java index e660f04c80..9225ea14f3 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java @@ -150,10 +150,10 @@ public class SolrSearchService implements KeywordSearchService, AutopsyService { try { KeywordSearch.getServer().connectToSolrServer(host, Integer.toString(port)); } catch (SolrServerException ex) { - logger.log(Level.SEVERE, "Uanble to connect to Solr server. Host: " + host + ", port: " + port, ex); + logger.log(Level.SEVERE, "Unable to connect to Solr server. Host: " + host + ", port: " + port, ex); throw new KeywordSearchServiceException(NbBundle.getMessage(SolrSearchService.class, "SolrConnectionCheck.HostnameOrPort")); //NON-NLS*/ } catch (IOException ex) { - logger.log(Level.SEVERE, "Uanble to connect to Solr server. Host: " + host + ", port: " + port, ex); + logger.log(Level.SEVERE, "Unable to connect to Solr server. Host: " + host + ", port: " + port, ex); String result = NbBundle.getMessage(SolrSearchService.class, "SolrConnectionCheck.HostnameOrPort"); //NON-NLS String message = ex.getCause().getMessage().toLowerCase(); if (message.startsWith(SERVER_REFUSED_CONNECTION)) { @@ -173,10 +173,10 @@ public class SolrSearchService implements KeywordSearchService, AutopsyService { } throw new KeywordSearchServiceException(result); } catch (NumberFormatException ex) { - logger.log(Level.SEVERE, "Uanble to connect to Solr server. Host: " + host + ", port: " + port, ex); + logger.log(Level.SEVERE, "Unable to connect to Solr server. Host: " + host + ", port: " + port, ex); throw new KeywordSearchServiceException(Bundle.SolrConnectionCheck_Port()); } catch (IllegalArgumentException ex) { - logger.log(Level.SEVERE, "Uanble to connect to Solr server. Host: " + host + ", port: " + port, ex); + logger.log(Level.SEVERE, "Unable to connect to Solr server. Host: " + host + ", port: " + port, ex); throw new KeywordSearchServiceException(ex.getMessage()); } }