com.alibaba.otter.shared.arbitrate.impl.zookeeper.lock.DistributedLock.java Source code

Java tutorial

Introduction

Here is the source code for com.alibaba.otter.shared.arbitrate.impl.zookeeper.lock.DistributedLock.java

Source

/*
 * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
 *
 * Licensed 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 com.alibaba.otter.shared.arbitrate.impl.zookeeper.lock;

import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.locks.ReentrantLock;

import org.I0Itec.zkclient.IZkConnection;
import org.I0Itec.zkclient.exception.ZkException;
import org.I0Itec.zkclient.exception.ZkInterruptedException;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.NestableRuntimeException;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.otter.shared.arbitrate.impl.zookeeper.AsyncWatcher;
import com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;
import com.alibaba.otter.shared.common.utils.lock.BooleanMutex;
import com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;
import com.alibaba.otter.shared.common.utils.zookeeper.ZooKeeperx;

/**
 * zookeeper?? <br/>
 * document : <br/>
 * <a href="http://zookeeper.apache.org/doc/trunk/recipes.html">http://zookeeper.apache.org/doc/trunk/recipes.html</a>
 * 
 * <pre>
 * ?
 *  {@linkplain ReentrantLock}ReentrantLock??????ReentrantLock
 *  {@linkplain DistributedLock}??????DistributedLock
 *  <strong>?DistributedLocklock/unlock?!!</strong>
 *  
 * ?
 * <code>
 *         DistributedLock lock = new DistributedLock("/lock/");
 *         try {
 *             lock.lock();
 *             // do something
 *         } catch (InterruptedException e1) {
 *             // ?
 *         } catch (KeeperException e1) {
 *             // zookeeper
 *         } finally {
 *             try {
 *                 lock.unlock();
 *             } catch (KeeperException e) {
 *                 // zookeeper
 *             }
 *         }
 * </code>
 * </pre>
 * 
 * @author jianghang 2011-9-29 ?11:16:07
 * @version 4.0.0
 */
public class DistributedLock {

    private static final Logger logger = LoggerFactory.getLogger(DistributedLock.class);
    private static final byte[] data = { 0x12, 0x34 };
    // private static final Long DEFAULT_TIMEOUT_PERIOD = 60 * 1000L;
    private ZkClientx zookeeper = ZooKeeperClient.getInstance();
    private final String root; // 
    private String id;
    private LockNode idName;
    private String ownerId;
    private String lastChildId;
    private Throwable other = null;
    private KeeperException exception = null;
    private InterruptedException interrupt = null;

    public DistributedLock(String root) {
        this.root = root;
        ensureExists(root);
    }

    /**
     * ??????
     */
    public void lock() throws InterruptedException, KeeperException {
        // ??
        if (exception != null) {
            throw exception;
        }

        if (interrupt != null) {
            throw interrupt;
        }

        if (other != null) {
            throw new NestableRuntimeException(other);
        }

        if (isOwner()) {// ??
            return;
        }

        BooleanMutex mutex = new BooleanMutex();
        acquireLock(mutex);

        mutex.get();
        // ??zookeeper???watcher??
        // try {
        // mutex.get(DEFAULT_TIMEOUT_PERIOD, TimeUnit.MILLISECONDS);// true
        // } catch (TimeoutException e) {
        // if (!mutex.state()) {
        // lock();
        // }
        // }

        if (exception != null) {
            unlock();
            throw exception;
        }

        if (interrupt != null) {
            unlock();
            throw interrupt;
        }

        if (other != null) {
            unlock();
            throw new NestableRuntimeException(other);
        }
    }

    /**
     * ???, ?
     * 
     * @throws InterruptedException
     * @throws KeeperException
     */
    public boolean tryLock() throws KeeperException {
        // ??
        if (exception != null) {
            throw exception;
        }

        if (isOwner()) {// ??
            return true;
        }

        acquireLock(null);

        if (exception != null) {
            unlock();
            throw exception;
        }

        if (interrupt != null) {
            unlock();
            Thread.currentThread().interrupt();
        }

        if (other != null) {
            unlock();
            throw new NestableRuntimeException(other);
        }

        return isOwner();
    }

