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 java.util.Collections.*;
016import static java.util.logging.Level.*;
017import static org.apache.juneau.html.HtmlDocSerializer.*;
018import static org.apache.juneau.httppart.HttpPartType.*;
019import static org.apache.juneau.internal.IOUtils.*;
020import static org.apache.juneau.serializer.Serializer.*;
021import static org.apache.juneau.rest.Enablement.*;
022import static org.apache.juneau.rest.HttpRuntimeException.*;
023
024import java.io.*;
025import java.lang.reflect.*;
026import java.lang.reflect.Method;
027import java.lang.reflect.Proxy;
028import java.net.*;
029import java.nio.charset.*;
030import java.text.*;
031import java.util.*;
032import java.util.logging.*;
033
034import javax.servlet.*;
035import javax.servlet.http.*;
036
037import org.apache.juneau.*;
038import org.apache.juneau.config.*;
039import org.apache.juneau.dto.swagger.*;
040import org.apache.juneau.http.*;
041import org.apache.juneau.http.ReaderResource;
042import org.apache.juneau.http.StreamResource;
043import org.apache.juneau.http.annotation.*;
044import org.apache.juneau.http.annotation.Body;
045import org.apache.juneau.http.annotation.FormData;
046import org.apache.juneau.http.annotation.Header;
047import org.apache.juneau.http.annotation.Path;
048import org.apache.juneau.http.annotation.Query;
049import org.apache.juneau.http.annotation.Response;
050import org.apache.juneau.httppart.*;
051import org.apache.juneau.httppart.bean.*;
052import org.apache.juneau.internal.*;
053import org.apache.juneau.jsonschema.*;
054import org.apache.juneau.marshall.*;
055import org.apache.juneau.oapi.*;
056import org.apache.juneau.parser.*;
057import org.apache.juneau.reflect.*;
058import org.apache.juneau.remote.*;
059import org.apache.juneau.rest.annotation.*;
060import org.apache.juneau.http.exception.*;
061import org.apache.juneau.rest.helper.*;
062import org.apache.juneau.rest.util.*;
063import org.apache.juneau.rest.widget.*;
064import org.apache.juneau.serializer.*;
065import org.apache.juneau.svl.*;
066import org.apache.juneau.uon.*;
067import org.apache.juneau.utils.*;
068
069/**
070 * Represents an HTTP request for a REST resource.
071 *
072 * <p>
073 * Equivalent to {@link HttpServletRequest} except with some additional convenience methods.
074 *
075 * <p>
076 * For reference, given the URL <js>"http://localhost:9080/contextRoot/servletPath/foo?bar=baz#qux"</js>, the
077 * following methods return the following values....
078 * <table class='styled'>
079 *    <tr><th>Method</th><th>Value</th></tr>
080 *    <tr><td>{@code getContextPath()}</td><td>{@code /contextRoot}</td></tr>
081 *    <tr><td>{@code getPathInfo()}</td><td>{@code /foo}</td></tr>
082 *    <tr><td>{@code getPathTranslated()}</td><td>{@code path-to-deployed-war-on-filesystem/foo}</td></tr>
083 *    <tr><td>{@code getQueryString()}</td><td>{@code bar=baz}</td></tr>
084 *    <tr><td>{@code getRequestURI()}</td><td>{@code /contextRoot/servletPath/foo}</td></tr>
085 *    <tr><td>{@code getRequestURL()}</td><td>{@code http://localhost:9080/contextRoot/servletPath/foo}</td></tr>
086 *    <tr><td>{@code getServletPath()}</td><td>{@code /servletPath}</td></tr>
087 * </table>
088 *
089 * <ul class='seealso'>
090 *    <li class='link'>{@doc juneau-rest-server.RestMethod.RestRequest}
091 * </ul>
092 */
093@SuppressWarnings({ "unchecked", "unused" })
094public final class RestRequest extends HttpServletRequestWrapper {
095
096   private HttpServletRequest inner;
097   private final RestContext context;
098   private RestMethodContext restJavaMethod;
099
100   private final String method;
101   private RequestBody body;
102   private Method javaMethod;
103   @SuppressWarnings("deprecation")
104   private RequestProperties properties;
105   private BeanSession beanSession;
106   private VarResolverSession varSession;
107   private final RequestQuery queryParams;
108   private RequestFormData formData;
109   private RequestPath pathParams;
110   private boolean isPost;
111   private UriContext uriContext;
112   private String charset, authorityPath;
113   private RequestHeaders headers;
114   private RequestAttributes attrs;
115   private Config cf;
116   private Swagger swagger;
117   private SerializerSessionArgs serializerSessionArgs;
118   private ParserSessionArgs parserSessionArgs;
119   private RestResponse res;
120
121   /**
122    * Constructor.
123    */
124   RestRequest(RestContext context, HttpServletRequest req) throws ServletException {
125      super(req);
126      this.inner = req;
127      this.context = context;
128
129      try {
130         isPost = req.getMethod().equalsIgnoreCase("POST");
131
132         // If this is a POST, we want to parse the query parameters ourselves to prevent
133         // the servlet code from processing the HTTP body as URL-Encoded parameters.
134         queryParams = new RequestQuery(this);
135         if (isPost)
136            RestUtils.parseQuery(getQueryString(), queryParams);
137         else
138            queryParams.putAll(req.getParameterMap());
139
140         // Get the HTTP method.
141         // Can be overridden through a "method" GET attribute.
142         String _method = super.getMethod();
143
144         String m = getQuery().getString("method");
145         if (m != null) {
146            Set<String> s = context.getAllowedMethodParams();
147            if (! s.isEmpty() && (s.contains("*") || s.contains(m)))
148               _method = m;
149         }
150
151         m = req.getHeader("X-Method");
152         if (m != null) {
153            Set<String> s = context.getAllowedMethodHeaders();
154            if (! s.isEmpty() && (s.contains("*") || s.contains(m)))
155               _method = m;
156         }
157
158         method = _method;
159
160         headers = new RequestHeaders(this);
161         for (Enumeration<String> e = getHeaderNames(); e.hasMoreElements();) {
162            String name = e.nextElement();
163            headers.put(name, super.getHeaders(name));
164         }
165
166         body = new RequestBody(this);
167
168         if (context.isAllowBodyParam()) {
169            String b = getQuery().getString("body");
170            if (b != null) {
171               headers.put("Content-Type", UonSerializer.DEFAULT.getResponseContentType());
172               body.load(MediaType.UON, UonParser.DEFAULT, b.getBytes(UTF8));
173            }
174         }
175
176         Set<String> s = context.getAllowedHeaderParams();
177         if (! s.isEmpty())
178            headers.queryParams(queryParams, s);
179
180         this.pathParams = new RequestPath(this);
181
182      } catch (Exception e) {
183         throw toHttpException(e, InternalServerError.class);
184      }
185   }
186
187   /*
188    * Called from RestServlet after a match has been made but before the guard or method invocation.
189    */
190   final void init(RestMethodContext rjm, @SuppressWarnings("deprecation") RequestProperties properties) throws IOException {
191      this.restJavaMethod = rjm;
192      this.javaMethod = rjm.method;
193      this.properties = properties;
194      this.beanSession = rjm.createSession();
195      this.pathParams
196         .parser(rjm.partParser);
197      this.queryParams
198         .addDefault(rjm.defaultQuery)
199         .parser(rjm.partParser);
200      this.headers
201         .addDefault(rjm.reqHeaders)
202         .addDefault(context.getReqHeaders())
203         .parser(rjm.partParser);
204      this.attrs = new RequestAttributes(this, rjm.reqAttrs);
205      this.body
206         .encoders(rjm.encoders)
207         .parsers(rjm.parsers)
208         .headers(headers)
209         .maxInput(rjm.maxInput);
210
211      if (isDebug()) {
212         setDebug();
213      }
214
215      String stylesheet = getQuery().getString("stylesheet");
216      if (stylesheet != null)
217         getSession().setAttribute(HTMLDOC_stylesheet, stylesheet.replace(' ', '$'));  // Prevent SVL insertion.
218      stylesheet = (String)getSession().getAttribute("stylesheet");
219      if (stylesheet != null)
220         properties.put(HTMLDOC_stylesheet, new String[]{stylesheet});
221
222   }
223
224   RestRequest setResponse(RestResponse res) {
225      this.res = res;
226      return this;
227   }
228
229   /**
230    * Returns a string of the form <js>"HTTP method-name full-url"</js>
231    *
232    * @return A description string of the request.
233    */
234   public String getDescription() {
235      String qs = getQueryString();
236      return "HTTP " + getMethod() + " " + getRequestURI() + (qs == null ? "" : "?" + qs);
237   }
238
239   //-----------------------------------------------------------------------------------------------------------------
240   // Properties
241   //-----------------------------------------------------------------------------------------------------------------
242
243   /**
244    * Retrieve the properties active for this request.
245    *
246    * <div class='warn'>
247    *    <b>Deprecated</b> - Use {@link #getAttributes()}
248    * </div>
249    *
250    * <p>
251    * This contains all resource and method level properties from the following:
252    * <ul class='javatree'>
253    *    <li class='ja'>{@link Rest#properties()}
254    *    <li class='ja'>{@link RestMethod#properties()}
255    *    <li class='jm'>{@link RestContextBuilder#set(String, Object)}
256    * </ul>
257    *
258    * <p>
259    * The returned object is modifiable and allows you to override session-level properties before
260    * they get passed to the serializers.
261    * <br>However, properties are open-ended, and can be used for any purpose.
262    *
263    * <h5 class='section'>Example:</h5>
264    * <p class='bcode w800'>
265    *    <ja>@RestMethod</ja>(
266    *       properties={
267    *          <ja>@Property</ja>(name=<jsf>SERIALIZER_sortMaps</jsf>, value=<js>"false"</js>)
268    *       }
269    *    )
270    *    <jk>public</jk> Map doGet(RestRequest req, <ja>@Query</ja>(<js>"sortMaps"</js>) Boolean sortMaps) {
271    *
272    *       <jc>// Override value if specified through query parameter.</jc>
273    *       <jk>if</jk> (sortMaps != <jk>null</jk>)
274    *          req.getProperties().put(<jsf>SERIALIZER_sortMaps</jsf>, sortMaps);
275    *
276    *       <jk>return</jk> <jsm>getMyMap</jsm>();
277    *    }
278    * </p>
279    *
280    * <ul class='seealso'>
281    *    <li class='jm'>{@link #prop(String, Object)}
282    *    <li class='link'>{@doc juneau-rest-server.ConfigurableProperties}
283    * </ul>
284    *
285    * @return The properties active for this request.
286    */
287   @Deprecated
288   public RequestProperties getProperties() {
289      return this.properties;
290   }
291
292   /**
293    * Shortcut for calling <c>getProperties().append(name, value);</c> fluently.
294    *
295    * <div class='warn'>
296    *    <b>Deprecated</b> - {@link RequestAttributes#put(String, Object)} or {@link #setAttribute(String, Object)}
297    * </div>
298    *
299    * @param name The property name.
300    * @param value The property value.
301    * @return This object (for method chaining).
302    */
303   @Deprecated
304   public RestRequest prop(String name, Object value) {
305      this.properties.append(name, value);
306      return this;
307   }
308
309
310   //-----------------------------------------------------------------------------------------------------------------
311   // Headers
312   //-----------------------------------------------------------------------------------------------------------------
313
314   /**
315    * Request headers.
316    *
317    * <p>
318    * Returns a {@link RequestHeaders} object that encapsulates access to HTTP headers on the request.
319    *
320    * <h5 class='section'>Example:</h5>
321    * <p class='bcode w800'>
322    *    <ja>@RestMethod</ja>(...)
323    *    <jk>public</jk> Object myMethod(RestRequest req) {
324    *
325    *       <jc>// Get access to headers.</jc>
326    *       RequestHeaders h = req.getHeaders();
327    *
328    *       <jc>// Add a default value.</jc>
329    *       h.addDefault(<js>"ETag"</js>, <jsf>DEFAULT_UUID</jsf>);
330    *
331    *    <jc>// Get a header value as a POJO.</jc>
332    *       UUID etag = h.get(<js>"ETag"</js>, UUID.<jk>class</jk>);
333    *
334    *       <jc>// Get a standard header.</jc>
335    *       CacheControl = h.getCacheControl();
336    *    }
337    * </p>
338    *
339    * <ul class='notes'>
340    *    <li>
341    *       This object is modifiable.
342    *    <li>
343    *       Values are converted from strings using the registered {@link RestContext#REST_partParser part-parser} on the resource class.
344    *    <li>
345    *       The {@link RequestHeaders} object can also be passed as a parameter on the method.
346    *    <li>
347    *       The {@link Header @Header} annotation can be used to access individual header values.
348    * </ul>
349    *
350    * <ul class='seealso'>
351    *    <li class='link'>{@doc juneau-rest-server.RestMethod.RequestHeaders}
352    * </ul>
353    *
354    * @return
355    *    The headers on this request.
356    *    <br>Never <jk>null</jk>.
357    */
358   public RequestHeaders getHeaders() {
359      return headers;
360   }
361
362   @Override /* ServletRequest */
363   public String getHeader(String name) {
364      return getHeaders().getString(name);
365   }
366
367   @Override /* ServletRequest */
368   public Enumeration<String> getHeaders(String name) {
369      String[] v = headers.get(name);
370      if (v == null || v.length == 0)
371         return Collections.enumeration(Collections.EMPTY_LIST);
372      return Collections.enumeration(Arrays.asList(v));
373   }
374
375   /**
376    * Returns the media types that are valid for <c>Accept</c> headers on the request.
377    *
378    * @return The set of media types registered in the serializer group of this request.
379    */
380   public List<MediaType> getProduces() {
381      return restJavaMethod == null ? Collections.<MediaType>emptyList() : restJavaMethod.supportedAcceptTypes;
382   }
383
384   /**
385    * Returns the media types that are valid for <c>Content-Type</c> headers on the request.
386    *
387    * @return The set of media types registered in the parser group of this request.
388    */
389   public List<MediaType> getConsumes() {
390      return restJavaMethod == null ? Collections.<MediaType>emptyList() : restJavaMethod.supportedContentTypes;
391   }
392
393   /**
394    * Returns the {@link PropertyStore} for this request.
395    *
396    * <p>
397    * Consists of a read-only roll-up of all configuration properties defined on this method and class.
398    *
399    * @return
400    *    The property store for this request.
401    *    <br>Never <jk>null</jk>.
402    */
403   public PropertyStore getPropertyStore() {
404      return restJavaMethod == null ? PropertyStore.DEFAULT : restJavaMethod.getPropertyStore();
405   }
406
407   /**
408    * Sets the charset to expect on the request body.
409    */
410   @Override /* ServletRequest */
411   public void setCharacterEncoding(String charset) {
412      this.charset = charset;
413   }
414
415   /**
416    * Returns the charset specified on the <c>Content-Type</c> header, or <js>"UTF-8"</js> if not specified.
417    */
418   @Override /* ServletRequest */
419   public String getCharacterEncoding() throws UnsupportedMediaType {
420      if (charset == null) {
421         // Determine charset
422         // NOTE:  Don't use super.getCharacterEncoding() because the spec is implemented inconsistently.
423         // Jetty returns the default charset instead of null if the character is not specified on the request.
424         String h = getHeader("Content-Type");
425         if (h != null) {
426            int i = h.indexOf(";charset=");
427            if (i > 0)
428               charset = h.substring(i+9).trim();
429         }
430         if (charset == null && restJavaMethod != null)
431            charset = restJavaMethod.defaultCharset;
432         if (charset == null)
433            charset = "UTF-8";
434         if (! Charset.isSupported(charset))
435            throw new UnsupportedMediaType("Unsupported charset in header ''Content-Type'': ''{0}''", h);
436      }
437      return charset;
438   }
439
440   /**
441    * Wrapper around {@link #getCharacterEncoding()} that converts the value to a {@link Charset}.
442    *
443    * @return The request character encoding converted to a {@link Charset}.
444    */
445   public Charset getCharset() {
446      String s = getCharacterEncoding();
447      return s == null ? null : Charset.forName(s);
448   }
449
450   @Override /* ServletRequest */
451   public Locale getLocale() {
452      String h = headers.getString("Accept-Language");
453      if (h != null) {
454         MediaTypeRange[] mr = MediaTypeRange.parse(h);
455         if (mr.length > 0)
456            return toLocale(mr[0].getMediaType().getType());
457      }
458      return super.getLocale();
459   }
460
461   @Override /* ServletRequest */
462   public Enumeration<Locale> getLocales() {
463      String h = headers.getString("Accept-Language");
464      if (h != null) {
465         MediaTypeRange[] mr = MediaTypeRange.parse(h);
466         if (mr.length > 0) {
467            List<Locale> l = new ArrayList<>(mr.length);
468            for (MediaTypeRange r : mr)
469               l.add(toLocale(r.getMediaType().getType()));
470            return enumeration(l);
471         }
472      }
473      return super.getLocales();
474   }
475
476   //-----------------------------------------------------------------------------------------------------------------
477   // Attributes
478   //-----------------------------------------------------------------------------------------------------------------
479
480   /**
481    * Request attributes.
482    *
483    * <p>
484    * Returns a {@link RequestAttributes} object that encapsulates access to attributes on the request.
485    *
486    * <h5 class='section'>Example:</h5>
487    * <p class='bcode w800'>
488    *    <ja>@RestMethod</ja>(...)
489    *    <jk>public</jk> Object myMethod(RestRequest req) {
490    *
491    *       <jc>// Get access to attributes.</jc>
492    *       RequestAttributes a = req.getAttributes();
493    *
494    *    <jc>// Get a header value as a POJO.</jc>
495    *       UUID etag = a.get(<js>"ETag"</js>, UUID.<jk>class</jk>);
496    *    }
497    * </p>
498    *
499    * <ul class='notes'>
500    *    <li>
501    *       This object is modifiable.
502    *    <li>
503    *       Values are converted from strings using the registered {@link RestContext#REST_partParser part-parser} on the resource class.
504    *    <li>
505    *       The {@link RequestAttributes} object can also be passed as a parameter on the method.
506    *    <li>
507    *       The {@link Attr @Attr} annotation can be used to access individual attribute values.
508    * </ul>
509    *
510    * <ul class='seealso'>
511    *    <li class='link'>{@doc juneau-rest-server.RestMethod.RequestAttributes}
512    * </ul>
513    *
514    * @return
515    *    The headers on this request.
516    *    <br>Never <jk>null</jk>.
517    */
518   public RequestAttributes getAttributes() {
519      return attrs;
520   }
521
522   /**
523    * Same as {@link #getAttribute(String)} but returns a default value if not found.
524    *
525    * @param name The request attribute name.
526    * @param def The default value if the attribute doesn't exist.
527    * @return The request attribute value.
528    */
529   public Object getAttribute(String name, Object def) {
530      Object o = super.getAttribute(name);
531      return (o == null ? def : o);
532   }
533
534   /**
535    * Shorthand method for calling {@link #setAttribute(String, Object)} fluently.
536    *
537    * @param name The request attribute name.
538    * @param value The request attribute value.
539    * @return This object (for method chaining).
540    */
541   public RestRequest attr(String name, Object value) {
542      setAttribute(name, value);
543      return this;
544   }
545
546   //-----------------------------------------------------------------------------------------------------------------
547   // Query parameters
548   //-----------------------------------------------------------------------------------------------------------------
549
550   /**
551    * Query parameters.
552    *
553    * <p>
554    * Returns a {@link RequestQuery} object that encapsulates access to URL GET parameters.
555    *
556    * <p>
557    * Similar to {@link #getParameterMap()} but only looks for query parameters in the URL and not form posts.
558    *
559    * <h5 class='section'>Example:</h5>
560    * <p class='bcode w800'>
561    *    <ja>@RestMethod</ja>(...)
562    *    <jk>public void</jk> doGet(RestRequest req) {
563    *
564    *       <jc>// Get access to query parameters on the URL.</jc>
565    *       RequestQuery q = req.getQuery();
566    *
567    *       <jc>// Get query parameters converted to various types.</jc>
568    *       <jk>int</jk> p1 = q.get(<js>"p1"</js>, 0, <jk>int</jk>.<jk>class</jk>);
569    *       String p2 = q.get(<js>"p2"</js>, String.<jk>class</jk>);
570    *       UUID p3 = q.get(<js>"p3"</js>, UUID.<jk>class</jk>);
571    *    }
572    * </p>
573    *
574    * <ul class='notes'>
575    *    <li>
576    *       This object is modifiable.
577    *    <li>
578    *       This method can be used to retrieve query parameters without triggering the underlying servlet API to load and parse the request body.
579    *    <li>
580    *       Values are converted from strings using the registered {@link RestContext#REST_partParser part-parser} on the resource class.
581    *    <li>
582    *       The {@link RequestQuery} object can also be passed as a parameter on the method.
583    *    <li>
584    *       The {@link Query @Query} annotation can be used to access individual query parameter values.
585    * </ul>
586    *
587    * <ul class='seealso'>
588    *    <li class='link'>{@doc juneau-rest-server.RestMethod.RequestQuery}
589    * </ul>
590    *
591    * @return
592    *    The query parameters as a modifiable map.
593    *    <br>Never <jk>null</jk>.
594    */
595   public RequestQuery getQuery() {
596      return queryParams;
597   }
598
599   /**
600    * Shortcut for calling <c>getQuery().getString(name)</c>.
601    *
602    * @param name The query parameter name.
603    * @return The query parameter value, or <jk>null</jk> if not found.
604    */
605   public String getQuery(String name) {
606      return getQuery().getString(name);
607   }
608
609
610   //-----------------------------------------------------------------------------------------------------------------
611   // Form data parameters
612   //-----------------------------------------------------------------------------------------------------------------
613
614   /**
615    * Form-data.
616    *
617    * <p>
618    * Returns a {@link RequestFormData} object that encapsulates access to form post parameters.
619    *
620    * <p>
621    * Similar to {@link #getParameterMap()}, but only looks for form data in the HTTP body.
622    *
623    * <h5 class='section'>Example:</h5>
624    * <p class='bcode w800'>
625    *    <ja>@RestMethod</ja>(...)
626    *    <jk>public void</jk> doPost(RestRequest req) {
627    *
628    *       <jc>// Get access to parsed form data parameters.</jc>
629    *       RequestFormData fd = req.getFormData();
630    *
631    *       <jc>// Get form data parameters converted to various types.</jc>
632    *       <jk>int</jk> p1 = fd.get(<js>"p1"</js>, 0, <jk>int</jk>.<jk>class</jk>);
633    *       String p2 = fd.get(<js>"p2"</js>, String.<jk>class</jk>);
634    *       UUID p3 = fd.get(<js>"p3"</js>, UUID.<jk>class</jk>);
635    *    }
636    * </p>
637    *
638    * <ul class='notes'>
639    *    <li>
640    *       This object is modifiable.
641    *    <li>
642    *       Values are converted from strings using the registered {@link RestContext#REST_partParser part-parser} on the resource class.
643    *    <li>
644    *       The {@link RequestFormData} object can also be passed as a parameter on the method.
645    *    <li>
646    *       The {@link FormData @FormDAta} annotation can be used to access individual form data parameter values.
647    * </ul>
648    *
649    * <ul class='seealso'>
650    *    <li class='link'>{@doc juneau-rest-server.RestMethod.RequestFormData}
651    * </ul>
652    *
653    * @return
654    *    The URL-encoded form data from the request.
655    *    <br>Never <jk>null</jk>.
656    * @throws InternalServerError If query parameters could not be parsed.
657    * @see org.apache.juneau.http.annotation.FormData
658    */
659   public RequestFormData getFormData() throws InternalServerError {
660      try {
661         if (formData == null) {
662            formData = new RequestFormData(this, restJavaMethod == null ? OpenApiParser.DEFAULT : restJavaMethod.partParser);
663            if (! body.isLoaded()) {
664               formData.putAll(getParameterMap());
665            } else {
666               Map<String,String[]> m = RestUtils.parseQuery(body.getReader());
667               for (Map.Entry<String,String[]> e : m.entrySet()) {
668                  for (String v : e.getValue())
669                     formData.put(e.getKey(), v);
670               }
671            }
672         }
673         formData.addDefault(restJavaMethod == null ? null : restJavaMethod.defaultFormData);
674         return formData;
675      } catch (Exception e) {
676         throw new InternalServerError(e);
677      }
678   }
679
680   /**
681    * Shortcut for calling <c>getFormData().getString(name)</c>.
682    *
683    * @param name The form data parameter name.
684    * @return The form data parameter value, or <jk>null</jk> if not found.
685    */
686   public String getFormData(String name) {
687      return getFormData().getString(name);
688   }
689
690
691   //-----------------------------------------------------------------------------------------------------------------
692   // Path parameters
693   //-----------------------------------------------------------------------------------------------------------------
694
695   /**
696    * Request path match.
697    *
698    * <p>
699    * Returns a {@link RequestPath} object that encapsulates access to everything related to the URL path.
700    *
701    * <h5 class='section'>Example:</h5>
702    * <p class='bcode w800'>
703    *    <ja>@RestMethod</ja>(..., path=<js>"/{foo}/{bar}/{baz}/*"</js>)
704    *    <jk>public void</jk> doGet(RestRequest req) {
705    *
706    *       <jc>// Get access to path data.</jc>
707    *       RequestPathMatch pm = req.getPathMatch();
708    *
709    *       <jc>// Example URL:  /123/qux/true/quux</jc>
710    *
711    *       <jk>int</jk> foo = pm.getInt(<js>"foo"</js>);  <jc>// =123</jc>
712    *       String bar = pm.getString(<js>"bar"</js>);  <jc>// =qux</jc>
713    *       <jk>boolean</jk> baz = pm.getBoolean(<js>"baz"</js>);  <jc>// =true</jc>
714    *       String remainder = pm.getRemainder();  <jc>// =quux</jc>
715    *    }
716    * </p>
717    *
718    * <ul class='notes'>
719    *    <li>
720    *       This object is modifiable.
721    *    <li>
722    *       Values are converted from strings using the registered {@link RestContext#REST_partParser part-parser} on the resource class.
723    *    <li>
724    *       The {@link RequestPath} object can also be passed as a parameter on the method.
725    *    <li>
726    *       The {@link Path @Path} annotation can be used to access individual values.
727    * </ul>
728    *
729    * <ul class='seealso'>
730    *    <li class='link'>{@doc juneau-rest-server.RestMethod.RequestPathMatch}
731    * </ul>
732    *
733    * @return
734    *    The path data from the URL.
735    *    <br>Never <jk>null</jk>.
736    */
737   public RequestPath getPathMatch() {
738      return pathParams;
739   }
740
741   /**
742    * Shortcut for calling <c>getPathMatch().get(name)</c>.
743    *
744    * @param name The path variable name.
745    * @return The path variable value, or <jk>null</jk> if not found.
746    */
747   public String getPath(String name) {
748      return getPathMatch().get(name);
749   }
750
751   /**
752    * Shortcut for calling <c>getPathMatch().getRemainder()</c>.
753    *
754    * @return The path remainder value, or <jk>null</jk> if not found.
755    */
756   public String getPathRemainder() {
757      return getPathMatch().getRemainder();
758   }
759
760   //-----------------------------------------------------------------------------------------------------------------
761   // Body methods
762   //-----------------------------------------------------------------------------------------------------------------
763
764   /**
765    * Request body.
766    *
767    * <p>
768    * Returns a {@link RequestBody} object that encapsulates access to the HTTP request body.
769    *
770    * <h5 class='section'>Example:</h5>
771    * <p class='bcode w800'>
772    *    <ja>@RestMethod</ja>(...)
773    *    <jk>public void</jk> doPost2(RestRequest req) {
774    *
775    *       <jc>// Convert body to a linked list of Person objects.</jc>
776    *       List&lt;Person&gt; l = req.getBody().asType(LinkedList.<jk>class</jk>, Person.<jk>class</jk>);
777    *       ..
778    *    }
779    * </p>
780    *
781    * <ul class='notes'>
782    *    <li>
783    *       The {@link RequestBody} object can also be passed as a parameter on the method.
784    *    <li>
785    *       The {@link Body @Body} annotation can be used to access the body as well.
786    * </ul>
787    *
788    * <ul class='seealso'>
789    *    <li class='link'>{@doc juneau-rest-server.RestMethod.RequestBody}
790    * </ul>
791    *
792    * @return
793    *    The body of this HTTP request.
794    *    <br>Never <jk>null</jk>.
795    */
796   public RequestBody getBody() {
797      return body;
798   }
799
800   /**
801    * Returns the HTTP body content as a {@link Reader}.
802    *
803    * <p>
804    * If {@code allowHeaderParams} init parameter is true, then first looks for {@code &body=xxx} in the URL query
805    * string.
806    *
807    * <p>
808    * Automatically handles GZipped input streams.
809    */
810   @Override /* ServletRequest */
811   public BufferedReader getReader() throws IOException {
812      return getBody().getReader();
813   }
814
815   /**
816    * Returns the HTTP body content as an {@link InputStream}.
817    *
818    * <p>
819    * Automatically handles GZipped input streams.
820    *
821    * @return The negotiated input stream.
822    * @throws IOException If any error occurred while trying to get the input stream or wrap it in the GZIP wrapper.
823    */
824   @Override /* ServletRequest */
825   public ServletInputStream getInputStream() throws IOException {
826      return getBody().getInputStream();
827   }
828
829   ServletInputStream getRawInputStream() throws IOException {
830      return inner.getInputStream();
831   }
832
833
834   //-----------------------------------------------------------------------------------------------------------------
835   // URI-related methods
836   //-----------------------------------------------------------------------------------------------------------------
837
838   @Override /* HttpServletRequest */
839   public String getContextPath() {
840      String cp = context.getUriContext();
841      return cp == null ? super.getContextPath() : cp;
842   }
843
844   /**
845    * Returns the URI authority portion of the request.
846    *
847    * @return The URI authority portion of the request.
848    */
849   public String getAuthorityPath() {
850      if (authorityPath == null)
851         authorityPath = context.getUriAuthority();
852      if (authorityPath == null) {
853         String scheme = getScheme();
854         int port = getServerPort();
855         StringBuilder sb = new StringBuilder(getScheme()).append("://").append(getServerName());
856         if (! (port == 80 && "http".equals(scheme) || port == 443 && "https".equals(scheme)))
857            sb.append(':').append(port);
858         authorityPath = sb.toString();
859      }
860      return authorityPath;
861   }
862
863   @Override /* HttpServletRequest */
864   public String getServletPath() {
865      String cp = context.getUriContext();
866      String sp = super.getServletPath();
867      return cp == null || ! sp.startsWith(cp) ? sp : sp.substring(cp.length());
868   }
869
870   /**
871    * Returns the URI context of the request.
872    *
873    * <p>
874    * The URI context contains all the information about the URI of the request, such as the servlet URI, context
875    * path, etc...
876    *
877    * @return The URI context of the request.
878    */
879   public UriContext getUriContext() {
880      if (uriContext == null)
881         uriContext = new UriContext(getAuthorityPath(), getContextPath(), getServletPath(), StringUtils.urlEncodePath(super.getPathInfo()));
882      return uriContext;
883   }
884
885   /**
886    * Returns a URI resolver that can be used to convert URIs to absolute or root-relative form.
887    *
888    * @param resolution The URI resolution rule.
889    * @param relativity The relative URI relativity rule.
890    * @return The URI resolver for this request.
891    */
892   public UriResolver getUriResolver(UriResolution resolution, UriRelativity relativity) {
893      return new UriResolver(resolution, relativity, getUriContext());
894   }
895
896   /**
897    * Shortcut for calling {@link #getUriResolver()} using {@link UriResolution#ROOT_RELATIVE} and
898    * {@link UriRelativity#RESOURCE}
899    *
900    * @return The URI resolver for this request.
901    */
902   public UriResolver getUriResolver() {
903      return new UriResolver(context.getUriResolution(), context.getUriRelativity(), getUriContext());
904   }
905
906   /**
907    * Returns the URI for this request.
908    *
909    * <p>
910    * Similar to {@link #getRequestURI()} but returns the value as a {@link URI}.
911    * It also gives you the capability to override the query parameters (e.g. add new query parameters to the existing
912    * URI).
913    *
914    * @param includeQuery If <jk>true</jk> include the query parameters on the request.
915    * @param addQueryParams Augment the request URI with the specified query parameters.
916    * @return A new URI.
917    */
918   public URI getUri(boolean includeQuery, Map<String,?> addQueryParams) {
919      String uri = getRequestURI();
920      if (includeQuery || addQueryParams != null) {
921         StringBuilder sb = new StringBuilder(uri);
922         RequestQuery rq = this.queryParams.copy();
923         if (addQueryParams != null)
924            for (Map.Entry<String,?> e : addQueryParams.entrySet())
925               rq.put(e.getKey(), e.getValue());
926         if (! rq.isEmpty())
927            sb.append('?').append(rq.toQueryString());
928         uri = sb.toString();
929      }
930      try {
931         return new URI(uri);
932      } catch (URISyntaxException e) {
933         // Shouldn't happen.
934         throw new RuntimeException(e);
935      }
936   }
937
938   //-----------------------------------------------------------------------------------------------------------------
939   // Labels
940   //-----------------------------------------------------------------------------------------------------------------
941
942   /**
943    * Resource information provider.
944    *
945    * <p>
946    * Returns a {@link RestInfoProvider} object that encapsulates all the textual meta-data on this resource such as
947    * descriptions, titles, and Swagger documentation.
948    *
949    * <h5 class='section'>Example:</h5>
950    * <p class='bcode w800'>
951    *    <ja>@RestMethod</ja>(...)
952    *    <jk>public void</jk> doGet(RestRequest req) {
953    *
954    *       <jc>// Get information provider.</jc>
955    *       RestInfoProvider p = req.getInfoProvider();
956    *
957    *       <jc>// Get localized strings.</jc>
958    *       String resourceTitle = p.getTitle(req);
959    *       String methodDescription = p.getMethodDescription(req.getMethod(), req);
960    *       Contact contact = p.getContact(req);
961    *       ..
962    *    }
963    * </p>
964    *
965    * <ul class='notes'>
966    *    <li>
967    *       The {@link RestInfoProvider} object can also be passed as a parameter on the method.
968    * </ul>
969    *
970    * <ul class='seealso'>
971    *    <li class='jf'>{@link org.apache.juneau.rest.RestContext#REST_infoProvider}
972    *    <li class='jic'>{@link org.apache.juneau.rest.RestInfoProvider}
973    *    <li class='jm'>{@link org.apache.juneau.rest.RestRequest#getSiteName()}
974    *    <li class='jm'>{@link org.apache.juneau.rest.RestRequest#getResourceTitle()}
975    *    <li class='jm'>{@link org.apache.juneau.rest.RestRequest#getResourceDescription()}
976    *    <li class='jm'>{@link org.apache.juneau.rest.RestRequest#getMethodSummary()}
977    *    <li class='jm'>{@link org.apache.juneau.rest.RestRequest#getMethodDescription()}
978    *    <li class='link'>{@doc juneau-rest-server.Swagger}
979    * </ul>
980    *
981    * @return
982    *    The info provider on the resource.
983    *    <br>Never <jk>null</jk>.
984    */
985   public RestInfoProvider getInfoProvider() {
986      return context.getInfoProvider();
987   }
988
989   /**
990    * Returns the localized swagger associated with the resource.
991    *
992    * <p>
993    * A shortcut for calling <c>getInfoProvider().getSwagger(request);</c>
994    *
995    * <h5 class='section'>Example:</h5>
996    * <p class='bcode w800'>
997    *    <ja>@RestMethod</ja>(...)
998    *    <jk>public</jk> List&lt;Tag&gt; getSwaggerTags(RestRequest req) {
999    *       <jk>return</jk> req.getSwagger().getTags();
1000    *    }
1001    * </p>
1002    *
1003    * <ul class='notes'>
1004    *    <li>
1005    *       The {@link Swagger} object can also be passed as a parameter on the method.
1006    * </ul>
1007    *
1008    * <ul class='seealso'>
1009    *    <li class='jf'>{@link org.apache.juneau.rest.RestContext#REST_infoProvider}
1010    *    <li class='jic'>{@link org.apache.juneau.rest.RestInfoProvider}
1011    *    <li class='jm'>{@link org.apache.juneau.rest.RestRequest#getInfoProvider()}
1012    *    <li class='link'>{@doc juneau-rest-server.Swagger}
1013    * </ul>
1014    *
1015    * @return
1016    *    The swagger associated with the resource.
1017    *    <br>Never <jk>null</jk>.
1018    */
1019   public Swagger getSwagger() {
1020      try {
1021         if (swagger == null)
1022            swagger = context.getInfoProvider().getSwagger(this);
1023         return swagger;
1024      } catch (Exception e) {
1025         throw toHttpException(e, InternalServerError.class);
1026      }
1027   }
1028
1029   /**
1030    * Returns the localized site name.
1031    *
1032    * <p>
1033    * The site name is intended to be a title that can be applied to the entire site.
1034    *
1035    * <p>
1036    * One possible use is if you want to add the same title to the top of all pages by defining a header on a
1037    * common parent class like so:
1038    * <p class='bcode w800'>
1039    *  <ja>@HtmlDocConfig</ja>(
1040    *       header={
1041    *          <js>"&lt;h1&gt;$R{siteName}&lt;/h1&gt;"</js>,
1042    *          <js>"&lt;h2&gt;$R{resourceTitle}&lt;/h2&gt;"</js>
1043    *       }
1044    *    )
1045    * </p>
1046    *
1047    * <p>
1048    * Equivalent to calling {@link RestInfoProvider#getSiteName(RestRequest)} with this object.
1049    *
1050    * @return The localized site name.
1051    */
1052   public String getSiteName() {
1053      try {
1054         return context.getInfoProvider().getSiteName(this);
1055      } catch (Exception e) {
1056         throw toHttpException(e, InternalServerError.class);
1057      }
1058   }
1059
1060   /**
1061    * Returns the localized resource title.
1062    *
1063    * <p>
1064    * Equivalent to calling {@link RestInfoProvider#getTitle(RestRequest)} with this object.
1065    *
1066    * @return The localized resource title.
1067    */
1068   public String getResourceTitle() {
1069      try {
1070         return context.getInfoProvider().getTitle(this);
1071      } catch (Exception e) {
1072         throw toHttpException(e, InternalServerError.class);
1073      }
1074   }
1075
1076   /**
1077    * Returns the localized resource description.
1078    *
1079    * <p>
1080    * Equivalent to calling {@link RestInfoProvider#getDescription(RestRequest)} with this object.
1081    *
1082    * @return The localized resource description.
1083    */
1084   public String getResourceDescription() {
1085      try {
1086         return context.getInfoProvider().getDescription(this);
1087      } catch (Exception e) {
1088         throw toHttpException(e, InternalServerError.class);
1089      }
1090   }
1091
1092   /**
1093    * Returns the localized method summary.
1094    *
1095    * <p>
1096    * Equivalent to calling {@link RestInfoProvider#getMethodSummary(Method, RestRequest)} with this object.
1097    *
1098    * @return The localized method description.
1099    */
1100   public String getMethodSummary() {
1101      try {
1102         return context.getInfoProvider().getMethodSummary(javaMethod, this);
1103      } catch (Exception e) {
1104         throw toHttpException(e, InternalServerError.class);
1105      }
1106   }
1107
1108   /**
1109    * Returns the localized method description.
1110    *
1111    * <p>
1112    * Equivalent to calling {@link RestInfoProvider#getMethodDescription(Method, RestRequest)} with this object.
1113    *
1114    * @return The localized method description.
1115    */
1116   public String getMethodDescription() {
1117      try {
1118         return context.getInfoProvider().getMethodDescription(javaMethod, this);
1119      } catch (Exception e) {
1120         throw toHttpException(e, InternalServerError.class);
1121      }
1122   }
1123
1124   //-----------------------------------------------------------------------------------------------------------------
1125   // Other methods
1126   //-----------------------------------------------------------------------------------------------------------------
1127
1128   /**
1129    * Returns the serializers associated with this request.
1130    *
1131    * <ul class='seealso'>
1132    *    <li class='link'>{@doc juneau-rest-server.Serializers}
1133    * </ul>
1134    *
1135    * @return The serializers associated with this request.
1136    */
1137   public SerializerGroup getSerializers() {
1138      return restJavaMethod == null ? SerializerGroup.EMPTY : restJavaMethod.serializers;
1139   }
1140
1141   /**
1142    * Returns the parsers associated with this request.
1143    *
1144    * <ul class='seealso'>
1145    *    <li class='link'>{@doc juneau-rest-server.Parsers}
1146    * </ul>
1147    *
1148    * @return The parsers associated with this request.
1149    */
1150   public ParserGroup getParsers() {
1151      return restJavaMethod == null ? ParserGroup.EMPTY : restJavaMethod.parsers;
1152   }
1153
1154   /**
1155    * Returns the part serializer associated with this request.
1156    *
1157    * @return The part serializer associated with this request.
1158    */
1159   public HttpPartParser getPartParser() {
1160      return restJavaMethod == null ? OpenApiParser.DEFAULT : restJavaMethod.partParser;
1161   }
1162
1163   /**
1164    * Returns the part serializer associated with this request.
1165    *
1166    * @return The part serializer associated with this request.
1167    */
1168   public HttpPartSerializer getPartSerializer() {
1169      return restJavaMethod == null ? OpenApiSerializer.DEFAULT : restJavaMethod.partSerializer;
1170   }
1171
1172   /**
1173    * Returns the method of this request.
1174    *
1175    * <p>
1176    * If <c>allowHeaderParams</c> init parameter is <jk>true</jk>, then first looks for
1177    * <c>&amp;method=xxx</c> in the URL query string.
1178    */
1179   @Override /* ServletRequest */
1180   public String getMethod() {
1181      return method;
1182   }
1183
1184   /**
1185    * Returns the HTTP 1.1 method name of the request as an enum.
1186    *
1187    * <p>
1188    * Note that non-RFC2616 method names resolve as {@link HttpMethod#OTHER}.
1189    *
1190    * @return The HTTP method.
1191    */
1192   public HttpMethod getHttpMethod() {
1193      return HttpMethod.forString(method);
1194   }
1195
1196   @Override /* ServletRequest */
1197   public int getContentLength() {
1198      return getBody().getContentLength();
1199   }
1200
1201   int getRawContentLength() {
1202      return super.getContentLength();
1203   }
1204
1205   /**
1206    * Returns <jk>true</jk> if <c>&amp;plainText=true</c> was specified as a URL parameter.
1207    *
1208    * <p>
1209    * This indicates that the <c>Content-Type</c> of the output should always be set to <js>"text/plain"</js>
1210    * to make it easy to render in a browser.
1211    *
1212    * <p>
1213    * This feature is useful for debugging.
1214    *
1215    * @return <jk>true</jk> if {@code &amp;plainText=true} was specified as a URL parameter
1216    */
1217   public boolean isPlainText() {
1218      return "true".equals(getQuery().getString("plainText", "false"));
1219   }
1220
1221   /**
1222    * Returns the resource bundle for the request locale.
1223    *
1224    * <h5 class='section'>Example:</h5>
1225    * <p class='bcode w800'>
1226    *    <ja>@RestMethod</ja>(...)
1227    *    <jk>public</jk> String sayHello(RestRequest req, <ja>@Query</ja>(<js>"user"</js>) String user) {
1228    *
1229    *       <jc>// Get message bundle.</jc>
1230    *       MessageBundle mb = req.getMessageBundle();
1231    *
1232    *       <jc>// Return a localized message.</jc>
1233    *       <jk>return</jk> mb.getString(<js>"hello.message"</js>, user);
1234    *    }
1235    * </p>
1236    *
1237    * <ul class='notes'>
1238    *    <li>
1239    *       The {@link MessageBundle} object can also be passed as a parameter on the method.
1240    * </ul>
1241    *
1242    * <ul class='seealso'>
1243    *    <li class='jf'>{@link org.apache.juneau.rest.RestContext#REST_messages}
1244    *    <li class='jm'>{@link org.apache.juneau.rest.RestRequest#getMessage(String,Object...)}
1245    *    <li class='link'>{@doc juneau-rest-server.Messages}
1246    * </ul>
1247    *
1248    * @return
1249    *    The resource bundle.
1250    *    <br>Never <jk>null</jk>.
1251    */
1252   public MessageBundle getMessageBundle() {
1253      return context.getMessages().getBundle(getLocale());
1254   }
1255
1256   /**
1257    * Shortcut method for calling {@link MessageBundle#getString(Locale, String, Object...)} based on the request locale.
1258    *
1259    * @param key The message key.
1260    * @param args Optional {@link MessageFormat}-style arguments.
1261    * @return The localized message.
1262    */
1263   public String getMessage(String key, Object...args) {
1264      return context.getMessages().getString(getLocale(), key, args);
1265   }
1266
1267   /**
1268    * Returns the resource context handling the request.
1269    *
1270    * <p>
1271    * Can be used to access servlet-init parameters or annotations during requests, such as in calls to
1272    * {@link RestGuard#guard(RestRequest, RestResponse)}..
1273    *
1274    * @return The resource context handling the request.
1275    */
1276   public RestContext getContext() {
1277      return context;
1278   }
1279
1280   /**
1281    * Returns the java method handling the request.
1282    *
1283    * <p>
1284    * Can be used to access the method name or method annotations during requests, such as in calls to
1285    * {@link RestGuard#guard(RestRequest, RestResponse)}.
1286    *
1287    * <ul class='notes'>
1288    *    <li>
1289    *       This returns <jk>null</jk> when evaluating servlet-level guards since the method has not been resolved at that
1290    *       point of execution.
1291    * </ul>
1292    *
1293    * @return The Java method handling the request, or <c>null</c> if the method has not yet been resolved.
1294    */
1295   public Method getJavaMethod() {
1296      return javaMethod;
1297   }
1298
1299   /**
1300    * Returns the {@link BeanSession} associated with this request.
1301    *
1302    * @return The request bean session.
1303    */
1304   public BeanSession getBeanSession() {
1305      return beanSession;
1306   }
1307
1308   /**
1309    * Returns <jk>true</jk> if debug mode is enabled.
1310    *
1311    * Debug mode is enabled by simply adding <js>"?debug=true"</js> to the query string or adding a <c>Debug: true</c> header on the request.
1312    *
1313    * @return <jk>true</jk> if debug mode is enabled.
1314    */
1315   public boolean isDebug() {
1316      Boolean b = ObjectUtils.castOrNull(getAttribute("Debug"), Boolean.class);
1317      if (b != null)
1318         return b;
1319      Enablement e = restJavaMethod != null ? restJavaMethod.getDebug() : context.getDebug();
1320      if (e == TRUE)
1321         return true;
1322      if (e == FALSE)
1323         return false;
1324      return "true".equalsIgnoreCase(getHeader("X-Debug"));
1325   }
1326
1327   /**
1328    * Sets the <js>"Exception"</js> attribute to the specified throwable.
1329    *
1330    * <p>
1331    * This exception is used by {@link BasicRestCallLogger} for logging purposes.
1332    *
1333    * @param t The attribute value.
1334    * @return This object (for method chaining).
1335    */
1336   public RestRequest setException(Throwable t) {
1337      setAttribute("Exception", t);
1338      return this;
1339   }
1340
1341   /**
1342    * Sets the <js>"NoTrace"</js> attribute to the specified boolean.
1343    *
1344    * <p>
1345    * This flag is used by {@link BasicRestCallLogger} and tells it not to log the current request.
1346    *
1347    * @param b The attribute value.
1348    * @return This object (for method chaining).
1349    */
1350   public RestRequest setNoTrace(Boolean b) {
1351      setAttribute("NoTrace", b);
1352      return this;
1353   }
1354
1355   /**
1356    * Shortcut for calling <c>setNoTrace(<jk>true</jk>)</c>.
1357    *
1358    * @return This object (for method chaining).
1359    */
1360   public RestRequest setNoTrace() {
1361      return setNoTrace(true);
1362   }
1363
1364   /**
1365    * Sets the <js>"Debug"</js> attribute to the specified boolean.
1366    *
1367    * <p>
1368    * This flag is used by {@link BasicRestCallLogger} to help determine how a request should be logged.
1369    *
1370    * @param b The attribute value.
1371    * @return This object (for method chaining).
1372    * @throws IOException If body could not be cached.
1373    */
1374   public RestRequest setDebug(Boolean b) throws IOException {
1375      setAttribute("Debug", b);
1376      if (b)
1377         inner = CachingHttpServletRequest.wrap(inner);
1378      return this;
1379   }
1380
1381   /**
1382    * Shortcut for calling <c>setDebug(<jk>true</jk>)</c>.
1383    *
1384    * @return This object (for method chaining).
1385    * @throws IOException If body could not be cached.
1386    */
1387   public RestRequest setDebug() throws IOException {
1388      return setDebug(true);
1389   }
1390
1391   /**
1392    * Request-level variable resolver session.
1393    *
1394    * <p>
1395    * Used to resolve SVL variables in text.
1396    *
1397    * <h5 class='section'>Example:</h5>
1398    * <p class='bcode w800'>
1399    *    <ja>@RestMethod</ja>(...)
1400    *    <jk>public</jk> String sayHello(RestRequest req) {
1401    *
1402    *       <jc>// Get var resolver session.</jc>
1403    *       VarResolverSession session = getVarResolverSession();
1404    *
1405    *       <jc>// Use it to construct a customized message from a query parameter.</jc>
1406    *       <jk>return</jk> session.resolve(<js>"Hello $RQ{user}!"</js>);
1407    *    }
1408    * </p>
1409    *
1410    * <ul class='notes'>
1411    *    <li>
1412    *       The {@link VarResolverSession} object can also be passed as a parameter on the method.
1413    * </ul>
1414    *
1415    * <ul class='seealso'>
1416    *    <li class='jm'>{@link org.apache.juneau.rest.RestContext#getVarResolver()}
1417    *    <li class='link'>{@doc juneau-rest-server.SvlVariables}
1418    * </ul>
1419    *
1420    * @return The variable resolver for this request.
1421    */
1422   public VarResolverSession getVarResolverSession() {
1423      if (varSession == null)
1424         varSession = context
1425            .getVarResolver()
1426            .createSession(context.getCallHandler().getSessionObjects(this, context.getResponse()))
1427            .sessionObject("req", this)
1428            .sessionObject("res", res);
1429      return varSession;
1430   }
1431
1432   /**
1433    * Returns an instance of a {@link ReaderResource} that represents the contents of a resource text file from the
1434    * classpath.
1435    *
1436    * <p>
1437    * <h5 class='section'>Example:</h5>
1438    * <p class='bcode w800'>
1439    *    <jc>// A rest method that (unsafely!) returns the contents of a localized file </jc>
1440    * <jc>// from the classpath and resolves any SVL variables embedded in it.</jc>
1441    *    <ja>@RestMethod</ja>(...)
1442    *    <jk>public</jk> String myMethod(RestRequest req, <ja>@Query</ja>(<js>"file"</js>) String file) {
1443    *       <jk>return</jk> req.getClasspathResourceAsString(file, <jk>true</jk>);
1444    *    }
1445    * </p>
1446    *
1447    * <ul class='seealso'>
1448    *    <li class='jf'>{@link org.apache.juneau.rest.RestContext#REST_classpathResourceFinder}
1449    *    <li class='jm'>{@link org.apache.juneau.rest.RestRequest#getClasspathReaderResource(String, boolean)}
1450    *    <li class='jm'>{@link org.apache.juneau.rest.RestRequest#getClasspathReaderResource(String)}
1451    * </ul>
1452    *
1453    * @param name The name of the resource (i.e. the value normally passed to {@link Class#getResourceAsStream(String)}.
1454    * @param resolveVars
1455    *    If <jk>true</jk>, any SVL variables will be
1456    *    resolved by the variable resolver returned by {@link #getVarResolverSession()}.
1457    *    <br>See {@link RestContext#getVarResolver()} for the list of supported variables.
1458    * @param mediaType The value to set as the <js>"Content-Type"</js> header for this object.
1459    * @param cached If <jk>true</jk>, the resource will be read into a byte array for fast serialization.
1460    * @return A new reader resource, or <jk>null</jk> if resource could not be found.
1461    * @throws IOException Thrown by underlying stream.
1462    */
1463   public ReaderResource getClasspathReaderResource(String name, boolean resolveVars, MediaType mediaType, boolean cached) throws IOException {
1464      String s = context.getClasspathResourceAsString(name, getLocale());
1465      if (s == null)
1466         return null;
1467      ResolvingReaderResource.Builder b = ResolvingReaderResource.create().mediaType(mediaType).contents(s);
1468      if (resolveVars)
1469         b.varResolver(getVarResolverSession());
1470      if (cached)
1471         b.cached();
1472      return b.build();
1473   }
1474
1475   /**
1476    * Same as {@link #getClasspathReaderResource(String, boolean, MediaType, boolean)} except uses the resource mime-type map
1477    * constructed using {@link RestContextBuilder#mimeTypes(String...)} to determine the media type.
1478    *
1479    * @param name The name of the resource (i.e. the value normally passed to {@link Class#getResourceAsStream(String)}.
1480    * @param resolveVars
1481    *    If <jk>true</jk>, any SVL variables will be
1482    *    resolved by the variable resolver returned by {@link #getVarResolverSession()}.
1483    *    <br>See {@link RestContext#getVarResolver()} for the list of supported variables.
1484    * @return A new reader resource, or <jk>null</jk> if resource could not be found.
1485    * @throws IOException Thrown by underlying stream.
1486    */
1487   public ReaderResource getClasspathReaderResource(String name, boolean resolveVars) throws IOException {
1488      return getClasspathReaderResource(name, resolveVars, MediaType.forString(context.getMediaTypeForName(name)), false);
1489   }
1490
1491   /**
1492    * Same as {@link #getClasspathReaderResource(String, boolean)} with <code>resolveVars == <jk>false</jk></code>
1493    *
1494    * @param name The name of the resource (i.e. the value normally passed to {@link Class#getResourceAsStream(String)}.
1495    * @return A new reader resource, or <jk>null</jk> if resource could not be found.
1496    * @throws IOException Thrown by underlying stream.
1497    */
1498   public ReaderResource getClasspathReaderResource(String name) throws IOException {
1499      return getClasspathReaderResource(name, false, MediaType.forString(context.getMediaTypeForName(name)), false);
1500   }
1501
1502   /**
1503    * Returns an instance of a {@link StreamResource} that represents the contents of a resource binary file from the
1504    * classpath.
1505    *
1506    * <ul class='seealso'>
1507    *    <li class='jf'>{@link org.apache.juneau.rest.RestContext#REST_classpathResourceFinder}
1508    *    <li class='jm'>{@link org.apache.juneau.rest.RestRequest#getClasspathStreamResource(String)}
1509    * </ul>
1510    *
1511    * @param name The name of the resource (i.e. the value normally passed to {@link Class#getResourceAsStream(String)}.
1512    * @param mediaType The value to set as the <js>"Content-Type"</js> header for this object.
1513    * @param cached If <jk>true</jk>, the resource will be read into a byte array for fast serialization.
1514    * @return A new stream resource, or <jk>null</jk> if resource could not be found.
1515    * @throws IOException Thrown by underlying stream.
1516    */
1517   @SuppressWarnings("resource")
1518   public StreamResource getClasspathStreamResource(String name, MediaType mediaType, boolean cached) throws IOException {
1519      InputStream is = context.getClasspathResource(name, getLocale());
1520      if (is == null)
1521         return null;
1522      StreamResource.Builder b = StreamResource.create().mediaType(mediaType).contents(is);
1523      if (cached)
1524         b.cached();
1525      return b.build();
1526   }
1527
1528   /**
1529    * Same as {@link #getClasspathStreamResource(String, MediaType, boolean)} except uses the resource mime-type map
1530    * constructed using {@link RestContextBuilder#mimeTypes(String...)} to determine the media type.
1531    *
1532    * @param name The name of the resource (i.e. the value normally passed to {@link Class#getResourceAsStream(String)}.
1533    * @return A new stream resource, or <jk>null</jk> if resource could not be found.
1534    * @throws IOException Thrown by underlying stream.
1535    */
1536   public StreamResource getClasspathStreamResource(String name) throws IOException {
1537      return getClasspathStreamResource(name, MediaType.forString(context.getMediaTypeForName(name)), false);
1538   }
1539
1540   /**
1541    * Config file associated with the resource.
1542    *
1543    * <p>
1544    * Returns a config file with session-level variable resolution.
1545    *
1546    * The config file is identified via one of the following:
1547    * <ul class='javatree'>
1548    *    <li class='ja'>{@link Rest#config()}
1549    *    <li class='jm'>{@link RestContextBuilder#config(Config)}
1550    * </ul>
1551    *
1552    * <h5 class='section'>Example:</h5>
1553    * <p class='bcode w800'>
1554    *    <ja>@RestMethod</ja>(...)
1555    *    <jk>public void</jk> doGet(RestRequest req) {
1556    *
1557    *       <jc>// Get config file.</jc>
1558    *       Config cf = req.getConfig();
1559    *
1560    *       <jc>// Get simple values from config file.</jc>
1561    *       <jk>int</jk> timeout = cf.getInt(<js>"MyResource/timeout"</js>, 10000);
1562    *
1563    *       <jc>// Get complex values from config file.</jc>
1564    *       MyBean b = cf.getObject(<js>"MyResource/myBean"</js>, MyBean.<jk>class</jk>);
1565    *    }
1566    * </p>
1567    *
1568    * <ul class='notes'>
1569    *    <li>
1570    *       The {@link Config} object can also be passed as a parameter on the method.
1571    * </ul>
1572    *
1573    * <ul class='seealso'>
1574    *    <li class='link'>{@doc juneau-rest-server.ConfigurationFiles}
1575    * </ul>
1576    *
1577    * @return
1578    *    The config file associated with the resource, or <jk>null</jk> if resource does not have a config file
1579    *    associated with it.
1580    */
1581   public Config getConfig() {
1582      if (cf == null)
1583         cf = context.getConfig().resolving(getVarResolverSession());
1584      return cf;
1585   }
1586
1587   /**
1588    * Returns the widgets used for resolving <js>"$W{...}"</js> string variables.
1589    *
1590    * <div class='warn'>
1591    *    <b>Deprecated</b> - No replacement.
1592    * </div>
1593    *
1594    * @return
1595    *    The widgets used for resolving <js>"$W{...}"</js> string variables.
1596    *    Never <jk>null</jk>.
1597    */
1598   @Deprecated
1599   public Map<String,Widget> getWidgets() {
1600      return restJavaMethod == null ? Collections.<String,Widget>emptyMap() : restJavaMethod.widgets;
1601   }
1602
1603   /**
1604    * Creates a proxy interface to retrieve HTTP parts of this request as a proxy bean.
1605    *
1606    * <h5 class='section'>Examples:</h5>
1607    * <p class='bcode w800'>
1608    *    <ja>@RestMethod</ja>(path=<js>"/mypath/{p1}/{p2}/*"</js>)
1609    *    <jk>public void</jk> myMethod(@Request MyRequest rb) {...}
1610    *
1611    *    <jk>public interface</jk> MyRequest {
1612    *
1613    *       <ja>@Path</ja> <jc>// Path variable name inferred from getter.</jc>
1614    *       String getP1();
1615    *
1616    *       <ja>@Path</ja>(<js>"p2"</js>)
1617    *       String getX();
1618    *
1619    *       <ja>@Path</ja>(<js>"/*"</js>)
1620    *       String getRemainder();
1621    *
1622    *       <ja>@Query</ja>
1623    *       String getQ1();
1624    *
1625    *    <jc>// Schema-based query parameter:  Pipe-delimited lists of comma-delimited lists of integers.</jc>
1626    *       <ja>@Query</ja>(
1627    *          collectionFormat=<js>"pipes"</js>
1628    *          items=<ja>@Items</ja>(
1629    *             items=<ja>@SubItems</ja>(
1630    *                collectionFormat=<js>"csv"</js>
1631    *                type=<js>"integer"</js>
1632    *             )
1633    *          )
1634    *       )
1635    *       <jk>int</jk>[][] getQ3();
1636    *
1637    *       <ja>@Header</ja>(<js>"*"</js>)
1638    *       Map&lt;String,Object&gt; getHeaders();
1639    * </p>
1640    *
1641    * @param c The request bean interface to instantiate.
1642    * @return A new request bean proxy for this REST request.
1643    */
1644   public <T> T getRequest(Class<T> c) {
1645      return getRequest(RequestBeanMeta.create(c, getContext().getPropertyStore()));
1646   }
1647
1648   /**
1649    * Same as {@link #getRequest(Class)} but used on pre-instantiated {@link RequestBeanMeta} objects.
1650    *
1651    * @param rbm The metadata about the request bean interface to create.
1652    * @return A new request bean proxy for this REST request.
1653    */
1654   public <T> T getRequest(final RequestBeanMeta rbm) {
1655      try {
1656         Class<T> c = (Class<T>)rbm.getClassMeta().getInnerClass();
1657         final BeanMeta<T> bm = getBeanSession().getBeanMeta(c);
1658         return (T)Proxy.newProxyInstance(
1659            c.getClassLoader(),
1660            new Class[] { c },
1661            new InvocationHandler() {
1662               @Override /* InvocationHandler */
1663               public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
1664                  RequestBeanPropertyMeta pm = rbm.getProperty(method.getName());
1665                  if (pm != null) {
1666                     HttpPartParser pp = pm.getParser(getPartParser());
1667                     HttpPartSchema schema = pm.getSchema();
1668                     String name = pm.getPartName();
1669                     ClassMeta<?> type = getContext().getClassMeta(method.getGenericReturnType());
1670                     HttpPartType pt = pm.getPartType();
1671                     if (pt == HttpPartType.BODY)
1672                        return getBody().schema(schema).asType(type);
1673                     if (pt == QUERY)
1674                        return getQuery().get(pp, schema, name, type);
1675                     if (pt == FORMDATA)
1676                        return getFormData().get(pp, schema, name, type);
1677                     if (pt == HEADER)
1678                        return getHeaders().get(pp, schema, name, type);
1679                     if (pt == PATH)
1680                        return getPathMatch().get(pp, schema, name, type);
1681                  }
1682                  return null;
1683               }
1684
1685         });
1686      } catch (Exception e) {
1687         throw new RuntimeException(e);
1688      }
1689   }
1690
1691   @Override /* Object */
1692   public String toString() {
1693      StringBuilder sb = new StringBuilder("\n").append(getDescription()).append("\n");
1694      sb.append("---Headers---\n");
1695      for (Enumeration<String> e = getHeaderNames(); e.hasMoreElements();) {
1696         String h = e.nextElement();
1697         sb.append("\t").append(h).append(": ").append(getHeader(h)).append("\n");
1698      }
1699      sb.append("---Default Servlet Headers---\n");
1700      for (Map.Entry<String,Object> e : context.getReqHeaders().entrySet()) {
1701         sb.append("\t").append(e.getKey()).append(": ").append(e.getValue()).append("\n");
1702      }
1703      if (javaMethod == null) {
1704         sb.append("***init() not called yet!***\n");
1705      } else if (method.equals("PUT") || method.equals("POST")) {
1706         try {
1707            sb.append("---Body UTF-8---\n");
1708            sb.append(body.asString()).append("\n");
1709            sb.append("---Body Hex---\n");
1710            sb.append(body.asSpacedHex()).append("\n");
1711         } catch (Exception e1) {
1712            sb.append(e1.getLocalizedMessage());
1713         }
1714      }
1715      return sb.toString();
1716   }
1717
1718   /**
1719    * Returns the session arguments to pass to serializers.
1720    *
1721    * @return The session arguments to pass to serializers.
1722    */
1723   public SerializerSessionArgs getSerializerSessionArgs() {
1724      if (serializerSessionArgs == null)
1725         serializerSessionArgs = SerializerSessionArgs
1726            .create()
1727            .properties(getProperties())
1728            .javaMethod(getJavaMethod())
1729            .locale(getLocale())
1730            .timeZone(getHeaders().getTimeZone())
1731            .debug(isDebug() ? true : null)
1732            .uriContext(getUriContext())
1733            .resolver(getVarResolverSession())
1734            .useWhitespace(isPlainText() ? true : null);
1735      return serializerSessionArgs;
1736   }
1737
1738   /**
1739    * Returns the session arguments to pass to parsers.
1740    *
1741    * @return The session arguments to pass to parsers.
1742    */
1743   public ParserSessionArgs getParserSessionArgs() {
1744      if (parserSessionArgs == null)
1745         parserSessionArgs =
1746            ParserSessionArgs
1747               .create()
1748               .properties(getProperties())
1749               .javaMethod(getJavaMethod())
1750               .locale(getLocale())
1751               .timeZone(getHeaders().getTimeZone())
1752               .debug(isDebug() ? true : null);
1753      return parserSessionArgs;
1754   }
1755
1756   /**
1757    * Logger.
1758    *
1759    * <div class='warn'>
1760    *    <b>Deprecated</b> - Use standard logging APIs
1761    * </div>
1762    *
1763    * <p>
1764    * Shortcut for calling <c>getContext().getLogger()</c>.
1765    *
1766    * <h5 class='section'>Example:</h5>
1767    * <p class='bcode w800'>
1768    *    <ja>@RestMethod</ja>(...)
1769    *    <jk>public void</jk> doGet(RestRequest req) {
1770    *
1771    *       req.getLogger().logObjects(<jsf>FINE</jsf>, <js>"Request query parameters = {0}"</js>, req.getQuery());
1772    *    }
1773    * </p>
1774    *
1775    * <ul class='notes'>
1776    *    <li>
1777    *       The {@link RestLogger} object can also be passed as a parameter on the method.
1778    * </ul>
1779    *
1780    * <ul class='seealso'>
1781    *    <li class='jf'>{@link org.apache.juneau.rest.RestContext#REST_logger}
1782    *    <li class='jac'>{@link org.apache.juneau.rest.RestLogger}
1783    *    <li class='jm'>{@link org.apache.juneau.rest.RestServlet#log(Level, String, Object...)}
1784    *    <li class='jm'>{@link org.apache.juneau.rest.RestServlet#logObjects(Level, String, Object...)}
1785    *    <li class='link'>{@doc juneau-rest-server.LoggingAndDebugging}
1786    * </ul>
1787    *
1788    * @return
1789    *    The logger associated with the resource context.
1790    *    <br>Never <jk>null</jk>.
1791    */
1792   @Deprecated
1793   public RestCallLogger getLogger() {
1794      return null;
1795   }
1796
1797   /**
1798    * Returns the logging configuration defined on the Java method that this request is executing against.
1799    *
1800    * @return The logging configuration defined on the Java method that this request is executing against.
1801    */
1802   public RestCallLoggerConfig getCallLoggerConfig() {
1803      if (restJavaMethod != null)
1804         return restJavaMethod.getCallLoggerConfig();
1805      return RestCallLoggerConfig.DEFAULT;
1806   }
1807
1808   void close() {
1809      if (cf != null) {
1810         try {
1811            cf.close();
1812         } catch (IOException e) {
1813            e.printStackTrace();
1814         }
1815      }
1816   }
1817
1818   /**
1819    * Returns metadata about the specified response object if it's annotated with {@link Response @Response}.
1820    *
1821    * @param o The response POJO.
1822    * @return Metadata about the specified response object, or <jk>null</jk> if it's not annotated with {@link Response @Response}.
1823    */
1824   public ResponseBeanMeta getResponseBeanMeta(Object o) {
1825      return restJavaMethod == null ? null : restJavaMethod.getResponseBeanMeta(o);
1826   }
1827
1828   /**
1829    * Returns metadata about the specified response object if it's annotated with {@link ResponseHeader @ResponseHeader}.
1830    *
1831    * @param o The response POJO.
1832    * @return Metadata about the specified response object, or <jk>null</jk> if it's not annotated with {@link ResponseHeader @ResponseHeader}.
1833    */
1834   public ResponsePartMeta getResponseHeaderMeta(Object o) {
1835      return restJavaMethod == null ? null : restJavaMethod.getResponseHeaderMeta(o);
1836   }
1837
1838   /**
1839    * Returns metadata about the specified response object if it's annotated with {@link ResponseBody @ResponseBody}.
1840    *
1841    * @param o The response POJO.
1842    * @return Metadata about the specified response object, or <jk>null</jk> if it's not annotated with {@link ResponseBody @ResponseBody}.
1843    */
1844   public ResponsePartMeta getResponseBodyMeta(Object o) {
1845      return restJavaMethod == null ? null : restJavaMethod.getResponseBodyMeta(o);
1846   }
1847
1848   /**
1849    * Returns the schema generator with settings assigned on this method and class.
1850    *
1851    * @return The schema generator.
1852    */
1853   public JsonSchemaGenerator getJsonSchemaGenerator() {
1854      return restJavaMethod == null ? context.getJsonSchemaGenerator() : restJavaMethod.getJsonSchemaGenerator();
1855   }
1856
1857   /**
1858    * Returns the wrapped servlet request.
1859    *
1860    * @return The wrapped servlet request.
1861    */
1862   protected HttpServletRequest getInner() {
1863      return inner;
1864   }
1865
1866   //-----------------------------------------------------------------------------------------------------------------
1867   // Utility methods
1868   //-----------------------------------------------------------------------------------------------------------------
1869
1870   /*
1871    * Converts an Accept-Language value entry to a Locale.
1872    */
1873   private static Locale toLocale(String lang) {
1874      String country = "";
1875      int i = lang.indexOf('-');
1876      if (i > -1) {
1877         country = lang.substring(i+1).trim();
1878         lang = lang.substring(0,i).trim();
1879      }
1880      return new Locale(lang, country);
1881   }
1882
1883   void setJavaMethod(Method method) {
1884      this.javaMethod = method;
1885   }
1886}