From 9069553a59be21ef610bcedbc20776ae97a711bd Mon Sep 17 00:00:00 2001 From: Karl Mortensen Date: Wed, 7 Oct 2015 16:00:27 -0400 Subject: [PATCH] add user-facing error messages for connection failures --- .../casemodule/NewCaseWizardAction.java | 3 +- .../sleuthkit/autopsy/core/Bundle.properties | 4 +- .../autopsy/core/ServicesMonitor.java | 82 ++++----- .../autopsy/core/UserPreferences.java | 11 +- .../autopsy/corecomponents/Bundle.properties | 4 + .../MultiUserSettingsPanel.form | 68 +++++-- .../MultiUserSettingsPanel.java | 168 ++++++++++++------ .../MultiUserSettingsPanelController.java | 1 + .../autopsy/events/Bundle.properties | 18 ++ .../events/MessageServiceConnectionInfo.java | 98 ++++++++-- .../KeywordSearchService.java | 24 ++- .../autopsy/keywordsearch/Bundle.properties | 6 +- .../KeywordSearchIngestModule.java | 9 +- .../keywordsearch/SolrSearchService.java | 83 ++++++--- 14 files changed, 416 insertions(+), 163 deletions(-) create mode 100755 Core/src/org/sleuthkit/autopsy/events/Bundle.properties diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardAction.java index 00c6616c5b..5e6b5e5ceb 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardAction.java @@ -39,6 +39,7 @@ import javax.swing.JOptionPane; import org.sleuthkit.autopsy.casemodule.Case.CaseType; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.datamodel.CaseDbConnectionInfo; +import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskData.DbType; /** @@ -112,7 +113,7 @@ final class NewCaseWizardAction extends CallableSystemAction { get(); CaseType currentCaseType = CaseType.values()[(int) wizardDescriptor.getProperty("caseType")]; //NON-NLS CaseDbConnectionInfo info = UserPreferences.getDatabaseConnectionInfo(); - if ((currentCaseType == CaseType.SINGLE_USER_CASE) || ((info.getDbType() != DbType.SQLITE) && info.canConnect())) { + if ((currentCaseType == CaseType.SINGLE_USER_CASE) || ((info.getDbType() != DbType.SQLITE) && SleuthkitCase.tryConnectOld(info.getHost(), info.getPort(), info.getUserName(), info.getPassword(), info.getDbType()))) { AddImageAction addImageAction = SystemAction.get(AddImageAction.class); addImageAction.actionPerformed(null); } else { diff --git a/Core/src/org/sleuthkit/autopsy/core/Bundle.properties b/Core/src/org/sleuthkit/autopsy/core/Bundle.properties index 4fa84d55dc..36d6bd4a25 100644 --- a/Core/src/org/sleuthkit/autopsy/core/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/core/Bundle.properties @@ -19,5 +19,5 @@ ServicesMonitor.restoredService.notify.msg=Connection to {0} is up ServicesMonitor.statusChange.notify.title=Service Status Update ServicesMonitor.statusChange.notify.msg=Status for {0} is {1} ServicesMonitor.nullServiceName.excepton.txt=Requested service name is null -ServicesMonitor.nullStatusOrDetails.excepton.txt=Status or details string is null -ServicesMonitor.unknownServiceName.excepton.txt=Requested service name {0} is unknown \ No newline at end of file +ServicesMonitor.unknownServiceName.excepton.txt=Requested service name {0} is unknown +ServicesMonitor.KeywordSearchNull=Keyword Search potentially not initialized \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/core/ServicesMonitor.java b/Core/src/org/sleuthkit/autopsy/core/ServicesMonitor.java index b7144c98fc..277f458892 100644 --- a/Core/src/org/sleuthkit/autopsy/core/ServicesMonitor.java +++ b/Core/src/org/sleuthkit/autopsy/core/ServicesMonitor.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.core; import org.sleuthkit.autopsy.core.events.ServiceEvent; import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.beans.PropertyChangeListener; +import java.io.IOException; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledThreadPoolExecutor; @@ -34,6 +35,13 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.events.AutopsyEventPublisher; import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService; +import org.sleuthkit.autopsy.events.MessageServiceConnectionInfo; +import java.net.URISyntaxException; +import java.sql.SQLException; +import javax.jms.JMSException; +import org.sleuthkit.datamodel.CaseDbConnectionInfo; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; /** * This class periodically checks availability of collaboration resources - @@ -149,28 +157,8 @@ public class ServicesMonitor { * @param status Updated status for the service. * @param details Details of the event. * - * @throws - * org.sleuthkit.autopsy.core.ServicesMonitor.ServicesMonitorException Thrown - * if - * either - * of - * input - * parameters - * is - * null. */ - public void setServiceStatus(String service, String status, String details) throws ServicesMonitorException { - - if (service == null) { - logger.log(Level.SEVERE, "Call to setServiceStatus() with null service name"); //NON-NLS - throw new ServicesMonitorException(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.nullServiceName.excepton.txt")); - } - - if (status == null || details == null) { - logger.log(Level.SEVERE, "Call to setServiceStatus() with null status or details"); //NON-NLS - throw new ServicesMonitorException(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.nullStatusOrDetails.excepton.txt")); - } - + public void setServiceStatus(String service, String status, String details) { // if the status update is for an existing service who's status hasn't changed - do nothing. if (statusByService.containsKey(service) && status.equals(statusByService.get(service))) { return; @@ -211,15 +199,8 @@ public class ServicesMonitor { * * @return ServiceStatus Status for the service. * - * @throws - * org.sleuthkit.autopsy.core.ServicesMonitor.ServicesMonitorException Thrown - * if - * service - * name is - * null or - * service - * doesn't - * exist. + * @throws ServicesMonitorException If service name is null or service + * doesn't exist. */ public String getServiceStatus(String service) throws ServicesMonitorException { @@ -260,14 +241,12 @@ public class ServicesMonitor { * Performs case database service availability status check. */ private void checkDatabaseConnectionStatus() { + CaseDbConnectionInfo info = UserPreferences.getDatabaseConnectionInfo(); try { - if (UserPreferences.getDatabaseConnectionInfo().canConnect()) { - setServiceStatus(Service.REMOTE_CASE_DATABASE.toString(), ServiceStatus.UP.toString(), ""); - } else { - setServiceStatus(Service.REMOTE_CASE_DATABASE.toString(), ServiceStatus.DOWN.toString(), ""); - } - } catch (Exception ex) { - logger.log(Level.SEVERE, "Exception while checking database connection status", ex); //NON-NLS + SleuthkitCase.tryConnect(info.getHost(), info.getPort(), info.getUserName(), info.getPassword(), info.getDbType()); + setServiceStatus(Service.REMOTE_CASE_DATABASE.toString(), ServiceStatus.UP.toString(), ""); + } catch (ClassNotFoundException | SQLException | TskCoreException ex) { + setServiceStatus(Service.REMOTE_CASE_DATABASE.toString(), ServiceStatus.DOWN.toString(), SleuthkitCase.getUserWarning(ex, info.getHost())); } } @@ -275,15 +254,19 @@ public class ServicesMonitor { * Performs keyword search service availability status check. */ private void checkKeywordSearchServerConnectionStatus() { + KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class); try { - KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class); - if (kwsService != null && kwsService.canConnectToRemoteSolrServer(UserPreferences.getIndexingServerHost(), UserPreferences.getIndexingServerPort())) { + if (kwsService != null) { + kwsService.tryConnect(UserPreferences.getIndexingServerHost(), UserPreferences.getIndexingServerPort()); setServiceStatus(Service.REMOTE_KEYWORD_SEARCH.toString(), ServiceStatus.UP.toString(), ""); } else { - setServiceStatus(Service.REMOTE_KEYWORD_SEARCH.toString(), ServiceStatus.DOWN.toString(), ""); + setServiceStatus(Service.REMOTE_KEYWORD_SEARCH.toString(), ServiceStatus.DOWN.toString(), + NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.KeywordSearchNull")); } - } catch (Exception ex) { - logger.log(Level.SEVERE, "Exception while checking keyword search server connection status", ex); //NON-NLS + } catch (NumberFormatException | IOException | TskCoreException ex) { + String rootCause = kwsService.getUserWarning(ex, UserPreferences.getIndexingServerHost()); + logger.log(Level.SEVERE, "Unable to connect to messaging server: " + rootCause, ex); //NON-NLS + setServiceStatus(Service.REMOTE_KEYWORD_SEARCH.toString(), ServiceStatus.DOWN.toString(), rootCause); } } @@ -291,14 +274,14 @@ public class ServicesMonitor { * Performs messaging service availability status check. */ private void checkMessagingServerConnectionStatus() { + MessageServiceConnectionInfo info = UserPreferences.getMessageServiceConnectionInfo(); try { - if (UserPreferences.getMessageServiceConnectionInfo().canConnect()) { - setServiceStatus(Service.MESSAGING.toString(), ServiceStatus.UP.toString(), ""); - } else { - setServiceStatus(Service.MESSAGING.toString(), ServiceStatus.DOWN.toString(), ""); - } - } catch (Exception ex) { - logger.log(Level.SEVERE, "Exception while checking messaging server connection status", ex); //NON-NLS + info.tryConnect(); + setServiceStatus(Service.MESSAGING.toString(), ServiceStatus.UP.toString(), ""); + } catch (URISyntaxException | JMSException | TskCoreException ex) { + String rootCause = info.getUserWarning(ex, info.getHost()); + logger.log(Level.SEVERE, "Unable to connect to messaging server: " + rootCause, ex); //NON-NLS + setServiceStatus(Service.MESSAGING.toString(), ServiceStatus.DOWN.toString(), rootCause); } } @@ -396,6 +379,7 @@ public class ServicesMonitor { * Exception thrown when service status query results in an error. */ public class ServicesMonitorException extends Exception { + private static final long serialVersionUID = 1L; public ServicesMonitorException(String message) { super(message); diff --git a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java index 2f1c6a5ae5..565ec6f395 100755 --- a/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java +++ b/Core/src/org/sleuthkit/autopsy/core/UserPreferences.java @@ -181,10 +181,10 @@ public final class UserPreferences { * @param info An object encapsulating the message service info. */ public static void setMessageServiceConnectionInfo(MessageServiceConnectionInfo info) { - preferences.put(MESSAGE_SERVICE_USER, info.getUserName()); - preferences.put(MESSAGE_SERVICE_PASSWORD, info.getPassword()); preferences.put(MESSAGE_SERVICE_HOST, info.getHost()); preferences.put(MESSAGE_SERVICE_PORT, info.getPort()); + preferences.put(MESSAGE_SERVICE_USER, info.getUserName()); + preferences.put(MESSAGE_SERVICE_PASSWORD, info.getPassword()); } /** @@ -193,10 +193,11 @@ public final class UserPreferences { * @return An object encapsulating the message service info. */ public static MessageServiceConnectionInfo getMessageServiceConnectionInfo() { - return new MessageServiceConnectionInfo(preferences.get(MESSAGE_SERVICE_USER, ""), - preferences.get(MESSAGE_SERVICE_PASSWORD, ""), + return new MessageServiceConnectionInfo( preferences.get(MESSAGE_SERVICE_HOST, ""), - preferences.get(MESSAGE_SERVICE_PORT, "61616")); + preferences.get(MESSAGE_SERVICE_PORT, "61616"), + preferences.get(MESSAGE_SERVICE_USER, ""), + preferences.get(MESSAGE_SERVICE_PASSWORD, "")); } /** diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties index 3b2ee0dac2..f878750bb9 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties @@ -191,3 +191,7 @@ MultiUserSettingsPanel.tbMsgPassword.toolTipText=Password MultiUserSettingsPanel.tbMsgPassword.text= MultiUserSettingsPanel.tbMsgHostname.toolTipText=Hostname or IP Address MultiUserSettingsPanel.tbMsgHostname.text= +MultiUserSettingsPanel.lbTestMessageWarning.text= +MultiUserSettingsPanel.lbTestSolrWarning.text= +MultiUserSettingsPanel.lbTestDbWarning.text= +MultiUserSettingsPanel.KeywordSearchNull=Keyword Search potentially not initialized \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanel.form index 3cf719efcc..57f1eca7d5 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanel.form @@ -16,12 +16,12 @@ - + - + @@ -57,10 +57,10 @@ - + - + @@ -78,7 +78,7 @@ - + @@ -92,6 +92,10 @@ + + + + @@ -114,7 +118,9 @@ - + + + @@ -201,6 +207,16 @@ + + + + + + + + + + @@ -215,7 +231,7 @@ - + @@ -227,6 +243,10 @@ + + + + @@ -247,7 +267,9 @@ - + + + @@ -300,6 +322,16 @@ + + + + + + + + + + @@ -314,7 +346,7 @@ - + @@ -328,6 +360,10 @@ + + + + @@ -344,7 +380,7 @@ - + @@ -353,6 +389,8 @@ + + @@ -437,6 +475,16 @@ + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanel.java index 807390cd25..1a58c2764a 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanel.java @@ -18,11 +18,17 @@ import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.events.MessageServiceConnectionInfo; import org.sleuthkit.autopsy.coreutils.Logger; import java.awt.Cursor; -import java.util.logging.Level; +import java.io.IOException; +import java.net.URISyntaxException; +import java.sql.SQLException; +import java.util.MissingResourceException; +import javax.jms.JMSException; import javax.swing.ImageIcon; import org.openide.util.ImageUtilities; import org.openide.util.Lookup; import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; public final class MultiUserSettingsPanel extends javax.swing.JPanel { @@ -41,7 +47,7 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { private static final Logger logger = Logger.getLogger(MultiUserSettingsPanel.class.getName()); private final ImageIcon goodIcon; private final ImageIcon badIcon; - + /** * Creates new form AutopsyMultiUserSettingsPanel * @@ -50,7 +56,8 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { public MultiUserSettingsPanel(MultiUserSettingsPanelController theController) { initComponents(); controller = theController; - + setSize(555, 600); + /** * Add text prompts to all of the text fields. */ @@ -98,9 +105,6 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { 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)); - lbTestDatabase.setIcon(null); - lbTestSolr.setIcon(null); - lbTestMessageService.setIcon(null); enableMultiUserComponents(textBoxes, cbEnableMultiUser.isSelected()); } @@ -148,12 +152,14 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { lbDatabaseSettings = new javax.swing.JLabel(); bnTestDatabase = new javax.swing.JButton(); lbTestDatabase = new javax.swing.JLabel(); + lbTestDbWarning = new javax.swing.JLabel(); pnSolrSettings = new javax.swing.JPanel(); lbSolrSettings = new javax.swing.JLabel(); tbSolrHostname = new javax.swing.JTextField(); tbSolrPort = new javax.swing.JTextField(); bnTestSolr = new javax.swing.JButton(); lbTestSolr = new javax.swing.JLabel(); + lbTestSolrWarning = new javax.swing.JLabel(); pnMessagingSettings = new javax.swing.JPanel(); lbMessageServiceSettings = new javax.swing.JLabel(); tbMsgHostname = new javax.swing.JTextField(); @@ -162,6 +168,7 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { tbMsgPassword = new javax.swing.JPasswordField(); bnTestMessageService = new javax.swing.JButton(); lbTestMessageService = new javax.swing.JLabel(); + lbTestMessageWarning = new javax.swing.JLabel(); cbEnableMultiUser = new javax.swing.JCheckBox(); tbOops = new javax.swing.JTextField(); @@ -197,6 +204,9 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { org.openide.awt.Mnemonics.setLocalizedText(lbTestDatabase, org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.lbTestDatabase.text")); // NOI18N lbTestDatabase.setAutoscrolls(true); + lbTestDbWarning.setForeground(new java.awt.Color(255, 0, 0)); + org.openide.awt.Mnemonics.setLocalizedText(lbTestDbWarning, org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.lbTestDbWarning.text")); // NOI18N + javax.swing.GroupLayout pnDatabaseSettingsLayout = new javax.swing.GroupLayout(pnDatabaseSettings); pnDatabaseSettings.setLayout(pnDatabaseSettingsLayout); pnDatabaseSettingsLayout.setHorizontalGroup( @@ -213,7 +223,10 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { .addComponent(lbTestDatabase, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(tbDbPort) .addComponent(tbDbUsername) - .addComponent(tbDbPassword)) + .addComponent(tbDbPassword) + .addGroup(pnDatabaseSettingsLayout.createSequentialGroup() + .addComponent(lbTestDbWarning) + .addGap(0, 0, Short.MAX_VALUE))) .addContainerGap()) ); pnDatabaseSettingsLayout.setVerticalGroup( @@ -232,7 +245,9 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { .addComponent(tbDbUsername, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(tbDbPassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(13, 13, 13)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lbTestDbWarning, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap()) ); pnSolrSettings.setBorder(javax.swing.BorderFactory.createEtchedBorder()); @@ -255,6 +270,9 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { org.openide.awt.Mnemonics.setLocalizedText(lbTestSolr, org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.lbTestSolr.text")); // NOI18N + lbTestSolrWarning.setForeground(new java.awt.Color(255, 0, 0)); + org.openide.awt.Mnemonics.setLocalizedText(lbTestSolrWarning, org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.lbTestSolrWarning.text")); // NOI18N + javax.swing.GroupLayout pnSolrSettingsLayout = new javax.swing.GroupLayout(pnSolrSettings); pnSolrSettings.setLayout(pnSolrSettingsLayout); pnSolrSettingsLayout.setHorizontalGroup( @@ -269,7 +287,10 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { .addComponent(bnTestSolr) .addGap(18, 18, 18) .addComponent(lbTestSolr, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(tbSolrPort)) + .addComponent(tbSolrPort) + .addGroup(pnSolrSettingsLayout.createSequentialGroup() + .addComponent(lbTestSolrWarning) + .addGap(0, 0, Short.MAX_VALUE))) .addContainerGap()) ); pnSolrSettingsLayout.setVerticalGroup( @@ -285,7 +306,9 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { .addComponent(tbSolrHostname, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(tbSolrPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(45, 45, 45)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lbTestSolrWarning, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap()) ); pnMessagingSettings.setBorder(javax.swing.BorderFactory.createEtchedBorder()); @@ -318,6 +341,9 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { org.openide.awt.Mnemonics.setLocalizedText(lbTestMessageService, org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.lbTestMessageService.text")); // NOI18N + lbTestMessageWarning.setForeground(new java.awt.Color(255, 0, 0)); + org.openide.awt.Mnemonics.setLocalizedText(lbTestMessageWarning, org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.lbTestMessageWarning.text")); // NOI18N + javax.swing.GroupLayout pnMessagingSettingsLayout = new javax.swing.GroupLayout(pnMessagingSettings); pnMessagingSettings.setLayout(pnMessagingSettingsLayout); pnMessagingSettingsLayout.setHorizontalGroup( @@ -334,7 +360,10 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { .addComponent(lbTestMessageService, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(tbMsgPort) .addComponent(tbMsgUsername) - .addComponent(tbMsgPassword)) + .addComponent(tbMsgPassword) + .addGroup(pnMessagingSettingsLayout.createSequentialGroup() + .addComponent(lbTestMessageWarning) + .addGap(0, 0, Short.MAX_VALUE))) .addContainerGap()) ); pnMessagingSettingsLayout.setVerticalGroup( @@ -346,7 +375,7 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { .addComponent(bnTestMessageService, javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(lbMessageServiceSettings)) .addComponent(lbTestMessageService, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(tbMsgHostname, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(tbMsgPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -354,7 +383,9 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { .addComponent(tbMsgUsername, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(tbMsgPassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap()) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lbTestMessageWarning, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); org.openide.awt.Mnemonics.setLocalizedText(cbEnableMultiUser, org.openide.util.NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.cbEnableMultiUser.text")); // NOI18N @@ -395,21 +426,21 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(pnDatabaseSettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(pnSolrSettings, javax.swing.GroupLayout.PREFERRED_SIZE, 106, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(pnSolrSettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(pnMessagingSettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap(39, Short.MAX_VALUE)) ); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(pnOverallPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(pnOverallPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 555, javax.swing.GroupLayout.PREFERRED_SIZE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(pnOverallPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(pnOverallPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 559, javax.swing.GroupLayout.PREFERRED_SIZE) ); }// //GEN-END:initComponents @@ -433,6 +464,9 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { lbTestSolr.setIcon(null); bnTestMessageService.setEnabled(false); lbTestMessageService.setIcon(null); + lbTestDbWarning.setText(""); + lbTestSolrWarning.setText(""); + lbTestMessageWarning.setText(""); } enableMultiUserComponents(textBoxes, cbEnableMultiUser.isSelected()); controller.changed(); @@ -440,50 +474,76 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { private void bnTestDatabaseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnTestDatabaseActionPerformed lbTestDatabase.setIcon(null); + lbTestDbWarning.setText(""); setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - CaseDbConnectionInfo dbInfo = new CaseDbConnectionInfo( - this.tbDbHostname.getText(), - this.tbDbPort.getText(), - this.tbDbUsername.getText(), - new String(this.tbDbPassword.getPassword()), - DbType.POSTGRESQL); - if (dbInfo.canConnect()) { + try { + SleuthkitCase.tryConnect(this.tbDbHostname.getText().trim(), + this.tbDbPort.getText().trim(), + this.tbDbUsername.getText().trim(), + new String(this.tbDbPassword.getPassword()), + DbType.POSTGRESQL); lbTestDatabase.setIcon(goodIcon); - } else { + lbTestDbWarning.setText(""); + } catch (ClassNotFoundException | SQLException | TskCoreException ex) { lbTestDatabase.setIcon(badIcon); + lbTestDbWarning.setText(SleuthkitCase.getUserWarning(ex, this.tbDbHostname.getText().trim())); + } finally { + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } - setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); }//GEN-LAST:event_bnTestDatabaseActionPerformed private void bnTestMessageServiceActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnTestMessageServiceActionPerformed lbTestMessageService.setIcon(null); + lbTestMessageWarning.setText(""); setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - MessageServiceConnectionInfo messagingConnectionInfo = new MessageServiceConnectionInfo( - this.tbMsgUsername.getText(), - new String(this.tbMsgPassword.getPassword()), - this.tbMsgHostname.getText(), - this.tbMsgPort.getText()); - if (messagingConnectionInfo.canConnect()) { + MessageServiceConnectionInfo info = new MessageServiceConnectionInfo( + this.tbMsgHostname.getText().trim(), + this.tbMsgPort.getText().trim(), + this.tbMsgUsername.getText().trim(), + new String(this.tbMsgPassword.getPassword())); + try { + info.tryConnect(); lbTestMessageService.setIcon(goodIcon); - } else { + lbTestMessageWarning.setText(""); + } catch (JMSException | URISyntaxException | TskCoreException ex) { lbTestMessageService.setIcon(badIcon); + lbTestMessageWarning.setText(info.getUserWarning(ex, tbMsgHostname.getText().trim())); + } finally { + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } - setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); }//GEN-LAST:event_bnTestMessageServiceActionPerformed private void bnTestSolrActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnTestSolrActionPerformed lbTestSolr.setIcon(null); + lbTestSolrWarning.setText(""); setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class); - if (kwsService != null && kwsService.canConnectToRemoteSolrServer(tbSolrHostname.getText(), tbSolrPort.getText())) { - lbTestSolr.setIcon(goodIcon); - } else { + try { + if (kwsService != null) { + kwsService.tryConnect(tbSolrHostname.getText().trim(), tbSolrPort.getText().trim()); + lbTestSolr.setIcon(goodIcon); + lbTestSolrWarning.setText(""); + } else { + lbTestSolr.setIcon(badIcon); + lbTestSolrWarning.setText(NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.KeywordSearchNull")); + } + } catch (NumberFormatException | IOException | TskCoreException | MissingResourceException ex) { lbTestSolr.setIcon(badIcon); + lbTestSolrWarning.setText(kwsService.getUserWarning(ex, tbSolrHostname.getText().trim())); + } finally { + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } - setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); }//GEN-LAST:event_bnTestSolrActionPerformed void load() { + lbTestDatabase.setIcon(null); + lbTestSolr.setIcon(null); + lbTestMessageService.setIcon(null); + lbTestDbWarning.setText(""); + lbTestSolrWarning.setText(""); + lbTestMessageWarning.setText(""); + CaseDbConnectionInfo dbInfo = UserPreferences.getDatabaseConnectionInfo(); tbDbHostname.setText(dbInfo.getHost().trim()); tbDbPort.setText(dbInfo.getPort().trim()); @@ -524,9 +584,9 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { * @return True or false. */ private boolean databaseFieldsArePopulated() { - return !tbDbHostname.getText().isEmpty() - && !tbDbPort.getText().isEmpty() - && !tbDbUsername.getText().isEmpty() + return !tbDbHostname.getText().trim().isEmpty() + && !tbDbPort.getText().trim().isEmpty() + && !tbDbUsername.getText().trim().isEmpty() && tbDbPassword.getPassword().length != 0; } @@ -537,8 +597,8 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { * @return True or false. */ private boolean solrFieldsArePopulated() { - return !tbSolrHostname.getText().isEmpty() - && !tbSolrPort.getText().isEmpty(); + return !tbSolrHostname.getText().trim().isEmpty() + && !tbSolrPort.getText().trim().isEmpty(); } /** @@ -548,9 +608,9 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { * @return True or false. */ private boolean messageServiceFieldsArePopulated() { - return !tbMsgHostname.getText().isEmpty() - && !tbMsgPort.getText().isEmpty() - && !tbMsgUsername.getText().isEmpty() + return !tbMsgHostname.getText().trim().isEmpty() + && !tbMsgPort.getText().trim().isEmpty() + && !tbMsgUsername.getText().trim().isEmpty() && tbMsgPassword.getPassword().length != 0; } @@ -573,10 +633,11 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { UserPreferences.setDatabaseConnectionInfo(info); MessageServiceConnectionInfo msgServiceInfo = new MessageServiceConnectionInfo( - tbMsgUsername.getText().trim(), - new String(tbMsgPassword.getPassword()), tbMsgHostname.getText().trim(), - tbMsgPort.getText().trim()); + tbMsgPort.getText().trim(), + tbMsgUsername.getText().trim(), + new String(tbMsgPassword.getPassword())); + UserPreferences.setMessageServiceConnectionInfo(msgServiceInfo); UserPreferences.setIndexingServerHost(tbSolrHostname.getText().trim()); @@ -638,7 +699,7 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { * @return True or false. */ boolean databaseSettingsAreValid() { - if (portNumberIsValid(tbDbPort.getText())) { + if (portNumberIsValid(tbDbPort.getText().trim())) { return true; } else { tbOops.setText(INVALID_DB_PORT_MSG); @@ -652,7 +713,7 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { * @return True or false. */ boolean messageServiceSettingsAreValid() { - if (!portNumberIsValid(tbMsgPort.getText())) { + if (!portNumberIsValid(tbMsgPort.getText().trim())) { tbOops.setText(INVALID_MESSAGE_SERVICE_PORT_MSG); return false; } @@ -666,7 +727,7 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { * @return True or false. */ boolean indexingServerSettingsAreValid() { - if (!portNumberIsValid(tbSolrPort.getText())) { + if (!portNumberIsValid(tbSolrPort.getText().trim())) { tbOops.setText(INVALID_INDEXING_SERVER_PORT_MSG); return false; } @@ -703,8 +764,11 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { private javax.swing.JLabel lbMessageServiceSettings; private javax.swing.JLabel lbSolrSettings; private javax.swing.JLabel lbTestDatabase; + private javax.swing.JLabel lbTestDbWarning; private javax.swing.JLabel lbTestMessageService; + private javax.swing.JLabel lbTestMessageWarning; private javax.swing.JLabel lbTestSolr; + private javax.swing.JLabel lbTestSolrWarning; private javax.swing.JPanel pnDatabaseSettings; private javax.swing.JPanel pnMessagingSettings; private javax.swing.JPanel pnOverallPanel; diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanelController.java b/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanelController.java index 80b3f9bb58..8cb2ce53a3 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanelController.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanelController.java @@ -27,6 +27,7 @@ import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import java.util.logging.Level; +import org.openide.util.NbPreferences; import org.sleuthkit.autopsy.coreutils.Logger; @OptionsPanelController.TopLevelRegistration(categoryName = "#OptionsCategory_Name_Multi_User_Settings", diff --git a/Core/src/org/sleuthkit/autopsy/events/Bundle.properties b/Core/src/org/sleuthkit/autopsy/events/Bundle.properties new file mode 100755 index 0000000000..9552399239 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/events/Bundle.properties @@ -0,0 +1,18 @@ +MessageServiceConnectionInfo.tbMsgPort.toolTipText=Port Number +MessageServiceConnectionInfo.tbMsgPort.text= +MessageServiceConnectionInfo.tbMsgUsername.toolTipText=User Name +MessageServiceConnectionInfo.tbMsgUsername.text= +MessageServiceConnectionInfo.tbMsgPassword.toolTipText=Password +MessageServiceConnectionInfo.tbMsgPassword.text= +MessageServiceConnectionInfo.tbMsgHostname.toolTipText=Hostname or IP Address +MessageServiceConnectionInfo.tbMsgHostname.text= +MessageServiceConnectionInfo.ConnectionCheck.Everything=Check hostname, port number, username, and password. +MessageServiceConnectionInfo.ConnectionCheck.Hostname=Check hostname. +MessageServiceConnectionInfo.ConnectionCheck.Port=Check port number. +MessageServiceConnectionInfo.ConnectionCheck.Username=Check username. +MessageServiceConnectionInfo.ConnectionCheck.Password=Check password. +MessageServiceConnectionInfo.ConnectionCheck.HostnameOrPort=Check hostname and/or port number. +MessageServiceConnectionInfo.MissingHostname=Missing hostname. +MessageServiceConnectionInfo.MissingPort=Missing port number. +MessageServiceConnectionInfo.MissingUsername=Missing username. +MessageServiceConnectionInfo.MissingPassword=Missing password. \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/events/MessageServiceConnectionInfo.java b/Core/src/org/sleuthkit/autopsy/events/MessageServiceConnectionInfo.java index acb20776bc..0cd0ac4816 100644 --- a/Core/src/org/sleuthkit/autopsy/events/MessageServiceConnectionInfo.java +++ b/Core/src/org/sleuthkit/autopsy/events/MessageServiceConnectionInfo.java @@ -24,6 +24,11 @@ import javax.annotation.concurrent.Immutable; import javax.jms.Connection; import javax.jms.JMSException; import org.apache.activemq.ActiveMQConnectionFactory; +import java.io.IOException; +import java.net.InetAddress; +import java.util.MissingResourceException; +import org.openide.util.NbBundle; +import org.sleuthkit.datamodel.TskCoreException; /** * Connection info for a Java Message Service (JMS) provider. Thread-safe. @@ -32,6 +37,9 @@ import org.apache.activemq.ActiveMQConnectionFactory; public final class MessageServiceConnectionInfo { private static final String MESSAGE_SERVICE_URI = "tcp://%s:%s?wireFormat.maxInactivityDuration=0"; + private static final String CONNECTION_TIMED_OUT = "connection timed out"; + private static final String CONNECTION_REFUSED = "connection refused"; + private static final int IS_REACHABLE_TIMEOUT_MS = 1000; private final String userName; private final String password; private final String host; @@ -41,17 +49,18 @@ public final class MessageServiceConnectionInfo { * Constructs an object containing connection info for a Java Message * Service (JMS) provider. * - * @param userName The user name to use for a message service connection. - * @param password The password to use for a message service connection. * @param host The host to use for a message service connection. May be * a host name or an IP address. * @param port The port number to use for a message service connection. + * @param userName The user name to use for a message service connection. + * @param password The password to use for a message service connection. + * */ - public MessageServiceConnectionInfo(String userName, String password, String host, String port) { - this.userName = userName; - this.password = password; + public MessageServiceConnectionInfo(String host, String port, String userName, String password) { this.host = host; this.port = port; + this.userName = userName; + this.password = password; } /** @@ -106,17 +115,76 @@ public final class MessageServiceConnectionInfo { /** * Verifies connection to messaging service. * - * @return True if connection can be established, false otherwise. + * @return throws if we cannot communicate with ActiveMQ. + * + * @throws java.net.URISyntaxException + * @throws javax.jms.JMSException */ - public boolean canConnect() { - try { - ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(getUserName(), getPassword(), getURI()); - Connection connection = connectionFactory.createConnection(getUserName(), getPassword()); - connection.start(); - connection.close(); - return true; - } catch (URISyntaxException | JMSException ex) { - return false; + public void tryConnect() throws URISyntaxException, JMSException, TskCoreException { + if (host == null || host.isEmpty()) { + throw new TskCoreException(NbBundle.getMessage(MessageServiceConnectionInfo.class, "MessageServiceConnectionInfo.MissingHostname")); //NON-NLS + } else if (port == null || port.isEmpty()) { + throw new TskCoreException(NbBundle.getMessage(MessageServiceConnectionInfo.class, "MessageServiceConnectionInfo.MissingPort")); //NON-NLS + } else if (userName == null || userName.isEmpty()) { + throw new TskCoreException(NbBundle.getMessage(MessageServiceConnectionInfo.class, "MessageServiceConnectionInfo.MissingUsername")); //NON-NLS + } else if (password == null || password.isEmpty()) { + throw new TskCoreException(NbBundle.getMessage(MessageServiceConnectionInfo.class, "MessageServiceConnectionInfo.MissingPassword")); //NON-NLS } + + URI uri = new URI(String.format(MESSAGE_SERVICE_URI, getHost(), getPort())); + ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(getUserName(), getPassword(), uri); + Connection connection = connectionFactory.createConnection(getUserName(), getPassword()); + connection.start(); + connection.close(); + } + + /** + * This method handles exceptions from the ActiveMQ tester, returning the + * appropriate text for the exception received. + * + * @param ex the Exception to analyze + * @param ipAddress the IP address to check against + * + * @return returns the String message to show the user + */ + public String getUserWarning(Exception ex, String ipAddress) { + String result = NbBundle.getMessage(MessageServiceConnectionInfo.class, "MessageServiceConnectionInfo.ConnectionCheck.Everything"); //NON-NLS + + if (ex instanceof JMSException) { + Throwable cause = ex.getCause(); + if (cause != null) { + // there is more information from another exception + String msg = cause.getMessage(); + if (msg.startsWith(CONNECTION_TIMED_OUT)) { + // The hostname or IP address seems bad + result = NbBundle.getMessage(MessageServiceConnectionInfo.class, "MessageServiceConnectionInfo.ConnectionCheck.Hostname"); //NON-NLS + } else if (msg.toLowerCase().startsWith(CONNECTION_REFUSED)) { + // The port seems bad + result = NbBundle.getMessage(MessageServiceConnectionInfo.class, "MessageServiceConnectionInfo.ConnectionCheck.Port"); //NON-NLS + } else { + // Could be either hostname or port number + result = NbBundle.getMessage(MessageServiceConnectionInfo.class, "MessageServiceConnectionInfo.ConnectionCheck.HostnameOrPort"); //NON-NLS + } + } else { + // there is no more information from another exception + try { + if (InetAddress.getByName(ipAddress).isReachable(IS_REACHABLE_TIMEOUT_MS)) { + // if we can reach the host, then it's probably a port problem + result = NbBundle.getMessage(MessageServiceConnectionInfo.class, "MessageServiceConnectionInfo.ConnectionCheck.Port"); //NON-NLS + } else { + result = NbBundle.getMessage(MessageServiceConnectionInfo.class, "MessageServiceConnectionInfo.ConnectionCheck.Hostname"); //NON-NLS + } + } catch (IOException | MissingResourceException any) { + // it may be anything + result = NbBundle.getMessage(MessageServiceConnectionInfo.class, "MessageServiceConnectionInfo.ConnectionCheck.Everything"); //NON-NLS + } + } + } else if (ex instanceof URISyntaxException) { + // The hostname or port seems bad + result = NbBundle.getMessage(MessageServiceConnectionInfo.class, "MessageServiceConnectionInfo.ConnectionCheck.HostnameOrPort"); //NON-NLS + } else if (ex instanceof TskCoreException) { + result = ex.getMessage(); + } + return result; } } diff --git a/Core/src/org/sleuthkit/autopsy/keywordsearchservice/KeywordSearchService.java b/Core/src/org/sleuthkit/autopsy/keywordsearchservice/KeywordSearchService.java index aa32d61916..a3579601d8 100644 --- a/Core/src/org/sleuthkit/autopsy/keywordsearchservice/KeywordSearchService.java +++ b/Core/src/org/sleuthkit/autopsy/keywordsearchservice/KeywordSearchService.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.keywordsearchservice; import java.io.Closeable; +import java.io.IOException; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.TskCoreException; @@ -38,13 +39,26 @@ public interface KeywordSearchService extends Closeable { public void indexArtifact(BlackboardArtifact artifact) throws TskCoreException; /** - * Are we able to connect to the remote Solr server. + * Checks if we can communicate with Solr using the passed-in host and port. + * Closes the connection upon exit. Throws if it cannot communicate with + * Solr. * - * @param host the hostname or IP address of the server - * @param port the port to connect to + * @param host the remote hostname or IP address of the Solr server + * @param port the remote port for Solr * - * @return true if we can connect, otherwise false + * @throws java.io.IOException + * @throws org.sleuthkit.datamodel.TskCoreException */ - public boolean canConnectToRemoteSolrServer(String host, String port); + public void tryConnect(String host, String port) throws NumberFormatException, IOException, TskCoreException; + /** + * This method handles exceptions from the connection tester, tryConnect(), + * returning the appropriate user-facing text for the exception received. + * + * @param ex the exception that was returned + * @param ipAddress the IP address to connect to + * + * @return returns the String message to show the user + */ + public String getUserWarning(Exception ex, String ipAddress); } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties index dcf82c78f3..2847b35618 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties @@ -106,7 +106,6 @@ KeywordSearchFilterNode.getFileActions.openExternViewActLbl=Open in External Vie KeywordSearchFilterNode.getFileActions.searchSameMd5=Search for files with the same MD5 hash KeywordSearchFilterNode.getFileActions.viewInNewWinActionLbl=View in New Window KeywordSearchIngestModule.init.badInitMsg=Keyword search server was not properly initialized, cannot run keyword search ingest. -KeywordSearchIngestModule.init.verifyConnection=Please verify credentials and connectivity to multi-user keyword search service. KeywordSearchIngestModule.init.tryStopSolrMsg={0}
Please try stopping old java Solr process (if it exists) and restart the application. KeywordSearchIngestModule.init.noKwInLstMsg=No keywords in keyword list. KeywordSearchIngestModule.init.onlyIdxKwSkipMsg=Only indexing will be done and and keyword search will be skipped (you can still add keyword lists using the Keyword Lists - Add to Ingest). @@ -288,3 +287,8 @@ SearchRunner.Searcher.done.err.msg=Error performing keyword search KeywordSearchGlobalSearchSettingsPanel.timeRadioButton5.toolTipText=Fastest overall, but no results until the end KeywordSearchGlobalSearchSettingsPanel.timeRadioButton5.text=No periodic searches KeywordSearchIngestModule.startUp.fileTypeDetectorInitializationException.msg=Error initializing the file type detector. +SolrConnectionCheck.HostnameOrPort=Check hostname and/or port number. +SolrConnectionCheck.Hostname=Check hostname. +SolrConnectionCheck.Port=Check port number. +SolrConnectionCheck.MissingHostname=Missing hostname. +SolrConnectionCheck.MissingPort=Missing port number. \ No newline at end of file diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java index 861d6f04e0..b5df6bc179 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.keywordsearch; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -147,10 +148,12 @@ public final class KeywordSearchIngestModule implements FileIngestModule { if (Case.getCurrentCase().getCaseType() == Case.CaseType.MULTI_USER_CASE) { // for multi-user cases need to verify connection to remore SOLR server KeywordSearchService kwsService = new SolrSearchService(); - if (!kwsService.canConnectToRemoteSolrServer(UserPreferences.getIndexingServerHost(), UserPreferences.getIndexingServerPort())) { + try { + kwsService.tryConnect(UserPreferences.getIndexingServerHost(), UserPreferences.getIndexingServerPort()); + } catch (NumberFormatException | IOException | TskCoreException ex) { String msg = NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.init.badInitMsg"); - logger.log(Level.SEVERE, msg); - String details = NbBundle.getMessage(this.getClass(), "KeywordSearchIngestModule.init.verifyConnection"); + String details = kwsService.getUserWarning(ex, UserPreferences.getIndexingServerHost()); + logger.log(Level.SEVERE, "{0}: {1}", new Object[]{msg, details}); services.postMessage(IngestMessage.createErrorMessage(KeywordSearchModuleFactory.getModuleName(), msg, details)); throw new IngestModuleException(msg); } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java index 50e4e9ec99..2d61e9fefb 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java @@ -33,6 +33,9 @@ import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.SleuthkitCase; +import org.openide.util.NbBundle; +import java.net.InetAddress; +import java.util.MissingResourceException; /** * An implementation of the KeywordSearchService interface that uses Solr for @@ -41,6 +44,10 @@ import org.sleuthkit.datamodel.SleuthkitCase; @ServiceProvider(service = KeywordSearchService.class) public class SolrSearchService implements KeywordSearchService { + private static final String BAD_IP_ADDRESS_FORMAT = "ioexception occured when talking to server"; + private static final String SERVER_REFUSED_CONNECTION = "server refused connection"; + private static final int IS_REACHABLE_TIMEOUT_MS = 1000; + @Override public void indexArtifact(BlackboardArtifact artifact) throws TskCoreException { if (artifact == null) { @@ -157,39 +164,75 @@ public class SolrSearchService implements KeywordSearchService { /** * Checks if we can communicate with Solr using the passed-in host and port. - * Closes the connection upon exit. + * Closes the connection upon exit. Throws if it cannot communicate with + * Solr. * * @param host the remote hostname or IP address of the Solr server * @param port the remote port for Solr * - * @return true if communication with Solr is functional, false otherwise + * @throws java.io.IOException + * @throws org.sleuthkit.datamodel.TskCoreException */ @Override - public boolean canConnectToRemoteSolrServer(String host, String port) { - if (host.isEmpty() || port.isEmpty()) { - return false; - } - + public void tryConnect(String host, String port) throws NumberFormatException, IOException, TskCoreException { try { - // if the port value is invalid, return false + if (host == null || host.isEmpty()) { + throw new TskCoreException(NbBundle.getMessage(SolrSearchService.class, "SolrConnectionCheck.MissingHostname")); //NON-NLS + } else if (port == null || port.isEmpty()) { + throw new TskCoreException(NbBundle.getMessage(SolrSearchService.class, "SolrConnectionCheck.MissingPort")); //NON-NLS + } + // if the port value is invalid, throw Integer.parseInt(port); - } catch (NumberFormatException ex) { - return false; - } - - HttpSolrServer solrServer = null; - try { - solrServer = new HttpSolrServer("http://" + host + ":" + port + "/solr"); //NON-NLS; + HttpSolrServer solrServer = new HttpSolrServer("http://" + host + ":" + port + "/solr"); //NON-NLS; KeywordSearch.getServer().connectToSolrServer(solrServer); - return true; // if we get here, it's at least up and responding - } catch (SolrServerException | IOException ignore) { - } finally { - if (solrServer != null) { + if (null != solrServer) { solrServer.shutdown(); } + } catch (SolrServerException ex) { + throw new IOException(ex); } + } - return false; + /** + * This method handles exceptions from the connection tester, tryConnect(), + * returning the appropriate user-facing text for the exception received. + * + * @param ex the exception that was returned + * @param ipAddress the IP address to connect to + * + * @return returns the String message to show the user + */ + @Override + public String getUserWarning(Exception ex, String ipAddress) { + + String result = NbBundle.getMessage(SolrSearchService.class, "SolrConnectionCheck.HostnameOrPort"); //NON-NLS + if (ex instanceof IOException) { + String message = ex.getCause().getMessage().toLowerCase(); + if (message.startsWith(SERVER_REFUSED_CONNECTION)) { + try { + if (InetAddress.getByName(ipAddress).isReachable(IS_REACHABLE_TIMEOUT_MS)) { + // if we can reach the host, then it's probably port problem + result = NbBundle.getMessage(SolrSearchService.class, "SolrConnectionCheck.Port"); //NON-NLS + } else { + result = NbBundle.getMessage(SolrSearchService.class, "SolrConnectionCheck.HostnameOrPort"); //NON-NLS + } + } catch (IOException | MissingResourceException any) { + // it may be anything + result = NbBundle.getMessage(SolrSearchService.class, "SolrConnectionCheck.HostnameOrPort"); //NON-NLS + } + } else if (message.startsWith(BAD_IP_ADDRESS_FORMAT)) { + result = NbBundle.getMessage(SolrSearchService.class, "SolrConnectionCheck.Hostname"); //NON-NLS + } + } else if (ex instanceof SolrServerException) { + result = NbBundle.getMessage(SolrSearchService.class, "SolrConnectionCheck.HostnameOrPort"); //NON-NLS + } else if (ex instanceof NumberFormatException) { + result = NbBundle.getMessage(SolrSearchService.class, "SolrConnectionCheck.Port"); //NON-NLS + } else if (ex instanceof TskCoreException) { + result = ex.getMessage(); + } else { + result = ex.getMessage(); + } + return result; } @Override