/* $Id$ */
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.manifoldcf.core.lockmanager;
import org.apache.manifoldcf.core.interfaces.*;
import org.apache.manifoldcf.core.system.Logging;
import org.apache.manifoldcf.core.system.ManifoldCF;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Stat;
import java.util.*;
import java.io.*;
/** An instance of this class is the Zookeeper analog to a database connection.
* Basically, it bundles up the Zookeeper functionality we need in a nice package,
* which we can share between users as needed. These connections will be pooled,
* and will be closed when the process they live in is shut down.
*/
public class ZooKeeperConnection
{
public static final String _rcsid = "@(#)$Id$";
private static final String READ_PREFIX = "read-";
private static final String NONEXWRITE_PREFIX = "nonexwrite-";
private static final String WRITE_PREFIX = "write-";
private static final String CHILD_PREFIX = "child-";
protected final String connectString;
protected final int sessionTimeout;
// Our zookeeper client
protected ZooKeeper zookeeper = null;
protected ZooKeeperWatcher zookeeperWatcher = null;
// Transient state
protected String lockNode = null;
protected String nodePath = null;
protected byte[] nodeData = null;
/** Constructor. */
public ZooKeeperConnection(String connectString, int sessionTimeout)
throws ManifoldCFException, InterruptedException
{
this.connectString = connectString;
this.sessionTimeout = sessionTimeout;
zookeeperWatcher = new ZooKeeperWatcher();
createSession();
}
protected void createSession()
throws ManifoldCFException, InterruptedException
{
try
{
zookeeper = new ZooKeeper(connectString, sessionTimeout, zookeeperWatcher);
}
catch (InterruptedIOException e)
{
throw new InterruptedException(e.getMessage());
}
catch (IOException e)
{
throw new ManifoldCFException("Zookeeper initialization error: "+e.getMessage(),e);
}
}
/** Create a transient node.
*/
public void createNode(String nodePath, byte[] nodeData)
throws ManifoldCFException, InterruptedException
{
if (this.nodePath != null)
throw new IllegalStateException("Ephemeral node '"+this.nodePath+"' already open; can't open '"+nodePath+"'.");
while (true)
{
try
{
if (this.nodePath == null)
{
zookeeper.create(nodePath, nodeData, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
// Keep a record of the ephemeral node
this.nodePath = nodePath;
this.nodeData = nodeData;
}
break;
}
catch (KeeperException e)
{
handleEphemeralNodeKeeperException(e,false);
}
}
}
/** Check whether a node exists.
*@param nodePath is the path of the node.
*@return the data, if the node if exists, otherwise null.
*/
public boolean checkNodeExists(String nodePath)
throws ManifoldCFException, InterruptedException
{
while (true)
{
try
{
return (zookeeper.exists(nodePath,false) != null);
}
catch (KeeperException e)
{
handleKeeperException(e,true);
}
}
}
/** Get node data.
*@param nodePath is the path of the node.
*@return the data, if the node if exists, otherwise null.
*/
public byte[] getNodeData(String nodePath)
throws ManifoldCFException, InterruptedException
{
return readData(nodePath);
}
/** Set node data.
*/
public void setNodeData(byte[] data)
throws ManifoldCFException, InterruptedException
{
if (nodePath == null)
throw new IllegalStateException("Can't set data for a node path we did not create: '"+nodePath+"'");
writeData(nodePath, data);
this.nodeData = data;
}
/** Delete a node.
*/
public void deleteNode()
throws ManifoldCFException, InterruptedException
{
if (nodePath == null)
throw new IllegalStateException("Can't delete ephemeral node that isn't registered: '"+nodePath+"'");
while (true)
{
try
{
if (nodePath != null)
{
zookeeper.delete(nodePath,-1);
nodePath = null;
nodeData = null;
}
return;
}
catch (KeeperException e)
{
handleEphemeralNodeKeeperException(e,false);
}
}
}
/** Delete all a node's children.
*/
public void deleteNodeChildren(String nodePath)
throws ManifoldCFException, InterruptedException
{
while (true)
{
try
{
List<String> children = zookeeper.getChildren(nodePath,false);
for (String child : children)
{
zookeeper.delete(nodePath + "/" + child,-1);
}
break;
}
catch (KeeperException.NoNodeException e)
{
break;
}
catch (KeeperException e)
{
handleKeeperException(e,true);
}
}
}
/** Get the relative paths of all node's children. If the node does not exist,
* return an empty list.
*/
public List<String> getChildren(String nodePath)
throws ManifoldCFException, InterruptedException
{
while (true)
{
try
{
//System.out.println("Children of '"+nodePath+"':");
List<String> children = zookeeper.getChildren(nodePath,false);
List<String> rval = new ArrayList<String>();
for (String child : children)
{
//System.out.println(" '"+child+"'");
if (child.startsWith(CHILD_PREFIX))
rval.add(child.substring(CHILD_PREFIX.length()));
}
return rval;
}
catch (KeeperException.NoNodeException e)
{
return new ArrayList<String>();
}
catch (KeeperException e)
{
handleKeeperException(e,true);
}
}
}
/** Create a persistent child of a node.
*/
public void createChild(String nodePath, String childName)
throws ManifoldCFException, InterruptedException
{
while (true)
{
try
{
//System.out.println("Creating child '"+childName+"' of nodepath '"+nodePath+"'");
createPersistentPath(nodePath + "/" + CHILD_PREFIX + childName, null);
break;
}
catch (KeeperException e)
{
handleKeeperException(e,true);
}
}
}
protected void createPersistentPath(String path, byte[] data)
throws KeeperException, InterruptedException
{
// Loop until we've created the entire path, but initially try the whole thing for performance
while (true)
{
try
{
zookeeper.create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
// Break on success
break;
}
catch (KeeperException.NoNodeException e)
{
// Strip off last part of path, and try recursively
int lastIndex = path.lastIndexOf("/");
// No last path: rethrow because we don't have a clue what is going on
if (lastIndex == -1 || lastIndex == 0)
throw e;
createPersistentPath(path.substring(0,lastIndex),null);
// Retry
}
catch (KeeperException.NodeExistsException e)
{
// Path is there: someone else beat us to it
// But we do have to set the data.
// NOTE: This code relies on the fact that only leaf persistent nodes have data.
if (data != null)
{
try
{
zookeeper.setData(path, data, -1);
}
catch (KeeperException.NoNodeException e2)
{
// Repeat, since we've lost our node
continue;
}
}
break;
}
}
}
/** Delete the child of a node.
*/
public void deleteChild(String nodePath, String childName)
throws ManifoldCFException, InterruptedException
{
while (true)
{
try
{
//System.out.println("Deleting child '"+childName+"' of nodePath '"+nodePath+"'");
zookeeper.delete(nodePath + "/" + CHILD_PREFIX + childName, -1);
//System.out.println("...done");
break;
}
catch (KeeperException e)
{
handleKeeperException(e,true);
}
}
}
/** Obtain a write lock, with no wait.
*@param lockPath is the lock node path.
*@return true if the lock was obtained, false otherwise.
*/
public boolean obtainWriteLockNoWait(String lockPath)
throws ManifoldCFException, InterruptedException
{
if (lockNode != null)
throw new IllegalStateException("Already have a lock in place: '"+lockNode+"'; can't also write lock '"+lockPath+"'");
while (true)
{
try
{
// Assert that we want a write lock
if (lockNode == null)
lockNode = createSequentialChild(lockPath,WRITE_PREFIX);
String lockSequenceNumber = lockNode.substring(lockPath.length() + 1 + WRITE_PREFIX.length());
// See if we got it
List<String> children = zookeeper.getChildren(lockPath,false);
for (String x : children)
{
String otherLock;
if (x.startsWith(WRITE_PREFIX))
otherLock = x.substring(WRITE_PREFIX.length());
else if (x.startsWith(NONEXWRITE_PREFIX))
otherLock = x.substring(NONEXWRITE_PREFIX.length());
else if (x.startsWith(READ_PREFIX))
otherLock = x.substring(READ_PREFIX.length());
else
continue;
if (otherLock.compareTo(lockSequenceNumber) < 0)
{
// We didn't get the lock. Clean up and exit
releaseLock();
return false;
}
}
// We got it!
return true;
}
catch (InterruptedException e)
{
lockNode = null;
throw e;
}
catch (KeeperException e)
{
handleEphemeralNodeKeeperException(e,true);
}
}
}
/** Obtain a write lock, with wait.
*@param lockPath is the lock node path.
*/
public void obtainWriteLock(String lockPath)
throws ManifoldCFException, InterruptedException
{
if (lockNode != null)
throw new IllegalStateException("Already have a lock in place: '"+lockNode+"'; can't also write lock '"+lockPath+"'");
while (true)
{
try
{
// Assert that we want a write lock
if (lockNode == null)
lockNode = createSequentialChild(lockPath,WRITE_PREFIX);
long lockSequenceNumber = new Long(lockNode.substring(lockPath.length() + 1 + WRITE_PREFIX.length())).longValue();
//System.out.println("Trying to get write lock for '"+lockSequenceNumber+"'");
while (true)
{
//System.out.println("Assessing whether we got lock for '"+lockNode+"'...");
// See if we got it
List<String> children = zookeeper.getChildren(lockPath,false);
String previousLock = null;
boolean gotLock = true;
long highestPreviousLockIndex = -1L;
for (String x : children)
{
String otherLock;
if (x.startsWith(WRITE_PREFIX))
otherLock = x.substring(WRITE_PREFIX.length());
else if (x.startsWith(NONEXWRITE_PREFIX))
otherLock = x.substring(NONEXWRITE_PREFIX.length());
else if (x.startsWith(READ_PREFIX))
otherLock = x.substring(READ_PREFIX.length());
else
continue;
long otherLockSequenceNumber = new Long(otherLock).longValue();
//System.out.println("Saw other child sequence number "+otherLockSequenceNumber);
if (otherLockSequenceNumber < lockSequenceNumber)
{
// We didn't get the lock. But keep going because we're looking for the node right before the
// one we just asserted.
gotLock = false;
if (otherLockSequenceNumber > highestPreviousLockIndex)
{
previousLock = x;
highestPreviousLockIndex = otherLockSequenceNumber;
}
}
}
if (gotLock)
{
// We got it!
//System.out.println("Got write lock for '"+lockSequenceNumber+"'");
return;
}
// There SHOULD be a previous node immediately prior to the one we asserted. If we didn't find one, go back around;
// the previous lock was probably created and destroyed before we managed to get the children.
if (previousLock != null)
{
//System.out.println(" Waiting on '"+previousLock+"' for write lock '"+lockSequenceNumber+"'");
// Create an exists() watch on the previous node, and wait until we are awakened by that watch firing.
ExistsWatcher w = new ExistsWatcher();
Stat s = zookeeper.exists(lockPath+"/"+previousLock, w);
if (s != null)
w.waitForEvent();
}
//else
// System.out.println(" Retrying for write lock '"+lockSequenceNumber+"'");
}
}
catch (InterruptedException e)
{
lockNode = null;
throw e;
}
catch (KeeperException e)
{
handleEphemeralNodeKeeperException(e,true);
}
}
}
/** Obtain a non-ex-write lock, with no wait.
*@param lockPath is the lock node path.
*@return true if the lock was obtained, false otherwise.
*/
public boolean obtainNonExWriteLockNoWait(String lockPath)
throws ManifoldCFException, InterruptedException
{
if (lockNode != null)
throw new IllegalStateException("Already have a lock in place: '"+lockNode+"'; can't also non-ex write lock '"+lockPath+"'");
while (true)
{
try
{
// Assert that we want a read lock
if (lockNode == null)
lockNode = createSequentialChild(lockPath,NONEXWRITE_PREFIX);
String lockSequenceNumber = lockNode.substring(lockPath.length() + 1 + NONEXWRITE_PREFIX.length());
// See if we got it
List<String> children = null;
while (true)
{
try
{
children = zookeeper.getChildren(lockPath,false);
break;
}
catch (KeeperException.NoNodeException e)
{
// New session; back around again.
break;
}
catch (KeeperException e)
{
handleKeeperException(e,true);
}
}
if (children == null)
{
// Reassert ephemeral node b/c we had a session restart
continue;
}
for (String x : children)
{
String otherLock;
if (x.startsWith(WRITE_PREFIX))
otherLock = x.substring(WRITE_PREFIX.length());
else if (x.startsWith(READ_PREFIX))
otherLock = x.substring(READ_PREFIX.length());
else
continue;
if (otherLock.compareTo(lockSequenceNumber) < 0)
{
// We didn't get the lock. Clean up and exit
releaseLock();
return false;
}
}
// We got it!
return true;
}
catch (InterruptedException e)
{
lockNode = null;
throw e;
}
catch (KeeperException e)
{
handleEphemeralNodeKeeperException(e,true);
}
}
}
/** Obtain a non-ex-write lock, with wait.
*@param lockPath is the lock node path.
*/
public void obtainNonExWriteLock(String lockPath)
throws ManifoldCFException, InterruptedException
{
if (lockNode != null)
throw new IllegalStateException("Already have a lock in place: '"+lockNode+"'; can't also non-ex write lock '"+lockPath+"'");
while (true)
{
try
{
// Assert that we want a read lock
if (lockNode == null)
lockNode = createSequentialChild(lockPath,NONEXWRITE_PREFIX);
long lockSequenceNumber = new Long(lockNode.substring(lockPath.length() + 1 + NONEXWRITE_PREFIX.length())).longValue();
while (true)
{
// See if we got it
List<String> children = null;
while (true)
{
try
{
children = zookeeper.getChildren(lockPath,false);
break;
}
catch (KeeperException.NoNodeException e)
{
break;
}
catch (KeeperException e)
{
handleKeeperException(e,true);
}
}
if (children == null)
break;
String previousLock = null;
boolean gotLock = true;
long highestPreviousLockIndex = -1L;
for (String x : children)
{
String otherLock;
if (x.startsWith(WRITE_PREFIX))
otherLock = x.substring(WRITE_PREFIX.length());
else if (x.startsWith(READ_PREFIX))
otherLock = x.substring(READ_PREFIX.length());
else
continue;
long otherLockSequenceNumber = new Long(otherLock).longValue();
//System.out.println("Saw other child sequence number "+otherLockSequenceNumber);
if (otherLockSequenceNumber < lockSequenceNumber)
{
// We didn't get the lock. But keep going because we're looking for the node right before the
// one we just asserted.
gotLock = false;
if (otherLockSequenceNumber > highestPreviousLockIndex)
{
previousLock = x;
highestPreviousLockIndex = otherLockSequenceNumber;
}
}
}
if (gotLock)
{
// We got it!
return;
}
// There SHOULD be a previous node immediately prior to the one we asserted. If we didn't find one, go back around;
// the previous lock was probably created and destroyed before we managed to get the children.
if (previousLock != null)
{
// Create an exists() watch on the previous node, and wait until we are awakened by that watch firing.
ExistsWatcher w = new ExistsWatcher();
Stat s = zookeeper.exists(lockPath+"/"+previousLock, w);
if (s != null)
w.waitForEvent();
}
}
}
catch (InterruptedException e)
{
lockNode = null;
throw e;
}
catch (KeeperException e)
{
handleEphemeralNodeKeeperException(e,true);
}
}
}
/** Obtain a read lock, with no wait.
*@param lockPath is the lock node path.
*@return true if the lock was obtained, false otherwise.
*/
public boolean obtainReadLockNoWait(String lockPath)
throws ManifoldCFException, InterruptedException
{
if (lockNode != null)
throw new IllegalStateException("Already have a lock in place: '"+lockNode+"'; can't also read lock '"+lockPath+"'");
while (true)
{
try
{
// Assert that we want a read lock
if (lockNode == null)
lockNode = createSequentialChild(lockPath,READ_PREFIX);
String lockSequenceNumber = lockNode.substring(lockPath.length() + 1 + READ_PREFIX.length());
// See if we got it
List<String> children = null;
while (true)
{
try
{
children = zookeeper.getChildren(lockPath,false);
break;
}
catch (KeeperException.NoNodeException e)
{
break;
}
catch (KeeperException e)
{
handleKeeperException(e,true);
}
}
if (children == null)
continue;
for (String x : children)
{
String otherLock;
if (x.startsWith(WRITE_PREFIX))
otherLock = x.substring(WRITE_PREFIX.length());
else if (x.startsWith(NONEXWRITE_PREFIX))
otherLock = x.substring(NONEXWRITE_PREFIX.length());
else
continue;
if (otherLock.compareTo(lockSequenceNumber) < 0)
{
// We didn't get the lock. Clean up and exit
releaseLock();
return false;
}
}
// We got it!
return true;
}
catch (InterruptedException e)
{
lockNode = null;
throw e;
}
catch (KeeperException e)
{
handleEphemeralNodeKeeperException(e,true);
}
}
}
/** Obtain a read lock, with wait.
*@param lockPath is the lock node path.
*/
public void obtainReadLock(String lockPath)
throws ManifoldCFException, InterruptedException
{
if (lockNode != null)
throw new IllegalStateException("Already have a lock in place: '"+lockNode+"'; can't also read lock '"+lockPath+"'");
while (true)
{
try
{
// Assert that we want a read lock
if (lockNode == null)
lockNode = createSequentialChild(lockPath,READ_PREFIX);
long lockSequenceNumber = new Long(lockNode.substring(lockPath.length() + 1 + READ_PREFIX.length())).longValue();
//System.out.println("Trying to get read lock for '"+lockSequenceNumber+"'");
while (true)
{
// See if we got it
List<String> children = null;
while (true)
{
try
{
children = zookeeper.getChildren(lockPath,false);
break;
}
catch (KeeperException.NoNodeException e)
{
break;
}
catch (KeeperException e)
{
handleKeeperException(e,true);
}
}
// Handle new session
if (children == null)
break;
String previousLock = null;
boolean gotLock = true;
long highestPreviousLockIndex = -1L;
for (String x : children)
{
String otherLock;
if (x.startsWith(WRITE_PREFIX))
otherLock = x.substring(WRITE_PREFIX.length());
else if (x.startsWith(NONEXWRITE_PREFIX))
otherLock = x.substring(NONEXWRITE_PREFIX.length());
else
continue;
long otherLockSequenceNumber = new Long(otherLock).longValue();
//System.out.println("Saw other child sequence number "+otherLockSequenceNumber);
if (otherLockSequenceNumber < lockSequenceNumber)
{
// We didn't get the lock. But keep going because we're looking for the node right before the
// one we just asserted.
gotLock = false;
if (otherLockSequenceNumber > highestPreviousLockIndex)
{
previousLock = x;
highestPreviousLockIndex = otherLockSequenceNumber;
}
}
}
if (gotLock)
{
// We got it!
//System.out.println("Got read lock for '"+lockSequenceNumber+"'");
return;
}
// There SHOULD be a previous node immediately prior to the one we asserted. If we didn't find one, go back around;
// the previous lock was probably created and destroyed before we managed to get the children.
if (previousLock != null)
{
//System.out.println(" Waiting on '"+previousLock+"' for read lock '"+lockSequenceNumber+"'");
// Create an exists() watch on the previous node, and wait until we are awakened by that watch firing.
ExistsWatcher w = new ExistsWatcher();
Stat s = zookeeper.exists(lockPath+"/"+previousLock, w);
if (s != null)
w.waitForEvent();
}
//else
// System.out.println(" Retrying for read lock '"+lockSequenceNumber+"'");
}
}
catch (InterruptedException e)
{
lockNode = null;
throw e;
}
catch (KeeperException e)
{
handleEphemeralNodeKeeperException(e,true);
}
}
}
/** Release the (saved) lock.
*/
public void releaseLock()
throws ManifoldCFException, InterruptedException
{
if (lockNode == null)
throw new IllegalStateException("Can't release lock we don't hold");
//System.out.println("Releasing lock '"+lockNode+"'");
while (true)
{
if (lockNode == null)
break;
try
{
zookeeper.delete(lockNode,-1);
lockNode = null;
break;
}
catch (InterruptedException e)
{
lockNode = null;
throw e;
}
catch (KeeperException e)
{
handleEphemeralNodeKeeperException(e,true);
}
}
}
public byte[] readData(String resourcePath)
throws ManifoldCFException, InterruptedException
{
while (true)
{
try
{
return zookeeper.getData(resourcePath,false,null);
}
catch (KeeperException.NoNodeException e)
{
return null;
}
catch (KeeperException e)
{
handleKeeperException(e,true);
}
}
}
public void writeData(String resourcePath, byte[] data)
throws ManifoldCFException, InterruptedException
{
while (true)
{
try
{
if (data == null)
{
try
{
zookeeper.delete(resourcePath, -1);
}
catch (KeeperException.NoNodeException e)
{
}
break;
}
else
{
try
{
zookeeper.setData(resourcePath, data, -1);
}
catch (KeeperException.NoNodeException e)
{
createPersistentPath(resourcePath, data);
}
break;
}
}
catch (KeeperException e)
{
handleKeeperException(e,true);
}
}
}
public void setGlobalFlag(String flagPath)
throws ManifoldCFException, InterruptedException
{
while (true)
{
try
{
createPersistentPath(flagPath, null);
break;
}
catch (KeeperException e)
{
handleKeeperException(e,true);
}
}
}
public void clearGlobalFlag(String flagPath)
throws ManifoldCFException, InterruptedException
{
while (true)
{
try
{
try
{
zookeeper.delete(flagPath,-1);
}
catch (KeeperException.NoNodeException e)
{
}
break;
}
catch (KeeperException e)
{
handleKeeperException(e,true);
}
}
}
public boolean checkGlobalFlag(String flagPath)
throws ManifoldCFException, InterruptedException
{
while (true)
{
try
{
Stat s = zookeeper.exists(flagPath,false);
return s != null;
}
catch (KeeperException e)
{
handleKeeperException(e,true);
}
}
}
/** Close this connection. */
public void close()
throws InterruptedException
{
if (lockNode != null)
throw new IllegalStateException("Should not be closing handles that have open locks! Locknode: '"+lockNode+"'");
zookeeper.close();
zookeeper = null;
zookeeperWatcher = null;
}
/** Handle keeper exceptions that may involve ephemeral node creation.
*/
protected void handleEphemeralNodeKeeperException(KeeperException e, boolean recreate)
throws ManifoldCFException, InterruptedException
{
if (e instanceof KeeperException.ConnectionLossException || e instanceof KeeperException.SessionExpiredException)
{
while (true)
{
try
{
// Close the handle, open a new one
lockNode = null;
if (!recreate)
{
nodePath = null;
nodeData = null;
}
zookeeper.close();
createSession();
// Lock is lost, but we can (and should) recreate the ephemeral nodes
if (nodePath != null)
{
zookeeper.create(nodePath, nodeData, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
}
break;
}
catch (KeeperException e2)
{
if (!(e2 instanceof KeeperException.ConnectionLossException) && !(e2 instanceof KeeperException.SessionExpiredException))
throw new ManifoldCFException(e2.getMessage(),e2);
}
}
}
else
{
// If nothing we know how to deal with, throw.
throw new ManifoldCFException(e.getMessage(),e);
}
}
/** Handle keeper exceptions that don't involve ephemeral node creation.
*/
protected void handleKeeperException(KeeperException e, boolean recreate)
throws ManifoldCFException, InterruptedException
{
if (e instanceof KeeperException.ConnectionLossException)
{
// Retry if connection loss
ManifoldCF.sleep(100L);
}
else if (e instanceof KeeperException.SessionExpiredException)
{
while (true)
{
try
{
// Close the handle, open a new one
lockNode = null;
if (!recreate)
{
nodePath = null;
nodeData = null;
}
zookeeper.close();
createSession();
// Lock is lost, but we can (and should) recreate the ephemeral nodes
if (nodePath != null)
{
zookeeper.create(nodePath, nodeData, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
}
break;
}
catch (KeeperException e2)
{
if (!(e2 instanceof KeeperException.ConnectionLossException) && !(e2 instanceof KeeperException.SessionExpiredException))
throw new ManifoldCFException(e2.getMessage(),e2);
}
}
}
else
{
// If nothing we know how to deal with, throw.
throw new ManifoldCFException(e.getMessage(),e);
}
}
public static String zooKeeperSafeName(String input)
{
// Escape "/" characters
StringBuilder sb = new StringBuilder();
for (int i = 0; i < input.length(); i++)
{
char x = input.charAt(i);
if (x == '/')
sb.append('\\').append('0');
else if (x == '\u007f')
sb.append('\\').append('1');
else if (x == '\\')
sb.append('\\').append('\\');
else if (x >= '\u0000' && x < '\u0020')
sb.append('\\').append(x + '\u0040');
else if (x >= '\u0080' && x < '\u00a0')
sb.append('\\').append(x + '\u0060' - '\u0080');
else
sb.append(x);
}
return sb.toString();
}
public static String zooKeeperDecodeSafeName(String input)
{
// Escape "/" characters
StringBuilder sb = new StringBuilder();
int i = 0;
while (i < input.length())
{
char x = input.charAt(i);
if (x == '\\')
{
i++;
if (i == input.length())
throw new RuntimeException("Supposedly safe zookeeper name is not properly encoded!!");
x = input.charAt(i);
if (x == '0')
sb.append('/');
else if (x == '1')
sb.append('\u007f');
else if (x == '\\')
sb.append('\\');
else if (x >= '\u0040' && x < '\u0060')
sb.append(x - '\u0040');
else if (x >= '\u0060' && x < '\u0080')
sb.append(x - '\u0060' + '\u0080');
else
throw new RuntimeException("Supposedly safe zookeeper name is not properly encoded!!");
}
else
sb.append(x);
i++;
}
return sb.toString();
}
// Protected methods
/** Create a node and a sequential child node. Neither node has any data.
*/
protected String createSequentialChild(String mainNode, String childPrefix)
throws KeeperException, InterruptedException
{
// Because zookeeper is so slow, AND reports all exceptions to the log, we do the minimum.
while (true)
{
try
{
return zookeeper.create(mainNode + "/" + childPrefix, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
}
catch (KeeperException.NoNodeException e)
{
try
{
zookeeper.create(mainNode, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
catch (KeeperException.NodeExistsException e2)
{
}
}
}
}
/** Watcher class for zookeeper, so we get notified about zookeeper events. */
protected static class ZooKeeperWatcher implements Watcher
{
public ZooKeeperWatcher()
{
}
@Override
public void process(WatchedEvent event)
{
}
}
/** Watcher class for exists state changes, so we get notified about deletions of lock request nodes. */
protected static class ExistsWatcher implements Watcher
{
protected boolean eventTriggered = false;
public ExistsWatcher()
{
}
@Override
public void process(WatchedEvent event)
{
synchronized (this)
{
eventTriggered = true;
notifyAll();
}
}
public void waitForEvent()
throws InterruptedException
{
synchronized (this)
{
if (eventTriggered)
return;
wait();
}
}
}
}