Autopsy  4.7.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
ServicesMonitor.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2013-2015 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.core;
20 
22 import com.google.common.util.concurrent.ThreadFactoryBuilder;
23 import java.beans.PropertyChangeListener;
24 import java.util.Set;
25 import java.util.concurrent.ConcurrentHashMap;
26 import java.util.concurrent.ScheduledThreadPoolExecutor;
27 import java.util.concurrent.TimeUnit;
28 import java.util.logging.Level;
29 import java.util.stream.Collectors;
30 import java.util.stream.Stream;
31 import org.openide.util.Lookup;
32 import org.openide.util.NbBundle;
40 import org.sleuthkit.datamodel.CaseDbConnectionInfo;
41 import org.sleuthkit.datamodel.SleuthkitCase;
42 import org.sleuthkit.datamodel.TskCoreException;
43 
49 public class ServicesMonitor {
50 
52  private static final Logger logger = Logger.getLogger(ServicesMonitor.class.getName());
53  private final ScheduledThreadPoolExecutor periodicTasksExecutor;
54 
55  private static final String PERIODIC_TASK_THREAD_NAME = "services-monitor-periodic-task-%d"; //NON-NLS
56  private static final int NUMBER_OF_PERIODIC_TASK_THREADS = 1;
57  private static final long CRASH_DETECTION_INTERVAL_MINUTES = 15;
58 
59  private static final Set<String> servicesList = Stream.of(ServicesMonitor.Service.values())
60  .map(Service::toString)
61  .collect(Collectors.toSet());
62 
67  private final ConcurrentHashMap<String, String> statusByService;
68 
73  private static ServicesMonitor instance = new ServicesMonitor();
74 
80  public enum Service {
81 
87  REMOTE_CASE_DATABASE(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.remoteCaseDatabase.displayName.text")),
93  REMOTE_KEYWORD_SEARCH(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.remoteKeywordSearch.displayName.text")),
98  MESSAGING(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.messaging.displayName.text"));
99 
100  private final String displayName;
101 
102  private Service(String name) {
103  this.displayName = name;
104  }
105 
106  public String getDisplayName() {
107  return displayName;
108  }
109  };
110 
114  public enum ServiceStatus {
115 
119  UP,
123  DOWN
124  };
125 
126  public synchronized static ServicesMonitor getInstance() {
127  if (instance == null) {
128  instance = new ServicesMonitor();
129  }
130  return instance;
131  }
132 
133  private ServicesMonitor() {
134 
135  this.eventPublisher = new AutopsyEventPublisher();
136  this.statusByService = new ConcurrentHashMap<>();
137 
138  // First check is triggered immediately on current thread.
140 
145  periodicTasksExecutor = new ScheduledThreadPoolExecutor(NUMBER_OF_PERIODIC_TASK_THREADS, new ThreadFactoryBuilder().setNameFormat(PERIODIC_TASK_THREAD_NAME).build());
146  periodicTasksExecutor.scheduleWithFixedDelay(new CrashDetectionTask(), CRASH_DETECTION_INTERVAL_MINUTES, CRASH_DETECTION_INTERVAL_MINUTES, TimeUnit.MINUTES);
147  }
148 
159  public void setServiceStatus(String service, String status, String details) {
160  // if the status update is for an existing service who's status hasn't changed - do nothing.
161  if (statusByService.containsKey(service) && status.equals(statusByService.get(service))) {
162  return;
163  }
164 
165  // new service or status has changed - identify service's display name
166  String serviceDisplayName;
167  try {
168  serviceDisplayName = ServicesMonitor.Service.valueOf(service).getDisplayName();
169  } catch (IllegalArgumentException ignore) {
170  // custom service that is not listed in ServicesMonitor.Service enum. Use service name as display name.
171  serviceDisplayName = service;
172  }
173 
174  if (status.equals(ServiceStatus.UP.toString())) {
175  logger.log(Level.INFO, "Connection to {0} is up", serviceDisplayName); //NON-NLS
176  MessageNotifyUtil.Notify.info(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredService.notify.title"),
177  NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredService.notify.msg", serviceDisplayName));
178  } else if (status.equals(ServiceStatus.DOWN.toString())) {
179  logger.log(Level.SEVERE, "Failed to connect to {0}. Reason: {1}", new Object[]{serviceDisplayName, details}); //NON-NLS
180  MessageNotifyUtil.Notify.error(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedService.notify.title"),
181  NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedService.notify.msg", serviceDisplayName));
182  } else {
183  logger.log(Level.INFO, "Status for {0} is {1}", new Object[]{serviceDisplayName, status}); //NON-NLS
184  MessageNotifyUtil.Notify.info(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.statusChange.notify.title"),
185  NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.statusChange.notify.msg", new Object[]{serviceDisplayName, status}));
186  }
187 
188  // update and publish new status
189  statusByService.put(service, status);
190  eventPublisher.publishLocally(new ServiceEvent(service, status, details));
191  }
192 
203  public String getServiceStatus(String service) throws ServicesMonitorException {
204 
205  if (service == null) {
206  throw new ServicesMonitorException(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.nullServiceName.excepton.txt"));
207  }
208 
209  // if request is for one of our "core" services - perform an on demand check
210  // to make sure we have the latest status.
211  if (servicesList.contains(service)) {
212  checkServiceStatus(service);
213  }
214 
215  String status = statusByService.get(service);
216  if (status == null) {
217  // no such service
218  throw new ServicesMonitorException(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.unknownServiceName.excepton.txt", service));
219  }
220  return status;
221  }
222 
228  private void checkServiceStatus(String service) {
229  if (service.equals(Service.REMOTE_CASE_DATABASE.toString())) {
231  } else if (service.equals(Service.REMOTE_KEYWORD_SEARCH.toString())) {
233  } else if (service.equals(Service.MESSAGING.toString())) {
235  }
236  }
237 
242  CaseDbConnectionInfo info;
243  try {
245  } catch (UserPreferencesException ex) {
246  logger.log(Level.SEVERE, "Error accessing case database connection info", ex); //NON-NLS
247  setServiceStatus(Service.REMOTE_CASE_DATABASE.toString(), ServiceStatus.DOWN.toString(), NbBundle.getMessage(this.getClass(), "ServicesMonitor.databaseConnectionInfo.error.msg"));
248  return;
249  }
250  try {
251  SleuthkitCase.tryConnect(info);
252  setServiceStatus(Service.REMOTE_CASE_DATABASE.toString(), ServiceStatus.UP.toString(), "");
253  } catch (TskCoreException ex) {
254  setServiceStatus(Service.REMOTE_CASE_DATABASE.toString(), ServiceStatus.DOWN.toString(), ex.getMessage());
255  }
256  }
257 
262  KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class);
263  try {
264  if (kwsService != null) {
265  int port = Integer.parseUnsignedInt(UserPreferences.getIndexingServerPort());
267  setServiceStatus(Service.REMOTE_KEYWORD_SEARCH.toString(), ServiceStatus.UP.toString(), "");
268  } else {
270  NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.KeywordSearchNull"));
271  }
272  } catch (NumberFormatException ex) {
273  String rootCause = NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.InvalidPortNumber");
274  logger.log(Level.SEVERE, "Unable to connect to messaging server: " + rootCause, ex); //NON-NLS
275  setServiceStatus(Service.REMOTE_KEYWORD_SEARCH.toString(), ServiceStatus.DOWN.toString(), rootCause);
276  } catch (KeywordSearchServiceException ex) {
277  String rootCause = ex.getMessage();
278  logger.log(Level.SEVERE, "Unable to connect to messaging server: " + rootCause, ex); //NON-NLS
279  setServiceStatus(Service.REMOTE_KEYWORD_SEARCH.toString(), ServiceStatus.DOWN.toString(), rootCause);
280  }
281  }
282 
288  try {
290  } catch (UserPreferencesException ex) {
291  logger.log(Level.SEVERE, "Error accessing messaging service connection info", ex); //NON-NLS
292  setServiceStatus(Service.MESSAGING.toString(), ServiceStatus.DOWN.toString(), NbBundle.getMessage(this.getClass(), "ServicesMonitor.messagingService.connErr.text"));
293  return;
294  }
295 
296  try {
297  info.tryConnect();
298  setServiceStatus(Service.MESSAGING.toString(), ServiceStatus.UP.toString(), "");
299  } catch (MessageServiceException ex) {
300  String rootCause = ex.getMessage();
301  logger.log(Level.SEVERE, "Unable to connect to messaging server: " + rootCause, ex); //NON-NLS
302  setServiceStatus(Service.MESSAGING.toString(), ServiceStatus.DOWN.toString(), rootCause);
303  }
304  }
305 
312  public void addSubscriber(PropertyChangeListener subscriber) {
313  eventPublisher.addSubscriber(servicesList, subscriber);
314  }
315 
322  public void addSubscriber(Set<String> eventNames, PropertyChangeListener subscriber) {
323  eventPublisher.addSubscriber(eventNames, subscriber);
324  }
325 
332  public void addSubscriber(String eventName, PropertyChangeListener subscriber) {
333  eventPublisher.addSubscriber(eventName, subscriber);
334  }
335 
342  public void removeSubscriber(Set<String> eventNames, PropertyChangeListener subscriber) {
343  eventPublisher.removeSubscriber(eventNames, subscriber);
344  }
345 
352  public void removeSubscriber(String eventName, PropertyChangeListener subscriber) {
353  eventPublisher.removeSubscriber(eventName, subscriber);
354  }
355 
362  public void removeSubscriber(PropertyChangeListener subscriber) {
363  eventPublisher.removeSubscriber(servicesList, subscriber);
364  }
365 
369  private void checkAllServices() {
371  return;
372  }
373 
374  for (String service : servicesList) {
375  checkServiceStatus(service);
376  }
377  }
378 
385  private final class CrashDetectionTask implements Runnable {
386 
390  @Override
391  public void run() {
392  try {
394  } catch (Exception ex) {
395  logger.log(Level.SEVERE, "Unexpected exception in CrashDetectionTask", ex); //NON-NLS
396  }
397  }
398  }
399 
403  public class ServicesMonitorException extends Exception {
404 
405  private static final long serialVersionUID = 1L;
406 
407  public ServicesMonitorException(String message) {
408  super(message);
409  }
410 
411  public ServicesMonitorException(String message, Throwable cause) {
412  super(message, cause);
413  }
414  }
415 }
void setServiceStatus(String service, String status, String details)
void removeSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
void addSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
final ConcurrentHashMap< String, String > statusByService
static CaseDbConnectionInfo getDatabaseConnectionInfo()
void addSubscriber(PropertyChangeListener subscriber)
void addSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
final ScheduledThreadPoolExecutor periodicTasksExecutor
static synchronized ServicesMonitor getInstance()
static void info(String title, String message)
void removeSubscriber(PropertyChangeListener subscriber)
void addSubscriber(String eventName, PropertyChangeListener subscriber)
void removeSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
static void error(String title, String message)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
void removeSubscriber(String eventName, PropertyChangeListener subscriber)
static MessageServiceConnectionInfo getMessageServiceConnectionInfo()

Copyright © 2012-2016 Basis Technology. Generated on: Mon Jun 18 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.