From 45ada5475192f6afc9645c401de46765be87ee3f Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 21 May 2015 16:39:25 +0200 Subject: Lean and mean first prototype - to be improved! --- src/main/java/org/openslx/dnbd3/status/App.java | 14 ++ .../openslx/dnbd3/status/StatisticsGenerator.java | 146 +++++++++++++++++++++ .../java/org/openslx/dnbd3/status/WebServer.java | 100 ++++++++++++++ .../dnbd3/status/output/EdgeSerializer.java | 25 ++++ .../openslx/dnbd3/status/output/OutputMain.java | 19 +++ .../openslx/dnbd3/status/output/ServerStats.java | 19 +++ .../openslx/dnbd3/status/poller/ServerPoller.java | 70 ++++++++++ .../java/org/openslx/dnbd3/status/rpc/Client.java | 31 +++++ .../java/org/openslx/dnbd3/status/rpc/Image.java | 37 ++++++ .../java/org/openslx/dnbd3/status/rpc/Status.java | 73 +++++++++++ 10 files changed, 534 insertions(+) create mode 100644 src/main/java/org/openslx/dnbd3/status/App.java create mode 100644 src/main/java/org/openslx/dnbd3/status/StatisticsGenerator.java create mode 100644 src/main/java/org/openslx/dnbd3/status/WebServer.java create mode 100644 src/main/java/org/openslx/dnbd3/status/output/EdgeSerializer.java create mode 100644 src/main/java/org/openslx/dnbd3/status/output/OutputMain.java create mode 100644 src/main/java/org/openslx/dnbd3/status/output/ServerStats.java create mode 100644 src/main/java/org/openslx/dnbd3/status/poller/ServerPoller.java create mode 100644 src/main/java/org/openslx/dnbd3/status/rpc/Client.java create mode 100644 src/main/java/org/openslx/dnbd3/status/rpc/Image.java create mode 100644 src/main/java/org/openslx/dnbd3/status/rpc/Status.java (limited to 'src/main/java/org/openslx/dnbd3') diff --git a/src/main/java/org/openslx/dnbd3/status/App.java b/src/main/java/org/openslx/dnbd3/status/App.java new file mode 100644 index 0000000..9f7ef5f --- /dev/null +++ b/src/main/java/org/openslx/dnbd3/status/App.java @@ -0,0 +1,14 @@ +package org.openslx.dnbd3.status; + +import java.io.IOException; + +public class App +{ + + public static void main( String[] args ) throws IOException + { + WebServer ws = new WebServer( 8888 ); + ws.run(); + } + +} diff --git a/src/main/java/org/openslx/dnbd3/status/StatisticsGenerator.java b/src/main/java/org/openslx/dnbd3/status/StatisticsGenerator.java new file mode 100644 index 0000000..777ce7a --- /dev/null +++ b/src/main/java/org/openslx/dnbd3/status/StatisticsGenerator.java @@ -0,0 +1,146 @@ +package org.openslx.dnbd3.status; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.openslx.dnbd3.status.output.EdgeSerializer; +import org.openslx.dnbd3.status.output.OutputMain; +import org.openslx.dnbd3.status.output.ServerStats; +import org.openslx.dnbd3.status.poller.ServerPoller; +import org.openslx.dnbd3.status.rpc.Client; +import org.openslx.dnbd3.status.rpc.Status; +import org.openslx.graph.ClientNode; +import org.openslx.graph.Edge; +import org.openslx.graph.Graph; +import org.openslx.graph.Node; +import org.openslx.graph.ServerNode; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +public class StatisticsGenerator +{ + private final List pollers; + private volatile long lastUpdate = 0; + private ExecutorService threadPool = new ThreadPoolExecutor( 1, 6, 1, TimeUnit.MINUTES, new ArrayBlockingQueue( 100 ) ); + private List> futureStatusList = new ArrayList<>(); + private List statusList = new ArrayList<>(); + private final Gson jsonBuilder; + + private final Graph graph = new Graph( "DNBD 3 Status" ); + private byte[] imgData = null; + private final OutputMain output = new OutputMain(); + + public StatisticsGenerator( List pollers ) + { + this.pollers = pollers; + for ( ServerPoller poller : pollers ) { + Node server = new ServerNode( poller.getAddress() ); + server.activate(); + graph.addNode( server ); + } + // Prepare json serializer for graph + final GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.excludeFieldsWithoutExposeAnnotation(); + gsonBuilder.registerTypeHierarchyAdapter( Edge.class, new EdgeSerializer() ); + jsonBuilder = gsonBuilder.create(); + output.graph = graph; + output.servers = new ArrayList<>(); + output.timestamp = 0; + } + + private synchronized void updateAll() + { + futureStatusList.clear(); + for ( final ServerPoller p : pollers ) { + Future ret = threadPool.submit( new Callable() { + @Override + public Status call() throws Exception + { + return p.update(); + } + } ); + futureStatusList.add( ret ); + } + statusList.clear(); + output.servers.clear(); + for ( Future future : futureStatusList ) { + Status status; + try { + status = future.get(); + } catch ( Exception e ) { + continue; + } + if ( status == null ) + continue; + statusList.add( status ); + ServerStats srv = new ServerStats(); + srv.address = status.getAddress(); + srv.bytesReceived = status.getBytesReceived(); + srv.bytesSent = status.getBytesSent(); + for ( Client c : status.getClients() ) { + srv.bytesSent += c.getBytesSent(); + } + srv.clientCount = status.getClients().size(); + srv.uptime = status.getUptime(); + output.servers.add( srv ); + } + output.timestamp = System.currentTimeMillis(); + synchronized ( graph ) { + graph.decay(); + for ( Status status : statusList ) { + String serverIp = status.getAddress(); + Node server = graph.getNode( serverIp ); + if ( server == null ) + server = new ServerNode( serverIp ); + server.activate(); + for ( Client client : status.getClients() ) { + if ( client.getAddress() == null ) + continue; + String ip = client.getAddress().replaceFirst( "\\:\\d+$", "" ); + Node n = graph.getNode( ip ); + if ( n == null ) + n = new ClientNode( ip ); + n.activate(); + graph.setEdge( server, n, ( client.getBytesSent() >> 12 ) + 1 ); + } + } + imgData = null; + } + } + + private synchronized void ensureUpToDate() + { + final long now = System.currentTimeMillis(); + if ( now - lastUpdate > 1900 ) { + lastUpdate = now; + updateAll(); + } + } + + public byte[] getImagePng() + { + ensureUpToDate(); + synchronized ( graph ) { + if ( imgData == null ) { + imgData = graph.makeNextImage(); + } + } + return imgData; + } + + public String getJson() + { + ensureUpToDate(); + synchronized ( graph ) { + return jsonBuilder.toJson( output ); + } + } + +} diff --git a/src/main/java/org/openslx/dnbd3/status/WebServer.java b/src/main/java/org/openslx/dnbd3/status/WebServer.java new file mode 100644 index 0000000..14d9a09 --- /dev/null +++ b/src/main/java/org/openslx/dnbd3/status/WebServer.java @@ -0,0 +1,100 @@ +package org.openslx.dnbd3.status; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.List; + +import org.openslx.dnbd3.status.poller.ServerPoller; + +import fi.iki.elonen.NanoHTTPD; + +public class WebServer extends NanoHTTPD +{ + + private final StatisticsGenerator imageGenerator; + + public WebServer( int port ) + { + super( port ); + List pollers = new ArrayList<>(); + pollers.add( new ServerPoller( "132.230.8.113", 5003 ) ); + pollers.add( new ServerPoller( "132.230.4.60", 5003 ) ); + pollers.add( new ServerPoller( "132.230.4.1", 5003 ) ); + imageGenerator = new StatisticsGenerator( pollers ); + } + + @Override + public Response serve( IHTTPSession session ) + { + String uri = session.getUri(); + + // Special/dynamic + if ( uri.equals( "/image.png" ) ) + return serveImage(); + if ( uri.equals( "/data.json" ) ) + return serveJson(); + + // Static files + if ( uri.equals( "/" ) ) + uri = "/index.html"; + + File f = new File( "./static/" + uri.replace( "/", "" ) ); + if ( f.isFile() ) { + InputStream is = null; + try { + is = new FileInputStream( f ); + /* + // TODO: Shit doesn't work + return new NanoHTTPD.Response( NanoHTTPD.Response.Status.OK, + URLConnection.guessContentTypeFromName( f.getName() ), is ); + */ + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[ 10000 ]; + for ( ;; ) { + int ret = is.read( buffer ); + if ( ret <= 0 ) + break; + baos.write( buffer, 0, ret ); + } + return new NanoHTTPD.Response( NanoHTTPD.Response.Status.OK, + URLConnection.guessContentTypeFromName( f.getName() ), baos.toString( "UTF-8" ) ); + } catch ( Exception e ) { + return new NanoHTTPD.Response( NanoHTTPD.Response.Status.INTERNAL_ERROR, + "text/plain", "Internal Server Error" ); + } finally { + safeClose( is ); + } + } + + return new NanoHTTPD.Response( NanoHTTPD.Response.Status.NOT_FOUND, "text/plain", "Nicht gefunden!" ); + } + + private NanoHTTPD.Response serveImage() + { + InputStream is = null; + byte[] imgData = imageGenerator.getImagePng(); + if ( imgData != null ) + is = new ByteArrayInputStream( imgData ); + if ( is == null ) { + return new NanoHTTPD.Response( NanoHTTPD.Response.Status.INTERNAL_ERROR, "text/plain", "Internal Server Error" ); + } else { + return new NanoHTTPD.Response( NanoHTTPD.Response.Status.OK, "image/png", is ); + } + } + + private NanoHTTPD.Response serveJson() + { + String data = imageGenerator.getJson(); + if ( data == null ) { + return new NanoHTTPD.Response( NanoHTTPD.Response.Status.INTERNAL_ERROR, "text/plain", "Internal Server Error" ); + } else { + return new NanoHTTPD.Response( NanoHTTPD.Response.Status.OK, "application/json", data ); + } + } + +} diff --git a/src/main/java/org/openslx/dnbd3/status/output/EdgeSerializer.java b/src/main/java/org/openslx/dnbd3/status/output/EdgeSerializer.java new file mode 100644 index 0000000..e014bb3 --- /dev/null +++ b/src/main/java/org/openslx/dnbd3/status/output/EdgeSerializer.java @@ -0,0 +1,25 @@ +package org.openslx.dnbd3.status.output; + +import java.lang.reflect.Type; + +import org.openslx.graph.Edge; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +public class EdgeSerializer implements JsonSerializer +{ + + @Override + public JsonElement serialize( Edge src, Type typeOfSrc, JsonSerializationContext context ) + { + final JsonObject out = new JsonObject(); + out.addProperty( "source", src.getSource().getId() ); + out.addProperty( "target", src.getTarget().getId() ); + out.addProperty( "width", src.getWeight() ); + return out; + } + +} diff --git a/src/main/java/org/openslx/dnbd3/status/output/OutputMain.java b/src/main/java/org/openslx/dnbd3/status/output/OutputMain.java new file mode 100644 index 0000000..c3204e7 --- /dev/null +++ b/src/main/java/org/openslx/dnbd3/status/output/OutputMain.java @@ -0,0 +1,19 @@ +package org.openslx.dnbd3.status.output; + +import java.util.List; + +import org.openslx.graph.Graph; + +import com.google.gson.annotations.Expose; + +public class OutputMain +{ + + @Expose + public Graph graph; + @Expose + public List servers; + @Expose + public long timestamp; + +} diff --git a/src/main/java/org/openslx/dnbd3/status/output/ServerStats.java b/src/main/java/org/openslx/dnbd3/status/output/ServerStats.java new file mode 100644 index 0000000..c58abb3 --- /dev/null +++ b/src/main/java/org/openslx/dnbd3/status/output/ServerStats.java @@ -0,0 +1,19 @@ +package org.openslx.dnbd3.status.output; + +import com.google.gson.annotations.Expose; + +public class ServerStats +{ + + @Expose + public String address; + @Expose + public int clientCount; + @Expose + public long uptime; + @Expose + public long bytesSent; + @Expose + public long bytesReceived; + +} diff --git a/src/main/java/org/openslx/dnbd3/status/poller/ServerPoller.java b/src/main/java/org/openslx/dnbd3/status/poller/ServerPoller.java new file mode 100644 index 0000000..bfa70fb --- /dev/null +++ b/src/main/java/org/openslx/dnbd3/status/poller/ServerPoller.java @@ -0,0 +1,70 @@ +package org.openslx.dnbd3.status.poller; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; + +import org.openslx.dnbd3.status.rpc.Status; + +import com.google.gson.Gson; + +/** + * Polling a dnbd3 server for its status. + * + */ +public class ServerPoller +{ + + private final String address; + private final String server; + private final Gson parseGson = new Gson(); + + public ServerPoller( String host, int port ) + { + this.address = host; + this.server = "http://" + host + ":" + port + "/"; + } + + public Status update() + { + HttpURLConnection con = null; + InputStream is; + try { + con = (HttpURLConnection)new URL( this.server ).openConnection(); + con.setRequestMethod( "GET" ); + + con.setConnectTimeout( 1000 ); + con.setReadTimeout( 3000 ); + + if ( con.getResponseCode() != HttpURLConnection.HTTP_OK ) { + return null; + } + + is = con.getInputStream(); + } catch ( java.net.SocketTimeoutException e ) { + return null; + } catch ( java.io.IOException e ) { + return null; + } + // Now read data + Status status; + try { + status = parseGson.fromJson( new InputStreamReader( is ), Status.class ); + status.setAddress( address ); + status.setTimestamp( System.currentTimeMillis() ); + } catch ( Exception e ) { + e.printStackTrace(); + status = null; + } + // TODO: http://docs.oracle.com/javase/6/docs/technotes/guides/net/http-keepalive.html + // once dnbd3 server supports keep-alive connections + return status; + } + + public String getAddress() + { + return address; + } + +} diff --git a/src/main/java/org/openslx/dnbd3/status/rpc/Client.java b/src/main/java/org/openslx/dnbd3/status/rpc/Client.java new file mode 100644 index 0000000..fcb8e3e --- /dev/null +++ b/src/main/java/org/openslx/dnbd3/status/rpc/Client.java @@ -0,0 +1,31 @@ +package org.openslx.dnbd3.status.rpc; + +public class Client +{ + + private String address = null; + private int imageId = -1; + private long bytesSent = -1; + + public String getAddress() + { + return address; + } + + public int getImageId() + { + return imageId; + } + + public long getBytesSent() + { + return bytesSent; + } + + @Override + public String toString() + { + return "[Addr: " + address + ", image: " + imageId + "]"; + } + +} diff --git a/src/main/java/org/openslx/dnbd3/status/rpc/Image.java b/src/main/java/org/openslx/dnbd3/status/rpc/Image.java new file mode 100644 index 0000000..910db82 --- /dev/null +++ b/src/main/java/org/openslx/dnbd3/status/rpc/Image.java @@ -0,0 +1,37 @@ +package org.openslx.dnbd3.status.rpc; + +public class Image +{ + + private String name = null; + private int rid = -1; + private int complete = -1; + private int users = -1; + + public String getName() + { + return name; + } + + public int getRid() + { + return rid; + } + + public int getComplete() + { + return complete; + } + + public int getUsers() + { + return users; + } + + @Override + public String toString() + { + return "[" + name + " (users: " + users + ")]"; + } + +} diff --git a/src/main/java/org/openslx/dnbd3/status/rpc/Status.java b/src/main/java/org/openslx/dnbd3/status/rpc/Status.java new file mode 100644 index 0000000..07fc782 --- /dev/null +++ b/src/main/java/org/openslx/dnbd3/status/rpc/Status.java @@ -0,0 +1,73 @@ +package org.openslx.dnbd3.status.rpc; + +import java.util.List; + +public class Status +{ + + private long bytesReceived = -1; + private long bytesSent = -1; + private int uptime = -1; + private List images = null; + private List clients = null; + private String address = null; + private long timeStamp = -1; + + public long getBytesReceived() + { + return bytesReceived; + } + + public long getBytesSent() + { + return bytesSent; + } + + public int getUptime() + { + return uptime; + } + + public List getImages() + { + return images; + } + + public List getClients() + { + return clients; + } + + public String getAddress() + { + return address; + } + + public void setAddress( String address ) + { + this.address = address; + } + + @Override + public String toString() + { + String ret = "(in: " + bytesReceived + ", out: " + bytesSent; + if ( clients != null ) + ret += ", clients: (" + clients.toString() + ")"; + if ( images != null ) + ret += ", images: (" + images.toString() + ")"; + ret += ")"; + return ret; + } + + public void setTimestamp( long currentTimeMillis ) + { + this.timeStamp = currentTimeMillis; + } + + public long getTimestamp() + { + return this.timeStamp; + } + +} -- cgit v1.2.3-55-g7522