Remote JMS clients cannot call IAgentProxy.restart; use stop/restart invoke like SMC.
644 lines
22 KiB
Java
644 lines
22 KiB
Java
import java.lang.reflect.Method;
|
|
import java.util.Hashtable;
|
|
import java.util.Iterator;
|
|
import java.util.Set;
|
|
|
|
import javax.management.MBeanOperationInfo;
|
|
import javax.management.MBeanInfo;
|
|
import javax.management.ObjectName;
|
|
|
|
/**
|
|
* Sonic Management Application API helper (same restart as SMC).
|
|
*
|
|
* Uses reflection so this file compiles with Java 8 without Sonic jars on the
|
|
* compile classpath. At runtime SonicHome\\lib must provide:
|
|
* mgmt_client / mfcontext / sonic_Client (etc.)
|
|
*
|
|
* Flow:
|
|
* Hashtable ConnectionURLs/DefaultUser/DefaultPassword
|
|
* -> JMSConnectorAddress / JMSConnectorClient.connect
|
|
* -> ObjectName {domain}.{container}:ID=AGENT
|
|
* -> restart via (in order):
|
|
* 1) MBean invoke("restart") / invoke("stop") [remote / unbounded OK]
|
|
* 2) IAgentProxy.restart / stop via MFProxyFactory
|
|
*
|
|
* 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
|
|
*/
|
|
public final class SonicMfContainerTool
|
|
{
|
|
public static void main(String[] args)
|
|
{
|
|
try
|
|
{
|
|
Args a = Args.parse(args);
|
|
if (a.command == null)
|
|
{
|
|
fail("Usage: SonicMfContainerTool ping|list|restart --domain D --url U --user U [--password P] [--container C] [--timeout SEC]");
|
|
return;
|
|
}
|
|
|
|
if (isBlank(a.domain) || isBlank(a.url) || isBlank(a.user))
|
|
{
|
|
fail("domain, url und user sind Pflicht.");
|
|
return;
|
|
}
|
|
|
|
if ("restart".equals(a.command) && isBlank(a.container))
|
|
{
|
|
fail("restart erfordert --container.");
|
|
return;
|
|
}
|
|
|
|
long timeoutMs = Math.max(5_000L, a.timeoutSec * 1000L);
|
|
Object connector = connect(a.url, a.user, a.password, timeoutMs);
|
|
try
|
|
{
|
|
if ("ping".equals(a.command))
|
|
{
|
|
ping(connector, a.domain);
|
|
}
|
|
else if ("list".equals(a.command))
|
|
{
|
|
list(connector, a.domain);
|
|
}
|
|
else if ("restart".equals(a.command))
|
|
{
|
|
restart(connector, a.domain, a.container);
|
|
}
|
|
else
|
|
{
|
|
fail("Unbekannter Befehl: " + a.command);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
try
|
|
{
|
|
connector.getClass().getMethod("disconnect").invoke(connector);
|
|
}
|
|
catch (Exception ignore)
|
|
{
|
|
/* ignore */
|
|
}
|
|
}
|
|
}
|
|
catch (Throwable t)
|
|
{
|
|
Throwable root = t;
|
|
while (root.getCause() != null && root.getCause() != root)
|
|
{
|
|
root = root.getCause();
|
|
}
|
|
fail(root.getClass().getName() + ": " + root.getMessage());
|
|
}
|
|
}
|
|
|
|
private static Object connect(String url, String user, String password, long timeoutMs)
|
|
throws Exception
|
|
{
|
|
Hashtable env = new Hashtable();
|
|
env.put("ConnectionURLs", url);
|
|
env.put("DefaultUser", user);
|
|
env.put("DefaultPassword", password == null ? "" : password);
|
|
|
|
Class addressCl = Class.forName("com.sonicsw.mf.jmx.client.JMSConnectorAddress");
|
|
Object address = addressCl.getConstructor(new Class[] { Hashtable.class }).newInstance(new Object[] { env });
|
|
|
|
Class clientCl = Class.forName("com.sonicsw.mf.jmx.client.JMSConnectorClient");
|
|
Object connector = clientCl.getConstructor(new Class[] {}).newInstance(new Object[] {});
|
|
|
|
Method connect = null;
|
|
Method[] methods = clientCl.getMethods();
|
|
for (int i = 0; i < methods.length; i++)
|
|
{
|
|
Method m = methods[i];
|
|
if ("connect".equals(m.getName()) && m.getParameterTypes().length == 2)
|
|
{
|
|
connect = m;
|
|
break;
|
|
}
|
|
}
|
|
if (connect == null)
|
|
{
|
|
fail("JMSConnectorClient.connect(address, timeout) nicht gefunden.");
|
|
return null;
|
|
}
|
|
|
|
Class[] pts = connect.getParameterTypes();
|
|
Object timeoutArg;
|
|
if (pts[1] == Long.TYPE || pts[1] == Long.class)
|
|
{
|
|
timeoutArg = Long.valueOf(timeoutMs);
|
|
}
|
|
else if (pts[1] == Integer.TYPE || pts[1] == Integer.class)
|
|
{
|
|
timeoutArg = Integer.valueOf((int) Math.min(Integer.MAX_VALUE, timeoutMs));
|
|
}
|
|
else
|
|
{
|
|
timeoutArg = Long.valueOf(timeoutMs);
|
|
}
|
|
|
|
connect.invoke(connector, new Object[] { address, timeoutArg });
|
|
info("Connected url=" + url + " user=" + user);
|
|
return connector;
|
|
}
|
|
|
|
private static void ping(Object connector, String domain) throws Exception
|
|
{
|
|
Set names = queryAgents(connector);
|
|
int matched = 0;
|
|
if (names != null)
|
|
{
|
|
for (Iterator it = names.iterator(); it.hasNext(); )
|
|
{
|
|
ObjectName on = (ObjectName) it.next();
|
|
if (extractContainer(on, domain) != null)
|
|
{
|
|
matched++;
|
|
}
|
|
}
|
|
}
|
|
System.out.println("OK:MfApiPing domain=" + domain + " agents=" + matched);
|
|
System.out.flush();
|
|
}
|
|
|
|
private static void list(Object connector, String domain) throws Exception
|
|
{
|
|
Set names = queryAgents(connector);
|
|
int count = 0;
|
|
if (names != null)
|
|
{
|
|
for (Iterator it = names.iterator(); it.hasNext(); )
|
|
{
|
|
ObjectName on = (ObjectName) it.next();
|
|
String container = extractContainer(on, domain);
|
|
if (container != null)
|
|
{
|
|
System.out.println(container);
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
if (count == 0)
|
|
{
|
|
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);
|
|
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[] 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
|
|
});
|
|
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
|
|
{
|
|
Method query = connector.getClass().getMethod("queryNames", new Class[] {
|
|
ObjectName.class, javax.management.QueryExp.class
|
|
});
|
|
return (Set) query.invoke(connector, new Object[] { new ObjectName("*:ID=AGENT"), null });
|
|
}
|
|
|
|
private static ObjectName resolveAgentObjectName(Object connector, String domain, String container)
|
|
throws Exception
|
|
{
|
|
ObjectName preferred = new ObjectName(domain + "." + container + ":ID=AGENT");
|
|
Method query = connector.getClass().getMethod("queryNames", new Class[] {
|
|
ObjectName.class, javax.management.QueryExp.class
|
|
});
|
|
Set exact = (Set) query.invoke(connector, new Object[] { preferred, null });
|
|
if (exact != null && !exact.isEmpty())
|
|
{
|
|
return (ObjectName) exact.iterator().next();
|
|
}
|
|
|
|
Set names = queryAgents(connector);
|
|
if (names != null)
|
|
{
|
|
for (Iterator it = names.iterator(); it.hasNext(); )
|
|
{
|
|
ObjectName on = (ObjectName) it.next();
|
|
String c = extractContainer(on, domain);
|
|
if (c != null && c.equalsIgnoreCase(container))
|
|
{
|
|
return on;
|
|
}
|
|
}
|
|
}
|
|
|
|
return preferred;
|
|
}
|
|
|
|
private static String extractContainer(ObjectName on, String domain)
|
|
{
|
|
if (on == null || isBlank(domain))
|
|
{
|
|
return null;
|
|
}
|
|
String jmxDomain = on.getDomain();
|
|
if (jmxDomain == null)
|
|
{
|
|
return null;
|
|
}
|
|
String prefix = domain + ".";
|
|
if (jmxDomain.regionMatches(true, 0, prefix, 0, prefix.length()))
|
|
{
|
|
return jmxDomain.substring(prefix.length());
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static String shortContainer(String container)
|
|
{
|
|
if (container == null)
|
|
{
|
|
return "";
|
|
}
|
|
int dot = container.indexOf('.');
|
|
if (dot >= 0 && dot < container.length() - 1)
|
|
{
|
|
return container.substring(dot + 1);
|
|
}
|
|
return container;
|
|
}
|
|
|
|
private static String safeState(Object agent)
|
|
{
|
|
try
|
|
{
|
|
Object s = agent.getClass().getMethod("getStateString").invoke(agent);
|
|
return s == null ? "?" : String.valueOf(s);
|
|
}
|
|
catch (Throwable t)
|
|
{
|
|
return "?";
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
private static boolean isBlank(String s)
|
|
{
|
|
return s == null || s.trim().length() == 0;
|
|
}
|
|
|
|
private static final class Args
|
|
{
|
|
String command;
|
|
String domain;
|
|
String url;
|
|
String user;
|
|
String password;
|
|
String container;
|
|
int timeoutSec = 120;
|
|
|
|
static Args parse(String[] args)
|
|
{
|
|
Args a = new Args();
|
|
if (args == null || args.length == 0)
|
|
{
|
|
return a;
|
|
}
|
|
|
|
a.command = args[0].toLowerCase();
|
|
for (int i = 1; i < args.length; i++)
|
|
{
|
|
String key = args[i];
|
|
if (!key.startsWith("--") || i + 1 >= args.length)
|
|
{
|
|
continue;
|
|
}
|
|
String val = args[++i];
|
|
if ("--domain".equals(key)) a.domain = val;
|
|
else if ("--url".equals(key)) a.url = val;
|
|
else if ("--user".equals(key)) a.user = val;
|
|
else if ("--password".equals(key)) a.password = val;
|
|
else if ("--container".equals(key)) a.container = val;
|
|
else if ("--timeout".equals(key))
|
|
{
|
|
try { a.timeoutSec = Integer.parseInt(val); } catch (Exception ignore) { /* keep */ }
|
|
}
|
|
}
|
|
|
|
if (isBlank(a.password))
|
|
{
|
|
String env = System.getenv("ESB_SONIC_PASSWORD");
|
|
if (!isBlank(env))
|
|
{
|
|
a.password = env;
|
|
}
|
|
}
|
|
return a;
|
|
}
|
|
}
|
|
}
|