Skip to content

Commit 1d1a881

Browse files
committed
remove dependency on javax packages from jersey jax-rs client and fix PATCH request for newer versions of Java
1 parent 1cef892 commit 1d1a881

File tree

2 files changed

+64
-100
lines changed

2 files changed

+64
-100
lines changed

pom.xml

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -49,27 +49,10 @@
4949

5050
<dependencies>
5151

52-
<!-- Jersey Http client -->
5352
<dependency>
54-
<groupId>org.glassfish.jersey.core</groupId>
55-
<artifactId>jersey-client</artifactId>
56-
<version>${jersey.version}</version>
57-
</dependency>
58-
59-
<!-- Jersey client dependency - as of version 2.26 HK2 hard dependency is removed.
60-
Therefore Jersey HK2 custom InjectionManagerFactory implementation is required.
61-
Details: https://stackoverflow.com/questions/44088493/jersey-stopped-working-with-injectionmanagerfactory-not-found -->
62-
<dependency>
63-
<groupId>org.glassfish.jersey.inject</groupId>
64-
<artifactId>jersey-hk2</artifactId>
65-
<version>${jersey.version}</version>
66-
</dependency>
67-
68-
<!-- Jersey client requires this dependency -->
69-
<dependency>
70-
<groupId>javax.activation</groupId>
71-
<artifactId>activation</artifactId>
72-
<version>1.1.1</version>
53+
<groupId>org.apache.httpcomponents.client5</groupId>
54+
<artifactId>httpclient5-fluent</artifactId>
55+
<version>5.2.1</version>
7356
</dependency>
7457

7558
<!-- Serialization to JSON -->

src/main/java/com/postmarkapp/postmark/client/HttpClient.java

Lines changed: 61 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
package com.postmarkapp.postmark.client;
22

3-
import org.glassfish.jersey.client.ClientConfig;
4-
import org.glassfish.jersey.client.ClientProperties;
5-
import org.glassfish.jersey.client.HttpUrlConnectorProvider;
6-
7-
import javax.ws.rs.client.Client;
8-
import javax.ws.rs.client.ClientBuilder;
9-
import javax.ws.rs.client.Entity;
10-
import javax.ws.rs.client.Invocation.Builder;
11-
import javax.ws.rs.core.Response;
3+
import org.apache.hc.client5.http.config.RequestConfig;
4+
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
5+
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
6+
import org.apache.hc.client5.http.impl.classic.HttpClients;
7+
import org.apache.hc.core5.http.ClassicHttpRequest;
8+
import org.apache.hc.core5.http.io.entity.EntityUtils;
9+
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
10+
import org.apache.hc.core5.util.Timeout;
11+
12+
import java.io.IOException;
1213
import java.util.Map;
1314

1415
/**
@@ -30,21 +31,35 @@ public enum DEFAULTS {
3031
}
3132

3233
private final Map<String,Object> headers;
33-
private final Client client;
34+
private CloseableHttpClient client;
3435

3536
private boolean secureConnection = true;
37+
private int connectTimeoutSeconds = DEFAULTS.CONNECT_TIMEOUT_SECONDS.value;
38+
private int readTimeoutSeconds = DEFAULTS.READ_TIMEOUT_SECONDS.value;
3639

3740
public HttpClient(Map<String,Object> headers, int connectTimeoutSeconds, int readTimeoutSeconds) {
38-
this(headers);
39-
setConnectTimeoutSeconds(connectTimeoutSeconds);
40-
setReadTimeoutSeconds(readTimeoutSeconds);
41+
this.headers = headers;
42+
this.connectTimeoutSeconds = connectTimeoutSeconds;
43+
this.readTimeoutSeconds = readTimeoutSeconds;
44+
buildClientWithCustomConfig();
4145
}
4246

4347
public HttpClient(Map<String,Object> headers) {
4448
this.headers = headers;
45-
this.client = buildClient();
46-
setReadTimeoutSeconds(DEFAULTS.READ_TIMEOUT_SECONDS.value);
47-
setConnectTimeoutSeconds(DEFAULTS.CONNECT_TIMEOUT_SECONDS.value);
49+
buildClientWithCustomConfig();
50+
}
51+
52+
/**
53+
* Overload method for executing requests, which doesn't contain data
54+
*
55+
* @param request_type type of HTTP request
56+
* @param url request URL
57+
* @return simplified HTTP request response (status and response text)
58+
*
59+
* @see #execute(REQUEST_TYPES, String, String) for details
60+
*/
61+
public ClientResponse execute(REQUEST_TYPES request_type, String url) throws IOException {
62+
return execute(request_type, url, null);
4863
}
4964

