Autopsy  4.6.0
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  */
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.List;
25 import java.util.Map;
26 import java.util.concurrent.ConcurrentHashMap;
27 import java.util.concurrent.TimeUnit;
28 import javax.annotation.concurrent.GuardedBy;
29 import javax.annotation.concurrent.ThreadSafe;
30 import org.apache.curator.RetryPolicy;
31 import org.apache.curator.framework.CuratorFramework;
32 import org.apache.curator.framework.CuratorFrameworkFactory;
33 import org.apache.curator.framework.recipes.locks.InterProcessMutex;
34 import org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock;
35 import org.apache.curator.retry.ExponentialBackoffRetry;
36 import org.apache.zookeeper.CreateMode;
37 import org.apache.zookeeper.KeeperException;
38 import org.apache.zookeeper.KeeperException.NoNodeException;
39 import org.apache.zookeeper.WatchedEvent;
40 import org.apache.zookeeper.ZooDefs;
41 import org.apache.zookeeper.ZooKeeper;
42 import org.openide.util.Lookup;
44 
50 @ThreadSafe
51 public 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 ZOOKEEPER_SESSION_TIMEOUT_MILLIS = 3000;
56  private static final int ZOOKEEPER_CONNECTION_TIMEOUT_MILLIS = 15000;
57  private static final int PORT_OFFSET = 1000; // When run in Solr, ZooKeeper defaults to Solr port + 1000
58  private static final String DEFAULT_NAMESPACE_ROOT = "autopsy";
59  @GuardedBy("CoordinationService.class")
60  private static CoordinationService instance;
61  private final CuratorFramework curator;
62  @GuardedBy("categoryNodeToPath")
63  private final Map<String, String> categoryNodeToPath;
64 
74  private static boolean isZooKeeperAccessible() throws InterruptedException, IOException {
75  boolean result = false;
76  Object workerThreadWaitNotifyLock = new Object();
77  int zooKeeperServerPort = Integer.valueOf(UserPreferences.getIndexingServerPort()) + PORT_OFFSET;
78  String connectString = UserPreferences.getIndexingServerHost() + ":" + zooKeeperServerPort;
79  ZooKeeper zooKeeper = new ZooKeeper(connectString, ZOOKEEPER_SESSION_TIMEOUT_MILLIS,
80  (WatchedEvent event) -> {
81  synchronized (workerThreadWaitNotifyLock) {
82  workerThreadWaitNotifyLock.notify();
83  }
84  });
85  synchronized (workerThreadWaitNotifyLock) {
86  workerThreadWaitNotifyLock.wait(ZOOKEEPER_CONNECTION_TIMEOUT_MILLIS);
87  }
88  ZooKeeper.States state = zooKeeper.getState();
89  if (state == ZooKeeper.States.CONNECTED || state == ZooKeeper.States.CONNECTEDREADONLY) {
90  result = true;
91  }
92  zooKeeper.close();
93  return result;
94  }
95 
105  public synchronized static CoordinationService getInstance() throws CoordinationServiceException {
106  if (null == instance) {
107  String rootNode;
108  Collection<? extends CoordinationServiceNamespace> providers = Lookup.getDefault().lookupAll(CoordinationServiceNamespace.class);
109  Iterator<? extends CoordinationServiceNamespace> it = providers.iterator();
110  if (it.hasNext()) {
111  rootNode = it.next().getNamespaceRoot();
112  } else {
113  rootNode = DEFAULT_NAMESPACE_ROOT;
114  }
115  try {
116  instance = new CoordinationService(rootNode);
117  } catch (IOException | InterruptedException | KeeperException | CoordinationServiceException ex) {
118  throw new CoordinationServiceException("Failed to create coordination service", ex);
119  }
120  }
121  return instance;
122  }
123 
133  private CoordinationService(String rootNodeName) throws InterruptedException, IOException, KeeperException, CoordinationServiceException {
134 
135  if (false == isZooKeeperAccessible()) {
136  throw new CoordinationServiceException("Unable to access ZooKeeper");
137  }
138 
139  /*
140  * Connect to ZooKeeper via Curator.
141  */
142  RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
143  int zooKeeperServerPort = Integer.valueOf(UserPreferences.getIndexingServerPort()) + PORT_OFFSET;
144  String connectString = UserPreferences.getIndexingServerHost() + ":" + zooKeeperServerPort;
145  curator = CuratorFrameworkFactory.newClient(connectString, SESSION_TIMEOUT_MILLISECONDS, CONNECTION_TIMEOUT_MILLISECONDS, retryPolicy);
146  curator.start();
147 
148  /*
149  * Create the top-level root and category nodes.
150  */
151  String rootNode = rootNodeName;
152 
153  if (!rootNode.startsWith("/")) {
154  rootNode = "/" + rootNode;
155  }
156  categoryNodeToPath = new ConcurrentHashMap<>();
157  for (CategoryNode node : CategoryNode.values()) {
158  String nodePath = rootNode + "/" + node.getDisplayName();
159  try {
160  curator.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE).forPath(nodePath);
161  } catch (KeeperException ex) {
162  if (ex.code() != KeeperException.Code.NODEEXISTS) {
163  throw ex;
164  }
165  } catch (Exception ex) {
166  throw new CoordinationServiceException("Curator experienced an error", ex);
167  }
168  categoryNodeToPath.put(node.getDisplayName(), nodePath);
169  }
170  }
171 
192  public Lock tryGetExclusiveLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit) throws CoordinationServiceException, InterruptedException {
193  String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
194  try {
195  InterProcessReadWriteLock lock = new InterProcessReadWriteLock(curator, fullNodePath);
196  if (lock.writeLock().acquire(timeOut, timeUnit)) {
197  return new Lock(nodePath, lock.writeLock());
198  } else {
199  return null;
200  }
201  } catch (Exception ex) {
202  if (ex instanceof InterruptedException) {
203  throw (InterruptedException) ex;
204  } else {
205  throw new CoordinationServiceException(String.format("Failed to get exclusive lock for %s", fullNodePath), ex);
206  }
207  }
208  }
209 
226  public Lock tryGetExclusiveLock(CategoryNode category, String nodePath) throws CoordinationServiceException {
227  String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
228  try {
229  InterProcessReadWriteLock lock = new InterProcessReadWriteLock(curator, fullNodePath);
230  if (!lock.writeLock().acquire(0, TimeUnit.SECONDS)) {
231  return null;
232  }
233  return new Lock(nodePath, lock.writeLock());
234  } catch (Exception ex) {
235  throw new CoordinationServiceException(String.format("Failed to get exclusive lock for %s", fullNodePath), ex);
236  }
237  }
238 
259  public Lock tryGetSharedLock(CategoryNode category, String nodePath, int timeOut, TimeUnit timeUnit) throws CoordinationServiceException, InterruptedException {
260  String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
261  try {
262  InterProcessReadWriteLock lock = new InterProcessReadWriteLock(curator, fullNodePath);
263  if (lock.readLock().acquire(timeOut, timeUnit)) {
264  return new Lock(nodePath, lock.readLock());
265  } else {
266  return null;
267  }
268  } catch (Exception ex) {
269  if (ex instanceof InterruptedException) {
270  throw (InterruptedException) ex;
271  } else {
272  throw new CoordinationServiceException(String.format("Failed to get shared lock for %s", fullNodePath), ex);
273  }
274  }
275  }
276 
293  public Lock tryGetSharedLock(CategoryNode category, String nodePath) throws CoordinationServiceException {
294  String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
295  try {
296  InterProcessReadWriteLock lock = new InterProcessReadWriteLock(curator, fullNodePath);
297  if (!lock.readLock().acquire(0, TimeUnit.SECONDS)) {
298  return null;
299  }
300  return new Lock(nodePath, lock.readLock());
301  } catch (Exception ex) {
302  throw new CoordinationServiceException(String.format("Failed to get shared lock for %s", fullNodePath), ex);
303  }
304  }
305 
320  public byte[] getNodeData(CategoryNode category, String nodePath) throws CoordinationServiceException, InterruptedException {
321  String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
322  try {
323  return curator.getData().forPath(fullNodePath);
324  } catch (NoNodeException ex) {
325  return null;
326  } catch (Exception ex) {
327  if (ex instanceof InterruptedException) {
328  throw (InterruptedException) ex;
329  } else {
330  throw new CoordinationServiceException(String.format("Failed to get data for %s", fullNodePath), ex);
331  }
332  }
333  }
334 
347  public void setNodeData(CategoryNode category, String nodePath, byte[] data) throws CoordinationServiceException, InterruptedException {
348  String fullNodePath = getFullyQualifiedNodePath(category, nodePath);
349  try {
350  curator.setData().forPath(fullNodePath, data);
351  } catch (Exception ex) {
352  if (ex instanceof InterruptedException) {
353  throw (InterruptedException) ex;
354  } else {
355  throw new CoordinationServiceException(String.format("Failed to set data for %s", fullNodePath), ex);
356  }
357  }
358  }
359 
370  public List<String> getNodeList(CategoryNode category) throws CoordinationServiceException {
371  try {
372  List<String> list = curator.getChildren().forPath(categoryNodeToPath.get(category.getDisplayName()));
373  return list;
374  } catch (Exception ex) {
375  throw new CoordinationServiceException(String.format("Failed to get node list for %s", category.getDisplayName()), ex);
376  }
377  }
378 
387  private String getFullyQualifiedNodePath(CategoryNode category, String nodePath) {
388  // nodePath on Unix systems starts with a "/" and ZooKeeper doesn't like two slashes in a row
389  if(nodePath.startsWith("/")){
390  return categoryNodeToPath.get(category.getDisplayName()) + nodePath.toUpperCase();
391  }else{
392  return categoryNodeToPath.get(category.getDisplayName()) + "/" + nodePath.toUpperCase();
393  }
394  }
395 
399  public final static class CoordinationServiceException extends Exception {
400 
401  private static final long serialVersionUID = 1L;
402 
403  private CoordinationServiceException(String message) {
404  super(message);
405  }
406 
407  private CoordinationServiceException(String message, Throwable cause) {
408  super(message, cause);
409  }
410  }
411 
417  public static class Lock implements AutoCloseable {
418 
423  private final InterProcessMutex interProcessLock;
424  private final String nodePath;
425 
426  private Lock(String nodePath, InterProcessMutex lock) {
427  this.nodePath = nodePath;
428  this.interProcessLock = lock;
429  }
430 
431  public String getNodePath() {
432  return nodePath;
433  }
434 
435  public void release() throws CoordinationServiceException {
436  try {
437  this.interProcessLock.release();
438  } catch (Exception ex) {
439  throw new CoordinationServiceException(String.format("Failed to release the lock on %s", nodePath), ex);
440  }
441  }
442 
443  @Override
444  public void close() throws CoordinationServiceException {
445  release();
446  }
447  }
448 
453  public enum CategoryNode {
454 
455  CASES("cases"),
456  MANIFESTS("manifests"),
457  CONFIG("config"),
458  CENTRAL_REPO("centralRepository"),
459  HEALTH_MONITOR("healthMonitor");
460 
461  private final String displayName;
462 
463  private CategoryNode(String displayName) {
464  this.displayName = displayName;
465  }
466 
467  public String getDisplayName() {
468  return displayName;
469  }
470  }
471 }
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 May 7 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.