net.scattersphere.api.ClientConnection Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scattersphere-api Show documentation
Show all versions of scattersphere-api Show documentation
Scattersphere API, used for clients to connect to servers and control them through code.
/*
* 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