Compare commits
No commits in common. "master" and "sewy-1.2.1" have entirely different histories.
master
...
sewy-1.2.1
20
build.gradle
20
build.gradle
@ -5,7 +5,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group 'me.bvn13'
|
group 'me.bvn13'
|
||||||
version '1.2.8'
|
version '1.2.1'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
@ -16,6 +16,9 @@ dependencies {
|
|||||||
// https://mvnrepository.com/artifact/org.slf4j/slf4j-api
|
// https://mvnrepository.com/artifact/org.slf4j/slf4j-api
|
||||||
implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.35'
|
implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.35'
|
||||||
|
|
||||||
|
// https://mvnrepository.com/artifact/commons-io/commons-io
|
||||||
|
implementation group: 'commons-io', name: 'commons-io', version: '2.11.0'
|
||||||
|
|
||||||
// https://mvnrepository.com/artifact/org.apache.commons/commons-lang3
|
// https://mvnrepository.com/artifact/org.apache.commons/commons-lang3
|
||||||
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.12.0'
|
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.12.0'
|
||||||
|
|
||||||
@ -34,18 +37,14 @@ tasks.withType(Test).configureEach {
|
|||||||
maxParallelForks = 1
|
maxParallelForks = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
task sourceJar(type: Jar) {
|
|
||||||
from sourceSets.main.allJava
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
publishing {
|
||||||
repositories {
|
repositories {
|
||||||
maven {
|
maven {
|
||||||
name = "GitHubPackages"
|
name = "GitHubPackages"
|
||||||
url = uri("https://maven.pkg.github.com/bvn13/sewy")
|
url = uri("https://maven.pkg.github.com/bvn13/sewy")
|
||||||
credentials {
|
credentials {
|
||||||
username = project.findProperty("gpr.user") ?: System.getenv("GITHUB_USER")
|
username = project.findProperty("gpr.user") ?: System.getenv("USERNAME")
|
||||||
password = project.findProperty("gpr.key") ?: System.getenv("GITHUB_TOKEN")
|
password = project.findProperty("gpr.key") ?: System.getenv("TOKEN")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,12 +52,5 @@ publishing {
|
|||||||
gpr(MavenPublication) {
|
gpr(MavenPublication) {
|
||||||
from(components.java)
|
from(components.java)
|
||||||
}
|
}
|
||||||
// mavenJava(MavenPublication) {
|
|
||||||
// from components.java
|
|
||||||
//
|
|
||||||
// artifact sourceJar {
|
|
||||||
// classifier "sources"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -47,7 +47,7 @@ public abstract class AbstractClientListener implements Runnable {
|
|||||||
this.in = socket.getInputStream();
|
this.in = socket.getInputStream();
|
||||||
log.debug("BufferedReader successfully created");
|
log.debug("BufferedReader successfully created");
|
||||||
log.debug("PrintWriter successfully created");
|
log.debug("PrintWriter successfully created");
|
||||||
this.out = socket.getOutputStream();
|
out = socket.getOutputStream();
|
||||||
log.debug("OutputStream successfully created");
|
log.debug("OutputStream successfully created");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
@ -63,10 +63,9 @@ public abstract class AbstractClientListener implements Runnable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads line (separated with '\n') from socket
|
* Reads line (separated with '\n') from socket
|
||||||
*
|
|
||||||
* @return the line read from socket
|
* @return the line read from socket
|
||||||
*/
|
*/
|
||||||
public String readLine() throws IOException {
|
public String readLine() {
|
||||||
final byte[] bytes = readBytes(getSeparator());
|
final byte[] bytes = readBytes(getSeparator());
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
for (byte aByte : bytes) {
|
for (byte aByte : bytes) {
|
||||||
@ -79,31 +78,17 @@ public abstract class AbstractClientListener implements Runnable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads data from socket until {@code separator} is encountered
|
* Reads data from socket until {@code separator} is encountered
|
||||||
*
|
|
||||||
* @param separator byte to separate data portions
|
* @param separator byte to separate data portions
|
||||||
* @return array of bytes read from socket
|
* @return array of bytes read from socket
|
||||||
*/
|
*/
|
||||||
public byte[] readBytes(byte[] separator) throws IOException {
|
public byte[] readBytes(byte separator) {
|
||||||
final List<Byte> data = new ArrayList<>(2048 * 2048);
|
final List<Byte> data = new ArrayList<>(256);
|
||||||
List<Byte> buffer = new ArrayList<>(separator.length);
|
try {
|
||||||
int separatorPosition = 0;
|
while (socket.isConnected()) {
|
||||||
while (socket.isConnected() && !socket.isClosed()) {
|
|
||||||
byte[] portion = in.readNBytes(1);
|
byte[] portion = in.readNBytes(1);
|
||||||
if (portion == null || portion.length == 0) {
|
if (portion == null || portion.length == 0 || portion[0] == separator) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (portion[0] == separator[separatorPosition]) {
|
|
||||||
if (separatorPosition == separator.length - 1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
separatorPosition++;
|
|
||||||
buffer.add(portion[0]);
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
separatorPosition = 0;
|
|
||||||
data.addAll(buffer);
|
|
||||||
buffer.clear();
|
|
||||||
}
|
|
||||||
data.add(portion[0]);
|
data.add(portion[0]);
|
||||||
}
|
}
|
||||||
final byte[] bytes = new byte[data.size()];
|
final byte[] bytes = new byte[data.size()];
|
||||||
@ -111,37 +96,38 @@ public abstract class AbstractClientListener implements Runnable {
|
|||||||
for (Byte aByte : data) {
|
for (Byte aByte : data) {
|
||||||
bytes[i++] = aByte;
|
bytes[i++] = aByte;
|
||||||
}
|
}
|
||||||
if (log.isTraceEnabled()) log.trace("Received {} bytes: {}", bytes.length, bytes);
|
if (log.isTraceEnabled()) log.trace("Received: " + new String(bytes));
|
||||||
return bytes;
|
return bytes;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes line into socket ending with default separator '\n'.
|
* Writes line into socket ending with default separator '\n'.
|
||||||
* Flushes after writing.
|
* Flushes after writing.
|
||||||
*
|
|
||||||
* @param bytes bytes to be sent into socket
|
* @param bytes bytes to be sent into socket
|
||||||
* @param separator byte to separate data portions
|
* @param separator byte to separate data portions
|
||||||
*/
|
*/
|
||||||
public void writeBytes(byte[] bytes, byte[] separator) throws IOException {
|
public void writeBytes(byte[] bytes, byte separator) {
|
||||||
if (log.isTraceEnabled()) log.trace("Sending {} bytes: {}", bytes.length, bytes);
|
if (log.isTraceEnabled()) log.trace("Sending: " + new String(bytes));
|
||||||
|
try {
|
||||||
out.write(bytes);
|
out.write(bytes);
|
||||||
out.write(separator);
|
out.write(separator);
|
||||||
out.flush();
|
out.flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes line into socket ending with default separator '\n'.
|
* Writes line into socket ending with default separator '\n'.
|
||||||
* Flushes after writing.
|
* Flushes after writing.
|
||||||
*
|
|
||||||
* @param data data to be sent into socket
|
* @param data data to be sent into socket
|
||||||
*/
|
*/
|
||||||
public void writeLine(String data) {
|
public void writeLine(String data) {
|
||||||
if (log.isTraceEnabled()) log.trace("Sending: " + data);
|
if (log.isTraceEnabled()) log.trace("Sending: " + data);
|
||||||
try {
|
|
||||||
writeBytes(data.getBytes(), getSeparator());
|
writeBytes(data.getBytes(), getSeparator());
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -153,12 +139,12 @@ public abstract class AbstractClientListener implements Runnable {
|
|||||||
out.close();
|
out.close();
|
||||||
in.close();
|
in.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.warn("Unable to close IN/OUT client buffer");
|
log.warn("Unable to close IN client buffer");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
socket.close();
|
socket.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.warn("Unable to close socket");
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,16 +89,7 @@ public class Client<T extends AbstractClientListener> {
|
|||||||
*/
|
*/
|
||||||
public void stop() {
|
public void stop() {
|
||||||
log.debug("Stopping client");
|
log.debug("Stopping client");
|
||||||
if (client != null) {
|
|
||||||
client.stop();
|
client.stop();
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (socket != null) {
|
|
||||||
socket.close();
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("Failed to close socket");
|
|
||||||
}
|
|
||||||
executor.shutdown();
|
executor.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,11 +106,7 @@ public class Client<T extends AbstractClientListener> {
|
|||||||
* @return the line read from socket
|
* @return the line read from socket
|
||||||
*/
|
*/
|
||||||
public String readLine() {
|
public String readLine() {
|
||||||
try {
|
|
||||||
return client.readLine();
|
return client.readLine();
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -127,7 +114,7 @@ public class Client<T extends AbstractClientListener> {
|
|||||||
* @param separator
|
* @param separator
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public byte[] readBytes(byte[] separator) throws IOException {
|
public byte[] readBytes(byte separator) {
|
||||||
return client.readBytes(separator);
|
return client.readBytes(separator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,6 @@ class ClientListenerFactory {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates client listener constructor
|
* Creates client listener constructor
|
||||||
*
|
|
||||||
* @param clientListenerClass class to be used as client listener
|
* @param clientListenerClass class to be used as client listener
|
||||||
* @param <T> generic type
|
* @param <T> generic type
|
||||||
* @return lambda method to create client listener
|
* @return lambda method to create client listener
|
||||||
@ -34,7 +33,8 @@ class ClientListenerFactory {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
static <T extends AbstractClientListener> Function<Socket, T> createClientListenerConstructor(Class clientListenerClass) {
|
static <T extends AbstractClientListener> Function<Socket, T> createClientListenerConstructor(Class clientListenerClass) {
|
||||||
|
|
||||||
if (clientListenerClass.getGenericSuperclass() == null) {
|
if (clientListenerClass.getGenericSuperclass() == null
|
||||||
|
/*|| !clientListenerClass.getGenericSuperclass().equals(T.class)*/) {
|
||||||
throw new IllegalArgumentException("Wrong client listener of type: "+clientListenerClass.getName());
|
throw new IllegalArgumentException("Wrong client listener of type: "+clientListenerClass.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,10 +19,10 @@ import me.bvn13.sewy.command.AbstractCommand;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import static java.lang.String.format;
|
||||||
import static me.bvn13.sewy.ClientListenerFactory.createClientListenerConstructor;
|
import static me.bvn13.sewy.ClientListenerFactory.createClientListenerConstructor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,7 +41,6 @@ public class CommandClient extends Client<CommandClientListener> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts to connect to server immediately
|
* Starts to connect to server immediately
|
||||||
*
|
|
||||||
* @param host host to connect to
|
* @param host host to connect to
|
||||||
* @param port port to be used while connecting
|
* @param port port to be used while connecting
|
||||||
*/
|
*/
|
||||||
@ -51,7 +50,6 @@ public class CommandClient extends Client<CommandClientListener> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts to connect to server immediately
|
* Starts to connect to server immediately
|
||||||
*
|
|
||||||
* @param host host to connect to
|
* @param host host to connect to
|
||||||
* @param port port to be used while connecting
|
* @param port port to be used while connecting
|
||||||
* @param clientListenerClass client listener class describing protocol of communications
|
* @param clientListenerClass client listener class describing protocol of communications
|
||||||
@ -62,7 +60,6 @@ public class CommandClient extends Client<CommandClientListener> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Connects to server immediately
|
* Connects to server immediately
|
||||||
*
|
|
||||||
* @param host host to connect to
|
* @param host host to connect to
|
||||||
* @param port port to be used while connecting
|
* @param port port to be used while connecting
|
||||||
* @param clientListenerConstructor to provide constructor for client listener (see {@link me.bvn13.sewy.Client#Client(java.lang.String, int, java.lang.Class)})
|
* @param clientListenerConstructor to provide constructor for client listener (see {@link me.bvn13.sewy.Client#Client(java.lang.String, int, java.lang.Class)})
|
||||||
@ -74,12 +71,10 @@ public class CommandClient extends Client<CommandClientListener> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends command to server
|
* Sends command to server
|
||||||
*
|
|
||||||
* @param command command to be sent
|
* @param command command to be sent
|
||||||
* @param <T> generic type
|
* @param <T> generic type
|
||||||
* @throws IOException if any error occurred while sending
|
|
||||||
*/
|
*/
|
||||||
public <T extends AbstractCommand> void send(T command) throws IOException {
|
public <T extends AbstractCommand> void send(T command) {
|
||||||
log.debug("Start to send command: " + command);
|
log.debug("Start to send command: " + command);
|
||||||
client.send(command);
|
client.send(command);
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@ package me.bvn13.sewy;
|
|||||||
import me.bvn13.sewy.command.AbstractCommand;
|
import me.bvn13.sewy.command.AbstractCommand;
|
||||||
import org.apache.commons.lang3.SerializationUtils;
|
import org.apache.commons.lang3.SerializationUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
|
||||||
@ -40,8 +39,7 @@ public class CommandClientListener extends AbstractClientListener implements Abs
|
|||||||
public void run() {
|
public void run() {
|
||||||
for (Thread.yield(); !socket.isConnected() && !socket.isClosed(); Thread.yield()) {
|
for (Thread.yield(); !socket.isConnected() && !socket.isClosed(); Thread.yield()) {
|
||||||
}
|
}
|
||||||
while (socket.isConnected() && !socket.isClosed()) {
|
while (socket.isConnected()) {
|
||||||
try {
|
|
||||||
Thread.yield();
|
Thread.yield();
|
||||||
byte[] line = readBytes(getSeparator());
|
byte[] line = readBytes(getSeparator());
|
||||||
if (line == null || line.length == 0) {
|
if (line == null || line.length == 0) {
|
||||||
@ -61,7 +59,7 @@ public class CommandClientListener extends AbstractClientListener implements Abs
|
|||||||
log.error("Unexpected command received");
|
log.error("Unexpected command received");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
log.debug("Command received: " + command.getClass());
|
log.debug("Command received: " + command);
|
||||||
if (!(command instanceof AbstractCommand)) {
|
if (!(command instanceof AbstractCommand)) {
|
||||||
log.warn("Incorrect command received: " + command);
|
log.warn("Incorrect command received: " + command);
|
||||||
continue;
|
continue;
|
||||||
@ -69,9 +67,6 @@ public class CommandClientListener extends AbstractClientListener implements Abs
|
|||||||
final Serializable response = onCommand((AbstractCommand) command);
|
final Serializable response = onCommand((AbstractCommand) command);
|
||||||
log.debug(format("Response for %s is: %s", command, response));
|
log.debug(format("Response for %s is: %s", command, response));
|
||||||
writeBytes(SerializationUtils.serialize(response), getSeparator());
|
writeBytes(SerializationUtils.serialize(response), getSeparator());
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Failed to communicate!", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,12 +85,11 @@ public class CommandClientListener extends AbstractClientListener implements Abs
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends command to opposite side
|
* Sends command to opposite side
|
||||||
*
|
|
||||||
* @param command command to be sent
|
* @param command command to be sent
|
||||||
* @param <T> generic type
|
* @param <T> generic type
|
||||||
*/
|
*/
|
||||||
public <T extends AbstractCommand> void send(T command) throws IOException {
|
public <T extends AbstractCommand> void send(T command) {
|
||||||
log.debug("Start to send command: {}", command);
|
log.debug("Start to send command: " + command);
|
||||||
writeBytes(SerializationUtils.serialize(command), getSeparator());
|
writeBytes(SerializationUtils.serialize(command), getSeparator());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,12 +16,19 @@
|
|||||||
package me.bvn13.sewy;
|
package me.bvn13.sewy;
|
||||||
|
|
||||||
import me.bvn13.sewy.command.AbstractCommand;
|
import me.bvn13.sewy.command.AbstractCommand;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.util.function.Consumer;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import static java.lang.String.format;
|
import static java.lang.String.format;
|
||||||
@ -52,6 +59,7 @@ public class CommandServer extends Server<CommandClientListener> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
*
|
||||||
* @param host host to bind in order to start listen to clients
|
* @param host host to bind in order to start listen to clients
|
||||||
* @param port port to start listen to
|
* @param port port to start listen to
|
||||||
* @param clientListenerConstructor to provide constructor for client listener (see {@link CommandServer#CommandServer(String, int, Class)})
|
* @param clientListenerConstructor to provide constructor for client listener (see {@link CommandServer#CommandServer(String, int, Class)})
|
||||||
@ -65,13 +73,11 @@ public class CommandServer extends Server<CommandClientListener> {
|
|||||||
socket = server;
|
socket = server;
|
||||||
|
|
||||||
while (!server.isClosed()) {
|
while (!server.isClosed()) {
|
||||||
if (!isMaximumClientsAchieved()) {
|
|
||||||
final Socket client = server.accept();
|
final Socket client = server.accept();
|
||||||
final CommandClientListener clientListener = clientListenerConstructor.apply(client);
|
final CommandClientListener clientListener = clientListenerConstructor.apply(client);
|
||||||
executor.execute(clientListener);
|
executor.execute(clientListener);
|
||||||
clients.add(clientListener);
|
clients.add(clientListener);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error(format("Error while conversation with %s:%d", host, port), e);
|
log.error(format("Error while conversation with %s:%d", host, port), e);
|
||||||
@ -81,31 +87,14 @@ public class CommandServer extends Server<CommandClientListener> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends command to every client
|
* Sends command to every client
|
||||||
*
|
|
||||||
* @param command command to be sent
|
* @param command command to be sent
|
||||||
* @param <T> generic type
|
* @param <T> generic type
|
||||||
*/
|
*/
|
||||||
public <T extends AbstractCommand> void send(T command) {
|
public <T extends AbstractCommand> void send(T command) {
|
||||||
send(command, client -> {});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends command to every client
|
|
||||||
*
|
|
||||||
* @param command command to be sent
|
|
||||||
* @param <T> generic type
|
|
||||||
* @param onException for catching errors while sending. Do not throw any Exception inside onException callback -
|
|
||||||
* it leads to stopping sending the command to remaining clients
|
|
||||||
*/
|
|
||||||
public <T extends AbstractCommand> void send(T command, Consumer<CommandClientListener> onException) {
|
|
||||||
log.debug("Start to send command: " + command);
|
log.debug("Start to send command: " + command);
|
||||||
for (CommandClientListener client : clients) {
|
for (CommandClientListener client : clients) {
|
||||||
try {
|
|
||||||
client.send(command);
|
client.send(command);
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("Failed to send command " + command, e);
|
|
||||||
onException.accept(client);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -46,8 +46,6 @@ public class Server<T extends AbstractClientListener> {
|
|||||||
|
|
||||||
protected ServerSocket socket;
|
protected ServerSocket socket;
|
||||||
|
|
||||||
private int maxClientsCount;
|
|
||||||
|
|
||||||
protected Server() {
|
protected Server() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,6 +59,7 @@ public class Server<T extends AbstractClientListener> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
*
|
||||||
* @param host host to bind in order to start listen to clients
|
* @param host host to bind in order to start listen to clients
|
||||||
* @param port port to start listen to
|
* @param port port to start listen to
|
||||||
* @param clientListenerConstructor to provide constructor for client listener (see {@link me.bvn13.sewy.Server#Server(java.lang.String, int, java.lang.Class)})
|
* @param clientListenerConstructor to provide constructor for client listener (see {@link me.bvn13.sewy.Server#Server(java.lang.String, int, java.lang.Class)})
|
||||||
@ -74,19 +73,17 @@ public class Server<T extends AbstractClientListener> {
|
|||||||
socket = server;
|
socket = server;
|
||||||
|
|
||||||
while (!server.isClosed()) {
|
while (!server.isClosed()) {
|
||||||
if (!isMaximumClientsAchieved()) {
|
|
||||||
final Socket client = server.accept();
|
final Socket client = server.accept();
|
||||||
final T clientListener = clientListenerConstructor.apply(client);
|
final T clientListener = clientListenerConstructor.apply(client);
|
||||||
executor.execute(clientListener);
|
executor.execute(clientListener);
|
||||||
clients.add(clientListener);
|
clients.add(clientListener);
|
||||||
}
|
}
|
||||||
Thread.yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error(format("Error while conversation with %s:%d", host, port), e);
|
log.error(format("Error while conversation with %s:%d", host, port), e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -101,45 +98,15 @@ public class Server<T extends AbstractClientListener> {
|
|||||||
client.stop();
|
client.stop();
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
if (socket != null) {
|
|
||||||
socket.close();
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("Failed to close socket");
|
|
||||||
}
|
|
||||||
executor.shutdown();
|
executor.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To check whether the server is ready for new connections
|
* To check whether the server is ready for new connections
|
||||||
*
|
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public boolean isListening() {
|
public boolean isListening() {
|
||||||
return socket != null && socket.isBound();
|
return socket != null && socket.isBound();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns count of connected clients
|
|
||||||
*
|
|
||||||
* @return count of connected clients
|
|
||||||
*/
|
|
||||||
public int getClientsCount() {
|
|
||||||
return clients.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets maximum clients to be connected to server
|
|
||||||
*
|
|
||||||
* @param count maximum clients count
|
|
||||||
*/
|
|
||||||
public void setMaxClientsCount(int count) {
|
|
||||||
maxClientsCount = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isMaximumClientsAchieved() {
|
|
||||||
return maxClientsCount == 0
|
|
||||||
|| clients.size() >= maxClientsCount;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ public final class Sewy {
|
|||||||
private static final ReentrantLock LOCK = new ReentrantLock();
|
private static final ReentrantLock LOCK = new ReentrantLock();
|
||||||
|
|
||||||
private final List<Class<?>> registeredDataTypes = new CopyOnWriteArrayList<>();
|
private final List<Class<?>> registeredDataTypes = new CopyOnWriteArrayList<>();
|
||||||
private byte[] separator = new byte[] { SEPARATOR };
|
private byte separator = SEPARATOR;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers command in white list for further communications
|
* Registers command in white list for further communications
|
||||||
@ -55,11 +55,11 @@ public final class Sewy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] getSeparator() {
|
public static byte getSeparator() {
|
||||||
return getInstance().separator;
|
return getInstance().separator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setSeparator(byte[] separator) {
|
public static void setSeparator(byte separator) {
|
||||||
try {
|
try {
|
||||||
LOCK.lock();
|
LOCK.lock();
|
||||||
getInstance().separator = separator;
|
getInstance().separator = separator;
|
||||||
|
@ -15,9 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package me.bvn13.sewy;
|
package me.bvn13.sewy;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,9 +22,6 @@ import java.net.Socket;
|
|||||||
* Writes into socket all the data received before
|
* Writes into socket all the data received before
|
||||||
*/
|
*/
|
||||||
public class EchoClientListener extends AbstractClientListener {
|
public class EchoClientListener extends AbstractClientListener {
|
||||||
|
|
||||||
private final Logger log = LoggerFactory.getLogger(EchoClientListener.class);
|
|
||||||
|
|
||||||
public EchoClientListener(Socket socket) {
|
public EchoClientListener(Socket socket) {
|
||||||
super(socket);
|
super(socket);
|
||||||
}
|
}
|
||||||
@ -39,12 +33,8 @@ public class EchoClientListener extends AbstractClientListener {
|
|||||||
public void run() {
|
public void run() {
|
||||||
while (socket.isConnected()) {
|
while (socket.isConnected()) {
|
||||||
Thread.yield();
|
Thread.yield();
|
||||||
try {
|
|
||||||
final String data = readLine();
|
final String data = readLine();
|
||||||
writeLine(data);
|
writeLine(data);
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
package me.bvn13.sewy;
|
package me.bvn13.sewy;
|
||||||
|
|
||||||
import me.bvn13.sewy.command.AbstractCommand;
|
import me.bvn13.sewy.command.AbstractCommand;
|
||||||
import me.bvn13.sewy.command.ComplexCommand;
|
|
||||||
import me.bvn13.sewy.command.PingCommand;
|
import me.bvn13.sewy.command.PingCommand;
|
||||||
import me.bvn13.sewy.command.PongCommand;
|
import me.bvn13.sewy.command.PongCommand;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
@ -24,7 +23,6 @@ import org.junit.jupiter.params.ParameterizedTest;
|
|||||||
import org.junit.jupiter.params.provider.ValueSource;
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
public class ServerTest {
|
public class ServerTest {
|
||||||
|
|
||||||
@ -83,7 +81,7 @@ public class ServerTest {
|
|||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ValueSource(ints = START_PORT + 6)
|
@ValueSource(ints = START_PORT + 6)
|
||||||
void serverIsAbleToPingPong(int port) throws Exception {
|
void serverIsAbleToPingPong(int port) throws InterruptedException {
|
||||||
Sewy.register(PingCommand.class);
|
Sewy.register(PingCommand.class);
|
||||||
Sewy.register(PongCommand.class);
|
Sewy.register(PongCommand.class);
|
||||||
|
|
||||||
@ -114,43 +112,4 @@ public class ServerTest {
|
|||||||
Assertions.assertTrue(latency.get() > 0);
|
Assertions.assertTrue(latency.get() > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@ValueSource(ints = START_PORT + 7)
|
|
||||||
void wideSeparatorTest(int port) throws Exception {
|
|
||||||
Sewy.register(ComplexCommand.class);
|
|
||||||
Sewy.setSeparator(new byte[] { '\n', 'e', 'n', 'd', '\n' });
|
|
||||||
|
|
||||||
AtomicReference<ComplexCommand> check = new AtomicReference<>();
|
|
||||||
|
|
||||||
CommandServer server = new CommandServer("localhost", port, (socket) -> new CommandClientListener(socket) {
|
|
||||||
@Override
|
|
||||||
public AbstractCommand onCommand(AbstractCommand command) {
|
|
||||||
if (command instanceof ComplexCommand) {
|
|
||||||
check.set((ComplexCommand) command);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException(command.toString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
CommandClient client = new CommandClient("localhost", port, (socket) -> new CommandClientListener(socket) {
|
|
||||||
@Override
|
|
||||||
public AbstractCommand onCommand(AbstractCommand command) {
|
|
||||||
throw new IllegalArgumentException(command.toString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ComplexCommand command = new ComplexCommand();
|
|
||||||
command.add(new ComplexCommand.SimpleData("a1"));
|
|
||||||
command.add(new ComplexCommand.SimpleData("b2"));
|
|
||||||
command.add(new ComplexCommand.SimpleData("finish"));
|
|
||||||
|
|
||||||
client.send(command);
|
|
||||||
Thread.sleep(1000);
|
|
||||||
Assertions.assertNotNull(check.get());
|
|
||||||
Assertions.assertEquals(3, check.get().getDatum().size());
|
|
||||||
Assertions.assertEquals("a1", check.get().getDatum().get(0).getString());
|
|
||||||
Assertions.assertEquals("b2", check.get().getDatum().get(1).getString());
|
|
||||||
Assertions.assertEquals("finish", check.get().getDatum().get(2).getString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
package me.bvn13.sewy.command;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class ComplexCommand extends AbstractCommand {
|
|
||||||
|
|
||||||
private List<SimpleData> datum;
|
|
||||||
|
|
||||||
public ComplexCommand() {
|
|
||||||
datum = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ComplexCommand(List<SimpleData> datum) {
|
|
||||||
this.datum = datum;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void add(SimpleData data) {
|
|
||||||
datum.add(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ComplexCommand setDatum(List<SimpleData> datum) {
|
|
||||||
this.datum = datum;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<SimpleData> getDatum() {
|
|
||||||
return datum;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class SimpleData implements Serializable {
|
|
||||||
private final String string;
|
|
||||||
|
|
||||||
public SimpleData(String string) {
|
|
||||||
this.string = string;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getString() {
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user