From 12fdfa4c6bd2515e142a996673489cfdd656cb0a Mon Sep 17 00:00:00 2001
From: ulrich
Date: Sun, 01 Dec 2024 17:17:55 +0000
Subject: [PATCH] Kommantare ergaenzt

---
 src/de/uhilger/neon/Scanner.java |  268 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 268 insertions(+), 0 deletions(-)

diff --git a/src/de/uhilger/neon/Scanner.java b/src/de/uhilger/neon/Scanner.java
new file mode 100644
index 0000000..8efdec0
--- /dev/null
+++ b/src/de/uhilger/neon/Scanner.java
@@ -0,0 +1,268 @@
+/*
+  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;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * Die Klasse Scanner enthaelt Methoden, um fuer eine Klasse zu bestimmen, an welchem Ablageort sie
+ * sich befindet und diesen Ort nach Klassen zu durchsuchen, die eine gegebene Annotation besitzen.
+ *
+ * Der Ort fur Klassen kann ein Java-Archiv (.jar) oder ein Ordner im Dateisystem sein.
+ *
+ * @author Ulrich Hilger
+ * @version 0.1, 30.11.2024
+ */
+public final class Scanner {
+
+  private final URI path;
+  private final Class annotation;
+
+  private final Class cls;
+  private final ClassLoader urlCL;
+
+  /**
+   * Einen Scanner erzeugen, der den Ort, in dem sich eine gegebene Klasse befindet, nach Klassen
+   * durchsucht, die eine bestimmte Annotation besitzen
+   *
+   * Der Ort fur Klassen kann ein Java-Archiv (.jar) oder ein Ordner im Dateisystem sein.
+   *
+   * @param c eine Klasse die sich im Archiv befindet, das durchsucht werden soll
+   * @param annotation die Annotation, nach der gesucht wird
+   */
+  public Scanner(Class c, Class annotation) {
+    this.annotation = annotation;
+    this.cls = c;
+    this.urlCL = getUrlClassLoader(cls);
+    this.path = getPath(c);
+  }
+
+  public Class getAnnotation() {
+    return annotation;
+  }
+
+  /**
+   * Klassen suchen, die die dem Konstruktor gegebene Annotation besitzen.
+   *
+   * Anhand der im Konstruktor uebergebenen Klasse wird deren Ablageort ermittelt, entweder ein
+   * Ordner im Dateisystem oder ein Java-Archiv (.jar). Dieser Ablageort wird dann nach annotierten
+   * Klassen durchsucht. Gefundene Klassen werden dem Listener gemeldet.
+   *
+   * @param packageName Name der Package, die einschl. Unterpackages durchsucht wird, nur Klassen
+   * dieser Package und ihrer Unterpackages werden geladen und auf die Anotation ueberprueft
+   * @param l ein Objekt, das verstaendigt wird, wenn eine annotierte Klasse gefunden wurde
+   * @param h der Handler, dem die gefundene Klasse hinzugefuegt werden soll
+   * @param contextName Name des Kontext, dem gefundene Klassen hinzugefuegt werden sollen
+   */
+  public void process(ScannerListener l, String packageName, Handler h, String contextName) {
+    if (isJar()) {
+      processZipContent(packageName, l, h, contextName);
+    } else {
+      processClasses(l, packageName, h, contextName);
+    }
+  }
+
+  /**
+   * Den Inhalt einer Jar-Datei nach Klassen durchsuchen, die die dem Konstruktor gegebene
+   * Annotation besitzen.
+   *
+   * @param packageName Name der Package, die einschl. Unterpackages durchsucht wird, nur Klassen
+   * dieser Package und ihrer Unterpackages werden geladen und auf die Anotation ueberprueft
+   * @param l ein Objekt, das verstaendigt wird, wenn eine annotierte Klasse gefunden wurde
+   * @param h der Handler, dem die gefundene Klasse hinzugefuegt werden soll
+   * @param contextName Name des Kontext, dem gefundene Klassen hinzugefuegt werden sollen
+   */
+  private void processZipContent(String packageName, ScannerListener l, Handler h, String contextName) {
+    try {
+      ZipFile zipfile = new ZipFile(new File(path));
+      Enumeration en = zipfile.entries();
+      //ClassLoader cl = getUrlClassLoader(cls);
+      while (en.hasMoreElements()) {
+        ZipEntry zipentry = (ZipEntry) en.nextElement();
+        if (!zipentry.isDirectory()) {
+          processZipEntry(zipentry, packageName, l, h, contextName);
+        } else {
+          // ZIP-Dir muss nicht bearbeitet werden
+        }
+      }
+    } catch (IOException ex) {
+      log(Level.SEVERE, ex.getLocalizedMessage());
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  private void processZipEntry(ZipEntry zipentry, String packageName, ScannerListener l, Handler h, String contextName) {
+    finest(zipentry.getName());
+    String zName = zipentry.getName();
+    if (zName.toLowerCase().endsWith(".class")) {
+      int pos = zName.indexOf(".class");
+      String fullClassName = zName.substring(0, pos);
+      finest("full class name: " + zName);
+      String fullClassNameDots = fullClassName.replace('/', '.');
+      finest("full class name dots: " + fullClassNameDots);
+      String pkgName = getPackageName(fullClassNameDots);
+      finest(" -- package name: " + pkgName);
+      if (null != urlCL && pkgName.toLowerCase().startsWith(packageName)) {
+        try {
+          Class c = urlCL.loadClass(fullClassNameDots);
+          if (c != null) {
+            if (c.isAnnotationPresent(annotation)) {
+              finest(" ---- ACTOR ---- " + fullClassNameDots);
+              l.annotationFound(c, h, contextName);
+            } else {
+              finest("kein Actor " + fullClassNameDots);
+            }
+          } else {
+            finest("class NOT loaded: " + zName);
+          }
+        } catch (ClassNotFoundException ex) {
+          finest(" +++++ Class not found: " + ex.getMessage());
+        }
+      }
+    }
+  }
+
+  /**
+   * Einen Ordner mit Klassen durchsuchen
+   *
+   * @param packageName Name der Package, die einschl. Unterpackages durchsucht wird, nur Klassen
+   * dieser Package und ihrer Unterpackages werden geladen und auf die Anotation ueberprueft
+   * @param l ein Objekt, das verstaendigt wird, wenn eine annotierte Klasse gefunden wurde
+   * @param h der Handler, dem die gefundene Klasse hinzugefuegt werden soll
+   * @param contextName Name des Kontext, dem gefundene Klassen hinzugefuegt werden sollen
+   */
+  @SuppressWarnings("unchecked")
+  private void processClasses(ScannerListener l, String packageName, Handler h, String contextName) {
+    ClassLoader cl = getCl();
+    InputStream stream = cl.getResourceAsStream(packageName.replaceAll("[.]", "/"));
+    BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
+    Iterator i = reader.lines().iterator();
+    while (i.hasNext()) {
+      String line = i.next().toString();
+      if (line.endsWith(".class")) {
+        try {
+          Class actorClass = cl.loadClass(packageName + "."
+                  + line.substring(0, line.lastIndexOf('.')));
+          if (actorClass != null && actorClass.isAnnotationPresent(getAnnotation())) {
+            //wire(h, actorClass, contextName);
+            l.annotationFound(actorClass, h, contextName);
+          }
+        } catch (ClassNotFoundException ex) {
+          // Klasse nicht gefunden. Muss das geloggt oder sonstwie behandel werden?
+        }
+      } else {
+        //wireActors(js, packageName + "." + line, h, contextName);
+        processClasses(l, packageName + "." + line, h, contextName);
+      }
+    }
+  }
+
+  private String getPackageName(String fullClassName) {
+    String packageName;
+    int pos = fullClassName.lastIndexOf(".");
+    if (pos > 0) {
+      packageName = fullClassName.substring(0, pos);
+    } else {
+      packageName = fullClassName;
+    }
+    return packageName;
+  }
+
+  public ClassLoader getCl() {
+    return cls.getClassLoader();
+  }
+
+  public ClassLoader getUrlClassLoader(Class c) {
+    ClassLoader cl = null;
+    try {
+      URL url = getPath(c).toURL();
+      finer("url: " + url.getPath());
+      cl = new URLClassLoader(new URL[]{url});
+    } catch (MalformedURLException ex) {
+      log(Level.SEVERE, ex.getMessage());
+    } finally {
+      return cl;
+    }
+  }
+
+  public String getPathStr() {
+    if (path != null) {
+      return path.toString();
+    } else {
+      return "";
+    }
+  }
+
+  public boolean isJar() {
+    return !getPathStr().toLowerCase().endsWith(".class");
+  }
+
+  private URI getPath(Class c) {
+    String className = c.getName();
+    finest("this name: " + className);
+    String classNameWoPkg = c.getSimpleName();//className.substring(className.lastIndexOf(".") + 1);
+    finest("Class name: " + classNameWoPkg);
+    String classPath = c.getResource(classNameWoPkg + ".class").getPath();
+    int pos = classPath.indexOf("!");
+    String jarPath;
+    if (pos > -1) {
+      jarPath = /*"jar:" + */ classPath.substring(0, pos);
+    } else {
+      jarPath = classPath;
+    }
+    finest("path: " + jarPath);
+    try {
+      return new URI(jarPath);
+    } catch (URISyntaxException ex) {
+      Logger.getLogger(Scanner.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
+      return null;
+    }
+  }
+
+  private void finest(String msg) {
+    log(Level.FINEST, msg);
+  }
+
+  private void finer(String msg) {
+    log(Level.FINER, msg);
+  }
+
+  private void log(Level l, String msg) {
+    Logger.getLogger(Scanner.class.getName()).log(l, msg);
+  }
+
+  public interface ScannerListener {
+
+    public void annotationFound(Class foundClass, Handler h, String contextName);
+  }
+}
\ No newline at end of file

--
Gitblit v1.9.3