tools.datasync.db2db.net.TCPConnection.java Source code

Java tutorial

Introduction

Here is the source code for tools.datasync.db2db.net.TCPConnection.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 * 
 * @author  Upendra Jariya
 * @sponsor Douglas Johnson
 * @version 1.0
 * @since   2014-11-10
 */
package tools.datasync.db2db.net;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ConnectException;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.ClientInfoStatus;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import tools.datasync.db2db.sync.SyncDataHandler;
import tools.datasync.db2db.util.ExceptionHandler;
import tools.datasync.db2db.util.JSONMapperBean;

@Service
public class TCPConnection implements Connection {

    @Autowired
    private ExceptionHandler exceptionHandler;
    @Autowired
    private JSONMapperBean jsonMapper;

    @Value("${peer.tcp.ip}")
    private String syncPeerIp;
    @Value("${peer.tcp.port}")
    private int syncPeerPort;
    @Value("${server.tcp.port}")
    private int serverPort;
    @Value("${peer.name}")
    private String peerName;

    private List<SyncDataHandler> handlers;
    private int messageNumber = 0;

    private final ReentrantLock connectionLock;
    private final AtomicBoolean isConnected;

    private ObjectInputStream inStream;
    private ObjectOutputStream outStream;

    private final Logger logger = Logger.getLogger(TCPConnection.class.getName());

    public TCPConnection() {
        logger.info("Creating TCP Connection service...");
        handlers = new ArrayList<SyncDataHandler>();
        connectionLock = new ReentrantLock();
        isConnected = new AtomicBoolean(false);
    }

    public void initiate() {
        try {
            if (serverPort == 0) {
                serverPort = 9991;
                syncPeerIp = "127.0.0.1";
                syncPeerPort = 7771;
            }
            logger.info("Initializing with serverPort=[" + serverPort + "], syncPeerIp=[" + syncPeerIp
                    + "], syncPeerPort=[" + syncPeerPort + "]");

            Thread connectionThread = null;
            if ("A".equalsIgnoreCase(peerName)) {
                logger.info("Starting TCP server listener thread... Peer: " + peerName);
                TCPServerListener server_ = new TCPServerListener(serverPort);
                connectionThread = new Thread(server_);
            } else {
                int temp = serverPort;
                serverPort = syncPeerPort;
                syncPeerPort = temp;

                logger.info("Starting TCP client listener thread...");
                TCPClientListener client_ = new TCPClientListener(syncPeerIp, syncPeerPort);
                connectionThread = new Thread(client_);
            }

            connectionThread.start();
            // Wait until connection is established...
            logger.info("Waiting until connection is established...");
            connectionThread.join();

            logger.info("Connection established... Starting the input reader thread...");
            TCPInputReader inputReader = new TCPInputReader(inStream);
            Thread inputReaderThread = new Thread(inputReader);
            inputReaderThread.start();
        } catch (Exception ex) {
            exceptionHandler.handle(ex, Level.SEVERE, "Exception occured while initializing");
        }
    }

    public int send(SyncMessage message) {

        String syncJSON = null;
        try {
            message.setMessageNumber(messageNumber++);
            syncJSON = jsonMapper.writeValueAsString(message);
            logger.info(
                    "sending sync message to the peer - length: " + syncJSON.length() + ", json data: " + syncJSON);
            outStream.writeUTF(String.valueOf(syncJSON.length()));
            outStream.writeUTF(" ");
            outStream.writeUTF(syncJSON);
        } catch (IOException e) {
            exceptionHandler.handle(e, Level.WARNING, "Unable to jsonify or send sync message.", message);
        }
        return 0;
    }

    public void onData(String jsonData) {
        try {
            SyncMessage syncMessage = jsonMapper.readValue(jsonData, SyncMessage.class);

            for (SyncDataHandler handler : handlers) {
                try {
                    // TODO: This can be very slow... Performance hack
                    handler.onData(syncMessage);

                } catch (Exception ex) {
                    // Handle exception...
                    exceptionHandler.handle(ex, Level.WARNING, "Exception occured while invoking handler",
                            jsonData);
                }
            }
        } catch (Exception ex) {
            // Handle exception...
            exceptionHandler.handle(ex, Level.WARNING, "Exception occured while de-serializing object", jsonData);
        }
    }

    public boolean addSyncDataHandler(SyncDataHandler handler) {

        return handlers.add(handler);
    }

    public boolean removeSyncDataHandler(SyncDataHandler handler) {

        return handlers.remove(handler);
    }

    public List<SyncDataHandler> getSyncDataHandlers() {
        return handlers;
    }

    public boolean checkOutboundConnection() {
        // TODO: No method found to check output stream
        return (isConnected.get() && outStream != null);
    }

    public boolean checkInboundConnection() {
        try {
            return (isConnected.get() && inStream != null && inStream.available() >= 0);
        } catch (IOException e) {
            return false;
        }
    }

