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