Skip to content

Commit 83728c6

Browse files
author
James Bognar
committed
Next Generation RestClient
1 parent c56362c commit 83728c6

8 files changed

Lines changed: 704 additions & 0 deletions

File tree

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Licensed to the Apache Software Foundation (ASF) under one or more
4+
contributor license agreements. See the NOTICE file distributed with
5+
this work for additional information regarding copyright ownership.
6+
The ASF licenses this file to You under the Apache License, Version 2.0
7+
(the "License"); you may not use this file except in compliance with
8+
the License. You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
-->
18+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
19+
20+
<modelVersion>4.0.0</modelVersion>
21+
22+
<parent>
23+
<groupId>org.apache.juneau</groupId>
24+
<artifactId>juneau-rest</artifactId>
25+
<version>9.2.1-SNAPSHOT</version>
26+
</parent>
27+
28+
<artifactId>juneau-ng-rest-client-apache-httpclient-45</artifactId>
29+
<name>Apache Juneau NG REST Client — Apache HttpClient 4.5 Transport</name>
30+
<description>Apache HttpClient 4.5 transport adapter for the next-generation Juneau REST client.</description>
31+
<packaging>bundle</packaging>
32+
33+
<properties>
34+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
35+
</properties>
36+
37+
<dependencies>
38+
<dependency>
39+
<groupId>org.apache.juneau</groupId>
40+
<artifactId>juneau-rest-client</artifactId>
41+
<version>${project.version}</version>
42+
</dependency>
43+
<dependency>
44+
<groupId>org.apache.httpcomponents</groupId>
45+
<artifactId>httpclient</artifactId>
46+
<version>4.5.14</version>
47+
</dependency>
48+
</dependencies>
49+
50+
<build>
51+
<plugins>
52+
<plugin>
53+
<groupId>org.apache.felix</groupId>
54+
<artifactId>maven-bundle-plugin</artifactId>
55+
<extensions>true</extensions>
56+
<configuration>
57+
<supportIncrementalBuild>true</supportIncrementalBuild>
58+
</configuration>
59+
<executions>
60+
<execution>
61+
<id>bundle-manifest</id>
62+
<phase>process-classes</phase>
63+
<goals>
64+
<goal>manifest</goal>
65+
</goals>
66+
</execution>
67+
</executions>
68+
</plugin>
69+
<plugin>
70+
<groupId>org.apache.maven.plugins</groupId>
71+
<artifactId>maven-source-plugin</artifactId>
72+
<executions>
73+
<execution>
74+
<id>attach-sources</id>
75+
<phase>verify</phase>
76+
<goals>
77+
<goal>jar-no-fork</goal>
78+
</goals>
79+
</execution>
80+
</executions>
81+
</plugin>
82+
<plugin>
83+
<groupId>org.apache.maven.plugins</groupId>
84+
<artifactId>maven-jar-plugin</artifactId>
85+
</plugin>
86+
</plugins>
87+
</build>
88+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.juneau.ng.rest.client.apachehttpclient45;
18+
19+
import java.io.*;
20+
21+
import org.apache.http.*;
22+
import org.apache.http.client.methods.*;
23+
import org.apache.http.entity.*;
24+
import org.apache.http.impl.client.*;
25+
import org.apache.juneau.ng.rest.client.*;
26+
27+
/**
28+
* {@link HttpTransport} implementation backed by Apache HttpClient 4.5.
29+
*
30+
* <p>
31+
* This transport is auto-discovered via {@link java.util.ServiceLoader} when
32+
* {@code org.apache.httpcomponents:httpclient} is on the classpath. You can also instantiate it explicitly:
33+
*
34+
* <p class='bjava'>
35+
* <jv>transport</jv> = ApacheHc45Transport.<jsm>builder</jsm>()
36+
* .httpClient(HttpClients.createDefault())
37+
* .build();
38+
*
39+
* <jv>client</jv> = NgRestClient.<jsm>builder</jsm>()
40+
* .transport(<jv>transport</jv>)
41+
* .build();
42+
* </p>
43+
*
44+
* <p>
45+
* <b>Beta — API subject to change:</b> This type is part of the next-generation REST client and HTTP stack
46+
* ({@code org.apache.juneau.ng.*}).
47+
* It is not API-frozen: binary- and source-incompatible changes may appear in the <b>next major</b> Juneau release
48+
* (and possibly earlier).
49+
*
50+
* <h5 class='section'>See Also:</h5><ul>
51+
* <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/juneau-ng-rest-client">juneau-ng REST client</a>
52+
* </ul>
53+
*
54+
* @since 9.2.1
55+
*/
56+
@SuppressWarnings({
57+
"resource" // httpClient is owned by this transport and closed in close()
58+
})
59+
public final class ApacheHc45Transport implements HttpTransport {
60+
61+
private final CloseableHttpClient httpClient;
62+
63+
ApacheHc45Transport(ApacheHc45TransportBuilder builder) {
64+
this.httpClient = builder.httpClient != null ? builder.httpClient : HttpClients.createDefault();
65+
}
66+
67+
/**
68+
* Returns a new builder for this transport.
69+
*
70+
* @return A new builder. Never <jk>null</jk>.
71+
*/
72+
public static ApacheHc45TransportBuilder builder() {
73+
return new ApacheHc45TransportBuilder();
74+
}
75+
76+
/**
77+
* Returns a new instance backed by a default {@link CloseableHttpClient}.
78+
*
79+
* @return A new instance. Never <jk>null</jk>.
80+
*/
81+
public static ApacheHc45Transport create() {
82+
return builder().build();
83+
}
84+
85+
@Override /* HttpTransport */
86+
public TransportResponse execute(TransportRequest request) throws TransportException {
87+
var hcRequest = buildHcRequest(request);
88+
CloseableHttpResponse hcResponse;
89+
try {
90+
hcResponse = httpClient.execute(hcRequest);
91+
} catch (IOException e) {
92+
throw new TransportException("HTTP transport error: " + e.getMessage(), e);
93+
}
94+
return buildTransportResponse(hcResponse);
95+
}
96+
97+
@Override /* Closeable */
98+
public void close() throws IOException {
99+
httpClient.close();
100+
}
101+
102+
// -----------------------------------------------------------------------------------------------------------------
103+
// Internal helpers
104+
// -----------------------------------------------------------------------------------------------------------------
105+
106+
private static HttpUriRequest buildHcRequest(TransportRequest request) throws TransportException {
107+
var builder = RequestBuilder.create(request.getMethod()).setUri(request.getUri());
108+
for (var h : request.getHeaders())
109+
builder.addHeader(h.name(), h.value());
110+
var body = request.getBody();
111+
if (body != null)
112+
builder.setEntity(new TransportBodyEntity(body));
113+
try {
114+
return builder.build();
115+
} catch (Exception e) {
116+
throw new TransportException("Failed to build HTTP request: " + e.getMessage(), e);
117+
}
118+
}
119+
120+
private static TransportResponse buildTransportResponse(CloseableHttpResponse hcResponse) throws TransportException {
121+
var statusLine = hcResponse.getStatusLine();
122+
var builder = TransportResponse.builder()
123+
.statusCode(statusLine.getStatusCode())
124+
.reasonPhrase(statusLine.getReasonPhrase())
125+
.closeCallback(hcResponse);
126+
for (var h : hcResponse.getAllHeaders())
127+
builder.header(h.getName(), h.getValue());
128+
var entity = hcResponse.getEntity();
129+
if (entity != null) {
130+
try {
131+
builder.body(entity.getContent());
132+
} catch (IOException e) {
133+
throw new TransportException("Failed to read response body: " + e.getMessage(), e);
134+
}
135+
}
136+
return builder.build();
137+
}
138+
139+
// -----------------------------------------------------------------------------------------------------------------
140+
// TransportBodyEntity — bridges TransportBody to Apache HttpEntity
141+
// -----------------------------------------------------------------------------------------------------------------
142+
143+
/**
144+
* Bridges a {@link TransportBody} to Apache HttpClient's {@link AbstractHttpEntity}.
145+
*/
146+
private static final class TransportBodyEntity extends AbstractHttpEntity {
147+
148+
private final TransportBody body;
149+
150+
TransportBodyEntity(TransportBody body) {
151+
this.body = body;
152+
var ct = body.getContentType();
153+
if (ct != null)
154+
setContentType(ct);
155+
}
156+
157+
@Override /* HttpEntity */
158+
public boolean isRepeatable() {
159+
return body.isRepeatable();
160+
}
161+
162+
@Override /* HttpEntity */
163+
public long getContentLength() {
164+
return body.getContentLength();
165+
}
166+
167+
@Override /* HttpEntity */
168+
public InputStream getContent() throws UnsupportedOperationException {
169+
throw new UnsupportedOperationException("Use writeTo(OutputStream) instead");
170+
}
171+
172+
@Override /* HttpEntity */
173+
public void writeTo(OutputStream out) throws IOException {
174+
body.writeTo(out);
175+
}
176+
177+
@Override /* HttpEntity */
178+
public boolean isStreaming() {
179+
return !body.isRepeatable();
180+
}
181+
}
182+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.juneau.ng.rest.client.apachehttpclient45;
18+
19+
import org.apache.http.impl.client.*;
20+
21+
/**
22+
* Fluent builder for {@link ApacheHc45Transport}.
23+
*
24+
* <p>
25+
* Obtain an instance via {@link ApacheHc45Transport#builder()}.
26+
*
27+
* <p>
28+
* <b>Beta — API subject to change:</b> This type is part of the next-generation REST client and HTTP stack
29+
* ({@code org.apache.juneau.ng.*}).
30+
* It is not API-frozen: binary- and source-incompatible changes may appear in the <b>next major</b> Juneau release
31+
* (and possibly earlier).
32+
*
33+
* <h5 class='section'>See Also:</h5><ul>
34+
* <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/juneau-ng-rest-client">juneau-ng REST client</a>
35+
* </ul>
36+
*
37+
* @since 9.2.1
38+
*/
39+
public final class ApacheHc45TransportBuilder {
40+
41+
CloseableHttpClient httpClient;
42+
43+
ApacheHc45TransportBuilder() {}
44+
45+
/**
46+
* Sets the underlying {@link CloseableHttpClient} to use.
47+
*
48+
* <p>
49+
* If not set, a default client is created via {@link HttpClients#createDefault()}.
50+
*
51+
* <p>
52+
* The transport takes ownership of the client and will close it when {@link ApacheHc45Transport#close()} is called.
53+
*
54+
* @param value The client to use. Must not be <jk>null</jk>.
55+
* @return This object.
56+
*/
57+
public ApacheHc45TransportBuilder httpClient(CloseableHttpClient value) {
58+
httpClient = value;
59+
return this;
60+
}
61+
62+
/**
63+
* Builds and returns the {@link ApacheHc45Transport}.
64+
*
65+
* @return A new instance. Never <jk>null</jk>.
66+
*/
67+
public ApacheHc45Transport build() {
68+
return new ApacheHc45Transport(this);
69+
}
70+
}

0 commit comments

Comments
 (0)