org.midonet.midolman.state.ZkDirectory.java Source code

Java tutorial

Introduction

Here is the source code for org.midonet.midolman.state.ZkDirectory.java

Source

/*
 * Copyright 2014 Midokura SARL
 *
 * 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 org.midonet.midolman.state;

import org.midonet.util.eventloop.Reactor;
import org.apache.zookeeper.*;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import java.util.*;

public class ZkDirectory implements Directory {
    static final Logger log = LoggerFactory.getLogger(ZkDirectory.class);

    public ZkConnection zk;
    private String basePath;
    private List<ACL> acl;
    private Reactor reactor;

    /**
     * @param zk       the zookeeper object
     * @param basePath must start with "/"
     * @param acl      the list of {@link ACL} the we need to use
     * @param reactor  the delayed reactor loop
     */
    public ZkDirectory(ZkConnection zk, String basePath, List<ACL> acl, Reactor reactor) {
        this.zk = zk;
        this.basePath = basePath;
        this.acl = Ids.OPEN_ACL_UNSAFE;
        this.reactor = reactor;
    }

    @Override
    public String toString() {
        return ("ZkDirectory: base=" + basePath);
    }

    @Override
    public String add(String relativePath, byte[] data, CreateMode mode)
            throws KeeperException, InterruptedException {
        String absPath = getAbsolutePath(relativePath);
        String path = zk.getZooKeeper().create(absPath, data, acl, mode);
        return path.substring(basePath.length());
    }

    @Override
    public void ensureHas(String relativePath, byte[] data) throws KeeperException, InterruptedException {
        try {
            this.add(relativePath, data, CreateMode.PERSISTENT);
        } catch (KeeperException.NodeExistsException e) {
            /* node was there */ }
    }

    @Override
    public void asyncAdd(String relativePath, final byte[] data, CreateMode mode, final DirectoryCallback.Add cb) {

        final String absPath = getAbsolutePath(relativePath);

        zk.getZooKeeper().create(absPath, data, acl, mode, new AsyncCallback.StringCallback() {

            @Override
            public void processResult(int rc, String path, Object ctx, String name) {
                KeeperException.Code code = KeeperException.Code.get(rc);
                switch (code) {
                case OK:
                    cb.onSuccess(name.substring(basePath.length()));
                    break;
                default:
                    cb.onError(KeeperException.create(code, path));
                }
            }
        }, null);
    }

    @Override
    public void asyncAdd(String relativePath, final byte[] data, CreateMode mode) {

        final String absPath = getAbsolutePath(relativePath);

        zk.getZooKeeper().create(absPath, data, acl, mode, new AsyncCallback.StringCallback() {
            @Override
            public void processResult(int rc, String path, Object ctx, String name) {
            }
        }, null);
    }

    @Override
    public void update(String relativePath, byte[] data) throws KeeperException, InterruptedException {
        update(relativePath, data, -1);
    }

    @Override
    public void update(String relativePath, byte[] data, int version) throws KeeperException, InterruptedException {
        String absPath = getAbsolutePath(relativePath);
        zk.getZooKeeper().setData(absPath, data, version);
    }

    private Watcher wrapCallback(Runnable runnable) {
        if (null == runnable)
            return null;
        if (runnable instanceof TypedWatcher)
            return new MyTypedWatcher((TypedWatcher) runnable);

        return new MyWatcher(runnable);
    }

    private class MyWatcher implements Watcher {
        Runnable watcher;

        MyWatcher(Runnable watch) {
            watcher = watch;
        }

        @Override
        public void process(WatchedEvent arg0) {
            if (arg0.getType() == Event.EventType.None)
                return;

            if (null == reactor) {
                log.warn("Reactor is null - processing ZK event in ZK thread.");
                watcher.run();
            } else {
                reactor.submit(watcher);
            }
        }
    }

    private class MyTypedWatcher implements Watcher, Runnable {
        TypedWatcher watcher;
        WatchedEvent watchedEvent;

        private MyTypedWatcher(TypedWatcher watcher) {
            this.watcher = watcher;
        }

        @Override
        public void process(WatchedEvent event) {
            if (null == reactor) {
                log.warn("Reactor is null - processing ZK event in ZK thread.");
                dispatchEvent(event, watcher);
            } else {
                watchedEvent = event;
                reactor.submit(this);
            }
        }

        @Override
        public void run() {
            dispatchEvent(watchedEvent, watcher);
        }

        private void dispatchEvent(WatchedEvent event, TypedWatcher typedWatcher) {
            switch (event.getType()) {
            case NodeDeleted:
                typedWatcher.pathDeleted(event.getPath());
                break;

            case NodeCreated:
                typedWatcher.pathCreated(event.getPath());
                break;

            case NodeChildrenChanged:
                typedWatcher.pathChildrenUpdated(event.getPath());
                break;

            case NodeDataChanged:
                typedWatcher.pathDataChanged(event.getPath());
                break;

            case None:
                typedWatcher.connectionStateChanged(event.getState());
                break;
            }
        }
    }

    @Override
    public byte[] get(String relativePath, Runnable watcher) throws KeeperException, InterruptedException {
        String absPath = getAbsolutePath(relativePath);
        return zk.getZooKeeper().getData(absPath, wrapCallback(watcher), null);
    }

    @Override
    public Map.Entry<byte[], Integer> getWithVersion(String relativePath, Runnable watcher)
            throws KeeperException, InterruptedException {
        String absPath = getAbsolutePath(relativePath);
        Stat returnStat = new Stat();
        int version = -1;

        byte[] data = zk.getZooKeeper().getData(absPath, wrapCallback(watcher), returnStat);

        if (returnStat != null) {
            version = returnStat.getVersion();
        }

        return new AbstractMap.SimpleEntry<byte[], Integer>(data, version);
    }

    @Override
    public void asyncGet(String relativePath, final DirectoryCallback<byte[]> dataCallback, TypedWatcher watcher) {
        zk.getZooKeeper().getData(getAbsolutePath(relativePath), wrapCallback(watcher),
                new AsyncCallback.DataCallback() {
                    @Override
                    public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
                        if (rc == KeeperException.Code.OK.intValue()) {
                            dataCallback.onSuccess(data);
                        } else {
                            dataCallback.onError(KeeperException.create(KeeperException.Code.get(rc), path));
                        }
                    }
                }, null);
    }

    @Override
    public Set<String> getChildren(String relativePath, Runnable watcher)
            throws KeeperException, InterruptedException {
        String absPath = getAbsolutePath(relativePath);

        // path cannot end with / so strip it off
        if (absPath.endsWith("/")) {
            absPath = absPath.substring(0, absPath.length() - 1);
        }

        return new HashSet<String>(zk.getZooKeeper().getChildren(absPath, wrapCallback(watcher)));
    }

    @Override
    public void asyncGetChildren(String relativePath, final DirectoryCallback<Set<String>> cb,
            TypedWatcher watcher) {
        zk.getZooKeeper().getChildren(getAbsolutePath(relativePath), wrapCallback(watcher),
                new AsyncCallback.Children2Callback() {
                    @Override
                    public void processResult(int rc, String path, Object ctx, List<String> children, Stat stat) {
                        if (rc == KeeperException.Code.OK.intValue()) {
                            cb.onSuccess(new HashSet<String>(children));
                        } else {
                            cb.onError(KeeperException.create(KeeperException.Code.get(rc), path));
                        }
                    }
                }, null);
    }

    @Override
    public boolean exists(String path, Watcher watcher) throws KeeperException, InterruptedException {
        String absPath = getAbsolutePath(path);
        return zk.getZooKeeper().exists(absPath, watcher) != null;
    }

    @Override
    public boolean exists(String path, Runnable watcher) throws KeeperException, InterruptedException {
        return exists(path, wrapCallback(watcher));
    }

    @Override
    public boolean has(String relativePath) throws KeeperException, InterruptedException {
        String absPath = getAbsolutePath(relativePath);
        return zk.getZooKeeper().exists(absPath, null) != null;
    }

    @Override
    public void delete(String relativePath) throws KeeperException, InterruptedException {
        String absPath = getAbsolutePath(relativePath);
        zk.getZooKeeper().delete(absPath, -1);
    }

    @Override
    public void asyncDelete(String relativePath, final DirectoryCallback.Void callback) {
        zk.getZooKeeper().delete(relativePath, -1, new AsyncCallback.VoidCallback() {
            @Override
            public void processResult(int rc, String path, Object ctx) {
                if (rc == KeeperException.Code.OK.intValue()) {
                    callback.onSuccess(null);
                } else {
                    callback.onError(KeeperException.create(KeeperException.Code.get(rc), path));
                }
            }
        }, null);
    }

    @Override
    public void asyncDelete(String relativePath) {
        String absPath = getAbsolutePath(relativePath);
        zk.getZooKeeper().delete(absPath, -1, new AsyncCallback.VoidCallback() {
            @Override
            public void processResult(int rc, String path, Object ctx) {

            }
        }, null);
    }

    @Override
    public Directory getSubDirectory(String relativePath) {
        return new ZkDirectory(zk, getAbsolutePath(relativePath), null, reactor);
    }

    private String getAbsolutePath(String relativePath) {
        if (relativePath.isEmpty())
            return basePath;
        if (!relativePath.startsWith("/"))
            throw new IllegalArgumentException("Path must start with '/'.");
        return basePath + relativePath;
    }

    @Override
    public List<OpResult> multi(List<Op> ops) throws InterruptedException, KeeperException {
        return zk.getZooKeeper().multi(ops);
    }

    public void asyncMultiPathGet(@Nonnull final Set<String> relativePaths,
            final DirectoryCallback<Set<byte[]>> cb) {
        if (relativePaths.isEmpty()) {
            log.debug("Empty set of paths, is that OK?");
            cb.onSuccess(Collections.<byte[]>emptySet());
        }
        // Map to keep track of the callbacks that returned
        // TODO(rossella) probably it's better to return a ConcurrentMap and make
        // sure that all the updates are seen
        // (http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap2.shtml)
        final Map<String, byte[]> callbackResults = new HashMap<String, byte[]>();
        for (final String path : relativePaths) {
            asyncGet(path, new DirectoryCallback<byte[]>() {

                @Override
                public void onTimeout() {
                    synchronized (callbackResults) {
                        callbackResults.put(path, null);
                    }
                    log.error("asyncMultiPathGet - Timeout {}", path);
                }

                @Override
                public void onError(KeeperException e) {
                    synchronized (callbackResults) {
                        callbackResults.put(path, null);
                    }
                    log.error("asyncMultiPathGet - Exception {}", path, e);
                }

                @Override
                public void onSuccess(byte[] data) {
                    synchronized (callbackResults) {
                        callbackResults.put(path, data);
                        if (callbackResults.size() == relativePaths.size()) {
                            Set<byte[]> results = new HashSet<byte[]>();
                            for (Map.Entry entry : callbackResults.entrySet()) {
                                if (entry != null)
                                    results.add((byte[]) entry.getValue());
                            }
                            cb.onSuccess(results);
                        }
                    }
                }
            }, null);
        }
    }

    @Override
    public long getSessionId() {
        return zk.getZooKeeper().getSessionId();
    }

    @Override
    public void closeConnection() {
        log.info("Closing the Zookeeper connection.");
        zk.close();
    }
}