From f4025a39226331d8c1c4686d2d1c73e080f9f4b3 Mon Sep 17 00:00:00 2001 From: ulrich Date: Sat, 30 Nov 2024 18:31:19 +0000 Subject: [PATCH] Fix: wireActors, wenn App als JAR laeuft --- src/de/uhilger/neon/Handler.java | 50 ++++++++- src/de/uhilger/neon/JarScanner.java | 144 ++++++++++++++++++++++++++++ src/de/uhilger/neon/RangeGroup.java | 2 src/de/uhilger/neon/Factory.java | 77 ++++++++++----- 4 files changed, 238 insertions(+), 35 deletions(-) diff --git a/src/de/uhilger/neon/Factory.java b/src/de/uhilger/neon/Factory.java index 14015d5..ff5977d 100644 --- a/src/de/uhilger/neon/Factory.java +++ b/src/de/uhilger/neon/Factory.java @@ -23,6 +23,7 @@ import com.sun.net.httpserver.HttpContext; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; +import de.uhilger.neon.JarScanner.JarScannerListener; import de.uhilger.neon.entity.ContextDescriptor; import de.uhilger.neon.entity.NeonDescriptor; import de.uhilger.neon.entity.ServerDescriptor; @@ -35,6 +36,9 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.InetSocketAddress; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -42,6 +46,8 @@ import java.util.List; import java.util.Map; import java.util.concurrent.Executors; +import java.util.logging.Level; +import java.util.logging.Logger; /** * Einen Neon-Server aus einer Beschreibungsdatei herstellen @@ -59,7 +65,7 @@ * @author Ulrich Hilger * @version 1, 6.2.2024 */ -public class Factory { +public class Factory implements JarScannerListener { public Factory() { listeners = new ArrayList<>(); @@ -89,16 +95,16 @@ return gson.fromJson(sb.toString(), NeonDescriptor.class); } - public void runInstance(NeonDescriptor d) + public void runInstance(Class c, NeonDescriptor d) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException { - this.runInstance(d, null, new ArrayList<>()); + this.runInstance(c, d, null, new ArrayList<>()); } - public void runInstance(NeonDescriptor d, List<String> packageNames) + public void runInstance(Class c, NeonDescriptor d, List<String> packageNames) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException { - this.runInstance(d, packageNames, new ArrayList<>()); + this.runInstance(c, d, packageNames, new ArrayList<>()); } /** * Einen Neon-Server gemaess einem Serverbeschreibungsobjekt herstellen und starten @@ -115,7 +121,7 @@ * @throws InvocationTargetException * @throws IOException */ - public void runInstance(NeonDescriptor d, List<String> packageNames, List<DataProvider> sdp) + public void runInstance(Class c, NeonDescriptor d, List<String> packageNames, List<DataProvider> sdp) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException { List serverList = d.server; @@ -128,7 +134,7 @@ if(packageNames == null) { packageNames = d.actorPackages; } - addContexts(d, server, sd.contexts, packageNames, sdp); + addContexts(c, d, server, sd.contexts, packageNames, sdp); server.setExecutor(Executors.newFixedThreadPool(10)); server.start(); @@ -156,7 +162,7 @@ return auth; } - private void addContexts(NeonDescriptor d, HttpServer server, List contextList, List<String> packageNames, + private void addContexts(Class c, NeonDescriptor d, HttpServer server, List contextList, List<String> packageNames, List<DataProvider> sdp) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException { @@ -176,7 +182,7 @@ ctxAttrs.putAll(cd.attributes); if (h instanceof Handler) { for (String packageName : packageNames) { - wireActors( + wireActors(c, packageName, Actor.class, (Handler) h, cd.attributes.get("contextName")); ctx.getAttributes().put("serverDataProviderList", sdp); @@ -254,27 +260,39 @@ } } - private void wireActors(String packageName, Class annotation, Handler h, String contextName) { - ClassLoader cl = ClassLoader.getSystemClassLoader(); - 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 = Class.forName(packageName + "." - + line.substring(0, line.lastIndexOf('.'))); - if (actorClass != null && actorClass.isAnnotationPresent(annotation)) { - wire(h, actorClass, contextName); + 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")) { + ClassLoader cl = c.getClassLoader(); + 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(annotation)) { + wire(h, actorClass, contextName); + } + } catch (ClassNotFoundException ex) { + // Klasse nicht gefunden. Muss das geloggt oder sonstwie behandel werden? + } + } else { + wireActors(c, packageName + "." + line, annotation, h, contextName); } - } catch (ClassNotFoundException ex) { - // Klasse nicht gefunden. Muss das geloggt oder sonstwie behandel werden? } } else { - wireActors(packageName + "." + line, annotation, h, contextName); + ClassLoader cl = js.getUrlClassLoader(c); + js.processZipContent(cl, new File(path), packageName, this, h, contextName); } + } catch (URISyntaxException ex) { + Logger.getLogger(Factory.class.getName()).log(Level.SEVERE, ex.getMessage(), ex); } } @@ -346,4 +364,11 @@ l.instanceStarted(); } } + + /* -------------- JarScannerListener Implementierung --------------- */ + + @Override + public void actorFound(Class actorClass, Handler h, String contextName) { + wire(h, actorClass, contextName); + } } diff --git a/src/de/uhilger/neon/Handler.java b/src/de/uhilger/neon/Handler.java index 14d34d3..db7d025 100644 --- a/src/de/uhilger/neon/Handler.java +++ b/src/de/uhilger/neon/Handler.java @@ -22,6 +22,7 @@ import de.uhilger.neon.Action.Type; import de.uhilger.neon.entity.ActionDescriptor; import java.io.IOException; +import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Parameter; @@ -30,6 +31,8 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; /** * Objekte der Klasse Handler nehmen Objekte entgegen die die Annotationen NeonActor enthalten. @@ -91,7 +94,17 @@ //Logger.getLogger(Handler.class.getName()) // .log(Level.INFO, "{0} {1} {2}", new Object[]{methodType, route, className}); - dispatcher.get(methodType).put(ad.route, ad); + //dispatcher.get(methodType).put(ad.route, ad); + Object adMapObj = dispatcher.get(methodType); + if(adMapObj instanceof HashMap hashMap) { + @SuppressWarnings("unchecked") + HashMap<String, ActionDescriptor> map = hashMap; + map.put(ad.route, ad); + Logger.getLogger(Handler.class.getName()).log(Level.FINER, "ActionDescriptor route {0} className {1}", new Object[]{route, className}); + } else { + Logger.getLogger(Handler.class.getName()).finer("ActionDescriptorMap nicht gefunden"); + } + } /** @@ -135,8 +148,9 @@ .getHttpContext() .getPath() .length()); - - Type requestMethod = Type.valueOf(exchange.getRequestMethod()); + String requestMethodStr = exchange.getRequestMethod(); + Logger.getLogger(Handler.class.getName()).log(Level.FINER, "method {0} route {1}", new Object[]{requestMethodStr, route}); + Type requestMethod = Type.valueOf(requestMethodStr); /* Es wird erst geprueft, ob zu einer bestimmten Route ein Actor registriert wurde. Wenn kein Actor mit dieser @@ -147,10 +161,12 @@ Object md = dispatcher.get(requestMethod); if (md instanceof Map) { int pos = route.lastIndexOf("/"); + Logger.getLogger(Handler.class.getName()).log(Level.FINER, "pos {0}", pos); Object o = ((Map) md).get(route); if (!(o instanceof ActionDescriptor)) { while (!found && (pos > -1)) { String routeRest = route.substring(0, pos); + Logger.getLogger(Handler.class.getName()).log(Level.FINER, "pos {0} routeRest {1}", new Object[]{pos, routeRest}); o = ((Map) md).get(routeRest); if (o instanceof ActionDescriptor) { found = true; @@ -163,16 +179,24 @@ handleRequest(exchange, o, route, route, requestMethod); } if (!found) { + Logger.getLogger(Handler.class.getName()).log(Level.FINER, "{0} not found ", route); o = dispatcher.get(requestMethod).get("/"); if (o instanceof ActionDescriptor) { handleRequest(exchange, o, route, route, requestMethod); + } else { + // kein ActionDescriptor für '/' + Logger.getLogger(Handler.class.getName()).log(Level.FINER, "Kein Actiondescriptor fuer '/'"); } } + } else { + // keine Actions fuer HTTP Methode + Logger.getLogger(Handler.class.getName()).log(Level.FINER, "Kein Actions fuer HTTP-Methode {0}", requestMethodStr); } } private void handleRequest(HttpExchange exchange, Object o, String route, String subroute, Type requestMethod) throws IOException { + Logger.getLogger(Handler.class.getName()).log(Level.FINER, "Handle Request route {0} subroute {1}", new Object[]{route, subroute}); ActionDescriptor ad = (ActionDescriptor) o; String actorClassName = ad.className; try { @@ -183,11 +207,20 @@ if (action != null) { if ((action.route().equals("/") || action.route().startsWith(route)) && action.type().equals(requestMethod)) { Object[] actionArgs = getActionArgs(exchange, method, ad, subroute); - Object actorObj = actorClass.getDeclaredConstructor().newInstance(); - addDataProvider(exchange, actorObj); - Object antwort = method.invoke(actorObj, actionArgs); - if (!action.handlesResponse()) { - respond(exchange, antwort); + @SuppressWarnings("unchecked") + Object conObj = actorClass.getDeclaredConstructor(); + if(conObj instanceof Constructor) { + Constructor con = (Constructor) conObj; + Object actorObj; + actorObj = con.newInstance(); + addDataProvider(exchange, actorObj); + Object antwort = method.invoke(actorObj, actionArgs); + if (!action.handlesResponse()) { + respond(exchange, antwort); + } + } else { + // kein Konstruktor + Logger.getLogger(Handler.class.getName()).info("Kein Konstruktor gefunden"); } } } @@ -196,6 +229,7 @@ InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { // Klasse nicht gefunden. Muss das geloggt oder sonstwie behandel werden? + Logger.getLogger(Handler.class.getName()).finer("Kein passende Actor-Klasse gefunden"); } //} } diff --git a/src/de/uhilger/neon/JarScanner.java b/src/de/uhilger/neon/JarScanner.java new file mode 100644 index 0000000..d2c350c --- /dev/null +++ b/src/de/uhilger/neon/JarScanner.java @@ -0,0 +1,144 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ + +package de.uhilger.neon; + +import java.io.File; +import java.io.IOException; +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.logging.Level; +import java.util.logging.Logger; +import java.util.zip.ZipEntry; +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. + * + * @author Ulrich Hilger + * @version 0.1, 30.11.2024 + */ +public class JarScanner { + + + public void processZipContent(ClassLoader urlCL, File archive, String packageName, JarScannerListener l, Handler h, String contextName) { + try { + ZipFile zipfile = new ZipFile(archive); + Enumeration en = zipfile.entries(); + while (en.hasMoreElements()) { + ZipEntry zipentry = (ZipEntry) en.nextElement(); + if (!zipentry.isDirectory()) { + processZipEntry(urlCL, 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) { + log(Level.FINEST, zipentry.getName()); + String zName = zipentry.getName(); + if (zName.toLowerCase().endsWith(".class")) { + int pos = zName.indexOf(".class"); + String fullClassName = zName.substring(0, pos); + log(Level.FINEST, "full class name: " + zName); + String fullClassNameDots = fullClassName.replace('/', '.'); + log(Level.FINEST, "full class name dots: " + fullClassNameDots); + String pkgName = getPackageName(fullClassNameDots); + log(Level.FINEST, " -- package name: " + pkgName); + if (null != urlCL && pkgName.toLowerCase().startsWith(packageName)) { + Class c = null; + try { + c = urlCL.loadClass(fullClassNameDots); + if (c != null) { + if (c.isAnnotationPresent(Actor.class)) { + log(Level.FINER, " ---- ACTOR ---- " + fullClassNameDots); + l.actorFound(c, h, contextName); + } else { + log(Level.FINER, "kein Actor " + fullClassNameDots); + } + } else { + log(Level.FINER, "class NOT loaded: " + zName); + } + } catch (ClassNotFoundException ex) { + log(Level.FINER, " +++++ Class not found: " + ex.getMessage()); + } + } + } + } + + 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 getUrlClassLoader(Class c) { + URL url; + ClassLoader urlCL = null; + try { + url = getPath(c).toURL(); + log(Level.FINER, "url: " + url.getPath()); + urlCL = new URLClassLoader(new URL[]{url}); + } catch (URISyntaxException ex) { + log(Level.SEVERE, ex.getMessage()); + } catch (MalformedURLException ex) { + log(Level.SEVERE, ex.getMessage()); + } finally { + return urlCL; + } + } + + public URI getPath(Class c) throws URISyntaxException { + //Class c = this.getClass(); + String className = c.getName(); + finer("this name: " + className); + + int pos = className.indexOf(".class"); + if(pos > -1) { + String classNameWoExt = className.substring(0, pos); + } + String classNameWoPkg = className.substring(className.lastIndexOf(".") + 1); + finer("Class name: " + classNameWoPkg); + String classPath = c.getResource(classNameWoPkg + ".class").getPath(); + pos = classPath.indexOf("!"); + String jarPath; + if(pos > -1) { + jarPath = /*"jar:" + */ classPath.substring(0, pos); + } else { + jarPath = classPath; + } + finer("path: " + jarPath); + return new URI(jarPath); + } + + private void finer(String msg) { + log(Level.FINER, msg); + } + + private void log(Level l, String msg) { + Logger.getLogger(JarScanner.class.getName()).log(l, msg); + } + + + public interface JarScannerListener { + public void actorFound(Class actorClass, Handler h, String contextName); + } + +} diff --git a/src/de/uhilger/neon/RangeGroup.java b/src/de/uhilger/neon/RangeGroup.java index 8aa2adb..1dc369c 100644 --- a/src/de/uhilger/neon/RangeGroup.java +++ b/src/de/uhilger/neon/RangeGroup.java @@ -35,7 +35,7 @@ * Ein neues Objekt der Klasse RangeGroup erzeugen */ public RangeGroup() { - ranges = new ArrayList(); + ranges = new ArrayList<>(); } /** -- Gitblit v1.9.3