summaryrefslogtreecommitdiffstats
path: root/src/jaegertracing/thrift/lib/java/src/org/apache
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/AsyncProcessFunction.java55
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/EncodingUtils.java148
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/Option.java125
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/ProcessFunction.java88
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TApplicationException.java150
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TAsyncProcessor.java33
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBase.java67
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseAsyncProcessor.java114
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseHelper.java297
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseProcessor.java41
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TByteArrayOutputStream.java61
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TDeserializer.java339
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TEnum.java24
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TEnumHelper.java56
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TException.java45
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TFieldIdEnum.java34
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TFieldRequirementType.java30
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TMultiplexedProcessor.java158
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TNonblockingMultiFetchClient.java399
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TNonblockingMultiFetchStats.java80
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TProcessor.java30
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TProcessorFactory.java43
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TSerializable.java44
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TSerializer.java110
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TServiceClient.java91
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TServiceClientFactory.java45
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TUnion.java279
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/annotation/Nullable.java33
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/AsyncMethodCallback.java51
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClient.java102
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClientFactory.java25
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClientManager.java201
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncMethodCall.java284
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/EnumMetaData.java31
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/FieldMetaData.java70
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/FieldValueMetaData.java72
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/ListMetaData.java29
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/MapMetaData.java31
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/SetMetaData.java29
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/StructMetaData.java31
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/ShortStack.java77
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TBase64Utils.java127
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TBinaryProtocol.java457
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TCompactProtocol.java904
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TField.java65
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TJSONProtocol.java973
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TList.java38
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMap.java40
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMessage.java76
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMessageType.java31
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMultiplexedProtocol.java93
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocol.java162
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolDecorator.java213
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolException.java82
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolFactory.java31
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolUtil.java221
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TSet.java42
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TSimpleJSONProtocol.java483
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TStruct.java36
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TTupleProtocol.java98
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TType.java40
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/IScheme.java29
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/SchemeFactory.java25
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/StandardScheme.java25
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/TupleScheme.java25
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/AbstractNonblockingServer.java618
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/Invocation.java20
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/ServerContext.java26
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TExtensibleServlet.java171
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/THsHaServer.java204
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TNonblockingServer.java247
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServer.java177
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServerEventHandler.java59
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServlet.java119
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TSimpleServer.java115
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TThreadPoolServer.java359
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TThreadedSelectorServer.java744
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBuffer.java50
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferReadTransport.java84
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferWriteTransport.java88
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TByteBuffer.java87
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFastFramedTransport.java200
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFileProcessor.java118
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFileTransport.java624
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFramedTransport.java190
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/THttpClient.java362
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TIOStreamTransport.java162
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TMemoryBuffer.java104
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TMemoryInputTransport.java96
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingServerSocket.java163
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingServerTransport.java31
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingSocket.java210
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingTransport.java47
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSSLTransportFactory.java447
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslClientTransport.java108
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslServerTransport.java230
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslTransport.java575
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSeekableFile.java33
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TServerSocket.java158
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TServerTransport.java80
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSimpleFileTransport.java216
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSocket.java248
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TStandardFile.java60
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransport.java163
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransportException.java81
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransportFactory.java41
-rw-r--r--src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TZlibTransport.java148
107 files changed, 16431 insertions, 0 deletions
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/AsyncProcessFunction.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/AsyncProcessFunction.java
new file mode 100644
index 000000000..483c8d054
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/AsyncProcessFunction.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift;
+
+import org.apache.thrift.async.AsyncMethodCallback;
+import org.apache.thrift.protocol.TMessage;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.server.AbstractNonblockingServer;
+
+public abstract class AsyncProcessFunction<I, T extends TBase, R> {
+ final String methodName;
+
+ public AsyncProcessFunction(String methodName) {
+ this.methodName = methodName;
+ }
+
+ protected abstract boolean isOneway();
+
+ public abstract void start(I iface, T args, AsyncMethodCallback<R> resultHandler) throws TException;
+
+ public abstract T getEmptyArgsInstance();
+
+ public abstract AsyncMethodCallback<R> getResultHandler(final AbstractNonblockingServer.AsyncFrameBuffer fb, int seqid);
+
+ public String getMethodName() {
+ return methodName;
+ }
+
+ public void sendResponse(final AbstractNonblockingServer.AsyncFrameBuffer fb, final TSerializable result, final byte type, final int seqid) throws TException {
+ TProtocol oprot = fb.getOutputProtocol();
+
+ oprot.writeMessageBegin(new TMessage(getMethodName(), type, seqid));
+ result.write(oprot);
+ oprot.writeMessageEnd();
+ oprot.getTransport().flush();
+
+ fb.responseReady();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/EncodingUtils.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/EncodingUtils.java
new file mode 100644
index 000000000..bf14ef57e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/EncodingUtils.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+/**
+ * Utility methods for use when encoding/decoding raw data as byte arrays.
+ */
+public class EncodingUtils {
+
+ /**
+ * Encode <code>integer</code> as a series of 4 bytes into <code>buf</code>
+ * starting at position 0 within that buffer.
+ *
+ * @param integer
+ * The integer to encode.
+ * @param buf
+ * The buffer to write to.
+ */
+ public static final void encodeBigEndian(final int integer, final byte[] buf) {
+ encodeBigEndian(integer, buf, 0);
+ }
+
+ /**
+ * Encode <code>integer</code> as a series of 4 bytes into <code>buf</code>
+ * starting at position <code>offset</code>.
+ *
+ * @param integer
+ * The integer to encode.
+ * @param buf
+ * The buffer to write to.
+ * @param offset
+ * The offset within <code>buf</code> to start the encoding.
+ */
+ public static final void encodeBigEndian(final int integer, final byte[] buf, int offset) {
+ buf[offset] = (byte) (0xff & (integer >> 24));
+ buf[offset + 1] = (byte) (0xff & (integer >> 16));
+ buf[offset + 2] = (byte) (0xff & (integer >> 8));
+ buf[offset + 3] = (byte) (0xff & (integer));
+ }
+
+ /**
+ * Decode a series of 4 bytes from <code>buf</code>, starting at position 0,
+ * and interpret them as an integer.
+ *
+ * @param buf
+ * The buffer to read from.
+ * @return An integer, as read from the buffer.
+ */
+ public static final int decodeBigEndian(final byte[] buf) {
+ return decodeBigEndian(buf, 0);
+ }
+
+ /**
+ * Decode a series of 4 bytes from <code>buf</code>, start at
+ * <code>offset</code>, and interpret them as an integer.
+ *
+ * @param buf
+ * The buffer to read from.
+ * @param offset
+ * The offset with <code>buf</code> to start the decoding.
+ * @return An integer, as read from the buffer.
+ */
+ public static final int decodeBigEndian(final byte[] buf, int offset) {
+ return ((buf[offset] & 0xff) << 24) | ((buf[offset + 1] & 0xff) << 16)
+ | ((buf[offset + 2] & 0xff) << 8) | ((buf[offset + 3] & 0xff));
+ }
+
+ /**
+ * Bitfield utilities.
+ * Returns true if the bit at position is set in v.
+ */
+ public static final boolean testBit(byte v, int position) {
+ return testBit((int)v, position);
+ }
+
+ public static final boolean testBit(short v, int position) {
+ return testBit((int)v, position);
+ }
+
+ public static final boolean testBit(int v, int position) {
+ return (v & (1 << position)) != 0;
+ }
+
+ public static final boolean testBit(long v, int position) {
+ return (v & (1L << position)) != 0L;
+ }
+
+ /**
+ * Returns v, with the bit at position set to zero.
+ */
+ public static final byte clearBit(byte v, int position) {
+ return (byte)clearBit((int)v, position);
+ }
+
+ public static final short clearBit(short v, int position) {
+ return (short)clearBit((int)v, position);
+ }
+
+ public static final int clearBit(int v, int position) {
+ return v & ~(1 << position);
+ }
+
+ public static final long clearBit(long v, int position) {
+ return v & ~(1L << position);
+ }
+
+ /**
+ * Returns v, with the bit at position set to 1 or 0 depending on value.
+ */
+ public static final byte setBit(byte v, int position, boolean value) {
+ return (byte)setBit((int)v, position, value);
+ }
+
+ public static final short setBit(short v, int position, boolean value) {
+ return (short)setBit((int)v, position, value);
+ }
+
+ public static final int setBit(int v, int position, boolean value) {
+ if(value)
+ return v | (1 << position);
+ else
+ return clearBit(v, position);
+ }
+
+ public static final long setBit(long v, int position, boolean value) {
+ if(value)
+ return v | (1L << position);
+ else
+ return clearBit(v, position);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/Option.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/Option.java
new file mode 100644
index 000000000..d5cd309e8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/Option.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+/**
+ * Implementation of the Option type pattern
+ */
+public abstract class Option<T> {
+
+ @SuppressWarnings("rawtypes")
+ private static final Option NONE = new None();
+
+ /**
+ * Whether the Option is defined or not
+ * @return
+ * true if the Option is defined (of type Some)
+ * false if the Option is not defined (of type None)
+ */
+ public abstract boolean isDefined();
+
+ /**
+ * Get the value of the Option (if it is defined)
+ * @return the value
+ * @throws IllegalStateException if called on a None
+ */
+ public abstract T get();
+
+ /**
+ * Get the contained value (if defined) or else return a default value
+ * @param other what to return if the value is not defined (a None)
+ * @return either the value, or other if the value is not defined
+ */
+ public T or(T other) {
+ if (isDefined()) {
+ return get();
+ } else {
+ return other;
+ }
+ }
+ /**
+ * The None type, representing an absent value (instead of "null")
+ */
+ public static class None<T> extends Option<T> {
+ public boolean isDefined() {
+ return false;
+ }
+
+ public T get() {
+ throw new IllegalStateException("Cannot call get() on None");
+ }
+
+ public String toString() {
+ return "None";
+ }
+ }
+
+ /**
+ * The Some type, representing an existence of some value
+ * @param <T> The type of value
+ */
+ public static class Some<T> extends Option<T> {
+ private final T value;
+ public Some(T value) {
+ this.value = value;
+ }
+
+ public boolean isDefined() {
+ return true;
+ }
+
+ public T get() {
+ return value;
+ }
+
+ public String toString() {
+ return "Some(" + value + ")";
+ }
+ }
+
+ /**
+ * Wraps value in an Option type, depending on whether or not value is null
+ * @param value
+ * @param <T> type of value
+ * @return Some(value) if value is not null, None if value is null
+ */
+ public static <T> Option<T> fromNullable(T value) {
+ if (value != null) {
+ return some(value);
+ } else {
+ return none();
+ }
+ }
+
+ /**
+ * Wrap value in a Some type (NB! value must not be null!)
+ * @param value
+ * @param <T> type of value
+ * @return a new Some(value)
+ */
+ public static <T> Some<T> some(T value) {
+ return new Some<T>(value);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T> None<T> none() {
+ return (None<T>) NONE;
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/ProcessFunction.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/ProcessFunction.java
new file mode 100644
index 000000000..e6213dfa1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/ProcessFunction.java
@@ -0,0 +1,88 @@
+package org.apache.thrift;
+
+import org.apache.thrift.protocol.TMessage;
+import org.apache.thrift.protocol.TMessageType;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolException;
+import org.apache.thrift.transport.TTransportException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class ProcessFunction<I, T extends TBase> {
+ private final String methodName;
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ProcessFunction.class.getName());
+
+ public ProcessFunction(String methodName) {
+ this.methodName = methodName;
+ }
+
+ public final void process(int seqid, TProtocol iprot, TProtocol oprot, I iface) throws TException {
+ T args = getEmptyArgsInstance();
+ try {
+ args.read(iprot);
+ } catch (TProtocolException e) {
+ iprot.readMessageEnd();
+ TApplicationException x = new TApplicationException(TApplicationException.PROTOCOL_ERROR, e.getMessage());
+ oprot.writeMessageBegin(new TMessage(getMethodName(), TMessageType.EXCEPTION, seqid));
+ x.write(oprot);
+ oprot.writeMessageEnd();
+ oprot.getTransport().flush();
+ return;
+ }
+ iprot.readMessageEnd();
+ TSerializable result = null;
+ byte msgType = TMessageType.REPLY;
+
+ try {
+ result = getResult(iface, args);
+ } catch (TTransportException ex) {
+ LOGGER.error("Transport error while processing " + getMethodName(), ex);
+ throw ex;
+ } catch (TApplicationException ex) {
+ LOGGER.error("Internal application error processing " + getMethodName(), ex);
+ result = ex;
+ msgType = TMessageType.EXCEPTION;
+ } catch (Exception ex) {
+ LOGGER.error("Internal error processing " + getMethodName(), ex);
+ if(rethrowUnhandledExceptions()) throw new RuntimeException(ex.getMessage(), ex);
+ if(!isOneway()) {
+ result = new TApplicationException(TApplicationException.INTERNAL_ERROR,
+ "Internal error processing " + getMethodName());
+ msgType = TMessageType.EXCEPTION;
+ }
+ }
+
+ if(!isOneway()) {
+ oprot.writeMessageBegin(new TMessage(getMethodName(), msgType, seqid));
+ result.write(oprot);
+ oprot.writeMessageEnd();
+ oprot.getTransport().flush();
+ }
+ }
+
+ private void handleException(int seqid, TProtocol oprot) throws TException {
+ if (!isOneway()) {
+ TApplicationException x = new TApplicationException(TApplicationException.INTERNAL_ERROR,
+ "Internal error processing " + getMethodName());
+ oprot.writeMessageBegin(new TMessage(getMethodName(), TMessageType.EXCEPTION, seqid));
+ x.write(oprot);
+ oprot.writeMessageEnd();
+ oprot.getTransport().flush();
+ }
+ }
+
+ protected boolean rethrowUnhandledExceptions(){
+ return false;
+ }
+
+ protected abstract boolean isOneway();
+
+ public abstract TBase getResult(I iface, T args) throws TException;
+
+ public abstract T getEmptyArgsInstance();
+
+ public String getMethodName() {
+ return methodName;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TApplicationException.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TApplicationException.java
new file mode 100644
index 000000000..4d693d9ce
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TApplicationException.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import org.apache.thrift.protocol.TField;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolUtil;
+import org.apache.thrift.protocol.TStruct;
+import org.apache.thrift.protocol.TType;
+
+/**
+ * Application level exception
+ *
+ */
+public class TApplicationException extends TException implements TSerializable {
+
+ private static final TStruct TAPPLICATION_EXCEPTION_STRUCT = new TStruct("TApplicationException");
+ private static final TField MESSAGE_FIELD = new TField("message", TType.STRING, (short)1);
+ private static final TField TYPE_FIELD = new TField("type", TType.I32, (short)2);
+
+ private static final long serialVersionUID = 1L;
+
+ public static final int UNKNOWN = 0;
+ public static final int UNKNOWN_METHOD = 1;
+ public static final int INVALID_MESSAGE_TYPE = 2;
+ public static final int WRONG_METHOD_NAME = 3;
+ public static final int BAD_SEQUENCE_ID = 4;
+ public static final int MISSING_RESULT = 5;
+ public static final int INTERNAL_ERROR = 6;
+ public static final int PROTOCOL_ERROR = 7;
+ public static final int INVALID_TRANSFORM = 8;
+ public static final int INVALID_PROTOCOL = 9;
+ public static final int UNSUPPORTED_CLIENT_TYPE = 10;
+
+ protected int type_ = UNKNOWN;
+ private String message_ = null;
+
+ public TApplicationException() {
+ super();
+ }
+
+ public TApplicationException(int type) {
+ super();
+ type_ = type;
+ }
+
+ public TApplicationException(int type, String message) {
+ super(message);
+ type_ = type;
+ }
+
+ public TApplicationException(String message) {
+ super(message);
+ }
+
+ public int getType() {
+ return type_;
+ }
+
+ @Override
+ public String getMessage() {
+ if (message_ == null) {
+ return super.getMessage();
+ }
+ else {
+ return message_;
+ }
+ }
+
+ public void read(TProtocol iprot) throws TException
+ {
+ TField field;
+ iprot.readStructBegin();
+
+ String message = null;
+ int type = UNKNOWN;
+
+ while (true) {
+ field = iprot.readFieldBegin();
+ if (field.type == TType.STOP) {
+ break;
+ }
+ switch (field.id) {
+ case 1:
+ if (field.type == TType.STRING) {
+ message = iprot.readString();
+ } else {
+ TProtocolUtil.skip(iprot, field.type);
+ }
+ break;
+ case 2:
+ if (field.type == TType.I32) {
+ type = iprot.readI32();
+ } else {
+ TProtocolUtil.skip(iprot, field.type);
+ }
+ break;
+ default:
+ TProtocolUtil.skip(iprot, field.type);
+ break;
+ }
+ iprot.readFieldEnd();
+ }
+ iprot.readStructEnd();
+ type_ = type;
+ message_ = message;
+ }
+
+ /**
+ * Convenience factory method for constructing a TApplicationException given a TProtocol input
+ */
+ public static TApplicationException readFrom(TProtocol iprot) throws TException
+ {
+ TApplicationException result = new TApplicationException();
+ result.read(iprot);
+ return result;
+ }
+
+ public void write(TProtocol oprot) throws TException
+ {
+ oprot.writeStructBegin(TAPPLICATION_EXCEPTION_STRUCT);
+ if (getMessage() != null) {
+ oprot.writeFieldBegin(MESSAGE_FIELD);
+ oprot.writeString(getMessage());
+ oprot.writeFieldEnd();
+ }
+ oprot.writeFieldBegin(TYPE_FIELD);
+ oprot.writeI32(type_);
+ oprot.writeFieldEnd();
+ oprot.writeFieldStop();
+ oprot.writeStructEnd();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TAsyncProcessor.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TAsyncProcessor.java
new file mode 100644
index 000000000..66f768896
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TAsyncProcessor.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift;
+
+import org.apache.thrift.server.AbstractNonblockingServer.AsyncFrameBuffer;
+
+public interface TAsyncProcessor {
+ /**
+ * Process a single frame.
+
+ * <b>Note:</b> Implementations must call fb.responseReady() once processing
+ * is complete
+ *
+ * @throws TException if the frame cannot be processed
+ */
+ public void process(final AsyncFrameBuffer fb) throws TException;
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBase.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBase.java
new file mode 100644
index 000000000..e1489d530
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBase.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import java.io.Serializable;
+
+import org.apache.thrift.protocol.TProtocol;
+
+/**
+ * Generic base interface for generated Thrift objects.
+ *
+ */
+public interface TBase<T extends TBase<T,F>, F extends TFieldIdEnum> extends Comparable<T>, TSerializable, Serializable {
+
+ /**
+ * Get the F instance that corresponds to fieldId.
+ */
+ public F fieldForId(int fieldId);
+
+ /**
+ * Check if a field is currently set or unset.
+ *
+ * @param field
+ */
+ public boolean isSet(F field);
+
+ /**
+ * Get a field's value by field variable. Primitive types will be wrapped in
+ * the appropriate "boxed" types.
+ *
+ * @param field
+ */
+ public Object getFieldValue(F field);
+
+ /**
+ * Set a field's value by field variable. Primitive types must be "boxed" in
+ * the appropriate object wrapper type.
+ *
+ * @param field
+ */
+ public void setFieldValue(F field, Object value);
+
+ public T deepCopy();
+
+ /**
+ * Return to the state of having just been initialized, as though you had just
+ * called the default constructor.
+ */
+ public void clear();
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseAsyncProcessor.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseAsyncProcessor.java
new file mode 100644
index 000000000..f13f068ef
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseAsyncProcessor.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift;
+
+import org.apache.thrift.protocol.*;
+import org.apache.thrift.async.AsyncMethodCallback;
+
+import org.apache.thrift.server.AbstractNonblockingServer.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.Map;
+
+public class TBaseAsyncProcessor<I> implements TAsyncProcessor, TProcessor {
+ protected final Logger LOGGER = LoggerFactory.getLogger(getClass().getName());
+
+ final I iface;
+ final Map<String,AsyncProcessFunction<I, ? extends TBase,?>> processMap;
+
+ public TBaseAsyncProcessor(I iface, Map<String, AsyncProcessFunction<I, ? extends TBase,?>> processMap) {
+ this.iface = iface;
+ this.processMap = processMap;
+ }
+
+ public Map<String,AsyncProcessFunction<I, ? extends TBase,?>> getProcessMapView() {
+ return Collections.unmodifiableMap(processMap);
+ }
+
+ public void process(final AsyncFrameBuffer fb) throws TException {
+
+ final TProtocol in = fb.getInputProtocol();
+ final TProtocol out = fb.getOutputProtocol();
+
+ //Find processing function
+ final TMessage msg = in.readMessageBegin();
+ AsyncProcessFunction fn = processMap.get(msg.name);
+ if (fn == null) {
+ TProtocolUtil.skip(in, TType.STRUCT);
+ in.readMessageEnd();
+
+ TApplicationException x = new TApplicationException(TApplicationException.UNKNOWN_METHOD,
+ "Invalid method name: '" + msg.name + "'");
+ LOGGER.debug("Invalid method name", x);
+
+ // this means it is a two-way request, so we can send a reply
+ if (msg.type == TMessageType.CALL) {
+ out.writeMessageBegin(new TMessage(msg.name, TMessageType.EXCEPTION, msg.seqid));
+ x.write(out);
+ out.writeMessageEnd();
+ out.getTransport().flush();
+ }
+ fb.responseReady();
+ return;
+ }
+
+ //Get Args
+ TBase args = fn.getEmptyArgsInstance();
+
+ try {
+ args.read(in);
+ } catch (TProtocolException e) {
+ in.readMessageEnd();
+
+ TApplicationException x = new TApplicationException(TApplicationException.PROTOCOL_ERROR,
+ e.getMessage());
+ LOGGER.debug("Could not retrieve function arguments", x);
+
+ if (!fn.isOneway()) {
+ out.writeMessageBegin(new TMessage(msg.name, TMessageType.EXCEPTION, msg.seqid));
+ x.write(out);
+ out.writeMessageEnd();
+ out.getTransport().flush();
+ }
+ fb.responseReady();
+ return;
+ }
+ in.readMessageEnd();
+
+ if (fn.isOneway()) {
+ fb.responseReady();
+ }
+
+ //start off processing function
+ AsyncMethodCallback resultHandler = fn.getResultHandler(fb, msg.seqid);
+ try {
+ fn.start(iface, args, resultHandler);
+ } catch (Exception e) {
+ LOGGER.debug("Exception handling function", e);
+ resultHandler.onError(e);
+ }
+ return;
+ }
+
+ @Override
+ public void process(TProtocol in, TProtocol out) throws TException {
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseHelper.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseHelper.java
new file mode 100644
index 000000000..6f6c6ebf5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseHelper.java
@@ -0,0 +1,297 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.thrift;
+
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+public final class TBaseHelper {
+
+ private TBaseHelper(){}
+
+ private static final Comparator comparator = new NestedStructureComparator();
+
+ public static int compareTo(Object o1, Object o2) {
+ if (o1 instanceof Comparable) {
+ return compareTo((Comparable)o1, (Comparable)o2);
+ } else if (o1 instanceof List) {
+ return compareTo((List)o1, (List)o2);
+ } else if (o1 instanceof Set) {
+ return compareTo((Set)o1, (Set)o2);
+ } else if (o1 instanceof Map) {
+ return compareTo((Map)o1, (Map)o2);
+ } else if (o1 instanceof byte[]) {
+ return compareTo((byte[])o1, (byte[])o2);
+ } else {
+ throw new IllegalArgumentException("Cannot compare objects of type " + o1.getClass());
+ }
+ }
+
+ public static int compareTo(boolean a, boolean b) {
+ return Boolean.compare(a, b);
+ }
+
+ public static int compareTo(byte a, byte b) {
+ return Byte.compare(a, b);
+ }
+
+ public static int compareTo(short a, short b) {
+ return Short.compare(a,b);
+ }
+
+ public static int compareTo(int a, int b) {
+ return Integer.compare(a, b);
+ }
+
+ public static int compareTo(long a, long b) {
+ return Long.compare(a, b);
+ }
+
+ public static int compareTo(double a, double b) {
+ return Double.compare(a, b);
+ }
+
+ public static int compareTo(String a, String b) {
+ return a.compareTo(b);
+ }
+
+ public static int compareTo(byte[] a, byte[] b) {
+ int compare = compareTo(a.length, b.length);
+ if (compare == 0) {
+ for (int i = 0; i < a.length; i++) {
+ compare = compareTo(a[i], b[i]);
+ if (compare != 0) {
+ break;
+ }
+ }
+ }
+ return compare;
+ }
+
+ public static int compareTo(Comparable a, Comparable b) {
+ return a.compareTo(b);
+ }
+
+ public static int compareTo(List a, List b) {
+ int compare = compareTo(a.size(), b.size());
+ if (compare == 0) {
+ for (int i = 0; i < a.size(); i++) {
+ compare = comparator.compare(a.get(i), b.get(i));
+ if (compare != 0) {
+ break;
+ }
+ }
+ }
+ return compare;
+ }
+
+ public static int compareTo(Set a, Set b) {
+ int compare = compareTo(a.size(), b.size());
+ if (compare == 0) {
+ ArrayList sortedA = new ArrayList(a);
+ ArrayList sortedB = new ArrayList(b);
+
+ Collections.sort(sortedA, comparator);
+ Collections.sort(sortedB, comparator);
+
+ Iterator iterA = sortedA.iterator();
+ Iterator iterB = sortedB.iterator();
+
+ // Compare each item.
+ while (iterA.hasNext() && iterB.hasNext()) {
+ compare = comparator.compare(iterA.next(), iterB.next());
+ if (compare != 0) {
+ break;
+ }
+ }
+ }
+ return compare;
+ }
+
+ public static int compareTo(Map a, Map b) {
+ int lastComparison = compareTo(a.size(), b.size());
+ if (lastComparison != 0) {
+ return lastComparison;
+ }
+
+ // Sort a and b so we can compare them.
+ SortedMap sortedA = new TreeMap(comparator);
+ sortedA.putAll(a);
+ Iterator<Map.Entry> iterA = sortedA.entrySet().iterator();
+ SortedMap sortedB = new TreeMap(comparator);
+ sortedB.putAll(b);
+ Iterator<Map.Entry> iterB = sortedB.entrySet().iterator();
+
+ // Compare each item.
+ while (iterA.hasNext() && iterB.hasNext()) {
+ Map.Entry entryA = iterA.next();
+ Map.Entry entryB = iterB.next();
+ lastComparison = comparator.compare(entryA.getKey(), entryB.getKey());
+ if (lastComparison != 0) {
+ return lastComparison;
+ }
+ lastComparison = comparator.compare(entryA.getValue(), entryB.getValue());
+ if (lastComparison != 0) {
+ return lastComparison;
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * Comparator to compare items inside a structure (e.g. a list, set, or map).
+ */
+ private static class NestedStructureComparator implements Comparator, Serializable {
+ public int compare(Object oA, Object oB) {
+ if (oA == null && oB == null) {
+ return 0;
+ } else if (oA == null) {
+ return -1;
+ } else if (oB == null) {
+ return 1;
+ } else if (oA instanceof List) {
+ return compareTo((List)oA, (List)oB);
+ } else if (oA instanceof Set) {
+ return compareTo((Set)oA, (Set)oB);
+ } else if (oA instanceof Map) {
+ return compareTo((Map)oA, (Map)oB);
+ } else if (oA instanceof byte[]) {
+ return compareTo((byte[])oA, (byte[])oB);
+ } else {
+ return compareTo((Comparable)oA, (Comparable)oB);
+ }
+ }
+ }
+
+ public static void toString(Collection<ByteBuffer> bbs, StringBuilder sb) {
+ Iterator<ByteBuffer> it = bbs.iterator();
+ if (!it.hasNext()) {
+ sb.append("[]");
+ } else {
+ sb.append("[");
+ while (true) {
+ ByteBuffer bb = it.next();
+ org.apache.thrift.TBaseHelper.toString(bb, sb);
+ if (!it.hasNext()) {
+ sb.append("]");
+ return;
+ } else {
+ sb.append(", ");
+ }
+ }
+ }
+ }
+
+ public static void toString(ByteBuffer bb, StringBuilder sb) {
+ byte[] buf = bb.array();
+
+ int arrayOffset = bb.arrayOffset();
+ int offset = arrayOffset + bb.position();
+ int origLimit = arrayOffset + bb.limit();
+ int limit = (origLimit - offset > 128) ? offset + 128 : origLimit;
+
+ for (int i = offset; i < limit; i++) {
+ if (i > offset) {
+ sb.append(" ");
+ }
+ sb.append(paddedByteString(buf[i]));
+ }
+ if (origLimit != limit) {
+ sb.append("...");
+ }
+ }
+
+ public static String paddedByteString(byte b) {
+ int extended = (b | 0x100) & 0x1ff;
+ return Integer.toHexString(extended).toUpperCase().substring(1);
+ }
+
+ public static byte[] byteBufferToByteArray(ByteBuffer byteBuffer) {
+ if (wrapsFullArray(byteBuffer)) {
+ return byteBuffer.array();
+ }
+ byte[] target = new byte[byteBuffer.remaining()];
+ byteBufferToByteArray(byteBuffer, target, 0);
+ return target;
+ }
+
+ public static boolean wrapsFullArray(ByteBuffer byteBuffer) {
+ return byteBuffer.hasArray()
+ && byteBuffer.position() == 0
+ && byteBuffer.arrayOffset() == 0
+ && byteBuffer.remaining() == byteBuffer.capacity();
+ }
+
+ public static int byteBufferToByteArray(ByteBuffer byteBuffer, byte[] target, int offset) {
+ int remaining = byteBuffer.remaining();
+ System.arraycopy(byteBuffer.array(),
+ byteBuffer.arrayOffset() + byteBuffer.position(),
+ target,
+ offset,
+ remaining);
+ return remaining;
+ }
+
+ public static ByteBuffer rightSize(ByteBuffer in) {
+ if (in == null) {
+ return null;
+ }
+ if (wrapsFullArray(in)) {
+ return in;
+ }
+ return ByteBuffer.wrap(byteBufferToByteArray(in));
+ }
+
+ public static ByteBuffer copyBinary(final ByteBuffer orig) {
+ if (orig == null) {
+ return null;
+ }
+ ByteBuffer copy = ByteBuffer.wrap(new byte[orig.remaining()]);
+ if (orig.hasArray()) {
+ System.arraycopy(orig.array(), orig.arrayOffset() + orig.position(), copy.array(), 0, orig.remaining());
+ } else {
+ orig.slice().get(copy.array());
+ }
+
+ return copy;
+ }
+
+ public static byte[] copyBinary(final byte[] orig) {
+ return (orig == null) ? null : Arrays.copyOf(orig, orig.length);
+ }
+
+ public static int hashCode(long value) {
+ return Long.hashCode(value);
+ }
+
+ public static int hashCode(double value) {
+ return Double.hashCode(value);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseProcessor.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseProcessor.java
new file mode 100644
index 000000000..55a0f15d3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseProcessor.java
@@ -0,0 +1,41 @@
+package org.apache.thrift;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.thrift.protocol.TMessage;
+import org.apache.thrift.protocol.TMessageType;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolUtil;
+import org.apache.thrift.protocol.TType;
+
+public abstract class TBaseProcessor<I> implements TProcessor {
+ private final I iface;
+ private final Map<String,ProcessFunction<I, ? extends TBase>> processMap;
+
+ protected TBaseProcessor(I iface, Map<String, ProcessFunction<I, ? extends TBase>> processFunctionMap) {
+ this.iface = iface;
+ this.processMap = processFunctionMap;
+ }
+
+ public Map<String,ProcessFunction<I, ? extends TBase>> getProcessMapView() {
+ return Collections.unmodifiableMap(processMap);
+ }
+
+ @Override
+ public void process(TProtocol in, TProtocol out) throws TException {
+ TMessage msg = in.readMessageBegin();
+ ProcessFunction fn = processMap.get(msg.name);
+ if (fn == null) {
+ TProtocolUtil.skip(in, TType.STRUCT);
+ in.readMessageEnd();
+ TApplicationException x = new TApplicationException(TApplicationException.UNKNOWN_METHOD, "Invalid method name: '"+msg.name+"'");
+ out.writeMessageBegin(new TMessage(msg.name, TMessageType.EXCEPTION, msg.seqid));
+ x.write(out);
+ out.writeMessageEnd();
+ out.getTransport().flush();
+ } else {
+ fn.process(msg.seqid, in, out, iface);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TByteArrayOutputStream.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TByteArrayOutputStream.java
new file mode 100644
index 000000000..3a2d56c88
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TByteArrayOutputStream.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.Charset;
+
+/**
+ * Class that allows access to the underlying buf without doing deep
+ * copies on it.
+ *
+ */
+public class TByteArrayOutputStream extends ByteArrayOutputStream {
+
+ private final int initialSize;
+
+ public TByteArrayOutputStream(int size) {
+ super(size);
+ this.initialSize = size;
+ }
+
+ public TByteArrayOutputStream() {
+ this(32);
+ }
+
+ public byte[] get() {
+ return buf;
+ }
+
+ public void reset() {
+ super.reset();
+ if (buf.length > initialSize) {
+ buf = new byte[initialSize];
+ }
+ }
+
+ public int len() {
+ return count;
+ }
+
+ public String toString(Charset charset) {
+ return new String(buf, 0, count, charset);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TDeserializer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TDeserializer.java
new file mode 100644
index 000000000..d1d396609
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TDeserializer.java
@@ -0,0 +1,339 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TField;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.protocol.TProtocolUtil;
+import org.apache.thrift.protocol.TType;
+import org.apache.thrift.transport.TMemoryInputTransport;
+
+/**
+ * Generic utility for easily deserializing objects from a byte array or Java
+ * String.
+ *
+ */
+public class TDeserializer {
+ private final TProtocol protocol_;
+ private final TMemoryInputTransport trans_;
+
+ /**
+ * Create a new TDeserializer that uses the TBinaryProtocol by default.
+ */
+ public TDeserializer() {
+ this(new TBinaryProtocol.Factory());
+ }
+
+ /**
+ * Create a new TDeserializer. It will use the TProtocol specified by the
+ * factory that is passed in.
+ *
+ * @param protocolFactory Factory to create a protocol
+ */
+ public TDeserializer(TProtocolFactory protocolFactory) {
+ trans_ = new TMemoryInputTransport();
+ protocol_ = protocolFactory.getProtocol(trans_);
+ }
+
+ /**
+ * Deserialize the Thrift object from a byte array.
+ *
+ * @param base The object to read into
+ * @param bytes The array to read from
+ */
+ public void deserialize(TBase base, byte[] bytes) throws TException {
+ deserialize(base, bytes, 0, bytes.length);
+ }
+
+ /**
+ * Deserialize the Thrift object from a byte array.
+ *
+ * @param base The object to read into
+ * @param bytes The array to read from
+ * @param offset The offset into {@code bytes}
+ * @param length The length to read from {@code bytes}
+ */
+ public void deserialize(TBase base, byte[] bytes, int offset, int length) throws TException {
+ try {
+ trans_.reset(bytes, offset, length);
+ base.read(protocol_);
+ } finally {
+ trans_.clear();
+ protocol_.reset();
+ }
+ }
+
+ /**
+ * Deserialize the Thrift object from a Java string, using a specified
+ * character set for decoding.
+ *
+ * @param base The object to read into
+ * @param data The string to read from
+ * @param charset Valid JVM charset
+ */
+ public void deserialize(TBase base, String data, String charset) throws TException {
+ try {
+ deserialize(base, data.getBytes(charset));
+ } catch (UnsupportedEncodingException uex) {
+ throw new TException("JVM DOES NOT SUPPORT ENCODING: " + charset);
+ } finally {
+ protocol_.reset();
+ }
+ }
+
+ /**
+ * Deserialize only a single Thrift object (addressed by recursively using field id)
+ * from a byte record.
+ * @param tb The object to read into
+ * @param bytes The serialized object to read from
+ * @param fieldIdPathFirst First of the FieldId's that define a path tb
+ * @param fieldIdPathRest The rest FieldId's that define a path tb
+ * @throws TException
+ */
+ public void partialDeserialize(TBase tb, byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ try {
+ if (locateField(bytes, fieldIdPathFirst, fieldIdPathRest) != null) {
+ // if this line is reached, iprot will be positioned at the start of tb.
+ tb.read(protocol_);
+ }
+ } catch (Exception e) {
+ throw new TException(e);
+ } finally {
+ trans_.clear();
+ protocol_.reset();
+ }
+ }
+
+ /**
+ * Deserialize only a boolean field (addressed by recursively using field id)
+ * from a byte record.
+ * @param bytes The serialized object to read from
+ * @param fieldIdPathFirst First of the FieldId's that define a path to a boolean field
+ * @param fieldIdPathRest The rest FieldId's that define a path to a boolean field
+ * @throws TException
+ */
+ public Boolean partialDeserializeBool(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ return (Boolean) partialDeserializeField(TType.BOOL, bytes, fieldIdPathFirst, fieldIdPathRest);
+ }
+
+ /**
+ * Deserialize only a byte field (addressed by recursively using field id)
+ * from a byte record.
+ * @param bytes The serialized object to read from
+ * @param fieldIdPathFirst First of the FieldId's that define a path to a byte field
+ * @param fieldIdPathRest The rest FieldId's that define a path to a byte field
+ * @throws TException
+ */
+ public Byte partialDeserializeByte(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ return (Byte) partialDeserializeField(TType.BYTE, bytes, fieldIdPathFirst, fieldIdPathRest);
+ }
+
+ /**
+ * Deserialize only a double field (addressed by recursively using field id)
+ * from a byte record.
+ * @param bytes The serialized object to read from
+ * @param fieldIdPathFirst First of the FieldId's that define a path to a double field
+ * @param fieldIdPathRest The rest FieldId's that define a path to a double field
+ * @throws TException
+ */
+ public Double partialDeserializeDouble(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ return (Double) partialDeserializeField(TType.DOUBLE, bytes, fieldIdPathFirst, fieldIdPathRest);
+ }
+
+ /**
+ * Deserialize only an i16 field (addressed by recursively using field id)
+ * from a byte record.
+ * @param bytes The serialized object to read from
+ * @param fieldIdPathFirst First of the FieldId's that define a path to an i16 field
+ * @param fieldIdPathRest The rest FieldId's that define a path to an i16 field
+ * @throws TException
+ */
+ public Short partialDeserializeI16(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ return (Short) partialDeserializeField(TType.I16, bytes, fieldIdPathFirst, fieldIdPathRest);
+ }
+
+ /**
+ * Deserialize only an i32 field (addressed by recursively using field id)
+ * from a byte record.
+ * @param bytes The serialized object to read from
+ * @param fieldIdPathFirst First of the FieldId's that define a path to an i32 field
+ * @param fieldIdPathRest The rest FieldId's that define a path to an i32 field
+ * @throws TException
+ */
+ public Integer partialDeserializeI32(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ return (Integer) partialDeserializeField(TType.I32, bytes, fieldIdPathFirst, fieldIdPathRest);
+ }
+
+ /**
+ * Deserialize only an i64 field (addressed by recursively using field id)
+ * from a byte record.
+ * @param bytes The serialized object to read from
+ * @param fieldIdPathFirst First of the FieldId's that define a path to an i64 field
+ * @param fieldIdPathRest The rest FieldId's that define a path to an i64 field
+ * @throws TException
+ */
+ public Long partialDeserializeI64(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ return (Long) partialDeserializeField(TType.I64, bytes, fieldIdPathFirst, fieldIdPathRest);
+ }
+
+ /**
+ * Deserialize only a string field (addressed by recursively using field id)
+ * from a byte record.
+ * @param bytes The serialized object to read from
+ * @param fieldIdPathFirst First of the FieldId's that define a path to a string field
+ * @param fieldIdPathRest The rest FieldId's that define a path to a string field
+ * @throws TException
+ */
+ public String partialDeserializeString(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ return (String) partialDeserializeField(TType.STRING, bytes, fieldIdPathFirst, fieldIdPathRest);
+ }
+
+ /**
+ * Deserialize only a binary field (addressed by recursively using field id)
+ * from a byte record.
+ * @param bytes The serialized object to read from
+ * @param fieldIdPathFirst First of the FieldId's that define a path to a binary field
+ * @param fieldIdPathRest The rest FieldId's that define a path to a binary field
+ * @throws TException
+ */
+ public ByteBuffer partialDeserializeByteArray(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ // TType does not have binary, so we use the arbitrary num 100
+ return (ByteBuffer) partialDeserializeField((byte)100, bytes, fieldIdPathFirst, fieldIdPathRest);
+ }
+
+ /**
+ * Deserialize only the id of the field set in a TUnion (addressed by recursively using field id)
+ * from a byte record.
+ * @param bytes The serialized object to read from
+ * @param fieldIdPathFirst First of the FieldId's that define a path to a TUnion
+ * @param fieldIdPathRest The rest FieldId's that define a path to a TUnion
+ * @throws TException
+ */
+ public Short partialDeserializeSetFieldIdInUnion(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ try {
+ TField field = locateField(bytes, fieldIdPathFirst, fieldIdPathRest);
+ if (field != null){
+ protocol_.readStructBegin(); // The Union
+ return protocol_.readFieldBegin().id; // The field set in the union
+ }
+ return null;
+ } catch (Exception e) {
+ throw new TException(e);
+ } finally {
+ trans_.clear();
+ protocol_.reset();
+ }
+ }
+
+ private Object partialDeserializeField(byte ttype, byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ try {
+ TField field = locateField(bytes, fieldIdPathFirst, fieldIdPathRest);
+ if (field != null) {
+ if (ttype == field.type) {
+ // if this point is reached, iprot will be positioned at the start of
+ // the field
+ switch (ttype) {
+ case TType.BOOL:
+ return protocol_.readBool();
+ case TType.BYTE:
+ return protocol_.readByte();
+ case TType.DOUBLE:
+ return protocol_.readDouble();
+ case TType.I16:
+ return protocol_.readI16();
+ case TType.I32:
+ return protocol_.readI32();
+ case TType.I64:
+ return protocol_.readI64();
+ case TType.STRING:
+ return protocol_.readString();
+ default:
+ return null;
+ }
+ }
+ // hack to differentiate between string and binary
+ if (ttype == 100 && field.type == TType.STRING) {
+ return protocol_.readBinary();
+ }
+ }
+ return null;
+ } catch (Exception e) {
+ throw new TException(e);
+ } finally {
+ trans_.clear();
+ protocol_.reset();
+ }
+ }
+
+ private TField locateField(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException {
+ trans_.reset(bytes);
+
+ TFieldIdEnum[] fieldIdPath = new TFieldIdEnum[fieldIdPathRest.length + 1];
+ fieldIdPath[0] = fieldIdPathFirst;
+ System.arraycopy(fieldIdPathRest, 0, fieldIdPath, 1, fieldIdPathRest.length);
+
+ // index into field ID path being currently searched for
+ int curPathIndex = 0;
+
+ // this will be the located field, or null if it is not located
+ TField field = null;
+
+ protocol_.readStructBegin();
+
+ while (curPathIndex < fieldIdPath.length) {
+ field = protocol_.readFieldBegin();
+ // we can stop searching if we either see a stop or we go past the field
+ // id we're looking for (since fields should now be serialized in asc
+ // order).
+ if (field.type == TType.STOP || field.id > fieldIdPath[curPathIndex].getThriftFieldId()) {
+ return null;
+ }
+
+ if (field.id != fieldIdPath[curPathIndex].getThriftFieldId()) {
+ // Not the field we're looking for. Skip field.
+ TProtocolUtil.skip(protocol_, field.type);
+ protocol_.readFieldEnd();
+ } else {
+ // This field is the next step in the path. Step into field.
+ curPathIndex++;
+ if (curPathIndex < fieldIdPath.length) {
+ protocol_.readStructBegin();
+ }
+ }
+ }
+ return field;
+ }
+
+ /**
+ * Deserialize the Thrift object from a Java string, using the default JVM
+ * charset encoding.
+ *
+ * @param base The object to read into
+ * @param data The string to read from
+ */
+ public void fromString(TBase base, String data) throws TException {
+ deserialize(base, data.getBytes());
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TEnum.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TEnum.java
new file mode 100644
index 000000000..325fdece7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TEnum.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+public interface TEnum {
+ public int getValue();
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TEnumHelper.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TEnumHelper.java
new file mode 100644
index 000000000..fbc778751
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TEnumHelper.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import java.lang.NoSuchMethodException;
+import java.lang.IllegalAccessException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Utility class with static methods for interacting with TEnum
+ */
+public class TEnumHelper {
+
+ /**
+ * Given a TEnum class and integer value, this method will return
+ * the associated constant from the given TEnum class.
+ * This method MUST be modified should the name of the 'findByValue' method
+ * change.
+ *
+ * @param enumClass TEnum from which to return a matching constant.
+ * @param value Value for which to return the constant.
+ *
+ * @return The constant in 'enumClass' whose value is 'value' or null if
+ * something went wrong.
+ */
+ public static TEnum getByValue(Class<? extends TEnum> enumClass, int value) {
+ try {
+ Method method = enumClass.getMethod("findByValue", int.class);
+ return (TEnum) method.invoke(null, value);
+ } catch (NoSuchMethodException nsme) {
+ return null;
+ } catch (IllegalAccessException iae) {
+ return null;
+ } catch (InvocationTargetException ite) {
+ return null;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TException.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TException.java
new file mode 100644
index 000000000..f84f4812e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TException.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+/**
+ * Generic exception class for Thrift.
+ *
+ */
+public class TException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public TException() {
+ super();
+ }
+
+ public TException(String message) {
+ super(message);
+ }
+
+ public TException(Throwable cause) {
+ super(cause);
+ }
+
+ public TException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TFieldIdEnum.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TFieldIdEnum.java
new file mode 100644
index 000000000..2956fba0b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TFieldIdEnum.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift;
+
+/**
+ * Interface for all generated struct Fields objects.
+ */
+public interface TFieldIdEnum {
+ /**
+ * Get the Thrift field id for the named field.
+ */
+ public short getThriftFieldId();
+
+ /**
+ * Get the field's name, exactly as in the IDL.
+ */
+ public String getFieldName();
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TFieldRequirementType.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TFieldRequirementType.java
new file mode 100644
index 000000000..74bac4eff
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TFieldRequirementType.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+/**
+ * Requirement type constants.
+ *
+ */
+public final class TFieldRequirementType {
+ public static final byte REQUIRED = 1;
+ public static final byte OPTIONAL = 2;
+ public static final byte DEFAULT = 3;
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TMultiplexedProcessor.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TMultiplexedProcessor.java
new file mode 100644
index 000000000..c49486217
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TMultiplexedProcessor.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import org.apache.thrift.protocol.*;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * <code>TMultiplexedProcessor</code> is a <code>TProcessor</code> allowing
+ * a single <code>TServer</code> to provide multiple services.
+ *
+ * <p>To do so, you instantiate the processor and then register additional
+ * processors with it, as shown in the following example:</p>
+ *
+ * <blockquote><code>
+ * TMultiplexedProcessor processor = new TMultiplexedProcessor();
+ *
+ * processor.registerProcessor(
+ * "Calculator",
+ * new Calculator.Processor(new CalculatorHandler()));
+ *
+ * processor.registerProcessor(
+ * "WeatherReport",
+ * new WeatherReport.Processor(new WeatherReportHandler()));
+ *
+ * TServerTransport t = new TServerSocket(9090);
+ * TSimpleServer server = new TSimpleServer(processor, t);
+ *
+ * server.serve();
+ * </code></blockquote>
+ */
+public class TMultiplexedProcessor implements TProcessor {
+
+ private final Map<String,TProcessor> SERVICE_PROCESSOR_MAP
+ = new HashMap<String,TProcessor>();
+ private TProcessor defaultProcessor;
+
+ /**
+ * 'Register' a service with this <code>TMultiplexedProcessor</code>. This
+ * allows us to broker requests to individual services by using the service
+ * name to select them at request time.
+ *
+ * @param serviceName Name of a service, has to be identical to the name
+ * declared in the Thrift IDL, e.g. "WeatherReport".
+ * @param processor Implementation of a service, usually referred to
+ * as "handlers", e.g. WeatherReportHandler implementing WeatherReport.Iface.
+ */
+ public void registerProcessor(String serviceName, TProcessor processor) {
+ SERVICE_PROCESSOR_MAP.put(serviceName, processor);
+ }
+
+ /**
+ * Register a service to be called to process queries without service name
+ * @param processor
+ */
+ public void registerDefault(TProcessor processor) {
+ defaultProcessor = processor;
+ }
+
+ /**
+ * This implementation of <code>process</code> performs the following steps:
+ *
+ * <ol>
+ * <li>Read the beginning of the message.</li>
+ * <li>Extract the service name from the message.</li>
+ * <li>Using the service name to locate the appropriate processor.</li>
+ * <li>Dispatch to the processor, with a decorated instance of TProtocol
+ * that allows readMessageBegin() to return the original TMessage.</li>
+ * </ol>
+ *
+ * @throws TProtocolException If the message type is not CALL or ONEWAY, if
+ * the service name was not found in the message, or if the service
+ * name was not found in the service map. You called {@link #registerProcessor(String, TProcessor) registerProcessor}
+ * during initialization, right? :)
+ */
+ public void process(TProtocol iprot, TProtocol oprot) throws TException {
+ /*
+ Use the actual underlying protocol (e.g. TBinaryProtocol) to read the
+ message header. This pulls the message "off the wire", which we'll
+ deal with at the end of this method.
+ */
+ TMessage message = iprot.readMessageBegin();
+
+ if (message.type != TMessageType.CALL && message.type != TMessageType.ONEWAY) {
+ throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
+ "This should not have happened!?");
+ }
+
+ // Extract the service name
+ int index = message.name.indexOf(TMultiplexedProtocol.SEPARATOR);
+ if (index < 0) {
+ if (defaultProcessor != null) {
+ // Dispatch processing to the stored processor
+ defaultProcessor.process(new StoredMessageProtocol(iprot, message), oprot);
+ return;
+ }
+ throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
+ "Service name not found in message name: " + message.name + ". Did you " +
+ "forget to use a TMultiplexProtocol in your client?");
+ }
+
+ // Create a new TMessage, something that can be consumed by any TProtocol
+ String serviceName = message.name.substring(0, index);
+ TProcessor actualProcessor = SERVICE_PROCESSOR_MAP.get(serviceName);
+ if (actualProcessor == null) {
+ throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
+ "Service name not found: " + serviceName + ". Did you forget " +
+ "to call registerProcessor()?");
+ }
+
+ // Create a new TMessage, removing the service name
+ TMessage standardMessage = new TMessage(
+ message.name.substring(serviceName.length()+TMultiplexedProtocol.SEPARATOR.length()),
+ message.type,
+ message.seqid
+ );
+
+ // Dispatch processing to the stored processor
+ actualProcessor.process(new StoredMessageProtocol(iprot, standardMessage), oprot);
+ }
+
+ /**
+ * Our goal was to work with any protocol. In order to do that, we needed
+ * to allow them to call readMessageBegin() and get a TMessage in exactly
+ * the standard format, without the service name prepended to TMessage.name.
+ */
+ private static class StoredMessageProtocol extends TProtocolDecorator {
+ TMessage messageBegin;
+ public StoredMessageProtocol(TProtocol protocol, TMessage messageBegin) {
+ super(protocol);
+ this.messageBegin = messageBegin;
+ }
+ @Override
+ public TMessage readMessageBegin() throws TException {
+ return messageBegin;
+ }
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TNonblockingMultiFetchClient.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TNonblockingMultiFetchClient.java
new file mode 100644
index 000000000..382d978db
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TNonblockingMultiFetchClient.java
@@ -0,0 +1,399 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.thrift;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.SocketChannel;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+
+/**
+ * This class uses a single thread to set up non-blocking sockets to a set
+ * of remote servers (hostname and port pairs), and sends a same request to
+ * all these servers. It then fetches responses from servers.
+ *
+ * Parameters:
+ * int maxRecvBufBytesPerServer - an upper limit for receive buffer size
+ * per server (in byte). If a response from a server exceeds this limit, the
+ * client will not allocate memory or read response data for it.
+ *
+ * int fetchTimeoutSeconds - time limit for fetching responses from all
+ * servers (in second). After the timeout, the fetch job is stopped and
+ * available responses are returned.
+ *
+ * ByteBuffer requestBuf - request message that is sent to all servers.
+ *
+ * Output:
+ * Responses are stored in an array of ByteBuffers. Index of elements in
+ * this array corresponds to index of servers in the server list. Content in
+ * a ByteBuffer may be in one of the following forms:
+ * 1. First 4 bytes form an integer indicating length of following data,
+ * then followed by the data.
+ * 2. First 4 bytes form an integer indicating length of following data,
+ * then followed by nothing - this happens when the response data size
+ * exceeds maxRecvBufBytesPerServer, and the client will not read any
+ * response data.
+ * 3. No data in the ByteBuffer - this happens when the server does not
+ * return any response within fetchTimeoutSeconds.
+ *
+ * In some special cases (no servers are given, fetchTimeoutSeconds less
+ * than or equal to 0, requestBuf is null), the return is null.
+ *
+ * Note:
+ * It assumes all remote servers are TNonblockingServers and use
+ * TFramedTransport.
+ *
+ */
+public class TNonblockingMultiFetchClient {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(
+ TNonblockingMultiFetchClient.class.getName()
+ );
+
+ // if the size of the response msg exceeds this limit (in byte), we will
+ // not read the msg
+ private int maxRecvBufBytesPerServer;
+
+ // time limit for fetching data from all servers (in second)
+ private int fetchTimeoutSeconds;
+
+ // store request that will be sent to servers
+ private ByteBuffer requestBuf;
+ private ByteBuffer requestBufDuplication;
+
+ // a list of remote servers
+ private List<InetSocketAddress> servers;
+
+ // store fetch results
+ private TNonblockingMultiFetchStats stats;
+ private ByteBuffer[] recvBuf;
+
+ public TNonblockingMultiFetchClient(int maxRecvBufBytesPerServer,
+ int fetchTimeoutSeconds, ByteBuffer requestBuf,
+ List<InetSocketAddress> servers) {
+ this.maxRecvBufBytesPerServer = maxRecvBufBytesPerServer;
+ this.fetchTimeoutSeconds = fetchTimeoutSeconds;
+ this.requestBuf = requestBuf;
+ this.servers = servers;
+
+ stats = new TNonblockingMultiFetchStats();
+ recvBuf = null;
+ }
+
+ public synchronized int getMaxRecvBufBytesPerServer() {
+ return maxRecvBufBytesPerServer;
+ }
+
+ public synchronized int getFetchTimeoutSeconds() {
+ return fetchTimeoutSeconds;
+ }
+
+ /**
+ * return a duplication of requestBuf, so that requestBuf will not
+ * be modified by others.
+ */
+ public synchronized ByteBuffer getRequestBuf() {
+ if (requestBuf == null) {
+ return null;
+ } else {
+ if (requestBufDuplication == null) {
+ requestBufDuplication = requestBuf.duplicate();
+ }
+ return requestBufDuplication;
+ }
+ }
+
+ public synchronized List<InetSocketAddress> getServerList() {
+ if (servers == null) {
+ return null;
+ }
+ return Collections.unmodifiableList(servers);
+ }
+
+ public synchronized TNonblockingMultiFetchStats getFetchStats() {
+ return stats;
+ }
+
+ /**
+ * main entry function for fetching from servers
+ */
+ public synchronized ByteBuffer[] fetch() {
+ // clear previous results
+ recvBuf = null;
+ stats.clear();
+
+ if (servers == null || servers.size() == 0 ||
+ requestBuf == null || fetchTimeoutSeconds <= 0) {
+ return recvBuf;
+ }
+
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ MultiFetch multiFetch = new MultiFetch();
+ FutureTask<?> task = new FutureTask(multiFetch, null);
+ executor.execute(task);
+ try {
+ task.get(fetchTimeoutSeconds, TimeUnit.SECONDS);
+ } catch(InterruptedException ie) {
+ // attempt to cancel execution of the task.
+ task.cancel(true);
+ LOGGER.error("interrupted during fetch: "+ie.toString());
+ } catch(ExecutionException ee) {
+ // attempt to cancel execution of the task.
+ task.cancel(true);
+ LOGGER.error("exception during fetch: "+ee.toString());
+ } catch(TimeoutException te) {
+ // attempt to cancel execution of the task.
+ task.cancel(true);
+ LOGGER.error("timeout for fetch: "+te.toString());
+ }
+
+ executor.shutdownNow();
+ multiFetch.close();
+ return recvBuf;
+ }
+
+ /**
+ * Private class that does real fetch job.
+ * Users are not allowed to directly use this class, as its run()
+ * function may run forever.
+ */
+ private class MultiFetch implements Runnable {
+ private Selector selector;
+
+ /**
+ * main entry function for fetching.
+ *
+ * Server responses are stored in TNonblocingMultiFetchClient.recvBuf,
+ * and fetch statistics is in TNonblockingMultiFetchClient.stats.
+ *
+ * Sanity check for parameters has been done in
+ * TNonblockingMultiFetchClient before calling this function.
+ */
+ public void run() {
+ long t1 = System.currentTimeMillis();
+
+ int numTotalServers = servers.size();
+ stats.setNumTotalServers(numTotalServers);
+
+ // buffer for receiving response from servers
+ recvBuf = new ByteBuffer[numTotalServers];
+ // buffer for sending request
+ ByteBuffer sendBuf[] = new ByteBuffer[numTotalServers];
+ long numBytesRead[] = new long[numTotalServers];
+ int frameSize[] = new int[numTotalServers];
+ boolean hasReadFrameSize[] = new boolean[numTotalServers];
+
+ try {
+ selector = Selector.open();
+ } catch (IOException e) {
+ LOGGER.error("selector opens error: "+e.toString());
+ return;
+ }
+
+ for (int i = 0; i < numTotalServers; i++) {
+ // create buffer to send request to server.
+ sendBuf[i] = requestBuf.duplicate();
+ // create buffer to read response's frame size from server
+ recvBuf[i] = ByteBuffer.allocate(4);
+ stats.incTotalRecvBufBytes(4);
+
+ InetSocketAddress server = servers.get(i);
+ SocketChannel s = null;
+ SelectionKey key = null;
+ try {
+ s = SocketChannel.open();
+ s.configureBlocking(false);
+ // now this method is non-blocking
+ s.connect(server);
+ key = s.register(selector, s.validOps());
+ // attach index of the key
+ key.attach(i);
+ } catch (Exception e) {
+ stats.incNumConnectErrorServers();
+ String err = String.format("set up socket to server %s error: %s",
+ server.toString(), e.toString());
+ LOGGER.error(err);
+ // free resource
+ if (s != null) {
+ try {s.close();} catch (Exception ex) {}
+ }
+ if (key != null) {
+ key.cancel();
+ }
+ }
+ }
+
+ // wait for events
+ while (stats.getNumReadCompletedServers() +
+ stats.getNumConnectErrorServers() < stats.getNumTotalServers()) {
+ // if the thread is interrupted (e.g., task is cancelled)
+ if (Thread.currentThread().isInterrupted()) {
+ return;
+ }
+
+ try{
+ selector.select();
+ } catch (Exception e) {
+ LOGGER.error("selector selects error: "+e.toString());
+ continue;
+ }
+
+ Iterator<SelectionKey> it = selector.selectedKeys().iterator();
+ while (it.hasNext()) {
+ SelectionKey selKey = it.next();
+ it.remove();
+
+ // get previously attached index
+ int index = (Integer)selKey.attachment();
+
+ if (selKey.isValid() && selKey.isConnectable()) {
+ // if this socket throws an exception (e.g., connection refused),
+ // print error msg and skip it.
+ try {
+ SocketChannel sChannel = (SocketChannel)selKey.channel();
+ sChannel.finishConnect();
+ } catch (Exception e) {
+ stats.incNumConnectErrorServers();
+ String err = String.format("socket %d connects to server %s " +
+ "error: %s",
+ index, servers.get(index).toString(), e.toString());
+ LOGGER.error(err);
+ }
+ }
+
+ if (selKey.isValid() && selKey.isWritable()) {
+ if (sendBuf[index].hasRemaining()) {
+ // if this socket throws an exception, print error msg and
+ // skip it.
+ try {
+ SocketChannel sChannel = (SocketChannel)selKey.channel();
+ sChannel.write(sendBuf[index]);
+ } catch (Exception e) {
+ String err = String.format("socket %d writes to server %s " +
+ "error: %s",
+ index, servers.get(index).toString(), e.toString());
+ LOGGER.error(err);
+ }
+ }
+ }
+
+ if (selKey.isValid() && selKey.isReadable()) {
+ // if this socket throws an exception, print error msg and
+ // skip it.
+ try {
+ SocketChannel sChannel = (SocketChannel)selKey.channel();
+ int bytesRead = sChannel.read(recvBuf[index]);
+
+ if (bytesRead > 0) {
+ numBytesRead[index] += bytesRead;
+
+ if (!hasReadFrameSize[index] &&
+ recvBuf[index].remaining()==0) {
+ // if the frame size has been read completely, then prepare
+ // to read the actual frame.
+ frameSize[index] = recvBuf[index].getInt(0);
+
+ if (frameSize[index] <= 0) {
+ stats.incNumInvalidFrameSize();
+ String err = String.format("Read an invalid frame size %d"
+ + " from %s. Does the server use TFramedTransport? ",
+ frameSize[index], servers.get(index).toString());
+ LOGGER.error(err);
+ sChannel.close();
+ continue;
+ }
+
+ if (frameSize[index] + 4 > stats.getMaxResponseBytes()) {
+ stats.setMaxResponseBytes(frameSize[index]+4);
+ }
+
+ if (frameSize[index] + 4 > maxRecvBufBytesPerServer) {
+ stats.incNumOverflowedRecvBuf();
+ String err = String.format("Read frame size %d from %s,"
+ + " total buffer size would exceed limit %d",
+ frameSize[index], servers.get(index).toString(),
+ maxRecvBufBytesPerServer);
+ LOGGER.error(err);
+ sChannel.close();
+ continue;
+ }
+
+ // reallocate buffer for actual frame data
+ recvBuf[index] = ByteBuffer.allocate(frameSize[index] + 4);
+ recvBuf[index].putInt(frameSize[index]);
+
+ stats.incTotalRecvBufBytes(frameSize[index]);
+ hasReadFrameSize[index] = true;
+ }
+
+ if (hasReadFrameSize[index] &&
+ numBytesRead[index] >= frameSize[index]+4) {
+ // has read all data
+ sChannel.close();
+ stats.incNumReadCompletedServers();
+ long t2 = System.currentTimeMillis();
+ stats.setReadTime(t2-t1);
+ }
+ }
+ } catch (Exception e) {
+ String err = String.format("socket %d reads from server %s " +
+ "error: %s",
+ index, servers.get(index).toString(), e.toString());
+ LOGGER.error(err);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * dispose any resource allocated
+ */
+ public void close() {
+ try {
+ if (selector.isOpen()) {
+ Iterator<SelectionKey> it = selector.keys().iterator();
+ while (it.hasNext()) {
+ SelectionKey selKey = it.next();
+ SocketChannel sChannel = (SocketChannel)selKey.channel();
+ sChannel.close();
+ }
+
+ selector.close();
+ }
+ } catch (IOException e) {
+ LOGGER.error("free resource error: "+e.toString());
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TNonblockingMultiFetchStats.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TNonblockingMultiFetchStats.java
new file mode 100644
index 000000000..90b86208b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TNonblockingMultiFetchStats.java
@@ -0,0 +1,80 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.thrift;
+
+/**
+ * This class keeps track of statistics for TNonblockinMultiFetchClient.
+ */
+public class TNonblockingMultiFetchStats {
+ private int numTotalServers;
+ private int numReadCompletedServers;
+ private int numConnectErrorServers;
+ private int totalRecvBufBytes;
+ private int maxResponseBytes;
+ private int numOverflowedRecvBuf;
+ private int numInvalidFrameSize;
+ // time from the beginning of fetch() function to the reading finish
+ // time of the last socket (in milli-second)
+ private long readTime;
+
+ public TNonblockingMultiFetchStats() {
+ clear();
+ }
+
+ public void clear() {
+ numTotalServers = 0;
+ numReadCompletedServers = 0;
+ numConnectErrorServers = 0;
+ totalRecvBufBytes = 0;
+ maxResponseBytes = 0;
+ numOverflowedRecvBuf = 0;
+ numInvalidFrameSize = 0;
+ readTime = 0;
+ }
+
+ public String toString() {
+ String stats = String.format("numTotalServers=%d, " +
+ "numReadCompletedServers=%d, numConnectErrorServers=%d, " +
+ "numUnresponsiveServers=%d, totalRecvBufBytes=%fM, " +
+ "maxResponseBytes=%d, numOverflowedRecvBuf=%d, " +
+ "numInvalidFrameSize=%d, readTime=%dms",
+ numTotalServers, numReadCompletedServers, numConnectErrorServers,
+ (numTotalServers-numReadCompletedServers-numConnectErrorServers),
+ totalRecvBufBytes/1024.0/1024, maxResponseBytes, numOverflowedRecvBuf,
+ numInvalidFrameSize, readTime);
+ return stats;
+ }
+
+ public void setNumTotalServers(int val) { numTotalServers = val; }
+ public void setMaxResponseBytes(int val) { maxResponseBytes = val; }
+ public void setReadTime(long val) { readTime = val; }
+ public void incNumReadCompletedServers() { numReadCompletedServers++; }
+ public void incNumConnectErrorServers() { numConnectErrorServers++; }
+ public void incNumOverflowedRecvBuf() { numOverflowedRecvBuf++; }
+ public void incTotalRecvBufBytes(int val) { totalRecvBufBytes += val; }
+ public void incNumInvalidFrameSize() { numInvalidFrameSize++; }
+
+ public int getMaxResponseBytes() { return maxResponseBytes; }
+ public int getNumReadCompletedServers() { return numReadCompletedServers; }
+ public int getNumConnectErrorServers() { return numConnectErrorServers; }
+ public int getNumTotalServers() { return numTotalServers; }
+ public int getNumOverflowedRecvBuf() { return numOverflowedRecvBuf;}
+ public int getTotalRecvBufBytes() { return totalRecvBufBytes;}
+ public int getNumInvalidFrameSize() { return numInvalidFrameSize; }
+ public long getReadTime() { return readTime; }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TProcessor.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TProcessor.java
new file mode 100644
index 000000000..15ba9c0fe
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TProcessor.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import org.apache.thrift.protocol.TProtocol;
+
+/**
+ * A processor is a generic object which operates upon an input stream and
+ * writes to some output stream.
+ */
+public interface TProcessor {
+ public void process(TProtocol in, TProtocol out) throws TException;
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TProcessorFactory.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TProcessorFactory.java
new file mode 100644
index 000000000..81933a211
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TProcessorFactory.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * The default processor factory just returns a singleton
+ * instance.
+ */
+public class TProcessorFactory {
+
+ private final TProcessor processor_;
+
+ public TProcessorFactory(TProcessor processor) {
+ processor_ = processor;
+ }
+
+ public TProcessor getProcessor(TTransport trans) {
+ return processor_;
+ }
+
+ public boolean isAsyncProcessor() {
+ return processor_ instanceof TAsyncProcessor;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TSerializable.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TSerializable.java
new file mode 100644
index 000000000..80002c761
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TSerializable.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import org.apache.thrift.protocol.TProtocol;
+
+/**
+ * Generic base interface for generated Thrift objects.
+ *
+ */
+public interface TSerializable {
+
+ /**
+ * Reads the TObject from the given input protocol.
+ *
+ * @param iprot Input protocol
+ */
+ public void read(TProtocol iprot) throws TException;
+
+ /**
+ * Writes the objects out to the protocol
+ *
+ * @param oprot Output protocol
+ */
+ public void write(TProtocol oprot) throws TException;
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TSerializer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TSerializer.java
new file mode 100644
index 000000000..4e1ce6129
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TSerializer.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.transport.TIOStreamTransport;
+
+/**
+ * Generic utility for easily serializing objects into a byte array or Java
+ * String.
+ *
+ */
+public class TSerializer {
+
+ /**
+ * This is the byte array that data is actually serialized into
+ */
+ private final ByteArrayOutputStream baos_ = new ByteArrayOutputStream();
+
+ /**
+ * This transport wraps that byte array
+ */
+ private final TIOStreamTransport transport_ = new TIOStreamTransport(baos_);
+
+ /**
+ * Internal protocol used for serializing objects.
+ */
+ private TProtocol protocol_;
+
+ /**
+ * Create a new TSerializer that uses the TBinaryProtocol by default.
+ */
+ public TSerializer() {
+ this(new TBinaryProtocol.Factory());
+ }
+
+ /**
+ * Create a new TSerializer. It will use the TProtocol specified by the
+ * factory that is passed in.
+ *
+ * @param protocolFactory Factory to create a protocol
+ */
+ public TSerializer(TProtocolFactory protocolFactory) {
+ protocol_ = protocolFactory.getProtocol(transport_);
+ }
+
+ /**
+ * Serialize the Thrift object into a byte array. The process is simple,
+ * just clear the byte array output, write the object into it, and grab the
+ * raw bytes.
+ *
+ * @param base The object to serialize
+ * @return Serialized object in byte[] format
+ */
+ public byte[] serialize(TBase base) throws TException {
+ baos_.reset();
+ base.write(protocol_);
+ return baos_.toByteArray();
+ }
+
+ /**
+ * Serialize the Thrift object into a Java string, using a specified
+ * character set for encoding.
+ *
+ * @param base The object to serialize
+ * @param charset Valid JVM charset
+ * @return Serialized object as a String
+ */
+ public String toString(TBase base, String charset) throws TException {
+ try {
+ return new String(serialize(base), charset);
+ } catch (UnsupportedEncodingException uex) {
+ throw new TException("JVM DOES NOT SUPPORT ENCODING: " + charset);
+ }
+ }
+
+ /**
+ * Serialize the Thrift object into a Java string, using the default JVM
+ * charset encoding.
+ *
+ * @param base The object to serialize
+ * @return Serialized object as a String
+ */
+ public String toString(TBase base) throws TException {
+ return new String(serialize(base));
+ }
+}
+
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TServiceClient.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TServiceClient.java
new file mode 100644
index 000000000..00a36ee7f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TServiceClient.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import org.apache.thrift.protocol.TMessage;
+import org.apache.thrift.protocol.TMessageType;
+import org.apache.thrift.protocol.TProtocol;
+
+/**
+ * A TServiceClient is used to communicate with a TService implementation
+ * across protocols and transports.
+ */
+public abstract class TServiceClient {
+ public TServiceClient(TProtocol prot) {
+ this(prot, prot);
+ }
+
+ public TServiceClient(TProtocol iprot, TProtocol oprot) {
+ iprot_ = iprot;
+ oprot_ = oprot;
+ }
+
+ protected TProtocol iprot_;
+ protected TProtocol oprot_;
+
+ protected int seqid_;
+
+ /**
+ * Get the TProtocol being used as the input (read) protocol.
+ * @return the TProtocol being used as the input (read) protocol.
+ */
+ public TProtocol getInputProtocol() {
+ return this.iprot_;
+ }
+
+ /**
+ * Get the TProtocol being used as the output (write) protocol.
+ * @return the TProtocol being used as the output (write) protocol.
+ */
+ public TProtocol getOutputProtocol() {
+ return this.oprot_;
+ }
+
+ protected void sendBase(String methodName, TBase<?,?> args) throws TException {
+ sendBase(methodName, args, TMessageType.CALL);
+ }
+
+ protected void sendBaseOneway(String methodName, TBase<?,?> args) throws TException {
+ sendBase(methodName, args, TMessageType.ONEWAY);
+ }
+
+ private void sendBase(String methodName, TBase<?,?> args, byte type) throws TException {
+ oprot_.writeMessageBegin(new TMessage(methodName, type, ++seqid_));
+ args.write(oprot_);
+ oprot_.writeMessageEnd();
+ oprot_.getTransport().flush();
+ }
+
+ protected void receiveBase(TBase<?,?> result, String methodName) throws TException {
+ TMessage msg = iprot_.readMessageBegin();
+ if (msg.type == TMessageType.EXCEPTION) {
+ TApplicationException x = new TApplicationException();
+ x.read(iprot_);
+ iprot_.readMessageEnd();
+ throw x;
+ }
+ if (msg.seqid != seqid_) {
+ throw new TApplicationException(TApplicationException.BAD_SEQUENCE_ID,
+ String.format("%s failed: out of sequence response: expected %d but got %d", methodName, seqid_, msg.seqid));
+ }
+ result.read(iprot_);
+ iprot_.readMessageEnd();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TServiceClientFactory.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TServiceClientFactory.java
new file mode 100644
index 000000000..988e65591
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TServiceClientFactory.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift;
+
+import org.apache.thrift.protocol.TProtocol;
+
+/**
+ * A TServiceClientFactory provides a general way to get a TServiceClient
+ * connected to a remote TService via a protocol.
+ * @param <T>
+ */
+public interface TServiceClientFactory<T extends TServiceClient> {
+ /**
+ * Get a brand-new T using <i>prot</i> as both the input and output protocol.
+ * @param prot
+ * @return A brand-new T using <i>prot</i> as both the input and output protocol.
+ */
+ public T getClient(TProtocol prot);
+
+ /**
+ * Get a brand new T using the specified input and output protocols. The
+ * input and output protocols may be the same instance.
+ * @param iprot
+ * @param oprot
+ * @return a brand new T using the specified input and output protocols
+ */
+ public T getClient(TProtocol iprot, TProtocol oprot);
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TUnion.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TUnion.java
new file mode 100644
index 000000000..1ef11df49
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TUnion.java
@@ -0,0 +1,279 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.thrift;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.thrift.protocol.TField;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolException;
+import org.apache.thrift.protocol.TStruct;
+import org.apache.thrift.scheme.IScheme;
+import org.apache.thrift.scheme.SchemeFactory;
+import org.apache.thrift.scheme.StandardScheme;
+import org.apache.thrift.scheme.TupleScheme;
+
+public abstract class TUnion<T extends TUnion<T,F>, F extends TFieldIdEnum> implements TBase<T, F> {
+
+ protected Object value_;
+ protected F setField_;
+
+ protected TUnion() {
+ setField_ = null;
+ value_ = null;
+ }
+
+ private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>();
+ static {
+ schemes.put(StandardScheme.class, new TUnionStandardSchemeFactory());
+ schemes.put(TupleScheme.class, new TUnionTupleSchemeFactory());
+ }
+
+ protected TUnion(F setField, Object value) {
+ setFieldValue(setField, value);
+ }
+
+ protected TUnion(TUnion<T, F> other) {
+ if (!other.getClass().equals(this.getClass())) {
+ throw new ClassCastException();
+ }
+ setField_ = other.setField_;
+ value_ = deepCopyObject(other.value_);
+ }
+
+ private static Object deepCopyObject(Object o) {
+ if (o instanceof TBase) {
+ return ((TBase)o).deepCopy();
+ } else if (o instanceof ByteBuffer) {
+ return TBaseHelper.copyBinary((ByteBuffer)o);
+ } else if (o instanceof List) {
+ return deepCopyList((List)o);
+ } else if (o instanceof Set) {
+ return deepCopySet((Set)o);
+ } else if (o instanceof Map) {
+ return deepCopyMap((Map)o);
+ } else {
+ return o;
+ }
+ }
+
+ private static Map deepCopyMap(Map<Object, Object> map) {
+ Map copy = new HashMap(map.size());
+ for (Map.Entry<Object, Object> entry : map.entrySet()) {
+ copy.put(deepCopyObject(entry.getKey()), deepCopyObject(entry.getValue()));
+ }
+ return copy;
+ }
+
+ private static Set deepCopySet(Set set) {
+ Set copy = new HashSet(set.size());
+ for (Object o : set) {
+ copy.add(deepCopyObject(o));
+ }
+ return copy;
+ }
+
+ private static List deepCopyList(List list) {
+ List copy = new ArrayList(list.size());
+ for (Object o : list) {
+ copy.add(deepCopyObject(o));
+ }
+ return copy;
+ }
+
+ public F getSetField() {
+ return setField_;
+ }
+
+ public Object getFieldValue() {
+ return value_;
+ }
+
+ public Object getFieldValue(F fieldId) {
+ if (fieldId != setField_) {
+ throw new IllegalArgumentException("Cannot get the value of field " + fieldId + " because union's set field is " + setField_);
+ }
+
+ return getFieldValue();
+ }
+
+ public Object getFieldValue(int fieldId) {
+ return getFieldValue(enumForId((short)fieldId));
+ }
+
+ public boolean isSet() {
+ return setField_ != null;
+ }
+
+ public boolean isSet(F fieldId) {
+ return setField_ == fieldId;
+ }
+
+ public boolean isSet(int fieldId) {
+ return isSet(enumForId((short)fieldId));
+ }
+
+ public void read(TProtocol iprot) throws TException {
+ schemes.get(iprot.getScheme()).getScheme().read(iprot, this);
+ }
+
+ public void setFieldValue(F fieldId, Object value) {
+ checkType(fieldId, value);
+ setField_ = fieldId;
+ value_ = value;
+ }
+
+ public void setFieldValue(int fieldId, Object value) {
+ setFieldValue(enumForId((short)fieldId), value);
+ }
+
+ public void write(TProtocol oprot) throws TException {
+ schemes.get(oprot.getScheme()).getScheme().write(oprot, this);
+ }
+
+ /**
+ * Implementation should be generated so that we can efficiently type check
+ * various values.
+ * @param setField
+ * @param value
+ */
+ protected abstract void checkType(F setField, Object value) throws ClassCastException;
+
+ /**
+ * Implementation should be generated to read the right stuff from the wire
+ * based on the field header.
+ * @param field
+ * @return read Object based on the field header, as specified by the argument.
+ */
+ protected abstract Object standardSchemeReadValue(TProtocol iprot, TField field) throws TException;
+ protected abstract void standardSchemeWriteValue(TProtocol oprot) throws TException;
+
+ protected abstract Object tupleSchemeReadValue(TProtocol iprot, short fieldID) throws TException;
+ protected abstract void tupleSchemeWriteValue(TProtocol oprot) throws TException;
+
+ protected abstract TStruct getStructDesc();
+
+ protected abstract TField getFieldDesc(F setField);
+
+ protected abstract F enumForId(short id);
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("<");
+ sb.append(this.getClass().getSimpleName());
+ sb.append(" ");
+
+ if (getSetField() != null) {
+ Object v = getFieldValue();
+ sb.append(getFieldDesc(getSetField()).name);
+ sb.append(":");
+ if(v instanceof ByteBuffer) {
+ TBaseHelper.toString((ByteBuffer)v, sb);
+ } else {
+ sb.append(v.toString());
+ }
+ }
+ sb.append(">");
+ return sb.toString();
+ }
+
+ public final void clear() {
+ this.setField_ = null;
+ this.value_ = null;
+ }
+
+ private static class TUnionStandardSchemeFactory implements SchemeFactory {
+ public TUnionStandardScheme getScheme() {
+ return new TUnionStandardScheme();
+ }
+ }
+
+ private static class TUnionStandardScheme extends StandardScheme<TUnion> {
+
+ @Override
+ public void read(TProtocol iprot, TUnion struct) throws TException {
+ struct.setField_ = null;
+ struct.value_ = null;
+
+ iprot.readStructBegin();
+
+ TField field = iprot.readFieldBegin();
+
+ struct.value_ = struct.standardSchemeReadValue(iprot, field);
+ if (struct.value_ != null) {
+ struct.setField_ = struct.enumForId(field.id);
+ }
+
+ iprot.readFieldEnd();
+ // this is so that we will eat the stop byte. we could put a check here to
+ // make sure that it actually *is* the stop byte, but it's faster to do it
+ // this way.
+ iprot.readFieldBegin();
+ iprot.readStructEnd();
+ }
+
+ @Override
+ public void write(TProtocol oprot, TUnion struct) throws TException {
+ if (struct.getSetField() == null || struct.getFieldValue() == null) {
+ throw new TProtocolException("Cannot write a TUnion with no set value!");
+ }
+ oprot.writeStructBegin(struct.getStructDesc());
+ oprot.writeFieldBegin(struct.getFieldDesc(struct.setField_));
+ struct.standardSchemeWriteValue(oprot);
+ oprot.writeFieldEnd();
+ oprot.writeFieldStop();
+ oprot.writeStructEnd();
+ }
+ }
+
+ private static class TUnionTupleSchemeFactory implements SchemeFactory {
+ public TUnionTupleScheme getScheme() {
+ return new TUnionTupleScheme();
+ }
+ }
+
+ private static class TUnionTupleScheme extends TupleScheme<TUnion> {
+
+ @Override
+ public void read(TProtocol iprot, TUnion struct) throws TException {
+ struct.setField_ = null;
+ struct.value_ = null;
+ short fieldID = iprot.readI16();
+ struct.value_ = struct.tupleSchemeReadValue(iprot, fieldID);
+ if (struct.value_ != null) {
+ struct.setField_ = struct.enumForId(fieldID);
+ }
+ }
+
+ @Override
+ public void write(TProtocol oprot, TUnion struct) throws TException {
+ if (struct.getSetField() == null || struct.getFieldValue() == null) {
+ throw new TProtocolException("Cannot write a TUnion with no set value!");
+ }
+ oprot.writeI16(struct.setField_.getThriftFieldId());
+ struct.tupleSchemeWriteValue(oprot);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/annotation/Nullable.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/annotation/Nullable.java
new file mode 100644
index 000000000..a34b01ebb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/annotation/Nullable.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Annotation indicating a field, method return, or method parameter may be {@code null}.
+ * We package our own annotation to avoid a mandatory third-party dependency.
+ */
+@Retention(RetentionPolicy.CLASS)
+public @interface Nullable {
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/AsyncMethodCallback.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/AsyncMethodCallback.java
new file mode 100644
index 000000000..4ebde0741
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/AsyncMethodCallback.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.async;
+
+/**
+ * A handler interface asynchronous clients can implement to receive future
+ * notice of the results of an asynchronous method call.
+ *
+ * @param <T> The return type of the asynchronously invoked method.
+ */
+public interface AsyncMethodCallback<T> {
+ /**
+ * This method will be called when the remote side has completed invoking
+ * your method call and the result is fully read. For {@code oneway} method
+ * calls, this method will be called as soon as we have completed writing out
+ * the request.
+ *
+ * @param response The return value of the asynchronously invoked method;
+ * {@code null} for void methods which includes
+ * {@code oneway} methods.
+ */
+ void onComplete(T response);
+
+ /**
+ * This method will be called when there is either an unexpected client-side
+ * exception like an IOException or else when the remote method raises an
+ * exception, either declared in the IDL or due to an unexpected server-side
+ * error.
+ *
+ * @param exception The exception encountered processing the the asynchronous
+ * method call, may be a local exception or an unmarshalled
+ * remote exception.
+ */
+ void onError(Exception exception);
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClient.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClient.java
new file mode 100644
index 000000000..005018a83
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClient.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.async;
+
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.transport.TNonblockingTransport;
+
+public abstract class TAsyncClient {
+ protected final TProtocolFactory ___protocolFactory;
+ protected final TNonblockingTransport ___transport;
+ protected final TAsyncClientManager ___manager;
+ protected TAsyncMethodCall ___currentMethod;
+ private Exception ___error;
+ private long ___timeout;
+
+ public TAsyncClient(TProtocolFactory protocolFactory, TAsyncClientManager manager, TNonblockingTransport transport) {
+ this(protocolFactory, manager, transport, 0);
+ }
+
+ public TAsyncClient(TProtocolFactory protocolFactory, TAsyncClientManager manager, TNonblockingTransport transport, long timeout) {
+ this.___protocolFactory = protocolFactory;
+ this.___manager = manager;
+ this.___transport = transport;
+ this.___timeout = timeout;
+ }
+
+ public TProtocolFactory getProtocolFactory() {
+ return ___protocolFactory;
+ }
+
+ public long getTimeout() {
+ return ___timeout;
+ }
+
+ public boolean hasTimeout() {
+ return ___timeout > 0;
+ }
+
+ public void setTimeout(long timeout) {
+ this.___timeout = timeout;
+ }
+
+ /**
+ * Is the client in an error state?
+ * @return If client in an error state?
+ */
+ public boolean hasError() {
+ return ___error != null;
+ }
+
+ /**
+ * Get the client's error - returns null if no error
+ * @return Get the client's error. <p> returns null if no error
+ */
+ public Exception getError() {
+ return ___error;
+ }
+
+ protected void checkReady() {
+ // Ensure we are not currently executing a method
+ if (___currentMethod != null) {
+ throw new IllegalStateException("Client is currently executing another method: " + ___currentMethod.getClass().getName());
+ }
+
+ // Ensure we're not in an error state
+ if (___error != null) {
+ throw new IllegalStateException("Client has an error!", ___error);
+ }
+ }
+
+ /**
+ * Called by delegate method when finished
+ */
+ protected void onComplete() {
+ ___currentMethod = null;
+ }
+
+ /**
+ * Called by delegate method on error
+ */
+ protected void onError(Exception exception) {
+ ___transport.close();
+ ___currentMethod = null;
+ ___error = exception;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClientFactory.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClientFactory.java
new file mode 100644
index 000000000..28feb73d1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClientFactory.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.async;
+
+import org.apache.thrift.transport.TNonblockingTransport;
+
+public interface TAsyncClientFactory<T extends TAsyncClient> {
+ public T getAsyncClient(TNonblockingTransport transport);
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClientManager.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClientManager.java
new file mode 100644
index 000000000..c07ccd540
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClientManager.java
@@ -0,0 +1,201 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.async;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.nio.channels.ClosedSelectorException;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.spi.SelectorProvider;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.thrift.TException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Contains selector thread which transitions method call objects
+ */
+public class TAsyncClientManager {
+ private static final Logger LOGGER = LoggerFactory.getLogger(TAsyncClientManager.class.getName());
+
+ private final SelectThread selectThread;
+ private final ConcurrentLinkedQueue<TAsyncMethodCall> pendingCalls = new ConcurrentLinkedQueue<TAsyncMethodCall>();
+
+ public TAsyncClientManager() throws IOException {
+ this.selectThread = new SelectThread();
+ selectThread.start();
+ }
+
+ public void call(TAsyncMethodCall method) throws TException {
+ if (!isRunning()) {
+ throw new TException("SelectThread is not running");
+ }
+ method.prepareMethodCall();
+ pendingCalls.add(method);
+ selectThread.getSelector().wakeup();
+ }
+
+ public void stop() {
+ selectThread.finish();
+ }
+
+ public boolean isRunning() {
+ return selectThread.isAlive();
+ }
+
+ private class SelectThread extends Thread {
+ private final Selector selector;
+ private volatile boolean running;
+ private final TreeSet<TAsyncMethodCall> timeoutWatchSet = new TreeSet<TAsyncMethodCall>(new TAsyncMethodCallTimeoutComparator());
+
+ public SelectThread() throws IOException {
+ this.selector = SelectorProvider.provider().openSelector();
+ this.running = true;
+ this.setName("TAsyncClientManager#SelectorThread " + this.getId());
+
+ // We don't want to hold up the JVM when shutting down
+ setDaemon(true);
+ }
+
+ public Selector getSelector() {
+ return selector;
+ }
+
+ public void finish() {
+ running = false;
+ selector.wakeup();
+ }
+
+ public void run() {
+ while (running) {
+ try {
+ try {
+ if (timeoutWatchSet.size() == 0) {
+ // No timeouts, so select indefinitely
+ selector.select();
+ } else {
+ // We have a timeout pending, so calculate the time until then and select appropriately
+ long nextTimeout = timeoutWatchSet.first().getTimeoutTimestamp();
+ long selectTime = nextTimeout - System.currentTimeMillis();
+ if (selectTime > 0) {
+ // Next timeout is in the future, select and wake up then
+ selector.select(selectTime);
+ } else {
+ // Next timeout is now or in past, select immediately so we can time out
+ selector.selectNow();
+ }
+ }
+ } catch (IOException e) {
+ LOGGER.error("Caught IOException in TAsyncClientManager!", e);
+ }
+ transitionMethods();
+ timeoutMethods();
+ startPendingMethods();
+ } catch (Exception exception) {
+ LOGGER.error("Ignoring uncaught exception in SelectThread", exception);
+ }
+ }
+
+ try {
+ selector.close();
+ } catch (IOException ex) {
+ LOGGER.warn("Could not close selector. This may result in leaked resources!", ex);
+ }
+ }
+
+ // Transition methods for ready keys
+ private void transitionMethods() {
+ try {
+ Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
+ while (keys.hasNext()) {
+ SelectionKey key = keys.next();
+ keys.remove();
+ if (!key.isValid()) {
+ // this can happen if the method call experienced an error and the
+ // key was cancelled. can also happen if we timeout a method, which
+ // results in a channel close.
+ // just skip
+ continue;
+ }
+ TAsyncMethodCall methodCall = (TAsyncMethodCall)key.attachment();
+ methodCall.transition(key);
+
+ // If done or error occurred, remove from timeout watch set
+ if (methodCall.isFinished() || methodCall.getClient().hasError()) {
+ timeoutWatchSet.remove(methodCall);
+ }
+ }
+ } catch (ClosedSelectorException e) {
+ LOGGER.error("Caught ClosedSelectorException in TAsyncClientManager!", e);
+ }
+ }
+
+ // Timeout any existing method calls
+ private void timeoutMethods() {
+ Iterator<TAsyncMethodCall> iterator = timeoutWatchSet.iterator();
+ long currentTime = System.currentTimeMillis();
+ while (iterator.hasNext()) {
+ TAsyncMethodCall methodCall = iterator.next();
+ if (currentTime >= methodCall.getTimeoutTimestamp()) {
+ iterator.remove();
+ methodCall.onError(new TimeoutException("Operation " + methodCall.getClass() + " timed out after " + (currentTime - methodCall.getStartTime()) + " ms."));
+ } else {
+ break;
+ }
+ }
+ }
+
+ // Start any new calls
+ private void startPendingMethods() {
+ TAsyncMethodCall methodCall;
+ while ((methodCall = pendingCalls.poll()) != null) {
+ // Catch registration errors. method will catch transition errors and cleanup.
+ try {
+ methodCall.start(selector);
+
+ // If timeout specified and first transition went smoothly, add to timeout watch set
+ TAsyncClient client = methodCall.getClient();
+ if (client.hasTimeout() && !client.hasError()) {
+ timeoutWatchSet.add(methodCall);
+ }
+ } catch (Exception exception) {
+ LOGGER.warn("Caught exception in TAsyncClientManager!", exception);
+ methodCall.onError(exception);
+ }
+ }
+ }
+ }
+
+ /** Comparator used in TreeSet */
+ private static class TAsyncMethodCallTimeoutComparator implements Comparator<TAsyncMethodCall>, Serializable {
+ public int compare(TAsyncMethodCall left, TAsyncMethodCall right) {
+ if (left.getTimeoutTimestamp() == right.getTimeoutTimestamp()) {
+ return (int)(left.getSequenceId() - right.getSequenceId());
+ } else {
+ return (int)(left.getTimeoutTimestamp() - right.getTimeoutTimestamp());
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncMethodCall.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncMethodCall.java
new file mode 100644
index 000000000..3bf1747f4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncMethodCall.java
@@ -0,0 +1,284 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.async;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.transport.TFramedTransport;
+import org.apache.thrift.transport.TMemoryBuffer;
+import org.apache.thrift.transport.TNonblockingTransport;
+import org.apache.thrift.transport.TTransportException;
+
+/**
+ * Encapsulates an async method call.
+ * <p>
+ * Need to generate:
+ * <ul>
+ * <li>protected abstract void write_args(TProtocol protocol)</li>
+ * <li>protected abstract T getResult() throws &lt;Exception_1&gt;, &lt;Exception_2&gt;, ...</li>
+ * </ul>
+ *
+ * @param <T> The return type of the encapsulated method call.
+ */
+public abstract class TAsyncMethodCall<T> {
+
+ private static final int INITIAL_MEMORY_BUFFER_SIZE = 128;
+ private static AtomicLong sequenceIdCounter = new AtomicLong(0);
+
+ public static enum State {
+ CONNECTING,
+ WRITING_REQUEST_SIZE,
+ WRITING_REQUEST_BODY,
+ READING_RESPONSE_SIZE,
+ READING_RESPONSE_BODY,
+ RESPONSE_READ,
+ ERROR;
+ }
+
+ /**
+ * Next step in the call, initialized by start()
+ */
+ private State state = null;
+
+ protected final TNonblockingTransport transport;
+ private final TProtocolFactory protocolFactory;
+ protected final TAsyncClient client;
+ private final AsyncMethodCallback<T> callback;
+ private final boolean isOneway;
+ private long sequenceId;
+ private final long timeout;
+
+ private ByteBuffer sizeBuffer;
+ private final byte[] sizeBufferArray = new byte[4];
+ private ByteBuffer frameBuffer;
+
+ private long startTime = System.currentTimeMillis();
+
+ protected TAsyncMethodCall(TAsyncClient client, TProtocolFactory protocolFactory, TNonblockingTransport transport, AsyncMethodCallback<T> callback, boolean isOneway) {
+ this.transport = transport;
+ this.callback = callback;
+ this.protocolFactory = protocolFactory;
+ this.client = client;
+ this.isOneway = isOneway;
+ this.sequenceId = TAsyncMethodCall.sequenceIdCounter.getAndIncrement();
+ this.timeout = client.getTimeout();
+ }
+
+ protected State getState() {
+ return state;
+ }
+
+ protected boolean isFinished() {
+ return state == State.RESPONSE_READ;
+ }
+
+ protected long getStartTime() {
+ return startTime;
+ }
+
+ protected long getSequenceId() {
+ return sequenceId;
+ }
+
+ public TAsyncClient getClient() {
+ return client;
+ }
+
+ public boolean hasTimeout() {
+ return timeout > 0;
+ }
+
+ public long getTimeoutTimestamp() {
+ return timeout + startTime;
+ }
+
+ protected abstract void write_args(TProtocol protocol) throws TException;
+
+ protected abstract T getResult() throws Exception;
+
+ /**
+ * Initialize buffers.
+ * @throws TException if buffer initialization fails
+ */
+ protected void prepareMethodCall() throws TException {
+ TMemoryBuffer memoryBuffer = new TMemoryBuffer(INITIAL_MEMORY_BUFFER_SIZE);
+ TProtocol protocol = protocolFactory.getProtocol(memoryBuffer);
+ write_args(protocol);
+
+ int length = memoryBuffer.length();
+ frameBuffer = ByteBuffer.wrap(memoryBuffer.getArray(), 0, length);
+
+ TFramedTransport.encodeFrameSize(length, sizeBufferArray);
+ sizeBuffer = ByteBuffer.wrap(sizeBufferArray);
+ }
+
+ /**
+ * Register with selector and start first state, which could be either connecting or writing.
+ * @throws IOException if register or starting fails
+ */
+ void start(Selector sel) throws IOException {
+ SelectionKey key;
+ if (transport.isOpen()) {
+ state = State.WRITING_REQUEST_SIZE;
+ key = transport.registerSelector(sel, SelectionKey.OP_WRITE);
+ } else {
+ state = State.CONNECTING;
+ key = transport.registerSelector(sel, SelectionKey.OP_CONNECT);
+
+ // non-blocking connect can complete immediately,
+ // in which case we should not expect the OP_CONNECT
+ if (transport.startConnect()) {
+ registerForFirstWrite(key);
+ }
+ }
+
+ key.attach(this);
+ }
+
+ void registerForFirstWrite(SelectionKey key) throws IOException {
+ state = State.WRITING_REQUEST_SIZE;
+ key.interestOps(SelectionKey.OP_WRITE);
+ }
+
+ protected ByteBuffer getFrameBuffer() {
+ return frameBuffer;
+ }
+
+ /**
+ * Transition to next state, doing whatever work is required. Since this
+ * method is only called by the selector thread, we can make changes to our
+ * select interests without worrying about concurrency.
+ * @param key
+ */
+ void transition(SelectionKey key) {
+ // Ensure key is valid
+ if (!key.isValid()) {
+ key.cancel();
+ Exception e = new TTransportException("Selection key not valid!");
+ onError(e);
+ return;
+ }
+
+ // Transition function
+ try {
+ switch (state) {
+ case CONNECTING:
+ doConnecting(key);
+ break;
+ case WRITING_REQUEST_SIZE:
+ doWritingRequestSize();
+ break;
+ case WRITING_REQUEST_BODY:
+ doWritingRequestBody(key);
+ break;
+ case READING_RESPONSE_SIZE:
+ doReadingResponseSize();
+ break;
+ case READING_RESPONSE_BODY:
+ doReadingResponseBody(key);
+ break;
+ default: // RESPONSE_READ, ERROR, or bug
+ throw new IllegalStateException("Method call in state " + state
+ + " but selector called transition method. Seems like a bug...");
+ }
+ } catch (Exception e) {
+ key.cancel();
+ key.attach(null);
+ onError(e);
+ }
+ }
+
+ protected void onError(Exception e) {
+ client.onError(e);
+ callback.onError(e);
+ state = State.ERROR;
+ }
+
+ private void doReadingResponseBody(SelectionKey key) throws IOException {
+ if (transport.read(frameBuffer) < 0) {
+ throw new IOException("Read call frame failed");
+ }
+ if (frameBuffer.remaining() == 0) {
+ cleanUpAndFireCallback(key);
+ }
+ }
+
+ private void cleanUpAndFireCallback(SelectionKey key) {
+ state = State.RESPONSE_READ;
+ key.interestOps(0);
+ // this ensures that the TAsyncMethod instance doesn't hang around
+ key.attach(null);
+ try {
+ T result = this.getResult();
+ client.onComplete();
+ callback.onComplete(result);
+ } catch (Exception e) {
+ key.cancel();
+ onError(e);
+ }
+ }
+
+ private void doReadingResponseSize() throws IOException {
+ if (transport.read(sizeBuffer) < 0) {
+ throw new IOException("Read call frame size failed");
+ }
+ if (sizeBuffer.remaining() == 0) {
+ state = State.READING_RESPONSE_BODY;
+ frameBuffer = ByteBuffer.allocate(TFramedTransport.decodeFrameSize(sizeBufferArray));
+ }
+ }
+
+ private void doWritingRequestBody(SelectionKey key) throws IOException {
+ if (transport.write(frameBuffer) < 0) {
+ throw new IOException("Write call frame failed");
+ }
+ if (frameBuffer.remaining() == 0) {
+ if (isOneway) {
+ cleanUpAndFireCallback(key);
+ } else {
+ state = State.READING_RESPONSE_SIZE;
+ sizeBuffer.rewind(); // Prepare to read incoming frame size
+ key.interestOps(SelectionKey.OP_READ);
+ }
+ }
+ }
+
+ private void doWritingRequestSize() throws IOException {
+ if (transport.write(sizeBuffer) < 0) {
+ throw new IOException("Write call frame size failed");
+ }
+ if (sizeBuffer.remaining() == 0) {
+ state = State.WRITING_REQUEST_BODY;
+ }
+ }
+
+ private void doConnecting(SelectionKey key) throws IOException {
+ if (!key.isConnectable() || !transport.finishConnect()) {
+ throw new IOException("not connectable or finishConnect returned false after we got an OP_CONNECT");
+ }
+ registerForFirstWrite(key);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/EnumMetaData.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/EnumMetaData.java
new file mode 100644
index 000000000..be49cb949
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/EnumMetaData.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+import org.apache.thrift.TEnum;
+
+public class EnumMetaData extends FieldValueMetaData {
+ public final Class<? extends TEnum> enumClass;
+
+ public EnumMetaData(byte type, Class<? extends TEnum> sClass){
+ super(type);
+ this.enumClass = sClass;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/FieldMetaData.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/FieldMetaData.java
new file mode 100644
index 000000000..445f7e474
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/FieldMetaData.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.thrift.TBase;
+import org.apache.thrift.TFieldIdEnum;
+
+/**
+ * This class is used to store meta data about thrift fields. Every field in a
+ * a struct should have a corresponding instance of this class describing it.
+ *
+ */
+public class FieldMetaData implements java.io.Serializable {
+ public final String fieldName;
+ public final byte requirementType;
+ public final FieldValueMetaData valueMetaData;
+ private static Map<Class<? extends TBase>, Map<? extends TFieldIdEnum, FieldMetaData>> structMap;
+
+ static {
+ structMap = new HashMap<Class<? extends TBase>, Map<? extends TFieldIdEnum, FieldMetaData>>();
+ }
+
+ public FieldMetaData(String name, byte req, FieldValueMetaData vMetaData){
+ this.fieldName = name;
+ this.requirementType = req;
+ this.valueMetaData = vMetaData;
+ }
+
+ public static synchronized void addStructMetaDataMap(Class<? extends TBase> sClass, Map<? extends TFieldIdEnum, FieldMetaData> map){
+ structMap.put(sClass, map);
+ }
+
+ /**
+ * Returns a map with metadata (i.e. instances of FieldMetaData) that
+ * describe the fields of the given class.
+ *
+ * @param sClass The TBase class for which the metadata map is requested
+ */
+ public static synchronized Map<? extends TFieldIdEnum, FieldMetaData> getStructMetaDataMap(Class<? extends TBase> sClass){
+ if (!structMap.containsKey(sClass)){ // Load class if it hasn't been loaded
+ try{
+ sClass.newInstance();
+ } catch (InstantiationException e){
+ throw new RuntimeException("InstantiationException for TBase class: " + sClass.getName() + ", message: " + e.getMessage());
+ } catch (IllegalAccessException e){
+ throw new RuntimeException("IllegalAccessException for TBase class: " + sClass.getName() + ", message: " + e.getMessage());
+ }
+ }
+ return structMap.get(sClass);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/FieldValueMetaData.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/FieldValueMetaData.java
new file mode 100644
index 000000000..2180b089b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/FieldValueMetaData.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+import org.apache.thrift.protocol.TType;
+
+/**
+ * FieldValueMetaData and collection of subclasses to store metadata about
+ * the value(s) of a field
+ */
+public class FieldValueMetaData implements java.io.Serializable {
+ public final byte type;
+
+ private final boolean isTypedefType;
+ private final String typedefName;
+ private final boolean isBinary;
+
+ public FieldValueMetaData(byte type, boolean binary) {
+ this.type = type;
+ this.isTypedefType = false;
+ this.typedefName = null;
+ this.isBinary = binary;
+ }
+
+ public FieldValueMetaData(byte type) {
+ this(type, false);
+ }
+
+ public FieldValueMetaData(byte type, String typedefName) {
+ this.type = type;
+ this.isTypedefType = true;
+ this.typedefName = typedefName;
+ this.isBinary = false;
+ }
+
+ public boolean isTypedef() {
+ return isTypedefType;
+ }
+
+ public String getTypedefName() {
+ return typedefName;
+ }
+
+ public boolean isStruct() {
+ return type == TType.STRUCT;
+ }
+
+ public boolean isContainer() {
+ return type == TType.LIST || type == TType.MAP || type == TType.SET;
+ }
+
+ public boolean isBinary() {
+ return isBinary;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/ListMetaData.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/ListMetaData.java
new file mode 100644
index 000000000..8e7073bf5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/ListMetaData.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+public class ListMetaData extends FieldValueMetaData {
+ public final FieldValueMetaData elemMetaData;
+
+ public ListMetaData(byte type, FieldValueMetaData eMetaData){
+ super(type);
+ this.elemMetaData = eMetaData;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/MapMetaData.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/MapMetaData.java
new file mode 100644
index 000000000..e7c408c78
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/MapMetaData.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+public class MapMetaData extends FieldValueMetaData {
+ public final FieldValueMetaData keyMetaData;
+ public final FieldValueMetaData valueMetaData;
+
+ public MapMetaData(byte type, FieldValueMetaData kMetaData, FieldValueMetaData vMetaData){
+ super(type);
+ this.keyMetaData = kMetaData;
+ this.valueMetaData = vMetaData;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/SetMetaData.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/SetMetaData.java
new file mode 100644
index 000000000..cf4b96aab
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/SetMetaData.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+public class SetMetaData extends FieldValueMetaData {
+ public final FieldValueMetaData elemMetaData;
+
+ public SetMetaData(byte type, FieldValueMetaData eMetaData){
+ super(type);
+ this.elemMetaData = eMetaData;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/StructMetaData.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/StructMetaData.java
new file mode 100644
index 000000000..b37d21dab
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/StructMetaData.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.meta_data;
+
+import org.apache.thrift.TBase;
+
+public class StructMetaData extends FieldValueMetaData {
+ public final Class<? extends TBase> structClass;
+
+ public StructMetaData(byte type, Class<? extends TBase> sClass){
+ super(type);
+ this.structClass = sClass;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/ShortStack.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/ShortStack.java
new file mode 100644
index 000000000..9e6593074
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/ShortStack.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.protocol;
+
+import java.util.Arrays;
+
+/**
+ * ShortStack is a short-specific Stack implementation written for the express
+ * purpose of very fast operations on TCompactProtocol's field id stack. This
+ * implementation performs at least 10x faster than java.util.Stack.
+ */
+class ShortStack {
+
+ private short[] vector;
+
+ /** Always points to the next location */
+ private int top = 0;
+
+ public ShortStack(int initialCapacity) {
+ vector = new short[initialCapacity];
+ }
+
+ public short pop() {
+ return vector[--top];
+ }
+
+ public void push(short pushed) {
+ if (vector.length == top) {
+ grow();
+ }
+ vector[top++] = pushed;
+ }
+
+ private void grow() {
+ vector = Arrays.copyOf(vector, vector.length << 1);
+ }
+
+ public void clear() {
+ top = 0;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("<ShortStack vector:[");
+ for (int i = 0; i < vector.length; i++) {
+ boolean isTop = (i == (top - 1));
+ short value = vector[i];
+ if (i != 0) {
+ sb.append(' ');
+ }
+ if (isTop) {
+ sb.append(">>").append(value).append("<<");
+ } else {
+ sb.append(value);
+ }
+ }
+ sb.append("]>");
+ return sb.toString();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TBase64Utils.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TBase64Utils.java
new file mode 100644
index 000000000..abfc965b7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TBase64Utils.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Class for encoding and decoding Base64 data.
+ *
+ * This class is kept at package level because the interface does no input
+ * validation and is therefore too low-level for generalized reuse.
+ *
+ * Note also that the encoding does not pad with equal signs , as discussed in
+ * section 2.2 of the RFC (http://www.faqs.org/rfcs/rfc3548.html). Furthermore,
+ * bad data encountered when decoding is neither rejected or ignored but simply
+ * results in bad decoded data -- this is not in compliance with the RFC but is
+ * done in the interest of performance.
+ *
+ */
+class TBase64Utils {
+
+ private static final String ENCODE_TABLE =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ /**
+ * Encode len bytes of data in src at offset srcOff, storing the result into
+ * dst at offset dstOff. len must be 1, 2, or 3. dst must have at least len+1
+ * bytes of space at dstOff. src and dst should not be the same object. This
+ * method does no validation of the input values in the interest of
+ * performance.
+ *
+ * @param src the source of bytes to encode
+ * @param srcOff the offset into the source to read the unencoded bytes
+ * @param len the number of bytes to encode (must be 1, 2, or 3).
+ * @param dst the destination for the encoding
+ * @param dstOff the offset into the destination to place the encoded bytes
+ */
+ static final void encode(byte[] src, int srcOff, int len, byte[] dst,
+ int dstOff) {
+ dst[dstOff] = (byte)ENCODE_TABLE.charAt((src[srcOff] >> 2) & 0x3F);
+ if (len == 3) {
+ dst[dstOff + 1] =
+ (byte)ENCODE_TABLE.charAt(
+ ((src[srcOff] << 4) & 0x30) | ((src[srcOff+1] >> 4) & 0x0F));
+ dst[dstOff + 2] =
+ (byte)ENCODE_TABLE.charAt(
+ ((src[srcOff+1] << 2) & 0x3C) | ((src[srcOff+2] >> 6) & 0x03));
+ dst[dstOff + 3] =
+ (byte)ENCODE_TABLE.charAt(src[srcOff+2] & 0x3F);
+ }
+ else if (len == 2) {
+ dst[dstOff+1] =
+ (byte)ENCODE_TABLE.charAt(
+ ((src[srcOff] << 4) & 0x30) | ((src[srcOff+1] >> 4) & 0x0F));
+ dst[dstOff + 2] =
+ (byte)ENCODE_TABLE.charAt((src[srcOff+1] << 2) & 0x3C);
+ }
+ else { // len == 1) {
+ dst[dstOff + 1] =
+ (byte)ENCODE_TABLE.charAt((src[srcOff] << 4) & 0x30);
+ }
+ }
+
+ private static final byte[] DECODE_TABLE = {
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,
+ 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
+ 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
+ -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
+ 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ };
+
+ /**
+ * Decode len bytes of data in src at offset srcOff, storing the result into
+ * dst at offset dstOff. len must be 2, 3, or 4. dst must have at least len-1
+ * bytes of space at dstOff. src and dst may be the same object as long as
+ * dstoff <= srcOff. This method does no validation of the input values in
+ * the interest of performance.
+ *
+ * @param src the source of bytes to decode
+ * @param srcOff the offset into the source to read the encoded bytes
+ * @param len the number of bytes to decode (must be 2, 3, or 4)
+ * @param dst the destination for the decoding
+ * @param dstOff the offset into the destination to place the decoded bytes
+ */
+ static final void decode(byte[] src, int srcOff, int len, byte[] dst,
+ int dstOff) {
+ dst[dstOff] = (byte)
+ ((DECODE_TABLE[src[srcOff] & 0x0FF] << 2) |
+ (DECODE_TABLE[src[srcOff+1] & 0x0FF] >> 4));
+ if (len > 2) {
+ dst[dstOff+1] = (byte)
+ (((DECODE_TABLE[src[srcOff+1] & 0x0FF] << 4) & 0xF0) |
+ (DECODE_TABLE[src[srcOff+2] & 0x0FF] >> 2));
+ if (len > 3) {
+ dst[dstOff+2] = (byte)
+ (((DECODE_TABLE[src[srcOff+2] & 0x0FF] << 6) & 0xC0) |
+ DECODE_TABLE[src[srcOff+3] & 0x0FF]);
+ }
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TBinaryProtocol.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TBinaryProtocol.java
new file mode 100644
index 000000000..7924e2fe6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TBinaryProtocol.java
@@ -0,0 +1,457 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * Binary protocol implementation for thrift.
+ *
+ */
+public class TBinaryProtocol extends TProtocol {
+ private static final TStruct ANONYMOUS_STRUCT = new TStruct();
+ private static final long NO_LENGTH_LIMIT = -1;
+
+ protected static final int VERSION_MASK = 0xffff0000;
+ protected static final int VERSION_1 = 0x80010000;
+
+ /**
+ * The maximum number of bytes to read from the transport for
+ * variable-length fields (such as strings or binary) or {@link #NO_LENGTH_LIMIT} for
+ * unlimited.
+ */
+ private final long stringLengthLimit_;
+
+ /**
+ * The maximum number of elements to read from the network for
+ * containers (maps, sets, lists), or {@link #NO_LENGTH_LIMIT} for unlimited.
+ */
+ private final long containerLengthLimit_;
+
+ protected boolean strictRead_;
+ protected boolean strictWrite_;
+
+ private final byte[] inoutTemp = new byte[8];
+
+ /**
+ * Factory
+ */
+ public static class Factory implements TProtocolFactory {
+ protected long stringLengthLimit_;
+ protected long containerLengthLimit_;
+ protected boolean strictRead_;
+ protected boolean strictWrite_;
+
+ public Factory() {
+ this(false, true);
+ }
+
+ public Factory(boolean strictRead, boolean strictWrite) {
+ this(strictRead, strictWrite, NO_LENGTH_LIMIT, NO_LENGTH_LIMIT);
+ }
+
+ public Factory(long stringLengthLimit, long containerLengthLimit) {
+ this(false, true, stringLengthLimit, containerLengthLimit);
+ }
+
+ public Factory(boolean strictRead, boolean strictWrite, long stringLengthLimit, long containerLengthLimit) {
+ stringLengthLimit_ = stringLengthLimit;
+ containerLengthLimit_ = containerLengthLimit;
+ strictRead_ = strictRead;
+ strictWrite_ = strictWrite;
+ }
+
+ public TProtocol getProtocol(TTransport trans) {
+ return new TBinaryProtocol(trans, stringLengthLimit_, containerLengthLimit_, strictRead_, strictWrite_);
+ }
+ }
+
+ /**
+ * Constructor
+ */
+ public TBinaryProtocol(TTransport trans) {
+ this(trans, false, true);
+ }
+
+ public TBinaryProtocol(TTransport trans, boolean strictRead, boolean strictWrite) {
+ this(trans, NO_LENGTH_LIMIT, NO_LENGTH_LIMIT, strictRead, strictWrite);
+ }
+
+ public TBinaryProtocol(TTransport trans, long stringLengthLimit, long containerLengthLimit) {
+ this(trans, stringLengthLimit, containerLengthLimit, false, true);
+ }
+
+ public TBinaryProtocol(TTransport trans, long stringLengthLimit, long containerLengthLimit, boolean strictRead, boolean strictWrite) {
+ super(trans);
+ stringLengthLimit_ = stringLengthLimit;
+ containerLengthLimit_ = containerLengthLimit;
+ strictRead_ = strictRead;
+ strictWrite_ = strictWrite;
+ }
+
+ @Override
+ public void writeMessageBegin(TMessage message) throws TException {
+ if (strictWrite_) {
+ int version = VERSION_1 | message.type;
+ writeI32(version);
+ writeString(message.name);
+ writeI32(message.seqid);
+ } else {
+ writeString(message.name);
+ writeByte(message.type);
+ writeI32(message.seqid);
+ }
+ }
+
+ @Override
+ public void writeMessageEnd() throws TException {}
+
+ @Override
+ public void writeStructBegin(TStruct struct) throws TException {}
+
+ @Override
+ public void writeStructEnd() throws TException {}
+
+ @Override
+ public void writeFieldBegin(TField field) throws TException {
+ writeByte(field.type);
+ writeI16(field.id);
+ }
+
+ @Override
+ public void writeFieldEnd() throws TException {}
+
+ @Override
+ public void writeFieldStop() throws TException {
+ writeByte(TType.STOP);
+ }
+
+ @Override
+ public void writeMapBegin(TMap map) throws TException {
+ writeByte(map.keyType);
+ writeByte(map.valueType);
+ writeI32(map.size);
+ }
+
+ @Override
+ public void writeMapEnd() throws TException {}
+
+ @Override
+ public void writeListBegin(TList list) throws TException {
+ writeByte(list.elemType);
+ writeI32(list.size);
+ }
+
+ @Override
+ public void writeListEnd() throws TException {}
+
+ @Override
+ public void writeSetBegin(TSet set) throws TException {
+ writeByte(set.elemType);
+ writeI32(set.size);
+ }
+
+ @Override
+ public void writeSetEnd() throws TException {}
+
+ @Override
+ public void writeBool(boolean b) throws TException {
+ writeByte(b ? (byte)1 : (byte)0);
+ }
+
+ @Override
+ public void writeByte(byte b) throws TException {
+ inoutTemp[0] = b;
+ trans_.write(inoutTemp, 0, 1);
+ }
+
+ @Override
+ public void writeI16(short i16) throws TException {
+ inoutTemp[0] = (byte)(0xff & (i16 >> 8));
+ inoutTemp[1] = (byte)(0xff & (i16));
+ trans_.write(inoutTemp, 0, 2);
+ }
+
+ @Override
+ public void writeI32(int i32) throws TException {
+ inoutTemp[0] = (byte)(0xff & (i32 >> 24));
+ inoutTemp[1] = (byte)(0xff & (i32 >> 16));
+ inoutTemp[2] = (byte)(0xff & (i32 >> 8));
+ inoutTemp[3] = (byte)(0xff & (i32));
+ trans_.write(inoutTemp, 0, 4);
+ }
+
+ @Override
+ public void writeI64(long i64) throws TException {
+ inoutTemp[0] = (byte)(0xff & (i64 >> 56));
+ inoutTemp[1] = (byte)(0xff & (i64 >> 48));
+ inoutTemp[2] = (byte)(0xff & (i64 >> 40));
+ inoutTemp[3] = (byte)(0xff & (i64 >> 32));
+ inoutTemp[4] = (byte)(0xff & (i64 >> 24));
+ inoutTemp[5] = (byte)(0xff & (i64 >> 16));
+ inoutTemp[6] = (byte)(0xff & (i64 >> 8));
+ inoutTemp[7] = (byte)(0xff & (i64));
+ trans_.write(inoutTemp, 0, 8);
+ }
+
+ @Override
+ public void writeDouble(double dub) throws TException {
+ writeI64(Double.doubleToLongBits(dub));
+ }
+
+ @Override
+ public void writeString(String str) throws TException {
+ byte[] dat = str.getBytes(StandardCharsets.UTF_8);
+ writeI32(dat.length);
+ trans_.write(dat, 0, dat.length);
+ }
+
+ @Override
+ public void writeBinary(ByteBuffer bin) throws TException {
+ int length = bin.limit() - bin.position();
+ writeI32(length);
+ trans_.write(bin.array(), bin.position() + bin.arrayOffset(), length);
+ }
+
+ /**
+ * Reading methods.
+ */
+
+ @Override
+ public TMessage readMessageBegin() throws TException {
+ int size = readI32();
+ if (size < 0) {
+ int version = size & VERSION_MASK;
+ if (version != VERSION_1) {
+ throw new TProtocolException(TProtocolException.BAD_VERSION, "Bad version in readMessageBegin");
+ }
+ return new TMessage(readString(), (byte)(size & 0x000000ff), readI32());
+ } else {
+ if (strictRead_) {
+ throw new TProtocolException(TProtocolException.BAD_VERSION, "Missing version in readMessageBegin, old client?");
+ }
+ return new TMessage(readStringBody(size), readByte(), readI32());
+ }
+ }
+
+ @Override
+ public void readMessageEnd() throws TException {}
+
+ @Override
+ public TStruct readStructBegin() throws TException {
+ return ANONYMOUS_STRUCT;
+ }
+
+ @Override
+ public void readStructEnd() throws TException {}
+
+ @Override
+ public TField readFieldBegin() throws TException {
+ byte type = readByte();
+ short id = type == TType.STOP ? 0 : readI16();
+ return new TField("", type, id);
+ }
+
+ @Override
+ public void readFieldEnd() throws TException {}
+
+ @Override
+ public TMap readMapBegin() throws TException {
+ TMap map = new TMap(readByte(), readByte(), readI32());
+ checkContainerReadLength(map.size);
+ return map;
+ }
+
+ @Override
+ public void readMapEnd() throws TException {}
+
+ @Override
+ public TList readListBegin() throws TException {
+ TList list = new TList(readByte(), readI32());
+ checkContainerReadLength(list.size);
+ return list;
+ }
+
+ @Override
+ public void readListEnd() throws TException {}
+
+ @Override
+ public TSet readSetBegin() throws TException {
+ TSet set = new TSet(readByte(), readI32());
+ checkContainerReadLength(set.size);
+ return set;
+ }
+
+ @Override
+ public void readSetEnd() throws TException {}
+
+ @Override
+ public boolean readBool() throws TException {
+ return (readByte() == 1);
+ }
+
+ @Override
+ public byte readByte() throws TException {
+ if (trans_.getBytesRemainingInBuffer() >= 1) {
+ byte b = trans_.getBuffer()[trans_.getBufferPosition()];
+ trans_.consumeBuffer(1);
+ return b;
+ }
+ readAll(inoutTemp, 0, 1);
+ return inoutTemp[0];
+ }
+
+ @Override
+ public short readI16() throws TException {
+ byte[] buf = inoutTemp;
+ int off = 0;
+
+ if (trans_.getBytesRemainingInBuffer() >= 2) {
+ buf = trans_.getBuffer();
+ off = trans_.getBufferPosition();
+ trans_.consumeBuffer(2);
+ } else {
+ readAll(inoutTemp, 0, 2);
+ }
+
+ return
+ (short)
+ (((buf[off] & 0xff) << 8) |
+ ((buf[off+1] & 0xff)));
+ }
+
+ @Override
+ public int readI32() throws TException {
+ byte[] buf = inoutTemp;
+ int off = 0;
+
+ if (trans_.getBytesRemainingInBuffer() >= 4) {
+ buf = trans_.getBuffer();
+ off = trans_.getBufferPosition();
+ trans_.consumeBuffer(4);
+ } else {
+ readAll(inoutTemp, 0, 4);
+ }
+ return
+ ((buf[off] & 0xff) << 24) |
+ ((buf[off+1] & 0xff) << 16) |
+ ((buf[off+2] & 0xff) << 8) |
+ ((buf[off+3] & 0xff));
+ }
+
+ @Override
+ public long readI64() throws TException {
+ byte[] buf = inoutTemp;
+ int off = 0;
+
+ if (trans_.getBytesRemainingInBuffer() >= 8) {
+ buf = trans_.getBuffer();
+ off = trans_.getBufferPosition();
+ trans_.consumeBuffer(8);
+ } else {
+ readAll(inoutTemp, 0, 8);
+ }
+
+ return
+ ((long)(buf[off] & 0xff) << 56) |
+ ((long)(buf[off+1] & 0xff) << 48) |
+ ((long)(buf[off+2] & 0xff) << 40) |
+ ((long)(buf[off+3] & 0xff) << 32) |
+ ((long)(buf[off+4] & 0xff) << 24) |
+ ((long)(buf[off+5] & 0xff) << 16) |
+ ((long)(buf[off+6] & 0xff) << 8) |
+ ((long)(buf[off+7] & 0xff));
+ }
+
+ @Override
+ public double readDouble() throws TException {
+ return Double.longBitsToDouble(readI64());
+ }
+
+ @Override
+ public String readString() throws TException {
+ int size = readI32();
+
+ checkStringReadLength(size);
+
+ if (trans_.getBytesRemainingInBuffer() >= size) {
+ String s = new String(trans_.getBuffer(), trans_.getBufferPosition(),
+ size, StandardCharsets.UTF_8);
+ trans_.consumeBuffer(size);
+ return s;
+ }
+
+ return readStringBody(size);
+ }
+
+ public String readStringBody(int size) throws TException {
+ checkStringReadLength(size);
+ byte[] buf = new byte[size];
+ trans_.readAll(buf, 0, size);
+ return new String(buf, StandardCharsets.UTF_8);
+ }
+
+ @Override
+ public ByteBuffer readBinary() throws TException {
+ int size = readI32();
+
+ checkStringReadLength(size);
+
+ if (trans_.getBytesRemainingInBuffer() >= size) {
+ ByteBuffer bb = ByteBuffer.wrap(trans_.getBuffer(), trans_.getBufferPosition(), size);
+ trans_.consumeBuffer(size);
+ return bb;
+ }
+
+ byte[] buf = new byte[size];
+ trans_.readAll(buf, 0, size);
+ return ByteBuffer.wrap(buf);
+ }
+
+ private void checkStringReadLength(int length) throws TProtocolException {
+ if (length < 0) {
+ throw new TProtocolException(TProtocolException.NEGATIVE_SIZE,
+ "Negative length: " + length);
+ }
+ if (stringLengthLimit_ != NO_LENGTH_LIMIT && length > stringLengthLimit_) {
+ throw new TProtocolException(TProtocolException.SIZE_LIMIT,
+ "Length exceeded max allowed: " + length);
+ }
+ }
+
+ private void checkContainerReadLength(int length) throws TProtocolException {
+ if (length < 0) {
+ throw new TProtocolException(TProtocolException.NEGATIVE_SIZE,
+ "Negative length: " + length);
+ }
+ if (containerLengthLimit_ != NO_LENGTH_LIMIT && length > containerLengthLimit_) {
+ throw new TProtocolException(TProtocolException.SIZE_LIMIT,
+ "Length exceeded max allowed: " + length);
+ }
+ }
+
+ private int readAll(byte[] buf, int off, int len) throws TException {
+ return trans_.readAll(buf, off, len);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TCompactProtocol.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TCompactProtocol.java
new file mode 100644
index 000000000..ee0586945
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TCompactProtocol.java
@@ -0,0 +1,904 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package org.apache.thrift.protocol;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * TCompactProtocol2 is the Java implementation of the compact protocol specified
+ * in THRIFT-110. The fundamental approach to reducing the overhead of
+ * structures is a) use variable-length integers all over the place and b) make
+ * use of unused bits wherever possible. Your savings will obviously vary
+ * based on the specific makeup of your structs, but in general, the more
+ * fields, nested structures, short strings and collections, and low-value i32
+ * and i64 fields you have, the more benefit you'll see.
+ */
+public class TCompactProtocol extends TProtocol {
+ private final static byte[] EMPTY_BYTES = new byte[0];
+ private final static ByteBuffer EMPTY_BUFFER = ByteBuffer.wrap(EMPTY_BYTES);
+
+ private final static long NO_LENGTH_LIMIT = -1;
+
+ private final static TStruct ANONYMOUS_STRUCT = new TStruct("");
+ private final static TField TSTOP = new TField("", TType.STOP, (short)0);
+
+ private final static byte[] ttypeToCompactType = new byte[16];
+
+ static {
+ ttypeToCompactType[TType.STOP] = TType.STOP;
+ ttypeToCompactType[TType.BOOL] = Types.BOOLEAN_TRUE;
+ ttypeToCompactType[TType.BYTE] = Types.BYTE;
+ ttypeToCompactType[TType.I16] = Types.I16;
+ ttypeToCompactType[TType.I32] = Types.I32;
+ ttypeToCompactType[TType.I64] = Types.I64;
+ ttypeToCompactType[TType.DOUBLE] = Types.DOUBLE;
+ ttypeToCompactType[TType.STRING] = Types.BINARY;
+ ttypeToCompactType[TType.LIST] = Types.LIST;
+ ttypeToCompactType[TType.SET] = Types.SET;
+ ttypeToCompactType[TType.MAP] = Types.MAP;
+ ttypeToCompactType[TType.STRUCT] = Types.STRUCT;
+ }
+
+ /**
+ * TProtocolFactory that produces TCompactProtocols.
+ */
+ public static class Factory implements TProtocolFactory {
+ private final long stringLengthLimit_;
+ private final long containerLengthLimit_;
+
+ public Factory() {
+ this(NO_LENGTH_LIMIT, NO_LENGTH_LIMIT);
+ }
+
+ public Factory(long stringLengthLimit) {
+ this(stringLengthLimit, NO_LENGTH_LIMIT);
+ }
+
+ public Factory(long stringLengthLimit, long containerLengthLimit) {
+ this.containerLengthLimit_ = containerLengthLimit;
+ this.stringLengthLimit_ = stringLengthLimit;
+ }
+
+ public TProtocol getProtocol(TTransport trans) {
+ return new TCompactProtocol(trans, stringLengthLimit_, containerLengthLimit_);
+ }
+ }
+
+ private static final byte PROTOCOL_ID = (byte)0x82;
+ private static final byte VERSION = 1;
+ private static final byte VERSION_MASK = 0x1f; // 0001 1111
+ private static final byte TYPE_MASK = (byte)0xE0; // 1110 0000
+ private static final byte TYPE_BITS = 0x07; // 0000 0111
+ private static final int TYPE_SHIFT_AMOUNT = 5;
+
+ /**
+ * All of the on-wire type codes.
+ */
+ private static class Types {
+ public static final byte BOOLEAN_TRUE = 0x01;
+ public static final byte BOOLEAN_FALSE = 0x02;
+ public static final byte BYTE = 0x03;
+ public static final byte I16 = 0x04;
+ public static final byte I32 = 0x05;
+ public static final byte I64 = 0x06;
+ public static final byte DOUBLE = 0x07;
+ public static final byte BINARY = 0x08;
+ public static final byte LIST = 0x09;
+ public static final byte SET = 0x0A;
+ public static final byte MAP = 0x0B;
+ public static final byte STRUCT = 0x0C;
+ }
+
+ /**
+ * Used to keep track of the last field for the current and previous structs,
+ * so we can do the delta stuff.
+ */
+ private ShortStack lastField_ = new ShortStack(15);
+
+ private short lastFieldId_ = 0;
+
+ /**
+ * If we encounter a boolean field begin, save the TField here so it can
+ * have the value incorporated.
+ */
+ private TField booleanField_ = null;
+
+ /**
+ * If we read a field header, and it's a boolean field, save the boolean
+ * value here so that readBool can use it.
+ */
+ private Boolean boolValue_ = null;
+
+ /**
+ * The maximum number of bytes to read from the transport for
+ * variable-length fields (such as strings or binary) or {@link #NO_LENGTH_LIMIT} for
+ * unlimited.
+ */
+ private final long stringLengthLimit_;
+
+ /**
+ * The maximum number of elements to read from the network for
+ * containers (maps, sets, lists), or {@link #NO_LENGTH_LIMIT} for unlimited.
+ */
+ private final long containerLengthLimit_;
+
+ /**
+ * Temporary buffer used for various operations that would otherwise require a
+ * small allocation.
+ */
+ private final byte[] temp = new byte[10];
+
+ /**
+ * Create a TCompactProtocol.
+ *
+ * @param transport the TTransport object to read from or write to.
+ * @param stringLengthLimit the maximum number of bytes to read for
+ * variable-length fields.
+ * @param containerLengthLimit the maximum number of elements to read
+ * for containers.
+ */
+ public TCompactProtocol(TTransport transport, long stringLengthLimit, long containerLengthLimit) {
+ super(transport);
+ this.stringLengthLimit_ = stringLengthLimit;
+ this.containerLengthLimit_ = containerLengthLimit;
+ }
+
+ /**
+ * Create a TCompactProtocol.
+ *
+ * @param transport the TTransport object to read from or write to.
+ * @param stringLengthLimit the maximum number of bytes to read for
+ * variable-length fields.
+ * @deprecated Use constructor specifying both string limit and container limit instead
+ */
+ @Deprecated
+ public TCompactProtocol(TTransport transport, long stringLengthLimit) {
+ this(transport, stringLengthLimit, NO_LENGTH_LIMIT);
+ }
+
+ /**
+ * Create a TCompactProtocol.
+ *
+ * @param transport the TTransport object to read from or write to.
+ */
+ public TCompactProtocol(TTransport transport) {
+ this(transport, NO_LENGTH_LIMIT, NO_LENGTH_LIMIT);
+ }
+
+ @Override
+ public void reset() {
+ lastField_.clear();
+ lastFieldId_ = 0;
+ }
+
+ //
+ // Public Writing methods.
+ //
+
+ /**
+ * Write a message header to the wire. Compact Protocol messages contain the
+ * protocol version so we can migrate forwards in the future if need be.
+ */
+ @Override
+ public void writeMessageBegin(TMessage message) throws TException {
+ writeByteDirect(PROTOCOL_ID);
+ writeByteDirect((VERSION & VERSION_MASK) | ((message.type << TYPE_SHIFT_AMOUNT) & TYPE_MASK));
+ writeVarint32(message.seqid);
+ writeString(message.name);
+ }
+
+ /**
+ * Write a struct begin. This doesn't actually put anything on the wire. We
+ * use it as an opportunity to put special placeholder markers on the field
+ * stack so we can get the field id deltas correct.
+ */
+ @Override
+ public void writeStructBegin(TStruct struct) throws TException {
+ lastField_.push(lastFieldId_);
+ lastFieldId_ = 0;
+ }
+
+ /**
+ * Write a struct end. This doesn't actually put anything on the wire. We use
+ * this as an opportunity to pop the last field from the current struct off
+ * of the field stack.
+ */
+ public void writeStructEnd() throws TException {
+ lastFieldId_ = lastField_.pop();
+ }
+
+ /**
+ * Write a field header containing the field id and field type. If the
+ * difference between the current field id and the last one is small (&lt; 15),
+ * then the field id will be encoded in the 4 MSB as a delta. Otherwise, the
+ * field id will follow the type header as a zigzag varint.
+ */
+ public void writeFieldBegin(TField field) throws TException {
+ if (field.type == TType.BOOL) {
+ // we want to possibly include the value, so we'll wait.
+ booleanField_ = field;
+ } else {
+ writeFieldBeginInternal(field, (byte)-1);
+ }
+ }
+
+ /**
+ * The workhorse of writeFieldBegin. It has the option of doing a
+ * 'type override' of the type header. This is used specifically in the
+ * boolean field case.
+ */
+ private void writeFieldBeginInternal(TField field, byte typeOverride) throws TException {
+ // short lastField = lastField_.pop();
+
+ // if there's a type override, use that.
+ byte typeToWrite = typeOverride == -1 ? getCompactType(field.type) : typeOverride;
+
+ // check if we can use delta encoding for the field id
+ if (field.id > lastFieldId_ && field.id - lastFieldId_ <= 15) {
+ // write them together
+ writeByteDirect((field.id - lastFieldId_) << 4 | typeToWrite);
+ } else {
+ // write them separate
+ writeByteDirect(typeToWrite);
+ writeI16(field.id);
+ }
+
+ lastFieldId_ = field.id;
+ // lastField_.push(field.id);
+ }
+
+ /**
+ * Write the STOP symbol so we know there are no more fields in this struct.
+ */
+ public void writeFieldStop() throws TException {
+ writeByteDirect(TType.STOP);
+ }
+
+ /**
+ * Write a map header. If the map is empty, omit the key and value type
+ * headers, as we don't need any additional information to skip it.
+ */
+ public void writeMapBegin(TMap map) throws TException {
+ if (map.size == 0) {
+ writeByteDirect(0);
+ } else {
+ writeVarint32(map.size);
+ writeByteDirect(getCompactType(map.keyType) << 4 | getCompactType(map.valueType));
+ }
+ }
+
+ /**
+ * Write a list header.
+ */
+ public void writeListBegin(TList list) throws TException {
+ writeCollectionBegin(list.elemType, list.size);
+ }
+
+ /**
+ * Write a set header.
+ */
+ public void writeSetBegin(TSet set) throws TException {
+ writeCollectionBegin(set.elemType, set.size);
+ }
+
+ /**
+ * Write a boolean value. Potentially, this could be a boolean field, in
+ * which case the field header info isn't written yet. If so, decide what the
+ * right type header is for the value and then write the field header.
+ * Otherwise, write a single byte.
+ */
+ public void writeBool(boolean b) throws TException {
+ if (booleanField_ != null) {
+ // we haven't written the field header yet
+ writeFieldBeginInternal(booleanField_, b ? Types.BOOLEAN_TRUE : Types.BOOLEAN_FALSE);
+ booleanField_ = null;
+ } else {
+ // we're not part of a field, so just write the value.
+ writeByteDirect(b ? Types.BOOLEAN_TRUE : Types.BOOLEAN_FALSE);
+ }
+ }
+
+ /**
+ * Write a byte. Nothing to see here!
+ */
+ public void writeByte(byte b) throws TException {
+ writeByteDirect(b);
+ }
+
+ /**
+ * Write an I16 as a zigzag varint.
+ */
+ public void writeI16(short i16) throws TException {
+ writeVarint32(intToZigZag(i16));
+ }
+
+ /**
+ * Write an i32 as a zigzag varint.
+ */
+ public void writeI32(int i32) throws TException {
+ writeVarint32(intToZigZag(i32));
+ }
+
+ /**
+ * Write an i64 as a zigzag varint.
+ */
+ public void writeI64(long i64) throws TException {
+ writeVarint64(longToZigzag(i64));
+ }
+
+ /**
+ * Write a double to the wire as 8 bytes.
+ */
+ public void writeDouble(double dub) throws TException {
+ fixedLongToBytes(Double.doubleToLongBits(dub), temp, 0);
+ trans_.write(temp, 0, 8);
+ }
+
+ /**
+ * Write a string to the wire with a varint size preceding.
+ */
+ public void writeString(String str) throws TException {
+ byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
+ writeBinary(bytes, 0, bytes.length);
+ }
+
+ /**
+ * Write a byte array, using a varint for the size.
+ */
+ public void writeBinary(ByteBuffer bin) throws TException {
+ int length = bin.limit() - bin.position();
+ writeBinary(bin.array(), bin.position() + bin.arrayOffset(), length);
+ }
+
+ private void writeBinary(byte[] buf, int offset, int length) throws TException {
+ writeVarint32(length);
+ trans_.write(buf, offset, length);
+ }
+
+ //
+ // These methods are called by structs, but don't actually have any wire
+ // output or purpose.
+ //
+
+ public void writeMessageEnd() throws TException {}
+ public void writeMapEnd() throws TException {}
+ public void writeListEnd() throws TException {}
+ public void writeSetEnd() throws TException {}
+ public void writeFieldEnd() throws TException {}
+
+ //
+ // Internal writing methods
+ //
+
+ /**
+ * Abstract method for writing the start of lists and sets. List and sets on
+ * the wire differ only by the type indicator.
+ */
+ protected void writeCollectionBegin(byte elemType, int size) throws TException {
+ if (size <= 14) {
+ writeByteDirect(size << 4 | getCompactType(elemType));
+ } else {
+ writeByteDirect(0xf0 | getCompactType(elemType));
+ writeVarint32(size);
+ }
+ }
+
+ /**
+ * Write an i32 as a varint. Results in 1-5 bytes on the wire.
+ * TODO: make a permanent buffer like writeVarint64?
+ */
+ private void writeVarint32(int n) throws TException {
+ int idx = 0;
+ while (true) {
+ if ((n & ~0x7F) == 0) {
+ temp[idx++] = (byte)n;
+ // writeByteDirect((byte)n);
+ break;
+ // return;
+ } else {
+ temp[idx++] = (byte)((n & 0x7F) | 0x80);
+ // writeByteDirect((byte)((n & 0x7F) | 0x80));
+ n >>>= 7;
+ }
+ }
+ trans_.write(temp, 0, idx);
+ }
+
+ /**
+ * Write an i64 as a varint. Results in 1-10 bytes on the wire.
+ */
+ private void writeVarint64(long n) throws TException {
+ int idx = 0;
+ while (true) {
+ if ((n & ~0x7FL) == 0) {
+ temp[idx++] = (byte)n;
+ break;
+ } else {
+ temp[idx++] = ((byte)((n & 0x7F) | 0x80));
+ n >>>= 7;
+ }
+ }
+ trans_.write(temp, 0, idx);
+ }
+
+ /**
+ * Convert l into a zigzag long. This allows negative numbers to be
+ * represented compactly as a varint.
+ */
+ private long longToZigzag(long l) {
+ return (l << 1) ^ (l >> 63);
+ }
+
+ /**
+ * Convert n into a zigzag int. This allows negative numbers to be
+ * represented compactly as a varint.
+ */
+ private int intToZigZag(int n) {
+ return (n << 1) ^ (n >> 31);
+ }
+
+ /**
+ * Convert a long into little-endian bytes in buf starting at off and going
+ * until off+7.
+ */
+ private void fixedLongToBytes(long n, byte[] buf, int off) {
+ buf[off+0] = (byte)( n & 0xff);
+ buf[off+1] = (byte)((n >> 8 ) & 0xff);
+ buf[off+2] = (byte)((n >> 16) & 0xff);
+ buf[off+3] = (byte)((n >> 24) & 0xff);
+ buf[off+4] = (byte)((n >> 32) & 0xff);
+ buf[off+5] = (byte)((n >> 40) & 0xff);
+ buf[off+6] = (byte)((n >> 48) & 0xff);
+ buf[off+7] = (byte)((n >> 56) & 0xff);
+ }
+
+ /**
+ * Writes a byte without any possibility of all that field header nonsense.
+ * Used internally by other writing methods that know they need to write a byte.
+ */
+ private void writeByteDirect(byte b) throws TException {
+ temp[0] = b;
+ trans_.write(temp, 0, 1);
+ }
+
+ /**
+ * Writes a byte without any possibility of all that field header nonsense.
+ */
+ private void writeByteDirect(int n) throws TException {
+ writeByteDirect((byte)n);
+ }
+
+
+ //
+ // Reading methods.
+ //
+
+ /**
+ * Read a message header.
+ */
+ public TMessage readMessageBegin() throws TException {
+ byte protocolId = readByte();
+ if (protocolId != PROTOCOL_ID) {
+ throw new TProtocolException("Expected protocol id " + Integer.toHexString(PROTOCOL_ID) + " but got " + Integer.toHexString(protocolId));
+ }
+ byte versionAndType = readByte();
+ byte version = (byte)(versionAndType & VERSION_MASK);
+ if (version != VERSION) {
+ throw new TProtocolException("Expected version " + VERSION + " but got " + version);
+ }
+ byte type = (byte)((versionAndType >> TYPE_SHIFT_AMOUNT) & TYPE_BITS);
+ int seqid = readVarint32();
+ String messageName = readString();
+ return new TMessage(messageName, type, seqid);
+ }
+
+ /**
+ * Read a struct begin. There's nothing on the wire for this, but it is our
+ * opportunity to push a new struct begin marker onto the field stack.
+ */
+ public TStruct readStructBegin() throws TException {
+ lastField_.push(lastFieldId_);
+ lastFieldId_ = 0;
+ return ANONYMOUS_STRUCT;
+ }
+
+ /**
+ * Doesn't actually consume any wire data, just removes the last field for
+ * this struct from the field stack.
+ */
+ public void readStructEnd() throws TException {
+ // consume the last field we read off the wire.
+ lastFieldId_ = lastField_.pop();
+ }
+
+ /**
+ * Read a field header off the wire.
+ */
+ public TField readFieldBegin() throws TException {
+ byte type = readByte();
+
+ // if it's a stop, then we can return immediately, as the struct is over.
+ if (type == TType.STOP) {
+ return TSTOP;
+ }
+
+ short fieldId;
+
+ // mask off the 4 MSB of the type header. it could contain a field id delta.
+ short modifier = (short)((type & 0xf0) >> 4);
+ if (modifier == 0) {
+ // not a delta. look ahead for the zigzag varint field id.
+ fieldId = readI16();
+ } else {
+ // has a delta. add the delta to the last read field id.
+ fieldId = (short)(lastFieldId_ + modifier);
+ }
+
+ TField field = new TField("", getTType((byte)(type & 0x0f)), fieldId);
+
+ // if this happens to be a boolean field, the value is encoded in the type
+ if (isBoolType(type)) {
+ // save the boolean value in a special instance variable.
+ boolValue_ = (byte)(type & 0x0f) == Types.BOOLEAN_TRUE ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ // push the new field onto the field stack so we can keep the deltas going.
+ lastFieldId_ = field.id;
+ return field;
+ }
+
+ /**
+ * Read a map header off the wire. If the size is zero, skip reading the key
+ * and value type. This means that 0-length maps will yield TMaps without the
+ * "correct" types.
+ */
+ public TMap readMapBegin() throws TException {
+ int size = readVarint32();
+ checkContainerReadLength(size);
+ byte keyAndValueType = size == 0 ? 0 : readByte();
+ return new TMap(getTType((byte)(keyAndValueType >> 4)), getTType((byte)(keyAndValueType & 0xf)), size);
+ }
+
+ /**
+ * Read a list header off the wire. If the list size is 0-14, the size will
+ * be packed into the element type header. If it's a longer list, the 4 MSB
+ * of the element type header will be 0xF, and a varint will follow with the
+ * true size.
+ */
+ public TList readListBegin() throws TException {
+ byte size_and_type = readByte();
+ int size = (size_and_type >> 4) & 0x0f;
+ if (size == 15) {
+ size = readVarint32();
+ }
+ checkContainerReadLength(size);
+ byte type = getTType(size_and_type);
+ return new TList(type, size);
+ }
+
+ /**
+ * Read a set header off the wire. If the set size is 0-14, the size will
+ * be packed into the element type header. If it's a longer set, the 4 MSB
+ * of the element type header will be 0xF, and a varint will follow with the
+ * true size.
+ */
+ public TSet readSetBegin() throws TException {
+ return new TSet(readListBegin());
+ }
+
+ /**
+ * Read a boolean off the wire. If this is a boolean field, the value should
+ * already have been read during readFieldBegin, so we'll just consume the
+ * pre-stored value. Otherwise, read a byte.
+ */
+ public boolean readBool() throws TException {
+ if (boolValue_ != null) {
+ boolean result = boolValue_.booleanValue();
+ boolValue_ = null;
+ return result;
+ }
+ return readByte() == Types.BOOLEAN_TRUE;
+ }
+
+ /**
+ * Read a single byte off the wire. Nothing interesting here.
+ */
+ public byte readByte() throws TException {
+ byte b;
+ if (trans_.getBytesRemainingInBuffer() > 0) {
+ b = trans_.getBuffer()[trans_.getBufferPosition()];
+ trans_.consumeBuffer(1);
+ } else {
+ trans_.readAll(temp, 0, 1);
+ b = temp[0];
+ }
+ return b;
+ }
+
+ /**
+ * Read an i16 from the wire as a zigzag varint.
+ */
+ public short readI16() throws TException {
+ return (short)zigzagToInt(readVarint32());
+ }
+
+ /**
+ * Read an i32 from the wire as a zigzag varint.
+ */
+ public int readI32() throws TException {
+ return zigzagToInt(readVarint32());
+ }
+
+ /**
+ * Read an i64 from the wire as a zigzag varint.
+ */
+ public long readI64() throws TException {
+ return zigzagToLong(readVarint64());
+ }
+
+ /**
+ * No magic here - just read a double off the wire.
+ */
+ public double readDouble() throws TException {
+ trans_.readAll(temp, 0, 8);
+ return Double.longBitsToDouble(bytesToLong(temp));
+ }
+
+ /**
+ * Reads a byte[] (via readBinary), and then UTF-8 decodes it.
+ */
+ public String readString() throws TException {
+ int length = readVarint32();
+ checkStringReadLength(length);
+
+ if (length == 0) {
+ return "";
+ }
+
+ final String str;
+ if (trans_.getBytesRemainingInBuffer() >= length) {
+ str = new String(trans_.getBuffer(), trans_.getBufferPosition(),
+ length, StandardCharsets.UTF_8);
+ trans_.consumeBuffer(length);
+ } else {
+ str = new String(readBinary(length), StandardCharsets.UTF_8);
+ }
+ return str;
+ }
+
+ /**
+ * Read a byte[] from the wire.
+ */
+ public ByteBuffer readBinary() throws TException {
+ int length = readVarint32();
+ checkStringReadLength(length);
+ if (length == 0) return EMPTY_BUFFER;
+
+ if (trans_.getBytesRemainingInBuffer() >= length) {
+ ByteBuffer bb = ByteBuffer.wrap(trans_.getBuffer(), trans_.getBufferPosition(), length);
+ trans_.consumeBuffer(length);
+ return bb;
+ }
+
+ byte[] buf = new byte[length];
+ trans_.readAll(buf, 0, length);
+ return ByteBuffer.wrap(buf);
+ }
+
+ /**
+ * Read a byte[] of a known length from the wire.
+ */
+ private byte[] readBinary(int length) throws TException {
+ if (length == 0) return EMPTY_BYTES;
+
+ byte[] buf = new byte[length];
+ trans_.readAll(buf, 0, length);
+ return buf;
+ }
+
+ private void checkStringReadLength(int length) throws TProtocolException {
+ if (length < 0) {
+ throw new TProtocolException(TProtocolException.NEGATIVE_SIZE,
+ "Negative length: " + length);
+ }
+ if (stringLengthLimit_ != NO_LENGTH_LIMIT && length > stringLengthLimit_) {
+ throw new TProtocolException(TProtocolException.SIZE_LIMIT,
+ "Length exceeded max allowed: " + length);
+ }
+ }
+
+ private void checkContainerReadLength(int length) throws TProtocolException {
+ if (length < 0) {
+ throw new TProtocolException(TProtocolException.NEGATIVE_SIZE,
+ "Negative length: " + length);
+ }
+ if (containerLengthLimit_ != NO_LENGTH_LIMIT && length > containerLengthLimit_) {
+ throw new TProtocolException(TProtocolException.SIZE_LIMIT,
+ "Length exceeded max allowed: " + length);
+ }
+ }
+
+ //
+ // These methods are here for the struct to call, but don't have any wire
+ // encoding.
+ //
+ public void readMessageEnd() throws TException {}
+ public void readFieldEnd() throws TException {}
+ public void readMapEnd() throws TException {}
+ public void readListEnd() throws TException {}
+ public void readSetEnd() throws TException {}
+
+ //
+ // Internal reading methods
+ //
+
+ /**
+ * Read an i32 from the wire as a varint. The MSB of each byte is set
+ * if there is another byte to follow. This can read up to 5 bytes.
+ */
+ private int readVarint32() throws TException {
+ int result = 0;
+ int shift = 0;
+ if (trans_.getBytesRemainingInBuffer() >= 5) {
+ byte[] buf = trans_.getBuffer();
+ int pos = trans_.getBufferPosition();
+ int off = 0;
+ while (true) {
+ byte b = buf[pos+off];
+ result |= (int) (b & 0x7f) << shift;
+ if ((b & 0x80) != 0x80) break;
+ shift += 7;
+ off++;
+ }
+ trans_.consumeBuffer(off+1);
+ } else {
+ while (true) {
+ byte b = readByte();
+ result |= (int) (b & 0x7f) << shift;
+ if ((b & 0x80) != 0x80) break;
+ shift += 7;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Read an i64 from the wire as a proper varint. The MSB of each byte is set
+ * if there is another byte to follow. This can read up to 10 bytes.
+ */
+ private long readVarint64() throws TException {
+ int shift = 0;
+ long result = 0;
+ if (trans_.getBytesRemainingInBuffer() >= 10) {
+ byte[] buf = trans_.getBuffer();
+ int pos = trans_.getBufferPosition();
+ int off = 0;
+ while (true) {
+ byte b = buf[pos+off];
+ result |= (long) (b & 0x7f) << shift;
+ if ((b & 0x80) != 0x80) break;
+ shift += 7;
+ off++;
+ }
+ trans_.consumeBuffer(off+1);
+ } else {
+ while (true) {
+ byte b = readByte();
+ result |= (long) (b & 0x7f) << shift;
+ if ((b & 0x80) != 0x80) break;
+ shift +=7;
+ }
+ }
+ return result;
+ }
+
+ //
+ // encoding helpers
+ //
+
+ /**
+ * Convert from zigzag int to int.
+ */
+ private int zigzagToInt(int n) {
+ return (n >>> 1) ^ -(n & 1);
+ }
+
+ /**
+ * Convert from zigzag long to long.
+ */
+ private long zigzagToLong(long n) {
+ return (n >>> 1) ^ -(n & 1);
+ }
+
+ /**
+ * Note that it's important that the mask bytes are long literals,
+ * otherwise they'll default to ints, and when you shift an int left 56 bits,
+ * you just get a messed up int.
+ */
+ private long bytesToLong(byte[] bytes) {
+ return
+ ((bytes[7] & 0xffL) << 56) |
+ ((bytes[6] & 0xffL) << 48) |
+ ((bytes[5] & 0xffL) << 40) |
+ ((bytes[4] & 0xffL) << 32) |
+ ((bytes[3] & 0xffL) << 24) |
+ ((bytes[2] & 0xffL) << 16) |
+ ((bytes[1] & 0xffL) << 8) |
+ ((bytes[0] & 0xffL));
+ }
+
+ //
+ // type testing and converting
+ //
+
+ private boolean isBoolType(byte b) {
+ int lowerNibble = b & 0x0f;
+ return lowerNibble == Types.BOOLEAN_TRUE || lowerNibble == Types.BOOLEAN_FALSE;
+ }
+
+ /**
+ * Given a TCompactProtocol.Types constant, convert it to its corresponding
+ * TType value.
+ */
+ private byte getTType(byte type) throws TProtocolException {
+ switch ((byte)(type & 0x0f)) {
+ case TType.STOP:
+ return TType.STOP;
+ case Types.BOOLEAN_FALSE:
+ case Types.BOOLEAN_TRUE:
+ return TType.BOOL;
+ case Types.BYTE:
+ return TType.BYTE;
+ case Types.I16:
+ return TType.I16;
+ case Types.I32:
+ return TType.I32;
+ case Types.I64:
+ return TType.I64;
+ case Types.DOUBLE:
+ return TType.DOUBLE;
+ case Types.BINARY:
+ return TType.STRING;
+ case Types.LIST:
+ return TType.LIST;
+ case Types.SET:
+ return TType.SET;
+ case Types.MAP:
+ return TType.MAP;
+ case Types.STRUCT:
+ return TType.STRUCT;
+ default:
+ throw new TProtocolException("don't know what type: " + (byte)(type & 0x0f));
+ }
+ }
+
+ /**
+ * Given a TType value, find the appropriate TCompactProtocol.Types constant.
+ */
+ private byte getCompactType(byte ttype) {
+ return ttypeToCompactType[ttype];
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TField.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TField.java
new file mode 100644
index 000000000..3872b008f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TField.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Helper class that encapsulates field metadata.
+ * <p>Two fields are considered equal if they have the same type and id.</p>
+ */
+public class TField {
+ public TField() {
+ this("", TType.STOP, (short)0);
+ }
+
+ public TField(String n, byte t, short i) {
+ name = n;
+ type = t;
+ id = i;
+ }
+
+ public final String name;
+ public final byte type;
+ public final short id;
+
+ public String toString() {
+ return "<TField name:'" + name + "' type:" + type + " field-id:" + id + ">";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + id;
+ result = prime * result + type;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ TField otherField = (TField) obj;
+ return type == otherField.type && id == otherField.id;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TJSONProtocol.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TJSONProtocol.java
new file mode 100644
index 000000000..d37c4937f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TJSONProtocol.java
@@ -0,0 +1,973 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Stack;
+
+import org.apache.thrift.TByteArrayOutputStream;
+import org.apache.thrift.TException;
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * JSON protocol implementation for thrift.
+ *
+ * This is a full-featured protocol supporting write and read.
+ *
+ * Please see the C++ class header for a detailed description of the
+ * protocol's wire format.
+ *
+ */
+public class TJSONProtocol extends TProtocol {
+
+ /**
+ * Factory for JSON protocol objects
+ */
+ public static class Factory implements TProtocolFactory {
+ protected boolean fieldNamesAsString_ = false;
+
+ public Factory() {}
+
+ public Factory(boolean fieldNamesAsString) {
+ fieldNamesAsString_ = fieldNamesAsString;
+ }
+
+ public TProtocol getProtocol(TTransport trans) {
+ return new TJSONProtocol(trans, fieldNamesAsString_);
+ }
+
+ }
+
+ private static final byte[] COMMA = new byte[] {','};
+ private static final byte[] COLON = new byte[] {':'};
+ private static final byte[] LBRACE = new byte[] {'{'};
+ private static final byte[] RBRACE = new byte[] {'}'};
+ private static final byte[] LBRACKET = new byte[] {'['};
+ private static final byte[] RBRACKET = new byte[] {']'};
+ private static final byte[] QUOTE = new byte[] {'"'};
+ private static final byte[] BACKSLASH = new byte[] {'\\'};
+ private static final byte[] ZERO = new byte[] {'0'};
+
+ private static final byte[] ESCSEQ = new byte[] {'\\','u','0','0'};
+
+ private static final long VERSION = 1;
+
+ private static final byte[] JSON_CHAR_TABLE = {
+ /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+ 0, 0, 0, 0, 0, 0, 0, 0,'b','t','n', 0,'f','r', 0, 0, // 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
+ 1, 1,'"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2
+ };
+
+ private static final String ESCAPE_CHARS = "\"\\/bfnrt";
+
+ private static final byte[] ESCAPE_CHAR_VALS = {
+ '"', '\\', '/', '\b', '\f', '\n', '\r', '\t',
+ };
+
+ private static final int DEF_STRING_SIZE = 16;
+
+ private static final byte[] NAME_BOOL = new byte[] {'t', 'f'};
+ private static final byte[] NAME_BYTE = new byte[] {'i','8'};
+ private static final byte[] NAME_I16 = new byte[] {'i','1','6'};
+ private static final byte[] NAME_I32 = new byte[] {'i','3','2'};
+ private static final byte[] NAME_I64 = new byte[] {'i','6','4'};
+ private static final byte[] NAME_DOUBLE = new byte[] {'d','b','l'};
+ private static final byte[] NAME_STRUCT = new byte[] {'r','e','c'};
+ private static final byte[] NAME_STRING = new byte[] {'s','t','r'};
+ private static final byte[] NAME_MAP = new byte[] {'m','a','p'};
+ private static final byte[] NAME_LIST = new byte[] {'l','s','t'};
+ private static final byte[] NAME_SET = new byte[] {'s','e','t'};
+
+ private static final TStruct ANONYMOUS_STRUCT = new TStruct();
+
+ private static final byte[] getTypeNameForTypeID(byte typeID)
+ throws TException {
+ switch (typeID) {
+ case TType.BOOL:
+ return NAME_BOOL;
+ case TType.BYTE:
+ return NAME_BYTE;
+ case TType.I16:
+ return NAME_I16;
+ case TType.I32:
+ return NAME_I32;
+ case TType.I64:
+ return NAME_I64;
+ case TType.DOUBLE:
+ return NAME_DOUBLE;
+ case TType.STRING:
+ return NAME_STRING;
+ case TType.STRUCT:
+ return NAME_STRUCT;
+ case TType.MAP:
+ return NAME_MAP;
+ case TType.SET:
+ return NAME_SET;
+ case TType.LIST:
+ return NAME_LIST;
+ default:
+ throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
+ "Unrecognized type");
+ }
+ }
+
+ private static final byte getTypeIDForTypeName(byte[] name)
+ throws TException {
+ byte result = TType.STOP;
+ if (name.length > 1) {
+ switch (name[0]) {
+ case 'd':
+ result = TType.DOUBLE;
+ break;
+ case 'i':
+ switch (name[1]) {
+ case '8':
+ result = TType.BYTE;
+ break;
+ case '1':
+ result = TType.I16;
+ break;
+ case '3':
+ result = TType.I32;
+ break;
+ case '6':
+ result = TType.I64;
+ break;
+ }
+ break;
+ case 'l':
+ result = TType.LIST;
+ break;
+ case 'm':
+ result = TType.MAP;
+ break;
+ case 'r':
+ result = TType.STRUCT;
+ break;
+ case 's':
+ if (name[1] == 't') {
+ result = TType.STRING;
+ }
+ else if (name[1] == 'e') {
+ result = TType.SET;
+ }
+ break;
+ case 't':
+ result = TType.BOOL;
+ break;
+ }
+ }
+ if (result == TType.STOP) {
+ throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
+ "Unrecognized type");
+ }
+ return result;
+ }
+
+ // Base class for tracking JSON contexts that may require inserting/reading
+ // additional JSON syntax characters
+ // This base context does nothing.
+ protected class JSONBaseContext {
+ protected void write() throws TException {}
+
+ protected void read() throws TException {}
+
+ protected boolean escapeNum() { return false; }
+ }
+
+ // Context for JSON lists. Will insert/read commas before each item except
+ // for the first one
+ protected class JSONListContext extends JSONBaseContext {
+ private boolean first_ = true;
+
+ @Override
+ protected void write() throws TException {
+ if (first_) {
+ first_ = false;
+ } else {
+ trans_.write(COMMA);
+ }
+ }
+
+ @Override
+ protected void read() throws TException {
+ if (first_) {
+ first_ = false;
+ } else {
+ readJSONSyntaxChar(COMMA);
+ }
+ }
+ }
+
+ // Context for JSON records. Will insert/read colons before the value portion
+ // of each record pair, and commas before each key except the first. In
+ // addition, will indicate that numbers in the key position need to be
+ // escaped in quotes (since JSON keys must be strings).
+ protected class JSONPairContext extends JSONBaseContext {
+ private boolean first_ = true;
+ private boolean colon_ = true;
+
+ @Override
+ protected void write() throws TException {
+ if (first_) {
+ first_ = false;
+ colon_ = true;
+ } else {
+ trans_.write(colon_ ? COLON : COMMA);
+ colon_ = !colon_;
+ }
+ }
+
+ @Override
+ protected void read() throws TException {
+ if (first_) {
+ first_ = false;
+ colon_ = true;
+ } else {
+ readJSONSyntaxChar(colon_ ? COLON : COMMA);
+ colon_ = !colon_;
+ }
+ }
+
+ @Override
+ protected boolean escapeNum() {
+ return colon_;
+ }
+ }
+
+ // Holds up to one byte from the transport
+ protected class LookaheadReader {
+
+ private boolean hasData_;
+ private byte[] data_ = new byte[1];
+
+ // Return and consume the next byte to be read, either taking it from the
+ // data buffer if present or getting it from the transport otherwise.
+ protected byte read() throws TException {
+ if (hasData_) {
+ hasData_ = false;
+ }
+ else {
+ trans_.readAll(data_, 0, 1);
+ }
+ return data_[0];
+ }
+
+ // Return the next byte to be read without consuming, filling the data
+ // buffer if it has not been filled already.
+ protected byte peek() throws TException {
+ if (!hasData_) {
+ trans_.readAll(data_, 0, 1);
+ }
+ hasData_ = true;
+ return data_[0];
+ }
+ }
+
+ // Stack of nested contexts that we may be in
+ private Stack<JSONBaseContext> contextStack_ = new Stack<JSONBaseContext>();
+
+ // Current context that we are in
+ private JSONBaseContext context_ = new JSONBaseContext();
+
+ // Reader that manages a 1-byte buffer
+ private LookaheadReader reader_ = new LookaheadReader();
+
+ // Write out the TField names as a string instead of the default integer value
+ private boolean fieldNamesAsString_ = false;
+
+ // Push a new JSON context onto the stack.
+ private void pushContext(JSONBaseContext c) {
+ contextStack_.push(context_);
+ context_ = c;
+ }
+
+ // Pop the last JSON context off the stack
+ private void popContext() {
+ context_ = contextStack_.pop();
+ }
+
+ // Reset the context stack to its initial state
+ private void resetContext() {
+ while (!contextStack_.isEmpty()) {
+ popContext();
+ }
+ }
+
+ /**
+ * Constructor
+ */
+ public TJSONProtocol(TTransport trans) {
+ super(trans);
+ }
+
+ public TJSONProtocol(TTransport trans, boolean fieldNamesAsString) {
+ super(trans);
+ fieldNamesAsString_ = fieldNamesAsString;
+ }
+
+ @Override
+ public void reset() {
+ contextStack_.clear();
+ context_ = new JSONBaseContext();
+ reader_ = new LookaheadReader();
+ }
+
+ // Temporary buffer used by several methods
+ private byte[] tmpbuf_ = new byte[4];
+
+ // Read a byte that must match b[0]; otherwise an exception is thrown.
+ // Marked protected to avoid synthetic accessor in JSONListContext.read
+ // and JSONPairContext.read
+ protected void readJSONSyntaxChar(byte[] b) throws TException {
+ byte ch = reader_.read();
+ if (ch != b[0]) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Unexpected character:" + (char)ch);
+ }
+ }
+
+ // Convert a byte containing a hex char ('0'-'9' or 'a'-'f') into its
+ // corresponding hex value
+ private static final byte hexVal(byte ch) throws TException {
+ if ((ch >= '0') && (ch <= '9')) {
+ return (byte)((char)ch - '0');
+ }
+ else if ((ch >= 'a') && (ch <= 'f')) {
+ return (byte)((char)ch - 'a' + 10);
+ }
+ else {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Expected hex character");
+ }
+ }
+
+ // Convert a byte containing a hex value to its corresponding hex character
+ private static final byte hexChar(byte val) {
+ val &= 0x0F;
+ if (val < 10) {
+ return (byte)((char)val + '0');
+ }
+ else {
+ return (byte)((char)(val - 10) + 'a');
+ }
+ }
+
+ // Write the bytes in array buf as a JSON characters, escaping as needed
+ private void writeJSONString(byte[] b) throws TException {
+ context_.write();
+ trans_.write(QUOTE);
+ int len = b.length;
+ for (int i = 0; i < len; i++) {
+ if ((b[i] & 0x00FF) >= 0x30) {
+ if (b[i] == BACKSLASH[0]) {
+ trans_.write(BACKSLASH);
+ trans_.write(BACKSLASH);
+ }
+ else {
+ trans_.write(b, i, 1);
+ }
+ }
+ else {
+ tmpbuf_[0] = JSON_CHAR_TABLE[b[i]];
+ if (tmpbuf_[0] == 1) {
+ trans_.write(b, i, 1);
+ }
+ else if (tmpbuf_[0] > 1) {
+ trans_.write(BACKSLASH);
+ trans_.write(tmpbuf_, 0, 1);
+ }
+ else {
+ trans_.write(ESCSEQ);
+ tmpbuf_[0] = hexChar((byte)(b[i] >> 4));
+ tmpbuf_[1] = hexChar(b[i]);
+ trans_.write(tmpbuf_, 0, 2);
+ }
+ }
+ }
+ trans_.write(QUOTE);
+ }
+
+ // Write out number as a JSON value. If the context dictates so, it will be
+ // wrapped in quotes to output as a JSON string.
+ private void writeJSONInteger(long num) throws TException {
+ context_.write();
+ String str = Long.toString(num);
+ boolean escapeNum = context_.escapeNum();
+ if (escapeNum) {
+ trans_.write(QUOTE);
+ }
+ byte[] buf = str.getBytes(StandardCharsets.UTF_8);
+ trans_.write(buf);
+ if (escapeNum) {
+ trans_.write(QUOTE);
+ }
+ }
+
+ // Write out a double as a JSON value. If it is NaN or infinity or if the
+ // context dictates escaping, write out as JSON string.
+ private void writeJSONDouble(double num) throws TException {
+ context_.write();
+ String str = Double.toString(num);
+ boolean special = false;
+ switch (str.charAt(0)) {
+ case 'N': // NaN
+ case 'I': // Infinity
+ special = true;
+ break;
+ case '-':
+ if (str.charAt(1) == 'I') { // -Infinity
+ special = true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ boolean escapeNum = special || context_.escapeNum();
+ if (escapeNum) {
+ trans_.write(QUOTE);
+ }
+ byte[] b = str.getBytes(StandardCharsets.UTF_8);
+ trans_.write(b, 0, b.length);
+ if (escapeNum) {
+ trans_.write(QUOTE);
+ }
+ }
+
+ // Write out contents of byte array b as a JSON string with base-64 encoded
+ // data
+ private void writeJSONBase64(byte[] b, int offset, int length) throws TException {
+ context_.write();
+ trans_.write(QUOTE);
+ int len = length;
+ int off = offset;
+ while (len >= 3) {
+ // Encode 3 bytes at a time
+ TBase64Utils.encode(b, off, 3, tmpbuf_, 0);
+ trans_.write(tmpbuf_, 0, 4);
+ off += 3;
+ len -= 3;
+ }
+ if (len > 0) {
+ // Encode remainder
+ TBase64Utils.encode(b, off, len, tmpbuf_, 0);
+ trans_.write(tmpbuf_, 0, len + 1);
+ }
+ trans_.write(QUOTE);
+ }
+
+ private void writeJSONObjectStart() throws TException {
+ context_.write();
+ trans_.write(LBRACE);
+ pushContext(new JSONPairContext());
+ }
+
+ private void writeJSONObjectEnd() throws TException {
+ popContext();
+ trans_.write(RBRACE);
+ }
+
+ private void writeJSONArrayStart() throws TException {
+ context_.write();
+ trans_.write(LBRACKET);
+ pushContext(new JSONListContext());
+ }
+
+ private void writeJSONArrayEnd() throws TException {
+ popContext();
+ trans_.write(RBRACKET);
+ }
+
+ @Override
+ public void writeMessageBegin(TMessage message) throws TException {
+ resetContext(); // THRIFT-3743
+ writeJSONArrayStart();
+ writeJSONInteger(VERSION);
+ byte[] b = message.name.getBytes(StandardCharsets.UTF_8);
+ writeJSONString(b);
+ writeJSONInteger(message.type);
+ writeJSONInteger(message.seqid);
+ }
+
+ @Override
+ public void writeMessageEnd() throws TException {
+ writeJSONArrayEnd();
+ }
+
+ @Override
+ public void writeStructBegin(TStruct struct) throws TException {
+ writeJSONObjectStart();
+ }
+
+ @Override
+ public void writeStructEnd() throws TException {
+ writeJSONObjectEnd();
+ }
+
+ @Override
+ public void writeFieldBegin(TField field) throws TException {
+ if (fieldNamesAsString_) {
+ writeString(field.name);
+ } else {
+ writeJSONInteger(field.id);
+ }
+ writeJSONObjectStart();
+ writeJSONString(getTypeNameForTypeID(field.type));
+ }
+
+ @Override
+ public void writeFieldEnd() throws TException {
+ writeJSONObjectEnd();
+ }
+
+ @Override
+ public void writeFieldStop() {}
+
+ @Override
+ public void writeMapBegin(TMap map) throws TException {
+ writeJSONArrayStart();
+ writeJSONString(getTypeNameForTypeID(map.keyType));
+ writeJSONString(getTypeNameForTypeID(map.valueType));
+ writeJSONInteger(map.size);
+ writeJSONObjectStart();
+ }
+
+ @Override
+ public void writeMapEnd() throws TException {
+ writeJSONObjectEnd();
+ writeJSONArrayEnd();
+ }
+
+ @Override
+ public void writeListBegin(TList list) throws TException {
+ writeJSONArrayStart();
+ writeJSONString(getTypeNameForTypeID(list.elemType));
+ writeJSONInteger(list.size);
+ }
+
+ @Override
+ public void writeListEnd() throws TException {
+ writeJSONArrayEnd();
+ }
+
+ @Override
+ public void writeSetBegin(TSet set) throws TException {
+ writeJSONArrayStart();
+ writeJSONString(getTypeNameForTypeID(set.elemType));
+ writeJSONInteger(set.size);
+ }
+
+ @Override
+ public void writeSetEnd() throws TException {
+ writeJSONArrayEnd();
+ }
+
+ @Override
+ public void writeBool(boolean b) throws TException {
+ writeJSONInteger(b ? (long)1 : (long)0);
+ }
+
+ @Override
+ public void writeByte(byte b) throws TException {
+ writeJSONInteger((long)b);
+ }
+
+ @Override
+ public void writeI16(short i16) throws TException {
+ writeJSONInteger((long)i16);
+ }
+
+ @Override
+ public void writeI32(int i32) throws TException {
+ writeJSONInteger((long)i32);
+ }
+
+ @Override
+ public void writeI64(long i64) throws TException {
+ writeJSONInteger(i64);
+ }
+
+ @Override
+ public void writeDouble(double dub) throws TException {
+ writeJSONDouble(dub);
+ }
+
+ @Override
+ public void writeString(String str) throws TException {
+ byte[] b = str.getBytes(StandardCharsets.UTF_8);
+ writeJSONString(b);
+ }
+
+ @Override
+ public void writeBinary(ByteBuffer bin) throws TException {
+ writeJSONBase64(bin.array(), bin.position() + bin.arrayOffset(), bin.limit() - bin.position() - bin.arrayOffset());
+ }
+
+ /**
+ * Reading methods.
+ */
+
+ // Read in a JSON string, unescaping as appropriate.. Skip reading from the
+ // context if skipContext is true.
+ private TByteArrayOutputStream readJSONString(boolean skipContext)
+ throws TException {
+ TByteArrayOutputStream arr = new TByteArrayOutputStream(DEF_STRING_SIZE);
+ ArrayList<Character> codeunits = new ArrayList<Character>();
+ if (!skipContext) {
+ context_.read();
+ }
+ readJSONSyntaxChar(QUOTE);
+ while (true) {
+ byte ch = reader_.read();
+ if (ch == QUOTE[0]) {
+ break;
+ }
+ if (ch == ESCSEQ[0]) {
+ ch = reader_.read();
+ if (ch == ESCSEQ[1]) {
+ trans_.readAll(tmpbuf_, 0, 4);
+ short cu = (short)(
+ ((short)hexVal(tmpbuf_[0]) << 12) +
+ ((short)hexVal(tmpbuf_[1]) << 8) +
+ ((short)hexVal(tmpbuf_[2]) << 4) +
+ (short)hexVal(tmpbuf_[3]));
+ try {
+ if (Character.isHighSurrogate((char)cu)) {
+ if (codeunits.size() > 0) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Expected low surrogate char");
+ }
+ codeunits.add((char)cu);
+ }
+ else if (Character.isLowSurrogate((char)cu)) {
+ if (codeunits.size() == 0) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Expected high surrogate char");
+ }
+
+ codeunits.add((char)cu);
+ arr.write(
+ (new String(new int[] { codeunits.get(0), codeunits.get(1) },
+ 0, 2)).getBytes(StandardCharsets.UTF_8));
+ codeunits.clear();
+ }
+ else {
+ arr.write((new String(new int[] { cu }, 0, 1))
+ .getBytes(StandardCharsets.UTF_8));
+ }
+ continue;
+ } catch (IOException ex) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Invalid unicode sequence");
+ }
+ }
+ else {
+ int off = ESCAPE_CHARS.indexOf(ch);
+ if (off == -1) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Expected control char");
+ }
+ ch = ESCAPE_CHAR_VALS[off];
+ }
+ }
+ arr.write(ch);
+ }
+ return arr;
+ }
+
+ // Return true if the given byte could be a valid part of a JSON number.
+ private boolean isJSONNumeric(byte b) {
+ switch (b) {
+ case '+':
+ case '-':
+ case '.':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'E':
+ case 'e':
+ return true;
+ }
+ return false;
+ }
+
+ // Read in a sequence of characters that are all valid in JSON numbers. Does
+ // not do a complete regex check to validate that this is actually a number.
+ private String readJSONNumericChars() throws TException {
+ StringBuilder strbld = new StringBuilder();
+ while (true) {
+ byte ch = reader_.peek();
+ if (!isJSONNumeric(ch)) {
+ break;
+ }
+ strbld.append((char)reader_.read());
+ }
+ return strbld.toString();
+ }
+
+ // Read in a JSON number. If the context dictates, read in enclosing quotes.
+ private long readJSONInteger() throws TException {
+ context_.read();
+ if (context_.escapeNum()) {
+ readJSONSyntaxChar(QUOTE);
+ }
+ String str = readJSONNumericChars();
+ if (context_.escapeNum()) {
+ readJSONSyntaxChar(QUOTE);
+ }
+ try {
+ return Long.valueOf(str);
+ }
+ catch (NumberFormatException ex) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Bad data encounted in numeric data");
+ }
+ }
+
+ // Read in a JSON double value. Throw if the value is not wrapped in quotes
+ // when expected or if wrapped in quotes when not expected.
+ private double readJSONDouble() throws TException {
+ context_.read();
+ if (reader_.peek() == QUOTE[0]) {
+ TByteArrayOutputStream arr = readJSONString(true);
+ double dub = Double.valueOf(arr.toString(StandardCharsets.UTF_8));
+ if (!context_.escapeNum() && !Double.isNaN(dub)
+ && !Double.isInfinite(dub)) {
+ // Throw exception -- we should not be in a string in this case
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Numeric data unexpectedly quoted");
+ }
+ return dub;
+ }
+ else {
+ if (context_.escapeNum()) {
+ // This will throw - we should have had a quote if escapeNum == true
+ readJSONSyntaxChar(QUOTE);
+ }
+ try {
+ return Double.valueOf(readJSONNumericChars());
+ }
+ catch (NumberFormatException ex) {
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Bad data encounted in numeric data");
+ }
+ }
+ }
+
+ // Read in a JSON string containing base-64 encoded data and decode it.
+ private byte[] readJSONBase64() throws TException {
+ TByteArrayOutputStream arr = readJSONString(false);
+ byte[] b = arr.get();
+ int len = arr.len();
+ int off = 0;
+ int size = 0;
+ // Ignore padding
+ int bound = len >= 2 ? len - 2 : 0;
+ for (int i = len - 1; i >= bound && b[i] == '='; --i) {
+ --len;
+ }
+ while (len >= 4) {
+ // Decode 4 bytes at a time
+ TBase64Utils.decode(b, off, 4, b, size); // NB: decoded in place
+ off += 4;
+ len -= 4;
+ size += 3;
+ }
+ // Don't decode if we hit the end or got a single leftover byte (invalid
+ // base64 but legal for skip of regular string type)
+ if (len > 1) {
+ // Decode remainder
+ TBase64Utils.decode(b, off, len, b, size); // NB: decoded in place
+ size += len - 1;
+ }
+ // Sadly we must copy the byte[] (any way around this?)
+ byte [] result = new byte[size];
+ System.arraycopy(b, 0, result, 0, size);
+ return result;
+ }
+
+ private void readJSONObjectStart() throws TException {
+ context_.read();
+ readJSONSyntaxChar(LBRACE);
+ pushContext(new JSONPairContext());
+ }
+
+ private void readJSONObjectEnd() throws TException {
+ readJSONSyntaxChar(RBRACE);
+ popContext();
+ }
+
+ private void readJSONArrayStart() throws TException {
+ context_.read();
+ readJSONSyntaxChar(LBRACKET);
+ pushContext(new JSONListContext());
+ }
+
+ private void readJSONArrayEnd() throws TException {
+ readJSONSyntaxChar(RBRACKET);
+ popContext();
+ }
+
+ @Override
+ public TMessage readMessageBegin() throws TException {
+ resetContext(); // THRIFT-3743
+ readJSONArrayStart();
+ if (readJSONInteger() != VERSION) {
+ throw new TProtocolException(TProtocolException.BAD_VERSION,
+ "Message contained bad version.");
+ }
+ String name = readJSONString(false).toString(StandardCharsets.UTF_8);
+ byte type = (byte) readJSONInteger();
+ int seqid = (int) readJSONInteger();
+ return new TMessage(name, type, seqid);
+ }
+
+ @Override
+ public void readMessageEnd() throws TException {
+ readJSONArrayEnd();
+ }
+
+ @Override
+ public TStruct readStructBegin() throws TException {
+ readJSONObjectStart();
+ return ANONYMOUS_STRUCT;
+ }
+
+ @Override
+ public void readStructEnd() throws TException {
+ readJSONObjectEnd();
+ }
+
+ @Override
+ public TField readFieldBegin() throws TException {
+ byte ch = reader_.peek();
+ byte type;
+ short id = 0;
+ if (ch == RBRACE[0]) {
+ type = TType.STOP;
+ }
+ else {
+ id = (short) readJSONInteger();
+ readJSONObjectStart();
+ type = getTypeIDForTypeName(readJSONString(false).get());
+ }
+ return new TField("", type, id);
+ }
+
+ @Override
+ public void readFieldEnd() throws TException {
+ readJSONObjectEnd();
+ }
+
+ @Override
+ public TMap readMapBegin() throws TException {
+ readJSONArrayStart();
+ byte keyType = getTypeIDForTypeName(readJSONString(false).get());
+ byte valueType = getTypeIDForTypeName(readJSONString(false).get());
+ int size = (int)readJSONInteger();
+ readJSONObjectStart();
+ return new TMap(keyType, valueType, size);
+ }
+
+ @Override
+ public void readMapEnd() throws TException {
+ readJSONObjectEnd();
+ readJSONArrayEnd();
+ }
+
+ @Override
+ public TList readListBegin() throws TException {
+ readJSONArrayStart();
+ byte elemType = getTypeIDForTypeName(readJSONString(false).get());
+ int size = (int)readJSONInteger();
+ return new TList(elemType, size);
+ }
+
+ @Override
+ public void readListEnd() throws TException {
+ readJSONArrayEnd();
+ }
+
+ @Override
+ public TSet readSetBegin() throws TException {
+ readJSONArrayStart();
+ byte elemType = getTypeIDForTypeName(readJSONString(false).get());
+ int size = (int)readJSONInteger();
+ return new TSet(elemType, size);
+ }
+
+ @Override
+ public void readSetEnd() throws TException {
+ readJSONArrayEnd();
+ }
+
+ @Override
+ public boolean readBool() throws TException {
+ return (readJSONInteger() == 0 ? false : true);
+ }
+
+ @Override
+ public byte readByte() throws TException {
+ return (byte) readJSONInteger();
+ }
+
+ @Override
+ public short readI16() throws TException {
+ return (short) readJSONInteger();
+ }
+
+ @Override
+ public int readI32() throws TException {
+ return (int) readJSONInteger();
+ }
+
+ @Override
+ public long readI64() throws TException {
+ return (long) readJSONInteger();
+ }
+
+ @Override
+ public double readDouble() throws TException {
+ return readJSONDouble();
+ }
+
+ @Override
+ public String readString() throws TException {
+ return readJSONString(false).toString(StandardCharsets.UTF_8);
+ }
+
+ @Override
+ public ByteBuffer readBinary() throws TException {
+ return ByteBuffer.wrap(readJSONBase64());
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TList.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TList.java
new file mode 100644
index 000000000..0d36e83d9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TList.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Helper class that encapsulates list metadata.
+ *
+ */
+public final class TList {
+ public TList() {
+ this(TType.STOP, 0);
+ }
+
+ public TList(byte t, int s) {
+ elemType = t;
+ size = s;
+ }
+
+ public final byte elemType;
+ public final int size;
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMap.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMap.java
new file mode 100644
index 000000000..20881f7ac
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMap.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Helper class that encapsulates map metadata.
+ *
+ */
+public final class TMap {
+ public TMap() {
+ this(TType.STOP, TType.STOP, 0);
+ }
+
+ public TMap(byte k, byte v, int s) {
+ keyType = k;
+ valueType = v;
+ size = s;
+ }
+
+ public final byte keyType;
+ public final byte valueType;
+ public final int size;
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMessage.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMessage.java
new file mode 100644
index 000000000..f13b8ca50
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMessage.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Helper class that encapsulates struct metadata.
+ *
+ */
+public final class TMessage {
+ public TMessage() {
+ this("", TType.STOP, 0);
+ }
+
+ public TMessage(String n, byte t, int s) {
+ name = n;
+ type = t;
+ seqid = s;
+ }
+
+ public final String name;
+ public final byte type;
+ public final int seqid;
+
+ @Override
+ public String toString() {
+ return "<TMessage name:'" + name + "' type: " + type + " seqid:" + seqid + ">";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + seqid;
+ result = prime * result + type;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ TMessage other = (TMessage) obj;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (seqid != other.seqid)
+ return false;
+ if (type != other.type)
+ return false;
+ return true;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMessageType.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMessageType.java
new file mode 100644
index 000000000..aa3f93177
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMessageType.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Message type constants in the Thrift protocol.
+ *
+ */
+public final class TMessageType {
+ public static final byte CALL = 1;
+ public static final byte REPLY = 2;
+ public static final byte EXCEPTION = 3;
+ public static final byte ONEWAY = 4;
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMultiplexedProtocol.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMultiplexedProtocol.java
new file mode 100644
index 000000000..0ea566ba6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMultiplexedProtocol.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import org.apache.thrift.TException;
+
+/**
+ * <code>TMultiplexedProtocol</code> is a protocol-independent concrete decorator
+ * that allows a Thrift client to communicate with a multiplexing Thrift server,
+ * by prepending the service name to the function name during function calls.
+ *
+ * <p>NOTE: THIS IS NOT USED BY SERVERS. On the server, use {@link org.apache.thrift.TMultiplexedProcessor TMultiplexedProcessor} to handle requests
+ * from a multiplexing client.
+ *
+ * <p>This example uses a single socket transport to invoke two services:
+ *
+ * <pre>
+ * {@code
+ * TSocket transport = new TSocket("localhost", 9090);
+ * transport.open();
+ *
+ * TBinaryProtocol protocol = new TBinaryProtocol(transport);
+ *
+ * TMultiplexedProtocol mp = new TMultiplexedProtocol(protocol, "Calculator");
+ * Calculator.Client service = new Calculator.Client(mp);
+ *
+ * TMultiplexedProtocol mp2 = new TMultiplexedProtocol(protocol, "WeatherReport");
+ * WeatherReport.Client service2 = new WeatherReport.Client(mp2);
+ *
+ * System.out.println(service.add(2,2));
+ * System.out.println(service2.getTemperature());
+ * }
+ * </pre>
+ *
+ * @see org.apache.thrift.protocol.TProtocolDecorator
+ */
+public class TMultiplexedProtocol extends TProtocolDecorator {
+
+ /** Used to delimit the service name from the function name */
+ public static final String SEPARATOR = ":";
+
+ private final String SERVICE_NAME;
+
+ /**
+ * Wrap the specified protocol, allowing it to be used to communicate with a
+ * multiplexing server. The <code>serviceName</code> is required as it is
+ * prepended to the message header so that the multiplexing server can broker
+ * the function call to the proper service.
+ *
+ * @param protocol Your communication protocol of choice, e.g. <code>TBinaryProtocol</code>.
+ * @param serviceName The service name of the service communicating via this protocol.
+ */
+ public TMultiplexedProtocol(TProtocol protocol, String serviceName) {
+ super(protocol);
+ SERVICE_NAME = serviceName;
+ }
+
+ /**
+ * Prepends the service name to the function name, separated by TMultiplexedProtocol.SEPARATOR.
+ *
+ * @param tMessage The original message.
+ * @throws TException Passed through from wrapped <code>TProtocol</code> instance.
+ */
+ @Override
+ public void writeMessageBegin(TMessage tMessage) throws TException {
+ if (tMessage.type == TMessageType.CALL || tMessage.type == TMessageType.ONEWAY) {
+ super.writeMessageBegin(new TMessage(
+ SERVICE_NAME + SEPARATOR + tMessage.name,
+ tMessage.type,
+ tMessage.seqid
+ ));
+ } else {
+ super.writeMessageBegin(tMessage);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocol.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocol.java
new file mode 100644
index 000000000..0e96368d4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocol.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import java.nio.ByteBuffer;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.scheme.IScheme;
+import org.apache.thrift.scheme.StandardScheme;
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * Protocol interface definition.
+ *
+ */
+public abstract class TProtocol {
+
+ /**
+ * Prevent direct instantiation
+ */
+ @SuppressWarnings("unused")
+ private TProtocol() {}
+
+ /**
+ * Transport
+ */
+ protected TTransport trans_;
+
+ /**
+ * Constructor
+ */
+ protected TProtocol(TTransport trans) {
+ trans_ = trans;
+ }
+
+ /**
+ * Transport accessor
+ */
+ public TTransport getTransport() {
+ return trans_;
+ }
+
+ /**
+ * Writing methods.
+ */
+
+ public abstract void writeMessageBegin(TMessage message) throws TException;
+
+ public abstract void writeMessageEnd() throws TException;
+
+ public abstract void writeStructBegin(TStruct struct) throws TException;
+
+ public abstract void writeStructEnd() throws TException;
+
+ public abstract void writeFieldBegin(TField field) throws TException;
+
+ public abstract void writeFieldEnd() throws TException;
+
+ public abstract void writeFieldStop() throws TException;
+
+ public abstract void writeMapBegin(TMap map) throws TException;
+
+ public abstract void writeMapEnd() throws TException;
+
+ public abstract void writeListBegin(TList list) throws TException;
+
+ public abstract void writeListEnd() throws TException;
+
+ public abstract void writeSetBegin(TSet set) throws TException;
+
+ public abstract void writeSetEnd() throws TException;
+
+ public abstract void writeBool(boolean b) throws TException;
+
+ public abstract void writeByte(byte b) throws TException;
+
+ public abstract void writeI16(short i16) throws TException;
+
+ public abstract void writeI32(int i32) throws TException;
+
+ public abstract void writeI64(long i64) throws TException;
+
+ public abstract void writeDouble(double dub) throws TException;
+
+ public abstract void writeString(String str) throws TException;
+
+ public abstract void writeBinary(ByteBuffer buf) throws TException;
+
+ /**
+ * Reading methods.
+ */
+
+ public abstract TMessage readMessageBegin() throws TException;
+
+ public abstract void readMessageEnd() throws TException;
+
+ public abstract TStruct readStructBegin() throws TException;
+
+ public abstract void readStructEnd() throws TException;
+
+ public abstract TField readFieldBegin() throws TException;
+
+ public abstract void readFieldEnd() throws TException;
+
+ public abstract TMap readMapBegin() throws TException;
+
+ public abstract void readMapEnd() throws TException;
+
+ public abstract TList readListBegin() throws TException;
+
+ public abstract void readListEnd() throws TException;
+
+ public abstract TSet readSetBegin() throws TException;
+
+ public abstract void readSetEnd() throws TException;
+
+ public abstract boolean readBool() throws TException;
+
+ public abstract byte readByte() throws TException;
+
+ public abstract short readI16() throws TException;
+
+ public abstract int readI32() throws TException;
+
+ public abstract long readI64() throws TException;
+
+ public abstract double readDouble() throws TException;
+
+ public abstract String readString() throws TException;
+
+ public abstract ByteBuffer readBinary() throws TException;
+
+ /**
+ * Reset any internal state back to a blank slate. This method only needs to
+ * be implemented for stateful protocols.
+ */
+ public void reset() {}
+
+ /**
+ * Scheme accessor
+ */
+ public Class<? extends IScheme> getScheme() {
+ return StandardScheme.class;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolDecorator.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolDecorator.java
new file mode 100644
index 000000000..2d29cd231
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolDecorator.java
@@ -0,0 +1,213 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import org.apache.thrift.TException;
+
+import java.nio.ByteBuffer;
+
+/**
+ * <code>TProtocolDecorator</code> forwards all requests to an enclosed
+ * <code>TProtocol</code> instance, providing a way to author concise
+ * concrete decorator subclasses. While it has no abstract methods, it
+ * is marked abstract as a reminder that by itself, it does not modify
+ * the behaviour of the enclosed <code>TProtocol</code>.
+ *
+ * <p>See p.175 of Design Patterns (by Gamma et al.)</p>
+ *
+ * @see org.apache.thrift.protocol.TMultiplexedProtocol
+ */
+public abstract class TProtocolDecorator extends TProtocol {
+
+ private final TProtocol concreteProtocol;
+
+ /**
+ * Encloses the specified protocol.
+ * @param protocol All operations will be forward to this protocol. Must be non-null.
+ */
+ public TProtocolDecorator(TProtocol protocol) {
+ super(protocol.getTransport());
+ concreteProtocol = protocol;
+ }
+
+ public void writeMessageBegin(TMessage tMessage) throws TException {
+ concreteProtocol.writeMessageBegin(tMessage);
+ }
+
+ public void writeMessageEnd() throws TException {
+ concreteProtocol.writeMessageEnd();
+ }
+
+ public void writeStructBegin(TStruct tStruct) throws TException {
+ concreteProtocol.writeStructBegin(tStruct);
+ }
+
+ public void writeStructEnd() throws TException {
+ concreteProtocol.writeStructEnd();
+ }
+
+ public void writeFieldBegin(TField tField) throws TException {
+ concreteProtocol.writeFieldBegin(tField);
+ }
+
+ public void writeFieldEnd() throws TException {
+ concreteProtocol.writeFieldEnd();
+ }
+
+ public void writeFieldStop() throws TException {
+ concreteProtocol.writeFieldStop();
+ }
+
+ public void writeMapBegin(TMap tMap) throws TException {
+ concreteProtocol.writeMapBegin(tMap);
+ }
+
+ public void writeMapEnd() throws TException {
+ concreteProtocol.writeMapEnd();
+ }
+
+ public void writeListBegin(TList tList) throws TException {
+ concreteProtocol.writeListBegin(tList);
+ }
+
+ public void writeListEnd() throws TException {
+ concreteProtocol.writeListEnd();
+ }
+
+ public void writeSetBegin(TSet tSet) throws TException {
+ concreteProtocol.writeSetBegin(tSet);
+ }
+
+ public void writeSetEnd() throws TException {
+ concreteProtocol.writeSetEnd();
+ }
+
+ public void writeBool(boolean b) throws TException {
+ concreteProtocol.writeBool(b);
+ }
+
+ public void writeByte(byte b) throws TException {
+ concreteProtocol.writeByte(b);
+ }
+
+ public void writeI16(short i) throws TException {
+ concreteProtocol.writeI16(i);
+ }
+
+ public void writeI32(int i) throws TException {
+ concreteProtocol.writeI32(i);
+ }
+
+ public void writeI64(long l) throws TException {
+ concreteProtocol.writeI64(l);
+ }
+
+ public void writeDouble(double v) throws TException {
+ concreteProtocol.writeDouble(v);
+ }
+
+ public void writeString(String s) throws TException {
+ concreteProtocol.writeString(s);
+ }
+
+ public void writeBinary(ByteBuffer buf) throws TException {
+ concreteProtocol.writeBinary(buf);
+ }
+
+ public TMessage readMessageBegin() throws TException {
+ return concreteProtocol.readMessageBegin();
+ }
+
+ public void readMessageEnd() throws TException {
+ concreteProtocol.readMessageEnd();
+ }
+
+ public TStruct readStructBegin() throws TException {
+ return concreteProtocol.readStructBegin();
+ }
+
+ public void readStructEnd() throws TException {
+ concreteProtocol.readStructEnd();
+ }
+
+ public TField readFieldBegin() throws TException {
+ return concreteProtocol.readFieldBegin();
+ }
+
+ public void readFieldEnd() throws TException {
+ concreteProtocol.readFieldEnd();
+ }
+
+ public TMap readMapBegin() throws TException {
+ return concreteProtocol.readMapBegin();
+ }
+
+ public void readMapEnd() throws TException {
+ concreteProtocol.readMapEnd();
+ }
+
+ public TList readListBegin() throws TException {
+ return concreteProtocol.readListBegin();
+ }
+
+ public void readListEnd() throws TException {
+ concreteProtocol.readListEnd();
+ }
+
+ public TSet readSetBegin() throws TException {
+ return concreteProtocol.readSetBegin();
+ }
+
+ public void readSetEnd() throws TException {
+ concreteProtocol.readSetEnd();
+ }
+
+ public boolean readBool() throws TException {
+ return concreteProtocol.readBool();
+ }
+
+ public byte readByte() throws TException {
+ return concreteProtocol.readByte();
+ }
+
+ public short readI16() throws TException {
+ return concreteProtocol.readI16();
+ }
+
+ public int readI32() throws TException {
+ return concreteProtocol.readI32();
+ }
+
+ public long readI64() throws TException {
+ return concreteProtocol.readI64();
+ }
+
+ public double readDouble() throws TException {
+ return concreteProtocol.readDouble();
+ }
+
+ public String readString() throws TException {
+ return concreteProtocol.readString();
+ }
+
+ public ByteBuffer readBinary() throws TException {
+ return concreteProtocol.readBinary();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolException.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolException.java
new file mode 100644
index 000000000..870f1b939
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolException.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import org.apache.thrift.TException;
+
+/**
+ * Protocol exceptions.
+ *
+ */
+public class TProtocolException extends TException {
+
+
+ private static final long serialVersionUID = 1L;
+ public static final int UNKNOWN = 0;
+ public static final int INVALID_DATA = 1;
+ public static final int NEGATIVE_SIZE = 2;
+ public static final int SIZE_LIMIT = 3;
+ public static final int BAD_VERSION = 4;
+ public static final int NOT_IMPLEMENTED = 5;
+ public static final int DEPTH_LIMIT = 6;
+
+ protected int type_ = UNKNOWN;
+
+ public TProtocolException() {
+ super();
+ }
+
+ public TProtocolException(int type) {
+ super();
+ type_ = type;
+ }
+
+ public TProtocolException(int type, String message) {
+ super(message);
+ type_ = type;
+ }
+
+ public TProtocolException(String message) {
+ super(message);
+ }
+
+ public TProtocolException(int type, Throwable cause) {
+ super(cause);
+ type_ = type;
+ }
+
+ public TProtocolException(Throwable cause) {
+ super(cause);
+ }
+
+ public TProtocolException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public TProtocolException(int type, String message, Throwable cause) {
+ super(message, cause);
+ type_ = type;
+ }
+
+ public int getType() {
+ return type_;
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolFactory.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolFactory.java
new file mode 100644
index 000000000..b72e87b38
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolFactory.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import java.io.Serializable;
+
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * Factory interface for constructing protocol instances.
+ */
+public interface TProtocolFactory extends Serializable {
+ public TProtocol getProtocol(TTransport trans);
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolUtil.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolUtil.java
new file mode 100644
index 000000000..cdaa30b87
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolUtil.java
@@ -0,0 +1,221 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import org.apache.thrift.TException;
+
+/**
+ * Utility class with static methods for interacting with protocol data
+ * streams.
+ *
+ */
+public class TProtocolUtil {
+
+ /**
+ * The maximum recursive depth the skip() function will traverse before
+ * throwing a TException.
+ */
+ private static int maxSkipDepth = Integer.MAX_VALUE;
+
+ /**
+ * Specifies the maximum recursive depth that the skip function will
+ * traverse before throwing a TException. This is a global setting, so
+ * any call to skip in this JVM will enforce this value.
+ *
+ * @param depth the maximum recursive depth. A value of 2 would allow
+ * the skip function to skip a structure or collection with basic children,
+ * but it would not permit skipping a struct that had a field containing
+ * a child struct. A value of 1 would only allow skipping of simple
+ * types and empty structs/collections.
+ */
+ public static void setMaxSkipDepth(int depth) {
+ maxSkipDepth = depth;
+ }
+
+ /**
+ * Skips over the next data element from the provided input TProtocol object.
+ *
+ * @param prot the protocol object to read from
+ * @param type the next value will be interpreted as this TType value.
+ */
+ public static void skip(TProtocol prot, byte type)
+ throws TException {
+ skip(prot, type, maxSkipDepth);
+ }
+
+ /**
+ * Skips over the next data element from the provided input TProtocol object.
+ *
+ * @param prot the protocol object to read from
+ * @param type the next value will be interpreted as this TType value.
+ * @param maxDepth this function will only skip complex objects to this
+ * recursive depth, to prevent Java stack overflow.
+ */
+ public static void skip(TProtocol prot, byte type, int maxDepth)
+ throws TException {
+ if (maxDepth <= 0) {
+ throw new TException("Maximum skip depth exceeded");
+ }
+ switch (type) {
+ case TType.BOOL:
+ prot.readBool();
+ break;
+
+ case TType.BYTE:
+ prot.readByte();
+ break;
+
+ case TType.I16:
+ prot.readI16();
+ break;
+
+ case TType.I32:
+ prot.readI32();
+ break;
+
+ case TType.I64:
+ prot.readI64();
+ break;
+
+ case TType.DOUBLE:
+ prot.readDouble();
+ break;
+
+ case TType.STRING:
+ prot.readBinary();
+ break;
+
+ case TType.STRUCT:
+ prot.readStructBegin();
+ while (true) {
+ TField field = prot.readFieldBegin();
+ if (field.type == TType.STOP) {
+ break;
+ }
+ skip(prot, field.type, maxDepth - 1);
+ prot.readFieldEnd();
+ }
+ prot.readStructEnd();
+ break;
+
+ case TType.MAP:
+ TMap map = prot.readMapBegin();
+ for (int i = 0; i < map.size; i++) {
+ skip(prot, map.keyType, maxDepth - 1);
+ skip(prot, map.valueType, maxDepth - 1);
+ }
+ prot.readMapEnd();
+ break;
+
+ case TType.SET:
+ TSet set = prot.readSetBegin();
+ for (int i = 0; i < set.size; i++) {
+ skip(prot, set.elemType, maxDepth - 1);
+ }
+ prot.readSetEnd();
+ break;
+
+ case TType.LIST:
+ TList list = prot.readListBegin();
+ for (int i = 0; i < list.size; i++) {
+ skip(prot, list.elemType, maxDepth - 1);
+ }
+ prot.readListEnd();
+ break;
+
+ default:
+ throw new TProtocolException(TProtocolException.INVALID_DATA,
+ "Unrecognized type " + type);
+ }
+ }
+
+ /**
+ * Attempt to determine the protocol used to serialize some data.
+ *
+ * The guess is based on known specificities of supported protocols.
+ * In some cases, no guess can be done, in that case we return the
+ * fallback TProtocolFactory.
+ * To be certain to correctly detect the protocol, the first encoded
+ * field should have a field id &lt; 256
+ *
+ * @param data The serialized data to guess the protocol for.
+ * @param fallback The TProtocol to return if no guess can be made.
+ * @return a Class implementing TProtocolFactory which can be used to create a deserializer.
+ */
+ public static TProtocolFactory guessProtocolFactory(byte[] data, TProtocolFactory fallback) {
+ //
+ // If the first and last bytes are opening/closing curly braces we guess the protocol as
+ // being TJSONProtocol.
+ // It could not be a TCompactBinary encoding for a field of type 0xb (Map)
+ // with delta id 7 as the last byte for TCompactBinary is always 0.
+ //
+
+ if ('{' == data[0] && '}' == data[data.length - 1]) {
+ return new TJSONProtocol.Factory();
+ }
+
+ //
+ // If the last byte is not 0, then it cannot be TCompactProtocol, it must be
+ // TBinaryProtocol.
+ //
+
+ if (data[data.length - 1] != 0) {
+ return new TBinaryProtocol.Factory();
+ }
+
+ //
+ // A first byte of value > 16 indicates TCompactProtocol was used, and the first byte
+ // encodes a delta field id (id <= 15) and a field type.
+ //
+
+ if (data[0] > 0x10) {
+ return new TCompactProtocol.Factory();
+ }
+
+ //
+ // If the second byte is 0 then it is a field id < 256 encoded by TBinaryProtocol.
+ // It cannot possibly be TCompactProtocol since a value of 0 would imply a field id
+ // of 0 as the zig zag varint encoding would end.
+ //
+
+ if (data.length > 1 && 0 == data[1]) {
+ return new TBinaryProtocol.Factory();
+ }
+
+ //
+ // If bit 7 of the first byte of the field id is set then we have two choices:
+ // 1. A field id > 63 was encoded with TCompactProtocol.
+ // 2. A field id > 0x7fff (32767) was encoded with TBinaryProtocol and the last byte of the
+ // serialized data is 0.
+ // Option 2 is impossible since field ids are short and thus limited to 32767.
+ //
+
+ if (data.length > 1 && (data[1] & 0x80) != 0) {
+ return new TCompactProtocol.Factory();
+ }
+
+ //
+ // The remaining case is either a field id <= 63 encoded as TCompactProtocol,
+ // one >= 256 encoded with TBinaryProtocol with a last byte at 0, or an empty structure.
+ // As we cannot really decide, we return the fallback protocol.
+ //
+ return fallback;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TSet.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TSet.java
new file mode 100644
index 000000000..38be9a991
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TSet.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Helper class that encapsulates set metadata.
+ *
+ */
+public final class TSet {
+ public TSet() {
+ this(TType.STOP, 0);
+ }
+
+ public TSet(byte t, int s) {
+ elemType = t;
+ size = s;
+ }
+
+ public TSet(TList list) {
+ this(list.elemType, list.size);
+ }
+
+ public final byte elemType;
+ public final int size;
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TSimpleJSONProtocol.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TSimpleJSONProtocol.java
new file mode 100644
index 000000000..eb7e23bf9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TSimpleJSONProtocol.java
@@ -0,0 +1,483 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Stack;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * JSON protocol implementation for thrift.
+ *
+ * This protocol is write-only and produces a simple output format
+ * suitable for parsing by scripting languages. It should not be
+ * confused with the full-featured TJSONProtocol.
+ *
+ */
+public class TSimpleJSONProtocol extends TProtocol {
+
+ /**
+ * Factory
+ */
+ public static class Factory implements TProtocolFactory {
+ public TProtocol getProtocol(TTransport trans) {
+ return new TSimpleJSONProtocol(trans);
+ }
+ }
+
+ private static final byte[] COMMA = new byte[] {','};
+ private static final byte[] COLON = new byte[] {':'};
+ private static final byte[] LBRACE = new byte[] {'{'};
+ private static final byte[] RBRACE = new byte[] {'}'};
+ private static final byte[] LBRACKET = new byte[] {'['};
+ private static final byte[] RBRACKET = new byte[] {']'};
+ private static final char QUOTE = '"';
+
+ private static final TStruct ANONYMOUS_STRUCT = new TStruct();
+ private static final TField ANONYMOUS_FIELD = new TField();
+ private static final TMessage EMPTY_MESSAGE = new TMessage();
+ private static final TSet EMPTY_SET = new TSet();
+ private static final TList EMPTY_LIST = new TList();
+ private static final TMap EMPTY_MAP = new TMap();
+ private static final String LIST = "list";
+ private static final String SET = "set";
+ private static final String MAP = "map";
+
+ protected class Context {
+ protected void write() throws TException {}
+
+ /**
+ * Returns whether the current value is a key in a map
+ */
+ protected boolean isMapKey() { return false; }
+ }
+
+ protected class ListContext extends Context {
+ protected boolean first_ = true;
+
+ protected void write() throws TException {
+ if (first_) {
+ first_ = false;
+ } else {
+ trans_.write(COMMA);
+ }
+ }
+ }
+
+ protected class StructContext extends Context {
+ protected boolean first_ = true;
+ protected boolean colon_ = true;
+
+ protected void write() throws TException {
+ if (first_) {
+ first_ = false;
+ colon_ = true;
+ } else {
+ trans_.write(colon_ ? COLON : COMMA);
+ colon_ = !colon_;
+ }
+ }
+ }
+
+ protected class MapContext extends StructContext {
+ protected boolean isKey = true;
+
+ @Override
+ protected void write() throws TException {
+ super.write();
+ isKey = !isKey;
+ }
+
+ protected boolean isMapKey() {
+ // we want to coerce map keys to json strings regardless
+ // of their type
+ return isKey;
+ }
+ }
+
+ protected final Context BASE_CONTEXT = new Context();
+
+ /**
+ * Stack of nested contexts that we may be in.
+ */
+ protected Stack<Context> writeContextStack_ = new Stack<Context>();
+
+ /**
+ * Current context that we are in
+ */
+ protected Context writeContext_ = BASE_CONTEXT;
+
+ /**
+ * Push a new write context onto the stack.
+ */
+ protected void pushWriteContext(Context c) {
+ writeContextStack_.push(writeContext_);
+ writeContext_ = c;
+ }
+
+ /**
+ * Pop the last write context off the stack
+ */
+ protected void popWriteContext() {
+ writeContext_ = writeContextStack_.pop();
+ }
+
+ /**
+ * Reset the write context stack to its initial state.
+ */
+ protected void resetWriteContext() {
+ while (!writeContextStack_.isEmpty()) {
+ popWriteContext();
+ }
+ }
+
+ /**
+ * Used to make sure that we are not encountering a map whose keys are containers
+ */
+ protected void assertContextIsNotMapKey(String invalidKeyType) throws CollectionMapKeyException {
+ if (writeContext_.isMapKey()) {
+ throw new CollectionMapKeyException("Cannot serialize a map with keys that are of type " + invalidKeyType);
+ }
+ }
+
+ /**
+ * Constructor
+ */
+ public TSimpleJSONProtocol(TTransport trans) {
+ super(trans);
+ }
+
+ @Override
+ public void writeMessageBegin(TMessage message) throws TException {
+ resetWriteContext(); // THRIFT-3743
+ trans_.write(LBRACKET);
+ pushWriteContext(new ListContext());
+ writeString(message.name);
+ writeByte(message.type);
+ writeI32(message.seqid);
+ }
+
+ @Override
+ public void writeMessageEnd() throws TException {
+ popWriteContext();
+ trans_.write(RBRACKET);
+ }
+
+ @Override
+ public void writeStructBegin(TStruct struct) throws TException {
+ writeContext_.write();
+ trans_.write(LBRACE);
+ pushWriteContext(new StructContext());
+ }
+
+ @Override
+ public void writeStructEnd() throws TException {
+ popWriteContext();
+ trans_.write(RBRACE);
+ }
+
+ @Override
+ public void writeFieldBegin(TField field) throws TException {
+ // Note that extra type information is omitted in JSON!
+ writeString(field.name);
+ }
+
+ @Override
+ public void writeFieldEnd() throws TException {}
+
+ @Override
+ public void writeFieldStop() throws TException {}
+
+ @Override
+ public void writeMapBegin(TMap map) throws TException {
+ assertContextIsNotMapKey(MAP);
+ writeContext_.write();
+ trans_.write(LBRACE);
+ pushWriteContext(new MapContext());
+ // No metadata!
+ }
+
+ @Override
+ public void writeMapEnd() throws TException {
+ popWriteContext();
+ trans_.write(RBRACE);
+ }
+
+ @Override
+ public void writeListBegin(TList list) throws TException {
+ assertContextIsNotMapKey(LIST);
+ writeContext_.write();
+ trans_.write(LBRACKET);
+ pushWriteContext(new ListContext());
+ // No metadata!
+ }
+
+ @Override
+ public void writeListEnd() throws TException {
+ popWriteContext();
+ trans_.write(RBRACKET);
+ }
+
+ @Override
+ public void writeSetBegin(TSet set) throws TException {
+ assertContextIsNotMapKey(SET);
+ writeContext_.write();
+ trans_.write(LBRACKET);
+ pushWriteContext(new ListContext());
+ // No metadata!
+ }
+
+ @Override
+ public void writeSetEnd() throws TException {
+ popWriteContext();
+ trans_.write(RBRACKET);
+ }
+
+ @Override
+ public void writeBool(boolean b) throws TException {
+ writeByte(b ? (byte)1 : (byte)0);
+ }
+
+ @Override
+ public void writeByte(byte b) throws TException {
+ writeI32(b);
+ }
+
+ @Override
+ public void writeI16(short i16) throws TException {
+ writeI32(i16);
+ }
+
+ @Override
+ public void writeI32(int i32) throws TException {
+ if(writeContext_.isMapKey()) {
+ writeString(Integer.toString(i32));
+ } else {
+ writeContext_.write();
+ _writeStringData(Integer.toString(i32));
+ }
+ }
+
+ public void _writeStringData(String s) throws TException {
+ byte[] b = s.getBytes(StandardCharsets.UTF_8);
+ trans_.write(b);
+ }
+
+ @Override
+ public void writeI64(long i64) throws TException {
+ if(writeContext_.isMapKey()) {
+ writeString(Long.toString(i64));
+ } else {
+ writeContext_.write();
+ _writeStringData(Long.toString(i64));
+ }
+ }
+
+ @Override
+ public void writeDouble(double dub) throws TException {
+ if(writeContext_.isMapKey()) {
+ writeString(Double.toString(dub));
+ } else {
+ writeContext_.write();
+ _writeStringData(Double.toString(dub));
+ }
+ }
+
+ @Override
+ public void writeString(String str) throws TException {
+ writeContext_.write();
+ int length = str.length();
+ StringBuffer escape = new StringBuffer(length + 16);
+ escape.append(QUOTE);
+ for (int i = 0; i < length; ++i) {
+ char c = str.charAt(i);
+ switch (c) {
+ case '"':
+ case '\\':
+ escape.append('\\');
+ escape.append(c);
+ break;
+ case '\b':
+ escape.append('\\');
+ escape.append('b');
+ break;
+ case '\f':
+ escape.append('\\');
+ escape.append('f');
+ break;
+ case '\n':
+ escape.append('\\');
+ escape.append('n');
+ break;
+ case '\r':
+ escape.append('\\');
+ escape.append('r');
+ break;
+ case '\t':
+ escape.append('\\');
+ escape.append('t');
+ break;
+ default:
+ // Control characters! According to JSON RFC u0020 (space)
+ if (c < ' ') {
+ String hex = Integer.toHexString(c);
+ escape.append('\\');
+ escape.append('u');
+ for (int j = 4; j > hex.length(); --j) {
+ escape.append('0');
+ }
+ escape.append(hex);
+ } else {
+ escape.append(c);
+ }
+ break;
+ }
+ }
+ escape.append(QUOTE);
+ _writeStringData(escape.toString());
+ }
+
+ @Override
+ public void writeBinary(ByteBuffer bin) throws TException {
+ // TODO(mcslee): Fix this
+ writeString(new String(bin.array(), bin.position() + bin.arrayOffset(),
+ bin.limit() - bin.position() - bin.arrayOffset(),
+ StandardCharsets.UTF_8));
+ }
+
+ /**
+ * Reading methods.
+ */
+
+ @Override
+ public TMessage readMessageBegin() throws TException {
+ // TODO(mcslee): implement
+ return EMPTY_MESSAGE;
+ }
+
+ @Override
+ public void readMessageEnd() throws TException {}
+
+ @Override
+ public TStruct readStructBegin() throws TException {
+ // TODO(mcslee): implement
+ return ANONYMOUS_STRUCT;
+ }
+
+ @Override
+ public void readStructEnd() throws TException {}
+
+ @Override
+ public TField readFieldBegin() throws TException {
+ // TODO(mcslee): implement
+ return ANONYMOUS_FIELD;
+ }
+
+ @Override
+ public void readFieldEnd() throws TException {}
+
+ @Override
+ public TMap readMapBegin() throws TException {
+ // TODO(mcslee): implement
+ return EMPTY_MAP;
+ }
+
+ @Override
+ public void readMapEnd() throws TException {}
+
+ @Override
+ public TList readListBegin() throws TException {
+ // TODO(mcslee): implement
+ return EMPTY_LIST;
+ }
+
+ @Override
+ public void readListEnd() throws TException {}
+
+ @Override
+ public TSet readSetBegin() throws TException {
+ // TODO(mcslee): implement
+ return EMPTY_SET;
+ }
+
+ @Override
+ public void readSetEnd() throws TException {}
+
+ @Override
+ public boolean readBool() throws TException {
+ return (readByte() == 1);
+ }
+
+ @Override
+ public byte readByte() throws TException {
+ // TODO(mcslee): implement
+ return 0;
+ }
+
+ @Override
+ public short readI16() throws TException {
+ // TODO(mcslee): implement
+ return 0;
+ }
+
+ @Override
+ public int readI32() throws TException {
+ // TODO(mcslee): implement
+ return 0;
+ }
+
+ @Override
+ public long readI64() throws TException {
+ // TODO(mcslee): implement
+ return 0;
+ }
+
+ @Override
+ public double readDouble() throws TException {
+ // TODO(mcslee): implement
+ return 0;
+ }
+
+ @Override
+ public String readString() throws TException {
+ // TODO(mcslee): implement
+ return "";
+ }
+
+ public String readStringBody(int size) throws TException {
+ // TODO(mcslee): implement
+ return "";
+ }
+
+ @Override
+ public ByteBuffer readBinary() throws TException {
+ // TODO(mcslee): implement
+ return ByteBuffer.wrap(new byte[0]);
+ }
+
+ public static class CollectionMapKeyException extends TException {
+ public CollectionMapKeyException(String message) {
+ super(message);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TStruct.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TStruct.java
new file mode 100644
index 000000000..a0f79012a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TStruct.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Helper class that encapsulates struct metadata.
+ *
+ */
+public final class TStruct {
+ public TStruct() {
+ this("");
+ }
+
+ public TStruct(String n) {
+ name = n;
+ }
+
+ public final String name;
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TTupleProtocol.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TTupleProtocol.java
new file mode 100644
index 000000000..74f5226c8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TTupleProtocol.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.protocol;
+
+import java.util.BitSet;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.scheme.IScheme;
+import org.apache.thrift.scheme.TupleScheme;
+import org.apache.thrift.transport.TTransport;
+
+public final class TTupleProtocol extends TCompactProtocol {
+ public static class Factory implements TProtocolFactory {
+ public Factory() {}
+
+ public TProtocol getProtocol(TTransport trans) {
+ return new TTupleProtocol(trans);
+ }
+ }
+
+ public TTupleProtocol(TTransport transport) {
+ super(transport);
+ }
+
+ @Override
+ public Class<? extends IScheme> getScheme() {
+ return TupleScheme.class;
+ }
+
+ public void writeBitSet(BitSet bs, int vectorWidth) throws TException {
+ byte[] bytes = toByteArray(bs, vectorWidth);
+ for (byte b : bytes) {
+ writeByte(b);
+ }
+ }
+
+ public BitSet readBitSet(int i) throws TException {
+ int length = (int) Math.ceil(i/8.0);
+ byte[] bytes = new byte[length];
+ for (int j = 0; j < length; j++) {
+ bytes[j] = readByte();
+ }
+ BitSet bs = fromByteArray(bytes);
+ return bs;
+ }
+
+ /**
+ * Returns a bitset containing the values in bytes. The byte-ordering must be
+ * big-endian.
+ */
+ public static BitSet fromByteArray(byte[] bytes) {
+ BitSet bits = new BitSet();
+ for (int i = 0; i < bytes.length * 8; i++) {
+ if ((bytes[bytes.length - i / 8 - 1] & (1 << (i % 8))) > 0) {
+ bits.set(i);
+ }
+ }
+ return bits;
+ }
+
+ /**
+ * Returns a byte array of at least length 1. The most significant bit in the
+ * result is guaranteed not to be a 1 (since BitSet does not support sign
+ * extension). The byte-ordering of the result is big-endian which means the
+ * most significant bit is in element 0. The bit at index 0 of the bit set is
+ * assumed to be the least significant bit.
+ *
+ * @param bits
+ * @param vectorWidth
+ * @return a byte array of at least length 1
+ */
+ public static byte[] toByteArray(BitSet bits, int vectorWidth) {
+ byte[] bytes = new byte[(int) Math.ceil(vectorWidth/8.0)];
+ for (int i = 0; i < bits.length(); i++) {
+ if (bits.get(i)) {
+ bytes[bytes.length - i / 8 - 1] |= 1 << (i % 8);
+ }
+ }
+ return bytes;
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TType.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TType.java
new file mode 100644
index 000000000..c3c1a0abd
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TType.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.protocol;
+
+/**
+ * Type constants in the Thrift protocol.
+ */
+public final class TType {
+ public static final byte STOP = 0;
+ public static final byte VOID = 1;
+ public static final byte BOOL = 2;
+ public static final byte BYTE = 3;
+ public static final byte DOUBLE = 4;
+ public static final byte I16 = 6;
+ public static final byte I32 = 8;
+ public static final byte I64 = 10;
+ public static final byte STRING = 11;
+ public static final byte STRUCT = 12;
+ public static final byte MAP = 13;
+ public static final byte SET = 14;
+ public static final byte LIST = 15;
+ public static final byte ENUM = 16;
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/IScheme.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/IScheme.java
new file mode 100644
index 000000000..aa3550705
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/IScheme.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.scheme;
+
+import org.apache.thrift.TBase;
+
+public interface IScheme<T extends TBase> {
+
+ public void read(org.apache.thrift.protocol.TProtocol iproto, T struct) throws org.apache.thrift.TException;
+
+ public void write(org.apache.thrift.protocol.TProtocol oproto, T struct) throws org.apache.thrift.TException;
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/SchemeFactory.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/SchemeFactory.java
new file mode 100644
index 000000000..006a66805
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/SchemeFactory.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.scheme;
+
+public interface SchemeFactory {
+
+ public <S extends IScheme> S getScheme();
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/StandardScheme.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/StandardScheme.java
new file mode 100644
index 000000000..ffab04db6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/StandardScheme.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.scheme;
+
+import org.apache.thrift.TBase;
+
+public abstract class StandardScheme<T extends TBase> implements IScheme<T> {
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/TupleScheme.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/TupleScheme.java
new file mode 100644
index 000000000..365242b11
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/TupleScheme.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.scheme;
+
+import org.apache.thrift.TBase;
+
+public abstract class TupleScheme<T extends TBase> implements IScheme<T> {
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/AbstractNonblockingServer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/AbstractNonblockingServer.java
new file mode 100644
index 000000000..8c206e427
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/AbstractNonblockingServer.java
@@ -0,0 +1,618 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.server;
+
+import org.apache.thrift.TAsyncProcessor;
+import org.apache.thrift.TByteArrayOutputStream;
+import org.apache.thrift.TException;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.transport.TFramedTransport;
+import org.apache.thrift.transport.TIOStreamTransport;
+import org.apache.thrift.transport.TMemoryInputTransport;
+import org.apache.thrift.transport.TNonblockingServerTransport;
+import org.apache.thrift.transport.TNonblockingTransport;
+import org.apache.thrift.transport.TTransport;
+import org.apache.thrift.transport.TTransportException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.spi.SelectorProvider;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Provides common methods and classes used by nonblocking TServer
+ * implementations.
+ */
+public abstract class AbstractNonblockingServer extends TServer {
+ protected final Logger LOGGER = LoggerFactory.getLogger(getClass().getName());
+
+ public static abstract class AbstractNonblockingServerArgs<T extends AbstractNonblockingServerArgs<T>> extends AbstractServerArgs<T> {
+ public long maxReadBufferBytes = 256 * 1024 * 1024;
+
+ public AbstractNonblockingServerArgs(TNonblockingServerTransport transport) {
+ super(transport);
+ transportFactory(new TFramedTransport.Factory());
+ }
+ }
+
+ /**
+ * The maximum amount of memory we will allocate to client IO buffers at a
+ * time. Without this limit, the server will gladly allocate client buffers
+ * right into an out of memory exception, rather than waiting.
+ */
+ final long MAX_READ_BUFFER_BYTES;
+
+ /**
+ * How many bytes are currently allocated to read buffers.
+ */
+ final AtomicLong readBufferBytesAllocated = new AtomicLong(0);
+
+ public AbstractNonblockingServer(AbstractNonblockingServerArgs args) {
+ super(args);
+ MAX_READ_BUFFER_BYTES = args.maxReadBufferBytes;
+ }
+
+ /**
+ * Begin accepting connections and processing invocations.
+ */
+ public void serve() {
+ // start any IO threads
+ if (!startThreads()) {
+ return;
+ }
+
+ // start listening, or exit
+ if (!startListening()) {
+ return;
+ }
+
+ setServing(true);
+
+ // this will block while we serve
+ waitForShutdown();
+
+ setServing(false);
+
+ // do a little cleanup
+ stopListening();
+ }
+
+ /**
+ * Starts any threads required for serving.
+ *
+ * @return true if everything went ok, false if threads could not be started.
+ */
+ protected abstract boolean startThreads();
+
+ /**
+ * A method that will block until when threads handling the serving have been
+ * shut down.
+ */
+ protected abstract void waitForShutdown();
+
+ /**
+ * Have the server transport start accepting connections.
+ *
+ * @return true if we started listening successfully, false if something went
+ * wrong.
+ */
+ protected boolean startListening() {
+ try {
+ serverTransport_.listen();
+ return true;
+ } catch (TTransportException ttx) {
+ LOGGER.error("Failed to start listening on server socket!", ttx);
+ return false;
+ }
+ }
+
+ /**
+ * Stop listening for connections.
+ */
+ protected void stopListening() {
+ serverTransport_.close();
+ }
+
+ /**
+ * Perform an invocation. This method could behave several different ways -
+ * invoke immediately inline, queue for separate execution, etc.
+ *
+ * @return true if invocation was successfully requested, which is not a
+ * guarantee that invocation has completed. False if the request
+ * failed.
+ */
+ protected abstract boolean requestInvoke(FrameBuffer frameBuffer);
+
+ /**
+ * An abstract thread that handles selecting on a set of transports and
+ * {@link FrameBuffer FrameBuffers} associated with selected keys
+ * corresponding to requests.
+ */
+ protected abstract class AbstractSelectThread extends Thread {
+ protected Selector selector;
+
+ // List of FrameBuffers that want to change their selection interests.
+ protected final Set<FrameBuffer> selectInterestChanges = new HashSet<FrameBuffer>();
+
+ public AbstractSelectThread() throws IOException {
+ this.selector = SelectorProvider.provider().openSelector();
+ }
+
+ /**
+ * If the selector is blocked, wake it up.
+ */
+ public void wakeupSelector() {
+ selector.wakeup();
+ }
+
+ /**
+ * Add FrameBuffer to the list of select interest changes and wake up the
+ * selector if it's blocked. When the select() call exits, it'll give the
+ * FrameBuffer a chance to change its interests.
+ */
+ public void requestSelectInterestChange(FrameBuffer frameBuffer) {
+ synchronized (selectInterestChanges) {
+ selectInterestChanges.add(frameBuffer);
+ }
+ // wakeup the selector, if it's currently blocked.
+ selector.wakeup();
+ }
+
+ /**
+ * Check to see if there are any FrameBuffers that have switched their
+ * interest type from read to write or vice versa.
+ */
+ protected void processInterestChanges() {
+ synchronized (selectInterestChanges) {
+ for (FrameBuffer fb : selectInterestChanges) {
+ fb.changeSelectInterests();
+ }
+ selectInterestChanges.clear();
+ }
+ }
+
+ /**
+ * Do the work required to read from a readable client. If the frame is
+ * fully read, then invoke the method call.
+ */
+ protected void handleRead(SelectionKey key) {
+ FrameBuffer buffer = (FrameBuffer) key.attachment();
+ if (!buffer.read()) {
+ cleanupSelectionKey(key);
+ return;
+ }
+
+ // if the buffer's frame read is complete, invoke the method.
+ if (buffer.isFrameFullyRead()) {
+ if (!requestInvoke(buffer)) {
+ cleanupSelectionKey(key);
+ }
+ }
+ }
+
+ /**
+ * Let a writable client get written, if there's data to be written.
+ */
+ protected void handleWrite(SelectionKey key) {
+ FrameBuffer buffer = (FrameBuffer) key.attachment();
+ if (!buffer.write()) {
+ cleanupSelectionKey(key);
+ }
+ }
+
+ /**
+ * Do connection-close cleanup on a given SelectionKey.
+ */
+ protected void cleanupSelectionKey(SelectionKey key) {
+ // remove the records from the two maps
+ FrameBuffer buffer = (FrameBuffer) key.attachment();
+ if (buffer != null) {
+ // close the buffer
+ buffer.close();
+ }
+ // cancel the selection key
+ key.cancel();
+ }
+ } // SelectThread
+
+ /**
+ * Possible states for the FrameBuffer state machine.
+ */
+ private enum FrameBufferState {
+ // in the midst of reading the frame size off the wire
+ READING_FRAME_SIZE,
+ // reading the actual frame data now, but not all the way done yet
+ READING_FRAME,
+ // completely read the frame, so an invocation can now happen
+ READ_FRAME_COMPLETE,
+ // waiting to get switched to listening for write events
+ AWAITING_REGISTER_WRITE,
+ // started writing response data, not fully complete yet
+ WRITING,
+ // another thread wants this framebuffer to go back to reading
+ AWAITING_REGISTER_READ,
+ // we want our transport and selection key invalidated in the selector
+ // thread
+ AWAITING_CLOSE
+ }
+
+ /**
+ * Class that implements a sort of state machine around the interaction with a
+ * client and an invoker. It manages reading the frame size and frame data,
+ * getting it handed off as wrapped transports, and then the writing of
+ * response data back to the client. In the process it manages flipping the
+ * read and write bits on the selection key for its client.
+ */
+ public class FrameBuffer {
+ private final Logger LOGGER = LoggerFactory.getLogger(getClass().getName());
+
+ // the actual transport hooked up to the client.
+ protected final TNonblockingTransport trans_;
+
+ // the SelectionKey that corresponds to our transport
+ protected final SelectionKey selectionKey_;
+
+ // the SelectThread that owns the registration of our transport
+ protected final AbstractSelectThread selectThread_;
+
+ // where in the process of reading/writing are we?
+ protected FrameBufferState state_ = FrameBufferState.READING_FRAME_SIZE;
+
+ // the ByteBuffer we'll be using to write and read, depending on the state
+ protected ByteBuffer buffer_;
+
+ protected final TByteArrayOutputStream response_;
+
+ // the frame that the TTransport should wrap.
+ protected final TMemoryInputTransport frameTrans_;
+
+ // the transport that should be used to connect to clients
+ protected final TTransport inTrans_;
+
+ protected final TTransport outTrans_;
+
+ // the input protocol to use on frames
+ protected final TProtocol inProt_;
+
+ // the output protocol to use on frames
+ protected final TProtocol outProt_;
+
+ // context associated with this connection
+ protected final ServerContext context_;
+
+ public FrameBuffer(final TNonblockingTransport trans,
+ final SelectionKey selectionKey,
+ final AbstractSelectThread selectThread) {
+ trans_ = trans;
+ selectionKey_ = selectionKey;
+ selectThread_ = selectThread;
+ buffer_ = ByteBuffer.allocate(4);
+
+ frameTrans_ = new TMemoryInputTransport();
+ response_ = new TByteArrayOutputStream();
+ inTrans_ = inputTransportFactory_.getTransport(frameTrans_);
+ outTrans_ = outputTransportFactory_.getTransport(new TIOStreamTransport(response_));
+ inProt_ = inputProtocolFactory_.getProtocol(inTrans_);
+ outProt_ = outputProtocolFactory_.getProtocol(outTrans_);
+
+ if (eventHandler_ != null) {
+ context_ = eventHandler_.createContext(inProt_, outProt_);
+ } else {
+ context_ = null;
+ }
+ }
+
+ /**
+ * Give this FrameBuffer a chance to read. The selector loop should have
+ * received a read event for this FrameBuffer.
+ *
+ * @return true if the connection should live on, false if it should be
+ * closed
+ */
+ public boolean read() {
+ if (state_ == FrameBufferState.READING_FRAME_SIZE) {
+ // try to read the frame size completely
+ if (!internalRead()) {
+ return false;
+ }
+
+ // if the frame size has been read completely, then prepare to read the
+ // actual frame.
+ if (buffer_.remaining() == 0) {
+ // pull out the frame size as an integer.
+ int frameSize = buffer_.getInt(0);
+ if (frameSize <= 0) {
+ LOGGER.error("Read an invalid frame size of " + frameSize
+ + ". Are you using TFramedTransport on the client side?");
+ return false;
+ }
+
+ // if this frame will always be too large for this server, log the
+ // error and close the connection.
+ if (frameSize > MAX_READ_BUFFER_BYTES) {
+ LOGGER.error("Read a frame size of " + frameSize
+ + ", which is bigger than the maximum allowable buffer size for ALL connections.");
+ return false;
+ }
+
+ // if this frame will push us over the memory limit, then return.
+ // with luck, more memory will free up the next time around.
+ if (readBufferBytesAllocated.get() + frameSize > MAX_READ_BUFFER_BYTES) {
+ return true;
+ }
+
+ // increment the amount of memory allocated to read buffers
+ readBufferBytesAllocated.addAndGet(frameSize + 4);
+
+ // reallocate the readbuffer as a frame-sized buffer
+ buffer_ = ByteBuffer.allocate(frameSize + 4);
+ buffer_.putInt(frameSize);
+
+ state_ = FrameBufferState.READING_FRAME;
+ } else {
+ // this skips the check of READING_FRAME state below, since we can't
+ // possibly go on to that state if there's data left to be read at
+ // this one.
+ return true;
+ }
+ }
+
+ // it is possible to fall through from the READING_FRAME_SIZE section
+ // to READING_FRAME if there's already some frame data available once
+ // READING_FRAME_SIZE is complete.
+
+ if (state_ == FrameBufferState.READING_FRAME) {
+ if (!internalRead()) {
+ return false;
+ }
+
+ // since we're already in the select loop here for sure, we can just
+ // modify our selection key directly.
+ if (buffer_.remaining() == 0) {
+ // get rid of the read select interests
+ selectionKey_.interestOps(0);
+ state_ = FrameBufferState.READ_FRAME_COMPLETE;
+ }
+
+ return true;
+ }
+
+ // if we fall through to this point, then the state must be invalid.
+ LOGGER.error("Read was called but state is invalid (" + state_ + ")");
+ return false;
+ }
+
+ /**
+ * Give this FrameBuffer a chance to write its output to the final client.
+ */
+ public boolean write() {
+ if (state_ == FrameBufferState.WRITING) {
+ try {
+ if (trans_.write(buffer_) < 0) {
+ return false;
+ }
+ } catch (IOException e) {
+ LOGGER.warn("Got an IOException during write!", e);
+ return false;
+ }
+
+ // we're done writing. now we need to switch back to reading.
+ if (buffer_.remaining() == 0) {
+ prepareRead();
+ }
+ return true;
+ }
+
+ LOGGER.error("Write was called, but state is invalid (" + state_ + ")");
+ return false;
+ }
+
+ /**
+ * Give this FrameBuffer a chance to set its interest to write, once data
+ * has come in.
+ */
+ public void changeSelectInterests() {
+ switch (state_) {
+ case AWAITING_REGISTER_WRITE:
+ // set the OP_WRITE interest
+ selectionKey_.interestOps(SelectionKey.OP_WRITE);
+ state_ = FrameBufferState.WRITING;
+ break;
+ case AWAITING_REGISTER_READ:
+ prepareRead();
+ break;
+ case AWAITING_CLOSE:
+ close();
+ selectionKey_.cancel();
+ break;
+ default:
+ LOGGER.error(
+ "changeSelectInterest was called, but state is invalid ({})",
+ state_);
+ }
+ }
+
+ /**
+ * Shut the connection down.
+ */
+ public void close() {
+ // if we're being closed due to an error, we might have allocated a
+ // buffer that we need to subtract for our memory accounting.
+ if (state_ == FrameBufferState.READING_FRAME ||
+ state_ == FrameBufferState.READ_FRAME_COMPLETE ||
+ state_ == FrameBufferState.AWAITING_CLOSE) {
+ readBufferBytesAllocated.addAndGet(-buffer_.array().length);
+ }
+ trans_.close();
+ if (eventHandler_ != null) {
+ eventHandler_.deleteContext(context_, inProt_, outProt_);
+ }
+ }
+
+ /**
+ * Check if this FrameBuffer has a full frame read.
+ */
+ public boolean isFrameFullyRead() {
+ return state_ == FrameBufferState.READ_FRAME_COMPLETE;
+ }
+
+ /**
+ * After the processor has processed the invocation, whatever thread is
+ * managing invocations should call this method on this FrameBuffer so we
+ * know it's time to start trying to write again. Also, if it turns out that
+ * there actually isn't any data in the response buffer, we'll skip trying
+ * to write and instead go back to reading.
+ */
+ public void responseReady() {
+ // the read buffer is definitely no longer in use, so we will decrement
+ // our read buffer count. we do this here as well as in close because
+ // we'd like to free this read memory up as quickly as possible for other
+ // clients.
+ readBufferBytesAllocated.addAndGet(-buffer_.array().length);
+
+ if (response_.len() == 0) {
+ // go straight to reading again. this was probably an oneway method
+ state_ = FrameBufferState.AWAITING_REGISTER_READ;
+ buffer_ = null;
+ } else {
+ buffer_ = ByteBuffer.wrap(response_.get(), 0, response_.len());
+
+ // set state that we're waiting to be switched to write. we do this
+ // asynchronously through requestSelectInterestChange() because there is
+ // a possibility that we're not in the main thread, and thus currently
+ // blocked in select(). (this functionality is in place for the sake of
+ // the HsHa server.)
+ state_ = FrameBufferState.AWAITING_REGISTER_WRITE;
+ }
+ requestSelectInterestChange();
+ }
+
+ /**
+ * Actually invoke the method signified by this FrameBuffer.
+ */
+ public void invoke() {
+ frameTrans_.reset(buffer_.array());
+ response_.reset();
+
+ try {
+ if (eventHandler_ != null) {
+ eventHandler_.processContext(context_, inTrans_, outTrans_);
+ }
+ processorFactory_.getProcessor(inTrans_).process(inProt_, outProt_);
+ responseReady();
+ return;
+ } catch (TException te) {
+ LOGGER.warn("Exception while invoking!", te);
+ } catch (Throwable t) {
+ LOGGER.error("Unexpected throwable while invoking!", t);
+ }
+ // This will only be reached when there is a throwable.
+ state_ = FrameBufferState.AWAITING_CLOSE;
+ requestSelectInterestChange();
+ }
+
+ /**
+ * Perform a read into buffer.
+ *
+ * @return true if the read succeeded, false if there was an error or the
+ * connection closed.
+ */
+ private boolean internalRead() {
+ try {
+ if (trans_.read(buffer_) < 0) {
+ return false;
+ }
+ return true;
+ } catch (IOException e) {
+ LOGGER.warn("Got an IOException in internalRead!", e);
+ return false;
+ }
+ }
+
+ /**
+ * We're done writing, so reset our interest ops and change state
+ * accordingly.
+ */
+ private void prepareRead() {
+ // we can set our interest directly without using the queue because
+ // we're in the select thread.
+ selectionKey_.interestOps(SelectionKey.OP_READ);
+ // get ready for another go-around
+ buffer_ = ByteBuffer.allocate(4);
+ state_ = FrameBufferState.READING_FRAME_SIZE;
+ }
+
+ /**
+ * When this FrameBuffer needs to change its select interests and execution
+ * might not be in its select thread, then this method will make sure the
+ * interest change gets done when the select thread wakes back up. When the
+ * current thread is this FrameBuffer's select thread, then it just does the
+ * interest change immediately.
+ */
+ protected void requestSelectInterestChange() {
+ if (Thread.currentThread() == this.selectThread_) {
+ changeSelectInterests();
+ } else {
+ this.selectThread_.requestSelectInterestChange(this);
+ }
+ }
+ } // FrameBuffer
+
+ public class AsyncFrameBuffer extends FrameBuffer {
+ public AsyncFrameBuffer(TNonblockingTransport trans, SelectionKey selectionKey, AbstractSelectThread selectThread) {
+ super(trans, selectionKey, selectThread);
+ }
+
+ public TProtocol getInputProtocol() {
+ return inProt_;
+ }
+
+ public TProtocol getOutputProtocol() {
+ return outProt_;
+ }
+
+
+ public void invoke() {
+ frameTrans_.reset(buffer_.array());
+ response_.reset();
+
+ try {
+ if (eventHandler_ != null) {
+ eventHandler_.processContext(context_, inTrans_, outTrans_);
+ }
+ ((TAsyncProcessor)processorFactory_.getProcessor(inTrans_)).process(this);
+ return;
+ } catch (TException te) {
+ LOGGER.warn("Exception while invoking!", te);
+ } catch (Throwable t) {
+ LOGGER.error("Unexpected throwable while invoking!", t);
+ }
+ // This will only be reached when there is a throwable.
+ state_ = FrameBufferState.AWAITING_CLOSE;
+ requestSelectInterestChange();
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/Invocation.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/Invocation.java
new file mode 100644
index 000000000..e8210f419
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/Invocation.java
@@ -0,0 +1,20 @@
+package org.apache.thrift.server;
+
+import org.apache.thrift.server.AbstractNonblockingServer.FrameBuffer;
+
+/**
+ * An Invocation represents a method call that is prepared to execute, given
+ * an idle worker thread. It contains the input and output protocols the
+ * thread's processor should use to perform the usual Thrift invocation.
+ */
+class Invocation implements Runnable {
+ private final FrameBuffer frameBuffer;
+
+ public Invocation(final FrameBuffer frameBuffer) {
+ this.frameBuffer = frameBuffer;
+ }
+
+ public void run() {
+ frameBuffer.invoke();
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/ServerContext.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/ServerContext.java
new file mode 100644
index 000000000..9b0b99eea
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/ServerContext.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Interface for storing server's connection context
+ */
+
+package org.apache.thrift.server;
+
+public interface ServerContext {}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TExtensibleServlet.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TExtensibleServlet.java
new file mode 100644
index 000000000..75082c0f7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TExtensibleServlet.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.server;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.TProcessor;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.transport.TIOStreamTransport;
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * Servlet implementation class ThriftServer, that allows {@link TProcessor} and
+ * {@link TProtocolFactory} to be supplied after the {@link #init()} method has
+ * finished. <br>
+ * Subclasses must implement the abstract methods that return the TProcessor and
+ * two TProtocolFactory. Those methods are guaranteed to be called exactly once,
+ * and that {@link ServletContext} is available.
+ */
+public abstract class TExtensibleServlet extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+
+ private TProcessor processor;
+
+ private TProtocolFactory inFactory;
+
+ private TProtocolFactory outFactory;
+
+ private Collection<Map.Entry<String, String>> customHeaders;
+
+ /**
+ * Returns the appropriate {@link TProcessor}. This will be called <b>once</b> just
+ * after the {@link #init()} method
+ *
+ * @return the appropriate {@link TProcessor}
+ */
+ protected abstract TProcessor getProcessor();
+
+ /**
+ * Returns the appropriate in {@link TProtocolFactory}. This will be called
+ * <b>once</b> just after the {@link #init()} method
+ *
+ * @return the appropriate in {@link TProtocolFactory}
+ */
+ protected abstract TProtocolFactory getInProtocolFactory();
+
+ /**
+ * Returns the appropriate out {@link TProtocolFactory}. This will be called
+ * <b>once</b> just after the {@link #init()} method
+ *
+ * @return the appropriate out {@link TProtocolFactory}
+ */
+ protected abstract TProtocolFactory getOutProtocolFactory();
+
+ @Override
+ public final void init(ServletConfig config) throws ServletException {
+ super.init(config); //no-args init() happens here
+ this.processor = getProcessor();
+ this.inFactory = getInProtocolFactory();
+ this.outFactory = getOutProtocolFactory();
+ this.customHeaders = new ArrayList<Map.Entry<String, String>>();
+
+ if (processor == null) {
+ throw new ServletException("processor must be set");
+ }
+ if (inFactory == null) {
+ throw new ServletException("inFactory must be set");
+ }
+ if (outFactory == null) {
+ throw new ServletException("outFactory must be set");
+ }
+ }
+
+ /**
+ * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
+ * response)
+ */
+ @Override
+ protected void doPost(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ TTransport inTransport = null;
+ TTransport outTransport = null;
+
+ try {
+ response.setContentType("application/x-thrift");
+
+ if (null != this.customHeaders) {
+ for (Map.Entry<String, String> header : this.customHeaders) {
+ response.addHeader(header.getKey(), header.getValue());
+ }
+ }
+
+ InputStream in = request.getInputStream();
+ OutputStream out = response.getOutputStream();
+
+ TTransport transport = new TIOStreamTransport(in, out);
+ inTransport = transport;
+ outTransport = transport;
+
+ TProtocol inProtocol = inFactory.getProtocol(inTransport);
+ TProtocol outProtocol = inFactory.getProtocol(outTransport);
+
+ processor.process(inProtocol, outProtocol);
+ out.flush();
+ } catch (TException te) {
+ throw new ServletException(te);
+ }
+ }
+
+ /**
+ * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
+ * response)
+ */
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ doPost(req, resp);
+ }
+
+ public void addCustomHeader(final String key, final String value) {
+ this.customHeaders.add(new Map.Entry<String, String>() {
+ public String getKey() {
+ return key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public String setValue(String value) {
+ return null;
+ }
+ });
+ }
+
+ public void setCustomHeaders(Collection<Map.Entry<String, String>> headers) {
+ this.customHeaders.clear();
+ this.customHeaders.addAll(headers);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/THsHaServer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/THsHaServer.java
new file mode 100644
index 000000000..4c5d7b5b5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/THsHaServer.java
@@ -0,0 +1,204 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package org.apache.thrift.server;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.thrift.transport.TNonblockingServerTransport;
+
+/**
+ * An extension of the TNonblockingServer to a Half-Sync/Half-Async server.
+ * Like TNonblockingServer, it relies on the use of TFramedTransport.
+ */
+public class THsHaServer extends TNonblockingServer {
+
+ public static class Args extends AbstractNonblockingServerArgs<Args> {
+ public int minWorkerThreads = 5;
+ public int maxWorkerThreads = Integer.MAX_VALUE;
+ private int stopTimeoutVal = 60;
+ private TimeUnit stopTimeoutUnit = TimeUnit.SECONDS;
+ private ExecutorService executorService = null;
+
+ public Args(TNonblockingServerTransport transport) {
+ super(transport);
+ }
+
+
+ /**
+ * Sets the min and max threads.
+ *
+ * @deprecated use {@link #minWorkerThreads(int)} and {@link #maxWorkerThreads(int)} instead.
+ */
+ @Deprecated
+ public Args workerThreads(int n) {
+ minWorkerThreads = n;
+ maxWorkerThreads = n;
+ return this;
+ }
+
+ /**
+ * @return what the min threads was set to.
+ * @deprecated use {@link #getMinWorkerThreads()} and {@link #getMaxWorkerThreads()} instead.
+ */
+ @Deprecated
+ public int getWorkerThreads() {
+ return minWorkerThreads;
+ }
+
+ public Args minWorkerThreads(int n) {
+ minWorkerThreads = n;
+ return this;
+ }
+
+ public Args maxWorkerThreads(int n) {
+ maxWorkerThreads = n;
+ return this;
+ }
+
+ public int getMinWorkerThreads() {
+ return minWorkerThreads;
+ }
+
+ public int getMaxWorkerThreads() {
+ return maxWorkerThreads;
+ }
+
+ public int getStopTimeoutVal() {
+ return stopTimeoutVal;
+ }
+
+ public Args stopTimeoutVal(int stopTimeoutVal) {
+ this.stopTimeoutVal = stopTimeoutVal;
+ return this;
+ }
+
+ public TimeUnit getStopTimeoutUnit() {
+ return stopTimeoutUnit;
+ }
+
+ public Args stopTimeoutUnit(TimeUnit stopTimeoutUnit) {
+ this.stopTimeoutUnit = stopTimeoutUnit;
+ return this;
+ }
+
+ public ExecutorService getExecutorService() {
+ return executorService;
+ }
+
+ public Args executorService(ExecutorService executorService) {
+ this.executorService = executorService;
+ return this;
+ }
+ }
+
+
+ // This wraps all the functionality of queueing and thread pool management
+ // for the passing of Invocations from the Selector to workers.
+ private final ExecutorService invoker;
+
+ private final Args args;
+
+ /**
+ * Create the server with the specified Args configuration
+ */
+ public THsHaServer(Args args) {
+ super(args);
+
+ invoker = args.executorService == null ? createInvokerPool(args) : args.executorService;
+ this.args = args;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void waitForShutdown() {
+ joinSelector();
+ gracefullyShutdownInvokerPool();
+ }
+
+ /**
+ * Helper to create an invoker pool
+ */
+ protected static ExecutorService createInvokerPool(Args options) {
+ int minWorkerThreads = options.minWorkerThreads;
+ int maxWorkerThreads = options.maxWorkerThreads;
+ int stopTimeoutVal = options.stopTimeoutVal;
+ TimeUnit stopTimeoutUnit = options.stopTimeoutUnit;
+
+ LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
+ ExecutorService invoker = new ThreadPoolExecutor(minWorkerThreads,
+ maxWorkerThreads, stopTimeoutVal, stopTimeoutUnit, queue);
+
+ return invoker;
+ }
+
+ protected ExecutorService getInvoker() {
+ return invoker;
+ }
+
+ protected void gracefullyShutdownInvokerPool() {
+ // try to gracefully shut down the executor service
+ invoker.shutdown();
+
+ // Loop until awaitTermination finally does return without a interrupted
+ // exception. If we don't do this, then we'll shut down prematurely. We want
+ // to let the executorService clear it's task queue, closing client sockets
+ // appropriately.
+ long timeoutMS = args.stopTimeoutUnit.toMillis(args.stopTimeoutVal);
+ long now = System.currentTimeMillis();
+ while (timeoutMS >= 0) {
+ try {
+ invoker.awaitTermination(timeoutMS, TimeUnit.MILLISECONDS);
+ break;
+ } catch (InterruptedException ix) {
+ long newnow = System.currentTimeMillis();
+ timeoutMS -= (newnow - now);
+ now = newnow;
+ }
+ }
+ }
+
+ /**
+ * We override the standard invoke method here to queue the invocation for
+ * invoker service instead of immediately invoking. The thread pool takes care
+ * of the rest.
+ */
+ @Override
+ protected boolean requestInvoke(FrameBuffer frameBuffer) {
+ try {
+ Runnable invocation = getRunnable(frameBuffer);
+ invoker.execute(invocation);
+ return true;
+ } catch (RejectedExecutionException rx) {
+ LOGGER.warn("ExecutorService rejected execution!", rx);
+ return false;
+ }
+ }
+
+ protected Runnable getRunnable(FrameBuffer frameBuffer){
+ return new Invocation(frameBuffer);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TNonblockingServer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TNonblockingServer.java
new file mode 100644
index 000000000..79610b0f7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TNonblockingServer.java
@@ -0,0 +1,247 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package org.apache.thrift.server;
+
+import org.apache.thrift.transport.TNonblockingServerTransport;
+import org.apache.thrift.transport.TNonblockingTransport;
+import org.apache.thrift.transport.TTransportException;
+
+import java.io.IOException;
+import java.nio.channels.SelectionKey;
+import java.util.Iterator;
+
+/**
+ * A nonblocking TServer implementation. This allows for fairness amongst all
+ * connected clients in terms of invocations.
+ *
+ * This server is inherently single-threaded. If you want a limited thread pool
+ * coupled with invocation-fairness, see THsHaServer.
+ *
+ * To use this server, you MUST use a TFramedTransport at the outermost
+ * transport, otherwise this server will be unable to determine when a whole
+ * method call has been read off the wire. Clients must also use TFramedTransport.
+ */
+public class TNonblockingServer extends AbstractNonblockingServer {
+
+ public static class Args extends AbstractNonblockingServerArgs<Args> {
+ public Args(TNonblockingServerTransport transport) {
+ super(transport);
+ }
+ }
+
+ private SelectAcceptThread selectAcceptThread_;
+
+ public TNonblockingServer(AbstractNonblockingServerArgs args) {
+ super(args);
+ }
+
+
+ /**
+ * Start the selector thread to deal with accepts and client messages.
+ *
+ * @return true if everything went ok, false if we couldn't start for some
+ * reason.
+ */
+ @Override
+ protected boolean startThreads() {
+ // start the selector
+ try {
+ selectAcceptThread_ = new SelectAcceptThread((TNonblockingServerTransport)serverTransport_);
+ selectAcceptThread_.start();
+ return true;
+ } catch (IOException e) {
+ LOGGER.error("Failed to start selector thread!", e);
+ return false;
+ }
+ }
+
+ @Override
+ protected void waitForShutdown() {
+ joinSelector();
+ }
+
+ /**
+ * Block until the selector thread exits.
+ */
+ protected void joinSelector() {
+ // wait until the selector thread exits
+ try {
+ selectAcceptThread_.join();
+ } catch (InterruptedException e) {
+ LOGGER.debug("Interrupted while waiting for accept thread", e);
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ /**
+ * Stop serving and shut everything down.
+ */
+ @Override
+ public void stop() {
+ stopped_ = true;
+ if (selectAcceptThread_ != null) {
+ selectAcceptThread_.wakeupSelector();
+ }
+ }
+
+ /**
+ * Perform an invocation. This method could behave several different ways
+ * - invoke immediately inline, queue for separate execution, etc.
+ */
+ @Override
+ protected boolean requestInvoke(FrameBuffer frameBuffer) {
+ frameBuffer.invoke();
+ return true;
+ }
+
+
+ public boolean isStopped() {
+ return selectAcceptThread_.isStopped();
+ }
+
+ /**
+ * The thread that will be doing all the selecting, managing new connections
+ * and those that still need to be read.
+ */
+ protected class SelectAcceptThread extends AbstractSelectThread {
+
+ // The server transport on which new client transports will be accepted
+ private final TNonblockingServerTransport serverTransport;
+
+ /**
+ * Set up the thread that will handle the non-blocking accepts, reads, and
+ * writes.
+ */
+ public SelectAcceptThread(final TNonblockingServerTransport serverTransport)
+ throws IOException {
+ this.serverTransport = serverTransport;
+ serverTransport.registerSelector(selector);
+ }
+
+ public boolean isStopped() {
+ return stopped_;
+ }
+
+ /**
+ * The work loop. Handles both selecting (all IO operations) and managing
+ * the selection preferences of all existing connections.
+ */
+ public void run() {
+ try {
+ if (eventHandler_ != null) {
+ eventHandler_.preServe();
+ }
+
+ while (!stopped_) {
+ select();
+ processInterestChanges();
+ }
+ for (SelectionKey selectionKey : selector.keys()) {
+ cleanupSelectionKey(selectionKey);
+ }
+ } catch (Throwable t) {
+ LOGGER.error("run() exiting due to uncaught error", t);
+ } finally {
+ try {
+ selector.close();
+ } catch (IOException e) {
+ LOGGER.error("Got an IOException while closing selector!", e);
+ }
+ stopped_ = true;
+ }
+ }
+
+ /**
+ * Select and process IO events appropriately:
+ * If there are connections to be accepted, accept them.
+ * If there are existing connections with data waiting to be read, read it,
+ * buffering until a whole frame has been read.
+ * If there are any pending responses, buffer them until their target client
+ * is available, and then send the data.
+ */
+ private void select() {
+ try {
+ // wait for io events.
+ selector.select();
+
+ // process the io events we received
+ Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
+ while (!stopped_ && selectedKeys.hasNext()) {
+ SelectionKey key = selectedKeys.next();
+ selectedKeys.remove();
+
+ // skip if not valid
+ if (!key.isValid()) {
+ cleanupSelectionKey(key);
+ continue;
+ }
+
+ // if the key is marked Accept, then it has to be the server
+ // transport.
+ if (key.isAcceptable()) {
+ handleAccept();
+ } else if (key.isReadable()) {
+ // deal with reads
+ handleRead(key);
+ } else if (key.isWritable()) {
+ // deal with writes
+ handleWrite(key);
+ } else {
+ LOGGER.warn("Unexpected state in select! " + key.interestOps());
+ }
+ }
+ } catch (IOException e) {
+ LOGGER.warn("Got an IOException while selecting!", e);
+ }
+ }
+
+ protected FrameBuffer createFrameBuffer(final TNonblockingTransport trans,
+ final SelectionKey selectionKey,
+ final AbstractSelectThread selectThread) {
+ return processorFactory_.isAsyncProcessor() ?
+ new AsyncFrameBuffer(trans, selectionKey, selectThread) :
+ new FrameBuffer(trans, selectionKey, selectThread);
+ }
+
+ /**
+ * Accept a new connection.
+ */
+ private void handleAccept() throws IOException {
+ SelectionKey clientKey = null;
+ TNonblockingTransport client = null;
+ try {
+ // accept the connection
+ client = (TNonblockingTransport)serverTransport.accept();
+ clientKey = client.registerSelector(selector, SelectionKey.OP_READ);
+
+ // add this key to the map
+ FrameBuffer frameBuffer = createFrameBuffer(client, clientKey, SelectAcceptThread.this);
+
+ clientKey.attach(frameBuffer);
+ } catch (TTransportException tte) {
+ // something went wrong accepting.
+ LOGGER.warn("Exception trying to accept!", tte);
+ if (clientKey != null) cleanupSelectionKey(clientKey);
+ if (client != null) client.close();
+ }
+ }
+ } // SelectAcceptThread
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServer.java
new file mode 100644
index 000000000..bac06b26b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServer.java
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.server;
+
+import org.apache.thrift.TProcessor;
+import org.apache.thrift.TProcessorFactory;
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.transport.TServerTransport;
+import org.apache.thrift.transport.TTransportFactory;
+
+/**
+ * Generic interface for a Thrift server.
+ *
+ */
+public abstract class TServer {
+
+ public static class Args extends AbstractServerArgs<Args> {
+ public Args(TServerTransport transport) {
+ super(transport);
+ }
+ }
+
+ public static abstract class AbstractServerArgs<T extends AbstractServerArgs<T>> {
+ final TServerTransport serverTransport;
+ TProcessorFactory processorFactory;
+ TTransportFactory inputTransportFactory = new TTransportFactory();
+ TTransportFactory outputTransportFactory = new TTransportFactory();
+ TProtocolFactory inputProtocolFactory = new TBinaryProtocol.Factory();
+ TProtocolFactory outputProtocolFactory = new TBinaryProtocol.Factory();
+
+ public AbstractServerArgs(TServerTransport transport) {
+ serverTransport = transport;
+ }
+
+ public T processorFactory(TProcessorFactory factory) {
+ this.processorFactory = factory;
+ return (T) this;
+ }
+
+ public T processor(TProcessor processor) {
+ this.processorFactory = new TProcessorFactory(processor);
+ return (T) this;
+ }
+
+ public T transportFactory(TTransportFactory factory) {
+ this.inputTransportFactory = factory;
+ this.outputTransportFactory = factory;
+ return (T) this;
+ }
+
+ public T inputTransportFactory(TTransportFactory factory) {
+ this.inputTransportFactory = factory;
+ return (T) this;
+ }
+
+ public T outputTransportFactory(TTransportFactory factory) {
+ this.outputTransportFactory = factory;
+ return (T) this;
+ }
+
+ public T protocolFactory(TProtocolFactory factory) {
+ this.inputProtocolFactory = factory;
+ this.outputProtocolFactory = factory;
+ return (T) this;
+ }
+
+ public T inputProtocolFactory(TProtocolFactory factory) {
+ this.inputProtocolFactory = factory;
+ return (T) this;
+ }
+
+ public T outputProtocolFactory(TProtocolFactory factory) {
+ this.outputProtocolFactory = factory;
+ return (T) this;
+ }
+ }
+
+ /**
+ * Core processor
+ */
+ protected TProcessorFactory processorFactory_;
+
+ /**
+ * Server transport
+ */
+ protected TServerTransport serverTransport_;
+
+ /**
+ * Input Transport Factory
+ */
+ protected TTransportFactory inputTransportFactory_;
+
+ /**
+ * Output Transport Factory
+ */
+ protected TTransportFactory outputTransportFactory_;
+
+ /**
+ * Input Protocol Factory
+ */
+ protected TProtocolFactory inputProtocolFactory_;
+
+ /**
+ * Output Protocol Factory
+ */
+ protected TProtocolFactory outputProtocolFactory_;
+
+ private volatile boolean isServing;
+
+ protected TServerEventHandler eventHandler_;
+
+ // Flag for stopping the server
+ // Please see THRIFT-1795 for the usage of this flag
+ protected volatile boolean stopped_ = false;
+
+ protected TServer(AbstractServerArgs args) {
+ processorFactory_ = args.processorFactory;
+ serverTransport_ = args.serverTransport;
+ inputTransportFactory_ = args.inputTransportFactory;
+ outputTransportFactory_ = args.outputTransportFactory;
+ inputProtocolFactory_ = args.inputProtocolFactory;
+ outputProtocolFactory_ = args.outputProtocolFactory;
+ }
+
+ /**
+ * The run method fires up the server and gets things going.
+ */
+ public abstract void serve();
+
+ /**
+ * Stop the server. This is optional on a per-implementation basis. Not
+ * all servers are required to be cleanly stoppable.
+ */
+ public void stop() {}
+
+ public boolean isServing() {
+ return isServing;
+ }
+
+ protected void setServing(boolean serving) {
+ isServing = serving;
+ }
+
+ public void setServerEventHandler(TServerEventHandler eventHandler) {
+ eventHandler_ = eventHandler;
+ }
+
+ public TServerEventHandler getEventHandler() {
+ return eventHandler_;
+ }
+
+ public boolean getShouldStop() {
+ return this.stopped_;
+ }
+
+ public void setShouldStop(boolean shouldStop) {
+ this.stopped_ = shouldStop;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServerEventHandler.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServerEventHandler.java
new file mode 100644
index 000000000..f069b9bfb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServerEventHandler.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.server;
+
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * Interface that can handle events from the server core. To
+ * use this you should subclass it and implement the methods that you care
+ * about. Your subclass can also store local data that you may care about,
+ * such as additional "arguments" to these methods (stored in the object
+ * instance's state).
+ */
+public interface TServerEventHandler {
+
+ /**
+ * Called before the server begins.
+ */
+ void preServe();
+
+ /**
+ * Called when a new client has connected and is about to being processing.
+ */
+ ServerContext createContext(TProtocol input,
+ TProtocol output);
+
+ /**
+ * Called when a client has finished request-handling to delete server
+ * context.
+ */
+ void deleteContext(ServerContext serverContext,
+ TProtocol input,
+ TProtocol output);
+
+ /**
+ * Called when a client is about to call the processor.
+ */
+ void processContext(ServerContext serverContext,
+ TTransport inputTransport, TTransport outputTransport);
+
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServlet.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServlet.java
new file mode 100644
index 000000000..c1ab9df55
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServlet.java
@@ -0,0 +1,119 @@
+package org.apache.thrift.server;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.TProcessor;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.transport.TIOStreamTransport;
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * Servlet implementation class ThriftServer
+ */
+public class TServlet extends HttpServlet {
+
+ private final TProcessor processor;
+
+ private final TProtocolFactory inProtocolFactory;
+
+ private final TProtocolFactory outProtocolFactory;
+
+ private final Collection<Map.Entry<String, String>> customHeaders;
+
+ /**
+ * @see HttpServlet#HttpServlet()
+ */
+ public TServlet(TProcessor processor, TProtocolFactory inProtocolFactory,
+ TProtocolFactory outProtocolFactory) {
+ super();
+ this.processor = processor;
+ this.inProtocolFactory = inProtocolFactory;
+ this.outProtocolFactory = outProtocolFactory;
+ this.customHeaders = new ArrayList<Map.Entry<String, String>>();
+ }
+
+ /**
+ * @see HttpServlet#HttpServlet()
+ */
+ public TServlet(TProcessor processor, TProtocolFactory protocolFactory) {
+ this(processor, protocolFactory, protocolFactory);
+ }
+
+ /**
+ * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
+ * response)
+ */
+ @Override
+ protected void doPost(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+
+ TTransport inTransport = null;
+ TTransport outTransport = null;
+
+ try {
+ response.setContentType("application/x-thrift");
+
+ if (null != this.customHeaders) {
+ for (Map.Entry<String, String> header : this.customHeaders) {
+ response.addHeader(header.getKey(), header.getValue());
+ }
+ }
+ InputStream in = request.getInputStream();
+ OutputStream out = response.getOutputStream();
+
+ TTransport transport = new TIOStreamTransport(in, out);
+ inTransport = transport;
+ outTransport = transport;
+
+ TProtocol inProtocol = inProtocolFactory.getProtocol(inTransport);
+ TProtocol outProtocol = outProtocolFactory.getProtocol(outTransport);
+
+ processor.process(inProtocol, outProtocol);
+ out.flush();
+ } catch (TException te) {
+ throw new ServletException(te);
+ }
+ }
+
+ /**
+ * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
+ * response)
+ */
+ protected void doGet(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ doPost(request, response);
+ }
+
+ public void addCustomHeader(final String key, final String value) {
+ this.customHeaders.add(new Map.Entry<String, String>() {
+ public String getKey() {
+ return key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public String setValue(String value) {
+ return null;
+ }
+ });
+ }
+
+ public void setCustomHeaders(Collection<Map.Entry<String, String>> headers) {
+ this.customHeaders.clear();
+ this.customHeaders.addAll(headers);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TSimpleServer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TSimpleServer.java
new file mode 100644
index 000000000..13501efc6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TSimpleServer.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.server;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.TProcessor;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.transport.TTransport;
+import org.apache.thrift.transport.TTransportException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Simple singlethreaded server for testing.
+ *
+ */
+public class TSimpleServer extends TServer {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TSimpleServer.class.getName());
+
+ public TSimpleServer(AbstractServerArgs args) {
+ super(args);
+ }
+
+ public void serve() {
+ try {
+ serverTransport_.listen();
+ } catch (TTransportException ttx) {
+ LOGGER.error("Error occurred during listening.", ttx);
+ return;
+ }
+
+ // Run the preServe event
+ if (eventHandler_ != null) {
+ eventHandler_.preServe();
+ }
+
+ setServing(true);
+
+ while (!stopped_) {
+ TTransport client = null;
+ TProcessor processor = null;
+ TTransport inputTransport = null;
+ TTransport outputTransport = null;
+ TProtocol inputProtocol = null;
+ TProtocol outputProtocol = null;
+ ServerContext connectionContext = null;
+ try {
+ client = serverTransport_.accept();
+ if (client != null) {
+ processor = processorFactory_.getProcessor(client);
+ inputTransport = inputTransportFactory_.getTransport(client);
+ outputTransport = outputTransportFactory_.getTransport(client);
+ inputProtocol = inputProtocolFactory_.getProtocol(inputTransport);
+ outputProtocol = outputProtocolFactory_.getProtocol(outputTransport);
+ if (eventHandler_ != null) {
+ connectionContext = eventHandler_.createContext(inputProtocol, outputProtocol);
+ }
+ while (true) {
+ if (eventHandler_ != null) {
+ eventHandler_.processContext(connectionContext, inputTransport, outputTransport);
+ }
+ processor.process(inputProtocol, outputProtocol);
+ }
+ }
+ } catch (TTransportException ttx) {
+ // Client died, just move on
+ } catch (TException tx) {
+ if (!stopped_) {
+ LOGGER.error("Thrift error occurred during processing of message.", tx);
+ }
+ } catch (Exception x) {
+ if (!stopped_) {
+ LOGGER.error("Error occurred during processing of message.", x);
+ }
+ }
+
+ if (eventHandler_ != null) {
+ eventHandler_.deleteContext(connectionContext, inputProtocol, outputProtocol);
+ }
+
+ if (inputTransport != null) {
+ inputTransport.close();
+ }
+
+ if (outputTransport != null) {
+ outputTransport.close();
+ }
+
+ }
+ setServing(false);
+ }
+
+ public void stop() {
+ stopped_ = true;
+ serverTransport_.interrupt();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TThreadPoolServer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TThreadPoolServer.java
new file mode 100644
index 000000000..87e873381
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TThreadPoolServer.java
@@ -0,0 +1,359 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.server;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.TProcessor;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.transport.TServerTransport;
+import org.apache.thrift.transport.TTransport;
+import org.apache.thrift.transport.TTransportException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Server which uses Java's built in ThreadPool management to spawn off
+ * a worker pool that
+ *
+ */
+public class TThreadPoolServer extends TServer {
+ private static final Logger LOGGER = LoggerFactory.getLogger(TThreadPoolServer.class.getName());
+
+ public static class Args extends AbstractServerArgs<Args> {
+ public int minWorkerThreads = 5;
+ public int maxWorkerThreads = Integer.MAX_VALUE;
+ public ExecutorService executorService;
+ public int stopTimeoutVal = 60;
+ public TimeUnit stopTimeoutUnit = TimeUnit.SECONDS;
+ public int requestTimeout = 20;
+ public TimeUnit requestTimeoutUnit = TimeUnit.SECONDS;
+ public int beBackoffSlotLength = 100;
+ public TimeUnit beBackoffSlotLengthUnit = TimeUnit.MILLISECONDS;
+
+ public Args(TServerTransport transport) {
+ super(transport);
+ }
+
+ public Args minWorkerThreads(int n) {
+ minWorkerThreads = n;
+ return this;
+ }
+
+ public Args maxWorkerThreads(int n) {
+ maxWorkerThreads = n;
+ return this;
+ }
+
+ public Args stopTimeoutVal(int n) {
+ stopTimeoutVal = n;
+ return this;
+ }
+
+ public Args stopTimeoutUnit(TimeUnit tu) {
+ stopTimeoutUnit = tu;
+ return this;
+ }
+
+ public Args requestTimeout(int n) {
+ requestTimeout = n;
+ return this;
+ }
+
+ public Args requestTimeoutUnit(TimeUnit tu) {
+ requestTimeoutUnit = tu;
+ return this;
+ }
+ //Binary exponential backoff slot length
+ public Args beBackoffSlotLength(int n) {
+ beBackoffSlotLength = n;
+ return this;
+ }
+
+ //Binary exponential backoff slot time unit
+ public Args beBackoffSlotLengthUnit(TimeUnit tu) {
+ beBackoffSlotLengthUnit = tu;
+ return this;
+ }
+
+ public Args executorService(ExecutorService executorService) {
+ this.executorService = executorService;
+ return this;
+ }
+ }
+
+ // Executor service for handling client connections
+ private ExecutorService executorService_;
+
+ private final TimeUnit stopTimeoutUnit;
+
+ private final long stopTimeoutVal;
+
+ private final TimeUnit requestTimeoutUnit;
+
+ private final long requestTimeout;
+
+ private final long beBackoffSlotInMillis;
+
+ private Random random = new Random(System.currentTimeMillis());
+
+ public TThreadPoolServer(Args args) {
+ super(args);
+
+ stopTimeoutUnit = args.stopTimeoutUnit;
+ stopTimeoutVal = args.stopTimeoutVal;
+ requestTimeoutUnit = args.requestTimeoutUnit;
+ requestTimeout = args.requestTimeout;
+ beBackoffSlotInMillis = args.beBackoffSlotLengthUnit.toMillis(args.beBackoffSlotLength);
+
+ executorService_ = args.executorService != null ?
+ args.executorService : createDefaultExecutorService(args);
+ }
+
+ private static ExecutorService createDefaultExecutorService(Args args) {
+ SynchronousQueue<Runnable> executorQueue =
+ new SynchronousQueue<Runnable>();
+ return new ThreadPoolExecutor(args.minWorkerThreads,
+ args.maxWorkerThreads,
+ args.stopTimeoutVal,
+ args.stopTimeoutUnit,
+ executorQueue);
+ }
+
+ protected ExecutorService getExecutorService() {
+ return executorService_;
+ }
+
+ protected boolean preServe() {
+ try {
+ serverTransport_.listen();
+ } catch (TTransportException ttx) {
+ LOGGER.error("Error occurred during listening.", ttx);
+ return false;
+ }
+
+ // Run the preServe event
+ if (eventHandler_ != null) {
+ eventHandler_.preServe();
+ }
+ stopped_ = false;
+ setServing(true);
+
+ return true;
+ }
+
+ public void serve() {
+ if (!preServe()) {
+ return;
+ }
+
+ execute();
+ waitForShutdown();
+
+ setServing(false);
+ }
+
+ protected void execute() {
+ int failureCount = 0;
+ while (!stopped_) {
+ try {
+ TTransport client = serverTransport_.accept();
+ WorkerProcess wp = new WorkerProcess(client);
+
+ int retryCount = 0;
+ long remainTimeInMillis = requestTimeoutUnit.toMillis(requestTimeout);
+ while(true) {
+ try {
+ executorService_.execute(wp);
+ break;
+ } catch(Throwable t) {
+ if (t instanceof RejectedExecutionException) {
+ retryCount++;
+ try {
+ if (remainTimeInMillis > 0) {
+ //do a truncated 20 binary exponential backoff sleep
+ long sleepTimeInMillis = ((long) (random.nextDouble() *
+ (1L << Math.min(retryCount, 20)))) * beBackoffSlotInMillis;
+ sleepTimeInMillis = Math.min(sleepTimeInMillis, remainTimeInMillis);
+ TimeUnit.MILLISECONDS.sleep(sleepTimeInMillis);
+ remainTimeInMillis = remainTimeInMillis - sleepTimeInMillis;
+ } else {
+ client.close();
+ wp = null;
+ LOGGER.warn("Task has been rejected by ExecutorService " + retryCount
+ + " times till timedout, reason: " + t);
+ break;
+ }
+ } catch (InterruptedException e) {
+ LOGGER.warn("Interrupted while waiting to place client on executor queue.");
+ Thread.currentThread().interrupt();
+ break;
+ }
+ } else if (t instanceof Error) {
+ LOGGER.error("ExecutorService threw error: " + t, t);
+ throw (Error)t;
+ } else {
+ //for other possible runtime errors from ExecutorService, should also not kill serve
+ LOGGER.warn("ExecutorService threw error: " + t, t);
+ break;
+ }
+ }
+ }
+ } catch (TTransportException ttx) {
+ if (!stopped_) {
+ ++failureCount;
+ LOGGER.warn("Transport error occurred during acceptance of message.", ttx);
+ }
+ }
+ }
+ }
+
+ protected void waitForShutdown() {
+ executorService_.shutdown();
+
+ // Loop until awaitTermination finally does return without a interrupted
+ // exception. If we don't do this, then we'll shut down prematurely. We want
+ // to let the executorService clear it's task queue, closing client sockets
+ // appropriately.
+ long timeoutMS = stopTimeoutUnit.toMillis(stopTimeoutVal);
+ long now = System.currentTimeMillis();
+ while (timeoutMS >= 0) {
+ try {
+ executorService_.awaitTermination(timeoutMS, TimeUnit.MILLISECONDS);
+ break;
+ } catch (InterruptedException ix) {
+ long newnow = System.currentTimeMillis();
+ timeoutMS -= (newnow - now);
+ now = newnow;
+ }
+ }
+ }
+
+ public void stop() {
+ stopped_ = true;
+ serverTransport_.interrupt();
+ }
+
+ private class WorkerProcess implements Runnable {
+
+ /**
+ * Client that this services.
+ */
+ private TTransport client_;
+
+ /**
+ * Default constructor.
+ *
+ * @param client Transport to process
+ */
+ private WorkerProcess(TTransport client) {
+ client_ = client;
+ }
+
+ /**
+ * Loops on processing a client forever
+ */
+ public void run() {
+ TProcessor processor = null;
+ TTransport inputTransport = null;
+ TTransport outputTransport = null;
+ TProtocol inputProtocol = null;
+ TProtocol outputProtocol = null;
+
+ TServerEventHandler eventHandler = null;
+ ServerContext connectionContext = null;
+
+ try {
+ processor = processorFactory_.getProcessor(client_);
+ inputTransport = inputTransportFactory_.getTransport(client_);
+ outputTransport = outputTransportFactory_.getTransport(client_);
+ inputProtocol = inputProtocolFactory_.getProtocol(inputTransport);
+ outputProtocol = outputProtocolFactory_.getProtocol(outputTransport);
+
+ eventHandler = getEventHandler();
+ if (eventHandler != null) {
+ connectionContext = eventHandler.createContext(inputProtocol, outputProtocol);
+ }
+ // we check stopped_ first to make sure we're not supposed to be shutting
+ // down. this is necessary for graceful shutdown.
+ while (true) {
+
+ if (eventHandler != null) {
+ eventHandler.processContext(connectionContext, inputTransport, outputTransport);
+ }
+
+ if (stopped_) {
+ break;
+ }
+ processor.process(inputProtocol, outputProtocol);
+ }
+ } catch (Exception x) {
+ // We'll usually receive RuntimeException types here
+ // Need to unwrap to ascertain real causing exception before we choose to ignore
+ // Ignore err-logging all transport-level/type exceptions
+ if (!isIgnorableException(x)) {
+ // Log the exception at error level and continue
+ LOGGER.error((x instanceof TException? "Thrift " : "") + "Error occurred during processing of message.", x);
+ }
+ } finally {
+ if (eventHandler != null) {
+ eventHandler.deleteContext(connectionContext, inputProtocol, outputProtocol);
+ }
+ if (inputTransport != null) {
+ inputTransport.close();
+ }
+ if (outputTransport != null) {
+ outputTransport.close();
+ }
+ if (client_.isOpen()) {
+ client_.close();
+ }
+ }
+ }
+
+ private boolean isIgnorableException(Exception x) {
+ TTransportException tTransportException = null;
+
+ if (x instanceof TTransportException) {
+ tTransportException = (TTransportException)x;
+ }
+ else if (x.getCause() instanceof TTransportException) {
+ tTransportException = (TTransportException)x.getCause();
+ }
+
+ if (tTransportException != null) {
+ switch(tTransportException.getType()) {
+ case TTransportException.END_OF_FILE:
+ case TTransportException.TIMED_OUT:
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TThreadedSelectorServer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TThreadedSelectorServer.java
new file mode 100644
index 000000000..038507e9c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TThreadedSelectorServer.java
@@ -0,0 +1,744 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.server;
+
+import org.apache.thrift.transport.TNonblockingServerTransport;
+import org.apache.thrift.transport.TNonblockingTransport;
+import org.apache.thrift.transport.TTransportException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.SelectableChannel;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.spi.SelectorProvider;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A Half-Sync/Half-Async server with a separate pool of threads to handle
+ * non-blocking I/O. Accepts are handled on a single thread, and a configurable
+ * number of nonblocking selector threads manage reading and writing of client
+ * connections. A synchronous worker thread pool handles processing of requests.
+ *
+ * Performs better than TNonblockingServer/THsHaServer in multi-core
+ * environments when the the bottleneck is CPU on the single selector thread
+ * handling I/O. In addition, because the accept handling is decoupled from
+ * reads/writes and invocation, the server has better ability to handle back-
+ * pressure from new connections (e.g. stop accepting when busy).
+ *
+ * Like TNonblockingServer, it relies on the use of TFramedTransport.
+ */
+public class TThreadedSelectorServer extends AbstractNonblockingServer {
+ private static final Logger LOGGER = LoggerFactory.getLogger(TThreadedSelectorServer.class.getName());
+
+ public static class Args extends AbstractNonblockingServerArgs<Args> {
+
+ /** The number of threads for selecting on already-accepted connections */
+ public int selectorThreads = 2;
+ /**
+ * The size of the executor service (if none is specified) that will handle
+ * invocations. This may be set to 0, in which case invocations will be
+ * handled directly on the selector threads (as is in TNonblockingServer)
+ */
+ private int workerThreads = 5;
+ /** Time to wait for server to stop gracefully */
+ private int stopTimeoutVal = 60;
+ private TimeUnit stopTimeoutUnit = TimeUnit.SECONDS;
+ /** The ExecutorService for handling dispatched requests */
+ private ExecutorService executorService = null;
+ /**
+ * The size of the blocking queue per selector thread for passing accepted
+ * connections to the selector thread
+ */
+ private int acceptQueueSizePerThread = 4;
+
+ /**
+ * Determines the strategy for handling new accepted connections.
+ */
+ public static enum AcceptPolicy {
+ /**
+ * Require accepted connection registration to be handled by the executor.
+ * If the worker pool is saturated, further accepts will be closed
+ * immediately. Slightly increases latency due to an extra scheduling.
+ */
+ FAIR_ACCEPT,
+ /**
+ * Handle the accepts as fast as possible, disregarding the status of the
+ * executor service.
+ */
+ FAST_ACCEPT
+ }
+
+ private AcceptPolicy acceptPolicy = AcceptPolicy.FAST_ACCEPT;
+
+ public Args(TNonblockingServerTransport transport) {
+ super(transport);
+ }
+
+ public Args selectorThreads(int i) {
+ selectorThreads = i;
+ return this;
+ }
+
+ public int getSelectorThreads() {
+ return selectorThreads;
+ }
+
+ public Args workerThreads(int i) {
+ workerThreads = i;
+ return this;
+ }
+
+ public int getWorkerThreads() {
+ return workerThreads;
+ }
+
+ public int getStopTimeoutVal() {
+ return stopTimeoutVal;
+ }
+
+ public Args stopTimeoutVal(int stopTimeoutVal) {
+ this.stopTimeoutVal = stopTimeoutVal;
+ return this;
+ }
+
+ public TimeUnit getStopTimeoutUnit() {
+ return stopTimeoutUnit;
+ }
+
+ public Args stopTimeoutUnit(TimeUnit stopTimeoutUnit) {
+ this.stopTimeoutUnit = stopTimeoutUnit;
+ return this;
+ }
+
+ public ExecutorService getExecutorService() {
+ return executorService;
+ }
+
+ public Args executorService(ExecutorService executorService) {
+ this.executorService = executorService;
+ return this;
+ }
+
+ public int getAcceptQueueSizePerThread() {
+ return acceptQueueSizePerThread;
+ }
+
+ public Args acceptQueueSizePerThread(int acceptQueueSizePerThread) {
+ this.acceptQueueSizePerThread = acceptQueueSizePerThread;
+ return this;
+ }
+
+ public AcceptPolicy getAcceptPolicy() {
+ return acceptPolicy;
+ }
+
+ public Args acceptPolicy(AcceptPolicy acceptPolicy) {
+ this.acceptPolicy = acceptPolicy;
+ return this;
+ }
+
+ public void validate() {
+ if (selectorThreads <= 0) {
+ throw new IllegalArgumentException("selectorThreads must be positive.");
+ }
+ if (workerThreads < 0) {
+ throw new IllegalArgumentException("workerThreads must be non-negative.");
+ }
+ if (acceptQueueSizePerThread <= 0) {
+ throw new IllegalArgumentException("acceptQueueSizePerThread must be positive.");
+ }
+ }
+ }
+
+ // The thread handling all accepts
+ private AcceptThread acceptThread;
+
+ // Threads handling events on client transports
+ private final Set<SelectorThread> selectorThreads = new HashSet<SelectorThread>();
+
+ // This wraps all the functionality of queueing and thread pool management
+ // for the passing of Invocations from the selector thread(s) to the workers
+ // (if any).
+ private final ExecutorService invoker;
+
+ private final Args args;
+
+ /**
+ * Create the server with the specified Args configuration
+ */
+ public TThreadedSelectorServer(Args args) {
+ super(args);
+ args.validate();
+ invoker = args.executorService == null ? createDefaultExecutor(args) : args.executorService;
+ this.args = args;
+ }
+
+ /**
+ * Start the accept and selector threads running to deal with clients.
+ *
+ * @return true if everything went ok, false if we couldn't start for some
+ * reason.
+ */
+ @Override
+ protected boolean startThreads() {
+ try {
+ for (int i = 0; i < args.selectorThreads; ++i) {
+ selectorThreads.add(new SelectorThread(args.acceptQueueSizePerThread));
+ }
+ acceptThread = new AcceptThread((TNonblockingServerTransport) serverTransport_,
+ createSelectorThreadLoadBalancer(selectorThreads));
+ for (SelectorThread thread : selectorThreads) {
+ thread.start();
+ }
+ acceptThread.start();
+ return true;
+ } catch (IOException e) {
+ LOGGER.error("Failed to start threads!", e);
+ return false;
+ }
+ }
+
+ /**
+ * Joins the accept and selector threads and shuts down the executor service.
+ */
+ @Override
+ protected void waitForShutdown() {
+ try {
+ joinThreads();
+ } catch (InterruptedException e) {
+ // Non-graceful shutdown occurred
+ LOGGER.error("Interrupted while joining threads!", e);
+ }
+ gracefullyShutdownInvokerPool();
+ }
+
+ protected void joinThreads() throws InterruptedException {
+ // wait until the io threads exit
+ acceptThread.join();
+ for (SelectorThread thread : selectorThreads) {
+ thread.join();
+ }
+ }
+
+ /**
+ * Stop serving and shut everything down.
+ */
+ @Override
+ public void stop() {
+ stopped_ = true;
+
+ // Stop queuing connect attempts asap
+ stopListening();
+
+ if (acceptThread != null) {
+ acceptThread.wakeupSelector();
+ }
+ if (selectorThreads != null) {
+ for (SelectorThread thread : selectorThreads) {
+ if (thread != null)
+ thread.wakeupSelector();
+ }
+ }
+ }
+
+ protected void gracefullyShutdownInvokerPool() {
+ // try to gracefully shut down the executor service
+ invoker.shutdown();
+
+ // Loop until awaitTermination finally does return without a interrupted
+ // exception. If we don't do this, then we'll shut down prematurely. We want
+ // to let the executorService clear it's task queue, closing client sockets
+ // appropriately.
+ long timeoutMS = args.stopTimeoutUnit.toMillis(args.stopTimeoutVal);
+ long now = System.currentTimeMillis();
+ while (timeoutMS >= 0) {
+ try {
+ invoker.awaitTermination(timeoutMS, TimeUnit.MILLISECONDS);
+ break;
+ } catch (InterruptedException ix) {
+ long newnow = System.currentTimeMillis();
+ timeoutMS -= (newnow - now);
+ now = newnow;
+ }
+ }
+ }
+
+ /**
+ * We override the standard invoke method here to queue the invocation for
+ * invoker service instead of immediately invoking. If there is no thread
+ * pool, handle the invocation inline on this thread
+ */
+ @Override
+ protected boolean requestInvoke(FrameBuffer frameBuffer) {
+ Runnable invocation = getRunnable(frameBuffer);
+ if (invoker != null) {
+ try {
+ invoker.execute(invocation);
+ return true;
+ } catch (RejectedExecutionException rx) {
+ LOGGER.warn("ExecutorService rejected execution!", rx);
+ return false;
+ }
+ } else {
+ // Invoke on the caller's thread
+ invocation.run();
+ return true;
+ }
+ }
+
+ protected Runnable getRunnable(FrameBuffer frameBuffer) {
+ return new Invocation(frameBuffer);
+ }
+
+ /**
+ * Helper to create the invoker if one is not specified
+ */
+ protected static ExecutorService createDefaultExecutor(Args options) {
+ return (options.workerThreads > 0) ? Executors.newFixedThreadPool(options.workerThreads) : null;
+ }
+
+ private static BlockingQueue<TNonblockingTransport> createDefaultAcceptQueue(int queueSize) {
+ if (queueSize == 0) {
+ // Unbounded queue
+ return new LinkedBlockingQueue<TNonblockingTransport>();
+ }
+ return new ArrayBlockingQueue<TNonblockingTransport>(queueSize);
+ }
+
+ /**
+ * The thread that selects on the server transport (listen socket) and accepts
+ * new connections to hand off to the IO selector threads
+ */
+ protected class AcceptThread extends Thread {
+
+ // The listen socket to accept on
+ private final TNonblockingServerTransport serverTransport;
+ private final Selector acceptSelector;
+
+ private final SelectorThreadLoadBalancer threadChooser;
+
+ /**
+ * Set up the AcceptThead
+ *
+ * @throws IOException
+ */
+ public AcceptThread(TNonblockingServerTransport serverTransport,
+ SelectorThreadLoadBalancer threadChooser) throws IOException {
+ this.serverTransport = serverTransport;
+ this.threadChooser = threadChooser;
+ this.acceptSelector = SelectorProvider.provider().openSelector();
+ this.serverTransport.registerSelector(acceptSelector);
+ }
+
+ /**
+ * The work loop. Selects on the server transport and accepts. If there was
+ * a server transport that had blocking accepts, and returned on blocking
+ * client transports, that should be used instead
+ */
+ public void run() {
+ try {
+ if (eventHandler_ != null) {
+ eventHandler_.preServe();
+ }
+
+ while (!stopped_) {
+ select();
+ }
+ } catch (Throwable t) {
+ LOGGER.error("run() on AcceptThread exiting due to uncaught error", t);
+ } finally {
+ try {
+ acceptSelector.close();
+ } catch (IOException e) {
+ LOGGER.error("Got an IOException while closing accept selector!", e);
+ }
+ // This will wake up the selector threads
+ TThreadedSelectorServer.this.stop();
+ }
+ }
+
+ /**
+ * If the selector is blocked, wake it up.
+ */
+ public void wakeupSelector() {
+ acceptSelector.wakeup();
+ }
+
+ /**
+ * Select and process IO events appropriately: If there are connections to
+ * be accepted, accept them.
+ */
+ private void select() {
+ try {
+ // wait for connect events.
+ acceptSelector.select();
+
+ // process the io events we received
+ Iterator<SelectionKey> selectedKeys = acceptSelector.selectedKeys().iterator();
+ while (!stopped_ && selectedKeys.hasNext()) {
+ SelectionKey key = selectedKeys.next();
+ selectedKeys.remove();
+
+ // skip if not valid
+ if (!key.isValid()) {
+ continue;
+ }
+
+ if (key.isAcceptable()) {
+ handleAccept();
+ } else {
+ LOGGER.warn("Unexpected state in select! " + key.interestOps());
+ }
+ }
+ } catch (IOException e) {
+ LOGGER.warn("Got an IOException while selecting!", e);
+ }
+ }
+
+ /**
+ * Accept a new connection.
+ */
+ private void handleAccept() {
+ final TNonblockingTransport client = doAccept();
+ if (client != null) {
+ // Pass this connection to a selector thread
+ final SelectorThread targetThread = threadChooser.nextThread();
+
+ if (args.acceptPolicy == Args.AcceptPolicy.FAST_ACCEPT || invoker == null) {
+ doAddAccept(targetThread, client);
+ } else {
+ // FAIR_ACCEPT
+ try {
+ invoker.submit(new Runnable() {
+ public void run() {
+ doAddAccept(targetThread, client);
+ }
+ });
+ } catch (RejectedExecutionException rx) {
+ LOGGER.warn("ExecutorService rejected accept registration!", rx);
+ // close immediately
+ client.close();
+ }
+ }
+ }
+ }
+
+ private TNonblockingTransport doAccept() {
+ try {
+ return (TNonblockingTransport) serverTransport.accept();
+ } catch (TTransportException tte) {
+ // something went wrong accepting.
+ LOGGER.warn("Exception trying to accept!", tte);
+ return null;
+ }
+ }
+
+ private void doAddAccept(SelectorThread thread, TNonblockingTransport client) {
+ if (!thread.addAcceptedConnection(client)) {
+ client.close();
+ }
+ }
+ } // AcceptThread
+
+ /**
+ * The SelectorThread(s) will be doing all the selecting on accepted active
+ * connections.
+ */
+ protected class SelectorThread extends AbstractSelectThread {
+
+ // Accepted connections added by the accept thread.
+ private final BlockingQueue<TNonblockingTransport> acceptedQueue;
+ private int SELECTOR_AUTO_REBUILD_THRESHOLD = 512;
+ private long MONITOR_PERIOD = 1000L;
+ private int jvmBug = 0;
+
+ /**
+ * Set up the SelectorThread with an unbounded queue for incoming accepts.
+ *
+ * @throws IOException
+ * if a selector cannot be created
+ */
+ public SelectorThread() throws IOException {
+ this(new LinkedBlockingQueue<TNonblockingTransport>());
+ }
+
+ /**
+ * Set up the SelectorThread with an bounded queue for incoming accepts.
+ *
+ * @throws IOException
+ * if a selector cannot be created
+ */
+ public SelectorThread(int maxPendingAccepts) throws IOException {
+ this(createDefaultAcceptQueue(maxPendingAccepts));
+ }
+
+ /**
+ * Set up the SelectorThread with a specified queue for connections.
+ *
+ * @param acceptedQueue
+ * The BlockingQueue implementation for holding incoming accepted
+ * connections.
+ * @throws IOException
+ * if a selector cannot be created.
+ */
+ public SelectorThread(BlockingQueue<TNonblockingTransport> acceptedQueue) throws IOException {
+ this.acceptedQueue = acceptedQueue;
+ }
+
+ /**
+ * Hands off an accepted connection to be handled by this thread. This
+ * method will block if the queue for new connections is at capacity.
+ *
+ * @param accepted
+ * The connection that has been accepted.
+ * @return true if the connection has been successfully added.
+ */
+ public boolean addAcceptedConnection(TNonblockingTransport accepted) {
+ try {
+ acceptedQueue.put(accepted);
+ } catch (InterruptedException e) {
+ LOGGER.warn("Interrupted while adding accepted connection!", e);
+ return false;
+ }
+ selector.wakeup();
+ return true;
+ }
+
+ /**
+ * The work loop. Handles selecting (read/write IO), dispatching, and
+ * managing the selection preferences of all existing connections.
+ */
+ public void run() {
+ try {
+ while (!stopped_) {
+ select();
+ processAcceptedConnections();
+ processInterestChanges();
+ }
+ for (SelectionKey selectionKey : selector.keys()) {
+ cleanupSelectionKey(selectionKey);
+ }
+ } catch (Throwable t) {
+ LOGGER.error("run() on SelectorThread exiting due to uncaught error", t);
+ } finally {
+ try {
+ selector.close();
+ } catch (IOException e) {
+ LOGGER.error("Got an IOException while closing selector!", e);
+ }
+ // This will wake up the accept thread and the other selector threads
+ TThreadedSelectorServer.this.stop();
+ }
+ }
+
+ /**
+ * Select and process IO events appropriately: If there are existing
+ * connections with data waiting to be read, read it, buffering until a
+ * whole frame has been read. If there are any pending responses, buffer
+ * them until their target client is available, and then send the data.
+ */
+ private void select() {
+ try {
+
+ doSelect();
+
+ // process the io events we received
+ Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
+ while (!stopped_ && selectedKeys.hasNext()) {
+ SelectionKey key = selectedKeys.next();
+ selectedKeys.remove();
+
+ // skip if not valid
+ if (!key.isValid()) {
+ cleanupSelectionKey(key);
+ continue;
+ }
+
+ if (key.isReadable()) {
+ // deal with reads
+ handleRead(key);
+ } else if (key.isWritable()) {
+ // deal with writes
+ handleWrite(key);
+ } else {
+ LOGGER.warn("Unexpected state in select! " + key.interestOps());
+ }
+ }
+ } catch (IOException e) {
+ LOGGER.warn("Got an IOException while selecting!", e);
+ }
+ }
+
+ /**
+ * Do select and judge epoll bug happen.
+ * See : https://issues.apache.org/jira/browse/THRIFT-4251
+ */
+ private void doSelect() throws IOException {
+ long beforeSelect = System.currentTimeMillis();
+ int selectedNums = selector.select();
+ long afterSelect = System.currentTimeMillis();
+
+ if (selectedNums == 0) {
+ jvmBug++;
+ } else {
+ jvmBug = 0;
+ }
+
+ long selectedTime = afterSelect - beforeSelect;
+ if (selectedTime >= MONITOR_PERIOD) {
+ jvmBug = 0;
+ } else if (jvmBug > SELECTOR_AUTO_REBUILD_THRESHOLD) {
+ LOGGER.warn("In {} ms happen {} times jvm bug; rebuilding selector.", MONITOR_PERIOD, jvmBug);
+ rebuildSelector();
+ selector.selectNow();
+ jvmBug = 0;
+ }
+
+ }
+
+ /**
+ * Replaces the current Selector of this SelectorThread with newly created Selector to work
+ * around the infamous epoll 100% CPU bug.
+ */
+ private synchronized void rebuildSelector() {
+ final Selector oldSelector = selector;
+ if (oldSelector == null) {
+ return;
+ }
+ Selector newSelector = null;
+ try {
+ newSelector = Selector.open();
+ LOGGER.warn("Created new Selector.");
+ } catch (IOException e) {
+ LOGGER.error("Create new Selector error.", e);
+ }
+
+ for (SelectionKey key : oldSelector.selectedKeys()) {
+ if (!key.isValid() && key.readyOps() == 0)
+ continue;
+ SelectableChannel channel = key.channel();
+ Object attachment = key.attachment();
+
+ try {
+ if (attachment == null) {
+ channel.register(newSelector, key.readyOps());
+ } else {
+ channel.register(newSelector, key.readyOps(), attachment);
+ }
+ } catch (ClosedChannelException e) {
+ LOGGER.error("Register new selector key error.", e);
+ }
+
+ }
+
+ selector = newSelector;
+ try {
+ oldSelector.close();
+ } catch (IOException e) {
+ LOGGER.error("Close old selector error.", e);
+ }
+ LOGGER.warn("Replace new selector success.");
+ }
+
+ private void processAcceptedConnections() {
+ // Register accepted connections
+ while (!stopped_) {
+ TNonblockingTransport accepted = acceptedQueue.poll();
+ if (accepted == null) {
+ break;
+ }
+ registerAccepted(accepted);
+ }
+ }
+
+ protected FrameBuffer createFrameBuffer(final TNonblockingTransport trans,
+ final SelectionKey selectionKey,
+ final AbstractSelectThread selectThread) {
+ return processorFactory_.isAsyncProcessor() ?
+ new AsyncFrameBuffer(trans, selectionKey, selectThread) :
+ new FrameBuffer(trans, selectionKey, selectThread);
+ }
+
+ private void registerAccepted(TNonblockingTransport accepted) {
+ SelectionKey clientKey = null;
+ try {
+ clientKey = accepted.registerSelector(selector, SelectionKey.OP_READ);
+
+ FrameBuffer frameBuffer = createFrameBuffer(accepted, clientKey, SelectorThread.this);
+
+ clientKey.attach(frameBuffer);
+ } catch (IOException e) {
+ LOGGER.warn("Failed to register accepted connection to selector!", e);
+ if (clientKey != null) {
+ cleanupSelectionKey(clientKey);
+ }
+ accepted.close();
+ }
+ }
+ } // SelectorThread
+
+ /**
+ * Creates a SelectorThreadLoadBalancer to be used by the accept thread for
+ * assigning newly accepted connections across the threads.
+ */
+ protected SelectorThreadLoadBalancer createSelectorThreadLoadBalancer(Collection<? extends SelectorThread> threads) {
+ return new SelectorThreadLoadBalancer(threads);
+ }
+
+ /**
+ * A round robin load balancer for choosing selector threads for new
+ * connections.
+ */
+ protected static class SelectorThreadLoadBalancer {
+ private final Collection<? extends SelectorThread> threads;
+ private Iterator<? extends SelectorThread> nextThreadIterator;
+
+ public <T extends SelectorThread> SelectorThreadLoadBalancer(Collection<T> threads) {
+ if (threads.isEmpty()) {
+ throw new IllegalArgumentException("At least one selector thread is required");
+ }
+ this.threads = Collections.unmodifiableList(new ArrayList<T>(threads));
+ nextThreadIterator = this.threads.iterator();
+ }
+
+ public SelectorThread nextThread() {
+ // Choose a selector thread (round robin)
+ if (!nextThreadIterator.hasNext()) {
+ nextThreadIterator = threads.iterator();
+ }
+ return nextThreadIterator.next();
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBuffer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBuffer.java
new file mode 100644
index 000000000..fc3aa92df
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBuffer.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.transport;
+
+import java.util.Arrays;
+
+/**
+ * Helper class that wraps a byte[] so that it can expand and be reused. Users
+ * should call resizeIfNecessary to make sure the buffer has suitable capacity,
+ * and then use the array as needed. Note that the internal array will grow at a
+ * rate slightly faster than the requested capacity with the (untested)
+ * objective of avoiding expensive buffer allocations and copies.
+ */
+class AutoExpandingBuffer {
+ private byte[] array;
+
+ public AutoExpandingBuffer(int initialCapacity) {
+ this.array = new byte[initialCapacity];
+ }
+
+ public void resizeIfNecessary(int size) {
+ final int currentCapacity = this.array.length;
+ if (currentCapacity < size) {
+ // Increase by a factor of 1.5x
+ int growCapacity = currentCapacity + (currentCapacity >> 1);
+ int newCapacity = Math.max(growCapacity, size);
+ this.array = Arrays.copyOf(array, newCapacity);
+ }
+ }
+
+ public byte[] array() {
+ return this.array;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferReadTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferReadTransport.java
new file mode 100644
index 000000000..a28d25485
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferReadTransport.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.transport;
+
+/**
+ * TTransport for reading from an AutoExpandingBuffer.
+ */
+public class AutoExpandingBufferReadTransport extends TTransport {
+
+ private final AutoExpandingBuffer buf;
+
+ private int pos = 0;
+ private int limit = 0;
+
+ public AutoExpandingBufferReadTransport(int initialCapacity) {
+ this.buf = new AutoExpandingBuffer(initialCapacity);
+ }
+
+ public void fill(TTransport inTrans, int length) throws TTransportException {
+ buf.resizeIfNecessary(length);
+ inTrans.readAll(buf.array(), 0, length);
+ pos = 0;
+ limit = length;
+ }
+
+ @Override
+ public void close() {}
+
+ @Override
+ public boolean isOpen() { return true; }
+
+ @Override
+ public void open() throws TTransportException {}
+
+ @Override
+ public final int read(byte[] target, int off, int len) throws TTransportException {
+ int amtToRead = Math.min(len, getBytesRemainingInBuffer());
+ System.arraycopy(buf.array(), pos, target, off, amtToRead);
+ consumeBuffer(amtToRead);
+ return amtToRead;
+ }
+
+ @Override
+ public void write(byte[] buf, int off, int len) throws TTransportException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public final void consumeBuffer(int len) {
+ pos += len;
+ }
+
+ @Override
+ public final byte[] getBuffer() {
+ return buf.array();
+ }
+
+ @Override
+ public final int getBufferPosition() {
+ return pos;
+ }
+
+ @Override
+ public final int getBytesRemainingInBuffer() {
+ return limit - pos;
+ }
+}
+ \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferWriteTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferWriteTransport.java
new file mode 100644
index 000000000..ec7e7d45a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferWriteTransport.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.transport;
+
+/**
+ * TTransport for writing to an AutoExpandingBuffer.
+ */
+public final class AutoExpandingBufferWriteTransport extends TTransport {
+
+ private final AutoExpandingBuffer buf;
+ private int pos;
+ private int res;
+
+ /**
+ * Constructor.
+ * @param initialCapacity the initial capacity of the buffer
+ * @param frontReserve space, if any, to reserve at the beginning such
+ * that the first write is after this reserve.
+ * This allows framed transport to reserve space
+ * for the frame buffer length.
+ * @throws IllegalArgumentException if initialCapacity is less than one
+ * @throws IllegalArgumentException if frontReserve is less than zero
+ * @throws IllegalArgumentException if frontReserve is greater than initialCapacity
+ */
+ public AutoExpandingBufferWriteTransport(int initialCapacity, int frontReserve) {
+ if (initialCapacity < 1) {
+ throw new IllegalArgumentException("initialCapacity");
+ }
+ if (frontReserve < 0 || initialCapacity < frontReserve) {
+ throw new IllegalArgumentException("frontReserve");
+ }
+ this.buf = new AutoExpandingBuffer(initialCapacity);
+ this.pos = frontReserve;
+ this.res = frontReserve;
+ }
+
+ @Override
+ public void close() {}
+
+ @Override
+ public boolean isOpen() {return true;}
+
+ @Override
+ public void open() throws TTransportException {}
+
+ @Override
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void write(byte[] toWrite, int off, int len) throws TTransportException {
+ buf.resizeIfNecessary(pos + len);
+ System.arraycopy(toWrite, off, buf.array(), pos, len);
+ pos += len;
+ }
+
+ public AutoExpandingBuffer getBuf() {
+ return buf;
+ }
+
+ /**
+ * @return length of the buffer, including any front reserve
+ */
+ public int getLength() {
+ return pos;
+ }
+
+ public void reset() {
+ pos = res;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TByteBuffer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TByteBuffer.java
new file mode 100644
index 000000000..b6b065748
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TByteBuffer.java
@@ -0,0 +1,87 @@
+package org.apache.thrift.transport;
+
+import java.nio.BufferOverflowException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+
+/**
+ * ByteBuffer-backed implementation of TTransport.
+ */
+public final class TByteBuffer extends TTransport {
+ private final ByteBuffer byteBuffer;
+
+ /**
+ * Creates a new TByteBuffer wrapping a given NIO ByteBuffer.
+ */
+ public TByteBuffer(ByteBuffer byteBuffer) {
+ this.byteBuffer = byteBuffer;
+ }
+
+ @Override
+ public boolean isOpen() {
+ return true;
+ }
+
+ @Override
+ public void open() {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ final int n = Math.min(byteBuffer.remaining(), len);
+ if (n > 0) {
+ try {
+ byteBuffer.get(buf, off, n);
+ } catch (BufferUnderflowException e) {
+ throw new TTransportException("Unexpected end of input buffer", e);
+ }
+ }
+ return n;
+ }
+
+ @Override
+ public void write(byte[] buf, int off, int len) throws TTransportException {
+ try {
+ byteBuffer.put(buf, off, len);
+ } catch (BufferOverflowException e) {
+ throw new TTransportException("Not enough room in output buffer", e);
+ }
+ }
+
+ /**
+ * Get the underlying NIO ByteBuffer.
+ */
+ public ByteBuffer getByteBuffer() {
+ return byteBuffer;
+ }
+
+ /**
+ * Convenience method to call clear() on the underlying NIO ByteBuffer.
+ */
+ public TByteBuffer clear() {
+ byteBuffer.clear();
+ return this;
+ }
+
+ /**
+ * Convenience method to call flip() on the underlying NIO ByteBuffer.
+ */
+ public TByteBuffer flip() {
+ byteBuffer.flip();
+ return this;
+ }
+
+ /**
+ * Convenience method to convert the underlying NIO ByteBuffer to a
+ * plain old byte array.
+ */
+ public byte[] toByteArray() {
+ final byte[] data = new byte[byteBuffer.remaining()];
+ byteBuffer.slice().get(data);
+ return data;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFastFramedTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFastFramedTransport.java
new file mode 100644
index 000000000..a1fd2490a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFastFramedTransport.java
@@ -0,0 +1,200 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.transport;
+
+/**
+ * This transport is wire compatible with {@link TFramedTransport}, but makes
+ * use of reusable, expanding read and write buffers in order to avoid
+ * allocating new byte[]s all the time. Since the buffers only expand, you
+ * should probably only use this transport if your messages are not too variably
+ * large, unless the persistent memory cost is not an issue.
+ *
+ * This implementation is NOT threadsafe.
+ */
+public class TFastFramedTransport extends TTransport {
+
+ public static class Factory extends TTransportFactory {
+ private final int initialCapacity;
+ private final int maxLength;
+
+ public Factory() {
+ this(DEFAULT_BUF_CAPACITY, DEFAULT_MAX_LENGTH);
+ }
+
+ public Factory(int initialCapacity) {
+ this(initialCapacity, DEFAULT_MAX_LENGTH);
+ }
+
+ public Factory(int initialCapacity, int maxLength) {
+ this.initialCapacity = initialCapacity;
+ this.maxLength = maxLength;
+ }
+
+ @Override
+ public TTransport getTransport(TTransport trans) {
+ return new TFastFramedTransport(trans,
+ initialCapacity,
+ maxLength);
+ }
+ }
+
+ /**
+ * How big should the default read and write buffers be?
+ */
+ public static final int DEFAULT_BUF_CAPACITY = 1024;
+ /**
+ * How big is the largest allowable frame? Defaults to 16MB.
+ */
+ public static final int DEFAULT_MAX_LENGTH = 16384000;
+
+ private final TTransport underlying;
+ private final AutoExpandingBufferWriteTransport writeBuffer;
+ private AutoExpandingBufferReadTransport readBuffer;
+ private final int initialBufferCapacity;
+ private final byte[] i32buf = new byte[4];
+ private final int maxLength;
+
+ /**
+ * Create a new {@link TFastFramedTransport}. Use the defaults
+ * for initial buffer size and max frame length.
+ * @param underlying Transport that real reads and writes will go through to.
+ */
+ public TFastFramedTransport(TTransport underlying) {
+ this(underlying, DEFAULT_BUF_CAPACITY, DEFAULT_MAX_LENGTH);
+ }
+
+ /**
+ * Create a new {@link TFastFramedTransport}. Use the specified
+ * initial buffer capacity and the default max frame length.
+ * @param underlying Transport that real reads and writes will go through to.
+ * @param initialBufferCapacity The initial size of the read and write buffers.
+ * In practice, it's not critical to set this unless you know in advance that
+ * your messages are going to be very large.
+ */
+ public TFastFramedTransport(TTransport underlying, int initialBufferCapacity) {
+ this(underlying, initialBufferCapacity, DEFAULT_MAX_LENGTH);
+ }
+
+ /**
+ *
+ * @param underlying Transport that real reads and writes will go through to.
+ * @param initialBufferCapacity The initial size of the read and write buffers.
+ * In practice, it's not critical to set this unless you know in advance that
+ * your messages are going to be very large. (You can pass
+ * TFramedTransportWithReusableBuffer.DEFAULT_BUF_CAPACITY if you're only
+ * using this constructor because you want to set the maxLength.)
+ * @param maxLength The max frame size you are willing to read. You can use
+ * this parameter to limit how much memory can be allocated.
+ */
+ public TFastFramedTransport(TTransport underlying, int initialBufferCapacity, int maxLength) {
+ this.underlying = underlying;
+ this.maxLength = maxLength;
+ this.initialBufferCapacity = initialBufferCapacity;
+ readBuffer = new AutoExpandingBufferReadTransport(initialBufferCapacity);
+ writeBuffer = new AutoExpandingBufferWriteTransport(initialBufferCapacity, 4);
+ }
+
+ @Override
+ public void close() {
+ underlying.close();
+ }
+
+ @Override
+ public boolean isOpen() {
+ return underlying.isOpen();
+ }
+
+ @Override
+ public void open() throws TTransportException {
+ underlying.open();
+ }
+
+ @Override
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ int got = readBuffer.read(buf, off, len);
+ if (got > 0) {
+ return got;
+ }
+
+ // Read another frame of data
+ readFrame();
+
+ return readBuffer.read(buf, off, len);
+ }
+
+ private void readFrame() throws TTransportException {
+ underlying.readAll(i32buf , 0, 4);
+ int size = TFramedTransport.decodeFrameSize(i32buf);
+
+ if (size < 0) {
+ close();
+ throw new TTransportException(TTransportException.CORRUPTED_DATA, "Read a negative frame size (" + size + ")!");
+ }
+
+ if (size > maxLength) {
+ close();
+ throw new TTransportException(TTransportException.CORRUPTED_DATA,
+ "Frame size (" + size + ") larger than max length (" + maxLength + ")!");
+ }
+
+ readBuffer.fill(underlying, size);
+ }
+
+ @Override
+ public void write(byte[] buf, int off, int len) throws TTransportException {
+ writeBuffer.write(buf, off, len);
+ }
+
+ @Override
+ public void consumeBuffer(int len) {
+ readBuffer.consumeBuffer(len);
+ }
+
+ /**
+ * Only clears the read buffer!
+ */
+ public void clear() {
+ readBuffer = new AutoExpandingBufferReadTransport(initialBufferCapacity);
+ }
+
+ @Override
+ public void flush() throws TTransportException {
+ int payloadLength = writeBuffer.getLength() - 4;
+ byte[] data = writeBuffer.getBuf().array();
+ TFramedTransport.encodeFrameSize(payloadLength, data);
+ underlying.write(data, 0, payloadLength + 4);
+ writeBuffer.reset();
+ underlying.flush();
+ }
+
+ @Override
+ public byte[] getBuffer() {
+ return readBuffer.getBuffer();
+ }
+
+ @Override
+ public int getBufferPosition() {
+ return readBuffer.getBufferPosition();
+ }
+
+ @Override
+ public int getBytesRemainingInBuffer() {
+ return readBuffer.getBytesRemainingInBuffer();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFileProcessor.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFileProcessor.java
new file mode 100644
index 000000000..96087d1a3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFileProcessor.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import org.apache.thrift.TProcessor;
+import org.apache.thrift.TException;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+
+/**
+ * FileProcessor: helps in processing files generated by TFileTransport.
+ * Port of original cpp implementation
+ */
+public class TFileProcessor {
+
+ private TProcessor processor_;
+ private TProtocolFactory inputProtocolFactory_;
+ private TProtocolFactory outputProtocolFactory_;
+ private TFileTransport inputTransport_;
+ private TTransport outputTransport_;
+
+ public TFileProcessor(TProcessor processor, TProtocolFactory protocolFactory,
+ TFileTransport inputTransport,
+ TTransport outputTransport) {
+ processor_ = processor;
+ inputProtocolFactory_ = outputProtocolFactory_ = protocolFactory;
+ inputTransport_ = inputTransport;
+ outputTransport_ = outputTransport;
+ }
+
+ public TFileProcessor(TProcessor processor,
+ TProtocolFactory inputProtocolFactory,
+ TProtocolFactory outputProtocolFactory,
+ TFileTransport inputTransport,
+ TTransport outputTransport) {
+ processor_ = processor;
+ inputProtocolFactory_ = inputProtocolFactory;
+ outputProtocolFactory_ = outputProtocolFactory;
+ inputTransport_ = inputTransport;
+ outputTransport_ = outputTransport;
+ }
+
+ private void processUntil(int lastChunk) throws TException {
+ TProtocol ip = inputProtocolFactory_.getProtocol(inputTransport_);
+ TProtocol op = outputProtocolFactory_.getProtocol(outputTransport_);
+ int curChunk = inputTransport_.getCurChunk();
+
+ try {
+ while (lastChunk >= curChunk) {
+ processor_.process(ip, op);
+ int newChunk = inputTransport_.getCurChunk();
+ curChunk = newChunk;
+ }
+ } catch (TTransportException e) {
+ // if we are processing the last chunk - we could have just hit EOF
+ // on EOF - trap the error and stop processing.
+ if(e.getType() != TTransportException.END_OF_FILE)
+ throw e;
+ else {
+ return;
+ }
+ }
+ }
+
+ /**
+ * Process from start to last chunk both inclusive where chunks begin from 0
+
+ * @param startChunkNum first chunk to be processed
+ * @param endChunkNum last chunk to be processed
+ */
+ public void processChunk(int startChunkNum, int endChunkNum) throws TException {
+ int numChunks = inputTransport_.getNumChunks();
+ if(endChunkNum < 0)
+ endChunkNum += numChunks;
+
+ if(startChunkNum < 0)
+ startChunkNum += numChunks;
+
+ if(endChunkNum < startChunkNum)
+ throw new TException("endChunkNum " + endChunkNum + " is less than " + startChunkNum);
+
+ inputTransport_.seekToChunk(startChunkNum);
+ processUntil(endChunkNum);
+ }
+
+ /**
+ * Process a single chunk
+ *
+ * @param chunkNum chunk to be processed
+ */
+ public void processChunk(int chunkNum) throws TException {
+ processChunk(chunkNum, chunkNum);
+ }
+
+ /**
+ * Process a current chunk
+ */
+ public void processChunk() throws TException {
+ processChunk(inputTransport_.getCurChunk());
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFileTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFileTransport.java
new file mode 100644
index 000000000..88b73e54d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFileTransport.java
@@ -0,0 +1,624 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+import java.util.Random;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * FileTransport implementation of the TTransport interface.
+ * Currently this is a straightforward port of the cpp implementation
+ *
+ * It may make better sense to provide a basic stream access on top of the framed file format
+ * The FileTransport can then be a user of this framed file format with some additional logic
+ * for chunking.
+ */
+public class TFileTransport extends TTransport {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TFileTransport.class.getName());
+
+ public static class TruncableBufferedInputStream extends BufferedInputStream {
+ public void trunc() {
+ pos = count = 0;
+ }
+ public TruncableBufferedInputStream(InputStream in) {
+ super(in);
+ }
+ public TruncableBufferedInputStream(InputStream in, int size) {
+ super(in, size);
+ }
+ }
+
+
+ public static class Event {
+ private byte[] buf_;
+ private int nread_;
+ private int navailable_;
+
+ /**
+ * Initialize an event. Initially, it has no valid contents
+ *
+ * @param buf byte array buffer to store event
+ */
+ public Event(byte[] buf) {
+ buf_ = buf;
+ nread_ = navailable_ = 0;
+ }
+
+ public byte[] getBuf() { return buf_;}
+ public int getSize() { return buf_.length; }
+
+
+ public void setAvailable(int sz) { nread_ = 0; navailable_=sz;}
+ public int getRemaining() { return (navailable_ - nread_); }
+
+ public int emit(byte[] buf, int offset, int ndesired) {
+ if((ndesired == 0) || (ndesired > getRemaining()))
+ ndesired = getRemaining();
+
+ if(ndesired <= 0)
+ return (ndesired);
+
+ System.arraycopy(buf_, nread_, buf, offset, ndesired);
+ nread_ += ndesired;
+
+ return(ndesired);
+ }
+ };
+
+ public static class ChunkState {
+ /**
+ * Chunk Size. Must be same across all implementations
+ */
+ public static final int DEFAULT_CHUNK_SIZE = 16 * 1024 * 1024;
+
+ private int chunk_size_ = DEFAULT_CHUNK_SIZE;
+ private long offset_ = 0;
+
+ public ChunkState() {}
+ public ChunkState(int chunk_size) { chunk_size_ = chunk_size; }
+
+ public void skip(int size) {offset_ += size; }
+ public void seek(long offset) {offset_ = offset;}
+
+ public int getChunkSize() { return chunk_size_;}
+ public int getChunkNum() { return ((int)(offset_/chunk_size_));}
+ public int getRemaining() { return (chunk_size_ - ((int)(offset_ % chunk_size_)));}
+ public long getOffset() { return (offset_);}
+ }
+
+ public static enum TailPolicy {
+
+ NOWAIT(0, 0),
+ WAIT_FOREVER(500, -1);
+
+ /**
+ * Time in milliseconds to sleep before next read
+ * If 0, no sleep
+ */
+ public final int timeout_;
+
+ /**
+ * Number of retries before giving up
+ * if 0, no retries
+ * if -1, retry forever
+ */
+ public final int retries_;
+
+ /**
+ * ctor for policy
+ *
+ * @param timeout sleep time for this particular policy
+ * @param retries number of retries
+ */
+
+ TailPolicy(int timeout, int retries) {
+ timeout_ = timeout;
+ retries_ = retries;
+ }
+ }
+
+ /**
+ * Current tailing policy
+ */
+ TailPolicy currentPolicy_ = TailPolicy.NOWAIT;
+
+
+ /**
+ * Underlying file being read
+ */
+ protected TSeekableFile inputFile_ = null;
+
+ /**
+ * Underlying outputStream
+ */
+ protected OutputStream outputStream_ = null;
+
+
+ /**
+ * Event currently read in
+ */
+ Event currentEvent_ = null;
+
+ /**
+ * InputStream currently being used for reading
+ */
+ InputStream inputStream_ = null;
+
+ /**
+ * current Chunk state
+ */
+ ChunkState cs = null;
+
+ /**
+ * is read only?
+ */
+ private boolean readOnly_ = false;
+
+ /**
+ * Get File Tailing Policy
+ *
+ * @return current read policy
+ */
+ public TailPolicy getTailPolicy() {
+ return (currentPolicy_);
+ }
+
+ /**
+ * Set file Tailing Policy
+ *
+ * @param policy New policy to set
+ * @return Old policy
+ */
+ public TailPolicy setTailPolicy(TailPolicy policy) {
+ TailPolicy old = currentPolicy_;
+ currentPolicy_ = policy;
+ return (old);
+ }
+
+
+ /**
+ * Initialize read input stream
+ *
+ * @return input stream to read from file
+ */
+ private InputStream createInputStream() throws TTransportException {
+ InputStream is;
+ try {
+ if(inputStream_ != null) {
+ ((TruncableBufferedInputStream)inputStream_).trunc();
+ is = inputStream_;
+ } else {
+ is = new TruncableBufferedInputStream(inputFile_.getInputStream());
+ }
+ } catch (IOException iox) {
+ throw new TTransportException(iox.getMessage(), iox);
+ }
+ return(is);
+ }
+
+ /**
+ * Read (potentially tailing) an input stream
+ *
+ * @param is InputStream to read from
+ * @param buf Buffer to read into
+ * @param off Offset in buffer to read into
+ * @param len Number of bytes to read
+ * @param tp policy to use if we hit EOF
+ *
+ * @return number of bytes read
+ */
+ private int tailRead(InputStream is, byte[] buf,
+ int off, int len, TailPolicy tp) throws TTransportException {
+ int orig_len = len;
+ try {
+ int retries = 0;
+ while(len > 0) {
+ int cnt = is.read(buf, off, len);
+ if(cnt > 0) {
+ off += cnt;
+ len -= cnt;
+ retries = 0;
+ cs.skip(cnt); // remember that we read so many bytes
+ } else if (cnt == -1) {
+ // EOF
+ retries++;
+
+ if((tp.retries_ != -1) && tp.retries_ < retries)
+ return (orig_len - len);
+
+ if(tp.timeout_ > 0) {
+ try {Thread.sleep(tp.timeout_);} catch(InterruptedException e) {}
+ }
+ } else {
+ // either non-zero or -1 is what the contract says!
+ throw new
+ TTransportException("Unexpected return from InputStream.read = "
+ + cnt);
+ }
+ }
+ } catch (IOException iox) {
+ throw new TTransportException(iox.getMessage(), iox);
+ }
+
+ return(orig_len - len);
+ }
+
+ /**
+ * Event is corrupted. Do recovery
+ *
+ * @return true if recovery could be performed and we can read more data
+ * false is returned only when nothing more can be read
+ */
+ private boolean performRecovery() throws TTransportException {
+ int numChunks = getNumChunks();
+ int curChunk = cs.getChunkNum();
+
+ if(curChunk >= (numChunks-1)) {
+ return false;
+ }
+ seekToChunk(curChunk+1);
+ return true;
+ }
+
+ /**
+ * Read event from underlying file
+ *
+ * @return true if event could be read, false otherwise (on EOF)
+ */
+ private boolean readEvent() throws TTransportException {
+ byte[] ebytes = new byte[4];
+ int esize;
+ int nread;
+ int nrequested;
+
+ retry:
+ do {
+ // corner case. read to end of chunk
+ nrequested = cs.getRemaining();
+ if(nrequested < 4) {
+ nread = tailRead(inputStream_, ebytes, 0, nrequested, currentPolicy_);
+ if(nread != nrequested) {
+ return(false);
+ }
+ }
+
+ // assuming serialized on little endian machine
+ nread = tailRead(inputStream_, ebytes, 0, 4, currentPolicy_);
+ if(nread != 4) {
+ return(false);
+ }
+
+ esize=0;
+ for(int i=3; i>=0; i--) {
+ int val = (0x000000ff & (int)ebytes[i]);
+ esize |= (val << (i*8));
+ }
+
+ // check if event is corrupted and do recovery as required
+ if(esize > cs.getRemaining()) {
+ throw new TTransportException("FileTransport error: bad event size");
+ /*
+ if(performRecovery()) {
+ esize=0;
+ } else {
+ return false;
+ }
+ */
+ }
+ } while (esize == 0);
+
+ // reset existing event or get a larger one
+ if(currentEvent_.getSize() < esize)
+ currentEvent_ = new Event(new byte [esize]);
+
+ // populate the event
+ byte[] buf = currentEvent_.getBuf();
+ nread = tailRead(inputStream_, buf, 0, esize, currentPolicy_);
+ if(nread != esize) {
+ return(false);
+ }
+ currentEvent_.setAvailable(esize);
+ return(true);
+ }
+
+ /**
+ * open if both input/output open unless readonly
+ *
+ * @return true
+ */
+ public boolean isOpen() {
+ return ((inputStream_ != null) && (readOnly_ || (outputStream_ != null)));
+ }
+
+
+ /**
+ * Diverging from the cpp model and sticking to the TSocket model
+ * Files are not opened in ctor - but in explicit open call
+ */
+ public void open() throws TTransportException {
+ if (isOpen())
+ throw new TTransportException(TTransportException.ALREADY_OPEN);
+
+ try {
+ inputStream_ = createInputStream();
+ cs = new ChunkState();
+ currentEvent_ = new Event(new byte [256]);
+
+ if(!readOnly_)
+ outputStream_ = new BufferedOutputStream(inputFile_.getOutputStream());
+ } catch (IOException iox) {
+ throw new TTransportException(TTransportException.NOT_OPEN, iox);
+ }
+ }
+
+ /**
+ * Closes the transport.
+ */
+ public void close() {
+ if (inputFile_ != null) {
+ try {
+ inputFile_.close();
+ } catch (IOException iox) {
+ LOGGER.warn("WARNING: Error closing input file: " +
+ iox.getMessage());
+ }
+ inputFile_ = null;
+ }
+ if (outputStream_ != null) {
+ try {
+ outputStream_.close();
+ } catch (IOException iox) {
+ LOGGER.warn("WARNING: Error closing output stream: " +
+ iox.getMessage());
+ }
+ outputStream_ = null;
+ }
+ }
+
+
+ /**
+ * File Transport ctor
+ *
+ * @param path File path to read and write from
+ * @param readOnly Whether this is a read-only transport
+ */
+ public TFileTransport(final String path, boolean readOnly) throws IOException {
+ inputFile_ = new TStandardFile(path);
+ readOnly_ = readOnly;
+ }
+
+ /**
+ * File Transport ctor
+ *
+ * @param inputFile open TSeekableFile to read/write from
+ * @param readOnly Whether this is a read-only transport
+ */
+ public TFileTransport(TSeekableFile inputFile, boolean readOnly) {
+ inputFile_ = inputFile;
+ readOnly_ = readOnly;
+ }
+
+
+ /**
+ * Cloned from TTransport.java:readAll(). Only difference is throwing an EOF exception
+ * where one is detected.
+ */
+ public int readAll(byte[] buf, int off, int len)
+ throws TTransportException {
+ int got = 0;
+ int ret = 0;
+ while (got < len) {
+ ret = read(buf, off+got, len-got);
+ if (ret < 0) {
+ throw new TTransportException("Error in reading from file");
+ }
+ if(ret == 0) {
+ throw new TTransportException(TTransportException.END_OF_FILE,
+ "End of File reached");
+ }
+ got += ret;
+ }
+ return got;
+ }
+
+
+ /**
+ * Reads up to len bytes into buffer buf, starting at offset off.
+ *
+ * @param buf Array to read into
+ * @param off Index to start reading at
+ * @param len Maximum number of bytes to read
+ * @return The number of bytes actually read
+ * @throws TTransportException if there was an error reading data
+ */
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ if(!isOpen())
+ throw new TTransportException(TTransportException.NOT_OPEN,
+ "Must open before reading");
+
+ if(currentEvent_.getRemaining() == 0) {
+ if(!readEvent())
+ return(0);
+ }
+
+ int nread = currentEvent_.emit(buf, off, len);
+ return nread;
+ }
+
+ public int getNumChunks() throws TTransportException {
+ if(!isOpen())
+ throw new TTransportException(TTransportException.NOT_OPEN,
+ "Must open before getNumChunks");
+ try {
+ long len = inputFile_.length();
+ if(len == 0)
+ return 0;
+ else
+ return (((int)(len/cs.getChunkSize())) + 1);
+
+ } catch (IOException iox) {
+ throw new TTransportException(iox.getMessage(), iox);
+ }
+ }
+
+ public int getCurChunk() throws TTransportException {
+ if(!isOpen())
+ throw new TTransportException(TTransportException.NOT_OPEN,
+ "Must open before getCurChunk");
+ return (cs.getChunkNum());
+
+ }
+
+
+ public void seekToChunk(int chunk) throws TTransportException {
+ if(!isOpen())
+ throw new TTransportException(TTransportException.NOT_OPEN,
+ "Must open before seeking");
+
+ int numChunks = getNumChunks();
+
+ // file is empty, seeking to chunk is pointless
+ if (numChunks == 0) {
+ return;
+ }
+
+ // negative indicates reverse seek (from the end)
+ if (chunk < 0) {
+ chunk += numChunks;
+ }
+
+ // too large a value for reverse seek, just seek to beginning
+ if (chunk < 0) {
+ chunk = 0;
+ }
+
+ long eofOffset=0;
+ boolean seekToEnd = (chunk >= numChunks);
+ if(seekToEnd) {
+ chunk = chunk - 1;
+ try { eofOffset = inputFile_.length(); }
+ catch (IOException iox) {throw new TTransportException(iox.getMessage(),
+ iox);}
+ }
+
+ if(chunk*cs.getChunkSize() != cs.getOffset()) {
+ try { inputFile_.seek((long)chunk*cs.getChunkSize()); }
+ catch (IOException iox) {
+ throw new TTransportException("Seek to chunk " +
+ chunk + " " +iox.getMessage(), iox);
+ }
+
+ cs.seek((long)chunk*cs.getChunkSize());
+ currentEvent_.setAvailable(0);
+ inputStream_ = createInputStream();
+ }
+
+ if(seekToEnd) {
+ // waiting forever here - otherwise we can hit EOF and end up
+ // having consumed partial data from the data stream.
+ TailPolicy old = setTailPolicy(TailPolicy.WAIT_FOREVER);
+ while(cs.getOffset() < eofOffset) { readEvent(); }
+ currentEvent_.setAvailable(0);
+ setTailPolicy(old);
+ }
+ }
+
+ public void seekToEnd() throws TTransportException {
+ if(!isOpen())
+ throw new TTransportException(TTransportException.NOT_OPEN,
+ "Must open before seeking");
+ seekToChunk(getNumChunks());
+ }
+
+
+ /**
+ * Writes up to len bytes from the buffer.
+ *
+ * @param buf The output data buffer
+ * @param off The offset to start writing from
+ * @param len The number of bytes to write
+ * @throws TTransportException if there was an error writing data
+ */
+ public void write(byte[] buf, int off, int len) throws TTransportException {
+ throw new TTransportException("Not Supported");
+ }
+
+ /**
+ * Flush any pending data out of a transport buffer.
+ *
+ * @throws TTransportException if there was an error writing out data.
+ */
+ public void flush() throws TTransportException {
+ throw new TTransportException("Not Supported");
+ }
+
+ /**
+ * test program
+ *
+ */
+ public static void main(String[] args) throws Exception {
+
+ int num_chunks = 10;
+
+ if((args.length < 1) || args[0].equals("--help")
+ || args[0].equals("-h") || args[0].equals("-?")) {
+ printUsage();
+ }
+
+ if(args.length > 1) {
+ try {
+ num_chunks = Integer.parseInt(args[1]);
+ } catch (Exception e) {
+ LOGGER.error("Cannot parse " + args[1]);
+ printUsage();
+ }
+ }
+
+ TFileTransport t = new TFileTransport(args[0], true);
+ t.open();
+ LOGGER.info("NumChunks="+t.getNumChunks());
+
+ Random r = new Random();
+ for(int j=0; j<num_chunks; j++) {
+ byte[] buf = new byte[4096];
+ int cnum = r.nextInt(t.getNumChunks()-1);
+ LOGGER.info("Reading chunk "+cnum);
+ t.seekToChunk(cnum);
+ for(int i=0; i<4096; i++) {
+ t.read(buf, 0, 4096);
+ }
+ }
+ }
+
+ private static void printUsage() {
+ LOGGER.error("Usage: TFileTransport <filename> [num_chunks]");
+ LOGGER.error(" (Opens and reads num_chunks chunks from file randomly)");
+ System.exit(1);
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFramedTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFramedTransport.java
new file mode 100644
index 000000000..a006c3a6a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFramedTransport.java
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import org.apache.thrift.TByteArrayOutputStream;
+
+/**
+ * TFramedTransport is a buffered TTransport that ensures a fully read message
+ * every time by preceding messages with a 4-byte frame size.
+ */
+public class TFramedTransport extends TTransport {
+
+ protected static final int DEFAULT_MAX_LENGTH = 16384000;
+
+ private int maxLength_;
+
+ /**
+ * Underlying transport
+ */
+ private TTransport transport_ = null;
+
+ /**
+ * Buffer for output
+ */
+ private final TByteArrayOutputStream writeBuffer_ =
+ new TByteArrayOutputStream(1024);
+
+ /**
+ * Buffer for input
+ */
+ private final TMemoryInputTransport readBuffer_ =
+ new TMemoryInputTransport(new byte[0]);
+
+ public static class Factory extends TTransportFactory {
+ private int maxLength_;
+
+ public Factory() {
+ maxLength_ = TFramedTransport.DEFAULT_MAX_LENGTH;
+ }
+
+ public Factory(int maxLength) {
+ maxLength_ = maxLength;
+ }
+
+ @Override
+ public TTransport getTransport(TTransport base) {
+ return new TFramedTransport(base, maxLength_);
+ }
+ }
+
+ /**
+ * Something to fill in the first four bytes of the buffer
+ * to make room for the frame size. This allows the
+ * implementation to write once instead of twice.
+ */
+ private static final byte[] sizeFiller_ = new byte[] { 0x00, 0x00, 0x00, 0x00 };
+
+ /**
+ * Constructor wraps around another transport
+ */
+ public TFramedTransport(TTransport transport, int maxLength) {
+ transport_ = transport;
+ maxLength_ = maxLength;
+ writeBuffer_.write(sizeFiller_, 0, 4);
+ }
+
+ public TFramedTransport(TTransport transport) {
+ transport_ = transport;
+ maxLength_ = TFramedTransport.DEFAULT_MAX_LENGTH;
+ writeBuffer_.write(sizeFiller_, 0, 4);
+ }
+
+ public void open() throws TTransportException {
+ transport_.open();
+ }
+
+ public boolean isOpen() {
+ return transport_.isOpen();
+ }
+
+ public void close() {
+ transport_.close();
+ }
+
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ int got = readBuffer_.read(buf, off, len);
+ if (got > 0) {
+ return got;
+ }
+
+ // Read another frame of data
+ readFrame();
+
+ return readBuffer_.read(buf, off, len);
+ }
+
+ @Override
+ public byte[] getBuffer() {
+ return readBuffer_.getBuffer();
+ }
+
+ @Override
+ public int getBufferPosition() {
+ return readBuffer_.getBufferPosition();
+ }
+
+ @Override
+ public int getBytesRemainingInBuffer() {
+ return readBuffer_.getBytesRemainingInBuffer();
+ }
+
+ @Override
+ public void consumeBuffer(int len) {
+ readBuffer_.consumeBuffer(len);
+ }
+
+ public void clear() {
+ readBuffer_.clear();
+ }
+
+ private final byte[] i32buf = new byte[4];
+
+ private void readFrame() throws TTransportException {
+ transport_.readAll(i32buf, 0, 4);
+ int size = decodeFrameSize(i32buf);
+
+ if (size < 0) {
+ close();
+ throw new TTransportException(TTransportException.CORRUPTED_DATA, "Read a negative frame size (" + size + ")!");
+ }
+
+ if (size > maxLength_) {
+ close();
+ throw new TTransportException(TTransportException.CORRUPTED_DATA,
+ "Frame size (" + size + ") larger than max length (" + maxLength_ + ")!");
+ }
+
+ byte[] buff = new byte[size];
+ transport_.readAll(buff, 0, size);
+ readBuffer_.reset(buff);
+ }
+
+ public void write(byte[] buf, int off, int len) throws TTransportException {
+ writeBuffer_.write(buf, off, len);
+ }
+
+ @Override
+ public void flush() throws TTransportException {
+ byte[] buf = writeBuffer_.get();
+ int len = writeBuffer_.len() - 4; // account for the prepended frame size
+ writeBuffer_.reset();
+ writeBuffer_.write(sizeFiller_, 0, 4); // make room for the next frame's size data
+
+ encodeFrameSize(len, buf); // this is the frame length without the filler
+ transport_.write(buf, 0, len + 4); // we have to write the frame size and frame data
+ transport_.flush();
+ }
+
+ public static final void encodeFrameSize(final int frameSize, final byte[] buf) {
+ buf[0] = (byte)(0xff & (frameSize >> 24));
+ buf[1] = (byte)(0xff & (frameSize >> 16));
+ buf[2] = (byte)(0xff & (frameSize >> 8));
+ buf[3] = (byte)(0xff & (frameSize));
+ }
+
+ public static final int decodeFrameSize(final byte[] buf) {
+ return
+ ((buf[0] & 0xff) << 24) |
+ ((buf[1] & 0xff) << 16) |
+ ((buf[2] & 0xff) << 8) |
+ ((buf[3] & 0xff));
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/THttpClient.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/THttpClient.java
new file mode 100644
index 000000000..c3063fe43
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/THttpClient.java
@@ -0,0 +1,362 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+import java.net.URL;
+import java.net.HttpURLConnection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.params.CoreConnectionPNames;
+
+/**
+ * HTTP implementation of the TTransport interface. Used for working with a
+ * Thrift web services implementation (using for example TServlet).
+ *
+ * This class offers two implementations of the HTTP transport.
+ * One uses HttpURLConnection instances, the other HttpClient from Apache
+ * Http Components.
+ * The chosen implementation depends on the constructor used to
+ * create the THttpClient instance.
+ * Using the THttpClient(String url) constructor or passing null as the
+ * HttpClient to THttpClient(String url, HttpClient client) will create an
+ * instance which will use HttpURLConnection.
+ *
+ * When using HttpClient, the following configuration leads to 5-15%
+ * better performance than the HttpURLConnection implementation:
+ *
+ * http.protocol.version=HttpVersion.HTTP_1_1
+ * http.protocol.content-charset=UTF-8
+ * http.protocol.expect-continue=false
+ * http.connection.stalecheck=false
+ *
+ * Also note that under high load, the HttpURLConnection implementation
+ * may exhaust the open file descriptor limit.
+ *
+ * @see <a href="https://issues.apache.org/jira/browse/THRIFT-970">THRIFT-970</a>
+ */
+
+public class THttpClient extends TTransport {
+
+ private URL url_ = null;
+
+ private final ByteArrayOutputStream requestBuffer_ = new ByteArrayOutputStream();
+
+ private InputStream inputStream_ = null;
+
+ private int connectTimeout_ = 0;
+
+ private int readTimeout_ = 0;
+
+ private Map<String,String> customHeaders_ = null;
+
+ private final HttpHost host;
+
+ private final HttpClient client;
+
+ public static class Factory extends TTransportFactory {
+
+ private final String url;
+ private final HttpClient client;
+
+ public Factory(String url) {
+ this.url = url;
+ this.client = null;
+ }
+
+ public Factory(String url, HttpClient client) {
+ this.url = url;
+ this.client = client;
+ }
+
+ @Override
+ public TTransport getTransport(TTransport trans) {
+ try {
+ if (null != client) {
+ return new THttpClient(url, client);
+ } else {
+ return new THttpClient(url);
+ }
+ } catch (TTransportException tte) {
+ return null;
+ }
+ }
+ }
+
+ public THttpClient(String url) throws TTransportException {
+ try {
+ url_ = new URL(url);
+ this.client = null;
+ this.host = null;
+ } catch (IOException iox) {
+ throw new TTransportException(iox);
+ }
+ }
+
+ public THttpClient(String url, HttpClient client) throws TTransportException {
+ try {
+ url_ = new URL(url);
+ this.client = client;
+ this.host = new HttpHost(url_.getHost(), -1 == url_.getPort() ? url_.getDefaultPort() : url_.getPort(), url_.getProtocol());
+ } catch (IOException iox) {
+ throw new TTransportException(iox);
+ }
+ }
+
+ public void setConnectTimeout(int timeout) {
+ connectTimeout_ = timeout;
+ if (null != this.client) {
+ // WARNING, this modifies the HttpClient params, this might have an impact elsewhere if the
+ // same HttpClient is used for something else.
+ client.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, connectTimeout_);
+ }
+ }
+
+ public void setReadTimeout(int timeout) {
+ readTimeout_ = timeout;
+ if (null != this.client) {
+ // WARNING, this modifies the HttpClient params, this might have an impact elsewhere if the
+ // same HttpClient is used for something else.
+ client.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, readTimeout_);
+ }
+ }
+
+ public void setCustomHeaders(Map<String,String> headers) {
+ customHeaders_ = headers;
+ }
+
+ public void setCustomHeader(String key, String value) {
+ if (customHeaders_ == null) {
+ customHeaders_ = new HashMap<String, String>();
+ }
+ customHeaders_.put(key, value);
+ }
+
+ public void open() {}
+
+ public void close() {
+ if (null != inputStream_) {
+ try {
+ inputStream_.close();
+ } catch (IOException ioe) {
+ ;
+ }
+ inputStream_ = null;
+ }
+ }
+
+ public boolean isOpen() {
+ return true;
+ }
+
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ if (inputStream_ == null) {
+ throw new TTransportException("Response buffer is empty, no request.");
+ }
+ try {
+ int ret = inputStream_.read(buf, off, len);
+ if (ret == -1) {
+ throw new TTransportException("No more data available.");
+ }
+ return ret;
+ } catch (IOException iox) {
+ throw new TTransportException(iox);
+ }
+ }
+
+ public void write(byte[] buf, int off, int len) {
+ requestBuffer_.write(buf, off, len);
+ }
+
+ /**
+ * copy from org.apache.http.util.EntityUtils#consume. Android has it's own httpcore
+ * that doesn't have a consume.
+ */
+ private static void consume(final HttpEntity entity) throws IOException {
+ if (entity == null) {
+ return;
+ }
+ if (entity.isStreaming()) {
+ InputStream instream = entity.getContent();
+ if (instream != null) {
+ instream.close();
+ }
+ }
+ }
+
+ private void flushUsingHttpClient() throws TTransportException {
+
+ if (null == this.client) {
+ throw new TTransportException("Null HttpClient, aborting.");
+ }
+
+ // Extract request and reset buffer
+ byte[] data = requestBuffer_.toByteArray();
+ requestBuffer_.reset();
+
+ HttpPost post = null;
+
+ InputStream is = null;
+
+ try {
+ // Set request to path + query string
+ post = new HttpPost(this.url_.getFile());
+
+ //
+ // Headers are added to the HttpPost instance, not
+ // to HttpClient.
+ //
+
+ post.setHeader("Content-Type", "application/x-thrift");
+ post.setHeader("Accept", "application/x-thrift");
+ post.setHeader("User-Agent", "Java/THttpClient/HC");
+
+ if (null != customHeaders_) {
+ for (Map.Entry<String, String> header : customHeaders_.entrySet()) {
+ post.setHeader(header.getKey(), header.getValue());
+ }
+ }
+
+ post.setEntity(new ByteArrayEntity(data));
+
+ HttpResponse response = this.client.execute(this.host, post);
+ int responseCode = response.getStatusLine().getStatusCode();
+
+ //
+ // Retrieve the inputstream BEFORE checking the status code so
+ // resources get freed in the finally clause.
+ //
+
+ is = response.getEntity().getContent();
+
+ if (responseCode != HttpStatus.SC_OK) {
+ throw new TTransportException("HTTP Response code: " + responseCode);
+ }
+
+ // Read the responses into a byte array so we can release the connection
+ // early. This implies that the whole content will have to be read in
+ // memory, and that momentarily we might use up twice the memory (while the
+ // thrift struct is being read up the chain).
+ // Proceeding differently might lead to exhaustion of connections and thus
+ // to app failure.
+
+ byte[] buf = new byte[1024];
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ int len = 0;
+ do {
+ len = is.read(buf);
+ if (len > 0) {
+ baos.write(buf, 0, len);
+ }
+ } while (-1 != len);
+
+ try {
+ // Indicate we're done with the content.
+ consume(response.getEntity());
+ } catch (IOException ioe) {
+ // We ignore this exception, it might only mean the server has no
+ // keep-alive capability.
+ }
+
+ inputStream_ = new ByteArrayInputStream(baos.toByteArray());
+ } catch (IOException ioe) {
+ // Abort method so the connection gets released back to the connection manager
+ if (null != post) {
+ post.abort();
+ }
+ throw new TTransportException(ioe);
+ } finally {
+ if (null != is) {
+ // Close the entity's input stream, this will release the underlying connection
+ try {
+ is.close();
+ } catch (IOException ioe) {
+ throw new TTransportException(ioe);
+ }
+ }
+ if (post != null) {
+ post.releaseConnection();
+ }
+ }
+ }
+
+ public void flush() throws TTransportException {
+
+ if (null != this.client) {
+ flushUsingHttpClient();
+ return;
+ }
+
+ // Extract request and reset buffer
+ byte[] data = requestBuffer_.toByteArray();
+ requestBuffer_.reset();
+
+ try {
+ // Create connection object
+ HttpURLConnection connection = (HttpURLConnection)url_.openConnection();
+
+ // Timeouts, only if explicitly set
+ if (connectTimeout_ > 0) {
+ connection.setConnectTimeout(connectTimeout_);
+ }
+ if (readTimeout_ > 0) {
+ connection.setReadTimeout(readTimeout_);
+ }
+
+ // Make the request
+ connection.setRequestMethod("POST");
+ connection.setRequestProperty("Content-Type", "application/x-thrift");
+ connection.setRequestProperty("Accept", "application/x-thrift");
+ connection.setRequestProperty("User-Agent", "Java/THttpClient");
+ if (customHeaders_ != null) {
+ for (Map.Entry<String, String> header : customHeaders_.entrySet()) {
+ connection.setRequestProperty(header.getKey(), header.getValue());
+ }
+ }
+ connection.setDoOutput(true);
+ connection.connect();
+ connection.getOutputStream().write(data);
+
+ int responseCode = connection.getResponseCode();
+ if (responseCode != HttpURLConnection.HTTP_OK) {
+ throw new TTransportException("HTTP Response code: " + responseCode);
+ }
+
+ // Read the responses
+ inputStream_ = connection.getInputStream();
+
+ } catch (IOException iox) {
+ throw new TTransportException(iox);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TIOStreamTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TIOStreamTransport.java
new file mode 100644
index 000000000..2d31f392f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TIOStreamTransport.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * This is the most commonly used base transport. It takes an InputStream
+ * and an OutputStream and uses those to perform all transport operations.
+ * This allows for compatibility with all the nice constructs Java already
+ * has to provide a variety of types of streams.
+ *
+ */
+public class TIOStreamTransport extends TTransport {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TIOStreamTransport.class.getName());
+
+ /** Underlying inputStream */
+ protected InputStream inputStream_ = null;
+
+ /** Underlying outputStream */
+ protected OutputStream outputStream_ = null;
+
+ /**
+ * Subclasses can invoke the default constructor and then assign the input
+ * streams in the open method.
+ */
+ protected TIOStreamTransport() {}
+
+ /**
+ * Input stream constructor.
+ *
+ * @param is Input stream to read from
+ */
+ public TIOStreamTransport(InputStream is) {
+ inputStream_ = is;
+ }
+
+ /**
+ * Output stream constructor.
+ *
+ * @param os Output stream to read from
+ */
+ public TIOStreamTransport(OutputStream os) {
+ outputStream_ = os;
+ }
+
+ /**
+ * Two-way stream constructor.
+ *
+ * @param is Input stream to read from
+ * @param os Output stream to read from
+ */
+ public TIOStreamTransport(InputStream is, OutputStream os) {
+ inputStream_ = is;
+ outputStream_ = os;
+ }
+
+ /**
+ *
+ * @return false after close is called.
+ */
+ public boolean isOpen() {
+ return inputStream_ != null && outputStream_ != null;
+ }
+
+ /**
+ * The streams must already be open. This method does nothing.
+ */
+ public void open() throws TTransportException {}
+
+ /**
+ * Closes both the input and output streams.
+ */
+ public void close() {
+ if (inputStream_ != null) {
+ try {
+ inputStream_.close();
+ } catch (IOException iox) {
+ LOGGER.warn("Error closing input stream.", iox);
+ }
+ inputStream_ = null;
+ }
+ if (outputStream_ != null) {
+ try {
+ outputStream_.close();
+ } catch (IOException iox) {
+ LOGGER.warn("Error closing output stream.", iox);
+ }
+ outputStream_ = null;
+ }
+ }
+
+ /**
+ * Reads from the underlying input stream if not null.
+ */
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ if (inputStream_ == null) {
+ throw new TTransportException(TTransportException.NOT_OPEN, "Cannot read from null inputStream");
+ }
+ int bytesRead;
+ try {
+ bytesRead = inputStream_.read(buf, off, len);
+ } catch (IOException iox) {
+ throw new TTransportException(TTransportException.UNKNOWN, iox);
+ }
+ if (bytesRead < 0) {
+ throw new TTransportException(TTransportException.END_OF_FILE, "Socket is closed by peer.");
+ }
+ return bytesRead;
+ }
+
+ /**
+ * Writes to the underlying output stream if not null.
+ */
+ public void write(byte[] buf, int off, int len) throws TTransportException {
+ if (outputStream_ == null) {
+ throw new TTransportException(TTransportException.NOT_OPEN, "Cannot write to null outputStream");
+ }
+ try {
+ outputStream_.write(buf, off, len);
+ } catch (IOException iox) {
+ throw new TTransportException(TTransportException.UNKNOWN, iox);
+ }
+ }
+
+ /**
+ * Flushes the underlying output stream if not null.
+ */
+ public void flush() throws TTransportException {
+ if (outputStream_ == null) {
+ throw new TTransportException(TTransportException.NOT_OPEN, "Cannot flush null outputStream");
+ }
+ try {
+ outputStream_.flush();
+ } catch (IOException iox) {
+ throw new TTransportException(TTransportException.UNKNOWN, iox);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TMemoryBuffer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TMemoryBuffer.java
new file mode 100644
index 000000000..b19ac86d2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TMemoryBuffer.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import org.apache.thrift.TByteArrayOutputStream;
+import java.nio.charset.Charset;
+
+/**
+ * Memory buffer-based implementation of the TTransport interface.
+ */
+public class TMemoryBuffer extends TTransport {
+ /**
+ * Create a TMemoryBuffer with an initial buffer size of <i>size</i>. The
+ * internal buffer will grow as necessary to accommodate the size of the data
+ * being written to it.
+ *
+ * @param size the initial size of the buffer
+ */
+ public TMemoryBuffer(int size) {
+ arr_ = new TByteArrayOutputStream(size);
+ }
+
+ @Override
+ public boolean isOpen() {
+ return true;
+ }
+
+ @Override
+ public void open() {
+ /* Do nothing */
+ }
+
+ @Override
+ public void close() {
+ /* Do nothing */
+ }
+
+ @Override
+ public int read(byte[] buf, int off, int len) {
+ byte[] src = arr_.get();
+ int amtToRead = (len > arr_.len() - pos_ ? arr_.len() - pos_ : len);
+ if (amtToRead > 0) {
+ System.arraycopy(src, pos_, buf, off, amtToRead);
+ pos_ += amtToRead;
+ }
+ return amtToRead;
+ }
+
+ @Override
+ public void write(byte[] buf, int off, int len) {
+ arr_.write(buf, off, len);
+ }
+
+ /**
+ * Output the contents of the memory buffer as a String, using the supplied
+ * encoding
+ * @param charset the encoding to use
+ * @return the contents of the memory buffer as a String
+ */
+ public String toString(Charset charset) {
+ return arr_.toString(charset);
+ }
+
+ public String inspect() {
+ StringBuilder buf = new StringBuilder();
+ byte[] bytes = arr_.toByteArray();
+ for (int i = 0; i < bytes.length; i++) {
+ buf.append(pos_ == i ? "==>" : "" ).append(Integer.toHexString(bytes[i] & 0xff)).append(" ");
+ }
+ return buf.toString();
+ }
+
+ // The contents of the buffer
+ private TByteArrayOutputStream arr_;
+
+ // Position to read next byte from
+ private int pos_;
+
+ public int length() {
+ return arr_.size();
+ }
+
+ public byte[] getArray() {
+ return arr_.get();
+ }
+}
+
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TMemoryInputTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TMemoryInputTransport.java
new file mode 100644
index 000000000..2530dcc36
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TMemoryInputTransport.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.transport;
+
+public final class TMemoryInputTransport extends TTransport {
+
+ private byte[] buf_;
+ private int pos_;
+ private int endPos_;
+
+ public TMemoryInputTransport() {
+ }
+
+ public TMemoryInputTransport(byte[] buf) {
+ reset(buf);
+ }
+
+ public TMemoryInputTransport(byte[] buf, int offset, int length) {
+ reset(buf, offset, length);
+ }
+
+ public void reset(byte[] buf) {
+ reset(buf, 0, buf.length);
+ }
+
+ public void reset(byte[] buf, int offset, int length) {
+ buf_ = buf;
+ pos_ = offset;
+ endPos_ = offset + length;
+ }
+
+ public void clear() {
+ buf_ = null;
+ }
+
+ @Override
+ public void close() {}
+
+ @Override
+ public boolean isOpen() {
+ return true;
+ }
+
+ @Override
+ public void open() throws TTransportException {}
+
+ @Override
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ int bytesRemaining = getBytesRemainingInBuffer();
+ int amtToRead = (len > bytesRemaining ? bytesRemaining : len);
+ if (amtToRead > 0) {
+ System.arraycopy(buf_, pos_, buf, off, amtToRead);
+ consumeBuffer(amtToRead);
+ }
+ return amtToRead;
+ }
+
+ @Override
+ public void write(byte[] buf, int off, int len) throws TTransportException {
+ throw new UnsupportedOperationException("No writing allowed!");
+ }
+
+ @Override
+ public byte[] getBuffer() {
+ return buf_;
+ }
+
+ public int getBufferPosition() {
+ return pos_;
+ }
+
+ public int getBytesRemainingInBuffer() {
+ return endPos_ - pos_;
+ }
+
+ public void consumeBuffer(int len) {
+ pos_ += len;
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingServerSocket.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingServerSocket.java
new file mode 100644
index 000000000..df37cb06e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingServerSocket.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package org.apache.thrift.transport;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.SocketException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Wrapper around ServerSocketChannel
+ */
+public class TNonblockingServerSocket extends TNonblockingServerTransport {
+ private static final Logger LOGGER = LoggerFactory.getLogger(TNonblockingServerSocket.class.getName());
+
+ /**
+ * This channel is where all the nonblocking magic happens.
+ */
+ private ServerSocketChannel serverSocketChannel = null;
+
+ /**
+ * Underlying ServerSocket object
+ */
+ private ServerSocket serverSocket_ = null;
+
+ /**
+ * Timeout for client sockets from accept
+ */
+ private int clientTimeout_ = 0;
+
+ public static class NonblockingAbstractServerSocketArgs extends
+ AbstractServerTransportArgs<NonblockingAbstractServerSocketArgs> {}
+
+ /**
+ * Creates just a port listening server socket
+ */
+ public TNonblockingServerSocket(int port) throws TTransportException {
+ this(port, 0);
+ }
+
+ /**
+ * Creates just a port listening server socket
+ */
+ public TNonblockingServerSocket(int port, int clientTimeout) throws TTransportException {
+ this(new NonblockingAbstractServerSocketArgs().port(port).clientTimeout(clientTimeout));
+ }
+
+ public TNonblockingServerSocket(InetSocketAddress bindAddr) throws TTransportException {
+ this(bindAddr, 0);
+ }
+
+ public TNonblockingServerSocket(InetSocketAddress bindAddr, int clientTimeout) throws TTransportException {
+ this(new NonblockingAbstractServerSocketArgs().bindAddr(bindAddr).clientTimeout(clientTimeout));
+ }
+
+ public TNonblockingServerSocket(NonblockingAbstractServerSocketArgs args) throws TTransportException {
+ clientTimeout_ = args.clientTimeout;
+ try {
+ serverSocketChannel = ServerSocketChannel.open();
+ serverSocketChannel.configureBlocking(false);
+
+ // Make server socket
+ serverSocket_ = serverSocketChannel.socket();
+ // Prevent 2MSL delay problem on server restarts
+ serverSocket_.setReuseAddress(true);
+ // Bind to listening port
+ serverSocket_.bind(args.bindAddr, args.backlog);
+ } catch (IOException ioe) {
+ serverSocket_ = null;
+ throw new TTransportException("Could not create ServerSocket on address " + args.bindAddr.toString() + ".", ioe);
+ }
+ }
+
+ public void listen() throws TTransportException {
+ // Make sure not to block on accept
+ if (serverSocket_ != null) {
+ try {
+ serverSocket_.setSoTimeout(0);
+ } catch (SocketException sx) {
+ LOGGER.error("Socket exception while setting socket timeout", sx);
+ }
+ }
+ }
+
+ protected TNonblockingSocket acceptImpl() throws TTransportException {
+ if (serverSocket_ == null) {
+ throw new TTransportException(TTransportException.NOT_OPEN, "No underlying server socket.");
+ }
+ try {
+ SocketChannel socketChannel = serverSocketChannel.accept();
+ if (socketChannel == null) {
+ return null;
+ }
+
+ TNonblockingSocket tsocket = new TNonblockingSocket(socketChannel);
+ tsocket.setTimeout(clientTimeout_);
+ return tsocket;
+ } catch (IOException iox) {
+ throw new TTransportException(iox);
+ }
+ }
+
+ public void registerSelector(Selector selector) {
+ try {
+ // Register the server socket channel, indicating an interest in
+ // accepting new connections
+ serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
+ } catch (ClosedChannelException e) {
+ // this shouldn't happen, ideally...
+ // TODO: decide what to do with this.
+ }
+ }
+
+ public void close() {
+ if (serverSocket_ != null) {
+ try {
+ serverSocket_.close();
+ } catch (IOException iox) {
+ LOGGER.warn("WARNING: Could not close server socket: " + iox.getMessage());
+ }
+ serverSocket_ = null;
+ }
+ }
+
+ public void interrupt() {
+ // The thread-safeness of this is dubious, but Java documentation suggests
+ // that it is safe to do this from a different thread context
+ close();
+ }
+
+ public int getPort() {
+ if (serverSocket_ == null)
+ return -1;
+ return serverSocket_.getLocalPort();
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingServerTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingServerTransport.java
new file mode 100644
index 000000000..ba45b09dc
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingServerTransport.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package org.apache.thrift.transport;
+
+import java.nio.channels.Selector;
+
+/**
+ * Server transport that can be operated in a nonblocking fashion.
+ */
+public abstract class TNonblockingServerTransport extends TServerTransport {
+
+ public abstract void registerSelector(Selector selector);
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingSocket.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingSocket.java
new file mode 100644
index 000000000..f86a48b42
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingSocket.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package org.apache.thrift.transport;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.SocketChannel;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Transport for use with async client.
+ */
+public class TNonblockingSocket extends TNonblockingTransport {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TNonblockingSocket.class.getName());
+
+ /**
+ * Host and port if passed in, used for lazy non-blocking connect.
+ */
+ private final SocketAddress socketAddress_;
+
+ private final SocketChannel socketChannel_;
+
+ public TNonblockingSocket(String host, int port) throws IOException {
+ this(host, port, 0);
+ }
+
+ /**
+ * Create a new nonblocking socket transport that will be connected to host:port.
+ * @param host
+ * @param port
+ * @throws IOException
+ */
+ public TNonblockingSocket(String host, int port, int timeout) throws IOException {
+ this(SocketChannel.open(), timeout, new InetSocketAddress(host, port));
+ }
+
+ /**
+ * Constructor that takes an already created socket.
+ *
+ * @param socketChannel Already created SocketChannel object
+ * @throws IOException if there is an error setting up the streams
+ */
+ public TNonblockingSocket(SocketChannel socketChannel) throws IOException {
+ this(socketChannel, 0, null);
+ if (!socketChannel.isConnected()) throw new IOException("Socket must already be connected");
+ }
+
+ private TNonblockingSocket(SocketChannel socketChannel, int timeout, SocketAddress socketAddress)
+ throws IOException {
+ socketChannel_ = socketChannel;
+ socketAddress_ = socketAddress;
+
+ // make it a nonblocking channel
+ socketChannel.configureBlocking(false);
+
+ // set options
+ Socket socket = socketChannel.socket();
+ socket.setSoLinger(false, 0);
+ socket.setTcpNoDelay(true);
+ socket.setKeepAlive(true);
+ setTimeout(timeout);
+ }
+
+ /**
+ * Register the new SocketChannel with our Selector, indicating
+ * we'd like to be notified when it's ready for I/O.
+ *
+ * @param selector
+ * @return the selection key for this socket.
+ */
+ public SelectionKey registerSelector(Selector selector, int interests) throws IOException {
+ return socketChannel_.register(selector, interests);
+ }
+
+ /**
+ * Sets the socket timeout, although this implementation never uses blocking operations so it is unused.
+ *
+ * @param timeout Milliseconds timeout
+ */
+ public void setTimeout(int timeout) {
+ try {
+ socketChannel_.socket().setSoTimeout(timeout);
+ } catch (SocketException sx) {
+ LOGGER.warn("Could not set socket timeout.", sx);
+ }
+ }
+
+ /**
+ * Returns a reference to the underlying SocketChannel.
+ */
+ public SocketChannel getSocketChannel() {
+ return socketChannel_;
+ }
+
+ /**
+ * Checks whether the socket is connected.
+ */
+ public boolean isOpen() {
+ // isConnected() does not return false after close(), but isOpen() does
+ return socketChannel_.isOpen() && socketChannel_.isConnected();
+ }
+
+ /**
+ * Do not call, the implementation provides its own lazy non-blocking connect.
+ */
+ public void open() throws TTransportException {
+ throw new RuntimeException("open() is not implemented for TNonblockingSocket");
+ }
+
+ /**
+ * Perform a nonblocking read into buffer.
+ */
+ public int read(ByteBuffer buffer) throws IOException {
+ return socketChannel_.read(buffer);
+ }
+
+
+ /**
+ * Reads from the underlying input stream if not null.
+ */
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ if ((socketChannel_.validOps() & SelectionKey.OP_READ) != SelectionKey.OP_READ) {
+ throw new TTransportException(TTransportException.NOT_OPEN,
+ "Cannot read from write-only socket channel");
+ }
+ try {
+ return socketChannel_.read(ByteBuffer.wrap(buf, off, len));
+ } catch (IOException iox) {
+ throw new TTransportException(TTransportException.UNKNOWN, iox);
+ }
+ }
+
+ /**
+ * Perform a nonblocking write of the data in buffer;
+ */
+ public int write(ByteBuffer buffer) throws IOException {
+ return socketChannel_.write(buffer);
+ }
+
+ /**
+ * Writes to the underlying output stream if not null.
+ */
+ public void write(byte[] buf, int off, int len) throws TTransportException {
+ if ((socketChannel_.validOps() & SelectionKey.OP_WRITE) != SelectionKey.OP_WRITE) {
+ throw new TTransportException(TTransportException.NOT_OPEN,
+ "Cannot write to write-only socket channel");
+ }
+ try {
+ socketChannel_.write(ByteBuffer.wrap(buf, off, len));
+ } catch (IOException iox) {
+ throw new TTransportException(TTransportException.UNKNOWN, iox);
+ }
+ }
+
+ /**
+ * Noop.
+ */
+ public void flush() throws TTransportException {
+ // Not supported by SocketChannel.
+ }
+
+ /**
+ * Closes the socket.
+ */
+ public void close() {
+ try {
+ socketChannel_.close();
+ } catch (IOException iox) {
+ LOGGER.warn("Could not close socket.", iox);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public boolean startConnect() throws IOException {
+ return socketChannel_.connect(socketAddress_);
+ }
+
+ /** {@inheritDoc} */
+ public boolean finishConnect() throws IOException {
+ return socketChannel_.finishConnect();
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingTransport.java
new file mode 100644
index 000000000..43c130688
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingTransport.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.io.IOException;
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+
+public abstract class TNonblockingTransport extends TTransport {
+
+ /**
+ * Non-blocking connection initialization.
+ * @see java.nio.channels.SocketChannel#connect(SocketAddress remote)
+ */
+ public abstract boolean startConnect() throws IOException;
+
+ /**
+ * Non-blocking connection completion.
+ * @see java.nio.channels.SocketChannel#finishConnect()
+ */
+ public abstract boolean finishConnect() throws IOException;
+
+ public abstract SelectionKey registerSelector(Selector selector, int interests) throws IOException;
+
+ public abstract int read(ByteBuffer buffer) throws IOException;
+
+ public abstract int write(ByteBuffer buffer) throws IOException;
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSSLTransportFactory.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSSLTransportFactory.java
new file mode 100644
index 000000000..73dfaaf18
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSSLTransportFactory.java
@@ -0,0 +1,447 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.URL;
+import java.net.MalformedURLException;
+import java.security.KeyStore;
+import java.util.Arrays;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManagerFactory;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A Factory for providing and setting up Client and Server SSL wrapped
+ * TSocket and TServerSocket
+ */
+public class TSSLTransportFactory {
+
+ private static final Logger LOGGER =
+ LoggerFactory.getLogger(TSSLTransportFactory.class);
+
+ /**
+ * Get a SSL wrapped TServerSocket bound to the specified port. In this
+ * configuration the default settings are used. Default settings are retrieved
+ * from System properties that are set.
+ *
+ * Example system properties:
+ * -Djavax.net.ssl.trustStore=&lt;truststore location&gt;
+ * -Djavax.net.ssl.trustStorePassword=password
+ * -Djavax.net.ssl.keyStore=&lt;keystore location&gt;
+ * -Djavax.net.ssl.keyStorePassword=password
+ *
+ * @param port
+ * @return A SSL wrapped TServerSocket
+ * @throws TTransportException
+ */
+ public static TServerSocket getServerSocket(int port) throws TTransportException {
+ return getServerSocket(port, 0);
+ }
+
+ /**
+ * Get a default SSL wrapped TServerSocket bound to the specified port
+ *
+ * @param port
+ * @param clientTimeout
+ * @return A SSL wrapped TServerSocket
+ * @throws TTransportException
+ */
+ public static TServerSocket getServerSocket(int port, int clientTimeout) throws TTransportException {
+ return getServerSocket(port, clientTimeout, false, null);
+ }
+
+ /**
+ * Get a default SSL wrapped TServerSocket bound to the specified port and interface
+ *
+ * @param port
+ * @param clientTimeout
+ * @param ifAddress
+ * @return A SSL wrapped TServerSocket
+ * @throws TTransportException
+ */
+ public static TServerSocket getServerSocket(int port, int clientTimeout, boolean clientAuth, InetAddress ifAddress) throws TTransportException {
+ SSLServerSocketFactory factory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
+ return createServer(factory, port, clientTimeout, clientAuth, ifAddress, null);
+ }
+
+ /**
+ * Get a configured SSL wrapped TServerSocket bound to the specified port and interface.
+ * Here the TSSLTransportParameters are used to set the values for the algorithms, keystore,
+ * truststore and other settings
+ *
+ * @param port
+ * @param clientTimeout
+ * @param ifAddress
+ * @param params
+ * @return A SSL wrapped TServerSocket
+ * @throws TTransportException
+ */
+ public static TServerSocket getServerSocket(int port, int clientTimeout, InetAddress ifAddress, TSSLTransportParameters params) throws TTransportException {
+ if (params == null || !(params.isKeyStoreSet || params.isTrustStoreSet)) {
+ throw new TTransportException("Either one of the KeyStore or TrustStore must be set for SSLTransportParameters");
+ }
+
+ SSLContext ctx = createSSLContext(params);
+ return createServer(ctx.getServerSocketFactory(), port, clientTimeout, params.clientAuth, ifAddress, params);
+ }
+
+ private static TServerSocket createServer(SSLServerSocketFactory factory, int port, int timeout, boolean clientAuth,
+ InetAddress ifAddress, TSSLTransportParameters params) throws TTransportException {
+ try {
+ SSLServerSocket serverSocket = (SSLServerSocket) factory.createServerSocket(port, 100, ifAddress);
+ serverSocket.setSoTimeout(timeout);
+ serverSocket.setNeedClientAuth(clientAuth);
+ if (params != null && params.cipherSuites != null) {
+ serverSocket.setEnabledCipherSuites(params.cipherSuites);
+ }
+ return new TServerSocket(new TServerSocket.ServerSocketTransportArgs().
+ serverSocket(serverSocket).clientTimeout(timeout));
+ } catch (Exception e) {
+ throw new TTransportException("Could not bind to port " + port, e);
+ }
+ }
+
+ /**
+ * Get a default SSL wrapped TSocket connected to the specified host and port. All
+ * the client methods return a bound connection. So there is no need to call open() on the
+ * TTransport.
+ *
+ * @param host
+ * @param port
+ * @param timeout
+ * @return A SSL wrapped TSocket
+ * @throws TTransportException
+ */
+ public static TSocket getClientSocket(String host, int port, int timeout) throws TTransportException {
+ SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
+ return createClient(factory, host, port, timeout);
+ }
+
+ /**
+ * Get a default SSL wrapped TSocket connected to the specified host and port.
+ *
+ * @param host
+ * @param port
+ * @return A SSL wrapped TSocket
+ * @throws TTransportException
+ */
+ public static TSocket getClientSocket(String host, int port) throws TTransportException {
+ return getClientSocket(host, port, 0);
+ }
+
+ /**
+ * Get a custom configured SSL wrapped TSocket. The SSL settings are obtained from the
+ * passed in TSSLTransportParameters.
+ *
+ * @param host
+ * @param port
+ * @param timeout
+ * @param params
+ * @return A SSL wrapped TSocket
+ * @throws TTransportException
+ */
+ public static TSocket getClientSocket(String host, int port, int timeout, TSSLTransportParameters params) throws TTransportException {
+ if (params == null || !(params.isKeyStoreSet || params.isTrustStoreSet)) {
+ throw new TTransportException("Either one of the KeyStore or TrustStore must be set for SSLTransportParameters");
+ }
+
+ SSLContext ctx = createSSLContext(params);
+ return createClient(ctx.getSocketFactory(), host, port, timeout);
+ }
+
+ private static SSLContext createSSLContext(TSSLTransportParameters params) throws TTransportException {
+ SSLContext ctx;
+ InputStream in = null;
+ InputStream is = null;
+
+ try {
+ ctx = SSLContext.getInstance(params.protocol);
+ TrustManagerFactory tmf = null;
+ KeyManagerFactory kmf = null;
+
+ if (params.isTrustStoreSet) {
+ tmf = TrustManagerFactory.getInstance(params.trustManagerType);
+ KeyStore ts = KeyStore.getInstance(params.trustStoreType);
+ if (params.trustStoreStream != null) {
+ in = params.trustStoreStream;
+ } else {
+ in = getStoreAsStream(params.trustStore);
+ }
+ ts.load(in,
+ (params.trustPass != null ? params.trustPass.toCharArray() : null));
+ tmf.init(ts);
+ }
+
+ if (params.isKeyStoreSet) {
+ kmf = KeyManagerFactory.getInstance(params.keyManagerType);
+ KeyStore ks = KeyStore.getInstance(params.keyStoreType);
+ if (params.keyStoreStream != null) {
+ is = params.keyStoreStream;
+ } else {
+ is = getStoreAsStream(params.keyStore);
+ }
+ ks.load(is, params.keyPass.toCharArray());
+ kmf.init(ks, params.keyPass.toCharArray());
+ }
+
+ if (params.isKeyStoreSet && params.isTrustStoreSet) {
+ ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+ }
+ else if (params.isKeyStoreSet) {
+ ctx.init(kmf.getKeyManagers(), null, null);
+ }
+ else {
+ ctx.init(null, tmf.getTrustManagers(), null);
+ }
+
+ } catch (Exception e) {
+ throw new TTransportException("Error creating the transport", e);
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ LOGGER.warn("Unable to close stream", e);
+ }
+ }
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ LOGGER.warn("Unable to close stream", e);
+ }
+ }
+ }
+
+ return ctx;
+ }
+
+ private static InputStream getStoreAsStream(String store) throws IOException {
+ try {
+ return new FileInputStream(store);
+ } catch(FileNotFoundException e) {
+ }
+
+ InputStream storeStream = null;
+ try {
+ storeStream = new URL(store).openStream();
+ if (storeStream != null) {
+ return storeStream;
+ }
+ } catch(MalformedURLException e) {
+ }
+
+ storeStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(store);
+
+ if (storeStream != null) {
+ return storeStream;
+ } else {
+ throw new IOException("Could not load file: " + store);
+ }
+ }
+
+ private static TSocket createClient(SSLSocketFactory factory, String host, int port, int timeout) throws TTransportException {
+ try {
+ SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
+ socket.setSoTimeout(timeout);
+ return new TSocket(socket);
+ } catch (Exception e) {
+ throw new TTransportException("Could not connect to " + host + " on port " + port, e);
+ }
+ }
+
+
+ /**
+ * A Class to hold all the SSL parameters
+ */
+ public static class TSSLTransportParameters {
+ protected String protocol = "TLS";
+ protected String keyStore;
+ protected InputStream keyStoreStream;
+ protected String keyPass;
+ protected String keyManagerType = KeyManagerFactory.getDefaultAlgorithm();
+ protected String keyStoreType = "JKS";
+ protected String trustStore;
+ protected InputStream trustStoreStream;
+ protected String trustPass;
+ protected String trustManagerType = TrustManagerFactory.getDefaultAlgorithm();
+ protected String trustStoreType = "JKS";
+ protected String[] cipherSuites;
+ protected boolean clientAuth = false;
+ protected boolean isKeyStoreSet = false;
+ protected boolean isTrustStoreSet = false;
+
+ public TSSLTransportParameters() {}
+
+ /**
+ * Create parameters specifying the protocol and cipher suites
+ *
+ * @param protocol The specific protocol (TLS/SSL) can be specified with versions
+ * @param cipherSuites
+ */
+ public TSSLTransportParameters(String protocol, String[] cipherSuites) {
+ this(protocol, cipherSuites, false);
+ }
+
+ /**
+ * Create parameters specifying the protocol, cipher suites and if client authentication
+ * is required
+ *
+ * @param protocol The specific protocol (TLS/SSL) can be specified with versions
+ * @param cipherSuites
+ * @param clientAuth
+ */
+ public TSSLTransportParameters(String protocol, String[] cipherSuites, boolean clientAuth) {
+ if (protocol != null) {
+ this.protocol = protocol;
+ }
+ this.cipherSuites = cipherSuites != null ? Arrays.copyOf(cipherSuites, cipherSuites.length) : null;
+ this.clientAuth = clientAuth;
+ }
+
+ /**
+ * Set the keystore, password, certificate type and the store type
+ *
+ * @param keyStore Location of the Keystore on disk
+ * @param keyPass Keystore password
+ * @param keyManagerType The default is X509
+ * @param keyStoreType The default is JKS
+ */
+ public void setKeyStore(String keyStore, String keyPass, String keyManagerType, String keyStoreType) {
+ this.keyStore = keyStore;
+ this.keyPass = keyPass;
+ if (keyManagerType != null) {
+ this.keyManagerType = keyManagerType;
+ }
+ if (keyStoreType != null) {
+ this.keyStoreType = keyStoreType;
+ }
+ isKeyStoreSet = true;
+ }
+
+ /**
+ * Set the keystore, password, certificate type and the store type
+ *
+ * @param keyStoreStream Keystore content input stream
+ * @param keyPass Keystore password
+ * @param keyManagerType The default is X509
+ * @param keyStoreType The default is JKS
+ */
+ public void setKeyStore(InputStream keyStoreStream, String keyPass, String keyManagerType, String keyStoreType) {
+ this.keyStoreStream = keyStoreStream;
+ setKeyStore("", keyPass, keyManagerType, keyStoreType);
+ }
+
+ /**
+ * Set the keystore and password
+ *
+ * @param keyStore Location of the Keystore on disk
+ * @param keyPass Keystore password
+ */
+ public void setKeyStore(String keyStore, String keyPass) {
+ setKeyStore(keyStore, keyPass, null, null);
+ }
+
+ /**
+ * Set the keystore and password
+ *
+ * @param keyStoreStream Keystore content input stream
+ * @param keyPass Keystore password
+ */
+ public void setKeyStore(InputStream keyStoreStream, String keyPass) {
+ setKeyStore(keyStoreStream, keyPass, null, null);
+ }
+
+ /**
+ * Set the truststore, password, certificate type and the store type
+ *
+ * @param trustStore Location of the Truststore on disk
+ * @param trustPass Truststore password
+ * @param trustManagerType The default is X509
+ * @param trustStoreType The default is JKS
+ */
+ public void setTrustStore(String trustStore, String trustPass, String trustManagerType, String trustStoreType) {
+ this.trustStore = trustStore;
+ this.trustPass = trustPass;
+ if (trustManagerType != null) {
+ this.trustManagerType = trustManagerType;
+ }
+ if (trustStoreType != null) {
+ this.trustStoreType = trustStoreType;
+ }
+ isTrustStoreSet = true;
+ }
+
+ /**
+ * Set the truststore, password, certificate type and the store type
+ *
+ * @param trustStoreStream Truststore content input stream
+ * @param trustPass Truststore password
+ * @param trustManagerType The default is X509
+ * @param trustStoreType The default is JKS
+ */
+ public void setTrustStore(InputStream trustStoreStream, String trustPass, String trustManagerType, String trustStoreType) {
+ this.trustStoreStream = trustStoreStream;
+ setTrustStore("", trustPass, trustManagerType, trustStoreType);
+ }
+
+ /**
+ * Set the truststore and password
+ *
+ * @param trustStore Location of the Truststore on disk
+ * @param trustPass Truststore password
+ */
+ public void setTrustStore(String trustStore, String trustPass) {
+ setTrustStore(trustStore, trustPass, null, null);
+ }
+
+ /**
+ * Set the truststore and password
+ *
+ * @param trustStoreStream Truststore content input stream
+ * @param trustPass Truststore password
+ */
+ public void setTrustStore(InputStream trustStoreStream, String trustPass) {
+ setTrustStore(trustStoreStream, trustPass, null, null);
+ }
+
+ /**
+ * Set if client authentication is required
+ *
+ * @param clientAuth
+ */
+ public void requireClientAuth(boolean clientAuth) {
+ this.clientAuth = clientAuth;
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslClientTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslClientTransport.java
new file mode 100644
index 000000000..4b1ca0a94
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslClientTransport.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Wraps another Thrift <code>TTransport</code>, but performs SASL client
+ * negotiation on the call to <code>open()</code>. This class will wrap ensuing
+ * communication over it, if a SASL QOP is negotiated with the other party.
+ */
+public class TSaslClientTransport extends TSaslTransport {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TSaslClientTransport.class);
+
+ /**
+ * The name of the mechanism this client supports.
+ */
+ private final String mechanism;
+
+ /**
+ * Uses the given <code>SaslClient</code>.
+ *
+ * @param saslClient
+ * The <code>SaslClient</code> to use for the subsequent SASL
+ * negotiation.
+ * @param transport
+ * Transport underlying this one.
+ */
+ public TSaslClientTransport(SaslClient saslClient, TTransport transport) {
+ super(saslClient, transport);
+ mechanism = saslClient.getMechanismName();
+ }
+
+ /**
+ * Creates a <code>SaslClient</code> using the given SASL-specific parameters.
+ * See the Java documentation for <code>Sasl.createSaslClient</code> for the
+ * details of the parameters.
+ *
+ * @param transport
+ * The underlying Thrift transport.
+ * @throws SaslException
+ */
+ public TSaslClientTransport(String mechanism, String authorizationId, String protocol,
+ String serverName, Map<String, String> props, CallbackHandler cbh, TTransport transport)
+ throws SaslException {
+ super(Sasl.createSaslClient(new String[] { mechanism }, authorizationId, protocol, serverName,
+ props, cbh), transport);
+ this.mechanism = mechanism;
+ }
+
+
+ @Override
+ protected SaslRole getRole() {
+ return SaslRole.CLIENT;
+ }
+
+ /**
+ * Performs the client side of the initial portion of the Thrift SASL
+ * protocol. Generates and sends the initial response to the server, including
+ * which mechanism this client wants to use.
+ */
+ @Override
+ protected void handleSaslStartMessage() throws TTransportException, SaslException {
+ SaslClient saslClient = getSaslClient();
+
+ byte[] initialResponse = new byte[0];
+ if (saslClient.hasInitialResponse())
+ initialResponse = saslClient.evaluateChallenge(initialResponse);
+
+ LOGGER.debug("Sending mechanism name {} and initial response of length {}", mechanism,
+ initialResponse.length);
+
+ byte[] mechanismBytes = mechanism.getBytes(StandardCharsets.UTF_8);
+ sendSaslMessage(NegotiationStatus.START,
+ mechanismBytes);
+ // Send initial response
+ sendSaslMessage(saslClient.isComplete() ? NegotiationStatus.COMPLETE : NegotiationStatus.OK,
+ initialResponse);
+ underlyingTransport.flush();
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslServerTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslServerTransport.java
new file mode 100644
index 000000000..39b81ca43
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslServerTransport.java
@@ -0,0 +1,230 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.lang.ref.WeakReference;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Wraps another Thrift <code>TTransport</code>, but performs SASL server
+ * negotiation on the call to <code>open()</code>. This class will wrap ensuing
+ * communication over it, if a SASL QOP is negotiated with the other party.
+ */
+public class TSaslServerTransport extends TSaslTransport {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TSaslServerTransport.class);
+
+ /**
+ * Mapping from SASL mechanism name -> all the parameters required to
+ * instantiate a SASL server.
+ */
+ private Map<String, TSaslServerDefinition> serverDefinitionMap = new HashMap<String, TSaslServerDefinition>();
+
+ /**
+ * Contains all the parameters used to define a SASL server implementation.
+ */
+ private static class TSaslServerDefinition {
+ public String mechanism;
+ public String protocol;
+ public String serverName;
+ public Map<String, String> props;
+ public CallbackHandler cbh;
+
+ public TSaslServerDefinition(String mechanism, String protocol, String serverName,
+ Map<String, String> props, CallbackHandler cbh) {
+ this.mechanism = mechanism;
+ this.protocol = protocol;
+ this.serverName = serverName;
+ this.props = props;
+ this.cbh = cbh;
+ }
+ }
+
+ /**
+ * Uses the given underlying transport. Assumes that addServerDefinition is
+ * called later.
+ *
+ * @param transport
+ * Transport underlying this one.
+ */
+ public TSaslServerTransport(TTransport transport) {
+ super(transport);
+ }
+
+ /**
+ * Creates a <code>SaslServer</code> using the given SASL-specific parameters.
+ * See the Java documentation for <code>Sasl.createSaslServer</code> for the
+ * details of the parameters.
+ *
+ * @param transport
+ * The underlying Thrift transport.
+ */
+ public TSaslServerTransport(String mechanism, String protocol, String serverName,
+ Map<String, String> props, CallbackHandler cbh, TTransport transport) {
+ super(transport);
+ addServerDefinition(mechanism, protocol, serverName, props, cbh);
+ }
+
+ private TSaslServerTransport(Map<String, TSaslServerDefinition> serverDefinitionMap, TTransport transport) {
+ super(transport);
+ this.serverDefinitionMap.putAll(serverDefinitionMap);
+ }
+
+ /**
+ * Add a supported server definition to this transport. See the Java
+ * documentation for <code>Sasl.createSaslServer</code> for the details of the
+ * parameters.
+ */
+ public void addServerDefinition(String mechanism, String protocol, String serverName,
+ Map<String, String> props, CallbackHandler cbh) {
+ serverDefinitionMap.put(mechanism, new TSaslServerDefinition(mechanism, protocol, serverName,
+ props, cbh));
+ }
+
+ @Override
+ protected SaslRole getRole() {
+ return SaslRole.SERVER;
+ }
+
+ /**
+ * Performs the server side of the initial portion of the Thrift SASL protocol.
+ * Receives the initial response from the client, creates a SASL server using
+ * the mechanism requested by the client (if this server supports it), and
+ * sends the first challenge back to the client.
+ */
+ @Override
+ protected void handleSaslStartMessage() throws TTransportException, SaslException {
+ SaslResponse message = receiveSaslMessage();
+
+ LOGGER.debug("Received start message with status {}", message.status);
+ if (message.status != NegotiationStatus.START) {
+ throw sendAndThrowMessage(NegotiationStatus.ERROR, "Expecting START status, received " + message.status);
+ }
+
+ // Get the mechanism name.
+ String mechanismName = new String(message.payload, StandardCharsets.UTF_8);
+ TSaslServerDefinition serverDefinition = serverDefinitionMap.get(mechanismName);
+ LOGGER.debug("Received mechanism name '{}'", mechanismName);
+
+ if (serverDefinition == null) {
+ throw sendAndThrowMessage(NegotiationStatus.BAD, "Unsupported mechanism type " + mechanismName);
+ }
+ SaslServer saslServer = Sasl.createSaslServer(serverDefinition.mechanism,
+ serverDefinition.protocol, serverDefinition.serverName, serverDefinition.props,
+ serverDefinition.cbh);
+ setSaslServer(saslServer);
+ }
+
+ /**
+ * <code>TTransportFactory</code> to create
+ * <code>TSaslServerTransports</code>. Ensures that a given
+ * underlying <code>TTransport</code> instance receives the same
+ * <code>TSaslServerTransport</code>. This is kind of an awful hack to work
+ * around the fact that Thrift is designed assuming that
+ * <code>TTransport</code> instances are stateless, and thus the existing
+ * <code>TServers</code> use different <code>TTransport</code> instances for
+ * input and output.
+ */
+ public static class Factory extends TTransportFactory {
+
+ /**
+ * This is the implementation of the awful hack described above.
+ * <code>WeakHashMap</code> is used to ensure that we don't leak memory.
+ */
+ private static Map<TTransport, WeakReference<TSaslServerTransport>> transportMap =
+ Collections.synchronizedMap(new WeakHashMap<TTransport, WeakReference<TSaslServerTransport>>());
+
+ /**
+ * Mapping from SASL mechanism name -> all the parameters required to
+ * instantiate a SASL server.
+ */
+ private Map<String, TSaslServerDefinition> serverDefinitionMap = new HashMap<String, TSaslServerDefinition>();
+
+ /**
+ * Create a new Factory. Assumes that <code>addServerDefinition</code> will
+ * be called later.
+ */
+ public Factory() {
+ super();
+ }
+
+ /**
+ * Create a new <code>Factory</code>, initially with the single server
+ * definition given. You may still call <code>addServerDefinition</code>
+ * later. See the Java documentation for <code>Sasl.createSaslServer</code>
+ * for the details of the parameters.
+ */
+ public Factory(String mechanism, String protocol, String serverName,
+ Map<String, String> props, CallbackHandler cbh) {
+ super();
+ addServerDefinition(mechanism, protocol, serverName, props, cbh);
+ }
+
+ /**
+ * Add a supported server definition to the transports created by this
+ * factory. See the Java documentation for
+ * <code>Sasl.createSaslServer</code> for the details of the parameters.
+ */
+ public void addServerDefinition(String mechanism, String protocol, String serverName,
+ Map<String, String> props, CallbackHandler cbh) {
+ serverDefinitionMap.put(mechanism, new TSaslServerDefinition(mechanism, protocol, serverName,
+ props, cbh));
+ }
+
+ /**
+ * Get a new <code>TSaslServerTransport</code> instance, or reuse the
+ * existing one if a <code>TSaslServerTransport</code> has already been
+ * created before using the given <code>TTransport</code> as an underlying
+ * transport. This ensures that a given underlying transport instance
+ * receives the same <code>TSaslServerTransport</code>.
+ */
+ @Override
+ public TTransport getTransport(TTransport base) {
+ WeakReference<TSaslServerTransport> ret = transportMap.get(base);
+ if (ret == null || ret.get() == null) {
+ LOGGER.debug("transport map does not contain key", base);
+ ret = new WeakReference<TSaslServerTransport>(new TSaslServerTransport(serverDefinitionMap, base));
+ try {
+ ret.get().open();
+ } catch (TTransportException e) {
+ LOGGER.debug("failed to open server transport", e);
+ throw new RuntimeException(e);
+ }
+ transportMap.put(base, ret); // No need for putIfAbsent().
+ // Concurrent calls to getTransport() will pass in different TTransports.
+ } else {
+ LOGGER.debug("transport map does contain key {}", base);
+ }
+ return ret.get();
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslTransport.java
new file mode 100644
index 000000000..4a453b68f
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslTransport.java
@@ -0,0 +1,575 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+
+import org.apache.thrift.EncodingUtils;
+import org.apache.thrift.TByteArrayOutputStream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A superclass for SASL client/server thrift transports. A subclass need only
+ * implement the <code>open</code> method.
+ */
+abstract class TSaslTransport extends TTransport {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TSaslTransport.class);
+
+ protected static final int DEFAULT_MAX_LENGTH = 0x7FFFFFFF;
+
+ protected static final int MECHANISM_NAME_BYTES = 1;
+ protected static final int STATUS_BYTES = 1;
+ protected static final int PAYLOAD_LENGTH_BYTES = 4;
+
+ protected static enum SaslRole {
+ SERVER, CLIENT;
+ }
+
+ /**
+ * Status bytes used during the initial Thrift SASL handshake.
+ */
+ protected static enum NegotiationStatus {
+ START((byte)0x01),
+ OK((byte)0x02),
+ BAD((byte)0x03),
+ ERROR((byte)0x04),
+ COMPLETE((byte)0x05);
+
+ private final byte value;
+
+ private static final Map<Byte, NegotiationStatus> reverseMap =
+ new HashMap<Byte, NegotiationStatus>();
+ static {
+ for (NegotiationStatus s : NegotiationStatus.class.getEnumConstants()) {
+ reverseMap.put(s.getValue(), s);
+ }
+ }
+
+ private NegotiationStatus(byte val) {
+ this.value = val;
+ }
+
+ public byte getValue() {
+ return value;
+ }
+
+ public static NegotiationStatus byValue(byte val) {
+ return reverseMap.get(val);
+ }
+ }
+
+ /**
+ * Transport underlying this one.
+ */
+ protected TTransport underlyingTransport;
+
+ /**
+ * Either a SASL client or a SASL server.
+ */
+ private SaslParticipant sasl;
+
+ /**
+ * Whether or not we should wrap/unwrap reads/writes. Determined by whether or
+ * not a QOP is negotiated during the SASL handshake.
+ */
+ private boolean shouldWrap = false;
+
+ /**
+ * Buffer for input.
+ */
+ private TMemoryInputTransport readBuffer = new TMemoryInputTransport();
+
+ /**
+ * Buffer for output.
+ */
+ private final TByteArrayOutputStream writeBuffer = new TByteArrayOutputStream(1024);
+
+ /**
+ * Create a TSaslTransport. It's assumed that setSaslServer will be called
+ * later to initialize the SASL endpoint underlying this transport.
+ *
+ * @param underlyingTransport
+ * The thrift transport which this transport is wrapping.
+ */
+ protected TSaslTransport(TTransport underlyingTransport) {
+ this.underlyingTransport = underlyingTransport;
+ }
+
+ /**
+ * Create a TSaslTransport which acts as a client.
+ *
+ * @param saslClient
+ * The <code>SaslClient</code> which this transport will use for SASL
+ * negotiation.
+ * @param underlyingTransport
+ * The thrift transport which this transport is wrapping.
+ */
+ protected TSaslTransport(SaslClient saslClient, TTransport underlyingTransport) {
+ sasl = new SaslParticipant(saslClient);
+ this.underlyingTransport = underlyingTransport;
+ }
+
+ protected void setSaslServer(SaslServer saslServer) {
+ sasl = new SaslParticipant(saslServer);
+ }
+
+ // Used to read the status byte and payload length.
+ private final byte[] messageHeader = new byte[STATUS_BYTES + PAYLOAD_LENGTH_BYTES];
+
+ /**
+ * Send a complete Thrift SASL message.
+ *
+ * @param status
+ * The status to send.
+ * @param payload
+ * The data to send as the payload of this message.
+ * @throws TTransportException
+ */
+ protected void sendSaslMessage(NegotiationStatus status, byte[] payload) throws TTransportException {
+ if (payload == null)
+ payload = new byte[0];
+
+ messageHeader[0] = status.getValue();
+ EncodingUtils.encodeBigEndian(payload.length, messageHeader, STATUS_BYTES);
+
+ LOGGER.debug("{}: Writing message with status {} and payload length {}",
+ getRole(), status, payload.length);
+
+ underlyingTransport.write(messageHeader);
+ underlyingTransport.write(payload);
+ underlyingTransport.flush();
+ }
+
+ /**
+ * Read a complete Thrift SASL message.
+ *
+ * @return The SASL status and payload from this message.
+ * @throws TTransportException
+ * Thrown if there is a failure reading from the underlying
+ * transport, or if a status code of BAD or ERROR is encountered.
+ */
+ protected SaslResponse receiveSaslMessage() throws TTransportException {
+ underlyingTransport.readAll(messageHeader, 0, messageHeader.length);
+
+ byte statusByte = messageHeader[0];
+
+ NegotiationStatus status = NegotiationStatus.byValue(statusByte);
+ if (status == null) {
+ throw sendAndThrowMessage(NegotiationStatus.ERROR, "Invalid status " + statusByte);
+ }
+
+ int payloadBytes = EncodingUtils.decodeBigEndian(messageHeader, STATUS_BYTES);
+ if (payloadBytes < 0 || payloadBytes > 104857600 /* 100 MB */) {
+ throw sendAndThrowMessage(
+ NegotiationStatus.ERROR, "Invalid payload header length: " + payloadBytes);
+ }
+
+ byte[] payload = new byte[payloadBytes];
+ underlyingTransport.readAll(payload, 0, payload.length);
+
+ if (status == NegotiationStatus.BAD || status == NegotiationStatus.ERROR) {
+ String remoteMessage = new String(payload, StandardCharsets.UTF_8);
+ throw new TTransportException("Peer indicated failure: " + remoteMessage);
+ }
+ LOGGER.debug("{}: Received message with status {} and payload length {}",
+ getRole(), status, payload.length);
+ return new SaslResponse(status, payload);
+ }
+
+ /**
+ * Send a Thrift SASL message with the given status (usually BAD or ERROR) and
+ * string message, and then throw a TTransportException with the given
+ * message.
+ *
+ * @param status
+ * The Thrift SASL status code to send. Usually BAD or ERROR.
+ * @param message
+ * The optional message to send to the other side.
+ * @throws TTransportException
+ * Always thrown with the message provided.
+ * @return always throws TTransportException but declares return type to allow
+ * throw sendAndThrowMessage(...) to inform compiler control flow
+ */
+ protected TTransportException sendAndThrowMessage(NegotiationStatus status, String message) throws TTransportException {
+ try {
+ sendSaslMessage(status, message.getBytes(StandardCharsets.UTF_8));
+ } catch (Exception e) {
+ LOGGER.warn("Could not send failure response", e);
+ message += "\nAlso, could not send response: " + e.toString();
+ }
+ throw new TTransportException(message);
+ }
+
+ /**
+ * Implemented by subclasses to start the Thrift SASL handshake process. When
+ * this method completes, the <code>SaslParticipant</code> in this class is
+ * assumed to be initialized.
+ *
+ * @throws TTransportException
+ * @throws SaslException
+ */
+ abstract protected void handleSaslStartMessage() throws TTransportException, SaslException;
+
+ protected abstract SaslRole getRole();
+
+ /**
+ * Opens the underlying transport if it's not already open and then performs
+ * SASL negotiation. If a QOP is negotiated during this SASL handshake, it used
+ * for all communication on this transport after this call is complete.
+ */
+ @Override
+ public void open() throws TTransportException {
+ /*
+ * readSaslHeader is used to tag whether the SASL header has been read properly.
+ * If there is a problem in reading the header, there might not be any
+ * data in the stream, possibly a TCP health check from load balancer.
+ */
+ boolean readSaslHeader = false;
+
+ LOGGER.debug("opening transport {}", this);
+ if (sasl != null && sasl.isComplete())
+ throw new TTransportException("SASL transport already open");
+
+ if (!underlyingTransport.isOpen())
+ underlyingTransport.open();
+
+ try {
+ // Negotiate a SASL mechanism. The client also sends its
+ // initial response, or an empty one.
+ handleSaslStartMessage();
+ readSaslHeader = true;
+ LOGGER.debug("{}: Start message handled", getRole());
+
+ SaslResponse message = null;
+ while (!sasl.isComplete()) {
+ message = receiveSaslMessage();
+ if (message.status != NegotiationStatus.COMPLETE &&
+ message.status != NegotiationStatus.OK) {
+ throw new TTransportException("Expected COMPLETE or OK, got " + message.status);
+ }
+
+ byte[] challenge = sasl.evaluateChallengeOrResponse(message.payload);
+
+ // If we are the client, and the server indicates COMPLETE, we don't need to
+ // send back any further response.
+ if (message.status == NegotiationStatus.COMPLETE &&
+ getRole() == SaslRole.CLIENT) {
+ LOGGER.debug("{}: All done!", getRole());
+ continue;
+ }
+
+ sendSaslMessage(sasl.isComplete() ? NegotiationStatus.COMPLETE : NegotiationStatus.OK,
+ challenge);
+ }
+ LOGGER.debug("{}: Main negotiation loop complete", getRole());
+
+ // If we're the client, and we're complete, but the server isn't
+ // complete yet, we need to wait for its response. This will occur
+ // with ANONYMOUS auth, for example, where we send an initial response
+ // and are immediately complete.
+ if (getRole() == SaslRole.CLIENT &&
+ (message == null || message.status == NegotiationStatus.OK)) {
+ LOGGER.debug("{}: SASL Client receiving last message", getRole());
+ message = receiveSaslMessage();
+ if (message.status != NegotiationStatus.COMPLETE) {
+ throw new TTransportException(
+ "Expected SASL COMPLETE, but got " + message.status);
+ }
+ }
+ } catch (SaslException e) {
+ try {
+ LOGGER.error("SASL negotiation failure", e);
+ throw sendAndThrowMessage(NegotiationStatus.BAD, e.getMessage());
+ } finally {
+ underlyingTransport.close();
+ }
+ } catch (TTransportException e) {
+ // If there is no-data or no-sasl header in the stream,
+ // log the failure, and clean up the underlying transport.
+ if (!readSaslHeader && e.getType() == TTransportException.END_OF_FILE) {
+ underlyingTransport.close();
+ LOGGER.debug("No data or no sasl data in the stream during negotiation");
+ }
+ throw e;
+ }
+
+ String qop = (String) sasl.getNegotiatedProperty(Sasl.QOP);
+ if (qop != null && !qop.equalsIgnoreCase("auth"))
+ shouldWrap = true;
+ }
+
+ /**
+ * Get the underlying <code>SaslClient</code>.
+ *
+ * @return The <code>SaslClient</code>, or <code>null</code> if this transport
+ * is backed by a <code>SaslServer</code>.
+ */
+ public SaslClient getSaslClient() {
+ return sasl.saslClient;
+ }
+
+ /**
+ * Get the underlying transport that Sasl is using.
+ * @return The <code>TTransport</code> transport
+ */
+ public TTransport getUnderlyingTransport() {
+ return underlyingTransport;
+ }
+
+ /**
+ * Get the underlying <code>SaslServer</code>.
+ *
+ * @return The <code>SaslServer</code>, or <code>null</code> if this transport
+ * is backed by a <code>SaslClient</code>.
+ */
+ public SaslServer getSaslServer() {
+ return sasl.saslServer;
+ }
+
+ /**
+ * Read a 4-byte word from the underlying transport and interpret it as an
+ * integer.
+ *
+ * @return The length prefix of the next SASL message to read.
+ * @throws TTransportException
+ * Thrown if reading from the underlying transport fails.
+ */
+ protected int readLength() throws TTransportException {
+ byte[] lenBuf = new byte[4];
+ underlyingTransport.readAll(lenBuf, 0, lenBuf.length);
+ return EncodingUtils.decodeBigEndian(lenBuf);
+ }
+
+ /**
+ * Write the given integer as 4 bytes to the underlying transport.
+ *
+ * @param length
+ * The length prefix of the next SASL message to write.
+ * @throws TTransportException
+ * Thrown if writing to the underlying transport fails.
+ */
+ protected void writeLength(int length) throws TTransportException {
+ byte[] lenBuf = new byte[4];
+ TFramedTransport.encodeFrameSize(length, lenBuf);
+ underlyingTransport.write(lenBuf);
+ }
+
+ // Below is the SASL implementation of the TTransport interface.
+
+ /**
+ * Closes the underlying transport and disposes of the SASL implementation
+ * underlying this transport.
+ */
+ @Override
+ public void close() {
+ underlyingTransport.close();
+ try {
+ sasl.dispose();
+ } catch (SaslException e) {
+ // Not much we can do here.
+ }
+ }
+
+ /**
+ * True if the underlying transport is open and the SASL handshake is
+ * complete.
+ */
+ @Override
+ public boolean isOpen() {
+ return underlyingTransport.isOpen() && sasl != null && sasl.isComplete();
+ }
+
+ /**
+ * Read from the underlying transport. Unwraps the contents if a QOP was
+ * negotiated during the SASL handshake.
+ */
+ @Override
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ if (!isOpen())
+ throw new TTransportException("SASL authentication not complete");
+
+ int got = readBuffer.read(buf, off, len);
+ if (got > 0) {
+ return got;
+ }
+
+ // Read another frame of data
+ try {
+ readFrame();
+ } catch (SaslException e) {
+ throw new TTransportException(e);
+ } catch (TTransportException transportException) {
+ // If there is no-data or no-sasl header in the stream, log the failure, and rethrow.
+ if (transportException.getType() == TTransportException.END_OF_FILE) {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("No data or no sasl data in the stream during negotiation");
+ }
+ }
+ throw transportException;
+ }
+
+ return readBuffer.read(buf, off, len);
+ }
+
+ /**
+ * Read a single frame of data from the underlying transport, unwrapping if
+ * necessary.
+ *
+ * @throws TTransportException
+ * Thrown if there's an error reading from the underlying transport.
+ * @throws SaslException
+ * Thrown if there's an error unwrapping the data.
+ */
+ private void readFrame() throws TTransportException, SaslException {
+ int dataLength = readLength();
+
+ if (dataLength < 0)
+ throw new TTransportException("Read a negative frame size (" + dataLength + ")!");
+
+ byte[] buff = new byte[dataLength];
+ LOGGER.debug("{}: reading data length: {}", getRole(), dataLength);
+ underlyingTransport.readAll(buff, 0, dataLength);
+ if (shouldWrap) {
+ buff = sasl.unwrap(buff, 0, buff.length);
+ LOGGER.debug("data length after unwrap: {}", buff.length);
+ }
+ readBuffer.reset(buff);
+ }
+
+ /**
+ * Write to the underlying transport.
+ */
+ @Override
+ public void write(byte[] buf, int off, int len) throws TTransportException {
+ if (!isOpen())
+ throw new TTransportException("SASL authentication not complete");
+
+ writeBuffer.write(buf, off, len);
+ }
+
+ /**
+ * Flushes to the underlying transport. Wraps the contents if a QOP was
+ * negotiated during the SASL handshake.
+ */
+ @Override
+ public void flush() throws TTransportException {
+ byte[] buf = writeBuffer.get();
+ int dataLength = writeBuffer.len();
+ writeBuffer.reset();
+
+ if (shouldWrap) {
+ LOGGER.debug("data length before wrap: {}", dataLength);
+ try {
+ buf = sasl.wrap(buf, 0, dataLength);
+ } catch (SaslException e) {
+ throw new TTransportException(e);
+ }
+ dataLength = buf.length;
+ }
+ LOGGER.debug("writing data length: {}", dataLength);
+ writeLength(dataLength);
+ underlyingTransport.write(buf, 0, dataLength);
+ underlyingTransport.flush();
+ }
+
+ /**
+ * Used exclusively by readSaslMessage to return both a status and data.
+ */
+ protected static class SaslResponse {
+ public NegotiationStatus status;
+ public byte[] payload;
+
+ public SaslResponse(NegotiationStatus status, byte[] payload) {
+ this.status = status;
+ this.payload = payload;
+ }
+ }
+
+ /**
+ * Used to abstract over the <code>SaslServer</code> and
+ * <code>SaslClient</code> classes, which share a lot of their interface, but
+ * unfortunately don't share a common superclass.
+ */
+ private static class SaslParticipant {
+ // One of these will always be null.
+ public SaslServer saslServer;
+ public SaslClient saslClient;
+
+ public SaslParticipant(SaslServer saslServer) {
+ this.saslServer = saslServer;
+ }
+
+ public SaslParticipant(SaslClient saslClient) {
+ this.saslClient = saslClient;
+ }
+
+ public byte[] evaluateChallengeOrResponse(byte[] challengeOrResponse) throws SaslException {
+ if (saslClient != null) {
+ return saslClient.evaluateChallenge(challengeOrResponse);
+ } else {
+ return saslServer.evaluateResponse(challengeOrResponse);
+ }
+ }
+
+ public boolean isComplete() {
+ if (saslClient != null)
+ return saslClient.isComplete();
+ else
+ return saslServer.isComplete();
+ }
+
+ public void dispose() throws SaslException {
+ if (saslClient != null)
+ saslClient.dispose();
+ else
+ saslServer.dispose();
+ }
+
+ public byte[] unwrap(byte[] buf, int off, int len) throws SaslException {
+ if (saslClient != null)
+ return saslClient.unwrap(buf, off, len);
+ else
+ return saslServer.unwrap(buf, off, len);
+ }
+
+ public byte[] wrap(byte[] buf, int off, int len) throws SaslException {
+ if (saslClient != null)
+ return saslClient.wrap(buf, off, len);
+ else
+ return saslServer.wrap(buf, off, len);
+ }
+
+ public Object getNegotiatedProperty(String propName) {
+ if (saslClient != null)
+ return saslClient.getNegotiatedProperty(propName);
+ else
+ return saslServer.getNegotiatedProperty(propName);
+ }
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSeekableFile.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSeekableFile.java
new file mode 100644
index 000000000..e02d36f6c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSeekableFile.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+
+public interface TSeekableFile {
+
+ public InputStream getInputStream() throws IOException;
+ public OutputStream getOutputStream() throws IOException;
+ public void close() throws IOException;
+ public long length() throws IOException;
+ public void seek(long pos) throws IOException;
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TServerSocket.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TServerSocket.java
new file mode 100644
index 000000000..79f7b7f49
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TServerSocket.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+
+/**
+ * Wrapper around ServerSocket for Thrift.
+ *
+ */
+public class TServerSocket extends TServerTransport {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TServerSocket.class.getName());
+
+ /**
+ * Underlying ServerSocket object
+ */
+ private ServerSocket serverSocket_ = null;
+
+ /**
+ * Timeout for client sockets from accept
+ */
+ private int clientTimeout_ = 0;
+
+ public static class ServerSocketTransportArgs extends AbstractServerTransportArgs<ServerSocketTransportArgs> {
+ ServerSocket serverSocket;
+
+ public ServerSocketTransportArgs serverSocket(ServerSocket serverSocket) {
+ this.serverSocket = serverSocket;
+ return this;
+ }
+ }
+
+ /**
+ * Creates a server socket from underlying socket object
+ */
+ public TServerSocket(ServerSocket serverSocket) throws TTransportException {
+ this(serverSocket, 0);
+ }
+
+ /**
+ * Creates a server socket from underlying socket object
+ */
+ public TServerSocket(ServerSocket serverSocket, int clientTimeout) throws TTransportException {
+ this(new ServerSocketTransportArgs().serverSocket(serverSocket).clientTimeout(clientTimeout));
+ }
+
+ /**
+ * Creates just a port listening server socket
+ */
+ public TServerSocket(int port) throws TTransportException {
+ this(port, 0);
+ }
+
+ /**
+ * Creates just a port listening server socket
+ */
+ public TServerSocket(int port, int clientTimeout) throws TTransportException {
+ this(new InetSocketAddress(port), clientTimeout);
+ }
+
+ public TServerSocket(InetSocketAddress bindAddr) throws TTransportException {
+ this(bindAddr, 0);
+ }
+
+ public TServerSocket(InetSocketAddress bindAddr, int clientTimeout) throws TTransportException {
+ this(new ServerSocketTransportArgs().bindAddr(bindAddr).clientTimeout(clientTimeout));
+ }
+
+ public TServerSocket(ServerSocketTransportArgs args) throws TTransportException {
+ clientTimeout_ = args.clientTimeout;
+ if (args.serverSocket != null) {
+ this.serverSocket_ = args.serverSocket;
+ return;
+ }
+ try {
+ // Make server socket
+ serverSocket_ = new ServerSocket();
+ // Prevent 2MSL delay problem on server restarts
+ serverSocket_.setReuseAddress(true);
+ // Bind to listening port
+ serverSocket_.bind(args.bindAddr, args.backlog);
+ } catch (IOException ioe) {
+ close();
+ throw new TTransportException("Could not create ServerSocket on address " + args.bindAddr.toString() + ".", ioe);
+ }
+ }
+
+ public void listen() throws TTransportException {
+ // Make sure to block on accept
+ if (serverSocket_ != null) {
+ try {
+ serverSocket_.setSoTimeout(0);
+ } catch (SocketException sx) {
+ LOGGER.error("Could not set socket timeout.", sx);
+ }
+ }
+ }
+
+ protected TSocket acceptImpl() throws TTransportException {
+ if (serverSocket_ == null) {
+ throw new TTransportException(TTransportException.NOT_OPEN, "No underlying server socket.");
+ }
+ try {
+ Socket result = serverSocket_.accept();
+ TSocket result2 = new TSocket(result);
+ result2.setTimeout(clientTimeout_);
+ return result2;
+ } catch (IOException iox) {
+ throw new TTransportException(iox);
+ }
+ }
+
+ public void close() {
+ if (serverSocket_ != null) {
+ try {
+ serverSocket_.close();
+ } catch (IOException iox) {
+ LOGGER.warn("Could not close server socket.", iox);
+ }
+ serverSocket_ = null;
+ }
+ }
+
+ public void interrupt() {
+ // The thread-safeness of this is dubious, but Java documentation suggests
+ // that it is safe to do this from a different thread context
+ close();
+ }
+
+ public ServerSocket getServerSocket() {
+ return serverSocket_;
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TServerTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TServerTransport.java
new file mode 100644
index 000000000..424e4faaa
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TServerTransport.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.io.Closeable;
+import java.net.InetSocketAddress;
+
+/**
+ * Server transport. Object which provides client transports.
+ *
+ */
+public abstract class TServerTransport implements Closeable {
+
+ public static abstract class AbstractServerTransportArgs<T extends AbstractServerTransportArgs<T>> {
+ int backlog = 0; // A value of 0 means the default value will be used (currently set at 50)
+ int clientTimeout = 0;
+ InetSocketAddress bindAddr;
+
+ public T backlog(int backlog) {
+ this.backlog = backlog;
+ return (T) this;
+ }
+
+ public T clientTimeout(int clientTimeout) {
+ this.clientTimeout = clientTimeout;
+ return (T) this;
+ }
+
+ public T port(int port) {
+ this.bindAddr = new InetSocketAddress(port);
+ return (T) this;
+ }
+
+ public T bindAddr(InetSocketAddress bindAddr) {
+ this.bindAddr = bindAddr;
+ return (T) this;
+ }
+ }
+
+ public abstract void listen() throws TTransportException;
+
+ public final TTransport accept() throws TTransportException {
+ TTransport transport = acceptImpl();
+ if (transport == null) {
+ throw new TTransportException("accept() may not return NULL");
+ }
+ return transport;
+ }
+
+ public abstract void close();
+
+ protected abstract TTransport acceptImpl() throws TTransportException;
+
+ /**
+ * Optional method implementation. This signals to the server transport
+ * that it should break out of any accept() or listen() that it is currently
+ * blocked on. This method, if implemented, MUST be thread safe, as it may
+ * be called from a different thread context than the other TServerTransport
+ * methods.
+ */
+ public void interrupt() {}
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSimpleFileTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSimpleFileTransport.java
new file mode 100644
index 000000000..42102d9e8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSimpleFileTransport.java
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.transport;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+
+/**
+ * Basic file support for the TTransport interface
+ */
+public final class TSimpleFileTransport extends TTransport {
+
+ private RandomAccessFile file = null;
+ private boolean readable;
+ private boolean writable;
+ private String path_;
+
+
+ /**
+ * Create a transport backed by a simple file
+ *
+ * @param path the path to the file to open/create
+ * @param read true to support read operations
+ * @param write true to support write operations
+ * @param openFile true to open the file on construction
+ * @throws TTransportException if file open fails
+ */
+ public TSimpleFileTransport(String path, boolean read,
+ boolean write, boolean openFile)
+ throws TTransportException {
+ if (path.length() <= 0) {
+ throw new TTransportException("No path specified");
+ }
+ if (!read && !write) {
+ throw new TTransportException("Neither READ nor WRITE specified");
+ }
+ readable = read;
+ writable = write;
+ path_ = path;
+ if (openFile) {
+ open();
+ }
+ }
+
+ /**
+ * Create a transport backed by a simple file
+ * Implicitly opens file to conform to C++ behavior.
+ *
+ * @param path the path to the file to open/create
+ * @param read true to support read operations
+ * @param write true to support write operations
+ * @throws TTransportException if file open fails
+ */
+ public TSimpleFileTransport(String path, boolean read, boolean write)
+ throws TTransportException {
+ this(path, read, write, true);
+ }
+
+ /**
+ * Create a transport backed by a simple read only disk file (implicitly opens
+ * file)
+ *
+ * @param path the path to the file to open/create
+ * @throws TTransportException if file open fails
+ */
+ public TSimpleFileTransport(String path) throws TTransportException {
+ this(path, true, false, true);
+ }
+
+ /**
+ * Test file status
+ *
+ * @return true if open, otherwise false
+ */
+ @Override
+ public boolean isOpen() {
+ return (file != null);
+ }
+
+ /**
+ * Open file if not previously opened.
+ *
+ * @throws TTransportException if open fails
+ */
+ @Override
+ public void open() throws TTransportException {
+ if (file == null){
+ try {
+ String access = "r"; //RandomAccessFile objects must be readable
+ if (writable) {
+ access += "w";
+ }
+ file = new RandomAccessFile(path_, access);
+ } catch (IOException ioe) {
+ file = null;
+ throw new TTransportException(ioe.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Close file, subsequent read/write activity will throw exceptions
+ */
+ @Override
+ public void close() {
+ if (file != null) {
+ try {
+ file.close();
+ } catch (Exception e) {
+ //Nothing to do
+ }
+ file = null;
+ }
+ }
+
+ /**
+ * Read up to len many bytes into buf at offset
+ *
+ * @param buf houses bytes read
+ * @param off offset into buff to begin writing to
+ * @param len maximum number of bytes to read
+ * @return number of bytes actually read
+ * @throws TTransportException on read failure
+ */
+ @Override
+ public int read(byte[] buf, int off, int len) throws TTransportException {
+ if (!readable) {
+ throw new TTransportException("Read operation on write only file");
+ }
+ int iBytesRead = 0;
+ try {
+ iBytesRead = file.read(buf, off, len);
+ } catch (IOException ioe) {
+ file = null;
+ throw new TTransportException(ioe.getMessage());
+ }
+ return iBytesRead;
+ }
+
+ /**
+ * Write len many bytes from buff starting at offset
+ *
+ * @param buf buffer containing bytes to write
+ * @param off offset into buffer to begin writing from
+ * @param len number of bytes to write
+ * @throws TTransportException on write failure
+ */
+ @Override
+ public void write(byte[] buf, int off, int len) throws TTransportException {
+ try {
+ file.write(buf, off, len);
+ } catch (IOException ioe) {
+ file = null;
+ throw new TTransportException(ioe.getMessage());
+ }
+ }
+
+ /**
+ * Move file pointer to specified offset, new read/write calls will act here
+ *
+ * @param offset bytes from beginning of file to move pointer to
+ * @throws TTransportException is seek fails
+ */
+ public void seek(long offset) throws TTransportException {
+ try {
+ file.seek(offset);
+ } catch (IOException ex) {
+ throw new TTransportException(ex.getMessage());
+ }
+ }
+
+ /**
+ * Return the length of the file in bytes
+ *
+ * @return length of the file in bytes
+ * @throws TTransportException if file access fails
+ */
+ public long length() throws TTransportException {
+ try {
+ return file.length();
+ } catch (IOException ex) {
+ throw new TTransportException(ex.getMessage());
+ }
+ }
+
+ /**
+ * Return current file pointer position in bytes from beginning of file
+ *
+ * @return file pointer position
+ * @throws TTransportException if file access fails
+ */
+ public long getFilePointer() throws TTransportException {
+ try {
+ return file.getFilePointer();
+ } catch (IOException ex) {
+ throw new TTransportException(ex.getMessage());
+ }
+ }
+} \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSocket.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSocket.java
new file mode 100644
index 000000000..b20b32b78
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSocket.java
@@ -0,0 +1,248 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketException;
+
+/**
+ * Socket implementation of the TTransport interface. To be commented soon!
+ *
+ */
+public class TSocket extends TIOStreamTransport {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TSocket.class.getName());
+
+ /**
+ * Wrapped Socket object
+ */
+ private Socket socket_;
+
+ /**
+ * Remote host
+ */
+ private String host_;
+
+ /**
+ * Remote port
+ */
+ private int port_;
+
+ /**
+ * Socket timeout - read timeout on the socket
+ */
+ private int socketTimeout_;
+
+ /**
+ * Connection timeout
+ */
+ private int connectTimeout_;
+
+ /**
+ * Constructor that takes an already created socket.
+ *
+ * @param socket Already created socket object
+ * @throws TTransportException if there is an error setting up the streams
+ */
+ public TSocket(Socket socket) throws TTransportException {
+ socket_ = socket;
+ try {
+ socket_.setSoLinger(false, 0);
+ socket_.setTcpNoDelay(true);
+ socket_.setKeepAlive(true);
+ } catch (SocketException sx) {
+ LOGGER.warn("Could not configure socket.", sx);
+ }
+
+ if (isOpen()) {
+ try {
+ inputStream_ = new BufferedInputStream(socket_.getInputStream());
+ outputStream_ = new BufferedOutputStream(socket_.getOutputStream());
+ } catch (IOException iox) {
+ close();
+ throw new TTransportException(TTransportException.NOT_OPEN, iox);
+ }
+ }
+ }
+
+ /**
+ * Creates a new unconnected socket that will connect to the given host
+ * on the given port.
+ *
+ * @param host Remote host
+ * @param port Remote port
+ */
+ public TSocket(String host, int port) {
+ this(host, port, 0);
+ }
+
+ /**
+ * Creates a new unconnected socket that will connect to the given host
+ * on the given port.
+ *
+ * @param host Remote host
+ * @param port Remote port
+ * @param timeout Socket timeout and connection timeout
+ */
+ public TSocket(String host, int port, int timeout) {
+ this(host, port, timeout, timeout);
+ }
+
+ /**
+ * Creates a new unconnected socket that will connect to the given host
+ * on the given port, with a specific connection timeout and a
+ * specific socket timeout.
+ *
+ * @param host Remote host
+ * @param port Remote port
+ * @param socketTimeout Socket timeout
+ * @param connectTimeout Connection timeout
+ */
+ public TSocket(String host, int port, int socketTimeout, int connectTimeout) {
+ host_ = host;
+ port_ = port;
+ socketTimeout_ = socketTimeout;
+ connectTimeout_ = connectTimeout;
+ initSocket();
+ }
+
+ /**
+ * Initializes the socket object
+ */
+ private void initSocket() {
+ socket_ = new Socket();
+ try {
+ socket_.setSoLinger(false, 0);
+ socket_.setTcpNoDelay(true);
+ socket_.setKeepAlive(true);
+ socket_.setSoTimeout(socketTimeout_);
+ } catch (SocketException sx) {
+ LOGGER.error("Could not configure socket.", sx);
+ }
+ }
+
+ /**
+ * Sets the socket timeout and connection timeout.
+ *
+ * @param timeout Milliseconds timeout
+ */
+ public void setTimeout(int timeout) {
+ this.setConnectTimeout(timeout);
+ this.setSocketTimeout(timeout);
+ }
+
+ /**
+ * Sets the time after which the connection attempt will time out
+ *
+ * @param timeout Milliseconds timeout
+ */
+ public void setConnectTimeout(int timeout) {
+ connectTimeout_ = timeout;
+ }
+
+ /**
+ * Sets the socket timeout
+ *
+ * @param timeout Milliseconds timeout
+ */
+ public void setSocketTimeout(int timeout) {
+ socketTimeout_ = timeout;
+ try {
+ socket_.setSoTimeout(timeout);
+ } catch (SocketException sx) {
+ LOGGER.warn("Could not set socket timeout.", sx);
+ }
+ }
+
+ /**
+ * Returns a reference to the underlying socket.
+ */
+ public Socket getSocket() {
+ if (socket_ == null) {
+ initSocket();
+ }
+ return socket_;
+ }
+
+ /**
+ * Checks whether the socket is connected.
+ */
+ public boolean isOpen() {
+ if (socket_ == null) {
+ return false;
+ }
+ return socket_.isConnected();
+ }
+
+ /**
+ * Connects the socket, creating a new socket object if necessary.
+ */
+ public void open() throws TTransportException {
+ if (isOpen()) {
+ throw new TTransportException(TTransportException.ALREADY_OPEN, "Socket already connected.");
+ }
+
+ if (host_ == null || host_.length() == 0) {
+ throw new TTransportException(TTransportException.NOT_OPEN, "Cannot open null host.");
+ }
+ if (port_ <= 0 || port_ > 65535) {
+ throw new TTransportException(TTransportException.NOT_OPEN, "Invalid port " + port_);
+ }
+
+ if (socket_ == null) {
+ initSocket();
+ }
+
+ try {
+ socket_.connect(new InetSocketAddress(host_, port_), connectTimeout_);
+ inputStream_ = new BufferedInputStream(socket_.getInputStream());
+ outputStream_ = new BufferedOutputStream(socket_.getOutputStream());
+ } catch (IOException iox) {
+ close();
+ throw new TTransportException(TTransportException.NOT_OPEN, iox);
+ }
+ }
+
+ /**
+ * Closes the socket.
+ */
+ public void close() {
+ // Close the underlying streams
+ super.close();
+
+ // Close the socket
+ if (socket_ != null) {
+ try {
+ socket_.close();
+ } catch (IOException iox) {
+ LOGGER.warn("Could not close socket.", iox);
+ }
+ socket_ = null;
+ }
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TStandardFile.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TStandardFile.java
new file mode 100644
index 000000000..7a33af8ee
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TStandardFile.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+
+public class TStandardFile implements TSeekableFile {
+
+ protected String path_ = null;
+ protected RandomAccessFile inputFile_ = null;
+
+ public TStandardFile(String path) throws IOException {
+ path_ = path;
+ inputFile_ = new RandomAccessFile(path_, "r");
+ }
+
+ public InputStream getInputStream() throws IOException {
+ return new FileInputStream(inputFile_.getFD());
+ }
+
+ public OutputStream getOutputStream() throws IOException {
+ return new FileOutputStream(path_);
+ }
+
+ public void close() throws IOException {
+ if(inputFile_ != null) {
+ inputFile_.close();
+ }
+ }
+
+ public long length() throws IOException {
+ return inputFile_.length();
+ }
+
+ public void seek(long pos) throws IOException {
+ inputFile_.seek(pos);
+ }
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransport.java
new file mode 100644
index 000000000..73ad730ce
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransport.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import java.io.Closeable;
+
+/**
+ * Generic class that encapsulates the I/O layer. This is basically a thin
+ * wrapper around the combined functionality of Java input/output streams.
+ *
+ */
+public abstract class TTransport implements Closeable {
+
+ /**
+ * Queries whether the transport is open.
+ *
+ * @return True if the transport is open.
+ */
+ public abstract boolean isOpen();
+
+ /**
+ * Is there more data to be read?
+ *
+ * @return True if the remote side is still alive and feeding us
+ */
+ public boolean peek() {
+ return isOpen();
+ }
+
+ /**
+ * Opens the transport for reading/writing.
+ *
+ * @throws TTransportException if the transport could not be opened
+ */
+ public abstract void open()
+ throws TTransportException;
+
+ /**
+ * Closes the transport.
+ */
+ public abstract void close();
+
+ /**
+ * Reads up to len bytes into buffer buf, starting at offset off.
+ *
+ * @param buf Array to read into
+ * @param off Index to start reading at
+ * @param len Maximum number of bytes to read
+ * @return The number of bytes actually read
+ * @throws TTransportException if there was an error reading data
+ */
+ public abstract int read(byte[] buf, int off, int len)
+ throws TTransportException;
+
+ /**
+ * Guarantees that all of len bytes are actually read off the transport.
+ *
+ * @param buf Array to read into
+ * @param off Index to start reading at
+ * @param len Maximum number of bytes to read
+ * @return The number of bytes actually read, which must be equal to len
+ * @throws TTransportException if there was an error reading data
+ */
+ public int readAll(byte[] buf, int off, int len)
+ throws TTransportException {
+ int got = 0;
+ int ret = 0;
+ while (got < len) {
+ ret = read(buf, off+got, len-got);
+ if (ret <= 0) {
+ throw new TTransportException(
+ "Cannot read. Remote side has closed. Tried to read "
+ + len
+ + " bytes, but only got "
+ + got
+ + " bytes. (This is often indicative of an internal error on the server side. Please check your server logs.)");
+ }
+ got += ret;
+ }
+ return got;
+ }
+
+ /**
+ * Writes the buffer to the output
+ *
+ * @param buf The output data buffer
+ * @throws TTransportException if an error occurs writing data
+ */
+ public void write(byte[] buf) throws TTransportException {
+ write(buf, 0, buf.length);
+ }
+
+ /**
+ * Writes up to len bytes from the buffer.
+ *
+ * @param buf The output data buffer
+ * @param off The offset to start writing from
+ * @param len The number of bytes to write
+ * @throws TTransportException if there was an error writing data
+ */
+ public abstract void write(byte[] buf, int off, int len)
+ throws TTransportException;
+
+ /**
+ * Flush any pending data out of a transport buffer.
+ *
+ * @throws TTransportException if there was an error writing out data.
+ */
+ public void flush()
+ throws TTransportException {}
+
+ /**
+ * Access the protocol's underlying buffer directly. If this is not a
+ * buffered transport, return null.
+ * @return protocol's Underlying buffer
+ */
+ public byte[] getBuffer() {
+ return null;
+ }
+
+ /**
+ * Return the index within the underlying buffer that specifies the next spot
+ * that should be read from.
+ * @return index within the underlying buffer that specifies the next spot
+ * that should be read from
+ */
+ public int getBufferPosition() {
+ return 0;
+ }
+
+ /**
+ * Get the number of bytes remaining in the underlying buffer. Returns -1 if
+ * this is a non-buffered transport.
+ * @return the number of bytes remaining in the underlying buffer. <br> Returns -1 if
+ * this is a non-buffered transport.
+ */
+ public int getBytesRemainingInBuffer() {
+ return -1;
+ }
+
+ /**
+ * Consume len bytes from the underlying buffer.
+ * @param len
+ */
+ public void consumeBuffer(int len) {}
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransportException.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransportException.java
new file mode 100644
index 000000000..b886bc269
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransportException.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+import org.apache.thrift.TException;
+
+/**
+ * Transport exceptions.
+ *
+ */
+public class TTransportException extends TException {
+
+ private static final long serialVersionUID = 1L;
+
+ public static final int UNKNOWN = 0;
+ public static final int NOT_OPEN = 1;
+ public static final int ALREADY_OPEN = 2;
+ public static final int TIMED_OUT = 3;
+ public static final int END_OF_FILE = 4;
+ public static final int CORRUPTED_DATA = 5;
+
+ protected int type_ = UNKNOWN;
+
+ public TTransportException() {
+ super();
+ }
+
+ public TTransportException(int type) {
+ super();
+ type_ = type;
+ }
+
+ public TTransportException(int type, String message) {
+ super(message);
+ type_ = type;
+ }
+
+ public TTransportException(String message) {
+ super(message);
+ }
+
+ public TTransportException(int type, Throwable cause) {
+ super(cause);
+ type_ = type;
+ }
+
+ public TTransportException(Throwable cause) {
+ super(cause);
+ }
+
+ public TTransportException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public TTransportException(int type, String message, Throwable cause) {
+ super(message, cause);
+ type_ = type;
+ }
+
+ public int getType() {
+ return type_;
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransportFactory.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransportFactory.java
new file mode 100644
index 000000000..3e71630ae
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransportFactory.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.transport;
+
+/**
+ * Factory class used to create wrapped instance of Transports.
+ * This is used primarily in servers, which get Transports from
+ * a ServerTransport and then may want to mutate them (i.e. create
+ * a BufferedTransport from the underlying base transport)
+ *
+ */
+public class TTransportFactory {
+
+ /**
+ * Return a wrapped instance of the base Transport.
+ *
+ * @param trans The base transport
+ * @return Wrapped Transport
+ */
+ public TTransport getTransport(TTransport trans) {
+ return trans;
+ }
+
+}
diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TZlibTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TZlibTransport.java
new file mode 100644
index 000000000..e755aa532
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TZlibTransport.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.transport;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.Inflater;
+import java.util.zip.InflaterInputStream;
+
+/**
+ * TZlibTransport deflates on write and inflates on read.
+ */
+public class TZlibTransport extends TIOStreamTransport {
+
+ private TTransport transport_ = null;
+
+ public static class Factory extends TTransportFactory {
+ public Factory() {
+ }
+
+ @Override
+ public TTransport getTransport(TTransport base) {
+ return new TZlibTransport(base);
+ }
+ }
+
+ /**
+ * Constructs a new TZlibTransport instance.
+ * @param transport the underlying transport to read from and write to
+ */
+ public TZlibTransport(TTransport transport) {
+ this(transport, Deflater.BEST_COMPRESSION);
+ }
+
+ /**
+ * Constructs a new TZlibTransport instance.
+ * @param transport the underlying transport to read from and write to
+ * @param compressionLevel 0 for no compression, 9 for maximum compression
+ */
+ public TZlibTransport(TTransport transport, int compressionLevel) {
+ transport_ = transport;
+ inputStream_ = new InflaterInputStream(new TTransportInputStream(transport_), new Inflater());
+ outputStream_ = new DeflaterOutputStream(new TTransportOutputStream(transport_), new Deflater(compressionLevel, false), true);
+ }
+
+ @Override
+ public boolean isOpen() {
+ return transport_.isOpen();
+ }
+
+ @Override
+ public void open() throws TTransportException {
+ transport_.open();
+ }
+
+ @Override
+ public void close() {
+ super.close();
+ if (transport_.isOpen()) {
+ transport_.close();
+ }
+ }
+}
+
+class TTransportInputStream extends InputStream {
+
+ private TTransport transport = null;
+
+ public TTransportInputStream(TTransport transport) {
+ this.transport = transport;
+ }
+
+ @Override
+ public int read() throws IOException {
+ try {
+ byte[] buf = new byte[1];
+ transport.read(buf, 0, 1);
+ return buf[0];
+ } catch (TTransportException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ public int read(byte b[], int off, int len) throws IOException {
+ try {
+ return transport.read(b, off, len);
+ } catch (TTransportException e) {
+ throw new IOException(e);
+ }
+ }
+}
+
+class TTransportOutputStream extends OutputStream {
+
+ private TTransport transport = null;
+
+ public TTransportOutputStream(TTransport transport) {
+ this.transport = transport;
+ }
+
+ @Override
+ public void write(final int b) throws IOException {
+ try {
+ transport.write(new byte[]{(byte) b});
+ } catch (TTransportException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ public void write(byte b[], int off, int len) throws IOException {
+ try {
+ transport.write(b, off, len);
+ } catch (TTransportException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ public void flush() throws IOException {
+ try {
+ transport.flush();
+ } catch (TTransportException e) {
+ throw new IOException(e);
+ }
+ }
+}
+