org.wso2.carbon.cluster.coordinator.rdbms.RDBMSCommunicationBusContextImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.cluster.coordinator.rdbms.RDBMSCommunicationBusContextImpl.java

Source

/*
 * Copyright (c) 2017, WSO2 Inc. (http://wso2.com) All Rights Reserved.
 * 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 org.wso2.carbon.cluster.coordinator.rdbms;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.cluster.coordinator.commons.exception.ClusterCoordinationException;
import org.wso2.carbon.cluster.coordinator.commons.node.NodeDetail;
import org.wso2.carbon.cluster.coordinator.commons.util.CommunicationBusContext;
import org.wso2.carbon.cluster.coordinator.commons.util.MemberEvent;
import org.wso2.carbon.cluster.coordinator.commons.util.MemberEventType;
import org.wso2.carbon.cluster.coordinator.rdbms.internal.ds.RDBMSClusterCoordinatorServiceHolder;
import org.wso2.carbon.cluster.coordinator.rdbms.util.RDBMSConstants;
import org.wso2.carbon.datasource.core.api.DataSourceService;
import org.wso2.carbon.datasource.core.beans.CarbonDataSource;
import org.wso2.carbon.datasource.core.exception.DataSourceException;

import javax.sql.DataSource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * The RDBMS based communication bus layer for the nodes. This layer handles the database level calls.
 */
public class RDBMSCommunicationBusContextImpl implements CommunicationBusContext {
    private static final Log log = LogFactory.getLog(RDBMSCommunicationBusContextImpl.class);
    /**
     * The logger class
     */
    private Log logger = LogFactory.getLog(RDBMSCommunicationBusContextImpl.class);
    /**
     * The datasource which is used to be connected to the database.
     */
    private DataSource datasource;

    public RDBMSCommunicationBusContextImpl() {

        DataSourceService dataSourceService = RDBMSClusterCoordinatorServiceHolder.getDataSourceService();
        CarbonDataSource carbonDataSource = null;
        try {
            Object datasource = dataSourceService.getDataSource("datasourceName");
            if (datasource instanceof CarbonDataSource) {
                carbonDataSource = (CarbonDataSource) dataSourceService.getDataSource("datasourceName");
                this.datasource = (DataSource) carbonDataSource.getDataSourceObject();
            }
        } catch (DataSourceException e) {
            throw new ClusterCoordinationException("Error in initializing the datasource", e);
        }
        createTables();
    }

    public RDBMSCommunicationBusContextImpl(DataSource dataSource) {
        this.datasource = dataSource;
        createTables();
    }

    /**
     * Create the tables needed for RDBMS communication.
     */
    private void createTables() {
        createLeaderStatusTable();
        createClusterNodeStatusTable();
        createMembershipEventTable();
        createRemovedMembersTable();
    }

