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