|  |  | 
 |  |  |  | 
 |  |  | import com.google.gson.Gson; | 
 |  |  | import com.sun.net.httpserver.HttpExchange; | 
 |  |  | import com.sun.net.httpserver.HttpHandler; | 
 |  |  | import de.uhilger.mediaz.App; | 
 |  |  | import static de.uhilger.mediaz.App.RB_EP_LISTE; | 
 |  |  | import static de.uhilger.mediaz.App.RB_EP_LISTE_ALLES; | 
 |  |  | import de.uhilger.mediaz.Server; | 
 |  |  | import static de.uhilger.mediaz.Server.RB_SLASH; | 
 |  |  | import de.uhilger.mediaz.store.FileStorage; | 
 |  |  | import de.uhilger.mediaz.entity.Entity; | 
 |  |  | import java.io.BufferedReader; | 
 |  |  | import de.uhilger.mediaz.entity.Geraet; | 
 |  |  | import static de.uhilger.mediaz.store.FileStorage.ST_ABLAGEORT; | 
 |  |  | import static de.uhilger.mediaz.store.FileStorage.ST_GERAET; | 
 |  |  | import de.uhilger.mediaz.store.Storage; | 
 |  |  | import java.io.IOException; | 
 |  |  | import java.io.InputStream; | 
 |  |  | import java.io.InputStreamReader; | 
 |  |  | import java.io.OutputStream; | 
 |  |  | import java.net.URI; | 
 |  |  | import java.util.Iterator; | 
 |  |  | import java.util.List; | 
 |  |  | import java.util.logging.Level; | 
 |  |  | import java.util.logging.Logger; | 
 |  |  |  | 
 |  |  | import java.net.http.HttpClient; | 
 |  |  | import java.net.http.HttpClient.Version; | 
 |  |  | import java.net.http.HttpClient.Redirect; | 
 |  |  | import java.net.http.HttpRequest; | 
 |  |  | import java.net.http.HttpResponse; | 
 |  |  | import java.net.http.HttpResponse.BodyHandlers; | 
 |  |  | import java.time.Duration; | 
 |  |  | import java.util.ArrayList; | 
 |  |  |  | 
 |  |  |  | 
 |  |  | /** | 
 |  |  |  * HttpHandler fuer die Verwaltung von Entitaeten der Mediazentrale | 
 |  |  |  *  | 
 |  |  |  * GET /mz/api/store/[typname]/[name] | 
 |  |  |  * GET /mz/api/store/[typname]/liste | 
 |  |  |  * GET /mz/api/store/[typname]/listealles  (nur Typ Geraet) | 
 |  |  |  *  | 
 |  |  |  * @author Ulrich Hilger | 
 |  |  |  * @version 1, 5.4.2021 | 
 |  |  |  */ | 
 |  |  | public class StorageHandler extends AbstractHandler implements HttpHandler { | 
 |  |  | public class StorageHandler extends AbstractHandler { | 
 |  |  |    | 
 |  |  |   private static final Logger logger = Logger.getLogger(StorageHandler.class.getName()); | 
 |  |  |  | 
 |  |  |    | 
 |  |  |   /* | 
 |  |  |    | 
 |  |  |     PUT Die angegebene Ressource wird angelegt. Wenn die Ressource bereits  | 
 |  |  |            existiert, wird sie geändert.  | 
 |  |  |    | 
 |  |  |     Das REST-Muster sieht je Entitaet fuenf Faelle vor (Beispiel Ablageort): | 
 |  |  |      | 
 |  |  |     1. HTTP GET Ablageort/[Name]  - Liefere den Ablageort namens [Name] als JSON | 
 |  |  |     2. HTTP GET Ablageort/liste/  - Liefere eine Liste mit Namen aller Ablageorte | 
 |  |  |                                      Es duerfen also keine Elemente namens "liste" angelegt werden | 
 |  |  |     3. HTTP GET Ablageort/        - Liefere eine Liste aller Ablageort-Objekte als JSON | 
 |  |  |     4. HTTP PUT Ablageort/[Name]  - Die angegebene Ressource wird angelegt. Wenn die Ressource  | 
 |  |  |                                      bereits existiert, wird sie geändert.  | 
 |  |  |     5. HTTP DELETE                - loesche den Ablageort | 
 |  |  |      | 
 |  |  |    | 
 |  |  |    | 
 |  |  |     falsch: | 
 |  |  |     1. HTTP GET Ablageort/[Name]  - Liefere den Ablageort namens [Name] als JSON | 
 |  |  |     2. HTTP GET Ablageort/liste/  - Liefere eine Liste mit Namen aller Ablageorte | 
 |  |  |     2. HTTP GET Ablageort/  - Liefere eine Liste aller Ablageort-Objekte als JSON | 
 |  |  |     3. HTTP PUT  - schreibe einen neuen Ablageort auf die Platte | 
 |  |  |     4. HTTP POST  - schreibe Aenderungen auf die Platte | 
 |  |  |     5. HTTP DELETE  - loesche den Ablageort | 
 |  |  |    | 
 |  |  |     Beispiele: | 
 |  |  |    | 
 |  |  |     HTTP GET an /mz/api/store/Ablageort/ | 
 |  |  |     liefert alle Ablageort-Objekte | 
 |  |  |    | 
 |  |  |     HTTP GET an /mz/api/store/Ablageort/liste/   | 
 |  |  |     liefert eine Liste der Namen vorhandener Ablageorte | 
 |  |  |    | 
 |  |  |     HTTP GET an /mz/api/store/Ablageort/[Name] | 
 |  |  |     liest den Ablageort namens [Name] | 
 |  |  |    | 
 |  |  |     HTTP PUT an /mz/api/store/Ablageort/[Name] | 
 |  |  |     wenn [Name] existiert: Aenderung, sonst neu anlegen | 
 |  |  |    | 
 |  |  |     HTTP DELETE an /mz/api/store/Ablageort/[Name] | 
 |  |  |     löscht den Ablageort namens [Name] | 
 |  |  |    | 
 |  |  |   */ | 
 |  |  |    | 
 |  |  |   /** Name der HTTP Methode GET */ | 
 |  |  |   public static final String HTTP_GET = "GET"; | 
 |  |  |    | 
 |  |  |   /** Name der HTTP Methode PUT */ | 
 |  |  |   public static final String HTTP_PUT = "PUT"; | 
 |  |  |    | 
 |  |  |   /** Name der HTTP Methode POST */ | 
 |  |  |   public static final String HTTP_POST = "POST"; | 
 |  |  |    | 
 |  |  |   /** Name der HTTP Methode DELETE */ | 
 |  |  |   public static final String HTTP_DELETE = "DELETE"; | 
 |  |  |    | 
 |  |  |   public static final String EP_LISTE = "liste/"; | 
 |  |  |  | 
 |  |  |   @Override | 
 |  |  |   public void handle(HttpExchange e) throws IOException { | 
 |  |  |     String method = e.getRequestMethod(); | 
 |  |  |     String response = ""; | 
 |  |  |     int code = 200; | 
 |  |  |     switch(method) { | 
 |  |  |       case HTTP_GET: | 
 |  |  |         String json = lesen(e); | 
 |  |  |         if(json != null) { | 
 |  |  |           response = json; | 
 |  |  |         } else { | 
 |  |  |           response = "nicht gefunden"; | 
 |  |  |           code = 404; | 
 |  |  |         } | 
 |  |  |         break; | 
 |  |  |          | 
 |  |  |       case HTTP_PUT: | 
 |  |  |         response = put(e); | 
 |  |  |         break; | 
 |  |  |          | 
 |  |  |       case HTTP_POST: | 
 |  |  |         response = "nicht unterstuertzt."; | 
 |  |  |         code = 404; | 
 |  |  |         break; | 
 |  |  |          | 
 |  |  |       case HTTP_DELETE: | 
 |  |  |         boolean geloescht = loeschen(e); | 
 |  |  |         if(geloescht) { | 
 |  |  |           response = "geloescht"; | 
 |  |  |         } else { | 
 |  |  |           response = "nicht geloescht"; | 
 |  |  |         } | 
 |  |  |         break; | 
 |  |  |     } | 
 |  |  |     logger.fine(response); | 
 |  |  |     e.sendResponseHeaders(code, response.length()); | 
 |  |  |     OutputStream os = e.getResponseBody(); | 
 |  |  |     os.write(response.getBytes()); | 
 |  |  |     os.close();         | 
 |  |  |   } | 
 |  |  |    | 
 |  |  |   private String put(HttpExchange e) throws IOException { | 
 |  |  |   protected String put(HttpExchange e) throws IOException { | 
 |  |  |     String path = e.getRequestURI().toString(); | 
 |  |  |     String[] elems = path.split(App.getRs(Server.RB_SLASH)); | 
 |  |  |     String[] elems = path.split(Server.SLASH); | 
 |  |  |     String type = elems[elems.length - 2]; | 
 |  |  |     String elemName = elems[elems.length - 1]; // alter Name, wenn Aenderung | 
 |  |  |     if(!elemName.equalsIgnoreCase(App.getRs(RB_EP_LISTE))) { | 
 |  |  | 
 |  |  |         } else { | 
 |  |  |           fs.write(entity, false); | 
 |  |  |         } | 
 |  |  |         return type + App.getRs(Server.RB_SLASH) + entity.getName(); | 
 |  |  |         return type + Server.SLASH + entity.getName(); | 
 |  |  |       } else { | 
 |  |  |         return "Ungueltiges Objekt im Body."; | 
 |  |  |       } | 
 |  |  | 
 |  |  |     } | 
 |  |  |   } | 
 |  |  |    | 
 |  |  |   /* | 
 |  |  |   private String schreiben(FileStorage fs, String typ, String name, String body, boolean overwrite) { | 
 |  |  |     Gson gson = new Gson(); | 
 |  |  |     logger.log(Level.FINE, "type: {0}, token: {1}", new Object[]{typ, fs.typeFromName(typ).getType().getTypeName()}); | 
 |  |  |     Object o = gson.fromJson(body, fs.typeFromName(typ).getType()); | 
 |  |  |     if(o instanceof Entity) { | 
 |  |  |       Object antwortObjekt = fs.write((Entity) o, overwrite); | 
 |  |  |       if(antwortObjekt instanceof File) { | 
 |  |  |         File file = (File) antwortObjekt; | 
 |  |  |         logger.log(Level.FINE, "Datei {0} geschrieben.", file.getAbsolutePath()); | 
 |  |  |         String filename = file.getName(); | 
 |  |  |         return typ + FileHandler.STR_BLANK + filename; | 
 |  |  |       } | 
 |  |  |     }     | 
 |  |  |     return typ + FileHandler.STR_BLANK + " etwas Seltsames ist passiert."; | 
 |  |  |   } | 
 |  |  |   */ | 
 |  |  |   /* | 
 |  |  |   private String neu(HttpExchange e) throws IOException { | 
 |  |  |     return schreiben(e, false); | 
 |  |  |   } | 
 |  |  |    | 
 |  |  |   private String aendern(HttpExchange e) throws IOException { | 
 |  |  |     return schreiben(e, true); | 
 |  |  |   } | 
 |  |  |   */ | 
 |  |  |    | 
 |  |  |   /* | 
 |  |  |   private String schreiben(HttpExchange e, boolean overwrite) throws IOException { | 
 |  |  |     String path = e.getRequestURI().toString(); | 
 |  |  |     String[] elems = path.split(App.getRs(Server.RB_SLASH)); | 
 |  |  |     String type = elems[elems.length - 1]; | 
 |  |  |     String body = bodyLesen(e); | 
 |  |  |     String filename = "";  | 
 |  |  |     FileStorage fs = new FileStorage(App.getInitParameter(App.getRs(App.RB_AP_CONF))); | 
 |  |  |     Gson gson = new Gson(); | 
 |  |  |     logger.log(Level.FINE, "type: {0}, token: {1}", new Object[]{type, fs.typeFromName(type).getType().getTypeName()}); | 
 |  |  |     Object o = gson.fromJson(body, fs.typeFromName(type).getType()); | 
 |  |  |     if(o instanceof Entity) { | 
 |  |  |       Object antwortObjekt = fs.write((Entity) o, overwrite); | 
 |  |  |       if(antwortObjekt instanceof File) { | 
 |  |  |         File file = (File) antwortObjekt; | 
 |  |  |         logger.log(Level.FINE, "Datei {0} geschrieben.", file.getAbsolutePath()); | 
 |  |  |         filename = file.getName(); | 
 |  |  |         return type + FileHandler.STR_BLANK + filename; | 
 |  |  |       } | 
 |  |  |     } | 
 |  |  |     return type + FileHandler.STR_BLANK + " Operation 'neu' fuer bereits existierende Entitaet."; | 
 |  |  |   } | 
 |  |  |   */ | 
 |  |  |    | 
 |  |  |   /* | 
 |  |  |   private String aendern(HttpExchange e) throws IOException { | 
 |  |  |     return neu(e); // einstweilen wird einfach ueberschrieben | 
 |  |  |   } | 
 |  |  |   */ | 
 |  |  |    | 
 |  |  |   private boolean loeschen(HttpExchange e) { | 
 |  |  |     String path = e.getRequestURI().toString(); | 
 |  |  |     String[] elems = path.split(App.getRs(Server.RB_SLASH)); | 
 |  |  |     String[] elems = path.split(Server.SLASH); | 
 |  |  |     String type = elems[elems.length - 2]; | 
 |  |  |     String elemName = elems[elems.length - 1]; | 
 |  |  |     FileStorage fs = new FileStorage(App.getInitParameter(App.getRs(App.RB_AP_CONF))); | 
 |  |  |     return fs.delete(type, elemName); | 
 |  |  |   } | 
 |  |  |    | 
 |  |  |   private String lesen(HttpExchange e) { | 
 |  |  |   private String lesen(HttpExchange e) throws IOException, InterruptedException { | 
 |  |  |     String path = e.getRequestURI().toString(); | 
 |  |  |     String[] elems = path.split(App.getRs(Server.RB_SLASH)); | 
 |  |  |     String[] elems = path.split(Server.SLASH); | 
 |  |  |     FileStorage fs = new FileStorage(App.getInitParameter(App.getRs(App.RB_AP_CONF))); | 
 |  |  |     if(path.endsWith(App.getRs(RB_SLASH))) { | 
 |  |  |     if(path.endsWith(Server.SLASH)) { | 
 |  |  |       List list = null; | 
 |  |  |       if(elems[elems.length - 1].equalsIgnoreCase(App.getRs(RB_EP_LISTE))) { | 
 |  |  |       if(elems[elems.length - 1].equalsIgnoreCase(App.getRs(RB_EP_LISTE_ALLES))) { | 
 |  |  |         String type = elems[elems.length - 2]; | 
 |  |  |         logger.fine(type); | 
 |  |  |         if(type.equalsIgnoreCase(ST_GERAET)) { | 
 |  |  |           list = collectDeviceStatus(fs, type); | 
 |  |  |           Gson gson = new Gson(); | 
 |  |  |           Object o = gson.fromJson(bodyLesen(e), fs.typeFromName(type).getType()); | 
 |  |  |           return gson.toJson(list); | 
 |  |  |         } | 
 |  |  |       } else if(elems[elems.length - 1].equalsIgnoreCase(App.getRs(RB_EP_LISTE))) { | 
 |  |  |         String type = elems[elems.length - 2]; | 
 |  |  |         logger.fine(type); | 
 |  |  |         list = fs.list(type); | 
 |  |  |         if(type.equalsIgnoreCase(ST_ABLAGEORT)) { | 
 |  |  |           list.add("Livestreams"); | 
 |  |  |         } | 
 |  |  |       } else { | 
 |  |  |         String type = elems[elems.length - 1]; | 
 |  |  |         logger.fine(type); | 
 |  |  | 
 |  |  |     } | 
 |  |  |   } | 
 |  |  |    | 
 |  |  |   private String bodyLesen(HttpExchange e) throws IOException { | 
 |  |  |     InputStream is = e.getRequestBody(); | 
 |  |  |     BufferedReader r = new BufferedReader(new InputStreamReader(is)); | 
 |  |  |     StringBuilder sb = new StringBuilder(); | 
 |  |  |     String line = r.readLine(); | 
 |  |  |     while(line != null) { | 
 |  |  |       sb.append(line); | 
 |  |  |       line = r.readLine(); | 
 |  |  |   @Override | 
 |  |  |   public String get(HttpExchange e) { | 
 |  |  |     try { | 
 |  |  |       return lesen(e); | 
 |  |  |     } catch (IOException | InterruptedException ex) { | 
 |  |  |       Logger.getLogger(StorageHandler.class.getName()).log(Level.SEVERE, null, ex); | 
 |  |  |       return ex.getLocalizedMessage(); | 
 |  |  |     } | 
 |  |  |     r.close(); | 
 |  |  |     String json = sb.toString(); | 
 |  |  |     logger.log(Level.FINE, "json: {0}", json); | 
 |  |  |     return json; | 
 |  |  |   } | 
 |  |  |  | 
 |  |  |   @Override | 
 |  |  |   public String post(HttpExchange e) { | 
 |  |  |     return "nicht unterstuetzt"; | 
 |  |  |   } | 
 |  |  |  | 
 |  |  |   @Override | 
 |  |  |   public boolean delete(HttpExchange e) { | 
 |  |  |     return loeschen(e); | 
 |  |  |   } | 
 |  |  |    | 
 |  |  |   private List collectDeviceStatus(Storage fs, String type) throws IOException, InterruptedException { | 
 |  |  |     List list = fs.listObjects(type); | 
 |  |  |     List<Geraet> newList = new ArrayList(); | 
 |  |  |     Iterator<Entity> i = list.iterator(); | 
 |  |  |     while (i.hasNext()) { | 
 |  |  |       Entity entity = i.next(); | 
 |  |  |       if (entity instanceof Geraet) { | 
 |  |  |         Geraet g = (Geraet) entity; | 
 |  |  |         String statusurl = g.getStatusUrl(); | 
 |  |  |         logger.info(statusurl); | 
 |  |  |  | 
 |  |  |         HttpRequest request = HttpRequest.newBuilder() | 
 |  |  |                 .uri(URI.create(statusurl)) | 
 |  |  |                 .build(); | 
 |  |  |         HttpClient client = HttpClient.newBuilder() | 
 |  |  |                 .version(Version.HTTP_1_1) | 
 |  |  |                 .followRedirects(Redirect.NORMAL) | 
 |  |  |                 .connectTimeout(Duration.ofSeconds(20)) | 
 |  |  |                 //.proxy(ProxySelector.of(new InetSocketAddress("proxy.example.com", 80))) | 
 |  |  |                 //.authenticator(Authenticator.getDefault()) | 
 |  |  |                 .build(); | 
 |  |  |         HttpResponse<String> response = client.send(request, BodyHandlers.ofString()); | 
 |  |  |         logger.finer(Integer.toString(response.statusCode())); | 
 |  |  |         logger.finer(response.body()); | 
 |  |  |         // {"ison":false,"has_timer":false,"overpower":false} | 
 |  |  |         String[] parts = response.body().split(",")[0].split(":"); | 
 |  |  |         logger.finer("ison: " + parts[1]); | 
 |  |  |         g.setStatus(Boolean.parseBoolean(parts[1])); | 
 |  |  |         newList.add(g); | 
 |  |  |       } | 
 |  |  |     } | 
 |  |  |     return newList; | 
 |  |  |   } | 
 |  |  | } |