    private class TCPInputReader implements Runnable {

        private ObjectInputStream inStream_;
        private Logger logger_ = Logger.getLogger(TCPInputReader.class.getName());
        private StringBuffer sbJSON = new StringBuffer();

        public TCPInputReader(ObjectInputStream inStream) {
            this.inStream_ = inStream;
        }

        public void run() {
            // Keep reading while connected with peer.
            while (isConnected.get()) {
                try {
                    if (inStream_.available() > 0) {
                        // This data can have many or incomplete JSON...
                        String data_ = inStream_.readUTF();
                        logger_.info("Received json data from peer " + data_);
                        sbJSON.append(data_);
                        logger_.info("Buffered JSON content: " + sbJSON.toString());
                    }
                } catch (IOException e) {
                    exceptionHandler.handle(e, Level.WARNING, "Exception while reading data...");
                }
                try {
                    if (sbJSON.length() > 0) {
                        if (sbJSON.indexOf("{") < 2 || !Character.isDigit(sbJSON.charAt(0))) {
                            throw new NetException("Invalid format of received data from peer...");
                        }
                        String lengthStr = sbJSON.substring(0, sbJSON.indexOf("{"));
                        sbJSON.delete(0, sbJSON.indexOf("{"));
                        Integer length = Integer.parseInt(lengthStr.trim());
                        String unitJSON = sbJSON.substring(0, length);
                        sbJSON.delete(0, length);

                        logger_.finer("Processing 1 unit JSON data: " + unitJSON);
                        onData(unitJSON);
                    }
                } catch (NetException e) {
                    exceptionHandler.handle(e, Level.WARNING, "Exception while parsing data...", sbJSON.toString());
                }
            }
        }

    }

    private class TCPClientListener implements Runnable {

        private String ip;
        private int port;
        private Socket clientSocket;
        private Logger logger_ = Logger.getLogger(TCPClientListener.class.getName());

        public TCPClientListener(String ip, int port) {
            this.ip = ip;
            this.port = port;
        }

        public void run() {
            while (!isConnected.get()) {
                try {
                    // Wait 1 second to let peer get started.
                    logger_.info("Wait 1 second to let peer get started..");
                    Thread.sleep(1000);

                    // Try connection with peer...
                    logger_.info("Try TCP client connection with peer... IP: " + ip + ", Port: " + port);
                    clientSocket = new Socket(ip, port);
                    logger_.info("TCP client connection accepted...");
                    if (connectionLock.tryLock()) {
                        if (isConnected.get()) {
                            logger_.info("Connection is already setup by the server socket.");
                        } else {
                            inStream = new ObjectInputStream(
                                    new BufferedInputStream(clientSocket.getInputStream()));
                            outStream = new ObjectOutputStream(clientSocket.getOutputStream());
                            isConnected.set(true);
                        }

                        connectionLock.unlock();
                    } else {
                        logger_.info("Connection lock is already acquired by server socket...");
                    }
                } catch (InterruptedException e) {
                    exceptionHandler.handle(e, Level.WARNING, "TCPClientListener interrupted");
                } catch (ConnectException ex) {
                    exceptionHandler.handle(ex, Level.WARNING, "TCPClientListener unable to connect with server");
                } catch (Exception ex) {
                    exceptionHandler.handle(ex, Level.WARNING, "Error in connection.");
                }
            }
            logger.info("Exiting TCPClientListener thread.");
        }

    }

    private class TCPServerListener implements Runnable {

        private int serverPort;
        private ServerSocket serverSocket;
        private Logger logger_ = Logger.getLogger(TCPServerListener.class.getName());

        public TCPServerListener(int serverPort) {
            this.serverPort = serverPort;
        }

        public void run() {
            try {
                logger_.info("Starting server socket at port# " + serverPort);
                serverSocket = new ServerSocket(serverPort);
                logger_.info("Waiting on the server socket for connection request from the peer.");
                // Wait for peer to attempt connection...
                Socket socket = serverSocket.accept();
                logger_.info("Connection request arrived from peer IP: " + socket.getInetAddress() + ", Port: "
                        + socket.getPort());
                // See if the connection lock is free...
                if (connectionLock.tryLock()) {
                    if (isConnected.get()) {
                        logger_.info("Connection is already setup by the client socket.");
                    } else {
                        inStream = new ObjectInputStream(new BufferedInputStream(socket.getInputStream()));
                        outStream = new ObjectOutputStream(socket.getOutputStream());
                        isConnected.set(true);
                    }

                    connectionLock.unlock();
                } else {
                    logger_.info("Connection lock is already acquired by client socket...");
                }

            } catch (IOException ex) {
                exceptionHandler.handle(ex, Level.SEVERE, "Exception occured while creating server socket",
                        serverPort);
            }
            logger.info("Exiting TCPServerListener thread.");
        }
    }
}