diff --git a/superx/edit/kern/etl_manager.jsp b/superx/edit/kern/etl_manager.jsp
new file mode 100644
index 0000000..923fb56
--- /dev/null
+++ b/superx/edit/kern/etl_manager.jsp
@@ -0,0 +1,744 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
+<%@ page import="java.io.File" %>
+<%@ page import="java.io.BufferedReader" %>
+<%@ page import="java.io.InputStreamReader" %>
+<%@ page import="java.lang.management.ManagementFactory" %>
+<%@ page import="java.sql.Connection" %>
+<%@ page import="java.sql.PreparedStatement" %>
+<%@ page import="java.sql.ResultSet" %>
+<%@ page import="java.sql.SQLException" %>
+<%@ page import="java.sql.Statement" %>
+<%@ page import="java.text.SimpleDateFormat" %>
+<%@ page import="java.util.Date" %>
+<%@ page import="java.util.List" %>
+<%@ page import="java.util.LinkedHashMap" %>
+<%@ page import="java.util.Map" %>
+<%@ page import="javax.naming.InitialContext" %>
+<%@ page import="javax.sql.DataSource" %>
+<%@ page import="jakarta.servlet.ServletContext" %>
+<%@ page import="javax.xml.parsers.DocumentBuilder" %>
+<%@ page import="javax.xml.parsers.DocumentBuilderFactory" %>
+<%@ page import="org.w3c.dom.Document" %>
+<%!
+private String html(Object value) {
+ if (value == null) return "";
+ String s = String.valueOf(value);
+ return s.replace("&", "&")
+ .replace("<", "<")
+ .replace(">", ">")
+ .replace(String.valueOf((char)34), """)
+ .replace("'", "'");
+}
+
+private String tag(String text, String type) {
+ String cls = "is-light";
+ if ("ok".equals(type)) cls = "is-success is-light";
+ else if ("warn".equals(type)) cls = "is-warning is-light";
+ else if ("error".equals(type)) cls = "is-danger is-light";
+ else if ("info".equals(type)) cls = "is-info is-light";
+ return "" + html(text) + " ";
+}
+
+private String formatBytes(long bytes) {
+ if (bytes < 0) return "-";
+ String[] units = {"B", "KB", "MB", "GB", "TB"};
+ double value = (double) bytes;
+ int idx = 0;
+ while (value >= 1024.0 && idx < units.length - 1) {
+ value = value / 1024.0;
+ idx++;
+ }
+ if (idx == 0) return String.valueOf(bytes) + " " + units[idx];
+ return String.format(java.util.Locale.GERMANY, "%.1f %s", value, units[idx]);
+}
+
+private String nullText(Object value) {
+ if (value == null || String.valueOf(value).length() == 0) {
+ return "nicht sichtbar / nicht gesetzt ";
+ }
+ return html(value);
+}
+
+private String jsString(Object value) {
+ if (value == null) return "";
+ String s = String.valueOf(value);
+ return s.replace("\\", "\\\\")
+ .replace("\r", "\\r")
+ .replace("\n", "\\n")
+ .replace("\t", "\\t")
+ .replace("\"", "\\\"");
+}
+
+private String joinPath(String base, String child) {
+ if (base == null || base.length() == 0) return null;
+ return new File(base, child).getPath();
+}
+
+private String fileCheckRow(String label, String path) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("
").append(html(label)).append(" ");
+ if (path == null || path.length() == 0) {
+ sb.append("nicht ermittelbar ");
+ sb.append(" ").append(tag("unbekannt", "warn")).append(" ");
+ return sb.toString();
+ }
+ File f = new File(path);
+ sb.append("").append(html(path)).append(" ");
+ sb.append("");
+ sb.append(f.exists() ? tag("vorhanden", "ok") : tag("fehlt", "warn"));
+ sb.append(" ");
+ if (f.exists()) {
+ sb.append("lesbar: ").append(f.canRead() ? "ja" : "nein");
+ sb.append(", schreibbar: ").append(f.canWrite() ? "ja" : "nein");
+ if (f.isDirectory()) sb.append(", betretbar: ").append(f.canExecute() ? "ja" : "nein");
+ }
+ sb.append(" ");
+ return sb.toString();
+}
+
+private boolean tableExistsByRegclass(Connection con, String regclassName) {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+ try {
+ ps = con.prepareStatement("select to_regclass(?) is not null");
+ ps.setString(1, regclassName);
+ rs = ps.executeQuery();
+ return rs.next() && rs.getBoolean(1);
+ } catch (Exception e) {
+ return false;
+ } finally {
+ try { if (rs != null) rs.close(); } catch (Exception e) { }
+ try { if (ps != null) ps.close(); } catch (Exception e) { }
+ }
+}
+
+private boolean hasDbVersionTable(Connection con) {
+ return tableExistsByRegclass(con, "db_version") || tableExistsByRegclass(con, "public.db_version");
+}
+
+private Connection connectionFromObject(Object obj, boolean requireDbVersion) throws Exception {
+ if (!(obj instanceof DataSource)) return null;
+ Connection con = null;
+ try {
+ con = ((DataSource) obj).getConnection();
+ if (!requireDbVersion || hasDbVersionTable(con)) return con;
+ } catch (Exception e) {
+ try { if (con != null) con.close(); } catch (Exception ex) { }
+ throw e;
+ }
+ try { if (con != null) con.close(); } catch (Exception e) { }
+ return null;
+}
+
+private Object springBean(Object wac, String beanName) {
+ try {
+ java.lang.reflect.Method getBean = wac.getClass().getMethod("getBean", String.class);
+ return getBean.invoke(wac, beanName);
+ } catch (Exception e) {
+ return null;
+ }
+}
+
+private Connection connectionFromMap(Object obj, boolean requireDbVersion) throws Exception {
+ if (!(obj instanceof Map)) return null;
+ Map map = (Map) obj;
+ String[] preferredKeys = {"superx", "SuperX", "default", "hisinone", "eduetl"};
+ for (int i = 0; i < preferredKeys.length; i++) {
+ if (map.containsKey(preferredKeys[i])) {
+ Connection c = connectionFromObject(map.get(preferredKeys[i]), requireDbVersion);
+ if (c != null) return c;
+ }
+ }
+ for (Object value : map.values()) {
+ Connection c = connectionFromObject(value, requireDbVersion);
+ if (c != null) return c;
+ }
+ return null;
+}
+
+private Connection findSpringConnection(ServletContext application, boolean requireDbVersion) throws Exception {
+ Object wac = null;
+ try {
+ Class util = Class.forName("org.springframework.web.context.support.WebApplicationContextUtils");
+ java.lang.reflect.Method m = util.getMethod("getWebApplicationContext", ServletContext.class);
+ wac = m.invoke(null, application);
+ } catch (Exception e) {
+ wac = null;
+ }
+ if (wac == null) return null;
+
+ String[] beanNames = {"dataSources", "superxDataSource", "dataSource", "eduetlDataSource", "hisinoneDataSource"};
+ for (int i = 0; i < beanNames.length; i++) {
+ Object bean = springBean(wac, beanNames[i]);
+ Connection c = connectionFromMap(bean, requireDbVersion);
+ if (c != null) return c;
+ c = connectionFromObject(bean, requireDbVersion);
+ if (c != null) return c;
+ }
+
+ try {
+ java.lang.reflect.Method namesMethod = wac.getClass().getMethod("getBeanNamesForType", Class.class);
+ String[] names = (String[]) namesMethod.invoke(wac, DataSource.class);
+ if (names != null) {
+ for (int i = 0; i < names.length; i++) {
+ Object bean = springBean(wac, names[i]);
+ Connection c = connectionFromObject(bean, requireDbVersion);
+ if (c != null) return c;
+ }
+ }
+ } catch (Exception e) { }
+ return null;
+}
+
+private Connection findJndiConnection(boolean requireDbVersion) throws Exception {
+ String[] jndiNames = {
+ "java:comp/env/jdbc/superx",
+ "java:comp/env/jdbc/eduetl",
+ "java:comp/env/jdbc/hisinone",
+ "jdbc/superx",
+ "jdbc/eduetl",
+ "jdbc/hisinone"
+ };
+ InitialContext ctx = null;
+ try { ctx = new InitialContext(); } catch (Exception e) { ctx = null; }
+ if (ctx == null) return null;
+ for (int i = 0; i < jndiNames.length; i++) {
+ try {
+ Object obj = ctx.lookup(jndiNames[i]);
+ Connection c = connectionFromObject(obj, requireDbVersion);
+ if (c != null) return c;
+ } catch (Exception e) { }
+ }
+ return null;
+}
+
+private Connection findServletContextConnection(ServletContext application, boolean requireDbVersion) throws Exception {
+ String[] attrNames = {"dataSources", "dataSource", "datasource", "superxDataSource", "eduetlDataSource", "javax.sql.DataSource"};
+ for (int i = 0; i < attrNames.length; i++) {
+ try {
+ Object obj = application.getAttribute(attrNames[i]);
+ Connection c = connectionFromMap(obj, requireDbVersion);
+ if (c != null) return c;
+ c = connectionFromObject(obj, requireDbVersion);
+ if (c != null) return c;
+ } catch (Exception e) { }
+ }
+ return null;
+}
+
+private Connection openDiagnosticConnection(ServletContext application) throws Exception {
+ // Erste Runde: bevorzugt eine Verbindung, in der db_version sichtbar ist.
+ Connection c = findSpringConnection(application, true);
+ if (c != null) return c;
+ c = findJndiConnection(true);
+ if (c != null) return c;
+ c = findServletContextConnection(application, true);
+ if (c != null) return c;
+
+ // Zweite Runde: irgendeine verfügbare Verbindung, damit Diagnose nicht komplett ausfällt.
+ c = findSpringConnection(application, false);
+ if (c != null) return c;
+ c = findJndiConnection(false);
+ if (c != null) return c;
+ c = findServletContextConnection(application, false);
+ if (c != null) return c;
+
+ throw new SQLException("Keine DataSource über Spring, JNDI oder ServletContext gefunden. REST-Checks und Dateisystem-Checks bleiben trotzdem verfügbar.");
+}
+
+private String sqlColumnRows(Connection con, String tableSchema, String tableName) throws Exception {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+ StringBuilder sb = new StringBuilder();
+ try {
+ ps = con.prepareStatement("select column_name, data_type from information_schema.columns where table_schema = ? and table_name = ? order by ordinal_position");
+ ps.setString(1, tableSchema);
+ ps.setString(2, tableName);
+ rs = ps.executeQuery();
+ while (rs.next()) {
+ sb.append("").append(html(rs.getString(1))).append(" ").append(html(rs.getString(2))).append(" ");
+ }
+ } finally {
+ try { if (rs != null) rs.close(); } catch (Exception e) { }
+ try { if (ps != null) ps.close(); } catch (Exception e) { }
+ }
+ return sb.toString();
+}
+private String readTrimmedFile(File f) {
+ if (f == null || !f.exists() || !f.isFile() || !f.canRead()) return "";
+ StringBuilder sb = new StringBuilder();
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new InputStreamReader(new java.io.FileInputStream(f), "UTF-8"));
+ String line;
+ while ((line = br.readLine()) != null) {
+ sb.append(line).append("\n");
+ }
+ } catch (Exception e) {
+ return "";
+ } finally {
+ try { if (br != null) br.close(); } catch (Exception e) { }
+ }
+ return sb.toString().trim();
+}
+
+private void disableXmlFeature(DocumentBuilderFactory factory, String feature) {
+ try { factory.setFeature(feature, false); } catch (Exception e) { }
+}
+
+private String readModuleXmlVersion(File xmlFile) {
+ if (xmlFile == null || !xmlFile.exists() || !xmlFile.isFile() || !xmlFile.canRead()) return "";
+ try {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setValidating(false);
+ factory.setNamespaceAware(false);
+ disableXmlFeature(factory, "http://apache.org/xml/features/nonvalidating/load-external-dtd");
+ disableXmlFeature(factory, "http://xml.org/sax/features/external-general-entities");
+ disableXmlFeature(factory, "http://xml.org/sax/features/external-parameter-entities");
+ try { factory.setXIncludeAware(false); } catch (Exception e) { }
+ try { factory.setExpandEntityReferences(false); } catch (Exception e) { }
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document doc = builder.parse(xmlFile);
+ if (doc != null && doc.getDocumentElement() != null) {
+ String version = doc.getDocumentElement().getAttribute("version");
+ if (version != null) return version.trim();
+ }
+ } catch (Exception e) {
+ return "";
+ }
+ return "";
+}
+
+private void addAvailableVersionInfo(Map map, String abbr, String xmlPath, String versionPath) {
+ if (abbr == null || abbr.trim().length() == 0) return;
+ String key = abbr.trim().toLowerCase();
+ Map info = new LinkedHashMap();
+ File xmlFile = xmlPath == null ? null : new File(xmlPath);
+ File versionFile = versionPath == null ? null : new File(versionPath);
+ String xmlVersion = readModuleXmlVersion(xmlFile);
+ String fileVersion = readTrimmedFile(versionFile);
+ info.put("xmlPath", xmlPath == null ? "" : xmlPath);
+ info.put("xmlVersion", xmlVersion == null ? "" : xmlVersion);
+ info.put("versionFilePath", versionPath == null ? "" : versionPath);
+ info.put("versionFileVersion", fileVersion == null ? "" : fileVersion);
+ if (xmlVersion != null && xmlVersion.length() > 0) {
+ info.put("version", xmlVersion);
+ info.put("source", "module_xml");
+ } else if (fileVersion != null && fileVersion.length() > 0) {
+ info.put("version", fileVersion);
+ info.put("source", "version_file");
+ } else {
+ info.put("version", "");
+ info.put("source", "");
+ }
+ map.put(key, info);
+}
+
+private String availableVersionJson(Map map) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("{");
+ boolean first = true;
+ for (Object entryObj : map.entrySet()) {
+ Map.Entry entry = (Map.Entry) entryObj;
+ if (!first) sb.append(",");
+ first = false;
+ String key = String.valueOf(entry.getKey());
+ Map info = (Map) entry.getValue();
+ sb.append("\"").append(jsString(key)).append("\":{");
+ String[] props = {"version", "source", "xmlVersion", "xmlPath", "versionFileVersion", "versionFilePath"};
+ for (int i = 0; i < props.length; i++) {
+ if (i > 0) sb.append(",");
+ Object val = info.get(props[i]);
+ sb.append("\"").append(props[i]).append("\":\"").append(jsString(val == null ? "" : String.valueOf(val))).append("\"");
+ }
+ sb.append("}");
+ }
+ sb.append("}");
+ return sb.toString();
+}
+
+private String availableVersionRows(Map map) {
+ StringBuilder sb = new StringBuilder();
+ for (Object entryObj : map.entrySet()) {
+ Map.Entry entry = (Map.Entry) entryObj;
+ String key = String.valueOf(entry.getKey());
+ Map info = (Map) entry.getValue();
+ sb.append("").append(html(key)).append(" ");
+ sb.append("").append(info.get("xmlVersion") == null || String.valueOf(info.get("xmlVersion")).length() == 0 ? "nicht gefunden " : html(info.get("xmlVersion"))).append(" ");
+ sb.append("").append(html(info.get("xmlPath"))).append(" ");
+ sb.append("").append(info.get("versionFileVersion") == null || String.valueOf(info.get("versionFileVersion")).length() == 0 ? "nicht gefunden " : html(info.get("versionFileVersion"))).append(" ");
+ sb.append("").append(html(info.get("versionFilePath"))).append(" ");
+ sb.append("").append(info.get("version") == null || String.valueOf(info.get("version")).length() == 0 ? "nicht geliefert " : html(info.get("version"))).append(" ");
+ }
+ return sb.toString();
+}
+%>
+<%
+String diagGeneratedAt = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss").format(new Date());
+String contextPath = request.getContextPath();
+String servletRealPath = application.getRealPath("/");
+String canonicalRealPath = "";
+try { canonicalRealPath = servletRealPath == null ? "" : new File(servletRealPath).getCanonicalPath(); }
+catch (Exception e) { canonicalRealPath = "nicht ermittelbar: " + e.getMessage(); }
+String webInfPath = joinPath(servletRealPath, "WEB-INF");
+String edustorePath = joinPath(webInfPath, "conf/edustore");
+String dbPath = joinPath(edustorePath, "db");
+String modulePath = joinPath(dbPath, "module");
+String kernInstallPath = joinPath(dbPath, "install");
+String kernXmlPath = joinPath(kernInstallPath, "conf/kern.xml");
+String kernVersionFilePath = joinPath(kernInstallPath, "VERSION");
+String kennPath = joinPath(modulePath, "kenn");
+String kennXmlPath = joinPath(kennPath, "conf/kenn.xml");
+String kennRohdatenPath = joinPath(kennPath, "rohdaten");
+String kennSchluesselPath = joinPath(kennPath, "schluesseltabellen");
+File diskBase = servletRealPath == null ? null : new File(servletRealPath);
+long diskTotal = diskBase == null ? -1 : diskBase.getTotalSpace();
+long diskFree = diskBase == null ? -1 : diskBase.getFreeSpace();
+long diskUsable = diskBase == null ? -1 : diskBase.getUsableSpace();
+boolean diskLow = diskUsable >= 0 && diskUsable < 1024L * 1024L * 1024L;
+List jvmArgsList = ManagementFactory.getRuntimeMXBean().getInputArguments();
+StringBuilder jvmArgsBuilder = new StringBuilder();
+for (int i = 0; i < jvmArgsList.size(); i++) {
+ if (i > 0) jvmArgsBuilder.append(System.lineSeparator());
+ jvmArgsBuilder.append(String.valueOf(jvmArgsList.get(i)));
+}
+String jvmArgs = jvmArgsBuilder.toString();
+boolean addOpensVisible = jvmArgs.indexOf("--add-opens=java.base/sun.net.www.protocol.jar=ALL-UNNAMED") >= 0;
+String dbDiagnosticMessage = "";
+String dbInfoHtml = "";
+String jobLogTableTag = tag("unbekannt", "warn");
+String jobLogColumnsHtml = "";
+boolean dbDirectAvailable = false;
+boolean jobLogExists = false;
+boolean jobLogHasJobInstanceId = false;
+boolean jobLogHasLog = false;
+boolean jobLogHasStatus = false;
+String dbVersionTableTag = tag("unbekannt", "warn");
+String dbVersionRowsHtml = "";
+String dbVersionFallbackJson = "{}";
+String availableVersionFallbackJson = "{}";
+String availableVersionRowsHtml = "";
+Map availableVersionMap = new LinkedHashMap();
+addAvailableVersionInfo(availableVersionMap, "kern", kernXmlPath, kernVersionFilePath);
+if (modulePath != null) {
+ File moduleDir = new File(modulePath);
+ File[] moduleDirs = moduleDir.exists() && moduleDir.isDirectory() ? moduleDir.listFiles() : null;
+ if (moduleDirs != null) {
+ for (int i = 0; i < moduleDirs.length; i++) {
+ File md = moduleDirs[i];
+ if (md != null && md.isDirectory()) {
+ String abbr = md.getName();
+ if (!"kern".equalsIgnoreCase(abbr)) {
+ addAvailableVersionInfo(availableVersionMap, abbr, joinPath(md.getPath(), "conf/" + abbr + ".xml"), joinPath(md.getPath(), "VERSION"));
+ }
+ }
+ }
+ }
+}
+availableVersionFallbackJson = availableVersionJson(availableVersionMap);
+availableVersionRowsHtml = availableVersionRows(availableVersionMap);
+boolean dbVersionExists = false;
+Connection diagCon = null;
+try {
+ diagCon = openDiagnosticConnection(application);
+ dbDirectAvailable = true;
+ Statement st = null;
+ ResultSet rs = null;
+ try {
+ st = diagCon.createStatement();
+ rs = st.executeQuery("select current_database(), current_schema(), version()");
+ if (rs.next()) {
+ dbInfoHtml = "Datenbank " + html(rs.getString(1)) + " " +
+ "Schema " + html(rs.getString(2)) + " " +
+ "DB-Version " + html(rs.getString(3)) + " ";
+ }
+ } catch (Exception e) {
+ dbInfoHtml = "Direkte DB-Information " + tag("nicht verfügbar", "warn") + " " + html(e.getMessage()) + " ";
+ } finally {
+ try { if (rs != null) rs.close(); } catch (Exception e) { }
+ try { if (st != null) st.close(); } catch (Exception e) { }
+ }
+ PreparedStatement ps = null;
+ rs = null;
+ try {
+ ps = diagCon.prepareStatement("select to_regclass('public.job_log') is not null");
+ rs = ps.executeQuery();
+ if (rs.next()) jobLogExists = rs.getBoolean(1);
+ jobLogTableTag = jobLogExists ? tag("vorhanden", "ok") : tag("fehlt", "warn");
+ } catch (Exception e) {
+ jobLogTableTag = tag("Prüfung fehlgeschlagen", "warn") + " " + html(e.getMessage());
+ } finally {
+ try { if (rs != null) rs.close(); } catch (Exception e) { }
+ try { if (ps != null) ps.close(); } catch (Exception e) { }
+ }
+ if (jobLogExists) {
+ jobLogColumnsHtml = sqlColumnRows(diagCon, "public", "job_log");
+ ps = null;
+ rs = null;
+ try {
+ ps = diagCon.prepareStatement("select column_name from information_schema.columns where table_schema = 'public' and table_name = 'job_log'");
+ rs = ps.executeQuery();
+ while (rs.next()) {
+ String c = rs.getString(1);
+ if ("job_instance_id".equalsIgnoreCase(c)) jobLogHasJobInstanceId = true;
+ if ("log".equalsIgnoreCase(c)) jobLogHasLog = true;
+ if ("status".equalsIgnoreCase(c)) jobLogHasStatus = true;
+ }
+ } finally {
+ try { if (rs != null) rs.close(); } catch (Exception e) { }
+ try { if (ps != null) ps.close(); } catch (Exception e) { }
+ }
+ }
+
+ ps = null;
+ rs = null;
+ try {
+ ps = diagCon.prepareStatement("select (to_regclass('db_version') is not null or to_regclass('public.db_version') is not null)");
+ rs = ps.executeQuery();
+ if (rs.next()) dbVersionExists = rs.getBoolean(1);
+ dbVersionTableTag = dbVersionExists ? tag("vorhanden", "ok") : tag("fehlt", "warn");
+ } catch (Exception e) {
+ dbVersionTableTag = tag("Prüfung fehlgeschlagen", "warn") + " " + html(e.getMessage());
+ } finally {
+ try { if (rs != null) rs.close(); } catch (Exception e) { }
+ try { if (ps != null) ps.close(); } catch (Exception e) { }
+ }
+ if (dbVersionExists) {
+ ps = null;
+ rs = null;
+ StringBuilder versionJson = new StringBuilder();
+ boolean firstVersion = true;
+ versionJson.append("{");
+ try {
+ ps = diagCon.prepareStatement("select trim(his_system) as his_system, trim(version) as version, systeminfo_id from db_version order by his_system, systeminfo_id");
+ rs = ps.executeQuery();
+ while (rs.next()) {
+ String sys = rs.getString(1);
+ String ver = rs.getString(2);
+ Integer sid = null;
+ try { sid = (Integer) rs.getObject(3); } catch (Exception e) { sid = null; }
+ if (sys != null && ver != null && sys.trim().length() > 0 && ver.trim().length() > 0) {
+ String key = sys.trim().toLowerCase(java.util.Locale.ROOT);
+ if (!firstVersion) versionJson.append(",");
+ versionJson.append("\"").append(jsString(key)).append("\":\"").append(jsString(ver.trim())).append("\"");
+ firstVersion = false;
+ if (sid != null) {
+ versionJson.append(",\"sid:").append(sid.intValue()).append("\":\"").append(jsString(ver.trim())).append("\"");
+ }
+ dbVersionRowsHtml += "" + html(sys.trim()) + " " + html(ver.trim()) + " " + html(sid == null ? "" : sid) + " ";
+ }
+ }
+ } catch (Exception e) {
+ dbVersionRowsHtml = "" + tag("Prüfung fehlgeschlagen", "warn") + " " + html(e.getMessage()) + " ";
+ } finally {
+ try { if (rs != null) rs.close(); } catch (Exception e) { }
+ try { if (ps != null) ps.close(); } catch (Exception e) { }
+ }
+ versionJson.append("}");
+ dbVersionFallbackJson = versionJson.toString();
+ }
+} catch (Exception e) {
+ dbDiagnosticMessage = e.getMessage();
+ dbInfoHtml = "Direkte DB-Prüfung " + tag("nicht verfügbar", "warn") + " " + html(e.getMessage()) + " ";
+} finally {
+ try { if (diagCon != null) diagCon.close(); } catch (Exception e) { }
+}
+%>
+
+
+
+
+
+SuperX ETL Manager
+
+
+
+
+
+
+
+
Alle Module Installiert Unload & Update möglich Unload möglich Update möglich DB verbunden DB nicht verbunden Aktualisieren Diagnose
+
+
+
Systemdiagnose Diagnose aktualisieren Schließen
+
Stand: <%= html(diagGeneratedAt) %>. Die Diagnose ändert nichts an Dateien oder Datenbankobjekten. SQL- und Shell-Befehle werden nur zum Kopieren angezeigt.
+
+
REST-Schnittstellen und aktuelle Oberfläche Noch nicht geprüft. Bitte Diagnose aktualisieren.
+
Versionen Noch nicht geprüft. Bitte Diagnose aktualisieren.
Verfügbare Versionen aus Modul-XML / VERSION-Dateien anzeigen Modul Modul-XML XML-Pfad VERSION-Datei VERSION-Pfad verwendbarer Fallback <%= availableVersionRowsHtml %>
Installierte Version: REST/JSON, sonst db_version. Verfügbare Version: REST/JSON, sonst version-Attribut der Modul-XML, sonst VERSION-Datei. Für kern wird db/install/conf/kern.xml verwendet.
+
Webapp / Tomcat / Java
+ Context Path <%= html(contextPath) %>
+ Servlet Real Path <%= html(servletRealPath) %>
+ Canonical Path <%= html(canonicalRealPath) %>
+ Server Info <%= html(application.getServerInfo()) %>
+ Java Version <%= html(System.getProperty("java.version")) %> / <%= html(System.getProperty("java.vendor")) %>
+ OS <%= html(System.getProperty("os.name")) %> <%= html(System.getProperty("os.version")) %> (<%= html(System.getProperty("os.arch")) %>)
+ user.name <%= html(System.getProperty("user.name")) %>
+ user.dir <%= html(System.getProperty("user.dir")) %>
+ catalina.base <%= html(System.getProperty("catalina.base")) %>
+ catalina.home <%= html(System.getProperty("catalina.home")) %>
+ JAVA_OPTS <%= nullText(System.getenv("JAVA_OPTS")) %>
+ CATALINA_OPTS <%= nullText(System.getenv("CATALINA_OPTS")) %>
+ JVM Input Arguments <%= html(jvmArgs) %>
+ Kettle/JarFileFactory add-opens <%= addOpensVisible ? tag("in JVM-Argumenten sichtbar", "ok") : tag("nicht sichtbar", "info") %>Nur Hinweis: relevant, wenn beim Laden/Ausführen der Jobs eine InaccessibleObjectException zu sun.net.www.protocol.jar auftritt.
+
+
Pfade und Dateisystem Prüfung Pfad Status Rechte
+ <%= fileCheckRow("WEB-INF", webInfPath) %><%= fileCheckRow("edustore", edustorePath) %><%= fileCheckRow("db", dbPath) %><%= fileCheckRow("db/install (Kernmodul)", kernInstallPath) %><%= fileCheckRow("db/install/conf/kern.xml", kernXmlPath) %><%= fileCheckRow("db/install/VERSION", kernVersionFilePath) %><%= fileCheckRow("module", modulePath) %><%= fileCheckRow("module/kenn", kennPath) %><%= fileCheckRow("kenn/conf/kenn.xml", kennXmlPath) %><%= fileCheckRow("kenn/rohdaten", kennRohdatenPath) %><%= fileCheckRow("kenn/schluesseltabellen", kennSchluesselPath) %>
+
Pfade werden nur angezeigt bzw. auf Existenz/Rechte geprüft. Aus dem Pfadnamen wird nicht automatisch abgeleitet, ob die Installation korrekt oder falsch ist. Besonderheit: Das Kernmodul wird unter db/install geprüft, nicht unter db/module/kern .
+
Festplattenspeicher Prüfpfad <%= html(servletRealPath) %> Gesamt <%= html(formatBytes(diskTotal)) %> Frei <%= html(formatBytes(diskFree)) %> Nutzbar für Tomcat-Prozess <%= html(formatBytes(diskUsable)) %> <%= diskLow ? tag("weniger als 1 GB", "warn") : tag("ok", "ok") %>
+
Datenbank / job_log <%= dbInfoHtml %>Direkte DB-Prüfung <%= dbDirectAvailable ? tag("verfügbar", "ok") : tag("nicht verfügbar", "warn") %><% if (!dbDirectAvailable) { %><%= html(dbDiagnosticMessage) %>
<% } %> Tabelle db_version <%= dbVersionTableTag %>Wird als Fallback für installierte Modulversionen verwendet, wenn /etl/list keine Version liefert.
Tabelle public.job_log <%= jobLogTableTag %> Spalte job_instance_id <%= jobLogHasJobInstanceId ? tag("vorhanden", "ok") : (jobLogExists ? tag("fehlt", "warn") : tag("nicht geprüft", "warn")) %> Spalte log <%= jobLogHasLog ? tag("vorhanden", "ok") : (jobLogExists ? tag("fehlt", "warn") : tag("nicht geprüft", "warn")) %> Spalte status <%= jobLogHasStatus ? tag("vorhanden", "ok") : (jobLogExists ? tag("fehlt / optional", "info") : tag("nicht geprüft", "warn")) %>
<% if (jobLogColumnsHtml != null && jobLogColumnsHtml.length() > 0) { %>Spalten von public.job_log anzeigen Spalte Datentyp <%= jobLogColumnsHtml %>
<% } %><% if (dbVersionRowsHtml != null && dbVersionRowsHtml.length() > 0) { %>Einträge aus public.db_version anzeigen his_system version systeminfo_id <%= dbVersionRowsHtml %>
<% } %>Hinweis: Wenn /ds/api/etl/jobLog/{id} HTTP 500 liefert, kann eine fehlende oder unvollständige Tabelle job_log eine mögliche Ursache sein.
+
SQL-Vorschlag: job_log anlegen Nur zum Kopieren. Die Diagnose führt diesen SQL nicht aus.
SQL kopieren
+
Bekannte Fehlerbilder und Hinweise /jobLog/{id} HTTP 500 Mögliche Ursachen: Tabelle job_log fehlt, Spalten fehlen, Logeintrag ist noch nicht geschrieben oder der Backend-Endpunkt fragt job_instance_id ab, während die Oberfläche eine JobExecutionId übergibt. /job/list/{modul} HTTP 500 Mögliche Ursachen: Moduldateien fehlen, Tomcat lädt eine andere Webapp als erwartet, alte Modulverzeichnisse liegen noch unter db/module oder JVM-Optionen für Kettle fehlen. /job/list/{modul} HTTP 404 Mögliche Ursachen: falscher Context-Pfad, falscher Port/Tunnel oder anderer Tomcat/Webapp-Kontext aktiv. Log lädt erst beim zweiten Klick Mögliche Ursachen: großer Logtext oder der Eintrag in job_log ist direkt nach COMPLETED noch nicht vollständig verfügbar.
Diese Hinweise sind bewusst vorsichtig formuliert. Sie nennen mögliche Ursachen, nicht zwingend die tatsächliche Ursache.
+
Shell-Hinweise für Administratoren Shell-Kommandos kopieren
+
+
+
+
Modul Status Versionen Letztes Update Datenquelle DB-Verbindung Aktionen Module werden geladen...
+
+
Aktueller Lauf Status aktualisieren Stoppen
JobExecutionId
-
Job
-
Status
Kein aktiver Lauf bekannt.
Fortschritt
+
Log-Ausgabe Noch kein Log geladen. Log laden Log leeren
Noch kein Lauf gestartet.
+
+
+
+
+
diff --git a/superx/xml/nd_templates.xsl b/superx/xml/nd_templates.xsl
index 3b718e2..7871ade 100644
--- a/superx/xml/nd_templates.xsl
+++ b/superx/xml/nd_templates.xsl
@@ -473,6 +473,7 @@ $( "#content" ).load('/superx/xml/welcome_wiki.jsp',ajaxErrorHandler);