The oracle BrowserWindowRun uses Java's browser and web server windows as a person does; the room keeps a browser window per computer and sends a program window's view, the start names the person's language. The editor shows the page in a sandboxed frame, links and forms go to the room, the web server has its button, virtual hosts and lines. Better than Java: relative images and links count from the page's folder, a mark stays in the page, the error page from Java's template, the virtual hosts' file with complete rows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
339 lines
16 KiB
Java
339 lines
16 KiB
Java
// 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 <scenario.json> <output json>
|
|
|
|
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<Object> steps = new ArrayList<>();
|
|
/** What the browser's program handed its window since the last step. */
|
|
static final List<String> pages = new ArrayList<>();
|
|
/** The requests the server got since the last step (its ">>" lines). */
|
|
static final List<String> 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<String, Object> 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<String, Object> 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<Object> 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", "<html><head><title>Links & mehr</title></head><body>\n"
|
|
+ "<a href=\"ziel.html\">rel</a> <a href=\"/text.txt\">abs</a> <a href=\"http://192.168.0.30/\">host</a>\n"
|
|
+ "<a href=\"ordner/drei.html\">folder</a> <a href=\"ziel.html?a=1&b=2\">query</a> <a href=\"#oben\">anchor</a>\n"
|
|
+ "<form action=\"ziel.html\" method=\"get\"><input name=\"q\" value=\"a b\"><input type=\"submit\" name=\"los\" value=\"Los\"></form>\n"
|
|
+ "<form action=\"ziel.html\" method=\"post\"><input name=\"name\" value=\"Fil ius\"><input type=\"checkbox\" name=\"c\" value=\"1\" checked>"
|
|
+ "<input type=\"checkbox\" name=\"d\" value=\"1\"><select name=\"s\"><option>x</option><option selected value=\"y\">Y</option></select>"
|
|
+ "<textarea name=\"t\">ü&=</textarea><input type=\"submit\" name=\"ok\" value=\"Gut\"></form>\n"
|
|
+ "</body></html>");
|
|
file(files, "/webserver", "ziel.html", "html", "<html><head><title>Ziel</title></head><body>Ziel</body></html>");
|
|
file(files, "/webserver", "ohne.html", "html", "<html><body>Ohne Titel</body></html>");
|
|
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", "<html><head><title>Drei</title></head><body><a href=\"vier.html\">vier</a><img src=\"bild.png\"></body></html>");
|
|
file(files, "/webserver/ordner", "vier.html", "html", "<html><body>Vier im Ordner</body></html>");
|
|
file(files, "/webserver", "vier.html", "html", "<html><body>Vier oben</body></html>");
|
|
|
|
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<String, Object> 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());
|
|
}
|
|
}
|