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