    /**
     * Create Leader Status Table.
     */
    private void createLeaderStatusTable() {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
            connection = getConnection();
            preparedStatement = connection.prepareStatement(RDBMSConstants.CREATE_LEADER_STATUS_TABLE);
            preparedStatement.execute();
            connection.commit();
        } catch (SQLException e) {
            throw new ClusterCoordinationException("Error in excecuting query.", e);
        } finally {
            close(preparedStatement, "Execute query");
            close(connection, "Execute query");
        }
    }

    /**
     * Create Cluster Node Status table.
     */
    private void createClusterNodeStatusTable() {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
            connection = getConnection();
            preparedStatement = connection.prepareStatement(RDBMSConstants.CREATE_CLUSTER_NODE_STATUS_TABLE);
            preparedStatement.execute();
            connection.commit();
        } catch (SQLException e) {
            throw new ClusterCoordinationException("Error in excecuting query.", e);
        } finally {
            close(preparedStatement, "Execute query");
            close(connection, "Execute query");
        }
    }

    /**
     * Create Membership Event Table.
     */
    private void createMembershipEventTable() {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
            connection = getConnection();
            preparedStatement = connection.prepareStatement(RDBMSConstants.CREATE_MEMBERSHIP_EVENT_TABLE);
            preparedStatement.execute();
            connection.commit();
        } catch (SQLException e) {
            throw new ClusterCoordinationException("Error in excecuting query.", e);
        } finally {
            close(preparedStatement, "Execute query");
            close(connection, "Execute query");
        }
    }

    /**
     * Create Removed Members Table.
     */
    private void createRemovedMembersTable() {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
            connection = getConnection();
            preparedStatement = connection.prepareStatement(RDBMSConstants.CREATE_REMOVED_MEMBERS_TABLE);
            preparedStatement.execute();
            connection.commit();
        } catch (SQLException e) {
            throw new ClusterCoordinationException("Error in excecuting query.", e);
        } finally {
            close(preparedStatement, "Execute query");
            close(connection, "Execute query");
        }
    }

    @Override
    public void storeMembershipEvent(String changedMember, String groupId, List<String> clusterNodes,
            int membershipEventType) throws ClusterCoordinationException {
        Connection connection = null;
        PreparedStatement storeMembershipEventPreparedStatement = null;
        String task = "Storing membership event: " + membershipEventType + " for member: " + changedMember
                + " in group " + groupId;
        try {
            connection = getConnection();
            storeMembershipEventPreparedStatement = connection
                    .prepareStatement(RDBMSConstants.PS_INSERT_MEMBERSHIP_EVENT);
            for (String clusterNode : clusterNodes) {
                storeMembershipEventPreparedStatement.setString(1, clusterNode);
                storeMembershipEventPreparedStatement.setString(2, groupId);
                storeMembershipEventPreparedStatement.setInt(3, membershipEventType);
                storeMembershipEventPreparedStatement.setString(4, changedMember);
                storeMembershipEventPreparedStatement.addBatch();
            }
            storeMembershipEventPreparedStatement.executeBatch();
            connection.commit();
        } catch (SQLException e) {
            rollback(connection, task);
            throw new ClusterCoordinationException("Error storing membership change: " + membershipEventType
                    + " for member: " + changedMember + " in group " + groupId, e);
        } finally {
            close(storeMembershipEventPreparedStatement, task);
            close(connection, task);
        }
    }

    @Override
    public String getCoordinatorNodeId(String groupId) throws ClusterCoordinationException {
        {
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            ResultSet resultSet = null;
            try {
                connection = getConnection();
                preparedStatement = connection.prepareStatement(RDBMSConstants.PS_GET_COORDINATOR_NODE_ID);
                preparedStatement.setString(1, groupId);
                resultSet = preparedStatement.executeQuery();
                String coordinatorNodeId;
                if (resultSet.next()) {
                    coordinatorNodeId = resultSet.getString(1);
                    if (logger.isDebugEnabled()) {
                        logger.debug("Coordinator node ID: " + coordinatorNodeId + " for group :" + groupId);
                    }
                } else {
                    if (logger.isDebugEnabled()) {
                        logger.debug("No coordinator present in database for group " + groupId);
                    }
                    coordinatorNodeId = null;
                }
                return coordinatorNodeId;
            } catch (SQLException e) {
                String errMsg = RDBMSConstants.TASK_GET_COORDINATOR_INFORMATION;
                throw new ClusterCoordinationException("Error occurred while " + errMsg, e);
            } finally {
                close(resultSet, RDBMSConstants.TASK_GET_COORDINATOR_INFORMATION);
                close(preparedStatement, RDBMSConstants.TASK_GET_COORDINATOR_INFORMATION);
                close(connection, RDBMSConstants.TASK_GET_COORDINATOR_INFORMATION);
            }
        }
    }

    /**
     * Get the connection to the database.
     */
    private Connection getConnection() throws SQLException {
        return datasource.getConnection();
    }

    /**
     * close the connection.
     *
     * @param connection The connection to be closed
     * @param task       The task which was running
     */
    private void close(Connection connection, String task) {
        try {
            if (connection != null && !connection.isClosed()) {
                connection.close();
            }
        } catch (SQLException e) {
            logger.error("Failed to close connection after " + task, e);
        }
    }

    /**
     * Close the prepared statement.
     *
     * @param preparedStatement The statement to be closed
     * @param task              The task which was running
     */
    private void close(PreparedStatement preparedStatement, String task) {
        if (preparedStatement != null) {
            try {
                preparedStatement.close();
            } catch (SQLException e) {
                logger.error("Closing prepared statement failed after " + task, e);
            }
        }
    }

    /**
     * The rollback method.
     *
     * @param connection The connection object which the rollback should be applied to
     * @param task       The task which was running
     */
    private void rollback(Connection connection, String task) {
        if (connection != null) {
            try {
                connection.rollback();
            } catch (SQLException e) {
                logger.warn("Rollback failed on " + task, e);
            }
        }
    }

    @Override
    public List<MemberEvent> readMemberShipEvents(String nodeID) throws ClusterCoordinationException {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        PreparedStatement clearMembershipEvents = null;
        ResultSet resultSet = null;
        List<MemberEvent> membershipEvents = new ArrayList<MemberEvent>();
        String task = "retrieving membership events destined to: " + nodeID;
        try {
            connection = getConnection();
            preparedStatement = connection.prepareStatement(RDBMSConstants.PS_SELECT_MEMBERSHIP_EVENT);
            preparedStatement.setString(1, nodeID);
            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()) {
                MemberEvent membershipEvent = new MemberEvent(
                        MemberEventType.getTypeFromInt(resultSet.getInt(RDBMSConstants.MEMBERSHIP_CHANGE_TYPE)),
                        resultSet.getString(RDBMSConstants.MEMBERSHIP_CHANGED_MEMBER_ID),
                        resultSet.getString(RDBMSConstants.GROUP_ID));
                membershipEvents.add(membershipEvent);
            }
            clearMembershipEvents = connection.prepareStatement(RDBMSConstants.PS_CLEAN_MEMBERSHIP_EVENTS_FOR_NODE);
            clearMembershipEvents.setString(1, nodeID);
            clearMembershipEvents.executeUpdate();
            connection.commit();
            return membershipEvents;
        } catch (SQLException e) {
            throw new ClusterCoordinationException("Error occurred while " + task, e);
        } finally {
            close(resultSet, task);
            close(preparedStatement, task);
            close(clearMembershipEvents, task);
            close(connection, task);
        }
    }

    /**
     * Close the resultset.
     *
     * @param resultSet The resultset which should be closed
     * @param task      The task which was running
     */
    private void close(ResultSet resultSet, String task) {
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                logger.error("Closing result set failed after " + task, e);
            }
        }
    }

    /**
     * Clear all the membership events.
     *
     * @throws ClusterCoordinationException
     */
    public void clearMembershipEvents() throws ClusterCoordinationException {
        Connection connection = null;
        PreparedStatement clearMembershipEvents = null;
        String task = "Clearing all membership events";
        try {
            connection = getConnection();
            clearMembershipEvents = connection.prepareStatement(RDBMSConstants.PS_CLEAR_ALL_MEMBERSHIP_EVENTS);
            clearMembershipEvents.executeUpdate();
            connection.commit();
        } catch (SQLException e) {
            rollback(connection, task);
            throw new ClusterCoordinationException("Error occurred while " + task, e);
        } finally {
            close(clearMembershipEvents, task);
            close(connection, task);
        }
    }

    @Override
    public boolean createCoordinatorEntry(String nodeId, String groupId) throws ClusterCoordinationException {
        Connection connection = null;
        PreparedStatement preparedStatement = null;

        try {
            connection = getConnection();
            preparedStatement = connection.prepareStatement(RDBMSConstants.PS_INSERT_COORDINATOR_ROW);
            preparedStatement.setString(1, groupId);
            preparedStatement.setString(2, nodeId);
            preparedStatement.setLong(3, System.currentTimeMillis());
            int updateCount = preparedStatement.executeUpdate();
            connection.commit();
            return updateCount != 0;
        } catch (SQLException e) {
            rollback(connection, RDBMSConstants.TASK_ADD_MESSAGE_ID);
            return false;
        } finally {
            close(preparedStatement, RDBMSConstants.TASK_ADD_MESSAGE_ID);
            close(connection, RDBMSConstants.TASK_ADD_MESSAGE_ID);
            //  return false;
        }
    }

    @Override
    public boolean checkIsCoordinator(String nodeId, String groupId) throws ClusterCoordinationException {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            connection = getConnection();
            preparedStatement = connection.prepareStatement(RDBMSConstants.PS_GET_COORDINATOR_ROW_FOR_NODE_ID);
            preparedStatement.setString(1, nodeId);
            preparedStatement.setString(2, groupId);
            resultSet = preparedStatement.executeQuery();
            boolean isCoordinator;
            isCoordinator = resultSet.next();
            return isCoordinator;
        } catch (SQLException e) {
            String errMsg = RDBMSConstants.TASK_CHECK_COORDINATOR_VALIDITY + " instance id: " + nodeId
                    + " group ID: " + groupId;
            throw new ClusterCoordinationException("Error occurred while " + errMsg, e);
        } finally {
            close(resultSet, RDBMSConstants.TASK_CHECK_COORDINATOR_VALIDITY);
            close(preparedStatement, RDBMSConstants.TASK_CHECK_COORDINATOR_VALIDITY);
            close(connection, RDBMSConstants.TASK_CHECK_COORDINATOR_VALIDITY);
        }
    }

    @Override
    public boolean updateCoordinatorHeartbeat(String nodeId, String groupId) throws ClusterCoordinationException {
        Connection connection = null;
        PreparedStatement preparedStatementForCoordinatorUpdate = null;
        try {
            connection = getConnection();
            preparedStatementForCoordinatorUpdate = connection
                    .prepareStatement(RDBMSConstants.PS_UPDATE_COORDINATOR_HEARTBEAT);
            preparedStatementForCoordinatorUpdate.setLong(1, System.currentTimeMillis());
            preparedStatementForCoordinatorUpdate.setString(2, nodeId);
            preparedStatementForCoordinatorUpdate.setString(3, groupId);
            int updateCount = preparedStatementForCoordinatorUpdate.executeUpdate();
            connection.commit();
            return updateCount != 0;
        } catch (SQLException e) {
            rollback(connection, RDBMSConstants.TASK_UPDATE_COORDINATOR_HEARTBEAT);
            throw new ClusterCoordinationException(
                    "Error occurred while " + RDBMSConstants.TASK_UPDATE_COORDINATOR_HEARTBEAT + ". instance ID: "
                            + nodeId + " group ID" + groupId,
                    e);
        } finally {
            close(preparedStatementForCoordinatorUpdate, RDBMSConstants.TASK_UPDATE_COORDINATOR_HEARTBEAT);
            close(connection, RDBMSConstants.TASK_UPDATE_COORDINATOR_HEARTBEAT);
        }
    }

    @Override
    public boolean checkIfCoordinatorValid(String groupId, int age) throws ClusterCoordinationException {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            connection = getConnection();
            preparedStatement = connection.prepareStatement(RDBMSConstants.PS_GET_COORDINATOR_HEARTBEAT);
            preparedStatement.setString(1, groupId);
            resultSet = preparedStatement.executeQuery();
            long currentTimeMillis = System.currentTimeMillis();
            boolean isCoordinator;
            if (resultSet.next()) {
                long coordinatorHeartbeat = resultSet.getLong(1);
                long heartbeatAge = currentTimeMillis - coordinatorHeartbeat;
                isCoordinator = heartbeatAge <= age;
                if (logger.isDebugEnabled()) {
                    logger.debug("isCoordinator: " + isCoordinator + ", heartbeatAge: " + age
                            + ", coordinatorHeartBeat: " + coordinatorHeartbeat + ", currentTime: "
                            + currentTimeMillis);
                }
            } else {
                if (logger.isDebugEnabled()) {
                    logger.debug("No coordinator present in database for group " + groupId);
                }
                isCoordinator = false;
            }
            return isCoordinator;
        } catch (SQLException e) {
            String errMsg = RDBMSConstants.TASK_GET_COORDINATOR_INFORMATION;
            throw new ClusterCoordinationException("Error occurred while " + errMsg, e);
        } finally {
            close(resultSet, RDBMSConstants.TASK_GET_COORDINATOR_INFORMATION);
            close(preparedStatement, RDBMSConstants.TASK_GET_COORDINATOR_INFORMATION);
            close(connection, RDBMSConstants.TASK_GET_COORDINATOR_INFORMATION);
        }
    }

    @Override
    public void removeCoordinator(String groupId, int age) throws ClusterCoordinationException {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
            connection = getConnection();
            long currentTimeMillis = System.currentTimeMillis();
            long thresholdTimeLimit = currentTimeMillis - age;
            preparedStatement = connection.prepareStatement(RDBMSConstants.PS_DELETE_COORDINATOR);
            preparedStatement.setString(1, groupId);
            preparedStatement.setLong(2, thresholdTimeLimit);
            preparedStatement.executeUpdate();
            connection.commit();
        } catch (SQLException e) {
            rollback(connection, RDBMSConstants.TASK_REMOVE_COORDINATOR);
            throw new ClusterCoordinationException("error occurred while " + RDBMSConstants.TASK_REMOVE_COORDINATOR,
                    e);
        } finally {
            close(preparedStatement, RDBMSConstants.TASK_REMOVE_COORDINATOR);
            close(connection, RDBMSConstants.TASK_REMOVE_COORDINATOR);
        }
    }

    @Override
    public boolean updateNodeHeartbeat(String nodeId, String groupId) throws ClusterCoordinationException {
        Connection connection = null;
        PreparedStatement preparedStatementForNodeUpdate = null;

        try {
            connection = getConnection();
            preparedStatementForNodeUpdate = connection.prepareStatement(RDBMSConstants.PS_UPDATE_NODE_HEARTBEAT);
            preparedStatementForNodeUpdate.setLong(1, System.currentTimeMillis());
            preparedStatementForNodeUpdate.setString(2, nodeId);
            preparedStatementForNodeUpdate.setString(3, groupId);
            int updateCount = preparedStatementForNodeUpdate.executeUpdate();
            connection.commit();
            return updateCount != 0;
        } catch (SQLException e) {
            rollback(connection, RDBMSConstants.TASK_UPDATE_NODE_HEARTBEAT);
            throw new ClusterCoordinationException(
                    "Error occurred while " + RDBMSConstants.TASK_UPDATE_NODE_HEARTBEAT + ". Node ID: " + nodeId
                            + "and Group ID : " + groupId,
                    e);
        } finally {
            close(preparedStatementForNodeUpdate, RDBMSConstants.TASK_UPDATE_NODE_HEARTBEAT);
            close(connection, RDBMSConstants.TASK_UPDATE_NODE_HEARTBEAT);
        }
    }

    @Override
    public void createNodeHeartbeatEntry(String nodeId, String groupId, Map propertiesMap)
            throws ClusterCoordinationException {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
            connection = getConnection();
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(propertiesMap);
            byte[] propertiesMapAsBytes = byteArrayOutputStream.toByteArray();
            preparedStatement = connection.prepareStatement(RDBMSConstants.PS_INSERT_NODE_HEARTBEAT_ROW);
            preparedStatement.setString(1, nodeId);
            preparedStatement.setLong(2, System.currentTimeMillis());
            preparedStatement.setString(3, groupId);
            preparedStatement.setBinaryStream(4, new ByteArrayInputStream(propertiesMapAsBytes));
            preparedStatement.executeUpdate();
            connection.commit();
        } catch (SQLException e) {
            rollback(connection, RDBMSConstants.TASK_CREATE_NODE_HEARTBEAT);
            throw new ClusterCoordinationException("Error occurred while "
                    + RDBMSConstants.TASK_CREATE_NODE_HEARTBEAT + ". Node ID: " + nodeId + " group ID" + groupId,
                    e);
        } catch (IOException e) {
            throw new ClusterCoordinationException(e);
        } finally {
            close(preparedStatement, RDBMSConstants.TASK_UPDATE_COORDINATOR_HEARTBEAT);
            close(connection, RDBMSConstants.TASK_CREATE_NODE_HEARTBEAT);
        }
    }

    @Override
    public List<NodeDetail> getAllNodeData(String groupId) throws ClusterCoordinationException {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        String coordinatorNodeId = getCoordinatorNodeId(groupId);
        ArrayList<NodeDetail> nodeDataList = new ArrayList<NodeDetail>();
        try {
            connection = getConnection();
            preparedStatement = connection.prepareStatement(RDBMSConstants.PS_GET_ALL_NODE_HEARTBEAT);
            preparedStatement.setString(1, groupId);
            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()) {
                String nodeId = resultSet.getString(2);
                boolean isCoordinatorNode = false;
                if (coordinatorNodeId != null) {
                    isCoordinatorNode = coordinatorNodeId.equals(nodeId);
                }
                Map<String, Object> propertiesMap = null;
                if (resultSet.getBlob(3) != null) {
                    int blobLength = (int) resultSet.getBlob(3).length();
                    byte[] bytes = resultSet.getBlob(3).getBytes(0L, blobLength);
                    ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
                    ObjectInputStream ois = new ObjectInputStream(bis);
                    Object blobObject = ois.readObject();
                    if (blobObject instanceof Map) {
                        propertiesMap = (Map) blobObject;
                    }
                }
                long lastHeartbeat = resultSet.getLong(4);
                boolean isNewNode = convertIntToBoolean(resultSet.getInt(5));
                NodeDetail heartBeatData = new NodeDetail(nodeId, groupId, isCoordinatorNode, lastHeartbeat,
                        isNewNode, propertiesMap);
                nodeDataList.add(heartBeatData);
            }

        } catch (SQLException e) {
            String errMsg = RDBMSConstants.TASK_GET_ALL_QUEUES;
            throw new ClusterCoordinationException("Error occurred while " + errMsg, e);
        } catch (IOException e) {
            throw new ClusterCoordinationException("Error retrieving the property map. ", e);
        } catch (ClassNotFoundException e) {
            throw new ClusterCoordinationException("Error retrieving the property map. ", e);
        } finally {
            close(resultSet, RDBMSConstants.TASK_GET_ALL_QUEUES);
            close(preparedStatement, RDBMSConstants.TASK_GET_ALL_QUEUES);
            close(connection, RDBMSConstants.TASK_GET_ALL_QUEUES);
        }
        return nodeDataList;
    }

    @Override
    public NodeDetail getRemovedNodeData(String nodeId, String groupId, String removedMemberId)
            throws ClusterCoordinationException {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        PreparedStatement clearMembershipEvents = null;
        ResultSet resultSet = null;
        NodeDetail nodeDetail = null;
        try {
            connection = getConnection();
            preparedStatement = connection.prepareStatement(RDBMSConstants.PS_SELECT_REMOVED_MEMBER_DETAILS);
            preparedStatement.setString(1, nodeId);
            preparedStatement.setString(2, removedMemberId);
            preparedStatement.setString(3, groupId);
            resultSet = preparedStatement.executeQuery();
            Map<String, Object> propertiesMap = null;

            if (resultSet.next()) {
                Blob blob = resultSet.getBlob(2);
                if (blob != null) {
                    int blobLength = (int) blob.length();
                    byte[] bytes = blob.getBytes(1, blobLength);
                    ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
                    ObjectInputStream ois = new ObjectInputStream(bis);
                    Object blobObject = ois.readObject();
                    if (blobObject instanceof Map) {
                        propertiesMap = (Map) blobObject;
                    }
                }
                nodeDetail = new NodeDetail(removedMemberId, groupId, false, 0, false, propertiesMap);
            }
            clearMembershipEvents = connection
                    .prepareStatement(RDBMSConstants.PS_DELETE_REMOVED_MEMBER_DETAIL_FOR_NODE);
            clearMembershipEvents.setString(1, nodeId);
            clearMembershipEvents.setString(2, removedMemberId);
            clearMembershipEvents.setString(3, groupId);
            clearMembershipEvents.executeUpdate();
            connection.commit();
        } catch (SQLException e) {
            String errMsg = RDBMSConstants.TASK_GET_ALL_QUEUES;
            throw new ClusterCoordinationException("Error occurred while " + errMsg, e);
        } catch (ClassNotFoundException e) {
            throw new ClusterCoordinationException("Error retrieving the removed node data. ", e);
        } catch (IOException e) {
            throw new ClusterCoordinationException("Error retrieving the removed node data. ", e);
        } finally {
            close(resultSet, RDBMSConstants.TASK_GET_ALL_QUEUES);
            close(preparedStatement, RDBMSConstants.TASK_GET_ALL_QUEUES);
            close(clearMembershipEvents, RDBMSConstants.TASK_GET_ALL_QUEUES);
            close(connection, RDBMSConstants.TASK_GET_ALL_QUEUES);
        }
        return nodeDetail;
    }

    @Override
    public NodeDetail getNodeData(String nodeId, String groupId) throws ClusterCoordinationException {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        String coordinatorNodeId = getCoordinatorNodeId(groupId);
        NodeDetail nodeDetail = null;
        try {
            connection = getConnection();
            preparedStatement = connection.prepareStatement(RDBMSConstants.PS_GET_NODE_DATA);
            preparedStatement.setString(1, groupId);
            preparedStatement.setString(2, nodeId);
            resultSet = preparedStatement.executeQuery();
            Map<String, Object> propertiesMap = null;
            if (resultSet.next()) {
                boolean isCoordinatorNode = coordinatorNodeId.equals(nodeId);
                if (resultSet.getBlob(3) != null) {
                    int blobLength = (int) resultSet.getBlob(3).length();
                    byte[] bytes = resultSet.getBlob(3).getBytes(0L, blobLength);
                    ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
                    ObjectInputStream ois = new ObjectInputStream(bis);
                    Object blobObject = ois.readObject();
                    if (blobObject instanceof Map) {
                        propertiesMap = (Map) blobObject;
                    }
                }
                long lastHeartbeat = resultSet.getLong(4);
                boolean isNewNode = convertIntToBoolean(resultSet.getInt(5));
                nodeDetail = new NodeDetail(nodeId, groupId, isCoordinatorNode, lastHeartbeat, isNewNode,
                        propertiesMap);
            }

        } catch (SQLException e) {
            String errMsg = RDBMSConstants.TASK_GET_ALL_QUEUES;
            throw new ClusterCoordinationException("Error occurred while " + errMsg, e);
        } catch (IOException e) {
            throw new ClusterCoordinationException("Error retrieving the node data", e);
        } catch (ClassNotFoundException e) {
            throw new ClusterCoordinationException("Error retrieving the node data", e);
        } finally {
            close(resultSet, RDBMSConstants.TASK_GET_ALL_QUEUES);
            close(preparedStatement, RDBMSConstants.TASK_GET_ALL_QUEUES);
            close(connection, RDBMSConstants.TASK_GET_ALL_QUEUES);
        }
        return nodeDetail;
    }

    /**
     * Convert a value to boolean.
     *
     * @param value the value to be converted to boolean
     * @return the converted boolean
     */
    private boolean convertIntToBoolean(int value) {
        return value != 0;
    }

    @Override
    public void removeNode(String nodeId, String groupId) throws ClusterCoordinationException {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
            connection = getConnection();
            preparedStatement = connection.prepareStatement(RDBMSConstants.PS_DELETE_NODE_HEARTBEAT);
            preparedStatement.setString(1, nodeId);
            preparedStatement.setString(2, groupId);
            preparedStatement.executeUpdate();
            connection.commit();
        } catch (SQLException e) {
            rollback(connection, RDBMSConstants.TASK_REMOVE_NODE_HEARTBEAT);
            throw new ClusterCoordinationException(
                    "error occurred while " + RDBMSConstants.TASK_REMOVE_NODE_HEARTBEAT, e);
        } finally {
            close(preparedStatement, RDBMSConstants.TASK_REMOVE_NODE_HEARTBEAT);
            close(connection, RDBMSConstants.TASK_REMOVE_NODE_HEARTBEAT);
        }
    }

    @Override
    public void insertRemovedNodeDetails(String removedMember, String groupId, List<String> clusterNodes,
            Map<String, Object> removedPropertiesMap) throws ClusterCoordinationException {
        Connection connection = null;
        PreparedStatement storeRemovedMembersPreparedStatement = null;
        String task = "Storing removed member: " + removedMember + " in group " + groupId;
        try {
            connection = getConnection();
            storeRemovedMembersPreparedStatement = connection
                    .prepareStatement(RDBMSConstants.PS_INSERT_REMOVED_MEMBER_DETAILS);
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(removedPropertiesMap);
            objectOutputStream.flush();
            byteArrayOutputStream.flush();
            objectOutputStream.close();
            byteArrayOutputStream.close();
            byte[] propertiesMapAsBytes = byteArrayOutputStream.toByteArray();
            // ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(propertiesMapAsBytes);
            for (String clusterNode : clusterNodes) {
                storeRemovedMembersPreparedStatement.setString(1, clusterNode);
                storeRemovedMembersPreparedStatement.setString(2, groupId);
                storeRemovedMembersPreparedStatement.setString(3, removedMember);
                storeRemovedMembersPreparedStatement.setBinaryStream(4,
                        new ByteArrayInputStream(propertiesMapAsBytes));
                storeRemovedMembersPreparedStatement.addBatch();
            }
            storeRemovedMembersPreparedStatement.executeBatch();

            connection.commit();
        } catch (SQLException e) {
            rollback(connection, task);
            throw new ClusterCoordinationException(
                    "Error storing removed member: " + removedMember + " in group " + groupId, e);
        } catch (IOException e) {
            throw new ClusterCoordinationException("Error while inserting removed node data", e);
        } finally {
            close(storeRemovedMembersPreparedStatement, task);
            close(connection, task);
        }
    }

    @Override
    public void markNodeAsNotNew(String nodeId, String groupId) throws ClusterCoordinationException {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
            connection = getConnection();
            preparedStatement = connection.prepareStatement(RDBMSConstants.PS_MARK_NODE_NOT_NEW);
            preparedStatement.setString(1, nodeId);
            preparedStatement.setString(2, groupId);
            int updateCount = preparedStatement.executeUpdate();
            if (updateCount == 0) {
                logger.warn("No record was updated while marking node as not new");
            }
            connection.commit();
        } catch (SQLException e) {
            rollback(connection, RDBMSConstants.TASK_MARK_NODE_NOT_NEW);
            throw new ClusterCoordinationException("error occurred while " + RDBMSConstants.TASK_MARK_NODE_NOT_NEW,
                    e);
        } finally {
            close(preparedStatement, RDBMSConstants.TASK_MARK_NODE_NOT_NEW);
            close(connection, RDBMSConstants.TASK_MARK_NODE_NOT_NEW);
        }
    }

    @Override
    public void clearHeartBeatData() throws ClusterCoordinationException {
        Connection connection = null;
        PreparedStatement clearNodeHeartbeatData = null;
        PreparedStatement clearCoordinatorHeartbeatData = null;
        String task = "Clearing all heartbeat data";
        try {
            connection = getConnection();
            clearNodeHeartbeatData = connection.prepareStatement(RDBMSConstants.PS_CLEAR_NODE_HEARTBEATS);
            clearNodeHeartbeatData.executeUpdate();

            clearCoordinatorHeartbeatData = connection
                    .prepareStatement(RDBMSConstants.PS_CLEAR_COORDINATOR_HEARTBEAT);
            clearCoordinatorHeartbeatData.executeUpdate();

            connection.commit();
        } catch (SQLException e) {
            rollback(connection, task);
            throw new ClusterCoordinationException("Error occurred while " + task, e);
        } finally {
            close(clearNodeHeartbeatData, task);
            close(clearCoordinatorHeartbeatData, task);
            close(connection, task);
        }
    }

    @Override
    public void clearMembershipEvents(String nodeID, String groupID) throws ClusterCoordinationException {
        Connection connection = null;
        PreparedStatement clearMembershipEvents = null;
        String task = "Clearing all membership events for node: " + nodeID;
        try {
            connection = getConnection();
            clearMembershipEvents = connection.prepareStatement(RDBMSConstants.PS_CLEAN_MEMBERSHIP_EVENTS_FOR_NODE);
            clearMembershipEvents.setString(1, nodeID);
            // clearMembershipEvents.setString(2, groupID);
            clearMembershipEvents.executeUpdate();
            connection.commit();
        } catch (SQLException e) {
            rollback(connection, task);
            throw new ClusterCoordinationException("Error occurred while " + task, e);
        } finally {
            close(clearMembershipEvents, task);
            close(connection, task);
        }
    }
}