/**
* Copyright 2011 LiveRamp
*
* 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.liveramp.hank.coordinator.zk;
import com.liveramp.hank.coordinator.*;
import com.liveramp.hank.zookeeper.WatchedMap;
import com.liveramp.hank.zookeeper.WatchedMapListener;
import com.liveramp.hank.zookeeper.ZkPath;
import com.liveramp.hank.zookeeper.ZooKeeperPlus;
import org.apache.zookeeper.KeeperException;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ZkRing extends AbstractRing {
private static final Pattern RING_NUMBER_PATTERN = Pattern.compile("ring-(\\d+)", Pattern.DOTALL);
private static final String HOSTS_PATH_SEGMENT = "hosts";
private final String ringPath;
private final WatchedMap<ZkHost> hosts;
private final ZooKeeperPlus zk;
private final Coordinator coordinator;
private final DataLocationChangeListener dataLocationChangeListener;
public static ZkRing create(ZooKeeperPlus zk,
Coordinator coordinator,
String ringGroup,
int ringNum,
RingGroup group,
DataLocationChangeListener dataLocationChangeListener) throws KeeperException, InterruptedException {
String ringPath = ZkPath.append(ringGroup, "ring-" + ringNum);
zk.create(ringPath, null);
zk.create(ZkPath.append(ringPath, HOSTS_PATH_SEGMENT), null);
return new ZkRing(zk, ringPath, group, coordinator, dataLocationChangeListener);
}
public ZkRing(ZooKeeperPlus zk,
final String ringPath,
RingGroup ringGroup,
final Coordinator coordinator,
final DataLocationChangeListener dataLocationChangeListener)
throws InterruptedException, KeeperException {
super(parseRingNum(ringPath), ringGroup);
this.zk = zk;
this.ringPath = ringPath;
this.coordinator = coordinator;
this.dataLocationChangeListener = dataLocationChangeListener;
if (coordinator == null) {
throw new RuntimeException("Cannot initialize a ZkRing with a null Coordinator.");
}
hosts = new WatchedMap<ZkHost>(zk, ZkPath.append(ringPath, HOSTS_PATH_SEGMENT), new WatchedMap.ElementLoader<ZkHost>() {
@Override
public ZkHost load(ZooKeeperPlus zk, String basePath, String relPath) throws InterruptedException, KeeperException {
return new ZkHost(zk, coordinator, ZkPath.append(basePath, relPath), dataLocationChangeListener,
false, null, null);
}
}, new DotComplete());
hosts.addListener(new ZkRing.HostsWatchedMapListener());
}
private class HostsWatchedMapListener implements WatchedMapListener<ZkHost> {
@Override
public void onWatchedMapChange(WatchedMap<ZkHost> zkHostWatchedMap) {
if (dataLocationChangeListener != null) {
dataLocationChangeListener.onDataLocationChange();
}
}
}
private static int parseRingNum(String ringPath) {
Matcher matcher = RING_NUMBER_PATTERN.matcher(ZkPath.getFilename(ringPath));
matcher.matches();
return Integer.parseInt(matcher.group(1));
}
@Override
public Set<Host> getHosts() {
return new HashSet<Host>(hosts.values());
}
@Override
public Host getHostByAddress(PartitionServerAddress address) {
for (ZkHost host : hosts.values()) {
if (host.getAddress().equals(address)) {
return host;
}
}
return null;
}
@Override
public Host addHost(PartitionServerAddress address,
List<String> flags) throws IOException {
try {
return ZkHost.create(zk, coordinator, ZkPath.append(ringPath, HOSTS_PATH_SEGMENT), address, dataLocationChangeListener, flags);
} catch (Exception e) {
throw new IOException(e);
}
}
@Override
public boolean removeHost(PartitionServerAddress address) throws IOException {
for (Map.Entry<String, ZkHost> entry : hosts.entrySet()) {
if (entry.getValue().getAddress().equals(address)) {
ZkHost host = hosts.remove(entry.getKey());
host.delete();
fireDataLocationChangeListener();
return true;
}
}
return false;
}
public void close() {
for (Host host : getHosts()) {
((ZkHost) host).close();
}
}
public void delete() throws IOException {
try {
zk.deleteNodeRecursively(ringPath);
} catch (Exception e) {
throw new IOException(e);
}
}
private void fireDataLocationChangeListener() {
if (dataLocationChangeListener != null) {
dataLocationChangeListener.onDataLocationChange();
}
}
}