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.Enablement.*; 018import static org.apache.juneau.common.internal.IOUtils.*; 019import static org.apache.juneau.common.internal.ThrowableUtils.*; 020import static org.apache.juneau.html.HtmlDocSerializer.*; 021import static org.apache.juneau.httppart.HttpPartType.*; 022import static org.apache.juneau.internal.CollectionUtils.*; 023import static org.apache.juneau.serializer.Serializer.*; 024import static java.lang.Integer.*; 025import static java.util.Collections.*; 026import static java.util.Optional.*; 027 028import java.io.*; 029import java.lang.reflect.*; 030import java.lang.reflect.Method; 031import java.lang.reflect.Proxy; 032import java.net.*; 033import java.nio.charset.*; 034import java.security.*; 035import java.text.*; 036import java.util.*; 037import java.util.logging.*; 038import java.util.stream.*; 039 040import jakarta.servlet.*; 041import jakarta.servlet.http.*; 042 043import org.apache.http.*; 044import org.apache.http.message.*; 045import org.apache.http.params.*; 046import org.apache.juneau.*; 047import org.apache.juneau.assertions.*; 048import org.apache.juneau.collections.*; 049import org.apache.juneau.common.internal.*; 050import org.apache.juneau.common.utils.*; 051import org.apache.juneau.config.*; 052import org.apache.juneau.cp.*; 053import org.apache.juneau.cp.Messages; 054import org.apache.juneau.dto.swagger.*; 055import org.apache.juneau.dto.swagger.Swagger; 056import org.apache.juneau.http.*; 057import org.apache.juneau.http.annotation.*; 058import org.apache.juneau.http.annotation.Content; 059import org.apache.juneau.http.annotation.FormData; 060import org.apache.juneau.http.annotation.Header; 061import org.apache.juneau.http.annotation.Path; 062import org.apache.juneau.http.annotation.Query; 063import org.apache.juneau.http.annotation.Response; 064import org.apache.juneau.httppart.*; 065import org.apache.juneau.httppart.bean.*; 066import org.apache.juneau.jsonschema.*; 067import org.apache.juneau.marshaller.*; 068import org.apache.juneau.oapi.*; 069import org.apache.juneau.parser.*; 070import org.apache.juneau.reflect.*; 071import org.apache.juneau.rest.annotation.*; 072import org.apache.juneau.rest.assertions.*; 073import org.apache.juneau.rest.beans.*; 074import org.apache.juneau.rest.guard.*; 075import org.apache.juneau.rest.httppart.*; 076import org.apache.juneau.rest.logger.*; 077import org.apache.juneau.http.header.*; 078import org.apache.juneau.http.header.BasicHeader; 079import org.apache.juneau.http.header.Date; 080import org.apache.juneau.http.response.*; 081import org.apache.juneau.http.response.BasicHttpException; 082import org.apache.juneau.rest.staticfile.*; 083import org.apache.juneau.rest.swagger.*; 084import org.apache.juneau.rest.util.*; 085import org.apache.juneau.rest.vars.*; 086import org.apache.juneau.rest.widget.*; 087import org.apache.juneau.serializer.*; 088import org.apache.juneau.svl.*; 089import org.apache.juneau.uon.*; 090 091/** 092 * Represents an HTTP request for a REST resource. 093 * 094 * <p> 095 * The {@link RestRequest} object is an extension of the <l>HttpServletRequest</l> class 096 * with various built-in convenience methods for use in building REST interfaces. 097 * It can be accessed by passing it as a parameter on your REST Java method: 098 * </p> 099 * 100 * <p class='bjava'> 101 * <ja>@RestPost</ja>(...) 102 * <jk>public</jk> Object myMethod(RestRequest <jv>req</jv>) {...} 103 * </p> 104 * 105 * <p> 106 * The primary methods on this class are: 107 * </p> 108 * <ul class='javatree'> 109 * <li class='jc'>{@link RestRequest} 110 * <ul class='spaced-list'> 111 * <li>Methods for accessing the request content: 112 * <ul class='javatreec'> 113 * <li class='jm'>{@link RestRequest#getContent() getContent()} 114 * <li class='jm'>{@link RestRequest#getInputStream() getInputStream()} 115 * <li class='jm'>{@link RestRequest#getReader() getReader()} 116 * </ul> 117 * <li>Methods for accessing HTTP parts: 118 * <ul class='javatreec'> 119 * <li class='jm'>{@link RestRequest#containsFormParam(String) containsFormParam(String)} 120 * <li class='jm'>{@link RestRequest#containsHeader(String) containsHeader(String)} 121 * <li class='jm'>{@link RestRequest#containsQueryParam(String) containsQueryParam(String)} 122 * <li class='jm'>{@link RestRequest#getHeader(Class) getHeader(Class)} 123 * <li class='jm'>{@link RestRequest#getHeader(String) getHeader(String)} 124 * <li class='jm'>{@link RestRequest#getHeaders() getHeaders()} 125 * <li class='jm'>{@link RestRequest#getFormParam(Class) getFormParam(Class)} 126 * <li class='jm'>{@link RestRequest#getFormParam(String) getFormParam(String)} 127 * <li class='jm'>{@link RestRequest#getFormParams() getFormParams()} 128 * <li class='jm'>{@link RestRequest#getPathParam(Class) getPathParam(Class)} 129 * <li class='jm'>{@link RestRequest#getPathParam(String) getPathParam(String)} 130 * <li class='jm'>{@link RestRequest#getPathParams() getPathParams()} 131 * <li class='jm'>{@link RestRequest#getPathRemainder() getPathRemainder()} 132 * <li class='jm'>{@link RestRequest#getQueryParam(Class) getQueryParam(Class)} 133 * <li class='jm'>{@link RestRequest#getQueryParam(String) getQueryParam(String)} 134 * <li class='jm'>{@link RestRequest#getQueryParams() getQueryParams()} 135 * <li class='jm'>{@link RestRequest#getQueryString() getQueryString()} 136 * </ul> 137 * <li>Methods for localization: 138 * <ul class='javatreec'> 139 * <li class='jm'>{@link RestRequest#getLocale() getLocale()} 140 * <li class='jm'>{@link RestRequest#getMessage(String,Object...) getMessage(String,Object...)} 141 * <li class='jm'>{@link RestRequest#getMessages() getMessages()} 142 * <li class='jm'>{@link RestRequest#getTimeZone() getTimeZone()} 143 * </ul> 144 * <li>Methods for accessing static files: 145 * <ul class='javatreec'> 146 * <li class='jm'>{@link RestRequest#getStaticFiles() getStaticFiles()} 147 * <li class='jm'>{@link RestRequest#getVarResolverSession() getVarResolverSession()} 148 * </ul> 149 * <li>Methods for assertions: 150 * <ul class='javatreec'> 151 * <li class='jm'>{@link RestRequest#assertContent() assertContent()} 152 * <li class='jm'>{@link RestRequest#assertCharset() assertCharset()} 153 * <li class='jm'>{@link RestRequest#assertFormParam(String) assertFormParam(String)} 154 * <li class='jm'>{@link RestRequest#assertHeader(String) assertHeader(String)} 155 * <li class='jm'>{@link RestRequest#assertQueryParam(String) assertQueryParam(String)} 156 * <li class='jm'>{@link RestRequest#assertRequestLine() assertRequestLine()} 157 * </ul> 158 * <li>Other: 159 * <ul class='javatreec'> 160 * <li class='jm'>{@link RestRequest#getAttribute(String) getAttribute(String)} 161 * <li class='jm'>{@link RestRequest#getAttributes() getAttributes()} 162 * <li class='jm'>{@link RestRequest#getAuthorityPath() getAuthorityPath()} 163 * <li class='jm'>{@link RestRequest#getBeanSession() getBeanSession()} 164 * <li class='jm'>{@link RestRequest#getCharset() getCharset()} 165 * <li class='jm'>{@link RestRequest#getConfig() getConfig()} 166 * <li class='jm'>{@link RestRequest#getContext() getContext()} 167 * <li class='jm'>{@link RestRequest#getContextPath() getContextPath()} 168 * <li class='jm'>{@link RestRequest#getHttpServletRequest() getHttpServletRequest()} 169 * <li class='jm'>{@link RestRequest#getMethod() getMethod()} 170 * <li class='jm'>{@link RestRequest#getOpContext() getOpContext()} 171 * <li class='jm'>{@link RestRequest#getOperationSwagger() getOperationSwagger()} 172 * <li class='jm'>{@link RestRequest#getPartParserSession() getPartParserSession()} 173 * <li class='jm'>{@link RestRequest#getPartSerializerSession() getPartSerializerSession()} 174 * <li class='jm'>{@link RestRequest#getPathInfo() getPathInfo()} 175 * <li class='jm'>{@link RestRequest#getProtocolVersion() getProtocolVersion()} 176 * <li class='jm'>{@link RestRequest#getRequest(Class) getRequest(Class)} 177 * <li class='jm'>{@link RestRequest#getRequestLine() getRequestLine()} 178 * <li class='jm'>{@link RestRequest#getRequestURI() getRequestURI()} 179 * <li class='jm'>{@link RestRequest#getRequestURL() getRequestURL()} 180 * <li class='jm'>{@link RestRequest#getServletPath() getServletPath()} 181 * <li class='jm'>{@link RestRequest#getSession() getSession()} 182 * <li class='jm'>{@link RestRequest#getSwagger() getSwagger()} 183 * <li class='jm'>{@link RestRequest#getUriContext() getUriContext()} 184 * <li class='jm'>{@link RestRequest#getUriResolver() getUriResolver()} 185 * <li class='jm'>{@link RestRequest#isDebug() isDebug()} 186 * <li class='jm'>{@link RestRequest#isPlainText() isPlainText()} 187 * <li class='jm'>{@link RestRequest#isUserInRole(String) isUserInRole(String)} 188 * <li class='jm'>{@link RestRequest#setAttribute(String,Object) setAttribute(String,Object)} 189 * <li class='jm'>{@link RestRequest#setCharset(Charset) setCharset(Charset)} 190 * <li class='jm'>{@link RestRequest#setDebug() setDebug()} 191 * <li class='jm'>{@link RestRequest#setException(Throwable) setException(Throwable)} 192 * <li class='jm'>{@link RestRequest#setNoTrace() setNoTrace()} 193 * </ul> 194 * </ul> 195 * </ul> 196 * 197 * <h5 class='section'>See Also:</h5><ul> 198 199 * </ul> 200 */ 201@SuppressWarnings({ "unchecked", "unused" }) 202public final class RestRequest extends HttpServletRequestWrapper { 203 204 // Constructor initialized. 205 private HttpServletRequest inner; 206 private final RestContext context; 207 private final RestOpContext opContext; 208 private final RequestContent content; 209 private final BeanSession beanSession; 210 private final RequestQueryParams queryParams; 211 private final RequestPathParams pathParams; 212 private final RequestHeaders headers; 213 private final RequestAttributes attrs; 214 private final HttpPartParserSession partParserSession; 215 private final RestSession session; 216 217 // Lazy initialized. 218 private VarResolverSession varSession; 219 private RequestFormParams formParams; 220 private UriContext uriContext; 221 private String authorityPath; 222 private Config config; 223 private Swagger swagger; 224 private Charset charset; 225 226 /** 227 * Constructor. 228 */ 229 RestRequest(RestOpContext opContext, RestSession session) throws Exception { 230 super(session.getRequest()); 231 this.session = session; 232 this.opContext = opContext; 233 234 inner = session.getRequest(); 235 context = session.getContext(); 236 237 attrs = new RequestAttributes(this); 238 239 queryParams = new RequestQueryParams(this, session.getQueryParams(), true); 240 241 headers = new RequestHeaders(this, queryParams, false); 242 243 content = new RequestContent(this); 244 245 if (context.isAllowContentParam()) { 246 String b = queryParams.get("content").asString().orElse(null); 247 if (b != null) { 248 headers.set("Content-Type", UonSerializer.DEFAULT.getResponseContentType()); 249 content.mediaType(MediaType.UON).parser(UonParser.DEFAULT).content(b.getBytes(UTF8)); 250 } 251 } 252 253 pathParams = new RequestPathParams(session, this, true); 254 255 beanSession = opContext.getBeanContext().getSession(); 256 257 partParserSession = opContext.getPartParser().getPartSession(); 258 259 pathParams.parser(partParserSession); 260 261 queryParams 262 .addDefault(opContext.getDefaultRequestQueryData().getAll()) 263 .parser(partParserSession); 264 265 headers 266 .addDefault(opContext.getDefaultRequestHeaders().getAll()) 267 .addDefault(context.getDefaultRequestHeaders().getAll()) 268 .parser(partParserSession); 269 270 content 271 .encoders(opContext.getEncoders()) 272 .parsers(opContext.getParsers()) 273 .maxInput(opContext.getMaxInput()); 274 275 attrs 276 .addDefault(opContext.getDefaultRequestAttributes()) 277 .addDefault(context.getDefaultRequestAttributes()); 278 279 if (isDebug()) 280 inner = CachingHttpServletRequest.wrap(inner); 281 } 282 283 //----------------------------------------------------------------------------------------------------------------- 284 // Request line. 285 //----------------------------------------------------------------------------------------------------------------- 286 287 /** 288 * Returns the request line of this request. 289 * 290 * @return The request line of this request. 291 */ 292 public RequestLine getRequestLine() { 293 String x = inner.getProtocol(); 294 int i = x.indexOf('/'); 295 int j = x.indexOf('.', i); 296 ProtocolVersion pv = new ProtocolVersion(x.substring(0,i), parseInt(x.substring(i+1,j)), parseInt(x.substring(j+1))); 297 return new BasicRequestLine(inner.getMethod(), inner.getRequestURI(), pv); 298 } 299 300 /** 301 * Returns the protocol version from the request line of this request. 302 * 303 * @return The protocol version from the request line of this request. 304 */ 305 public ProtocolVersion getProtocolVersion() { 306 return getRequestLine().getProtocolVersion(); 307 } 308 309 //----------------------------------------------------------------------------------------------------------------- 310 // Assertions 311 //----------------------------------------------------------------------------------------------------------------- 312 313 /** 314 * Returns an assertion on the request line returned by {@link #getRequestLine()}. 315 * 316 * <h5 class='section'>Example:</h5> 317 * <p class='bjava'> 318 * <jc>// Validates the request content contains "foo".</jc> 319 * <jv>request</jv> 320 * .assertRequestLine().protocol().minor().is(1); 321 * </p> 322 * 323 * @return A new assertion object. 324 */ 325 public FluentRequestLineAssertion<RestRequest> assertRequestLine() { 326 return new FluentRequestLineAssertion<>(getRequestLine(), this); 327 } 328 329 /** 330 * Returns a fluent assertion for the request content. 331 * 332 * <h5 class='section'>Example:</h5> 333 * <p class='bjava'> 334 * <jc>// Validates the request content contains "foo".</jc> 335 * <jv>request</jv> 336 * .assertContent().asString().is(<js>"foo"</js>); 337 * </p> 338 * 339 * @return A new fluent assertion on the content, never <jk>null</jk>. 340 */ 341 public FluentRequestContentAssertion<RestRequest> assertContent() { 342 return new FluentRequestContentAssertion<>(getContent(), this); 343 } 344 345 /** 346 * Returns a fluent assertion for the specified header. 347 * 348 * <h5 class='section'>Example:</h5> 349 * <p class='bjava'> 350 * <jc>// Validates the content type is JSON.</jc> 351 * <jv>request</jv> 352 * .assertHeader(<js>"Content-Type"</js>).asString().is(<js>"application/json"</js>); 353 * </p> 354 * 355 * @param name The header name. 356 * @return A new fluent assertion on the parameter, never <jk>null</jk>. 357 */ 358 public FluentRequestHeaderAssertion<RestRequest> assertHeader(String name) { 359 return new FluentRequestHeaderAssertion<>(getHeaderParam(name), this); 360 } 361 362 /** 363 * Returns a fluent assertion for the specified query parameter. 364 * 365 * <h5 class='section'>Example:</h5> 366 * <p class='bjava'> 367 * <jc>// Validates the content type is JSON.</jc> 368 * <jv>request</jv> 369 * .assertQueryParam(<js>"foo"</js>).asString().contains(<js>"bar"</js>); 370 * </p> 371 * 372 * @param name The query parameter name. 373 * @return A new fluent assertion on the parameter, never <jk>null</jk>. 374 */ 375 public FluentRequestQueryParamAssertion<RestRequest> assertQueryParam(String name) { 376 return new FluentRequestQueryParamAssertion<>(getQueryParam(name), this); 377 } 378 379 380 /** 381 * Returns a fluent assertion for the specified form parameter. 382 * 383 * <h5 class='section'>Example:</h5> 384 * <p class='bjava'> 385 * <jc>// Validates the content type is JSON.</jc> 386 * <jv>request</jv> 387 * .assertFormParam(<js>"foo"</js>).asString().contains(<js>"bar"</js>); 388 * </p> 389 * 390 * @param name The query parameter name. 391 * @return A new fluent assertion on the parameter, never <jk>null</jk>. 392 */ 393 public FluentRequestFormParamAssertion<RestRequest> assertFormParam(String name) { 394 return new FluentRequestFormParamAssertion<>(getFormParam(name), this); 395 } 396 397 //----------------------------------------------------------------------------------------------------------------- 398 // Headers 399 //----------------------------------------------------------------------------------------------------------------- 400 401 /** 402 * Request headers. 403 * 404 * <p> 405 * Returns a {@link RequestHeaders} object that encapsulates access to HTTP headers on the request. 406 * 407 * <h5 class='section'>Example:</h5> 408 * <p class='bjava'> 409 * <ja>@RestPost</ja>(...) 410 * <jk>public</jk> Object myMethod(RestRequest <jv>req</jv>) { 411 * 412 * <jc>// Get access to headers.</jc> 413 * RequestHeaders <jv>headers</jv> = <jv>req</jv>.getRequestHeaders(); 414 * 415 * <jc>// Add a default value.</jc> 416 * <jv>headers</jv>.addDefault(<js>"ETag"</js>, <jsf>DEFAULT_UUID</jsf>); 417 * 418 * <jc>// Get a header value as a POJO.</jc> 419 * UUID etag = <jv>headers</jv>.get(<js>"ETag"</js>).as(UUID.<jk>class</jk>).orElse(<jk>null</jk>); 420 * 421 * <jc>// Get a standard header.</jc> 422 * Optional<CacheControl> = <jv>headers</jv>.getCacheControl(); 423 * } 424 * </p> 425 * 426 * <h5 class='section'>Notes:</h5><ul> 427 * <li class='note'> 428 * This object is modifiable. 429 * <li class='note'> 430 * Values are converted from strings using the registered part parser on the resource class. 431 * <li class='note'> 432 * The {@link RequestHeaders} object can also be passed as a parameter on the method. 433 * <li class='note'> 434 * The {@link Header @Header} annotation can be used to access individual header values. 435 * </ul> 436 * 437 * <h5 class='section'>See Also:</h5><ul> 438 * <li class='link'><a class="doclink" href="../../../../index.html#jrs.HttpParts">HTTP Parts</a> 439 * </ul> 440 * 441 * @return 442 * The headers on this request. 443 * <br>Never <jk>null</jk>. 444 */ 445 public RequestHeaders getHeaders() { 446 return headers; 447 } 448 449 /** 450 * Returns the last header with a specified name of this message. 451 * 452 * <p> 453 * If there is more than one matching header in the message the last element of <c>getHeaders(String)</c> is returned. 454 * <br>If there is no matching header in the message, an empty request header object is returned. 455 * 456 * <h5 class='section'>Example:</h5> 457 * <p class='bjava'> 458 * <jc>// Gets a header and throws a BadRequest if it doesn't exist.</jc> 459 * <jv>request</jv> 460 * .getHeader(<js>"Foo"</js>) 461 * .assertValue().exists() 462 * .get(); 463 * </p> 464 * 465 * @param name The header name. 466 * @return The request header object, never <jk>null</jk>. 467 */ 468 public RequestHeader getHeaderParam(String name) { 469 return headers.getLast(name); 470 } 471 472 /** 473 * Returns <jk>true</jk> if this request contains the specified header. 474 * 475 * @param name The header name. 476 * @return <jk>true</jk> if this request contains the specified header. 477 */ 478 public boolean containsHeader(String name) { 479 return headers.contains(name); 480 } 481 482 /** 483 * Provides the ability to perform fluent-style assertions on the response character encoding. 484 * 485 * <h5 class='section'>Examples:</h5> 486 * <p class='bjava'> 487 * <jc>// Validates that the response content charset is UTF-8.</jc> 488 * <jv>request</jv> 489 * .assertCharset().is(<js>"utf-8"</js>); 490 * </p> 491 * 492 * @return A new fluent assertion object. 493 * @throws BasicHttpException If REST call failed. 494 */ 495 public FluentStringAssertion<RestRequest> assertCharset() { 496 return new FluentStringAssertion<>(getCharset().name(), this); 497 } 498 499 /** 500 * Sets the charset to expect on the request content. 501 * 502 * @param value The new value to use for the request content. 503 */ 504 public void setCharset(Charset value) { 505 this.charset = value; 506 } 507 508 /** 509 * Returns the charset specified on the <c>Content-Type</c> header, or <js>"UTF-8"</js> if not specified. 510 * 511 * @return The charset to use to decode the request content. 512 */ 513 public Charset getCharset() { 514 if (charset == null) { 515 // Determine charset 516 // NOTE: Don't use super.getCharacterEncoding() because the spec is implemented inconsistently. 517 // Jetty returns the default charset instead of null if the character is not specified on the request. 518 String h = getHeaderParam("Content-Type").orElse(null); 519 if (h != null) { 520 int i = h.indexOf(";charset="); 521 if (i > 0) 522 charset = Charset.forName(h.substring(i+9).trim()); 523 } 524 if (charset == null) 525 charset = opContext.getDefaultCharset(); 526 if (charset == null) 527 charset = Charset.forName("UTF-8"); 528 } 529 return charset; 530 } 531 532 /** 533 * Returns the preferred Locale that the client will accept content in, based on the Accept-Language header. 534 * 535 * <p> 536 * If the client request doesn't provide an <c>Accept-Language</c> header, this method returns the default locale for the server. 537 * 538 * @return The preferred Locale that the client will accept content in. Never <jk>null</jk>. 539 */ 540 @Override 541 public Locale getLocale() { 542 Locale best = inner.getLocale(); 543 String h = headers.get("Accept-Language").asString().orElse(null); 544 if (h != null) { 545 StringRanges sr = StringRanges.of(h); 546 float qValue = 0; 547 for (StringRange r : sr.toList()) { 548 if (r.getQValue() > qValue) { 549 best = toLocale(r.getName()); 550 qValue = r.getQValue(); 551 } 552 } 553 } 554 return best; 555 } 556 557 //----------------------------------------------------------------------------------------------------------------- 558 // Standard headers. 559 //----------------------------------------------------------------------------------------------------------------- 560 561 /** 562 * Returns the request header of the specified type. 563 * 564 * <p> 565 * Type must have a name specified via the {@link org.apache.juneau.http.annotation.Header} annotation 566 * and a public constructor that takes in either <c>value</c> or <c>name,value</c> as strings. 567 * 568 * <p> 569 * Typically any of the following: 570 * <ul class='javatreec'> 571 * <li class='jc'>{@link Accept} 572 * <li class='jc'>{@link AcceptCharset} 573 * <li class='jc'>{@link AcceptEncoding} 574 * <li class='jc'>{@link AcceptLanguage} 575 * <li class='jc'>{@link AcceptRanges} 576 * <li class='jc'>{@link Authorization} 577 * <li class='jc'>{@link CacheControl} 578 * <li class='jc'>{@link ClientVersion} 579 * <li class='jc'>{@link Connection} 580 * <li class='jc'>{@link ContentDisposition} 581 * <li class='jc'>{@link ContentEncoding} 582 * <li class='jc'>{@link ContentLength} 583 * <li class='jc'>{@link ContentType} 584 * <li class='jc'>{@link Date} 585 * <li class='jc'>{@link Debug} 586 * <li class='jc'>{@link Expect} 587 * <li class='jc'>{@link Forwarded} 588 * <li class='jc'>{@link From} 589 * <li class='jc'>{@link Host} 590 * <li class='jc'>{@link IfMatch} 591 * <li class='jc'>{@link IfModifiedSince} 592 * <li class='jc'>{@link IfNoneMatch} 593 * <li class='jc'>{@link IfRange} 594 * <li class='jc'>{@link IfUnmodifiedSince} 595 * <li class='jc'>{@link MaxForwards} 596 * <li class='jc'>{@link NoTrace} 597 * <li class='jc'>{@link Origin} 598 * <li class='jc'>{@link Pragma} 599 * <li class='jc'>{@link ProxyAuthorization} 600 * <li class='jc'>{@link Range} 601 * <li class='jc'>{@link Referer} 602 * <li class='jc'>{@link TE} 603 * <li class='jc'>{@link Thrown} 604 * <li class='jc'>{@link Upgrade} 605 * <li class='jc'>{@link UserAgent} 606 * <li class='jc'>{@link Warning} 607 * </ul> 608 * 609 * @param <T> The bean type to create. 610 * @param type The bean type to create. 611 * @return The parsed header on the request, never <jk>null</jk>. 612 */ 613 public <T> Optional<T> getHeader(Class<T> type) { 614 return headers.get(type); 615 } 616 617 /** 618 * Returns the <c>Time-Zone</c> header value on the request if there is one. 619 * 620 * <p> 621 * Example: <js>"GMT"</js>. 622 * 623 * @return The parsed header on the request, never <jk>null</jk>. 624 */ 625 public Optional<TimeZone> getTimeZone() { 626 String tz = headers.get("Time-Zone").asString().orElse(null); 627 if (tz != null) 628 return optional(TimeZone.getTimeZone(tz)); 629 return Optional.empty(); 630 } 631 632 //----------------------------------------------------------------------------------------------------------------- 633 // Attributes 634 //----------------------------------------------------------------------------------------------------------------- 635 636 /** 637 * Request attributes. 638 * 639 * <p> 640 * Returns a {@link RequestAttributes} object that encapsulates access to attributes on the request. 641 * 642 * <h5 class='section'>Example:</h5> 643 * <p class='bjava'> 644 * <ja>@RestPost</ja>(...) 645 * <jk>public</jk> Object myMethod(RestRequest <jv>req</jv>) { 646 * 647 * <jc>// Get access to attributes.</jc> 648 * RequestAttributes <jv>attributes</jv> = <jv>req</jv>.getAttributes(); 649 * 650 * <jc>// Get a header value as a POJO.</jc> 651 * UUID <jv>etag</jv> = <jv>attributes</jv>.get(<js>"ETag"</js>, UUID.<jk>class</jk>); 652 * } 653 * </p> 654 * 655 * <h5 class='section'>Notes:</h5><ul> 656 * <li class='note'> 657 * This object is modifiable. 658 * <li class='note'> 659 * Values are converted from strings using the registered part parser on the resource class. 660 * <li class='note'> 661 * The {@link RequestAttributes} object can also be passed as a parameter on the method. 662 * <li class='note'> 663 * The {@link Attr @Attr} annotation can be used to access individual attribute values. 664 * </ul> 665 * 666 * <h5 class='section'>See Also:</h5><ul> 667 * <li class='link'><a class="doclink" href="../../../../index.html#jrs.HttpParts">HTTP Parts</a> 668 * </ul> 669 * 670 * @return 671 * The headers on this request. 672 * <br>Never <jk>null</jk>. 673 */ 674 public RequestAttributes getAttributes() { 675 return attrs; 676 } 677 678 /** 679 * Returns the request attribute with the specified name. 680 * 681 * @param name The attribute name. 682 * @return The attribute value, never <jk>null</jk>. 683 */ 684 @Override 685 public RequestAttribute getAttribute(String name) { 686 return attrs.get(name); 687 } 688 689 /** 690 * Sets a request attribute. 691 * 692 * @param name The attribute name. 693 * @param value The attribute value. 694 */ 695 @Override 696 public void setAttribute(String name, Object value) { 697 attrs.set(name, value); 698 } 699 700 //----------------------------------------------------------------------------------------------------------------- 701 // Query parameters 702 //----------------------------------------------------------------------------------------------------------------- 703 704 /** 705 * Query parameters. 706 * 707 * <p> 708 * Returns a {@link RequestQueryParams} object that encapsulates access to URL GET parameters. 709 * 710 * <p> 711 * Similar to {@link HttpServletRequest#getParameterMap()} but only looks for query parameters in the URL and not form posts. 712 * 713 * <h5 class='section'>Example:</h5> 714 * <p class='bjava'> 715 * <ja>@RestGet</ja>(...) 716 * <jk>public void</jk> doGet(RestRequest <jv>req</jv>) { 717 * 718 * <jc>// Get access to query parameters on the URL.</jc> 719 * RequestQueryParams <jv>query</jv> = <jv>req</jv>.getQuery(); 720 * 721 * <jc>// Get query parameters converted to various types.</jc> 722 * <jk>int</jk> <jv>p1/</jv> = <jv>query</jv>.getInteger(<js>"p1"</js>).orElse(<jk>null</jk>); 723 * String <jv>p2</jv> = <jv>query</jv>.getString(<js>"p2"</js>).orElse(<jk>null</jk>); 724 * UUID <jv>p3</jv> = <jv>query</jv>.get(<js>"p3"</js>).as(UUID.<jk>class</jk>).orElse(<jk>null</jk>); 725 * } 726 * </p> 727 * 728 * <h5 class='section'>Notes:</h5><ul> 729 * <li class='note'> 730 * This object is modifiable. 731 * </ul> 732 * 733 * <h5 class='section'>See Also:</h5><ul> 734 * <li class='link'><a class="doclink" href="../../../../index.html#jrs.HttpParts">HTTP Parts</a> 735 * </ul> 736 * 737 * @return 738 * The query parameters as a modifiable map. 739 * <br>Never <jk>null</jk>. 740 */ 741 public RequestQueryParams getQueryParams() { 742 return queryParams; 743 } 744 745 /** 746 * Shortcut for calling <c>getRequestQuery().getLast(<jv>name</jv>)</c>. 747 * 748 * @param name The query parameter name. 749 * @return The query parameter, never <jk>null</jk>. 750 */ 751 public RequestQueryParam getQueryParam(String name) { 752 return queryParams.get(name); 753 } 754 755 /** 756 * Returns the request query parameter of the specified type. 757 * 758 * <p> 759 * Type must have a name specified via the {@link org.apache.juneau.http.annotation.Query} annotation 760 * and a public constructor that takes in either <c>value</c> or <c>name,value</c> as strings. 761 * 762 * @param <T> The bean type to create. 763 * @param type The bean type to create. 764 * @return The parsed query parameter on the request, never <jk>null</jk>. 765 */ 766 public <T> Optional<T> getQueryParam(Class<T> type) { 767 return queryParams.get(type); 768 } 769 770 /** 771 * Returns <jk>true</jk> if this request contains the specified header. 772 * 773 * @param name The header name. 774 * @return <jk>true</jk> if this request contains the specified header. 775 */ 776 public boolean containsQueryParam(String name) { 777 return queryParams.contains(name); 778 } 779 780 781 //----------------------------------------------------------------------------------------------------------------- 782 // Form data parameters 783 //----------------------------------------------------------------------------------------------------------------- 784 785 /** 786 * Form-data. 787 * 788 * <p> 789 * Returns a {@link RequestFormParams} object that encapsulates access to form post parameters. 790 * 791 * <p> 792 * Similar to {@link HttpServletRequest#getParameterMap()}, but only looks for form data in the HTTP content. 793 * 794 * <h5 class='section'>Example:</h5> 795 * <p class='bjava'> 796 * <ja>@RestPost</ja>(...) 797 * <jk>public void</jk> doPost(RestRequest <jv>req</jv>) { 798 * 799 * <jc>// Get access to parsed form data parameters.</jc> 800 * RequestFormParams <jv>formParams</jv> = <jv>req</jv>.getFormParams(); 801 * 802 * <jc>// Get form data parameters converted to various types.</jc> 803 * <jk>int</jk> <jv>p1</jv> = <jv>formParams</jv>.get(<js>"p1"</js>).asInteger().orElse(0); 804 * String <jv>p2</jv> = <jv>formParams</jv>.get(<js>"p2"</js>).asString().orElse(<jk>null</jk>); 805 * UUID <jv>p3</jv> = <jv>formParams</jv>.get(<js>"p3"</js>).as(UUID.<jk>class</jk>).orElse(<jk>null</jk>); 806 * } 807 * </p> 808 * 809 * <h5 class='section'>Notes:</h5><ul> 810 * <li class='note'> 811 * This object is modifiable. 812 * <li class='note'> 813 * Values are converted from strings using the registered part parser on the resource class. 814 * <li class='note'> 815 * The {@link RequestFormParams} object can also be passed as a parameter on the method. 816 * <li class='note'> 817 * The {@link FormData @FormDAta} annotation can be used to access individual form data parameter values. 818 * </ul> 819 * 820 * <h5 class='section'>See Also:</h5><ul> 821 * <li class='link'><a class="doclink" href="../../../../index.html#jrs.HttpParts">HTTP Parts</a> 822 * </ul> 823 * 824 * @return 825 * The URL-encoded form data from the request. 826 * <br>Never <jk>null</jk>. 827 * @throws InternalServerError If query parameters could not be parsed. 828 * @see org.apache.juneau.http.annotation.FormData 829 */ 830 public RequestFormParams getFormParams() throws InternalServerError { 831 try { 832 if (formParams == null) 833 formParams = new RequestFormParams(this, true).parser(partParserSession); 834 formParams.addDefault(opContext.getDefaultRequestFormData().getAll()); 835 return formParams; 836 } catch (Exception e) { 837 throw new InternalServerError(e); 838 } 839 } 840 841 /** 842 * Shortcut for calling <c>getFormData().getString(name)</c>. 843 * 844 * @param name The form data parameter name. 845 * @return The form data parameter value, or <jk>null</jk> if not found. 846 */ 847 public RequestFormParam getFormParam(String name) { 848 return getFormParams().get(name); 849 } 850 851 /** 852 * Returns the request form-data parameter of the specified type. 853 * 854 * <p> 855 * Type must have a name specified via the {@link org.apache.juneau.http.annotation.FormData} annotation 856 * and a public constructor that takes in either <c>value</c> or <c>name,value</c> as strings. 857 * 858 * @param <T> The bean type to create. 859 * @param type The bean type to create. 860 * @return The parsed form-data parameter on the request, never <jk>null</jk>. 861 */ 862 public <T> Optional<T> getFormParam(Class<T> type) { 863 return getFormParams().get(type); 864 } 865 866 /** 867 * Returns <jk>true</jk> if this request contains the specified header. 868 * 869 * @param name The header name. 870 * @return <jk>true</jk> if this request contains the specified header. 871 */ 872 public boolean containsFormParam(String name) { 873 return getFormParams().contains(name); 874 } 875 876 //----------------------------------------------------------------------------------------------------------------- 877 // Path parameters 878 //----------------------------------------------------------------------------------------------------------------- 879 880 /** 881 * Path parameters. 882 * 883 * <p> 884 * Returns a {@link RequestPathParams} object that encapsulates access to URL path parameters. 885 * 886 * <h5 class='section'>Example:</h5> 887 * <p class='bjava'> 888 * <ja>@RestGet</ja>(<js>"/{foo}/{bar}/{baz}/*"</js>) 889 * <jk>public void</jk> doGet(RestRequest <jv>req</jv>) { 890 * 891 * <jc>// Get access to path data.</jc> 892 * RequestPathParams <jv>pathParams</jv> = <jv>req</jv>.getPathParams(); 893 * 894 * <jc>// Example URL: /123/qux/true/quux</jc> 895 * 896 * <jk>int</jk> <jv>foo</jv> = <jv>pathParams</jv>.get(<js>"foo"</js>).asInteger().orElse(-1); <jc>// =123</jc> 897 * String <jv>bar</jv> = <jv>pathParams</jv>.get(<js>"bar"</js>).orElse(<jk>null</jk>); <jc>// =qux</jc> 898 * <jk>boolean</jk> <jv>baz</jv> = <jv>pathParams</jv>.get(<js>"baz"</js>).asBoolean().orElse(<jk>false</jk>); <jc>// =true</jc> 899 * String <jv>remainder</jv> = <jv>pathParams</jv>.getRemainder().orElse(<jk>null</jk>); <jc>// =quux</jc> 900 * } 901 * </p> 902 * 903 * <h5 class='section'>Notes:</h5><ul> 904 * <li class='note'> 905 * This object is modifiable. 906 * </ul> 907 * 908 * @return 909 * The path parameters. 910 * <br>Never <jk>null</jk>. 911 */ 912 public RequestPathParams getPathParams() { 913 return pathParams; 914 } 915 916 /** 917 * Shortcut for calling <c>getPathParams().get(<jv>name</jv>)</c>. 918 * 919 * @param name The path parameter name. 920 * @return The path parameter, never <jk>null</jk>. 921 */ 922 public RequestPathParam getPathParam(String name) { 923 return pathParams.get(name); 924 } 925 926 /** 927 * Returns the request path parameter of the specified type. 928 * 929 * <p> 930 * Type must have a name specified via the {@link org.apache.juneau.http.annotation.Path} annotation 931 * and a public constructor that takes in either <c>value</c> or <c>name,value</c> as strings. 932 * 933 * @param <T> The bean type to create. 934 * @param type The bean type to create. 935 * @return The parsed form-data parameter on the request, never <jk>null</jk>. 936 */ 937 public <T> Optional<T> getPathParam(Class<T> type) { 938 return pathParams.get(type); 939 } 940 941 /** 942 * Shortcut for calling <c>getPathParams().getRemainder()</c>. 943 * 944 * @return The path remainder value, never <jk>null</jk>. 945 */ 946 public RequestPathParam getPathRemainder() { 947 return pathParams.getRemainder(); 948 } 949 950 //----------------------------------------------------------------------------------------------------------------- 951 // Content methods 952 //----------------------------------------------------------------------------------------------------------------- 953 954 /** 955 * Request content. 956 * 957 * <p> 958 * Returns a {@link RequestContent} object that encapsulates access to the HTTP request content. 959 * 960 * <h5 class='section'>Example:</h5> 961 * <p class='bjava'> 962 * <ja>@RestPost</ja>(...) 963 * <jk>public void</jk> doPost(RestRequest <jv>req</jv>) { 964 * 965 * <jc>// Convert content to a linked list of Person objects.</jc> 966 * List<Person> <jv>list</jv> = <jv>req</jv>.getContent().as(LinkedList.<jk>class</jk>, Person.<jk>class</jk>); 967 * .. 968 * } 969 * </p> 970 * 971 * <h5 class='section'>Notes:</h5><ul> 972 * <li class='note'> 973 * The {@link RequestContent} object can also be passed as a parameter on the method. 974 * <li class='note'> 975 * The {@link Content @Content} annotation can be used to access the content as well. 976 * </ul> 977 * 978 * <h5 class='section'>See Also:</h5><ul> 979 * <li class='link'><a class="doclink" href="../../../../index.html#jrs.HttpParts">HTTP Parts</a> 980 * </ul> 981 * 982 * @return 983 * The content of this HTTP request. 984 * <br>Never <jk>null</jk>. 985 */ 986 public RequestContent getContent() { 987 return content; 988 } 989 990 /** 991 * Returns the HTTP content content as a {@link Reader}. 992 * 993 * <p> 994 * If {@code allowHeaderParams} init parameter is true, then first looks for {@code &content=xxx} in the URL query 995 * string. 996 * 997 * <p> 998 * Automatically handles GZipped input streams. 999 * 1000 * <p> 1001 * This method is equivalent to calling <c>getContent().getReader()</c>. 1002 * 1003 * @return The HTTP content content as a {@link Reader}. 1004 * @throws IOException If content could not be read. 1005 */ 1006 @Override 1007 public BufferedReader getReader() throws IOException { 1008 return getContent().getReader(); 1009 } 1010 1011 /** 1012 * Returns the HTTP content content as an {@link InputStream}. 1013 * 1014 * <p> 1015 * Automatically handles GZipped input streams. 1016 * 1017 * <p> 1018 * This method is equivalent to calling <c>getContent().getInputStream()</c>. 1019 * 1020 * @return The negotiated input stream. 1021 * @throws IOException If any error occurred while trying to get the input stream or wrap it in the GZIP wrapper. 1022 */ 1023 @Override 1024 public ServletInputStream getInputStream() throws IOException { 1025 return getContent().getInputStream(); 1026 } 1027 1028 //----------------------------------------------------------------------------------------------------------------- 1029 // URI-related methods 1030 //----------------------------------------------------------------------------------------------------------------- 1031 1032 /** 1033 * Returns the portion of the request URI that indicates the context of the request. 1034 * 1035 * <p>The context path always comes first in a request URI. 1036 * The path starts with a <js>"/"</js> character but does not end with a <js>"/"</js> character. 1037 * For servlets in the default (root) context, this method returns <js>""</js>. 1038 * The container does not decode this string. 1039 * 1040 * @return The context path, never <jk>null</jk>. 1041 * @see HttpServletRequest#getContextPath() 1042 */ 1043 @Override 1044 public String getContextPath() { 1045 String cp = context.getUriContext(); 1046 return cp == null ? inner.getContextPath() : cp; 1047 } 1048 1049 /** 1050 * Returns the URI authority portion of the request. 1051 * 1052 * @return The URI authority portion of the request. 1053 */ 1054 public String getAuthorityPath() { 1055 if (authorityPath == null) 1056 authorityPath = context.getUriAuthority(); 1057 if (authorityPath == null) { 1058 String scheme = inner.getScheme(); 1059 int port = inner.getServerPort(); 1060 StringBuilder sb = new StringBuilder(inner.getScheme()).append("://").append(inner.getServerName()); 1061 if (! (port == 80 && "http".equals(scheme) || port == 443 && "https".equals(scheme))) 1062 sb.append(':').append(port); 1063 authorityPath = sb.toString(); 1064 } 1065 return authorityPath; 1066 } 1067 1068 /** 1069 * Returns the part of this request's URL that calls the servlet. 1070 * 1071 * <p> 1072 * This path starts with a <js>"/"</js> character and includes either the servlet name or a path to the servlet, 1073 * but does not include any extra path information or a query string. 1074 * 1075 * @return The servlet path, never <jk>null</jk>. 1076 * @see HttpServletRequest#getServletPath() 1077 */ 1078 @Override 1079 public String getServletPath() { 1080 String cp = context.getUriContext(); 1081 String sp = inner.getServletPath(); 1082 return cp == null || ! sp.startsWith(cp) ? sp : sp.substring(cp.length()); 1083 } 1084 1085 /** 1086 * Returns the URI context of the request. 1087 * 1088 * <p> 1089 * The URI context contains all the information about the URI of the request, such as the servlet URI, context 1090 * path, etc... 1091 * 1092 * @return The URI context of the request. 1093 */ 1094 public UriContext getUriContext() { 1095 if (uriContext == null) 1096 uriContext = UriContext.of(getAuthorityPath(), getContextPath(), getServletPath(), StringUtils.urlEncodePath(inner.getPathInfo())); 1097 return uriContext; 1098 } 1099 1100 /** 1101 * Returns a URI resolver that can be used to convert URIs to absolute or root-relative form. 1102 * 1103 * @param resolution The URI resolution rule. 1104 * @param relativity The relative URI relativity rule. 1105 * @return The URI resolver for this request. 1106 */ 1107 public UriResolver getUriResolver(UriResolution resolution, UriRelativity relativity) { 1108 return UriResolver.of(resolution, relativity, getUriContext()); 1109 } 1110 1111 /** 1112 * Shortcut for calling {@link #getUriResolver()} using {@link UriResolution#ROOT_RELATIVE} and 1113 * {@link UriRelativity#RESOURCE} 1114 * 1115 * @return The URI resolver for this request. 1116 */ 1117 public UriResolver getUriResolver() { 1118 return UriResolver.of(context.getUriResolution(), context.getUriRelativity(), getUriContext()); 1119 } 1120 1121 /** 1122 * Returns the URI for this request. 1123 * 1124 * <p> 1125 * Similar to {@link #getRequestURI()} but returns the value as a {@link URI}. 1126 * It also gives you the capability to override the query parameters (e.g. add new query parameters to the existing 1127 * URI). 1128 * 1129 * @param includeQuery If <jk>true</jk> include the query parameters on the request. 1130 * @param addQueryParams Augment the request URI with the specified query parameters. 1131 * @return A new URI. 1132 */ 1133 public URI getUri(boolean includeQuery, Map<String,Object> addQueryParams) { 1134 String uri = inner.getRequestURI(); 1135 if (includeQuery || addQueryParams != null) { 1136 StringBuilder sb = new StringBuilder(uri); 1137 RequestQueryParams rq = this.queryParams.copy(); 1138 if (addQueryParams != null) 1139 for (Map.Entry<String,?> e : addQueryParams.entrySet()) 1140 rq.set(e.getKey(), e.getValue()); 1141 if (! rq.isEmpty()) 1142 sb.append('?').append(rq.asQueryString()); 1143 uri = sb.toString(); 1144 } 1145 try { 1146 return new URI(uri); 1147 } catch (URISyntaxException e) { 1148 // Shouldn't happen. 1149 throw asRuntimeException(e); 1150 } 1151 } 1152 1153 //----------------------------------------------------------------------------------------------------------------- 1154 // Labels 1155 //----------------------------------------------------------------------------------------------------------------- 1156 1157 /** 1158 * Returns the localized swagger associated with the resource. 1159 * 1160 * <p> 1161 * A shortcut for calling <c>getInfoProvider().getSwagger(request);</c> 1162 * 1163 * <h5 class='section'>Example:</h5> 1164 * <p class='bjava'> 1165 * <ja>@RestGet</ja> 1166 * <jk>public</jk> List<Tag> swaggerTags(RestRequest <jv>req</jv>) { 1167 * <jk>return</jk> <jv>req</jv>.getSwagger().getTags(); 1168 * } 1169 * </p> 1170 * 1171 * <h5 class='section'>Notes:</h5><ul> 1172 * <li class='note'> 1173 * The {@link Swagger} object can also be passed as a parameter on the method. 1174 * </ul> 1175 * 1176 * <h5 class='section'>See Also:</h5><ul> 1177 * <li class='jm'>{@link RestContext.Builder#swaggerProvider(Class)} 1178 * <li class='jm'>{@link RestContext.Builder#swaggerProvider(SwaggerProvider)} 1179 * <li class='link'><a class="doclink" href="../../../../index.html#jrs.Swagger">Swagger</a> 1180 * </ul> 1181 * 1182 * @return 1183 * The swagger associated with the resource. 1184 * <br>Never <jk>null</jk>. 1185 */ 1186 public Optional<Swagger> getSwagger() { 1187 return context.getSwagger(getLocale()); 1188 } 1189 1190 /** 1191 * Returns the swagger for the Java method invoked. 1192 * 1193 * @return The swagger for the Java method as an {@link Optional}. Never <jk>null</jk>. 1194 */ 1195 public Optional<Operation> getOperationSwagger() { 1196 1197 Optional<Swagger> swagger = getSwagger(); 1198 if (! swagger.isPresent()) 1199 return Optional.empty(); 1200 1201 return ofNullable(swagger.get().getOperation(opContext.getPathPattern(), getMethod().toLowerCase())); 1202 } 1203 1204 //----------------------------------------------------------------------------------------------------------------- 1205 // Other methods 1206 //----------------------------------------------------------------------------------------------------------------- 1207 1208 /** 1209 * Returns the part serializer associated with this request. 1210 * 1211 * @return The part serializer associated with this request. 1212 */ 1213 public HttpPartParserSession getPartParserSession() { 1214 return partParserSession; 1215 } 1216 1217 /** 1218 * Returns the HTTP method of this request. 1219 * 1220 * <p> 1221 * If <c>allowHeaderParams</c> init parameter is <jk>true</jk>, then first looks for 1222 * <c>&method=xxx</c> in the URL query string. 1223 * 1224 * @return The HTTP method of this request. 1225 */ 1226 @Override 1227 public String getMethod() { 1228 return session.getMethod(); 1229 } 1230 1231 /** 1232 * Returns <jk>true</jk> if <c>&plainText=true</c> was specified as a URL parameter. 1233 * 1234 * <p> 1235 * This indicates that the <c>Content-Type</c> of the output should always be set to <js>"text/plain"</js> 1236 * to make it easy to render in a browser. 1237 * 1238 * <p> 1239 * This feature is useful for debugging. 1240 * 1241 * @return <jk>true</jk> if {@code &plainText=true} was specified as a URL parameter 1242 */ 1243 public boolean isPlainText() { 1244 return "true".equals(queryParams.get("plainText").asString().orElse("false")); 1245 } 1246 1247 /** 1248 * Returns the resource bundle for the request locale. 1249 * 1250 * <h5 class='section'>Example:</h5> 1251 * <p class='bjava'> 1252 * <ja>@RestGet</ja> 1253 * <jk>public</jk> String hello(RestRequest <jv>req</jv>, <ja>@Query</ja>(<js>"user"</js>) String <jv>user</jv>) { 1254 * 1255 * <jc>// Return a localized message.</jc> 1256 * <jk>return</jk> <jv>req</jv>.getMessages().getString(<js>"hello.message"</js>, <jv>user</jv>); 1257 * } 1258 * </p> 1259 * 1260 * <h5 class='section'>Notes:</h5><ul> 1261 * <li class='note'> 1262 * The {@link Messages} object can also be passed as a parameter on the method. 1263 * </ul> 1264 * 1265 * <h5 class='section'>See Also:</h5><ul> 1266 * <li class='jm'>{@link org.apache.juneau.rest.RestRequest#getMessage(String,Object...)} 1267 * <li class='link'><a class="doclink" href="../../../../index.html#jrs.LocalizedMessages">Localized Messages</a> 1268 * </ul> 1269 * 1270 * @return 1271 * The resource bundle. 1272 * <br>Never <jk>null</jk>. 1273 */ 1274 public Messages getMessages() { 1275 return context.getMessages().forLocale(getLocale()); 1276 } 1277 1278 /** 1279 * Shortcut method for calling {@link RestRequest#getMessages()} and {@link Messages#getString(String,Object...)}. 1280 * 1281 * @param key The message key. 1282 * @param args Optional {@link MessageFormat}-style arguments. 1283 * @return The localized message. 1284 */ 1285 public String getMessage(String key, Object...args) { 1286 return getMessages().getString(key, args); 1287 } 1288 1289 /** 1290 * Returns the resource context handling the request. 1291 * 1292 * <p> 1293 * Can be used to access servlet-init parameters or annotations during requests, such as in calls to 1294 * {@link RestGuard#guard(RestRequest, RestResponse)}.. 1295 * 1296 * @return The resource context handling the request. 1297 */ 1298 public RestContext getContext() { 1299 return context; 1300 } 1301 1302 /** 1303 * Returns access to the inner {@link RestOpContext} of this method. 1304 * 1305 * @return The {@link RestOpContext} of this method. May be <jk>null</jk> if method has not yet been found. 1306 */ 1307 public RestOpContext getOpContext() { 1308 return opContext; 1309 } 1310 1311 /** 1312 * Returns the {@link BeanSession} associated with this request. 1313 * 1314 * @return The request bean session. 1315 */ 1316 public BeanSession getBeanSession() { 1317 return beanSession; 1318 } 1319 1320 /** 1321 * Returns <jk>true</jk> if debug mode is enabled. 1322 * 1323 * Debug mode is enabled by simply adding <js>"?debug=true"</js> to the query string or adding a <c>Debug: true</c> header on the request. 1324 * 1325 * @return <jk>true</jk> if debug mode is enabled. 1326 */ 1327 public boolean isDebug() { 1328 return getAttribute("Debug").as(Boolean.class).orElse(false); 1329 } 1330 1331 /** 1332 * Sets the <js>"Exception"</js> attribute to the specified throwable. 1333 * 1334 * <p> 1335 * This exception is used by {@link CallLogger} for logging purposes. 1336 * 1337 * @param t The attribute value. 1338 * @return This object. 1339 */ 1340 public RestRequest setException(Throwable t) { 1341 setAttribute("Exception", t); 1342 return this; 1343 } 1344 1345 /** 1346 * Sets the <js>"NoTrace"</js> attribute to the specified boolean. 1347 * 1348 * <p> 1349 * This flag is used by {@link CallLogger} and tells it not to log the current request. 1350 * 1351 * @param b The attribute value. 1352 * @return This object. 1353 */ 1354 public RestRequest setNoTrace(Boolean b) { 1355 setAttribute("NoTrace", b); 1356 return this; 1357 } 1358 1359 /** 1360 * Shortcut for calling <c>setNoTrace(<jk>true</jk>)</c>. 1361 * 1362 * @return This object. 1363 */ 1364 public RestRequest setNoTrace() { 1365 return setNoTrace(true); 1366 } 1367 1368 /** 1369 * Sets the <js>"Debug"</js> attribute to the specified boolean. 1370 * 1371 * <p> 1372 * This flag is used by {@link CallLogger} to help determine how a request should be logged. 1373 * 1374 * @param b The attribute value. 1375 * @return This object. 1376 * @throws IOException If content could not be cached. 1377 */ 1378 public RestRequest setDebug(Boolean b) throws IOException { 1379 setAttribute("Debug", b); 1380 if (b) 1381 inner = CachingHttpServletRequest.wrap(inner); 1382 return this; 1383 } 1384 1385 /** 1386 * Shortcut for calling <c>setDebug(<jk>true</jk>)</c>. 1387 * 1388 * @return This object. 1389 * @throws IOException If content could not be cached. 1390 */ 1391 public RestRequest setDebug() throws IOException { 1392 return setDebug(true); 1393 } 1394 1395 /** 1396 * Request-level variable resolver session. 1397 * 1398 * <p> 1399 * Used to resolve SVL variables in text. 1400 * 1401 * <h5 class='section'>Example:</h5> 1402 * <p class='bjava'> 1403 * <ja>@RestGet</ja> 1404 * <jk>public</jk> String hello(RestRequest <jv>req</jv>) { 1405 * 1406 * <jc>// Get var resolver session.</jc> 1407 * VarResolverSession <jv>session</jv> = getVarResolverSession(); 1408 * 1409 * <jc>// Use it to construct a customized message from a query parameter.</jc> 1410 * <jk>return</jk> <jv>session</jv>.resolve(<js>"Hello $RQ{user}!"</js>); 1411 * } 1412 * </p> 1413 * 1414 * <h5 class='section'>Notes:</h5><ul> 1415 * <li class='note'> 1416 * The {@link VarResolverSession} object can also be passed as a parameter on the method. 1417 * </ul> 1418 * 1419 * <h5 class='section'>See Also:</h5><ul> 1420 * <li class='jm'>{@link org.apache.juneau.rest.RestContext#getVarResolver()} 1421 * <li class='link'><a class="doclink" href="../../../../index.html#jrs.SvlVariables">SVL Variables</a> 1422 * </ul> 1423 * 1424 * @return The variable resolver for this request. 1425 */ 1426 public VarResolverSession getVarResolverSession() { 1427 if (varSession == null) 1428 varSession = context 1429 .getVarResolver() 1430 .createSession(session.getBeanStore()) 1431 .bean(RestRequest.class, this) 1432 .bean(RestSession.class, session); 1433 return varSession; 1434 } 1435 1436 /** 1437 * Returns the static files registered on the REST resource context object. 1438 * 1439 * <p> 1440 * Used to retrieve localized files to be served up as static files through the REST API. 1441 * 1442 * @return This object. 1443 */ 1444 public StaticFiles getStaticFiles() { 1445 return context.getStaticFiles(); 1446 } 1447 1448 /** 1449 * Config file associated with the resource. 1450 * 1451 * <p> 1452 * Returns a config file with session-level variable resolution. 1453 * 1454 * The config file is identified via one of the following: 1455 * <ul class='javatree'> 1456 * <li class='ja'>{@link Rest#config()} 1457 * <li class='jm'>{@link RestContext.Builder#config(Config)} 1458 * </ul> 1459 * 1460 * <h5 class='section'>Example:</h5> 1461 * <p class='bjava'> 1462 * <ja>@RestGet</ja>(...) 1463 * <jk>public void</jk> doGet(RestRequest <jv>req</jv>) { 1464 * 1465 * <jc>// Get config file.</jc> 1466 * Config <jv>config</jv> = <jv>req</jv>.getConfig(); 1467 * 1468 * <jc>// Get simple values from config file.</jc> 1469 * <jk>int</jk> <jv>timeout</jv> = <jv>config</jv>.get(<js>"MyResource/timeout"</js>).asInteger().orElse(=10000); 1470 * 1471 * <jc>// Get complex values from config file.</jc> 1472 * MyBean <jv>bean</jv> = <jv>config</jv>.get(<js>"MyResource/myBean"</js>).as(MyBean.<jk>class</jk>).orElse(<jk>null</jk>); 1473 * } 1474 * </p> 1475 * 1476 * <h5 class='section'>Notes:</h5><ul> 1477 * <li class='note'> 1478 * The {@link Config} object can also be passed as a parameter on the method. 1479 * </ul> 1480 * 1481 * <h5 class='section'>See Also:</h5><ul> 1482 * <li class='link'><a class="doclink" href="../../../../index.html#jrs.ConfigurationFiles">Configuration Files</a> 1483 * </ul> 1484 * 1485 * @return 1486 * The config file associated with the resource, or <jk>null</jk> if resource does not have a config file 1487 * associated with it. 1488 */ 1489 public Config getConfig() { 1490 if (config == null) 1491 config = context.getConfig().resolving(getVarResolverSession()); 1492 return config; 1493 } 1494 1495 /** 1496 * Creates a proxy interface to retrieve HTTP parts of this request as a proxy bean. 1497 * 1498 * <h5 class='section'>Examples:</h5> 1499 * <p class='bjava'> 1500 * <ja>@RestPost</ja>(<js>"/mypath/{p1}/{p2}/*"</js>) 1501 * <jk>public void</jk> myMethod(<ja>@Request</ja> MyRequest <jv>requestBean</jv>) {...} 1502 * 1503 * <jk>public interface</jk> MyRequest { 1504 * 1505 * <ja>@Path</ja> <jc>// Path variable name inferred from getter.</jc> 1506 * String getP1(); 1507 * 1508 * <ja>@Path</ja>(<js>"p2"</js>) 1509 * String getX(); 1510 * 1511 * <ja>@Path</ja>(<js>"/*"</js>) 1512 * String getRemainder(); 1513 * 1514 * <ja>@Query</ja> 1515 * String getQ1(); 1516 * 1517 * <jc>// Schema-based query parameter: Pipe-delimited lists of comma-delimited lists of integers.</jc> 1518 * <ja>@Query</ja>( 1519 * collectionFormat=<js>"pipes"</js> 1520 * items=<ja>@Items</ja>( 1521 * items=<ja>@SubItems</ja>( 1522 * collectionFormat=<js>"csv"</js> 1523 * type=<js>"integer"</js> 1524 * ) 1525 * ) 1526 * ) 1527 * <jk>int</jk>[][] getQ3(); 1528 * 1529 * <ja>@Header</ja>(<js>"*"</js>) 1530 * Map<String,Object> getHeaders(); 1531 * </p> 1532 * 1533 * @param <T> The request bean interface to instantiate. 1534 * @param c The request bean interface to instantiate. 1535 * @return A new request bean proxy for this REST request. 1536 */ 1537 public <T> T getRequest(Class<T> c) { 1538 return getRequest(RequestBeanMeta.create(c, getContext().getAnnotations())); 1539 } 1540 1541 /** 1542 * Same as {@link #getRequest(Class)} but used on pre-instantiated {@link RequestBeanMeta} objects. 1543 * 1544 * @param <T> The request bean interface to instantiate. 1545 * @param rbm The metadata about the request bean interface to create. 1546 * @return A new request bean proxy for this REST request. 1547 */ 1548 public <T> T getRequest(final RequestBeanMeta rbm) { 1549 try { 1550 Class<T> c = (Class<T>)rbm.getClassMeta().getInnerClass(); 1551 final BeanSession bs = getBeanSession(); 1552 final BeanMeta<T> bm = bs.getBeanMeta(c); 1553 return (T)Proxy.newProxyInstance( 1554 c.getClassLoader(), 1555 new Class[] { c }, 1556 new InvocationHandler() { 1557 @Override /* InvocationHandler */ 1558 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 1559 RequestBeanPropertyMeta pm = rbm.getProperty(method.getName()); 1560 if (pm != null) { 1561 HttpPartParserSession pp = pm.getParser(getPartParserSession()); 1562 HttpPartSchema schema = pm.getSchema(); 1563 String name = pm.getPartName(); 1564 ClassMeta<?> type = bs.getClassMeta(method.getGenericReturnType()); 1565 HttpPartType pt = pm.getPartType(); 1566 if (pt == HttpPartType.BODY) 1567 return getContent().setSchema(schema).as(type); 1568 if (pt == QUERY) 1569 return getQueryParam(name).parser(pp).schema(schema).as(type).orElse(null); 1570 if (pt == FORMDATA) 1571 return getFormParam(name).parser(pp).schema(schema).as(type).orElse(null); 1572 if (pt == HEADER) 1573 return getHeaderParam(name).parser(pp).schema(schema).as(type).orElse(null); 1574 if (pt == PATH) 1575 return getPathParam(name).parser(pp).schema(schema).as(type).orElse(null); 1576 } 1577 return null; 1578 } 1579 1580 }); 1581 } catch (Exception e) { 1582 throw asRuntimeException(e); 1583 } 1584 } 1585 1586 /* Called by RestSession.finish() */ 1587 void close() { 1588 if (config != null) { 1589 try { 1590 config.close(); 1591 } catch (IOException e) { 1592 e.printStackTrace(); 1593 } 1594 } 1595 } 1596 1597 /** 1598 * Returns the wrapped servlet request. 1599 * 1600 * @return The wrapped servlet request. 1601 */ 1602 public HttpServletRequest getHttpServletRequest() { 1603 return inner; 1604 } 1605 1606 /** 1607 * Returns the part serializer session for this request. 1608 * 1609 * @return The part serializer session for this request. 1610 */ 1611 public HttpPartSerializerSession getPartSerializerSession() { 1612 return opContext.getPartSerializer().getPartSession(); 1613 } 1614 1615 @Override /* Object */ 1616 public String toString() { 1617 StringBuilder sb = new StringBuilder("\n").append(getRequestLine()).append("\n"); 1618 sb.append("---Headers---\n"); 1619 getHeaders().forEach(x -> sb.append("\t").append(x).append("\n")); 1620 String m = getMethod(); 1621 if (m.equals("PUT") || m.equals("POST")) { 1622 try { 1623 sb.append("---Content UTF-8---\n"); 1624 sb.append(content.asString()).append("\n"); 1625 sb.append("---Content Hex---\n"); 1626 sb.append(content.asSpacedHex()).append("\n"); 1627 } catch (Exception e1) { 1628 sb.append(e1.getLocalizedMessage()); 1629 } 1630 } 1631 return sb.toString(); 1632 } 1633 1634 //----------------------------------------------------------------------------------------------------------------- 1635 // Utility methods 1636 //----------------------------------------------------------------------------------------------------------------- 1637 1638 /* 1639 * Converts an Accept-Language value entry to a Locale. 1640 */ 1641 private static Locale toLocale(String lang) { 1642 String country = ""; 1643 int i = lang.indexOf('-'); 1644 if (i > -1) { 1645 country = lang.substring(i+1).trim(); 1646 lang = lang.substring(0,i).trim(); 1647 } 1648 return new Locale(lang, country); 1649 } 1650}