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