<%@ 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 Komponentenverwaltung

SuperX Komponentenverwaltung

Aufgeräumte Modulübersicht für den täglichen Betrieb. Technische Details, Jobauswahl und JSON-Rohdaten sind über den Detailbereich erreichbar.

ModulStatusVersionenLetztes UpdateDatenquelleDB-VerbindungAktionen
Module werden geladen...

Aktueller Lauf

JobExecutionId
-
Job
-
Status
Kein aktiver Lauf bekannt.
Fortschritt
-

Log-Ausgabe

Noch kein Log geladen.
Noch kein Lauf gestartet.