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

net.scattersphere.api.ClientConnection Maven / Gradle / Ivy

Go to download

Scattersphere API, used for clients to connect to servers and control them through code.

There is a newer version: 0.0.4
Show newest version
/*
 * Scattersphere
 * Copyright 2014-2015, Scattersphere Project.
 *
 * 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 net.scattersphere.api;

import net.scattersphere.data.message.JobMessage;
import org.slf4j.LoggerFactory;
import org.vertx.java.core.*;
import org.vertx.java.core.buffer.Buffer;
import org.vertx.java.core.net.NetClient;
import org.vertx.java.core.net.NetSocket;

import java.util.Objects;
import java.util.function.Consumer;

/**
 * This class handles the connection for the CLI to the endpoint server.  All connection operations are handled asynchronously,
 * which means that a handler must be established to support whatever command is expected.
 *
 * Created by kenji on 11/29/14.
 */
public class ClientConnection {

    private NetClient client;
    private NetSocket clientSocket;

    private String clientAddress;
    private String connectStatus;
    private boolean connected;

    private Buffer readBuffer;
    private long expectedBufferSize;

    private Consumer onConnect;
    private Consumer onMessage;

    private final org.slf4j.Logger LOG = LoggerFactory.getLogger(ClientConnection.class);

    /**
     * Constructor.
     */
    public ClientConnection() {
        this.connected = false;
        this.connectStatus = "Not connected";

        this.client = null;
        this.clientSocket = null;
        this.readBuffer = new Buffer();
        this.expectedBufferSize = 0;
    }

    public ClientConnection onConnect(Consumer onConnect) {
        this.onConnect = onConnect;

        return this;
    }

    public ClientConnection messageReceived(Consumer onMessage) {
        this.onMessage = onMessage;

        return this;
    }

    /**
     * Connects to a specified address, in the format of "address" or "address:port".  If no port is specified,
     * port 10001 is used by default.
     *
     * @param endpointSent The endpoint address to connect to.
     */
    public void connect(final String endpointSent) {
        Objects.requireNonNull(endpointSent);

        String endpoint = endpointSent.trim();

        if (endpoint == null || endpoint.length() == 0) {
            throw new NullPointerException("Endpoint address must not be null.");
        }

        if (client != null) {
            throw new IllegalStateException("Connection already established to endpoint at " + clientSocket.remoteAddress());
        }

        String endpoints[] = endpoint.split(":");
        int endpointPort = 10001;
        String endpointIp = null;

        try {
            if (endpoints.length == 1) {
                endpointIp = endpoints[0];
            } else {
                endpointIp = endpoints[0];
                endpointPort = Integer.parseInt(endpoints[1]);
            }
        } catch(Exception ex) {
            throw new IllegalArgumentException("Unable to connect to address: " + endpointSent, ex);
        }

        client = VertxFactory.newVertx().createNetClient();

        this.connectStatus = "Connecting to " + endpointSent;

        this.clientAddress = endpointIp;

        onConnect.accept(ClientConnectionState.WAITING);

        /**
         * TODO: This should be moved to a class that handles the connect and read functions ...
         */
        client.connect(endpointPort, endpointIp, new AsyncResultHandler() {
            @Override
            public void handle(AsyncResult netSocketAsyncResult) {
                if (netSocketAsyncResult.succeeded()) {
                    connectStatus = "Connected to " + endpointSent;
                    connected = true;
                    clientSocket = netSocketAsyncResult.result();

                    onConnect.accept(ClientConnectionState.CONNECTED);

                    clientSocket.closeHandler(new VoidHandler() {
                        @Override
                        public void handle() {
                            client = null;
                            connectStatus = "Not connected";
                            connected = false;
                            clientSocket = null;

                            onConnect.accept(ClientConnectionState.CLOSED);
                        }
                    });

                    clientSocket.dataHandler(buffer -> {
                        byte data[] = null;

                        if (expectedBufferSize == 0) {
                            int payloadSize = 0;

                            if (buffer.length() > 4) {
                                data = buffer.getBytes();

                                payloadSize = ((data[0] & 0xff) << 24) |
                                        ((data[1] & 0xff) << 16) |
                                        ((data[2] & 0xff) << 8) |
                                        (data[3] & 0xff);
                            }

                            LOG.debug("READ: payloadSize={} appendedBufferSize={}", payloadSize, buffer.length());

                            byte[] newData = new byte[data.length - 4];

                            System.arraycopy(data, 4, newData, 0, data.length - 4);

                            expectedBufferSize = payloadSize;
                            readBuffer = new Buffer(newData);
                        } else {
                            readBuffer.appendBuffer(buffer);
                        }

                        data = readBuffer.getBytes();

                        if (data.length >= expectedBufferSize) {
                            int newBufferLength = (int) (data.length - expectedBufferSize);
                            byte[] newBuffer = null;

                            readBuffer = new Buffer();

                            if (newBufferLength > 0) {
                                newBuffer = new byte[newBufferLength];
                                System.arraycopy(data, (int) expectedBufferSize, newBuffer, 0, newBufferLength);
                                readBuffer.setBytes(0, newBuffer);
                            }

                            expectedBufferSize = 0;

                            LOG.debug("READ: received full packet: length={}", expectedBufferSize);

                            JobMessage jMessage = JobMessage.fromByteArray(data);

                            onMessage.accept(jMessage);
                        }
                    });
                } else if (netSocketAsyncResult.failed()) {
                    onConnect.accept(ClientConnectionState.FAILED);

                    client = null;
                    connectStatus = "Not connected";
                    connected = false;
                    clientSocket = null;
                }
            }
        });
    }

    /**
     * Handles the disconnection from the currently active client.
     */
    public void disconnect() {
        if (client == null) {
            return;
        }

        client.close();

        client = null;
        this.connectStatus = "Not connected";

        connected = false;
        clientSocket = null;

        // call connection closed callback
    }

    /**
     * Sends a command payload to the server, packetizing the data (header with length), and sending it across the wire
     * in a {@link Buffer}.
     *
     * @param data A {@link byte[]} array of data to send.
     */
    public void sendMessage(byte[] data) {
        Objects.requireNonNull(data);

        LOG.debug("sendMessage: Writing: size={}", data.length);

        clientSocket.write(new Buffer(data));
    }

    /**
     * Returns the status of the current connection.
     *
     * @return {@link String} containing a human-readable representation of the connection status.
     */
    public String getConnectStatus() {
        return connectStatus;
    }

    /**
     * Flag indicating whether or not the connection is active.
     *
     * @return {@code true} if the connection is active, {@code false} otherwise.
     */
    public boolean isConnected() {
        return connected;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy