
net.anthavio.httl.HttlSender Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hatatitla Show documentation
Show all versions of hatatitla Show documentation
Compact but tweakable REST client library you have been dreaming of
The newest version!
package net.anthavio.httl;
import java.io.Closeable;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import net.anthavio.httl.HttlBuilder.TransportChooser;
import net.anthavio.httl.HttlExecutionChain.SenderExecutionChain;
import net.anthavio.httl.HttlRequest.Method;
import net.anthavio.httl.HttlRequestBuilder.BodyfulRequestBuilder;
import net.anthavio.httl.HttlRequestBuilder.BodylessRequestBuilder;
import net.anthavio.httl.HttlResponseExtractor.ExtractedResponse;
import net.anthavio.httl.cache.CachedResponse;
import net.anthavio.httl.util.Cutils;
import net.anthavio.httl.util.GenericType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Immutable
*
* @author martin.vanek
*
*/
public class HttlSender implements SenderOperations, Closeable {
/**
* Initiate HttlSender Builder...
*
* @param url
* @return Ultimate builder/configurer
*/
public static TransportChooser url(String url) {
return HttlBuilder.sender(url);
}
protected final Logger logger = LoggerFactory.getLogger(getClass());
private final HttlTransport transport;
private final SenderConfigurer config;
private final ExecutorService executor; //can be null
private final HttlBodyMarshaller marshaller;
private final HttlBodyUnmarshaller unmarshaller;
private final List executionFilters;
public HttlSender(SenderConfigurer config) {
if (config == null) {
throw new IllegalArgumentException("null config");
}
this.config = config;
this.transport = config.getTransport();
this.executor = config.getExecutorService();
this.marshaller = config.getMarshaller();
this.unmarshaller = config.getUnmarshaller();
this.executionFilters = config.getExecutionFilters();
}
public HttlTransport getTransport() {
return transport;
}
public ExecutorService getExecutor() {
return executor;
}
public HttlBodyMarshaller getMarshaller() {
return marshaller;
}
public HttlBodyUnmarshaller getUnmarshaller() {
return unmarshaller;
}
public SenderConfigurer getConfig() {
return config;
}
public void close() {
transport.close();
}
HttlResponse doExecute(HttlRequest request) throws IOException {
if (this.logger.isDebugEnabled()) {
this.logger.debug(request.getMethod() + " " + request.getUrl());
}
return transport.call(request);
}
/**
* Execute Request and return raw unprocessed Response.
* Response is left open and caller is responsibe for closing it.
*/
public HttlResponse execute(HttlRequest request) throws HttlRequestException {
HttlResponse response = null;
try {
if (executionFilters != null && executionFilters.size() != 0) {
response = new SenderExecutionChain(executionFilters, this).next(request);
} else {
response = doExecute(request);
}
} catch (Exception x) {
Cutils.close(response);
if (x instanceof RuntimeException) {
throw (RuntimeException) x;
} else {
throw new HttlRequestException(x);
}
}
return response;
}
/**
* Execute Request and use ResponseHandler parameter to process Response.
* Response is closed automaticaly.
*
*/
public void execute(HttlRequest request, HttlResponseHandler handler) throws HttlException {
if (handler == null) {
throw new IllegalArgumentException("null handler");
}
HttlResponse response = null;
try {
response = execute(request);
} catch (HttlException sx) {
handler.onFailure(request, sx.getException());
return;
} catch (Exception x) {
handler.onFailure(request, x);
return;
}
try {
handler.onResponse(response);
} catch (Exception x) {
if (x instanceof RuntimeException) {
throw (RuntimeException) x;
} else {
throw new HttlResponseException(response, x);
}
} finally {
Cutils.close(response);
}
}
/**
* Extracted response version. Response is extracted, closed and result is returned to caller
* ResponseExtractor is created and used to extract the specified resultType
*/
public ExtractedResponse extract(HttlRequest request, Class resultType) throws HttlException {
if (resultType == null) {
throw new IllegalArgumentException("resultType is null");
}
HttlResponse response = execute(request);
return extract(response, resultType);
}
/**
* To overcome Java generics shortcomings use HttlSender#extract(request, new GenericType> {})
* to extract Generic collection
*/
public ExtractedResponse extract(HttlRequest request, GenericType resultType) throws HttlException {
if (resultType == null) {
throw new IllegalArgumentException("resultType is null");
}
HttlResponse response = execute(request);
return extract(response, resultType.getParameterizedType());
}
/**
* Mainly for CachingSender and CachingExtractor to access HttpSenders extracting facilities
*/
public ExtractedResponse extract(HttlResponse response, Type resultType) throws HttlException {
try {
Object payload;
if (resultType.equals(String.class)) {
payload = config.getStringExtractor().extract(response);
} else if (resultType.equals(byte[].class)) {
payload = config.getBytesExtractor().extract(response);
} else {
payload = this.unmarshaller.unmarshall(response, resultType);
}
return new ExtractedResponse(response, (T) payload);//XXX this cast is erased!
} catch (IOException iox) {
throw new HttlResponseException(response, iox);
} finally {
Cutils.close(response);
}
}
/**
* Extracted response version. Response is extracted, closed and result is returned to caller.
*
* Response is closed automaticaly.
*/
public ExtractedResponse extract(HttlRequest request, HttlResponseExtractor extractor) throws HttlException {
if (extractor == null) {
throw new IllegalArgumentException("ResponseExtractor is null");
}
HttlResponse response = null;
try {
response = execute(request);
T extracted = (T) extractor.extract(response);
return new ExtractedResponse(response, extracted);
/*
Class resultType = (Class) ((ParameterizedType) extractor.getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
Object extract = unmarshaller.unmarshall(response, resultType);
if (extract instanceof RuntimeException) {
throw (RuntimeException) extract;
} else if (extract instanceof Exception) {
throw new HttlProcessingException(response, (Exception) extract);
} else {
return new ExtractedResponse(response, (T) extract);
}
*/
} catch (Exception x) {
throw new HttlResponseException(response, x);
} finally {
Cutils.close(response);
}
}
/**
* Asynchronous extraction with Future as response
*/
public Future> start(final HttlRequest request, final HttlResponseExtractor extractor) {
if (executor == null) {
throw new IllegalStateException("Executor for asynchronous requests is not configured");
}
return executor.submit(new Callable>() {
@Override
public ExtractedResponse call() throws Exception {
return extract(request, extractor);
}
});
}
/**
* Asynchronous extraction with Future as response
*/
public Future> start(final HttlRequest request, final Class resultType) {
if (executor == null) {
throw new IllegalStateException("Executor for asynchronous requests is not configured");
}
return executor.submit(new Callable>() {
@Override
public ExtractedResponse call() throws Exception {
return extract(request, resultType);
}
});
}
/**
* Asynchronous execution whith ResponseHandler
*/
public void start(final HttlRequest request, final HttlResponseHandler handler) {
if (executor == null) {
throw new IllegalStateException("Executor for asynchronous requests is not configured");
}
executor.execute(new Runnable() {
@Override
public void run() {
try {
execute(request, handler);
} catch (Exception x) {
logger.warn("Failed asynchronous request", x);
}
}
});
}
/**
* Asynchronous execution with Future as response
*/
public Future start(final HttlRequest request) {
if (executor == null) {
throw new IllegalStateException("Executor for asynchronous requests is not configured");
}
return executor.submit(new Callable() {
@Override
public HttlResponse call() throws Exception {
HttlResponse response = execute(request);
//too dangerous to left the SenderResponse unclosed
//we will cache the response...
return new CachedResponse(request, response);
}
});
}
/**
* Fluent builders
*/
public BodylessRequestBuilder GET(String path) {
return new BodylessRequestBuilder(this, Method.GET, path);
}
public BodylessRequestBuilder HEAD(String path) {
return new BodylessRequestBuilder(this, Method.HEAD, path);
}
public BodylessRequestBuilder TRACE(String path) {
return new BodylessRequestBuilder(this, Method.TRACE, path);
}
public BodylessRequestBuilder OPTIONS(String path) {
return new BodylessRequestBuilder(this, Method.OPTIONS, path);
}
public BodylessRequestBuilder DELETE(String path) {
return new BodylessRequestBuilder(this, Method.DELETE, path);
}
public BodyfulRequestBuilder POST(String path) {
return new BodyfulRequestBuilder(this, Method.POST, path);
}
public BodyfulRequestBuilder PUT(String path) {
return new BodyfulRequestBuilder(this, Method.PUT, path);
}
public BodyfulRequestBuilder PATCH(String path) {
return new BodyfulRequestBuilder(this, Method.PATCH, path);
}
@Override
public String toString() {
return "HttlSender [" + config.getUrl() + ", executor=" + executor + "]";
}
/*
public static class HttlHeaders extends Multival {
private static final long serialVersionUID = 1L;
public HttlHeaders() {
}
}
public static class Parameters extends Multival {
private static final long serialVersionUID = 1L;
}
*/
/**
*
* @author martin.vanek
*
*/
public static class Multival implements Iterable, Serializable {
private static final long serialVersionUID = 1L;
private Map> entries;
public Multival() {
}
/**
* Set value(s) replacing existing
*
*/
public void set(String name, Collection values) {
if (values != null && values.size() != 0) {
List list = get(name, true);
for (T value : values) {
list.add(value);
}
} else {
get(name, true);//.add(null);
}
}
/**
* Set value(s) replacing existing
*
*/
public void set(String name, T... values) {
if (values != null && values.length != 0) {
List list = get(name, true);
for (T value : values) {
list.add(value);
}
} else {
get(name, true);//.add(null);
}
}
/**
* Set value(s) replacing existing
*
*/
public void set(String name, T value) {
get(name, true).add(value);
}
public List remove(String name) {
return this.entries.remove(name);
}
/**
* Add value(s) keeping existing
*/
public void add(String name, Collection values) {
if (values != null && values.size() != 0) {
List list = get(name, false);
for (T value : values) {
list.add(value);
}
} else {
get(name, false);//.add(null);
}
}
/**
* Add value(s) keeping existing
*/
public void add(String name, T... values) {
if (values != null && values.length != 0) {
List list = get(name, false);
for (T value : values) {
list.add(value);
}
} else {
get(name, false);//.add(null);
}
}
/**
* Add value(s) keeping existing
*/
public void add(String name, T value) {
get(name, false).add(value);
}
public void put(String name, T value, boolean clear) {
get(name, clear).add(value);
}
protected List get(String name, boolean clear) {
if (Cutils.isBlank(name)) {
throw new IllegalArgumentException("Name is blank");
}
List list = null;
if (this.entries == null) {
this.entries = new TreeMap>();
} else {
list = this.entries.get(name);
}
if (list == null) {
list = new LinkedList();
this.entries.put(name, list);
} else if (clear) {
list.clear();
}
return list;
}
public Set names() {
if (this.entries == null) {
return Collections.emptySet();
} else {
return this.entries.keySet();
}
}
public int size() {
return this.entries == null ? 0 : this.entries.size();
}
public List get(String name) {
if (this.entries == null) {
return null;
} else {
return this.entries.get(name);
}
}
public T getFirst(String name) {
List values = get(name);
if (values != null && values.size() != 0) {
return values.get(0);
} else {
return null;
}
}
public T getLast(String name) {
List values = get(name);
if (values != null && values.size() != 0) {
return values.get(values.size() - 1);
} else {
return null;
}
}
public void clear() {
if (this.entries != null) {
entries.clear();
}
}
@Override
public Iterator iterator() {
if (this.entries == null) {
return Collections. emptySet().iterator();
}
return names().iterator();
}
@Override
public String toString() {
return String.valueOf(this.entries);
}
@Override
public int hashCode() {
return this.entries == null ? 0 : this.entries.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Multival other = (Multival) obj;
if (this.entries == null) {
if (other.entries != null) {
return false;
}
} else if (!this.entries.equals(other.entries)) {
return false;
}
return true;
}
@Override
public Multival clone() {
Multival multival = new Multival();
multival.entries = new HashMap>();
for (Entry> entry : entries.entrySet()) {
multival.entries.put(entry.getKey(), entry.getValue());
}
return multival;
}
}
private static final Comparator COMPARATOR = new NullSafeStringComparator();
private static class NullSafeStringComparator implements Comparator {
@Override
public int compare(String o1, String o2) {
if (o1 == o2) {
return 0;
} else if (o1 == null) {
return -1;
} else if (o2 == null) {
return 1;
} else {
return o1.compareTo(o2);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy