com.jkoolcloud.tnt4j.streams.configure.zookeeper.ZKConfigManager.java Source code

Java tutorial

Introduction

Here is the source code for com.jkoolcloud.tnt4j.streams.configure.zookeeper.ZKConfigManager.java

Source

/*
 * Copyright 2014-2017 JKOOL, LLC.
 *
 * 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.jkoolcloud.tnt4j.streams.configure.zookeeper;

import java.io.IOException;
import java.util.Properties;

import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import com.jkoolcloud.tnt4j.core.OpLevel;
import com.jkoolcloud.tnt4j.sink.DefaultEventSinkFactory;
import com.jkoolcloud.tnt4j.sink.EventSink;
import com.jkoolcloud.tnt4j.streams.StreamsAgent;
import com.jkoolcloud.tnt4j.streams.utils.StreamsResources;
import com.jkoolcloud.tnt4j.streams.utils.Utils;

/**
 * Helper class to manage ZooKeeper stored streams configuration.
 *
 * @version $Revision: 1 $
 */
public class ZKConfigManager implements ZKConfigConstants {
    private static final EventSink LOGGER = DefaultEventSinkFactory.defaultEventSink(StreamsAgent.class);

    // create static instance for ZKConnection class.
    private static ZKConnection conn;

    private static Properties zkConfigProperties;

