/* ____ ________ / / / ____/ /___ /_ / ___ / / __/ / __ `// / / _ \ / /_/ / / /_/ // /_/ __/ \____/_/\__,_// /\___/ HTTP delight /___/ */ package glaze; import glaze.client.Response; import glaze.client.async.AsyncClient; import glaze.client.async.AsyncMap; import glaze.client.handlers.ErrorHandler; import glaze.client.sync.SyncClient; import glaze.client.sync.SyncMap; import glaze.client.wire.tasks.MapCall; import glaze.client.wire.tasks.SendCall; import glaze.client.wire.tasks.SerializableResponse; import glaze.client.wire.tasks.CallableRequest.SerializableResponseCallback; import glaze.func.Closures.Closure; import glaze.spi.Registry; import glaze.util.EntityMapper; import glaze.util.RequestUtil; import glaze.util.TypeHelper; import java.io.Serializable; import java.net.URI; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.Map; import java.util.TimeZone; import java.util.concurrent.Future; import org.apache.http.Consts; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpHeaders; import org.apache.http.HttpHost; import org.apache.http.HttpVersion; import org.apache.http.auth.Credentials; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpDelete; 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.HttpPatch; 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.HttpTrace; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.concurrent.FutureCallback; import org.apache.http.conn.params.ConnRoutePNames; import org.apache.http.entity.ContentType; import org.apache.http.impl.auth.BasicScheme; import org.apache.http.nio.protocol.HttpAsyncRequestProducer; import org.apache.http.nio.protocol.HttpAsyncResponseConsumer; import org.apache.http.params.CoreConnectionPNames; import org.apache.http.params.CoreProtocolPNames; import org.apache.http.params.HttpParams; import org.apache.http.protocol.HttpContext; import com.fasterxml.jackson.core.type.TypeReference; import com.google.common.base.Preconditions; /** * <p> * Fluent API for comfortable HTTP interactions. Glaze offers a simple yet * complete toolkit to ease the building of powerful REST clients. * </p> * Provides: * <ul> * <li>Automatic serialization/deserialization by content type</li> * <li>Easy form handling, including multipart requests</li> * <li>Easy asynchronous HTTP interaction over NIO</li> * <li>Error handling</li> * <li>Convenient facilities for local multi-threaded and remote distributed * executors</li> * <li>Pluggable mappers and services</li> * </ul> * <h3>Examples</h3> * * <strong>Mapping</strong> * * <pre> * * // Simple map * * Map<String, Object> result = Get(uri).map(); * * // Map a bean * * MyBean out = Get(uri).map(MyBean.class); * * // Map with error handling * * MyBean out = Get(uri).withErrorHandler(new ErrorHandler(){...}).map(MyBean.class); * * // Post a bean as url-encoded content * * Post(uri).bean(in).send(); * * // Post a bean as json * * Post(uri).bean(in).as(APPLICATION_JSON).send(); * * // or * * Post(uri, APPLICATION_JSON).bean(in).send(); * * // Post a bean as json and get the response mapped back to a bean according to * // the response content-type * * MyBean out = Post(uri).bean(in).as(APPLICATION_JSON).map(MyBean.class); * * // force to be mapped back from xml ignoring the response content-type. i.e. * // broken headers * * Post(uri).bean(in).as(APPLICATION_JSON).map(MyBean.class, APPLICATION_XML); * * </pre> * * <strong>Asynchronous interaction</strong> * * <pre> * * // Basic send async * * Future<HttpResponse> out = Get(uri).sendAsync(); * out.get(); * * // Basic map async * * Future<MyBean> out = Get(uri).mapAsync(MyBean.class); * out.get(); * * // With consumer * * Future<MyResult> out = Get(uri).withConsumer(myAsyncConsumer).executeAsync(); * out.get(); * * </pre> * * <strong>Multipart</strong> * * <pre> * * // Post a file * * Post(uri).bean(new File("myfile.png")).as(MULTIPART_FORM_DATA).send(); * * // or * * Post(uri, MULTIPART_FORM_DATA).bean(new FileInputStream(file)).send(); * * // or bytes * * byte[] bytes = new byte[] { 0x1, 0x1, 0x1, 0x0, 0xB, 0xA, 0xB, 0xB, 0xE }; * * Post(uri, MULTIPART_FORM_DATA).bean(bytes).send(); * * // Post a bean annotated with specific multipart annotations * * class MultipartBean * { * @BinaryMultipart * private File attachment; * * @BinaryMultipart(fileName = "tangerine.jpg", mime = "image/jpeg", name = "photo") * private File pht; * * @TextMultipart * private String hello = "world!"; * * @TextMultipart(name = "ho", mime = "application/json") * private String hi = "{\"num\":1}"; * } * * Post(uri).bean(multipartBean).as(MULTIPART_FORM_DATA).send(); * * </pre> * * @author mfornos * */ public final class Glaze { /** Default date format for HTTP headers. "EEE, dd MMM yyyy HH:mm:ss zzz" */ public static final String DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz"; /** Default date locale. Locale.US */ public static final Locale DATE_LOCALE = Locale.US; /** Default time zone. "GMT" */ public static final TimeZone TIME_ZONE = TimeZone.getTimeZone("GMT"); /** * Instantiates a builder for a DELETE request. See RFC 2616 section 9, * Method Definitions. * * @param uri * The URI of the request * @return builder instance */ public static Glaze Delete(String uri) { return new Glaze(new HttpDelete(uri)); } /** * Instantiates a builder for a DELETE request. See RFC 2616 section 9, * Method Definitions. * * @param uri * The URI of the request * @return builder instance */ public static Glaze Delete(URI uri) { return new Glaze(new HttpDelete(uri)); } /** * Instantiates a builder for a GET request. See RFC 2616 section 9, Method * Definitions. * * @param uri * The URI of the request * @return builder instance */ public static Glaze Get(String uri) { return new Glaze(new HttpGet(uri)); } /** * Instantiates a builder for a GET request. See RFC 2616 section 9, Method * Definitions. * * @param uri * The URI of the request * @return builder instance */ public static Glaze Get(URI uri) { return new Glaze(new HttpGet(uri)); } /** * Instantiates a builder for a HEAD request. See RFC 2616 section 9, Method * Definitions. * * @param uri * The URI of the request * @return builder instance */ public static Glaze Head(String uri) { return new Glaze(new HttpHead(uri)); } /** * Instantiates a builder for a HEAD request. See RFC 2616 section 9, Method * Definitions. * * @param uri * The URI of the request * @return builder instance */ public static Glaze Head(URI uri) { return new Glaze(new HttpHead(uri)); } /** * Instantiates a builder for a OPTIONS request. The OPTIONS method * represents a request for information about the communication options * available on the request/response chain identified by the Request-URI. * This method allows the client to determine the options and/or requirements * associated with a resource, or the capabilities of a server, without * implying a resource action or initiating a resource retrieval. See RFC * 2616 section 9, Method Definitions. * * @param uri * The URI of the request * @return builder instance */ public static Glaze Options(String uri) { return new Glaze(new HttpOptions(uri)); } /** * Instantiates a builder for a OPTIONS request. The OPTIONS method * represents a request for information about the communication options * available on the request/response chain identified by the Request-URI. * This method allows the client to determine the options and/or requirements * associated with a resource, or the capabilities of a server, without * implying a resource action or initiating a resource retrieval. See RFC * 2616 section 9, Method Definitions. * * @param uri * The URI of the request * @return builder instance */ public static Glaze Options(URI uri) { return new Glaze(new HttpOptions(uri)); } /** * Instantiates a builder for a PATCH request. The PATCH method is used to * apply partial modifications to a resource. See RFC 2616 section 9, Method * Definitions. * * @param uri * The URI of the request * @return builder instance */ public static Glaze Patch(String uri) { return new Glaze(new HttpPatch(uri)); } /** * Instantiates a builder for a PATCH request. The PATCH method is used to * apply partial modifications to a resource. See RFC 2616 section 9, Method * Definitions. * * @param uri * The URI of the request * @return builder instance */ public static Glaze Patch(URI uri) { return new Glaze(new HttpPatch(uri)); } /** * Instantiates a builder for a POST request. Defaults to * 'application-form-urlencoded' content-type. See RFC 2616 section 9, Method * Definitions. * * @param uri * The URI of the request * @return builder instance * @see #as(ContentType) */ public static Glaze Post(String uri) { return Post(uri, ContentType.APPLICATION_FORM_URLENCODED); } /** * Instantiates a builder for a POST request with the given content-type. See * RFC 2616 section 9, Method Definitions. * * @param uri * The URI of the request * @param contentType * The content-type * @return builder instance * @see #as(ContentType) */ public static Glaze Post(String uri, ContentType contentType) { return new Glaze(new HttpPost(uri)).as(contentType); } /** * Instantiates a builder for a POST request. Defaults to * 'application-form-urlencoded' content-type. See RFC 2616 section 9, Method * Definitions. * * @param uri * The URI of the request * @return builder instance * @see #as(ContentType) */ public static Glaze Post(URI uri) { return Post(uri, ContentType.APPLICATION_FORM_URLENCODED); } /** * Instantiates a builder for a POST request with the given content-type. See * RFC 2616 section 9, Method Definitions. * * @param uri * The URI of the request * @param contentType * The content-type * @return builder instance * @see #as(ContentType) */ public static Glaze Post(URI uri, ContentType contentType) { return new Glaze(new HttpPost(uri)).as(contentType); } /** * Instantiates a builder for a PUT request. Defaults to * 'application-form-urlencoded' content-type. See RFC 2616 section 9, Method * Definitions. * * @param uri * The URI of the request * @return builder instance * @see #as(ContentType) */ public static Glaze Put(String uri) { return Put(uri, ContentType.APPLICATION_FORM_URLENCODED); } /** * Instantiates a builder for a PUT request with the given content-type. See * RFC 2616 section 9, Method Definitions. * * @param uri * The URI of the request * @param contentType * The content-type * @return builder instance * @see #as(ContentType) */ public static Glaze Put(String uri, ContentType contentType) { return new Glaze(new HttpPut(uri)).as(contentType); } /** * Instantiates a builder for a PUT request. Defaults to * 'application-form-urlencoded' content-type. See RFC 2616 section 9, Method * Definitions. * * @param uri * The URI of the request * @return builder instance * @see #as(ContentType) */ public static Glaze Put(URI uri) { return Put(uri, ContentType.APPLICATION_FORM_URLENCODED); } /** * Instantiates a builder for a POST request with the given content-type. See * RFC 2616 section 9, Method Definitions. * * @param uri * The URI of the request * @param contentType * The content-type * @return builder instance * @see #as(ContentType) */ public static Glaze Put(URI uri, ContentType contentType) { return new Glaze(new HttpPut(uri)).as(contentType); } /** * Instantiates a builder for a TRACE request. The TRACE method is used to * invoke a remote, application-layer loop- back of the request message. See * RFC 2616 section 9, Method Definitions. * * @param uri * The URI of the request * @return builder instance */ public static Glaze Trace(String uri) { return new Glaze(new HttpTrace(uri)); } /** * Instantiates a builder for a TRACE request. The TRACE method is used to * invoke a remote, application-layer loop- back of the request message. See * RFC 2616 section 9, Method Definitions. * * @param uri * The URI of the request * @return builder instance */ public static Glaze Trace(URI uri) { return new Glaze(new HttpTrace(uri)); } private DateFormat dateFormatter; private final HttpRequestBase request; private final HttpParams localParams; private ContentType serializationType; private Object bean; private HttpEntity entity; private ErrorHandler errorHandler; private String namespace; private ResponseHandler<?> responseHandler; private Closure<HttpRequestBase> requestClosure; private HttpAsyncResponseConsumer<?> asyncConsumer; private boolean repeatable; private Glaze(HttpRequestBase request) { this.request = request; this.localParams = request.getParams(); this.repeatable = false; } /** * Adds a HTTP header. The request-header fields allow the client to pass * additional information about the request, and about the client itself, to * the server. * * @param header * The HTTP header * @return builder instance * @see HttpHeaders */ public Glaze addHeader(final Header header) { this.request.addHeader(header); return this; } /** * Adds a HTTP header. The request-header fields allow the client to pass * additional information about the request, and about the client itself, to * the server. * * @param name * The header name * @param value * The header value * @return builder instance * @see HttpHeaders */ public Glaze addHeader(final String name, final Object value) { return addHeader(name, value.toString()); } /** * Adds a HTTP header. The request-header fields allow the client to pass * additional information about the request, and about the client itself, to * the server. * * @param name * The header name * @param value * The header value * @return builder instance * @see HttpHeaders */ public Glaze addHeader(final String name, final String value) { this.request.addHeader(name, value); return this; } /** * Specifies the type for content serialization and deserialization. Note * that the {@link ContentType} must match a mapper available in the * {@link Registry}. * * @param contentType * The content type * @return builder instance * @see ContentType */ public Glaze as(ContentType contentType) { this.serializationType = contentType; return this; } /** * Authenticates the current request with basic-auth scheme. Per request * preemptive authentication. Generally, preemptive authentication can be * considered less secure than a response to an authentication challenge and * therefore discouraged. * * @param username * The user name * @param password * The password * @return builder instance * @see SyncClient#auth(Credentials) */ public Glaze auth(String username, String password) { UsernamePasswordCredentials creds = new UsernamePasswordCredentials(username, password); return addHeader(BasicScheme.authenticate(creds, Consts.ASCII.name(), false)); } /** * Specifies a bean to be sent as the content of the request, serialized * according to the given content type. * * @param bean * The bean to be serialized * @return builder instance * @see #as(ContentType) */ public Glaze bean(Object bean) { this.bean = bean; return this; } /** * Builds the request. * * @return the built HttpRequest */ public HttpUriRequest build() { if (needsEntityMapping(request)) { entity = EntityMapper.map(namespace, bean, serializationType, repeatable); } // TODO repeatable if (entity != null && RequestUtil.isEnclosingEntity(request)) { RequestUtil.setEntity(request, entity); } if (requestClosure != null) { requestClosure.on(request); } return request; } /** * @param type * The deserialization type * @return a request suitable for remote execution */ public <T extends Serializable> MapCall<T> buildMapCall(Class<T> type) { return new MapCall<T>(build(), type); } /** * @param callback * @param type * The deserialization type * @return a request suitable for remote execution */ public <T extends Serializable> MapCall<T> buildMapCall(SerializableResponseCallback<T> callback, Class<T> type) { return new MapCall<T>(build(), callback, type); } /** * @return a request suitable for remote execution */ public SendCall buildSendCall() { return new SendCall(build()); } /** * @param callback * @return a request suitable for remote execution */ public SendCall buildSendCall(SerializableResponseCallback<SerializableResponse> callback) { return new SendCall(build(), callback); } /** * Sets an arbitrary configuration parameter. * * @param param * The parameter name * @param object * The parameter value * @return builder instance */ public Glaze config(final String param, final Object object) { this.localParams.setParameter(param, object); return this; } /** * The timeout until a connection is established. A value of zero means the * timeout is not used. * * @param timeout * The timeout in milliseconds * @return builder instance */ public Glaze connectTimeout(int timeout) { return config(CoreConnectionPNames.CONNECTION_TIMEOUT, timeout); } /** * Decorates the request before send. * * @param closure * The closure * @return builder instance */ public Glaze decorate(Closure<HttpRequestBase> closure) { this.requestClosure = closure; return this; } /** * The charset to be used for encoding/decoding HTTP protocol elements * (status line and headers). * * @param charset * The charset. * @return builder instance */ public Glaze elementCharset(final String charset) { return config(CoreProtocolPNames.HTTP_ELEMENT_CHARSET, charset); } /** * Specifies the entity to be sent. It has preference over any bean already * set. * * @param entity * The HTTP entity * @return builder instance */ public Glaze entity(HttpEntity entity) { this.entity = entity; return this; } /** * Executes the current request. * * @return the response */ public <T> T execute() { return execute(defaultSyncClient()); } /** * Executes the current request with the given context. * * @param context * the HTTP context * @return the response */ public <T> T execute(HttpContext context) { return execute(defaultSyncClient(), context); } /** * Executes the current request. * * @param client * The execution client. * @return the response */ @SuppressWarnings("unchecked") public <T> T execute(SyncClient client) { return (T) (responseHandler == null ? client.execute(build(), errorHandler) : client.execute(request, responseHandler)); } /** * Executes the current request. * * @param client * The execution client. * @param context * The HTTP context. * @return the response */ @SuppressWarnings("unchecked") public <T> T execute(SyncClient client, HttpContext context) { return (T) (responseHandler == null ? client.execute(build(), errorHandler, context) : client.execute(request, responseHandler, context)); } public <T> Future<T> executeAsync() { return executeAsync(defaultAsyncClient()); } @SuppressWarnings("unchecked") public <T> Future<T> executeAsync(AsyncClient client) { Preconditions.checkNotNull(asyncConsumer, "Please, specify an asynchronous consumer '.withConsumer(consumer)'."); HttpAsyncRequestProducer producer = client.createAsyncProducer(build()); return (Future<T>) client.execute(producer, asyncConsumer); } /** * Maps the response to a {@link Map}. * * @return a Map filled with values of the response */ public Map<String, Object> map() { return map(TypeHelper.plainMap()); } /** * Maps the response to a type instance. * * @param type * The class to be mapped. * @return an instance of the given type populated with response values. * @see #as(ContentType) */ public <T> T map(Class<T> type) { return map(defaultSyncClient(), type); } /** * Maps the response to a type instance. * * @param type * The class to be mapped. * @param overrideType * The type that overrides any ContentType already set. * @return an instance of the given type populated with response values. * @see #as(ContentType) */ public <T> T map(Class<T> type, ContentType overrideType) { return map(defaultSyncClient(), type, overrideType); } /** * Maps the response to a type instance. * * @param type * The class to be mapped. * @param context * The HttpContext of the request. * @return an instance of the given type populated with response values. */ public <T> T map(Class<T> type, HttpContext context) { return map(defaultSyncClient(), type, context); } /** * Maps the response to a type instance. * * @param type * The class to be mapped. * @param context * The HttpContext of the request. * @param overrideType * The type that overrides any ContentType already set. * @return an instance of the given type populated with response values. */ public <T> T map(Class<T> type, HttpContext context, ContentType overrideType) { return map(defaultSyncClient(), type, context, overrideType); } /** * Maps the response to a type instance. * * @param client * The execution client. * @param type * The class to be mapped. * @return an instance of the given type populated with response values. */ public <T> T map(SyncClient client, Class<T> type) { return client.map(new SyncMap<T>(namespace, build(), type, errorHandler)); } /** * Maps the response to a type instance. * * @param client * The execution client. * @param type * The class to be mapped. * @param overrideType * The type that overrides any ContentType already set. * @return an instance of the given type populated with response values. */ public <T> T map(SyncClient client, Class<T> type, ContentType overrideType) { return client.map(new SyncMap<T>(namespace, build(), type, errorHandler, overrideType)); } /** * Maps the response to a type instance. * * @param client * The execution client. * @param type * The class to be mapped. * @param context * The HttpContext of the request. * @return an instance of the given type populated with response values. */ public <T> T map(SyncClient client, Class<T> type, HttpContext context) { return client.map(new SyncMap<T>(namespace, build(), type, context, errorHandler)); } /** * Maps the response to a type instance. * * @param client * The execution client. * @param type * The class to be mapped. * @param context * The HttpContext of the request. * @param overrideType * The type that overrides any ContentType already set. * @return an instance of the given type populated with response values. */ public <T> T map(SyncClient client, Class<T> type, HttpContext context, ContentType overrideType) { return client.map(new SyncMap<T>(namespace, build(), type, context, errorHandler, overrideType)); } /** * Maps the response to a type instance. * * @param client * The execution client. * @param type * The TypeReference to be mapped. * @return an instance of the given type populated with response values. * @see TypeReference */ public <T> T map(SyncClient client, TypeReference<T> type) { return map(client, TypeHelper.resolveClass(type)); } /** * Maps the response to a type instance. * * @param type * The TypeReference to be mapped. * @return an instance of the given type populated with response values. * @see TypeReference */ public <T> T map(TypeReference<T> type) { return map(TypeHelper.resolveClass(type)); } /** * Asynchronously maps the response to a type instance. * * @param client * The execution client. * @param type * The class to be mapped. * @return an instance of the given type populated with response values. */ public <T> Future<T> mapAsync(AsyncClient client, Class<T> type) { return client.map(new AsyncMap<T>(namespace, build(), type, errorHandler)); } public <T> Future<T> mapAsync(AsyncClient client, Class<T> type, ContentType overrideType) { return client.map(new AsyncMap<T>(namespace, build(), type, errorHandler, overrideType)); } /** * Asynchronously maps the response to a type instance. * * @param client * The execution client. * @param type * The class to be mapped. * @param callback * The FutureCallback. * @return an instance of the given type populated with response values. */ public <T> Future<T> mapAsync(AsyncClient client, Class<T> type, FutureCallback<T> callback) { return client.map(new AsyncMap<T>(namespace, build(), type, callback, errorHandler)); } /** * Asynchronously maps the response to a type instance. * * @param client * The execution client. * @param type * The class to be mapped. * @param context * The HttpContext of the request. * @return an instance of the given type populated with response values. */ public <T> Future<T> mapAsync(AsyncClient client, Class<T> type, HttpContext context) { return client.map(new AsyncMap<T>(namespace, build(), type, context, errorHandler)); } /** * Asynchronously maps the response to a type instance. * * @param client * The execution client. * @param type * The class to be mapped. * @param context * The HttpContext of the request. * @param callback * The FutureCallback. * @return an instance of the given type populated with response values. */ public <T> Future<T> mapAsync(AsyncClient client, Class<T> type, HttpContext context, FutureCallback<T> callback) { return client.map(new AsyncMap<T>(namespace, build(), type, context, callback, errorHandler)); } public <T> Future<T> mapAsync(AsyncClient client, TypeReference<T> type) { return mapAsync(client, TypeHelper.resolveClass(type)); } /** * Asynchronously maps the response to a type instance. * * @param type * The class to be mapped. * @return an instance of the given type populated with response values. */ public <T> Future<T> mapAsync(Class<T> type) { return mapAsync(defaultAsyncClient(), type); } public <T> Future<T> mapAsync(Class<T> type, ContentType overrideType) { return mapAsync(defaultAsyncClient(), type, overrideType); } /** * Asynchronously maps the response to a type instance. * * @param type * The class to be mapped. * @param callback * The FutureCallback. * @return an instance of the given type populated with response values. */ public <T> Future<T> mapAsync(Class<T> type, FutureCallback<T> callback) { return mapAsync(defaultAsyncClient(), type, callback); } /** * Asynchronously maps the response to a type instance. * * @param type * The class to be mapped. * @param context * The HttpContext of the request. * @return an instance of the given type populated with response values. */ public <T> Future<T> mapAsync(Class<T> type, HttpContext context) { return mapAsync(defaultAsyncClient(), type, context); } /** * Asynchronously maps the response to a type instance. * * @param type * The class to be mapped. * @param context * The HttpContext of the request. * @param callback * The FutureCallback. * @return an instance of the given type populated with response values. */ public <T> Future<T> mapAsync(Class<T> type, HttpContext context, FutureCallback<T> callback) { return mapAsync(defaultAsyncClient(), type, context, callback); } /** * @param namespace * @return builder instance */ public Glaze ns(String namespace) { this.namespace = namespace; return this; } /** * Removes a request configuration parameter. * * @param param * The param name * @return builder instance */ public Glaze removeConfig(final String param) { this.localParams.removeParameter(param); return this; } /** * Removes a HTTP header. * * @see HttpHeaders * @param header * The header * @return builder instance */ public Glaze removeHeader(final Header header) { this.request.removeHeader(header); return this; } /** * Removes all HTTP headers with the given name. * * @param name * The header name * @return builder instance */ public Glaze removeHeaders(final String name) { this.request.removeHeaders(name); return this; } public Glaze repeatable() { this.repeatable = true; return this; } /** * Executes the current request. * * @return the response */ public Response send() { return send(defaultSyncClient()); } /** * Executes the current request with the given context. * * @param context * the HTTP context * @return the response */ public Response send(HttpContext context) { return send(defaultSyncClient(), context); } /** * Executes the current request. * * @param client * The execution client. * @return the response */ public Response send(SyncClient client) { Preconditions.checkArgument(responseHandler == null, "Response handler is not null, please use the 'execute' method instead of 'send'"); return client.execute(build(), errorHandler); } /** * Executes the current request. * * @param client * The execution client. * @param context * The HTTP context. * @return the response */ public Response send(SyncClient client, HttpContext context) { Preconditions.checkArgument(responseHandler == null, "Response handler is not null, please use the 'execute' method instead of 'send'"); return client.execute(build(), errorHandler, context); } /** * Executes the current request asynchronously. * * @return the future response */ public Future<Response> sendAsync() { return sendAsync(defaultAsyncClient()); } /** * Executes the current request asynchronously. * * @param client * The execution client. * @return the future response */ public Future<Response> sendAsync(AsyncClient client) { return client.execute(build(), errorHandler); } /** * Executes the current request asynchronously. * * @param client * The execution client. * @param callback * The FutureCallback. * @return the future response */ public Future<Response> sendAsync(AsyncClient client, FutureCallback<Response> callback) { return client.execute(build(), callback, errorHandler); } /** * Executes the current request asynchronously. * * @param client * The execution client. * @param context * The HTTP context. * @return the future response */ public Future<Response> sendAsync(AsyncClient client, HttpContext context) { return client.execute(build(), context, null, errorHandler); } /** * Executes the current request asynchronously. * * @param client * The execution client. * @param context * The HTTP context. * @param callback * The FutureCallback. * @return the future response */ public Future<Response> sendAsync(AsyncClient client, HttpContext context, FutureCallback<Response> callback) { return client.execute(build(), context, callback, errorHandler); } /** * Executes the current request asynchronously. * * @param callback * The FutureCallback. * @return the future response */ public Future<Response> sendAsync(FutureCallback<Response> callback) { return sendAsync(defaultAsyncClient(), callback); } /** * Executes the current request asynchronously with the given context. * * @param context * The HTTP context * @return the future response */ public Future<Response> sendAsync(HttpContext context) { return sendAsync(defaultAsyncClient(), context); } /** * Executes the current request asynchronously. * * @param context * The HTTP context. * @param callback * The FutureCallback. * @return the future response */ public Future<Response> sendAsync(HttpContext context, FutureCallback<Response> callback) { return sendAsync(defaultAsyncClient(), context, callback); } /** * Specifies the accept HTTP header. * * @param contentType * The content type * @return builder instance */ public Glaze setAccept(ContentType contentType) { return addHeader(HttpHeaders.ACCEPT, contentType.getMimeType()); } /** * Specifies the cache-control HTTP header. Useful for proxied request. <br/> * HTTP 1.1. Allowed values = PUBLIC | PRIVATE | NO-CACHE | NO-STORE. <br/> * <ul> * <li>public - may be cached in public shared caches</li> * <li>private - may only be cached in private cache</li> * <li>no-cache - may not be cached</li> * <li>no-store - may be cached but not archived</li> * </ul> * * @param cacheControl * The cache-control value * @return builder instance */ public Glaze setCacheControl(String cacheControl) { this.request.setHeader(HttpHeaders.CACHE_CONTROL, cacheControl); return this; } /** * Specifies the date HTTP header formatted by {@link #getDateFormat()}. * * @param date * The date * @return builder instance * @see #getDateFormat() */ public Glaze setDate(final Date date) { this.request.setHeader(HttpHeaders.DATE, getDateFormat().format(date)); return this; } /** * Sets the date format for HTTP headers. * * @param format * The date format * @return builder instance */ public Glaze setDateFormat(DateFormat format) { this.dateFormatter = format; return this; } /** * Sets the HTTP headers. * * @param headers * Array of headers * @return builder instance */ public Glaze setHeaders(final Header[] headers) { this.request.setHeaders(headers); return this; } /** * The If-Unmodified-Since request-header field is used with a method to make * it conditional. If the requested resource has not been modified since the * time specified in this field, the server SHOULD perform the requested * operation as if the If-Unmodified-Since header were not present. If the * requested variant has been modified since the specified time, the server * MUST NOT perform the requested operation, and MUST return a 412 * (Precondition Failed). * * @param date * The date * @return builder instance */ public Glaze setIfModifiedSince(final Date date) { this.request.setHeader(HttpHeaders.IF_MODIFIED_SINCE, getDateFormat().format(date)); return this; } /** * The If-Modified-Since request-header field is used with a method to make * it conditional: if the requested variant has not been modified since the * time specified in this field, an entity will not be returned from the * server; instead, a 304 (not modified) response will be returned without * any message-body. * * @param date * The date * @return builder instance */ public Glaze setIfUnmodifiedSince(final Date date) { this.request.setHeader(HttpHeaders.IF_UNMODIFIED_SINCE, getDateFormat().format(date)); return this; } /** * Defines the socket timeout in milliseconds, which is the timeout for * waiting for data or, put differently, a maximum period inactivity between * two consecutive data packets. A timeout value of zero is interpreted as an * infinite timeout. * * @param timeout * The timeout in milliseconds. * @return builder instance */ public Glaze socketTimeout(int timeout) { return config(CoreConnectionPNames.SO_TIMEOUT, timeout); } /** * Determines whether stale connection check is to be used. Disabling stale * connection check may result in a noticeable performance improvement (the * check can cause up to 30 millisecond overhead per request) at the risk of * getting an I/O error when executing a request over a connection that has * been closed at the server side. For performance critical operations the * check should be disabled. * * @param b * Boolean * @return builder instance */ public Glaze staleConnectionCheck(boolean b) { return config(CoreConnectionPNames.STALE_CONNECTION_CHECK, b); } /** * Activates 'Expect: 100-Continue' handshake for the entity enclosing * methods. The 'Expect: 100-Continue' handshake allows a client that is * sending a request message with a request body to determine if the origin * server is willing to accept the request (based on the request headers) * before the client sends the request body. The use of the 'Expect: * 100-continue' handshake can result in noticeable performance improvement * for entity enclosing requests (such as POST and PUT) that require the * target server's authentication. 'Expect: 100-continue' handshake should be * used with caution, as it may cause problems with HTTP servers and proxies * that do not support HTTP/1.1 protocol. * * @return builder instance */ public Glaze useExpectContinue() { return config(CoreProtocolPNames.USE_EXPECT_CONTINUE, true); } /** * Configures the user agent string. * * @param agent * String identifying the user agent * @return builder instance */ public Glaze userAgent(final String agent) { return config(CoreProtocolPNames.USER_AGENT, agent); } /** * Specifies the HTTP protocol version header. * * @param version * The protocol version * @return builder instance */ public Glaze version(final HttpVersion version) { return config(CoreProtocolPNames.PROTOCOL_VERSION, version); } /** * Configures the default proxy host. * * @param proxy * The proxy host * @return builder instance */ public Glaze viaProxy(final HttpHost proxy) { return config(ConnRoutePNames.DEFAULT_PROXY, proxy); } /** * @param consumer * @return builder instance */ public <T> Glaze withConsumer(HttpAsyncResponseConsumer<T> consumer) { this.asyncConsumer = consumer; return this; } /** * Sets an error handler for the current request. * * @param errorHandler * The ErrorHandler. * @return builder instance * @see ErrorHandler */ public Glaze withErrorHandler(ErrorHandler errorHandler) { this.errorHandler = errorHandler; return this; } /** * Sets a response handler for the current request. * * @param responseHandler * The ResponseHandler * @return builder instance * @see ResponseHandler */ public Glaze withHandler(ResponseHandler<?> responseHandler) { this.responseHandler = responseHandler; return this; } private AsyncClient defaultAsyncClient() { return namespace == null ? Registry.lookup(AsyncClient.class) : Registry.lookup(namespace, AsyncClient.class); } private SyncClient defaultSyncClient() { return namespace == null ? Registry.lookup(SyncClient.class) : Registry.lookup(namespace, SyncClient.class); } private DateFormat getDateFormat() { if (this.dateFormatter == null) { this.dateFormatter = new SimpleDateFormat(DATE_FORMAT, DATE_LOCALE); this.dateFormatter.setTimeZone(TIME_ZONE); } return this.dateFormatter; } private boolean hasContentType() { return serializationType != null; } private boolean needsEntityMapping(HttpUriRequest request) { return entity == null && bean != null && RequestUtil.isEnclosingEntity(request) && hasContentType(); } }