package edu.uw.zookeeper.server;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import com.google.common.base.CaseFormat;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Iterators;
import edu.uw.zookeeper.common.ParameterizedFactory;
import edu.uw.zookeeper.common.Processor;
import edu.uw.zookeeper.protocol.FourLetterRequest;
import edu.uw.zookeeper.protocol.FourLetterWord;
import edu.uw.zookeeper.protocol.FourLetterResponse;
import edu.uw.zookeeper.protocol.FourLetterWords;
import edu.uw.zookeeper.protocol.FourLetterWords.Mntr.MntrServerState;
import edu.uw.zookeeper.protocol.FourLetterWords.Wchs;
/**
* TODO
*/
public abstract class FourLetterCommands {
@SuppressWarnings("unchecked")
public static Processor<FourLetterRequest, FourLetterResponse> create(FourLetterWord word, Object...args) throws InstantiationException, IllegalAccessException, InvocationTargetException {
Class<? extends Processor<FourLetterRequest, FourLetterResponse>> type = Types.TYPES.get(word);
if (type == null) {
throw new UnsupportedOperationException(String.valueOf(word));
}
if (type.isEnum()) {
return Iterators.getOnlyElement(Iterators.forArray(type.getEnumConstants()));
} else {
Constructor<?>[] ctors = type.getConstructors();
for (Constructor<?> ctor: ctors) {
if (ctor.getParameterTypes().length == args.length) {
return (Processor<FourLetterRequest, FourLetterResponse>) ctor.newInstance(args);
}
}
for (Constructor<?> ctor: ctors) {
if (ctor.getParameterTypes().length == 0) {
return (Processor<FourLetterRequest, FourLetterResponse>) ctor.newInstance();
}
}
throw new IllegalArgumentException();
}
}
protected static enum Types implements ParameterizedFactory<FourLetterWord, Class<? extends Processor<FourLetterRequest, FourLetterResponse>>> {
TYPES(FourLetterCommands.class);
private final Map<FourLetterWord, Class<? extends Processor<FourLetterRequest, FourLetterResponse>>> types;
@SuppressWarnings("unchecked")
private Types(Class<?> type) {
ImmutableMap.Builder<FourLetterWord, Class<? extends Processor<FourLetterRequest, FourLetterResponse>>> builder = ImmutableMap.builder();
for (Class<?> cls: FourLetterCommands.class.getClasses()) {
FourLetterCommand annotation = cls.getAnnotation(FourLetterCommand.class);
if (annotation != null) {
builder.put(annotation.value(), (Class<? extends Processor<FourLetterRequest, FourLetterResponse>>) cls);
}
}
this.types = builder.build();
}
@Override
public Class<? extends Processor<FourLetterRequest, FourLetterResponse>> get(
FourLetterWord value) {
return types.get(value);
}
}
public static final String ZK_NOT_SERVING =
"This ZooKeeper instance is not currently serving requests\n";
public static String ZOOKEEPER_VERSION = "3.5.0";
@FourLetterCommand(FourLetterWord.RUOK)
public static enum RuokCommand implements Processor<FourLetterRequest, FourLetterResponse> {
RUOK_COMMAND;
public static final String IMOK = "imok";
protected static final FourLetterResponse RESPONSE = FourLetterResponse.fromString(IMOK);
@Override
public FourLetterResponse apply(FourLetterRequest input) {
return RESPONSE;
}
}
@FourLetterCommand(FourLetterWord.GTMK)
public static class GtmkCommand implements Processor<FourLetterRequest, FourLetterResponse> {
@Override
public FourLetterResponse apply(FourLetterRequest input) {
long trace = 0L;
return FourLetterResponse.fromString(new StringBuilder().append(trace).toString());
}
}
@FourLetterCommand(FourLetterWord.STMK)
public static class StmkCommand implements Processor<FourLetterRequest, FourLetterResponse> {
@Override
public FourLetterResponse apply(FourLetterRequest input) {
ByteBuf arg = Unpooled.wrappedBuffer(input.second());
long trace = arg.readLong();
arg.release();
return FourLetterResponse.fromString(new StringBuilder().append(trace).toString());
}
}
@FourLetterCommand(FourLetterWord.ENVI)
public static class EnviCommand implements Processor<FourLetterRequest, FourLetterResponse> {
public static String FORMAT = "Environment:%n%s";
public static String DEFAULT_VALUE = "<NA>";
public static enum EnvKey {
ZOOKEEPER_VERSION,
HOST_NAME,
JAVA_VERSION,
JAVA_VENDOR,
JAVA_HOME,
JAVA_CLASS_PATH,
JAVA_LIBRARY_PATH,
JAVA_IO_TMPDIR,
JAVA_COMPILER,
OS_NAME,
OS_ARCH,
OS_VERSION,
USER_NAME,
USER_HOME,
USER_DIR;
public static Map<EnvKey, String> getValues() {
ImmutableSortedMap.Builder<EnvKey, String> builder = ImmutableSortedMap.naturalOrder();
for (EnvKey k: values()) {
builder.put(k, k.value());
}
return builder.build();
}
public String value() {
switch (this) {
case ZOOKEEPER_VERSION:
return FourLetterCommands.ZOOKEEPER_VERSION;
case HOST_NAME:
try {
return InetAddress.getLocalHost().getCanonicalHostName();
} catch (UnknownHostException e) {
return DEFAULT_VALUE;
}
default:
return System.getProperty(toString(), DEFAULT_VALUE);
}
}
@Override
public String toString() {
return name().toLowerCase().replace('_', '.');
}
}
@Override
public FourLetterResponse apply(FourLetterRequest input) {
Map<EnvKey, String> values = EnvKey.getValues();
StringBuilder builder = Joiner.on('\n').withKeyValueSeparator("=").appendTo(new StringBuilder(), values);
if (! values.isEmpty()) {
builder.append('\n');
}
return FourLetterResponse.fromString(String.format(FORMAT, builder.toString()));
}
}
@FourLetterCommand(FourLetterWord.CONF)
public static class ConfCommand implements Processor<FourLetterRequest, FourLetterResponse> {
public static enum ConfKey {
CLIENT_PORT,
DATA_DIR,
DATA_LOG_DIR,
TICK_TIME,
MAX_CLIENT_CNXNS,
MIN_SESSION_TIMEOUT,
MAX_SESSION_TIMEOUT,
SERVER_ID;
public static Map<ConfKey, String> getValues() {
ImmutableSortedMap.Builder<ConfKey, String> builder = ImmutableSortedMap.naturalOrder();
for (ConfKey k: values()) {
builder.put(k, k.value());
}
return builder.build();
}
public String value() {
switch (this) {
case CLIENT_PORT:
case TICK_TIME:
case MAX_CLIENT_CNXNS:
case MIN_SESSION_TIMEOUT:
case MAX_SESSION_TIMEOUT:
return String.valueOf(0);
case DATA_DIR:
case DATA_LOG_DIR:
return "";
case SERVER_ID:
return String.valueOf(0L);
}
return "";
}
@Override
public String toString() {
return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, name());
}
}
@Override
public FourLetterResponse apply(FourLetterRequest input) {
Map<ConfKey, String> values = ConfKey.getValues();
StringBuilder builder = Joiner.on('\n').withKeyValueSeparator("=").appendTo(new StringBuilder(), values);
if (! values.isEmpty()) {
builder.append('\n');
}
return FourLetterResponse.fromString(builder.toString());
}
}
@FourLetterCommand(FourLetterWord.SRST)
public static class SrstCommand implements Processor<FourLetterRequest, FourLetterResponse> {
public static final String RESULT = "Server stats reset.\n";
protected static final FourLetterResponse RESPONSE = FourLetterResponse.fromString(RESULT);
@Override
public FourLetterResponse apply(FourLetterRequest input) {
return RESPONSE;
}
}
@FourLetterCommand(FourLetterWord.CRST)
public static class CrstCommand implements Processor<FourLetterRequest, FourLetterResponse> {
public static final String RESULT = "Connection stats reset.\n";
protected static final FourLetterResponse RESPONSE = FourLetterResponse.fromString(RESULT);
@Override
public FourLetterResponse apply(FourLetterRequest input) {
return RESPONSE;
}
}
@FourLetterCommand(FourLetterWord.DUMP)
public static class DumpCommand implements Processor<FourLetterRequest, FourLetterResponse> {
public static final String FORMAT = "SessionTracker dump:%n%sephemeral nodes dump:%n%sConnections dump:%n%s";
@Override
public FourLetterResponse apply(FourLetterRequest input) {
return FourLetterResponse.fromString(
String.format(
FORMAT, "", "", ""));
}
}
@FourLetterCommand(FourLetterWord.SRVR)
public static class SrvrCommand implements Processor<FourLetterRequest, FourLetterResponse> {
public static final String FORMAT = "Zookeeper version: %s%n%s%s%sNode count: %d%n";
public static final String READ_ONLY = "READ-ONLY mode; serving only read-only clients";
public static final String STAT_FORMAT = "Clients:%n%s%n";
@Override
public FourLetterResponse apply(FourLetterRequest input) {
return FourLetterResponse.fromString(
String.format(
FORMAT, ZOOKEEPER_VERSION, "", "", "", 0));
}
}
@FourLetterCommand(FourLetterWord.STAT)
public static class StatCommand extends SrvrCommand {
public static final String STAT_FORMAT = "Clients:%n%s%n";
@Override
public FourLetterResponse apply(FourLetterRequest input) {
return FourLetterResponse.fromString(
String.format(
FORMAT, ZOOKEEPER_VERSION, "",
String.format(STAT_FORMAT, ""), "", 0));
}
}
@FourLetterCommand(FourLetterWord.CONS)
public static class ConsCommand implements Processor<FourLetterRequest, FourLetterResponse> {
@Override
public FourLetterResponse apply(FourLetterRequest input) {
return FourLetterResponse.fromString("\n");
}
}
public static abstract class WatchCommand implements Processor<FourLetterRequest, FourLetterResponse> {
protected final ReentrantReadWriteLock lock;
protected final Watches dataWatches;
protected final Watches childWatches;
protected WatchCommand(
ReentrantReadWriteLock lock,
Watches dataWatches,
Watches childWatches) {
this.lock = lock;
this.dataWatches = dataWatches;
this.childWatches = childWatches;
}
@Override
public FourLetterResponse apply(FourLetterRequest input) {
lock.readLock().lock();
try {
return FourLetterResponse.fromString(apply());
} finally {
lock.readLock().unlock();
}
}
protected abstract String apply();
}
@FourLetterCommand(FourLetterWord.WCHC)
public static class WchcCommand extends WatchCommand {
public WchcCommand(
SimpleServer.Builder<?> server) {
this(server.getLock(), server.getDataWatches(), server.getChildWatches());
}
public WchcCommand(
ReentrantReadWriteLock lock,
Watches dataWatches,
Watches childWatches) {
super(lock, dataWatches, childWatches);
}
@Override
protected String apply() {
return FourLetterWords.Wchc.toString(dataWatches.bySession());
}
}
@FourLetterCommand(FourLetterWord.WCHP)
public static class WchpCommand extends WatchCommand {
public WchpCommand(
SimpleServer.Builder<?> server) {
this(server.getLock(), server.getDataWatches(), server.getChildWatches());
}
public WchpCommand(
ReentrantReadWriteLock lock,
Watches dataWatches,
Watches childWatches) {
super(lock, dataWatches, childWatches);
}
@Override
protected String apply() {
return FourLetterWords.Wchp.toString(dataWatches.byPath());
}
}
@FourLetterCommand(FourLetterWord.WCHS)
public static class WchsCommand extends WatchCommand {
public WchsCommand(
SimpleServer.Builder<?> server) {
this(server.getLock(), server.getDataWatches(), server.getChildWatches());
}
public WchsCommand(
ReentrantReadWriteLock lock,
Watches dataWatches,
Watches childWatches) {
super(lock, dataWatches, childWatches);
}
@Override
protected String apply() {
return FourLetterWords.Wchs.toString(
new Wchs(dataWatches.bySession().keySet().size(),
dataWatches.byPath().keySet().size(),
dataWatches.bySession().size()));
}
}
@FourLetterCommand(FourLetterWord.MNTR)
public static class MntrCommand implements Processor<FourLetterRequest, FourLetterResponse> {
@Override
public FourLetterResponse apply(FourLetterRequest input) {
ImmutableSortedMap.Builder<FourLetterWords.Mntr.MntrKey, String> properties = ImmutableSortedMap.naturalOrder();
for (FourLetterWords.Mntr.MntrKey k: FourLetterWords.Mntr.MntrKey.values()) {
Class<?> type = k.type();
String v;
if (type == String.class) {
v = "";
} else if (type == Integer.class) {
v = String.valueOf(0);
} else if (type == Long.class) {
v = String.valueOf(0L);
} else if (type == MntrServerState.class) {
v = FourLetterWords.Mntr.MntrServerState.STANDALONE.toString();
} else {
throw new AssertionError();
}
properties.put(k, v);
}
Map<FourLetterWords.Mntr.MntrKey, String> values = properties.build();
StringBuilder builder = Joiner.on('\n').withKeyValueSeparator("\t").appendTo(new StringBuilder(), values);
if (! values.isEmpty()) {
builder.append('\n');
}
return FourLetterResponse.fromString(builder.toString());
}
}
@FourLetterCommand(FourLetterWord.ISRO)
public static class IsroCommand implements Processor<FourLetterRequest, FourLetterResponse> {
public static enum IsRoValue {
NULL, RO, RW;
@Override
public String toString() {
return name().toLowerCase();
}
}
@Override
public FourLetterResponse apply(FourLetterRequest input) {
return FourLetterResponse.fromString(IsRoValue.RW.toString());
}
}
}