Fallback MfApi restart via MBean stop when IAgentProxy is unbounded.

Remote JMS clients cannot call IAgentProxy.restart; use stop/restart invoke like SMC.
This commit is contained in:
2026-07-24 10:19:29 +02:00
parent a89a7ad4b6
commit ada6b6ca83
5 changed files with 310 additions and 17 deletions
@@ -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).");
}
@@ -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);
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 stateBefore = safeState(agent);
System.out.println("INFO:StateBefore=" + stateBefore);
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;
}
agent.getClass().getMethod("restart").invoke(agent);
System.out.println("OK:RestartInvoked container=" + shortName + " domain=" + domain
+ " stateBefore=" + stateBefore);
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
});
invoke.invoke(connector, new Object[] { on, op, new Object[0], new String[0] });
}
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" }
});
}
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);
}