kz.greetgo.logging.zookeeper.config.EventConfigStorageZooKeeper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of greetgo-logging-zookeeper Show documentation
Show all versions of greetgo-logging-zookeeper Show documentation
Logging mechanism using in greetgo!
package kz.greetgo.logging.zookeeper.config;
import kz.greetgo.logging.zookeeper.core.ZookeeperConnectParams;
import lombok.NonNull;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.api.CuratorWatcher;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
public class EventConfigStorageZooKeeper extends EventConfigStorageAbstract implements AutoCloseable {
private final @NonNull ZookeeperConnectParams connectParams;
private final String rootPath;
private final AtomicReference zkHolder = new AtomicReference<>(null);
private final AtomicReference clientHolder = new AtomicReference<>(null);
public EventConfigStorageZooKeeper(String rootPath, @NonNull ZookeeperConnectParams connectParams) {
this.connectParams = connectParams;
this.rootPath = rootPath;
}
public void reset() {
{
ZooKeeper current = zkHolder.getAndSet(null);
if (current != null) {
try {
current.close();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
{
CuratorFramework current = clientHolder.getAndSet(null);
if (current != null) {
current.close();
}
}
}
public CuratorFramework client() {
if (!opened.get()) {
throw new RuntimeException(getClass().getSimpleName() + " closed");
}
return clientHolder.accumulateAndGet(null, (current, ignore) -> current != null ? current : createClient());
}
private CuratorFramework createClient() {
CuratorFramework client = connectParams.createClient();
client.start();
prepareWatchers(client);
return client;
}
private final AtomicBoolean opened = new AtomicBoolean(true);
@Override
public void close() {
opened.set(false);
reset();
}
private String slashRootPath() {
if (rootPath == null) {
return "/";
}
if (rootPath.startsWith("/")) {
return rootPath;
}
return "/" + rootPath;
}
private String zNode(String path) {
String slashPath = path == null ? "/" : (path.startsWith("/") ? path : "/" + path);
if (rootPath == null) {
return slashPath;
}
String slashRootPath = slashRootPath();
if (slashRootPath.endsWith("/")) {
return slashRootPath + slashPath.substring(1);
} else {
return slashRootPath + slashPath;
}
}
private String zNodeToPath(String zNode) {
if (rootPath == null) {
if (zNode == null || zNode.isEmpty()) {
return null;
}
if (zNode.startsWith("/")) {
return zNode.substring(1);
}
return zNode;
}
String slashRootPath = slashRootPath();
if (zNode == null || zNode.isEmpty()) {
return null;
}
if (slashRootPath.equals(zNode)) {
return slashRootPath;
}
if (!zNode.startsWith(slashRootPath + "/")) {
return null;
}
return zNode.substring(slashRootPath.length() + 1);
}
@Override
public Optional createdAt(String path) {
try {
//noinspection resource
return Optional.ofNullable(
client().checkExists().forPath(zNode(path))
).map(Stat::getCtime).map(Date::new);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private Result retrySome(Set> retryExceptions, ZookeeperTry zookeeperTry) {
int tryCount = connectParams.getRetriesOnSessionExpiredException();
int tries = 0;
List prevExceptions = new ArrayList<>();
TRY:
while (true) {
tries++;
try {
return zookeeperTry.tryOperation();
} catch (Exception error) {
for (Class> retryException : retryExceptions) {
if (retryException.isInstance(error)) {
if (tries <= tryCount) {
prevExceptions.add(error.getClass().getName() + ": " + error.getMessage());
reset();
continue TRY;
}
throw new RuntimeException("IWQ5D8RolL :: PrevExceptions: " + String.join("; ", prevExceptions), error);
}
}
throw new RuntimeException("doJF0012B1 :: PrevExceptions: " + String.join("; ", prevExceptions), error);
}
}
}
@Override
public Optional lastModifiedAt(String path) {
return retrySome(Set.of(
KeeperException.SessionExpiredException.class
), () -> {
//noinspection resource
return Optional.ofNullable(
client().checkExists().forPath(zNode(path))
).map(Stat::getMtime).map(Date::new);
});
}
@Override
public byte[] readContent(String path) {
return retrySome(Set.of(
KeeperException.SessionExpiredException.class
), () -> {
//noinspection resource
CuratorFramework client = client();
String zNode = zNode(path);
client.checkExists().forPath(zNode);
Stat stat = client.checkExists().forPath(zNode);
if (stat == null) {
return null;
}
return client.getData().forPath(zNode);
});
}
@Override
public void writeContent(String path, byte[] content) {
retrySome(Set.of(
KeeperException.SessionExpiredException.class,
KeeperException.NodeExistsException.class,
KeeperException.BadVersionException.class
), () -> {
doWriteContent(path, content);
return null;
});
}
private void doWriteContent(String path, byte[] content) throws Exception {
byte[] current = readContent(path);
if (Arrays.equals(current, content)) {
return;
}
//noinspection resource
CuratorFramework client = client();
String zNode = zNode(path);
Stat stat = client.checkExists().forPath(zNode);
if (content == null) {
if (stat == null) {
return;
}
nodesData.remove(path);
client.delete().withVersion(stat.getVersion()).forPath(zNode);
} else {
nodesData.put(path, content);
if (stat == null) {
client.create().creatingParentContainersIfNeeded().forPath(zNode, content);
} else {
client.setData().withVersion(stat.getVersion()).forPath(zNode, content);
}
}
}
private final ConcurrentHashMap lookingForMap = new ConcurrentHashMap<>();
private void prepareWatchers(CuratorFramework newClient) {
List zNodes = new ArrayList<>(lookingForMap.keySet());
for (String zNode : zNodes) {
installWatcherOn(newClient, zNode);
}
}
private void installWatcherOn(CuratorFramework client, String zNode) {
retrySome(Set.of(
KeeperException.SessionExpiredException.class
), () -> {
lookingForMap.put(zNode, client);
client.checkExists().usingWatcher((CuratorWatcher) this::processEvent).forPath(zNode);
return null;
});
}
private static ConfigEventType eventTypeToType(Watcher.Event.EventType type) {
if (type == null) {
return null;
}
switch (type) {
case NodeCreated:
return ConfigEventType.CREATE;
case NodeDataChanged:
return ConfigEventType.UPDATE;
case NodeDeleted:
return ConfigEventType.DELETE;
default:
return null;
}
}
@Override
public void ensureLookingFor(String path) {
if (path == null) {
throw new IllegalArgumentException("path == null");
}
CuratorFramework client = client();
String zNode = zNode(path);
if (client == lookingForMap.get(zNode)) {
return;
}
{
byte[] content = readContent(path);
if (content == null) {
nodesData.remove(path);
} else {
nodesData.put(path, content);
}
}
installWatcherOn(client, zNode);
}
private void processEvent(WatchedEvent event) {
String zNode = event.getPath();
String path = zNodeToPath(zNode);
if (path == null) {
return;
}
ConfigEventType eventType = eventTypeToType(event.getType());
if (eventType == null) {
return;
}
if (opened.get() && lookingForMap.containsKey(zNode)) {
installWatcherOn(client(), zNode);
}
fireConfigEventHandlerLocal(path, eventType);
}
private final ConcurrentHashMap nodesData = new ConcurrentHashMap<>();
private void fireConfigEventHandlerLocal(String path, ConfigEventType eventType) {
if (eventType == ConfigEventType.CREATE || eventType == ConfigEventType.UPDATE) {
while (true) {
byte[] current = readContent(path);
if (current == null) {
return;
}
byte[] cached = nodesData.get(path);
if (Arrays.equals(current, cached)) {
return;
}
if (nodesData.replace(path, cached, current)) {
fireConfigEventHandler(path, eventType);
return;
}
}
}
if (eventType == ConfigEventType.DELETE) {
while (true) {
byte[] current = readContent(path);
if (current != null) {
return;
}
byte[] cached = nodesData.get(path);
if (cached == null) {
return;
}
if (nodesData.remove(path, cached)) {
fireConfigEventHandler(path, eventType);
}
}
}
}
}