Autopsy 4.22.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
CoordinationService.java
Go to the documentation of this file.
1/*
2 * Autopsy Forensic Browser
3 *
4 * Copyright 2011-2018 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.coordinationservice;
20
21import java.io.IOException;
22import java.util.Collection;
23import java.util.Iterator;
24import java.util.List;
25import java.util.Map;
26import java.util.concurrent.ConcurrentHashMap;
27import java.util.concurrent.TimeUnit;
28import javax.annotation.concurrent.GuardedBy;
29import javax.annotation.concurrent.ThreadSafe;
30import org.apache.curator.RetryPolicy;
31import org.apache.curator.framework.CuratorFramework;
32import org.apache.curator.framework.CuratorFrameworkFactory;
33import org.apache.curator.framework.recipes.locks.InterProcessMutex;
34import org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock;
35import org.apache.curator.retry.ExponentialBackoffRetry;
36import org.apache.curator.utils.ZKPaths;
37import org.apache.zookeeper.CreateMode;
38import org.apache.zookeeper.KeeperException;
39import org.apache.zookeeper.KeeperException.NoNodeException;
40import org.apache.zookeeper.ZooDefs;
41import org.openide.util.Lookup;
42import org.sleuthkit.autopsy.coordinationservice.utils.CoordinationServiceUtils;
43import org.sleuthkit.autopsy.core.UserPreferences;
44
50@ThreadSafe
51public final class CoordinationService {
52
53 private static final int SESSION_TIMEOUT_MILLISECONDS = 300000;
54 private static final int CONNECTION_TIMEOUT_MILLISECONDS = 300000;
55 private static final int PORT_OFFSET = 1000; // When run in Solr, ZooKeeper defaults to Solr port + 1000
56 private static final String DEFAULT_NAMESPACE_ROOT = "autopsy";
57 @GuardedBy("CoordinationService.class")
59 private final CuratorFramework curator;
60 @GuardedBy("categoryNodeToPath")
61 private final Map<String, String> categoryNodeToPath;
62
72 public synchronized static CoordinationService getInstance() throws CoordinationServiceException {
73 if (null == instance) {
74 String rootNode;
75 Collection<? extends CoordinationServiceNamespace> providers = Lookup.getDefault().lookupAll(CoordinationServiceNamespace.class);
76 Iterator<? extends CoordinationServiceNamespace> it = providers.iterator();
77 if (it.hasNext()) {
78 rootNode = it.next().getNamespaceRoot();
79 } else {
80 rootNode = DEFAULT_NAMESPACE_ROOT;
81 }
82 try {
83 instance = new CoordinationService(rootNode);
84 } catch (IOException | KeeperException | CoordinationServiceException ex) {
85 throw new CoordinationServiceException("Failed to create coordination service", ex);
86 } catch (InterruptedException ex) {
87 /*
88 * The interrupted exception should be propagated to support
89 * task cancellation. To avoid a public API change here, restore
90 * the interrupted flag and then throw the InterruptedException
91 * in its wrapper.
92 */
93 Thread.currentThread().interrupt();
94 throw new CoordinationServiceException("Failed to create coordination service", ex);
95 }
96 }
97 return instance;
98 }
99
109 private CoordinationService(String rootNodeName) throws InterruptedException, IOException, KeeperException, CoordinationServiceException {
110
111 // read ZK connection info
112 String hostName = UserPreferences.getZkServerHost();
113 String port = UserPreferences.getZkServerPort();
114 if (hostName.isEmpty() || port.isEmpty()) {
115 // use defaults for embedded ZK that runs on Solr server
117 int portInt = Integer.valueOf(UserPreferences.getIndexingServerPort()) + PORT_OFFSET;
118 port = Integer.toString(portInt);
119 }
120 if (false == CoordinationServiceUtils.isZooKeeperAccessible(hostName, port)) {
121 throw new CoordinationServiceException("Unable to access ZooKeeper");
122 }
123
124 // We are using ZK for all coordination/locking, so ZK connection info cannot be changed.
125 // A reboot is required in order to use a different ZK server for coordination services.
126 /*
127 * Connect to ZooKeeper via Curator.
128 */
129 RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
130 String connectString = hostName + ":" + port;
131 curator = CuratorFrameworkFactory.newClient(connectString, SESSION_TIMEOUT_MILLISECONDS, CONNECTION_TIMEOUT_MILLISECONDS, retryPolicy);
132 curator.start();
133
134 /*
135 * Create the top-level root and category nodes.
136 */
137 String rootNode = rootNodeName;
138
139 if (!rootNode.startsWith("/")) {
140 rootNode = "/" + rootNode;
141 }
142 categoryNodeToPath = new ConcurrentHashMap<>();
143 for (CategoryNode node : CategoryNode.values()) {
144 String nodePath = rootNode + "/" + node.getDisplayName();
145 try {
146 curator.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE).forPath(nodePath);
147 } catch (KeeperException ex) {
148 if (ex.code() != KeeperException.Code.NODEEXISTS) {
149 throw ex;
150 }
151 } catch (Exception ex) {
152 throw new CoordinationServiceException("Curator experienced an error", ex);
153 }
154 categoryNodeToPath.put(node.getDisplayName(), nodePath);
155 }
156 }
157
165 private String upsertNodePath(CategoryNode category, String nodePath) throws CoordinationServiceException {
166 String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
167
168 // ZKPaths.mkdirs throws an exception with trailing slash.
169 // Remove trailing slash if slash is present to prevent this issue.
170 while(fullNodePath.endsWith("/")) {
171 fullNodePath = fullNodePath.substring(0, fullNodePath.length() - 1);
172 }
173
174 try {
175 // ensure leading path is present
176 ZKPaths.mkdirs(curator.getZookeeperClient().getZooKeeper(), fullNodePath);
177 return fullNodePath;
178 } catch (Exception ex) {
179 throw new CoordinationServiceException("An error occurred while creating node path at: " + fullNodePath, ex);
180 }
181 }
182
203 public Lock tryGetExclusiveLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit) throws CoordinationServiceException, InterruptedException {
204 String fullNodePath = "";
205 try {
206 // ensure node is present
207 fullNodePath = upsertNodePath(category, nodePath);
208 InterProcessReadWriteLock lock = new InterProcessReadWriteLock(curator, fullNodePath);
209 if (lock.writeLock().acquire(timeOut, timeUnit)) {
210 return new Lock(nodePath, lock.writeLock());
211 } else {
212 return null;
213 }
214 } catch (Exception ex) {
215 if (ex instanceof InterruptedException) {
216 throw (InterruptedException) ex;
217 } else {
218 throw new CoordinationServiceException(String.format("Failed to get exclusive lock for %s", fullNodePath), ex);
219 }
220 }
221 }
222
239 public Lock tryGetExclusiveLock(CategoryNode category, String nodePath) throws CoordinationServiceException {
240 String fullNodePath = "";
241 try {
242 // ensure node is present
243 fullNodePath = upsertNodePath(category, nodePath);
244 InterProcessReadWriteLock lock = new InterProcessReadWriteLock(curator, fullNodePath);
245 if (!lock.writeLock().acquire(0, TimeUnit.SECONDS)) {
246 return null;
247 }
248 return new Lock(nodePath, lock.writeLock());
249 } catch (Exception ex) {
250 throw new CoordinationServiceException(String.format("Failed to get exclusive lock for %s", fullNodePath), ex);
251 }
252 }
253
274 public Lock tryGetSharedLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit) throws CoordinationServiceException, InterruptedException {
275 String fullNodePath = "";
276 try {
277 // ensure node is present
278 fullNodePath = upsertNodePath(category, nodePath);
279 InterProcessReadWriteLock lock = new InterProcessReadWriteLock(curator, fullNodePath);
280 if (lock.readLock().acquire(timeOut, timeUnit)) {
281 return new Lock(nodePath, lock.readLock());
282 } else {
283 return null;
284 }
285 } catch (Exception ex) {
286 if (ex instanceof InterruptedException) {
287 throw (InterruptedException) ex;
288 } else {
289 throw new CoordinationServiceException(String.format("Failed to get shared lock for %s", fullNodePath), ex);
290 }
291 }
292 }
293
310 public Lock tryGetSharedLock(CategoryNode category, String nodePath) throws CoordinationServiceException {
311 String fullNodePath = "";
312 try {
313 // ensure node is present
314 fullNodePath = upsertNodePath(category, nodePath);
315 InterProcessReadWriteLock lock = new InterProcessReadWriteLock(curator, fullNodePath);
316 if (!lock.readLock().acquire(0, TimeUnit.SECONDS)) {
317 return null;
318 }
319 return new Lock(nodePath, lock.readLock());
320 } catch (Exception ex) {
321 throw new CoordinationServiceException(String.format("Failed to get shared lock for %s", fullNodePath), ex);
322 }
323 }
324
339 public byte[] getNodeData(CategoryNode category, String nodePath) throws CoordinationServiceException, InterruptedException {
340 String fullNodePath = "";
341 try {
342 // ensure node is present
343 fullNodePath = upsertNodePath(category, nodePath);
344
345 // return node data path
346 return curator.getData().forPath(fullNodePath);
347 } catch (NoNodeException ex) {
348 return null;
349 } catch (Exception ex) {
350 if (ex instanceof InterruptedException) {
351 throw (InterruptedException) ex;
352 } else {
353 throw new CoordinationServiceException(String.format("Failed to get data for %s", fullNodePath), ex);
354 }
355 }
356 }
357
370 public void setNodeData(CategoryNode category, String nodePath, byte[] data) throws CoordinationServiceException, InterruptedException {
371 String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
372 try {
373 curator.setData().forPath(fullNodePath, data);
374 } catch (Exception ex) {
375 if (ex instanceof InterruptedException) {
376 throw (InterruptedException) ex;
377 } else {
378 throw new CoordinationServiceException(String.format("Failed to set data for %s", fullNodePath), ex);
379 }
380 }
381 }
382
395 public void deleteNode(CategoryNode category, String nodePath) throws CoordinationServiceException, InterruptedException {
396 String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
397 try {
398 curator.delete().forPath(fullNodePath);
399 } catch (Exception ex) {
400 if (ex instanceof InterruptedException) {
401 throw (InterruptedException) ex;
402 } else {
403 throw new CoordinationServiceException(String.format("Failed to delete node %s", fullNodePath), ex);
404 }
405 }
406 }
407
421 public List<String> getNodeList(CategoryNode category) throws CoordinationServiceException, InterruptedException {
422 try {
423 List<String> list = curator.getChildren().forPath(categoryNodeToPath.get(category.getDisplayName()));
424 return list;
425 } catch (Exception ex) {
426 if (ex instanceof InterruptedException) {
427 throw (InterruptedException) ex;
428 } else {
429 throw new CoordinationServiceException(String.format("Failed to get node list for %s", category.getDisplayName()), ex);
430 }
431 }
432 }
433
442 private String getFullyQualifiedNodePath(CategoryNode category, String nodePath) {
443 // nodePath on Unix systems starts with a "/" and ZooKeeper doesn't like two slashes in a row
444 if (nodePath.startsWith("/")) {
445 return categoryNodeToPath.get(category.getDisplayName()) + nodePath.toUpperCase();
446 } else {
447 return categoryNodeToPath.get(category.getDisplayName()) + "/" + nodePath.toUpperCase();
448 }
449 }
450
454 public final static class CoordinationServiceException extends Exception {
455
456 private static final long serialVersionUID = 1L;
457
458 private CoordinationServiceException(String message) {
459 super(message);
460 }
461
462 private CoordinationServiceException(String message, Throwable cause) {
463 super(message, cause);
464 }
465 }
466
472 public static class Lock implements AutoCloseable {
473
478 private final InterProcessMutex interProcessLock;
479 private final String nodePath;
480
481 private Lock(String nodePath, InterProcessMutex lock) {
482 this.nodePath = nodePath;
483 this.interProcessLock = lock;
484 }
485
486 public String getNodePath() {
487 return nodePath;
488 }
489
491 try {
492 this.interProcessLock.release();
493 } catch (Exception ex) {
494 throw new CoordinationServiceException(String.format("Failed to release the lock on %s", nodePath), ex);
495 }
496 }
497
498 @Override
499 public void close() throws CoordinationServiceException {
500 release();
501 }
502 }
503
508 public enum CategoryNode {
509
510 CASES("cases"),
511 MANIFESTS("manifests"),
512 CONFIG("config"),
513 CENTRAL_REPO("centralRepository"),
514 HEALTH_MONITOR("healthMonitor");
515
516 private final String displayName;
517
518 private CategoryNode(String displayName) {
519 this.displayName = displayName;
520 }
521
522 public String getDisplayName() {
523 return displayName;
524 }
525 }
526}
Lock tryGetExclusiveLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit)
void setNodeData(CategoryNode category, String nodePath, byte[] data)
Lock tryGetSharedLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit)
String getFullyQualifiedNodePath(CategoryNode category, String nodePath)
Lock tryGetExclusiveLock(CategoryNode category, String nodePath)
byte[] getNodeData(CategoryNode category, String nodePath)
Lock tryGetSharedLock(CategoryNode category, String nodePath)
String upsertNodePath(CategoryNode category, String nodePath)

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