001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.rest.client;
014
015import static org.apache.juneau.internal.StringUtils.*;
016import static org.apache.juneau.internal.ClassUtils.*;
017import static org.apache.juneau.httppart.HttpPartType.*;
018
019import java.io.*;
020import java.lang.reflect.*;
021import java.lang.reflect.Proxy;
022import java.net.*;
023import java.net.URI;
024import java.util.*;
025import java.util.concurrent.*;
026import java.util.regex.*;
027
028import org.apache.http.*;
029import org.apache.http.client.ClientProtocolException;
030import org.apache.http.client.methods.*;
031import org.apache.http.client.utils.*;
032import org.apache.http.entity.*;
033import org.apache.http.impl.client.*;
034import org.apache.juneau.*;
035import org.apache.juneau.annotation.*;
036import org.apache.juneau.httppart.*;
037import org.apache.juneau.httppart.bean.*;
038import org.apache.juneau.internal.*;
039import org.apache.juneau.json.*;
040import org.apache.juneau.oapi.*;
041import org.apache.juneau.parser.*;
042import org.apache.juneau.remote.*;
043import org.apache.juneau.rest.client.remote.*;
044import org.apache.juneau.serializer.*;
045import org.apache.juneau.urlencoding.*;
046import org.apache.juneau.utils.*;
047
048/**
049 * Utility class for interfacing with remote REST interfaces.
050 *
051 * <h5 class='topic'>Features</h5>
052 * <ul class='spaced-list'>
053 *    <li>
054 *       Convert POJOs directly to HTTP request message bodies using {@link Serializer} class.
055 *    <li>
056 *       Convert HTTP response message bodies directly to POJOs using {@link Parser} class.
057 *    <li>
058 *       Fluent interface.
059 *    <li>
060 *       Thread safe.
061 *    <li>
062 *       API for interacting with remote services.
063 * </ul>
064 *
065 *
066 * <ul class='seealso'>
067 *    <li class='link'>{@doc juneau-rest-client}
068 * </ul>
069 */
070@SuppressWarnings("rawtypes")
071@ConfigurableContext(nocache=true)
072public class RestClient extends BeanContext implements Closeable {
073
074   //-------------------------------------------------------------------------------------------------------------------
075   // Configurable properties
076   //-------------------------------------------------------------------------------------------------------------------
077
078   private static final String PREFIX = "RestClient.";
079
080   /**
081    * Configuration property:  Debug.
082    *
083    * <h5 class='section'>Property:</h5>
084    * <ul>
085    *    <li><b>Name:</b>  <js>"RestClient.debug.b"</js>
086    *    <li><b>Data type:</b>  <c>Boolean</c>
087    *    <li><b>Default:</b>  <jk>false</jk>
088    *    <li><b>Methods:</b>
089    *       <ul>
090    *          <li class='jm'>{@link RestClientBuilder#debug()}
091    *       </ul>
092    * </ul>
093    *
094    * <h5 class='section'>Description:</h5>
095    * <p>
096    * Enable debug mode.
097    */
098   public static final String RESTCLIENT_debug = PREFIX + "debug.b";
099
100   /**
101    * Configuration property:  Executor service.
102    *
103    * <h5 class='section'>Property:</h5>
104    * <ul>
105    *    <li><b>Name:</b>  <js>"RestClient.executorService.o"</js>
106    *    <li><b>Data type:</b>  <code>Class&lt;? <jk>implements</jk> ExecutorService&gt;</code> or {@link ExecutorService}.
107    *    <li><b>Default:</b>  <jk>null</jk>.
108    *    <li><b>Methods:</b>
109    *       <ul>
110    *          <li class='jm'>{@link RestClientBuilder#executorService(ExecutorService, boolean)}
111    *       </ul>
112    * </ul>
113    *
114    * <h5 class='section'>Description:</h5>
115    * <p>
116    * Defines the executor service to use when calling future methods on the {@link RestCall} class.
117    *
118    * <p>
119    * This executor service is used to create {@link Future} objects on the following methods:
120    * <ul>
121    *    <li>{@link RestCall#runFuture()}
122    *    <li>{@link RestCall#getResponseFuture(Class)}
123    *    <li>{@link RestCall#getResponseFuture(Type,Type...)}
124    *    <li>{@link RestCall#getResponseAsString()}
125    * </ul>
126    *
127    * <p>
128    * The default executor service is a single-threaded {@link ThreadPoolExecutor} with a 30 second timeout
129    * and a queue size of 10.
130    */
131   public static final String RESTCLIENT_executorService = PREFIX + "executorService.o";
132
133   /**
134    * Configuration property:  Shut down executor service on close.
135    *
136    * <h5 class='section'>Property:</h5>
137    * <ul>
138    *    <li><b>Name:</b>  <js>"RestClient.executorServiceShutdownOnClose.b"</js>
139    *    <li><b>Data type:</b>  <c>Boolean</c>
140    *    <li><b>Default:</b>  <jk>false</jk>
141    *    <li><b>Methods:</b>
142    *       <ul>
143    *          <li class='jm'>{@link RestClientBuilder#executorService(ExecutorService, boolean)}
144    *       </ul>
145    * </ul>
146    *
147    * <h5 class='section'>Description:</h5>
148    * <p>
149    * Call {@link ExecutorService#shutdown()} when {@link RestClient#close()} is called.
150    */
151   public static final String RESTCLIENT_executorServiceShutdownOnClose = PREFIX + "executorServiceShutdownOnClose.b";
152
153   /**
154    * Configuration property:  Request headers.
155    *
156    * <h5 class='section'>Property:</h5>
157    * <ul>
158    *    <li><b>Name:</b>  <js>"RestClient.requestHeader.sms"</js>
159    *    <li><b>Data type:</b>  <c>Map&lt;String,String&gt;</c>
160    *    <li><b>Default:</b>  empty map
161    *    <li><b>Methods:</b>
162    *       <ul>
163    *          <li class='jm'>{@link RestClientBuilder#defaultHeaders(Collection)}
164    *          <li class='jm'>{@link RestClientBuilder#header(String, Object)}
165    *       </ul>
166    * </ul>
167    *
168    * <h5 class='section'>Description:</h5>
169    * <p>
170    * Headers to add to every request.
171    */
172   public static final String RESTCLIENT_headers = PREFIX + "headers.sms";
173
174   /**
175    * Configuration property:  Call interceptors.
176    *
177    * <h5 class='section'>Property:</h5>
178    * <ul>
179    *    <li><b>Name:</b>  <js>"RestClient.interceptors.lo"</js>
180    *    <li><b>Data type:</b>  <code>List&lt;Class&lt;? <jk>implements</jk> RestCallInterceptor&gt; | RestCallInterceptor&gt;</code>
181    *    <li><b>Default:</b>  empty list.
182    *    <li><b>Methods:</b>
183    *       <ul>
184    *          <li class='jm'>{@link RestClientBuilder#interceptors(RestCallInterceptor...)}
185    *       </ul>
186    * </ul>
187    *
188    * <h5 class='section'>Description:</h5>
189    * <p>
190    * Interceptors that get called immediately after a connection is made.
191    */
192   public static final String RESTCLIENT_interceptors = PREFIX + "interceptors.lo";
193
194   /**
195    * Add to the Call interceptors property.
196    */
197   public static final String RESTCLIENT_interceptors_add = PREFIX + "interceptors.lo/add";
198
199   /**
200    * Configuration property:  Keep HttpClient open.
201    *
202    * <h5 class='section'>Property:</h5>
203    * <ul>
204    *    <li><b>Name:</b>  <js>"RestClient.keepHttpClientOpen.b"</js>
205    *    <li><b>Data type:</b>  <c>Boolean</c>
206    *    <li><b>Default:</b>  <jk>false</jk>
207    *    <li><b>Methods:</b>
208    *       <ul>
209    *          <li class='jm'>{@link RestClientBuilder#keepHttpClientOpen(boolean)}
210    *       </ul>
211    * </ul>
212    *
213    * <h5 class='section'>Description:</h5>
214    * <p>
215    * Don't close this client when the {@link RestClient#close()} method is called.
216    */
217   public static final String RESTCLIENT_keepHttpClientOpen = PREFIX + "keepHttpClientOpen.b";
218
219   /**
220    * Configuration property:  Parser.
221    *
222    * <h5 class='section'>Property:</h5>
223    * <ul>
224    *    <li><b>Name:</b>  <js>"RestClient.parser.o"</js>
225    *    <li><b>Data type:</b>  <code>Class&lt;? <jk>extends</jk> Parser&gt;</code> or {@link Parser}.
226    *    <li><b>Default:</b>  {@link JsonParser};
227    *    <li><b>Methods:</b>
228    *       <ul>
229    *          <li class='jm'>{@link RestClientBuilder#parser(Class)}
230    *          <li class='jm'>{@link RestClientBuilder#parser(Parser)}
231    *       </ul>
232    * </ul>
233    *
234    * <h5 class='section'>Description:</h5>
235    * <p>
236    * The parser to use for parsing POJOs in response bodies.
237    */
238   public static final String RESTCLIENT_parser = PREFIX + "parser.o";
239
240   /**
241    * Configuration property:  Part parser.
242    *
243    * <h5 class='section'>Property:</h5>
244    * <ul>
245    *    <li><b>Name:</b>  <js>"RestClient.partParser.o"</js>
246    *    <li><b>Data type:</b>  <code>Class&lt;? <jk>implements</jk> HttpPartParser&gt;</code> or {@link HttpPartParser}.
247    *    <li><b>Default:</b>  {@link OpenApiParser};
248    *    <li><b>Methods:</b>
249    *       <ul>
250    *          <li class='jm'>{@link RestClientBuilder#partParser(Class)}
251    *          <li class='jm'>{@link RestClientBuilder#partParser(HttpPartParser)}
252    *       </ul>
253    * </ul>
254    *
255    * <h5 class='section'>Description:</h5>
256    * <p>
257    * The parser to use for parsing POJOs from form data, query parameters, headers, and path variables.
258    */
259   public static final String RESTCLIENT_partParser = PREFIX + "partParser.o";
260
261   /**
262    * Configuration property:  Part serializer.
263    *
264    * <h5 class='section'>Property:</h5>
265    * <ul>
266    *    <li><b>Name:</b>  <js>"RestClient.partSerializer.o"</js>
267    *    <li><b>Data type:</b>  <code>Class&lt;? <jk>implements</jk> HttpPartSerializer&gt;</code> or {@link HttpPartSerializer}.
268    *    <li><b>Default:</b>  {@link OpenApiSerializer};
269    *    <li><b>Methods:</b>
270    *       <ul>
271    *          <li class='jm'>{@link RestClientBuilder#partSerializer(Class)}
272    *          <li class='jm'>{@link RestClientBuilder#partSerializer(HttpPartSerializer)}
273    *       </ul>
274    * </ul>
275    *
276    * <h5 class='section'>Description:</h5>
277    * <p>
278    * The serializer to use for serializing POJOs in form data, query parameters, headers, and path variables.
279    */
280   public static final String RESTCLIENT_partSerializer = PREFIX + "partSerializer.o";
281
282   /**
283    * Configuration property:  Request query parameters.
284    *
285    * <h5 class='section'>Property:</h5>
286    * <ul>
287    *    <li><b>Name:</b>  <js>"RestClient.query.sms"</js>
288    *    <li><b>Data type:</b>  <c>Map&lt;String,String&gt;</c>
289    *    <li><b>Default:</b>  empty map
290    *    <li><b>Methods:</b>
291    *       <ul>
292    *          <li class='jm'>{@link RestClientBuilder#query(String, Object)}
293    *       </ul>
294    * </ul>
295    *
296    * <h5 class='section'>Description:</h5>
297    * <p>
298    * Query parameters to add to every request.
299    */
300   public static final String RESTCLIENT_query = PREFIX + "query.sms";
301
302   /**
303    * Configuration property:  Number of retries to attempt.
304    *
305    * <h5 class='section'>Property:</h5>
306    * <ul>
307    *    <li><b>Name:</b>  <js>"RestClient.retries.i"</js>
308    *    <li><b>Data type:</b>  <c>Integer</c>
309    *    <li><b>Default:</b>  <c>1</c>
310    *    <li><b>Methods:</b>
311    *       <ul>
312    *          <li class='jm'>{@link RestClientBuilder#retryable(int, int, RetryOn)}
313    *       </ul>
314    * </ul>
315    *
316    * <h5 class='section'>Description:</h5>
317    * <p>
318    * The number of retries to attempt when the connection cannot be made or a <c>&gt;400</c> response is received.
319    */
320   public static final String RESTCLIENT_retries = PREFIX + "retries.i";
321
322   /**
323    * Configuration property:  The time in milliseconds between retry attempts.
324    *
325    * <h5 class='section'>Property:</h5>
326    * <ul>
327    *    <li><b>Name:</b>  <js>"RestClient.retryInterval.i"</js>
328    *    <li><b>Data type:</b>  <c>Integer</c>
329    *    <li><b>Default:</b>  <c>-1</c>
330    *    <li><b>Methods:</b>
331    *       <ul>
332    *          <li class='jm'>{@link RestClientBuilder#retryable(int, int, RetryOn)}
333    *       </ul>
334    * </ul>
335    *
336    * <h5 class='section'>Description:</h5>
337    * <p>
338    * The time in milliseconds between retry attempts.
339    * <c>-1</c> means retry immediately.
340    */
341   public static final String RESTCLIENT_retryInterval = PREFIX + "retryInterval.i";
342
343   /**
344    * Configuration property:  Retry-on determination object.
345    *
346    * <h5 class='section'>Property:</h5>
347    * <ul>
348    *    <li><b>Name:</b>  <js>"RestClient.retryOn.o"</js>
349    *    <li><b>Data type:</b>  <c>Class&lt;? extends {@link RetryOn}</c> or {@link RetryOn}
350    *    <li><b>Default:</b>  {@link RetryOn#DEFAULT}
351    *    <li><b>Methods:</b>
352    *       <ul>
353    *          <li class='jm'>{@link RestClientBuilder#retryable(int, int, RetryOn)}
354    *       </ul>
355    * </ul>
356    *
357    * <h5 class='section'>Description:</h5>
358    * <p>
359    * Object used for determining whether a retry should be attempted.
360    */
361   public static final String RESTCLIENT_retryOn = PREFIX + "retryOn.o";
362
363   /**
364    * Configuration property:  Root URI.
365    *
366    * <h5 class='section'>Property:</h5>
367    * <ul>
368    *    <li><b>Name:</b>  <js>"RestClient.rootUri.s"</js>
369    *    <li><b>Data type:</b>  <c>String</c>
370    *    <li><b>Default:</b>  <jk>false</jk>
371    *    <li><b>Methods:</b>
372    *       <ul>
373    *          <li class='jm'>{@link RestClientBuilder#rootUrl(Object)}
374    *       </ul>
375    * </ul>
376    *
377    * <h5 class='section'>Description:</h5>
378    * <p>
379    * When set, relative URL strings passed in through the various rest call methods (e.g. {@link RestClient#doGet(Object)}
380    * will be prefixed with the specified root.
381    * <br>This root URL is ignored on those methods if you pass in a {@link URL}, {@link URI}, or an absolute URL string.
382    * <br>Trailing slashes are trimmed.
383    */
384   public static final String RESTCLIENT_rootUri = PREFIX + "rootUri.s";
385
386   /**
387    * Configuration property:  Serializer.
388    *
389    * <h5 class='section'>Property:</h5>
390    * <ul>
391    *    <li><b>Name:</b>  <js>"RestClient.serializer.o"</js>
392    *    <li><b>Data type:</b>  <code>Class&lt;? <jk>extends</jk> Serializer&gt;</code> or {@link Serializer}.
393    *    <li><b>Default:</b>  {@link JsonSerializer};
394    *    <li><b>Methods:</b>
395    *       <ul>
396    *          <li class='jm'>{@link RestClientBuilder#serializer(Class)}
397    *          <li class='jm'>{@link RestClientBuilder#serializer(Serializer)}
398    *       </ul>
399    * </ul>
400    *
401    * <h5 class='section'>Description:</h5>
402    * <p>
403    * The serializer to use for serializing POJOs in request bodies.
404    */
405   public static final String RESTCLIENT_serializer = PREFIX + "serializer.o";
406
407   private static final Set<String> NO_BODY_METHODS = Collections.unmodifiableSet(ASet.<String>create("GET","HEAD","DELETE","CONNECT","OPTIONS","TRACE"));
408
409   private static final ConcurrentHashMap<Class,HttpPartSerializer> partSerializerCache = new ConcurrentHashMap<>();
410
411   private final Map<String,String> headers, query;
412   private final HttpClientBuilder httpClientBuilder;
413   private final CloseableHttpClient httpClient;
414   private final boolean keepHttpClientOpen, debug;
415   private final UrlEncodingSerializer urlEncodingSerializer;  // Used for form posts only.
416   private final HttpPartSerializer partSerializer;
417   private final HttpPartParser partParser;
418   private final String rootUrl;
419   private volatile boolean isClosed = false;
420   private final StackTraceElement[] creationStack;
421   private StackTraceElement[] closedStack;
422
423   // These are read directly by RestCall.
424   final Serializer serializer;
425   final Parser parser;
426   final RetryOn retryOn;
427   final int retries;
428   final long retryInterval;
429   final RestCallInterceptor[] interceptors;
430
431   // This is lazy-created.
432   private volatile ExecutorService executorService;
433   private final boolean executorServiceShutdownOnClose;
434
435   /**
436    * Instantiates a new clean-slate {@link RestClientBuilder} object.
437    *
438    * @return A new {@link RestClientBuilder} object.
439    */
440   public static RestClientBuilder create() {
441      return new RestClientBuilder(PropertyStore.DEFAULT, null);
442   }
443
444   /**
445    * Instantiates a new {@link RestClientBuilder} object using the specified serializer and parser.
446    *
447    * <p>
448    * Shortcut for calling <code>RestClient.<jsm>create</jsm>().serializer(s).parser(p);</code>
449    *
450    * @param s The serializer to use for output.
451    * @param p The parser to use for input.
452    * @return A new {@link RestClientBuilder} object.
453    */
454   public static RestClientBuilder create(Serializer s, Parser p) {
455      return create().serializer(s).parser(p);
456   }
457
458   /**
459    * Instantiates a new {@link RestClientBuilder} object using the specified serializer and parser.
460    *
461    * <p>
462    * Shortcut for calling <code>RestClient.<jsm>create</jsm>().serializer(s).parser(p);</code>
463    *
464    * @param s The serializer class to use for output.
465    * @param p The parser class to use for input.
466    * @return A new {@link RestClientBuilder} object.
467    */
468   public static RestClientBuilder create(Class<? extends Serializer> s, Class<? extends Parser> p) {
469      return create().serializer(s).parser(p);
470   }
471
472   @Override /* Context */
473   public RestClientBuilder builder() {
474      return new RestClientBuilder(getPropertyStore(), httpClientBuilder);
475   }
476
477   /**
478    * Constructor.
479    *
480    * @param ps
481    *    Configuration properties for this client.
482    *    <br>Can be <jk>null</jk>.
483    * @param httpClientBuilder
484    *    The HTTP client builder to use to create the HTTP client.
485    *    <br>Can be <jk>null</jk>.
486    * @param httpClient
487    *    The HTTP client.
488    *    <br>Must not be <jk>null</jk>.
489    */
490   @SuppressWarnings("unchecked")
491   protected RestClient(
492         PropertyStore ps,
493         HttpClientBuilder httpClientBuilder,
494         CloseableHttpClient httpClient) {
495      super(ps);
496      if (ps == null)
497         ps = PropertyStore.DEFAULT;
498      this.httpClientBuilder = httpClientBuilder;
499      this.httpClient = httpClient;
500      this.keepHttpClientOpen = getBooleanProperty(RESTCLIENT_keepHttpClientOpen, false);
501      this.headers = getMapProperty(RESTCLIENT_headers, String.class);
502      this.query = getMapProperty(RESTCLIENT_query, String.class);
503      this.retries = getIntegerProperty(RESTCLIENT_retries, 1);
504      this.retryInterval = getIntegerProperty(RESTCLIENT_retryInterval, -1);
505      this.retryOn = getInstanceProperty(RESTCLIENT_retryOn, RetryOn.class, RetryOn.DEFAULT);
506      this.debug = getBooleanProperty(RESTCLIENT_debug, false);
507      this.executorServiceShutdownOnClose = getBooleanProperty(RESTCLIENT_executorServiceShutdownOnClose, false);
508      this.rootUrl = StringUtils.nullIfEmpty(getStringProperty(RESTCLIENT_rootUri, "").replaceAll("\\/$", ""));
509
510      Object o = getProperty(RESTCLIENT_serializer, Object.class, null);
511      if (o instanceof Serializer) {
512         this.serializer = ((Serializer)o).builder().apply(ps).build();
513      } else if (o instanceof Class) {
514         this.serializer = ContextCache.INSTANCE.create((Class<? extends Serializer>)o, ps);
515      } else {
516         this.serializer = null;
517      }
518
519      o = getProperty(RESTCLIENT_parser, Object.class, null);
520      if (o instanceof Parser) {
521         this.parser = ((Parser)o).builder().apply(ps).build();
522      } else if (o instanceof Class) {
523         this.parser = ContextCache.INSTANCE.create((Class<? extends Parser>)o, ps);
524      } else {
525         this.parser = null;
526      }
527
528      this.urlEncodingSerializer = new SerializerBuilder(ps).build(UrlEncodingSerializer.class);
529      this.partSerializer = getInstanceProperty(RESTCLIENT_partSerializer, HttpPartSerializer.class, OpenApiSerializer.class, ResourceResolver.FUZZY, ps);
530      this.partParser = getInstanceProperty(RESTCLIENT_partParser, HttpPartParser.class, OpenApiParser.class, ResourceResolver.FUZZY, ps);
531      this.executorService = getInstanceProperty(RESTCLIENT_executorService, ExecutorService.class, null);
532
533      RestCallInterceptor[] rci = getInstanceArrayProperty(RESTCLIENT_interceptors, RestCallInterceptor.class, new RestCallInterceptor[0]);
534      if (debug)
535         rci = ArrayUtils.append(rci, RestCallLogger.DEFAULT);
536      this.interceptors = rci;
537
538      if (Boolean.getBoolean("org.apache.juneau.rest.client.RestClient.trackLifecycle"))
539         creationStack = Thread.currentThread().getStackTrace();
540      else
541         creationStack = null;
542   }
543
544   /**
545    * Returns <jk>true</jk> if specified http method has content.
546    * <p>
547    * By default, anything not in this list can have content:  <c>GET, HEAD, DELETE, CONNECT, OPTIONS, TRACE</c>.
548    *
549    * @param httpMethod The HTTP method.  Must be upper-case.
550    * @return <jk>true</jk> if specified http method has content.
551    */
552   protected boolean hasContent(String httpMethod) {
553      return ! NO_BODY_METHODS.contains(httpMethod);
554   }
555
556   /**
557    * Calls {@link CloseableHttpClient#close()} on the underlying {@link CloseableHttpClient}.
558    *
559    * <p>
560    * It's good practice to call this method after the client is no longer used.
561    *
562    * @throws IOException Thrown by underlying stream.
563    */
564   @Override
565   public void close() throws IOException {
566      isClosed = true;
567      if (httpClient != null && ! keepHttpClientOpen)
568         httpClient.close();
569      if (executorService != null && executorServiceShutdownOnClose)
570         executorService.shutdown();
571      if (creationStack != null)
572         closedStack = Thread.currentThread().getStackTrace();
573   }
574
575   /**
576    * Same as {@link #close()}, but ignores any exceptions.
577    */
578   public void closeQuietly() {
579      isClosed = true;
580      try {
581         if (httpClient != null && ! keepHttpClientOpen)
582            httpClient.close();
583         if (executorService != null && executorServiceShutdownOnClose)
584            executorService.shutdown();
585      } catch (Throwable t) {}
586      if (creationStack != null)
587         closedStack = Thread.currentThread().getStackTrace();
588   }
589
590   /**
591    * Execute the specified request.
592    *
593    * <p>
594    * Subclasses can override this method to provide specialized handling.
595    *
596    * @param req The HTTP request.
597    * @return The HTTP response.
598    * @throws IOException Stream exception occurred.
599    * @throws ClientProtocolException ignals an error in the HTTP protocol.
600    */
601   protected HttpResponse execute(HttpUriRequest req) throws ClientProtocolException, IOException {
602      return httpClient.execute(req);
603   }
604
605   /**
606    * Perform a <c>GET</c> request against the specified URL.
607    *
608    * @param url
609    *    The URL of the remote REST resource.
610    *    Can be any of the following:  {@link String}, {@link URI}, {@link URL}.
611    * @return
612    *    A {@link RestCall} object that can be further tailored before executing the request and getting the response
613    *    as a parsed object.
614    * @throws RestCallException If any authentication errors occurred.
615    */
616   public RestCall doGet(Object url) throws RestCallException {
617      return doCall("GET", url, false);
618   }
619
620   /**
621    * Perform a <c>PUT</c> request against the specified URL.
622    *
623    * @param url
624    *    The URL of the remote REST resource.
625    *    Can be any of the following:  {@link String}, {@link URI}, {@link URL}.
626    * @param o
627    *    The object to serialize and transmit to the URL as the body of the request.
628    *    Can be of the following types:
629    *    <ul class='spaced-list'>
630    *       <li>
631    *          {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource.
632    *       <li>
633    *          {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource.
634    *       <li>
635    *          {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the
636    *          {@link RestClient}.
637    *       <li>
638    *          {@link HttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient.
639    *    </ul>
640    * @return
641    *    A {@link RestCall} object that can be further tailored before executing the request
642    *    and getting the response as a parsed object.
643    * @throws RestCallException If any authentication errors occurred.
644    */
645   public RestCall doPut(Object url, Object o) throws RestCallException {
646      return doCall("PUT", url, true).body(o);
647   }
648
649   /**
650    * Same as {@link #doPut(Object, Object)} but don't specify the input yet.
651    *
652    * <p>
653    * You must call either {@link RestCall#body(Object)} or {@link RestCall#formData(String, Object)}
654    * to set the contents on the result object.
655    *
656    * @param url
657    *    The URL of the remote REST resource.
658    *    Can be any of the following:  {@link String}, {@link URI}, {@link URL}.
659    * @return
660    *    A {@link RestCall} object that can be further tailored before executing the request and getting the response
661    *    as a parsed object.
662    * @throws RestCallException REST call failed.
663    */
664   public RestCall doPut(Object url) throws RestCallException {
665      return doCall("PUT", url, true);
666   }
667
668   /**
669    * Perform a <c>POST</c> request against the specified URL.
670    *
671    * <ul class='notes'>
672    *    <li>Use {@link #doFormPost(Object, Object)} for <c>application/x-www-form-urlencoded</c> form posts.
673    * </ul>
674    *
675    * @param url
676    *    The URL of the remote REST resource.
677    *    Can be any of the following:  {@link String}, {@link URI}, {@link URL}.
678    * @param o
679    *    The object to serialize and transmit to the URL as the body of the request.
680    *    Can be of the following types:
681    *    <ul class='spaced-list'>
682    *       <li>
683    *          {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource.
684    *       <li>
685    *          {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource.
686    *       <li>
687    *          {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the {@link RestClient}.
688    *       <li>
689    *          {@link HttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient.
690    *    </ul>
691    * @return
692    *    A {@link RestCall} object that can be further tailored before executing the request and getting the response
693    *    as a parsed object.
694    * @throws RestCallException If any authentication errors occurred.
695    */
696   public RestCall doPost(Object url, Object o) throws RestCallException {
697      return doCall("POST", url, true).body(o);
698   }
699
700   /**
701    * Same as {@link #doPost(Object, Object)} but don't specify the input yet.
702    *
703    * <p>
704    * You must call either {@link RestCall#body(Object)} or {@link RestCall#formData(String, Object)} to set the
705    * contents on the result object.
706    *
707    * <ul class='notes'>
708    *    <li>Use {@link #doFormPost(Object, Object)} for <c>application/x-www-form-urlencoded</c> form posts.
709    * </ul>
710    *
711    * @param url
712    *    The URL of the remote REST resource.
713    *    Can be any of the following:  {@link String}, {@link URI}, {@link URL}.
714    * @return
715    *    A {@link RestCall} object that can be further tailored before executing the request and getting the response
716    *    as a parsed object.
717    * @throws RestCallException REST call failed.
718    */
719   public RestCall doPost(Object url) throws RestCallException {
720      return doCall("POST", url, true);
721   }
722
723   /**
724    * Perform a <c>DELETE</c> request against the specified URL.
725    *
726    * @param url
727    *    The URL of the remote REST resource.
728    *    Can be any of the following:  {@link String}, {@link URI}, {@link URL}.
729    * @return
730    *    A {@link RestCall} object that can be further tailored before executing the request and getting the response
731    *    as a parsed object.
732    * @throws RestCallException If any authentication errors occurred.
733    */
734   public RestCall doDelete(Object url) throws RestCallException {
735      return doCall("DELETE", url, false);
736   }
737
738   /**
739    * Perform an <c>OPTIONS</c> request against the specified URL.
740    *
741    * @param url
742    *    The URL of the remote REST resource.
743    *    Can be any of the following:  {@link String}, {@link URI}, {@link URL}.
744    * @return
745    *    A {@link RestCall} object that can be further tailored before executing the request and getting the response
746    *    as a parsed object.
747    * @throws RestCallException If any authentication errors occurred.
748    */
749   public RestCall doOptions(Object url) throws RestCallException {
750      return doCall("OPTIONS", url, true);
751   }
752
753   /**
754    * Perform a <c>POST</c> request with a content type of <c>application/x-www-form-urlencoded</c>
755    * against the specified URL.
756    *
757    * @param url
758    *    The URL of the remote REST resource.
759    *    Can be any of the following:  {@link String}, {@link URI}, {@link URL}.
760    * @param o
761    *    The object to serialize and transmit to the URL as the body of the request, serialized as a form post
762    *    using the {@link UrlEncodingSerializer#DEFAULT} serializer.
763    * @return
764    *    A {@link RestCall} object that can be further tailored before executing the request and getting the response
765    *    as a parsed object.
766    * @throws RestCallException If any authentication errors occurred.
767    */
768   public RestCall doFormPost(Object url, Object o) throws RestCallException {
769      return doCall("POST", url, true)
770         .body(o instanceof HttpEntity ? o : new RestRequestEntity(o, urlEncodingSerializer, null));
771   }
772
773   /**
774    * Perform a <c>PATCH</c> request against the specified URL.
775    *
776    * @param url
777    *    The URL of the remote REST resource.
778    *    Can be any of the following:  {@link String}, {@link URI}, {@link URL}.
779    * @param o
780    *    The object to serialize and transmit to the URL as the body of the request.
781    *    Can be of the following types:
782    *    <ul class='spaced-list'>
783    *       <li>
784    *          {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource.
785    *       <li>
786    *          {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource.
787    *       <li>
788    *          {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the {@link RestClient}.
789    *       <li>
790    *          {@link HttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient.
791    *    </ul>
792    * @return
793    *    A {@link RestCall} object that can be further tailored before executing the request and getting the response
794    *    as a parsed object.
795    * @throws RestCallException If any authentication errors occurred.
796    */
797   public RestCall doPatch(Object url, Object o) throws RestCallException {
798      return doCall("PATCH", url, true).body(o);
799   }
800
801   /**
802    * Same as {@link #doPatch(Object, Object)} but don't specify the input yet.
803    *
804    * <p>
805    * You must call either {@link RestCall#body(Object)} or {@link RestCall#formData(String, Object)} to set the
806    * contents on the result object.
807    *
808    * <ul class='notes'>
809    *    <li>Use {@link #doFormPost(Object, Object)} for <c>application/x-www-form-urlencoded</c> form posts.
810    * </ul>
811    *
812    * @param url
813    *    The URL of the remote REST resource.
814    *    Can be any of the following:  {@link String}, {@link URI}, {@link URL}.
815    * @return
816    *    A {@link RestCall} object that can be further tailored before executing the request and getting the response
817    *    as a parsed object.
818    * @throws RestCallException REST call failed.
819    */
820   public RestCall doPatch(Object url) throws RestCallException {
821      return doCall("PATCH", url, true);
822   }
823
824
825   /**
826    * Performs a REST call where the entire call is specified in a simple string.
827    *
828    * <p>
829    * This method is useful for performing callbacks when the target of a callback is passed in
830    * on an initial request, for example to signal when a long-running process has completed.
831    *
832    * <p>
833    * The call string can be any of the following formats:
834    * <ul class='spaced-list'>
835    *    <li>
836    *       <js>"[method] [url]"</js> - e.g. <js>"GET http://localhost/callback"</js>
837    *    <li>
838    *       <js>"[method] [url] [payload]"</js> - e.g. <js>"POST http://localhost/callback some text payload"</js>
839    *    <li>
840    *       <js>"[method] [headers] [url] [payload]"</js> - e.g. <js>"POST {'Content-Type':'text/json'} http://localhost/callback {'some':'json'}"</js>
841    * </ul>
842    * <p>
843    * The payload will always be sent using a simple {@link StringEntity}.
844    *
845    * @param callString The call string.
846    * @return
847    *    A {@link RestCall} object that can be further tailored before executing the request and getting the response
848    *    as a parsed object.
849    * @throws RestCallException REST call failed.
850    */
851   public RestCall doCallback(String callString) throws RestCallException {
852      String s = callString;
853      try {
854         RestCall rc = null;
855         String method = null, uri = null, content = null;
856         ObjectMap h = null;
857         int i = s.indexOf(' ');
858         if (i != -1) {
859            method = s.substring(0, i).trim();
860            s = s.substring(i).trim();
861            if (s.length() > 0) {
862               if (s.charAt(0) == '{') {
863                  i = s.indexOf('}');
864                  if (i != -1) {
865                     String json = s.substring(0, i+1);
866                     h = JsonParser.DEFAULT.parse(json, ObjectMap.class);
867                     s = s.substring(i+1).trim();
868                  }
869               }
870               if (s.length() > 0) {
871                  i = s.indexOf(' ');
872                  if (i == -1)
873                     uri = s;
874                  else {
875                     uri = s.substring(0, i).trim();
876                     s = s.substring(i).trim();
877                     if (s.length() > 0)
878                        content = s;
879                  }
880               }
881            }
882         }
883         if (method != null && uri != null) {
884            rc = doCall(method, uri, content != null);
885            if (content != null)
886               rc.body(new StringEntity(content));
887            if (h != null)
888               for (Map.Entry<String,Object> e : h.entrySet())
889                  rc.header(e.getKey(), e.getValue());
890            return rc;
891         }
892      } catch (Exception e) {
893         throw new RestCallException(e);
894      }
895      throw new RestCallException("Invalid format for call string.");
896   }
897
898   /**
899    * Perform a generic REST call.
900    *
901    * @param method The HTTP method.
902    * @param url
903    *    The URL of the remote REST resource.
904    *    Can be any of the following:  {@link String}, {@link URI}, {@link URL}.
905    * @param content
906    *    The HTTP body content.
907    *    Can be of the following types:
908    *    <ul class='spaced-list'>
909    *       <li>
910    *          {@link Reader} - Raw contents of {@code Reader} will be serialized to remote resource.
911    *       <li>
912    *          {@link InputStream} - Raw contents of {@code InputStream} will be serialized to remote resource.
913    *       <li>
914    *          {@link Object} - POJO to be converted to text using the {@link Serializer} registered with the
915    *          {@link RestClient}.
916    *       <li>
917    *          {@link HttpEntity} - Bypass Juneau serialization and pass HttpEntity directly to HttpClient.
918    *       <li>
919    *          {@link NameValuePairs} - Converted to a URL-encoded FORM post.
920    *    </ul>
921    *    This parameter is IGNORED if {@link HttpMethod#hasContent()} is <jk>false</jk>.
922    * @return
923    *    A {@link RestCall} object that can be further tailored before executing the request and getting the response
924    *    as a parsed object.
925    * @throws RestCallException If any authentication errors occurred.
926    */
927   public RestCall doCall(HttpMethod method, Object url, Object content) throws RestCallException {
928      RestCall rc = doCall(method.name(), url, method.hasContent());
929      if (method.hasContent())
930         rc.body(content);
931      return rc;
932   }
933
934   /**
935    * Perform a generic REST call.
936    *
937    * @param method The method name (e.g. <js>"GET"</js>, <js>"OPTIONS"</js>).
938    * @param url
939    *    The URL of the remote REST resource.
940    *    Can be any of the following:  {@link String}, {@link URI}, {@link URL}.
941    * @param hasContent Boolean flag indicating if the specified request has content associated with it.
942    * @return
943    *    A {@link RestCall} object that can be further tailored before executing the request and getting the response
944    *    as a parsed object.
945    * @throws RestCallException If any authentication errors occurred.
946    */
947   public RestCall doCall(String method, Object url, boolean hasContent) throws RestCallException {
948      if (isClosed) {
949         Exception e2 = null;
950         if (closedStack != null) {
951            e2 = new Exception("Creation stack:");
952            e2.setStackTrace(closedStack);
953            throw new RestCallException(e2, "RestClient.close() has already been called.  This client cannot be reused.");
954         }
955         throw new RestCallException("RestClient.close() has already been called.  This client cannot be reused.  Closed location stack trace can be displayed by setting the system property 'org.apache.juneau.rest.client.RestClient.trackCreation' to true.");
956      }
957
958      HttpRequestBase req = null;
959      RestCall restCall = null;
960      final String methodUC = method.toUpperCase(Locale.ENGLISH);
961      try {
962         if (hasContent) {
963            req = new HttpEntityEnclosingRequestBase() {
964               @Override /* HttpRequest */
965               public String getMethod() {
966                  return methodUC;
967               }
968            };
969            restCall = new RestCall(this, req, toURI(url));
970         } else {
971            req = new HttpRequestBase() {
972               @Override /* HttpRequest */
973               public String getMethod() {
974                  return methodUC;
975               }
976            };
977            restCall = new RestCall(this, req, toURI(url));
978         }
979      } catch (URISyntaxException e1) {
980         throw new RestCallException(e1);
981      }
982
983      for (Map.Entry<String,String> e : query.entrySet())
984         restCall.query(e.getKey(), e.getValue());
985
986      for (Map.Entry<String,String> e : headers.entrySet())
987         restCall.header(e.getKey(), e.getValue());
988
989      if (parser != null && ! req.containsHeader("Accept"))
990         req.setHeader("Accept", parser.getPrimaryMediaType().toString());
991
992      return restCall;
993   }
994
995   /**
996    * Create a new proxy interface against a 3rd-party REST interface.
997    *
998    * <p>
999    * The URL to the REST interface is based on the following values:
1000    * <ul>
1001    *    <li>The {@link RemoteResource#path() @RemoteResource(path)} annotation on the interface (<c>remote-path</c>).
1002    *    <li>The {@link RestClientBuilder#rootUrl(Object) rootUrl} on the client (<c>root-url</c>).
1003    *    <li>The fully-qualified class name of the interface (<c>class-name</c>).
1004    * </ul>
1005    *
1006    * <p>
1007    * The URL calculation is as follows:
1008    * <ul>
1009    *    <li><c>remote-path</c> - If remote path is absolute.
1010    *    <li><c>root-url/remote-path</c> - If remote path is relative and root-url has been specified.
1011    *    <li><c>root-url/class-name</c> - If remote path is not specified.
1012    * </ul>
1013    *
1014    * <p>
1015    * If the information is not available to resolve to an absolute URL, a {@link RemoteMetadataException} is thrown.
1016    *
1017    * <p>
1018    * Examples:
1019    * <p class='bcode w800'>
1020    *    <jk>package</jk> org.apache.foo;
1021    *
1022    *    <ja>@RemoteResource</ja>(path=<js>"http://hostname/resturl/myinterface1"</js>)
1023    *    <jk>public interface</jk> MyInterface1 { ... }
1024    *
1025    *    <ja>@RemoteResource</ja>(path=<js>"/myinterface2"</js>)
1026    *    <jk>public interface</jk> MyInterface2 { ... }
1027    *
1028    *    <jk>public interface</jk> MyInterface3 { ... }
1029    *
1030    *    <jc>// Resolves to "http://localhost/resturl/myinterface1"</jc>
1031    *    MyInterface1 i1 = RestClient
1032    *       .<jsm>create</jsm>()
1033    *       .build()
1034    *       .getRemoteResource(MyInterface1.<jk>class</jk>);
1035    *
1036    *    <jc>// Resolves to "http://hostname/resturl/myinterface2"</jc>
1037    *    MyInterface2 i2 = RestClient
1038    *       .<jsm>create</jsm>()
1039    *       .rootUrl(<js>"http://hostname/resturl"</js>)
1040    *       .build()
1041    *       .getRemoteResource(MyInterface2.<jk>class</jk>);
1042    *
1043    *    <jc>// Resolves to "http://hostname/resturl/org.apache.foo.MyInterface3"</jc>
1044    *    MyInterface3 i3 = RestClient
1045    *       .<jsm>create</jsm>()
1046    *       .rootUrl(<js>"http://hostname/resturl"</js>)
1047    *       .build()
1048    *       .getRemoteResource(MyInterface3.<jk>class</jk>);
1049    * </p>
1050    *
1051    * <ul class='notes'>
1052    *    <li>
1053    *       If you plan on using your proxy in a multi-threaded environment, you'll want to use an underlying
1054    *       pooling client connection manager.
1055    *       The easiest way to do this is to use the {@link RestClientBuilder#pooled()} method.
1056    *       If you don't do this, you may end up seeing "Connection still allocated" exceptions.
1057    * </ul>
1058    *
1059    * @param interfaceClass The interface to create a proxy for.
1060    * @return The new proxy interface.
1061    * @throws RemoteMetadataException If the REST URI cannot be determined based on the information given.
1062    */
1063   public <T> T getRemoteResource(final Class<T> interfaceClass) {
1064      return getRemoteResource(interfaceClass, null);
1065   }
1066
1067   /**
1068    * Same as {@link #getRemoteResource(Class)} except explicitly specifies the URL of the REST interface.
1069    *
1070    * @param interfaceClass The interface to create a proxy for.
1071    * @param restUrl The URL of the REST interface.
1072    * @return The new proxy interface.
1073    */
1074   public <T> T getRemoteResource(final Class<T> interfaceClass, final Object restUrl) {
1075      return getRemoteResource(interfaceClass, restUrl, serializer, parser);
1076   }
1077
1078   /**
1079    * Same as {@link #getRemoteResource(Class, Object)} but allows you to override the serializer and parser used.
1080    *
1081    * @param interfaceClass The interface to create a proxy for.
1082    * @param restUrl The URL of the REST interface.
1083    * @param serializer The serializer used to serialize POJOs to the body of the HTTP request.
1084    * @param parser The parser used to parse POJOs from the body of the HTTP response.
1085    * @return The new proxy interface.
1086    */
1087   @SuppressWarnings({ "unchecked" })
1088   public <T> T getRemoteResource(final Class<T> interfaceClass, Object restUrl, final Serializer serializer, final Parser parser) {
1089
1090      if (restUrl == null)
1091         restUrl = rootUrl;
1092
1093      final String restUrl2 = trimSlashes(emptyIfNull(restUrl));
1094
1095      try {
1096         return (T)Proxy.newProxyInstance(
1097            interfaceClass.getClassLoader(),
1098            new Class[] { interfaceClass },
1099            new InvocationHandler() {
1100
1101               final RemoteResourceMeta rm = new RemoteResourceMeta(interfaceClass);
1102
1103               @Override /* InvocationHandler */
1104               public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
1105                  RemoteMethodMeta rmm = rm.getMethodMeta(method);
1106
1107                  if (rmm == null)
1108                     throw new RuntimeException("Method is not exposed as a remote method.");
1109
1110                  String url = rmm.getFullPath();
1111                  if (url.indexOf("://") == -1)
1112                     url = restUrl2 + '/' + url;
1113                  if (url.indexOf("://") == -1)
1114                     throw new RemoteMetadataException(interfaceClass, "Root URI has not been specified.  Cannot construct absolute path to remote resource.");
1115
1116                  String httpMethod = rmm.getHttpMethod();
1117                  HttpPartSerializer s = getPartSerializer();
1118
1119                  try (RestCall rc = doCall(httpMethod, url, hasContent(httpMethod))) {
1120
1121                     rc.serializer(serializer).parser(parser);
1122
1123                     for (RemoteMethodArg a : rmm.getPathArgs())
1124                        rc.path(a.getName(), args[a.getIndex()], a.getSerializer(s), a.getSchema());
1125
1126                     for (RemoteMethodArg a : rmm.getQueryArgs())
1127                        rc.query(a.getName(), args[a.getIndex()], a.isSkipIfEmpty(), a.getSerializer(s), a.getSchema());
1128
1129                     for (RemoteMethodArg a : rmm.getFormDataArgs())
1130                        rc.formData(a.getName(), args[a.getIndex()], a.isSkipIfEmpty(), a.getSerializer(s), a.getSchema());
1131
1132                     for (RemoteMethodArg a : rmm.getHeaderArgs())
1133                        rc.header(a.getName(), args[a.getIndex()], a.isSkipIfEmpty(), a.getSerializer(s), a.getSchema());
1134
1135                     RemoteMethodArg ba = rmm.getBodyArg();
1136                     if (ba != null)
1137                        rc.requestBodySchema(ba.getSchema()).body(args[ba.getIndex()]);
1138
1139                     if (rmm.getRequestArgs().length > 0) {
1140                        for (RemoteMethodBeanArg rmba : rmm.getRequestArgs()) {
1141                           RequestBeanMeta rbm = rmba.getMeta();
1142                           Object bean = args[rmba.getIndex()];
1143                           if (bean != null) {
1144                              for (RequestBeanPropertyMeta p : rbm.getProperties()) {
1145                                 Object val = p.getGetter().invoke(bean);
1146                                 HttpPartType pt = p.getPartType();
1147                                 HttpPartSerializer ps = p.getSerializer(s);
1148                                 String pn = p.getPartName();
1149                                 HttpPartSchema schema = p.getSchema();
1150                                 boolean sie = schema.isSkipIfEmpty();
1151                                 if (pt == PATH)
1152                                    rc.path(pn, val, p.getSerializer(s), schema);
1153                                 else if (val != null) {
1154                                    if (pt == QUERY)
1155                                       rc.query(pn, val, sie, ps, schema);
1156                                    else if (pt == FORMDATA)
1157                                       rc.formData(pn, val, sie, ps, schema);
1158                                    else if (pt == HEADER)
1159                                       rc.header(pn, val, sie, ps, schema);
1160                                    else if (pt == HttpPartType.BODY)
1161                                       rc.requestBodySchema(schema).body(val);
1162                                 }
1163                              }
1164                           }
1165                        }
1166                     }
1167
1168                     if (rmm.getOtherArgs().length > 0) {
1169                        Object[] otherArgs = new Object[rmm.getOtherArgs().length];
1170                        int i = 0;
1171                        for (RemoteMethodArg a : rmm.getOtherArgs())
1172                           otherArgs[i++] = args[a.getIndex()];
1173                        rc.body(otherArgs);
1174                     }
1175
1176                     RemoteMethodReturn rmr = rmm.getReturns();
1177                     if (rmr.getReturnValue() == RemoteReturn.NONE) {
1178                        rc.run();
1179                        return null;
1180                     } else if (rmr.getReturnValue() == RemoteReturn.STATUS) {
1181                        rc.ignoreErrors();
1182                        int returnCode = rc.run();
1183                        Class<?> rt = method.getReturnType();
1184                        if (rt == Integer.class || rt == int.class)
1185                           return returnCode;
1186                        if (rt == Boolean.class || rt == boolean.class)
1187                           return returnCode < 400;
1188                        throw new RestCallException("Invalid return type on method annotated with @RemoteMethod(returns=HTTP_STATUS).  Only integer and booleans types are valid.");
1189                     } else if (rmr.getReturnValue() == RemoteReturn.BEAN) {
1190                        return rc.getResponse(rmr.getResponseBeanMeta());
1191                     } else {
1192                        Object v = rc.getResponseBody(rmr.getReturnType());
1193                        if (v == null && method.getReturnType().isPrimitive())
1194                           v = getClassInfo(method.getReturnType()).getPrimitiveDefault();
1195                        return v;
1196                     }
1197
1198                  } catch (RestCallException e) {
1199                     // Try to throw original exception if possible.
1200                     e.throwServerException(interfaceClass.getClassLoader(), rmm.getExceptions());
1201                     throw new RuntimeException(e);
1202                  } catch (Exception e) {
1203                     throw new RuntimeException(e);
1204                  }
1205               }
1206         });
1207      } catch (Exception e) {
1208         throw new RuntimeException(e);
1209      }
1210   }
1211
1212   /**
1213    * Create a new Remote Interface against a {@link RemoteInterface @RemoteInterface}-annotated class.
1214    *
1215    * <p>
1216    * Remote interfaces are interfaces exposed on the server side using either the <c>RrpcServlet</c>
1217    * or <c>RRPC</c> REST methods.
1218    *
1219    * <p>
1220    * The URL to the REST interface is based on the following values:
1221    * <ul>
1222    *    <li>The {@link RemoteResource#path() @RemoteResource(path)} annotation on the interface (<c>remote-path</c>).
1223    *    <li>The {@link RestClientBuilder#rootUrl(Object) rootUrl} on the client (<c>root-url</c>).
1224    *    <li>The fully-qualified class name of the interface (<c>class-name</c>).
1225    * </ul>
1226    *
1227    * <p>
1228    * The URL calculation is as follows:
1229    * <ul>
1230    *    <li><c>remote-path</c> - If remote path is absolute.
1231    *    <li><c>root-url/remote-path</c> - If remote path is relative and root-url has been specified.
1232    *    <li><c>root-url/class-name</c> - If remote path is not specified.
1233    * </ul>
1234    *
1235    * <p>
1236    * If the information is not available to resolve to an absolute URL, a {@link RemoteMetadataException} is thrown.
1237    *
1238    * <ul class='notes'>
1239    *    <li>
1240    *       If you plan on using your proxy in a multi-threaded environment, you'll want to use an underlying
1241    *       pooling client connection manager.
1242    *       The easiest way to do this is to use the {@link RestClientBuilder#pooled()} method.
1243    *       If you don't do this, you may end up seeing "Connection still allocated" exceptions.
1244    * </ul>
1245    *
1246    * @param interfaceClass The interface to create a proxy for.
1247    * @return The new proxy interface.
1248    * @throws RemoteMetadataException If the REST URI cannot be determined based on the information given.
1249    */
1250   public <T> T getRrpcInterface(final Class<T> interfaceClass) {
1251      return getRrpcInterface(interfaceClass, null);
1252   }
1253
1254   /**
1255    * Same as {@link #getRrpcInterface(Class)} except explicitly specifies the URL of the REST interface.
1256    *
1257    * @param interfaceClass The interface to create a proxy for.
1258    * @param restUrl The URL of the REST interface.
1259    * @return The new proxy interface.
1260    */
1261   public <T> T getRrpcInterface(final Class<T> interfaceClass, final Object restUrl) {
1262      return getRrpcInterface(interfaceClass, restUrl, serializer, parser);
1263   }
1264
1265   /**
1266    * Same as {@link #getRrpcInterface(Class, Object)} but allows you to override the serializer and parser used.
1267    *
1268    * @param interfaceClass The interface to create a proxy for.
1269    * @param restUrl The URL of the REST interface.
1270    * @param serializer The serializer used to serialize POJOs to the body of the HTTP request.
1271    * @param parser The parser used to parse POJOs from the body of the HTTP response.
1272    * @return The new proxy interface.
1273    */
1274   @SuppressWarnings({ "unchecked" })
1275   public <T> T getRrpcInterface(final Class<T> interfaceClass, Object restUrl, final Serializer serializer, final Parser parser) {
1276
1277      if (restUrl == null) {
1278         RemoteInterfaceMeta rm = new RemoteInterfaceMeta(interfaceClass, stringify(restUrl));
1279         String path = rm.getPath();
1280         if (path.indexOf("://") == -1) {
1281            if (rootUrl == null)
1282               throw new RemoteMetadataException(interfaceClass, "Root URI has not been specified.  Cannot construct absolute path to remote interface.");
1283            path = trimSlashes(rootUrl) + '/' + path;
1284         }
1285         restUrl = path;
1286      }
1287
1288      final String restUrl2 = stringify(restUrl);
1289
1290      try {
1291         return (T)Proxy.newProxyInstance(
1292            interfaceClass.getClassLoader(),
1293            new Class[] { interfaceClass },
1294            new InvocationHandler() {
1295
1296               final RemoteInterfaceMeta rm = new RemoteInterfaceMeta(interfaceClass, restUrl2);
1297
1298               @Override /* InvocationHandler */
1299               public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
1300                  RemoteInterfaceMethod rim = rm.getMethodMeta(method);
1301
1302                  if (rim == null)
1303                     throw new RuntimeException("Method is not exposed as a remote method.");
1304
1305                  String url = rim.getUrl();
1306
1307                  try (RestCall rc = doCall("POST", url, true)) {
1308
1309                     rc.serializer(serializer).parser(parser).body(args);
1310
1311                     Object v = rc.getResponse(method.getGenericReturnType());
1312                     if (v == null && method.getReturnType().isPrimitive())
1313                        v = getClassInfo(method.getReturnType()).getPrimitiveDefault();
1314                     return v;
1315
1316                  } catch (RestCallException e) {
1317                     // Try to throw original exception if possible.
1318                     e.throwServerException(interfaceClass.getClassLoader(), method.getExceptionTypes());
1319                     throw new RuntimeException(e);
1320                  } catch (Exception e) {
1321                     throw new RuntimeException(e);
1322                  }
1323               }
1324         });
1325      } catch (Exception e) {
1326         throw new RuntimeException(e);
1327      }
1328   }
1329
1330   static final String getName(String name1, String name2, BeanPropertyMeta pMeta) {
1331      String n = name1.isEmpty() ? name2 : name1;
1332      ClassMeta<?> cm = pMeta.getClassMeta();
1333      if (n.isEmpty() && (cm.isMapOrBean() || cm.isReader() || cm.isInstanceOf(NameValuePairs.class)))
1334         n = "*";
1335      if (n.isEmpty())
1336         n = pMeta.getName();
1337      return n;
1338   }
1339
1340   final HttpPartSerializer getPartSerializer(Class c, HttpPartSerializer c2) {
1341      if (c2 != null)
1342         return c2;
1343      if (c == HttpPartSerializer.Null.class)
1344         return null;
1345      HttpPartSerializer pf = partSerializerCache.get(c);
1346      if (pf == null) {
1347         partSerializerCache.putIfAbsent(c, castOrCreate(HttpPartSerializer.class, c, true, getPropertyStore()));
1348         pf = partSerializerCache.get(c);
1349      }
1350      return pf;
1351   }
1352
1353   private Pattern absUrlPattern = Pattern.compile("^\\w+\\:\\/\\/.*");
1354
1355   HttpPartSerializer getPartSerializer() {
1356      return partSerializer;
1357   }
1358
1359   HttpPartParser getPartParser() {
1360      return partParser;
1361   }
1362
1363   URI toURI(Object url) throws URISyntaxException {
1364      if (url instanceof URI)
1365         return (URI)url;
1366      if (url instanceof URL)
1367         ((URL)url).toURI();
1368      if (url instanceof URIBuilder)
1369         return ((URIBuilder)url).build();
1370      String s = url == null ? "" : url.toString();
1371      if (rootUrl != null && ! absUrlPattern.matcher(s).matches()) {
1372         if (s.isEmpty())
1373            s = rootUrl;
1374         else {
1375            StringBuilder sb = new StringBuilder(rootUrl);
1376            if (! s.startsWith("/"))
1377               sb.append('/');
1378            sb.append(s);
1379            s = sb.toString();
1380         }
1381      }
1382      if (s.indexOf('{') != -1)
1383         s = s.replace("{", "%7B").replace("}", "%7D");
1384      return new URI(s);
1385   }
1386
1387   ExecutorService getExecutorService(boolean create) {
1388      if (executorService != null || ! create)
1389         return executorService;
1390      synchronized(this) {
1391         if (executorService == null)
1392            executorService = new ThreadPoolExecutor(1, 1, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10));
1393         return executorService;
1394      }
1395   }
1396
1397   @Override
1398   protected void finalize() throws Throwable {
1399      if (! isClosed && ! keepHttpClientOpen) {
1400         System.err.println("WARNING:  RestClient garbage collected before it was finalized.");  // NOT DEBUG
1401         if (creationStack != null) {
1402            System.err.println("Creation Stack:");  // NOT DEBUG
1403            for (StackTraceElement e : creationStack)
1404               System.err.println(e);  // NOT DEBUG
1405         } else {
1406            System.err.println("Creation stack traces can be displayed by setting the system property 'org.apache.juneau.rest.client.RestClient.trackLifecycle' to true.");  // NOT DEBUG
1407         }
1408      }
1409   }
1410
1411   //-----------------------------------------------------------------------------------------------------------------
1412   // Other methods.
1413   //-----------------------------------------------------------------------------------------------------------------
1414
1415   @Override /* Context */
1416   public ObjectMap toMap() {
1417      return super.toMap()
1418         .append("RestClient", new DefaultFilteringObjectMap()
1419            .append("debug", debug)
1420            .append("executorService", executorService)
1421            .append("executorServiceShutdownOnClose", executorServiceShutdownOnClose)
1422            .append("headers", headers)
1423            .append("interceptors", interceptors)
1424            .append("keepHttpClientOpen", keepHttpClientOpen)
1425            .append("parser", parser)
1426            .append("partParser", partParser)
1427            .append("partSerializer", partSerializer)
1428            .append("query", query)
1429            .append("retries", retries)
1430            .append("retryInterval", retryInterval)
1431            .append("retryOn", retryOn)
1432            .append("rootUri", rootUrl)
1433            .append("serializer", serializer)
1434         );
1435   }
1436}