Autopsy 4.22.1
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-2020 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 */
19package org.sleuthkit.autopsy.core;
20
21import org.sleuthkit.autopsy.core.events.ServiceEvent;
22import com.google.common.util.concurrent.ThreadFactoryBuilder;
23import java.beans.PropertyChangeListener;
24import java.util.Set;
25import java.util.concurrent.ConcurrentHashMap;
26import java.util.concurrent.ScheduledThreadPoolExecutor;
27import java.util.concurrent.TimeUnit;
28import java.util.logging.Level;
29import java.util.stream.Collectors;
30import java.util.stream.Stream;
31import org.openide.util.Lookup;
32import org.openide.util.NbBundle;
33import org.sleuthkit.autopsy.coreutils.Logger;
34import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
35import org.sleuthkit.autopsy.events.AutopsyEventPublisher;
36import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService;
37import org.sleuthkit.autopsy.events.MessageServiceConnectionInfo;
38import org.sleuthkit.autopsy.events.MessageServiceException;
39import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException;
40import org.sleuthkit.datamodel.CaseDbConnectionInfo;
41import org.sleuthkit.datamodel.SleuthkitCase;
42import org.sleuthkit.datamodel.TskCoreException;
43
51public class ServicesMonitor {
52
59 public enum Service {
60 REMOTE_CASE_DATABASE(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.remoteCaseDatabase.displayName.text")),
61 REMOTE_KEYWORD_SEARCH(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.remoteKeywordSearch.displayName.text")),
62 MESSAGING(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.messaging.displayName.text"));
63
64 private final String displayName;
65
66 private Service(String displayName) {
67 this.displayName = displayName;
68 }
69
70 public String getDisplayName() {
71 return displayName;
72 }
73 };
74
78 public enum ServiceStatus {
81 };
82
83 private static final Logger logger = Logger.getLogger(ServicesMonitor.class.getName());
84 private static final String PERIODIC_TASK_THREAD_NAME = "services-monitor-periodic-task-%d"; //NON-NLS
85 private static final int NUMBER_OF_PERIODIC_TASK_THREADS = 1;
86 private static final long CRASH_DETECTION_INTERVAL_MINUTES = 15;
87 private static final Set<String> coreServices = Stream.of(ServicesMonitor.Service.values()).map(Service::toString).collect(Collectors.toSet());
89 private final ScheduledThreadPoolExecutor periodicTasksExecutor;
90 private final ConcurrentHashMap<String, String> statusByService;
92
100 public synchronized static ServicesMonitor getInstance() {
101 if (servicesMonitor == null) {
103 }
104 return servicesMonitor;
105 }
106
112 private ServicesMonitor() {
114 statusByService = new ConcurrentHashMap<>();
115
116 /*
117 * The first service statuses check is performed immediately in the
118 * current thread.
119 */
121
125 periodicTasksExecutor = new ScheduledThreadPoolExecutor(NUMBER_OF_PERIODIC_TASK_THREADS, new ThreadFactoryBuilder().setNameFormat(PERIODIC_TASK_THREAD_NAME).build());
127 }
128
138 public void setServiceStatus(String service, String status, String details) {
139 if (statusByService.containsKey(service) && status.equals(statusByService.get(service))) {
140 return;
141 }
142
143 statusByService.put(service, status);
144
145 String serviceDisplayName;
146 try {
147 serviceDisplayName = ServicesMonitor.Service.valueOf(service).getDisplayName();
148 } catch (IllegalArgumentException ignore) {
149 serviceDisplayName = service;
150 }
151
152 if (status.equals(ServiceStatus.UP.toString())) {
153 logger.log(Level.INFO, "Connection to {0} is up", serviceDisplayName); //NON-NLS
155 NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredService.notify.title"),
156 NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.restoredService.notify.msg", serviceDisplayName));
157 } else if (status.equals(ServiceStatus.DOWN.toString())) {
158 logger.log(Level.SEVERE, "Failed to connect to {0}. Reason: {1}", new Object[]{serviceDisplayName, details}); //NON-NLS
160 NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedService.notify.title"),
161 NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.failedService.notify.msg", serviceDisplayName));
162 } else {
163 logger.log(Level.INFO, "Status for {0} is {1} ({2})", new Object[]{serviceDisplayName, status}); //NON-NLS
165 NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.statusChange.notify.title"),
166 NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.statusChange.notify.msg", new Object[]{serviceDisplayName, status, details}));
167 }
168
169 eventPublisher.publishLocally(new ServiceEvent(service, status, details));
170 }
171
182 public String getServiceStatus(String service) throws ServicesMonitorException {
183
184 if (service == null || service.isEmpty()) {
185 throw new ServicesMonitorException(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.nullServiceName.excepton.txt"));
186 }
187
188 /*
189 * If the request is for a core service, perform an "on demand" check to
190 * get the current status.
191 */
192 if (coreServices.contains(service)) {
193 checkServiceStatus(service);
194 }
195
196 String status = statusByService.get(service);
197 if (status == null) {
198 throw new ServicesMonitorException(NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.unknownServiceName.excepton.txt", service));
199 }
200 return status;
201 }
202
208 public void addSubscriber(PropertyChangeListener subscriber) {
209 eventPublisher.addSubscriber(coreServices, subscriber);
210 }
211
219 public void addSubscriber(Set<String> services, PropertyChangeListener subscriber) {
220 eventPublisher.addSubscriber(services, subscriber);
221 }
222
230 public void addSubscriber(String service, PropertyChangeListener subscriber) {
231 eventPublisher.addSubscriber(service, subscriber);
232 }
233
239 public void removeSubscriber(PropertyChangeListener subscriber) {
240 eventPublisher.removeSubscriber(coreServices, subscriber);
241 }
242
250 public void removeSubscriber(Set<String> services, PropertyChangeListener subscriber) {
251 eventPublisher.removeSubscriber(services, subscriber);
252 }
253
261 public void removeSubscriber(String service, PropertyChangeListener subscriber) {
262 eventPublisher.removeSubscriber(service, subscriber);
263 }
264
271 private void checkAllServices() {
273 return;
274 }
275
276 for (String service : coreServices) {
277 checkServiceStatus(service);
278 }
279 }
280
287 private final class ServicesMonitoringTask implements Runnable {
288
289 @Override
290 public void run() {
291 try {
293 } catch (Exception ex) { // Exception firewall
294 logger.log(Level.SEVERE, "An error occurred during services monitoring", ex); //NON-NLS
295 }
296 }
297 }
298
302 public class ServicesMonitorException extends Exception {
303
304 private static final long serialVersionUID = 1L;
305
306 public ServicesMonitorException(String message) {
307 super(message);
308 }
309
310 public ServicesMonitorException(String message, Throwable cause) {
311 super(message, cause);
312 }
313 }
314
320 private void checkServiceStatus(String service) {
321 if (service.equals(Service.REMOTE_CASE_DATABASE.toString())) {
323 } else if (service.equals(Service.REMOTE_KEYWORD_SEARCH.toString())) {
325 } else if (service.equals(Service.MESSAGING.toString())) {
327 }
328 }
329
334 CaseDbConnectionInfo info;
335 try {
337 } catch (UserPreferencesException ex) {
338 logger.log(Level.SEVERE, "Error accessing case database connection info", ex); //NON-NLS
339 setServiceStatus(Service.REMOTE_CASE_DATABASE.toString(), ServiceStatus.DOWN.toString(), NbBundle.getMessage(this.getClass(), "ServicesMonitor.databaseConnectionInfo.error.msg"));
340 return;
341 }
342 try {
343 SleuthkitCase.tryConnect(info);
345 } catch (TskCoreException ex) {
346 setServiceStatus(Service.REMOTE_CASE_DATABASE.toString(), ServiceStatus.DOWN.toString(), ex.getMessage());
347 }
348 }
349
354 KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class);
355 try {
356 if (kwsService != null) {
358 // check Solr 8
359 String kwsHostName = UserPreferences.getIndexingServerHost();
360 if (!kwsHostName.isEmpty()) {
361 int port = Integer.parseUnsignedInt(UserPreferences.getIndexingServerPort());
363 status = ServiceStatus.UP;
364 }
365
366 // check Solr 4
367 if (!UserPreferences.getSolr4ServerHost().trim().isEmpty()) {
368 int port = Integer.parseUnsignedInt(UserPreferences.getSolr4ServerPort().trim());
369 kwsService.tryConnect(UserPreferences.getSolr4ServerHost().trim(), port);
370 status = ServiceStatus.UP;
371 }
372 setServiceStatus(Service.REMOTE_KEYWORD_SEARCH.toString(), status.toString(), "");
373 } else {
375 NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.KeywordSearchNull"));
376 }
377 } catch (NumberFormatException ex) {
378 String rootCause = NbBundle.getMessage(ServicesMonitor.class, "ServicesMonitor.InvalidPortNumber");
379 logger.log(Level.SEVERE, "Unable to connect to Keyword Search server: " + rootCause, ex); //NON-NLS
380 setServiceStatus(Service.REMOTE_KEYWORD_SEARCH.toString(), ServiceStatus.DOWN.toString(), rootCause);
381 } catch (KeywordSearchServiceException ex) {
382 String rootCause = ex.getMessage();
383 logger.log(Level.SEVERE, "Unable to connect to Keyword Search server: " + rootCause, ex); //NON-NLS
384 setServiceStatus(Service.REMOTE_KEYWORD_SEARCH.toString(), ServiceStatus.DOWN.toString(), rootCause);
385 }
386 }
387
393 try {
395 } catch (UserPreferencesException ex) {
396 logger.log(Level.SEVERE, "Error accessing messaging service connection info", ex); //NON-NLS
397 setServiceStatus(Service.MESSAGING.toString(), ServiceStatus.DOWN.toString(), NbBundle.getMessage(this.getClass(), "ServicesMonitor.messagingService.connErr.text"));
398 return;
399 }
400
401 try {
402 info.tryConnect();
403 setServiceStatus(Service.MESSAGING.toString(), ServiceStatus.UP.toString(), "");
404 } catch (MessageServiceException ex) {
405 String rootCause = ex.getMessage();
406 logger.log(Level.SEVERE, "Unable to connect to messaging server: " + rootCause, ex); //NON-NLS
407 setServiceStatus(Service.MESSAGING.toString(), ServiceStatus.DOWN.toString(), rootCause);
408 }
409 }
410
411}
static synchronized ServicesMonitor getInstance()
void addSubscriber(Set< String > services, PropertyChangeListener subscriber)
final ScheduledThreadPoolExecutor periodicTasksExecutor
void removeSubscriber(Set< String > services, PropertyChangeListener subscriber)
final ConcurrentHashMap< String, String > statusByService
void setServiceStatus(String service, String status, String details)
void removeSubscriber(String service, PropertyChangeListener subscriber)
void addSubscriber(String service, PropertyChangeListener subscriber)
void removeSubscriber(PropertyChangeListener subscriber)
void addSubscriber(PropertyChangeListener subscriber)
static CaseDbConnectionInfo getDatabaseConnectionInfo()
static MessageServiceConnectionInfo getMessageServiceConnectionInfo()
synchronized static Logger getLogger(String name)
Definition Logger.java:124

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.