From cc007e5339f7ffc35cdd9b94ce3b712596a1494e Mon Sep 17 00:00:00 2001
From: ulrich
Date: Tue, 03 Dec 2024 16:20:46 +0000
Subject: [PATCH] Scan nach Actor-Klassen aus der Initialisierung der Kontexte herausgeloest und zu 'runInstance' verlagert. Hilfstabelle fuer Actors waehrend der Initialisierung eingefuehrt.

---
 src/de/uhilger/neon/Handler.java   |    8 +-
 src/de/uhilger/neon/Factory.java   |  112 +++++++++++++++++++++----------------
 src/de/uhilger/neon/TempActor.java |   51 +++++++++++++++++
 3 files changed, 119 insertions(+), 52 deletions(-)

diff --git a/src/de/uhilger/neon/Factory.java b/src/de/uhilger/neon/Factory.java
index 3748d5b..9f9ef19 100644
--- a/src/de/uhilger/neon/Factory.java
+++ b/src/de/uhilger/neon/Factory.java
@@ -66,9 +66,12 @@
  * @version 1, 6.2.2024
  */
 public class Factory implements ScannerListener {
+  
+  private Map<String, List<TempActor>> actorMap;
 
   public Factory() {
     listeners = new ArrayList<>();
+    actorMap = new HashMap<>();
   }
 
   /**
@@ -129,17 +132,22 @@
 
     Logger.getLogger(Factory.class.getName()).log(Level.FINER, System.getProperty("java.class.path"));
 
-    List serverList = d.server;
-    Iterator<ServerDescriptor> serverIterator = serverList.iterator();
-    while (serverIterator.hasNext()) {
-      ServerDescriptor sd = serverIterator.next();
+    List<ServerDescriptor> serverList = d.server;
+    for (ServerDescriptor sd : serverList) {
       HttpServer server = HttpServer.create(new InetSocketAddress(sd.port), 0);
       fireServerCreated(server);
 
       if (packageNames == null) {
         packageNames = d.actorPackages;
       }      
-      addContexts(new Scanner(starter, Actor.class), d, server, sd.contexts, packageNames, sdp);
+      
+      Scanner scn = new Scanner(starter, Actor.class);
+      for (String packageName : packageNames) {
+        scn.process(this, packageName, new Object[]{});
+        // ctx.getAttributes().put("serverDataProviderList", sdp);
+      }
+      
+      addContexts(d, server, sd.contexts, sdp);
 
       server.setExecutor(Executors.newFixedThreadPool(10));
       server.start();
@@ -167,11 +175,11 @@
     return auth;
   }
 
-  private void addContexts(Scanner scn, NeonDescriptor d, HttpServer server, List contextList, List<String> packageNames,
+  private void addContexts(NeonDescriptor d, HttpServer server, List<ContextDescriptor> contextList,
           List<DataProvider> sdp)
           throws ClassNotFoundException, NoSuchMethodException, InstantiationException,
           IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException {
-    Map<String, HttpHandler> sharedHandlers = new HashMap();
+    Map<String, HttpHandler> sharedHandlers = new HashMap<>();
     Iterator<ContextDescriptor> contextIterator = contextList.iterator();
     Authenticator auth = null;
     while (contextIterator.hasNext()) {
@@ -182,14 +190,12 @@
         Map<String, Object> ctxAttrs = ctx.getAttributes();
         /*
           Achtung: Wenn verschiedene Elemente dasselbe Attribut 
-          deklarieren, ueberschreiben sie sich die Attribute gegenseitig.
+          deklarieren, ueberschreiben sich die Attribute gegenseitig.
          */
         ctxAttrs.putAll(cd.attributes);
-        if (h instanceof Handler) {
-          for (String packageName : packageNames) {
-            scn.process(this, packageName, (Handler) h, cd.attributes.get("contextName"));
-            ctx.getAttributes().put("serverDataProviderList", sdp);
-          }
+        ctxAttrs.put("serverDataProviderList", sdp);
+        if (h instanceof Handler handler) {
+          wire(handler, cd.attributes.get("contextName"));
         }
         if (cd.authenticator instanceof String) {
           if (!(auth instanceof Authenticator)) {
@@ -200,22 +206,13 @@
             ctx.getAttributes().putAll(d.authenticator.attributes);
             fireAuthenticatorCreated(ctx, auth); // event umbenennen in etwas wie authAdded oder so
           }
-
         }
-
-        //Authenticator auth = createAuthenticator(d);
-        //if (auth instanceof Authenticator && cd.authenticator instanceof String) {
-        //    ctx.setAuthenticator(auth);      
-        //    ctx.getAttributes().putAll(d.authenticator.attributes);
-        //    fireAuthenticatorCreated(ctx, auth);
-        //}
         if (cd.filter != null) {
           for (String filterClassName : cd.filter) {
             //
             Object filterObj = Class.forName(filterClassName)
                     .getDeclaredConstructor().newInstance();
-            if (filterObj instanceof Filter) {
-              Filter filter = (Filter) filterObj;
+            if (filterObj instanceof Filter filter) {
               ctx.getFilters().add(filter);
             }
           }
@@ -330,22 +327,32 @@
     Kontextpfades ausgefuehrt werden soll, muss die Action 
     als Route '/' angeben.
    */
+  
+  /*
+   Tradeoff:
+      Es muss bei Initialisierung die Actor-Klasse ganz durchlaufen werden, um alle Methoden 
+      zu finden, die eine Action-Annotation haben. Der Handler 'merkt' sich lediglich den Namen der 
+      Actor-Klassen. Daher muessen bei jedem Aufruf eines Actors ueber den Handler abermals 
+      alle Methoden dieses Actors durchsucht werden, um diejenige Methode zu finden, die mit 
+      der zur Route des Requests passenden Action annotiert ist.
+  
+      Dieser Tradeoff bewirkt, dass nicht grosse Geflechte aus Klassen- und Methodenobjekten
+      fuer die gesamten Actors einer Anwendung im Speicher gehalten werden muessen 
+      sondern dynamisch zur Laufzeit instanziiert werden.
+  */
+ 
   /**
-   *
+   * Actor-Klassen dem Handler hinzufuegen
+   * 
    * @param h Handler, dem der Actor hinzugefuegt wird, falls der Kontext uebereinstimmt
-   * @param c hinzuzufuegende Actor-Klasse
-   * @param contextName Name des Kontext, dem der Actor hinzugefuegt wird
+   * @param contextName Name des Kontext, dem der Handler zugeordnet ist
    */
-  private void wire(Handler h, Class c, String contextName) {
-    Method[] methods = c.getMethods();
-    for (Method method : methods) {
-      Action action = method.getAnnotation(Action.class);
-      if (action != null) {
-        List actionHandlers = Arrays.asList(action.handler());
-        if (actionHandlers.contains(contextName)) {
-          h.setActor(action.type(), action.route(), c.getName());
-        }
-      }
+  private void wire(Handler h, String contextName) {
+    List<TempActor> actorList = actorMap.get(contextName);
+    Iterator<TempActor> i = actorList.iterator();
+    while(i.hasNext()) {
+      TempActor actor = i.next();
+      h.setActor(actor.getHttpMethod(), actor.getRoute(), actor.getActorClassName());
     }
   }
 
@@ -363,6 +370,8 @@
   public void destroy() {
     this.listeners.clear();
     this.listeners = null;
+    this.actorMap.clear();
+    this.actorMap = null;
   }
 
   private void fireServerCreated(HttpServer server) {
@@ -398,19 +407,26 @@
   /* -------------- ScannerListener Implementierung --------------- */
   @Override
   public void annotationFound(Class foundClass, Object[] params) {
-    Handler h = null;
-    String contextName = null;
-    for(Object param : params) {
-      if(param instanceof Handler) {
-        h = (Handler) param;
-      } else if(param instanceof String) {
-        contextName = (String) param;
+    Method[] methods = foundClass.getMethods();
+    for (Method method : methods) {
+      Action action = method.getAnnotation(Action.class);
+      if (action != null) {
+        List<String> actionHandlers = Arrays.asList(action.handler());
+        for (String contextName : actionHandlers) {
+           TempActor tempActor = new TempActor();
+           tempActor.setContextName(contextName);
+           tempActor.setHttpMethod(action.type());
+           tempActor.setRoute(action.route());
+           tempActor.setActorClassName(foundClass.getName());
+           
+           List<TempActor> actorList = actorMap.get(contextName);
+           if(actorList == null) {
+             actorList = new ArrayList<>();
+           }
+           actorList.add(tempActor);
+           actorMap.put(contextName, actorList);
+        }
       }
-    }
-    if(h == null || contextName == null) {
-      Logger.getLogger(Factory.class.getName()).log(Level.FINER, "Handler oder contextName ist null");
-    } else {
-      wire(h, foundClass, contextName);
     }
   }
 }
\ No newline at end of file
diff --git a/src/de/uhilger/neon/Handler.java b/src/de/uhilger/neon/Handler.java
index db7d025..bbd5e78 100644
--- a/src/de/uhilger/neon/Handler.java
+++ b/src/de/uhilger/neon/Handler.java
@@ -35,11 +35,11 @@
 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
diff --git a/src/de/uhilger/neon/TempActor.java b/src/de/uhilger/neon/TempActor.java
new file mode 100644
index 0000000..6f78624
--- /dev/null
+++ b/src/de/uhilger/neon/TempActor.java
@@ -0,0 +1,51 @@
+package de.uhilger.neon;
+
+import de.uhilger.neon.Action.Type;
+
+/**
+ *
+ * @author Ulrich Hilger
+ * @version 0.1, 03.12.2024
+ */
+public class TempActor {
+
+  private String contextName;
+  private Type httpMethod;
+  private String route;
+  private String actorClassName;
+
+  public String getContextName() {
+    return contextName;
+  }
+
+  public void setContextName(String contextName) {
+    this.contextName = contextName;
+  }
+
+  public Type getHttpMethod() {
+    return httpMethod;
+  }
+
+  public void setHttpMethod(Type httpMethod) {
+    this.httpMethod = httpMethod;
+  }
+
+  public String getRoute() {
+    return route;
+  }
+
+  public void setRoute(String route) {
+    this.route = route;
+  }
+
+  public String getActorClassName() {
+    return actorClassName;
+  }
+
+  public void setActorClassName(String actorClassName) {
+    this.actorClassName = actorClassName;
+  }
+  
+  
+  
+}

--
Gitblit v1.9.3