Ultrakompakter HTTP Server
3 files modified
178 ■■■■ changed files
README.md 32 ●●●●● patch | view | raw | blame | history
src/de/uhilger/neon/Factory.java 32 ●●●●● patch | view | raw | blame | history
src/de/uhilger/neon/entity/NeonDescriptor.java 114 ●●●●● patch | view | raw | blame | history
README.md
@@ -14,7 +14,7 @@
## Abhängigkeiten
neon erfordert die Klassenbibliothek [Gson](https://google.github.io/gson/).
neon erfordert die Klassenbibliothek [nanojson](https://github.com/mmastrac/nanojson).
## Klassenbibliothek herstellen
@@ -24,7 +24,7 @@
cd $NEON
mkdir classes
mkdir dist
$JDK/bin/javac -parameters -classpath $JLIB/gson-2.8.6.jar -d classes src/de/uhilger/neon/*.java src/de/uhilger/neon/entity/*.java
$JDK/bin/javac -parameters -classpath $JLIB/nanojson.jar -d classes src/de/uhilger/neon/*.java src/de/uhilger/neon/entity/*.java
$JDK/bin/jar -cf dist/neon.jar -C classes .
```
@@ -34,7 +34,33 @@
## Klassenbibliothek verwenden
Zur Verwendung der Klassen von neon wird die Klassenbibliothek in den Classpath des Programmes aufgenommen, von dem aus neon genutzt werden soll.
Zur Verwendung der Klassen von neon wird die Klassenbibliothek `neon.jar` zusammen mit `nanojson.jar`
in den Classpath des Programmes aufgenommen, von dem aus neon genutzt werden soll.
Ist beispielsweise eine App `MyApp.jar` im Ordner `~/apps/MyApp` gespeichert, die mit Hilfe von
neon einen eingebetteten Webserver einsetzen soll, würde die Klassenbibliothek `neon.jar` in den
Ordner `~/apps/MyApp/lib` gelegt. Ferner wird eine [Serverbeschreibungsdatei](https://uhilger.de/data/pg/neon/anleitung.adoc#srv-desc) benötigt, die z.B. im Ordner `~/apps/MyApp/conf`
gespeichert wird.
Es ergibt sich folgender Verzeichnisinhalt
````
~/apps/MyApp/MyApp.jar
~/apps/MyApp/conf/server.json
~/apps/MyApp/lib/neon.jar
~/apps/MyApp/lib/nanojson.jar
````
Die Anleitung von neon beschreibt im Kapitel [Neon starten](https://uhilger.de/data/pg/neon/anleitung.adoc#_neon_starten), wie neon aus einer Anwendung wie `MyApp.jar` heraus gestartet wird. Die App
`MyApp.jar` wird wie ein normales Java-Programm von der Kommandozeile ausgeführt:
````
cd ~/apps/MyApp
java -jar MyApp.jar
````
Hierbei wird automatisch der Inhalt des Unterverzeichnisses `lib` in `~/apps/MyApp` beim Start der
App einbezogen, wo sich die Klassenbibliotheken `neon` und `nanojson` befinden.
## Lizenz
src/de/uhilger/neon/Factory.java
@@ -18,7 +18,6 @@
package de.uhilger.neon;
import de.uhilger.neon.entity.ActorDescriptor;
import com.google.gson.Gson;
import com.sun.net.httpserver.Authenticator;
import com.sun.net.httpserver.Filter;
import com.sun.net.httpserver.HttpContext;
@@ -28,8 +27,6 @@
import de.uhilger.neon.entity.NeonDescriptor;
import de.uhilger.neon.entity.ServerDescriptor;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -48,12 +45,12 @@
import de.uhilger.neon.Scanner.ScannerListener;
/**
 * Einen Neon-Server aus einer Beschreibungsdatei herstellen
 * Einen Neon-Server aus einer Beschreibungsdatei herstellen und starten
 *
 * Die Werte aus der Beschreibungsdatei werden in die Attribute der HttpContext-Objekte geschrieben,
 * die zu jedem Server eroeffnet werden.
 *
 * Die Entitaeten stehen wie folgt in Beziehung: HttpServer -1:n-> HttpContext -1:1-> HttpHandler
 * Die Entitaeten stehen wie folgt in Beziehung: HttpServer -1:n-> HttpContext -1:n-> HttpHandler.
 *
 * Die Factory legt die Kontexte, Handler sowie die Verbindung zu den Actors selbsttaetig an. Alle
 * Parameter aus 'attributes'-Elementen der Beschreibungsdatei werden als Attribute in den
@@ -62,6 +59,7 @@
 *
 * @author Ulrich Hilger
 * @version 1, 6.2.2024
 * @version 2, 24.2.2026
 */
public class Factory implements ScannerListener {
  
@@ -70,30 +68,6 @@
  public Factory() {
    listeners = new ArrayList<>();
    actorMap = new HashMap<>();
  }
  /**
   * Beschreibungsdatei lesen
   *
   * @param file die Datei, die den Server beschreibt
   * @return ein Objekt, das den Server beschreibt
   * @throws IOException wenn die Datei nicht gelesen werden konnte
   */
  public NeonDescriptor readDescriptor(File file) throws IOException {
    //Logger logger = Logger.getLogger(Factory.class.getName());
    //logger.log(Level.INFO, "reading NeonDescriptor from {0}", file.getAbsolutePath());
    StringBuilder sb = new StringBuilder();
    BufferedReader r = new BufferedReader(new FileReader(file));
    String line = r.readLine();
    while (line != null) {
      sb.append(line);
      line = r.readLine();
    }
    r.close();
    Gson gson = new Gson();
    return gson.fromJson(sb.toString(), NeonDescriptor.class);
  }
  public void runInstance(Class c, NeonDescriptor d)
src/de/uhilger/neon/entity/NeonDescriptor.java
@@ -17,7 +17,20 @@
 */
package de.uhilger.neon.entity;
import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject;
import com.grack.nanojson.JsonParser;
import com.grack.nanojson.JsonParserException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
 * Objekte der Klasse NeonDescriptor beschreiben einen Neon-Server
@@ -35,5 +48,104 @@
  public AuthenticatorDescriptor authenticator;
  public List<ServerDescriptor> server; 
  public List<WsServerDescriptor> wsserver;
  /**
   * Den NeonDescriptor aus einer in JSON abgefassten Serverbeschreibungsdatei lesen.
   *
   * vgl. https://uhilger.de/data/pg/neon/anleitung.adoc#srv-desc
   *
   * @param fileName Name und ggf. Pfad zur Serverbeschreibung
   */
  public void read(String fileName) {
    try {
      File f = new File(fileName);
      JsonObject serverDesc = JsonParser.object().from(new FileReader(f));
      //NeonDescriptor d = new NeonDescriptor();
      this.instanceName = serverDesc.getString("instanceName");
      this.contentType = serverDesc.getString("contentType");
      this.contentId = serverDesc.getString("contentId");
      this.contentDescription = serverDesc.getString("contentDescription");
      this.instanceDescription = serverDesc.getString("instanceDescription");
      this.actorPackages = getArray(serverDesc, "actorPackages");
      JsonObject authObj = serverDesc.getObject("authenticator");
      if (null != authObj) {
        this.authenticator = new AuthenticatorDescriptor();
        this.authenticator.className = authObj.getString("className");
        this.authenticator.attributes = getAttrMap(authObj, "attributes");
      }
      JsonArray serverArray = serverDesc.getArray("server");
      this.server = new ArrayList<>();
      Iterator serverIterator = serverArray.iterator();
      while (serverIterator.hasNext()) {
        Object iteratorObject = serverIterator.next();
        if (iteratorObject instanceof JsonObject) {
          JsonObject serverObject = (JsonObject) iteratorObject;
          ServerDescriptor sd = new ServerDescriptor();
          sd.name = serverObject.getString("name");
          sd.port = serverObject.getInt("port", 0);
          sd.contexts = new ArrayList<>();
          JsonArray contextsArray = serverObject.getArray("contexts");
          Iterator contextIterator = contextsArray.iterator();
          while (contextIterator.hasNext()) {
            Object contextObj = contextIterator.next();
            if (contextObj instanceof JsonObject) {
              JsonObject context = (JsonObject) contextObj;
              ContextDescriptor cd = new ContextDescriptor();
              cd.className = context.getString("className");
              cd.sharedHandler = context.getBoolean("sharedHandler");
              cd.contextPath = context.getString("contextPath");
              if (context.containsKey("filter")) {
                cd.filter = getArray(context, "filter");
              }
              cd.authenticator = context.getString("authenticator");
              cd.attributes = getAttrMap(context, "attributes");
              sd.contexts.add(cd);
            }
          }
          this.server.add(sd);
        }
      }
      //return d;
    } catch (JsonParserException | FileNotFoundException ex) {
      Logger.getLogger(NeonDescriptor.class.getName()).severe(ex.getMessage());
      //return null;
    }
  }
  /**
   * Die Eintraege eines einfachen JSON-Array in eine String-Liste uebertragen
   * @param src das Objekt, aus dem die Inhalte des JSON-Arrays gelesen werden
   * @param name der Name des zu lesenden JSON-Arrays
   * @return der Inhalt des JSON-Arrays als String-Liste
   */
  private List<String> getArray(JsonObject src, String name) {
    return src.getArray(name)
            .stream()
            .map((o) -> Objects.toString(o, null))
            .collect(Collectors.toList());
  }
  /**
   * Eine Liste von Attributen (Schluessel/Wert-Paare) aus einem JSON-Objekt
   * in eine Map uebertragen.
   *
   * @param src das JSON-Objekt, aus dem die Attribute gelesen werden sollen
   * @param name der Name des JSON-Elements, das die Attribute enthaelt
   * @return eine Map mit den gelesenen Attributen
   */
  private HashMap<String, String> getAttrMap(JsonObject src, String name) {
    HashMap<String, String> map = new HashMap<>();
    JsonObject attrObj = src.getObject(name);
    Iterator attributeIterator = attrObj.keySet().iterator();
    while (attributeIterator.hasNext()) {
      Object keyObj = attributeIterator.next();
      if (keyObj instanceof String) {
        String key = (String) keyObj;
        map.put(key, attrObj.get(key).toString());
      }
    }
    return map;
  }
}