// *************************************************************************************************************************** | |
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * | |
// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * | |
// * to you 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. * | |
// *************************************************************************************************************************** | |
package org.apache.juneau.rest.client; | |
import static org.apache.juneau.internal.ClassUtils.*; | |
import static org.apache.juneau.internal.CollectionUtils.*; | |
import static org.apache.juneau.httppart.HttpPartType.*; | |
import static org.apache.juneau.collections.JsonMap.*; | |
import static org.apache.juneau.common.internal.IOUtils.*; | |
import static org.apache.juneau.common.internal.StringUtils.*; | |
import static org.apache.juneau.http.HttpEntities.*; | |
import static org.apache.juneau.http.HttpHeaders.*; | |
import static org.apache.juneau.rest.client.RestOperation.*; | |
import static java.util.stream.Collectors.*; | |
import java.io.*; | |
import java.lang.reflect.*; | |
import java.net.*; | |
import java.text.*; | |
import java.util.*; | |
import java.util.concurrent.*; | |
import java.util.function.*; | |
import java.util.logging.*; | |
import org.apache.http.*; | |
import org.apache.http.ParseException; | |
import org.apache.http.client.config.*; | |
import org.apache.http.client.entity.*; | |
import org.apache.http.client.methods.*; | |
import org.apache.http.client.utils.*; | |
import org.apache.http.concurrent.*; | |
import org.apache.http.entity.BasicHttpEntity; | |
import org.apache.http.params.*; | |
import org.apache.http.protocol.*; | |
import org.apache.juneau.*; | |
import org.apache.juneau.collections.*; | |
import org.apache.juneau.html.*; | |
import org.apache.juneau.http.*; | |
import org.apache.juneau.http.HttpHeaders; | |
import org.apache.juneau.http.entity.*; | |
import org.apache.juneau.http.header.*; | |
import org.apache.juneau.http.part.*; | |
import org.apache.juneau.http.resource.*; | |
import org.apache.juneau.httppart.*; | |
import org.apache.juneau.internal.*; | |
import org.apache.juneau.json.*; | |
import org.apache.juneau.msgpack.*; | |
import org.apache.juneau.oapi.*; | |
import org.apache.juneau.parser.*; | |
import org.apache.juneau.plaintext.*; | |
import org.apache.juneau.reflect.*; | |
import org.apache.juneau.serializer.*; | |
import org.apache.juneau.uon.*; | |
import org.apache.juneau.urlencoding.*; | |
import org.apache.juneau.xml.*; | |
/** | |
* Represents a request to a remote REST resource. | |
* | |
* <p> | |
* Instances of this class are created by the various creator methods on the {@link RestClient} class. | |
* | |
* <h5 class='section'>Notes:</h5><ul> | |
* <li class='warn'>This class is not thread safe. | |
* </ul> | |
* | |
* <h5 class='section'>See Also:</h5><ul> | |
* <li class='link'><a class="doclink" href="../../../../../index.html#juneau-rest-client">juneau-rest-client</a> | |
* </ul> | |
*/ | |
@FluentSetters | |
public class RestRequest extends BeanSession implements HttpUriRequest, Configurable, AutoCloseable { | |
private static final ContentType TEXT_PLAIN = ContentType.TEXT_PLAIN; | |
final RestClient client; // The client that created this call. | |
private final HttpRequestBase request; // The request. | |
private RestResponse response; // The response. | |
List<RestCallInterceptor> interceptors = list(); // Used for intercepting and altering requests. | |
private HeaderList headerData; | |
private PartList queryData, formData, pathData; | |
private boolean ignoreErrors, suppressLogging; | |
private Object content; | |
private Serializer serializer; | |
private Parser parser; | |
private HttpPartSchema contentSchema; | |
private URIBuilder uriBuilder; | |
private Predicate<Integer> errorCodes; | |
private HttpHost target; | |
private HttpContext context; | |
private List<Class<? extends Throwable>> rethrow; | |
private HttpPartSerializerSession partSerializerSession; | |
private final Map<HttpPartSerializer,HttpPartSerializerSession> partSerializerSessions = new IdentityHashMap<>(); | |
/** | |
* Constructs a REST call with the specified method name. | |
* | |
* @param client The client that created this request. | |
* @param uri The target URI. | |
* @param method The HTTP method name (uppercase). | |
* @param hasBody Whether this method has a body. | |
* @throws RestCallException If an exception or non-200 response code occurred during the connection attempt. | |
*/ | |
protected RestRequest(RestClient client, URI uri, String method, boolean hasBody) throws RestCallException { | |
super(client.getBeanContext().createSession()); | |
this.client = client; | |
this.request = createInnerRequest(method, uri, hasBody); | |
this.errorCodes = client.errorCodes; | |
this.uriBuilder = new URIBuilder(request.getURI()); | |
this.ignoreErrors = client.ignoreErrors; | |
this.headerData = client.createHeaderData(); | |
this.queryData = client.createQueryData(); | |
this.formData = client.createFormData(); | |
this.pathData = client.createPathData(); | |
} | |
/** | |
* Constructs the {@link HttpRequestBase} object that ends up being passed to the client execute method. | |
* | |
* <p> | |
* Subclasses can override this method to create their own request base objects. | |
* | |
* @param method The HTTP method. | |
* @param uri The HTTP URI. | |
* @param hasBody Whether the HTTP request has a body. | |
* @return A new {@link HttpRequestBase} object. | |
*/ | |
protected HttpRequestBase createInnerRequest(String method, URI uri, boolean hasBody) { | |
HttpRequestBase req = hasBody ? new BasicHttpEntityRequestBase(this, method) : new BasicHttpRequestBase(this, method); | |
req.setURI(uri); | |
return req; | |
} | |
//------------------------------------------------------------------------------------------------------------------ | |
// Configuration | |
//------------------------------------------------------------------------------------------------------------------ | |
/** | |
* Convenience method for specifying JSON as the marshalling transmission media type for this request only. | |
* | |
* <p> | |
* {@link JsonSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. | |
* <ul> | |
* <li>The serializer can be configured using any of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* </ul> | |
* <p> | |
* {@link JsonParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. | |
* <ul> | |
* <li>The parser can be configured using any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* </ul> | |
* <p> | |
* <c>Accept</c> request header will be set to <js>"application/json"</js> unless overridden | |
* by {@link RestRequest#header(String,Object)} or {@link RestRequest#accept(String)}. | |
* <p> | |
* <c>Content-Type</c> request header will be set to <js>"application/json"</js> unless overridden | |
* {@link RestRequest#header(String,Object)} or {@link RestRequest#contentType(String)}. | |
* <p> | |
* Identical to calling <c>serializer(JsonSerializer.<jk>class</jk>).parser(JsonParser.<jk>class</jk>)</c>. | |
* | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest json() { | |
return serializer(JsonSerializer.class).parser(JsonParser.class); | |
} | |
/** | |
* Convenience method for specifying Simplified JSON as the marshalling transmission media type for this request only. | |
* | |
* <p> | |
* Simplified JSON is typically useful for automated tests because you can do simple string comparison of results | |
* without having to escape lots of quotes. | |
* | |
* <p> | |
* {@link Json5Serializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. | |
* <ul> | |
* <li>The serializer can be configured using any of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* </ul> | |
* <p> | |
* {@link Json5Parser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. | |
* <ul> | |
* <li>The parser can be configured using any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* </ul> | |
* <p> | |
* <c>Accept</c> request header will be set to <js>"application/json"</js> unless overridden | |
* by {@link #header(String,Object)} or {@link #accept(String)}, or per-request via {@link RestRequest#header(String,Object)} or {@link RestRequest#accept(String)}. | |
* <p> | |
* <c>Content-Type</c> request header will be set to <js>"application/json+simple"</js> unless overridden | |
* by {@link #header(String,Object)} or {@link #contentType(String)}, or per-request via {@link RestRequest#header(String,Object)} or {@link RestRequest#contentType(String)}. | |
* <p> | |
* Can be combined with other marshaller setters such as {@link #xml()} to provide support for multiple languages. | |
* <ul> | |
* <li>When multiple languages are supported, the <c>Accept</c> and <c>Content-Type</c> headers control which marshallers are used, or uses the | |
* last-enabled language if the headers are not set. | |
* </ul> | |
* <p> | |
* Identical to calling <c>serializer(Json5Serializer.<jk>class</jk>).parser(Json5Parser.<jk>class</jk>)</c>. | |
* | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest json5() { | |
return serializer(Json5Serializer.class).parser(Json5Parser.class); | |
} | |
/** | |
* Convenience method for specifying XML as the marshalling transmission media type for this request only. | |
* | |
* <p> | |
* {@link XmlSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. | |
* <ul> | |
* <li>The serializer can be configured using any of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* </ul> | |
* <p> | |
* {@link XmlParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. | |
* <ul> | |
* <li>The parser can be configured using any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* </ul> | |
* <p> | |
* <c>Accept</c> request header will be set to <js>"text/xml"</js> unless overridden | |
* by {@link RestRequest#header(String,Object)} or {@link RestRequest#accept(String)}. | |
* <p> | |
* <c>Content-Type</c> request header will be set to <js>"text/xml"</js> unless overridden | |
* by {@link RestRequest#header(String,Object)} or {@link RestRequest#contentType(String)}. | |
* <p> | |
* Identical to calling <c>serializer(XmlSerializer.<jk>class</jk>).parser(XmlParser.<jk>class</jk>)</c>. | |
* | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest xml() { | |
return serializer(XmlSerializer.class).parser(XmlParser.class); | |
} | |
/** | |
* Convenience method for specifying HTML as the marshalling transmission media type for this request only. | |
* | |
* <p> | |
* POJOs are converted to HTML without any sort of doc wrappers. | |
* | |
* <p> | |
* {@link HtmlSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. | |
* <ul> | |
* <li>The serializer can be configured using any of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* </ul> | |
* <p> | |
* {@link HtmlParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. | |
* <ul> | |
* <li>The parser can be configured using any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* </ul> | |
* <p> | |
* <c>Accept</c> request header will be set to <js>"text/html"</js> unless overridden | |
* by {@link RestRequest#header(String,Object)} or {@link RestRequest#accept(String)}. | |
* <p> | |
* <c>Content-Type</c> request header will be set to <js>"text/html"</js> unless overridden | |
* by {@link RestRequest#header(String,Object)} or {@link RestRequest#contentType(String)}. | |
* <p> | |
* Identical to calling <c>serializer(HtmlSerializer.<jk>class</jk>).parser(HtmlParser.<jk>class</jk>)</c>. | |
* | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest html() { | |
return serializer(HtmlSerializer.class).parser(HtmlParser.class); | |
} | |
/** | |
* Convenience method for specifying HTML DOC as the marshalling transmission media type for this request only. | |
* | |
* <p> | |
* POJOs are converted to fully renderable HTML pages. | |
* | |
* <p> | |
* {@link HtmlDocSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. | |
* <ul> | |
* <li>The serializer can be configured using any of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* </ul> | |
* <p> | |
* {@link HtmlParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. | |
* <ul> | |
* <li>The parser can be configured using any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* </ul> | |
* <p> | |
* <c>Accept</c> request header will be set to <js>"text/html"</js> unless overridden | |
* by {@link RestRequest#header(String,Object)} or {@link RestRequest#accept(String)}. | |
* <p> | |
* <c>Content-Type</c> request header will be set to <js>"text/html"</js> unless overridden | |
* by {@link RestRequest#header(String,Object)} or {@link RestRequest#contentType(String)}. | |
* <p> | |
* Identical to calling <c>serializer(HtmlDocSerializer.<jk>class</jk>).parser(HtmlParser.<jk>class</jk>)</c>. | |
* | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest htmlDoc() { | |
return serializer(HtmlDocSerializer.class).parser(HtmlParser.class); | |
} | |
/** | |
* Convenience method for specifying Stripped HTML DOC as the marshalling transmission media type for this request only. | |
* | |
* <p> | |
* Same as {@link #htmlDoc()} but without the header and body tags and page title and description. | |
* | |
* <p> | |
* {@link HtmlStrippedDocSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. | |
* <ul> | |
* <li>The serializer can be configured using any of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* </ul> | |
* <p> | |
* {@link HtmlParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. | |
* <ul> | |
* <li>The parser can be configured using any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* </ul> | |
* <p> | |
* <c>Accept</c> request header will be set to <js>"text/html+stripped"</js> unless overridden | |
* by {@link RestRequest#header(String,Object)} or {@link RestRequest#accept(String)}. | |
* <p> | |
* <c>Content-Type</c> request header will be set to <js>"text/html+stripped"</js> unless overridden | |
* by {@link RestRequest#header(String,Object)} or {@link RestRequest#contentType(String)}. | |
* <p> | |
* Identical to calling <c>serializer(HtmlStrippedDocSerializer.<jk>class</jk>).parser(HtmlParser.<jk>class</jk>)</c>. | |
* | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest htmlStrippedDoc() { | |
return serializer(HtmlStrippedDocSerializer.class).parser(HtmlParser.class); | |
} | |
/** | |
* Convenience method for specifying Plain Text as the marshalling transmission media type for this request only. | |
* | |
* <p> | |
* Plain text marshalling typically only works on simple POJOs that can be converted to and from strings using | |
* swaps, swap methods, etc... | |
* | |
* <p> | |
* {@link PlainTextSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. | |
* <ul> | |
* <li>The serializer can be configured using any of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* </ul> | |
* <p> | |
* {@link PlainTextParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. | |
* <ul> | |
* <li>The parser can be configured using any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* </ul> | |
* <p> | |
* <c>Accept</c> request header will be set to <js>"text/plain"</js> unless overridden | |
* by {@link RestRequest#header(String,Object)} or {@link RestRequest#accept(String)}. | |
* <p> | |
* <c>Content-Type</c> request header will be set to <js>"text/plain"</js> unless overridden | |
* by {@link RestRequest#header(String,Object)} or {@link RestRequest#contentType(String)}. | |
* <p> | |
* Identical to calling <c>serializer(PlainTextSerializer.<jk>class</jk>).parser(PlainTextParser.<jk>class</jk>)</c>. | |
* | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest plainText() { | |
return serializer(PlainTextSerializer.class).parser(PlainTextParser.class); | |
} | |
/** | |
* Convenience method for specifying MessagePack as the marshalling transmission media type for this request only. | |
* | |
* <p> | |
* MessagePack is a binary equivalent to JSON that takes up considerably less space than JSON. | |
* | |
* <p> | |
* {@link MsgPackSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. | |
* <ul> | |
* <li>The serializer can be configured using any of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* </ul> | |
* <p> | |
* {@link MsgPackParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. | |
* <ul> | |
* <li>The parser can be configured using any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* </ul> | |
* <p> | |
* <c>Accept</c> request header will be set to <js>"octal/msgpack"</js> unless overridden | |
* by {@link RestRequest#header(String,Object)} or {@link RestRequest#accept(String)}. | |
* <p> | |
* <c>Content-Type</c> request header will be set to <js>"octal/msgpack"</js> unless overridden | |
* by {@link RestRequest#header(String,Object)} or {@link RestRequest#contentType(String)}. | |
* <p> | |
* Identical to calling <c>serializer(MsgPackSerializer.<jk>class</jk>).parser(MsgPackParser.<jk>class</jk>)</c>. | |
* | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest msgPack() { | |
return serializer(MsgPackSerializer.class).parser(MsgPackParser.class); | |
} | |
/** | |
* Convenience method for specifying UON as the marshalling transmission media type for this request only. | |
* | |
* <p> | |
* UON is Url-Encoding Object notation that is equivalent to JSON but suitable for transmission as URL-encoded | |
* query and form post values. | |
* | |
* <p> | |
* {@link UonSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. | |
* <ul> | |
* <li>The serializer can be configured using any of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* </ul> | |
* <p> | |
* {@link UonParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. | |
* <ul> | |
* <li>The parser can be configured using any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* </ul> | |
* <p> | |
* <c>Accept</c> request header will be set to <js>"text/uon"</js> unless overridden | |
* by {@link RestRequest#header(String,Object)} or {@link RestRequest#accept(String)}. | |
* <p> | |
* <c>Content-Type</c> request header will be set to <js>"text/uon"</js> unless overridden | |
* by {@link RestRequest#header(String,Object)} or {@link RestRequest#contentType(String)}. | |
* <p> | |
* Identical to calling <c>serializer(UonSerializer.<jk>class</jk>).parser(UonParser.<jk>class</jk>)</c>. | |
* | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest uon() { | |
return serializer(UonSerializer.class).parser(UonParser.class); | |
} | |
/** | |
* Convenience method for specifying URL-Encoding as the marshalling transmission media type for this request only. | |
* | |
* <p> | |
* {@link UrlEncodingSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. | |
* <ul> | |
* <li>The serializer can be configured using any of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* <li>This serializer is NOT used when using the {@link RestRequest#formData(String, Object)} (and related) methods for constructing | |
* the request body. Instead, the part serializer specified via {@link RestClient.Builder#partSerializer(Class)} is used. | |
* </ul> | |
* <p> | |
* {@link UrlEncodingParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. | |
* <ul> | |
* <li>The parser can be configured using any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* </ul> | |
* <p> | |
* <c>Accept</c> request header will be set to <js>"application/x-www-form-urlencoded"</js> unless overridden | |
* by {@link RestRequest#header(String,Object)} or {@link RestRequest#accept(String)}. | |
* <p> | |
* <c>Content-Type</c> request header will be set to <js>"application/x-www-form-urlencoded"</js> unless overridden | |
* by {@link RestRequest#header(String,Object)} or {@link RestRequest#contentType(String)}. | |
* <p> | |
* Identical to calling <c>serializer(UrlEncodingSerializer.<jk>class</jk>).parser(UrlEncodingParser.<jk>class</jk>)</c>. | |
* | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest urlEnc() { | |
return serializer(UrlEncodingSerializer.class).parser(UrlEncodingParser.class); | |
} | |
/** | |
* Convenience method for specifying OpenAPI as the marshalling transmission media type for this request only. | |
* | |
* <p> | |
* OpenAPI is a language that allows serialization to formats that use {@link HttpPartSchema} objects to describe their structure. | |
* | |
* <p> | |
* {@link OpenApiSerializer} will be used to serialize POJOs to request bodies unless overridden per request via {@link RestRequest#serializer(Serializer)}. | |
* <ul> | |
* <li>The serializer can be configured using any of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* <li>Typically the {@link RestRequest#content(Object, HttpPartSchema)} method will be used to specify the body of the request with the | |
* schema describing it's structure. | |
* </ul> | |
* <p> | |
* {@link OpenApiParser} will be used to parse POJOs from response bodies unless overridden per request via {@link RestRequest#parser(Parser)}. | |
* <ul> | |
* <li>The parser can be configured using any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* <li>Typically the {@link ResponseContent#schema(HttpPartSchema)} method will be used to specify the structure of the response body. | |
* </ul> | |
* <p> | |
* <c>Accept</c> request header will be set to <js>"text/openapi"</js> unless overridden | |
* by {@link RestRequest#header(String,Object)} or {@link RestRequest#accept(String)}. | |
* <p> | |
* <c>Content-Type</c> request header will be set to <js>"text/openapi"</js> unless overridden | |
* by {@link RestRequest#header(String,Object)} or {@link RestRequest#contentType(String)}. | |
* <p> | |
* Identical to calling <c>serializer(OpenApiSerializer.<jk>class</jk>).parser(OpenApiParser.<jk>class</jk>)</c>. | |
* | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest openApi() { | |
return serializer(OpenApiSerializer.class).parser(OpenApiParser.class); | |
} | |
/** | |
* Specifies the serializer to use on the request body. | |
* | |
* <p> | |
* Overrides the serializers specified on the {@link RestClient}. | |
* | |
* <p> | |
* The serializer is not modified by an of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* | |
* <p> | |
* If the <c>Content-Type</c> header is not set on the request, it will be set to the media type of this serializer. | |
* | |
* @param serializer The serializer used to serialize POJOs to the body of the HTTP request. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest serializer(Serializer serializer) { | |
this.serializer = serializer; | |
return this; | |
} | |
/** | |
* Specifies the serializer to use on the request body. | |
* | |
* <p> | |
* Overrides the serializers specified on the {@link RestClient}. | |
* | |
* <p> | |
* The serializer can be configured using any of the serializer property setters (e.g. {@link RestClient.Builder#sortCollections()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* | |
* <p> | |
* If the <c>Content-Type</c> header is not set on the request, it will be set to the media type of this serializer. | |
* | |
* @param serializer The serializer used to serialize POJOs to the body of the HTTP request. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest serializer(Class<? extends Serializer> serializer) { | |
this.serializer = client.getInstance(serializer); | |
return this; | |
} | |
/** | |
* Specifies the parser to use on the response body. | |
* | |
* <p> | |
* Overrides the parsers specified on the {@link RestClient}. | |
* | |
* <p> | |
* The parser is not modified by any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* | |
* <p> | |
* If the <c>Accept</c> header is not set on the request, it will be set to the media type of this parser. | |
* | |
* @param parser The parser used to parse POJOs from the body of the HTTP response. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest parser(Parser parser) { | |
this.parser = parser; | |
return this; | |
} | |
/** | |
* Specifies the parser to use on the response body. | |
* | |
* <p> | |
* Overrides the parsers specified on the {@link RestClient}. | |
* | |
* <p> | |
* The parser can be configured using any of the parser property setters (e.g. {@link RestClient.Builder#strict()}) or | |
* bean context property setters (e.g. {@link RestClient.Builder#swaps(Class...)}) defined on this builder class. | |
* | |
* <p> | |
* If the <c>Accept</c> header is not set on the request, it will be set to the media type of this parser. | |
* | |
* @param parser The parser used to parse POJOs from the body of the HTTP response. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest parser(Class<? extends Parser> parser) { | |
this.parser = client.getInstance(parser); | |
return this; | |
} | |
/** | |
* Allows you to override what status codes are considered error codes that would result in a {@link RestCallException}. | |
* | |
* <p> | |
* The default error code predicate is: <code>x -> x >= 400</code>. | |
* | |
* @param value The new predicate for calculating error codes. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest errorCodes(Predicate<Integer> value) { | |
this.errorCodes = value; | |
return this; | |
} | |
/** | |
* Add one or more interceptors for this call only. | |
* | |
* @param interceptors The interceptors to add to this call. | |
* @return This object. | |
* @throws RestCallException If init method on interceptor threw an exception. | |
*/ | |
@FluentSetter | |
public RestRequest interceptors(RestCallInterceptor...interceptors) throws RestCallException { | |
try { | |
for (RestCallInterceptor i : interceptors) { | |
this.interceptors.add(i); | |
i.onInit(this); | |
} | |
} catch (RuntimeException | RestCallException e) { | |
throw e; | |
} catch (Exception e) { | |
throw new RestCallException(null, e, "Interceptor threw an exception on init."); | |
} | |
return this; | |
} | |
/** | |
* Prevent {@link RestCallException RestCallExceptions} from being thrown when HTTP status 400+ is encountered. | |
* | |
* <p> | |
* This overrides the <l>ignoreErrors</l> property on the client. | |
* | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest ignoreErrors() { | |
this.ignoreErrors = true; | |
return this; | |
} | |
/** | |
* Rethrow any of the specified exception types if a matching <c>Exception-Name</c> header is found. | |
* | |
* <p> | |
* Rethrown exceptions will be set on the caused-by exception of {@link RestCallException} when | |
* thrown from the {@link #run()} method. | |
* | |
* <p> | |
* Can be called multiple times to append multiple rethrows. | |
* | |
* @param values The classes to rethrow. | |
* @return This object. | |
*/ | |
@SuppressWarnings("unchecked") | |
@FluentSetter | |
public RestRequest rethrow(Class<?>...values) { | |
if (rethrow == null) | |
rethrow = list(); | |
for (Class<?> v : values) { | |
if (v != null && Throwable.class.isAssignableFrom(v)) | |
rethrow.add((Class<? extends Throwable>)v); | |
} | |
return this; | |
} | |
/** | |
* Sets <c>Debug: value</c> header on this request. | |
* | |
* @return This object. | |
* @throws RestCallException Invalid input. | |
*/ | |
@FluentSetter | |
public RestRequest debug() throws RestCallException { | |
header("Debug", true); | |
return this; | |
} | |
/** | |
* Returns <jk>true</jk> if debug mode is currently enabled. | |
*/ | |
@Override | |
public boolean isDebug() { | |
return headerData.get("Debug", Boolean.class).orElse(false); | |
} | |
/** | |
* Causes logging to be suppressed for the duration of this request. | |
* | |
* <p> | |
* Overrides the {@link #debug()} setting on this request or client. | |
* | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest suppressLogging() { | |
this.suppressLogging = true; | |
return this; | |
} | |
boolean isLoggingSuppressed() { | |
return suppressLogging; | |
} | |
/** | |
* Specifies the target host for the request. | |
* | |
* @param target The target host for the request. | |
* Implementations may accept <jk>null</jk> if they can still determine a route, for example to a default | |
* target or by inspecting the request. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest target(HttpHost target) { | |
this.target = target; | |
return this; | |
} | |
/** | |
* Override the context to use for the execution. | |
* | |
* @param context The context to use for the execution, or <jk>null</jk> to use the default context. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest context(HttpContext context) { | |
this.context = context; | |
return this; | |
} | |
//------------------------------------------------------------------------------------------------------------------ | |
// Part builders | |
//------------------------------------------------------------------------------------------------------------------ | |
/** | |
* Returns the header data for the request. | |
* | |
* @return An immutable list of headers to send on the request. | |
*/ | |
public HeaderList getHeaders() { | |
return headerData; | |
} | |
/** | |
* Returns the query data for the request. | |
* | |
* @return An immutable list of query data to send on the request. | |
*/ | |
public PartList getQueryData() { | |
return queryData; | |
} | |
/** | |
* Returns the form data for the request. | |
* | |
* @return An immutable list of form data to send on the request. | |
*/ | |
public PartList getFormData() { | |
return formData; | |
} | |
/** | |
* Returns the path data for the request. | |
* | |
* @return An immutable list of path data to send on the request. | |
*/ | |
public PartList getPathData() { | |
return pathData; | |
} | |
//----------------------------------------------------------------------------------------------------------------- | |
// Parts | |
//----------------------------------------------------------------------------------------------------------------- | |
/** | |
* Appends a header to the request. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bjava'> | |
* <jc>// Adds header "Foo: bar".</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .header(Accept.<jsf>TEXT_XML</jsf>) | |
* .run(); | |
* </p> | |
* | |
* @param part | |
* The parameter to set. | |
* <jk>null</jk> values are ignored. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest header(Header part) { | |
return headers(part); | |
} | |
/** | |
* Appends multiple headers to the request. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bjava'> | |
* <jc>// Appends two headers to the request.</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .headers( | |
* BasicHeader.<jsm>of</jsm>(<js>"Foo"</js>, <js>"bar"</js>), | |
* Accept.<jsf>TEXT_XML</jsf> | |
* ) | |
* .run(); | |
* </p> | |
* | |
* @param parts | |
* The parameters to set. | |
* <jk>null</jk> values are ignored. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest headers(Header...parts) { | |
headerData.append(parts); | |
return this; | |
} | |
/** | |
* Appends multiple query parameters to the request. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bjava'> | |
* <jc>// Appends two query parameters to the request.</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .queryData( | |
* BasicStringPart.<jsm>of</jsm>(<js>"foo"</js>, <js>"bar"</js>), | |
* BasicBooleanPart.<jsm>of</jsm>(<js>"baz"</js>, <jk>true</jk>) | |
* ) | |
* .run(); | |
* </p> | |
* | |
* @param parts | |
* The parameters to set. | |
* <jk>null</jk> values are ignored. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest queryData(NameValuePair...parts) { | |
queryData.append(parts); | |
return this; | |
} | |
/** | |
* Appends multiple form-data parameters to the request. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bjava'> | |
* <jc>// Appends two form-data parameters to the request.</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .formData( | |
* BasicStringPart.<jsm>of</jsm>(<js>"foo"</js>, <js>"bar"</js>), | |
* BasicBooleanPart.<jsm>of</jsm>(<js>"baz"</js>, <jk>true</jk>) | |
* ) | |
* .run(); | |
* </p> | |
* | |
* @param parts | |
* The parameters to set. | |
* <jk>null</jk> values are ignored. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest formData(NameValuePair...parts) { | |
formData.append(parts); | |
return this; | |
} | |
/** | |
* Sets or replaces multiple path parameters on the request. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bjava'> | |
* <jc>// Appends two path parameters to the request.</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .pathData( | |
* BasicStringPart.<jsm>of</jsm>(<js>"foo"</js>, <js>"bar"</js>), | |
* BasicBooleanPart.<jsm>of</jsm>(<js>"baz"</js>, <jk>true</jk>) | |
* ) | |
* .run(); | |
* </p> | |
* | |
* @param parts | |
* The parameters to set. | |
* <jk>null</jk> values are ignored. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest pathData(NameValuePair...parts) { | |
pathData.set(parts); | |
return this; | |
} | |
/** | |
* Appends a header to the request. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bjava'> | |
* <jc>// Adds header "Foo: bar".</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .header(<js>"Foo"</js>, <js>"bar"</js>) | |
* .run(); | |
* </p> | |
* | |
* @param name | |
* The parameter name. | |
* @param value | |
* The parameter value. | |
* <br>Non-string values are converted to strings using the {@link HttpPartSerializer} defined on the client. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest header(String name, Object value) { | |
headerData.append(createHeader(name, value)); | |
return this; | |
} | |
/** | |
* Appends a query parameter to the URI of the request. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bjava'> | |
* <jc>// Adds query parameter "foo=bar".</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .queryData(<js>"foo"</js>, <js>"bar"</js>) | |
* .run(); | |
* </p> | |
* | |
* @param name | |
* The parameter name. | |
* @param value | |
* The parameter value. | |
* <br>Non-string values are converted to strings using the {@link HttpPartSerializer} defined on the client. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest queryData(String name, Object value) { | |
queryData.append(createPart(QUERY, name, value)); | |
return this; | |
} | |
/** | |
* Adds a form-data parameter to the request body. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bjava'> | |
* <jc>// Adds form data parameter "foo=bar".</jc> | |
* <jv>client</jv> | |
* .formPost(<jsf>URI</jsf>) | |
* .formData(<js>"foo"</js>, <js>"bar"</js>) | |
* .run(); | |
* </p> | |
* | |
* @param name | |
* The parameter name. | |
* @param value | |
* The parameter value. | |
* <br>Non-string values are converted to strings using the {@link HttpPartSerializer} defined on the client. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest formData(String name, Object value) { | |
formData.append(createPart(FORMDATA, name, value)); | |
return this; | |
} | |
/** | |
* Sets or replaces a path parameter of the form <js>"{name}"</js> in the URI. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bjava'> | |
* <jc>// Sets path to "/bar".</jc> | |
* <jv>client</jv> | |
* .get(<js>"/{foo}"</js>) | |
* .pathData(<js>"foo"</js>, <js>"bar"</js>) | |
* .run(); | |
* </p> | |
* | |
* @param name | |
* The parameter name. | |
* @param value | |
* The parameter value. | |
* <br>Non-string values are converted to strings using the {@link HttpPartSerializer} defined on the client. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest pathData(String name, Object value) { | |
pathData.set(createPart(PATH, name, value)); | |
return this; | |
} | |
/** | |
* Appends multiple headers to the request using freeform key/value pairs. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bjava'> | |
* <jc>// Adds headers "Foo: bar" and "Baz: qux".</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .headerPairs(<js>"Foo"</js>,<js>"bar"</js>,<js>"Baz"</js>,<js>"qux"</js>) | |
* .run(); | |
* </p> | |
* | |
* @param pairs The form-data key/value pairs. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest headerPairs(String...pairs) { | |
if (pairs.length % 2 != 0) | |
throw new RuntimeException("Odd number of parameters passed into headerPairs(String...)"); | |
HeaderList b = headerData; | |
for (int i = 0; i < pairs.length; i+=2) | |
b.append(pairs[i], pairs[i+1]); | |
return this; | |
} | |
/** | |
* Adds query parameters to the URI query using free-form key/value pairs.. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bjava'> | |
* <jc>// Adds query parameters "foo=bar&baz=qux".</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .queryDataPairs(<js>"foo"</js>,<js>"bar"</js>,<js>"baz"</js>,<js>"qux"</js>) | |
* .run(); | |
* </p> | |
* | |
* @param pairs The query key/value pairs. | |
* <ul> | |
* <li>Values can be any POJO. | |
* <li>Values converted to a string using the configured part serializer. | |
* </ul> | |
* @return This object. | |
* @throws RestCallException Invalid input. | |
*/ | |
@FluentSetter | |
public RestRequest queryDataPairs(String...pairs) throws RestCallException { | |
if (pairs.length % 2 != 0) | |
throw new RuntimeException("Odd number of parameters passed into queryDataPairs(String...)"); | |
PartList b = queryData; | |
for (int i = 0; i < pairs.length; i+=2) | |
b.append(pairs[i], pairs[i+1]); | |
return this; | |
} | |
/** | |
* Adds form-data parameters to the request body using free-form key/value pairs. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bjava'> | |
* <jc>// Creates form data "key1=val1&key2=val2".</jc> | |
* <jv>client</jv> | |
* .formPost(<jsf>URI</jsf>) | |
* .formDataPairs(<js>"key1"</js>,<js>"val1"</js>,<js>"key2"</js>,<js>"val2"</js>) | |
* .run(); | |
* </p> | |
* | |
* @param pairs The form-data key/value pairs. | |
* <ul> | |
* <li>Values can be any POJO. | |
* <li>Values converted to a string using the configured part serializer. | |
* </ul> | |
* @return This object. | |
* @throws RestCallException Invalid input. | |
*/ | |
@FluentSetter | |
public RestRequest formDataPairs(String...pairs) throws RestCallException { | |
if (pairs.length % 2 != 0) | |
throw new RuntimeException("Odd number of parameters passed into formDataPairs(String...)"); | |
PartList b = formData; | |
for (int i = 0; i < pairs.length; i+=2) | |
b.append(pairs[i], pairs[i+1]); | |
return this; | |
} | |
/** | |
* Replaces path parameters of the form <js>"{name}"</js> in the URI using free-form key/value pairs. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bjava'> | |
* <jc>// Sets path to "/baz/qux".</jc> | |
* <jv>client</jv> | |
* .get(<js>"/{foo}/{bar}"</js>) | |
* .pathDataPairs( | |
* <js>"foo"</js>,<js>"baz"</js>, | |
* <js>"bar"</js>,<js>"qux"</js> | |
* ) | |
* .run(); | |
* </p> | |
* | |
* @param pairs The path key/value pairs. | |
* <ul> | |
* <li>Values can be any POJO. | |
* <li>Values converted to a string using the configured part serializer. | |
* </ul> | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest pathDataPairs(String...pairs) { | |
if (pairs.length % 2 != 0) | |
throw new RuntimeException("Odd number of parameters passed into pathDataPairs(String...)"); | |
PartList b = pathData; | |
for (int i = 0; i < pairs.length; i+=2) | |
b.set(pairs[i], pairs[i+1]); | |
return this; | |
} | |
/** | |
* Appends multiple headers to the request from properties defined on a Java bean. | |
* | |
* <p> | |
* Uses {@link PropertyNamerDUCS} for resolving the header names from property names. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bjava'> | |
* <ja>@Bean</ja> | |
* <jk>public class</jk> MyHeaders { | |
* <jk>public</jk> String getFooBar() { <jk>return</jk> <js>"baz"</js>; } | |
* <jk>public</jk> Integer getQux() { <jk>return</jk> 123; } | |
* } | |
* | |
* <jc>// Appends headers "Foo-Bar: baz" and "Qux: 123".</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .headersBean(<jk>new</jk> MyHeaders()) | |
* .run(); | |
* </p> | |
* | |
* @param value The bean containing the properties to set as header values. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest headersBean(Object value) { | |
if (! isBean(value)) | |
throw new RuntimeException("Object passed into headersBean(Object) is not a bean."); | |
HeaderList b = headerData; | |
toBeanMap(value, PropertyNamerDUCS.INSTANCE).forEach((k,v) -> b.append(createHeader(k, v))); | |
return this; | |
} | |
/** | |
* Appends multiple query parameters to the request from properties defined on a Java bean. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bjava'> | |
* <jk>public class</jk> MyQuery { | |
* <jk>public</jk> String getFooBar() { <jk>return</jk> <js>"baz"</js>; } | |
* <jk>public</jk> Integer getQux() { <jk>return</jk> 123; } | |
* } | |
* | |
* <jc>// Appends query "fooBar=baz&qux=123".</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .queryDataBean(<jk>new</jk> MyQuery()) | |
* .run(); | |
* </p> | |
* | |
* @param value The bean containing the properties to set as query parameter values. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest queryDataBean(Object value) { | |
if (! isBean(value)) | |
throw new RuntimeException("Object passed into queryDataBean(Object) is not a bean."); | |
PartList b = queryData; | |
toBeanMap(value).forEach((k,v) -> b.append(createPart(QUERY, k, v))); | |
return this; | |
} | |
/** | |
* Appends multiple form-data parameters to the request from properties defined on a Java bean. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bjava'> | |
* <jk>public class</jk> MyFormData { | |
* <jk>public</jk> String getFooBar() { <jk>return</jk> <js>"baz"</js>; } | |
* <jk>public</jk> Integer getQux() { <jk>return</jk> 123; } | |
* } | |
* | |
* <jc>// Appends form data "fooBar=baz&qux=123".</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .formDataBean(<jk>new</jk> MyFormData()) | |
* .run(); | |
* </p> | |
* | |
* @param value The bean containing the properties to set as form-data parameter values. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest formDataBean(Object value) { | |
if (! isBean(value)) | |
throw new RuntimeException("Object passed into formDataBean(Object) is not a bean."); | |
PartList b = formData; | |
toBeanMap(value).forEach((k,v) -> b.append(createPart(FORMDATA, k, v))); | |
return this; | |
} | |
/** | |
* Sets multiple path parameters to the request from properties defined on a Java bean. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bjava'> | |
* <jk>public class</jk> MyPathVars { | |
* <jk>public</jk> String getFooBar() { <jk>return</jk> <js>"baz"</js>; } | |
* <jk>public</jk> Integer getQux() { <jk>return</jk> 123; } | |
* } | |
* | |
* <jc>// Given path "/{fooBar}/{qux}/", gets converted to "/baz/123/".</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .pathDataBean(<jk>new</jk> MyPathVars()) | |
* .run(); | |
* </p> | |
* | |
* @param value The bean containing the properties to set as path parameter values. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest pathDataBean(Object value) { | |
if (! isBean(value)) | |
throw new RuntimeException("Object passed into pathDataBean(Object) is not a bean."); | |
PartList b = pathData; | |
toBeanMap(value).forEach((k,v) -> b.set(createPart(PATH, k, v))); | |
return this; | |
} | |
//------------------------------------------------------------------------------------------------------------------ | |
// URI | |
//------------------------------------------------------------------------------------------------------------------ | |
/** | |
* Sets the URI for this request. | |
* | |
* <p> | |
* Can be any of the following types: | |
* <ul> | |
* <li>{@link URI} | |
* <li>{@link URL} | |
* <li>{@link URIBuilder} | |
* <li>Anything else converted to a string using {@link Object#toString()}. | |
* </ul> | |
* | |
* <p> | |
* Relative URI strings will be interpreted as relative to the root URI defined on the client. | |
* | |
* @param uri | |
* The URI of the remote REST resource. | |
* <br>This overrides the URI passed in from the client. | |
* <br>Can be any of the following types: | |
* <ul> | |
* <li>{@link URIBuilder} | |
* <li>{@link URI} | |
* <li>{@link URL} | |
* <li>{@link String} | |
* <li>{@link Object} - Converted to <c>String</c> using <c>toString()</c> | |
* </ul> | |
* @return This object. | |
* @throws RestCallException Invalid URI syntax detected. | |
*/ | |
@FluentSetter | |
public RestRequest uri(Object uri) throws RestCallException { | |
URI x = client.toURI(uri, null); | |
if (x.getScheme() != null) | |
uriBuilder.setScheme(x.getScheme()); | |
if (x.getHost() != null) | |
uriBuilder.setHost(x.getHost()); | |
if (x.getPort() != -1) | |
uriBuilder.setPort(x.getPort()); | |
if (x.getUserInfo() != null) | |
uriBuilder.setUserInfo(x.getUserInfo()); | |
if (x.getFragment() != null) | |
uriBuilder.setFragment(x.getFragment()); | |
if (x.getQuery() != null) | |
uriBuilder.setCustomQuery(x.getQuery()); | |
uriBuilder.setPath(x.getPath()); | |
return this; | |
} | |
/** | |
* Sets the URI scheme. | |
* | |
* @param scheme The new URI host. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest uriScheme(String scheme) { | |
uriBuilder.setScheme(scheme); | |
return this; | |
} | |
/** | |
* Sets the URI host. | |
* | |
* @param host The new URI host. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest uriHost(String host) { | |
uriBuilder.setHost(host); | |
return this; | |
} | |
/** | |
* Sets the URI port. | |
* | |
* @param port The new URI port. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest uriPort(int port) { | |
uriBuilder.setPort(port); | |
return this; | |
} | |
/** | |
* Sets the URI user info. | |
* | |
* @param userInfo The new URI user info. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest uriUserInfo(String userInfo) { | |
uriBuilder.setUserInfo(userInfo); | |
return this; | |
} | |
/** | |
* Sets the URI user info. | |
* | |
* @param username The new URI username. | |
* @param password The new URI password. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest uriUserInfo(String username, String password) { | |
uriBuilder.setUserInfo(username, password); | |
return this; | |
} | |
/** | |
* Sets the URI fragment. | |
* | |
* @param fragment The URI fragment. The value is expected to be unescaped and may contain non ASCII characters. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest uriFragment(String fragment) { | |
uriBuilder.setFragment(fragment); | |
return this; | |
} | |
/** | |
* Adds a free-form custom query. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bjava'> | |
* <jc>// Adds query parameter "foo=bar&baz=qux".</jc> | |
* <jv>client</jv> | |
* .get(<jsf>URI</jsf>) | |
* .queryCustom(<js>"foo=bar&baz=qux"</js>) | |
* .run(); | |
* </p> | |
* | |
* @param value The parameter value. | |
* <br>Can be any of the following types: | |
* <ul> | |
* <li> | |
* {@link CharSequence} | |
* <li> | |
* {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. | |
* <li> | |
* {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. | |
* <li> | |
* {@link PartList} - Converted to a URL-encoded query. | |
* </ul> | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest queryCustom(Object value) { | |
try { | |
String q = null; | |
if (value instanceof Reader) | |
q = read((Reader)value); | |
else if (value instanceof InputStream) | |
q = read((InputStream)value); | |
else | |
q = stringify(value); // Works for NameValuePairs. | |
uriBuilder.setCustomQuery(q); | |
} catch (IOException e) { | |
throw new BasicRuntimeException(e, "Could not read custom query."); | |
} | |
return this; | |
} | |
/** | |
* Adds form-data parameters as the entire body of the request. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bjava'> | |
* <jc>// Creates form data "foo=bar&baz=qux".</jc> | |
* <jv>client</jv> | |
* .formPost(<jsf>URI</jsf>) | |
* .formDataCustom(<js>"foo=bar&baz=qux"</js>) | |
* .run(); | |
* | |
* <jc>// Creates form data "foo=bar&baz=qux" using StringEntity.</jc> | |
* <jv>client</jv> | |
* .formPost(<jsf>URI</jsf>) | |
* .formDataCustom(<jk>new</jk> StringEntity(<js>"foo=bar&baz=qux"</js>,<js>"application/x-www-form-urlencoded"</js>)) | |
* .run(); | |
* | |
* <jc>// Creates form data "foo=bar&baz=qux" using StringEntity and body().</jc> | |
* <jv>client</jv> | |
* .formPost(<jsf>URI</jsf>) | |
* .content(<jk>new</jk> StringEntity(<js>"foo=bar&baz=qux"</js>,<js>"application/x-www-form-urlencoded"</js>)) | |
* .run(); | |
* </p> | |
* | |
* @param value The parameter value. | |
* <br>Can be any of the following types: | |
* <ul class='spaced-list'> | |
* <li> | |
* {@link CharSequence} | |
* <li> | |
* {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. | |
* <li> | |
* {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. | |
* <li> | |
* {@link HttpResource} - Raw contents will be serialized to remote resource. Additional headers and media type will be set on request. | |
* <li> | |
* {@link HttpEntity}/{@link BasicHttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. | |
* <li> | |
* {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the | |
* {@link RestClient}. | |
* <li> | |
* {@link PartList} - Converted to a URL-encoded FORM post. | |
* </ul> | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest formDataCustom(Object value) { | |
header(ContentType.APPLICATION_FORM_URLENCODED); | |
content(value instanceof CharSequence ? new StringReader(value.toString()) : value); | |
return this; | |
} | |
//------------------------------------------------------------------------------------------------------------------ | |
// Args | |
//------------------------------------------------------------------------------------------------------------------ | |
RestRequest headerArg(String name, Object value, HttpPartSchema schema, HttpPartSerializer serializer, boolean skipIfEmpty) { | |
boolean isMulti = isEmpty(name) || "*".equals(name) || value instanceof HeaderList || isHeaderArray(value); | |
if (! isMulti) { | |
if (! (skipIfEmpty && isEmpty(stringify(value)))) | |
return header(createHeader(name, value, serializer, schema, skipIfEmpty)); | |
return this; | |
} | |
List<Header> l = list(); | |
if (HttpHeaders.canCast(value)) { | |
l.add(HttpHeaders.cast(value)); | |
} else if (value instanceof HeaderList) { | |
((HeaderList)value).forEach(x->l.add(x)); | |
} else if (value instanceof Collection) { | |
((Collection<?>)value).forEach(x -> l.add(HttpHeaders.cast(x))); | |
} else if (value != null && value.getClass().isArray()) { | |
for (int i = 0; i < Array.getLength(value); i++) | |
l.add(HttpHeaders.cast(Array.get(value, i))); | |
} else if (value instanceof Map) { | |
toMap(value).forEach((k,v) -> l.add(createHeader(stringify(k), v, serializer, schema, skipIfEmpty))); | |
} else if (isBean(value)) { | |
toBeanMap(value).forEach((k,v) -> l.add(createHeader(k, v, serializer, schema, skipIfEmpty))); | |
} else if (value != null) { | |
throw new BasicRuntimeException("Invalid value type for header arg ''{0}'': {1}", name, className(value)); | |
} | |
if (skipIfEmpty) | |
l.removeIf(x -> isEmpty(x.getValue())); | |
headerData.append(l); | |
return this; | |
} | |
RestRequest queryArg(String name, Object value, HttpPartSchema schema, HttpPartSerializer serializer, boolean skipIfEmpty) { | |
boolean isMulti = isEmpty(name) || "*".equals(name) || value instanceof PartList || isNameValuePairArray(value); | |
if (! isMulti) { | |
if (! (skipIfEmpty && isEmpty(stringify(value)))) | |
return queryData(createPart(name, value, QUERY, serializer, schema, skipIfEmpty)); | |
return this; | |
} | |
List<NameValuePair> l = list(); | |
if (HttpParts.canCast(value)) { | |
l.add(HttpParts.cast(value)); | |
} else if (value instanceof PartList) { | |
((PartList)value).forEach(x->l.add(x)); | |
} else if (value instanceof Collection) { | |
((Collection<?>)value).forEach(x -> l.add(HttpParts.cast(x))); | |
} else if (value != null && value.getClass().isArray()) { | |
for (int i = 0; i < Array.getLength(value); i++) | |
l.add(HttpParts.cast(Array.get(value, i))); | |
} else if (value instanceof Map) { | |
toMap(value).forEach((k,v) -> l.add(createPart(stringify(k), v, QUERY, serializer, schema, skipIfEmpty))); | |
} else if (isBean(value)) { | |
toBeanMap(value).forEach((k,v) -> l.add(createPart(k, v, QUERY, serializer, schema, skipIfEmpty))); | |
} else if (value != null) { | |
queryCustom(value); | |
return this; | |
} | |
if (skipIfEmpty) | |
l.removeIf(x -> isEmpty(x.getValue())); | |
queryData.append(l); | |
return this; | |
} | |
RestRequest formDataArg(String name, Object value, HttpPartSchema schema, HttpPartSerializer serializer, boolean skipIfEmpty) { | |
boolean isMulti = isEmpty(name) || "*".equals(name) || value instanceof PartList || isNameValuePairArray(value); | |
if (! isMulti) { | |
if (! (skipIfEmpty && isEmpty(stringify(value)))) | |
return formData(createPart(name, value, FORMDATA, serializer, schema, skipIfEmpty)); | |
return this; | |
} | |
List<NameValuePair> l = list(); | |
if (HttpParts.canCast(value)) { | |
l.add(HttpParts.cast(value)); | |
} else if (value instanceof PartList) { | |
((PartList)value).forEach(x->l.add(x)); | |
} else if (value instanceof Collection) { | |
((Collection<?>)value).forEach(x -> l.add(HttpParts.cast(x))); | |
} else if (value != null && value.getClass().isArray()) { | |
for (int i = 0; i < Array.getLength(value); i++) | |
l.add(HttpParts.cast(Array.get(value, i))); | |
} else if (value instanceof Map) { | |
toMap(value).forEach((k,v) -> l.add(createPart(stringify(k), v, FORMDATA, serializer, schema, skipIfEmpty))); | |
} else if (isBean(value)) { | |
toBeanMap(value).forEach((k,v) -> l.add(createPart(k, v, FORMDATA, serializer, schema, skipIfEmpty))); | |
} else if (value != null) { | |
formDataCustom(value); | |
return this; | |
} | |
if (skipIfEmpty) | |
l.removeIf(x -> isEmpty(x.getValue())); | |
formData.append(l); | |
return this; | |
} | |
RestRequest pathArg(String name, Object value, HttpPartSchema schema, HttpPartSerializer serializer) { | |
boolean isMulti = isEmpty(name) || "*".equals(name) || value instanceof PartList || isNameValuePairArray(value); | |
if (! isMulti) | |
return pathData(createPart(name, value, PATH, serializer, schema, false)); | |
List<NameValuePair> l = list(); | |
if (HttpParts.canCast(value)) { | |
l.add(HttpParts.cast(value)); | |
} else if (value instanceof PartList) { | |
((PartList)value).forEach(x->l.add(x)); | |
} else if (value instanceof Collection) { | |
((Collection<?>)value).forEach(x -> l.add(HttpParts.cast(x))); | |
} else if (value != null && value.getClass().isArray()) { | |
for (int i = 0; i < Array.getLength(value); i++) | |
l.add(HttpParts.cast(Array.get(value, i))); | |
} else if (value instanceof Map) { | |
toMap(value).forEach((k,v) -> l.add(createPart(stringify(k), v, PATH, serializer, schema, false))); | |
} else if (isBean(value)) { | |
toBeanMap(value).forEach((k,v) -> l.add(createPart(k, v, PATH, serializer, schema, false))); | |
} else if (value != null) { | |
throw new BasicRuntimeException("Invalid value type for path arg ''{0}'': {1}", name, className(value)); | |
} | |
pathData.append(l); | |
return this; | |
} | |
//------------------------------------------------------------------------------------------------------------------ | |
// Request body | |
//------------------------------------------------------------------------------------------------------------------ | |
/** | |
* Sets the body of this request. | |
* | |
* @param value | |
* The input to be sent to the REST resource (only valid for PUT/POST/PATCH) requests. | |
* <br>Can be of the following types: | |
* <ul class='spaced-list'> | |
* <li> | |
* {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. | |
* <li> | |
* {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. | |
* <li> | |
* {@link HttpResource} - Raw contents will be serialized to remote resource. Additional headers and media type will be set on request. | |
* <li> | |
* {@link HttpEntity}/{@link BasicHttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. | |
* <li> | |
* {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the | |
* {@link RestClient}. | |
* <li> | |
* {@link PartList} - Converted to a URL-encoded FORM post. | |
* <li> | |
* A {@link Supplier} of anything on this list. | |
* </ul> | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest content(Object value) { | |
this.content = value; | |
return this; | |
} | |
/** | |
* Sets the body of this request as straight text bypassing the serializer. | |
* | |
* <p class='bjava'> | |
* <jv>client</jv> | |
* .put(<js>"/foo"</js>) | |
* .content(<jk>new</jk> StringReader(<js>"foo"</js>)) | |
* .contentType(<js>"text/foo"</js>) | |
* .run(); | |
* | |
* <jv>client</jv> | |
* .put(<js>"/foo"</js>) | |
* .bodyString(<js>"foo"</js>) | |
* .run(); | |
* </p> | |
* | |
* <p> | |
* Note that this is different than the following which will serialize <l>foo</l> as a JSON string <l>"foo"</l>. | |
* <p class='bjava'> | |
* <jv>client</jv> | |
* .put(<js>"/foo"</js>) | |
* .json() | |
* .content(<js>"foo"</js>) | |
* .run(); | |
* </p> | |
* | |
* @param input | |
* The input to be sent to the REST resource (only valid for PUT/POST/PATCH) requests. | |
* @return This object. | |
* @throws RestCallException If a retry was attempted, but the entity was not repeatable. | |
*/ | |
@FluentSetter | |
public RestRequest contentString(Object input) throws RestCallException { | |
return content(input == null ? null : new StringReader(stringify(input))); | |
} | |
/** | |
* Sets the body of this request. | |
* | |
* @param input | |
* The input to be sent to the REST resource (only valid for PUT/POST/PATCH) requests. | |
* <br>Can be of the following types: | |
* <ul class='spaced-list'> | |
* <li> | |
* {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource. | |
* <li> | |
* {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource. | |
* <li> | |
* {@link HttpResource} - Raw contents will be serialized to remote resource. Additional headers and media type will be set on request. | |
* <li> | |
* {@link HttpEntity}/{@link BasicHttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient. | |
* <li> | |
* {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the | |
* {@link RestClient}. | |
* <li> | |
* {@link PartList} - Converted to a URL-encoded FORM post. | |
* <li> | |
* A {@link Supplier} of anything on this list. | |
* </ul> | |
* @param schema The schema object that defines the format of the output. | |
* <ul> | |
* <li>If <jk>null</jk>, defaults to {@link HttpPartSchema#DEFAULT}. | |
* <li>Only used if serializer is schema-aware (e.g. {@link OpenApiSerializer}). | |
* </ul> | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest content(Object input, HttpPartSchema schema) { | |
this.content = input; | |
this.contentSchema = schema; | |
return this; | |
} | |
//------------------------------------------------------------------------------------------------------------------ | |
// Specialized headers. | |
//------------------------------------------------------------------------------------------------------------------ | |
/** | |
* Sets the value for the <c>Accept</c> request header. | |
* | |
* <p> | |
* This overrides the media type specified on the parser, but is overridden by calling | |
* <code>header(<js>"Accept"</js>, value);</code> | |
* | |
* @param value The new header value. | |
* @return This object. | |
* @throws RestCallException Invalid input. | |
*/ | |
@FluentSetter | |
public RestRequest accept(String value) throws RestCallException { | |
return header(Accept.of(value)); | |
} | |
/** | |
* Sets the value for the <c>Accept-Charset</c> request header. | |
* | |
* <p> | |
* This is a shortcut for calling <code>header(<js>"Accept-Charset"</js>, value);</code> | |
* | |
* @param value The new header value. | |
* @return This object. | |
* @throws RestCallException Invalid input. | |
*/ | |
@FluentSetter | |
public RestRequest acceptCharset(String value) throws RestCallException { | |
return header(AcceptCharset.of(value)); | |
} | |
/** | |
* Sets the value for the <c>Content-Type</c> request header. | |
* | |
* <p> | |
* This overrides the media type specified on the serializer, but is overridden by calling | |
* <code>header(<js>"Content-Type"</js>, value);</code> | |
* | |
* @param value The new header value. | |
* @return This object. | |
* @throws RestCallException Invalid input. | |
*/ | |
@FluentSetter | |
public RestRequest contentType(String value) throws RestCallException { | |
return header(ContentType.of(value)); | |
} | |
/** | |
* Shortcut for setting the <c>Accept</c> and <c>Content-Type</c> headers on a request. | |
* | |
* @param value The new header values. | |
* @return This object. | |
* @throws RestCallException Invalid input. | |
*/ | |
@FluentSetter | |
public RestRequest mediaType(String value) throws RestCallException { | |
return header(Accept.of(value)).header(ContentType.of(value)); | |
} | |
/** | |
* When called, <c>No-Trace: true</c> is added to requests. | |
* | |
* <p> | |
* This gives the opportunity for the servlet to not log errors on invalid requests. | |
* This is useful for testing purposes when you don't want your log file to show lots of errors that are simply the | |
* results of testing. | |
* | |
* @return This object. | |
* @throws RestCallException Invalid input. | |
*/ | |
@FluentSetter | |
public RestRequest noTrace() throws RestCallException { | |
return header(NoTrace.TRUE); | |
} | |
//------------------------------------------------------------------------------------------------------------------ | |
// Execution methods. | |
//------------------------------------------------------------------------------------------------------------------ | |
/** | |
* Runs this request and returns the resulting response object. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bjava'> | |
* <jk>try</jk> { | |
* <jk>int</jk> <jv>rc</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).execute().getResponseStatus(); | |
* <jc>// Succeeded!</jc> | |
* } <jk>catch</jk> (RestCallException <jv>e</jv>) { | |
* <jc>// Failed!</jc> | |
* } | |
* </p> | |
* | |
* <h5 class='section'>Notes:</h5><ul> | |
* <li class='note'>Calling this method multiple times will return the same original response object. | |
* <li class='note'>You must close the returned object if you do not consume the response or execute a method that consumes | |
* the response. | |
* <li class='note'>If you are only interested in the response code, use the {@link #complete()} method which will automatically | |
* consume the response so that you don't need to call {@link InputStream#close()} on the response body. | |
* </ul> | |
* | |
* @return The response object. | |
* @throws RestCallException If an exception or non-200 response code occurred during the connection attempt. | |
*/ | |
public RestResponse run() throws RestCallException { | |
if (response != null) | |
throw new RestCallException(response, null, "run() already called."); | |
try { | |
queryData.stream().map(SimpleQuery::new).filter(SimplePart::isValid).forEach( | |
x -> uriBuilder.addParameter(x.name, x.value) | |
); | |
pathData.stream().map(SimplePath::new).forEach(x -> | |
{ | |
String path = uriBuilder.getPath(); | |
String name = x.name, value = x.value; | |
String var = "{" + name + "}"; | |
if (path.indexOf(var) == -1 && ! name.equals("/*")) | |
throw new RuntimeException("Path variable {"+name+"} was not found in path."); | |
if (name.equals("/*")) | |
path = path.replaceAll("\\/\\*$", "/" + value); | |
else | |
path = path.replace(var, String.valueOf(value)); | |
uriBuilder.setPath(path); | |
} | |
); | |
HttpEntityEnclosingRequestBase request2 = request instanceof HttpEntityEnclosingRequestBase ? (HttpEntityEnclosingRequestBase)request : null; | |
request.setURI(uriBuilder.build()); | |
// Pick the serializer if it hasn't been overridden. | |
HeaderList hl = headerData; | |
Optional<Header> h = hl.getLast("Content-Type"); | |
String contentType = h.isPresent() ? h.get().getValue() : null; | |
Serializer serializer = this.serializer; | |
if (serializer == null) | |
serializer = client.getMatchingSerializer(contentType); | |
if (contentType == null && serializer != null) | |
contentType = serializer.getPrimaryMediaType().toString(); | |
// Pick the parser if it hasn't been overridden. | |
h = hl.getLast("Accept"); | |
String accept = h.isPresent() ? h.get().getValue() : null; | |
Parser parser = this.parser; | |
if (parser == null) | |
parser = client.getMatchingParser(accept); | |
if (accept == null && parser != null) | |
hl.set(Accept.of( parser.getPrimaryMediaType())); | |
headerData.stream().map(SimpleHeader::new).filter(SimplePart::isValid).forEach(x -> request.addHeader(x)); | |
if (request2 == null && content != NO_BODY) | |
throw new RestCallException(null, null, "Method does not support content entity. Method={0}, URI={1}", getMethod(), getURI()); | |
if (request2 != null) { | |
Object input2 = null; | |
if (content != NO_BODY) { | |
input2 = content; | |
} else { | |
input2 = new UrlEncodedFormEntity(formData.stream().map(SimpleFormData::new).filter(SimplePart::isValid).collect(toList())); | |
} | |
if (input2 instanceof Supplier) | |
input2 = ((Supplier<?>)input2).get(); | |
HttpEntity entity = null; | |
if (input2 instanceof PartList) | |
entity = new UrlEncodedFormEntity(((PartList)input2).stream().map(SimpleFormData::new).filter(SimplePart::isValid).collect(toList())); | |
else if (input2 instanceof HttpResource) { | |
HttpResource r = (HttpResource)input2; | |
r.getHeaders().forEach(x -> request.addHeader(x)); | |
entity = (HttpEntity)input2; | |
} | |
else if (input2 instanceof HttpEntity) { | |
if (input2 instanceof SerializedEntity) { | |
entity = ((SerializedEntity)input2).copyWith(serializer, contentSchema); | |
} else { | |
entity = (HttpEntity)input2; | |
} | |
} | |
else if (input2 instanceof Reader) | |
entity = readerEntity((Reader)input2, getRequestContentType(TEXT_PLAIN)); | |
else if (input2 instanceof InputStream) | |
entity = streamEntity((InputStream)input2, -1, getRequestContentType(ContentType.APPLICATION_OCTET_STREAM)); | |
else if (serializer != null) | |
entity = serializedEntity(input2, serializer, contentSchema).setContentType(contentType); | |
else { | |
if (client.hasSerializers()) { | |
if (contentType == null) | |
throw new RestCallException(null, null, "Content-Type not specified on request. Cannot match correct serializer. Use contentType(String) or mediaType(String) to specify transport language."); | |
throw new RestCallException(null, null, "No matching serializer for media type ''{0}''", contentType); | |
} | |
entity = stringEntity(input2 == null ? "" : BeanContext.DEFAULT.getClassMetaForObject(input2).toString(input2), getRequestContentType(TEXT_PLAIN)); | |
} | |
request2.setEntity(entity); | |
} | |
try { | |
response = client.createResponse(this, client.run(target, request, context), parser); | |
} catch (Exception e) { | |
throw e; | |
} | |
if (isDebug() || client.logRequests == DetailLevel.FULL) | |
response.cacheContent(); | |
for (RestCallInterceptor rci : interceptors) | |
rci.onConnect(this, response); | |
client.onCallConnect(this, response); | |
String method = getMethod(); | |
int sc = response.getStatusCode(); | |
Thrown thrown = response.getHeader("Thrown").asHeader(Thrown.class); | |
if (thrown.isPresent() && rethrow != null) { | |
Thrown.Part thrownPart = thrown.asParts().get().get(0); | |
String className = thrownPart.getClassName(); | |
String message = thrownPart.getMessage(); | |
for (Class<? extends Throwable> t : rethrow) { | |
if (t.getName().equals(className)) { | |
ConstructorInfo c = null; | |
ClassInfo ci = ClassInfo.of(t); | |
c = ci.getPublicConstructor(x -> x.hasParamTypes(HttpResponse.class)); | |
if (c != null) | |
throw c.<Throwable>invoke(response); | |
c = ci.getPublicConstructor(x -> x.hasParamTypes(String.class)); | |
if (c != null) | |
throw c.<Throwable>invoke(message != null ? message : response.getContent().asString()); | |
c = ci.getPublicConstructor(x -> x.hasParamTypes(String.class,Throwable.class)); | |
if (c != null) | |
throw c.<Throwable>invoke(message != null ? message : response.getContent().asString(), null); | |
c = ci.getPublicConstructor(x -> x.hasNoParams()); | |
if (c != null) | |
throw c.<Throwable>invoke(); | |
} | |
} | |
} | |
if (errorCodes.test(sc) && ! ignoreErrors) { | |
throw new RestCallException(response, null, "HTTP method ''{0}'' call to ''{1}'' caused response code ''{2}, {3}''.\nResponse: \n{4}", | |
method, getURI(), sc, response.getReasonPhrase(), response.getContent().asAbbreviatedString(1000)); | |
} | |
} catch (RuntimeException | RestCallException e) { | |
if (response != null) | |
response.close(); | |
throw e; | |
} catch (Throwable e) { | |
if (response != null) | |
response.close(); | |
throw new RestCallException(response, e, "Call failed."); | |
} | |
return this.response; | |
} | |
/** | |
* Same as {@link #run()} but allows you to run the call asynchronously. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bjava'> | |
* Future<RestResponse> <jv>future</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).runFuture(); | |
* | |
* <jc>// Do some other stuff</jc> | |
* | |
* <jk>try</jk> { | |
* String <jv>body</jv> = <jv>future</jv>.get().getContent().asString(); | |
* <jc>// Succeeded!</jc> | |
* } <jk>catch</jk> (RestCallException <jv>e</jv>) { | |
* <jc>// Failed!</jc> | |
* } | |
* </p> | |
* | |
* <h5 class='section'>Notes:</h5><ul> | |
* <li class='note'>Use the {@link RestClient.Builder#executorService(ExecutorService, boolean)} method to customize the | |
* executor service used for creating {@link Future Futures}. | |
* </ul> | |
* | |
* @return The HTTP status code. | |
* @throws RestCallException If the executor service was not defined. | |
*/ | |
public Future<RestResponse> runFuture() throws RestCallException { | |
return client.getExecutorService().submit( | |
new Callable<RestResponse>() { | |
@Override /* Callable */ | |
public RestResponse call() throws Exception { | |
return run(); | |
} | |
} | |
); | |
} | |
/** | |
* Same as {@link #run()} but immediately calls {@link RestResponse#consume()} to clean up the response. | |
* | |
* <p> | |
* Use this method if you're only interested in the status line of the response and not the response entity. | |
* Attempts to call any of the methods on the response object that retrieve the body (e.g. {@link ResponseContent#asReader()} | |
* will cause a {@link RestCallException} to be thrown. | |
* | |
* <h5 class='section'>Notes:</h5><ul> | |
* <li class='note'>You do not need to execute {@link InputStream#close()} on the response body to consume the response. | |
* </ul> | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bjava'> | |
* <jc>// Get the response code. | |
* // No need to call close() on the RestResponse object.</jc> | |
* <jk>int</jk> <jv>rc</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).complete().getResponseCode(); | |
* </p> | |
* | |
* @return The response object. | |
* @throws RestCallException If an exception or non-200 response code occurred during the connection attempt. | |
*/ | |
public RestResponse complete() throws RestCallException { | |
return run().consume(); | |
} | |
/** | |
* Same as {@link #complete()} but allows you to run the call asynchronously. | |
* | |
* <h5 class='section'>Example:</h5> | |
* <p class='bjava'> | |
* Future<RestResponse> <jv>future</jv> = <jv>client</jv>.get(<jsf>URI</jsf>).completeFuture(); | |
* | |
* <jc>// Do some other stuff</jc> | |
* | |
* <jk>int</jk> <jv>rc</jv> = <jv>future</jv>.get().getResponseStatus(); | |
* </p> | |
* | |
* <h5 class='section'>Notes:</h5><ul> | |
* <li class='note'>Use the {@link RestClient.Builder#executorService(ExecutorService, boolean)} method to customize the | |
* executor service used for creating {@link Future Futures}. | |
* <li class='note'>You do not need to execute {@link InputStream#close()} on the response body to consume the response. | |
* </ul> | |
* | |
* @return The HTTP status code. | |
* @throws RestCallException If the executor service was not defined. | |
*/ | |
public Future<RestResponse> completeFuture() throws RestCallException { | |
return client.getExecutorService().submit( | |
new Callable<RestResponse>() { | |
@Override /* Callable */ | |
public RestResponse call() throws Exception { | |
return complete(); | |
} | |
} | |
); | |
} | |
/** | |
* A shortcut for calling <c>run().getContent().asString()</c>. | |
* | |
* @return The response content as a simple string. | |
* @throws RestCallException If an exception or non-200 response code occurred during the connection attempt. | |
*/ | |
public String getResponseAsString() throws RestCallException { | |
return run().getContent().asString(); | |
} | |
/** | |
* A shortcut for calling <c>run().getContent().as(<js>type</js>)</c>. | |
* | |
* @param type The object type to create. | |
* @param <T> The object type to create. | |
* @see ResponseContent#as(Class) | |
* @return The response content as a simple string. | |
* @throws RestCallException If an exception or non-200 response code occurred during the connection attempt. | |
*/ | |
public <T> T getResponse(Class<T> type) throws RestCallException { | |
return run().getContent().as(type); | |
} | |
/** | |
* A shortcut for calling <c>run().getContent().as(<js>type</js>,<js>args</js>)</c>. | |
* | |
* @param <T> | |
* The object type to create. | |
* @param type | |
* The object type to create. | |
* <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} | |
* @param args | |
* The type arguments of the class if it's a collection or map. | |
* <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} | |
* <br>Ignored if the main type is not a map or collection. | |
* @see ResponseContent#as(Type,Type...) | |
* @return The response content as a simple string. | |
* @throws RestCallException If an exception or non-200 response code occurred during the connection attempt. | |
*/ | |
public <T> T getResponse(Type type, Type...args) throws RestCallException { | |
return run().getContent().as(type, args); | |
} | |
/** | |
* Returns <jk>true</jk> if this request has a body. | |
* | |
* @return <jk>true</jk> if this request has a body. | |
*/ | |
public boolean hasHttpEntity() { | |
return request instanceof HttpEntityEnclosingRequestBase; | |
} | |
/** | |
* Returns the body of this request. | |
* | |
* @return The body of this request, or <jk>null</jk> if it doesn't have a body. | |
*/ | |
public HttpEntity getHttpEntity() { | |
return hasHttpEntity() ? ((HttpEntityEnclosingRequestBase)request).getEntity() : null; | |
} | |
/** | |
* Logs a message. | |
* | |
* @param level The log level. | |
* @param t The throwable cause. | |
* @param msg The message with {@link MessageFormat}-style arguments. | |
* @param args The arguments. | |
* @return This object. | |
*/ | |
public RestRequest log(Level level, Throwable t, String msg, Object...args) { | |
client.log(level, t, msg, args); | |
return this; | |
} | |
/** | |
* Logs a message. | |
* | |
* @param level The log level. | |
* @param msg The message with {@link MessageFormat}-style arguments. | |
* @param args The arguments. | |
* @return This object. | |
*/ | |
public RestRequest log(Level level, String msg, Object...args) { | |
client.log(level, msg, args); | |
return this; | |
} | |
//----------------------------------------------------------------------------------------------------------------- | |
// HttpRequestBase pass-through methods. | |
//----------------------------------------------------------------------------------------------------------------- | |
/** | |
* Sets the actual request configuration. | |
* | |
* @param value The new value. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest config(RequestConfig value) { | |
request.setConfig(value); | |
return this; | |
} | |
/** | |
* Sets {@link Cancellable} for the ongoing operation. | |
* | |
* @param cancellable The cancellable object. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest cancellable(Cancellable cancellable) { | |
request.setCancellable(cancellable); | |
return this; | |
} | |
/** | |
* Sets the protocol version for this request. | |
* | |
* @param version The protocol version for this request. | |
* @return This object. | |
*/ | |
@FluentSetter | |
public RestRequest protocolVersion(ProtocolVersion version) { | |
request.setProtocolVersion(version); | |
return this; | |
} | |
/** | |
* Used in combination with {@link #cancellable(Cancellable)}. | |
* | |
* @return This object. | |
*/ | |
@Deprecated | |
public RestRequest completed() { | |
request.completed(); | |
return this; | |
} | |
// ----------------------------------------------------------------------------------------------------------------- | |
// HttpUriRequest pass-through methods. | |
// ----------------------------------------------------------------------------------------------------------------- | |
/** | |
* Returns the HTTP method this request uses, such as GET, PUT, POST, or other. | |
* | |
* @return The HTTP method this request uses, such as GET, PUT, POST, or other. | |
*/ | |
@Override /* HttpUriRequest */ | |
public String getMethod() { | |
return request.getMethod(); | |
} | |
/** | |
* Returns the original request URI. | |
* | |
* <h5 class='section'>Notes:</h5><ul> | |
* <li class='note'>URI remains unchanged in the course of request execution and is not updated if the request is redirected to another location. | |
* </ul> | |
* | |
* @return The original request URI. | |
*/ | |
@Override /* HttpUriRequest */ | |
public URI getURI() { | |
return request.getURI(); | |
} | |
/** | |
* Aborts this http request. Any active execution of this method should return immediately. | |
* | |
* If the request has not started, it will abort after the next execution. | |
* <br>Aborting this request will cause all subsequent executions with this request to fail. | |
*/ | |
@Override /* HttpUriRequest */ | |
public void abort() throws UnsupportedOperationException { | |
request.abort(); | |
} | |
@Override /* HttpUriRequest */ | |
public boolean isAborted() { | |
return request.isAborted(); | |
} | |
/** | |
* Returns the request line of this request. | |
* | |
* @return The request line. | |
*/ | |
@Override /* HttpRequest */ | |
public RequestLine getRequestLine() { | |
return request.getRequestLine(); | |
} | |
/** | |
* Returns the protocol version this message is compatible with. | |
* | |
* @return The protocol version. | |
*/ | |
@Override /* HttpMessage */ | |
public ProtocolVersion getProtocolVersion() { | |
return request.getProtocolVersion(); | |
} | |
/** | |
* Checks if a certain header is present in this message. | |
* | |
* Header values are ignored. | |
* | |
* @param name The header name to check for. | |
* @return <jk>true</jk> if at least one header with this name is present. | |
*/ | |
@Override /* HttpMessage */ | |
public boolean containsHeader(String name) { | |
return headerData.contains(name); | |
} | |
/** | |
* Returns all the headers with a specified name of this message. | |
* | |
* Header values are ignored. | |
* <br>Headers are ordered in the sequence they will be sent over a connection. | |
* | |
* @param name The name of the headers to return. | |
* @return The headers whose name property equals name. | |
*/ | |
@Override /* HttpMessage */ | |
public Header[] getHeaders(String name) { | |
return headerData.getAll(name); | |
} | |
/** | |
* Returns the first header with a specified name of this message. | |
* | |
* Header values are ignored. | |
* <br>If there is more than one matching header in the message the first element of {@link #getHeaders(String)} is returned. | |
* <br>If there is no matching header in the message <jk>null</jk> is returned. | |
* | |
* @param name The name of the header to return. | |
* @return The first header whose name property equals name or <jk>null</jk> if no such header could be found. | |
*/ | |
@Override /* HttpMessage */ | |
public Header getFirstHeader(String name) { | |
return headerData.getFirst(name).orElse(null); | |
} | |
/** | |
* Returns the last header with a specified name of this message. | |
* | |
* Header values are ignored. | |
* <br>If there is more than one matching header in the message the last element of {@link #getHeaders(String)} is returned. | |
* <br>If there is no matching header in the message null is returned. | |
* | |
* @param name The name of the header to return. | |
* @return The last header whose name property equals name or <jk>null</jk> if no such header could be found. | |
*/ | |
@Override /* HttpMessage */ | |
public Header getLastHeader(String name) { | |
return headerData.getLast(name).orElse(null); | |
} | |
/** | |
* Returns all the headers of this message. | |
* | |
* Headers are ordered in the sequence they will be sent over a connection. | |
* | |
* @return All the headers of this message | |
*/ | |
@Override /* HttpMessage */ | |
public Header[] getAllHeaders() { | |
return headerData.getAll(); | |
} | |
/** | |
* Adds a header to this message. | |
* | |
* The header will be appended to the end of the list. | |
* | |
* <h5 class='section'>Notes:</h5><ul> | |
* <li class='note'>{@link #header(Header)} is an equivalent method and the preferred method for fluent-style coding. | |
* </ul> | |
* | |
* @param header The header to append. | |
*/ | |
@Override /* HttpMessage */ | |
public void addHeader(Header header) { | |
headerData.append(header); | |
} | |
/** | |
* Adds a header to this message. | |
* | |
* The header will be appended to the end of the list. | |
* | |
* <h5 class='section'>Notes:</h5><ul> | |
* <li class='note'>{@link #header(String,Object)} is an equivalent method and the preferred method for fluent-style coding. | |
* </ul> | |
* | |
* @param name The name of the header. | |
* @param value The value of the header. | |
*/ | |
@Override /* HttpMessage */ | |
public void addHeader(String name, String value) { | |
headerData.append(stringHeader(name, value)); | |
} | |
/** | |
* Overwrites the first header with the same name. | |
* | |
* The new header will be appended to the end of the list, if no header with the given name can be found. | |
* | |
* @param header The header to set. | |
*/ | |
@Override /* HttpMessage */ | |
public void setHeader(Header header) { | |
headerData.set(header); | |
} | |
/** | |
* Overwrites the first header with the same name. | |
* | |
* The new header will be appended to the end of the list, if no header with the given name can be found. | |
* | |
* @param name The name of the header. | |
* @param value The value of the header. | |
*/ | |
@Override /* HttpMessage */ | |
public void setHeader(String name, String value) { | |
headerData.set(stringHeader(name, value)); | |
} | |
/** | |
* Overwrites all the headers in the message. | |
* | |
* @param headers The array of headers to set. | |
*/ | |
@Override /* HttpMessage */ | |
public void setHeaders(Header[] headers) { | |
headerData.set(headers); | |
} | |
/** | |
* Removes a header from this message. | |
* | |
* @param header The header to remove. | |
*/ | |
@Override /* HttpMessage */ | |
public void removeHeader(Header header) { | |
headerData.remove(header); | |
} | |
/** | |
* Removes all headers with a certain name from this message. | |
* | |
* @param name The name of the headers to remove. | |
*/ | |
@Override /* HttpMessage */ | |
public void removeHeaders(String name) { | |
headerData.remove(name); | |
} | |
/** | |
* Returns an iterator of all the headers. | |
* | |
* @return Iterator that returns {@link Header} objects in the sequence they are sent over a connection. | |
*/ | |
@Override /* HttpMessage */ | |
public HeaderIterator headerIterator() { | |
return headerData.headerIterator(); | |
} | |
/** | |
* Returns an iterator of the headers with a given name. | |
* | |
* @param name the name of the headers over which to iterate, or <jk>null</jk> for all headers. | |
* @return Iterator that returns {@link Header} objects with the argument name in the sequence they are sent over a connection. | |
*/ | |
@Override /* HttpMessage */ | |
public HeaderIterator headerIterator(String name) { | |
return headerData.headerIterator(name); | |
} | |
/** | |
* Returns the parameters effective for this message as set by {@link #setParams(HttpParams)}. | |
* | |
* @return The parameters effective for this message as set by {@link #setParams(HttpParams)}. | |
* @deprecated Use constructor parameters of configuration API provided by HttpClient. | |
*/ | |
@Override /* HttpMessage */ | |
@Deprecated | |
public HttpParams getParams() { | |
return request.getParams(); | |
} | |
/** | |
* Provides parameters to be used for the processing of this message. | |
* | |
* @param params The parameters. | |
* @deprecated Use constructor parameters of configuration API provided by HttpClient. | |
*/ | |
@Override /* HttpMessage */ | |
@Deprecated | |
public void setParams(HttpParams params) { | |
request.setParams(params); | |
} | |
/** | |
* Returns the actual request configuration. | |
* | |
* @return The actual request configuration. | |
*/ | |
@Override /* Configurable */ | |
public RequestConfig getConfig() { | |
return request.getConfig(); | |
} | |
// ----------------------------------------------------------------------------------------------------------------- | |
// Utility methods | |
// ----------------------------------------------------------------------------------------------------------------- | |
private ContentType getRequestContentType(ContentType def) { | |
Header h = request.getFirstHeader("Content-Type"); | |
if (h != null) { | |
String s = h.getValue(); | |
if (! isEmpty(s)) | |
return ContentType.of(s); | |
} | |
return def; | |
} | |
@SuppressWarnings("unchecked") | |
private static Map<Object,Object> toMap(Object o) { | |
return (Map<Object,Object>)o; | |
} | |
/** | |
* Creates a new header. | |
* | |
* @param name The header name. | |
* @param value The header value. | |
* @param serializer The part serializer to use, or <jk>null</jk> to use the part serializer defined on the client. | |
* @param schema Optional HTTP part schema to provide to the part serializer. | |
* @param skipIfEmpty If <jk>true</jk>, empty string values will be ignored on the request. | |
* @return A new header. | |
*/ | |
protected Header createHeader(String name, Object value, HttpPartSerializer serializer, HttpPartSchema schema, Boolean skipIfEmpty) { | |
if (isEmpty(name)) | |
return null; | |
if (skipIfEmpty == null) | |
skipIfEmpty = client.isSkipEmptyHeaderData(); | |
if (serializer == null) | |
serializer = client.getPartSerializer(); | |
return new SerializedHeader(name, value, getPartSerializerSession(serializer), schema, skipIfEmpty); | |
} | |
private Header createHeader(String name, Object value) { | |
return createHeader(name, value, null, null, null); | |
} | |
/** | |
* Creates a new query/form-data/path part. | |
* | |
* @param name The part name. | |
* @param value The part value. | |
* @param type The HTTP part type. | |
* @param serializer The part serializer to use, or <jk>null</jk> to use the part serializer defined on the client. | |
* @param schema Optional HTTP part schema to provide to the part serializer. | |
* @param skipIfEmpty If <jk>true</jk>, empty string values will be ignored on the request. | |
* @return A new part. | |
*/ | |
protected NameValuePair createPart(String name, Object value, HttpPartType type, HttpPartSerializer serializer, HttpPartSchema schema, Boolean skipIfEmpty) { | |
if (isEmpty(name)) | |
return null; | |
if (skipIfEmpty == null) { | |
if (type == QUERY) | |
skipIfEmpty = client.isSkipEmptyQueryData(); | |
else if (type == FORMDATA) | |
skipIfEmpty = client.isSkipEmptyFormData(); | |
else | |
skipIfEmpty = false; | |
} | |
if (serializer == null) | |
serializer = client.getPartSerializer(); | |
return new SerializedPart(name, value, type, getPartSerializerSession(serializer), schema, skipIfEmpty); | |
} | |
private NameValuePair createPart(HttpPartType type, String name, Object value) { | |
return createPart(name, value, type, null, null, null); | |
} | |
private HttpPartSerializerSession getPartSerializerSession(HttpPartSerializer serializer) { | |
if (serializer == null) | |
serializer = client.getPartSerializer(); | |
HttpPartSerializerSession s = partSerializerSessions.get(serializer); | |
if (s == null) { | |
s = serializer.getPartSession(); | |
partSerializerSessions.put(serializer, s); | |
} | |
return s; | |
} | |
HttpPartSerializerSession getPartSerializerSession() { | |
if (partSerializerSession == null) | |
partSerializerSession = getPartSerializerSession(null); | |
return partSerializerSession; | |
} | |
private static boolean isNameValuePairArray(Object o) { | |
if (o == null || ! o.getClass().isArray()) | |
return false; | |
if (NameValuePair.class.isAssignableFrom(o.getClass().getComponentType())) | |
return true; | |
return false; | |
} | |
private static boolean isHeaderArray(Object o) { | |
if (o == null || ! o.getClass().isArray()) | |
return false; | |
if (Header.class.isAssignableFrom(o.getClass().getComponentType())) | |
return true; | |
return false; | |
} | |
//----------------------------------------------------------------------------------------------------------------- | |
// Simple parts | |
//----------------------------------------------------------------------------------------------------------------- | |
private class SimplePart implements NameValuePair { | |
final String name; | |
final String value; | |
SimplePart(NameValuePair x, boolean skipIfEmpty) { | |
name = x.getName(); | |
if (x instanceof SerializedHeader) { | |
value = ((SerializedHeader)x).copyWith(getPartSerializerSession(), null).getValue(); | |
} else if (x instanceof SerializedPart) { | |
value = ((SerializedPart)x).copyWith(getPartSerializerSession(), null).getValue(); | |
} else { | |
String v = x.getValue(); | |
value = (isEmpty(v) && skipIfEmpty) ? null : v; | |
} | |
} | |
boolean isValid() { | |
if (isEmpty(name) || value == null) | |
return false; | |
return true; | |
} | |
@Override | |
public String getName() { | |
return name; | |
} | |
@Override | |
public String getValue() { | |
return value; | |
} | |
} | |
private class SimpleQuery extends SimplePart { | |
SimpleQuery(NameValuePair x) { | |
super(x, client.isSkipEmptyQueryData()); | |
} | |
} | |
private class SimpleFormData extends SimplePart { | |
SimpleFormData(NameValuePair x) { | |
super(x, client.isSkipEmptyFormData()); | |
} | |
} | |
private class SimplePath extends SimplePart { | |
SimplePath(NameValuePair x) { | |
super(x, false); | |
} | |
} | |
private class SimpleHeader extends SimplePart implements Header { | |
SimpleHeader(NameValuePair x) { | |
super(x, client.isSkipEmptyHeaderData()); | |
} | |
@Override | |
public HeaderElement[] getElements() throws ParseException { | |
return null; | |
} | |
} | |
//----------------------------------------------------------------------------------------------------------------- | |
// Fluent setters | |
//----------------------------------------------------------------------------------------------------------------- | |
// <FluentSetters> | |
// </FluentSetters> | |
//----------------------------------------------------------------------------------------------------------------- | |
// Other methods | |
//----------------------------------------------------------------------------------------------------------------- | |
@Override | |
public void close() throws Exception { | |
complete(); | |
} | |
@Override /* ContextSession */ | |
protected JsonMap properties() { | |
return filteredMap() | |
.append("client", client.properties()) | |
.append("ignoreErrors", ignoreErrors) | |
.append("interceptors", interceptors) | |
.append("requestBodySchema", contentSchema) | |
.append("response", response) | |
.append("serializer", serializer); | |
} | |
} |