package com.dianping.pigeon.remoting.common.codec.thrift.annotation;
import com.facebook.swift.codec.ThriftCodec;
import com.facebook.swift.codec.ThriftCodecManager;
import com.facebook.swift.codec.internal.TProtocolReader;
import com.facebook.swift.codec.internal.TProtocolWriter;
import com.facebook.swift.codec.metadata.ThriftFieldMetadata;
import com.facebook.swift.codec.metadata.ThriftParameterInjection;
import com.facebook.swift.codec.metadata.ThriftType;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import org.apache.thrift.TApplicationException;
import org.apache.thrift.protocol.TMessage;
import org.apache.thrift.protocol.TProtocol;
import javax.annotation.concurrent.ThreadSafe;
import java.util.List;
import java.util.Map;
import static org.apache.thrift.protocol.TMessageType.CALL;
/**
* @author qi.yin
* 2016/05/23 下午6:07.
*/
@ThreadSafe
public class ThriftMethodHandler {
private final String name;
private final String qualifiedName;
private final List<ParameterHandler> parameterCodecs;
private final ThriftCodec<Object> successCodec;
private final Map<Short, ThriftCodec<Object>> exceptionCodecs;
public ThriftMethodHandler(ThriftMethodMetadata methodMetadata, ThriftCodecManager codecManager) {
name = methodMetadata.getName();
qualifiedName = methodMetadata.getQualifiedName();
ParameterHandler[] parameters = new ParameterHandler[methodMetadata.getParameters().size()];
for (ThriftFieldMetadata fieldMetadata : methodMetadata.getParameters()) {
ThriftParameterInjection parameter = (ThriftParameterInjection) fieldMetadata.getInjections().get(0);
ParameterHandler handler = new ParameterHandler(
fieldMetadata.getId(),
fieldMetadata.getName(),
(ThriftCodec<Object>) codecManager.getCodec(fieldMetadata.getThriftType()));
parameters[parameter.getParameterIndex()] = handler;
}
parameterCodecs = ImmutableList.copyOf(parameters);
ImmutableMap.Builder<Short, ThriftCodec<Object>> exceptions = ImmutableMap.builder();
for (Map.Entry<Short, ThriftType> entry : methodMetadata.getExceptions().entrySet()) {
exceptions.put(entry.getKey(), (ThriftCodec<Object>) codecManager.getCodec(entry.getValue()));
}
exceptionCodecs = exceptions.build();
successCodec = (ThriftCodec<Object>) codecManager.getCodec(methodMetadata.getReturnType());
}
public String getName() {
return name;
}
public String getQualifiedName() {
return qualifiedName;
}
public Object readResponse(TProtocol in)
throws Exception {
TProtocolReader reader = new TProtocolReader(in);
reader.readStructBegin();
Object results = null;
Exception exception = null;
while (reader.nextField()) {
if (reader.getFieldId() == 0) {
results = reader.readField(successCodec);
} else {
ThriftCodec<Object> exceptionCodec = exceptionCodecs.get(reader.getFieldId());
if (exceptionCodec != null) {
exception = (Exception) reader.readField(exceptionCodec);
} else {
reader.skipFieldData();
}
}
}
reader.readStructEnd();
if (exception != null) {
return exception;
}
if (successCodec.getType() == ThriftType.VOID) {
// TODO: check for non-null return from a void function?
return null;
}
if (results == null) {
throw new TApplicationException(TApplicationException.MISSING_RESULT, name + " failed: unknown result");
}
return results;
}
public void writeArguments(TProtocol out, int sequenceId, Object[] args)
throws Exception {
out.writeMessageBegin(new TMessage(name, CALL, sequenceId));
TProtocolWriter writer = new TProtocolWriter(out);
writer.writeStructBegin(name + "_args");
if (args != null && args.length != 0) {
for (int i = 0; i < args.length; i++) {
Object value = args[i];
ParameterHandler parameter = parameterCodecs.get(i);
writer.writeField(parameter.getName(), parameter.getId(), parameter.getCodec(), value);
}
}
writer.writeStructEnd();
out.writeMessageEnd();
out.getTransport().flush();
}
private static final class ParameterHandler {
private final short id;
private final String name;
private final ThriftCodec<Object> codec;
private ParameterHandler(short id, String name, ThriftCodec<Object> codec) {
this.id = id;
this.name = name;
this.codec = codec;
}
public short getId() {
return id;
}
public String getName() {
return name;
}
public ThriftCodec<Object> getCodec() {
return codec;
}
}
}