    /**
     * ?
     */
    public void unlock() throws KeeperException {
        if (id != null) {
            zookeeper.delete(root + "/" + id);
            id = null;
            idName = null;
        } else {
            // do nothing
        }
    }

    private void ensureExists(final String path) {
        try {
            if (zookeeper.exists(path)) {
                return;
            }

            zookeeper.create(path, data, CreateMode.PERSISTENT);
        } catch (ZkInterruptedException e) {
            Thread.currentThread().interrupt();
            interrupt = (InterruptedException) e.getCause();
        } catch (ZkException e) {
            exception = (KeeperException) e.getCause();
        }
    }

    /**
     * ?path
     */
    public String getRoot() {
        return root;
    }

    /**
     * ???owner
     */
    public boolean isOwner() {
        return id != null && ownerId != null && id.equals(ownerId);
    }

    /**
     * ?id
     */
    public String getId() {
        return this.id;
    }

    // ===================== helper method =============================

    /**
     * lock??watch????lock?
     */
    private Boolean acquireLock(final BooleanMutex mutex) {
        try {
            do {
                if (id == null) {// ?lock
                    long sessionId = getSessionId();
                    String prefix = "x-" + sessionId + "-";
                    // 
                    String path = zookeeper.create(root + "/" + prefix, data, CreateMode.EPHEMERAL_SEQUENTIAL);
                    int index = path.lastIndexOf("/");
                    id = StringUtils.substring(path, index + 1);
                    idName = new LockNode(id);
                }

                if (id != null) {
                    List<String> names = zookeeper.getChildren(root);
                    if (names.isEmpty()) {
                        logger.warn("lock lost with scene:empty list, id[] and node[]", id, idName);
                        unlock();// ??
                    } else {
                        // ?
                        SortedSet<LockNode> sortedNames = new TreeSet<LockNode>();
                        for (String name : names) {
                            sortedNames.add(new LockNode(name));
                        }

                        if (sortedNames.contains(idName) == false) {
                            logger.warn("lock lost with scene:not contains ,id[] and node[]", id, idName);
                            unlock();// ??
                            continue;
                        }

                        // ?ownerId
                        ownerId = sortedNames.first().getName();
                        if (mutex != null && isOwner()) {
                            mutex.set(true);// ?
                            return true;
                        } else if (mutex == null) {
                            return isOwner();
                        }

                        SortedSet<LockNode> lessThanMe = sortedNames.headSet(idName);
                        if (!lessThanMe.isEmpty()) {
                            // ?
                            LockNode lastChildName = lessThanMe.last();
                            lastChildId = lastChildName.getName();
                            // watcher?
                            IZkConnection connection = zookeeper.getConnection();
                            // zkclient?zk?lock??watcher?zk?
                            ZooKeeper orginZk = ((ZooKeeperx) connection).getZookeeper();
                            Stat stat = orginZk.exists(root + "/" + lastChildId, new AsyncWatcher() {

                                public void asyncProcess(WatchedEvent event) {
                                    if (!mutex.state()) { // ?????lock
                                        acquireLock(mutex);
                                    } else {
                                        logger.warn("locked successful.");
                                    }
                                }

                            });

                            if (stat == null) {
                                acquireLock(mutex);// ????watcher?
                            }
                        } else {
                            if (isOwner()) {
                                mutex.set(true);
                            } else {
                                logger.warn("lock lost with scene:no less ,id[] and node[]", id, idName);
                                unlock();// ?idownerId??
                            }
                        }
                    }
                }
            } while (id == null);
        } catch (KeeperException e) {
            exception = e;
            if (mutex != null) {
                mutex.set(true);
            }
        } catch (InterruptedException e) {
            interrupt = e;
            if (mutex != null) {
                mutex.set(true);
            }
        } catch (Throwable e) {
            other = e;
            if (mutex != null) {
                mutex.set(true);
            }
        }

        if (isOwner() && mutex != null) {
            mutex.set(true);
        }
        return Boolean.FALSE;
    }

    private long getSessionId() {
        IZkConnection connection = zookeeper.getConnection();
        return ((ZooKeeperx) connection).getZookeeper().getSessionId();
    }
}