Refactored service monitor to perform on-demand checks when service status is unknown

This commit is contained in:
Eugene Livis 2015-07-07 16:38:56 -04:00
parent f4bc81f93e
commit c9f2863e27

View File

@ -38,7 +38,6 @@ import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.events.AutopsyEventPublisher; import org.sleuthkit.autopsy.events.AutopsyEventPublisher;
import org.sleuthkit.autopsy.events.MessageServiceConnectionInfo; import org.sleuthkit.autopsy.events.MessageServiceConnectionInfo;
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService; import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService;
import org.sleuthkit.datamodel.CaseDbConnectionInfo;
/** /**
* This class periodically checks availability of collaboration resources - * This class periodically checks availability of collaboration resources -
@ -61,7 +60,8 @@ public class ServicesMonitor {
.collect(Collectors.toSet()); .collect(Collectors.toSet());
/** /**
* The service monitor maintains a mapping of each service to it's last status update. * The service monitor maintains a mapping of each service to it's last
* status update.
*/ */
private final ConcurrentHashMap<String, String> statusByService; private final ConcurrentHashMap<String, String> statusByService;
@ -77,13 +77,12 @@ public class ServicesMonitor {
* New value is set to updated ServiceStatus, old value is null. * New value is set to updated ServiceStatus, old value is null.
*/ */
REMOTE_CASE_DATABASE, REMOTE_CASE_DATABASE,
/** /**
* Property change event fired when remote keyword search service status changes. * Property change event fired when remote keyword search service status
* New value is set to updated ServiceStatus, old value is null. * changes. New value is set to updated ServiceStatus, old value is
* null.
*/ */
REMOTE_KEYWORD_SEARCH, REMOTE_KEYWORD_SEARCH,
/** /**
* Property change event fired when messaging service status changes. * Property change event fired when messaging service status changes.
* New value is set to updated ServiceStatus, old value is null. * New value is set to updated ServiceStatus, old value is null.
@ -100,11 +99,14 @@ public class ServicesMonitor {
* Service is currently up. * Service is currently up.
*/ */
UP, UP,
/** /**
* Service is currently down. * Service is currently down.
*/ */
DOWN DOWN,
/**
* Service status is unknown.
*/
UNKNOWN,
}; };
public synchronized static ServicesMonitor getInstance() { public synchronized static ServicesMonitor getInstance() {
@ -118,10 +120,8 @@ public class ServicesMonitor {
this.eventPublisher = new AutopsyEventPublisher(); this.eventPublisher = new AutopsyEventPublisher();
this.statusByService = new ConcurrentHashMap<>(); this.statusByService = new ConcurrentHashMap<>();
// Services are assumed to be "UP" by default. See CrashDetectionTask class for more info.
for (String serviceName : serviceNames) { for (String serviceName : serviceNames) {
this.statusByService.put(serviceName, ServiceStatus.UP.toString()); this.statusByService.put(serviceName, ServiceStatus.UNKNOWN.toString());
} }
/** /**
@ -161,12 +161,56 @@ public class ServicesMonitor {
if (status == null) { if (status == null) {
// no such service // no such service
throw new UnknownServiceException("Requested service name " + service + " is unknown"); throw new UnknownServiceException("Requested service name " + service + " is unknown");
} else if (status.equals(ServiceStatus.UNKNOWN.toString())) {
// status for the service is not known. This is likely because we haven't
// checked it's status yet. Perform an on-demand check of the service status.
status = checkServiceStatusStatus(service);
} }
return status; return status;
} }
/** /**
* Publish an event signifying change in service status. Event is published locally. * Performs on-demand check of service availability.
*
* @param service Name of the service.
* @return String Status for the service.
*/
private String checkServiceStatusStatus(String service) {
if (service.equals(ServiceName.REMOTE_CASE_DATABASE.toString())) {
if (canConnectToRemoteDb()) {
setServiceStatus(ServiceName.REMOTE_CASE_DATABASE.toString(), ServiceStatus.UP.toString());
return ServiceStatus.UP.toString();
} else {
setServiceStatus(ServiceName.REMOTE_CASE_DATABASE.toString(), ServiceStatus.DOWN.toString());
return ServiceStatus.DOWN.toString();
}
} else if (service.equals(ServiceName.REMOTE_KEYWORD_SEARCH.toString())){
KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class);
// TODO - do I need to check for kwsService == null?
if (kwsService.canConnectToRemoteSolrServer()) {
setServiceStatus(ServiceName.REMOTE_KEYWORD_SEARCH.toString(), ServiceStatus.UP.toString());
return ServiceStatus.UP.toString();
} else {
setServiceStatus(ServiceName.REMOTE_KEYWORD_SEARCH.toString(), ServiceStatus.DOWN.toString());
return ServiceStatus.DOWN.toString();
}
} else if (service.equals(ServiceName.MESSAGING.toString())) {
if (canConnectToMessagingService()) {
setServiceStatus(ServiceName.MESSAGING.toString(), ServiceStatus.UP.toString());
return ServiceStatus.UP.toString();
} else {
setServiceStatus(ServiceName.MESSAGING.toString(), ServiceStatus.DOWN.toString());
return ServiceStatus.DOWN.toString();
}
}
return ServiceStatus.UNKNOWN.toString();
}
/**
* Publish an event signifying change in service status. Event is published
* locally.
* *
* @param service Name of the service. * @param service Name of the service.
* @param status Updated status for the event. * @param status Updated status for the event.
@ -246,20 +290,41 @@ public class ServicesMonitor {
eventPublisher.removeSubscriber(serviceNames, subscriber); eventPublisher.removeSubscriber(serviceNames, subscriber);
} }
/**
* Verifies connection to remote database.
*
* @return True if connection can be established, false otherwise.
*/
private boolean canConnectToRemoteDb() {
return UserPreferences.getDatabaseConnectionInfo().canConnect();
}
/**
* Verifies connection to messaging service.
*
* @return True if connection can be established, false otherwise.
*/
private boolean canConnectToMessagingService() {
MessageServiceConnectionInfo msgInfo = UserPreferences.getMessageServiceConnectionInfo();
try {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(msgInfo.getUserName(), msgInfo.getPassword(), msgInfo.getURI());
Connection connection = connectionFactory.createConnection();
connection.start();
connection.close();
return true;
} catch (URISyntaxException | JMSException ex) {
return false;
}
}
/** /**
* A Runnable task that periodically checks the availability of * A Runnable task that periodically checks the availability of
* collaboration resources (PostgreSQL server, Solr server, Active MQ * collaboration resources (remote database, remote keyword search service,
* message broker) and reports status to the user in case of a gap in * message broker) and reports status to the user in case of a gap in
* service. * service.
*/ */
private final class CrashDetectionTask implements Runnable { private final class CrashDetectionTask implements Runnable {
// Services are assumed to be "UP" by default. Change default value in ServicesMonitor()
// constructor if this assumption changes.
private boolean dbServerIsRunning = true;
private boolean solrServerIsRunning = true;
private boolean messageServerIsRunning = true;
private final Object lock = new Object(); private final Object lock = new Object();
/** /**
@ -268,17 +333,15 @@ public class ServicesMonitor {
@Override @Override
public void run() { public void run() {
synchronized (lock) { synchronized (lock) {
CaseDbConnectionInfo dbInfo = UserPreferences.getDatabaseConnectionInfo(); try {
if (dbInfo.canConnect()) { if (canConnectToRemoteDb()) {
if (!dbServerIsRunning) { if (!getServiceStatus(ServiceName.REMOTE_CASE_DATABASE.toString()).equals(ServiceStatus.UP.toString())) {
dbServerIsRunning = true;
logger.log(Level.INFO, "Connection to PostgreSQL server restored"); //NON-NLS logger.log(Level.INFO, "Connection to PostgreSQL server restored"); //NON-NLS
MessageNotifyUtil.Notify.info(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredService.notify.title"), NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredDbService.notify.msg")); MessageNotifyUtil.Notify.info(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredService.notify.title"), NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredDbService.notify.msg"));
setServiceStatus(ServiceName.REMOTE_CASE_DATABASE.toString(), ServiceStatus.UP.toString()); setServiceStatus(ServiceName.REMOTE_CASE_DATABASE.toString(), ServiceStatus.UP.toString());
} }
} else { } else {
if (dbServerIsRunning) { if (!getServiceStatus(ServiceName.REMOTE_CASE_DATABASE.toString()).equals(ServiceStatus.DOWN.toString())) {
dbServerIsRunning = false;
logger.log(Level.SEVERE, "Failed to connect to PostgreSQL server"); //NON-NLS logger.log(Level.SEVERE, "Failed to connect to PostgreSQL server"); //NON-NLS
MessageNotifyUtil.Notify.error(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedService.notify.title"), NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedDbService.notify.msg")); MessageNotifyUtil.Notify.error(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedService.notify.title"), NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedDbService.notify.msg"));
setServiceStatus(ServiceName.REMOTE_CASE_DATABASE.toString(), ServiceStatus.DOWN.toString()); setServiceStatus(ServiceName.REMOTE_CASE_DATABASE.toString(), ServiceStatus.DOWN.toString());
@ -288,41 +351,36 @@ public class ServicesMonitor {
KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class); KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class);
// TODO - do I need to check for kwsService == null? // TODO - do I need to check for kwsService == null?
if (kwsService.canConnectToRemoteSolrServer()) { if (kwsService.canConnectToRemoteSolrServer()) {
if (!solrServerIsRunning) { if (!getServiceStatus(ServiceName.REMOTE_KEYWORD_SEARCH.toString()).equals(ServiceStatus.UP.toString())) {
solrServerIsRunning = true;
logger.log(Level.INFO, "Connection to Solr server restored"); //NON-NLS logger.log(Level.INFO, "Connection to Solr server restored"); //NON-NLS
MessageNotifyUtil.Notify.info(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredService.notify.title"), NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredSolrService.notify.msg")); MessageNotifyUtil.Notify.info(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredService.notify.title"), NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredSolrService.notify.msg"));
setServiceStatus(ServiceName.REMOTE_KEYWORD_SEARCH.toString(), ServiceStatus.UP.toString()); setServiceStatus(ServiceName.REMOTE_KEYWORD_SEARCH.toString(), ServiceStatus.UP.toString());
} }
} else { } else {
if (solrServerIsRunning) { if (!getServiceStatus(ServiceName.REMOTE_KEYWORD_SEARCH.toString()).equals(ServiceStatus.DOWN.toString())) {
solrServerIsRunning = false;
logger.log(Level.SEVERE, "Failed to connect to Solr server"); //NON-NLS logger.log(Level.SEVERE, "Failed to connect to Solr server"); //NON-NLS
MessageNotifyUtil.Notify.error(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedService.notify.title"), NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedSolrService.notify.msg")); MessageNotifyUtil.Notify.error(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedService.notify.title"), NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedSolrService.notify.msg"));
setServiceStatus(ServiceName.REMOTE_KEYWORD_SEARCH.toString(), ServiceStatus.DOWN.toString()); setServiceStatus(ServiceName.REMOTE_KEYWORD_SEARCH.toString(), ServiceStatus.DOWN.toString());
} }
} }
MessageServiceConnectionInfo msgInfo = UserPreferences.getMessageServiceConnectionInfo(); if (canConnectToMessagingService()) {
try { if (!getServiceStatus(ServiceName.MESSAGING.toString()).equals(ServiceStatus.UP.toString())) {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(msgInfo.getUserName(), msgInfo.getPassword(), msgInfo.getURI());
Connection connection = connectionFactory.createConnection();
connection.start();
connection.close();
if (!messageServerIsRunning) {
messageServerIsRunning = true;
logger.log(Level.INFO, "Connection to ActiveMQ server restored"); //NON-NLS logger.log(Level.INFO, "Connection to ActiveMQ server restored"); //NON-NLS
MessageNotifyUtil.Notify.info(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredService.notify.title"), NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredMessageService.notify.msg")); MessageNotifyUtil.Notify.info(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredService.notify.title"), NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredMessageService.notify.msg"));
setServiceStatus(ServiceName.MESSAGING.toString(), ServiceStatus.UP.toString()); setServiceStatus(ServiceName.MESSAGING.toString(), ServiceStatus.UP.toString());
} }
} catch (URISyntaxException | JMSException ex) { } else {
if (messageServerIsRunning) { if (!getServiceStatus(ServiceName.MESSAGING.toString()).equals(ServiceStatus.DOWN.toString())) {
messageServerIsRunning = false; logger.log(Level.SEVERE, "Failed to connect to ActiveMQ server"); //NON-NLS
logger.log(Level.SEVERE, "Failed to connect to ActiveMQ server", ex); //NON-NLS
MessageNotifyUtil.Notify.error(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedService.notify.title"), NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedMessageService.notify.msg")); MessageNotifyUtil.Notify.error(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedService.notify.title"), NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedMessageService.notify.msg"));
setServiceStatus(ServiceName.MESSAGING.toString(), ServiceStatus.DOWN.toString()); setServiceStatus(ServiceName.MESSAGING.toString(), ServiceStatus.DOWN.toString());
} }
} }
} catch (UnknownServiceException ex) {
logger.log(Level.SEVERE, "Exception while checking current service status", ex); //NON-NLS
}
} }
} }
} }