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