org.apache.manifoldcf.core.lockmanager.ZooKeeperConnection.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.manifoldcf.core.lockmanager.ZooKeeperConnection.java

Source

/* $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();
            }
        }

    }

}