package edu.uw.zookeeper.protocol.server;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
import org.apache.jute.BinaryInputArchive;
import org.apache.logging.log4j.LogManager;
import com.google.common.base.MoreObjects;
import com.google.common.base.Optional;
import com.google.common.collect.Range;
import edu.uw.zookeeper.common.Automaton;
import edu.uw.zookeeper.common.Automatons;
import edu.uw.zookeeper.common.Automatons.AutomatonListener;
import edu.uw.zookeeper.common.Stateful;
import edu.uw.zookeeper.net.Decoder;
import edu.uw.zookeeper.net.Encoder;
import edu.uw.zookeeper.protocol.ProtocolMessageAutomaton;
import edu.uw.zookeeper.protocol.ConnectMessage;
import edu.uw.zookeeper.protocol.Encodable;
import edu.uw.zookeeper.protocol.EncodableEncoder;
import edu.uw.zookeeper.protocol.FourLetterRequest;
import edu.uw.zookeeper.protocol.Frame;
import edu.uw.zookeeper.protocol.Message;
import edu.uw.zookeeper.protocol.ProtocolCodec;
import edu.uw.zookeeper.protocol.ProtocolState;
import edu.uw.zookeeper.protocol.ProtocolRequestMessage;
import edu.uw.zookeeper.protocol.TelnetCloseRequest;
public class ServerProtocolCodec implements ProtocolCodec<Message.Server, Message.Client, Message.Server, Message.Client> {
public static ServerProtocolCodec defaults() {
return newInstance(ProtocolState.ANONYMOUS);
}
public static ServerProtocolCodec newInstance(ProtocolState state) {
Automatons.SynchronizedEventfulAutomaton<ProtocolState,Object,?> automaton =
Automatons.createSynchronizedEventful(
Automatons.createEventful(
Automatons.createLogging(
LogManager.getLogger(ServerProtocolCodec.class),
ProtocolMessageAutomaton.asAutomaton(state))));
return new ServerProtocolCodec(automaton, ServerProtocolEncoder.create(automaton), ServerProtocolDecoder.create(automaton));
}
protected final Automatons.EventfulAutomaton<ProtocolState, Object> automaton;
protected final Encoder<? super Message.Server, ?> encoder;
protected final Decoder<Optional<Message.Client>, ?> decoder;
protected ServerProtocolCodec(
Automatons.EventfulAutomaton<ProtocolState, Object> automaton,
Encoder<? super Message.Server, ?> encoder,
Decoder<Optional<Message.Client>, ?> decoder) {
this.automaton = automaton;
this.encoder = encoder;
this.decoder = decoder;
}
/**
* Don't call concurrently!
*/
@Override
public void encode(Message.Server input, ByteBuf output) throws IOException {
encoder.encode(input, output);
automaton.apply(input);
}
/**
* Don't call concurrently!
*/
@Override
public Optional<Message.Client> decode(ByteBuf input)
throws IOException {
Optional<Message.Client> out = decoder.decode(input);
if (out.isPresent()) {
automaton.apply(out.get());
}
return out;
}
@Override
public ProtocolState state() {
return automaton.state();
}
@Override
public Class<? extends Message.Server> encodeType() {
return Message.Server.class;
}
@Override
public Class<? extends Message.Client> decodeType() {
return Message.Client.class;
}
@Override
public void subscribe(AutomatonListener<ProtocolState> listener) {
automaton.subscribe(listener);
}
@Override
public boolean unsubscribe(AutomatonListener<ProtocolState> listener) {
return automaton.unsubscribe(listener);
}
@Override
public Optional<Automaton.Transition<ProtocolState>> apply(
ProtocolState input) {
return automaton.apply(input);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this).add("state", state()).toString();
}
public static class ServerProtocolEncoder implements
Stateful<ProtocolState>,
Encoder<Message.Server, Message.Server> {
public static ServerProtocolEncoder create(
Stateful<ProtocolState> stateful) {
return new ServerProtocolEncoder(stateful);
}
private final Stateful<ProtocolState> stateful;
private final Encoder<Encodable, ?> frameEncoder;
private ServerProtocolEncoder(
Stateful<ProtocolState> stateful) {
this.stateful = stateful;
this.frameEncoder = Frame.FramedEncoder.create(EncodableEncoder.getInstance());
}
@Override
public ProtocolState state() {
return stateful.state();
}
@Override
public Class<? extends Message.Server> encodeType() {
return Message.Server.class;
}
@Override
public void encode(Message.Server input, ByteBuf output) throws IOException {
ProtocolState state = state();
if (input instanceof Message.ServerSession) {
switch (state) {
case CONNECTING:
case CONNECTED:
case DISCONNECTING:
frameEncoder.encode(input, output);
break;
default:
throw new IllegalStateException(state.toString());
}
} else {
switch (state) {
case ANONYMOUS:
case CONNECTING:
input.encode(output);
break;
default:
throw new IllegalStateException(state.toString());
}
}
}
}
public static class ServerProtocolDecoder implements
Stateful<ProtocolState>,
Decoder<Optional<Message.Client>, Message.Client> {
public static ServerProtocolDecoder create(
Stateful<ProtocolState> stateful) {
return new ServerProtocolDecoder(stateful);
}
private final Stateful<ProtocolState> stateful;
private final Decoder<Optional<Message.ClientSession>, ?> sessionDecoder;
private ServerProtocolDecoder(
Stateful<ProtocolState> stateful) {
this.stateful = stateful;
this.sessionDecoder = Frame.FramedDecoder.create(
Frame.FrameDecoder.create(Range.closed(Integer.valueOf(0), Integer.valueOf(BinaryInputArchive.maxBuffer))),
SessionRequestDecoder.create(stateful));
}
@Override
public ProtocolState state() {
return stateful.state();
}
@Override
public Class<? extends Message.Client> decodeType() {
return Message.Client.class;
}
@Override
public Optional<Message.Client> decode(ByteBuf input) throws IOException {
Message.Client out = null;
ProtocolState state = state();
switch (state) {
case ANONYMOUS:
out = FourLetterRequest.decode(input).orNull();
if (out == null) {
out = TelnetCloseRequest.decode(input).orNull();
if (out == null) {
out = sessionDecoder.decode(input).orNull();
}
}
break;
case CONNECTING:
// don't decode messages until we are connected
break;
case CONNECTED:
out = sessionDecoder.decode(input).orNull();
break;
default:
throw new IllegalStateException(state.toString());
}
return Optional.fromNullable(out);
}
}
public static class SessionRequestDecoder implements
Stateful<ProtocolState>, Decoder<Message.ClientSession, Message.ClientSession> {
public static SessionRequestDecoder create(
Stateful<ProtocolState> stateful) {
return new SessionRequestDecoder(stateful);
}
private final Stateful<ProtocolState> stateful;
private SessionRequestDecoder(Stateful<ProtocolState> stateful) {
this.stateful = stateful;
}
@Override
public ProtocolState state() {
return stateful.state();
}
@Override
public Class<? extends Message.ClientSession> decodeType() {
return Message.ClientSession.class;
}
@Override
public Message.ClientSession decode(ByteBuf input)
throws IOException {
ProtocolState state = state();
Message.ClientSession output;
switch (state) {
case ANONYMOUS:
output = ConnectMessage.Request.decode(input);
break;
case CONNECTING:
case CONNECTED:
output = ProtocolRequestMessage.decode(input);
break;
default:
throw new IllegalStateException(state.toString());
}
return output;
}
}
}