Ultrakompakter HTTP Server
ulrich
2024-12-05 7189ff3d683f97870fc11ec4e14de3f23abe3c4b
src/de/uhilger/neon/Handler.java
@@ -22,6 +22,7 @@
import de.uhilger.neon.Action.Type;
import de.uhilger.neon.entity.ActionDescriptor;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
@@ -30,13 +31,15 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
 * 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 Actor enthalten.
 * Deren mit Action 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
 * Wird ein Neon-Server mit der Klasse Factory erzeugt, kann mit der Verwendung dieses Handlers
 * die Factory den Server selbsttaetig erstellen, ohne zusaetzlichen Boilerplate Code, den eine
 * eigene Anwendung mitbringen muesste.
 *
 * @author Ulrich Hilger
@@ -91,7 +94,17 @@
    //Logger.getLogger(Handler.class.getName())
    //        .log(Level.INFO, "{0} {1} {2}", new Object[]{methodType, route, className});
    dispatcher.get(methodType).put(ad.route, ad);
    //dispatcher.get(methodType).put(ad.route, ad);
    Object adMapObj = dispatcher.get(methodType);
    if(adMapObj instanceof HashMap hashMap) {
      @SuppressWarnings("unchecked")
      HashMap<String, ActionDescriptor> map = hashMap;
      map.put(ad.route, ad);
      Logger.getLogger(Handler.class.getName()).log(Level.FINER, "ActionDescriptor route {0} className {1}", new Object[]{route, className});
    } else {
      Logger.getLogger(Handler.class.getName()).finer("ActionDescriptorMap nicht gefunden");
    }
  }
  /**
@@ -135,8 +148,9 @@
                    .getHttpContext()
                    .getPath()
                    .length());            
    Type requestMethod = Type.valueOf(exchange.getRequestMethod());
    String requestMethodStr = exchange.getRequestMethod();
    Logger.getLogger(Handler.class.getName()).log(Level.FINER, "method {0} route {1}", new Object[]{requestMethodStr, route});
    Type requestMethod = Type.valueOf(requestMethodStr);
    /*
      Es wird erst geprueft, ob zu einer bestimmten Route 
      ein Actor registriert wurde. Wenn kein Actor mit dieser 
@@ -147,32 +161,42 @@
    Object md = dispatcher.get(requestMethod);
    if (md instanceof Map) {
      int pos = route.lastIndexOf("/");
      Logger.getLogger(Handler.class.getName()).log(Level.FINER, "pos {0}", pos);
      Object o = ((Map) md).get(route);
      if (!(o instanceof ActionDescriptor)) {
        while (!found && (pos > -1)) {
          String routeRest = route.substring(0, pos);
          Logger.getLogger(Handler.class.getName()).log(Level.FINER, "pos {0} routeRest {1}", new Object[]{pos, routeRest});
          o = ((Map) md).get(routeRest);
          if (o instanceof ActionDescriptor) {
            found = true;
            handleRequest(exchange, o, routeRest, route.substring(routeRest.length()));
            handleRequest(exchange, o, routeRest, route.substring(routeRest.length()), requestMethod);
          }
          pos = routeRest.lastIndexOf("/");
        }
      } else {
        found = true;
        handleRequest(exchange, o, route, route);
        handleRequest(exchange, o, route, route, requestMethod);
      }
      if (!found) {
        Logger.getLogger(Handler.class.getName()).log(Level.FINER, "{0} not found ", route);
        o = dispatcher.get(requestMethod).get("/");
        if (o instanceof ActionDescriptor) {
          handleRequest(exchange, o, route, route);
          handleRequest(exchange, o, route, route, requestMethod);
        } else {
          // kein ActionDescriptor für '/'
          Logger.getLogger(Handler.class.getName()).log(Level.FINER, "Kein Actiondescriptor fuer '/'");
        }
      }
    } else {
      // keine Actions fuer HTTP Methode
      Logger.getLogger(Handler.class.getName()).log(Level.FINER, "Kein Actions fuer HTTP-Methode {0}", requestMethodStr);
    }
  }
  private void handleRequest(HttpExchange exchange, Object o, String route, String subroute) throws IOException {
  private void handleRequest(HttpExchange exchange, Object o, String route, String subroute, Type requestMethod) throws IOException {
    Logger.getLogger(Handler.class.getName()).log(Level.FINER, "Handle Request route {0} subroute {1}", new Object[]{route, subroute});
    ActionDescriptor ad = (ActionDescriptor) o;
    String actorClassName = ad.className;
    try {
@@ -181,13 +205,22 @@
      for (Method method : methods) {
        Action action = method.getAnnotation(Action.class);
        if (action != null) {
          if (action.route().equals("/") || action.route().startsWith(route)) {
          if ((action.route().equals("/") || action.route().startsWith(route)) && action.type().equals(requestMethod)) {
            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);
            @SuppressWarnings("unchecked")
            Object conObj = actorClass.getDeclaredConstructor();
            if(conObj instanceof Constructor) {
              Constructor con = (Constructor) conObj;
              Object actorObj;
              actorObj = con.newInstance();
              addDataProvider(exchange, actorObj);
              Object antwort = method.invoke(actorObj, actionArgs);
              if (!action.handlesResponse()) {
                respond(exchange, antwort);
              }
            } else {
              // kein Konstruktor
              Logger.getLogger(Handler.class.getName()).info("Kein Konstruktor gefunden");
            }
          }
        }
@@ -196,6 +229,7 @@
            InstantiationException | IllegalAccessException | IllegalArgumentException | 
            InvocationTargetException ex) {
      // Klasse nicht gefunden. Muss das geloggt oder sonstwie behandel werden?
      Logger.getLogger(Handler.class.getName()).finer("Kein passende Actor-Klasse gefunden");
    }
    //}
  }
@@ -215,8 +249,8 @@
      
      Wenn einer dieser beiden Faelle eintritt, wird alles als Parameter an die Methode 
      uebergeben, was eventuell als Teil einer Query im URL oder im Body enthalten ist.
      Ein Actor kann dann den Body nicht mehr lesen, weil das bereits an dieser Stelle
      gemacht wurde.
      Fuer Mthoden, die nicht vom Typ HTTP GET sind, kann ein Actor kann dann den Body
      nicht mehr lesen, weil das bereits an dieser Stelle gemacht wurde.
    */
    Map queryParams = new HashMap();
    if ((count > 1 && count > routeParams.length)