THRIFT-5587 add uuid support for java and kotlin
Client: java, kt
Patch: Jiayu Liu

This closes #2621
diff --git a/lib/java/gradle.properties b/lib/java/gradle.properties
index fa499cc..3ef6a5f 100644
--- a/lib/java/gradle.properties
+++ b/lib/java/gradle.properties
@@ -34,3 +34,9 @@
 mockito.version=1.10.19
 javax.annotation.version=1.3.2
 commons-lang3.version=3.12.0
+
+org.gradle.jvmargs=--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
+  --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \
+  --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \
+  --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \
+  --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
diff --git a/lib/java/src/crossTest/java/org/apache/thrift/test/TestClient.java b/lib/java/src/crossTest/java/org/apache/thrift/test/TestClient.java
index 3c54988..2807a43 100644
--- a/lib/java/src/crossTest/java/org/apache/thrift/test/TestClient.java
+++ b/lib/java/src/crossTest/java/org/apache/thrift/test/TestClient.java
@@ -27,6 +27,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.UUID;
 import java.util.stream.IntStream;
 import org.apache.http.impl.client.HttpClients;
 import org.apache.thrift.TApplicationException;
@@ -251,6 +252,15 @@
           System.out.println("*** FAILURE ***\n");
         }
 
+        /** UUID TEST */
+        System.out.println("testUuid(\"00112233-4455-6677-8899-aabbccddeeff\")");
+        UUID uuid = testClient.testUuid(UUID.fromString("00112233-4455-6677-8899-aabbccddeeff"));
+        System.out.print(" = \"" + uuid + "\"\n");
+        if (!uuid.equals(UUID.fromString("00112233-4455-6677-8899-aabbccddeeff"))) {
+          returnCode |= ERR_BASETYPES;
+          System.out.println("*** FAILURE ***\n");
+        }
+
         /** Multiplexed test */
         if (protocol_type.startsWith("multi")) {
           SecondService.Client secondClient = new SecondService.Client(tProtocol2);
diff --git a/lib/java/src/main/java/org/apache/thrift/protocol/TBinaryProtocol.java b/lib/java/src/main/java/org/apache/thrift/protocol/TBinaryProtocol.java
index 34e7517..263a818 100644
--- a/lib/java/src/main/java/org/apache/thrift/protocol/TBinaryProtocol.java
+++ b/lib/java/src/main/java/org/apache/thrift/protocol/TBinaryProtocol.java
@@ -21,6 +21,7 @@
 
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
+import java.util.UUID;
 import org.apache.thrift.TException;
 import org.apache.thrift.partial.TFieldData;
 import org.apache.thrift.transport.TTransport;
@@ -49,7 +50,7 @@
   protected boolean strictRead_;
   protected boolean strictWrite_;
 
-  private final byte[] inoutTemp = new byte[8];
+  private final byte[] inoutTemp = new byte[16];
 
   /** Factory */
   public static class Factory implements TProtocolFactory {
@@ -219,6 +220,33 @@
   }
 
   @Override
+  public void writeUuid(UUID uuid) throws TException {
+    {
+      long lsb = uuid.getLeastSignificantBits();
+      inoutTemp[0] = (byte) (0xff & (lsb >> 56));
+      inoutTemp[1] = (byte) (0xff & (lsb >> 48));
+      inoutTemp[2] = (byte) (0xff & (lsb >> 40));
+      inoutTemp[3] = (byte) (0xff & (lsb >> 32));
+      inoutTemp[4] = (byte) (0xff & (lsb >> 24));
+      inoutTemp[5] = (byte) (0xff & (lsb >> 16));
+      inoutTemp[6] = (byte) (0xff & (lsb >> 8));
+      inoutTemp[7] = (byte) (0xff & (lsb));
+    }
+    {
+      long msb = uuid.getMostSignificantBits();
+      inoutTemp[8] = (byte) (0xff & (msb >> 56));
+      inoutTemp[1 + 8] = (byte) (0xff & (msb >> 48));
+      inoutTemp[2 + 8] = (byte) (0xff & (msb >> 40));
+      inoutTemp[3 + 8] = (byte) (0xff & (msb >> 32));
+      inoutTemp[4 + 8] = (byte) (0xff & (msb >> 24));
+      inoutTemp[5 + 8] = (byte) (0xff & (msb >> 16));
+      inoutTemp[6 + 8] = (byte) (0xff & (msb >> 8));
+      inoutTemp[7 + 8] = (byte) (0xff & (msb));
+    }
+    trans_.write(inoutTemp, 0, 16);
+  }
+
+  @Override
   public void writeDouble(double dub) throws TException {
     writeI64(Double.doubleToLongBits(dub));
   }
@@ -388,6 +416,40 @@
   }
 
   @Override
+  public UUID readUuid() throws TException {
+    byte[] buf = inoutTemp;
+    int off = 0;
+
+    if (trans_.getBytesRemainingInBuffer() >= 16) {
+      buf = trans_.getBuffer();
+      off = trans_.getBufferPosition();
+      trans_.consumeBuffer(16);
+    } else {
+      readAll(inoutTemp, 0, 16);
+    }
+    long lsb =
+        ((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));
+
+    long msb =
+        ((long) (buf[off + 8] & 0xff) << 56)
+            | ((long) (buf[off + 8 + 1] & 0xff) << 48)
+            | ((long) (buf[off + 8 + 2] & 0xff) << 40)
+            | ((long) (buf[off + 8 + 3] & 0xff) << 32)
+            | ((long) (buf[off + 8 + 4] & 0xff) << 24)
+            | ((long) (buf[off + 8 + 5] & 0xff) << 16)
+            | ((long) (buf[off + 8 + 6] & 0xff) << 8)
+            | ((long) (buf[off + 8 + 7] & 0xff));
+    return new UUID(msb, lsb);
+  }
+
+  @Override
   public double readDouble() throws TException {
     return Double.longBitsToDouble(readI64());
   }
diff --git a/lib/java/src/main/java/org/apache/thrift/protocol/TCompactProtocol.java b/lib/java/src/main/java/org/apache/thrift/protocol/TCompactProtocol.java
index fffd687..b522956 100644
--- a/lib/java/src/main/java/org/apache/thrift/protocol/TCompactProtocol.java
+++ b/lib/java/src/main/java/org/apache/thrift/protocol/TCompactProtocol.java
@@ -21,6 +21,7 @@
 
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
+import java.util.UUID;
 import org.apache.thrift.TException;
 import org.apache.thrift.transport.TTransport;
 import org.apache.thrift.transport.TTransportException;
@@ -42,7 +43,7 @@
   private static final TStruct ANONYMOUS_STRUCT = new TStruct("");
   private static final TField TSTOP = new TField("", TType.STOP, (short) 0);
 
-  private static final byte[] ttypeToCompactType = new byte[16];
+  private static final byte[] ttypeToCompactType = new byte[18];
 
   static {
     ttypeToCompactType[TType.STOP] = TType.STOP;
@@ -57,6 +58,7 @@
     ttypeToCompactType[TType.SET] = Types.SET;
     ttypeToCompactType[TType.MAP] = Types.MAP;
     ttypeToCompactType[TType.STRUCT] = Types.STRUCT;
+    ttypeToCompactType[TType.UUID] = Types.UUID;
   }
 
   /** TProtocolFactory that produces TCompactProtocols. */
@@ -104,6 +106,7 @@
     public static final byte SET = 0x0A;
     public static final byte MAP = 0x0B;
     public static final byte STRUCT = 0x0C;
+    public static final byte UUID = 0x0D;
   }
 
   /**
@@ -141,7 +144,7 @@
   /**
    * Temporary buffer used for various operations that would otherwise require a small allocation.
    */
-  private final byte[] temp = new byte[10];
+  private final byte[] temp = new byte[16];
 
   /**
    * Create a TCompactProtocol.
@@ -338,6 +341,13 @@
     trans_.write(temp, 0, 8);
   }
 
+  @Override
+  public void writeUuid(UUID uuid) throws TException {
+    fixedLongToBytes(uuid.getLeastSignificantBits(), temp, 0);
+    fixedLongToBytes(uuid.getMostSignificantBits(), temp, 8);
+    trans_.write(temp, 0, 16);
+  }
+
   /** Write a string to the wire with a varint size preceding. */
   @Override
   public void writeString(String str) throws TException {
@@ -650,6 +660,14 @@
     return Double.longBitsToDouble(bytesToLong(temp));
   }
 
+  @Override
+  public UUID readUuid() throws TException {
+    trans_.readAll(temp, 0, 16);
+    long mostSigBits = bytesToLong(temp, 8);
+    long leastSigBits = bytesToLong(temp, 0);
+    return new UUID(mostSigBits, leastSigBits);
+  }
+
   /** Reads a byte[] (via readBinary), and then UTF-8 decodes it. */
   @Override
   public String readString() throws TException {
@@ -825,14 +843,18 @@
    * 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));
+    return bytesToLong(bytes, 0);
+  }
+
+  private long bytesToLong(byte[] bytes, int offset) {
+    return ((bytes[offset + 7] & 0xffL) << 56)
+        | ((bytes[offset + 6] & 0xffL) << 48)
+        | ((bytes[offset + 5] & 0xffL) << 40)
+        | ((bytes[offset + 4] & 0xffL) << 32)
+        | ((bytes[offset + 3] & 0xffL) << 24)
+        | ((bytes[offset + 2] & 0xffL) << 16)
+        | ((bytes[offset + 1] & 0xffL) << 8)
+        | ((bytes[offset + 0] & 0xffL));
   }
 
   //
@@ -860,6 +882,8 @@
         return TType.I32;
       case Types.I64:
         return TType.I64;
+      case Types.UUID:
+        return TType.UUID;
       case Types.DOUBLE:
         return TType.DOUBLE;
       case Types.BINARY:
diff --git a/lib/java/src/main/java/org/apache/thrift/protocol/TJSONProtocol.java b/lib/java/src/main/java/org/apache/thrift/protocol/TJSONProtocol.java
index dd24ca4..a2f0eac 100644
--- a/lib/java/src/main/java/org/apache/thrift/protocol/TJSONProtocol.java
+++ b/lib/java/src/main/java/org/apache/thrift/protocol/TJSONProtocol.java
@@ -24,6 +24,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Stack;
+import java.util.UUID;
 import org.apache.thrift.TByteArrayOutputStream;
 import org.apache.thrift.TException;
 import org.apache.thrift.transport.TTransport;
@@ -87,6 +88,7 @@
   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_UUID = new byte[] {'u', 'i', 'd'};
   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'};
@@ -96,7 +98,7 @@
 
   private static final TStruct ANONYMOUS_STRUCT = new TStruct();
 
