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"