Browse Source

implemented Server Sent events and Heartbeat

lookup-tables
Stephan Richter 5 years ago
parent
commit
02caf2866e
  1. 4
      resources/js/plan.js
  2. 67
      src/main/java/de/srsoftware/web4rail/Application.java
  3. 60
      src/main/java/de/srsoftware/web4rail/Plan.java

4
resources/js/plan.js

@ -158,4 +158,8 @@ window.onload = function () {
$('.menu .move .list div').click(enableMove); $('.menu .move .list div').click(enableMove);
$('.menu .actions .list > div').click(runAction); $('.menu .actions .list > div').click(runAction);
$(BODY).click(bodyClick); $(BODY).click(bodyClick);
var stream = new EventSource("stream");
stream.onmessage = function(ev){
console.log(ev);
}
} }

67
src/main/java/de/srsoftware/web4rail/Application.java

@ -6,6 +6,7 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.URI; import java.net.URI;
@ -38,6 +39,7 @@ public class Application {
server.createContext("/plan", client -> sendPlan(client)); server.createContext("/plan", client -> sendPlan(client));
server.createContext("/css" , client -> sendFile(client)); server.createContext("/css" , client -> sendFile(client));
server.createContext("/js" , client -> sendFile(client)); server.createContext("/js" , client -> sendFile(client));
server.createContext("/stream", client -> stream(client));
server.setExecutor(java.util.concurrent.Executors.newCachedThreadPool()); server.setExecutor(java.util.concurrent.Executors.newCachedThreadPool());
server.start(); server.start();
try { try {
@ -48,35 +50,6 @@ public class Application {
Desktop.getDesktop().browse(URI.create("http://localhost:"+config.getInt(PORT)+"/plan")); Desktop.getDesktop().browse(URI.create("http://localhost:"+config.getInt(PORT)+"/plan"));
} }
private static void sendFile(HttpExchange client) throws IOException {
URI uri = client.getRequestURI();
File file = new File(System.getProperty("user.dir")+"/resources"+uri);
LOG.debug("requesting file: {}",file);
if (file.exists()) {
client.getResponseHeaders().add("Content-Type", Files.probeContentType(file.toPath()));
client.sendResponseHeaders(200, file.length());
OutputStream out = client.getResponseBody();
FileInputStream in = new FileInputStream(file);
in.transferTo(out);
in.close();
out.close();
return;
}
sendError(client,404,t("Could not find \"{}\"",uri));
}
private static void sendError(HttpExchange client, int code, String msg) throws IOException {
client.sendResponseHeaders(code, msg.length());
LOG.error(msg);
OutputStream out = client.getResponseBody();
out.write(msg.getBytes(UTF8));
out.close();
}
private static HashMap<String, String> inflate(byte[] data) {
return inflate(new String(data,UTF8));
}
private static HashMap<String, String> inflate(String data) { private static HashMap<String, String> inflate(String data) {
LOG.debug("inflate({})",data); LOG.debug("inflate({})",data);
HashMap<String, String> params = new HashMap<String, String>(); HashMap<String, String> params = new HashMap<String, String>();
@ -91,6 +64,10 @@ public class Application {
return params; return params;
} }
private static HashMap<String, String> inflate(byte[] data) {
return inflate(new String(data,UTF8));
}
private static void send(HttpExchange client, Object response) throws IOException { private static void send(HttpExchange client, Object response) throws IOException {
byte[] html; byte[] html;
if (response instanceof Page) { if (response instanceof Page) {
@ -107,6 +84,31 @@ public class Application {
os.close(); os.close();
} }
private static void sendError(HttpExchange client, int code, String msg) throws IOException {
client.sendResponseHeaders(code, msg.length());
LOG.error(msg);
OutputStream out = client.getResponseBody();
out.write(msg.getBytes(UTF8));
out.close();
}
private static void sendFile(HttpExchange client) throws IOException {
URI uri = client.getRequestURI();
File file = new File(System.getProperty("user.dir")+"/resources"+uri);
LOG.debug("requesting file: {}",file);
if (file.exists()) {
client.getResponseHeaders().add("Content-Type", Files.probeContentType(file.toPath()));
client.sendResponseHeaders(200, file.length());
OutputStream out = client.getResponseBody();
FileInputStream in = new FileInputStream(file);
in.transferTo(out);
in.close();
out.close();
return;
}
sendError(client,404,t("Could not find \"{}\"",uri));
}
private static void sendPlan(HttpExchange client) throws IOException { private static void sendPlan(HttpExchange client) throws IOException {
try { try {
HashMap<String, String> params = inflate(client.getRequestBody().readAllBytes()); HashMap<String, String> params = inflate(client.getRequestBody().readAllBytes());
@ -117,6 +119,13 @@ public class Application {
} }
} }
private static void stream(HttpExchange client) throws IOException {
client.getResponseHeaders().set("content-type", "text/event-stream");
client.sendResponseHeaders(200, 0);
OutputStreamWriter sseWriter = new OutputStreamWriter(client.getResponseBody());
plan.addClient(sseWriter);
}
private static String t(String text, Object...fills) { private static String t(String text, Object...fills) {
return Translation.get(Application.class, text, fills); return Translation.get(Application.class, text, fills);
} }

60
src/main/java/de/srsoftware/web4rail/Plan.java

@ -6,9 +6,11 @@ import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.security.InvalidParameterException; import java.security.InvalidParameterException;
import java.util.Collection; import java.util.Collection;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -62,6 +64,21 @@ public class Plan {
public enum Direction{ public enum Direction{
NORTH, SOUTH, EAST, WEST NORTH, SOUTH, EAST, WEST
} }
private class Heartbeat extends Thread {
@Override
public void run() {
try {
while (true) {
sleep(10000);
heatbeat();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private static final String ACTION = "action"; private static final String ACTION = "action";
private static final String ACTION_ADD = "add"; private static final String ACTION_ADD = "add";
private static final String ACTION_ANALYZE = "analyze"; private static final String ACTION_ANALYZE = "analyze";
@ -78,11 +95,47 @@ public class Plan {
private static final String ACTION_ROUTE = "openRoute"; private static final String ACTION_ROUTE = "openRoute";
private static final String ID = "id"; private static final String ID = "id";
private static final String ROUTE = "route"; private static final String ROUTE = "route";
private static final HashMap<OutputStreamWriter,Integer> clients = new HashMap<OutputStreamWriter, Integer>();
private HashMap<Integer,HashMap<Integer,Tile>> tiles = new HashMap<Integer,HashMap<Integer,Tile>>(); private HashMap<Integer,HashMap<Integer,Tile>> tiles = new HashMap<Integer,HashMap<Integer,Tile>>();
private HashSet<Block> blocks = new HashSet<Block>(); private HashSet<Block> blocks = new HashSet<Block>();
private HashMap<String, Route> routes = new HashMap<String, Route>(); private HashMap<String, Route> routes = new HashMap<String, Route>();
public Plan() {
new Heartbeat().start();
}
public void heatbeat() {
stream("hearbeat @ "+new Date().getTime());
}
private void stream(String data) {
LOG.debug("streaming {}",data);
Vector<OutputStreamWriter> badClients = null;
for (Entry<OutputStreamWriter, Integer> entry : clients.entrySet()) {
OutputStreamWriter client = entry.getKey();
try {
client.write("data: "+data+"\n\n");
client.flush();
clients.put(client,0);
} catch (IOException e) {
int errorCount = entry.getValue()+1;
LOG.info("Error #{} on client: {}",errorCount,e.getMessage());
if (errorCount > 4) {
if (badClients == null) badClients = new Vector<OutputStreamWriter>();
try {
client.close();
} catch (IOException e1) {}
badClients.add(client);
} else clients.put(client,errorCount);
}
}
if (badClients != null) for (OutputStreamWriter client: badClients) {
LOG.info("Disconnecting client.");
clients.remove(client);
}
}
private Tag actionMenu() throws IOException { private Tag actionMenu() throws IOException {
Tag tileMenu = new Tag("div").clazz("actions").content(t("Actions")); Tag tileMenu = new Tag("div").clazz("actions").content(t("Actions"));
StringBuffer tiles = new StringBuffer(); StringBuffer tiles = new StringBuffer();
@ -91,6 +144,11 @@ public class Plan {
return new Tag("div").clazz("list").content(tiles.toString()).addTo(tileMenu); return new Tag("div").clazz("list").content(tiles.toString()).addTo(tileMenu);
} }
public void addClient(OutputStreamWriter client) {
LOG.debug("Client connected.");
clients.put(client, 0);
}
public static void addLink(Tile tile,String content,Tag list) { public static void addLink(Tile tile,String content,Tag list) {
new Tag("li").clazz("link").attr("onclick", "return clickTile("+tile.x+","+tile.y+");").content(content).addTo(list); new Tag("li").clazz("link").attr("onclick", "return clickTile("+tile.x+","+tile.y+");").content(content).addTo(list);
} }
@ -187,7 +245,7 @@ public class Plan {
String line = br.readLine().trim(); String line = br.readLine().trim();
String[] parts = line.split("=",2); String[] parts = line.split("=",2);
try { try {
String id = parts[0]; //String id = parts[0];
JSONObject json = new JSONObject(parts[1]); JSONObject json = new JSONObject(parts[1]);
Route route = new Route(); Route route = new Route();
json.getJSONArray(Route.PATH).forEach(entry -> { json.getJSONArray(Route.PATH).forEach(entry -> {

Loading…
Cancel
Save