com.rapleaf.hank.coordinator.zk.ZkHostConfig.java Source code

Java tutorial

Introduction

Here is the source code for com.rapleaf.hank.coordinator.zk.ZkHostConfig.java

Source

/**
 *  Copyright 2011 Rapleaf
 *
 *  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.rapleaf.hank.coordinator.zk;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.KeeperException.Code;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.data.Stat;

import com.rapleaf.hank.coordinator.HostCommand;
import com.rapleaf.hank.coordinator.HostCommandQueueChangeListener;
import com.rapleaf.hank.coordinator.HostConfig;
import com.rapleaf.hank.coordinator.HostDomainConfig;
import com.rapleaf.hank.coordinator.HostState;
import com.rapleaf.hank.coordinator.HostStateChangeListener;
import com.rapleaf.hank.coordinator.PartDaemonAddress;

public class ZkHostConfig extends BaseZkConsumer implements HostConfig {
    private static final Logger LOG = Logger.getLogger(ZkHostConfig.class);

    private static final String STATUS_PATH_SEGMENT = "/status";
    private static final String COMPLETE_PATH_SEGMENT = "/.complete";
    private static final String PARTS_PATH_SEGMENT = "/parts";
    private static final String COMMAND_QUEUE_PATH_SEGMENT = "/command_queue";
    private static final String CURRENT_COMMAND_PATH_SEGMENT = "/current_command";

    private class CommandQueueWatcher extends HankWatcher {
        protected CommandQueueWatcher() throws KeeperException, InterruptedException {
            super();
        }

        @Override
        public void realProcess(WatchedEvent event) {
            LOG.trace(event);
            switch (event.getType()) {
            case NodeCreated:
            case NodeDeleted:
            case NodeDataChanged:
            case NodeChildrenChanged:
                for (HostCommandQueueChangeListener listener : commandQueueListeners) {
                    listener.onCommandQueueChange(ZkHostConfig.this);
                }
            }
        }

        @Override
        public void setWatch() throws KeeperException, InterruptedException {
            zk.getChildren(hostPath + COMMAND_QUEUE_PATH_SEGMENT, this);
        }
    }

    private class StateChangeWatcher extends HankWatcher {
        public StateChangeWatcher() throws KeeperException, InterruptedException {
            super();
        }

        @Override
        public void realProcess(WatchedEvent event) {
            switch (event.getType()) {
            case NodeCreated:
            case NodeDeleted:
            case NodeDataChanged:
                for (HostStateChangeListener listener : stateListeners) {
                    listener.onHostStateChange(ZkHostConfig.this);
                }
            }
        }

        @Override
        public void setWatch() throws KeeperException, InterruptedException {
            if (zk.exists(hostPath + STATUS_PATH_SEGMENT, this) != null) {
                zk.getData(hostPath + STATUS_PATH_SEGMENT, this, new Stat());
            }
        }
    }

    private final ZooKeeper zk;
    private final String hostPath;
    private final PartDaemonAddress address;

    private final Set<HostStateChangeListener> stateListeners = new HashSet<HostStateChangeListener>();
    private final StateChangeWatcher stateChangeWatcher;

    private final Set<HostCommandQueueChangeListener> commandQueueListeners = new HashSet<HostCommandQueueChangeListener>();
    private final CommandQueueWatcher commandQueueWatcher;

    public ZkHostConfig(ZooKeeper zk, String hostPath) throws KeeperException, InterruptedException {
        super(zk);
        this.zk = zk;
        this.hostPath = hostPath;

        String[] toks = hostPath.split("/");
        this.address = PartDaemonAddress.parse(toks[toks.length - 1]);

        stateChangeWatcher = new StateChangeWatcher();
        stateChangeWatcher.setWatch();
        commandQueueWatcher = new CommandQueueWatcher();
    }

    @Override
    public PartDaemonAddress getAddress() {
        return address;
    }

    @Override
    public HostState getState() throws IOException {
        try {
            if (zk.exists(hostPath + STATUS_PATH_SEGMENT, false) == null) {
                return HostState.OFFLINE;
            }
            try {
                return HostState.valueOf(getString(hostPath + STATUS_PATH_SEGMENT));
            } catch (KeeperException e) {
                if (e.code() == Code.NONODE) {
                    // the node disappeared between our exists check and our get. must be
                    // offline now.
                    return HostState.OFFLINE;
                }
                throw (e);
            }
        } catch (Exception e) {
            throw new IOException(e);
        }
    }

    @Override
    public void setStateChangeListener(final HostStateChangeListener listener) throws IOException {
        synchronized (stateListeners) {
            stateListeners.add(listener);
        }
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((address == null) ? 0 : address.hashCode());
        result = prime * result + ((hostPath == null) ? 0 : hostPath.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        ZkHostConfig other = (ZkHostConfig) obj;
        if (address == null) {
            if (other.address != null)
                return false;
        } else if (!address.equals(other.address))
            return false;
        if (hostPath == null) {
            if (other.hostPath != null)
                return false;
        } else if (!hostPath.equals(other.hostPath))
            return false;
        return true;
    }

    @Override
    public Set<HostDomainConfig> getAssignedDomains() throws IOException {
        List<String> domains;
        try {
            domains = zk.getChildren(hostPath + PARTS_PATH_SEGMENT, false);
        } catch (Exception e) {
            throw new IOException(e);
        }
        Set<HostDomainConfig> results = new HashSet<HostDomainConfig>();
        for (String domain : domains) {
            results.add(new ZkHostDomainConfig(zk, hostPath + PARTS_PATH_SEGMENT, Integer.parseInt(domain)));
        }
        return results;
    }

    @Override
    public HostDomainConfig addDomain(int domainId) throws IOException {
        try {
            if (zk.exists(hostPath + PARTS_PATH_SEGMENT + "/" + domainId, false) != null) {
                throw new IOException("Domain " + domainId + " is already assigned to this host!");
            }
        } catch (Exception e) {
            throw new IOException(e);
        }
        HostDomainConfig hdc = ZkHostDomainConfig.create(zk, hostPath + PARTS_PATH_SEGMENT, domainId);
        return hdc;
    }

    @Override
    public HostDomainConfig getDomainById(int domainId) {
        // TODO: this should be done with a map and caching
        try {
            for (HostDomainConfig hdc : getAssignedDomains()) {
                if (hdc.getDomainId() == domainId) {
                    return hdc;
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return null;
    }

    @Override
    public HostCommand getCurrentCommand() throws IOException {
        try {
            String commandString = getString(hostPath + CURRENT_COMMAND_PATH_SEGMENT);
            if (commandString == null) {
                return null;
            }
            return HostCommand.valueOf(commandString);
        } catch (Exception e) {
            throw new IOException(e);
        }
    }

    @Override
    public boolean isOnline() throws IOException {
        return getState() != HostState.OFFLINE;
    }

    @Override
    public void setState(HostState state) throws IOException {
        try {
            if (state == HostState.OFFLINE) {
                deleteIfExists(hostPath + STATUS_PATH_SEGMENT);
            } else {
                setOrCreate(hostPath + STATUS_PATH_SEGMENT, state.toString(), CreateMode.EPHEMERAL);
            }
        } catch (Exception e) {
            throw new IOException(e);
        }
    }

    @Override
    public void completeCommand() throws IOException {
        try {
            zk.setData(hostPath + CURRENT_COMMAND_PATH_SEGMENT, null, -1);
        } catch (Exception e) {
            throw new IOException(e);
        }
    }

    @Override
    public void enqueueCommand(HostCommand command) throws IOException {
        try {
            zk.create(hostPath + COMMAND_QUEUE_PATH_SEGMENT + "/command_", command.toString().getBytes(),
                    Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
        } catch (Exception e) {
            throw new IOException(e);
        }
    }

    @Override
    public List<HostCommand> getCommandQueue() throws IOException {
        try {
            List<String> children = zk.getChildren(hostPath + COMMAND_QUEUE_PATH_SEGMENT, false);
            Collections.sort(children);
            List<HostCommand> queue = new ArrayList<HostCommand>();
            for (String child : children) {
                queue.add(HostCommand.valueOf(getString(hostPath + COMMAND_QUEUE_PATH_SEGMENT + "/" + child)));
            }
            return queue;
        } catch (Exception e) {
            throw new IOException(e);
        }
    }

    @Override
    public HostCommand processNextCommand() throws IOException {
        try {
            // get the queue and sort so we have correct ordering
            List<String> children = zk.getChildren(hostPath + COMMAND_QUEUE_PATH_SEGMENT, false);
            Collections.sort(children);

            // if there are no children, the queue is empty.
            if (children.size() == 0) {
                return null;
            }

            // parse out the actual command
            String headOfQueuePath = hostPath + COMMAND_QUEUE_PATH_SEGMENT + "/" + children.get(0);
            HostCommand nextCommand = HostCommand.valueOf(getString(headOfQueuePath));

            // set the current command
            zk.setData(hostPath + CURRENT_COMMAND_PATH_SEGMENT, nextCommand.toString().getBytes(), -1);
            // delete the head of the queue
            zk.delete(headOfQueuePath, -1);

            return nextCommand;
        } catch (Exception e) {
            throw new IOException(e);
        }
    }

    @Override
    public void setCommandQueueChangeListener(HostCommandQueueChangeListener listener) {
        synchronized (commandQueueListeners) {
            commandQueueListeners.add(listener);
        }
    }

    public static ZkHostConfig create(ZooKeeper zk, String root, PartDaemonAddress partDaemonAddress)
            throws KeeperException, InterruptedException {
        String hostPath = root + "/" + partDaemonAddress.toString();
        LOG.trace("creating host " + hostPath);
        zk.create(hostPath, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        zk.create(hostPath + PARTS_PATH_SEGMENT, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        zk.create(hostPath + CURRENT_COMMAND_PATH_SEGMENT, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        zk.create(hostPath + COMMAND_QUEUE_PATH_SEGMENT, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        zk.create(hostPath + COMPLETE_PATH_SEGMENT, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        return new ZkHostConfig(zk, hostPath);
    }

    @Override
    public String toString() {
        return "ZkHostConfig [address=" + address + "]";
    }

    @Override
    public void cancelStateChangeListener(HostStateChangeListener listener) {
        synchronized (stateListeners) {
            stateListeners.remove(listener);
        }
    }

    public void close() {
        stateChangeWatcher.cancel();
        commandQueueWatcher.cancel();
    }
}