Skip to content

Commit

Permalink
Replace deprecated Spring Framework classes
Browse files Browse the repository at this point in the history
  • Loading branch information
larsgrefer committed Aug 25, 2023
1 parent 5598deb commit 54d6b4d
Show file tree
Hide file tree
Showing 12 changed files with 406 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ public OkHttpClient okHttp3Client(

eventListener.ifUnique(builder::eventListener);

builder.connectTimeout(okHttpProperties.getConnectTimeout().toMillis(), TimeUnit.MILLISECONDS);
builder.readTimeout(okHttpProperties.getReadTimeout().toMillis(), TimeUnit.MILLISECONDS);
builder.writeTimeout(okHttpProperties.getWriteTimeout().toMillis(), TimeUnit.MILLISECONDS);
builder.pingInterval(okHttpProperties.getPingInterval().toMillis(), TimeUnit.MILLISECONDS);
builder.connectTimeout(okHttpProperties.getConnectTimeout());
builder.readTimeout(okHttpProperties.getReadTimeout());
builder.writeTimeout(okHttpProperties.getWriteTimeout());
builder.pingInterval(okHttpProperties.getPingInterval());

cookieJar.ifUnique(builder::cookieJar);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package io.freefair.spring.okhttp;

import io.freefair.spring.okhttp.client.OkHttpClientRequestFactory;
import okhttp3.OkHttpClient;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
import org.springframework.boot.web.client.RestClientCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.web.client.RestClient;

/**
Expand All @@ -21,7 +21,7 @@ public class OkHttpRestClientAutoConfiguration {

@Bean
public RestClientCustomizer okHttpRestClientCustomizer(OkHttpClient okHttpClient) {
return restClientBuilder -> restClientBuilder.requestFactory(new OkHttp3ClientHttpRequestFactory(okHttpClient));
return restClientBuilder -> restClientBuilder.requestFactory(new OkHttpClientRequestFactory(okHttpClient));
}

}
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
package io.freefair.spring.okhttp;

import io.freefair.spring.okhttp.client.OkHttpClientRequestFactory;
import lombok.AllArgsConstructor;
import okhttp3.OkHttpClient;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
import org.springframework.boot.autoconfigure.web.client.RestTemplateBuilderConfigurer;
import org.springframework.boot.ssl.SslBundle;
import org.springframework.boot.web.client.ClientHttpRequestFactorySettings;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.time.Duration;
import java.util.function.Function;

/**
* @author Lars Grefer
* @see RestTemplateAutoConfiguration
Expand All @@ -30,8 +42,46 @@ public class OkHttpRestTemplateAutoConfiguration {
public RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer restTemplateBuilderConfigurer,
OkHttpClient okHttpClient) {
RestTemplateBuilder builder = new RestTemplateBuilder();
builder = builder.requestFactory(() -> new OkHttp3ClientHttpRequestFactory(okHttpClient));
builder = builder.requestFactory(new RequestFactoryFunction(okHttpClient));
return restTemplateBuilderConfigurer.configure(builder);
}

@AllArgsConstructor
static class RequestFactoryFunction implements Function<ClientHttpRequestFactorySettings, ClientHttpRequestFactory> {

private OkHttpClient okHttpClient;

@Override
public ClientHttpRequestFactory apply(ClientHttpRequestFactorySettings settings) {

OkHttpClient.Builder builder = okHttpClient.newBuilder();

Duration connectTimeout = settings.connectTimeout();
if (connectTimeout != null) {
builder.connectTimeout(connectTimeout);
}

Duration readTimeout = settings.readTimeout();
if (readTimeout != null) {
builder.readTimeout(readTimeout);
}

SslBundle sslBundle = settings.sslBundle();
if (sslBundle != null) {
Assert.state(!sslBundle.getOptions().isSpecified(), "SSL Options cannot be specified with OkHttp");

SSLContext sslContext = sslBundle.createSslContext();
SSLSocketFactory socketFactory = sslContext.getSocketFactory();

TrustManager[] trustManagers = sslBundle.getManagers().getTrustManagers();
Assert.state(trustManagers.length == 1,
"Trust material must be provided in the SSL bundle for OkHttp3ClientHttpRequestFactory");

builder.sslSocketFactory(socketFactory, (X509TrustManager) trustManagers[0]);
}

return new OkHttpClientRequestFactory(builder.build());
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package io.freefair.spring.okhttp.client;

import lombok.RequiredArgsConstructor;
import okhttp3.*;
import okio.Buffer;
import okio.ByteString;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.StreamingHttpOutputMessage;
import org.springframework.http.client.AbstractClientHttpRequest;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URI;

/**
* OkHttp based {@link ClientHttpRequest} implementation.
*
* @author Lars Grefer
* @see OkHttpClientRequestFactory
*/
@RequiredArgsConstructor
class OkHttpClientRequest extends AbstractClientHttpRequest implements StreamingHttpOutputMessage {

private final OkHttpClient okHttpClient;

private final URI uri;

private final HttpMethod method;


@Nullable
private Body streamingBody;

@Nullable
private Buffer bufferBody;


@Override
public HttpMethod getMethod() {
return method;
}

@Override
public URI getURI() {
return uri;
}

@Override
public void setBody(Body body) {
Assert.notNull(body, "body must not be null");
assertNotExecuted();
Assert.state(bufferBody == null, "getBody has already been used.");
this.streamingBody = body;
}

@Override
protected OutputStream getBodyInternal(HttpHeaders headers) {
Assert.state(this.streamingBody == null, "setBody has already been used.");

if (bufferBody == null) {
bufferBody = new Buffer();
}

return bufferBody.outputStream();
}

@Override
protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {

Request okHttpRequest = buildRequest(headers);

Response okHttpResponse = this.okHttpClient.newCall(okHttpRequest).execute();

return new OkHttpClientResponse(okHttpResponse);
}

private Request buildRequest(HttpHeaders headers) throws MalformedURLException {

Request.Builder builder = new Request.Builder();

builder.url(uri.toURL());

MediaType contentType = null;

String contentTypeHeader = headers.getFirst(HttpHeaders.CONTENT_TYPE);
if (StringUtils.hasText(contentTypeHeader)) {
contentType = MediaType.parse(contentTypeHeader);
}

RequestBody body = null;

if (bufferBody != null) {
ByteString bodyData = bufferBody.readByteString();
if (headers.getContentLength() < 0) {
headers.setContentLength(bodyData.size());
}
body = RequestBody.create(bodyData, contentType);
} else if (streamingBody != null) {
body = new StreamingBodyRequestBody(streamingBody, contentType, headers.getContentLength());
} else if (okhttp3.internal.http.HttpMethod.requiresRequestBody(method.name())) {
body = RequestBody.create(new byte[0], contentType);
}

builder.method(getMethod().name(), body);

headers.forEach((name, values) -> {
for (String value : values) {
builder.addHeader(name, value);
}
});

return builder.build();


}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package io.freefair.spring.okhttp.client;

import lombok.NonNull;
import okhttp3.OkHttpClient;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;

import java.net.URI;

/**
* OkHttp based {@link ClientHttpRequestFactory} implementation.
* <p>
* Serves as replacement for the deprecated {@link org.springframework.http.client.OkHttp3ClientHttpRequestFactory}.
*
* @author Lars Grefer
*/
public record OkHttpClientRequestFactory(@NonNull OkHttpClient okHttpClient) implements ClientHttpRequestFactory {

@Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) {
return new OkHttpClientRequest(okHttpClient, uri, httpMethod);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package io.freefair.spring.okhttp.client;

import kotlin.Pair;
import lombok.RequiredArgsConstructor;
import okhttp3.Headers;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.client.ClientHttpResponse;

import java.io.InputStream;

/**
* OkHttp based {@link ClientHttpResponse} implementation.
*
* @author Lars Grefer
* @see OkHttpClientRequest
*/
@RequiredArgsConstructor
class OkHttpClientResponse implements ClientHttpResponse {

private final Response okHttpResponse;

private HttpHeaders springHeaders;

@Override
public HttpStatusCode getStatusCode() {
return HttpStatusCode.valueOf(okHttpResponse.code());
}

@Override
public String getStatusText() {
return okHttpResponse.message();
}

@Override
public void close() {
ResponseBody body = okHttpResponse.body();
if (body != null) {
body.close();
}
}

@Override
public InputStream getBody() {
ResponseBody body = okHttpResponse.body();
if (body != null) {
return body.byteStream();
} else {
return InputStream.nullInputStream();
}
}

@Override
public HttpHeaders getHeaders() {
if (springHeaders == null) {
springHeaders = convertHeaders(okHttpResponse.headers());
}

return springHeaders;
}

/**
* Converts the given {@link Headers OkHttp Headers} to {@link HttpHeaders Spring Web HttpHeaders}
*/
static HttpHeaders convertHeaders(Headers okHttpHeaders) {
HttpHeaders springHeaders = new HttpHeaders();

for (Pair<? extends String, ? extends String> header : okHttpHeaders) {
springHeaders.add(header.getFirst(), header.getSecond());
}

return springHeaders;
}


}
Loading

0 comments on commit 54d6b4d

Please sign in to comment.