diff --git a/ZA.CoreService.ESBCertificateManager/Services/SonicManagementClient.cs b/ZA.CoreService.ESBCertificateManager/Services/SonicManagementClient.cs index 6408c34..e349c0c 100644 --- a/ZA.CoreService.ESBCertificateManager/Services/SonicManagementClient.cs +++ b/ZA.CoreService.ESBCertificateManager/Services/SonicManagementClient.cs @@ -359,6 +359,8 @@ public sealed class SonicManagementClient : IDisposable $"- JavaPath={_connection.JavaPath}\n" + "- Dieselben ConnectionUrl/User/Pass wie beim SMC-Login\n" + "SMC-ObjectName Beispiel: proalpha-test.ct-ZADBService:ID=AGENT → ContainerName=ct-ZADBService\n" + + "Hinweis: Bei 'unbounded client connector' nutzt das Tool MBean stop/restart (wie SMC),\n" + + "nicht nur IAgentProxy.restart.\n" + "Hinweis: stopcontainer.bat wird nicht verwendet (SMC-only)."); } diff --git a/ZA.CoreService.ESBCertificateManager/Tools/SonicMfContainerTool$Args.class b/ZA.CoreService.ESBCertificateManager/Tools/SonicMfContainerTool$Args.class index 8056dfd..fbb478c 100644 Binary files a/ZA.CoreService.ESBCertificateManager/Tools/SonicMfContainerTool$Args.class 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 index 955a408..52c19d8 100644 Binary files a/ZA.CoreService.ESBCertificateManager/Tools/SonicMfContainerTool.class 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 index 94de672..2894ff6 100644 Binary files a/ZA.CoreService.ESBCertificateManager/Tools/SonicMfContainerTool.jar and b/ZA.CoreService.ESBCertificateManager/Tools/SonicMfContainerTool.jar differ diff --git a/ZA.CoreService.ESBCertificateManager/Tools/SonicMfContainerTool.java b/ZA.CoreService.ESBCertificateManager/Tools/SonicMfContainerTool.java index 8e6376c..99d1ff5 100644 --- a/ZA.CoreService.ESBCertificateManager/Tools/SonicMfContainerTool.java +++ b/ZA.CoreService.ESBCertificateManager/Tools/SonicMfContainerTool.java @@ -3,6 +3,8 @@ import java.util.Hashtable; import java.util.Iterator; import java.util.Set; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanInfo; import javax.management.ObjectName; /** @@ -12,13 +14,18 @@ import javax.management.ObjectName; * compile classpath. At runtime SonicHome\\lib must provide: * mgmt_client / mfcontext / sonic_Client (etc.) * - * Flow (CX Messenger Management Application API): + * Flow: * Hashtable ConnectionURLs/DefaultUser/DefaultPassword * -> JMSConnectorAddress / JMSConnectorClient.connect - * -> MFProxyFactory.createAgentProxy(connector, ObjectName) - * -> IAgentProxy.restart() + * -> ObjectName {domain}.{container}:ID=AGENT + * -> restart via (in order): + * 1) MBean invoke("restart") / invoke("stop") [remote / unbounded OK] + * 2) IAgentProxy.restart / stop via MFProxyFactory * - * ObjectName: {domain}.{container}:ID=AGENT + * Note: IAgentProxy.restart() often throws + * "Operation unsupported for unbounded client connector" + * on remote JMSConnectorClient. SMC Restart typically stops the agent; the + * Launch Daemon / container host brings it back up. * * Compile: javac --release 8 SonicMfContainerTool.java */ @@ -138,7 +145,7 @@ public final class SonicMfContainerTool } connect.invoke(connector, new Object[] { address, timeoutArg }); - System.out.println("INFO:Connected url=" + url + " user=" + user); + info("Connected url=" + url + " user=" + user); return connector; } @@ -158,6 +165,7 @@ public final class SonicMfContainerTool } } System.out.println("OK:MfApiPing domain=" + domain + " agents=" + matched); + System.out.flush(); } private static void list(Object connector, String domain) throws Exception @@ -179,30 +187,284 @@ public final class SonicMfContainerTool } if (count == 0) { - System.out.println("WARN:KeineAgentContainer Domain=" + domain); + info("KeineAgentContainer Domain=" + domain); } System.out.println("OK:List count=" + count); + System.out.flush(); } private static void restart(Object connector, String domain, String container) throws Exception { String shortName = shortContainer(container); ObjectName on = resolveAgentObjectName(connector, domain, shortName); - System.out.println("INFO:ObjectName=" + on); + info("ObjectName=" + on); - Class factoryCl = Class.forName("com.sonicsw.mf.mgmtapi.runtime.MFProxyFactory"); - Method create = factoryCl.getMethod("createAgentProxy", new Class[] { - Class.forName("com.sonicsw.mf.jmx.client.JMSConnectorClient"), - ObjectName.class + dumpOps(connector, on); + + // 1) Remote-JMX invoke (funktioniert mit unbounded client) + if (tryMBeanLifecycle(connector, on, shortName, domain)) + { + return; + } + + // 2) Typisierte Proxies (kann unbounded-Fehler werfen) + if (tryAgentProxyLifecycle(connector, on, shortName, domain)) + { + return; + } + + // 3) DomainManager / Collector Fallback + if (tryDomainManagerRestart(connector, domain, shortName)) + { + return; + } + + fail("Kein Neustart-Weg funktioniert (MBean invoke / IAgentProxy / DomainManager). " + + "Siehe INFO:Ops und unbounded-Hinweise oben."); + } + + private static boolean tryMBeanLifecycle(Object connector, ObjectName on, String shortName, String domain) + { + // Reihenfolge: stop zuerst (SMC-Restart = Stop + Auto-Relaunch), dann restart + String[] ops = new String[] { "stop", "restart", "shutdown" }; + for (int i = 0; i < ops.length; i++) + { + String op = ops[i]; + try + { + info("Trying MBean.invoke(" + op + ") on " + on); + invokeNoArgs(connector, on, op); + System.out.println("OK:RestartInvoked method=MBean." + op + + " container=" + shortName + " domain=" + domain); + System.out.flush(); + return true; + } + catch (Throwable t) + { + Throwable root = rootOf(t); + warn("MBean." + op + " failed: " + root.getClass().getName() + ": " + root.getMessage()); + if (!isUnbounded(root) && !"stop".equals(op) && !"restart".equals(op) && !"shutdown".equals(op)) + { + // continue trying other ops + } + } + } + return false; + } + + private static boolean tryAgentProxyLifecycle(Object connector, ObjectName on, String shortName, String domain) + { + try + { + Class factoryCl = Class.forName("com.sonicsw.mf.mgmtapi.runtime.MFProxyFactory"); + Method create = factoryCl.getMethod("createAgentProxy", new Class[] { + Class.forName("com.sonicsw.mf.jmx.client.JMSConnectorClient"), + ObjectName.class + }); + Object agent = create.invoke(null, new Object[] { connector, on }); + info("StateBefore=" + safeState(agent)); + + String[] methods = new String[] { "stop", "restart", "shutdown" }; + for (int i = 0; i < methods.length; i++) + { + String mName = methods[i]; + try + { + info("Trying IAgentProxy." + mName); + agent.getClass().getMethod(mName).invoke(agent); + System.out.println("OK:RestartInvoked method=IAgentProxy." + mName + + " container=" + shortName + " domain=" + domain); + System.out.flush(); + return true; + } + catch (Throwable t) + { + Throwable root = rootOf(t); + warn("IAgentProxy." + mName + " failed: " + root.getClass().getName() + ": " + root.getMessage()); + } + } + } + catch (Throwable t) + { + Throwable root = rootOf(t); + warn("IAgentProxy path failed: " + root.getClass().getName() + ": " + root.getMessage()); + } + return false; + } + + private static boolean tryDomainManagerRestart(Object connector, String domain, String shortName) + { + // Häufige ObjectNames für Domain Manager / Collector + String[] candidates = new String[] { + domain + ".DomainManager:ID=DomainManager", + domain + ".DomainManager:ID=AGENT", + domain + ".dm:ID=AGENT", + domain + ".DOMAIN_MANAGER:ID=AGENT", + domain + ".ManagementFramework:ID=AGENT" + }; + + for (int i = 0; i < candidates.length; i++) + { + try + { + ObjectName dm = new ObjectName(candidates[i]); + Set found = (Set) connector.getClass() + .getMethod("queryNames", new Class[] { ObjectName.class, javax.management.QueryExp.class }) + .invoke(connector, new Object[] { dm, null }); + if (found == null || found.isEmpty()) + { + continue; + } + ObjectName real = (ObjectName) found.iterator().next(); + info("DomainManager candidate=" + real); + dumpOps(connector, real); + + String[] ops = new String[] { + "restartContainer", "stopContainer", "startContainer", + "restart", "stop", "shutdown" + }; + for (int o = 0; o < ops.length; o++) + { + String op = ops[o]; + try + { + if (op.endsWith("Container")) + { + info("Trying DomainManager." + op + "(\"" + shortName + "\")"); + invokeStringArg(connector, real, op, shortName); + } + else + { + // nur wenn es der Ziel-Container selbst ist + continue; + } + System.out.println("OK:RestartInvoked method=DomainManager." + op + + " container=" + shortName + " domain=" + domain); + System.out.flush(); + return true; + } + catch (Throwable t) + { + Throwable root = rootOf(t); + warn("DomainManager." + op + " failed: " + root.getClass().getName() + ": " + root.getMessage()); + } + } + } + catch (Throwable ignore) + { + /* try next */ + } + } + + // Alle AGENT-MBeans scannen nach *Container* Ops + try + { + Set agents = queryAgents(connector); + if (agents != null) + { + for (Iterator it = agents.iterator(); it.hasNext(); ) + { + ObjectName on = (ObjectName) it.next(); + String jmxDomain = on.getDomain(); + if (jmxDomain == null) + { + continue; + } + String lower = jmxDomain.toLowerCase(); + boolean interesting = lower.contains("domainmanager") + || lower.contains(".dm") + || lower.contains("collector") + || lower.contains("host") + || lower.contains("launch"); + if (!interesting) + { + continue; + } + info("Scanning manager agent " + on); + if (tryInvokeContainerOp(connector, on, shortName, domain)) + { + return true; + } + } + } + } + catch (Throwable t) + { + warn("Manager scan failed: " + rootOf(t).getMessage()); + } + + return false; + } + + private static boolean tryInvokeContainerOp(Object connector, ObjectName on, String shortName, String domain) + { + String[] ops = new String[] { "restartContainer", "stopContainer" }; + for (int i = 0; i < ops.length; i++) + { + try + { + info("Trying " + on + "." + ops[i] + "(\"" + shortName + "\")"); + invokeStringArg(connector, on, ops[i], shortName); + System.out.println("OK:RestartInvoked method=" + ops[i] + + " on=" + on + " container=" + shortName + " domain=" + domain); + System.out.flush(); + return true; + } + catch (Throwable t) + { + warn(ops[i] + " on " + on + " failed: " + rootOf(t).getMessage()); + } + } + return false; + } + + private static void invokeNoArgs(Object connector, ObjectName on, String op) throws Exception + { + Method invoke = connector.getClass().getMethod("invoke", new Class[] { + ObjectName.class, String.class, Object[].class, String[].class }); - Object agent = create.invoke(null, new Object[] { connector, on }); + invoke.invoke(connector, new Object[] { on, op, new Object[0], new String[0] }); + } - String stateBefore = safeState(agent); - System.out.println("INFO:StateBefore=" + stateBefore); + private static void invokeStringArg(Object connector, ObjectName on, String op, String arg) throws Exception + { + Method invoke = connector.getClass().getMethod("invoke", new Class[] { + ObjectName.class, String.class, Object[].class, String[].class + }); + invoke.invoke(connector, new Object[] { + on, op, new Object[] { arg }, new String[] { "java.lang.String" } + }); + } - agent.getClass().getMethod("restart").invoke(agent); - System.out.println("OK:RestartInvoked container=" + shortName + " domain=" + domain - + " stateBefore=" + stateBefore); + private static void dumpOps(Object connector, ObjectName on) + { + try + { + Method getInfo = connector.getClass().getMethod("getMBeanInfo", new Class[] { ObjectName.class }); + MBeanInfo info = (MBeanInfo) getInfo.invoke(connector, new Object[] { on }); + if (info == null || info.getOperations() == null) + { + return; + } + StringBuffer sb = new StringBuffer(); + MBeanOperationInfo[] ops = info.getOperations(); + for (int i = 0; i < ops.length; i++) + { + String name = ops[i].getName(); + String lower = name.toLowerCase(); + if (lower.contains("restart") || lower.contains("stop") || lower.contains("start") + || lower.contains("shutdown") || lower.contains("container")) + { + if (sb.length() > 0) sb.append(','); + sb.append(name); + } + } + info("Ops(" + on + ")=" + (sb.length() == 0 ? "(keine lifecycle)" : sb.toString())); + } + catch (Throwable t) + { + warn("getMBeanInfo failed: " + rootOf(t).getMessage()); + } } private static Set queryAgents(Object connector) throws Exception @@ -289,9 +551,38 @@ public final class SonicMfContainerTool } } + private static boolean isUnbounded(Throwable t) + { + String msg = t == null || t.getMessage() == null ? "" : t.getMessage(); + return msg.toLowerCase().indexOf("unbounded") >= 0; + } + + private static Throwable rootOf(Throwable t) + { + Throwable root = t; + while (root.getCause() != null && root.getCause() != root) + { + root = root.getCause(); + } + return root == null ? t : root; + } + + private static void info(String message) + { + System.out.println("INFO:" + message); + System.out.flush(); + } + + private static void warn(String message) + { + System.out.println("WARN:" + message); + System.out.flush(); + } + private static void fail(String message) { System.err.println("ERROR:" + message); + System.err.flush(); System.exit(2); }