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