diff --git a/ZA.CoreService.ESBCertificateManager/Models/SonicConnection.cs b/ZA.CoreService.ESBCertificateManager/Models/SonicConnection.cs index 4265d2d..d9fad35 100644 --- a/ZA.CoreService.ESBCertificateManager/Models/SonicConnection.cs +++ b/ZA.CoreService.ESBCertificateManager/Models/SonicConnection.cs @@ -130,6 +130,17 @@ public sealed class SonicConnection /// Sonic-Installationsroot, z.B. "C:\Sonic\MQ10.0" (enthält lib\*.jar für MfApi). public string SonicHome { get; init; } = @"C:\Sonic\MQ10.0"; + /// + /// Optional: JRE/JDK-Home (z.B. C:\Sonic\MQ10.0\jre) oder Pfad zu java.exe. + /// Leer = automatische Suche (JAVA_HOME, PATH, SonicHome\jre, Program Files\…). + /// + public string JavaHome { get; init; } = string.Empty; + + /// + /// Optionaler direkter Pfad zu java.exe (überschreibt JavaHome wenn gesetzt). + /// + public string JavaPath { get; init; } = string.Empty; + /// /// Optionaler Pfad zu Sonic-Client-JARs für MfApi (mgmt_client.jar etc.). /// Leer = SonicHome\lib. diff --git a/ZA.CoreService.ESBCertificateManager/Services/SonicManagementClient.cs b/ZA.CoreService.ESBCertificateManager/Services/SonicManagementClient.cs index 3e0e2ec..0160e5a 100644 --- a/ZA.CoreService.ESBCertificateManager/Services/SonicManagementClient.cs +++ b/ZA.CoreService.ESBCertificateManager/Services/SonicManagementClient.cs @@ -267,7 +267,7 @@ public sealed class SonicManagementClient : IDisposable $"über {_connection.ConnectionUrl} fehlgeschlagen.\n" + $"Fehler: {error}\nAusgabe: {output}\n" + "Voraussetzungen: Domain Manager erreichbar, SMC-Logins korrekt, " + - "Java/JDK + Sonic-Client-JARs (SonicHome\\lib), Container online."); + "JRE (JavaHome/SonicHome\\jre/JAVA_HOME) + Sonic-Client-JARs (SonicHome\\lib), Container online."); } await Task.Delay( @@ -290,6 +290,8 @@ public sealed class SonicManagementClient : IDisposable WinRmPassword = source.WinRmPassword, ManagementMode = SonicManagementMode.LocalCmd, SonicHome = source.SonicHome, + JavaHome = source.JavaHome, + JavaPath = source.JavaPath, MfClientLibPath = source.MfClientLibPath, KnownContainers = source.KnownContainers, ManagementHttpPort = source.ManagementHttpPort, diff --git a/ZA.CoreService.ESBCertificateManager/Services/SonicMfApiExecutor.cs b/ZA.CoreService.ESBCertificateManager/Services/SonicMfApiExecutor.cs index b674ca2..0984f6d 100644 --- a/ZA.CoreService.ESBCertificateManager/Services/SonicMfApiExecutor.cs +++ b/ZA.CoreService.ESBCertificateManager/Services/SonicMfApiExecutor.cs @@ -162,25 +162,37 @@ public sealed class SonicMfApiExecutor string? javaExe = ResolveJavaExecutable(); if (javaExe is null) { + string sonicHint = string.IsNullOrWhiteSpace(_connection.SonicHome) + ? "" + : $" Erwartet z.B. '{Path.Combine(_connection.SonicHome, "jre", "bin", "java.exe")}'."; return (false, null, null, null, - "Java nicht gefunden (JAVA_HOME/bin/java.exe oder PATH). " + - "Für MfApi wird ein JRE/JDK benötigt."); + "Java nicht gefunden. Gesucht: SonicConnection.JavaHome/JavaPath, JAVA_HOME, PATH, " + + "SonicHome\\jre|jdk\\bin\\java.exe, Program Files\\Java|Eclipse Adoptium." + + sonicHint + + " Für MfApi JavaHome in appsettings setzen oder JRE unter SonicHome nutzen."); } - string? libDir = ResolveLibDirectory(); + (string? libDir, string? libError) = ResolveLibDirectoryDetailed(); if (libDir is null) { - return (false, null, null, null, - "Sonic-Client-JARs nicht gefunden. SonicHome\\lib oder MfClientLibPath setzen " + - $"(aktuell SonicHome='{_connection.SonicHome}', MfClientLibPath='{_connection.MfClientLibPath}'). " + - "Benötigt u.a. mgmt_client.jar / mfcontext.jar / sonic_Client.jar."); + return (false, null, null, null, libError); } + // Bevorzugt: vorcompilierte .jar/.class aus Tools\ (kein javac nötig). + if (TryResolvePrebuiltTool(out string? prebuiltDir, out string? prebuiltEntry, out string? prebuiltError) + && prebuiltDir is not null && prebuiltEntry is not null) + { + string classpath = BuildClasspath(libDir, prebuiltEntry); + return (true, javaExe, prebuiltDir, classpath, null); + } + + // Fallback: zur Laufzeit aus .java kompilieren (braucht javac + Sonic-Libs). string? sourcePath = ResolveToolSourcePath(); if (sourcePath is null) { return (false, null, null, null, - "Tools\\SonicMfContainerTool.java nicht gefunden (App-Ausgabeverzeichnis prüfen)."); + (prebuiltError ?? "Kein vorcompilierter SonicMfContainerTool gefunden.") + + " Tools\\SonicMfContainerTool.java/.class/.jar fehlen im Ausgabeverzeichnis."); } string workDir = Path.Combine(Path.GetTempPath(), "esb-sonic-mf"); @@ -191,7 +203,8 @@ public sealed class SonicMfApiExecutor File.Copy(sourcePath, javaTarget, overwrite: true); - string classpath = BuildClasspath(libDir, workDir); + string compileClasspath = BuildClasspath(libDir, null); + string runtimeClasspath = BuildClasspath(libDir, workDir); bool needsCompile = !File.Exists(classFile) || File.GetLastWriteTimeUtc(classFile) < File.GetLastWriteTimeUtc(sourcePath); @@ -201,14 +214,15 @@ public sealed class SonicMfApiExecutor if (javac is null) { return (false, null, null, null, - "javac nicht gefunden. JDK installieren (nicht nur JRE), " + - "damit SonicMfContainerTool.java kompiliert werden kann."); + "Vorcompilierter SonicMfContainerTool fehlt und javac wurde nicht gefunden. " + + "JDK installieren oder Tools\\SonicMfContainerTool.jar/.class mit ausliefern. " + + $"Gefundene java.exe: {javaExe}"); } ProcessStartInfo compilePsi = new() { FileName = javac, - Arguments = $"-encoding UTF-8 -cp {Quote(BuildClasspath(libDir, null))} -d {Quote(workDir)} {Quote(javaTarget)}", + Arguments = $"-encoding UTF-8 -cp {Quote(compileClasspath)} -d {Quote(workDir)} {Quote(javaTarget)}", UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, @@ -236,42 +250,111 @@ public sealed class SonicMfApiExecutor } } - return (true, javaExe, workDir, classpath, null); + return (true, javaExe, workDir, runtimeClasspath, null); } - private string? ResolveLibDirectory() + private bool TryResolvePrebuiltTool(out string? workDir, out string? classpathEntry, out string? error) { - List candidates = []; + workDir = null; + classpathEntry = null; + error = null; + + string[] searchDirs = + [ + Path.Combine(AppContext.BaseDirectory, "Tools"), + Path.Combine(Directory.GetCurrentDirectory(), "Tools"), + AppContext.BaseDirectory + ]; + + foreach (string dir in searchDirs.Where(Directory.Exists)) + { + string jar = Path.Combine(dir, "SonicMfContainerTool.jar"); + if (File.Exists(jar)) + { + workDir = dir; + classpathEntry = jar; + return true; + } + + string classFile = Path.Combine(dir, "SonicMfContainerTool.class"); + if (File.Exists(classFile)) + { + workDir = dir; + classpathEntry = dir; + return true; + } + } + + error = "Tools\\SonicMfContainerTool.jar/.class nicht gefunden."; + return false; + } + + private (string? Dir, string? Error) ResolveLibDirectoryDetailed() + { + List<(string Path, string Label)> candidates = []; if (!string.IsNullOrWhiteSpace(_connection.MfClientLibPath)) { - candidates.Add(_connection.MfClientLibPath); + candidates.Add((_connection.MfClientLibPath, "MfClientLibPath")); } if (!string.IsNullOrWhiteSpace(_connection.SonicHome)) { - candidates.Add(Path.Combine(_connection.SonicHome, "lib")); - candidates.Add(_connection.SonicHome); + candidates.Add((Path.Combine(_connection.SonicHome, "lib"), "SonicHome\\lib")); + candidates.Add((_connection.SonicHome, "SonicHome")); } - foreach (string dir in candidates.Where(Directory.Exists)) + List checkedPaths = []; + foreach ((string path, string label) in candidates) { - if (Directory.EnumerateFiles(dir, "*.jar", SearchOption.TopDirectoryOnly).Any()) + checkedPaths.Add($"{label}='{path}'"); + if (!Directory.Exists(path)) { - return dir; + continue; + } + + if (Directory.EnumerateFiles(path, "*.jar", SearchOption.TopDirectoryOnly).Any()) + { + return (path, null); } } - return null; + string sonicHome = _connection.SonicHome ?? string.Empty; + bool sonicHomeExists = !string.IsNullOrWhiteSpace(sonicHome) && Directory.Exists(sonicHome); + string libPath = string.IsNullOrWhiteSpace(sonicHome) + ? "(SonicHome leer)" + : Path.Combine(sonicHome, "lib"); + + if (sonicHomeExists && Directory.Exists(libPath) + && !Directory.EnumerateFiles(libPath, "*.jar", SearchOption.TopDirectoryOnly).Any()) + { + return (null, + $"Sonic-Client-JARs fehlen unter '{libPath}' (Ordner existiert, aber keine *.jar). " + + "MfApi braucht u.a. mgmt_client.jar / mfcontext.jar / sonic_Client.jar. " + + "MfClientLibPath auf den korrekten Lib-Ordner setzen."); + } + + if (sonicHomeExists && !Directory.Exists(libPath)) + { + return (null, + $"SonicHome='{sonicHome}' gefunden, aber Lib-Ordner fehlt: '{libPath}'. " + + "Sonic-Client-Installation prüfen oder MfClientLibPath setzen " + + "(erwartet *.jar, u.a. mgmt_client.jar)."); + } + + return (null, + "Sonic-Client-JARs nicht gefunden. SonicHome\\lib oder MfClientLibPath setzen " + + $"(geprüft: {string.Join("; ", checkedPaths)}). " + + "Benötigt u.a. mgmt_client.jar / mfcontext.jar / sonic_Client.jar."); } - private static string BuildClasspath(string libDir, string? extraDir) + private static string BuildClasspath(string libDir, string? extraEntry) { // Java-Classpath-Wildcard für alle JARs im Lib-Ordner string jars = Path.Combine(libDir, "*"); - return string.IsNullOrWhiteSpace(extraDir) + return string.IsNullOrWhiteSpace(extraEntry) ? jars - : jars + Path.PathSeparator + extraDir; + : jars + Path.PathSeparator + extraEntry; } private static string? ResolveToolSourcePath() @@ -286,47 +369,308 @@ public sealed class SonicMfApiExecutor return candidates.FirstOrDefault(File.Exists); } - private static string? ResolveJavaExecutable() + private string? ResolveJavaExecutable() { - string? javaHome = Environment.GetEnvironmentVariable("JAVA_HOME"); - if (!string.IsNullOrWhiteSpace(javaHome)) + foreach (string? configured in new[] { _connection.JavaPath, _connection.JavaHome }) { - string candidate = Path.Combine(javaHome, "bin", "java.exe"); - if (File.Exists(candidate)) + string? fromConfig = ResolveJavaFromConfiguredPath(configured); + if (fromConfig is not null) { - return candidate; + return fromConfig; } } - return FindOnPath("java.exe") ?? FindOnPath("java"); + string? javaHome = Environment.GetEnvironmentVariable("JAVA_HOME"); + string? fromEnv = ResolveJavaFromHomeDir(javaHome); + if (fromEnv is not null) + { + return fromEnv; + } + + string? fromPath = FindOnPath(JavaExeName) ?? FindOnPath("java"); + if (fromPath is not null) + { + return fromPath; + } + + if (!string.IsNullOrWhiteSpace(_connection.SonicHome)) + { + string sonicHome = _connection.SonicHome; + string[] direct = + [ + Path.Combine(sonicHome, "jre", "bin", JavaExeName), + Path.Combine(sonicHome, "jdk", "bin", JavaExeName), + Path.Combine(sonicHome, "JRE", "bin", JavaExeName), + Path.Combine(sonicHome, "JDK", "bin", JavaExeName) + ]; + foreach (string candidate in direct) + { + if (File.Exists(candidate)) + { + return candidate; + } + } + + // Häufige Layouts: SonicHome\MQ*\jre, SonicHome\jre64, SonicHome\Java\jre, ... + string? nested = FindJavaUnderSonicHome(sonicHome); + if (nested is not null) + { + return nested; + } + } + + return FindJavaInCommonInstallLocations(); } - private static string? ResolveJavacExecutable(string javaExe) + private string? ResolveJavacExecutable(string javaExe) { + string javacName = OperatingSystem.IsWindows() ? "javac.exe" : "javac"; + string? dir = Path.GetDirectoryName(javaExe); if (!string.IsNullOrWhiteSpace(dir)) { - string sibling = Path.Combine(dir, OperatingSystem.IsWindows() ? "javac.exe" : "javac"); + string sibling = Path.Combine(dir, javacName); if (File.Exists(sibling)) { return sibling; } } + foreach (string? configured in new[] { _connection.JavaPath, _connection.JavaHome }) + { + string? fromConfig = ResolveToolFromConfiguredPath(configured, javacName); + if (fromConfig is not null) + { + return fromConfig; + } + } + string? javaHome = Environment.GetEnvironmentVariable("JAVA_HOME"); if (!string.IsNullOrWhiteSpace(javaHome)) { - string candidate = Path.Combine(javaHome, "bin", - OperatingSystem.IsWindows() ? "javac.exe" : "javac"); + string candidate = Path.Combine(javaHome, "bin", javacName); if (File.Exists(candidate)) { return candidate; } } - return FindOnPath("javac.exe") ?? FindOnPath("javac"); + if (!string.IsNullOrWhiteSpace(_connection.SonicHome)) + { + foreach (string root in EnumerateJdkRootsUnderSonicHome(_connection.SonicHome)) + { + string candidate = Path.Combine(root, "bin", javacName); + if (File.Exists(candidate)) + { + return candidate; + } + } + } + + return FindOnPath(javacName) ?? FindOnPath("javac"); } + private static string? ResolveJavaFromConfiguredPath(string? configured) + => ResolveToolFromConfiguredPath(configured, JavaExeName); + + private static string? ResolveToolFromConfiguredPath(string? configured, string exeName) + { + if (string.IsNullOrWhiteSpace(configured)) + { + return null; + } + + string path = configured.Trim().Trim('"'); + + if (File.Exists(path)) + { + if (path.EndsWith(exeName, StringComparison.OrdinalIgnoreCase)) + { + return path; + } + + // Konfiguriert: ...\bin\java.exe → sibling javac.exe (oder umgekehrt) + string? parent = Path.GetDirectoryName(path); + if (!string.IsNullOrWhiteSpace(parent)) + { + string sibling = Path.Combine(parent, exeName); + if (File.Exists(sibling)) + { + return sibling; + } + } + + // java ohne Endung (Unix) oder exakter Treffer + string fileName = Path.GetFileName(path); + if (fileName.Equals(Path.GetFileNameWithoutExtension(exeName), StringComparison.OrdinalIgnoreCase) + || fileName.Equals(exeName, StringComparison.OrdinalIgnoreCase)) + { + return path; + } + } + + string fromHome = Path.Combine(path, "bin", exeName); + return File.Exists(fromHome) ? fromHome : null; + } + + private static string? ResolveJavaFromHomeDir(string? home) + { + if (string.IsNullOrWhiteSpace(home)) + { + return null; + } + + string candidate = Path.Combine(home.Trim().Trim('"'), "bin", JavaExeName); + return File.Exists(candidate) ? candidate : null; + } + + private static string? FindJavaUnderSonicHome(string sonicHome) + { + if (!Directory.Exists(sonicHome)) + { + return null; + } + + // Direkte Unterordner mit jre/jdk + foreach (string root in EnumerateJdkRootsUnderSonicHome(sonicHome)) + { + string candidate = Path.Combine(root, "bin", JavaExeName); + if (File.Exists(candidate)) + { + return candidate; + } + } + + return null; + } + + private static IEnumerable EnumerateJdkRootsUnderSonicHome(string sonicHome) + { + if (!Directory.Exists(sonicHome)) + { + yield break; + } + + string[] preferredNames = + [ + "jre", "jdk", "JRE", "JDK", "jre64", "jdk64", "Java", "java" + ]; + + foreach (string name in preferredNames) + { + string path = Path.Combine(sonicHome, name); + if (Directory.Exists(path)) + { + yield return path; + string nestedJre = Path.Combine(path, "jre"); + if (Directory.Exists(nestedJre)) + { + yield return nestedJre; + } + } + } + + // {SonicHome}\*\jre und {SonicHome}\*\jdk + IEnumerable children; + try + { + children = Directory.EnumerateDirectories(sonicHome); + } + catch + { + yield break; + } + + foreach (string child in children) + { + foreach (string leaf in new[] { "jre", "jdk", "JRE", "JDK" }) + { + string nested = Path.Combine(child, leaf); + if (Directory.Exists(nested)) + { + yield return nested; + } + } + + // z.B. ...\jre1.8.0_xxx direkt als Kind + string leafName = Path.GetFileName(child); + if (leafName.StartsWith("jre", StringComparison.OrdinalIgnoreCase) + || leafName.StartsWith("jdk", StringComparison.OrdinalIgnoreCase)) + { + yield return child; + } + } + } + + private static string? FindJavaInCommonInstallLocations() + { + string[] roots = + [ + Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), + Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), + @"C:\Program Files", + @"C:\Program Files (x86)" + ]; + + string[] vendorFolders = + [ + "Java", + "Eclipse Adoptium", + "AdoptOpenJDK", + "Microsoft", + "Amazon Corretto", + "Zulu", + "BellSoft", + "Semeru" + ]; + + foreach (string root in roots.Where(r => !string.IsNullOrWhiteSpace(r)).Distinct(StringComparer.OrdinalIgnoreCase)) + { + foreach (string vendor in vendorFolders) + { + string vendorDir = Path.Combine(root, vendor); + if (!Directory.Exists(vendorDir)) + { + continue; + } + + // vendor\bin\java.exe + string direct = Path.Combine(vendorDir, "bin", JavaExeName); + if (File.Exists(direct)) + { + return direct; + } + + try + { + foreach (string sub in Directory.EnumerateDirectories(vendorDir) + .OrderByDescending(d => d, StringComparer.OrdinalIgnoreCase)) + { + string candidate = Path.Combine(sub, "bin", JavaExeName); + if (File.Exists(candidate)) + { + return candidate; + } + + string nestedJre = Path.Combine(sub, "jre", "bin", JavaExeName); + if (File.Exists(nestedJre)) + { + return nestedJre; + } + } + } + catch + { + // ignore inaccessible vendor dirs + } + } + } + + return null; + } + + private static string JavaExeName => OperatingSystem.IsWindows() ? "java.exe" : "java"; + private static string? FindOnPath(string fileName) { string? pathEnv = Environment.GetEnvironmentVariable("PATH"); @@ -339,7 +683,7 @@ public sealed class SonicMfApiExecutor { try { - string candidate = Path.Combine(dir.Trim('"'), fileName); + string candidate = Path.Combine(dir.Trim().Trim('"'), fileName); if (File.Exists(candidate)) { return candidate; diff --git a/ZA.CoreService.ESBCertificateManager/Tools/SonicMfContainerTool$Args.class b/ZA.CoreService.ESBCertificateManager/Tools/SonicMfContainerTool$Args.class new file mode 100644 index 0000000..71aa15d Binary files /dev/null and b/ZA.CoreService.ESBCertificateManager/Tools/SonicMfContainerTool$Args.class differ diff --git a/ZA.CoreService.ESBCertificateManager/Tools/SonicMfContainerTool.class b/ZA.CoreService.ESBCertificateManager/Tools/SonicMfContainerTool.class new file mode 100644 index 0000000..aad6253 Binary files /dev/null and b/ZA.CoreService.ESBCertificateManager/Tools/SonicMfContainerTool.class differ diff --git a/ZA.CoreService.ESBCertificateManager/Tools/SonicMfContainerTool.jar b/ZA.CoreService.ESBCertificateManager/Tools/SonicMfContainerTool.jar new file mode 100644 index 0000000..3151f07 Binary files /dev/null and b/ZA.CoreService.ESBCertificateManager/Tools/SonicMfContainerTool.jar differ diff --git a/ZA.CoreService.ESBCertificateManager/appsettings.json b/ZA.CoreService.ESBCertificateManager/appsettings.json index 9628465..c108d53 100644 --- a/ZA.CoreService.ESBCertificateManager/appsettings.json +++ b/ZA.CoreService.ESBCertificateManager/appsettings.json @@ -14,10 +14,13 @@ "Username": "Administrator", "Password": "Administrator", // Standard: Sonic MF Management API (IAgentProxy.restart) – kein WinRM. - // Benötigt Java/JDK + Client-JARs unter SonicHome\\lib (oder MfClientLibPath). + // Benötigt JRE + Client-JARs unter SonicHome\\lib (oder MfClientLibPath). + // Java: oft unter SonicHome\\jre – sonst JavaHome/JavaPath setzen (kein systemweites JAVA_HOME nötig). // Optional-Fallback: "WinRm" / "LocalCmd" (stopcontainer/startcontainer). "ManagementMode": "MfApi", "SonicHome": "C:\\Sonic\\MQ10.0", + "JavaHome": "", + "JavaPath": "", "MfClientLibPath": "", "KnownContainers": [ "DE-Test"