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