package org.corfudb.util.quickcheck;
import org.codehaus.plexus.util.ExceptionUtils;
import org.corfudb.infrastructure.CorfuServer;
import org.corfudb.infrastructure.LogUnitServer;
import org.corfudb.infrastructure.ManagementServer;
import org.corfudb.runtime.CorfuRuntime;
import org.corfudb.util.GitRepositoryState;
import org.docopt.Docopt;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;
import static org.corfudb.util.quickcheck.QCUtil.configureRuntime;
import static org.corfudb.util.quickcheck.QCUtil.replyErr;
import static org.corfudb.util.quickcheck.QCUtil.replyOk;
public class QCSMRobject {
static private ConcurrentHashMap rtMap = new ConcurrentHashMap<String,CorfuRuntime>();
private static final String USAGE =
"quickcheck interface legacy code.\n"
+ "\n"
+ "Usage:\n"
+ "\tcorfu_smrobject -c <config> -s <stream-id> <class> <method> [<args>] [-d <level>] [-p <qapp>]\n"
+ "\n"
+ "Options:\n"
+ " -c <config>, --config=<config> The config string to pass to the org.corfudb.runtime. \n"
+ " Usually a comma-delimited list of layout servers.\n"
+ " -s <stream-id>, --stream-id=<stream-id> The stream id to use. \n"
+ " -d <level>, --log-level=<level> Set the logging level, valid levels are: \n"
+ " ERROR,WARN,INFO,DEBUG,TRACE [default: INFO].\n"
+ " -p <qapp>, --quickcheck-ap-prefix=<qapp> Set QuickCheck addressportPrefix.\n"
+ " -h, --help Show this screen\n"
+ " --version Show version\n";
public static String[] main(String[] args) {
if (args != null && args.length > 0 && args[0].contentEquals("reboot")) {
ManagementServer ms = CorfuServer.getManagementServer();
ms.shutdown();
CorfuServer.addManagementServer();
LogUnitServer ls = CorfuServer.getLogUnitServer();
if (ls != null) {
ls.shutdown();
CorfuServer.addLogUnit();
return replyOk();
} else {
return replyErr("No active log server");
}
}
// Parse the options given, using docopt.
Map<String, Object> opts =
new Docopt(USAGE).withVersion(GitRepositoryState.getRepositoryState().describe).parse(args);
// Get a org.corfudb.runtime instance from the options.
String config = (String) opts.get("--config");
String qapp = (String) opts.get("--quickcheck-ap-prefix");
String addressportPrefix = "";
if (qapp != null) {
addressportPrefix = qapp;
}
CorfuRuntime rt;
if (rtMap.get(addressportPrefix + config) == null) {
rt = configureRuntime(opts);
rtMap.putIfAbsent(addressportPrefix + config, rt);
}
rt = (CorfuRuntime) rtMap.get(addressportPrefix + config);
String argz = ((String) opts.get("<args>"));
int arity;
String[] split;
if (argz == null) {
split = new String[0];
arity = 0;
} else {
split = argz.split(",");
if (argz.charAt(argz.length() - 1) == ',') {
arity = split.length + 1;
String[] new_split = new String[arity];
for (int i = 0; i < arity - 1; i++) {
new_split[i] = split[i];
}
new_split[arity - 1] = "";
split = new_split;
} else {
arity = split.length;
}
}
// Attempt to open the object
Class<?> cls;
try {
cls = Class.forName((String) opts.get("<class>"));
} catch (ClassNotFoundException cnfe) {
throw new RuntimeException(cnfe);
}
Object o = rt.getObjectsView().build()
.setStreamName((String) opts.get("--stream-id"))
.setType(cls)
.open();
// Use reflection to find the method...
Method m;
try {
m = Arrays.stream(cls.getDeclaredMethods())
.filter(x -> x.getName().equals(opts.get("<method>")))
.filter(x -> x.getParameterCount() == arity)
.findFirst().get();
} catch (NoSuchElementException nsee) {
return replyErr("Method " + opts.get("<method>") + " with " +
arity
+ " arguments not found!");
}
if (m == null) {
return replyErr("Method " + opts.get("<method>") + " with " +
arity
+ " arguments not found!");
}
Object ret;
final int c10 = 10, c50 = 50;
for (int i = 0; i < c10; i++) {
try {
ret = m.invoke(o, split);
} catch (InvocationTargetException e) {
Throwable c = ExceptionUtils.getCause(e);
if (c.getClass() == org.corfudb.runtime.exceptions.NetworkException.class &&
c.toString().matches(".*Disconnected endpoint.*")) {
// Very occasionally, QuickCheck tests will encounter an exception
// caused by a disconnection with the remote endpoint.
try { Thread.sleep(c50); } catch (InterruptedException ie){};
continue;
} else {
return replyErr("exception", e.getClass().getSimpleName(),
"stack: " + ExceptionUtils.getStackTrace(e),
"cause: " + ExceptionUtils.getCause(e));
}
} catch (IllegalAccessException e) {
return replyErr("exception", e.getClass().getSimpleName(),
"stack: " + ExceptionUtils.getStackTrace(e));
} catch (Exception e) {
return replyErr("Exception on object: " + e,
"stack: " + ExceptionUtils.getStackTrace(e),
"cause: " + ExceptionUtils.getCause(e));
}
if (ret != null) {
return replyOk(ret.toString());
} else {
return replyOk();
}
}
return replyErr("Exhausted for loop retries");
}
}