5065
/**
@@ -56,57 +71,50 @@ public HttpClient(Map<String,Object> headers) {
5671
* @param data data sent for POST/PUT requests
5772
* @return response from HTTP request
5873
*/
59-
public ClientResponse execute(REQUEST_TYPES requestType, String url, String data) {
60-
Response response;
61-
final Builder requestBuilder = clientRequestBuilder((url));
74+
public ClientResponse execute(REQUEST_TYPES requestType, String url, String data) throws IOException {
75+
ClassicHttpRequest request;
6276

6377
switch (requestType) {
6478
case POST:
65-
response = requestBuilder.post(Entity.json(data), Response.class);
79+
request = ClassicRequestBuilder.post(getHttpUrl(url)).setEntity(data).build();
6680
break;
6781

6882
case PUT:
69-
response = requestBuilder.put(Entity.json(data), Response.class);
83+
request = ClassicRequestBuilder.put(getHttpUrl(url)).setEntity(data).build();
7084
break;
7185

7286
case PATCH:
73-
response = requestBuilder.method("PATCH", Entity.json(data), Response.class);
87+
request = ClassicRequestBuilder.patch(getHttpUrl(url)).setEntity(data).build();
7488
break;
7589

7690
case DELETE:
77-
response = requestBuilder.delete(Response.class);
91+
request = ClassicRequestBuilder.delete(getHttpUrl(url)).build();
7892
break;
7993

8094
default:
81-
response = requestBuilder.get(Response.class);
95+
request = ClassicRequestBuilder.get(getHttpUrl(url)).build();
8296
break;
83-
8497
}
8598

86-
return transformResponse(response);
87-
}
99+
for (Map.Entry<String, Object> header : headers.entrySet()) {
100+
request.setHeader(header.getKey(), header.getValue().toString());
101+
}
88102

89-
/**
90-
* Overload method for executing requests, which doesn't contain data
91-
*
92-
* @param request_type type of HTTP request
93-
* @param url request URL
94-
* @return simplified HTTP request response (status and response text)
95-
*
96-
* @see #execute(REQUEST_TYPES, String, String) for details
97-
*/
98-
public ClientResponse execute(REQUEST_TYPES request_type, String url) {
99-
return execute(request_type, url, null);
103+
return client.execute(
104+
request,
105+
response -> new ClientResponse(response.getCode(), EntityUtils.toString(response.getEntity())));
100106
}
101107

102108
// Setters and Getters
103109

104110
public void setConnectTimeoutSeconds(int connectTimeoutSeconds) {
105-
client.property(ClientProperties.CONNECT_TIMEOUT, connectTimeoutSeconds * 1000);
111+
this.connectTimeoutSeconds = connectTimeoutSeconds;
112+
buildClientWithCustomConfig();
106113
}
107114

108115
public void setReadTimeoutSeconds(int readTimeoutSeconds) {
109-
client.property(ClientProperties.READ_TIMEOUT, readTimeoutSeconds * 1000);
116+
this.readTimeoutSeconds = readTimeoutSeconds;
117+
buildClientWithCustomConfig();
110118
}
111119

112120
public void setSecureConnection(boolean secureConnection) {
@@ -123,59 +131,32 @@ private String getHttpUrl(String url) {
123131
*
124132
* @return original HTTP client
125133
*/
126-
public Client getClient() {
134+
public CloseableHttpClient getClient() {
127135
return client;
128136
}
129137

130138
/**
131139
* Build default HTTP client used for requests
132140
*
133-
* Jersey client uses HttpUrlConnection by default which doesn't have PATCH method:
134-
* https://docs.oracle.com/javase/8/docs/api/java/net/HttpURLConnection.html#setRequestMethod-java.lang.String-
135-
* https://github.com/eclipse-ee4j/jersey/issues/4825
136-
*
137-
* Workaround for being able to do PATCH requests is by using reflection, which would work up to Java 16.
138-
* https://stackoverflow.com/questions/55778145/how-to-use-patch-method-with-jersey-invocation-builder
139-
*
140141
* @return initialized HTTP client
141142
*/
142-
private Client buildClient() {
143-
Client client = ClientBuilder.newClient();
144-
// this allows calls to PATCH by using reflection
145-
client.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
146-
return client;
147-
}
148-
149-
private Builder clientRequestBuilder(String url) {
150-
Builder requestBuilder = client.target(getHttpUrl(url)).request();
151-
152-
for (Map.Entry<String, Object> header : headers.entrySet()) {
153-
requestBuilder.header(header.getKey(), header.getValue());
154-
}
155-
156-
return requestBuilder;
157-
}
158-
159-
/**
160-
* Build HTTP client with custom config used for requests
161-
* like:
162-
*
163-
* ClientConfig clientConfig = new ClientConfig();
164-
* clientConfig.connectorProvider(new GrizzlyConnectorProvider());
165-
* clientConfig.connectorProvider(new HttpUrlConnectorProvider().useSetMethodWorkaround());
166-
*/
167-
private Client buildClient(ClientConfig config) {
168-
return ClientBuilder.newClient(config);
143+
private CloseableHttpClient buildClient() {
144+
return HttpClients.createDefault();
169145
}
170146

171147
/**
172-
* Gets simplified HTTP request response that will contain only response and status.
148+
* Build HTTP client used for requests with custom config settings
173149
*
174-
* @param response HTTP request response result
175-
* @return simplified HTTP request response
150+
* @return initialized HTTP client
176151
*/
177-
private ClientResponse transformResponse(Response response) {
178-
return new ClientResponse(response.getStatus(), response.readEntity(String.class));
152+
private void buildClientWithCustomConfig() {
153+
RequestConfig requestConfig = RequestConfig
154+
.custom()
155+
.setConnectTimeout(Timeout.ofSeconds(connectTimeoutSeconds))
156+
.setResponseTimeout(Timeout.ofSeconds(readTimeoutSeconds))
157+
.build();
158+
159+
this.client = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).build();
179160
}
180161

181162
/**

0 commit comments

Comments
 (0)