package guang.crawler.centerConfig;
import guang.crawler.connector.ZookeeperConnector;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.Properties;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.Transaction;
import org.apache.zookeeper.Watcher;
/**
* 该类表示中央配置器中的每个节点.可以对该节点进行增删改查,加锁解锁,监听.
* <p>
* 每个节点中的内容是相同的:都是基于Java Properties类的key value属性.
* </p>
*
* @author sun
*
*/
public abstract class CenterConfigElement {
/**
* 当前节点的路径
*/
protected final String path;
/**
* 底层的Zookeeper连接器
*/
protected final ZookeeperConnector connector;
/**
* 当前节点中存储的属性.
*/
private Properties values;
/**
* 当前节点的锁的路径.每个节点都可以有一个锁,从而控制对节点的并发访问.
*/
private static final String PATH_LOCK = "_lock";
/**
* 如果有进程向该节点发送通知,通知信息将写在当前节点属性中,属性的名称即为当前变量.
*/
private static final String KEY_NOTIFY_CHANGED = "notify.changed";
/**
* 创建一个配置器元素,需要传入代表该节点的路径,以及Zookeeper连接器.
*
* @param path
* @param connector
*/
public CenterConfigElement(final String path,
final ZookeeperConnector connector) {
this.path = path;
this.connector = connector;
this.values = new Properties();
}
/**
* 删除当前节点
*
* @param transaction
* @return
* @throws InterruptedException
*/
public boolean delete(final Transaction transaction)
throws InterruptedException {
return this.connector.recursiveDelete(this.path, transaction);
}
/**
* 从当前节点中删除某个属性
*
* @param key
* @param refreshNow
* @throws InterruptedException
* @throws IOException
* @throws KeeperException
*/
public void deleteProperty(final String key, final boolean refreshNow)
throws InterruptedException, IOException, KeeperException {
this.values.remove(key);
if (refreshNow) {
this.update();
}
}
/**
* 判断当前节点是否存在
*
* @return
* @throws KeeperException
* @throws InterruptedException
*/
public boolean exists() throws KeeperException, InterruptedException {
return this.connector.isNodeExists(this.path);
}
/**
* 获取当前节点的路径
*
* @return
*/
public String getPath() {
return this.path;
}
/**
* 获取当前节点中的所有属性
*
* @return
*/
public Properties getProperties() {
return this.values;
}
/**
* 根据key获取当前节点的属性
*
* @param key
* @return
*/
public String getProperty(final String key) {
return this.values.getProperty(key);
}
/**
* 从Zookeeper中加载该节点.在获取当前节点相应的属性之前应当调用该方法.另外,如果需要获取最新的Zookeeper中的数据,
* 也需要调用当前方法.
*
* @return
* @throws InterruptedException
* @throws IOException
*/
public boolean load() throws InterruptedException, IOException {
byte[] data = this.connector.getData(this.path);
if (data != null) {
this.values.load(new ByteArrayInputStream(data));
}
return true;
}
/**
* 对当前节点进行锁定
*
* @return
*/
public boolean lock() {
// TODO 这里应当仔细的检查该锁是否已经被当前线程获取了。
try {
String realPath = this.connector.createNode(this.path
+ CenterConfigElement.PATH_LOCK,
CreateMode.EPHEMERAL,
Long.toString(Thread.currentThread()
.getId())
.getBytes());
if (realPath == null) {
return false;
} else {
return true;
}
} catch (InterruptedException e) {
return false;
}
}
/**
* 通知当前节点有事件发生了.目前暂时不支持设置事件的种类和内容,只是通知一下.被通知的一方可以从阻塞状态中恢复,检测具体是什么样的事件发生了.
*
* @throws InterruptedException
* @throws IOException
* @throws KeeperException
*/
public void notifyChanged() throws InterruptedException, IOException,
KeeperException {
this.setProperty(CenterConfigElement.KEY_NOTIFY_CHANGED,
new Date().toString(), true);
}
/**
* 向当前节点中设置某个属性
*
* @param key
* @param value
* @param refreshNow
* @throws InterruptedException
* @throws IOException
* @throws KeeperException
*/
public void setProperty(final String key, final String value,
final boolean refreshNow) throws InterruptedException, IOException,
KeeperException {
this.values.put(key, value);
if (refreshNow) {
this.update();
}
}
/**
* 解除对当前节点的锁定
*
* @return
*/
public boolean unlock() {
try {
return this.connector.simpleDelete(this.path
+ CenterConfigElement.PATH_LOCK, null);
} catch (InterruptedException e) {
return false;
}
}
/**
* 将当前节点的数据存储到Zookeeper中,更新Zookeeper目录树.
*
* @return
* @throws InterruptedException
* @throws IOException
* @throws KeeperException
*/
public boolean update() throws InterruptedException, IOException,
KeeperException {
if (!this.exists()) {
return false;
}
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
this.values.store(byteOut, "update at " + new Date().toString());
byte[] data = byteOut.toByteArray();
try {
this.connector.updateData(this.path, data);
} finally {
byteOut.close();
}
return true;
}
/**
* 对当前节点的子节点进行监听,当前节点的子节点被删除或者增加时,监听的线程将得到通知.
*
* @param watcher
* @throws KeeperException
* @throws InterruptedException
*/
public void watchChildren(final Watcher watcher) throws KeeperException,
InterruptedException {
this.connector.watchChildren(this.path, watcher);
}
/**
* 对当前节点进行监听,当前节点创建,删除,数据更改都将触发事件,监听的线程将得到通知.
*
* @param watcher
* @throws KeeperException
* @throws InterruptedException
*/
public void watchNode(final Watcher watcher) throws KeeperException,
InterruptedException {
this.connector.watchNode(this.path, watcher);
}
}