mirror of
https://github.com/elisspace/autopsy.git
synced 2026-09-01 00:43:53 +00:00
Tidy up the ServiceMonitor class
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-2015 Basis Technology Corp.
|
||||
* Copyright 2013-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -42,65 +42,29 @@ import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* This class periodically checks availability of collaboration resources -
|
||||
* remote database, remote keyword search server, messaging service - and
|
||||
* reports status updates to the user in case of a gap in service.
|
||||
* Monitors the status of services and publishes events and user notifications
|
||||
* when the status of a service changes. The database server, keyword search
|
||||
* server, and messaging service are considered to be core services in a
|
||||
* collaborative, multi-user case environment. Additional services can provide
|
||||
* current status by calling the setServiceStatus() method.
|
||||
*/
|
||||
public class ServicesMonitor {
|
||||
|
||||
private AutopsyEventPublisher eventPublisher;
|
||||
private static final Logger logger = Logger.getLogger(ServicesMonitor.class.getName());
|
||||
private final ScheduledThreadPoolExecutor periodicTasksExecutor;
|
||||
|
||||
private static final String PERIODIC_TASK_THREAD_NAME = "services-monitor-periodic-task-%d"; //NON-NLS
|
||||
private static final int NUMBER_OF_PERIODIC_TASK_THREADS = 1;
|
||||
private static final long CRASH_DETECTION_INTERVAL_MINUTES = 15;
|
||||
|
||||
private static final Set<String> servicesList = Stream.of(ServicesMonitor.Service.values())
|
||||
.map(Service::toString)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
/**
|
||||
* The service monitor maintains a mapping of each service to it's last
|
||||
* status update.
|
||||
*/
|
||||
private final ConcurrentHashMap<String, String> statusByService;
|
||||
|
||||
/**
|
||||
* Call constructor on start-up so that the first check of services is done
|
||||
* as soon as possible.
|
||||
*/
|
||||
private static ServicesMonitor instance = new ServicesMonitor();
|
||||
|
||||
/**
|
||||
* List of services that are being monitored. The service names should be
|
||||
* representative of the service functionality and readable as they get
|
||||
* logged when service outage occurs.
|
||||
* An enumeration of the core services in a collaborative, multi-user case
|
||||
* environment. The display names provided here can be used to identify the
|
||||
* service status events published for these services and to directly query
|
||||
* the ServicesMonitor for the current status of these services.
|
||||
*/
|
||||
public enum Service {
|
||||
|
||||
/**
|
||||
* Property change event fired when remote case database service status
|
||||
* changes. New value is set to updated ServiceStatus, old value is
|
||||
* null.
|
||||
*/
|
||||
REMOTE_CASE_DATABASE(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.remoteCaseDatabase.displayName.text")),
|
||||
/**
|
||||
* Property change event fired when remote keyword search service status
|
||||
* changes. New value is set to updated ServiceStatus, old value is
|
||||
* null.
|
||||
*/
|
||||
REMOTE_KEYWORD_SEARCH(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.remoteKeywordSearch.displayName.text")),
|
||||
/**
|
||||
* Property change event fired when messaging service status changes.
|
||||
* New value is set to updated ServiceStatus, old value is null.
|
||||
*/
|
||||
MESSAGING(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.messaging.displayName.text"));
|
||||
|
||||
private final String displayName;
|
||||
|
||||
private Service(String name) {
|
||||
this.displayName = name;
|
||||
private Service(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
@@ -109,121 +73,249 @@ public class ServicesMonitor {
|
||||
};
|
||||
|
||||
/**
|
||||
* List of possible service statuses.
|
||||
* An enumeration of the standard service statuses.
|
||||
*/
|
||||
public enum ServiceStatus {
|
||||
|
||||
/**
|
||||
* Service is currently up.
|
||||
*/
|
||||
UP,
|
||||
/**
|
||||
* Service is currently down.
|
||||
*/
|
||||
DOWN
|
||||
};
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ServicesMonitor.class.getName());
|
||||
private static final String PERIODIC_TASK_THREAD_NAME = "services-monitor-periodic-task-%d"; //NON-NLS
|
||||
private static final int NUMBER_OF_PERIODIC_TASK_THREADS = 1;
|
||||
private static final long CRASH_DETECTION_INTERVAL_MINUTES = 15;
|
||||
private static final Set<String> coreServices = Stream.of(ServicesMonitor.Service.values()).map(Service::toString).collect(Collectors.toSet());
|
||||
private static ServicesMonitor servicesMonitor = new ServicesMonitor();
|
||||
private final ScheduledThreadPoolExecutor periodicTasksExecutor;
|
||||
private final ConcurrentHashMap<String, String> statusByService;
|
||||
private final AutopsyEventPublisher eventPublisher;
|
||||
|
||||
/**
|
||||
* Gets the services monitor that monitors the status of services and
|
||||
* publishes events and user notifications when the status of a service
|
||||
* changes.
|
||||
*
|
||||
* @return The services monitor singleton.
|
||||
*/
|
||||
public synchronized static ServicesMonitor getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new ServicesMonitor();
|
||||
if (servicesMonitor == null) {
|
||||
servicesMonitor = new ServicesMonitor();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private ServicesMonitor() {
|
||||
|
||||
this.eventPublisher = new AutopsyEventPublisher();
|
||||
this.statusByService = new ConcurrentHashMap<>();
|
||||
|
||||
// First check is triggered immediately on current thread.
|
||||
checkAllServices();
|
||||
|
||||
/**
|
||||
* Start periodic task that check the availability of key collaboration
|
||||
* services.
|
||||
*/
|
||||
periodicTasksExecutor = new ScheduledThreadPoolExecutor(NUMBER_OF_PERIODIC_TASK_THREADS, new ThreadFactoryBuilder().setNameFormat(PERIODIC_TASK_THREAD_NAME).build());
|
||||
periodicTasksExecutor.scheduleWithFixedDelay(new CrashDetectionTask(), CRASH_DETECTION_INTERVAL_MINUTES, CRASH_DETECTION_INTERVAL_MINUTES, TimeUnit.MINUTES);
|
||||
return servicesMonitor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates service status and publishes the service status update if it is
|
||||
* different from previous status. Event is published locally. Logs status
|
||||
* Constructs a services monitor that monitors the status of services and
|
||||
* publishes events and user notifications when the status of a service
|
||||
* changes.
|
||||
*/
|
||||
private ServicesMonitor() {
|
||||
eventPublisher = new AutopsyEventPublisher();
|
||||
statusByService = new ConcurrentHashMap<>();
|
||||
|
||||
/*
|
||||
* The first service statuses check is performed immediately in the
|
||||
* current thread.
|
||||
*/
|
||||
checkAllServices();
|
||||
|
||||
/**
|
||||
* Start a periodic task to do ongoing service status checks.
|
||||
*/
|
||||
periodicTasksExecutor = new ScheduledThreadPoolExecutor(NUMBER_OF_PERIODIC_TASK_THREADS, new ThreadFactoryBuilder().setNameFormat(PERIODIC_TASK_THREAD_NAME).build());
|
||||
periodicTasksExecutor.scheduleWithFixedDelay(new ServicesMonitoringTask(), CRASH_DETECTION_INTERVAL_MINUTES, CRASH_DETECTION_INTERVAL_MINUTES, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the status of a service and publishes a service status event if
|
||||
* the current status is different from the previously reported status.
|
||||
*
|
||||
* @param service Name of the service.
|
||||
* @param status Updated status for the service.
|
||||
* @param details Details of the event.
|
||||
* @param status Current status of the service.
|
||||
* @param details Additional status details.
|
||||
*
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
// new service or status has changed - identify service's display name
|
||||
statusByService.put(service, status);
|
||||
|
||||
String serviceDisplayName;
|
||||
try {
|
||||
serviceDisplayName = ServicesMonitor.Service.valueOf(service).getDisplayName();
|
||||
} catch (IllegalArgumentException ignore) {
|
||||
// custom service that is not listed in ServicesMonitor.Service enum. Use service name as display name.
|
||||
serviceDisplayName = service;
|
||||
}
|
||||
|
||||
if (status.equals(ServiceStatus.UP.toString())) {
|
||||
logger.log(Level.INFO, "Connection to {0} is up", serviceDisplayName); //NON-NLS
|
||||
MessageNotifyUtil.Notify.info(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredService.notify.title"),
|
||||
MessageNotifyUtil.Notify.info(
|
||||
NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredService.notify.title"),
|
||||
NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredService.notify.msg", serviceDisplayName));
|
||||
} else if (status.equals(ServiceStatus.DOWN.toString())) {
|
||||
logger.log(Level.SEVERE, "Failed to connect to {0}. Reason: {1}", new Object[]{serviceDisplayName, details}); //NON-NLS
|
||||
MessageNotifyUtil.Notify.error(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedService.notify.title"),
|
||||
MessageNotifyUtil.Notify.error(
|
||||
NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedService.notify.title"),
|
||||
NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedService.notify.msg", serviceDisplayName));
|
||||
} else {
|
||||
logger.log(Level.INFO, "Status for {0} is {1}", new Object[]{serviceDisplayName, status}); //NON-NLS
|
||||
MessageNotifyUtil.Notify.info(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.statusChange.notify.title"),
|
||||
NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.statusChange.notify.msg", new Object[]{serviceDisplayName, status}));
|
||||
logger.log(Level.INFO, "Status for {0} is {1} ({2})", new Object[]{serviceDisplayName, status}); //NON-NLS
|
||||
MessageNotifyUtil.Notify.info(
|
||||
NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.statusChange.notify.title"),
|
||||
NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.statusChange.notify.msg", new Object[]{serviceDisplayName, status, details}));
|
||||
}
|
||||
|
||||
// update and publish new status
|
||||
statusByService.put(service, status);
|
||||
eventPublisher.publishLocally(new ServiceEvent(service, status, details));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last status update for a service.
|
||||
* Get last recorded status for a service.
|
||||
*
|
||||
* @param service Name of the service.
|
||||
*
|
||||
* @return ServiceStatus Status for the service.
|
||||
*
|
||||
* @throws ServicesMonitorException If service name is null or service
|
||||
* doesn't exist.
|
||||
* @throws ServicesMonitorException If the service name is unknown to the
|
||||
* services monitor.
|
||||
*/
|
||||
public String getServiceStatus(String service) throws ServicesMonitorException {
|
||||
|
||||
if (service == null) {
|
||||
if (service == null || service.isEmpty()) {
|
||||
throw new ServicesMonitorException(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.nullServiceName.excepton.txt"));
|
||||
}
|
||||
|
||||
// if request is for one of our "core" services - perform an on demand check
|
||||
// to make sure we have the latest status.
|
||||
if (servicesList.contains(service)) {
|
||||
/*
|
||||
* If the request is for a core service, perform an "on demand" check to
|
||||
* get the current status.
|
||||
*/
|
||||
if (coreServices.contains(service)) {
|
||||
checkServiceStatus(service);
|
||||
}
|
||||
|
||||
String status = statusByService.get(service);
|
||||
if (status == null) {
|
||||
// no such service
|
||||
throw new ServicesMonitorException(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.unknownServiceName.excepton.txt", service));
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs service availability status check.
|
||||
* Adds a subscriber to service status events for the core services.
|
||||
*
|
||||
* @param service Name of the service.
|
||||
* @param subscriber The subscriber to add.
|
||||
*/
|
||||
public void addSubscriber(PropertyChangeListener subscriber) {
|
||||
eventPublisher.addSubscriber(coreServices, subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a subscriber to service status events for a subset of the services
|
||||
* known to the services monitor.
|
||||
*
|
||||
* @param services The services the subscriber is interested in.
|
||||
* @param subscriber The subscriber to add.
|
||||
*/
|
||||
public void addSubscriber(Set<String> services, PropertyChangeListener subscriber) {
|
||||
eventPublisher.addSubscriber(services, subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a subscriber to service status events for a specific service known
|
||||
* to the services monitor.
|
||||
*
|
||||
* @param service The service the subscriber is interested in.
|
||||
* @param subscriber The subscriber to add.
|
||||
*/
|
||||
public void addSubscriber(String service, PropertyChangeListener subscriber) {
|
||||
eventPublisher.addSubscriber(service, subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a subscriber to service status events for the core services.
|
||||
*
|
||||
* @param subscriber The subscriber to remove.
|
||||
*/
|
||||
public void removeSubscriber(PropertyChangeListener subscriber) {
|
||||
eventPublisher.removeSubscriber(coreServices, subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a subscriber to service status events for a subset of the
|
||||
* services known to the services monitor.
|
||||
*
|
||||
* @param services The services the subscriber is no longer interested in.
|
||||
* @param subscriber The subscriber to remove.
|
||||
*/
|
||||
public void removeSubscriber(Set<String> services, PropertyChangeListener subscriber) {
|
||||
eventPublisher.removeSubscriber(services, subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a subscriber to service status events for a specific service known
|
||||
* to the services monitor.
|
||||
*
|
||||
* @param service The service the subscriber is no longer interested in.
|
||||
* @param subscriber The subscriber to remove.
|
||||
*/
|
||||
public void removeSubscriber(String service, PropertyChangeListener subscriber) {
|
||||
eventPublisher.removeSubscriber(service, subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the status of the core services in a collaborative, multi-user
|
||||
* case environment: the database server, the keyword search server and the
|
||||
* messaging service. Publishes a service event and user notification if the
|
||||
* status of a service has changed since the last check.
|
||||
*/
|
||||
private void checkAllServices() {
|
||||
if (!UserPreferences.getIsMultiUserModeEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (String service : coreServices) {
|
||||
checkServiceStatus(service);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A task that checks the status of the core services in a collaborative,
|
||||
* multi-user case environment: the database server, the keyword search
|
||||
* server and the messaging service. Publishes a service event and user
|
||||
* notification if the status of a service has changed since the last check.
|
||||
*/
|
||||
private final class ServicesMonitoringTask implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
checkAllServices();
|
||||
} catch (Exception ex) { // Exception firewall
|
||||
logger.log(Level.SEVERE, "An error occurred during services monitoring", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception thrown if an error occurs during a service status query.
|
||||
*/
|
||||
public class ServicesMonitorException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ServicesMonitorException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ServicesMonitorException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a core service availability status check.
|
||||
*
|
||||
* @param service Name of the service to check.
|
||||
*/
|
||||
private void checkServiceStatus(String service) {
|
||||
if (service.equals(Service.REMOTE_CASE_DATABASE.toString())) {
|
||||
@@ -236,7 +328,7 @@ public class ServicesMonitor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs case database service availability status check.
|
||||
* Performs a database server availability status check.
|
||||
*/
|
||||
private void checkDatabaseConnectionStatus() {
|
||||
CaseDbConnectionInfo info;
|
||||
@@ -256,7 +348,7 @@ public class ServicesMonitor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs keyword search service availability status check.
|
||||
* Performs a keyword search service availability status check.
|
||||
*/
|
||||
private void checkKeywordSearchServerConnectionStatus() {
|
||||
KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class);
|
||||
@@ -303,113 +395,4 @@ public class ServicesMonitor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an event subscriber to this publisher. Subscriber will be subscribed
|
||||
* to all events from this publisher.
|
||||
*
|
||||
* @param subscriber The subscriber to add.
|
||||
*/
|
||||
public void addSubscriber(PropertyChangeListener subscriber) {
|
||||
eventPublisher.addSubscriber(servicesList, subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an event subscriber to this publisher.
|
||||
*
|
||||
* @param eventNames The events the subscriber is interested in.
|
||||
* @param subscriber The subscriber to add.
|
||||
*/
|
||||
public void addSubscriber(Set<String> eventNames, PropertyChangeListener subscriber) {
|
||||
eventPublisher.addSubscriber(eventNames, subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an event subscriber to this publisher.
|
||||
*
|
||||
* @param eventName The event the subscriber is interested in.
|
||||
* @param subscriber The subscriber to add.
|
||||
*/
|
||||
public void addSubscriber(String eventName, PropertyChangeListener subscriber) {
|
||||
eventPublisher.addSubscriber(eventName, subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an event subscriber from this publisher.
|
||||
*
|
||||
* @param eventNames The events the subscriber is no longer interested in.
|
||||
* @param subscriber The subscriber to remove.
|
||||
*/
|
||||
public void removeSubscriber(Set<String> eventNames, PropertyChangeListener subscriber) {
|
||||
eventPublisher.removeSubscriber(eventNames, subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an event subscriber from this publisher.
|
||||
*
|
||||
* @param eventName The event the subscriber is no longer interested in.
|
||||
* @param subscriber The subscriber to remove.
|
||||
*/
|
||||
public void removeSubscriber(String eventName, PropertyChangeListener subscriber) {
|
||||
eventPublisher.removeSubscriber(eventName, subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an event subscriber to this publisher. Subscriber will be removed
|
||||
* from all event notifications from this publisher.
|
||||
*
|
||||
* @param subscriber The subscriber to remove.
|
||||
*/
|
||||
public void removeSubscriber(PropertyChangeListener subscriber) {
|
||||
eventPublisher.removeSubscriber(servicesList, subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies connectivity to all services.
|
||||
*/
|
||||
private void checkAllServices() {
|
||||
if (!UserPreferences.getIsMultiUserModeEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (String service : servicesList) {
|
||||
checkServiceStatus(service);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Runnable task that periodically checks the availability of
|
||||
* collaboration resources (remote database, remote keyword search service,
|
||||
* message broker) and reports status to the user in case of a gap in
|
||||
* service.
|
||||
*/
|
||||
private final class CrashDetectionTask implements Runnable {
|
||||
|
||||
/**
|
||||
* Monitor the availability of collaboration resources
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
checkAllServices();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Unexpected exception in CrashDetectionTask", ex); //NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
public ServicesMonitorException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user