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