Autopsy  4.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-2017 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.coordinationservice;
20 
21 import java.io.IOException;
22 import java.util.Collection;
23 import java.util.Iterator;
24 import java.util.Map;
25 import java.util.concurrent.ConcurrentHashMap;
26 import java.util.concurrent.TimeUnit;
27 import javax.annotation.concurrent.GuardedBy;
28 import javax.annotation.concurrent.ThreadSafe;
29 import org.apache.curator.RetryPolicy;
30 import org.apache.curator.framework.CuratorFramework;
31 import org.apache.curator.framework.CuratorFrameworkFactory;
32 import org.apache.curator.framework.recipes.locks.InterProcessMutex;
33 import org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock;
34 import org.apache.curator.retry.ExponentialBackoffRetry;
35 import org.apache.zookeeper.CreateMode;
36 import org.apache.zookeeper.KeeperException;
37 import org.apache.zookeeper.KeeperException.NoNodeException;
38 import org.apache.zookeeper.WatchedEvent;
39 import org.apache.zookeeper.ZooDefs;
40 import org.apache.zookeeper.ZooKeeper;
41 import org.openide.util.Lookup;
43 
49 @ThreadSafe
50 public final class CoordinationService {
51 
52  private static final int SESSION_TIMEOUT_MILLISECONDS = 300000;
53  private static final int CONNECTION_TIMEOUT_MILLISECONDS = 300000;
54  private static final int ZOOKEEPER_SESSION_TIMEOUT_MILLIS = 3000;
55  private static final int ZOOKEEPER_CONNECTION_TIMEOUT_MILLIS = 15000;
56  private static final int PORT_OFFSET = 1000; // When run in Solr, ZooKeeper defaults to Solr port + 1000
57  private static final String DEFAULT_NAMESPACE_ROOT = "autopsy";
58  @GuardedBy("CoordinationService.class")
59  private static CoordinationService instance;
60  private final CuratorFramework curator;
61  @GuardedBy("categoryNodeToPath")
62  private final Map<String, String> categoryNodeToPath;
63 
73  private static boolean isZooKeeperAccessible() throws InterruptedException, IOException {
74  boolean result = false;
75  Object workerThreadWaitNotifyLock = new Object();
76  int zooKeeperServerPort = Integer.valueOf(UserPreferences.getIndexingServerPort()) + PORT_OFFSET;
77  String connectString = UserPreferences.getIndexingServerHost() + ":" + zooKeeperServerPort;
78  ZooKeeper zooKeeper = new ZooKeeper(connectString, ZOOKEEPER_SESSION_TIMEOUT_MILLIS,
79  (WatchedEvent event) -> {
80  synchronized (workerThreadWaitNotifyLock) {
81  workerThreadWaitNotifyLock.notify();
82  }
83  });
84  synchronized (workerThreadWaitNotifyLock) {
85  workerThreadWaitNotifyLock.wait(ZOOKEEPER_CONNECTION_TIMEOUT_MILLIS);
86  }
87  ZooKeeper.States state = zooKeeper.getState();
88  if (state == ZooKeeper.States.CONNECTED || state == ZooKeeper.States.CONNECTEDREADONLY) {
89  result = true;
90  }
91  zooKeeper.close();
92  return result;
93  }
94 
104  public synchronized static CoordinationService getInstance() throws CoordinationServiceException {
105  if (null == instance) {
106  String rootNode;
107  Collection<? extends CoordinationServiceNamespace> providers = Lookup.getDefault().lookupAll(CoordinationServiceNamespace.class);
108  Iterator<? extends CoordinationServiceNamespace> it = providers.iterator();
109  if (it.hasNext()) {
110  rootNode = it.next().getNamespaceRoot();
111  } else {
112  rootNode = DEFAULT_NAMESPACE_ROOT;
113  }
114  try {
115  instance = new CoordinationService(rootNode);
116  } catch (IOException | InterruptedException | KeeperException | CoordinationServiceException ex) {
117  throw new CoordinationServiceException("Failed to create coordination service", ex);
118  }
119  }
120  return instance;
121  }
122 
132  private CoordinationService(String rootNodeName) throws InterruptedException, IOException, KeeperException, CoordinationServiceException {
133 
134  if (false == isZooKeeperAccessible()) {
135  throw new CoordinationServiceException("Unable to access ZooKeeper");
136  }
137 
138  /*
139  * Connect to ZooKeeper via Curator.
140  */
141  RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
142  int zooKeeperServerPort = Integer.valueOf(UserPreferences.getIndexingServerPort()) + PORT_OFFSET;
143  String connectString = UserPreferences.getIndexingServerHost() + ":" + zooKeeperServerPort;
144  curator = CuratorFrameworkFactory.newClient(connectString, SESSION_TIMEOUT_MILLISECONDS, CONNECTION_TIMEOUT_MILLISECONDS, retryPolicy);
145  curator.start();
146 
147  /*
148  * Create the top-level root and category nodes.
149  */
150  String rootNode = rootNodeName;
151  if (!rootNode.startsWith("/")) {
152  rootNode = "/" + rootNode;
153  }
154  categoryNodeToPath = new ConcurrentHashMap<>();
155  for (CategoryNode node : CategoryNode.values()) {
156  String nodePath = rootNode + "/" + node.getDisplayName();
157  try {
158  curator.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE).forPath(nodePath);
159  } catch (KeeperException ex) {
160  if (ex.code() != KeeperException.Code.NODEEXISTS) {
161  throw ex;
162  }
163  } catch (Exception ex) {
164  throw new CoordinationServiceException("Curator experienced an error", ex);
165  }
166  categoryNodeToPath.put(node.getDisplayName(), nodePath);
167  }
168  }
169 
190  public Lock tryGetExclusiveLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit) throws CoordinationServiceException, InterruptedException {
191  String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
192  try {
193  InterProcessReadWriteLock lock = new InterProcessReadWriteLock(curator, fullNodePath);
194  if (lock.writeLock().acquire(timeOut, timeUnit)) {
195  return new Lock(nodePath, lock.writeLock());
196  } else {
197  return null;
198  }
199  } catch (Exception ex) {
200  if (ex instanceof InterruptedException) {
201  throw (InterruptedException) ex;
202  } else {
203  throw new CoordinationServiceException(String.format("Failed to get exclusive lock for %s", fullNodePath), ex);
204  }
205  }
206  }
207 
224  public Lock tryGetExclusiveLock(CategoryNode category, String nodePath) throws CoordinationServiceException {
225  String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
226  try {
227  InterProcessReadWriteLock lock = new InterProcessReadWriteLock(curator, fullNodePath);
228  if (!lock.writeLock().acquire(0, TimeUnit.SECONDS)) {
229  return null;
230  }
231  return new Lock(nodePath, lock.writeLock());
232  } catch (Exception ex) {
233  throw new CoordinationServiceException(String.format("Failed to get exclusive lock for %s", fullNodePath), ex);
234  }
235  }
236 
257  public Lock tryGetSharedLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit) throws CoordinationServiceException, InterruptedException {
258  String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
259  try {
260  InterProcessReadWriteLock lock = new InterProcessReadWriteLock(curator, fullNodePath);
261  if (lock.readLock().acquire(timeOut, timeUnit)) {
262  return new Lock(nodePath, lock.readLock());
263  } else {
264  return null;
265  }
266  } catch (Exception ex) {
267  if (ex instanceof InterruptedException) {
268  throw (InterruptedException) ex;
269  } else {
270  throw new CoordinationServiceException(String.format("Failed to get shared lock for %s", fullNodePath), ex);
271  }
272  }
273  }
274 
291  public Lock tryGetSharedLock(CategoryNode category, String nodePath) throws CoordinationServiceException {
292  String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
293  try {
294  InterProcessReadWriteLock lock = new InterProcessReadWriteLock(curator, fullNodePath);
295  if (!lock.readLock().acquire(0, TimeUnit.SECONDS)) {
296  return null;
297  }
298  return new Lock(nodePath, lock.readLock());
299  } catch (Exception ex) {
300  throw new CoordinationServiceException(String.format("Failed to get shared lock for %s", fullNodePath), ex);
301  }
302  }
303 
318  public byte[] getNodeData(CategoryNode category, String nodePath) throws CoordinationServiceException, InterruptedException {
319  String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
320  try {
321  return curator.getData().forPath(fullNodePath);
322  } catch (NoNodeException ex) {
323  return null;
324  } catch (Exception ex) {
325  if (ex instanceof InterruptedException) {
326  throw (InterruptedException) ex;
327  } else {
328  throw new CoordinationServiceException(String.format("Failed to get data for %s", fullNodePath), ex);
329  }
330  }
331  }
332 
345  public void setNodeData(CategoryNode category, String nodePath, byte[] data) throws CoordinationServiceException, InterruptedException {
346  String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
347  try {
348  curator.setData().forPath(fullNodePath, data);
349  } catch (Exception ex) {
350  if (ex instanceof InterruptedException) {
351  throw (InterruptedException) ex;
352  } else {
353  throw new CoordinationServiceException(String.format("Failed to set data for %s", fullNodePath), ex);
354  }
355  }
356  }
357 
366  private String getFullyQualifiedNodePath(CategoryNode category, String nodePath) {
367  return categoryNodeToPath.get(category.getDisplayName()) + "/" + nodePath.toUpperCase();
368  }
369 
373  public final static class CoordinationServiceException extends Exception {
374 
375  private static final long serialVersionUID = 1L;
376 
377  private CoordinationServiceException(String message) {
378  super(message);
379  }
380 
381  private CoordinationServiceException(String message, Throwable cause) {
382  super(message, cause);
383  }
384  }
385 
391  public static class Lock implements AutoCloseable {
392 
397  private final InterProcessMutex interProcessLock;
398  private final String nodePath;
399 
400  private Lock(String nodePath, InterProcessMutex lock) {
401  this.nodePath = nodePath;
402  this.interProcessLock = lock;
403  }
404 
405  public String getNodePath() {
406  return nodePath;
407  }
408 
409  public void release() throws CoordinationServiceException {
410  try {
411  this.interProcessLock.release();
412  } catch (Exception ex) {
413  throw new CoordinationServiceException(String.format("Failed to release the lock on %s", nodePath), ex);
414  }
415  }
416 
417  @Override
418  public void close() throws CoordinationServiceException {
419  release();
420  }
421  }
422 
427  public enum CategoryNode {
428 
429  CASES("cases"),
430  MANIFESTS("manifests"),
431  CONFIG("config");
432 
433  private final String displayName;
434 
435  private CategoryNode(String displayName) {
436  this.displayName = displayName;
437  }
438 
439  public String getDisplayName() {
440  return displayName;
441  }
442  }
443 }
Lock tryGetExclusiveLock(CategoryNode category, String nodePath)
byte[] getNodeData(CategoryNode category, String nodePath)
String getFullyQualifiedNodePath(CategoryNode category, String nodePath)
Lock tryGetSharedLock(CategoryNode category, String nodePath)
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)

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