de.xwic.appkit.core.cluster.impl.OutboundChannel.java Source code

Java tutorial

Introduction

Here is the source code for de.xwic.appkit.core.cluster.impl.OutboundChannel.java

Source

/*******************************************************************************
 * Copyright 2015 xWic group (http://www.xwic.de)
 *
 * 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 de.xwic.appkit.core.cluster.impl;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.StreamCorruptedException;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import de.xwic.appkit.core.cluster.CommunicationException;
import de.xwic.appkit.core.cluster.INode;
import de.xwic.appkit.core.cluster.INode.NodeStatus;
import de.xwic.appkit.core.cluster.Message;
import de.xwic.appkit.core.cluster.NodeAddress;
import de.xwic.appkit.core.cluster.NodeUnavailableException;
import de.xwic.appkit.core.cluster.Response;

/**
 * Holds the connection to a remote Node. 
 * @author lippisch
 */
public class OutboundChannel {

    //private final static int SOCKET_TIMEOUT = 10000; // 10 seconds

    private final Log log = LogFactory.getLog(getClass());

    private Socket socket = null;
    private NodeAddress nodeAddress;

    private ObjectOutputStream out;

    private ObjectInputStream in;

    private Cluster cluster;

    private INode node;

    /**
     * Constructor.
     * @param nodeAddress
     */
    public OutboundChannel(Cluster cluster) {
        this.cluster = cluster;

    }

    /**
     * Initiate the connection to the given node.
     * @param nodeAddress
     */
    public void openConnection(INode node, boolean callBack) throws NodeUnavailableException {
        openConnection(node, callBack, 0);
    }

    /**
     * Initiate the connection to the given node.
     * @param nodeAddress
     */
    public void openConnection(INode node, boolean callBack, int remoteNodeId) throws NodeUnavailableException {

        this.node = node;
        this.nodeAddress = node.getAddress();
        int internalId = ((ClusterNode) node).getInternalNumber();

        try {
            openConnection();
        } catch (IOException e) {
            log.debug("Can not connect to node (" + e + ")");
            throw new NodeUnavailableException("Can not connect to node " + nodeAddress, e);
        }

        try {

            // send identification
            Response res = sendMessage(new Message(Message.CMD_IDENTIFY_CLIENT, "ClusterNode"));
            if (!res.isSuccess()) {
                log.error("Client Identification failed " + res.getReason());
                closeConnection();
                throw new NodeUnavailableException("Connection initialization failed...");
            }

            int myPort = cluster.getConfig().getPortNumber();
            int masterPriority = cluster.getConfig().getMasterPriority();
            Message msg;
            if (callBack) {
                msg = new Message(ClusterNodeClientProtocol.CMD_CALLBACK, cluster.getConfig().getNodeName(),
                        new Integer[] { myPort, remoteNodeId, masterPriority });
            } else {
                msg = new Message(ClusterNodeClientProtocol.CMD_CONNECT, cluster.getConfig().getNodeName(),
                        new Integer[] { myPort, internalId, masterPriority });
            }
            res = sendMessage(msg);
            if (!res.isSuccess()) {
                log.error("The callback/connect process failed. " + res.getReason());
                throw new NodeUnavailableException("Connection callback failed...");
            }

            // connection successfully established

            // Ask remote node for the list of nodes
            res = sendMessage(new Message(ClusterNodeClientProtocol.CMD_GET_NODES));
            if (res.isSuccess()) {
                NodeInfo[] naList = (NodeInfo[]) res.getData();
                for (NodeInfo na : naList) {
                    // register the nodes. If the node is already known, it will not be registered by the cluster
                    if (na != null && !na.getName().equals(cluster.getConfig().getNodeName())) {
                        cluster.registerNode(na.getNodeAddress());
                    }
                }
            }

            if (node.getStatus() == NodeStatus.DISCONNECTED || node.getStatus() == NodeStatus.NEW) {
                ((ClusterNode) node)._connected(this);

                cluster.getClusterServiceManager().handleNewNode(node);
            }

        } catch (CommunicationException ce) {
            log.error("Communication error during connection initialization", ce);
            // abort the connection if initialization fails
            closeConnection();
            throw new NodeUnavailableException("Can not open connection", ce);
        }

    }

    /**
     * @throws IOException 
     * @throws UnknownHostException 
     * 
     */
    private void openConnection() throws UnknownHostException, IOException {

        socket = new Socket(nodeAddress.getHostname(), nodeAddress.getPort());

        //      socket.setSoTimeout(SOCKET_TIMEOUT);

        out = new ObjectOutputStream(socket.getOutputStream());
        in = new ObjectInputStream(socket.getInputStream());

    }

    /**
     * Send a message and read the response.
     * @param message
     * @return
     * @throws CommunicationException 
     */
    public Response sendMessage(Message message) throws CommunicationException {

        try {

            Response response = null;
            // need to synchronize any communication accross the socket
            synchronized (out) {
                out.writeObject(message);

                response = (Response) in.readObject();
                if (response.getResponseTo() != 0 && response.getResponseTo() != message.getMessageId()) {
                    // the response is not the one we expected
                    throw new CommunicationException("Communication out of sync. Expected response to "
                            + message.getMessageId() + " but received response #" + response.getResponseTo());
                }
            }
            return response;
        } catch (StreamCorruptedException sce) {
            // so the stream is corrupt.. disconnect the node and try to reconnect
            closeConnection();
            cluster.nodeDisconnected(node); // notify the cluster that the node was disconnected. The cluster will attempt to reconnect
            throw new CommunicationException("The connection to another node was corrupted and closed.");

        } catch (SocketException se) {
            closeConnection();
            cluster.nodeDisconnected(node); // notify the cluster that the node was disconnected. The cluster will attempt to reconnect
            throw new CommunicationException("The connection to another node got lost.");

        } catch (Exception e) {
            // if the de-serialization of the object failed, the stream might be corrupted
            // need to test if the 'De'serialization can resume properly or if the channel needs to be re-opened.
            log.error("Error receiving node response", e);
            throw new CommunicationException(e);
        }
    }

    /**
     * Close all connection related objects.
     */
    public void closeConnection() {

        if (out != null) {
            try {
                out.close();
            } catch (Exception e) {
                log.error("Error closing Out", e);
            }
        }
        ;
        if (in != null) {
            try {
                in.close();
            } catch (Exception e) {
                log.error("Error closing In", e);
            }
        }
        ;
        if (socket != null) {
            try {
                socket.close();
            } catch (Exception e) {
                log.error("Error closing Socket", e);
            }
        }
        ;
    }

}