org.bidib.jbidibc.netbidib.client.NetBidibClientPort Maven / Gradle / Ivy
package org.bidib.jbidibc.netbidib.client;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import org.bidib.jbidibc.messages.utils.ByteUtils;
import org.bidib.jbidibc.messages.utils.ThreadFactoryBuilder;
import org.bidib.jbidibc.netbidib.client.listener.NetBidibPortConnectionStatusListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class NetBidibClientPort implements NetBidibPort {
private static final Logger LOGGER = LoggerFactory.getLogger(NetBidibClientPort.class);
private final NetMessageHandler netMessageHandler;
private Socket socket;
private final Set connectionStatusListeners = new HashSet<>();
private final ScheduledExecutorService socketWorker;
private String targetAddress;
public NetBidibClientPort(InetAddress address, int portNumber, NetMessageHandler netMessageHandler)
throws IOException {
if (address == null) {
throw new IllegalArgumentException("address must not be null!");
}
this.netMessageHandler = netMessageHandler;
this.targetAddress = address.toString() + ":" + portNumber;
this.socketWorker =
Executors
.newScheduledThreadPool(1,
new ThreadFactoryBuilder().setNameFormat("netBidibClientSocketWorkers-thread-%d").build());
final int connectTimeout = 10 * 1000;
LOGGER
.info("Created TCP socket for address: {}, port number: {}, connectTimeout: {}", address, portNumber,
connectTimeout);
// create the client socket
this.socket = new Socket();
this.socket.setTcpNoDelay(true);
// connect the socket with the provided connect timeout
this.socket.connect(new InetSocketAddress(address, portNumber), connectTimeout);
LOGGER.info("Create TCP socket passed for address: {}, port number: {}", address, portNumber);
}
@Override
public void addConnectionStatusListener(final NetBidibPortConnectionStatusListener listener) {
synchronized (connectionStatusListeners) {
connectionStatusListeners.add(listener);
}
}
@Override
public void removeConnectionStatusListener(final NetBidibPortConnectionStatusListener listener) {
synchronized (connectionStatusListeners) {
connectionStatusListeners.remove(listener);
}
}
private AtomicBoolean acceptorRunEnabled = new AtomicBoolean();
private NetBidibClientSocketHandler netBidibClientSocketHandler;
@Override
public void run() {
processSocketTraffic(socket);
}
protected void processSocketTraffic(final Socket socket) {
LOGGER.info("Start processing the TCP socket.");
LOGGER.info("Start client receiver handler.");
// add a task to the worker to let the node process the send queue
try {
final InputStream socketInputStream = socket.getInputStream();
final InetAddress remoteAddress = socket.getInetAddress();
final int portNumber = socket.getPort();
this.netBidibClientSocketHandler =
new NetBidibClientSocketHandler(socketInputStream, remoteAddress, portNumber, netMessageHandler);
// the NetBidibClientSocketHandler handles the messages from the socket
socketWorker.submit(this.netBidibClientSocketHandler);
LOGGER.info("Start client receiver handler has passed.");
// signal connection opened to the UI
synchronized (connectionStatusListeners) {
for (NetBidibPortConnectionStatusListener listener : connectionStatusListeners) {
listener.opened();
}
}
}
catch (IOException ex) {
LOGGER.warn("Get the inputStream of the socket failed.", ex);
// TODO: handle exception
}
}
@Override
public void stop() {
LOGGER.info("Stop the TCP packet receiver, socket: {}", socket);
acceptorRunEnabled.set(false);
if (this.netBidibClientSocketHandler != null) {
try {
this.netBidibClientSocketHandler.stop();
}
catch (Exception ex) {
LOGGER.warn("Stop the netBidibClientSocketHandler failed.", ex);
}
this.netBidibClientSocketHandler = null;
}
if (socket != null) {
LOGGER.info("Close the client socket.");
try {
socket.close();
}
catch (IOException ex) {
LOGGER.warn("Close socket failed.", ex);
}
socket = null;
}
synchronized (connectionStatusListeners) {
for (NetBidibPortConnectionStatusListener listener : connectionStatusListeners) {
listener.closed();
}
}
}
private static final Logger MSG_RAW_LOGGER = LoggerFactory.getLogger("RAW");
/**
* Send the data to the host.
*
* @param sendData
* the data to send
* @param address
* the receiving address of the host
* @param portNumber
* the receiving port number of the host
* @throws IOException
*/
@Override
public void send(byte[] sendData, InetAddress address, int portNumber) throws IOException {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Send data to socket, port: {}, bytes: {}", portNumber, ByteUtils.bytesToHex(sendData));
}
if (MSG_RAW_LOGGER.isInfoEnabled()) {
MSG_RAW_LOGGER.info(">> {}", ByteUtils.bytesToHex(sendData));
}
socket.getOutputStream().write(sendData);
socket.getOutputStream().flush();
}
@Override
public String toString() {
return "NetBidibPlainTcpPort[" + targetAddress + "]";
}
}