755 lines
18 KiB
Java
755 lines
18 KiB
Java
import java.lang.reflect.Method;
|
|
import java.util.Hashtable;
|
|
|
|
import javax.management.ObjectName;
|
|
|
|
/**
|
|
* Sonic-MfApi-Hilfsprogramm.
|
|
*
|
|
* Unterstützte Befehle:
|
|
*
|
|
* validate
|
|
* Prüft Anmeldung und lesenden Zugriff auf den Container.
|
|
* Es wird kein Neustart ausgeführt.
|
|
*
|
|
* restart
|
|
* Startet den angegebenen Container neu.
|
|
*
|
|
* Kompilierung mit Java 8:
|
|
*
|
|
* javac -source 8 -target 8 SonicMfContainerTool.java
|
|
*/
|
|
public final class SonicMfConta*nerTool
|
|
{
|
|
public static final *tring TOOL_VERSION =
|
|
"2026*07-27-validate-and-restart";
|
|
|
|
*ublic static void main(String[] ar*s)
|
|
{
|
|
info("ToolVersion*" + TOOL_VERSION);
|
|
|
|
try
|
|
* {
|
|
Args parsedArgu*ents = Args.parse(args);
|
|
|
|
* if (!"validate".equals(parsedAr*uments.command)
|
|
&&*!"restart".equals(parsedArguments.*ommand))
|
|
{
|
|
* fail(
|
|
"Usa*e: SonicMfContainerTool "
|
|
* + "<validate|restart> "* + "--domain D *
|
|
+ "--url U "
|
|
* + "--user U "
|
|
* + "--container C*"
|
|
+ "[--timeout SEC]");
|
|
|
|
return;
|
|
* }
|
|
|
|
if (isBl*nk(parsedArguments.domain)
|
|
* || isBlank(parsedArguments*url)
|
|
|| isBlank(pa*sedArguments.user)
|
|
*|| isBlank(parsedArguments.contain*r))
|
|
{
|
|
*ail(
|
|
"domain, *rl, user und container sind Pflich*.");
|
|
|
|
return;
|
|
* }
|
|
|
|
if (isBlank(*arsedArguments.password))
|
|
* {
|
|
fail(
|
|
* "Das Sonic-Kennwort wurde nicht über "
|
|
+ "ESB_SONIC_PASSWORD bereitgestellt.");
|
|
|
|
return;
|
|
}
|
|
|
|
long timeoutMilliseconds = Math.max(
|
|
5_000L,
|
|
parsedArguments.timeoutSec * 1000L);
|
|
|
|
Object connector = connect(
|
|
parsedArguments.url,
|
|
parsedArguments.user,
|
|
parsedArguments.password,
|
|
timeoutMilliseconds);
|
|
|
|
try
|
|
{
|
|
if ("validate".equals(
|
|
parsedArguments.command))
|
|
{
|
|
validateConnection(
|
|
connector,
|
|
parsedArguments.domain,
|
|
parsedArguments.container);
|
|
}
|
|
else
|
|
{
|
|
restart(
|
|
connector,
|
|
parsedArguments.domain,
|
|
parsedArguments.container);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
disconnect(connector);
|
|
}
|
|
}
|
|
catch (Throwable throwable)
|
|
{
|
|
Throwable root = rootOf(throwable);
|
|
|
|
String message = root.getMessage();
|
|
|
|
if (message == null)
|
|
{
|
|
message = "(keine Detailmeldung)";
|
|
}
|
|
|
|
fail(
|
|
root.getClass().getName()
|
|
+ ": "
|
|
+ message);
|
|
}
|
|
}
|
|
|
|
private static Object connect(
|
|
String url,
|
|
String user,
|
|
String password,
|
|
long timeoutMilliseconds)
|
|
throws Exception
|
|
{
|
|
Hashtable environment = new Hashtable();
|
|
|
|
environment.put(
|
|
"ConnectionURLs",
|
|
url);
|
|
|
|
environment.put(
|
|
"DefaultUser",
|
|
user);
|
|
|
|
environment.put(
|
|
"DefaultPassword",
|
|
password);
|
|
|
|
Class addressClass = Class.forName(
|
|
"com.sonicsw.mf.jmx.client.JMSConnectorAddress");
|
|
|
|
Object address = addressClass
|
|
.getConstructor(
|
|
new Class[]
|
|
{
|
|
Hashtable.class
|
|
})
|
|
.newInstance(
|
|
new Object[]
|
|
{
|
|
environment
|
|
});
|
|
|
|
Class clientClass = Class.forName(
|
|
"com.sonicsw.mf.jmx.client.JMSConnectorClient");
|
|
|
|
Object connector = clientClass
|
|
.getConstructor(new Class[] {})
|
|
.newInstance(new Object[] {});
|
|
|
|
trySetTimeout(
|
|
connector,
|
|
timeoutMilliseconds);
|
|
|
|
Method connectMethod =
|
|
findConnect(clientClass);
|
|
|
|
if (connectMethod == null)
|
|
{
|
|
throw new IllegalStateException(
|
|
"JMSConnectorClient.connect(...) "
|
|
+ "wurde nicht gefunden.");
|
|
}
|
|
|
|
Class[] parameterTypes =
|
|
connectMethod.getParameterTypes();
|
|
|
|
if (parameterTypes.length == 2)
|
|
{
|
|
connectMethod.invoke(
|
|
connector,
|
|
new Object[]
|
|
{
|
|
address,
|
|
boxTimeout(
|
|
parameterTypes[1],
|
|
timeoutMilliseconds)
|
|
});
|
|
}
|
|
else
|
|
{
|
|
connectMethod.invoke(
|
|
connector,
|
|
new Object[]
|
|
{
|
|
address
|
|
});
|
|
}
|
|
|
|
return connector;
|
|
}
|
|
|
|
/**
|
|
* Führt ausschließlich eine lesende Prüfung aus.
|
|
*
|
|
* Die erfolgreiche Verbindung bestätigt bereits die Anmeldung.
|
|
* Zusätzlich wird versucht, die MBean-Information des Containers
|
|
* zu lesen. Es wird keine stop-, start- oder restart-Operation
|
|
* aufgerufen.
|
|
*/
|
|
private static void validateConnection(
|
|
Object connector,
|
|
String domain,
|
|
String container)
|
|
throws Exception
|
|
{
|
|
String shortName =
|
|
shortContainer(container);
|
|
|
|
ObjectName objectName =
|
|
new ObjectName(
|
|
domain
|
|
+ "."
|
|
+ shortName
|
|
+ ":ID=AGENT");
|
|
|
|
info(
|
|
"ValidatingObjectName="
|
|
+ objectName);
|
|
|
|
Method getMBeanInfoMethod =
|
|
findMethod(
|
|
connector.getClass(),
|
|
"getMBeanInfo",
|
|
new Class[]
|
|
{
|
|
ObjectName.class
|
|
});
|
|
|
|
if (getMBeanInfoMethod == null)
|
|
{
|
|
throw new IllegalStateException(
|
|
"Die lesende MfApi-Methode "
|
|
+ "getMBeanInfo(ObjectName) "
|
|
+ "wurde nicht gefunden.");
|
|
}
|
|
|
|
Object result = getMBeanInfoMethod.invoke(
|
|
connector,
|
|
new Object[]
|
|
{
|
|
objectName
|
|
});
|
|
|
|
if (result == null)
|
|
{
|
|
throw new IllegalStateException(
|
|
"Die Container-MBean konnte nicht gelesen werden.");
|
|
}
|
|
|
|
System.out.println(
|
|
"OK:ConnectionValidated"
|
|
+ " container="
|
|
+ shortName
|
|
+ " domain="
|
|
+ domain
|
|
+ " tool="
|
|
+ TOOL_VERSION);
|
|
|
|
System.out.flush();
|
|
}
|
|
|
|
private static void restart(
|
|
Object connector,
|
|
String domain,
|
|
String container)
|
|
throws Exception
|
|
{
|
|
String shortName =
|
|
shortContainer(container);
|
|
|
|
ObjectName objectName =
|
|
new ObjectName(
|
|
domain
|
|
+ "."
|
|
+ shortName
|
|
+ ":ID=AGENT");
|
|
|
|
info(
|
|
"ObjectName="
|
|
+ objectName);
|
|
|
|
StringBuffer attempts =
|
|
new StringBuffer();
|
|
|
|
String[] operations =
|
|
new String[]
|
|
{
|
|
"stop",
|
|
"restart"
|
|
};
|
|
|
|
for (int index = 0;
|
|
index < operations.length;
|
|
index++)
|
|
{
|
|
String operation =
|
|
operations[index];
|
|
|
|
try
|
|
{
|
|
info(
|
|
"Trying MBean.invoke("
|
|
+ operation
|
|
+ ")");
|
|
|
|
invokeNoArgs(
|
|
connector,
|
|
objectName,
|
|
operation);
|
|
|
|
okRestart(
|
|
"MBean." + operation,
|
|
shortName,
|
|
domain);
|
|
|
|
return;
|
|
}
|
|
catch (Throwable throwable)
|
|
{
|
|
Throwable root =
|
|
rootOf(throwable);
|
|
|
|
String line =
|
|
"MBean."
|
|
+ operation
|
|
+ " => "
|
|
+ root.getClass().getName()
|
|
+ ": "
|
|
+ root.getMessage();
|
|
|
|
warn(line);
|
|
|
|
attempts
|
|
.append(line)
|
|
.append('\n');
|
|
|
|
if ("stop".equals(operation)
|
|
&& looksLikeStopSideEffect(root))
|
|
{
|
|
okRestart(
|
|
"MBean.stop(side-effect)",
|
|
shortName,
|
|
domain);
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
throw new IllegalStateException(
|
|
"Neustart fehlgeschlagen. "
|
|
+ "ToolVersion="
|
|
+ TOOL_VERSION
|
|
+ "\nAttempts:\n"
|
|
+ attempts.toString());
|
|
}
|
|
|
|
private static void invokeNoArgs(
|
|
Object connector,
|
|
ObjectName objectName,
|
|
String operation)
|
|
throws Exception
|
|
{
|
|
Method invokeMethod =
|
|
connector.getClass().getMethod(
|
|
"invoke",
|
|
new Class[]
|
|
{
|
|
ObjectName.class,
|
|
String.class,
|
|
Object[].class,
|
|
String[].class
|
|
});
|
|
|
|
invokeMethod.invoke(
|
|
connector,
|
|
new Object[]
|
|
{
|
|
objectName,
|
|
operation,
|
|
new Object[0],
|
|
new String[0]
|
|
});
|
|
}
|
|
|
|
private static void disconnect(
|
|
Object connector)
|
|
{
|
|
if (connector == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
connector
|
|
.getClass()
|
|
.getMethod("disconnect")
|
|
.invoke(connector);
|
|
}
|
|
catch (Exception ignored)
|
|
{
|
|
// Die eigentliche Prüfung oder der Neustart
|
|
// darf durch einen Disconnect-Fehler nicht
|
|
// überschrieben werden.
|
|
}
|
|
}
|
|
|
|
private static Method findMethod(
|
|
Class targetClass,
|
|
String methodName,
|
|
Class[] parameterTypes)
|
|
{
|
|
try
|
|
{
|
|
return targetClass.getMethod(
|
|
methodName,
|
|
parameterTypes);
|
|
}
|
|
catch (Exception ignored)
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private static boolean looksLikeStopSideEffect(
|
|
Throwable root)
|
|
{
|
|
if (root == null
|
|
|| root.getMessage() == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
String message =
|
|
root.getMessage().toLowerCase();
|
|
|
|
return message.indexOf("disconnect") >= 0
|
|
|| message.indexOf("closed") >= 0
|
|
|| message.indexOf("not connected") >= 0
|
|
|| message.indexOf("connection lost") >= 0;
|
|
}
|
|
|
|
private static void okRestart(
|
|
String method,
|
|
String shortName,
|
|
String domain)
|
|
{
|
|
System.out.println(
|
|
"OK:RestartInvoked"
|
|
+ " method="
|
|
+ method
|
|
+ " container="
|
|
+ shortName
|
|
+ " domain="
|
|
+ domain
|
|
+ " tool="
|
|
+ TOOL_VERSION);
|
|
|
|
System.out.flush();
|
|
}
|
|
|
|
private static Method findConnect(
|
|
Class clientClass)
|
|
{
|
|
Method[] methods =
|
|
clientClass.getMethods();
|
|
|
|
Method bestMethod = null;
|
|
|
|
for (int index = 0;
|
|
index < methods.length;
|
|
index++)
|
|
{
|
|
Method method =
|
|
methods[index];
|
|
|
|
if (!"connect".equals(
|
|
method.getName()))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Class[] parameterTypes =
|
|
method.getParameterTypes();
|
|
|
|
if (parameterTypes.length == 1
|
|
|| parameterTypes.length == 2)
|
|
{
|
|
bestMethod = method;
|
|
|
|
if (parameterTypes.length == 2)
|
|
{
|
|
return method;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bestMethod;
|
|
}
|
|
|
|
private static void trySetTimeout(
|
|
Object connector,
|
|
long timeoutMilliseconds)
|
|
{
|
|
try
|
|
{
|
|
connector
|
|
.getClass()
|
|
.getMethod(
|
|
"setTimeout",
|
|
new Class[]
|
|
{
|
|
Long.TYPE
|
|
})
|
|
.invoke(
|
|
connector,
|
|
new Object[]
|
|
{
|
|
Long.valueOf(
|
|
timeoutMilliseconds)
|
|
});
|
|
}
|
|
catch (Exception ignored)
|
|
{
|
|
try
|
|
{
|
|
connector
|
|
.getClass()
|
|
.getMethod(
|
|
"setRequestTimeout",
|
|
new Class[]
|
|
{
|
|
Long.TYPE
|
|
})
|
|
.invoke(
|
|
connector,
|
|
new Object[]
|
|
{
|
|
Long.valueOf(
|
|
timeoutMilliseconds)
|
|
});
|
|
}
|
|
catch (Exception ignoredSecond)
|
|
{
|
|
// Optional, nicht jede Sonic-Version
|
|
// besitzt diese Methode.
|
|
}
|
|
}
|
|
}
|
|
|
|
private static Object boxTimeout(
|
|
Class type,
|
|
long timeoutMilliseconds)
|
|
{
|
|
if (type == Integer.TYPE
|
|
|| type == Integer.class)
|
|
{
|
|
return Integer.valueOf(
|
|
(int) Math.min(
|
|
Integer.MAX_VALUE,
|
|
timeoutMilliseconds));
|
|
}
|
|
|
|
return Long.valueOf(
|
|
timeoutMilliseconds);
|
|
}
|
|
|
|
private static String shortContainer(
|
|
String container)
|
|
{
|
|
String value =
|
|
container.trim();
|
|
|
|
int dotIndex =
|
|
value.lastIndexOf('.');
|
|
|
|
return dotIndex >= 0
|
|
? value.substring(dotIndex + 1)
|
|
: value;
|
|
}
|
|
|
|
private static Throwable rootOf(
|
|
Throwable throwable)
|
|
{
|
|
Throwable current =
|
|
throwable;
|
|
|
|
while (current.getCause() != null
|
|
&& current.getCause() != current)
|
|
{
|
|
current =
|
|
current.getCause();
|
|
}
|
|
|
|
return current;
|
|
}
|
|
|
|
private static boolean isBlank(
|
|
String value)
|
|
{
|
|
return value == null
|
|
|| value.trim().length() == 0;
|
|
}
|
|
|
|
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(1);
|
|
}
|
|
|
|
private static final class Args
|
|
{
|
|
String command;
|
|
String domain;
|
|
String url;
|
|
String user;
|
|
String password;
|
|
String container;
|
|
|
|
int timeoutSec = 120;
|
|
|
|
static Args parse(
|
|
String[] arguments)
|
|
{
|
|
Args parsedArguments =
|
|
new Args();
|
|
|
|
if (arguments == null
|
|
|| arguments.length == 0)
|
|
{
|
|
return parsedArguments;
|
|
}
|
|
|
|
parsedArguments.command =
|
|
arguments[0];
|
|
|
|
for (int index = 1;
|
|
index < arguments.length;
|
|
index++)
|
|
{
|
|
String key =
|
|
arguments[index];
|
|
|
|
String value =
|
|
index + 1 < arguments.length
|
|
? arguments[index + 1]
|
|
: null;
|
|
|
|
if ("--domain".equals(key)
|
|
&& value != null)
|
|
{
|
|
parsedArguments.domain = value;
|
|
index++;
|
|
}
|
|
else if ("--url".equals(key)
|
|
&& value != null)
|
|
{
|
|
parsedArguments.url = value;
|
|
index++;
|
|
}
|
|
else if ("--user".equals(key)
|
|
&& value != null)
|
|
{
|
|
parsedArguments.user = value;
|
|
index++;
|
|
}
|
|
else if ("--password".equals(key)
|
|
&& value != null)
|
|
{
|
|
parsedArguments.password = value;
|
|
index++;
|
|
}
|
|
else if ("--container".equals(key)
|
|
&& value != null)
|
|
{
|
|
parsedArguments.container = value;
|
|
index++;
|
|
}
|
|
else if ("--timeout".equals(key)
|
|
&& value != null)
|
|
{
|
|
try
|
|
{
|
|
parsedArguments.timeoutSec =
|
|
Integer.parseInt(value);
|
|
}
|
|
catch (Exception ignored)
|
|
{
|
|
// Standardwert beibehalten.
|
|
}
|
|
|
|
index++;
|
|
}
|
|
}
|
|
|
|
if (isBlank(
|
|
parsedArguments.password))
|
|
{
|
|
String environmentPassword =
|
|
System.getenv(
|
|
"ESB_SONIC_PASSWORD");
|
|
|
|
if (!isBlank(
|
|
environmentPassword))
|
|
{
|
|
parsedArguments.password =
|
|
environmentPassword;
|
|
}
|
|
}
|
|
|
|
return parsedArguments;
|
|
}
|
|
}
|
|
|
|
private SonicMfContainerTool()
|
|
{
|
|
}
|
|
} |