From 5ebab9058498312ad234226040f0ec506509e476 Mon Sep 17 00:00:00 2001
From: ulrich
Date: Wed, 21 Feb 2024 13:17:06 +0000
Subject: [PATCH] Verarbeitung von Routen um variable Elemente erweitert (einstweilen noch experimentell)
---
src/de/uhilger/neon/Handler.java | 154 +++++++++++++++++++++++--------------
src/de/uhilger/neon/entity/ActionDescriptor.java | 30 +++++++
2 files changed, 125 insertions(+), 59 deletions(-)
diff --git a/src/de/uhilger/neon/Handler.java b/src/de/uhilger/neon/Handler.java
index 44c3b0d..6caa331 100644
--- a/src/de/uhilger/neon/Handler.java
+++ b/src/de/uhilger/neon/Handler.java
@@ -20,6 +20,7 @@
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import de.uhilger.neon.Action.Type;
+import de.uhilger.neon.entity.ActionDescriptor;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -31,13 +32,12 @@
import java.util.Map;
/**
- * Objekte der Klasse Handler nehmen Objekte entgegen die die Annotationen
- * NeonActor enthalten. Deren mit NeonMethod annotierten Methoden stellt
- * der Handler via HTTP bereit.
+ * Objekte der Klasse Handler nehmen Objekte entgegen die die Annotationen NeonActor enthalten.
+ * Deren mit NeonMethod annotierten Methoden stellt der Handler via HTTP bereit.
*
- * Wird ein Neon-Server mit der Klasse NeonFactory erzeugt, kann mit der Verwendung
- * dieses Handlers die NeonFactory den Server selbsttaetig erstellen, ohne
- * zusaetzlichen Boilerplate Code, den eine eigene Anwendung mitbringen muesste.
+ * Wird ein Neon-Server mit der Klasse NeonFactory erzeugt, kann mit der Verwendung dieses Handlers
+ * die NeonFactory den Server selbsttaetig erstellen, ohne zusaetzlichen Boilerplate Code, den eine
+ * eigene Anwendung mitbringen muesste.
*
* @author Ulrich Hilger
* @version 1, 6.2.2024
@@ -51,10 +51,14 @@
*/
public Handler() {
dispatcher = new EnumMap<>(Type.class);
- dispatcher.put(Type.GET, new HashMap<String, String>());
- dispatcher.put(Type.PUT, new HashMap<String, String>());
- dispatcher.put(Type.POST, new HashMap<String, String>());
- dispatcher.put(Type.DELETE, new HashMap<String, String>());
+ //dispatcher.put(Type.GET, new HashMap<String, String>());
+ //dispatcher.put(Type.PUT, new HashMap<String, String>());
+ //dispatcher.put(Type.POST, new HashMap<String, String>());
+ //dispatcher.put(Type.DELETE, new HashMap<String, String>());
+ dispatcher.put(Type.GET, new HashMap<String, ActionDescriptor>());
+ dispatcher.put(Type.PUT, new HashMap<String, ActionDescriptor>());
+ dispatcher.put(Type.POST, new HashMap<String, ActionDescriptor>());
+ dispatcher.put(Type.DELETE, new HashMap<String, ActionDescriptor>());
}
/**
@@ -66,9 +70,28 @@
* ausgefuehrt werden soll
*/
public void setActor(Type methodType, String route, String className) {
+ ActionDescriptor ad = new ActionDescriptor();
+ ad.className = className;
+ ad.routeParams = new HashMap<>();
+ int pos = route.indexOf("{");
+ if (pos > -1) {
+ String paramStr = route.substring(pos);
+ String[] params = paramStr
+ .replaceAll("\\{", "")
+ .replaceAll("\\}", "")
+ .split("/");
+ for (int i = 0; i < params.length; i++) {
+ ad.routeParams.put(params[i], i);
+ }
+ ad.route = route.substring(0, pos - 1);
+ } else {
+ // Map kann leer bleiben
+ ad.route = route;
+ }
+
//Logger.getLogger(Handler.class.getName())
// .log(Level.INFO, "{0} {1} {2}", new Object[]{methodType, route, className});
- dispatcher.get(methodType).put(route, className);
+ dispatcher.get(methodType).put(ad.route, ad);
}
/**
@@ -105,69 +128,80 @@
*/
@Override
public void handle(HttpExchange exchange) throws IOException {
- HttpHelper hh = new HttpHelper();
+ HttpHelper hh = new HttpHelper();
String route = hh.getRouteString(exchange);
Type requestMethod = Type.valueOf(exchange.getRequestMethod());
- //Map queryParams = hh.getQueryMap(exchange);
- //Object o;
-
/*
Es wird erst geprueft, ob zu einer bestimmten Route
ein Actor registriert wurde. Wenn kein Actor mit dieser
Route existiert, wird geprueft, ob ein Actor
mit der Route '/' vorhanden ist.
*/
+ boolean found = false;
Object md = dispatcher.get(requestMethod);
- if(md instanceof Map) {
- Object o = ((Map) md).get(route);
- if (!(o instanceof String)) {
- o = dispatcher.get(requestMethod).get("/");
- }
-
- if (o instanceof String) {
- String actorClassName = (String) o;
- try {
- Class actorClass = Class.forName(actorClassName);
- Method[] methods = actorClass.getMethods();
- for (Method method : methods) {
- Action action = method.getAnnotation(Action.class);
- if (action != null) {
- if (action.route().equals("/") || action.route().equals(route)) {
- Object[] actionArgs = getActionArgs(exchange, method/*, queryParams*/);
- Object actorObj = actorClass.getDeclaredConstructor().newInstance();
- addDataProvider(exchange, actorObj);
- Object antwort = method.invoke(actorObj, actionArgs);
- if(!action.handlesResponse()) {
- respond(exchange, antwort);
- }
- }
+ if (md instanceof Map) {
+ int pos = route.lastIndexOf("/");
+ Object o = ((Map) md).get(route);
+ if (!(o instanceof ActionDescriptor)) {
+ while (!found && (pos > -1)) {
+ String routeRest = route.substring(0, pos);
+ o = ((Map) md).get(routeRest);
+ if (o instanceof ActionDescriptor) {
+ found = true;
+ handleRequest(exchange, o, routeRest, route.substring(routeRest.length()));
}
+ pos = routeRest.lastIndexOf("/");
}
- } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
- // Klasse nicht gefunden. Muss das geloggt oder sonstwie behandel werden?
+ } else {
+ found = true;
+ handleRequest(exchange, o, route, route);
}
- }
+ if (!found) {
+ o = dispatcher.get(requestMethod).get("/");
+ if (o instanceof ActionDescriptor) {
+ handleRequest(exchange, o, route, route);
+ }
+ }
}
}
- private Object[] getActionArgs(HttpExchange exchange, Method method/*, Map queryParams*/) {
+ private void handleRequest(HttpExchange exchange, Object o, String route, String subroute) throws IOException {
+ ActionDescriptor ad = (ActionDescriptor) o;
+ String actorClassName = ad.className;
+ try {
+ Class actorClass = Class.forName(actorClassName);
+ Method[] methods = actorClass.getMethods();
+ for (Method method : methods) {
+ Action action = method.getAnnotation(Action.class);
+ if (action != null) {
+ if (action.route().equals("/") || action.route().startsWith(route)) {
+ Object[] actionArgs = getActionArgs(exchange, method, ad, subroute);
+ Object actorObj = actorClass.getDeclaredConstructor().newInstance();
+ addDataProvider(exchange, actorObj);
+ Object antwort = method.invoke(actorObj, actionArgs);
+ if (!action.handlesResponse()) {
+ respond(exchange, antwort);
+ }
+ }
+ }
+ }
+ } catch (ClassNotFoundException | NoSuchMethodException | SecurityException |
+ InstantiationException | IllegalAccessException | IllegalArgumentException |
+ InvocationTargetException ex) {
+ // Klasse nicht gefunden. Muss das geloggt oder sonstwie behandel werden?
+ }
+ //}
+ }
+
+ private Object[] getActionArgs(HttpExchange exchange, Method method, ActionDescriptor ad, String subroute) {
int count = method.getParameterCount();
Parameter[] methodParams = method.getParameters();
Object[] actionArgs = new Object[count];
+ String[] routeParams = subroute.split("/");
Map queryParams = new HashMap();
- /*
- Lesen des Body der Anfrage geht nur einmal.
-
- bodyLesen soll nur in getQueryMap gerufen werden, wenn
- die Liste der Parameter mehr als einen Parameter umfasst
- oder wenn es nur ein Parameter ist, der nicht
- der HttpExchange ist.
-
- Anderenfalls sollte erst der Actor den Body aus dem
- HttpExchange lesen und nicht hier schon der Handler.
- */
- if(count > 1 || !methodParams[0].getType().equals(HttpExchange.class)) {
+ if ((count > 1 && count > routeParams.length)
+ || (methodParams.length > 0 && !methodParams[0].getType().equals(HttpExchange.class))) {
queryParams = new HttpHelper().getQueryMap(exchange);
}
int k = 0;
@@ -175,12 +209,14 @@
if (methodParam.getType().equals(HttpExchange.class)) {
actionArgs[k] = exchange;
} else {
- /*
- Konvention: Aktor-Parameter sind immer vom Typ String
- und Parametername der Methode ist gleich dem Namen in der Query
- */
- actionArgs[k++] = queryParams.get(methodParam.getName());
+ Integer i = ad.routeParams.getOrDefault(methodParam.getName(), -1);
+ if (i < 0) {
+ actionArgs[k] = queryParams.get(methodParam.getName());
+ } else {
+ actionArgs[k] = routeParams[i + 1];
+ }
}
+ ++k;
}
return actionArgs;
}
diff --git a/src/de/uhilger/neon/entity/ActionDescriptor.java b/src/de/uhilger/neon/entity/ActionDescriptor.java
new file mode 100644
index 0000000..997f3c6
--- /dev/null
+++ b/src/de/uhilger/neon/entity/ActionDescriptor.java
@@ -0,0 +1,30 @@
+/*
+ neon - Embeddable HTTP Server based on jdk.httpserver
+ Copyright (C) 2024 Ulrich Hilger
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+package de.uhilger.neon.entity;
+
+import java.util.Map;
+
+/**
+ *
+ * @author Ulrich Hilger
+ */
+public class ActionDescriptor {
+ public String route;
+ public Map<String, Integer> routeParams;
+ public String className;
+}
--
Gitblit v1.9.3