package edu.uw.zookeeper.protocol;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Arrays;
import java.util.Map;
import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import edu.uw.zookeeper.common.Automaton;
import edu.uw.zookeeper.protocol.proto.OpCode;
public enum ProtocolMessageAutomaton implements Automaton<ProtocolState, Message> {
ANONYMOUS {
@Override
public ProtocolState state() {
return ProtocolState.ANONYMOUS;
}
@Override
public Optional<Automaton.Transition<ProtocolState>> apply(Message input) {
if (input instanceof ConnectMessage.Request) {
return Optional.of(Automaton.Transition.create(state(), CONNECTING.state()));
} else if (input instanceof Message.Anonymous) {
// no-op
} else {
throw new IllegalArgumentException(String.valueOf(input));
}
return Optional.absent();
}
},
CONNECTING {
@Override
public ProtocolState state() {
return ProtocolState.CONNECTING;
}
@Override
public Optional<Automaton.Transition<ProtocolState>> apply(Message input) {
if (input instanceof ConnectMessage.Response) {
if (input instanceof ConnectMessage.Response.Valid) {
return Optional.of(Automaton.Transition.create(state(), CONNECTED.state()));
} else {
return Optional.of(Automaton.Transition.create(state(), ERROR.state()));
}
} else if (input instanceof Message.ClientRequest || input instanceof FourLetterResponse) {
// no-op
} else {
throw new IllegalArgumentException(String.valueOf(input));
}
return Optional.absent();
}
},
CONNECTED {
@Override
public ProtocolState state() {
return ProtocolState.CONNECTED;
}
@Override
public Optional<Automaton.Transition<ProtocolState>> apply(Message input) {
if (input instanceof Message.ClientRequest) {
if (OpCode.CLOSE_SESSION == ((Message.ClientRequest<?>) input).record().opcode()) {
return Optional.of(Automaton.Transition.create(state(), DISCONNECTING.state()));
}
} else if (input instanceof Message.ServerResponse) {
// noop
} else {
throw new IllegalArgumentException(String.valueOf(input));
}
return Optional.absent();
}
},
DISCONNECTING {
@Override
public ProtocolState state() {
return ProtocolState.DISCONNECTING;
}
@Override
public Optional<Automaton.Transition<ProtocolState>> apply(Message input) {
if (input instanceof Message.ServerResponse) {
if (OpCode.CLOSE_SESSION == ((Message.ServerResponse<?>) input).record().opcode()) {
return Optional.of(Automaton.Transition.create(state(), DISCONNECTED.state()));
}
} else {
throw new IllegalArgumentException(String.valueOf(input));
}
return Optional.absent();
}
},
DISCONNECTED {
@Override
public ProtocolState state() {
return ProtocolState.DISCONNECTED;
}
public Optional<Automaton.Transition<ProtocolState>> apply(Message input) {
throw new IllegalArgumentException(String.valueOf(input));
}
},
ERROR {
@Override
public ProtocolState state() {
return ProtocolState.ERROR;
}
public Optional<Automaton.Transition<ProtocolState>> apply(Message input) {
if (input instanceof Message.ClientRequest) {
if (OpCode.CLOSE_SESSION == ((Message.ClientRequest<?>) input).record().opcode()) {
return Optional.of(Automaton.Transition.create(state(), DISCONNECTING.state()));
}
} else if (input instanceof Message.ServerResponse) {
if (OpCode.CLOSE_SESSION == ((Message.ServerResponse<?>) input).record().opcode()) {
return Optional.of(Automaton.Transition.create(state(), DISCONNECTED.state()));
}
}
throw new IllegalArgumentException(String.valueOf(input));
}
};
private static final Map<ProtocolState, ProtocolMessageAutomaton> forState =
ImmutableMap.copyOf(
Maps.uniqueIndex(Arrays.asList(values()),
new Function<ProtocolMessageAutomaton, ProtocolState>() {
@Override
public ProtocolState apply(
ProtocolMessageAutomaton input) {
return input.state();
}
}));
public static ProtocolMessageAutomaton forState(ProtocolState state) {
return forState.get(state);
}
public static Automaton<ProtocolState, Object> asAutomaton() {
return asAutomaton(ProtocolState.ANONYMOUS);
}
public static Automaton<ProtocolState, Object> asAutomaton(ProtocolState state) {
return new SimpleAutomaton(forState(state));
}
/**
* Not thread-safe
*/
protected static final class SimpleAutomaton implements Automaton<ProtocolState, Object> {
private ProtocolMessageAutomaton instance;
public SimpleAutomaton(ProtocolMessageAutomaton instance) {
this.instance = checkNotNull(instance);
}
@Override
public ProtocolState state() {
return instance.state();
}
@Override
public Optional<Automaton.Transition<ProtocolState>> apply(Object input) {
Optional<Automaton.Transition<ProtocolState>> output;
if (input instanceof Message) {
output = instance.apply((Message) input);
} else if (input instanceof ProtocolState) {
output = instance.state().apply((ProtocolState) input);
} else {
throw new IllegalArgumentException(String.valueOf(input));
}
if (output.isPresent()) {
ProtocolState next = output.get().to();
if (next != instance.state()) {
instance = forState(next);
}
}
return output;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(Automaton.class).add("state", instance).toString();
}
}
}