// Uses FILIUS' own web browser and web server windows // (GUIApplicationWebBrowserWindow, GUIApplicationWebServerWindow on their // GUIDesktopPanels) on web-lan's network as a person does: an address typed // into the browser's address line and Enter pressed, a link clicked (the // event Swing's HTMLEditorKit makes of it: the href against the document's // base), a form's button pressed (the real button Swing draws for it), and // on the server's window the start button, the virtual hosts' check box and // table. After each step it writes what the windows show: the browser's // address line and title, the page its program handed the window; the // server's button, check box, table and log, and the requests it got. The // reference for the editor's browser and web server windows // (packages/sim/test/browserWindow.test.ts). // // Usage: java -cp filius.jar:lib/*:classes BrowserWindowRun import filius.gui.anwendungssicht.GUIApplicationWebBrowserWindow; import filius.gui.anwendungssicht.GUIApplicationWebServerWindow; import filius.gui.anwendungssicht.GUIApplicationWindow; import filius.gui.anwendungssicht.GUIDesktopPanel; import filius.software.system.Betriebssystem; import filius.software.system.Datei; import filius.software.system.Dateisystem; import filius.software.www.HTTPNachricht; import filius.software.www.WebBrowser; import filius.software.www.WebServer; import java.awt.Component; import java.awt.Container; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.io.FileOutputStream; import java.lang.reflect.Field; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.swing.AbstractButton; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JEditorPane; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.SwingUtilities; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; import javax.swing.text.html.HTMLDocument; public class BrowserWindowRun { static final String BROWSER = "00000000-0000-0000-0000-000000000201"; static final String SERVER = "00000000-0000-0000-0000-000000000203"; static final List steps = new ArrayList<>(); /** What the browser's program handed its window since the last step. */ static final List pages = new ArrayList<>(); /** The requests the server got since the last step (its ">>" lines). */ static final List requests = new ArrayList<>(); static Object field(Class owner, Object object, String name) throws Exception { Field field = owner.getDeclaredField(name); field.setAccessible(true); return field.get(object); } static String title(GUIApplicationWindow window) throws Exception { return ((JLabel) field(GUIApplicationWindow.class, window, "titleLabel")).getText(); } static void browserStep(GUIApplicationWebBrowserWindow window, String step) throws Exception { Map result = new LinkedHashMap<>(); result.put("step", step); result.put("address", ((JTextField) field(GUIApplicationWebBrowserWindow.class, window, "urlFeld")).getText()); result.put("title", title(window)); synchronized (pages) { result.put("pages", new ArrayList<>(pages)); pages.clear(); } synchronized (requests) { result.put("requests", new ArrayList<>(requests)); requests.clear(); } steps.add(result); } static void serverStep(GUIApplicationWebServerWindow window, WebServer server, Dateisystem files, String step) throws Exception { Map result = new LinkedHashMap<>(); result.put("step", step); result.put("title", title(window)); result.put("button", ((JButton) field(GUIApplicationWebServerWindow.class, window, "buttonStart")).getText()); result.put("virtualHosts", ((JCheckBox) field(GUIApplicationWebServerWindow.class, window, "showVHosts")).isSelected()); result.put("serverVirtualHosts", server.isUseVHost()); List rows = new ArrayList<>(); for (String[] row : server.getVHostArray()) rows.add(Arrays.asList(row[0], row[1])); result.put("table", rows); Datei vhosts = files.holeDatei("/www.conf/vhosts"); result.put("vhostsFile", vhosts == null ? null : vhosts.getDateiInhalt()); result.put("log", ((JTextArea) field(GUIApplicationWebServerWindow.class, window, "logArea")).getText()); steps.add(result); } static void type(GUIApplicationWebBrowserWindow window, String typed) throws Exception { JTextField address = (JTextField) field(GUIApplicationWebBrowserWindow.class, window, "urlFeld"); SwingUtilities.invokeAndWait(() -> { address.setText(typed); KeyEvent event = new KeyEvent(address, KeyEvent.KEY_PRESSED, System.currentTimeMillis(), 0, KeyEvent.VK_ENTER, KeyEvent.CHAR_UNDEFINED); for (KeyListener listener : address.getKeyListeners()) listener.keyPressed(event); }); settle(); } /** Waits until the browser's program handed its window a page and its images came (or 25 seconds passed: nothing loads). */ static void settle() throws Exception { long until = System.currentTimeMillis() + 25000; int known = -1; long quiet = 0; while (System.currentTimeMillis() < until) { Thread.sleep(100); int count; boolean page; synchronized (pages) { count = pages.size(); page = pages.stream().anyMatch(each -> !each.equals("update")); } synchronized (requests) { count += requests.size(); } if (count != known) { known = count; quiet = System.currentTimeMillis(); } else if (page && System.currentTimeMillis() - quiet > 2500) { return; } } } /** A click on a link: HTMLEditorKit.LinkController's event, the href against the document's base. */ static void follow(GUIApplicationWebBrowserWindow window, String href) throws Exception { JEditorPane pane = (JEditorPane) field(GUIApplicationWebBrowserWindow.class, window, "anzeigeFeld"); SwingUtilities.invokeAndWait(() -> { try { URL base = ((HTMLDocument) pane.getDocument()).getBase(); HyperlinkEvent event = new HyperlinkEvent(pane, HyperlinkEvent.EventType.ACTIVATED, new URL(base, href), href); for (HyperlinkListener listener : pane.getHyperlinkListeners()) listener.hyperlinkUpdate(event); } catch (Exception error) { throw new RuntimeException(error); } }); settle(); } static void reload(GUIApplicationWebBrowserWindow window) throws Exception { type(window, "www.filius.de/links.html"); synchronized (pages) { pages.clear(); } synchronized (requests) { requests.clear(); } } static AbstractButton find(Container container, String text) { for (Component component : container.getComponents()) { if (component instanceof AbstractButton && text.equals(((AbstractButton) component).getText())) return (AbstractButton) component; if (component instanceof Container) { AbstractButton found = find((Container) component, text); if (found != null) return found; } } return null; } /** A press on the button Swing drew for a form's submit input. */ static void press(GUIApplicationWebBrowserWindow window, String label) throws Exception { JEditorPane pane = (JEditorPane) field(GUIApplicationWebBrowserWindow.class, window, "anzeigeFeld"); SwingUtilities.invokeAndWait(() -> { pane.revalidate(); pane.doLayout(); }); Thread.sleep(500); AbstractButton[] button = new AbstractButton[1]; SwingUtilities.invokeAndWait(() -> button[0] = find(pane, label)); if (button[0] == null) throw new IllegalStateException("no button " + label); SwingUtilities.invokeAndWait(() -> button[0].doClick()); settle(); } static void file(Dateisystem files, String directory, String name, String type, String content) { files.speicherDatei(directory, new Datei(name, type, content)); } public static void main(String[] args) throws Exception { try { run(args); } catch (Throwable error) { error.printStackTrace(); System.exit(1); } System.exit(0); } static void run(String[] args) throws Exception { ScenarioNetwork network = ScenarioNetwork.read(args[0]); Betriebssystem serverSystem = (Betriebssystem) network.nodes.get(SERVER).getSystemSoftware(); Betriebssystem browserSystem = (Betriebssystem) network.nodes.get(BROWSER).getSystemSoftware(); serverSystem.installApp("filius.software.www.WebServer", false); browserSystem.installApp("filius.software.www.WebBrowser", false); WebServer server = (WebServer) serverSystem.holeSoftware("filius.software.www.WebServer"); WebBrowser browser = (WebBrowser) browserSystem.holeSoftware("filius.software.www.WebBrowser"); Dateisystem files = serverSystem.getDateisystem(); String image = files.holeDatei("/webserver/splashscreen-mini.png").getDateiInhalt(); file(files, "/webserver", "links.html", "html", "Links & mehr\n" + "rel abs host\n" + "folder query anchor\n" + "
\n" + "
" + "" + "
\n" + ""); file(files, "/webserver", "ziel.html", "html", "ZielZiel"); file(files, "/webserver", "ohne.html", "html", "Ohne Titel"); file(files, "/webserver", "text.txt", "txt", "Nur Text\nzwei Zeilen"); file(files, "/webserver", "bild.png", "png", image); files.erstelleVerzeichnis("/webserver", "ordner"); file(files, "/webserver/ordner", "drei.html", "html", "Dreivier"); file(files, "/webserver/ordner", "vier.html", "html", "Vier im Ordner"); file(files, "/webserver", "vier.html", "html", "Vier oben"); server.hinzuBeobachter((observable, data) -> { String text = String.valueOf(data); if (text.startsWith(">>")) synchronized (requests) { requests.add(text.substring(2)); } }); browser.hinzuBeobachter((observable, data) -> { synchronized (pages) { if (data instanceof HTTPNachricht) { HTTPNachricht page = (HTTPNachricht) data; pages.add(page.getStatusCode() + " " + page.getContentType() + "\n" + page.getDaten()); } else { pages.add("update"); } } }); network.start(); Thread.sleep(500); JFrame frame = new JFrame(); GUIDesktopPanel serverDesktop = new GUIDesktopPanel(serverSystem); GUIDesktopPanel browserDesktop = new GUIDesktopPanel(browserSystem); frame.getContentPane().setLayout(new java.awt.FlowLayout()); frame.getContentPane().add(serverDesktop); frame.getContentPane().add(browserDesktop); GUIApplicationWebServerWindow serverWindow = (GUIApplicationWebServerWindow) serverDesktop.starteAnwendung("filius.software.www.WebServer"); GUIApplicationWebBrowserWindow window = (GUIApplicationWebBrowserWindow) browserDesktop.starteAnwendung("filius.software.www.WebBrowser"); SwingUtilities.invokeAndWait(() -> { frame.pack(); frame.setVisible(true); }); Thread.sleep(500); serverStep(serverWindow, server, files, "server:open"); browserStep(window, "open"); type(window, "192.168.0.30"); browserStep(window, "type:192.168.0.30 (server off)"); // The links page loaded anew before each link and button, without a step of its own. SwingUtilities.invokeAndWait(() -> { try { ((JButton) field(GUIApplicationWebServerWindow.class, serverWindow, "buttonStart")).doClick(); } catch (Exception error) { throw new RuntimeException(error); } }); Thread.sleep(500); serverStep(serverWindow, server, files, "server:start"); for (String typed : new String[] { "192.168.0.30", "www.filius.de/ohne.html", "kein host!", "http://www.filius.de/nichts.html", "leer.filius.de", "www.filius.de/text.txt", "www.filius.de/links.html?x=1", "www.filius.de/links.html" }) { type(window, typed); browserStep(window, "type:" + typed); } serverStep(serverWindow, server, files, "server:after pages"); for (String href : new String[] { "ziel.html", "/text.txt", "http://192.168.0.30/", "ordner/drei.html", "vier.html" }) { follow(window, href); browserStep(window, "follow:" + href); reload(window); } for (String href : new String[] { "ziel.html?a=1&b=2", "#oben" }) { follow(window, href); browserStep(window, "follow:" + href); reload(window); } press(window, "Los"); browserStep(window, "press:Los"); reload(window); press(window, "Gut"); browserStep(window, "press:Gut"); // The virtual hosts: switched on, rows written as the table's editor does (changeVHostTable), a stop and a start. JCheckBox box = (JCheckBox) field(GUIApplicationWebServerWindow.class, serverWindow, "showVHosts"); SwingUtilities.invokeAndWait(box::doClick); Thread.sleep(300); serverStep(serverWindow, server, files, "server:virtual hosts on"); String[][] cells = { { "0", "0", "seite.filius.de" }, { "0", "1", "vhost1" }, { "1", "0", "nurhost.filius.de" }, { "2", "1", "nurordner" }, { "3", "0", "andere.filius.de" }, { "3", "1", "vhost2" } }; for (String[] cell : cells) server.changeVHostTable(Integer.parseInt(cell[0]), Integer.parseInt(cell[1]), cell[2]); serverStep(serverWindow, server, files, "server:rows written"); serverSystem.beenden(); serverSystem.starten(); Thread.sleep(500); serverStep(serverWindow, server, files, "server:restart"); SwingUtilities.invokeAndWait(box::doClick); Thread.sleep(300); serverStep(serverWindow, server, files, "server:virtual hosts off"); serverSystem.beenden(); serverSystem.starten(); Thread.sleep(500); serverStep(serverWindow, server, files, "server:restart after off"); SwingUtilities.invokeAndWait(() -> { try { ((JButton) field(GUIApplicationWebServerWindow.class, serverWindow, "buttonStart")).doClick(); } catch (Exception error) { throw new RuntimeException(error); } }); Thread.sleep(1000); serverStep(serverWindow, server, files, "server:stop"); Map output = new LinkedHashMap<>(); output.put("steps", steps); try (FileOutputStream out = new FileOutputStream(args[1])) { out.write((FlsOracle.Json.write(output, "") + "\n").getBytes(StandardCharsets.UTF_8)); } network.stop(); System.out.println("browser window steps: " + steps.size()); } }