
org.jsoftware.restclient.impl.ApacheHttpClientImplRestClient Maven / Gradle / Ivy
The newest version!
package org.jsoftware.restclient.impl;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.ProtocolException;
import org.apache.http.client.RedirectStrategy;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpOptions;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.jsoftware.restclient.BaseRestClientCall;
import org.jsoftware.restclient.RestClient;
import org.jsoftware.restclient.RestClientCall;
import org.jsoftware.restclient.RestClientDataCall;
import org.jsoftware.restclient.RestClientFeature;
import org.jsoftware.restclient.RestClientPlugin;
import org.jsoftware.restclient.RestClientResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* RestClient implementation based on Apache http-client
* @author szalik
*/
public class ApacheHttpClientImplRestClient implements RestClient {
private final CloseableHttpClient httpClient;
private final HttpClientContext httpClientContext;
private RestClientPlugin[] plugins = new RestClientPlugin[0];
private static class NoneRedirectStrategy implements RedirectStrategy {
@Override
public boolean isRedirected(HttpRequest request, HttpResponse response, HttpContext context) throws ProtocolException {
return false;
}
@Override
public HttpUriRequest getRedirect(HttpRequest request, HttpResponse response, HttpContext context) throws ProtocolException {
throw new AssertionError("Should be never called!");
}
}
/**
* @param features features to enable
* @param plugins plugins to be added
*/
public ApacheHttpClientImplRestClient(RestClientFeature[] features, RestClientPlugin[] plugins, HttpClientBuilder builder) {
httpClientContext = HttpClientContext.create();
Set f = new HashSet<>(Arrays.asList(features));
if (f.contains(RestClientFeature.ENABLE_COOKIES)) {
httpClientContext.setCookieStore(new BasicCookieStore());
}
if (! f.contains(RestClientFeature.AUTOMATIC_REDIRECTS)) { // fixme - this makes modification in factory property "builder"
builder.setRedirectStrategy(new NoneRedirectStrategy());
}
if (plugins != null) {
this.plugins = plugins;
}
httpClient = builder.build();
}
@Override
public RestClientCall get(String url) throws MalformedURLException {
return new RestClientCallImpl<>(url, new HttpGet());
}
@Override
public RestClientCall delete(String url) throws MalformedURLException {
return new RestClientCallImpl<>(url, new HttpDelete());
}
@Override
public RestClientCall head(String url) throws MalformedURLException {
return new RestClientCallImpl<>(url, new HttpHead());
}
@Override
public RestClientCall options(String url) throws MalformedURLException {
return new RestClientCallImpl<>(url, new HttpOptions());
}
@Override
public RestClientDataCall post(String url) throws MalformedURLException {
return new RestClientDataCallImpl<>(url, new HttpPost());
}
@Override
public RestClientDataCall put(String url) throws MalformedURLException {
return new RestClientDataCallImpl<>(url, new HttpPut());
}
@Override
public void close() throws Exception {
if (httpClient != null) {
httpClient.close();
}
}
private static String encode(String s) {
try {
return URLEncoder.encode(s, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new AssertionError(e);
}
}
abstract class AbstractRestClientCall implements BaseRestClientCall {
private final Map parameters = new LinkedHashMap<>();
protected final M method;
private final String uri;
protected AbstractRestClientCall(String uri, M method) {
this.method = method;
this.uri = uri;
}
@Override
public RestClientResponse execute() throws IOException {
PluginContextImpl ctx = new PluginContextImpl();
ctx.setRequest(method);
ctx.setURI(uri);
if (! parameters.isEmpty()) {
applyParameters(method, ctx, parameters);
}
InvocationChain chain = InvocationChain.create(plugins, ctx, () -> {
try {
this.method.setURI(new URL(ctx.getURI()).toURI());
} catch (URISyntaxException e) {
MalformedURLException mex = new MalformedURLException("Invalid url '" + uri + "'");
mex.initCause(e);
throw mex;
}
HttpResponse response = httpClient.execute(method, httpClientContext);
RestClientResponse cr = new StandardRestClientResponse(response);
ctx.setResponse(cr);
return ctx;
});
try {
chain.operation.call();
} catch (IOException ioe) {
throw ioe;
} catch (Exception e) {
throw new IOException("Chain exception", e);
}
return ctx.getResponse();
}
@Override
public C parameter(String name, Object value) {
String[] actual = parameters.get(name);
String str = value == null ? "" : value.toString();
if (actual == null) {
parameters.put(name, new String[] { str });
} else {
String[] values = new String[actual.length + 1];
System.arraycopy(actual, 0, values, 0, actual.length);
values[actual.length] = str;
parameters.put(name, values);
}
return (C) this;
}
@Override
public C header(String name, String value) {
method.setHeader(name, value);
return (C) this;
}
protected abstract void applyParameters(M method, RestClientPlugin.PluginContext ctx, Map params);
}
class RestClientCallImpl extends ApacheHttpClientImplRestClient.AbstractRestClientCall implements RestClientCall {
RestClientCallImpl(String url, M method) throws MalformedURLException {
super(url, method);
}
@Override
protected void applyParameters(M method, RestClientPlugin.PluginContext ctx, Map params) {
String uri = ctx.getURI();
boolean hasParam = uri.contains("?");
StringBuilder sb = new StringBuilder(uri);
for(Map.Entry x : params.entrySet()) {
final String name = encode(x.getKey());
for(String val : x.getValue()) {
sb.append(hasParam ? '&' : '?').append(name).append('=').append(encode(val));
hasParam = true;
}
}
ctx.setURI(sb.toString());
}
}
class RestClientDataCallImpl extends ApacheHttpClientImplRestClient.AbstractRestClientCall implements RestClientDataCall {
private Charset charset;
RestClientDataCallImpl(String url, M method) throws MalformedURLException {
super(url, method);
charset = Charset.forName("UTF-8");
}
@Override
protected void applyParameters(M method, RestClientPlugin.PluginContext ctx, Map params) {
List list = new LinkedList<>();
for(Map.Entry x : params.entrySet()) {
final String name = x.getKey();
for(String val : x.getValue()) {
list.add(new BasicNameValuePair(name, val));
}
}
method.setEntity(new UrlEncodedFormEntity(list, charset));
}
@Override
public RestClientDataCall body(InputStream inputStream, ContentType contentType) {
notNullCheck(inputStream, contentType);
method.setEntity(new InputStreamEntity(inputStream, contentType));
return this;
}
@Override
public RestClientDataCall body(byte[] data, ContentType contentType) {
notNullCheck(data, contentType);
method.setEntity(new ByteArrayEntity(data, contentType));
return this;
}
@Override
public RestClientDataCall body(String data, ContentType contentType) {
notNullCheck(data, contentType);
method.setEntity(new StringEntity(data, contentType));
return this;
}
@Override
public RestClientDataCall parametersEncoding(String charset) {
this.charset = Charset.forName(charset);
return this;
}
private void notNullCheck(Object data, ContentType ct) {
if (data == null) {
throw new IllegalArgumentException("Body data cannot be null.");
}
if (ct == null) {
throw new IllegalArgumentException("Body content-type must be set.");
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy