-
-
Save tbroyer/509aeae1e0738bcdd28a to your computer and use it in GitHub Desktop.
| /* | |
| * Copyright 2015 Thomas Broyer | |
| * | |
| * Licensed 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. | |
| */ | |
| import com.squareup.okhttp.Headers; | |
| import com.squareup.okhttp.MediaType; | |
| import com.squareup.okhttp.OkHttpClient; | |
| import com.squareup.okhttp.Request; | |
| import com.squareup.okhttp.RequestBody; | |
| import com.squareup.okhttp.Response; | |
| import java.io.IOException; | |
| import java.io.InputStream; | |
| import java.util.List; | |
| import java.util.Map; | |
| import javax.annotation.Nullable; | |
| import javax.inject.Inject; | |
| import javax.net.ssl.HostnameVerifier; | |
| import javax.net.ssl.SSLContext; | |
| import javax.ws.rs.ProcessingException; | |
| import javax.ws.rs.core.HttpHeaders; | |
| import javax.ws.rs.core.MultivaluedMap; | |
| import okio.Buffer; | |
| import okio.BufferedSink; | |
| import org.jboss.resteasy.client.jaxrs.ClientHttpEngine; | |
| import org.jboss.resteasy.client.jaxrs.internal.ClientInvocation; | |
| import org.jboss.resteasy.client.jaxrs.internal.ClientResponse; | |
| import org.jboss.resteasy.util.CaseInsensitiveMap; | |
| /** | |
| * @author Thomas Broyer <t.broyer@ltgt.net> | |
| */ | |
| public class OkHttpClientEngine implements ClientHttpEngine { | |
| private final OkHttpClient client; | |
| private SSLContext sslContext; | |
| @Inject | |
| public OkHttpClientEngine(OkHttpClient client) { | |
| this.client = client; | |
| } | |
| @Override | |
| public SSLContext getSslContext() { | |
| return sslContext; | |
| } | |
| public void setSslContext(SSLContext sslContext) { | |
| this.sslContext = sslContext; | |
| } | |
| @Override | |
| public HostnameVerifier getHostnameVerifier() { | |
| return client.getHostnameVerifier(); | |
| } | |
| @Override | |
| public ClientResponse invoke(ClientInvocation request) { | |
| Request req = createRequest(request); | |
| Response response; | |
| try { | |
| response = client.newCall(req).execute(); | |
| } catch (IOException e) { | |
| throw new ProcessingException("Unable to invoke request", e); | |
| } | |
| return createResponse(request, response); | |
| } | |
| private Request createRequest(ClientInvocation request) { | |
| Request.Builder builder = new Request.Builder() | |
| .method(request.getMethod(), createRequestBody(request)) | |
| .url(request.getUri().toString()); | |
| for (Map.Entry<String, List<String>> header : request.getHeaders().asMap().entrySet()) { | |
| String headerName = header.getKey(); | |
| for (String headerValue : header.getValue()) { | |
| builder.addHeader(headerName, headerValue); | |
| } | |
| } | |
| return builder.build(); | |
| } | |
| @Nullable | |
| private RequestBody createRequestBody(final ClientInvocation request) { | |
| if (request.getEntity() == null) { | |
| return null; | |
| } | |
| // NOTE: this will invoke WriterInterceptors which can possibly change the request, | |
| // so it must be done first, before reading any header. | |
| final Buffer buffer = new Buffer(); | |
| try { | |
| request.writeRequestBody(buffer.outputStream()); | |
| } catch (IOException e) { | |
| throw new RuntimeException(e); | |
| } | |
| javax.ws.rs.core.MediaType mediaType = request.getHeaders().getMediaType(); | |
| final MediaType contentType = (mediaType == null) ? null : MediaType.parse(mediaType.toString()); | |
| return new RequestBody() { | |
| @Override | |
| public long contentLength() throws IOException { | |
| return buffer.size(); | |
| } | |
| @Override | |
| public MediaType contentType() { | |
| return contentType; | |
| } | |
| @Override | |
| public void writeTo(BufferedSink sink) throws IOException { | |
| sink.write(buffer, buffer.size()); | |
| } | |
| }; | |
| } | |
| private ClientResponse createResponse(ClientInvocation request, final Response response) { | |
| ClientResponse clientResponse = new ClientResponse(request.getClientConfiguration()) { | |
| private InputStream stream; | |
| @Override | |
| protected InputStream getInputStream() { | |
| if (stream == null) { | |
| try { | |
| stream = response.body().byteStream(); | |
| } catch (IOException e) { | |
| throw new RuntimeException(e); | |
| } | |
| } | |
| return stream; | |
| } | |
| @Override | |
| protected void setInputStream(InputStream is) { | |
| stream = is; | |
| } | |
| @Override | |
| protected void releaseConnection() throws IOException { | |
| // Stream might have been entirely replaced, so we need to close it independently from response.body() | |
| Throwable primaryExc = null; | |
| try { | |
| if (stream != null) { | |
| stream.close(); | |
| } | |
| } catch (Throwable t) { | |
| primaryExc = t; | |
| throw t; | |
| } finally { | |
| if (primaryExc != null) { | |
| try { | |
| response.body().close(); | |
| } catch (Throwable suppressedExc) { | |
| primaryExc.addSuppressed(suppressedExc); | |
| } | |
| } else { | |
| response.body().close(); | |
| } | |
| } | |
| } | |
| }; | |
| clientResponse.setStatus(response.code()); | |
| clientResponse.setHeaders(transformHeaders(response.headers())); | |
| return clientResponse; | |
| } | |
| private MultivaluedMap<String, String> transformHeaders(Headers headers) { | |
| MultivaluedMap<String, String> ret = new CaseInsensitiveMap<>(); | |
| for (int i = 0, l = headers.size(); i < l; i++) { | |
| ret.add(headers.name(i), headers.value(i)); | |
| } | |
| return ret; | |
| } | |
| @Override | |
| public void close() { | |
| // no-op | |
| } | |
| } |
I have found a serious bug in this implementation: when POSTing JSON data, the encoding is somehow broken - the remote Server won't accept it in some instances (the Mailchimp API in my case). I had to use the default Adapter... I have tested it by building a raw call with plain OkHttp and that worked, so it can't be OkHttps fault. Not wanting to say that this is a bad project though - i'd love to use OkHttp for all my Java Networking
You might want to use resteasy-client-okhttp or resteasy-client-okhttp3 from https://github.com/tbroyer/jaxrs-utils (https://search.maven.org/search?q=g:net.ltgt.jaxrs); that said, the code there is mostly the same as it is here (only difference AFAICT: tbroyer/jaxrs-utils@6a5ee07#diff-2f027da64d17f1a994b1a6e39f11c65e) so it would have the same bug; but it also has tests so maybe you could do a PR with a failing test (and/or even better, a fix for it 😉; but at a minimum please file an issue with more details about your failing request)
I must say I moved all my projects to using plain OkHttp so I can't promise anything wrt when I could fix the issue.
To use it: