namespace ZA.CoreService.ESBCertificateManager.Models;
///
/// Verbindungskonfiguration für eine Progress Sonic ESB Management Instanz.
/// Name = Verbindungs-Alias (hier oft gleich ContainerName, z.B. "DE-Test").
/// DomainName = Sonic-Domain (z.B. "proalpha-test").
/// Container wird in KnownContainers / DeploymentTarget.ContainerName geführt (z.B. "DE-Test").
///
public sealed class SonicConnection
{
///
/// Alias der Verbindung; referenziert von .
///
public required string Name { get; init; }
/// Sonic-Domain, z.B. "proalpha-test" (nicht der Containername).
public required string DomainName { get; init; }
///
/// Domain-Manager-/Broker-URL, z.B. "tcp://dekun-painwbdet:13070".
///
public required string ConnectionUrl { get; init; }
public required string Username { get; init; }
public required string Password { get; init; }
public SonicManagementMode ManagementMode { get; init; } = SonicManagementMode.WinRm;
/// Sonic-Installationsroot, z.B. "C:\Sonic\MQ10.0".
public string SonicHome { get; init; } = @"C:\Sonic\MQ10.0";
///
/// Container-Namen (kurz), z.B. ["DE-Test"]. Nicht die Domain.
///
public List KnownContainers { get; init; } = [];
public int ManagementHttpPort { get; init; } = 8080;
public string ApiBasePath { get; init; } = "/api/v1";
public string ContainerListPath { get; init; } = string.Empty;
public string ContainerRestartPath { get; init; } = string.Empty;
public string ContainerStopPath { get; init; } = string.Empty;
public string ContainerStartPath { get; init; } = string.Empty;
public int WinRmPort { get; init; } = 5985;
public string WinRmRestartScript { get; init; } = string.Empty;
public string WinRmContainerListScript { get; init; } = string.Empty;
public string WinRmXapiImportScript { get; init; } = string.Empty;
public int TimeoutSeconds { get; init; } = 120;
public int PostRestartDelaySeconds { get; init; } = 20;
public string ResolveRestartScript()
=> string.IsNullOrWhiteSpace(WinRmRestartScript)
? DefaultRestartScript
: WinRmRestartScript;
public string ResolveContainerListScript()
=> string.IsNullOrWhiteSpace(WinRmContainerListScript)
? DefaultContainerListScript
: WinRmContainerListScript;
///
/// Offizieller MF-Container-Neustart laut Aurea CX Messenger Doku:
/// SonicHome\bin\stopcontainer.bat Domain.Container
/// SonicHome\bin\startcontainer.bat Domain.Container
/// Danach Prozess-Verifikation (alte PIDs weg, neue PIDs da).
///
public const string DefaultRestartScript =
"""
$ErrorActionPreference = 'Stop'
$sonicHome = '{sonicHome}'
$domain = '{domain}'
$container = '{container}'
$bin = Join-Path $sonicHome 'bin'
$stopBat = Join-Path $bin 'stopcontainer.bat'
$startBat = Join-Path $bin 'startcontainer.bat'
# Kurzname = Container (z.B. DE-Test), Full = Domain.Container (z.B. proalpha-test.DE-Test)
$shortName = if ($container -like '*.*') { ($container -split '\.', 2)[1] } else { $container }
$fullName = if ($container -like '*.*') { $container } else { "$domain.$container" }
$markers = @($shortName, $fullName, "$domain.$shortName") | Select-Object -Unique
function Get-ContainerPids {
$pids = @()
Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
Where-Object {
$cmd = $_.CommandLine
if (-not $cmd) { return $false }
if ($_.Name -notmatch 'java|javaw|sonic') { return $false }
foreach ($m in $markers) {
if ($cmd -like "*$m*") { return $true }
}
return $false
} |
ForEach-Object { $pids += [int]$_.ProcessId }
return @($pids | Select-Object -Unique)
}
function Wait-PidsGone([int[]]$pids, [int]$seconds) {
$deadline = (Get-Date).AddSeconds($seconds)
while ((Get-Date) -lt $deadline) {
$alive = @($pids | Where-Object { Get-Process -Id $_ -ErrorAction SilentlyContinue })
if ($alive.Count -eq 0) { return $true }
Start-Sleep -Seconds 2
}
return $false
}
function Wait-NewPids([int[]]$oldPids, [int]$seconds) {
$deadline = (Get-Date).AddSeconds($seconds)
while ((Get-Date) -lt $deadline) {
$now = @(Get-ContainerPids)
$fresh = @($now | Where-Object { $oldPids -notcontains $_ })
if ($fresh.Count -gt 0) { return ,$fresh }
Start-Sleep -Seconds 2
}
return ,@()
}
function Invoke-ContainerBat([string]$bat, [string]$name) {
if (-not (Test-Path -LiteralPath $bat)) {
throw "Sonic BAT fehlt: $bat (SonicHome pruefen)"
}
$arg = '/c "' + $bat + '" "' + $name + '"'
$p = Start-Process -FilePath 'cmd.exe' -ArgumentList $arg -Wait -PassThru -NoNewWindow
return [int]$p.ExitCode
}
Write-Output "INFO:Domain=$domain Container=$shortName Full=$fullName"
Write-Output "INFO:RestartVia=stopcontainer/startcontainer (MF Container)"
$before = @(Get-ContainerPids)
Write-Output "INFO:PIDsBefore=$($before -join ',')"
if ($before.Count -eq 0) {
Write-Output "WARN:No running Java/Sonic process for '$shortName' - starting anyway"
}
# 1) Offiziell: stopcontainer.bat Domain.Container (danach Kurzname als Fallback)
$stopOk = $false
foreach ($n in @($fullName, $shortName)) {
Write-Output "INFO:stopcontainer $n"
$code = Invoke-ContainerBat -bat $stopBat -name $n
Write-Output "INFO:stopcontainer ExitCode=$code Name=$n"
if ($code -eq 0) { $stopOk = $true; break }
}
if (-not $stopOk) {
Write-Output "WARN:stopcontainer non-zero; will force-stop remaining PIDs if any"
}
Start-Sleep -Seconds 3
$still = @(Get-ContainerPids)
# 2) Force-Stop falls BAT den Prozess nicht beendet (sonst kein echter Restart in SMC)
if ($still.Count -gt 0) {
Write-Output "INFO:Force-Stop PIDs=$($still -join ',')"
foreach ($procId in $still) {
Stop-Process -Id $procId -Force -ErrorAction SilentlyContinue
}
}
if ($before.Count -gt 0) {
if (-not (Wait-PidsGone -pids $before -seconds 45)) {
throw "Container process still running after stop (PIDs=$($before -join ','))"
}
Write-Output "INFO:Old PIDs gone"
}
Start-Sleep -Seconds 3
# 3) Offiziell: startcontainer.bat Domain.Container
$started = $false
foreach ($n in @($fullName, $shortName)) {
Write-Output "INFO:startcontainer $n"
$code = Invoke-ContainerBat -bat $startBat -name $n
Write-Output "INFO:startcontainer ExitCode=$code Name=$n"
if ($code -eq 0) { $started = $true; break }
}
if (-not $started) {
throw "startcontainer failed for $fullName / $shortName"
}
$after = @(Wait-NewPids -oldPids $before -seconds 60)
if ($after.Count -eq 0) {
$any = @(Get-ContainerPids)
if ($any.Count -eq 0) {
throw "After start no process for container '$shortName'. Check SMC / SonicHome / ContainerName."
}
Write-Output "INFO:PIDsAfter=$($any -join ',')"
}
else {
Write-Output "INFO:PIDsAfter=$($after -join ',')"
}
Write-Output "OK:ContainerRestartVerified Domain=$domain Container=$shortName"
""";
public const string DefaultContainerListScript =
"""
$ErrorActionPreference = 'Continue'
$sonicHome = '{sonicHome}'
$domain = '{domain}'
$names = New-Object System.Collections.Generic.List[string]
function Add-Name([string]$n) {
if ([string]::IsNullOrWhiteSpace($n)) { return }
$n = $n.Trim()
if (-not $names.Contains($n)) { [void]$names.Add($n) }
}
if (-not (Test-Path -LiteralPath $sonicHome)) {
Write-Output "WARN:SonicHomeNichtGefunden:$sonicHome"
}
else {
Write-Output "INFO:SonicHomeOk:$sonicHome Domain=$domain"
Get-ChildItem -LiteralPath $sonicHome -Directory -ErrorAction SilentlyContinue |
ForEach-Object {
if ($_.Name -like '*.cache') {
Add-Name ($_.Name -replace '\.cache$', '')
}
Get-ChildItem -LiteralPath $_.FullName -Directory -ErrorAction SilentlyContinue |
Where-Object { $_.Name -like '*.cache' } |
ForEach-Object { Add-Name ($_.Name -replace '\.cache$', '') }
}
Get-ChildItem -LiteralPath $sonicHome -Recurse -Filter 'container.xml' -File -ErrorAction SilentlyContinue |
Select-Object -First 40 |
ForEach-Object {
Add-Name (Split-Path $_.DirectoryName -Leaf)
}
}
Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
Where-Object { $_.CommandLine -match 'sonic|mf\.framework|container\.xml' } |
ForEach-Object {
if ($_.CommandLine -match '([A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+)\.cache') {
Add-Name $Matches[1]
}
elseif ($_.CommandLine -match '\\([A-Za-z0-9_\-]+)\\container\.xml') {
Add-Name $Matches[1]
}
}
# Kurzform Domain.Container → Container
foreach ($n in @($names.ToArray())) {
if ($n -like '*.*') {
$parts = $n -split '\.', 2
if ($parts.Count -eq 2) { Add-Name $parts[1] }
}
}
if ($names.Count -eq 0) {
Write-Output "WARN:KeineContainerGefunden Domain=$domain SonicHome=$sonicHome"
}
$names | Sort-Object -Unique
""";
}
public enum SonicManagementMode
{
HttpApi = 0,
WinRm = 1,
LocalCmd = 2
}