/** * Copyright (C) 2014 Stratio (http://stratio.com) * * 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.stratio.decision.utils; import java.util.Date; import java.util.Iterator; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.imps.CuratorFrameworkState; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.data.Stat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.gson.Gson; import com.stratio.decision.commons.constants.STREAMING; import com.stratio.decision.commons.constants.STREAM_OPERATIONS; import com.stratio.decision.commons.messages.StratioStreamingMessage; public class ZKUtils { private static Logger logger = LoggerFactory.getLogger(ZKUtils.class); private static ZKUtils self; private CuratorFramework client; private ExecutorService backgroundZookeeperCleanerTasks; private String groupId; private ZKUtils(String zookeeperCluster) throws Exception { this(zookeeperCluster, null); } private ZKUtils(String zookeeperCluster, String groupId) throws Exception { this.groupId = groupId; // ZOOKEPER CONNECTION client = CuratorFrameworkFactory.newClient(zookeeperCluster, 25 * 1000, 10 * 1000, new ExponentialBackoffRetry( 1000, 3)); client.start(); client.getZookeeperClient().blockUntilConnectedOrTimedOut(); if (client.getState().compareTo(CuratorFrameworkState.STARTED) != 0) { throw new Exception("Connection to Zookeeper timed out after seconds"); } else { backgroundZookeeperCleanerTasks = Executors.newFixedThreadPool(1); backgroundZookeeperCleanerTasks.submit(new ZookeeperBackgroundCleaner(client, groupId)); } } public static ZKUtils getZKUtils(String zookeeperCluster) throws Exception { if (self == null) { self = new ZKUtils(zookeeperCluster); } return self; } public static ZKUtils getZKUtils(String zookeeperCluster, String groupId) throws Exception { if (self == null) { self = new ZKUtils(zookeeperCluster, groupId); } return self; } public static void shutdownZKUtils() { if (self != null) { self.backgroundZookeeperCleanerTasks.shutdownNow(); self.client.close(); } } public void createEphemeralZNode(String path, byte[] data) throws Exception { if (client.checkExists().forPath(path) != null) { client.delete().deletingChildrenIfNeeded().forPath(path); } client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path, data); } public void createPath(String path) throws Exception { if (client.checkExists().forPath(path) != null) { logger.debug("Path already exists " + path); } else { client.create().creatingParentsIfNeeded().forPath(path); } } public void createZNodeJsonReply(StratioStreamingMessage request, Object reply) throws Exception { // String path = STREAMING.ZK_BASE_PATH + "/" + request.getOperation().toLowerCase() + "/" // + request.getRequest_id(); // // // if (client.checkExists().forPath(path) != null) { // client.delete().deletingChildrenIfNeeded().forPath(path); // } // // client.create().creatingParentsIfNeeded().forPath(path, new Gson().toJson(reply).getBytes()); String path = STREAMING.ZK_BASE_PATH + "/" + request.getOperation().toLowerCase() + "/" + request.getRequest_id(); createZNodeJsonReplyForPath(request, reply, path); logger.info("**** ZKUTILS " + request.getOperation() + "//" + request.getRequest_id() + "//" + reply + "//" + path); } public String getTempZNodeJsonReplyPath(StratioStreamingMessage request){ return STREAMING.ZK_BASE_PATH + "/" + request.getOperation().toLowerCase() + "/ack" + "/" + request.getRequest_id() ; } public void createTempZNodeJsonReply(StratioStreamingMessage request, Object reply, String groupId) throws Exception { String path = getTempZNodeJsonReplyPath(request) + "/" + groupId; createZNodeJsonReplyForPath(request, reply, path); logger.info("**** ZKUTILS. Temporal ack Node " + request.getOperation() + "//" + request.getRequest_id() + "//" + reply + "//" + path); } private void createZNodeJsonReplyForPath(StratioStreamingMessage request, Object reply, String path) throws Exception { if (client.checkExists().forPath(path) != null) { client.delete().deletingChildrenIfNeeded().forPath(path); } client.create().creatingParentsIfNeeded().forPath(path, new Gson().toJson(reply).getBytes()); } public void createZNode(String path, byte[] data) throws Exception { if (client.checkExists().forPath(path) != null) { client.delete().deletingChildrenIfNeeded().forPath(path); } client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(path, data); } public byte[] getZNode(String path) throws Exception { return client.getData().forPath(path); } public boolean existZNode(String path) throws Exception { Stat stat = client.checkExists().forPath(path); return (stat == null) ? false : true; } private class ZookeeperBackgroundCleaner implements Runnable { private Logger logger = LoggerFactory.getLogger(ZookeeperBackgroundCleaner.class); private CuratorFramework client; private String groupId; private static final long ZNODES_TTL = 600000; // 10 minutes private static final long CLEAN_INTERVAL = 300000; // 5 minutes private static final long MAX_LIVE_FOR_OPERATION_NODE = 60000; // 1 minute /** * */ public ZookeeperBackgroundCleaner(CuratorFramework client) { this.client = client; logger.debug("Starting ZookeeperBackgroundCleaner..."); logger.info("ZookeeperBackgroundCleaner BASE path " + STREAMING.ZK_BASE_PATH); } public ZookeeperBackgroundCleaner(CuratorFramework client, String groupId) { this(client); this.groupId = groupId; } private int removeOldChildZnodes(String path) throws Exception { int counter = 0; Iterator<String> children = client.getChildren().forPath(path).iterator(); while (children.hasNext()) { String childrenPath = children.next(); if (!STREAMING.ZK_HIGH_AVAILABILITY_NODE.equals('/'+childrenPath) && !STREAMING.ZK_PERSISTENCE_NODE.equals('/'+childrenPath)) { if (client.getChildren().forPath(path + "/" + childrenPath).size() > 0) { counter += removeOldChildZnodes(path + "/" + childrenPath); } else { Stat znode = client.checkExists().forPath(path + "/" + childrenPath); Boolean deleteNode = true; // avoid nulls and ephemeral znodes if (znode != null && znode.getEphemeralOwner() == 0) { String parentPath = path.substring(path.lastIndexOf("/") +1); if (STREAM_OPERATIONS.SyncOperations.getAckOperations().contains(parentPath)) { if ( new Date().getTime() - znode.getMtime() < MAX_LIVE_FOR_OPERATION_NODE) { deleteNode = false; } } if (deleteNode) { client.delete().deletingChildrenIfNeeded().forPath(path + "/" + childrenPath); counter++; } } } } } return counter; } /* * (non-Javadoc) * * @see java.lang.Runnable#run() */ @Override public void run() { String zkPath = STREAMING.ZK_BASE_PATH; /* if (groupId != null){ zkPath = zkPath.concat("/").concat(groupId); } */ while (!Thread.currentThread().isInterrupted()) { try { if (client.getState().compareTo(CuratorFrameworkState.STARTED) == 0) { int childsRemoved = removeOldChildZnodes(zkPath); logger.debug(childsRemoved + " old zNodes removed from ZK"); } Thread.sleep(CLEAN_INTERVAL); } catch (InterruptedException ie) { // no need to clean anything, as client is shared logger.info("Shutting down Zookeeper Background Cleaner"); } catch (Exception e) { logger.info("Error on Zookeeper Background Cleaner: " + e.getMessage()); } } } } }