Ultrakompakter HTTP Server
ulrich
2024-12-01 1f6776f237cb98c1222ee9dd2f75220de0d02c34
commit | author | age
f4025a 1 /*
c2e8cf 2   neon - Embeddable HTTP Server based on jdk.httpserver
U 3   Copyright (C) 2024  Ulrich Hilger
f4025a 4
c2e8cf 5   This program is free software: you can redistribute it and/or modify
U 6   it under the terms of the GNU Affero General Public License as
7   published by the Free Software Foundation, either version 3 of the
8   License, or (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU Affero General Public License for more details.
14
15   You should have received a copy of the GNU Affero General Public License
16   along with this program.  If not, see <https://www.gnu.org/licenses/>.
17  */
f4025a 18 package de.uhilger.neon;
U 19
692dc7 20 import java.io.BufferedReader;
f4025a 21 import java.io.File;
U 22 import java.io.IOException;
692dc7 23 import java.io.InputStream;
U 24 import java.io.InputStreamReader;
f4025a 25 import java.net.MalformedURLException;
U 26 import java.net.URI;
27 import java.net.URISyntaxException;
28 import java.net.URL;
29 import java.net.URLClassLoader;
30 import java.util.Enumeration;
692dc7 31 import java.util.Iterator;
f4025a 32 import java.util.logging.Level;
U 33 import java.util.logging.Logger;
34 import java.util.zip.ZipEntry;
35 import java.util.zip.ZipFile;
36
37 /**
1f6776 38  * Die Klasse Scanner enthaelt Methoden, um fuer eine Klasse zu bestimmen, in welcher JAR-Datei sie
U 39  * liegt und diese JAR-Datei nach Klassen zu durchsuchen.
47e67b 40  *
f4025a 41  * @author Ulrich Hilger
U 42  * @version 0.1, 30.11.2024
43  */
692dc7 44 public final class Scanner {
47e67b 45
U 46   private final URI path;
47   private final Class annotation;
692dc7 48
47e67b 49   private final Class cls;
U 50   private final ClassLoader urlCL;
51
52   /**
1f6776 53    * Einen Scanner erzeugen, der das Archiv, in dem sich eine gegebene Klasse befindet, nach Klassen
U 54    * durchsucht, die eine bestimmte Annotation besitzen
47e67b 55    *
U 56    * @param c eine Klasse die sich im Archiv befindet, das durchsucht werden soll
57    * @param annotation die Annotation, nach der gesucht wird
58    */
692dc7 59   public Scanner(Class c, Class annotation) {
47e67b 60     this.annotation = annotation;
U 61     this.cls = c;
62     this.urlCL = getUrlClassLoader(cls);
63     this.path = getPath(c);
692dc7 64   }
U 65
66   public Class getAnnotation() {
67     return annotation;
1f6776 68   }
U 69
70   public void process(ScannerListener l, String packageName, Handler h, String contextName) {
71     if (isJar()) {
72       processZipContent(packageName, l, h, contextName);
73     } else {
74       processClasses(l, packageName, h, contextName);
75     }
692dc7 76   }
47e67b 77
U 78   /**
79    * Den Inhalt einer Jar-Datei nach Klassen durchsuchen, die die dem Konstruktor gegebene
80    * Annotation besitzen.
81    *
82    * @param packageName Name der Package, die einschl. Unterpackages durchsucht wird, nur Klassen
83    * dieser Package und ihrer Unterpackages werden geladen und auf die Anotation ueberprueft
692dc7 84    * @param l ein Objekt, das verstaendigt wird, wenn eine annotierte Klasse gefunden wurde
47e67b 85    * @param h der Handler, dem die gefundene Klasse hinzugefuegt werden soll
U 86    * @param contextName Name des Kontext, dem gefundene Klassen hinzugefuegt werden sollen
87    */
692dc7 88   public void processZipContent(String packageName, ScannerListener l, Handler h, String contextName) {
f4025a 89     try {
47e67b 90       ZipFile zipfile = new ZipFile(new File(path));
f4025a 91       Enumeration en = zipfile.entries();
47e67b 92       //ClassLoader cl = getUrlClassLoader(cls);
f4025a 93       while (en.hasMoreElements()) {
U 94         ZipEntry zipentry = (ZipEntry) en.nextElement();
95         if (!zipentry.isDirectory()) {
47e67b 96           processZipEntry(zipentry, packageName, l, h, contextName);
f4025a 97         } else {
U 98           // ZIP-Dir muss nicht bearbeitet werden
99         }
100       }
101     } catch (IOException ex) {
102       log(Level.SEVERE, ex.getLocalizedMessage());
103     }
104   }
105
47e67b 106   @SuppressWarnings("unchecked")
692dc7 107   private void processZipEntry(ZipEntry zipentry, String packageName, ScannerListener l, Handler h, String contextName) {
7456da 108     finest(zipentry.getName());
f4025a 109     String zName = zipentry.getName();
U 110     if (zName.toLowerCase().endsWith(".class")) {
111       int pos = zName.indexOf(".class");
112       String fullClassName = zName.substring(0, pos);
7456da 113       finest("full class name: " + zName);
f4025a 114       String fullClassNameDots = fullClassName.replace('/', '.');
7456da 115       finest("full class name dots: " + fullClassNameDots);
f4025a 116       String pkgName = getPackageName(fullClassNameDots);
7456da 117       finest(" -- package name: " + pkgName);
f4025a 118       if (null != urlCL && pkgName.toLowerCase().startsWith(packageName)) {
U 119         try {
47e67b 120           Class c = urlCL.loadClass(fullClassNameDots);
f4025a 121           if (c != null) {
47e67b 122             if (c.isAnnotationPresent(annotation)) {
7456da 123               finest(" ---- ACTOR ---- " + fullClassNameDots);
47e67b 124               l.annotationFound(c, h, contextName);
f4025a 125             } else {
7456da 126               finest("kein Actor " + fullClassNameDots);
f4025a 127             }
U 128           } else {
7456da 129             finest("class NOT loaded: " + zName);
f4025a 130           }
U 131         } catch (ClassNotFoundException ex) {
7456da 132           finest(" +++++ Class not found: " + ex.getMessage());
f4025a 133         }
U 134       }
135     }
136   }
47e67b 137
1f6776 138   @SuppressWarnings("unchecked")
692dc7 139   public void processClasses(ScannerListener l, String packageName, Handler h, String contextName) {
1f6776 140     ClassLoader cl = getCl();
U 141     InputStream stream = cl.getResourceAsStream(packageName.replaceAll("[.]", "/"));
142     BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
143     Iterator i = reader.lines().iterator();
144     while (i.hasNext()) {
145       String line = i.next().toString();
146       if (line.endsWith(".class")) {
147         try {
148           Class actorClass = cl.loadClass(packageName + "."
149                   + line.substring(0, line.lastIndexOf('.')));
150           if (actorClass != null && actorClass.isAnnotationPresent(getAnnotation())) {
151             //wire(h, actorClass, contextName);
152             l.annotationFound(actorClass, h, contextName);
692dc7 153           }
1f6776 154         } catch (ClassNotFoundException ex) {
U 155           // Klasse nicht gefunden. Muss das geloggt oder sonstwie behandel werden?
692dc7 156         }
1f6776 157       } else {
U 158         //wireActors(js, packageName + "." + line, h, contextName);
159         processClasses(l, packageName + "." + line, h, contextName);
692dc7 160       }
1f6776 161     }
692dc7 162   }
1f6776 163
f4025a 164   private String getPackageName(String fullClassName) {
U 165     String packageName;
166     int pos = fullClassName.lastIndexOf(".");
167     if (pos > 0) {
168       packageName = fullClassName.substring(0, pos);
169     } else {
170       packageName = fullClassName;
171     }
172     return packageName;
692dc7 173   }
U 174
175   public ClassLoader getCl() {
176     return cls.getClassLoader();
f4025a 177   }
47e67b 178
f4025a 179   public ClassLoader getUrlClassLoader(Class c) {
47e67b 180     ClassLoader cl = null;
f4025a 181     try {
47e67b 182       URL url = getPath(c).toURL();
7456da 183       finer("url: " + url.getPath());
47e67b 184       cl = new URLClassLoader(new URL[]{url});
f4025a 185     } catch (MalformedURLException ex) {
U 186       log(Level.SEVERE, ex.getMessage());
187     } finally {
47e67b 188       return cl;
f4025a 189     }
U 190   }
47e67b 191
U 192   public String getPathStr() {
193     if (path != null) {
194       return path.toString();
195     } else {
196       return "";
197     }
198   }
199
200   public boolean isJar() {
201     return !getPathStr().toLowerCase().endsWith(".class");
202   }
203
204   private URI getPath(Class c) {
f4025a 205     String className = c.getName();
7456da 206     finest("this name: " + className);
47e67b 207     String classNameWoPkg = c.getSimpleName();//className.substring(className.lastIndexOf(".") + 1);
7456da 208     finest("Class name: " + classNameWoPkg);
f4025a 209     String classPath = c.getResource(classNameWoPkg + ".class").getPath();
47e67b 210     int pos = classPath.indexOf("!");
f4025a 211     String jarPath;
47e67b 212     if (pos > -1) {
f4025a 213       jarPath = /*"jar:" + */ classPath.substring(0, pos);
U 214     } else {
215       jarPath = classPath;
216     }
7456da 217     finest("path: " + jarPath);
47e67b 218     try {
U 219       return new URI(jarPath);
220     } catch (URISyntaxException ex) {
692dc7 221       Logger.getLogger(Scanner.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
47e67b 222       return null;
U 223     }
f4025a 224   }
47e67b 225
7456da 226   private void finest(String msg) {
U 227     log(Level.FINEST, msg);
228   }
47e67b 229
f4025a 230   private void finer(String msg) {
U 231     log(Level.FINER, msg);
232   }
47e67b 233
f4025a 234   private void log(Level l, String msg) {
692dc7 235     Logger.getLogger(Scanner.class.getName()).log(l, msg);
f4025a 236   }
47e67b 237
692dc7 238   public interface ScannerListener {
47e67b 239     public void annotationFound(Class foundClass, Handler h, String contextName);
f4025a 240   }
1f6776 241 }