Ultrakompakter HTTP Server
ulrich
2024-12-01 47e67b0aa12758fcbe6eb68f95a35ceb66c268e7
Code aufgeraeumt
2 files modified
155 ■■■■■ changed files
src/de/uhilger/neon/Factory.java 59 ●●●● patch | view | raw | blame | history
src/de/uhilger/neon/JarScanner.java 96 ●●●●● patch | view | raw | blame | history
src/de/uhilger/neon/Factory.java
@@ -106,6 +106,7 @@
          IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException {
    this.runInstance(c, d, packageNames, new ArrayList<>());
  }
  /**
   * Einen Neon-Server gemaess einem Serverbeschreibungsobjekt herstellen und starten
   *
@@ -156,9 +157,9 @@
          auth = (Authenticator) authObj;
          return auth;
        }
      } catch (ClassNotFoundException | NoSuchMethodException | SecurityException |
              InstantiationException | IllegalAccessException | IllegalArgumentException |
              InvocationTargetException ex) {
      } catch (ClassNotFoundException | NoSuchMethodException | SecurityException
              | InstantiationException | IllegalAccessException | IllegalArgumentException
              | InvocationTargetException ex) {
        // Klasse nicht gefunden. Muss das geloggt oder sonstwie behandel werden?
        return null;
      }      
@@ -256,20 +257,18 @@
        // kein HttpHandler aus newInstance
        return null;
      }
    } catch (ClassNotFoundException | NoSuchMethodException | SecurityException |
            InstantiationException | IllegalAccessException | IllegalArgumentException |
            InvocationTargetException ex) {
    } catch (ClassNotFoundException | NoSuchMethodException | SecurityException
            | InstantiationException | IllegalAccessException | IllegalArgumentException
            | InvocationTargetException ex) {
      // Klasse nicht gefunden. Muss das geloggt oder sonstwie behandel werden?
      return null;
    }
  }
  @SuppressWarnings("unchecked")
  private void wireActors(Class c, String packageName, Class annotation, Handler h, String contextName) {
    JarScanner js = new JarScanner();
    URI path;
    try {
      path = js.getPath(c);
      if(path.toString().endsWith(".class")) {
    JarScanner js = new JarScanner(c, annotation);
    if (!js.isJar()) {
        ClassLoader cl = c.getClassLoader();
        InputStream stream = cl
                .getResourceAsStream(packageName.replaceAll("[.]", "/"));
@@ -292,12 +291,7 @@
          }
        }
      } else {
        ClassLoader cl = js.getUrlClassLoader(c);
        js.processZipContent(cl, new File(path), packageName, this, h, contextName);
      }
      //listClasses(c, packageName);
    } catch (URISyntaxException ex) {
      Logger.getLogger(Factory.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
      js.processZipContent(packageName, this, h, contextName);
    }
  }
  
@@ -305,23 +299,21 @@
   * Diese Testmethode zeigt, dass die Methode getResourceAsStream nicht funktioniert wie 
   * dokumentiert. 
   * 
   * 1. Sie liefert den Inhalt einer gegebenen Package mitsamt Unterpackages als
   * Stream, wenn sie auf eine Packagestruktur angewendet wird, die unverpackt in einem
   * Verzeichnis des Dateisystems liegt.
   * 1. Sie liefert den Inhalt einer gegebenen Package mitsamt Unterpackages als Stream, wenn sie
   * auf eine Packagestruktur angewendet wird, die unverpackt in einem Verzeichnis des Dateisystems
   * liegt.
   * 
   * 2. Sie liefert - faelschlicherweise - null bzw. einen leeren Stream, wenn die Packagestruktur 
   * in einem Jar verpackt ist.
   * 
   * Saemtliche Versuche, ueber den ClassPath oder die Pfadangabe der Package das Verhalten zu 
   * aendern, gehen bislang fehl (z.B. / oder . als Separator,
   * / oder . zu Beginn enthalten oder nicht, realative oder absolute packagepfadangabe).
   * Es ist auch unerheblich, ob
   * aendern, gehen bislang fehl (z.B. / oder . als Separator, / oder . zu Beginn enthalten oder
   * nicht, realative oder absolute packagepfadangabe). Es ist auch unerheblich, ob
   * Class.getResourceAsStream oder Class.getClassLoader().getResourceAsStream verwendet wird.
   * 
   * Unabhaengig davon, ob und wie letztlich im Fall 2. oben die Methode getResourceAsStream
   * dazu zu bringen waere, eine Inhaltsliste fuer eine Package zu liefern ist allein die
   * Tatsache, dass sich die Methode unterschiedlich verhaelt bereits ein
   * schwerer Bug.
   * Unabhaengig davon, ob und wie letztlich im Fall 2. oben die Methode getResourceAsStream dazu zu
   * bringen waere, eine Inhaltsliste fuer eine Package zu liefern ist allein die Tatsache, dass
   * sich die Methode unterschiedlich verhaelt bereits ein schwerer Bug.
   * 
   * @param c
   * @param packageName 
@@ -361,7 +353,6 @@
        }
  }
  
  /*
    Eine Action-Annotation enthaelt gewoehnlich die Route, 
    die 'unterhalb' des Kontextpfades als 'Ausloeser' zur 
@@ -370,6 +361,12 @@
    Wenn die Action fuer alle Routen 'unterhalb' des 
    Kontextpfades ausgefuehrt werden soll, muss die Action 
    als Route '/' angeben.
   */
  /**
   *
   * @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
  */
  private void wire(Handler h, Class c, String contextName) {
    Method[] methods = c.getMethods();
@@ -385,7 +382,6 @@
  }
  /* -------------- FactoryListener Implementierung --------------- */
  private List<FactoryListener> listeners;
  public void addListener(FactoryListener l) {
@@ -432,9 +428,8 @@
  }
  /* -------------- JarScannerListener Implementierung --------------- */
  @Override
  public void actorFound(Class actorClass, Handler h, String contextName) {
    wire(h, actorClass, contextName);
  public void annotationFound(Class foundClass, Handler h, String contextName) {
    wire(h, foundClass, contextName);
  }
}
src/de/uhilger/neon/JarScanner.java
@@ -31,34 +31,63 @@
import java.util.zip.ZipFile;
/**
 * Die Klasse JarScanner enthaelt Methoden, um fuer eine Klasse zu bestimmen, in
 * welcher JAR-Datei sie liegt und diese JAR-Datei nach Klassen zu durchsuchen.
 * Die Klasse JarScanner enthaelt Methoden, um fuer eine Klasse zu bestimmen, in welcher JAR-Datei
 * sie liegt und diese JAR-Datei nach Klassen zu durchsuchen.
 * 
 * @author Ulrich Hilger
 * @version 0.1, 30.11.2024
 */
