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.*; 022 023import java.io.*; 024import java.lang.reflect.*; 025import java.lang.reflect.Method; 026import java.lang.reflect.Proxy; 027import java.net.*; 028import java.nio.charset.*; 029import java.text.*; 030import java.util.*; 031import java.util.logging.*; 032 033import javax.servlet.*; 034import javax.servlet.http.*; 035 036import org.apache.juneau.*; 037import org.apache.juneau.config.*; 038import org.apache.juneau.dto.swagger.*; 039import org.apache.juneau.http.*; 040import org.apache.juneau.http.ReaderResource; 041import org.apache.juneau.http.StreamResource; 042import org.apache.juneau.http.annotation.*; 043import org.apache.juneau.http.annotation.Body; 044import org.apache.juneau.http.annotation.FormData; 045import org.apache.juneau.http.annotation.Header; 046import org.apache.juneau.http.annotation.Path; 047import org.apache.juneau.http.annotation.Query; 048import org.apache.juneau.http.annotation.Response; 049import org.apache.juneau.httppart.*; 050import org.apache.juneau.httppart.bean.*; 051import org.apache.juneau.internal.*; 052import org.apache.juneau.jsonschema.*; 053import org.apache.juneau.marshall.*; 054import org.apache.juneau.oapi.*; 055import org.apache.juneau.parser.*; 056import org.apache.juneau.remote.*; 057import org.apache.juneau.rest.annotation.*; 058import org.apache.juneau.rest.exception.*; 059import org.apache.juneau.rest.helper.*; 060import org.apache.juneau.rest.util.*; 061import org.apache.juneau.rest.widget.*; 062import org.apache.juneau.serializer.*; 063import org.apache.juneau.svl.*; 064import org.apache.juneau.uon.*; 065import org.apache.juneau.utils.*; 066 067/** 068 * Represents an HTTP request for a REST resource. 069 * 070 * <p> 071 * Equivalent to {@link HttpServletRequest} except with some additional convenience methods. 072 * 073 * <p> 074 * For reference, given the URL <js>"http://localhost:9080/contextRoot/servletPath/foo?bar=baz#qux"</js>, the 075 * following methods return the following values.... 076 * <table class='styled'> 077 * <tr><th>Method</th><th>Value</th></tr> 078 * <tr><td>{@code getContextPath()}</td><td>{@code /contextRoot}</td></tr> 079 * <tr><td>{@code getPathInfo()}</td><td>{@code /foo}</td></tr> 080 * <tr><td>{@code getPathTranslated()}</td><td>{@code path-to-deployed-war-on-filesystem/foo}</td></tr> 081 * <tr><td>{@code getQueryString()}</td><td>{@code bar=baz}</td></tr> 082 * <tr><td>{@code getRequestURI()}</td><td>{@code /contextRoot/servletPath/foo}</td></tr> 083 * <tr><td>{@code getRequestURL()}</td><td>{@code http://localhost:9080/contextRoot/servletPath/foo}</td></tr> 084 * <tr><td>{@code getServletPath()}</td><td>{@code /servletPath}</td></tr> 085 * </table> 086 * 087 * <ul class='seealso'> 088 * <li class='link'>{@doc juneau-rest-server.RestMethod.RestRequest} 089 * </ul> 090 */ 091@SuppressWarnings({ "unchecked", "unused" }) 092public final class RestRequest extends HttpServletRequestWrapper { 093 094 private HttpServletRequest inner; 095 private final RestContext context; 096 private RestMethodContext restJavaMethod; 097 098 private final String method; 099 private RequestBody body; 100 private Method javaMethod; 101 @SuppressWarnings("deprecation") 102 private RequestProperties properties; 103 private BeanSession beanSession; 104 private VarResolverSession varSession; 105 private final RequestQuery queryParams; 106 private RequestFormData formData; 107 private RequestPath pathParams; 108 private boolean isPost; 109 private UriContext uriContext; 110 private String charset, authorityPath; 111 private RequestHeaders headers; 112 private RequestAttributes attributes; 113 private Config cf; 114 private Swagger swagger; 115 private SerializerSessionArgs serializerSessionArgs; 116 private ParserSessionArgs parserSessionArgs; 117 private RestResponse res; 118 119 /** 120 * Constructor. 121 */ 122 RestRequest(RestContext context, HttpServletRequest req) throws ServletException { 123 super(req); 124 this.inner = req; 125 this.context = context; 126 127 try { 128 isPost = req.getMethod().equalsIgnoreCase("POST"); 129 130 // If this is a POST, we want to parse the query parameters ourselves to prevent 131 // the servlet code from processing the HTTP body as URL-Encoded parameters. 132 queryParams = new RequestQuery(this); 133 if (isPost) 134 RestUtils.parseQuery(getQueryString(), queryParams); 135 else 136 queryParams.putAll(req.getParameterMap()); 137 138 // Get the HTTP method. 139 // Can be overridden through a "method" GET attribute. 140 String _method = super.getMethod(); 141 142 String m = getQuery().getString("method"); 143 if (m != null) { 144 Set<String> s = context.getAllowedMethodParams(); 145 if (! s.isEmpty() && (s.contains("*") || s.contains(m))) 146 _method = m; 147 } 148 149 m = req.getHeader("X-Method"); 150 if (m != null) { 151 Set<String> s = context.getAllowedMethodHeaders(); 152 if (! s.isEmpty() && (s.contains("*") || s.contains(m))) 153 _method = m; 154 } 155 156 method = _method; 157 158 headers = new RequestHeaders(this); 159 for (Enumeration<String> e = getHeaderNames(); e.hasMoreElements();) { 160 String name = e.nextElement(); 161 headers.put(name, super.getHeaders(name)); 162 } 163 164 body = new RequestBody(this); 165 166 if (context.isAllowBodyParam()) { 167 String b = getQuery().getString("body"); 168 if (b != null) { 169 headers.put("Content-Type", UonSerializer.DEFAULT.getResponseContentType()); 170 body.load(MediaType.UON, UonParser.DEFAULT, b.getBytes(UTF8)); 171 } 172 } 173 174 Set<String> s = context.getAllowedHeaderParams(); 175 if (! s.isEmpty()) 176 headers.queryParams(queryParams, s); 177 178 this.pathParams = new RequestPath(this); 179 180 } catch (RestException e) { 181 throw e; 182 } catch (Exception e) { 183 throw new ServletException(e); 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("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 (RestException e) { 1019 throw e; 1020 } catch (Exception e) { 1021 throw new InternalServerError(e); 1022 } 1023 } 1024 1025 /** 1026 * Returns the localized site name. 1027 * 1028 * <p> 1029 * The site name is intended to be a title that can be applied to the entire site. 1030 * 1031 * <p> 1032 * One possible use is if you want to add the same title to the top of all pages by defining a header on a 1033 * common parent class like so: 1034 * <p class='bcode w800'> 1035 * htmldoc=<ja>@HtmlDoc</ja>( 1036 * header={ 1037 * <js>"<h1>$R{siteName}</h1>"</js>, 1038 * <js>"<h2>$R{resourceTitle}</h2>"</js> 1039 * } 1040 * ) 1041 * </p> 1042 * 1043 * <p> 1044 * Equivalent to calling {@link RestInfoProvider#getSiteName(RestRequest)} with this object. 1045 * 1046 * @return The localized site name. 1047 */ 1048 public String getSiteName() { 1049 try { 1050 return context.getInfoProvider().getSiteName(this); 1051 } catch (RestException e) { 1052 throw e; 1053 } catch (Exception e) { 1054 throw new InternalServerError(e); 1055 } 1056 } 1057 1058 /** 1059 * Returns the localized resource title. 1060 * 1061 * <p> 1062 * Equivalent to calling {@link RestInfoProvider#getTitle(RestRequest)} with this object. 1063 * 1064 * @return The localized resource title. 1065 */ 1066 public String getResourceTitle() { 1067 try { 1068 return context.getInfoProvider().getTitle(this); 1069 } catch (RestException e) { 1070 throw e; 1071 } catch (Exception e) { 1072 throw new InternalServerError(e); 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 (RestException e) { 1088 throw e; 1089 } catch (Exception e) { 1090 throw new InternalServerError(e); 1091 } 1092 } 1093 1094 /** 1095 * Returns the localized method summary. 1096 * 1097 * <p> 1098 * Equivalent to calling {@link RestInfoProvider#getMethodSummary(Method, RestRequest)} with this object. 1099 * 1100 * @return The localized method description. 1101 */ 1102 public String getMethodSummary() { 1103 try { 1104 return context.getInfoProvider().getMethodSummary(javaMethod, this); 1105 } catch (RestException e) { 1106 throw e; 1107 } catch (Exception e) { 1108 throw new InternalServerError(e); 1109 } 1110 } 1111 1112 /** 1113 * Returns the localized method description. 1114 * 1115 * <p> 1116 * Equivalent to calling {@link RestInfoProvider#getMethodDescription(Method, RestRequest)} with this object. 1117 * 1118 * @return The localized method description. 1119 */ 1120 public String getMethodDescription() { 1121 try { 1122 return context.getInfoProvider().getMethodDescription(javaMethod, this); 1123 } catch (RestException e) { 1124 throw e; 1125 } catch (Exception e) { 1126 throw new InternalServerError(e); 1127 } 1128 } 1129 1130 //----------------------------------------------------------------------------------------------------------------- 1131 // Other methods 1132 //----------------------------------------------------------------------------------------------------------------- 1133 1134 /** 1135 * Returns the serializers associated with this request. 1136 * 1137 * <ul class='seealso'> 1138 * <li class='link'>{@doc juneau-rest-server.Serializers} 1139 * </ul> 1140 * 1141 * @return The serializers associated with this request. 1142 */ 1143 public SerializerGroup getSerializers() { 1144 return restJavaMethod == null ? SerializerGroup.EMPTY : restJavaMethod.serializers; 1145 } 1146 1147 /** 1148 * Returns the parsers associated with this request. 1149 * 1150 * <ul class='seealso'> 1151 * <li class='link'>{@doc juneau-rest-server.Parsers} 1152 * </ul> 1153 * 1154 * @return The parsers associated with this request. 1155 */ 1156 public ParserGroup getParsers() { 1157 return restJavaMethod == null ? ParserGroup.EMPTY : restJavaMethod.parsers; 1158 } 1159 1160 /** 1161 * Returns the part serializer associated with this request. 1162 * 1163 * @return The part serializer associated with this request. 1164 */ 1165 public HttpPartParser getPartParser() { 1166 return restJavaMethod == null ? OpenApiParser.DEFAULT : restJavaMethod.partParser; 1167 } 1168 1169 /** 1170 * Returns the part serializer associated with this request. 1171 * 1172 * @return The part serializer associated with this request. 1173 */ 1174 public HttpPartSerializer getPartSerializer() { 1175 return restJavaMethod == null ? OpenApiSerializer.DEFAULT : restJavaMethod.partSerializer; 1176 } 1177 1178 /** 1179 * Returns the method of this request. 1180 * 1181 * <p> 1182 * If <c>allowHeaderParams</c> init parameter is <jk>true</jk>, then first looks for 1183 * <c>&method=xxx</c> in the URL query string. 1184 */ 1185 @Override /* ServletRequest */ 1186 public String getMethod() { 1187 return method; 1188 } 1189 1190 /** 1191 * Returns the HTTP 1.1 method name of the request as an enum. 1192 * 1193 * <p> 1194 * Note that non-RFC2616 method names resolve as {@link HttpMethod#OTHER}. 1195 * 1196 * @return The HTTP method. 1197 */ 1198 public HttpMethod getHttpMethod() { 1199 return HttpMethod.forString(method); 1200 } 1201 1202 @Override /* ServletRequest */ 1203 public int getContentLength() { 1204 return getBody().getContentLength(); 1205 } 1206 1207 int getRawContentLength() { 1208 return super.getContentLength(); 1209 } 1210 1211 /** 1212 * Returns <jk>true</jk> if <c>&plainText=true</c> was specified as a URL parameter. 1213 * 1214 * <p> 1215 * This indicates that the <c>Content-Type</c> of the output should always be set to <js>"text/plain"</js> 1216 * to make it easy to render in a browser. 1217 * 1218 * <p> 1219 * This feature is useful for debugging. 1220 * 1221 * @return <jk>true</jk> if {@code &plainText=true} was specified as a URL parameter 1222 */ 1223 public boolean isPlainText() { 1224 return "true".equals(getQuery().getString("plainText", "false")); 1225 } 1226 1227 /** 1228 * Returns the resource bundle for the request locale. 1229 * 1230 * <h5 class='section'>Example:</h5> 1231 * <p class='bcode w800'> 1232 * <ja>@RestMethod</ja>(...) 1233 * <jk>public</jk> String sayHello(RestRequest req, <ja>@Query</ja>(<js>"user"</js>) String user) { 1234 * 1235 * <jc>// Get message bundle.</jc> 1236 * MessageBundle mb = req.getMessageBundle(); 1237 * 1238 * <jc>// Return a localized message.</jc> 1239 * <jk>return</jk> mb.getString(<js>"hello.message"</js>, user); 1240 * } 1241 * </p> 1242 * 1243 * <ul class='notes'> 1244 * <li> 1245 * The {@link MessageBundle} object can also be passed as a parameter on the method. 1246 * </ul> 1247 * 1248 * <ul class='seealso'> 1249 * <li class='jf'>{@link org.apache.juneau.rest.RestContext#REST_messages} 1250 * <li class='jm'>{@link org.apache.juneau.rest.RestRequest#getMessage(String,Object...)} 1251 * <li class='link'>{@doc juneau-rest-server.Messages} 1252 * </ul> 1253 * 1254 * @return 1255 * The resource bundle. 1256 * <br>Never <jk>null</jk>. 1257 */ 1258 public MessageBundle getMessageBundle() { 1259 return context.getMessages().getBundle(getLocale()); 1260 } 1261 1262 /** 1263 * Shortcut method for calling {@link MessageBundle#getString(Locale, String, Object...)} based on the request locale. 1264 * 1265 * @param key The message key. 1266 * @param args Optional {@link MessageFormat}-style arguments. 1267 * @return The localized message. 1268 */ 1269 public String getMessage(String key, Object...args) { 1270 return context.getMessages().getString(getLocale(), key, args); 1271 } 1272 1273 /** 1274 * Returns the resource context handling the request. 1275 * 1276 * <p> 1277 * Can be used to access servlet-init parameters or annotations during requests, such as in calls to 1278 * {@link RestGuard#guard(RestRequest, RestResponse)}.. 1279 * 1280 * @return The resource context handling the request. 1281 */ 1282 public RestContext getContext() { 1283 return context; 1284 } 1285 1286 /** 1287 * Returns the java method handling the request. 1288 * 1289 * <p> 1290 * Can be used to access the method name or method annotations during requests, such as in calls to 1291 * {@link RestGuard#guard(RestRequest, RestResponse)}. 1292 * 1293 * <ul class='notes'> 1294 * <li> 1295 * This returns <jk>null</jk> when evaluating servlet-level guards since the method has not been resolved at that 1296 * point of execution. 1297 * </ul> 1298 * 1299 * @return The Java method handling the request, or <c>null</c> if the method has not yet been resolved. 1300 */ 1301 public Method getJavaMethod() { 1302 return javaMethod; 1303 } 1304 1305 /** 1306 * Returns the {@link BeanSession} associated with this request. 1307 * 1308 * @return The request bean session. 1309 */ 1310 public BeanSession getBeanSession() { 1311 return beanSession; 1312 } 1313 1314 /** 1315 * Returns <jk>true</jk> if debug mode is enabled. 1316 * 1317 * 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. 1318 * 1319 * @return <jk>true</jk> if debug mode is enabled. 1320 */ 1321 public boolean isDebug() { 1322 Boolean b = ObjectUtils.castOrNull(getAttribute("Debug"), Boolean.class); 1323 if (b != null) 1324 return b; 1325 Enablement e = context.getDebug(); 1326 if (e == TRUE) 1327 return true; 1328 if (e == FALSE) 1329 return false; 1330 return "true".equalsIgnoreCase(getHeader("X-Debug")); 1331 } 1332 1333 /** 1334 * Sets the <js>"Exception"</js> attribute to the specified throwable. 1335 * 1336 * <p> 1337 * This exception is used by {@link BasicRestCallLogger} for logging purposes. 1338 * 1339 * @param t The attribute value. 1340 * @return This object (for method chaining). 1341 */ 1342 public RestRequest setException(Throwable t) { 1343 setAttribute("Exception", t); 1344 return this; 1345 } 1346 1347 /** 1348 * Sets the <js>"NoTrace"</js> attribute to the specified boolean. 1349 * 1350 * <p> 1351 * This flag is used by {@link BasicRestCallLogger} and tells it not to log the current request. 1352 * 1353 * @param b The attribute value. 1354 * @return This object (for method chaining). 1355 */ 1356 public RestRequest setNoTrace(Boolean b) { 1357 setAttribute("NoTrace", b); 1358 return this; 1359 } 1360 1361 /** 1362 * Shortcut for calling <c>setNoTrace(<jk>true</jk>)</c>. 1363 * 1364 * @return This object (for method chaining). 1365 */ 1366 public RestRequest setNoTrace() { 1367 return setNoTrace(true); 1368 } 1369 1370 /** 1371 * Sets the <js>"Debug"</js> attribute to the specified boolean. 1372 * 1373 * <p> 1374 * This flag is used by {@link BasicRestCallLogger} to help determine how a request should be logged. 1375 * 1376 * @param b The attribute value. 1377 * @return This object (for method chaining). 1378 * @throws IOException If body could not be cached. 1379 */ 1380 public RestRequest setDebug(Boolean b) throws IOException { 1381 setAttribute("Debug", b); 1382 if (b) 1383 inner = CachingHttpServletRequest.wrap(inner); 1384 return this; 1385 } 1386 1387 /** 1388 * Shortcut for calling <c>setDebug(<jk>true</jk>)</c>. 1389 * 1390 * @return This object (for method chaining). 1391 * @throws IOException If body could not be cached. 1392 */ 1393 public RestRequest setDebug() throws IOException { 1394 return setDebug(true); 1395 } 1396 1397 /** 1398 * Request-level variable resolver session. 1399 * 1400 * <p> 1401 * Used to resolve SVL variables in text. 1402 * 1403 * <h5 class='section'>Example:</h5> 1404 * <p class='bcode w800'> 1405 * <ja>@RestMethod</ja>(...) 1406 * <jk>public</jk> String sayHello(RestRequest req) { 1407 * 1408 * <jc>// Get var resolver session.</jc> 1409 * VarResolverSession session = getVarResolverSession(); 1410 * 1411 * <jc>// Use it to construct a customized message from a query parameter.</jc> 1412 * <jk>return</jk> session.resolve(<js>"Hello $RQ{user}!"</js>); 1413 * } 1414 * </p> 1415 * 1416 * <ul class='notes'> 1417 * <li> 1418 * The {@link VarResolverSession} object can also be passed as a parameter on the method. 1419 * </ul> 1420 * 1421 * <ul class='seealso'> 1422 * <li class='jm'>{@link org.apache.juneau.rest.RestContext#getVarResolver()} 1423 * <li class='link'>{@doc juneau-rest-server.SvlVariables} 1424 * </ul> 1425 * 1426 * @return The variable resolver for this request. 1427 */ 1428 public VarResolverSession getVarResolverSession() { 1429 if (varSession == null) 1430 varSession = context 1431 .getVarResolver() 1432 .createSession(context.getCallHandler().getSessionObjects(this, context.getResponse())) 1433 .sessionObject("req", this) 1434 .sessionObject("res", res); 1435 return varSession; 1436 } 1437 1438 /** 1439 * Returns an instance of a {@link ReaderResource} that represents the contents of a resource text file from the 1440 * classpath. 1441 * 1442 * <p> 1443 * <h5 class='section'>Example:</h5> 1444 * <p class='bcode w800'> 1445 * <jc>// A rest method that (unsafely!) returns the contents of a localized file </jc> 1446 * <jc>// from the classpath and resolves any SVL variables embedded in it.</jc> 1447 * <ja>@RestMethod</ja>(...) 1448 * <jk>public</jk> String myMethod(RestRequest req, <ja>@Query</ja>(<js>"file"</js>) String file) { 1449 * <jk>return</jk> req.getClasspathResourceAsString(file, <jk>true</jk>); 1450 * } 1451 * </p> 1452 * 1453 * <ul class='seealso'> 1454 * <li class='jf'>{@link org.apache.juneau.rest.RestContext#REST_classpathResourceFinder} 1455 * <li class='jm'>{@link org.apache.juneau.rest.RestRequest#getClasspathReaderResource(String, boolean)} 1456 * <li class='jm'>{@link org.apache.juneau.rest.RestRequest#getClasspathReaderResource(String)} 1457 * </ul> 1458 * 1459 * @param name The name of the resource (i.e. the value normally passed to {@link Class#getResourceAsStream(String)}. 1460 * @param resolveVars 1461 * If <jk>true</jk>, any SVL variables will be 1462 * resolved by the variable resolver returned by {@link #getVarResolverSession()}. 1463 * <br>See {@link RestContext#getVarResolver()} for the list of supported variables. 1464 * @param mediaType The value to set as the <js>"Content-Type"</js> header for this object. 1465 * @param cached If <jk>true</jk>, the resource will be read into a byte array for fast serialization. 1466 * @return A new reader resource, or <jk>null</jk> if resource could not be found. 1467 * @throws IOException Thrown by underlying stream. 1468 */ 1469 public ReaderResource getClasspathReaderResource(String name, boolean resolveVars, MediaType mediaType, boolean cached) throws IOException { 1470 String s = context.getClasspathResourceAsString(name, getLocale()); 1471 if (s == null) 1472 return null; 1473 ResolvingReaderResource.Builder b = ResolvingReaderResource.create().mediaType(mediaType).contents(s); 1474 if (resolveVars) 1475 b.varResolver(getVarResolverSession()); 1476 if (cached) 1477 b.cached(); 1478 return b.build(); 1479 } 1480 1481 /** 1482 * Same as {@link #getClasspathReaderResource(String, boolean, MediaType, boolean)} except uses the resource mime-type map 1483 * constructed using {@link RestContextBuilder#mimeTypes(String...)} to determine the media type. 1484 * 1485 * @param name The name of the resource (i.e. the value normally passed to {@link Class#getResourceAsStream(String)}. 1486 * @param resolveVars 1487 * If <jk>true</jk>, any SVL variables will be 1488 * resolved by the variable resolver returned by {@link #getVarResolverSession()}. 1489 * <br>See {@link RestContext#getVarResolver()} for the list of supported variables. 1490 * @return A new reader resource, or <jk>null</jk> if resource could not be found. 1491 * @throws IOException Thrown by underlying stream. 1492 */ 1493 public ReaderResource getClasspathReaderResource(String name, boolean resolveVars) throws IOException { 1494 return getClasspathReaderResource(name, resolveVars, MediaType.forString(context.getMediaTypeForName(name)), false); 1495 } 1496 1497 /** 1498 * Same as {@link #getClasspathReaderResource(String, boolean)} with <code>resolveVars == <jk>false</jk></code> 1499 * 1500 * @param name The name of the resource (i.e. the value normally passed to {@link Class#getResourceAsStream(String)}. 1501 * @return A new reader resource, or <jk>null</jk> if resource could not be found. 1502 * @throws IOException Thrown by underlying stream. 1503 */ 1504 public ReaderResource getClasspathReaderResource(String name) throws IOException { 1505 return getClasspathReaderResource(name, false, MediaType.forString(context.getMediaTypeForName(name)), false); 1506 } 1507 1508 /** 1509 * Returns an instance of a {@link StreamResource} that represents the contents of a resource binary file from the 1510 * classpath. 1511 * 1512 * <ul class='seealso'> 1513 * <li class='jf'>{@link org.apache.juneau.rest.RestContext#REST_classpathResourceFinder} 1514 * <li class='jm'>{@link org.apache.juneau.rest.RestRequest#getClasspathStreamResource(String)} 1515 * </ul> 1516 * 1517 * @param name The name of the resource (i.e. the value normally passed to {@link Class#getResourceAsStream(String)}. 1518 * @param mediaType The value to set as the <js>"Content-Type"</js> header for this object. 1519 * @param cached If <jk>true</jk>, the resource will be read into a byte array for fast serialization. 1520 * @return A new stream resource, or <jk>null</jk> if resource could not be found. 1521 * @throws IOException Thrown by underlying stream. 1522 */ 1523 @SuppressWarnings("resource") 1524 public StreamResource getClasspathStreamResource(String name, MediaType mediaType, boolean cached) throws IOException { 1525 InputStream is = context.getClasspathResource(name, getLocale()); 1526 if (is == null) 1527 return null; 1528 StreamResource.Builder b = StreamResource.create().mediaType(mediaType).contents(is); 1529 if (cached) 1530 b.cached(); 1531 return b.build(); 1532 } 1533 1534 /** 1535 * Same as {@link #getClasspathStreamResource(String, MediaType, boolean)} except uses the resource mime-type map 1536 * constructed using {@link RestContextBuilder#mimeTypes(String...)} to determine the media type. 1537 * 1538 * @param name The name of the resource (i.e. the value normally passed to {@link Class#getResourceAsStream(String)}. 1539 * @return A new stream resource, or <jk>null</jk> if resource could not be found. 1540 * @throws IOException Thrown by underlying stream. 1541 */ 1542 public StreamResource getClasspathStreamResource(String name) throws IOException { 1543 return getClasspathStreamResource(name, MediaType.forString(context.getMediaTypeForName(name)), false); 1544 } 1545 1546 /** 1547 * Config file associated with the resource. 1548 * 1549 * <p> 1550 * Returns a config file with session-level variable resolution. 1551 * 1552 * The config file is identified via one of the following: 1553 * <ul class='javatree'> 1554 * <li class='ja'>{@link RestResource#config()} 1555 * <li class='jm'>{@link RestContextBuilder#config(Config)} 1556 * </ul> 1557 * 1558 * <h5 class='section'>Example:</h5> 1559 * <p class='bcode w800'> 1560 * <ja>@RestMethod</ja>(...) 1561 * <jk>public void</jk> doGet(RestRequest req) { 1562 * 1563 * <jc>// Get config file.</jc> 1564 * Config cf = req.getConfig(); 1565 * 1566 * <jc>// Get simple values from config file.</jc> 1567 * <jk>int</jk> timeout = cf.getInt(<js>"MyResource/timeout"</js>, 10000); 1568 * 1569 * <jc>// Get complex values from config file.</jc> 1570 * MyBean b = cf.getObject(<js>"MyResource/myBean"</js>, MyBean.<jk>class</jk>); 1571 * } 1572 * </p> 1573 * 1574 * <ul class='notes'> 1575 * <li> 1576 * The {@link Config} object can also be passed as a parameter on the method. 1577 * </ul> 1578 * 1579 * <ul class='seealso'> 1580 * <li class='link'>{@doc juneau-rest-server.ConfigurationFiles} 1581 * </ul> 1582 * 1583 * @return 1584 * The config file associated with the resource, or <jk>null</jk> if resource does not have a config file 1585 * associated with it. 1586 */ 1587 public Config getConfig() { 1588 if (cf == null) 1589 cf = context.getConfig().resolving(getVarResolverSession()); 1590 return cf; 1591 } 1592 1593 /** 1594 * Returns the widgets used for resolving <js>"$W{...}"</js> string variables. 1595 * 1596 * @return 1597 * The widgets used for resolving <js>"$W{...}"</js> string variables. 1598 * Never <jk>null</jk>. 1599 * 1600 * @deprecated No replacement. 1601 */ 1602 @Deprecated 1603 public Map<String,Widget> getWidgets() { 1604 return restJavaMethod == null ? Collections.<String,Widget>emptyMap() : restJavaMethod.widgets; 1605 } 1606 1607 /** 1608 * Creates a proxy interface to retrieve HTTP parts of this request as a proxy bean. 1609 * 1610 * <h5 class='section'>Examples:</h5> 1611 * <p class='bcode w800'> 1612 * <ja>@RestMethod</ja>(path=<js>"/mypath/{p1}/{p2}/*"</js>) 1613 * <jk>public void</jk> myMethod(@Request MyRequest rb) {...} 1614 * 1615 * <jk>public interface</jk> MyRequest { 1616 * 1617 * <ja>@Path</ja> <jc>// Path variable name inferred from getter.</jc> 1618 * String getP1(); 1619 * 1620 * <ja>@Path</ja>(<js>"p2"</js>) 1621 * String getX(); 1622 * 1623 * <ja>@Path</ja>(<js>"/*"</js>) 1624 * String getRemainder(); 1625 * 1626 * <ja>@Query</ja> 1627 * String getQ1(); 1628 * 1629 * <jc>// Schema-based query parameter: Pipe-delimited lists of comma-delimited lists of integers.</jc> 1630 * <ja>@Query</ja>( 1631 * collectionFormat=<js>"pipes"</js> 1632 * items=<ja>@Items</ja>( 1633 * items=<ja>@SubItems</ja>( 1634 * collectionFormat=<js>"csv"</js> 1635 * type=<js>"integer"</js> 1636 * ) 1637 * ) 1638 * ) 1639 * <jk>int</jk>[][] getQ3(); 1640 * 1641 * <ja>@Header</ja>(<js>"*"</js>) 1642 * Map<String,Object> getHeaders(); 1643 * </p> 1644 * 1645 * @param c The request bean interface to instantiate. 1646 * @return A new request bean proxy for this REST request. 1647 */ 1648 public <T> T getRequest(Class<T> c) { 1649 return getRequest(RequestBeanMeta.create(c, getContext().getPropertyStore())); 1650 } 1651 1652 /** 1653 * Same as {@link #getRequest(Class)} but used on pre-instantiated {@link RequestBeanMeta} objects. 1654 * 1655 * @param rbm The metadata about the request bean interface to create. 1656 * @return A new request bean proxy for this REST request. 1657 */ 1658 public <T> T getRequest(final RequestBeanMeta rbm) { 1659 try { 1660 Class<T> c = (Class<T>)rbm.getClassMeta().getInnerClass(); 1661 final BeanMeta<T> bm = getBeanSession().getBeanMeta(c); 1662 return (T)Proxy.newProxyInstance( 1663 c.getClassLoader(), 1664 new Class[] { c }, 1665 new InvocationHandler() { 1666 @Override /* InvocationHandler */ 1667 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 1668 RequestBeanPropertyMeta pm = rbm.getProperty(method.getName()); 1669 if (pm != null) { 1670 HttpPartParser pp = pm.getParser(getPartParser()); 1671 HttpPartSchema schema = pm.getSchema(); 1672 String name = pm.getPartName(); 1673 ClassMeta<?> type = getContext().getClassMeta(method.getGenericReturnType()); 1674 HttpPartType pt = pm.getPartType(); 1675 if (pt == HttpPartType.BODY) 1676 return getBody().schema(schema).asType(type); 1677 if (pt == QUERY) 1678 return getQuery().get(pp, schema, name, type); 1679 if (pt == FORMDATA) 1680 return getFormData().get(pp, schema, name, type); 1681 if (pt == HEADER) 1682 return getHeaders().get(pp, schema, name, type); 1683 if (pt == PATH) 1684 return getPathMatch().get(pp, schema, name, type); 1685 } 1686 return null; 1687 } 1688 1689 }); 1690 } catch (Exception e) { 1691 throw new RuntimeException(e); 1692 } 1693 } 1694 1695 @Override /* Object */ 1696 public String toString() { 1697 StringBuilder sb = new StringBuilder("\n").append(getDescription()).append("\n"); 1698 sb.append("---Headers---\n"); 1699 for (Enumeration<String> e = getHeaderNames(); e.hasMoreElements();) { 1700 String h = e.nextElement(); 1701 sb.append("\t").append(h).append(": ").append(getHeader(h)).append("\n"); 1702 } 1703 sb.append("---Default Servlet Headers---\n"); 1704 for (Map.Entry<String,Object> e : context.getDefaultRequestHeaders().entrySet()) { 1705 sb.append("\t").append(e.getKey()).append(": ").append(e.getValue()).append("\n"); 1706 } 1707 if (javaMethod == null) { 1708 sb.append("***init() not called yet!***\n"); 1709 } else if (method.equals("PUT") || method.equals("POST")) { 1710 try { 1711 sb.append("---Body UTF-8---\n"); 1712 sb.append(body.asString()).append("\n"); 1713 sb.append("---Body Hex---\n"); 1714 sb.append(body.asSpacedHex()).append("\n"); 1715 } catch (Exception e1) { 1716 sb.append(e1.getLocalizedMessage()); 1717 } 1718 } 1719 return sb.toString(); 1720 } 1721 1722 /** 1723 * Returns the session arguments to pass to serializers. 1724 * 1725 * @return The session arguments to pass to serializers. 1726 */ 1727 public SerializerSessionArgs getSerializerSessionArgs() { 1728 if (serializerSessionArgs == null) 1729 serializerSessionArgs = SerializerSessionArgs 1730 .create() 1731 .properties(getProperties()) 1732 .javaMethod(getJavaMethod()) 1733 .locale(getLocale()) 1734 .timeZone(getHeaders().getTimeZone()) 1735 .debug(isDebug() ? true : null) 1736 .uriContext(getUriContext()) 1737 .resolver(getVarResolverSession()) 1738 .useWhitespace(isPlainText() ? true : null); 1739 return serializerSessionArgs; 1740 } 1741 1742 /** 1743 * Returns the session arguments to pass to parsers. 1744 * 1745 * @return The session arguments to pass to parsers. 1746 */ 1747 public ParserSessionArgs getParserSessionArgs() { 1748 if (parserSessionArgs == null) 1749 parserSessionArgs = 1750 ParserSessionArgs 1751 .create() 1752 .properties(getProperties()) 1753 .javaMethod(getJavaMethod()) 1754 .locale(getLocale()) 1755 .timeZone(getHeaders().getTimeZone()) 1756 .debug(isDebug() ? true : null); 1757 return parserSessionArgs; 1758 } 1759 1760 /** 1761 * Logger. 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 Use standard logging APIs. 1793 */ 1794 @Deprecated 1795 public RestCallLogger getLogger() { 1796 return null; 1797 } 1798 1799 /** 1800 * Returns the logging configuration defined on the Java method that this request is executing against. 1801 * 1802 * @return The logging configuration defined on the Java method that this request is executing against. 1803 */ 1804 public RestCallLoggerConfig getCallLoggerConfig() { 1805 if (restJavaMethod != null) 1806 return restJavaMethod.getCallLoggerConfig(); 1807 return RestCallLoggerConfig.DEFAULT; 1808 } 1809 1810 void close() { 1811 if (cf != null) { 1812 try { 1813 cf.close(); 1814 } catch (IOException e) { 1815 e.printStackTrace(); 1816 } 1817 } 1818 } 1819 1820 /** 1821 * Returns metadata about the specified response object if it's annotated with {@link Response @Response}. 1822 * 1823 * @param o The response POJO. 1824 * @return Metadata about the specified response object, or <jk>null</jk> if it's not annotated with {@link Response @Response}. 1825 */ 1826 public ResponseBeanMeta getResponseBeanMeta(Object o) { 1827 return restJavaMethod == null ? null : restJavaMethod.getResponseBeanMeta(o); 1828 } 1829 1830 /** 1831 * Returns metadata about the specified response object if it's annotated with {@link ResponseHeader @ResponseHeader}. 1832 * 1833 * @param o The response POJO. 1834 * @return Metadata about the specified response object, or <jk>null</jk> if it's not annotated with {@link ResponseHeader @ResponseHeader}. 1835 */ 1836 public ResponsePartMeta getResponseHeaderMeta(Object o) { 1837 return restJavaMethod == null ? null : restJavaMethod.getResponseHeaderMeta(o); 1838 } 1839 1840 /** 1841 * Returns metadata about the specified response object if it's annotated with {@link ResponseBody @ResponseBody}. 1842 * 1843 * @param o The response POJO. 1844 * @return Metadata about the specified response object, or <jk>null</jk> if it's not annotated with {@link ResponseBody @ResponseBody}. 1845 */ 1846 public ResponsePartMeta getResponseBodyMeta(Object o) { 1847 return restJavaMethod == null ? null : restJavaMethod.getResponseBodyMeta(o); 1848 } 1849 1850 /** 1851 * Returns the schema generator with settings assigned on this method and class. 1852 * 1853 * @return The schema generator. 1854 */ 1855 public JsonSchemaGenerator getJsonSchemaGenerator() { 1856 return restJavaMethod == null ? context.getJsonSchemaGenerator() : restJavaMethod.getJsonSchemaGenerator(); 1857 } 1858 1859 /** 1860 * Returns the wrapped servlet request. 1861 * 1862 * @return The wrapped servlet request. 1863 */ 1864 protected HttpServletRequest getInner() { 1865 return inner; 1866 } 1867 1868 //----------------------------------------------------------------------------------------------------------------- 1869 // Utility methods 1870 //----------------------------------------------------------------------------------------------------------------- 1871 1872 /* 1873 * Converts an Accept-Language value entry to a Locale. 1874 */ 1875 private static Locale toLocale(String lang) { 1876 String country = ""; 1877 int i = lang.indexOf('-'); 1878 if (i > -1) { 1879 country = lang.substring(i+1).trim(); 1880 lang = lang.substring(0,i).trim(); 1881 } 1882 return new Locale(lang, country); 1883 } 1884 1885 void setJavaMethod(Method method) { 1886 this.javaMethod = method; 1887 } 1888}