THRIFT-5700: Migrate to JakartaEE and Apache HttpComponents 5 (#2746)
- Migrate to JakartaEE and Apache HttpComponents 5
- Modify C code to choose which namespace choose for "Generated" annotations (jakarta by default)
diff --git a/compiler/cpp/src/thrift/generate/t_java_generator.cc b/compiler/cpp/src/thrift/generate/t_java_generator.cc
index 7e55bf9..172d137 100644
--- a/compiler/cpp/src/thrift/generate/t_java_generator.cc
+++ b/compiler/cpp/src/thrift/generate/t_java_generator.cc
@@ -80,6 +80,7 @@
rethrow_unhandled_exceptions_ = false;
unsafe_binaries_ = false;
annotations_as_metadata_ = false;
+ jakarta_annotations_ = false;
for (iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
if (iter->first.compare("beans") == 0) {
bean_style_ = true;
@@ -128,6 +129,8 @@
unsafe_binaries_ = true;
} else if (iter->first.compare("annotations_as_metadata") == 0) {
annotations_as_metadata_ = true;
+ } else if (iter->first.compare("jakarta_annotations") == 0) {
+ jakarta_annotations_ = true;
} else {
throw "unknown option java:" + iter->first;
}
@@ -465,6 +468,7 @@
bool rethrow_unhandled_exceptions_;
bool unsafe_binaries_;
bool annotations_as_metadata_;
+ bool jakarta_annotations_;
};
/**
@@ -5808,7 +5812,12 @@
void t_java_generator::generate_javax_generated_annotation(ostream& out) {
time_t seconds = time(nullptr);
struct tm* now = localtime(&seconds);
- indent(out) << "@javax.annotation.Generated(value = \"" << autogen_summary() << "\"";
+ if (jakarta_annotations_) {
+ indent(out) << "@jakarta.annotation.Generated(value = \"" << autogen_summary() << "\"";
+ } else {
+ indent(out) << "@javax.annotation.Generated(value = \"" << autogen_summary() << "\"";
+ }
+
if (undated_generated_annotations_) {
out << ")" << endl;
} else {
@@ -5849,5 +5858,6 @@
" undated: suppress the date at @Generated annotations\n"
" suppress: suppress @Generated annotations entirely\n"
" unsafe_binaries: Do not copy ByteBuffers in constructors, getters, and setters.\n"
+ " jakarta_annotations: generate jakarta annotations (javax by default)\n"
" annotations_as_metadata:\n"
" Include Thrift field annotations as metadata in the generated code.\n")
diff --git a/lib/java/gradle.properties b/lib/java/gradle.properties
index bfd5c22..2e3d041 100644
--- a/lib/java/gradle.properties
+++ b/lib/java/gradle.properties
@@ -25,14 +25,14 @@
maven-repository-id=apache.releases.https
# Dependency versions
-httpclient.version=4.5.13
-httpcore.version=4.4.15
+httpclient.version=5.2.1
+httpcore.version=5.2
slf4j.version=1.7.36
-servlet.version=4.0.3
-tomcat.embed.version=9.0.43
-junit.version=5.8.2
+servlet.version=5.0.0
+tomcat.embed.version=10.1.4
+junit.version=5.9.1
mockito.version=5.3.0
-javax.annotation.version=1.3.5
+javax.annotation.version=2.1.1
commons-lang3.version=3.12.0
org.gradle.jvmargs=--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
diff --git a/lib/java/gradle/environment.gradle b/lib/java/gradle/environment.gradle
index 8c14a52..977c07e 100644
--- a/lib/java/gradle/environment.gradle
+++ b/lib/java/gradle/environment.gradle
@@ -61,8 +61,8 @@
dependencies {
implementation "org.slf4j:slf4j-api:${slf4jVersion}"
- implementation "org.apache.httpcomponents:httpclient:${httpclientVersion}"
- implementation "org.apache.httpcomponents:httpcore:${httpcoreVersion}"
+ implementation "org.apache.httpcomponents.client5:httpclient5:${httpclientVersion}"
+ implementation "org.apache.httpcomponents.core5:httpcore5:${httpcoreVersion}"
implementation "jakarta.servlet:jakarta.servlet-api:${servletVersion}"
implementation "jakarta.annotation:jakarta.annotation-api:${javaxAnnotationVersion}"
implementation "org.apache.commons:commons-lang3:${commonsLang3Version}"
diff --git a/lib/java/gradle/generateTestThrift.gradle b/lib/java/gradle/generateTestThrift.gradle
index 10bb16b..7e33b15 100644
--- a/lib/java/gradle/generateTestThrift.gradle
+++ b/lib/java/gradle/generateTestThrift.gradle
@@ -38,7 +38,7 @@
// Code generation for Unit Testing
// A callable closure to make this easier
-ext.thriftCompile = { Task task, String thriftFileName, String generator = 'java', File outputDir = genSrc ->
+ext.thriftCompile = { Task task, String thriftFileName, String generator = 'java:jakarta_annotations', File outputDir = genSrc ->
def thriftFile = file("$thriftRoot/test/$thriftFileName")
if (!thriftFile.exists()) {
thriftFile = file("$projectDir/src/test/resources/$thriftFileName")
@@ -98,7 +98,7 @@
ext.outputBuffer = new ByteArrayOutputStream()
- thriftCompile(it, 'JavaOptionTypeJdk8Test.thrift', 'java:option_type=jdk8', genOptionTypeJdk8Src)
+ thriftCompile(it, 'JavaOptionTypeJdk8Test.thrift', 'java:option_type=jdk8,jakarta_annotations', genOptionTypeJdk8Src)
}
task generateBeanJava(group: 'Build') {
@@ -107,7 +107,7 @@
ext.outputBuffer = new ByteArrayOutputStream()
- thriftCompile(it, 'JavaBeansTest.thrift', 'java:beans,nocamel,future_iface', genBeanSrc)
+ thriftCompile(it, 'JavaBeansTest.thrift', 'java:beans,nocamel,future_iface,jakarta_annotations', genBeanSrc)
}
task generateReuseJava(group: 'Build') {
@@ -116,7 +116,7 @@
ext.outputBuffer = new ByteArrayOutputStream()
- thriftCompile(it, 'FullCamelTest.thrift', 'java:fullcamel,future_iface', genFullCamelSrc)
+ thriftCompile(it, 'FullCamelTest.thrift', 'java:fullcamel,future_iface,jakarta_annotations', genFullCamelSrc)
}
task generateFullCamelJava(group: 'Build') {
@@ -125,7 +125,7 @@
ext.outputBuffer = new ByteArrayOutputStream()
- thriftCompile(it, 'ReuseObjects.thrift', 'java:reuse_objects', genReuseSrc)
+ thriftCompile(it, 'ReuseObjects.thrift', 'java:reuse_objects,jakarta_annotations', genReuseSrc)
}
task generateUnsafeBinariesJava(group: 'Build') {
@@ -134,7 +134,7 @@
ext.outputBuffer = new ByteArrayOutputStream()
- thriftCompile(it, 'UnsafeTypes.thrift', 'java:unsafe_binaries', genUnsafeSrc)
+ thriftCompile(it, 'UnsafeTypes.thrift', 'java:unsafe_binaries,jakarta_annotations', genUnsafeSrc)
}
task generateWithAnnotationMetadata(group: 'Build') {
@@ -143,7 +143,7 @@
ext.outputBuffer = new ByteArrayOutputStream()
- thriftCompile(it, 'JavaAnnotationTest.thrift', 'java:annotations_as_metadata', genSrc)
+ thriftCompile(it, 'JavaAnnotationTest.thrift', 'java:annotations_as_metadata,jakarta_annotations', genSrc)
}
task generateJavaDefinitionOrderTestJava(group: 'Build') {
@@ -152,6 +152,6 @@
ext.outputBuffer = new ByteArrayOutputStream()
- thriftCompile(it, 'JavaDefinitionOrderA.thrift', 'java', genDefinitionOrderTestASrc)
- thriftCompile(it, 'JavaDefinitionOrderB.thrift', 'java', genDefinitionOrderTestBSrc)
+ thriftCompile(it, 'JavaDefinitionOrderA.thrift', 'java:jakarta_annotations', genDefinitionOrderTestASrc)
+ thriftCompile(it, 'JavaDefinitionOrderB.thrift', 'java:jakarta_annotations', genDefinitionOrderTestBSrc)
}
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 2807a43..54f3424 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
@@ -29,7 +29,7 @@
import java.util.Set;
import java.util.UUID;
import java.util.stream.IntStream;
-import org.apache.http.impl.client.HttpClients;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.thrift.TApplicationException;
import org.apache.thrift.TException;
import org.apache.thrift.TSerializer;
diff --git a/lib/java/src/main/java/org/apache/thrift/THttpClientResponseHandler.java b/lib/java/src/main/java/org/apache/thrift/THttpClientResponseHandler.java
new file mode 100644
index 0000000..1ed1318
--- /dev/null
+++ b/lib/java/src/main/java/org/apache/thrift/THttpClientResponseHandler.java
@@ -0,0 +1,74 @@
+package org.apache.thrift;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.io.HttpClientResponseHandler;
+
+public class THttpClientResponseHandler implements HttpClientResponseHandler<InputStream> {
+ @Override
+ public InputStream handleResponse(ClassicHttpResponse response)
+ throws HttpException, IOException {
+ try (InputStream is = response.getEntity().getContent()) {
+ int responseCode = response.getCode();
+ if (responseCode != HttpStatus.SC_OK) {
+ throw new IOException("HTTP Response code: " + responseCode);
+ }
+ byte[] readByteArray = readIntoByteArray(is);
+ 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.
+ }
+ return new ByteArrayInputStream(readByteArray);
+ } catch (IOException ioe) {
+ throw ioe;
+ }
+ }
+
+ /**
+ * 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.
+ *
+ * @param is input stream
+ * @return read bytes
+ * @throws IOException when exception during read
+ */
+ private static byte[] readIntoByteArray(InputStream is) throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte[] buf = new byte[1024];
+ int len;
+ do {
+ len = is.read(buf);
+ if (len > 0) {
+ baos.write(buf, 0, len);
+ }
+ } while (-1 != len);
+ return baos.toByteArray();
+ }
+
+ /**
+ * 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();
+ }
+ }
+ }
+}
diff --git a/lib/java/src/main/java/org/apache/thrift/server/TExtensibleServlet.java b/lib/java/src/main/java/org/apache/thrift/server/TExtensibleServlet.java
index fa5acac..91f0515 100644
--- a/lib/java/src/main/java/org/apache/thrift/server/TExtensibleServlet.java
+++ b/lib/java/src/main/java/org/apache/thrift/server/TExtensibleServlet.java
@@ -19,18 +19,18 @@
package org.apache.thrift.server;
+import jakarta.servlet.ServletConfig;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
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;
diff --git a/lib/java/src/main/java/org/apache/thrift/server/TServlet.java b/lib/java/src/main/java/org/apache/thrift/server/TServlet.java
index 831842b..dbdf615 100644
--- a/lib/java/src/main/java/org/apache/thrift/server/TServlet.java
+++ b/lib/java/src/main/java/org/apache/thrift/server/TServlet.java
@@ -1,15 +1,15 @@
package org.apache.thrift.server;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
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;
diff --git a/lib/java/src/main/java/org/apache/thrift/transport/THttpClient.java b/lib/java/src/main/java/org/apache/thrift/transport/THttpClient.java
index e514dd5..3a5ec36 100644
--- a/lib/java/src/main/java/org/apache/thrift/transport/THttpClient.java
+++ b/lib/java/src/main/java/org/apache/thrift/transport/THttpClient.java
@@ -19,7 +19,6 @@
package org.apache.thrift.transport;
-import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -28,15 +27,15 @@
import java.util.Collections;
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.config.RequestConfig;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.entity.ByteArrayEntity;
+import org.apache.hc.client5.http.classic.HttpClient;
+import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.config.ConnectionConfig;
+import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
+import org.apache.hc.core5.util.Timeout;
import org.apache.thrift.TConfiguration;
+import org.apache.thrift.THttpClientResponseHandler;
/**
* HTTP implementation of the TTransport interface. Used for working with a Thrift web services
@@ -139,9 +138,9 @@
this.client = client;
this.host =
new HttpHost(
+ url_.getProtocol(),
url_.getHost(),
- -1 == url_.getPort() ? url_.getDefaultPort() : url_.getPort(),
- url_.getProtocol());
+ -1 == url_.getPort() ? url_.getDefaultPort() : url_.getPort());
} catch (IOException iox) {
throw new TTransportException(iox);
}
@@ -154,9 +153,9 @@
this.client = client;
this.host =
new HttpHost(
+ url_.getProtocol(),
url_.getHost(),
- -1 == url_.getPort() ? url_.getDefaultPort() : url_.getPort(),
- url_.getProtocol());
+ -1 == url_.getPort() ? url_.getDefaultPort() : url_.getPort());
} catch (IOException iox) {
throw new TTransportException(iox);
}
@@ -166,6 +165,13 @@
connectTimeout_ = timeout;
}
+ /**
+ * Use instead {@link
+ * org.apache.hc.client5.http.impl.io.BasicHttpClientConnectionManager#setConnectionConfig} or
+ * {@link
+ * org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager#setDefaultConnectionConfig}
+ */
+ @Deprecated
public void setReadTimeout(int timeout) {
readTimeout_ = timeout;
}
@@ -230,14 +236,24 @@
RequestConfig requestConfig = RequestConfig.DEFAULT;
if (connectTimeout_ > 0) {
requestConfig =
- RequestConfig.copy(requestConfig).setConnectionRequestTimeout(connectTimeout_).build();
- }
- if (readTimeout_ > 0) {
- requestConfig = RequestConfig.copy(requestConfig).setSocketTimeout(readTimeout_).build();
+ RequestConfig.copy(requestConfig)
+ .setConnectionRequestTimeout(Timeout.ofMilliseconds(connectTimeout_))
+ .build();
}
return requestConfig;
}
+ private ConnectionConfig getConnectionConfig() {
+ ConnectionConfig connectionConfig = ConnectionConfig.DEFAULT;
+ if (readTimeout_ > 0) {
+ connectionConfig =
+ ConnectionConfig.copy(connectionConfig)
+ .setSocketTimeout(Timeout.ofMilliseconds(readTimeout_))
+ .build();
+ }
+ return connectionConfig;
+ }
+
private static Map<String, String> getDefaultHeaders() {
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "application/x-thrift");
@@ -246,22 +262,6 @@
return headers;
}
- /**
- * 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.");
@@ -279,64 +279,17 @@
if (null != customHeaders_) {
customHeaders_.forEach(post::addHeader);
}
- post.setEntity(new ByteArrayEntity(data));
- HttpResponse response = this.client.execute(this.host, post);
- handleResponse(response);
+ post.setEntity(new ByteArrayEntity(data, null));
+ inputStream_ = client.execute(this.host, post, new THttpClientResponseHandler());
} catch (IOException ioe) {
// Abort method so the connection gets released back to the connection manager
post.abort();
throw new TTransportException(ioe);
} finally {
resetConsumedMessageSize(-1);
- post.releaseConnection();
}
}
- private void handleResponse(HttpResponse response) throws TTransportException {
- // Retrieve the InputStream BEFORE checking the status code so
- // resources get freed in the with clause.
- try (InputStream is = response.getEntity().getContent()) {
- int responseCode = response.getStatusLine().getStatusCode();
- if (responseCode != HttpStatus.SC_OK) {
- throw new TTransportException("HTTP Response code: " + responseCode);
- }
- byte[] readByteArray = readIntoByteArray(is);
- 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(readByteArray);
- } catch (IOException ioe) {
- throw new TTransportException(ioe);
- }
- }
-
- /**
- * 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.
- *
- * @param is input stream
- * @return read bytes
- * @throws IOException when exception during read
- */
- private static byte[] readIntoByteArray(InputStream is) throws IOException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- byte[] buf = new byte[1024];
- int len;
- do {
- len = is.read(buf);
- if (len > 0) {
- baos.write(buf, 0, len);
- }
- } while (-1 != len);
- return baos.toByteArray();
- }
-
public void flush() throws TTransportException {
if (null != this.client) {