Browse Source

Major refactor of endpoint handling implementation, updating metrics.

Matt Clark 1 year ago
parent
commit
03064b48e6
28 changed files with 438 additions and 251 deletions
  1. 4 0
      resources/static/index.css
  2. 20 13
      src/main/java/org/barcodeapi/core/ServerLoader.java
  3. 23 0
      src/main/java/org/barcodeapi/core/ServerRuntime.java
  4. 33 0
      src/main/java/org/barcodeapi/core/utils/StringUtils.java
  5. 4 6
      src/main/java/org/barcodeapi/server/admin/CacheDumpHandler.java
  6. 6 8
      src/main/java/org/barcodeapi/server/admin/CacheFlushHandler.java
  7. 0 26
      src/main/java/org/barcodeapi/server/admin/SessionClearHandler.java
  8. 24 0
      src/main/java/org/barcodeapi/server/admin/SessionFlushHandler.java
  9. 6 8
      src/main/java/org/barcodeapi/server/admin/SessionListHandler.java
  10. 0 25
      src/main/java/org/barcodeapi/server/admin/StatsDumpHandler.java
  11. 30 0
      src/main/java/org/barcodeapi/server/api/AboutHandler.java
  12. 6 7
      src/main/java/org/barcodeapi/server/api/BarcodeAPIHandler.java
  13. 2 3
      src/main/java/org/barcodeapi/server/api/BulkHandler.java
  14. 0 36
      src/main/java/org/barcodeapi/server/api/CacheHandler.java
  15. 25 0
      src/main/java/org/barcodeapi/server/api/SessionDetailsHandler.java
  16. 0 27
      src/main/java/org/barcodeapi/server/api/SessionHandler.java
  17. 6 5
      src/main/java/org/barcodeapi/server/api/StaticHandler.java
  18. 23 0
      src/main/java/org/barcodeapi/server/api/StatsHandler.java
  19. 3 5
      src/main/java/org/barcodeapi/server/api/TypesHandler.java
  20. 6 3
      src/main/java/org/barcodeapi/server/core/BackgroundTask.java
  21. 4 0
      src/main/java/org/barcodeapi/server/core/CachedObject.java
  22. 10 7
      src/main/java/org/barcodeapi/server/core/ObjectCache.java
  23. 65 19
      src/main/java/org/barcodeapi/server/core/RestHandler.java
  24. 9 7
      src/main/java/org/barcodeapi/server/gen/CodeGenerator.java
  25. 2 4
      src/main/java/org/barcodeapi/server/session/CachedSession.java
  26. 79 32
      src/main/java/org/barcodeapi/server/statistics/StatsCollector.java
  27. 1 1
      src/main/java/org/barcodeapi/server/tasks/StatsDumpTask.java
  28. 47 9
      src/main/java/org/barcodeapi/server/tasks/WatchdogTask.java

+ 4 - 0
resources/static/index.css

@@ -100,6 +100,10 @@ h3, h4 {
 	line-height: 1.5;
 }
 
