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<Person> 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<Tag> 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>"<h1>$R{siteName}</h1>"</js>, 1042 * <js>"<h2>$R{resourceTitle}</h2>"</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>&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>&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 &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<String,Object> 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}