All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.googlecode.mobilityrpc.network.impl.tcp.TCPConnection Maven / Gradle / Ivy

Go to download

A high performance and easy to use library for Code Mobility and RPC on the Java platform.

The newest version!
/**
 * Copyright 2011, 2012 Niall Gallagher
 *
 * 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.googlecode.mobilityrpc.network.impl.tcp;

import com.googlecode.mobilityrpc.network.*;
import com.googlecode.mobilityrpc.network.impl.*;

import java.net.Socket;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @author Niall Gallagher
 */
public class TCPConnection implements ConnectionInternal {

    private final Logger logger = Logger.getLogger(getClass().getName());

    private final Socket socket;
    private final ConnectionId connectionId;

    private final BlockingQueue outgoingMessageQueue = new LinkedBlockingQueue();
    private final IncomingMessageHandler incomingMessageHandler;
    private final ConnectionStateListener connectionStateListener;

    private IncomingByteStreamReader incomingByteStreamReader = null;
    private OutgoingByteStreamWriter outgoingByteStreamWriter = null;

    /**
     *
     * @param socket The socket through which this connection communicates.
     * @param connectionId The connection id, comprising the address, port and auxiliary connection id. If auxiliary
     * connection id is zero, indicates that this is a primary connection, otherwise this is simply a number which
     * distinguishes this connection from other "auxiliary" connections to the remote machine
     * @param incomingMessageHandler An object to which messages received on this connection should be supplied.
     * @param connectionStateListener An object which this connection should notify if the connection is closed.
     */
    public TCPConnection(Socket socket, ConnectionId connectionId, IncomingMessageHandler incomingMessageHandler, ConnectionStateListener connectionStateListener) {
        this.socket = socket;
        this.connectionId = connectionId;
        this.incomingMessageHandler = incomingMessageHandler;
        this.connectionStateListener = connectionStateListener;
    }

    @Override
    public ConnectionId getConnectionId() {
        return connectionId;
    }

    @Override
    public void enqueueOutgoingMessage(byte[] message) {
        outgoingMessageQueue.add(message);
        if (logger.isLoggable(Level.FINER)) {
            logger.log(Level.FINER, "Enqueued outgoing message for connection id '" + connectionId + "': " + message.length + " bytes");
        }
    }

    @Override
    public void init() {
        if (incomingByteStreamReader != null || outgoingByteStreamWriter != null) {
            throw new IllegalStateException("Already initialised.");
        }
        try {
            incomingByteStreamReader = new IncomingByteStreamReader(
                    connectionId,
                socket.getInputStream(),
                incomingMessageHandler,
                new ConnectionErrorHandler() {
                    @Override
                    public void handle(Exception e) {
                        if (e instanceof StreamClosedException) {
                            // Remote side closed connection in an orderly manner...
                            if (logger.isLoggable(Level.FINEST)) {
                                // FINEST level logging is enabled
                                // Log that we are closing our end of the connection and include more detail
                                // (stack trace of where we were when stream was closed)...
                                logger.log(Level.FINEST, "Stream closed explicitly by remote side, closing connection: " + connectionId, e);
                            } else {
                                // FINEST level logging is not enabled.
                                // Log at FINE level with minimal detail that remote side disconnected and that we are
                                // closing our end of the connection (this will be a common and expected occurrence)...
                                logger.log(Level.FINE, "Stream closed explicitly by remote side, closing connection (enable finest-level logging for more detail): {0}", connectionId);
                            }
                        }
                        else {
                            // Some unexpected exception occurred.
                            // Log at WARNING level details of the exception and that we will close our connection
                            // as a result...
                            logger.log(Level.WARNING, "Exception in IncomingByteStreamReader, closing connection: " + connectionId, e);
                        }
                        destroy();
                    }
                }
            );
        }
        catch (Exception e) {
            throw new IllegalStateException("Failed to initialize IncomingByteStreamReader for: " + connectionId, e);
        }
        try {
            outgoingByteStreamWriter = new OutgoingByteStreamWriter(
                    connectionId,
                socket.getOutputStream(),
                new MessageProvider() {
                    @Override
                    public byte[] getNextMessage() {
                        try {
                            return outgoingMessageQueue.take();
                        }
                        catch (InterruptedException e) {
                            throw new IllegalStateException("Interrupted while waiting to take message fom outgoing message queue", e);
                        }
                    }
                },
                new ConnectionErrorHandler() {
                    @Override
                    public void handle(Exception e) {
                        logger.log(Level.WARNING, "Exception in OutgoingByteStreamWriter, closing connection: " + connectionId, e);
                        destroy();
                    }
                }
            );
        }
        catch (Exception e) {
            throw new IllegalStateException("Failed to initialize OutgoingByteStreamWriter for: " + connectionId, e);
        }
        incomingByteStreamReader.start();
        outgoingByteStreamWriter.start();
        logger.log(Level.FINER, "Initialized TCP connection for: {0}", connectionId);
    }

    @Override
    public void destroy() {
        if (incomingByteStreamReader == null || outgoingByteStreamWriter == null) {
            return;
        }
        incomingByteStreamReader.shutdown();
        outgoingByteStreamWriter.shutdown();
        connectionStateListener.notifyConnectionClosed(this);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy