THRIFT-5855: Add java fuzzers

Add fuzzers for java support, to improve the reliability/robustness of the implementation
diff --git a/FUZZING.md b/FUZZING.md
index f239bb7..5cd4418 100644
--- a/FUZZING.md
+++ b/FUZZING.md
@@ -18,6 +18,7 @@
 - Go
 - c_glib (partially supported, needs round-trip support)
 - C++
+- Java/JVM (and other JVM languages)
 
 We are working on adding fuzzers for the following languages:
 
@@ -25,7 +26,6 @@
 - Swift
 - Python
 - JavaScript
-- Java/JVM (and other JVM languages)
 - netstd
 
 ## Fuzzer Types
diff --git a/lib/java/gradle/generateTestThrift.gradle b/lib/java/gradle/generateTestThrift.gradle
index 7e33b15..7d1f248 100644
--- a/lib/java/gradle/generateTestThrift.gradle
+++ b/lib/java/gradle/generateTestThrift.gradle
@@ -90,6 +90,7 @@
     thriftCompile(it, 'VoidMethExceptionsTest.thrift')
     thriftCompile(it, 'JavaAnnotationTest.thrift')
     thriftCompile(it, 'partial/thrift_test_schema.thrift')
+    thriftCompile(it, 'FuzzTest.thrift')
 }
 
 task generateOptionalTypeJava(group: 'Build') {
diff --git a/lib/java/src/test/java/org/apache/thrift/test/fuzz/FuzzTestUtils.java b/lib/java/src/test/java/org/apache/thrift/test/fuzz/FuzzTestUtils.java
new file mode 100644
index 0000000..0cd8a71
--- /dev/null
+++ b/lib/java/src/test/java/org/apache/thrift/test/fuzz/FuzzTestUtils.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.test.fuzz;
+
+import org.apache.thrift.TConfiguration;
+import org.apache.thrift.TException;
+import org.apache.thrift.fuzz.FuzzTest;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.transport.TMemoryBuffer;
+import org.apache.thrift.transport.TMemoryInputTransport;
+import org.apache.thrift.transport.TTransport;
+
+public class FuzzTestUtils {
+  // 10MB message size limit to prevent over-allocation during fuzzing
+  private static final int FUZZ_MAX_MESSAGE_SIZE = 10 * 1024 * 1024;
+
+  /**
+   * Common fuzzing test implementation that attempts to parse input data using the provided
+   * protocol factory.
+   *
+   * @param input The input bytes to fuzz
+   * @param factory The protocol factory to use for parsing
+   * @throws Exception if an unexpected error occurs
+   */
+  public static void testParse(byte[] input, TProtocolFactory factory) throws Exception {
+    try {
+      TConfiguration config = new TConfiguration();
+      config.setMaxMessageSize(FUZZ_MAX_MESSAGE_SIZE);
+      TTransport transport = new TMemoryInputTransport(config, input);
+      TProtocol protocol = factory.getProtocol(transport);
+
+      // Try to read a FuzzTest object from the input
+      FuzzTest obj = new FuzzTest();
+      obj.read(protocol);
+    } catch (TException e) {
+      // Ignore Thrift exceptions - they're expected when fuzzing
+    } catch (Exception e) {
+      // TODO: For now we are ignoring unexpected exceptions, but we should fix this
+      // and handle them appropriately. Need to understand the contract to see if it's a
+      // spec violation or not for us to throw non-TException when parsing.
+    }
+  }
+
+  /**
+   * Common fuzzing test implementation that performs a roundtrip test using the provided protocol
+   * factory. It deserializes the input, serializes it again, and verifies the objects are equal
+   * after a second deserialization.
+   *
+   * @param input The input bytes to fuzz
+   * @param factory The protocol factory to use for serialization/deserialization
+   * @throws Exception if an unexpected error occurs
+   */
+  public static void testRoundtrip(byte[] input, TProtocolFactory factory) throws Exception {
+    try {
+      TConfiguration config = new TConfiguration();
+      config.setMaxMessageSize(FUZZ_MAX_MESSAGE_SIZE);
+
+      // First try to deserialize the raw input bytes
+      TTransport inputTransport = new TMemoryInputTransport(config, input);
+      TProtocol inProtocol = factory.getProtocol(inputTransport);
+
+      // Try to read a FuzzTest object from the input
+      FuzzTest inputObj = new FuzzTest();
+      inputObj.read(inProtocol);
+
+      // Now do the roundtrip test with the successfully deserialized object
+      TMemoryBuffer memoryBuffer = new TMemoryBuffer(config, input.length);
+      TProtocol outProtocol = factory.getProtocol(memoryBuffer);
+      inputObj.write(outProtocol);
+
+      // Get the serialized bytes
+      byte[] serialized = memoryBuffer.getArray();
+
+      // Deserialize again
+      TTransport secondInputTransport = new TMemoryInputTransport(config, serialized);
+      TProtocol secondInProtocol = factory.getProtocol(secondInputTransport);
+      FuzzTest outputObj = new FuzzTest();
+      outputObj.read(secondInProtocol);
+
+      // Assert equality
+      if (!inputObj.equals(outputObj)) {
+        throw new AssertionError("Roundtrip objects are not equal");
+      }
+    } catch (TException e) {
+      // Ignore Thrift exceptions - they're expected when fuzzing
+    } catch (Exception e) {
+      // TODO: For now we are ignoring unexpected exceptions, but we should fix this
+      // and handle them appropriately. Need to understand the contract to see if it's a
+      // spec violation or not for us to throw non-TException when parsing.
+    }
+  }
+}
diff --git a/lib/java/src/test/java/org/apache/thrift/test/fuzz/ParseBinaryFuzzer.java b/lib/java/src/test/java/org/apache/thrift/test/fuzz/ParseBinaryFuzzer.java
new file mode 100644
index 0000000..07935c6
--- /dev/null
+++ b/lib/java/src/test/java/org/apache/thrift/test/fuzz/ParseBinaryFuzzer.java
@@ -0,0 +1,28 @@
+/*
+ * 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.test.fuzz;
+
+import org.apache.thrift.protocol.TBinaryProtocol;
+
+public class ParseBinaryFuzzer {
+  public static void fuzzerTestOneInput(byte[] input) throws Exception {
+    FuzzTestUtils.testParse(input, new TBinaryProtocol.Factory());
+  }
+}
diff --git a/lib/java/src/test/java/org/apache/thrift/test/fuzz/ParseCompactFuzzer.java b/lib/java/src/test/java/org/apache/thrift/test/fuzz/ParseCompactFuzzer.java
new file mode 100644
index 0000000..4c73b29
--- /dev/null
+++ b/lib/java/src/test/java/org/apache/thrift/test/fuzz/ParseCompactFuzzer.java
@@ -0,0 +1,28 @@
+/*
+ * 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.test.fuzz;
+
+import org.apache.thrift.protocol.TCompactProtocol;
+
+public class ParseCompactFuzzer {
+  public static void fuzzerTestOneInput(byte[] input) throws Exception {
+    FuzzTestUtils.testParse(input, new TCompactProtocol.Factory());
+  }
+}
diff --git a/lib/java/src/test/java/org/apache/thrift/test/fuzz/ParseJSONFuzzer.java b/lib/java/src/test/java/org/apache/thrift/test/fuzz/ParseJSONFuzzer.java
new file mode 100644
index 0000000..f7dcaf8
--- /dev/null
+++ b/lib/java/src/test/java/org/apache/thrift/test/fuzz/ParseJSONFuzzer.java
@@ -0,0 +1,28 @@
+/*
+ * 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.test.fuzz;
+
+import org.apache.thrift.protocol.TJSONProtocol;
+
+public class ParseJSONFuzzer {
+  public static void fuzzerTestOneInput(byte[] input) throws Exception {
+    FuzzTestUtils.testParse(input, new TJSONProtocol.Factory());
+  }
+}
diff --git a/lib/java/src/test/java/org/apache/thrift/test/fuzz/README.md b/lib/java/src/test/java/org/apache/thrift/test/fuzz/README.md
new file mode 100644
index 0000000..71a8c9f
--- /dev/null
+++ b/lib/java/src/test/java/org/apache/thrift/test/fuzz/README.md
@@ -0,0 +1,20 @@
+# Java Fuzzing README
+
+The Java Thrift implementation uses Jazzer for fuzzing. Jazzer is a coverage-guided, in-process fuzzer for the JVM.
+
+Unlike the C++ implementation, the Java fuzzers are not directly runnable in a local environment. Instead, Jazzer generates Java programs that need to be executed through the appropriate build system.
+
+We currently have several fuzz targets that test different aspects of the Thrift implementation:
+
+* FuzzParseBinary -- fuzzes the deserialization of the Binary protocol
+* FuzzParseCompact -- fuzzes the deserialization of the Compact protocol
+* FuzzParseJson -- fuzzes the deserialization of the JSON protocol
+* FuzzRoundtripBinary -- fuzzes the roundtrip of the Binary protocol (i.e. serializes then deserializes and compares the result)
+* FuzzRoundtripCompact -- fuzzes the roundtrip of the Compact protocol
+* FuzzRoundtripJson -- fuzzes the roundtrip of the JSON protocol
+
+The fuzzers use Jazzer's mutation engine to generate test cases. Each fuzzer implements the standard Jazzer interface and uses common testing code from the fuzz test utilities.
+
+For more information about Jazzer and its options, see the [Jazzer documentation](https://github.com/CodeIntelligenceTesting/jazzer).
+
+You can also use the corpus generator from the Rust implementation to generate initial corpus files that can be used with these Java fuzzers, since the wire formats are identical between implementations.
diff --git a/lib/java/src/test/java/org/apache/thrift/test/fuzz/RoundtripBinaryFuzzer.java b/lib/java/src/test/java/org/apache/thrift/test/fuzz/RoundtripBinaryFuzzer.java
new file mode 100644
index 0000000..5bb56bd
--- /dev/null
+++ b/lib/java/src/test/java/org/apache/thrift/test/fuzz/RoundtripBinaryFuzzer.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.test.fuzz;
+
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+
+public class RoundtripBinaryFuzzer {
+  public static void fuzzerTestOneInput(byte[] input) throws Exception {
+    TProtocolFactory factory = new TBinaryProtocol.Factory();
+    FuzzTestUtils.testRoundtrip(input, factory);
+  }
+}
diff --git a/lib/java/src/test/java/org/apache/thrift/test/fuzz/RoundtripCompactFuzzer.java b/lib/java/src/test/java/org/apache/thrift/test/fuzz/RoundtripCompactFuzzer.java
new file mode 100644
index 0000000..f3ad5fd
--- /dev/null
+++ b/lib/java/src/test/java/org/apache/thrift/test/fuzz/RoundtripCompactFuzzer.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.test.fuzz;
+
+import org.apache.thrift.protocol.TCompactProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+
+public class RoundtripCompactFuzzer {
+  public static void fuzzerTestOneInput(byte[] input) throws Exception {
+    TProtocolFactory factory = new TCompactProtocol.Factory();
+    FuzzTestUtils.testRoundtrip(input, factory);
+  }
+}
diff --git a/lib/java/src/test/java/org/apache/thrift/test/fuzz/RoundtripJSONFuzzer.java b/lib/java/src/test/java/org/apache/thrift/test/fuzz/RoundtripJSONFuzzer.java
new file mode 100644
index 0000000..61a5719
--- /dev/null
+++ b/lib/java/src/test/java/org/apache/thrift/test/fuzz/RoundtripJSONFuzzer.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.test.fuzz;
+
+import org.apache.thrift.protocol.TJSONProtocol;
+import org.apache.thrift.protocol.TProtocolFactory;
+
+public class RoundtripJSONFuzzer {
+  public static void fuzzerTestOneInput(byte[] input) throws Exception {
+    TProtocolFactory factory = new TJSONProtocol.Factory();
+    FuzzTestUtils.testRoundtrip(input, factory);
+  }
+}