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