public class JarScanner {
public final class JarScanner {
  
  private final URI path;
  private final Class annotation;
  private final Class cls;
  private final ClassLoader urlCL;
  
  public void processZipContent(ClassLoader urlCL, File archive, String packageName, JarScannerListener l, Handler h, String contextName) {
  /**
   * Einen JarScanner erzeugen, der das Archiv, in dem sich eine gegebene Klasse befindet, nach
   * Klassen durchsucht, die eine bestimmte Annotation besitzen
   *
   * @param c eine Klasse die sich im Archiv befindet, das durchsucht werden soll
   * @param annotation die Annotation, nach der gesucht wird
   */
  public JarScanner(Class c, Class annotation) {
    this.annotation = annotation;
    this.cls = c;
    this.urlCL = getUrlClassLoader(cls);
    this.path = getPath(c);
 }
  /**
   * 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 eine Klasse, die 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 processZipContent(String packageName, JarScannerListener l, Handler h, String contextName) {
    try {
      ZipFile zipfile = new ZipFile(archive);
      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(urlCL, zipentry, packageName, l, h, contextName);
          processZipEntry(zipentry, packageName, l, h, contextName);
        } else {
          // ZIP-Dir muss nicht bearbeitet werden
        }
      }
      zipfile.close();
    } catch (IOException ex) {
      log(Level.SEVERE, ex.getLocalizedMessage());
    }
  }
  private void processZipEntry(ClassLoader urlCL, ZipEntry zipentry, String packageName, JarScannerListener l, Handler h, String contextName) {
  @SuppressWarnings("unchecked")
  private void processZipEntry(ZipEntry zipentry, String packageName, JarScannerListener l, Handler h, String contextName) {
    finest(zipentry.getName());
    String zName = zipentry.getName();
    if (zName.toLowerCase().endsWith(".class")) {
@@ -70,13 +99,12 @@
      String pkgName = getPackageName(fullClassNameDots);
      finest(" -- package name: " + pkgName);
      if (null != urlCL && pkgName.toLowerCase().startsWith(packageName)) {
        Class c = null;
        try {
          c = urlCL.loadClass(fullClassNameDots);
          Class c = urlCL.loadClass(fullClassNameDots);
          if (c != null) {
            if (c.isAnnotationPresent(Actor.class)) {
            if (c.isAnnotationPresent(annotation)) {
              finest(" ---- ACTOR ---- " + fullClassNameDots);
              l.actorFound(c, h, contextName);
              l.annotationFound(c, h, contextName);
            } else {
              finest("kein Actor " + fullClassNameDots);
            }
@@ -102,34 +130,37 @@
  }
  
  public ClassLoader getUrlClassLoader(Class c) {
    URL url;
    ClassLoader urlCL = null;
    ClassLoader cl = null;
    try {
      url = getPath(c).toURL();
      URL url = getPath(c).toURL();
      finer("url: " + url.getPath());
      urlCL = new URLClassLoader(new URL[]{url});
    } catch (URISyntaxException ex) {
      log(Level.SEVERE, ex.getMessage());
      cl = new URLClassLoader(new URL[]{url});
    } catch (MalformedURLException ex) {
      log(Level.SEVERE, ex.getMessage());
    } finally {
      return urlCL;
      return cl;
    }
  }
  
  public URI getPath(Class c) throws URISyntaxException {
    //Class c = this.getClass();
  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);
    int pos = className.indexOf(".class");
    if(pos > -1) {
      String classNameWoExt = className.substring(0, pos);
    }
    String classNameWoPkg = className.substring(className.lastIndexOf(".") + 1);
    String classNameWoPkg = c.getSimpleName();//className.substring(className.lastIndexOf(".") + 1);
    finest("Class name: " + classNameWoPkg);
    String classPath = c.getResource(classNameWoPkg + ".class").getPath();
    pos = classPath.indexOf("!");
    int pos = classPath.indexOf("!");
    String jarPath;
    if(pos > -1) {
      jarPath = /*"jar:" + */ classPath.substring(0, pos);
@@ -137,7 +168,12 @@
      jarPath = classPath;
    }
    finest("path: " + jarPath);
    try {
    return new URI(jarPath);
    } catch (URISyntaxException ex) {
      Logger.getLogger(JarScanner.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
      return null;
    }
  }
  
  private void finest(String msg) {
@@ -152,9 +188,9 @@
    Logger.getLogger(JarScanner.class.getName()).log(l, msg);
  }
  
  public interface JarScannerListener {
    public void actorFound(Class actorClass, Handler h, String contextName);
    public void annotationFound(Class foundClass, Handler h, String contextName);
  }
  
}