+.main {
+	height: 60%;
+}
+
 .logo {
 	max-width: 120px;
 	margin: 0 auto;

+ 20 - 13
src/main/java/org/barcodeapi/core/ServerLoader.java

@@ -3,15 +3,16 @@ package org.barcodeapi.core;
 import java.util.Timer;
 import java.util.concurrent.TimeUnit;
 
-import org.barcodeapi.server.admin.CacheClearHandler;
 import org.barcodeapi.server.admin.CacheDumpHandler;
-import org.barcodeapi.server.admin.SessionClearHandler;
-import org.barcodeapi.server.admin.SessionDumpHandler;
-import org.barcodeapi.server.admin.StatsDumpHandler;
+import org.barcodeapi.server.admin.CacheFlushHandler;
+import org.barcodeapi.server.admin.SessionFlushHandler;
+import org.barcodeapi.server.admin.SessionListHandler;
+import org.barcodeapi.server.api.AboutHandler;
 import org.barcodeapi.server.api.BarcodeAPIHandler;
 import org.barcodeapi.server.api.BulkHandler;
-import org.barcodeapi.server.api.SessionHandler;
+import org.barcodeapi.server.api.SessionDetailsHandler;
 import org.barcodeapi.server.api.StaticHandler;
+import org.barcodeapi.server.api.StatsHandler;
 import org.barcodeapi.server.api.TypesHandler;
 import org.barcodeapi.server.core.Log;
 import org.barcodeapi.server.core.Log.LOG;
@@ -68,6 +69,8 @@ public class ServerLoader {
 	 */
 	public void launch() throws Exception {
 
+		Log.out(LOG.SERVER, "Initializing: " + ServerRuntime.getRuntimeID());
+
 		initApiServer();
 
 		initSystemTasks();
@@ -121,17 +124,21 @@ public class ServerLoader {
 		server.setHandler(handlers);
 
 		// setup rest handlers
-		initHandler("/session", SessionHandler.class);
-		initHandler("/types", TypesHandler.class);
-		initHandler("/bulk", BulkHandler.class);
 		initHandler("/api", BarcodeAPIHandler.class);
+		initHandler("/bulk", BulkHandler.class);
+		initHandler("/types", TypesHandler.class);
+		initHandler("/session", SessionDetailsHandler.class);
+
+		// setup server handlers
+		initHandler("/server/about", AboutHandler.class);
+		initHandler("/server/stats", StatsHandler.class);
 
 		// setup admin handlers
-		initHandler("/admin/stats", StatsDumpHandler.class);
 		initHandler("/admin/cache/dump", CacheDumpHandler.class);
-		initHandler("/admin/cache/clear", CacheClearHandler.class);
-		initHandler("/admin/session/dump", SessionDumpHandler.class);
-		initHandler("/admin/session/clear", SessionClearHandler.class);
+		initHandler("/admin/cache/flush", CacheFlushHandler.class);
+
+		initHandler("/admin/session/list", SessionListHandler.class);
+		initHandler("/admin/session/flush", SessionFlushHandler.class);
 
 		// Instantiate the static resource handler and add it to the collection
 		Log.out(LOG.SERVER, "Initializing static resource handler");
@@ -166,7 +173,7 @@ public class ServerLoader {
 		// run watch-dog every 1 minute
 		WatchdogTask watchdogTask = new WatchdogTask();
 		timer.schedule(watchdogTask, 0, //
-				TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES));
+				TimeUnit.MILLISECONDS.convert(15, TimeUnit.SECONDS));
 
 		// print stats to log every 5 minutes
 		StatsDumpTask statsTask = new StatsDumpTask(telemetry);

+ 23 - 0
src/main/java/org/barcodeapi/core/ServerRuntime.java

@@ -0,0 +1,23 @@
+package org.barcodeapi.core;
+
+import java.util.UUID;
+
+public class ServerRuntime {
+
+	// System runtime identifier
+	private static final String _RUNTIME_ID = UUID.randomUUID().toString();
+
+	public static final String getRuntimeID() {
+		return _RUNTIME_ID;
+	}
+
+	private static final long _RUNTIME_TIMESTART = System.currentTimeMillis();
+
+	public static final long getTimeStart() {
+		return _RUNTIME_TIMESTART;
+	}
+
+	public static final long getTimeRunning() {
+		return System.currentTimeMillis() - getTimeStart();
+	}
+}

+ 33 - 0
src/main/java/org/barcodeapi/core/utils/StringUtils.java

@@ -3,6 +3,7 @@ package org.barcodeapi.core.utils;
 import java.io.UnsupportedEncodingException;
 import java.net.URLDecoder;
 import java.net.URLEncoder;
+import java.security.MessageDigest;
 
 import org.barcodeapi.server.core.GenerationException;
 import org.barcodeapi.server.core.GenerationException.ExceptionType;
@@ -52,4 +53,36 @@ public class StringUtils {
 
 		return options;
 	}
+
+	public static String generateChecksum(byte[] in) {
+
+		try {
+
+			MessageDigest md = MessageDigest.getInstance("SHA-256");
+			return StringUtils.bytesToHex(md.digest(in));
+		} catch (Exception e) {
+
+			return null;
+		}
+	}
+
+	public static String bytesToHex(byte[] bytes) {
+
+		if (bytes == null) {
+
+			throw new IllegalArgumentException("supplied value cannot be null");
+		}
+
+		char[] hexArray = "0123456789ABCDEF".toCharArray();
+
+		char[] hexChars = new char[bytes.length * 2];
+
+		for (int j = 0; j < bytes.length; j++) {
+
+			int v = bytes[j] & 0xFF;
+			hexChars[j * 2] = hexArray[v >>> 4];
+			hexChars[j * 2 + 1] = hexArray[v & 0x0F];
+		}
+		return new String(hexChars);
+	}
 }

+ 4 - 6
src/main/java/org/barcodeapi/server/admin/CacheDumpHandler.java

@@ -2,25 +2,23 @@ package org.barcodeapi.server.admin;
 
 import java.io.IOException;
 
-import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.barcodeapi.server.cache.BarcodeCache;
 import org.barcodeapi.server.core.RestHandler;
 import org.barcodeapi.server.gen.CodeType;
-import org.eclipse.jetty.server.Request;
+import org.json.JSONException;
 
 public class CacheDumpHandler extends RestHandler {
 
 	public CacheDumpHandler() {
-		super();
+		super(true);
 	}
 
 	@Override
-	public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
-			throws IOException, ServletException {
-		super.handle(target, baseRequest, request, response);
+	protected void onRequest(HttpServletRequest request, HttpServletResponse response)
+			throws JSONException, IOException {
 
 		// loop all caches
 		String output = "";

+ 6 - 8
src/main/java/org/barcodeapi/server/admin/CacheClearHandler.java → src/main/java/org/barcodeapi/server/admin/CacheFlushHandler.java

@@ -2,25 +2,23 @@ package org.barcodeapi.server.admin;
 
 import java.io.IOException;
 
-import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.barcodeapi.server.cache.BarcodeCache;
 import org.barcodeapi.server.core.RestHandler;
 import org.barcodeapi.server.gen.CodeType;
-import org.eclipse.jetty.server.Request;
+import org.json.JSONException;
 
-public class CacheClearHandler extends RestHandler {
+public class CacheFlushHandler extends RestHandler {
 
-	public CacheClearHandler() {
-		super();
+	public CacheFlushHandler() {
+		super(true);
 	}
 
 	@Override
-	public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
-			throws IOException, ServletException {
-		super.handle(target, baseRequest, request, response);
+	protected void onRequest(HttpServletRequest request, HttpServletResponse response)
+			throws JSONException, IOException {
 
 		for (CodeType type : CodeType.values()) {
 			BarcodeCache.getCache(type).clearCache();

+ 0 - 26
src/main/java/org/barcodeapi/server/admin/SessionClearHandler.java

@@ -1,26 +0,0 @@
-package org.barcodeapi.server.admin;
-
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.barcodeapi.server.core.RestHandler;
-import org.barcodeapi.server.session.SessionCache;
-import org.eclipse.jetty.server.Request;
-
-public class SessionClearHandler extends RestHandler {
-
-	public SessionClearHandler() {
-		super();
-	}
-
-	@Override
-	public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
-			throws IOException, ServletException {
-		super.handle(target, baseRequest, request, response);
-
-		SessionCache.getCache().clearCache();
-	}
-}

+ 24 - 0
src/main/java/org/barcodeapi/server/admin/SessionFlushHandler.java

@@ -0,0 +1,24 @@
+package org.barcodeapi.server.admin;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.barcodeapi.server.core.RestHandler;
+import org.barcodeapi.server.session.SessionCache;
+import org.json.JSONException;
+
+public class SessionFlushHandler extends RestHandler {
+
+	public SessionFlushHandler() {
+		super(true);
+	}
+
+	@Override
+	protected void onRequest(HttpServletRequest request, HttpServletResponse response)
+			throws JSONException, IOException {
+
+		SessionCache.getCache().clearCache();
+	}
+}

+ 6 - 8
src/main/java/org/barcodeapi/server/admin/SessionDumpHandler.java → src/main/java/org/barcodeapi/server/admin/SessionListHandler.java

@@ -2,24 +2,22 @@ package org.barcodeapi.server.admin;
 
 import java.io.IOException;
 
-import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.barcodeapi.server.core.RestHandler;
 import org.barcodeapi.server.session.SessionCache;
-import org.eclipse.jetty.server.Request;
+import org.json.JSONException;
 
-public class SessionDumpHandler extends RestHandler {
+public class SessionListHandler extends RestHandler {
 
-	public SessionDumpHandler() {
-		super();
+	public SessionListHandler() {
+		super(true);
 	}
 
 	@Override
-	public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
-			throws IOException, ServletException {
-		super.handle(target, baseRequest, request, response);
+	protected void onRequest(HttpServletRequest request, HttpServletResponse response)
+			throws JSONException, IOException {
 
 		String output = "";
 		for (String key : SessionCache.getCache().getKeys()) {

+ 0 - 25
src/main/java/org/barcodeapi/server/admin/StatsDumpHandler.java

@@ -1,25 +0,0 @@
-package org.barcodeapi.server.admin;
-
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.barcodeapi.server.core.RestHandler;
-import org.eclipse.jetty.server.Request;
-
-public class StatsDumpHandler extends RestHandler {
-
-	public StatsDumpHandler() {
-		super();
-	}
-
-	@Override
-	public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
-			throws IOException, ServletException {
-		super.handle(target, baseRequest, request, response);
-
-		response.getOutputStream().println(getStats().dumpJSON().toString());
-	}
-}

+ 30 - 0
src/main/java/org/barcodeapi/server/api/AboutHandler.java

@@ -0,0 +1,30 @@
+package org.barcodeapi.server.api;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.barcodeapi.core.ServerRuntime;
+import org.barcodeapi.server.core.RestHandler;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public class AboutHandler extends RestHandler {
+
+	public AboutHandler() {
+		super();
+	}
+
+	@Override
+	protected void onRequest(HttpServletRequest request, HttpServletResponse response)
+			throws JSONException, IOException {
+
+		response.getOutputStream().println((new JSONObject()//
+				.put("runtimeId", ServerRuntime.getRuntimeID())//
+				.put("uptime", "---")//
+				.put("admin", "---")//
+				.put("hostname", "---")//
+		).toString(4));
+	}
+}

+ 6 - 7
src/main/java/org/barcodeapi/server/api/BarcodeAPIHandler.java

@@ -2,7 +2,6 @@ package org.barcodeapi.server.api;
 
 import java.io.IOException;
 
-import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
@@ -13,7 +12,7 @@ import org.barcodeapi.server.core.Log.LOG;
 import org.barcodeapi.server.core.RestHandler;
 import org.barcodeapi.server.gen.BarcodeGenerator;
 import org.barcodeapi.server.gen.BarcodeRequest;
-import org.eclipse.jetty.server.Request;
+import org.json.JSONException;
 
 public class BarcodeAPIHandler extends RestHandler {
 
@@ -35,22 +34,22 @@ public class BarcodeAPIHandler extends RestHandler {
 	}
 
 	@Override
-	public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
-			throws IOException, ServletException {
-		super.handle(target, baseRequest, request, response);
+	protected void onRequest(HttpServletRequest request, HttpServletResponse response)
+			throws JSONException, IOException {
 
 		CachedBarcode barcode;
+		String uri = request.getRequestURI();
+
 		try {
 
 			// generate user requested barcode
-			String uri = baseRequest.getOriginalURI();
 			BarcodeRequest barcodeRequest = new BarcodeRequest(uri);
 			barcode = BarcodeGenerator.requestBarcode(barcodeRequest);
 
 		} catch (GenerationException e) {
 
 			Log.out(LOG.ERROR, "" + //
-					"Failed [ " + target + " ] " + //
+					"Failed [ " + uri + " ] " + //
 					"reason [ " + e.getMessage() + " ]");
 
 			switch (e.getExceptionType()) {

+ 2 - 3
src/main/java/org/barcodeapi/server/api/BulkHandler.java

@@ -22,9 +22,8 @@ public class BulkHandler extends RestHandler {
 	}
 
 	@Override
-	public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
-			throws IOException, ServletException {
-		super.handle(target, baseRequest, request, response);
+	protected void onRequest(HttpServletRequest request, HttpServletResponse response)
+			throws ServletException, IOException {
 
 		// Setup accept multi-part
 		request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, MULTI_PART_CONFIG);

+ 0 - 36
src/main/java/org/barcodeapi/server/api/CacheHandler.java

@@ -1,36 +0,0 @@
-package org.barcodeapi.server.api;
-
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.barcodeapi.server.cache.BarcodeCache;
-import org.barcodeapi.server.core.RestHandler;
-import org.barcodeapi.server.gen.CodeType;
-import org.eclipse.jetty.server.Request;
-
-public class CacheHandler extends RestHandler {
-
-	public CacheHandler() {
-		super();
-	}
-
-	@Override
-	public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
-			throws IOException, ServletException {
-		super.handle(target, baseRequest, request, response);
-
-		// loop all barcode caches
-		String output = "";
-		for (CodeType type : CodeType.values()) {
-			for (String key : BarcodeCache.getCache(type).getKeys()) {
-				output += type.toString() + ":" + key + "\n";
-			}
-		}
-
-		// write to client
-		response.getOutputStream().println(output);
-	}
-}

+ 25 - 0
src/main/java/org/barcodeapi/server/api/SessionDetailsHandler.java

@@ -0,0 +1,25 @@
+package org.barcodeapi.server.api;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.barcodeapi.server.core.RestHandler;
+import org.json.JSONException;
+
+public class SessionDetailsHandler extends RestHandler {
+
+	public SessionDetailsHandler() {
+		super();
+	}
+
+	@Override
+	protected void onRequest(HttpServletRequest request, HttpServletResponse response)
+			throws JSONException, IOException {
+
+		// print user session details
+		response.getOutputStream()//
+				.println(getSession(request).getDetails());
+	}
+}

+ 0 - 27
src/main/java/org/barcodeapi/server/api/SessionHandler.java

@@ -1,27 +0,0 @@
-package org.barcodeapi.server.api;
-
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.barcodeapi.server.core.RestHandler;
-import org.eclipse.jetty.server.Request;
-
-public class SessionHandler extends RestHandler {
-
-	public SessionHandler() {
-		super();
-	}
-
-	@Override
-	public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
-			throws IOException, ServletException {
-		super.handle(target, baseRequest, request, response);
-
-		// print user session details
-		response.getOutputStream()//
-				.println(getSession(request).getDetails());
-	}
-}

+ 6 - 5
src/main/java/org/barcodeapi/server/api/StaticHandler.java

@@ -10,6 +10,7 @@ import org.barcodeapi.server.core.RestHandler;
 import org.eclipse.jetty.server.Request;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.handler.ResourceHandler;
+import org.json.JSONException;
 
 public class StaticHandler extends RestHandler {
 
@@ -31,11 +32,6 @@ public class StaticHandler extends RestHandler {
 			throws IOException, ServletException {
 		super.handle(target, baseRequest, request, response);
 
-		String proto = request.getHeader("X-Forwarded-Proto");
-		if (proto != null) {
-			baseRequest.getMetaData().getURI().setScheme(proto);
-		}
-
 		// call through to resources
 		baseRequest.setHandled(false);
 		resources.handle(target, baseRequest, request, response);
@@ -45,4 +41,9 @@ public class StaticHandler extends RestHandler {
 			response.sendRedirect("/api/auto" + request.getPathInfo());
 		}
 	}
+
+	@Override
+	protected void onRequest(HttpServletRequest request, HttpServletResponse response)
+			throws JSONException, IOException {
+	}
 }

+ 23 - 0
src/main/java/org/barcodeapi/server/api/StatsHandler.java

@@ -0,0 +1,23 @@
+package org.barcodeapi.server.api;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.barcodeapi.server.core.RestHandler;
+import org.json.JSONException;
+
+public class StatsHandler extends RestHandler {
+
+	public StatsHandler() {
+		super();
+	}
+
+	@Override
+	protected void onRequest(HttpServletRequest request, HttpServletResponse response)
+			throws JSONException, IOException {
+
+		response.getOutputStream().println(getStats().getDetails().toString(2));
+	}
+}

+ 3 - 5
src/main/java/org/barcodeapi/server/api/TypesHandler.java

@@ -2,14 +2,13 @@ package org.barcodeapi.server.api;
 
 import java.io.IOException;
 
-import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.barcodeapi.server.core.RestHandler;
 import org.barcodeapi.server.gen.CodeType;
-import org.eclipse.jetty.server.Request;
 import org.json.JSONArray;
+import org.json.JSONException;
 import org.json.JSONObject;
 
 public class TypesHandler extends RestHandler {
@@ -19,9 +18,8 @@ public class TypesHandler extends RestHandler {
 	}
 
 	@Override
-	public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
-			throws IOException, ServletException {
-		super.handle(target, baseRequest, request, response);
+	protected void onRequest(HttpServletRequest request, HttpServletResponse response)
+			throws JSONException, IOException {
 
 		JSONArray output = new JSONArray();
 		for (CodeType type : CodeType.values()) {

+ 6 - 3
src/main/java/org/barcodeapi/server/core/BackgroundTask.java

@@ -25,12 +25,15 @@ public abstract class BackgroundTask extends TimerTask {
 		String taskName = getClass().getName();
 		taskName = taskName.substring(taskName.lastIndexOf('.') + 1);
 		Log.out(LOG.SERVER, "Running task : " + taskName);
-
-		// hit the counter
-		getStats().incrementCounter("task.count." + taskName);
+		long timeStart = System.currentTimeMillis();
 
 		// call implemented method
 		onRun();
+
+		// hit the counter
+		getStats().hitCounter("task", taskName, "count");
+		long timeTask = System.currentTimeMillis() - timeStart;
+		getStats().hitCounter(timeTask, "task", taskName, "time");
 	}
 
 	public abstract void onRun();

+ 4 - 0
src/main/java/org/barcodeapi/server/core/CachedObject.java

@@ -28,6 +28,10 @@ public abstract class CachedObject {
 		return this.timeTouched;
 	}
 
+	public long getTimeExpires() {
+		return this.timeTouched + this.timeTimeout;
+	}
+
 	public void setTimeout(long timeoutTime, TimeUnit timeoutUnit) {
 		this.timeTimeout = TimeUnit.MILLISECONDS.convert(timeoutTime, timeoutUnit);
 	}

+ 10 - 7
src/main/java/org/barcodeapi/server/core/ObjectCache.java

@@ -29,7 +29,7 @@ public class ObjectCache {
 
 	public void put(String key, CachedObject value) {
 
-		stats.incrementCounter("cache." + name + ".add");
+		stats.hitCounter("cache", name, "add");
 		cache.put(key, value);
 	}
 
@@ -62,9 +62,9 @@ public class ObjectCache {
 	public CachedObject get(String key) {
 
 		if (cache.containsKey(key)) {
-			stats.incrementCounter("cache." + name + ".hit");
+			stats.hitCounter("cache", name, "hit");
 		} else {
-			stats.incrementCounter("cache." + name + ".miss");
+			stats.hitCounter("cache", name, "miss");
 		}
 
 		return cache.get(key);
@@ -76,15 +76,18 @@ public class ObjectCache {
 
 	public CachedObject remove(String key) {
 
-		stats.incrementCounter("cache." + name + ".remove");
+		stats.hitCounter("cache", name, "remove");
 		return cache.remove(key);
 	}
 
 	public void clearCache() {
 
-		double num = cache.size();
-		stats.incrementCounter("cache." + name + ".clear", num);
-		cache.clear();
+		synchronized (cache) {
+
+			double num = cache.size();
+			stats.hitCounter(num, "cache", name, "clear");
+			cache.clear();
+		}
 	}
 
 	public static synchronized ObjectCache getCache(String name) {

+ 65 - 19
src/main/java/org/barcodeapi/server/core/RestHandler.java

@@ -23,7 +23,13 @@ public abstract class RestHandler extends AbstractHandler {
 
 	private final StatsCollector stats;
 
+	private final boolean authRequired;
+
 	public RestHandler() {
+		this(false);
+	}
+
+	public RestHandler(boolean authRequired) {
 
 		// extract class name
 		String className = getClass().getName();
@@ -36,23 +42,34 @@ public abstract class RestHandler extends AbstractHandler {
 		}
 
 		this.stats = StatsCollector.getInstance();
+		this.authRequired = authRequired;
 	}
 
 	public StatsCollector getStats() {
 		return stats;
 	}
 
+	public boolean authRequired() {
+		return authRequired;
+	}
+
 	public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
 			throws IOException, ServletException {
 
-		// skip if handled
-		if (baseRequest.isHandled()) {
+		long timeStart = System.currentTimeMillis();
+
+		// skip if already handled
+		if (!baseRequest.isHandled()) {
+			baseRequest.setHandled(true);
+		} else {
 			return;
 		}
 
-		// request is handled
-		baseRequest.setHandled(true);
-		response.setStatus(HttpServletResponse.SC_OK);
+		// update scheme is via proxy
+		String proto = request.getHeader("X-Forwarded-Proto");
+		if (proto != null) {
+			baseRequest.getMetaData().getURI().setScheme(proto);
+		}
 
 		// get source of the request
 		String source;
@@ -73,23 +90,14 @@ public abstract class RestHandler extends AbstractHandler {
 			from = via;
 		}
 
+		// set response code
+		response.setStatus(HttpServletResponse.SC_OK);
+
 		// log the request
 		Log.out(LOG.REQUEST, _NAME + " : " + target + " : " + source + " : " + from);
 
-		// hit the counters
-		getStats().incrementCounter("request.count.total");
-		getStats().incrementCounter("request.count." + _NAME);
-		getStats().incrementCounter("request.method." + request.getMethod());
-
-		// add CORS headers
-		addCORS(baseRequest, response);
-
-		// TODO make this end the call
-		if (request.getMethod().equals("OPTIONS")) {
-			return;
-		}
-
 		// server details
+		addCORSHeaders(baseRequest, response);
 		response.setHeader("Server", "BarcodeAPI.org");
 		response.setHeader("Server-Node", serverName);
 		response.setHeader("Accept-Charset", "utf-8");
@@ -99,9 +107,40 @@ public abstract class RestHandler extends AbstractHandler {
 		CachedSession session = getSession(request);
 		session.hit(baseRequest.getOriginalURI().toString());
 		response.addCookie(session.getCookie());
+
+		// authenticate the user if required
+		if (authRequired() && !validateAdmin(request)) {
+
+			getStats().hitCounter("request", "authfail", request.getMethod());
+			response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+			response.setHeader("WWW-Authenticate", "Basic realm=Welcome");
+			return;
+		}
+
+		try {
+
+			// call the implemented method
+			this.onRequest(request, response);
+		} catch (Exception e) {
+
+			// TODO handle this
+			e.printStackTrace();
+		}
+
+		long targetTime = System.currentTimeMillis() - timeStart;
+
+		// hit the counters
+		getStats().hitCounter("request", "count");
+		getStats().hitCounter(targetTime, "request", "time");
+		getStats().hitCounter("request", "method", request.getMethod());
+		getStats().hitCounter("request", "target", _NAME, "count");
+		getStats().hitCounter(targetTime, "request", "target", _NAME, "time");
+		getStats().hitCounter("request", "target", _NAME, "method", request.getMethod());
 	}
 
-	protected void addCORS(HttpServletRequest request, HttpServletResponse response) {
+	protected abstract void onRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
+
+	protected void addCORSHeaders(HttpServletRequest request, HttpServletResponse response) {
 
 		String origin = request.getHeader("origin");
 		if (origin != null) {
@@ -116,6 +155,13 @@ public abstract class RestHandler extends AbstractHandler {
 		}
 	}
 
+	protected boolean validateAdmin(HttpServletRequest request) {
+
+		String auth = request.getHeader("Authorization");
+		System.out.println("Got: " + auth);
+		return false;
+	}
+
 	protected CachedSession getSession(HttpServletRequest request) {
 
 		// get existing user session

+ 9 - 7
src/main/java/org/barcodeapi/server/gen/CodeGenerator.java

@@ -1,8 +1,8 @@
 package org.barcodeapi.server.gen;
 
 import org.barcodeapi.server.core.GenerationException;
-import org.barcodeapi.server.core.Log;
 import org.barcodeapi.server.core.GenerationException.ExceptionType;
+import org.barcodeapi.server.core.Log;
 import org.barcodeapi.server.core.Log.LOG;
 import org.barcodeapi.server.statistics.StatsCollector;
 import org.json.JSONObject;
@@ -39,6 +39,8 @@ public abstract class CodeGenerator {
 	 */
 	public byte[] getCode(String data, JSONObject options) throws GenerationException {
 
+		String type = getType().toString();
+
 		// validate code format
 		if (!data.matches(getType().getFormatPattern())) {
 
@@ -50,8 +52,8 @@ public abstract class CodeGenerator {
 		String validated = onValidateRequest(data);
 
 		// update global and engine counters
-		StatsCollector.getInstance().incrementCounter("render.count.total");
-		StatsCollector.getInstance().incrementCounter("render.count." + getType().toString());
+		StatsCollector.getInstance().hitCounter("render", "count");
+		StatsCollector.getInstance().hitCounter("render", "type", type, "count");
 
 		try {
 
@@ -61,8 +63,8 @@ public abstract class CodeGenerator {
 			double time = System.currentTimeMillis() - timeStart;
 
 			// update global and engine counters
-			StatsCollector.getInstance().incrementCounter("render.time.total", time);
-			StatsCollector.getInstance().incrementCounter("render.time." + getType(), time);
+			StatsCollector.getInstance().hitCounter(time, "render", "time");
+			StatsCollector.getInstance().hitCounter(time, "render", "type", type, "time");
 
 			Log.out(LOG.BARCODE, "" + //
 					"Rendered [ " + getType().toString() + " ] " + //
@@ -74,8 +76,8 @@ public abstract class CodeGenerator {
 		} catch (Exception e) {
 
 			// update global and engine counters
-			StatsCollector.getInstance().incrementCounter("render.fail.total");
-			StatsCollector.getInstance().incrementCounter("render.fail." + getType().toString());
+			StatsCollector.getInstance().hitCounter("render", "fail");
+			StatsCollector.getInstance().hitCounter("render", "type", type, "fail");
 
 			throw new GenerationException(ExceptionType.FAILED, e);
 		}

+ 2 - 4
src/main/java/org/barcodeapi/server/session/CachedSession.java

@@ -53,15 +53,13 @@ public class CachedSession extends CachedObject {
 				" Key: " + getKey() + "\n" + //
 				" Created: " + getTimeCreated() + "\n" + //
 				" Last Seen: " + getTimeLastSeen() + "\n" + //
+				" Expiration: " + getTimeExpires() + "\n" + //
 				" Request Count: " + requestCount + "\n" + //
 				"\n  --\n\n" + requests;
 	}
 
 	public Cookie getCookie() {
 
-		Cookie cookie = new Cookie("session", getKey());
-		cookie.setSecure(true);
-
-		return cookie;
+		return new Cookie("session", getKey());
 	}
 }

+ 79 - 32
src/main/java/org/barcodeapi/server/statistics/StatsCollector.java

@@ -1,65 +1,113 @@
 package org.barcodeapi.server.statistics;
 
-import java.util.Map;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-
+import org.barcodeapi.core.ServerRuntime;
 import org.json.JSONObject;;
 
 public class StatsCollector {
 
-	private static final String _RUNTIME_ID = UUID.randomUUID().toString();
-
 	private static StatsCollector statsCollector;
 
-	private ConcurrentHashMap<String, Double> hitCounters;
+	private final JSONObject counterCache = new JSONObject();
 
 	public StatsCollector() {
 
-		hitCounters = new ConcurrentHashMap<String, Double>();
+		counterCache.put("runtimeId", ServerRuntime.getRuntimeID());
 	}
 
-	public void incrementCounter(String counter) {
-
-		incrementCounter(counter, 1d);
+	/**
+	 * Increment the value of a single counter by one.
+	 * 
+	 * @param name
+	 */
+	public void hitCounter(String... name) {
+		hitCounter(1, name);
 	}
 
-	public void incrementCounter(String counter, Double inc) {
+	/**
+	 * Increment the value of a single counter by the specified value.
+	 * 
+	 * @param name
+	 */
+	public void hitCounter(double count, String... name) {
 
-		Double value = hitCounters.get(counter);
-		if (value == null) {
+		synchronized (counterCache) {
 
-			value = 0d;
-		}
+			JSONObject outer = counterCache;
+			for (int x = 0; x < name.length - 1; x++) {
 
-		hitCounters.put(counter, value + inc);
-	}
+				JSONObject inner = outer.optJSONObject(name[x]);
+				if (inner == null) {
 
-	public double getCounter(String counter) {
+					inner = new JSONObject();
+					outer.put(name[x], inner);
+				}
+				outer = inner;
+			}
 
-		return hitCounters.get(counter);
+			String key = name[name.length - 1];
+			double current = outer.optDouble(key, 0);
+			outer.put(key, current + count);
+		}
 	}
 
-	public void setCounter(String counter, Double value) {
+	/**
+	 * Get a single counter by name.
+	 * 
+	 * @param name
+	 * @return
+	 */
+	public double getCounter(String... name) {
 
-		hitCounters.put(counter, value);
-	}
+		JSONObject outer = counterCache;
+		for (String key : name) {
 
-	public ConcurrentHashMap<String, Double> getCounters() {
+			JSONObject inner = outer.optJSONObject(key);
+			if (inner == null) {
 
-		return hitCounters;
+				inner = new JSONObject();
+				outer.put(key, inner);
+			}
+			outer = inner;
+		}
+
+		return outer.getDouble("value");
 	}
 
-	public JSONObject dumpJSON() {
+	/**
+	 * Set the value for a counter.
+	 * 
+	 * @param name
+	 * @param value
+	 */
+	public void setCounter(double value, String... name) {
+
+		synchronized (counterCache) {
+
+			JSONObject outer = counterCache;
+			for (int x = 0; x < name.length - 1; x++) {
+
+				JSONObject inner = outer.optJSONObject(name[x]);
+				if (inner == null) {
 
-		JSONObject output = new JSONObject()//
-				.put("runtimeId", _RUNTIME_ID);
+					inner = new JSONObject();
+					outer.put(name[x], inner);
+				}
+				outer = inner;
+			}
 
-		for (Map.Entry<String, Double> entry : hitCounters.entrySet()) {
-			output.put(entry.getKey(), entry.getValue());
+			String key = name[name.length - 1];
+			outer.put(key, value);
 		}
+	}
+
+	/**
+	 * Get the details of the counter cache.
+	 * 
+	 * @return
+	 */
+	public JSONObject getDetails() {
 
-		return output;
+		return counterCache;
 	}
 
 	public static synchronized StatsCollector getInstance() {
@@ -70,5 +118,4 @@ public class StatsCollector {
 		}
 		return statsCollector;
 	}
-
 }

+ 1 - 1
src/main/java/org/barcodeapi/server/tasks/StatsDumpTask.java

@@ -24,7 +24,7 @@ public class StatsDumpTask extends BackgroundTask {
 	public void onRun() {
 
 		// get and print metric data
-		String data = getStats().dumpJSON().toString();
+		String data = getStats().getDetails().toString();
 		Log.out(LOG.SERVER, "STATS : " + data);
 
 		// upload metrics if enabled

+ 47 - 9
src/main/java/org/barcodeapi/server/tasks/WatchdogTask.java

@@ -1,29 +1,67 @@
 package org.barcodeapi.server.tasks;
 
+import java.lang.Thread.State;
+import java.lang.management.GarbageCollectorMXBean;
+import java.lang.management.ManagementFactory;
+import java.util.HashMap;
+import java.util.Set;
+
+import org.barcodeapi.core.ServerRuntime;
 import org.barcodeapi.server.core.BackgroundTask;
 
 public class WatchdogTask extends BackgroundTask {
 
 	private final Runtime runtime;
-	private final long timeStart;
 
 	public WatchdogTask() {
 		super();
 
 		this.runtime = Runtime.getRuntime();
-		this.timeStart = System.currentTimeMillis();
 	}
 
 	@Override
 	public void onRun() {
 
-		// update uptime metric
-		getStats().setCounter("system.uptime", //
-				(double) (System.currentTimeMillis() - timeStart));
+		// update system runtime statistics
+		getStats().setCounter(System.currentTimeMillis(), "system", "time");
+		getStats().setCounter(ServerRuntime.getTimeRunning(), "system", "uptime");
+
+		// update jvm memory statistics
+		double memUsed = (double) (runtime.totalMemory() - runtime.freeMemory());
+		getStats().setCounter(memUsed, "system", "jvm", "memory", "used");
+
+		double memFree = (double) (runtime.freeMemory());
+		getStats().setCounter(memFree, "system", "jvm", "memory", "free");
+
+		double memTotal = (double) (runtime.totalMemory());
+		getStats().setCounter(memTotal, "system", "jvm", "memory", "total");
+
+		double memMax = (double) (runtime.maxMemory());
+		getStats().setCounter(memMax, "system", "jvm", "memory", "max");
+
+		// update jvm gc statistics
+		for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) {
+
+			String name = gc.getName().replace(" ", "_");
+			getStats().setCounter(gc.getCollectionCount(), "system", "jvm", "gc", name, "count");
+			getStats().setCounter(gc.getCollectionTime(), "system", "jvm", "gc", name, "time");
+		}
+
+		// update jvm thread statistics
+		Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
+		Thread[] threads = threadSet.toArray(new Thread[threadSet.size()]);
+		getStats().setCounter(threads.length, "system", "jvm", "threads", "count");
+
+		// get state of each thread
+		HashMap<State, Integer> threadStates = new HashMap<>();
+		for (Thread t : threads) {
+			int cur = threadStates.getOrDefault(t.getState(), 0);
+			threadStates.put(t.getState(), cur + 1);
+		}
 
-		// update jvm memory stats
-		getStats().setCounter("jvm.memory.used", (double) (runtime.totalMemory() - runtime.freeMemory()));
-		getStats().setCounter("jvm.memory.free", (double) (runtime.freeMemory()));
-		getStats().setCounter("jvm.memory.total", (double) (runtime.totalMemory()));
+		// update thread state counters
+		for (State state : threadStates.keySet()) {
+			getStats().setCounter(threadStates.get(state), "system", "jvm", "threads", "state", state.toString());
+		}
 	}
 }