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;
014
015import static org.apache.juneau.rest.Enablement.*;
016import static org.apache.juneau.internal.ClassUtils.*;
017import static org.apache.juneau.internal.ObjectUtils.*;
018import static org.apache.juneau.internal.StringUtils.*;
019import static org.apache.juneau.internal.StringUtils.firstNonEmpty;
020import static org.apache.juneau.httppart.HttpPartType.*;
021import static org.apache.juneau.rest.RestContext.*;
022import static org.apache.juneau.rest.util.RestUtils.*;
023import static org.apache.juneau.rest.HttpRuntimeException.*;
024
025import java.lang.annotation.*;
026import java.lang.reflect.*;
027import java.util.*;
028import java.util.concurrent.*;
029import java.util.concurrent.atomic.*;
030
031import javax.servlet.*;
032import javax.servlet.http.*;
033
034import org.apache.juneau.*;
035import org.apache.juneau.annotation.*;
036import org.apache.juneau.collections.*;
037import org.apache.juneau.encoders.*;
038import org.apache.juneau.http.*;
039import org.apache.juneau.http.annotation.*;
040import org.apache.juneau.httppart.*;
041import org.apache.juneau.httppart.bean.*;
042import org.apache.juneau.internal.*;
043import org.apache.juneau.internal.HttpUtils;
044import org.apache.juneau.jsonschema.*;
045import org.apache.juneau.parser.*;
046import org.apache.juneau.reflect.*;
047import org.apache.juneau.rest.annotation.*;
048import org.apache.juneau.rest.annotation.Method;
049import org.apache.juneau.http.exception.*;
050import org.apache.juneau.http.remote.*;
051import org.apache.juneau.rest.guards.*;
052import org.apache.juneau.rest.util.*;
053import org.apache.juneau.rest.widget.*;
054import org.apache.juneau.serializer.*;
055import org.apache.juneau.svl.*;
056import org.apache.juneau.utils.*;
057
058/**
059 * Represents a single Java servlet/resource method annotated with {@link RestMethod @RestMethod}.
060 */
061@ConfigurableContext(nocache=true)
062public class RestMethodContext extends BeanContext implements Comparable<RestMethodContext>  {
063
064   //-------------------------------------------------------------------------------------------------------------------
065   // Configurable properties
066   //-------------------------------------------------------------------------------------------------------------------
067
068   static final String PREFIX = "RestMethodContext";
069
070   /**
071    * Configuration property:  Default request attributes.
072    *
073    * <div class='warn'>
074    *    <b>Deprecated</b> - Use {@link #RESTMETHOD_reqAttrs}
075    * </div>
076    */
077   @Deprecated
078   public static final String RESTMETHOD_attrs = PREFIX + ".reqAttrs.smo";
079
080   /**
081    * Configuration property:  Client version pattern matcher.
082    *
083    * <h5 class='section'>Property:</h5>
084    * <ul class='spaced-list'>
085    *    <li><b>ID:</b>  {@link org.apache.juneau.rest.RestMethodContext#RESTMETHOD_clientVersion RESTMETHOD_clientVersion}
086    *    <li><b>Name:</b>  <js>"RestMethodContext.clientVersion.s"</js>
087    *    <li><b>Data type:</b>  <c>String</c>
088    *    <li><b>System property:</b>  <c>RestMethodContext.clientVersion</c>
089    *    <li><b>Environment variable:</b>  <c>RESTMETHODCONTEXT_CLIENTVERSION</c>
090    *    <li><b>Default:</b>  empty string
091    *    <li><b>Session property:</b>  <jk>false</jk>
092    *    <li><b>Annotations:</b>
093    *       <ul>
094    *          <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#clientVersion()}
095    *       </ul>
096    * </ul>
097    *
098    * <h5 class='section'>Description:</h5>
099    * Specifies whether this method can be called based on the client version.
100    *
101    * <p>
102    * The client version is identified via the HTTP request header identified by
103    * {@link Rest#clientVersionHeader() @Rest(clientVersionHeader)} which by default is <js>"X-Client-Version"</js>.
104    *
105    * <p>
106    * This is a specialized kind of {@link RestMatcher} that allows you to invoke different Java methods for the same
107    * method/path based on the client version.
108    *
109    * <p>
110    * The format of the client version range is similar to that of OSGi versions.
111    *
112    * <p>
113    * In the following example, the Java methods are mapped to the same HTTP method and URL <js>"/foobar"</js>.
114    * <p class='bcode w800'>
115    *    <jc>// Call this method if X-Client-Version is at least 2.0.
116    *    // Note that this also matches 2.0.1.</jc>
117    *    <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/foobar"</js>, clientVersion=<js>"2.0"</js>)
118    *    <jk>public</jk> Object method1()  {...}
119    *
120    *    <jc>// Call this method if X-Client-Version is at least 1.1, but less than 2.0.</jc>
121    *    <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/foobar"</js>, clientVersion=<js>"[1.1,2.0)"</js>)
122    *    <jk>public</jk> Object method2()  {...}
123    *
124    *    <jc>// Call this method if X-Client-Version is less than 1.1.</jc>
125    *    <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/foobar"</js>, clientVersion=<js>"[0,1.1)"</js>)
126    *    <jk>public</jk> Object method3()  {...}
127    * </p>
128    *
129    * <p>
130    * It's common to combine the client version with transforms that will convert new POJOs into older POJOs for
131    * backwards compatibility.
132    * <p class='bcode w800'>
133    *    <jc>// Call this method if X-Client-Version is at least 2.0.</jc>
134    *    <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/foobar"</js>, clientVersion=<js>"2.0"</js>)
135    *    <jk>public</jk> NewPojo newMethod()  {...}
136    *
137    *    <jc>// Call this method if X-Client-Version is at least 1.1, but less than 2.0.</jc>
138    *    <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/foobar"</js>, clientVersion=<js>"[1.1,2.0)"</js>, transforms={NewToOldPojoSwap.<jk>class</jk>})
139    *    <jk>public</jk> NewPojo oldMethod() {
140    *       <jk>return</jk> newMethod();
141    *    }
142    *
143    * <p>
144    * Note that in the previous example, we're returning the exact same POJO, but using a transform to convert it into
145    * an older form.
146    * The old method could also just return back a completely different object.
147    * The range can be any of the following:
148    * <ul>
149    *    <li><js>"[0,1.0)"</js> = Less than 1.0.  1.0 and 1.0.0 does not match.
150    *    <li><js>"[0,1.0]"</js> = Less than or equal to 1.0.  Note that 1.0.1 will match.
151    *    <li><js>"1.0"</js> = At least 1.0.  1.0 and 2.0 will match.
152    * </ul>
153    *
154    * <ul class='seealso'>
155    *    <li class='jf'>{@link RestContext#REST_clientVersionHeader}
156    * </ul>
157    */
158   public static final String RESTMETHOD_clientVersion = PREFIX + ".clientVersion.s";
159
160   /**
161    * Configuration property:  Debug mode.
162    *
163    * <h5 class='section'>Property:</h5>
164    * <ul class='spaced-list'>
165    *    <li><b>ID:</b>  {@link org.apache.juneau.rest.RestMethodContext#RESTMETHOD_debug RESTMETHOD_debug}
166    *    <li><b>Name:</b>  <js>"RestMethodContext.debug.s"</js>
167    *    <li><b>Data type:</b>  {@link org.apache.juneau.rest.Enablement}
168    *    <li><b>System property:</b>  <c>RestMethodContext.debug</c>
169    *    <li><b>Environment variable:</b>  <c>RESTMETHODCONTEXT_DEBUG</c>
170    *    <li><b>Default:</b>  {@link org.apache.juneau.rest.Enablement#FALSE}
171    *    <li><b>Session property:</b>  <jk>false</jk>
172    *    <li><b>Annotations:</b>
173    *       <ul>
174    *          <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#debug()}
175    *       </ul>
176    * </ul>
177    *
178    * <h5 class='section'>Description:</h5>
179    * <p>
180    * Enables the following:
181    * <ul class='spaced-list'>
182    *    <li>
183    *       HTTP request/response bodies are cached in memory for logging purposes.
184    * </ul>
185    */
186   public static final String RESTMETHOD_debug = PREFIX + ".debug.s";
187
188   /**
189    * Configuration property:  Default form data.
190    *
191    * <h5 class='section'>Property:</h5>
192    * <ul class='spaced-list'>
193    *    <li><b>ID:</b>  {@link org.apache.juneau.rest.RestMethodContext#RESTMETHOD_defaultFormData RESTMETHOD_defaultFormData}
194    *    <li><b>Name:</b>  <js>"RestMethodContext.defaultFormData.omo"</js>
195    *    <li><b>Data type:</b>  <c>Map&lt;String,Object&gt;</c>
196    *    <li><b>System property:</b>  <c>RestMethodContext.defaultFormData</c>
197    *    <li><b>Environment variable:</b>  <c>RESTMETHODCONTEXT_DEFAULTFORMDATA</c>
198    *    <li><b>Default:</b>  empty map
199    *    <li><b>Session property:</b>  <jk>false</jk>
200    *    <li><b>Annotations:</b>
201    *       <ul>
202    *          <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#defaultFormData()}
203    *       </ul>
204    * </ul>
205    *
206    * <h5 class='section'>Description:</h5>
207    * Specifies default values for form-data parameters.
208    *
209    * <p>
210    * Strings are of the format <js>"name=value"</js>.
211    *
212    * <p>
213    * Affects values returned by {@link RestRequest#getFormData(String)} when the parameter is not present on the
214    * request.
215    *
216    * <h5 class='section'>Example:</h5>
217    * <p class='bcode w800'>
218    *    <ja>@RestMethod</ja>(name=<jsf>POST</jsf>, path=<js>"/*"</js>, defaultFormData={<js>"foo=bar"</js>})
219    *    <jk>public</jk> String doGet(<ja>@FormData</ja>(<js>"foo"</js>) String foo)  {...}
220    * </p>
221    */
222   public static final String RESTMETHOD_defaultFormData = PREFIX + ".defaultFormData.omo";
223
224   /**
225    * Configuration property:  Default query parameters.
226    *
227    * <h5 class='section'>Property:</h5>
228    * <ul class='spaced-list'>
229    *    <li><b>ID:</b>  {@link org.apache.juneau.rest.RestMethodContext#RESTMETHOD_defaultQuery RESTMETHOD_defaultQuery}
230    *    <li><b>Name:</b>  <js>"RestMethodContext.defaultQuery.omo"</js>
231    *    <li><b>Data type:</b>  <c>Map&lt;String,Object&gt;</c>
232    *    <li><b>System property:</b>  <c>RestMethodContext.defaultQuery</c>
233    *    <li><b>Environment variable:</b>  <c>RESTMETHODCONTEXT_DEFAULTQUERY</c>
234    *    <li><b>Default:</b>  empty map
235    *    <li><b>Session property:</b>  <jk>false</jk>
236    *    <li><b>Annotations:</b>
237    *       <ul>
238    *          <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#defaultQuery()}
239    *       </ul>
240    * </ul>
241    *
242    * <h5 class='section'>Description:</h5>
243    * Specifies default values for query parameters.
244    *
245    * <p>
246    * Strings are of the format <js>"name=value"</js>.
247    *
248    * <p>
249    * Affects values returned by {@link RestRequest#getQuery(String)} when the parameter is not present on the request.
250    *
251    * <h5 class='section'>Example:</h5>
252    * <p class='bcode w800'>
253    *    <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/*"</js>, defaultQuery={<js>"foo=bar"</js>})
254    *    <jk>public</jk> String doGet(<ja>@Query</ja>(<js>"foo"</js>) String foo)  {...}
255    * </p>
256    */
257   public static final String RESTMETHOD_defaultQuery = PREFIX + ".defaultQuery.omo";
258
259   /**
260    * Configuration property:  Default request headers.
261    *
262    * <div class='warn'>
263    *    <b>Deprecated</b> - Use {@link #RESTMETHOD_defaultRequestHeaders}
264    * </div>
265    */
266   @Deprecated
267   public static final String RESTMETHOD_defaultRequestHeaders = PREFIX + ".reqHeaders.smo";
268
269   /**
270    * Configuration property:  HTTP method name.
271    *
272    * <h5 class='section'>Property:</h5>
273    * <ul class='spaced-list'>
274    *    <li><b>ID:</b>  {@link org.apache.juneau.rest.RestMethodContext#RESTMETHOD_httpMethod RESTMETHOD_httpMethod}
275    *    <li><b>Name:</b>  <js>"RestMethodContext.httpMethod.s"</js>
276    *    <li><b>Data type:</b>  <c>String</c>
277    *    <li><b>System property:</b>  <c>RestMethodContext.httpMethod</c>
278    *    <li><b>Environment variable:</b>  <c>RESTMETHODCONTEXT_HTTPMETHOD</c>
279    *    <li><b>Default:</b>  <jk>null</jk>
280    *    <li><b>Session property:</b>  <jk>false</jk>
281    *    <li><b>Annotations:</b>
282    *       <ul>
283    *          <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#name()}
284    *          <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#method()}
285    *       </ul>
286    * </ul>
287    *
288    * <h5 class='section'>Description:</h5>
289    * REST method name.
290    *
291    * <p>
292    * Typically <js>"GET"</js>, <js>"PUT"</js>, <js>"POST"</js>, <js>"DELETE"</js>, or <js>"OPTIONS"</js>.
293    *
294    * <p>
295    * Method names are case-insensitive (always folded to upper-case).
296    *
297    * <p>
298    * Note that you can use {@link org.apache.juneau.http.HttpMethod} for constant values.
299    *
300    * <p>
301    * Besides the standard HTTP method names, the following can also be specified:
302    * <ul class='spaced-list'>
303    *    <li>
304    *       <js>"*"</js>
305    *       - Denotes any method.
306    *       <br>Use this if you want to capture any HTTP methods in a single Java method.
307    *       <br>The {@link Method @Method} annotation and/or {@link RestRequest#getMethod()} method can be used to
308    *       distinguish the actual HTTP method name.
309    *    <li>
310    *       <js>""</js>
311    *       - Auto-detect.
312    *       <br>The method name is determined based on the Java method name.
313    *       <br>For example, if the method is <c>doPost(...)</c>, then the method name is automatically detected
314    *       as <js>"POST"</js>.
315    *       <br>Otherwise, defaults to <js>"GET"</js>.
316    *    <li>
317    *       <js>"RRPC"</js>
318    *       - Remote-proxy interface.
319    *       <br>This denotes a Java method that returns an object (usually an interface, often annotated with the
320    *       {@link Remote @Remote} annotation) to be used as a remote proxy using
321    *       <c>RestClient.getRemoteInterface(Class&lt;T&gt; interfaceClass, String url)</c>.
322    *       <br>This allows you to construct client-side interface proxies using REST as a transport medium.
323    *       <br>Conceptually, this is simply a fancy <c>POST</c> against the url <js>"/{path}/{javaMethodName}"</js>
324    *       where the arguments are marshalled from the client to the server as an HTTP body containing an array of
325    *       objects, passed to the method as arguments, and then the resulting object is marshalled back to the client.
326    *    <li>
327    *       Anything else
328    *       - Overloaded non-HTTP-standard names that are passed in through a <c>&amp;method=methodName</c> URL
329    *       parameter.
330    * </ul>
331    */
332   public static final String RESTMETHOD_httpMethod = PREFIX + ".httpMethod.s";
333
334   /**
335    * Configuration property:  Logging rules.
336    *
337    * <h5 class='section'>Property:</h5>
338    * <ul class='spaced-list'>
339    *    <li><b>ID:</b>  {@link org.apache.juneau.rest.RestMethodContext#RESTMETHOD_callLoggerConfig RESTMETHOD_callLoggerConfig}
340    *    <li><b>Name:</b>  <js>"RestContext.logRules.lo"</js>
341    *    <li><b>Data type:</b>  <c>{@link org.apache.juneau.rest.RestCallLoggerConfig}</c>
342    *    <li><b>Default:</b>  <jk>null</jk>
343    *    <li><b>Session property:</b>  <jk>false</jk>
344    *    <li><b>Annotations:</b>
345    *       <ul>
346    *          <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#logging()}
347    *       </ul>
348    * </ul>
349    *
350    * <h5 class='section'>Description:</h5>
351    * <p>
352    * Specifies rules on how to handle logging of HTTP requests/responses.
353    *
354    * <ul class='seealso'>
355    *    <li class='link'>{@doc RestLoggingAndDebugging}
356    * </ul>
357    */
358   public static final String RESTMETHOD_callLoggerConfig = PREFIX + ".callLoggerConfig.o";
359
360   /**
361    * Configuration property:  Method-level matchers.
362    *
363    * <h5 class='section'>Property:</h5>
364    * <ul class='spaced-list'>
365    *    <li><b>ID:</b>  {@link org.apache.juneau.rest.RestMethodContext#RESTMETHOD_matchers RESTMETHOD_matchers}
366    *    <li><b>Name:</b>  <js>"RestMethodContext.matchers.lo"</js>
367    *    <li><b>Data type:</b>  <c>List&lt;{@link org.apache.juneau.rest.RestMatcher}|Class&lt;{@link org.apache.juneau.rest.RestMatcher}&gt;&gt;</c>
368    *    <li><b>Default:</b>  empty list
369    *    <li><b>Session property:</b>  <jk>false</jk>
370    *    <li><b>Annotations:</b>
371    *       <ul>
372    *          <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#matchers()}
373    *       </ul>
374    * </ul>
375    *
376    * <h5 class='section'>Description:</h5>
377    * <p>
378    * Associates one or more {@link RestMatcher RestMatchers} with the specified method.
379    *
380    * <p>
381    * If multiple matchers are specified, <b>ONE</b> matcher must pass.
382    * <br>Note that this is different than guards where <b>ALL</b> guards needs to pass.
383    *
384    * <ul class='notes'>
385    *    <li>
386    *       When defined as a class, the implementation must have one of the following constructors:
387    *       <ul>
388    *          <li><code><jk>public</jk> T(RestContext)</code>
389    *          <li><code><jk>public</jk> T()</code>
390    *          <li><code><jk>public static</jk> T <jsm>create</jsm>(RestContext)</code>
391    *          <li><code><jk>public static</jk> T <jsm>create</jsm>()</code>
392    *       </ul>
393    *    <li>
394    *       Inner classes of the REST resource class are allowed.
395    * </ul>
396    *
397    * <ul class='seealso'>
398    *    <li class='link'>{@doc RestmMatchers}
399    * </ul>
400    */
401   public static final String RESTMETHOD_matchers = PREFIX + ".matchers.lo";
402
403   /**
404    * Configuration property:  Resource method path.
405    *
406    * <h5 class='section'>Property:</h5>
407    * <ul class='spaced-list'>
408    *    <li><b>ID:</b>  {@link org.apache.juneau.rest.RestMethodContext#RESTMETHOD_path RESTMETHOD_path}
409    *    <li><b>Name:</b>  <js>"RestMethodContext.path.s"</js>
410    *    <li><b>Data type:</b>  <c>String</c>
411    *    <li><b>System property:</b>  <c>RestMethodContext.path</c>
412    *    <li><b>Environment variable:</b>  <c>RESTMETHODCONTEXT_PATH</c>
413    *    <li><b>Default:</b>  <jk>null</jk>
414    *    <li><b>Session property:</b>  <jk>false</jk>
415    *    <li><b>Annotations:</b>
416    *       <ul>
417    *          <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#path()}
418    *       </ul>
419    * </ul>
420    *
421    * <h5 class='section'>Description:</h5>
422    * <p>
423    * Identifies the URL subpath relative to the servlet class.
424    *
425    * <p>
426    * <ul class='notes'>
427    *    <li>
428    *       This method is only applicable for Java methods.
429    *    <li>
430    *       Slashes are trimmed from the path ends.
431    *       <br>As a convention, you may want to start your path with <js>'/'</js> simple because it make it easier to read.
432    * </ul>
433    * @deprecated Use {@link #RESTMETHOD_paths}
434    */
435   @Deprecated
436   public static final String RESTMETHOD_path = PREFIX + ".path.s";
437
438   /**
439    * Configuration property:  Resource method paths.
440    *
441    * <h5 class='section'>Property:</h5>
442    * <ul class='spaced-list'>
443    *    <li><b>ID:</b>  {@link org.apache.juneau.rest.RestMethodContext#RESTMETHOD_paths RESTMETHOD_paths}
444    *    <li><b>Name:</b>  <js>"RestMethodContext.path.ls"</js>
445    *    <li><b>Data type:</b>  <c>String[]</c>
446    *    <li><b>System property:</b>  <c>RestMethodContext.paths</c>
447    *    <li><b>Environment variable:</b>  <c>RESTMETHODCONTEXT_PATHS</c>
448    *    <li><b>Default:</b>  <jk>null</jk>
449    *    <li><b>Session property:</b>  <jk>false</jk>
450    *    <li><b>Annotations:</b>
451    *       <ul>
452    *          <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#path()}
453    *          <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#paths()}
454    *       </ul>
455    * </ul>
456    *
457    * <h5 class='section'>Description:</h5>
458    * <p>
459    * Identifies the URL subpath relative to the servlet class.
460    *
461    * <p>
462    * <ul class='notes'>
463    *    <li>
464    *       This method is only applicable for Java methods.
465    *    <li>
466    *       Slashes are trimmed from the path ends.
467    *       <br>As a convention, you may want to start your path with <js>'/'</js> simple because it make it easier to read.
468    * </ul>
469    */
470   public static final String RESTMETHOD_paths = PREFIX + ".paths.ls";
471
472   /**
473    * Configuration property:  Priority.
474    *
475    * <h5 class='section'>Property:</h5>
476    * <ul class='spaced-list'>
477    *    <li><b>ID:</b>  {@link org.apache.juneau.rest.RestMethodContext#RESTMETHOD_priority RESTMETHOD_priority}
478    *    <li><b>Name:</b>  <js>"RestMethodContext.priority.i"</js>
479    *    <li><b>Data type:</b>  <jk>int</jk>
480    *    <li><b>System property:</b>  <c>RestMethodContext.priority</c>
481    *    <li><b>Environment variable:</b>  <c>RESTMETHODCONTEXT_PRIORITY</c>
482    *    <li><b>Default:</b>  <c>0</c>
483    *    <li><b>Session property:</b>  <jk>false</jk>
484    *    <li><b>Annotations:</b>
485    *       <ul>
486    *          <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#priority()}
487    *       </ul>
488    * </ul>
489    *
490    * <h5 class='section'>Description:</h5>
491    * URL path pattern priority.
492    *
493    * <p>
494    * To force path patterns to be checked before other path patterns, use a higher priority number.
495    *
496    * <p>
497    * By default, it's <c>0</c>, which means it will use an internal heuristic to determine a best match.
498    */
499   public static final String RESTMETHOD_priority = PREFIX + ".priority.i";
500
501   /**
502    * Configuration property:  Default request attributes.
503    *
504    * <h5 class='section'>Property:</h5>
505    * <ul class='spaced-list'>
506    *    <li><b>ID:</b>  {@link org.apache.juneau.rest.RestMethodContext#RESTMETHOD_reqAttrs RESTMETHOD_reqAttrs}
507    *    <li><b>Name:</b>  <js>"RestMethodContext.reqAttrs.smo"</js>
508    *    <li><b>Data type:</b>  <c>Map&lt;String,Object&gt;</c>
509    *    <li><b>System property:</b>  <c>RestMethodContext.reqAttrs</c>
510    *    <li><b>Environment variable:</b>  <c>RESTMETHODCONTEXT_REQATTRS</c>
511    *    <li><b>Default:</b>  <jk>null</jk>
512    *    <li><b>Session property:</b>  <jk>false</jk>
513    *    <li><b>Annotations:</b>
514    *       <ul>
515    *          <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#reqAttrs()}
516    *       </ul>
517    * </ul>
518    *
519    * <h5 class='section'>Description:</h5>
520    * Default request attributes.
521    *
522    * <p>
523    * Specifies default values for request attributes if they are not already set on the request.
524    *
525    * <h5 class='section'>Example:</h5>
526    * <p class='bcode w800'>
527    *    <jc>// Assume "text/json" Accept value when Accept not specified</jc>
528    *    <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/*"</js>, reqAttrs={<js>"Foo: bar"</js>})
529    *    <jk>public</jk> String doGet()  {...}
530    * </p>
531    *
532    * <ul class='notes'>
533    *    <li>
534    *       Supports {@doc RestSvlVariables}
535    *       (e.g. <js>"$S{mySystemProperty}"</js>).
536    * </ul>
537    *
538    * <ul class='seealso'>
539    *    <li class='jf'>{@link RestContext#REST_reqAttrs}
540    * </ul>
541    */
542   public static final String RESTMETHOD_reqAttrs = PREFIX + ".reqAttrs.smo";
543
544   /**
545    * Configuration property:  Default request headers.
546    *
547    * <h5 class='section'>Property:</h5>
548    * <ul class='spaced-list'>
549    *    <li><b>ID:</b>  {@link org.apache.juneau.rest.RestMethodContext#RESTMETHOD_reqHeaders RESTMETHOD_reqHeaders}
550    *    <li><b>Name:</b>  <js>"RestMethodContext.reqHeaders.smo"</js>
551    *    <li><b>Data type:</b>  <c>Map&lt;String,Object&gt;</c>
552    *    <li><b>System property:</b>  <c>RestMethodContext.reqHeaders</c>
553    *    <li><b>Environment variable:</b>  <c>RESTMETHODCONTEXT_REQHEADERS</c>
554    *    <li><b>Default:</b>  <jk>null</jk>
555    *    <li><b>Session property:</b>  <jk>false</jk>
556    *    <li><b>Annotations:</b>
557    *       <ul>
558    *          <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#reqHeaders()}
559    *          <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#defaultAccept()}
560    *          <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#defaultContentType()}
561    *       </ul>
562    * </ul>
563    *
564    * <h5 class='section'>Description:</h5>
565    * Default request headers.
566    *
567    * <p>
568    * Specifies default values for request headers if they're not passed in through the request.
569    *
570    * <h5 class='section'>Example:</h5>
571    * <p class='bcode w800'>
572    *    <jc>// Assume "text/json" Accept value when Accept not specified</jc>
573    *    <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/*"</js>, reqHeaders={<js>"Accept: text/json"</js>})
574    *    <jk>public</jk> String doGet()  {...}
575    * </p>
576    *
577    * <ul class='notes'>
578    *    <li>
579    *       Supports {@doc RestSvlVariables}
580    *       (e.g. <js>"$S{mySystemProperty}"</js>).
581    * </ul>
582    *
583    * <ul class='seealso'>
584    *    <li class='jf'>{@link RestContext#REST_reqHeaders}
585    * </ul>
586    */
587   public static final String RESTMETHOD_reqHeaders = PREFIX + ".reqHeaders.smo";
588
589   //-------------------------------------------------------------------------------------------------------------------
590   // Instance
591   //-------------------------------------------------------------------------------------------------------------------
592
593   private final String httpMethod;
594   private final UrlPathPattern[] pathPatterns;
595   final RestMethodParam[] methodParams;
596   private final RestGuard[] guards;
597   private final RestMatcher[] optionalMatchers;
598   private final RestMatcher[] requiredMatchers;
599   private final RestConverter[] converters;
600   @SuppressWarnings("deprecation")
601   private final RestMethodProperties properties;
602   private final Integer priority;
603   private final RestContext context;
604   final java.lang.reflect.Method method;
605   final MethodInvoker methodInvoker;
606   final MethodInfo mi;
607   final SerializerGroup serializers;
608   final ParserGroup parsers;
609   final EncoderGroup encoders;
610   final HttpPartSerializer partSerializer;
611   final HttpPartParser partParser;
612   final JsonSchemaGenerator jsonSchemaGenerator;
613   final Map<String,Object>
614      reqHeaders,
615      defaultQuery,
616      defaultFormData;
617   final OMap reqAttrs;
618   final String defaultCharset;
619   final long maxInput;
620   final Map<String,Widget> widgets;
621   final List<MediaType>
622      supportedAcceptTypes,
623      supportedContentTypes;
624   final RestCallLoggerConfig callLoggerConfig;
625
626   final Map<Class<?>,ResponseBeanMeta> responseBeanMetas = new ConcurrentHashMap<>();
627   final Map<Class<?>,ResponsePartMeta> headerPartMetas = new ConcurrentHashMap<>();
628   final Map<Class<?>,ResponsePartMeta> bodyPartMetas = new ConcurrentHashMap<>();
629   final ResponseBeanMeta responseMeta;
630
631   final Map<Integer,AtomicInteger> statusCodes = new ConcurrentHashMap<>();
632
633   final Enablement debug;
634   final int hierarchyDepth;
635
636   @SuppressWarnings("deprecation")
637   RestMethodContext(RestMethodContextBuilder b) throws ServletException {
638      super(b.getPropertyStore());
639
640      this.context = b.context;
641      this.method = b.method;
642      this.methodInvoker = new MethodInvoker(method, context.getMethodExecStats(method));
643      this.mi = MethodInfo.of(method);
644
645      // Need this to access methods in anonymous inner classes.
646      mi.setAccessible();
647
648      int hd = 0;
649      Class<?> sc = b.method.getDeclaringClass().getSuperclass();
650      while (sc != null) {
651         hd++;
652         sc = sc.getSuperclass();
653      }
654      hierarchyDepth = hd;
655
656      PropertyStore ps = getPropertyStore();
657      ResourceResolver rr = context.getResourceResolver();
658      Object r = context.getResource();
659
660      String _httpMethod = getProperty(RESTMETHOD_httpMethod, String.class, null);
661      if (_httpMethod == null)
662         _httpMethod = HttpUtils.detectHttpMethod(method, true, "GET");
663      if ("METHOD".equals(_httpMethod))
664         _httpMethod = "*";
665      this.httpMethod = _httpMethod.toUpperCase(Locale.ENGLISH);
666
667      this.defaultCharset = getProperty(REST_defaultCharset, String.class, "utf-8");
668
669      this.maxInput = StringUtils.parseLongWithSuffix(getProperty(REST_maxInput, String.class, "100M"));
670
671      this.serializers = SerializerGroup
672         .create()
673         .append(getArrayProperty(REST_serializers, Object.class))
674         .apply(ps)
675         .build();
676
677      this.parsers = ParserGroup
678         .create()
679         .append(getArrayProperty(REST_parsers, Object.class))
680         .apply(ps)
681         .build();
682
683      HttpPartParser hpp = context.getPartParser();
684      if (hpp instanceof Parser) {
685         Parser pp = (Parser)hpp;
686         hpp = (HttpPartParser)pp.builder().apply(ps).build();
687      }
688      this.partParser = hpp;
689
690      this.partSerializer = context.getPartSerializer();
691
692      this.responseMeta = ResponseBeanMeta.create(mi, ps);
693
694      boolean dotAll = b.dotAll;
695      List<UrlPathPattern> pathPatterns = new ArrayList<>();
696      for (String p : getArrayProperty(RESTMETHOD_paths, String.class)) {
697         if (dotAll && ! p.endsWith("/*"))
698            p += "/*";
699         pathPatterns.add(new UrlPathPattern(p));
700      }
701      if (pathPatterns.isEmpty()) {
702         String p = HttpUtils.detectHttpPath(method, true);
703         if (dotAll && ! p.endsWith("/*"))
704            p += "/*";
705         pathPatterns.add(new UrlPathPattern(p));
706      }
707
708      this.pathPatterns = pathPatterns.toArray(new UrlPathPattern[pathPatterns.size()]);
709
710      this.methodParams = context.findParams(mi, false, this.pathPatterns[this.pathPatterns.length-1]);
711
712      this.converters = getInstanceArrayProperty(REST_converters, RestConverter.class, new RestConverter[0], rr, r, this);
713
714      AList<RestGuard> _guards = AList.of();
715      _guards.a(getInstanceArrayProperty(REST_guards, RestGuard.class, new RestGuard[0], rr, r, this));
716      Set<String> rolesDeclared = getSetProperty(REST_rolesDeclared, String.class, null);
717      Set<String> roleGuard = getSetProperty(REST_roleGuard, String.class, Collections.emptySet());
718
719      for (String rg : roleGuard) {
720         try {
721            _guards.add(new RoleBasedRestGuard(rolesDeclared, rg));
722         } catch (java.text.ParseException e1) {
723            throw new ServletException(e1);
724         }
725      }
726      this.guards = _guards.toArray(new RestGuard[_guards.size()]);
727
728      List<RestMatcher> optionalMatchers = new LinkedList<>(), requiredMatchers = new LinkedList<>();
729      for (RestMatcher matcher : getInstanceArrayProperty(RESTMETHOD_matchers, RestMatcher.class, new RestMatcher[0], rr, r, this)) {
730         if (matcher.mustMatch())
731            requiredMatchers.add(matcher);
732         else
733            optionalMatchers.add(matcher);
734      }
735      String clientVersion = getProperty(RESTMETHOD_clientVersion, String.class, null);
736      if (clientVersion != null)
737         requiredMatchers.add(new ClientVersionMatcher(context.getClientVersionHeader(), mi));
738
739      this.requiredMatchers = requiredMatchers.toArray(new RestMatcher[requiredMatchers.size()]);
740      this.optionalMatchers = optionalMatchers.toArray(new RestMatcher[optionalMatchers.size()]);
741
742      this.encoders = EncoderGroup
743         .create()
744         .append(IdentityEncoder.INSTANCE)
745         .append(getInstanceArrayProperty(REST_encoders, Encoder.class, new Encoder[0], rr, r, this))
746         .build();
747
748      this.jsonSchemaGenerator = JsonSchemaGenerator.create().apply(ps).build();
749
750      Map<String,Object> _reqHeaders = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
751      _reqHeaders.putAll(getMapProperty(RESTMETHOD_reqHeaders, Object.class));
752
753      OMap _reqAttrs = new OMap(context.getReqAttrs()).appendAll(getMapProperty(RESTMETHOD_reqAttrs, Object.class));
754
755      Map<String,Object> _defaultQuery = new LinkedHashMap<>(getMapProperty(RESTMETHOD_defaultQuery, Object.class));
756
757      Map<String,Object> _defaultFormData = new LinkedHashMap<>(getMapProperty(RESTMETHOD_defaultFormData, Object.class));
758
759      Type[] pt = method.getGenericParameterTypes();
760      Annotation[][] pa = method.getParameterAnnotations();
761      for (int i = 0; i < pt.length; i++) {
762         for (Annotation a : pa[i]) {
763            if (a instanceof Header) {
764               Header h = (Header)a;
765               String def = joinnlFirstNonEmptyArray(h._default(), h.df());
766               if (def != null) {
767                  try {
768                     _reqHeaders.put(firstNonEmpty(h.name(), h.n(), h.value()), parseAnything(def));
769                  } catch (ParseException e) {
770                     throw new ConfigException(e, "Malformed @Header annotation");
771                  }
772               }
773            } else if (a instanceof Query) {
774               Query q = (Query)a;
775               String def = joinnlFirstNonEmptyArray(q._default(), q.df());
776               if (def != null) {
777                  try {
778                     _defaultQuery.put(firstNonEmpty(q.name(), q.n(), q.value()), parseAnything(def));
779                  } catch (ParseException e) {
780                     throw new ConfigException(e, "Malformed @Query annotation");
781                  }
782               }
783            } else if (a instanceof FormData) {
784               FormData f = (FormData)a;
785               String def = joinnlFirstNonEmptyArray(f._default(), f.df());
786               if (def != null) {
787                  try {
788                     _defaultFormData.put(firstNonEmpty(f.name(), f.value(), f.n()), parseAnything(def));
789                  } catch (ParseException e) {
790                     throw new ConfigException(e, "Malformed @FormData annotation");
791                  }
792               }
793            }
794         }
795      }
796
797      this.reqHeaders = Collections.unmodifiableMap(_reqHeaders);
798      this.reqAttrs = _reqAttrs.unmodifiable();
799      this.defaultQuery = Collections.unmodifiableMap(_defaultQuery);
800      this.defaultFormData = Collections.unmodifiableMap(_defaultFormData);
801
802      this.priority = getIntegerProperty(RESTMETHOD_priority, 0);
803
804      AMap<String,Widget> _widgets = AMap.of();
805      for (Widget w : getInstanceArrayProperty(REST_widgets, Widget.class, new Widget[0]))
806         _widgets.put(w.getName(), w);
807      this.widgets = _widgets.unmodifiable();
808
809      this.properties = b.properties;
810
811      this.supportedAcceptTypes = getListProperty(REST_produces, MediaType.class, serializers.getSupportedMediaTypes());
812      this.supportedContentTypes = getListProperty(REST_consumes, MediaType.class, parsers.getSupportedMediaTypes());
813
814      this.debug = context.getDebug(method);
815
816      Object clc = getProperty(RESTMETHOD_callLoggerConfig);
817      if (clc instanceof RestCallLoggerConfig)
818         this.callLoggerConfig = (RestCallLoggerConfig)clc;
819      else if (clc instanceof OMap)
820         this.callLoggerConfig = RestCallLoggerConfig.create().parent(context.getCallLoggerConfig()).apply((OMap)clc).build();
821      else
822         this.callLoggerConfig = context.getCallLoggerConfig();
823   }
824
825   private String joinnlFirstNonEmptyArray(String[]...s) {
826      for (String[] ss : s)
827         if (ss.length > 0)
828            return joinnl(ss);
829      return null;
830   }
831
832   ResponseBeanMeta getResponseBeanMeta(Object o) {
833      if (o == null)
834         return null;
835      Class<?> c = o.getClass();
836      ResponseBeanMeta rbm = responseBeanMetas.get(c);
837      if (rbm == null) {
838         rbm = ResponseBeanMeta.create(c, serializers.getPropertyStore());
839         if (rbm == null)
840            rbm = ResponseBeanMeta.NULL;
841         responseBeanMetas.put(c, rbm);
842      }
843      if (rbm == ResponseBeanMeta.NULL)
844         return null;
845      return rbm;
846   }
847
848   ResponsePartMeta getResponseHeaderMeta(Object o) {
849      if (o == null)
850         return null;
851      Class<?> c = o.getClass();
852      ResponsePartMeta pm = headerPartMetas.get(c);
853      if (pm == null) {
854         ResponseHeader a = c.getAnnotation(ResponseHeader.class);
855         if (a != null) {
856            HttpPartSchema schema = HttpPartSchema.create(a);
857            HttpPartSerializer serializer = createPartSerializer(schema.getSerializer(), serializers.getPropertyStore(), partSerializer);
858            pm = new ResponsePartMeta(HEADER, schema, serializer);
859         }
860         if (pm == null)
861            pm = ResponsePartMeta.NULL;
862         headerPartMetas.put(c, pm);
863      }
864      if (pm == ResponsePartMeta.NULL)
865         return null;
866      return pm;
867   }
868
869   ResponsePartMeta getResponseBodyMeta(Object o) {
870      if (o == null)
871         return null;
872      Class<?> c = o.getClass();
873      ResponsePartMeta pm = bodyPartMetas.get(c);
874      if (pm == null) {
875         ResponseBody a = c.getAnnotation(ResponseBody.class);
876         if (a != null) {
877            HttpPartSchema schema = HttpPartSchema.create(a);
878            HttpPartSerializer serializer = createPartSerializer(schema.getSerializer(), serializers.getPropertyStore(), partSerializer);
879            pm = new ResponsePartMeta(BODY, schema, serializer);
880         }
881         if (pm == null)
882            pm = ResponsePartMeta.NULL;
883         bodyPartMetas.put(c, pm);
884      }
885      if (pm == ResponsePartMeta.NULL)
886         return null;
887      return pm;
888   }
889
890   /**
891    * Returns <jk>true</jk> if this Java method has any guards or matchers.
892    */
893   boolean hasGuardsOrMatchers() {
894      return (guards.length != 0 || requiredMatchers.length != 0 || optionalMatchers.length != 0);
895   }
896
897   /**
898    * Returns the HTTP method name (e.g. <js>"GET"</js>).
899    */
900   String getHttpMethod() {
901      return httpMethod;
902   }
903
904   /**
905    * Returns the path pattern for this method.
906    */
907   String getPathPattern() {
908      return pathPatterns[0].toString();
909   }
910
911   /**
912    * Returns <jk>true</jk> if the specified request object can call this method.
913    */
914   boolean isRequestAllowed(RestRequest req) {
915      for (RestGuard guard : guards) {
916         req.setJavaMethod(method);
917         if (! guard.isRequestAllowed(req))
918            return false;
919      }
920      return true;
921   }
922
923   boolean matches(UrlPathInfo pathInfo) {
924      for (UrlPathPattern p : pathPatterns)
925         if (p.match(pathInfo) != null)
926            return true;
927      return false;
928   }
929
930   /**
931    * Identifies if this method can process the specified call.
932    *
933    * <p>
934    * To process the call, the following must be true:
935    * <ul>
936    *    <li>Path pattern must match.
937    *    <li>Matchers (if any) must match.
938    * </ul>
939    *
940    * @param call The call to check.
941    * @return
942    *    One of the following values:
943    *    <ul>
944    *       <li><c>0</c> - Path doesn't match.
945    *       <li><c>1</c> - Path matched but matchers did not.
946    *       <li><c>2</c> - Matches.
947    *    </ul>
948    */
949   protected int match(RestCall call) {
950
951      UrlPathPatternMatch pm = matchPattern(call);
952
953      if (pm == null)
954         return 0;
955
956      if (requiredMatchers.length == 0 && optionalMatchers.length == 0) {
957         call.urlPathPatternMatch(pm);  // Cache so we don't have to recalculate.
958         return 2;
959      }
960
961      try {
962         RestRequest req = call.getRestRequest();
963         RestResponse res = call.getRestResponse();
964
965         @SuppressWarnings("deprecation")
966         RequestProperties requestProperties = new RequestProperties(req.getVarResolverSession(), properties);
967
968         req.init(this, requestProperties);
969         res.init(this, requestProperties);
970
971         // If the method implements matchers, test them.
972         for (RestMatcher m : requiredMatchers)
973            if (! m.matches(req))
974               return 1;
975         if (optionalMatchers.length > 0) {
976            boolean matches = false;
977            for (RestMatcher m : optionalMatchers)
978               matches |= m.matches(req);
979            if (! matches)
980               return 1;
981         }
982
983         call.urlPathPatternMatch(pm);  // Cache so we don't have to recalculate.
984         return 2;
985      } catch (Exception e) {
986         throw new InternalServerError(e);
987      }
988   }
989
990   private UrlPathPatternMatch matchPattern(RestCall call) {
991      UrlPathPatternMatch pm = null;
992      for (UrlPathPattern pp : pathPatterns)
993         if (pm == null)
994            pm = pp.match(call.getUrlPathInfo());
995      return pm;
996   }
997
998
999   /**
1000    * Workhorse method.
1001    *
1002    * @param pathInfo The value of {@link HttpServletRequest#getPathInfo()} (sorta)
1003    */
1004   void invoke(RestCall call) throws Throwable {
1005
1006      UrlPathPatternMatch pm = call.getUrlPathPatternMatch();
1007      if (pm == null)
1008         pm = matchPattern(call);
1009
1010      if (pm == null)
1011         throw new NotFound();
1012
1013      RestRequest req = call.getRestRequest();
1014      RestResponse res = call.getRestResponse();
1015
1016      RequestPath rp = req.getPathMatch();
1017      for (Map.Entry<String,String> e : pm.getVars().entrySet())
1018         rp.put(e.getKey(), e.getValue());
1019      if (pm.getRemainder() != null)
1020         rp.remainder(pm.getRemainder());
1021
1022      @SuppressWarnings("deprecation")
1023      RequestProperties requestProperties = new RequestProperties(req.getVarResolverSession(), properties);
1024
1025      req.init(this, requestProperties);
1026      res.init(this, requestProperties);
1027
1028      context.preCall(call);
1029
1030      call.loggerConfig(callLoggerConfig);
1031
1032      if (debug == TRUE) {
1033         call.debug(true);
1034         call.loggerConfig(RestCallLoggerConfig.DEFAULT_DEBUG);
1035      } else if (debug == FALSE) {
1036         call.debug(false);
1037         call.loggerConfig(RestCallLoggerConfig.DEFAULT_NOOP);
1038      } else if (debug == PER_REQUEST) {
1039         boolean b = "true".equalsIgnoreCase(req.getHeader("X-Debug"));
1040         if (b) {
1041            call.debug(true);
1042            call.loggerConfig(RestCallLoggerConfig.DEFAULT_DEBUG);
1043         } else {
1044            call.debug(false);
1045            call.loggerConfig(RestCallLoggerConfig.DEFAULT_NOOP);
1046         }
1047      }
1048
1049      Object[] args = new Object[methodParams.length];
1050      for (int i = 0; i < methodParams.length; i++) {
1051         try {
1052            args[i] = methodParams[i].resolve(req, res);
1053         } catch (Exception e) {
1054            throw toHttpException(e, BadRequest.class, "Invalid data conversion.  Could not convert {0} ''{1}'' to type ''{2}'' on method ''{3}.{4}''.", methodParams[i].getParamType().name(), methodParams[i].getName(), methodParams[i].getType(), mi.getDeclaringClass().getFullName(), mi.getSimpleName());
1055         }
1056      }
1057
1058      try {
1059
1060         for (RestGuard guard : guards)
1061            if (! guard.guard(req, res))
1062               return;
1063
1064         Object output;
1065         try {
1066            output = methodInvoker.invoke(context.getResource(), args);
1067
1068            // Handle manual call to req.setDebug().
1069            Boolean debug = ObjectUtils.castOrNull(req.getAttribute("Debug"), Boolean.class);
1070            if (debug == Boolean.TRUE) {
1071               call.debug(true);
1072               call.loggerConfig(RestCallLoggerConfig.DEFAULT_DEBUG);
1073            } else if (debug == Boolean.FALSE) {
1074               call.debug(false);
1075               call.loggerConfig(RestCallLoggerConfig.DEFAULT_NOOP);
1076            }
1077
1078            if (res.getStatus() == 0)
1079               res.setStatus(200);
1080            if (! method.getReturnType().equals(Void.TYPE)) {
1081               if (output != null || ! res.getOutputStreamCalled())
1082                  res.setOutput(output);
1083            }
1084         } catch (InvocationTargetException e) {
1085            Throwable e2 = e.getTargetException();    // Get the throwable thrown from the doX() method.
1086            res.setStatus(500);
1087            ResponsePartMeta rpm = getResponseBodyMeta(e2);
1088            ResponseBeanMeta rbm = getResponseBeanMeta(e2);
1089            if (rpm != null || rbm != null) {
1090               res.setOutput(e2);
1091               res.setResponseMeta(rbm);
1092            } else {
1093               throw e;
1094            }
1095         }
1096
1097         context.postCall(call);
1098
1099         if (res.hasOutput())
1100            for (RestConverter converter : converters)
1101               res.setOutput(converter.convert(req, res.getOutput()));
1102
1103      } catch (IllegalArgumentException e) {
1104         throw new BadRequest(e,
1105            "Invalid argument type passed to the following method: ''{0}''.\n\tArgument types: {1}",
1106            mi.toString(), mi.getFullName()
1107         );
1108      } catch (InvocationTargetException e) {
1109         throw e.getTargetException();
1110      }
1111   }
1112//
1113// protected void addStatusCode(int code) {
1114//    AtomicInteger ai = statusCodes.get(code);
1115//    if (ai == null) {
1116//       synchronized(statusCodes) {
1117//          ai = new AtomicInteger();
1118//          statusCodes.putIfAbsent(code, ai);
1119//          ai = statusCodes.get(code);
1120//       }
1121//    }
1122//    ai.incrementAndGet();
1123// }
1124//
1125// protected Map<Integer,Integer> getStatusCodes() {
1126//    Map<Integer,Integer> m = new TreeMap<>();
1127//    for (Map.Entry<Integer,AtomicInteger> e : statusCodes.entrySet())
1128//       m.put(e.getKey(), e.getValue().get());
1129//    return m;
1130// }
1131//
1132   /*
1133    * compareTo() method is used to keep SimpleMethods ordered in the RestCallRouter list.
1134    * It maintains the order in which matches are made during requests.
1135    */
1136   @Override /* Comparable */
1137   public int compareTo(RestMethodContext o) {
1138      int c;
1139
1140      c = priority.compareTo(o.priority);
1141      if (c != 0)
1142         return c;
1143
1144      for (int i = 0; i < Math.min(pathPatterns.length, o.pathPatterns.length); i++) {
1145         c = pathPatterns[i].compareTo(o.pathPatterns[i]);
1146         if (c != 0)
1147            return c;
1148      }
1149
1150      c = compare(o.hierarchyDepth, hierarchyDepth);
1151      if (c != 0)
1152         return c;
1153
1154      c = compare(o.requiredMatchers.length, requiredMatchers.length);
1155      if (c != 0)
1156         return c;
1157
1158      c = compare(o.optionalMatchers.length, optionalMatchers.length);
1159      if (c != 0)
1160         return c;
1161
1162      c = compare(o.guards.length, guards.length);
1163      if (c != 0)
1164         return c;
1165
1166      c = compare(method.getName(), o.method.getName());
1167      if (c != 0)
1168         return c;
1169
1170      c = compare(method.getParameterCount(), o.method.getParameterCount());
1171      if (c != 0)
1172         return c;
1173
1174      for (int i = 0; i < method.getParameterCount(); i++) {
1175         c = compare(method.getParameterTypes()[i].getName(), o.method.getParameterTypes()[i].getName());
1176         if (c != 0)
1177            return c;
1178      }
1179
1180      c = compare(method.getReturnType().getName(), o.method.getReturnType().getName());
1181      if (c != 0)
1182         return c;
1183
1184      return 0;
1185   }
1186
1187   /**
1188    * Bean property getter:  <property>serializers</property>.
1189    *
1190    * @return The value of the <property>serializers</property> property on this bean, or <jk>null</jk> if it is not set.
1191    */
1192   public SerializerGroup getSerializers() {
1193      return serializers;
1194   }
1195
1196   /**
1197    * Bean property getter:  <property>parsers</property>.
1198    *
1199    * @return The value of the <property>parsers</property> property on this bean, or <jk>null</jk> if it is not set.
1200    */
1201   public ParserGroup getParsers() {
1202      return parsers;
1203   }
1204
1205   /**
1206    * Bean property getter:  <property>partSerializer</property>.
1207    *
1208    * @return The value of the <property>partSerializer</property> property on this bean, or <jk>null</jk> if it is not set.
1209    */
1210   public HttpPartSerializer getPartSerializer() {
1211      return partSerializer;
1212   }
1213
1214   /**
1215    * Bean property getter:  <property>partParser</property>.
1216    *
1217    * @return The value of the <property>partParser</property> property on this bean, or <jk>null</jk> if it is not set.
1218    */
1219   public HttpPartParser getPartParser() {
1220      return partParser;
1221   }
1222
1223   /**
1224    * Returns the JSON-Schema generator applicable to this Java method.
1225    *
1226    * @return The JSON-Schema generator applicable to this Java method.
1227    */
1228   public JsonSchemaGenerator getJsonSchemaGenerator() {
1229      return jsonSchemaGenerator;
1230   }
1231
1232   /**
1233    * @return The REST call logger config for this method.
1234    */
1235   protected RestCallLoggerConfig getCallLoggerConfig() {
1236      return callLoggerConfig;
1237   }
1238
1239   Enablement getDebug() {
1240      return debug;
1241   }
1242
1243   @Override /* Object */
1244   public boolean equals(Object o) {
1245      return (o instanceof RestMethodContext) && eq(this, (RestMethodContext)o, (x,y)->x.method.equals(y.method));
1246   }
1247
1248   @Override /* Object */
1249   public int hashCode() {
1250      return method.hashCode();
1251   }
1252
1253   //-----------------------------------------------------------------------------------------------------------------
1254   // Utility methods.
1255   //-----------------------------------------------------------------------------------------------------------------
1256
1257   static String[] resolveVars(VarResolver vr, String[] in) {
1258      String[] out = new String[in.length];
1259      for (int i = 0; i < in.length; i++)
1260         out[i] = vr.resolve(in[i]);
1261      return out;
1262   }
1263
1264   static HttpPartSerializer createPartSerializer(Class<? extends HttpPartSerializer> c, PropertyStore ps, HttpPartSerializer _default) {
1265      HttpPartSerializer hps = castOrCreate(HttpPartSerializer.class, c, true, ps);
1266      return hps == null ? _default : hps;
1267   }
1268
1269   //-----------------------------------------------------------------------------------------------------------------
1270   // Other methods.
1271   //-----------------------------------------------------------------------------------------------------------------
1272
1273   @Override /* Context */
1274   public OMap toMap() {
1275      return super.toMap()
1276         .a("RestMethodContext", new DefaultFilteringOMap()
1277            .a("defaultFormData", defaultFormData)
1278            .a("defaultQuery", defaultQuery)
1279            .a("reqHeaders", reqHeaders)
1280            .a("httpMethod", httpMethod)
1281            .a("priority", priority)
1282         );
1283   }
1284}