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