diff --git a/java_console/.idea/libraries/javax_json.xml b/java_console/.idea/libraries/javax_json.xml
new file mode 100644
index 0000000000..cb9483089a
--- /dev/null
+++ b/java_console/.idea/libraries/javax_json.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/java_console/.idea/libraries/takes.xml b/java_console/.idea/libraries/takes.xml
new file mode 100644
index 0000000000..ac672250c8
--- /dev/null
+++ b/java_console/.idea/libraries/takes.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/java_console/.idea/modules.xml b/java_console/.idea/modules.xml
index 6e70510c52..b107eaff7e 100644
--- a/java_console/.idea/modules.xml
+++ b/java_console/.idea/modules.xml
@@ -11,6 +11,7 @@
+
diff --git a/java_console/autotest/autotest.iml b/java_console/autotest/autotest.iml
index 3ee7cabab0..50df370975 100644
--- a/java_console/autotest/autotest.iml
+++ b/java_console/autotest/autotest.iml
@@ -10,5 +10,6 @@
+
\ No newline at end of file
diff --git a/java_console/build.xml b/java_console/build.xml
index 5a0b79a5af..c8be7f72c4 100644
--- a/java_console/build.xml
+++ b/java_console/build.xml
@@ -1,6 +1,7 @@
+
@@ -39,11 +40,13 @@
+ classpath="${lib_list}">
+
+
@@ -76,7 +79,7 @@
+ path="build/classes:lib/junit.jar:${lib_list}:lib/commons-logging.jar"/>
diff --git a/java_console/io/src/main/java/com/rusefi/binaryprotocol/BinaryProtocol.java b/java_console/io/src/main/java/com/rusefi/binaryprotocol/BinaryProtocol.java
index 38fb55e3b7..4afdb431aa 100644
--- a/java_console/io/src/main/java/com/rusefi/binaryprotocol/BinaryProtocol.java
+++ b/java_console/io/src/main/java/com/rusefi/binaryprotocol/BinaryProtocol.java
@@ -288,7 +288,7 @@ public class BinaryProtocol implements BinaryProtocolCommands {
setController(newVersion);
}
- private byte[] receivePacket(String msg, boolean allowLongResponse) throws InterruptedException, EOFException {
+ private byte[] receivePacket(String msg, boolean allowLongResponse) throws EOFException {
long start = System.currentTimeMillis();
synchronized (ioLock) {
return incomingData.getPacket(logger, msg, allowLongResponse, start);
@@ -408,7 +408,7 @@ public class BinaryProtocol implements BinaryProtocolCommands {
sendPacket(packet);
return receivePacket(msg, allowLongResponse);
- } catch (InterruptedException | IOException e) {
+ } catch (IOException e) {
logger.error(msg + ": executeCommand failed: " + e);
close();
return null;
diff --git a/java_console/io/src/main/java/com/rusefi/server/Backend.java b/java_console/io/src/main/java/com/rusefi/server/Backend.java
deleted file mode 100644
index 769e90835c..0000000000
--- a/java_console/io/src/main/java/com/rusefi/server/Backend.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package com.rusefi.server;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import static com.rusefi.binaryprotocol.BinaryProtocol.sleep;
-
-public class Backend {
-
- // guarded by own monitor
- private final Set clients = new HashSet<>();
- private final int clientTimeout;
-
- public Backend(int clientTimeout) {
- this.clientTimeout = clientTimeout;
-
- new Thread(() -> {
- while (true) {
- runCleanup();
- sleep(clientTimeout);
- }
- }, "rusEFI Server Cleanup").start();
-
-
- }
-
- private void runCleanup() {
- List inactiveClients = new ArrayList<>();
-
- synchronized (clients) {
- long now = System.currentTimeMillis();
- for (ClientConnectionState client : clients) {
- if (now - client.getLastActivityTimestamp() > clientTimeout)
- inactiveClients.add(client);
- }
- }
-
- for (ClientConnectionState inactiveClient : inactiveClients) {
- close(inactiveClient);
- }
-
- }
-
- public void register(ClientConnectionState clientConnectionState) {
- synchronized (clients) {
- clients.add(clientConnectionState);
- }
- }
-
- private void close(ClientConnectionState inactiveClient) {
- inactiveClient.close();
- synchronized (clients) {
- clients.remove(inactiveClient);
- }
- }
-
- public List getClients() {
- synchronized (clients) {
- return new ArrayList<>(clients);
- }
- }
-
- public int getCount() {
- synchronized (clients) {
- return clients.size();
- }
- }
-}
diff --git a/java_console/io/src/main/java/com/rusefi/server/ClientConnectionState.java b/java_console/io/src/main/java/com/rusefi/server/ClientConnectionState.java
deleted file mode 100644
index 8d293f29b8..0000000000
--- a/java_console/io/src/main/java/com/rusefi/server/ClientConnectionState.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package com.rusefi.server;
-
-import com.opensr5.Logger;
-import com.rusefi.auth.AutoTokenUtil;
-import com.rusefi.binaryprotocol.BinaryProtocolCommands;
-import com.rusefi.binaryprotocol.IncomingDataBuffer;
-import com.rusefi.io.IoStream;
-import com.rusefi.io.commands.HelloCommand;
-import com.rusefi.io.tcp.TcpIoStream;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.net.Socket;
-
-import static com.rusefi.binaryprotocol.IoHelper.checkResponseCode;
-
-public class ClientConnectionState {
- private final Socket clientSocket;
- private final Logger logger;
-
- private long lastActivityTimestamp;
- private boolean isClosed;
- private IoStream stream;
- private IncomingDataBuffer incomingData;
-
- public ClientConnectionState(Socket clientSocket, Logger logger) {
- this.clientSocket = clientSocket;
- this.logger = logger;
- try {
- stream = new TcpIoStream(logger, clientSocket);
- incomingData = stream.getDataBuffer();
- } catch (IOException e) {
- close();
- }
- }
-
- public long getLastActivityTimestamp() {
- return lastActivityTimestamp;
- }
-
- public void close() {
- isClosed = true;
- close(clientSocket);
- }
-
- public void sayHello() {
- try {
- HelloCommand.send(stream, logger);
- byte[] response = incomingData.getPacket(logger, "", false);
- if (!checkResponseCode(response, BinaryProtocolCommands.RESPONSE_OK))
- return;
- String tokenAndSignature = new String(response, 1, response.length - 1);
- String token = tokenAndSignature.length() > AutoTokenUtil.TOKEN_LENGTH ? tokenAndSignature.substring(0, AutoTokenUtil.TOKEN_LENGTH) : null;
- if (!AutoTokenUtil.isToken(token))
- throw new IOException("Invalid token");
- String signature = tokenAndSignature.substring(AutoTokenUtil.TOKEN_LENGTH);
-
- logger.info(token + " New client: " + signature);
-
- } catch (IOException e) {
- close();
- }
- }
-
- private static void close(Closeable closeable) {
- if (closeable != null) {
- try {
- closeable.close();
- } catch (IOException ignored) {
- // ignored
- }
- }
- }
-}
diff --git a/java_console/lib/server/cactoos.jar b/java_console/lib/server/cactoos.jar
new file mode 100644
index 0000000000..37ef818b2b
Binary files /dev/null and b/java_console/lib/server/cactoos.jar differ
diff --git a/java_console/lib/server/javax.json.jar b/java_console/lib/server/javax.json.jar
new file mode 100644
index 0000000000..f6ca0cc431
Binary files /dev/null and b/java_console/lib/server/javax.json.jar differ
diff --git a/java_console/lib/server/takes-sources.jar b/java_console/lib/server/takes-sources.jar
new file mode 100644
index 0000000000..7e05659814
Binary files /dev/null and b/java_console/lib/server/takes-sources.jar differ
diff --git a/java_console/lib/server/takes.jar b/java_console/lib/server/takes.jar
new file mode 100644
index 0000000000..f118aceafb
Binary files /dev/null and b/java_console/lib/server/takes.jar differ
diff --git a/java_console/ui/src/test/java/com/rusefi/ServerTest.java b/java_console/ui/src/test/java/com/rusefi/ServerTest.java
index 3de2e37ce1..5e0d7f5334 100644
--- a/java_console/ui/src/test/java/com/rusefi/ServerTest.java
+++ b/java_console/ui/src/test/java/com/rusefi/ServerTest.java
@@ -1,24 +1,40 @@
package com.rusefi;
import com.opensr5.Logger;
+import com.rusefi.io.TcpCommunicationIntegrationTest;
import com.rusefi.io.tcp.BinaryProtocolServer;
import com.rusefi.server.Backend;
import com.rusefi.server.ClientConnectionState;
+import com.rusefi.server.UserDetails;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.util.EntityUtils;
+import org.jetbrains.annotations.NotNull;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
import org.junit.Test;
import java.io.IOException;
import java.net.Socket;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import static com.rusefi.binaryprotocol.BinaryProtocol.sleep;
+import static com.rusefi.server.Backend.LIST_PATH;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* integration test of the rusEFI online backend process
+ * At the moment this test is very loose with timing it must be unreliable?
*/
public class ServerTest {
private final static Logger logger = Logger.CONSOLE;
@@ -30,8 +46,19 @@ public class ServerTest {
CountDownLatch serverCreated = new CountDownLatch(1);
- Backend backend = new Backend(5 * Timeouts.SECOND);
+ Function userDetailsResolver = authToken -> new UserDetails(authToken.substring(0, 5), authToken.charAt(6));
+ CountDownLatch allClientsDisconnected = new CountDownLatch(1);
+
+ int httpPort = 8000;
+ Backend backend = new Backend(userDetailsResolver, httpPort) {
+ @Override
+ public void close(ClientConnectionState inactiveClient) {
+ super.close(inactiveClient);
+ if (getCount() == 0)
+ allClientsDisconnected.countDown();
+ }
+ };
BinaryProtocolServer.tcpServerSocket(serverPort, "Server", new Function() {
@Override
@@ -39,14 +66,15 @@ public class ServerTest {
return new Runnable() {
@Override
public void run() {
- ClientConnectionState clientConnectionState = new ClientConnectionState(clientSocket, logger);
- clientConnectionState.sayHello();
- backend.register(clientConnectionState);
-
- while(true) {
+ ClientConnectionState clientConnectionState = new ClientConnectionState(clientSocket, logger, backend.getUserDetailsResolver());
+ try {
+ clientConnectionState.sayHello();
+ backend.register(clientConnectionState);
+ clientConnectionState.runEndlessLoop();
+ } catch (IOException e) {
+ backend.close(clientConnectionState);
}
-
}
};
}
@@ -66,6 +94,38 @@ public class ServerTest {
List clients = backend.getClients();
assertEquals(2, clients.size());
+ List onlineUsers = getOnlineUsers(httpPort);
+ assertEquals(2, onlineUsers.size());
+
+ assertTrue(allClientsDisconnected.await(30, TimeUnit.SECONDS));
}
+
+ @NotNull
+ private List getOnlineUsers(int httpPort) throws IOException {
+ HttpClient httpclient = new DefaultHttpClient();
+ String url = "http://" + TcpCommunicationIntegrationTest.LOCALHOST + ":" + httpPort + LIST_PATH;
+ System.out.println("Connecting to " + url);
+ HttpGet httpget = new HttpGet(url);
+ HttpResponse httpResponse = httpclient.execute(httpget);
+
+ HttpEntity entity = httpResponse.getEntity();
+ String responseString = EntityUtils.toString(entity, "UTF-8");
+ JSONParser parser = new JSONParser();
+ List userLists = new ArrayList<>();
+ try {
+ JSONArray array = (JSONArray) parser.parse(responseString);
+
+ for (int i = 0; i < array.size(); i++) {
+ JSONObject element = (JSONObject) array.get(i);
+
+ userLists.add(UserDetails.valueOf(element));
+ }
+
+ System.out.println("object=" + array);
+ } catch (ParseException e) {
+ throw new IllegalStateException(e);
+ }
+ return userLists;
+ }
}
diff --git a/java_console/ui/ui.iml b/java_console/ui/ui.iml
index 4d078bd03e..07864d9b01 100644
--- a/java_console/ui/ui.iml
+++ b/java_console/ui/ui.iml
@@ -28,5 +28,7 @@
+
+
\ No newline at end of file
diff --git a/java_tools/proxy_server/proxy_server.iml b/java_tools/proxy_server/proxy_server.iml
new file mode 100644
index 0000000000..55d1282c89
--- /dev/null
+++ b/java_tools/proxy_server/proxy_server.iml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/java_tools/proxy_server/src/main/java/com/rusefi/server/Backend.java b/java_tools/proxy_server/src/main/java/com/rusefi/server/Backend.java
new file mode 100644
index 0000000000..a2216e0351
--- /dev/null
+++ b/java_tools/proxy_server/src/main/java/com/rusefi/server/Backend.java
@@ -0,0 +1,126 @@
+package com.rusefi.server;
+
+import org.jetbrains.annotations.NotNull;
+import org.takes.Take;
+import org.takes.facets.fork.FkRegex;
+import org.takes.facets.fork.TkFork;
+import org.takes.http.Exit;
+import org.takes.http.FtBasic;
+import org.takes.rs.RsJson;
+
+import javax.json.Json;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonObject;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+
+public class Backend {
+
+ public static final String LIST_PATH = "/list_online";
+
+ private final FkRegex showOnlineUsers = new FkRegex(LIST_PATH,
+ (Take) req -> getUsersOnline()
+ );
+
+ @NotNull
+ private RsJson getUsersOnline() throws IOException {
+ JsonArrayBuilder builder = Json.createArrayBuilder();
+ List clients = getClients();
+ for (ClientConnectionState client : clients) {
+
+ JsonObject clientObject = Json.createObjectBuilder()
+ .add(UserDetails.USER_ID, client.getUserDetails().getId())
+ .add(UserDetails.USERNAME, client.getUserDetails().getUserName())
+ .add("signature", client.getSignature())
+ .build();
+ builder.add(clientObject);
+ }
+ return new RsJson(builder.build());
+ }
+
+ // guarded by own monitor
+ private final Set clients = new HashSet<>();
+ // private final int clientTimeout;
+ private final Function userDetailsResolver;
+
+ public Backend(Function userDetailsResolver, int httpPort) {
+// this.clientTimeout = clientTimeout;
+ this.userDetailsResolver = userDetailsResolver;
+
+
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ new FtBasic(
+ new TkFork(showOnlineUsers,
+ new FkRegex("/", "rusEFI Online")
+ ), httpPort
+ ).start(Exit.NEVER);
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+
+ }
+ }).start();
+
+
+// new Thread(() -> {
+// while (true) {
+// runCleanup();
+// sleep(clientTimeout);
+// }
+// }, "rusEFI Server Cleanup").start();
+ }
+
+ public Function getUserDetailsResolver() {
+ return userDetailsResolver;
+ }
+
+// private void runCleanup() {
+// List inactiveClients = new ArrayList<>();
+//
+// synchronized (clients) {
+// long now = System.currentTimeMillis();
+// for (ClientConnectionState client : clients) {
+// if (now - client.getLastActivityTimestamp() > clientTimeout)
+// inactiveClients.add(client);
+// }
+// }
+//
+// for (ClientConnectionState inactiveClient : inactiveClients) {
+// close(inactiveClient);
+// }
+//
+// }
+
+ public void register(ClientConnectionState clientConnectionState) {
+ synchronized (clients) {
+ clients.add(clientConnectionState);
+ }
+ }
+
+ public void close(ClientConnectionState inactiveClient) {
+ inactiveClient.close();
+ synchronized (clients) {
+ // in case of exception in the initialization phase we do not even add client into the the collection
+ clients.remove(inactiveClient);
+ }
+ }
+
+ public List getClients() {
+ synchronized (clients) {
+ return new ArrayList<>(clients);
+ }
+ }
+
+ public int getCount() {
+ synchronized (clients) {
+ return clients.size();
+ }
+ }
+}
diff --git a/java_tools/proxy_server/src/main/java/com/rusefi/server/ClientConnectionState.java b/java_tools/proxy_server/src/main/java/com/rusefi/server/ClientConnectionState.java
new file mode 100644
index 0000000000..18f164518f
--- /dev/null
+++ b/java_tools/proxy_server/src/main/java/com/rusefi/server/ClientConnectionState.java
@@ -0,0 +1,105 @@
+package com.rusefi.server;
+
+import com.opensr5.Logger;
+import com.rusefi.auth.AutoTokenUtil;
+import com.rusefi.binaryprotocol.BinaryProtocolCommands;
+import com.rusefi.binaryprotocol.IncomingDataBuffer;
+import com.rusefi.io.IoStream;
+import com.rusefi.io.commands.GetOutputsCommand;
+import com.rusefi.io.commands.HelloCommand;
+import com.rusefi.io.tcp.TcpIoStream;
+
+import java.io.Closeable;
+import java.io.EOFException;
+import java.io.IOException;
+import java.net.Socket;
+import java.util.function.Function;
+
+import static com.rusefi.binaryprotocol.IoHelper.checkResponseCode;
+
+public class ClientConnectionState {
+ private final Socket clientSocket;
+ private final Logger logger;
+ private final Function userDetailsResolver;
+
+ private long lastActivityTimestamp;
+ private boolean isClosed;
+ private IoStream stream;
+ private IncomingDataBuffer incomingData;
+ private UserDetails userDetails;
+ private String signature;
+
+ public ClientConnectionState(Socket clientSocket, Logger logger, Function userDetailsResolver) {
+ this.clientSocket = clientSocket;
+ this.logger = logger;
+ this.userDetailsResolver = userDetailsResolver;
+ try {
+ stream = new TcpIoStream(logger, clientSocket);
+ incomingData = stream.getDataBuffer();
+ } catch (IOException e) {
+ close();
+ }
+ }
+
+// public long getLastActivityTimestamp() {
+// return lastActivityTimestamp;
+// }
+
+
+ public boolean isClosed() {
+ return isClosed;
+ }
+
+ public void close() {
+ isClosed = true;
+ close(clientSocket);
+ }
+
+ public void sayHello() throws IOException {
+ HelloCommand.send(stream, logger);
+ byte[] response = incomingData.getPacket(logger, "", false);
+ if (!checkResponseCode(response, BinaryProtocolCommands.RESPONSE_OK))
+ return;
+ String tokenAndSignature = new String(response, 1, response.length - 1);
+ String authToken = tokenAndSignature.length() > AutoTokenUtil.TOKEN_LENGTH ? tokenAndSignature.substring(0, AutoTokenUtil.TOKEN_LENGTH) : null;
+ if (!AutoTokenUtil.isToken(authToken))
+ throw new IOException("Invalid token");
+ signature = tokenAndSignature.substring(AutoTokenUtil.TOKEN_LENGTH);
+
+ logger.info(authToken + " New client: " + signature);
+ userDetails = userDetailsResolver.apply(authToken);
+ logger.info("User " + userDetails);
+ }
+
+ public UserDetails getUserDetails() {
+ return userDetails;
+ }
+
+ public String getSignature() {
+ return signature;
+ }
+
+ private static void close(Closeable closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (IOException ignored) {
+ // ignored
+ }
+ }
+ }
+
+ public void runEndlessLoop() throws IOException {
+
+ while (true) {
+ byte[] commandPacket = GetOutputsCommand.createRequest();
+
+ stream.sendPacket(commandPacket, logger);
+
+ byte[] packet = incomingData.getPacket(logger, "msg", true);
+ if (packet == null)
+ throw new IOException("No response");
+
+ }
+ }
+}
diff --git a/java_tools/proxy_server/src/main/java/com/rusefi/server/UserDetails.java b/java_tools/proxy_server/src/main/java/com/rusefi/server/UserDetails.java
new file mode 100644
index 0000000000..2eb08e7186
--- /dev/null
+++ b/java_tools/proxy_server/src/main/java/com/rusefi/server/UserDetails.java
@@ -0,0 +1,39 @@
+package com.rusefi.server;
+
+import org.json.simple.JSONObject;
+
+public class UserDetails {
+
+
+ public static final String USER_ID = "user_id";
+ public static final String USERNAME = "username";
+ private final String userName;
+ private final int id;
+
+ public UserDetails(String userName, int id) {
+ this.userName = userName;
+ this.id = id;
+ }
+
+ public static UserDetails valueOf(JSONObject element) {
+ long userId = (long) element.get(USER_ID);
+ String userName = (String) element.get(USERNAME);
+ return new UserDetails(userName, (int) userId);
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ @Override
+ public String toString() {
+ return "UserDetails{" +
+ "userName='" + userName + '\'' +
+ ", id=" + id +
+ '}';
+ }
+}
diff --git a/java_tools/proxy_server/src/test/java/com/rusefi/server/ServerSandbox.java b/java_tools/proxy_server/src/test/java/com/rusefi/server/ServerSandbox.java
new file mode 100644
index 0000000000..e170408120
--- /dev/null
+++ b/java_tools/proxy_server/src/test/java/com/rusefi/server/ServerSandbox.java
@@ -0,0 +1,65 @@
+package com.rusefi.server;
+
+import org.takes.Request;
+import org.takes.Response;
+import org.takes.Take;
+import org.takes.facets.fork.FkRegex;
+import org.takes.facets.fork.TkFork;
+import org.takes.http.Exit;
+import org.takes.http.FtBasic;
+import org.takes.misc.Href;
+import org.takes.rq.RqHref;
+import org.takes.rs.RsJson;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+
+public class ServerSandbox {
+
+ public static void main(final String... args) throws Exception {
+ new FtBasic(
+ new TkFork(
+ new FkRegex("/list", new Take() {
+ @Override
+ public Response act(Request request) throws Exception {
+ Href href = new RqHref.Base(request).href();
+// URI uri = href.uri();
+ Iterable values = href.param("user");
+ String name = values.iterator().next();
+
+
+ JsonArray result = Json.createArrayBuilder()
+ .add(Json.createObjectBuilder().add("name", name))
+ .add(Json.createObjectBuilder().add("name", name))
+ .build();
+
+ return new RsJson(result);
+
+
+ }
+ }),
+ new FkRegex("/", "hello, world!")
+
+
+ ), 8080
+ ).start(Exit.NEVER);
+ }
+
+
+ public static final class User implements RsJson.Source {
+ private final String name;
+
+ public User(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public JsonObject toJson() {
+ return Json.createObjectBuilder()
+ .add("name", name)
+ .build();
+ }
+ }
+
+}