-  private static final byte[] getTypeNameForTypeID(byte typeID) throws TException {
+  private static byte[] getTypeNameForTypeID(byte typeID) throws TException {
     switch (typeID) {
       case TType.BOOL:
         return NAME_BOOL;
@@ -108,6 +110,8 @@
         return NAME_I32;
       case TType.I64:
         return NAME_I64;
+      case TType.UUID:
+        return NAME_UUID;
       case TType.DOUBLE:
         return NAME_DOUBLE;
       case TType.STRING:
@@ -125,7 +129,7 @@
     }
   }
 
-  private static final byte getTypeIDForTypeName(byte[] name) throws TException {
+  private static byte getTypeIDForTypeName(byte[] name) throws TException {
     byte result = TType.STOP;
     if (name.length > 1) {
       switch (name[0]) {
@@ -164,6 +168,9 @@
             result = TType.SET;
           }
           break;
+        case 'u':
+          result = TType.UUID;
+          break;
         case 't':
           result = TType.BOOL;
           break;
@@ -591,6 +598,11 @@
   }
 
   @Override
+  public void writeUuid(UUID uuid) throws TException {
+    writeJSONString(uuid.toString().getBytes(StandardCharsets.UTF_8));
+  }
+
+  @Override
   public void writeDouble(double dub) throws TException {
     writeJSONDouble(dub);
   }
@@ -937,6 +949,11 @@
   }
 
   @Override
+  public UUID readUuid() throws TException {
+    return UUID.fromString(readString());
+  }
+
+  @Override
   public double readDouble() throws TException {
     return readJSONDouble();
   }
diff --git a/lib/java/src/main/java/org/apache/thrift/protocol/TProtocolDecorator.java b/lib/java/src/main/java/org/apache/thrift/protocol/TProtocolDecorator.java
index 668ebce..c237ce4 100644
--- a/lib/java/src/main/java/org/apache/thrift/protocol/TProtocolDecorator.java
+++ b/lib/java/src/main/java/org/apache/thrift/protocol/TProtocolDecorator.java
@@ -20,6 +20,7 @@
 package org.apache.thrift.protocol;
 
 import java.nio.ByteBuffer;
+import java.util.UUID;
 import org.apache.thrift.TException;
 
 /**
@@ -62,6 +63,16 @@
   }
 
   @Override
+  public UUID readUuid() throws TException {
+    return concreteProtocol.readUuid();
+  }
+
+  @Override
+  public void writeUuid(UUID uuid) throws TException {
+    concreteProtocol.writeUuid(uuid);
+  }
+
+  @Override
   public void writeStructEnd() throws TException {
     concreteProtocol.writeStructEnd();
   }
diff --git a/lib/java/src/main/java/org/apache/thrift/protocol/TProtocolUtil.java b/lib/java/src/main/java/org/apache/thrift/protocol/TProtocolUtil.java
index a9f566e..7b44b10 100644
--- a/lib/java/src/main/java/org/apache/thrift/protocol/TProtocolUtil.java
+++ b/lib/java/src/main/java/org/apache/thrift/protocol/TProtocolUtil.java
@@ -86,6 +86,10 @@
         prot.readI64();
         break;
 
+      case TType.UUID:
+        prot.readUuid();
+        break;
+
       case TType.DOUBLE:
         prot.readDouble();
         break;
diff --git a/lib/java/src/main/java/org/apache/thrift/protocol/TReadProtocol.java b/lib/java/src/main/java/org/apache/thrift/protocol/TReadProtocol.java
index d21c9a0..b15737a 100644
--- a/lib/java/src/main/java/org/apache/thrift/protocol/TReadProtocol.java
+++ b/lib/java/src/main/java/org/apache/thrift/protocol/TReadProtocol.java
@@ -1,6 +1,7 @@
 package org.apache.thrift.protocol;
 
 import java.nio.ByteBuffer;
+import java.util.UUID;
 import org.apache.thrift.TException;
 
 public interface TReadProtocol {
@@ -39,6 +40,8 @@
 
   long readI64() throws TException;
 
+  UUID readUuid() throws TException;
+
   double readDouble() throws TException;
 
   String readString() throws TException;
diff --git a/lib/java/src/main/java/org/apache/thrift/protocol/TSimpleJSONProtocol.java b/lib/java/src/main/java/org/apache/thrift/protocol/TSimpleJSONProtocol.java
index 0b94887..1b69032 100644
--- a/lib/java/src/main/java/org/apache/thrift/protocol/TSimpleJSONProtocol.java
+++ b/lib/java/src/main/java/org/apache/thrift/protocol/TSimpleJSONProtocol.java
@@ -22,6 +22,7 @@
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 import java.util.Stack;
+import java.util.UUID;
 import org.apache.thrift.TException;
 import org.apache.thrift.transport.TTransport;
 import org.apache.thrift.transport.TTransportException;
@@ -277,6 +278,11 @@
   }
 
   @Override
+  public void writeUuid(UUID uuid) throws TException {
+    writeString(uuid.toString());
+  }
+
+  @Override
   public void writeDouble(double dub) throws TException {
     if (writeContext_.isMapKey()) {
       writeString(Double.toString(dub));
@@ -443,6 +449,11 @@
   }
 
   @Override
+  public UUID readUuid() throws TException {
+    throw new TException("Not implemented");
+  }
+
+  @Override
   public double readDouble() throws TException {
     throw new TException("Not implemented");
   }
diff --git a/lib/java/src/main/java/org/apache/thrift/protocol/TType.java b/lib/java/src/main/java/org/apache/thrift/protocol/TType.java
index 4b70bb2..5a64e42 100644
--- a/lib/java/src/main/java/org/apache/thrift/protocol/TType.java
+++ b/lib/java/src/main/java/org/apache/thrift/protocol/TType.java
@@ -35,4 +35,5 @@
   public static final byte SET = 14;
   public static final byte LIST = 15;
   public static final byte ENUM = 16;
+  public static final byte UUID = 17;
 }
diff --git a/lib/java/src/main/java/org/apache/thrift/protocol/TWriteProtocol.java b/lib/java/src/main/java/org/apache/thrift/protocol/TWriteProtocol.java
index 339a6b8..e44ac8e 100644
--- a/lib/java/src/main/java/org/apache/thrift/protocol/TWriteProtocol.java
+++ b/lib/java/src/main/java/org/apache/thrift/protocol/TWriteProtocol.java
@@ -1,6 +1,7 @@
 package org.apache.thrift.protocol;
 
 import java.nio.ByteBuffer;
+import java.util.UUID;
 import org.apache.thrift.TException;
 
 public interface TWriteProtocol {
@@ -41,6 +42,8 @@
 
   void writeI64(long i64) throws TException;
 
+  void writeUuid(UUID uuid) throws TException;
+
   void writeDouble(double dub) throws TException;
 
   void writeString(String str) throws TException;
diff --git a/lib/java/src/test/java/org/apache/thrift/TestStruct.java b/lib/java/src/test/java/org/apache/thrift/TestStruct.java
index 17a00f1..aa283f4 100644
--- a/lib/java/src/test/java/org/apache/thrift/TestStruct.java
+++ b/lib/java/src/test/java/org/apache/thrift/TestStruct.java
@@ -210,17 +210,19 @@
     Map<CrazyNesting._Fields, FieldMetaData> mdMap = CrazyNesting.metaDataMap;
 
     // Check for struct fields existence
-    assertEquals(4, mdMap.size());
+    assertEquals(5, mdMap.size());
     assertTrue(mdMap.containsKey(CrazyNesting._Fields.SET_FIELD));
     assertTrue(mdMap.containsKey(CrazyNesting._Fields.LIST_FIELD));
     assertTrue(mdMap.containsKey(CrazyNesting._Fields.STRING_FIELD));
     assertTrue(mdMap.containsKey(CrazyNesting._Fields.BINARY_FIELD));
+    assertTrue(mdMap.containsKey(CrazyNesting._Fields.UUID_FIELD));
 
     // Check for struct fields contents
     assertEquals("string_field", mdMap.get(CrazyNesting._Fields.STRING_FIELD).fieldName);
     assertEquals("list_field", mdMap.get(CrazyNesting._Fields.LIST_FIELD).fieldName);
     assertEquals("set_field", mdMap.get(CrazyNesting._Fields.SET_FIELD).fieldName);
     assertEquals("binary_field", mdMap.get(CrazyNesting._Fields.BINARY_FIELD).fieldName);
+    assertEquals("uuid_field", mdMap.get(CrazyNesting._Fields.UUID_FIELD).fieldName);
 
     assertEquals(
         TFieldRequirementType.DEFAULT,
@@ -235,6 +237,7 @@
     assertEquals(TType.LIST, mdMap.get(CrazyNesting._Fields.LIST_FIELD).valueMetaData.type);
     assertEquals(TType.SET, mdMap.get(CrazyNesting._Fields.SET_FIELD).valueMetaData.type);
     assertEquals(TType.STRING, mdMap.get(CrazyNesting._Fields.BINARY_FIELD).valueMetaData.type);
+    assertEquals(TType.UUID, mdMap.get(CrazyNesting._Fields.UUID_FIELD).valueMetaData.type);
     assertTrue(mdMap.get(CrazyNesting._Fields.BINARY_FIELD).valueMetaData.isBinary());
 
     // Check nested structures
diff --git a/lib/java/src/test/java/org/apache/thrift/protocol/ProtocolTestBase.java b/lib/java/src/test/java/org/apache/thrift/protocol/ProtocolTestBase.java
index a111292..0dd48a4 100644
--- a/lib/java/src/test/java/org/apache/thrift/protocol/ProtocolTestBase.java
+++ b/lib/java/src/test/java/org/apache/thrift/protocol/ProtocolTestBase.java
@@ -27,6 +27,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.UUID;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import org.apache.thrift.Fixtures;
@@ -126,6 +127,15 @@
   }
 
   @Test
+  public void testUuid() throws Exception {
+    UUID uuid = UUID.fromString("00112233-4455-6677-8899-aabbccddeeff");
+    if (canBeUsedNaked()) {
+      internalTestNakedUuid(uuid);
+    }
+    internalTestUuidField(uuid);
+  }
+
+  @Test
   public void testLong() throws Exception {
     if (canBeUsedNaked()) {
       internalTestNakedI64(0);
@@ -215,6 +225,28 @@
         });
   }
 
+  private void internalTestNakedUuid(UUID uuid) throws TException {
+    TMemoryBuffer buf = new TMemoryBuffer(0);
+    TProtocol protocol = getFactory().getProtocol(buf);
+    protocol.writeUuid(uuid);
+    assertEquals(uuid, protocol.readUuid());
+  }
+
+  private void internalTestUuidField(UUID uuid) throws Exception {
+    internalTestStructField(
+        new StructFieldTestCase(TType.UUID, (short) 17) {
+          @Override
+          public void writeMethod(TProtocol proto) throws TException {
+            proto.writeUuid(uuid);
+          }
+
+          @Override
+          public void readMethod(TProtocol proto) throws TException {
+            assertEquals(uuid, proto.readUuid());
+          }
+        });
+  }
+
   private void internalTestNakedI32(int n) throws Exception {
     TMemoryBuffer buf = new TMemoryBuffer(0);
     TProtocol proto = getFactory().getProtocol(buf);
diff --git a/lib/java/src/test/java/org/apache/thrift/server/ServerTestBase.java b/lib/java/src/test/java/org/apache/thrift/server/ServerTestBase.java
index a1e19eb..8d0c7aa 100644
--- a/lib/java/src/test/java/org/apache/thrift/server/ServerTestBase.java
+++ b/lib/java/src/test/java/org/apache/thrift/server/ServerTestBase.java
@@ -29,6 +29,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.UUID;
 import org.apache.thrift.TException;
 import org.apache.thrift.TProcessor;
 import org.apache.thrift.async.AsyncMethodCallback;
@@ -115,6 +116,12 @@
     }
 
     @Override
+    public UUID testUuid(UUID thing) throws TException {
+      System.out.println("testUuid(" + thing + ")");
+      return thing;
+    }
+
+    @Override
     public Xtruct testStruct(Xtruct thing) {
       System.out.print(
           "testStruct({"
@@ -675,6 +682,11 @@
     }
 
     @Override
+    public void testUuid(UUID thing, AsyncMethodCallback<UUID> resultHandler) throws TException {
+      resultHandler.onComplete(handler.testUuid(thing));
+    }
+
+    @Override
     public void testStruct(Xtruct thing, AsyncMethodCallback<Xtruct> resultHandler)
         throws TException {
       resultHandler.onComplete(handler.testStruct(thing));
diff --git a/lib/java/src/test/resources/JavaTypes.thrift b/lib/java/src/test/resources/JavaTypes.thrift
index 5553340..6afb6f9 100644
--- a/lib/java/src/test/resources/JavaTypes.thrift
+++ b/lib/java/src/test/resources/JavaTypes.thrift
@@ -51,6 +51,10 @@
   1: double val
 }
 
+struct Uuid {
+  1: uuid val
+}
+
 struct List {
   1: list<string> vals
 }