    /**
     * Creates node in ZK ensemble.
     * 
     * @param zk
     *            ZooKeeper instance
     * @param path
     *            node path
     * @param data
     *            node data to set
     * @param cMode
     *            create mode
     * @throws KeeperException
     *             if the server returns a non-zero error code
     * @throws InterruptedException
     *             if the transaction is interrupted
     */
    public static void create(ZooKeeper zk, String path, byte[] data, CreateMode cMode)
            throws KeeperException, InterruptedException {
        zk.create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, cMode);
    }

    /**
     * Creates node in ZK ensemble.
     * 
     * @param zk
     *            ZooKeeper instance
     * @param path
     *            node path
     * @param data
     *            node data to set
     * @throws KeeperException
     *             if the server returns a non-zero error code
     * @throws InterruptedException
     *             if the transaction is interrupted
     *
     * @see #create(org.apache.zookeeper.ZooKeeper, String, byte[], org.apache.zookeeper.CreateMode)
     */
    public static void create(ZooKeeper zk, String path, byte[] data) throws KeeperException, InterruptedException {
        create(zk, path, data, CreateMode.PERSISTENT);
    }

    /**
     * Updates ZK ensemble node stored data.
     * 
     * @param zk
     *            ZooKeeper instance
     * @param path
     *            node path
     * @param data
     *            node data to set
     * @throws KeeperException
     *             if the server signals an error with a non-zero error code
     * @throws InterruptedException
     *             if the server transaction is interrupted
     */
    public static void update(ZooKeeper zk, String path, byte[] data) throws KeeperException, InterruptedException {
        zk.setData(path, data, nodeExists(zk, path).getVersion());
    }

    /**
     * Checks existence of node and its status, if node is available.
     * 
     * @param zk
     *            ZooKeeper instance
     * @param path
     *            node path
     * @return the stat of the node of the given path, or {@code null} if such node does not exists
     * @throws KeeperException
     *             if the server signals an error
     * @throws InterruptedException
     *             if the server transaction is interrupted
     */
    public static Stat nodeExists(ZooKeeper zk, String path) throws KeeperException, InterruptedException {
        return zk.exists(path, true);
    }

    /**
     * Deletes node from ZK ensemble.
     * 
     * @param zk
     *            ZooKeeper instance
     * @param path
     *            node path
     * @throws KeeperException
     *             if the server signals an error with a non-zero return code
     * @throws InterruptedException
     *             if the server transaction is interrupted
     */
    public static void delete(ZooKeeper zk, String path) throws KeeperException, InterruptedException {
        zk.delete(path, nodeExists(zk, path).getVersion());
    }

    /**
     * Checks whether node exists in ZK ensemble by given path and creates one if it does not. Does same as
     * {@link #ensureNodeExists(org.apache.zookeeper.ZooKeeper, String, byte[])} setting node data to {@code null}.
     *
     * @param zk
     *            ZooKeeper instance
     * @param path
     *            node path
     * @throws KeeperException
     *             if the server signals an error
     * @throws InterruptedException
     *             if the server transaction is interrupted
     *
     * @see #ensureNodeExists(org.apache.zookeeper.ZooKeeper, String, byte[])
     */
    public static void ensureNodeExists(ZooKeeper zk, String path) throws KeeperException, InterruptedException {
        ensureNodeExists(zk, path, null);
    }

    /**
     * Checks whether node exists in ZK ensemble by given path and creates one if it does not.
     *
     * @param zk
     *            ZooKeeper instance
     * @param path
     *            node path
     * @param data
     *            node data to set
     * @throws KeeperException
     *             if the server signals an error
     * @throws InterruptedException
     *             if the server transaction is interrupted
     *
     * @see #nodeExists(org.apache.zookeeper.ZooKeeper, String)
     * @see #createAllNodes(org.apache.zookeeper.ZooKeeper, String, byte[])
     */
    public static void ensureNodeExists(ZooKeeper zk, String path, byte[] data)
            throws KeeperException, InterruptedException {
        Stat nStat = nodeExists(zk, path);

        if (nStat == null) {
            createAllNodes(zk, path, data);
        }
    }

    /**
     * Sets ZK ensemble node data. Checks whether node exists in ZK ensemble by given path. If node exists then updates
     * node data, if does not exist - creates new node with defined data.
     * 
     * @param zk
     *            ZooKeeper instance
     * @param path
     *            node path
     * @param data
     *            node data to set
     * @throws KeeperException
     *             if the server signals an error
     * @throws InterruptedException
     *             if the server transaction is interrupted
     *
     * @see #nodeExists(org.apache.zookeeper.ZooKeeper, String)
     * @see #createAllNodes(org.apache.zookeeper.ZooKeeper, String, byte[])
     * @see #update(org.apache.zookeeper.ZooKeeper, String, byte[])
     */
    public static void setNodeData(ZooKeeper zk, String path, byte[] data)
            throws KeeperException, InterruptedException {
        Stat nStat = nodeExists(zk, path);

        if (nStat == null) {
            createAllNodes(zk, path, data);
        } else {
            update(zk, path, data);
        }
    }

    /**
     * Creates all missing ZK ensemble nodes for provided path and sets/updates provided node data.
     * 
     * @param zk
     *            ZooKeeper instance
     * @param path
     *            node path
     * @param data
     *            node data to set
     * @throws KeeperException
     *             if the server signals an error
     * @throws InterruptedException
     *             if the server transaction is interrupted
     *
     * @see #nodeExists(org.apache.zookeeper.ZooKeeper, String)
     * @see #create(org.apache.zookeeper.ZooKeeper, String, byte[])
     * @see #update(org.apache.zookeeper.ZooKeeper, String, byte[])
     */
    public static void createAllNodes(ZooKeeper zk, String path, byte[] data)
            throws KeeperException, InterruptedException {
        String[] pNodes = path.split(PATH_DELIM);
        Stat nStat;
        String cPath = "";

        for (int i = 0; i < pNodes.length; i++) {
            String node = pNodes[i];

            if (StringUtils.isEmpty(node)) {
                continue;
            }

            cPath += PATH_DELIM + node;

            nStat = nodeExists(zk, cPath);

            if (i == pNodes.length - 1) {
                if (nStat == null) {
                    create(zk, cPath, data);
                } else {
                    update(zk, cPath, data);
                }
            } else {
                if (nStat == null) {
                    create(zk, cPath, null);
                }
            }
        }
    }

    /**
     * Initializes ZK ensemble node data monitoring (over {@link org.apache.zookeeper.Watcher}) and initial data
     * loading.
     * 
     * @param path
     *            node path
     * @param zkCfgChangeListener
     *            zookeeper node data change listener instance
     *
     * @see #handleZKStoredConfiguration(org.apache.zookeeper.ZooKeeper, String, ZKConfigChangeListener)
     */
    public static void handleZKStoredConfiguration(final String path,
            final ZKConfigChangeListener zkCfgChangeListener) throws IOException, InterruptedException {
        handleZKStoredConfiguration(zk(), path, zkCfgChangeListener);
    }

    /**
     * Initializes ZK ensemble node data monitoring (over {@link org.apache.zookeeper.Watcher}) and initial data
     * loading.
     * 
     * @param zk
     *            ZooKeeper instance
     * @param path
     *            node path
     * @param zkCfgChangeListener
     *            zookeeper node data change listener instance
     */
    public static void handleZKStoredConfiguration(final ZooKeeper zk, final String path,
            final ZKConfigChangeListener zkCfgChangeListener) {
        Watcher watch = new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                if (path.equals(watchedEvent.getPath())) {
                    if (watchedEvent.getType() == Event.EventType.NodeDataChanged) {
                        LOGGER.log(OpLevel.DEBUG, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME,
                                "ZKConfigManager.node.changed"), path);
                        zkCfgChangeListener.reconfigure(zk, path, this);
                    } else if (watchedEvent.getType() == Event.EventType.NodeDeleted) {
                        LOGGER.log(OpLevel.DEBUG, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME,
                                "ZKConfigManager.node.deleted"), path);
                        zk.exists(path, this, null, null);
                    } else if (watchedEvent.getType() == Event.EventType.NodeCreated) {
                        LOGGER.log(OpLevel.DEBUG, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME,
                                "ZKConfigManager.node.created"), path);
                        zkCfgChangeListener.reconfigure(zk, path, this);
                    }
                }
            }
        };

        Stat nStat = null;

        try {
            nStat = zk.exists(path, false);
        } catch (Exception exc) {
            LOGGER.log(OpLevel.WARNING, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME,
                    "ZKConfigManager.node.exists.failed"), path, exc);
        }

        if (nStat == null) {
            LOGGER.log(OpLevel.DEBUG, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME,
                    "ZKConfigManager.node.create.wait"), path);
            zk.exists(path, watch, null, null);
        } else {
            zkCfgChangeListener.reconfigure(zk, path, watch);
        }
    }

    /**
     * Reads streams ZooKeeper configuration file and loads contents into {@link java.util.Properties} object.
     *
     * @param zookeeperCfgFile
     *            streams zookeeper configuration file path string
     * @return properties loaded from configuration file
     */
    public static Properties readStreamsZKConfig(String zookeeperCfgFile) {
        if (StringUtils.isNotEmpty(zookeeperCfgFile)) {
            LOGGER.log(OpLevel.INFO, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME,
                    "ZKConfigManager.loading.cfg.file"), zookeeperCfgFile);

            try {
                zkConfigProperties = Utils.loadPropertiesFile(zookeeperCfgFile);
                LOGGER.log(OpLevel.DEBUG, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME,
                        "ZKConfigManager.loaded.props"), zkConfigProperties.size());
            } catch (Exception exc) {
                LOGGER.log(OpLevel.ERROR, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME,
                        "ZKConfigManager.props.load.error"), zookeeperCfgFile);
                zkConfigProperties = null;
            }
        } else {
            if (zkConfigProperties != null) {
                LOGGER.log(OpLevel.INFO, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME,
                        "ZKConfigManager.resetting.cfg"));
                zkConfigProperties.clear();
            }
        }

        return zkConfigProperties;
    }

    /**
     * Gets streams ZooKeeper configuration property value.
     *
     * @param propName
     *            streams ZooKeeper configuration property name
     * @return streams ZooKeeper configuration property contained value
     */
    public static String getZKCfgProperty(String propName) {
        return zkConfigProperties == null ? null : zkConfigProperties.getProperty(propName);
    }

    /**
     * Gets streams ZooKeeper configuration property value.
     *
     * @param propName
     *            streams ZooKeeper configuration property name
     * @param defValue
     *            a default value
     * @return streams ZooKeeper configuration property contained value, or {@code defValue} if no property found by
     *         name
     */
    public static String getZKCfgProperty(String propName, String defValue) {
        return zkConfigProperties == null ? defValue : zkConfigProperties.getProperty(propName, defValue);
    }

    /**
     * Opens ZK ensemble connection using configuration properties defined values.
     *
     * @param zkConfProps
     *            streams ZooKeeper configuration properties
     * @return instance of opened ZK connection
     * @throws IOException
     *             if I/O exception occurs while initializing ZooKeeper connection
     * @throws InterruptedException
     *             if the current thread is interrupted while waiting
     */
    public static ZKConnection openConnection(Properties zkConfProps) throws IOException, InterruptedException {
        LOGGER.log(OpLevel.DEBUG,
                StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ZKConfigManager.connecting"));

        if (MapUtils.isEmpty(zkConfProps)) {
            LOGGER.log(OpLevel.DEBUG, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME,
                    "ZKConfigManager.empty.cfg.props"));
        }

        conn = new ZKConnection();

        String zkConnStr = zkConfProps.getProperty(PROP_ZK_CONN, DEFAULT_CONN_HOST);
        int timeout = Integer
                .parseInt(zkConfProps.getProperty(PROP_ZK_CONN_TIMEOUT, String.valueOf(DEFAULT_CONN_TIMEOUT)));

        conn.connect(zkConnStr, timeout); // NON-NLS

        LOGGER.log(OpLevel.DEBUG,
                StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ZKConfigManager.connected"),
                zkConnStr);

        return conn;
    }

    /**
     * Returns {@link org.apache.zookeeper.ZooKeeper} instance connection was established to. If no connection is
     * established, then opens new connection using last read streams ZK configuration properties.
     *
     * @return zookeeper instance
     * @throws IOException
     *             if I/O exception occurs while initializing ZooKeeper connection
     * @throws InterruptedException
     *             if the current thread is interrupted while waiting
     */
    public static ZooKeeper zk() throws IOException, InterruptedException {
        if (conn == null || !conn.isConnected()) {
            openConnection(zkConfigProperties);
        }

        return conn.zk();
    }

    /**
     * Sets up ZooKeeper node data for defined configuration entity. Node path is referenced by property named
     * {@code cfgId} + {@value com.jkoolcloud.tnt4j.streams.configure.zookeeper.ZKConfigConstants#CFG_SUFFIX_ZK_PATH}.
     * <p>
     * If node exists and node data is not {@code null} - does nothing.
     * <p>
     * If node does not exist - creates all missing ZK path nodes and sets node data from configuration file referenced
     * by property named {@code cfgId} +
     * {@value com.jkoolcloud.tnt4j.streams.configure.zookeeper.ZKConfigConstants#CFG_SUFFIX_CFG_FILE}.
     * <p>
     * If node exists, but node data is null or empty - updates node data from configuration file referenced by property
     * named {@code cfgId} +
     * {@value com.jkoolcloud.tnt4j.streams.configure.zookeeper.ZKConfigConstants#CFG_SUFFIX_CFG_FILE}.
     *
     * @param zkConfProps
     *            streams ZooKeeper configuration properties
     * @param cfgId
     *            configuration entity identifier
     * @throws IOException
     *             if I/O exception occurs while initializing ZooKeeper connection
     * @throws InterruptedException
     *             if the current thread is interrupted while waiting
     * @throws KeeperException
     *             if the server signals an error
     *
     * @see #nodeExists(org.apache.zookeeper.ZooKeeper, String)
     * @see #createAllNodes(org.apache.zookeeper.ZooKeeper, String, byte[])
     * @see #update(org.apache.zookeeper.ZooKeeper, String, byte[])
     * @see #makeCfgFilePathProperty(String)
     * @see #makeZKNodePathProperty(String)
     */
    public static void setupZKNodeData(Properties zkConfProps, String cfgId)
            throws IOException, InterruptedException, KeeperException {
        String zkPath = getZKNodePath(zkConfProps, cfgId);
        LOGGER.log(OpLevel.DEBUG, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME,
                "ZKConfigManager.cfg.ent.setup.zk.path"), cfgId, zkPath);
        if (StringUtils.isEmpty(zkPath)) {
            LOGGER.log(OpLevel.INFO, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME,
                    "ZKConfigManager.cfg.ent.setup.zk.path.empty"), cfgId);

            return;
        }

        Stat nStat = nodeExists(zk(), zkPath);

        if (nStat == null || nStat.getDataLength() <= 0) {
            String cfgFile = getCfgFilePath(zkConfProps, cfgId);
            LOGGER.log(OpLevel.DEBUG, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME,
                    "ZKConfigManager.cfg.ent.setup.file.path"), cfgId, cfgFile);
            byte[] cfgData = ZKConfigInit.loadDataFromFile(cfgFile);

            if (nStat == null) {
                LOGGER.log(OpLevel.DEBUG, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME,
                        "ZKConfigManager.cfg.ent.setup.setting"), cfgId, cfgFile);
                createAllNodes(zk(), zkPath, cfgData);
            } else {
                LOGGER.log(OpLevel.DEBUG, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME,
                        "ZKConfigManager.cfg.ent.setup.updating"), cfgId, cfgFile);
                if (cfgData != null) {
                    update(zk(), zkPath, cfgData);
                }
            }
        } else {
            LOGGER.log(OpLevel.DEBUG, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME,
                    "ZKConfigManager.cfg.ent.setup.ok"), cfgId);
        }
    }

    /**
     * Makes configuration entity ZK node path property name: {@code cfgId} +
     * {@value com.jkoolcloud.tnt4j.streams.configure.zookeeper.ZKConfigConstants#CFG_SUFFIX_ZK_PATH}.
     *
     * @param cfgId
     *            configuration entity identifier
     * @return configuration entity ZK node path property name
     */
    public static String makeZKNodePathProperty(String cfgId) {
        return cfgId + CFG_SUFFIX_ZK_PATH;
    }

    /**
     * Makes configuration entity configuration file path property name: {@code cfgId} +
     * {@value com.jkoolcloud.tnt4j.streams.configure.zookeeper.ZKConfigConstants#CFG_SUFFIX_CFG_FILE}.
     *
     * @param cfgId
     *            configuration entity identifier
     * @return configuration entity configuration file path property name
     */
    public static String makeCfgFilePathProperty(String cfgId) {
        return cfgId + CFG_SUFFIX_CFG_FILE;
    }

    /**
     * Finds and returns configuration entity ZK node path defined in streams ZooKeeper configuration properties.
     *
     * @param zkConfProps
     *            streams ZooKeeper configuration properties
     * @param cfgId
     *            configuration entity identifier
     * @return configuration entity ZK node path
     */
    public static String getZKNodePath(Properties zkConfProps, String cfgId) {
        return zkConfProps == null ? null : zkConfProps.getProperty(makeZKNodePathProperty(cfgId));
    }

    /**
     * Finds and returns configuration entity configuration file path defined in streams ZooKeeper configuration
     * properties.
     *
     * @param zkConfProps
     *            streams ZooKeeper configuration properties
     * @param cfgId
     *            configuration entity identifier
     * @return configuration entity configuration file path
     */
    public static String getCfgFilePath(Properties zkConfProps, String cfgId) {
        return zkConfProps == null ? null : zkConfProps.getProperty(makeCfgFilePathProperty(cfgId));
    }

    /**
     * Closes ZK ensemble connection.
     */
    public static void close() {
        Utils.close(conn);
    }

    /**
     * Abstract ZooKeeper stored configuration change listener.
     */
    public abstract static class ZKConfigChangeListener {
        /**
         * Invoked when ZK node kept configuration changes or node under defined path gets created.
         *
         * @param zk
         *            ZooKeeper instance
         * @param path
         *            node path
         * @param watcher
         *            node watcher instance
         */
        public void reconfigure(ZooKeeper zk, String path, Watcher watcher) {
            LOGGER.log(OpLevel.INFO, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME,
                    "ZKConfigManager.loading.node.data"), path);

            try {
                byte[] data = zk.getData(path, watcher, null);

                applyConfigurationData(data);
            } catch (Exception exc) {
                LOGGER.log(OpLevel.WARNING, StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME,
                        "ZKConfigManager.cfg.loading.failed"), exc);
            }
        }

        /**
         * Defines ZK node retrieved configuration data processing.
         *
         * @param data
         *            data retrieved from ZK ensemble node
         */
        public abstract void applyConfigurationData(byte[] data);
    }
}