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 javax.servlet.http.HttpServletResponse.*; 016import static org.apache.juneau.internal.CollectionUtils.*; 017import static org.apache.juneau.internal.ObjectUtils.*; 018import static org.apache.juneau.internal.IOUtils.*; 019import static org.apache.juneau.internal.StringUtils.*; 020import static org.apache.juneau.rest.util.RestUtils.*; 021import static org.apache.juneau.rest.Enablement.*; 022import static org.apache.juneau.rest.HttpRuntimeException.*; 023import static org.apache.juneau.BasicIllegalArgumentException.*; 024 025import java.io.*; 026import java.lang.reflect.*; 027import java.lang.reflect.Method; 028import java.nio.charset.*; 029import java.time.*; 030import java.util.*; 031import java.util.concurrent.*; 032import java.util.concurrent.atomic.*; 033import java.util.stream.*; 034 035import javax.activation.*; 036import javax.servlet.*; 037import javax.servlet.http.*; 038 039import org.apache.juneau.*; 040import org.apache.juneau.annotation.*; 041import org.apache.juneau.collections.*; 042import org.apache.juneau.config.*; 043import org.apache.juneau.cp.*; 044import org.apache.juneau.cp.Messages; 045import org.apache.juneau.encoders.*; 046import org.apache.juneau.html.*; 047import org.apache.juneau.html.annotation.*; 048import org.apache.juneau.http.*; 049import org.apache.juneau.http.annotation.*; 050import org.apache.juneau.http.annotation.Body; 051import org.apache.juneau.http.annotation.FormData; 052import org.apache.juneau.http.annotation.HasFormData; 053import org.apache.juneau.http.annotation.HasQuery; 054import org.apache.juneau.http.annotation.Header; 055import org.apache.juneau.http.annotation.Path; 056import org.apache.juneau.http.annotation.Query; 057import org.apache.juneau.http.annotation.Response; 058import org.apache.juneau.httppart.*; 059import org.apache.juneau.httppart.bean.*; 060import org.apache.juneau.json.*; 061import org.apache.juneau.jsonschema.*; 062import org.apache.juneau.msgpack.*; 063import org.apache.juneau.mstat.*; 064import org.apache.juneau.oapi.*; 065import org.apache.juneau.parser.*; 066import org.apache.juneau.plaintext.*; 067import org.apache.juneau.reflect.*; 068import org.apache.juneau.rest.annotation.*; 069import org.apache.juneau.rest.converters.*; 070import org.apache.juneau.http.exception.*; 071import org.apache.juneau.http.remote.*; 072import org.apache.juneau.rest.reshandlers.*; 073import org.apache.juneau.rest.util.*; 074import org.apache.juneau.rest.vars.*; 075import org.apache.juneau.serializer.*; 076import org.apache.juneau.soap.*; 077import org.apache.juneau.svl.*; 078import org.apache.juneau.uon.*; 079import org.apache.juneau.urlencoding.*; 080import org.apache.juneau.utils.*; 081import org.apache.juneau.xml.*; 082 083/** 084 * Contains all the configuration on a REST resource and the entry points for handling REST calls. 085 * 086 * <ul class='seealso'> 087 * <li class='link'>{@doc RestContext} 088 * </ul> 089 */ 090@ConfigurableContext(nocache=true) 091public class RestContext extends BeanContext { 092 093 /** Represents a null value for the {@link Rest#context()} annotation.*/ 094 @SuppressWarnings("javadoc") 095 public static final class Null extends RestContext { 096 public Null(RestContextBuilder builder) throws Exception { 097 super(builder); 098 } 099 } 100 101 //------------------------------------------------------------------------------------------------------------------- 102 // Configurable properties 103 //------------------------------------------------------------------------------------------------------------------- 104 105 static final String PREFIX = "RestContext"; 106 107 /** 108 * Configuration property: Allow body URL parameter. 109 * 110 * <h5 class='section'>Property:</h5> 111 * <ul class='spaced-list'> 112 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_allowBodyParam REST_allowBodyParam} 113 * <li><b>Name:</b> <js>"RestContext.allowBodyParam.b"</js> 114 * <li><b>Data type:</b> <jk>boolean</jk> 115 * <li><b>System property:</b> <c>RestContext.allowBodyParam</c> 116 * <li><b>Environment variable:</b> <c>RESTCONTEXT_ALLOWBODYPARAM</c> 117 * <li><b>Default:</b> <jk>true</jk> 118 * <li><b>Session property:</b> <jk>false</jk> 119 * <li><b>Annotations:</b> 120 * <ul> 121 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#allowBodyParam()} 122 * </ul> 123 * <li><b>Methods:</b> 124 * <ul> 125 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#allowBodyParam(boolean)} 126 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#dontAllowBodyParam()} 127 * </ul> 128 * </ul> 129 * 130 * <h5 class='section'>Description:</h5> 131 * <p> 132 * When enabled, the HTTP body content on PUT and POST requests can be passed in as text using the <js>"body"</js> 133 * URL parameter. 134 * <br> 135 * For example: 136 * <p class='bcode w800'> 137 * ?body=(name='John%20Smith',age=45) 138 * </p> 139 * 140 * <h5 class='section'>Example:</h5> 141 * <p class='bcode w800'> 142 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 143 * <ja>@Rest</ja>(allowBodyParam=<js>"$C{REST/allowBodyParam,false}"</js>) 144 * <jk>public class</jk> MyResource { 145 * 146 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 147 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 148 * 149 * <jc>// Using method on builder.</jc> 150 * builder.allowBodyParam(<jk>false</jk>); 151 * 152 * <jc>// Same, but using property.</jc> 153 * builder.set(<jsf>REST_allowBodyParam</jsf>, <jk>false</jk>); 154 * } 155 * 156 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 157 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 158 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 159 * builder.allowBodyParam(<jk>false</jk>); 160 * } 161 * } 162 * </p> 163 * 164 * <ul class='notes'> 165 * <li> 166 * <js>'body'</js> parameter name is case-insensitive. 167 * <li> 168 * Useful for debugging PUT and POST methods using only a browser. 169 * </ul> 170 */ 171 public static final String REST_allowBodyParam = PREFIX + ".allowBodyParam.b"; 172 173 /** 174 * Configuration property: Allowed header URL parameters. 175 * 176 * <h5 class='section'>Property:</h5> 177 * <ul class='spaced-list'> 178 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_allowedHeaderParams REST_allowedHeaderParams} 179 * <li><b>Name:</b> <js>"RestContext.allowedHeaderParams.s"</js> 180 * <li><b>Data type:</b> <c>String</c> (comma-delimited) 181 * <li><b>System property:</b> <c>RestContext.allowedHeaderParams</c> 182 * <li><b>Environment variable:</b> <c>RESTCONTEXT_ALLOWHEADERPARAMS</c> 183 * <li><b>Default:</b> <js>"Accept,Content-Type"</js> 184 * <li><b>Session property:</b> <jk>false</jk> 185 * <li><b>Annotations:</b> 186 * <ul> 187 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#allowedHeaderParams()} 188 * </ul> 189 * <li><b>Methods:</b> 190 * <ul> 191 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#allowedHeaderParams(String)} 192 * </ul> 193 * </ul> 194 * 195 * <h5 class='section'>Description:</h5> 196 * <p> 197 * When specified, allows headers such as <js>"Accept"</js> and <js>"Content-Type"</js> to be passed in as URL query 198 * parameters. 199 * <br> 200 * For example: 201 * <p class='bcode w800'> 202 * ?Accept=text/json&Content-Type=text/json 203 * </p> 204 * 205 * <h5 class='section'>Example:</h5> 206 * <p class='bcode w800'> 207 * <jc>// Option #1 - Defined via annotation.</jc> 208 * <ja>@Rest</ja>(allowedHeaderParams=<js>"Accept,Content-Type"</js>) 209 * <jk>public class</jk> MyResource { 210 * 211 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 212 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 213 * 214 * <jc>// Using method on builder.</jc> 215 * builder.allowedHeaderParams(<js>"Accept,Content-Type"</js>); 216 * 217 * <jc>// Same, but using property.</jc> 218 * builder.set(<jsf>REST_allowedHeaderParams</jsf>, <js>"Accept,Content-Type"</js>); 219 * } 220 * 221 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 222 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 223 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 224 * builder.allowedHeaderParams(<js>"Accept,Content-Type"</js>); 225 * } 226 * } 227 * </p> 228 * 229 * <ul class='notes'> 230 * <li> 231 * Useful for debugging REST interface using only a browser so that you can quickly simulate header values 232 * in the URL bar. 233 * <li> 234 * Header names are case-insensitive. 235 * <li> 236 * Use <js>"*"</js> to allow any headers to be specified as URL parameters. 237 * <li> 238 * Use <js>"NONE"</js> (case insensitive) to suppress inheriting a value from a parent class. 239 * </ul> 240 */ 241 public static final String REST_allowedHeaderParams = PREFIX + ".allowedHeaderParams.s"; 242 243 /** 244 * Configuration property: Allowed method headers. 245 * 246 * <h5 class='section'>Property:</h5> 247 * <ul class='spaced-list'> 248 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_allowedMethodHeaders REST_allowedMethodHeaders} 249 * <li><b>Name:</b> <js>"RestContext.allowedMethodHeaders.s"</js> 250 * <li><b>Data type:</b> <c>String</c> (comma-delimited) 251 * <li><b>System property:</b> <c>RestContext.allowedMethodHeaders</c> 252 * <li><b>Environment variable:</b> <c>RESTCONTEXT_ALLOWEDMETHODHEADERS</c> 253 * <li><b>Default:</b> empty string 254 * <li><b>Session property:</b> <jk>false</jk> 255 * <li><b>Annotations:</b> 256 * <ul> 257 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#allowedMethodHeaders()} 258 * </ul> 259 * <li><b>Methods:</b> 260 * <ul> 261 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#allowedMethodHeaders(String)} 262 * </ul> 263 * </ul> 264 * 265 * <h5 class='section'>Description:</h5> 266 * <p> 267 * A comma-delimited list of HTTP method names that are allowed to be passed as values in an <c>X-Method</c> HTTP header 268 * to override the real HTTP method name. 269 * <p> 270 * Allows you to override the actual HTTP method with a simulated method. 271 * <br>For example, if an HTTP Client API doesn't support <c>PATCH</c> but does support <c>POST</c> (because 272 * <c>PATCH</c> is not part of the original HTTP spec), you can add a <c>X-Method: PATCH</c> header on a normal 273 * <c>HTTP POST /foo</c> request call which will make the HTTP call look like a <c>PATCH</c> request in any of the REST APIs. 274 * 275 * <h5 class='section'>Example:</h5> 276 * <p class='bcode w800'> 277 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 278 * <ja>@Rest</ja>(allowedMethodHeaders=<js>"PATCH"</js>) 279 * <jk>public class</jk> MyResource { 280 * 281 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 282 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 283 * 284 * <jc>// Using method on builder.</jc> 285 * builder.allowedMethodHeaders(<js>"PATCH"</js>); 286 * 287 * <jc>// Same, but using property.</jc> 288 * builder.set(<jsf>REST_allowedMethodHeaders</jsf>, <js>"PATCH"</js>); 289 * } 290 * 291 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 292 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 293 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 294 * builder.allowedMethodHeaders(<js>"PATCH"</js>); 295 * } 296 * } 297 * </p> 298 * 299 * <ul class='notes'> 300 * <li> 301 * Method names are case-insensitive. 302 * <li> 303 * Use <js>"*"</js> to represent all methods. 304 * <li> 305 * Use <js>"NONE"</js> (case insensitive) to suppress inheriting a value from a parent class. 306 * </ul> 307 */ 308 public static final String REST_allowedMethodHeaders = PREFIX + ".allowedMethodHeaders.s"; 309 310 /** 311 * Configuration property: Allowed method URL parameters. 312 * 313 * <h5 class='section'>Property:</h5> 314 * <ul class='spaced-list'> 315 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_allowedMethodParams REST_allowedMethodParams} 316 * <li><b>Name:</b> <js>"RestContext.allowedMethodParams.s"</js> 317 * <li><b>Data type:</b> <c>String</c> (comma-delimited) 318 * <li><b>System property:</b> <c>RestContext.allowedMethodParams</c> 319 * <li><b>Environment variable:</b> <c>RESTCONTEXT_ALLOWEDMETHODPARAMS</c> 320 * <li><b>Default:</b> <js>"HEAD,OPTIONS"</js> 321 * <li><b>Session property:</b> <jk>false</jk> 322 * <li><b>Annotations:</b> 323 * <ul> 324 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#allowedMethodParams()} 325 * </ul> 326 * <li><b>Methods:</b> 327 * <ul> 328 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#allowedMethodParams(String)} 329 * </ul> 330 * </ul> 331 * 332 * <h5 class='section'>Description:</h5> 333 * <p> 334 * When specified, the HTTP method can be overridden by passing in a <js>"method"</js> (case-insensitive) URL parameter on a regular 335 * GET request. 336 * <br> 337 * For example: 338 * <p class='bcode w800'> 339 * /myservlet/myendpoint?method=OPTIONS 340 * </p> 341 * <p> 342 * Useful in cases where you want to simulate a non-GET request in a browser by simply adding a parameter. 343 * <br>Also useful if you want to construct hyperlinks to non-GET REST endpoints such as links to <c>OPTIONS</c> 344 * pages. 345 * 346 * <p> 347 * Note that per the {@doc ExtRFC2616.section9 HTTP specification}, special care should 348 * be taken when allowing non-safe (<c>POST</c>, <c>PUT</c>, <c>DELETE</c>) methods to be invoked through GET requests. 349 * 350 * 351 * <h5 class='section'>Example:</h5> 352 * <p class='bcode w800'> 353 * <jc>// Option #1 - Defined via annotation.</jc> 354 * <ja>@Rest</ja>(allowedMethodParams=<js>"HEAD,OPTIONS,PUT"</js>) 355 * <jk>public class</jk> MyResource { 356 * 357 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 358 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 359 * 360 * <jc>// Using method on builder.</jc> 361 * builder.allowedMethodParams(<js>"HEAD,OPTIONS,PUT"</js>); 362 * 363 * <jc>// Same, but using property.</jc> 364 * builder.set(<jsf>REST_allowedMethodParams</jsf>, <js>"HEAD,OPTIONS,PUT"</js>); 365 * } 366 * 367 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 368 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 369 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 370 * builder.allowedMethodParams(<js>"HEAD,OPTIONS,PUT"</js>); 371 * } 372 * } 373 * </p> 374 * 375 * <ul class='notes'> 376 * <li> 377 * Format is a comma-delimited list of HTTP method names that can be passed in as a method parameter. 378 * <li> 379 * <js>'method'</js> parameter name is case-insensitive. 380 * <li> 381 * Use <js>"*"</js> to represent all methods. 382 * <li> 383 * Use <js>"NONE"</js> (case insensitive) to suppress inheriting a value from a parent class. 384 * </ul> 385 */ 386 public static final String REST_allowedMethodParams = PREFIX + ".allowedMethodParams.s"; 387 388 /** 389 * Configuration property: Allow header URL parameters. 390 * 391 * <div class='warn'> 392 * <b>Deprecated</b> - Use {@link #REST_allowedHeaderParams} 393 * </div> 394 */ 395 @Deprecated 396 public static final String REST_allowHeaderParams = PREFIX + ".allowHeaderParams.b"; 397 398 /** 399 * Configuration property: REST call handler. 400 * 401 * <div class='warn'> 402 * <b>Deprecated</b> - Use {@link RestContext#REST_context} and override methods. 403 * </div> 404 */ 405 @Deprecated 406 public static final String REST_callHandler = PREFIX + ".callHandler.o"; 407 408 /** 409 * Configuration property: REST call logger. 410 * 411 * <h5 class='section'>Property:</h5> 412 * <ul class='spaced-list'> 413 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_callLogger REST_callLogger} 414 * <li><b>Name:</b> <js>"RestContext.callLogger.o"</js> 415 * <li><b>Data type:</b> 416 * <ul> 417 * <li>{@link org.apache.juneau.rest.RestCallLogger} 418 * <li><c>Class<{@link org.apache.juneau.rest.RestCallLogger}></c> 419 * </ul> 420 * <li><b>Default:</b> {@link org.apache.juneau.rest.BasicRestCallLogger} 421 * <li><b>Session property:</b> <jk>false</jk> 422 * <li><b>Annotations:</b> 423 * <ul> 424 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#callLogger()} 425 * </ul> 426 * <li><b>Methods:</b> 427 * <ul> 428 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#callLogger(Class)} 429 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#callLogger(RestCallLogger)} 430 * </ul> 431 * </ul> 432 * 433 * <h5 class='section'>Description:</h5> 434 * <p> 435 * Specifies the logger to use for logging of HTTP requests and responses. 436 * 437 * <h5 class='section'>Example:</h5> 438 * <p class='bcode w800'> 439 * <jc>// Our customized logger.</jc> 440 * <jk>public class</jk> MyLogger <jk>extends</jk> BasicRestCallLogger { 441 * 442 * <ja>@Override</ja> 443 * <jk>public void</jk> log(RestCallLoggerConfig config, HttpServletRequest req, HttpServletResponse res) { 444 * <jc>// Handle logging ourselves.</jc> 445 * } 446 * } 447 * 448 * <jc>// Option #1 - Registered via annotation resolving to a config file setting with default value.</jc> 449 * <ja>@Rest</ja>(callLogger=MyLogger.<jk>class</jk>) 450 * <jk>public class</jk> MyResource { 451 * 452 * <jc>// Option #2 - Registered via builder passed in through resource constructor.</jc> 453 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 454 * 455 * <jc>// Using method on builder.</jc> 456 * builder.callLogger(MyLogger.<jk>class</jk>); 457 * 458 * <jc>// Same, but using property.</jc> 459 * builder.set(<jsf>REST_callLogger</jsf>, MyLogger.<jk>class</jk>); 460 * } 461 * 462 * <jc>// Option #3 - Registered via builder passed in through init method.</jc> 463 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 464 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 465 * builder.callLogger(MyLogger.<jk>class</jk>); 466 * } 467 * } 468 * </p> 469 * 470 * <ul class='notes'> 471 * <li> 472 * The default call logger if not specified is {@link BasicRestCallLogger}. 473 * <li> 474 * The resource class itself will be used if it implements the {@link RestCallLogger} interface and not 475 * explicitly overridden via this annotation. 476 * <li> 477 * The {@link RestServlet} and {@link BasicRest} classes implement the {@link RestCallLogger} interface with the same 478 * that gets used if not overridden by this annotation. 479 * <br>Subclasses can also alter the behavior by overriding these methods. 480 * <li> 481 * When defined as a class, the implementation must have one of the following constructors: 482 * <ul> 483 * <li><code><jk>public</jk> T(RestContext)</code> 484 * <li><code><jk>public</jk> T()</code> 485 * <li><code><jk>public static</jk> T <jsm>create</jsm>(RestContext)</code> 486 * <li><code><jk>public static</jk> T <jsm>create</jsm>()</code> 487 * </ul> 488 * <li> 489 * Inner classes of the REST resource class are allowed. 490 * </ul> 491 * 492 * <ul class='seealso'> 493 * <li class='link'>{@doc RestLoggingAndDebugging} 494 * </ul> 495 */ 496 public static final String REST_callLogger = PREFIX + ".callLogger.o"; 497 498 /** 499 * Configuration property: REST call logging rules. 500 * 501 * <h5 class='section'>Property:</h5> 502 * <ul class='spaced-list'> 503 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_callLoggerConfig REST_callLoggerConfig} 504 * <li><b>Name:</b> <js>"RestContext.callLoggerConfig.o"</js> 505 * <li><b>Data type:</b> {@link org.apache.juneau.rest.RestCallLoggerConfig} 506 * <li><b>Default:</b> {@link org.apache.juneau.rest.RestCallLoggerConfig#DEFAULT_NOOP} 507 * <li><b>Session property:</b> <jk>false</jk> 508 * <li><b>Annotations:</b> 509 * <ul> 510 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#logging()} 511 * </ul> 512 * <li><b>Methods:</b> 513 * <ul> 514 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#callLoggerConfig(RestCallLoggerConfig)} 515 * </ul> 516 * </ul> 517 * 518 * <h5 class='section'>Description:</h5> 519 * <p> 520 * Specifies rules on how to handle logging of HTTP requests/responses. 521 * 522 * <h5 class='section'>Example:</h5> 523 * <p class='bcode w800'> 524 * <jc>// Option #1 - Registered via annotation.</jc> 525 * <ja>@Rest</ja>( 526 * logging=<ja>@Logging</ja>( 527 * level=<js>"INFO"</js>, 528 * rules={ 529 * <ja>@LoggingRule</ja>(codes=<js>"400-499"</js>, level=<js>"WARNING"</js>, req=<js>"SHORT"</js>, res=<js>"MEDIUM"</js>), 530 * <ja>@LoggingRule</ja>(codes=<js>">=500"</js>, level=<js>"SEVERE"</js>, req=<js>"LONG"</js>, res=<js>"LONG"</js>) 531 * } 532 * } 533 * ) 534 * <jk>public class</jk> MyResource { 535 * 536 * <jc>// Option #2 - Registered via builder passed in through resource constructor.</jc> 537 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 538 * 539 * <jc>// Using method on builder.</jc> 540 * builder.callLoggerConfig( 541 * RestCallLoggerConfig 542 * .<jsm>create</jsm>() 543 * .level(Level.<jsf>INFO</jsf>) 544 * .rules( 545 * RestCallLoggingRule 546 * .<jsm>create</jsm>() 547 * .codes(<js>"400-499"</js>) 548 * .level(<jsf>WARNING</jsf>) 549 * .req(<jsf>SHORT</jsf>) 550 * .res(<jsf>MEDIUM</jsf>) 551 * .build(), 552 * RestCallLoggingRule 553 * .<jsm>create</jsm>() 554 * .codes(<js>">=500"</js>) 555 * .level(<jsf>SEVERE</jsf>) 556 * .req(<jsf>LONG</jsf>) 557 * .res(<jsf>LONG</jsf>) 558 * .build() 559 * ) 560 * .build() 561 * ); 562 * 563 * <jc>// Same, but using property with JSON value.</jc> 564 * builder.set(<jsf>REST_callLoggerConfig</jsf>, <js>"{level:'INFO',rules:[{codes:'400-499',level:'WARNING',...},...]}"</js>); 565 * } 566 * } 567 * </p> 568 * 569 * <ul class='seealso'> 570 * <li class='link'>{@doc RestLoggingAndDebugging} 571 * </ul> 572 */ 573 public static final String REST_callLoggerConfig = PREFIX + ".callLoggerConfig.o"; 574 575 /** 576 * Configuration property: Children. 577 * 578 * <h5 class='section'>Property:</h5> 579 * <ul class='spaced-list'> 580 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_children REST_children} 581 * <li><b>Name:</b> <js>"RestContext.children.lo"</js> 582 * <li><b>Data type:</b> <c>List<Class|Object|{@link org.apache.juneau.rest.RestChild}></c> 583 * <li><b>Default:</b> empty list 584 * <li><b>Session property:</b> <jk>false</jk> 585 * <li><b>Annotations:</b> 586 * <ul> 587 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#children()} 588 * </ul> 589 * <li><b>Methods:</b> 590 * <ul> 591 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#child(String,Object)} 592 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#children(Class...)} 593 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#children(Object...)} 594 * </ul> 595 * </ul> 596 * 597 * <h5 class='section'>Description:</h5> 598 * <p> 599 * Defines children of this resource. 600 * 601 * <p> 602 * A REST child resource is simply another servlet or object that is initialized as part of the ascendant resource and has a 603 * servlet path directly under the ascendant resource object path. 604 * <br>The main advantage to defining servlets as REST children is that you do not need to define them in the 605 * <c>web.xml</c> file of the web application. 606 * <br>This can cut down on the number of entries that show up in the <c>web.xml</c> file if you are defining 607 * large numbers of servlets. 608 * 609 * <p> 610 * Child resources must specify a value for {@link Rest#path() @Rest(path)} that identifies the subpath of the child resource 611 * relative to the ascendant path UNLESS you use the {@link RestContextBuilder#child(String, Object)} method to register it. 612 * 613 * <p> 614 * Child resources can be nested arbitrarily deep using this technique (i.e. children can also have children). 615 * 616 * <dl> 617 * <dt>Servlet initialization:</dt> 618 * <dd> 619 * <p> 620 * A child resource will be initialized immediately after the ascendant servlet/resource is initialized. 621 * <br>The child resource receives the same servlet config as the ascendant servlet/resource. 622 * <br>This allows configuration information such as servlet initialization parameters to filter to child 623 * resources. 624 * </p> 625 * </dd> 626 * <dt>Runtime behavior:</dt> 627 * <dd> 628 * <p> 629 * As a rule, methods defined on the <c>HttpServletRequest</c> object will behave as if the child 630 * servlet were deployed as a top-level resource under the child's servlet path. 631 * <br>For example, the <c>getServletPath()</c> and <c>getPathInfo()</c> methods on the 632 * <c>HttpServletRequest</c> object will behave as if the child resource were deployed using the 633 * child's servlet path. 634 * <br>Therefore, the runtime behavior should be equivalent to deploying the child servlet in the 635 * <c>web.xml</c> file of the web application. 636 * </p> 637 * </dd> 638 * </dl> 639 * 640 * <h5 class='section'>Example:</h5> 641 * <p class='bcode w800'> 642 * <jc>// Our child resource.</jc> 643 * <ja>@Rest</ja>(path=<js>"/child"</js>) 644 * <jk>public class</jk> MyChildResource {...} 645 * 646 * <jc>// Option #1 - Registered via annotation.</jc> 647 * <ja>@Rest</ja>(children={MyChildResource.<jk>class</jk>}) 648 * <jk>public class</jk> MyResource { 649 * 650 * <jc>// Option #2 - Registered via builder passed in through resource constructor.</jc> 651 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 652 * 653 * <jc>// Using method on builder.</jc> 654 * builder.children(MyChildResource.<jk>class</jk>); 655 * 656 * <jc>// Same, but using property.</jc> 657 * builder.addTo(<jsf>REST_children</jsf>, MyChildResource.<jk>class</jk>)); 658 * 659 * <jc>// Use a pre-instantiated object instead.</jc> 660 * builder.child(<js>"/child"</js>, <jk>new</jk> MyChildResource()); 661 * } 662 * 663 * <jc>// Option #3 - Registered via builder passed in through init method.</jc> 664 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 665 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 666 * builder.children(MyChildResource.<jk>class</jk>); 667 * } 668 * } 669 * </p> 670 * 671 * <ul class='notes'> 672 * <li> 673 * When defined as classes, instances are resolved using the registered {@link #REST_resourceResolver} which 674 * by default is {@link BasicRestResourceResolver} which requires the class have one of the following 675 * constructors: 676 * <ul> 677 * <li><code><jk>public</jk> T(RestContextBuilder)</code> 678 * <li><code><jk>public</jk> T()</code> 679 * </ul> 680 * </ul> 681 * 682 * <ul class='seealso'> 683 * <li class='link'>{@doc RestChildren} 684 * </ul> 685 */ 686 public static final String REST_children = PREFIX + ".children.lo"; 687 688 /** 689 * Configuration property: Classpath resource finder. 690 * 691 * <h5 class='section'>Property:</h5> 692 * <ul class='spaced-list'> 693 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_classpathResourceFinder REST_classpathResourceFinder} 694 * <li><b>Name:</b> <js>"RestContext.classpathResourceFinder.o"</js> 695 * <li><b>Data type:</b> {@link org.apache.juneau.cp.ResourceFinder} 696 * <li><b>Default:</b> {@link org.apache.juneau.cp.BasicResourceFinder} 697 * <li><b>Session property:</b> <jk>false</jk> 698 * <li><b>Annotations:</b> 699 * <ul> 700 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#classpathResourceFinder()} 701 * </ul> 702 * <li><b>Methods:</b> 703 * <ul> 704 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#classpathResourceFinder(Class)} 705 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#classpathResourceFinder(ResourceFinder)} 706 * </ul> 707 * </ul> 708 * 709 * <h5 class='section'>Description:</h5> 710 * <p> 711 * Used to retrieve localized files from the classpath. 712 * 713 * <p> 714 * Used by the following methods: 715 * <ul class='javatree'> 716 * <li class='jc'>{@link RestContext} 717 * <ul> 718 * <li class='jm'>{@link #getClasspathResource(String,Locale) getClasspathResource(String,Locale)} 719 * <li class='jm'>{@link #getClasspathResource(Class,MediaType,String,Locale) getClasspathResource(Class,MediaType,String,Locale)} 720 * <li class='jm'>{@link #getClasspathResourceAsString(String,Locale) getClasspathResourceAsString(String,Locale)} 721 * <li class='jm'>{@link #getStaticFile(String) resolveStaticFile(String)} 722 * </ul> 723 * <li class='jc'>{@link RestRequest} 724 * <ul> 725 * <li class='jm'>{@link RestRequest#getClasspathHttpResource(String) getClasspathHttpResource(String)} 726 * <li class='jm'>{@link RestRequest#getClasspathHttpResource(String,boolean) getClasspathHttpResource(String,boolean)} 727 * <li class='jm'>{@link RestRequest#getClasspathHttpResource(String,boolean,MediaType,boolean) getClasspathHttpResource(String,boolean,MediaType,boolean)} 728 * </ul> 729 * </ul> 730 * 731 * <p> 732 * It also affects the behavior of the {@link #REST_staticFiles} property. 733 * 734 * <h5 class='section'>Example:</h5> 735 * <p class='bcode w800'> 736 * <jc>// Our customized classpath resource finder.</jc> 737 * <jk>public class</jk> MyClasspathResourceFinder <jk>extends</jk> ClasspathResourceFinderBasic { 738 * <ja>@Override</ja> 739 * <jk>public</jk> InputStream findResource(Class<?> baseClass, String name, Locale locale) <jk>throws</jk> IOException { 740 * <jc>// Do your own resolution.</jc> 741 * } 742 * } 743 * 744 * <jc>// Option #1 - Registered via annotation.</jc> 745 * <ja>@Rest</ja>(classpathResourceFinder=MyClasspathResourceFinder.<jk>class</jk>) 746 * <jk>public class</jk> MyResource { 747 * 748 * <jc>// Option #2 - Registered via builder passed in through resource constructor.</jc> 749 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 750 * 751 * <jc>// Using method on builder.</jc> 752 * builder.classpathResourceFinder(MyClasspathResourceFinder.<jk>class</jk>); 753 * 754 * <jc>// Same, but using property.</jc> 755 * builder.set(<jsf>REST_classpathResourceFinder</jsf>, MyClasspathResourceFinder.<jk>class</jk>)); 756 * 757 * <jc>// Use a pre-instantiated object instead.</jc> 758 * builder.classpathResourceFinder(<jk>new</jk> MyClasspathResourceFinder()); 759 * } 760 * 761 * <jc>// Option #3 - Registered via builder passed in through init method.</jc> 762 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 763 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 764 * builder.classpathResourceFinder(MyClasspathResourceFinder.<jk>class</jk>); 765 * } 766 * } 767 * </p> 768 * 769 * <ul class='notes'> 770 * <li> 771 * The default value is {@link BasicResourceFinder} which provides basic support for finding localized 772 * resources on the classpath and JVM working directory. 773 * <br>The {@link RecursiveResourceFinder} is another option that also recursively searches for resources 774 * up the class-hierarchy. 775 * <br>Each of these classes can be extended to provide customized handling of resource retrieval. 776 * <li> 777 * The resource class itself will be used if it implements the {@link ResourceFinder} interface and not 778 * explicitly overridden via this annotation. 779 * <li> 780 * The {@link RestServlet} and {@link BasicRest} classes implement the {@link ResourceFinder} interface with the same 781 * functionality as {@link BasicResourceFinder} that gets used if not overridden by this annotation. 782 * <br>Subclasses can also alter the behavior by overriding these methods. 783 * <li> 784 * When defined as a class, the implementation must have one of the following constructors: 785 * <ul> 786 * <li><code><jk>public</jk> T(RestContext)</code> 787 * <li><code><jk>public</jk> T()</code> 788 * <li><code><jk>public static</jk> T <jsm>create</jsm>(RestContext)</code> 789 * <li><code><jk>public static</jk> T <jsm>create</jsm>()</code> 790 * </ul> 791 * <li> 792 * Inner classes of the REST resource class are allowed. 793 * </ul> 794 */ 795 public static final String REST_classpathResourceFinder = PREFIX + ".classpathResourceFinder.o"; 796 797 /** 798 * Configuration property: Client version header. 799 * 800 * <h5 class='section'>Property:</h5> 801 * <ul class='spaced-list'> 802 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_clientVersionHeader REST_clientVersionHeader} 803 * <li><b>Name:</b> <js>"RestContext.clientVersionHeader.s"</js> 804 * <li><b>Data type:</b> <c>String</c> 805 * <li><b>System property:</b> <c>RestContext.clientVersionHeader</c> 806 * <li><b>Environment variable:</b> <c>RESTCONTEXT_CLIENTVERSIONHEADER</c> 807 * <li><b>Default:</b> <js>"X-Client-Version"</js> 808 * <li><b>Session property:</b> <jk>false</jk> 809 * <li><b>Annotations:</b> 810 * <ul> 811 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#clientVersionHeader()} 812 * </ul> 813 * <li><b>Methods:</b> 814 * <ul> 815 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#clientVersionHeader(String)} 816 * </ul> 817 * </ul> 818 * 819 * <h5 class='section'>Description:</h5> 820 * <p> 821 * Specifies the name of the header used to denote the client version on HTTP requests. 822 * 823 * <p> 824 * The client version is used to support backwards compatibility for breaking REST interface changes. 825 * <br>Used in conjunction with {@link RestMethod#clientVersion() @RestMethod(clientVersion)} annotation. 826 * 827 * <h5 class='section'>Example:</h5> 828 * <p class='bcode w800'> 829 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 830 * <ja>@Rest</ja>(clientVersionHeader=<js>"$C{REST/clientVersionHeader,Client-Version}"</js>) 831 * <jk>public class</jk> MyResource { 832 * 833 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 834 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 835 * 836 * <jc>// Using method on builder.</jc> 837 * builder.clientVersionHeader(<js>"Client-Version"</js>); 838 * 839 * <jc>// Same, but using property.</jc> 840 * builder.set(<jsf>REST_clientVersionHeader</jsf>, <js>"Client-Version"</js>); 841 * } 842 * 843 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 844 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 845 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 846 * builder.clientVersionHeader(<js>"Client-Version"</js>); 847 * } 848 * } 849 * </p> 850 * <p class='bcode w800'> 851 * <jc>// Call this method if Client-Version is at least 2.0. 852 * // Note that this also matches 2.0.1.</jc> 853 * <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/foobar"</js>, clientVersion=<js>"2.0"</js>) 854 * <jk>public</jk> Object method1() { 855 * ... 856 * } 857 * 858 * <jc>// Call this method if Client-Version is at least 1.1, but less than 2.0.</jc> 859 * <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/foobar"</js>, clientVersion=<js>"[1.1,2.0)"</js>) 860 * <jk>public</jk> Object method2() { 861 * ... 862 * } 863 * 864 * <jc>// Call this method if Client-Version is less than 1.1.</jc> 865 * <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/foobar"</js>, clientVersion=<js>"[0,1.1)"</js>) 866 * <jk>public</jk> Object method3() { 867 * ... 868 * } 869 * </p> 870 */ 871 public static final String REST_clientVersionHeader = PREFIX + ".clientVersionHeader.s"; 872 873 /** 874 * Configuration property: Class-level response converters. 875 * 876 * <h5 class='section'>Property:</h5> 877 * <ul class='spaced-list'> 878 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_converters REST_converters} 879 * <li><b>Name:</b> <js>"RestContext.converters.lo"</js> 880 * <li><b>Data type:</b> <c>List<{@link org.apache.juneau.rest.RestConverter}|Class<{@link org.apache.juneau.rest.RestConverter}>></c> 881 * <li><b>Default:</b> empty list 882 * <li><b>Session property:</b> <jk>false</jk> 883 * <li><b>Annotations:</b> 884 * <ul> 885 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#converters()} 886 * <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#converters()} 887 * </ul> 888 * <li><b>Methods:</b> 889 * <ul> 890 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#converters(Class...)} 891 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#converters(RestConverter...)} 892 * </ul> 893 * </ul> 894 * 895 * <h5 class='section'>Description:</h5> 896 * <p> 897 * Associates one or more {@link RestConverter converters} with a resource class. 898 * <br>These converters get called immediately after execution of the REST method in the same order specified in the 899 * annotation. 900 * <br>The object passed into this converter is the object returned from the Java method or passed into 901 * the {@link RestResponse#setOutput(Object)} method. 902 * 903 * <p> 904 * Can be used for performing post-processing on the response object before serialization. 905 * 906 * <p> 907 * When multiple converters are specified, they're executed in the order they're specified in the annotation 908 * (e.g. first the results will be traversed, then the resulting node will be searched/sorted). 909 * 910 * <h5 class='section'>Example:</h5> 911 * <p class='bcode w800'> 912 * <jc>// Our converter.</jc> 913 * <jk>public class</jk> MyConverter <jk>implements</jk> RestConverter { 914 * <ja>@Override</ja> 915 * <jk>public</jk> Object convert(RestRequest req, Object o) { 916 * <jc>// Do something with object and return another object.</jc> 917 * <jc>// Or just return the same object for a no-op.</jc> 918 * } 919 * } 920 * 921 * <jc>// Option #1 - Registered via annotation resolving to a config file setting with default value.</jc> 922 * <ja>@Rest</ja>(converters={MyConverter.<jk>class</jk>}) 923 * <jk>public class</jk> MyResource { 924 * 925 * <jc>// Option #2 - Registered via builder passed in through resource constructor.</jc> 926 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 927 * 928 * <jc>// Using method on builder.</jc> 929 * builder.converters(MyConverter.<jk>class</jk>); 930 * 931 * <jc>// Same, but using property.</jc> 932 * builder.set(<jsf>REST_converters</jsf>, MyConverter.<jk>class</jk>); 933 * 934 * <jc>// Pass in an instance instead.</jc> 935 * builder.converters(<jk>new</jk> MyConverter()); 936 * } 937 * 938 * <jc>// Option #3 - Registered via builder passed in through init method.</jc> 939 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 940 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 941 * builder.converters(MyConverter.<jk>class</jk>); 942 * } 943 * } 944 * </p> 945 * 946 * <ul class='seealso'> 947 * <li class='jc'>{@link Traversable} - Allows URL additional path info to address individual elements in a POJO tree. 948 * <li class='jc'>{@link Queryable} - Allows query/view/sort functions to be performed on POJOs. 949 * <li class='jc'>{@link Introspectable} - Allows Java public methods to be invoked on the returned POJOs. 950 * <li class='link'>{@doc RestConverters} 951 * </ul> 952 * 953 * <ul class='notes'> 954 * <li> 955 * When defined as a class, the implementation must have one of the following constructors: 956 * <ul> 957 * <li><code><jk>public</jk> T(BeanContext)</code> 958 * <li><code><jk>public</jk> T()</code> 959 * <li><code><jk>public static</jk> T <jsm>create</jsm>(RestContext)</code> 960 * <li><code><jk>public static</jk> T <jsm>create</jsm>()</code> 961 * </ul> 962 * <li> 963 * Inner classes of the REST resource class are allowed. 964 * </ul> 965 */ 966 public static final String REST_converters = PREFIX + ".converters.lo"; 967 968 /** 969 * Configuration property: Debug mode. 970 * 971 * <h5 class='section'>Property:</h5> 972 * <ul class='spaced-list'> 973 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_debug REST_debug} 974 * <li><b>Name:</b> <js>"RestContext.debug.s"</js> 975 * <li><b>Data type:</b> {@link org.apache.juneau.rest.Enablement} 976 * <li><b>System property:</b> <c>RestContext.debug</c> 977 * <li><b>Environment variable:</b> <c>RESTCONTEXT_DEBUG</c> 978 * <li><b>Default:</b> {@link org.apache.juneau.rest.Enablement#FALSE} 979 * <li><b>Session property:</b> <jk>false</jk> 980 * <li><b>Annotations:</b> 981 * <ul> 982 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#debug()} 983 * </ul> 984 * <li><b>Methods:</b> 985 * <ul> 986 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#debug()} 987 * </ul> 988 * </ul> 989 * 990 * <h5 class='section'>Description:</h5> 991 * <p> 992 * Enables the following: 993 * <ul class='spaced-list'> 994 * <li> 995 * HTTP request/response bodies are cached in memory for logging purposes. 996 * <li> 997 * Request/response messages are automatically logged always or per request. 998 * </ul> 999 */ 1000 public static final String REST_debug = PREFIX + ".debug.s"; 1001 1002 /** 1003 * Configuration property: Debug mode on specified classes/methods. 1004 * 1005 * <h5 class='section'>Property:</h5> 1006 * <ul class='spaced-list'> 1007 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_debugOn REST_debugOn} 1008 * <li><b>Name:</b> <js>"RestContext.debugOn.s"</js> 1009 * <li><b>Data type:</b> <c>String</c> (comma-delimited) 1010 * <li><b>System property:</b> <c>RestContext.debugOn</c> 1011 * <li><b>Environment variable:</b> <c>RESTCONTEXT_DEBUGON</c> 1012 * <li><b>Default:</b> Empty string 1013 * <li><b>Session property:</b> <jk>false</jk> 1014 * <li><b>Annotations:</b> 1015 * <ul> 1016 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#debugOn()} 1017 * </ul> 1018 * <li><b>Methods:</b> 1019 * <ul> 1020 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#debugOn(String)} 1021 * </ul> 1022 * </ul> 1023 * 1024 * <h5 class='section'>Description:</h5> 1025 * <p> 1026 * Enables the following: 1027 * <ul class='spaced-list'> 1028 * <li> 1029 * HTTP request/response bodies are cached in memory for logging purposes. 1030 * <li> 1031 * Request/response messages are automatically logged always or per request. 1032 * </ul> 1033 */ 1034 public static final String REST_debugOn = PREFIX + ".debugOn.s"; 1035 1036 /** 1037 * Configuration property: Default character encoding. 1038 * 1039 * <h5 class='section'>Property:</h5> 1040 * <ul class='spaced-list'> 1041 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_defaultCharset REST_defaultCharset} 1042 * <li><b>Name:</b> <js>"RestContext.defaultCharset.s"</js> 1043 * <li><b>Data type:</b> <c>String</c> 1044 * <li><b>System property:</b> <c>RestContext.defaultCharset</c> 1045 * <li><b>Environment variable:</b> <c>RESTCONTEXT_DEFAULTCHARSET</c> 1046 * <li><b>Default:</b> <js>"utf-8"</js> 1047 * <li><b>Session property:</b> <jk>false</jk> 1048 * <li><b>Annotations:</b> 1049 * <ul> 1050 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#defaultCharset()} 1051 * <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#defaultCharset()} 1052 * </ul> 1053 * <li><b>Methods:</b> 1054 * <ul> 1055 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#defaultCharset(String)} 1056 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#defaultCharset(Charset)} 1057 * </ul> 1058 * </ul> 1059 * 1060 * <h5 class='section'>Description:</h5> 1061 * <p> 1062 * The default character encoding for the request and response if not specified on the request. 1063 * 1064 * <h5 class='section'>Example:</h5> 1065 * <p class='bcode w800'> 1066 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 1067 * <ja>@Rest</ja>(defaultCharset=<js>"$C{REST/defaultCharset,US-ASCII}"</js>) 1068 * <jk>public class</jk> MyResource { 1069 * 1070 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 1071 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 1072 * 1073 * <jc>// Using method on builder.</jc> 1074 * builder.defaultCharset(<js>"US-ASCII"</js>); 1075 * 1076 * <jc>// Same, but using property.</jc> 1077 * builder.set(<jsf>REST_defaultCharset</jsf>, <js>"US-ASCII"</js>); 1078 * } 1079 * 1080 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 1081 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 1082 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 1083 * builder.defaultCharset(<js>"US-ASCII"</js>); 1084 * } 1085 * 1086 * <jc>// Override at the method level.</jc> 1087 * <ja>@RestMethod</ja>(defaultCharset=<js>"UTF-16"</js>) 1088 * public Object myMethod() {...} 1089 * } 1090 * </p> 1091 */ 1092 public static final String REST_defaultCharset = PREFIX + ".defaultCharset.s"; 1093 1094 /** 1095 * Configuration property: Default request attributes. 1096 * 1097 * <div class='warn'> 1098 * <b>Deprecated</b> - Use {@link #REST_reqAttrs} 1099 * </div> 1100 */ 1101 @Deprecated 1102 public static final String REST_attrs = PREFIX + ".reqAttrs.smo"; 1103 1104 /** 1105 * Configuration property: Default request headers. 1106 * 1107 * <div class='warn'> 1108 * <b>Deprecated</b> - Use {@link #REST_reqHeaders} 1109 * </div> 1110 */ 1111 @Deprecated 1112 public static final String REST_defaultRequestHeaders = PREFIX + ".reqHeaders.smo"; 1113 1114 /** 1115 * Configuration property: Default response headers. 1116 * 1117 * <div class='warn'> 1118 * <b>Deprecated</b> - Use {@link #REST_resHeaders} 1119 * </div> 1120 */ 1121 @Deprecated 1122 public static final String REST_defaultResponseHeaders = PREFIX + ".resHeaders.omo"; 1123 1124 /** 1125 * Configuration property: Compression encoders. 1126 * 1127 * <h5 class='section'>Property:</h5> 1128 * <ul class='spaced-list'> 1129 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_encoders REST_encoders} 1130 * <li><b>Name:</b> <js>"RestContext.encoders.o"</js> 1131 * <li><b>Data type:</b> <c>List<{@link org.apache.juneau.encoders.Encoder}|Class<{@link org.apache.juneau.encoders.Encoder}>></c> 1132 * <li><b>Default:</b> empty list 1133 * <li><b>Session property:</b> <jk>false</jk> 1134 * <li><b>Annotations:</b> 1135 * <ul> 1136 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#encoders()} 1137 * <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#encoders()} 1138 * </ul> 1139 * <li><b>Methods:</b> 1140 * <ul> 1141 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#encoders(Class...)} 1142 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#encoders(Encoder...)} 1143 * </ul> 1144 * </ul> 1145 * 1146 * <h5 class='section'>Description:</h5> 1147 * <p> 1148 * These can be used to enable various kinds of compression (e.g. <js>"gzip"</js>) on requests and responses. 1149 * 1150 * <h5 class='section'>Example:</h5> 1151 * <p class='bcode w800'> 1152 * <jc>// Option #1 - Registered via annotation.</jc> 1153 * <ja>@Rest</ja>(encoders={GzipEncoder.<jk>class</jk>}) 1154 * <jk>public class</jk> MyResource { 1155 * 1156 * <jc>// Option #2 - Registered via builder passed in through resource constructor.</jc> 1157 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 1158 * 1159 * <jc>// Using method on builder.</jc> 1160 * builder.encoders(GzipEncoder.<jk>class</jk>); 1161 * 1162 * <jc>// Same, but using property.</jc> 1163 * builder.addTo(<jsf>REST_encoders</jsf>, GzipEncoder.<jk>class</jk>); 1164 * } 1165 * 1166 * <jc>// Option #3 - Registered via builder passed in through init method.</jc> 1167 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 1168 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 1169 * builder.encoders(GzipEncoder.<jk>class</jk>); 1170 * } 1171 * 1172 * <jc>// Override at the method level.</jc> 1173 * <ja>@RestMethod</ja>(encoders={MySpecialEncoder.<jk>class</jk>}, inherit={<js>"ENCODERS"</js>}) 1174 * public Object myMethod() {...} 1175 * } 1176 * </p> 1177 * 1178 * <ul class='notes'> 1179 * <li> 1180 * When defined as a class, the implementation must have one of the following constructors: 1181 * <ul> 1182 * <li><code><jk>public</jk> T(BeanContext)</code> 1183 * <li><code><jk>public</jk> T()</code> 1184 * <li><code><jk>public static</jk> T <jsm>create</jsm>(RestContext)</code> 1185 * <li><code><jk>public static</jk> T <jsm>create</jsm>()</code> 1186 * </ul> 1187 * <li> 1188 * Inner classes of the REST resource class are allowed. 1189 * </ul> 1190 * 1191 * <ul class='seealso'> 1192 * <li class='link'>{@doc RestEncoders} 1193 * </ul> 1194 */ 1195 public static final String REST_encoders = PREFIX + ".encoders.lo"; 1196 1197 /** 1198 * Configuration property: Class-level guards. 1199 * 1200 * <h5 class='section'>Property:</h5> 1201 * <ul class='spaced-list'> 1202 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_guards REST_guards} 1203 * <li><b>Name:</b> <js>"RestContext.guards.lo"</js> 1204 * <li><b>Data type:</b> <c>List<{@link org.apache.juneau.rest.RestGuard}|Class<{@link org.apache.juneau.rest.RestGuard}>></c> 1205 * <li><b>Default:</b> empty list 1206 * <li><b>Session property:</b> <jk>false</jk> 1207 * <li><b>Annotations:</b> 1208 * <ul> 1209 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#guards()} 1210 * <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#guards()} 1211 * </ul> 1212 * <li><b>Methods:</b> 1213 * <ul> 1214 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#guards(Class...)} 1215 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#guards(RestGuard...)} 1216 * </ul> 1217 * </ul> 1218 * 1219 * <h5 class='section'>Description:</h5> 1220 * <p> 1221 * Associates one or more {@link RestGuard RestGuards} with all REST methods defined in this class. 1222 * <br>These guards get called immediately before execution of any REST method in this class. 1223 * 1224 * <p> 1225 * If multiple guards are specified, <b>ALL</b> guards must pass. 1226 * <br>Note that this is different than matchers where only ONE matcher needs to pass. 1227 * 1228 * <h5 class='section'>Example:</h5> 1229 * <p class='bcode w800'> 1230 * <jc>// Define a guard that only lets Billy make a request.</jc> 1231 * <jk>public</jk> BillyGuard <jk>extends</jk> RestGuard { 1232 * <ja>@Override</ja> 1233 * <jk>public boolean</jk> isRequestAllowed(RestRequest req) { 1234 * <jk>return</jk> req.getUserPrincipal().getName().equals(<js>"Billy"</js>); 1235 * } 1236 * } 1237 * 1238 * <jc>// Option #1 - Registered via annotation.</jc> 1239 * <ja>@Rest</ja>(guards={BillyGuard.<jk>class</jk>}) 1240 * <jk>public class</jk> MyResource { 1241 * 1242 * <jc>// Option #2 - Registered via builder passed in through resource constructor.</jc> 1243 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 1244 * 1245 * <jc>// Using method on builder.</jc> 1246 * builder.guards(BillyGuard.<jk>class</jk>); 1247 * 1248 * <jc>// Same, but using property.</jc> 1249 * builder.addTo(<jsf>REST_guards</jsf>, BillyGuard.<jk>class</jk>); 1250 * } 1251 * 1252 * <jc>// Option #3 - Registered via builder passed in through init method.</jc> 1253 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 1254 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 1255 * builder.guards(BillyGuard.<jk>class</jk>); 1256 * } 1257 * 1258 * <jc>// Override at the method level.</jc> 1259 * <ja>@RestMethod</ja>(guards={SomeOtherGuard.<jk>class</jk>}) 1260 * public Object myMethod() {...} 1261 * } 1262 * </p> 1263 * 1264 * <ul class='notes'> 1265 * <li> 1266 * When defined as a class, the implementation must have one of the following constructors: 1267 * <ul> 1268 * <li><code><jk>public</jk> T(RestContext)</code> 1269 * <li><code><jk>public</jk> T()</code> 1270 * <li><code><jk>public static</jk> T <jsm>create</jsm>(RestContext)</code> 1271 * <li><code><jk>public static</jk> T <jsm>create</jsm>()</code> 1272 * </ul> 1273 * <li> 1274 * Inner classes of the REST resource class are allowed. 1275 * </ul> 1276 * 1277 * <ul class='seealso'> 1278 * <li class='link'>{@doc RestGuards} 1279 * </ul> 1280 */ 1281 public static final String REST_guards = PREFIX + ".guards.lo"; 1282 1283 /** 1284 * Configuration property: REST info provider. 1285 * 1286 * <h5 class='section'>Property:</h5> 1287 * <ul class='spaced-list'> 1288 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_infoProvider REST_infoProvider} 1289 * <li><b>Name:</b> <js>"RestContext.infoProvider.o"</js> 1290 * <li><b>Data type:</b> 1291 * <ul> 1292 * <li>{@link org.apache.juneau.rest.RestInfoProvider} 1293 * <li><c>Class<{@link org.apache.juneau.rest.RestInfoProvider}></c> 1294 * </ul> 1295 * <li><b>Default:</b> {@link org.apache.juneau.rest.BasicRestInfoProvider} 1296 * <li><b>Session property:</b> <jk>false</jk> 1297 * <li><b>Annotations:</b> 1298 * <ul> 1299 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#infoProvider()} 1300 * </ul> 1301 * <li><b>Methods:</b> 1302 * <ul> 1303 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#infoProvider(Class)} 1304 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#infoProvider(RestInfoProvider)} 1305 * </ul> 1306 * </ul> 1307 * 1308 * <h5 class='section'>Description:</h5> 1309 * <p> 1310 * Class used to retrieve title/description/swagger information about a resource. 1311 * 1312 * <h5 class='section'>Example:</h5> 1313 * <p class='bcode w800'> 1314 * <jc>// Our customized info provider.</jc> 1315 * <jc>// Extend from the default implementation and selectively override values.</jc> 1316 * <jk>public class</jk> MyRestInfoProvider <jk>extends</jk> BasicRestInfoProvider { 1317 * 1318 * <jc>// Must provide this constructor!</jc> 1319 * <jk>public</jk> MyRestInfoProvider(RestContext context) { 1320 * <jk>super</jk>(context); 1321 * } 1322 * 1323 * <ja>@Override</ja> 1324 * <jk>public</jk> Swagger getSwaggerFromFile(RestRequest req) <jk>throws</jk> RestException { 1325 * <jc>// Provide our own method of retrieving swagger from file system.</jc> 1326 * } 1327 * 1328 * <ja>@Override</ja> 1329 * <jk>public</jk> Swagger getSwagger(RestRequest req) <jk>throws</jk> RestException { 1330 * Swagger s = <jk>super</jk>.getSwagger(req); 1331 * <jc>// Made inline modifications to generated swagger.</jc> 1332 * <jk>return</jk> s; 1333 * } 1334 * 1335 * <ja>@Override</ja> 1336 * <jk>public</jk> String getSiteName(RestRequest req) { 1337 * <jc>// Override the site name.</jc> 1338 * } 1339 * } 1340 * 1341 * <jc>// Option #1 - Registered via annotation resolving to a config file setting with default value.</jc> 1342 * <ja>@Rest</ja>(infoProvider=MyRestInfoProvider.<jk>class</jk>) 1343 * <jk>public class</jk> MyResource { 1344 * 1345 * <jc>// Option #2 - Registered via builder passed in through resource constructor.</jc> 1346 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 1347 * 1348 * <jc>// Using method on builder.</jc> 1349 * builder.infoProvider(MyRestInfoProvider.<jk>class</jk>); 1350 * 1351 * <jc>// Same, but using property.</jc> 1352 * builder.set(<jsf>REST_infoProvider</jsf>, MyRestInfoProvider.<jk>class</jk>); 1353 * } 1354 * 1355 * <jc>// Option #3 - Registered via builder passed in through init method.</jc> 1356 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 1357 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 1358 * builder.infoProvider(MyRestInfoProvider.<jk>class</jk>); 1359 * } 1360 * } 1361 * </p> 1362 * 1363 * <ul class='notes'> 1364 * <li> 1365 * The default info provider if not specified is {@link BasicRestInfoProvider}. 1366 * <li> 1367 * The resource class itself will be used if it implements the {@link RestInfoProvider} interface and not 1368 * explicitly overridden via this annotation. 1369 * <li> 1370 * The{@link RestServlet} and {@link BasicRest} classes implement the {@link RestInfoProvider} interface with the same 1371 * functionality as {@link BasicRestInfoProvider} that gets used if not overridden by this annotation. 1372 * <br>Subclasses can also alter the behavior by overriding these methods. 1373 * <li> 1374 * When defined as a class, the implementation must have one of the following constructors: 1375 * <ul> 1376 * <li><code><jk>public</jk> T(RestContext)</code> 1377 * <li><code><jk>public</jk> T()</code> 1378 * <li><code><jk>public static</jk> T <jsm>create</jsm>(RestContext)</code> 1379 * <li><code><jk>public static</jk> T <jsm>create</jsm>()</code> 1380 * </ul> 1381 * <li> 1382 * Inner classes of the REST resource class are allowed. 1383 * </ul> 1384 */ 1385 public static final String REST_infoProvider = PREFIX + ".infoProvider.o"; 1386 1387 /** 1388 * Configuration property: REST logger. 1389 * 1390 * <div class='warn'> 1391 * <b>Deprecated</b> - Use {@link #REST_callLogger} 1392 * </div> 1393 */ 1394 @Deprecated 1395 public static final String REST_logger = PREFIX + ".logger.o"; 1396 1397 /** 1398 * Configuration property: The maximum allowed input size (in bytes) on HTTP requests. 1399 * 1400 * <h5 class='section'>Property:</h5> 1401 * <ul class='spaced-list'> 1402 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_maxInput REST_maxInput} 1403 * <li><b>Name:</b> <js>"RestContext.maxInput.s"</js> 1404 * <li><b>Data type:</b> <c>String</c> 1405 * <li><b>System property:</b> <c>RestContext.maxInput</c> 1406 * <li><b>Environment variable:</b> <c>RESTCONTEXT_MAXINPUT</c> 1407 * <li><b>Default:</b> <js>"100M"</js> 1408 * <li><b>Session property:</b> <jk>false</jk> 1409 * <li><b>Annotations:</b> 1410 * <ul> 1411 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#maxInput()} 1412 * <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#maxInput()} 1413 * </ul> 1414 * <li><b>Methods:</b> 1415 * <ul> 1416 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#maxInput(String)} 1417 * </ul> 1418 * </ul> 1419 * 1420 * <h5 class='section'>Description:</h5> 1421 * <p> 1422 * Useful for alleviating DoS attacks by throwing an exception when too much input is received instead of resulting 1423 * in out-of-memory errors which could affect system stability. 1424 * 1425 * <h5 class='section'>Example:</h5> 1426 * <p class='bcode w800'> 1427 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 1428 * <ja>@Rest</ja>(maxInput=<js>"$C{REST/maxInput,10M}"</js>) 1429 * <jk>public class</jk> MyResource { 1430 * 1431 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 1432 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 1433 * 1434 * <jc>// Using method on builder.</jc> 1435 * builder.maxInput(<js>"10M"</js>); 1436 * 1437 * <jc>// Same, but using property.</jc> 1438 * builder.set(<jsf>REST_maxInput</jsf>, <js>"10M"</js>); 1439 * } 1440 * 1441 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 1442 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 1443 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 1444 * builder.maxInput(<js>"10M"</js>); 1445 * } 1446 * 1447 * <jc>// Override at the method level.</jc> 1448 * <ja>@RestMethod</ja>(maxInput=<js>"10M"</js>) 1449 * public Object myMethod() {...} 1450 * } 1451 * </p> 1452 * 1453 * <ul class='notes'> 1454 * <li> 1455 * String value that gets resolved to a <jk>long</jk>. 1456 * <li> 1457 * Can be suffixed with any of the following representing kilobytes, megabytes, and gigabytes: 1458 * <js>'K'</js>, <js>'M'</js>, <js>'G'</js>. 1459 * <li> 1460 * A value of <js>"-1"</js> can be used to represent no limit. 1461 * </ul> 1462 */ 1463 public static final String REST_maxInput = PREFIX + ".maxInput.s"; 1464 1465 /** 1466 * Configuration property: Messages. 1467 * 1468 * <h5 class='section'>Property:</h5> 1469 * <ul class='spaced-list'> 1470 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_messages REST_messages} 1471 * <li><b>Name:</b> <js>"RestContext.messages.lo"</js> 1472 * <li><b>Data type:</b> <c>List<{@link org.apache.juneau.utils.Tuple2}<Class,String>></c> 1473 * <li><b>Default:</b> <jk>null</jk> 1474 * <li><b>Session property:</b> <jk>false</jk> 1475 * <li><b>Annotations:</b> 1476 * <ul> 1477 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#messages()} 1478 * </ul> 1479 * <li><b>Methods:</b> 1480 * <ul> 1481 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#messages(String)}, 1482 * </ul> 1483 * </ul> 1484 * 1485 * <h5 class='section'>Description:</h5> 1486 * <p> 1487 * Identifies the location of the resource bundle for this class if it's different from the class name. 1488 * 1489 * <p> 1490 * By default, the resource bundle name is assumed to match the class name. For example, given the class 1491 * <c>MyClass.java</c>, the resource bundle is assumed to be <c>MyClass.properties</c>. This property 1492 * allows you to override this setting to specify a different location such as <c>MyMessages.properties</c> by 1493 * specifying a value of <js>"MyMessages"</js>. 1494 * 1495 * <p> 1496 * Resource bundles are searched using the following base name patterns: 1497 * <ul> 1498 * <li><js>"{package}.{name}"</js> 1499 * <li><js>"{package}.i18n.{name}"</js> 1500 * <li><js>"{package}.nls.{name}"</js> 1501 * <li><js>"{package}.messages.{name}"</js> 1502 * </ul> 1503 * 1504 * <p> 1505 * This annotation is used to provide request-localized (based on <c>Accept-Language</c>) messages for the following methods: 1506 * <ul class='javatree'> 1507 * <li class='jm'>{@link RestRequest#getMessage(String, Object...)} 1508 * <li class='jm'>{@link RestContext#getMessages() RestContext.getMessages()} 1509 * </ul> 1510 * 1511 * <p> 1512 * Request-localized messages are also available by passing either of the following parameter types into your Java method: 1513 * <ul class='javatree'> 1514 * <li class='jc'>{@link ResourceBundle} - Basic Java resource bundle. 1515 * <li class='jc'>{@link Messages} - Extended resource bundle with several convenience methods. 1516 * </ul> 1517 * 1518 * The value can be a relative path like <js>"nls/Messages"</js>, indicating to look for the resource bundle 1519 * <js>"com.foo.sample.nls.Messages"</js> if the resource class is in <js>"com.foo.sample"</js>, or it can be an 1520 * absolute path like <js>"com.foo.sample.nls.Messages"</js> 1521 * 1522 * <h5 class='section'>Examples:</h5> 1523 * <p class='bcode w800'> 1524 * <cc># Contents of org/apache/foo/nls/MyMessages.properties</cc> 1525 * 1526 * <ck>HelloMessage</ck> = <cv>Hello {0}!</cv> 1527 * </p> 1528 * <p class='bcode w800'> 1529 * <jc>// Contents of org/apache/foo/MyResource.java</jc> 1530 * 1531 * <ja>@Rest</ja>(messages=<js>"nls/MyMessages"</js>) 1532 * <jk>public class</jk> MyResource {...} 1533 * 1534 * <ja>@RestMethod</ja>(name=<js>"GET"</js>, path=<js>"/hello/{you}"</js>) 1535 * <jk>public</jk> Object helloYou(RestRequest <jv>req</jv>, Messages <jv>messages</jv>, <ja>@Path</ja>(<js>"name"</js>) String <jv>you</jv>) { 1536 * String <jv>s</jv>; 1537 * 1538 * <jc>// Get it from the RestRequest object.</jc> 1539 * <jv>s</jv> = <jv>req</jv>.getMessage(<js>"HelloMessage"</js>, <jv>you</jv>); 1540 * 1541 * <jc>// Or get it from the method parameter.</jc> 1542 * <jv>s</jv> = <jv>messages</jv>.getString(<js>"HelloMessage"</js>, <jv>you</jv>); 1543 * 1544 * <jc>// Or get the message in a locale different from the request.</jc> 1545 * <jv>s</jv> = <jv>messages</jv>.forLocale(Locale.<jsf>UK</jsf>).getString(<js>"HelloMessage"</js>, <jv>you</jv>); 1546 * 1547 * <jk>return</jk> <jv>s</jv>; 1548 * } 1549 * } 1550 * </p> 1551 * 1552 * <ul class='notes'> 1553 * <li>Mappings are cumulative from super classes. 1554 * <br>Therefore, you can find and retrieve messages up the class-hierarchy chain. 1555 * </ul> 1556 * 1557 * <ul class='seealso'> 1558 * <li class='jc'>{@link Messages} 1559 * <li class='link'>{@doc RestMessages} 1560 * </ul> 1561 */ 1562 public static final String REST_messages = PREFIX + ".messages.lo"; 1563 1564 /** 1565 * Configuration property: MIME types. 1566 * 1567 * <h5 class='section'>Property:</h5> 1568 * <ul class='spaced-list'> 1569 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_mimeTypes REST_mimeTypes} 1570 * <li><b>Name:</b> <js>"RestContext.mimeTypes.ss"</js> 1571 * <li><b>Data type:</b> <c>Set<String></c> 1572 * <li><b>System property:</b> <c>RestContext.mimeTypes</c> 1573 * <li><b>Environment variable:</b> <c>RESTCONTEXT_MIMETYPES</c> 1574 * <li><b>Default:</b> empty list 1575 * <li><b>Session property:</b> <jk>false</jk> 1576 * <li><b>Annotations:</b> 1577 * <ul> 1578 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#mimeTypes()} 1579 * </ul> 1580 * <li><b>Methods:</b> 1581 * <ul> 1582 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#mimeTypes(String...)} 1583 * </ul> 1584 * </ul> 1585 * 1586 * <h5 class='section'>Description:</h5> 1587 * <p> 1588 * Defines MIME-type file type mappings. 1589 * 1590 * <p> 1591 * Used for specifying the content type on file resources retrieved through the following methods: 1592 * <ul class='javatree'> 1593 * <li class='jm'>{@link RestContext#getStaticFile(String) RestContext.resolveStaticFile(String)} 1594 * <li class='jm'>{@link RestRequest#getClasspathHttpResource(String,boolean,MediaType,boolean)} 1595 * <li class='jm'>{@link RestRequest#getClasspathHttpResource(String,boolean)} 1596 * <li class='jm'>{@link RestRequest#getClasspathHttpResource(String)} 1597 * </ul> 1598 * 1599 * <p> 1600 * This list appends to the existing list provided by {@link ExtendedMimetypesFileTypeMap}. 1601 * 1602 * <h5 class='section'>Example:</h5> 1603 * <p class='bcode w800'> 1604 * <jc>// Option #1 - Defined via annotation.</jc> 1605 * <ja>@Rest</ja>(mimeTypes={<js>"text/plain txt text TXT"</js>}) 1606 * <jk>public class</jk> MyResource { 1607 * 1608 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 1609 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 1610 * 1611 * <jc>// Using method on builder.</jc> 1612 * builder.mimeTypes(<js>"text/plain txt text TXT"</js>); 1613 * 1614 * <jc>// Same, but using property.</jc> 1615 * builder.addTo(<jsf>REST_mimeTypes</jsf>, <js>"text/plain txt text TXT"</js>); 1616 * } 1617 * 1618 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 1619 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 1620 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 1621 * builder.mimeTypes(<js>"text/plain txt text TXT"</js>); 1622 * } 1623 * } 1624 * </p> 1625 * 1626 * <ul class='notes'> 1627 * <li> 1628 * Values are .mime.types formatted entry string. 1629 * <br>Example: <js>"image/svg+xml svg"</js> 1630 * </ul> 1631 */ 1632 public static final String REST_mimeTypes = PREFIX + ".mimeTypes.ss"; 1633 1634 /** 1635 * Configuration property: Java method parameter resolvers. 1636 * 1637 * <h5 class='section'>Property:</h5> 1638 * <ul class='spaced-list'> 1639 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_paramResolvers REST_paramResolvers} 1640 * <li><b>Name:</b> <js>"RestContext.paramResolvers.lo"</js> 1641 * <li><b>Data type:</b> <c>List<{@link org.apache.juneau.rest.RestMethodParam}|Class<{@link org.apache.juneau.rest.RestMethodParam}>></c> 1642 * <li><b>Default:</b> empty list 1643 * <li><b>Session property:</b> <jk>false</jk> 1644 * <li><b>Annotations:</b> 1645 * <ul> 1646 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#paramResolvers()} 1647 * </ul> 1648 * <li><b>Methods:</b> 1649 * <ul> 1650 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#paramResolvers(Class...)} 1651 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#paramResolvers(RestMethodParam...)} 1652 * </ul> 1653 * </ul> 1654 * 1655 * <h5 class='section'>Description:</h5> 1656 * <p> 1657 * By default, the Juneau framework will automatically Java method parameters of various types (e.g. 1658 * <c>RestRequest</c>, <c>Accept</c>, <c>Reader</c>). 1659 * This setting allows you to provide your own resolvers for your own class types that you want resolved. 1660 * 1661 * <p> 1662 * For example, if you want to pass in instances of <c>MySpecialObject</c> to your Java method, define 1663 * the following resolver: 1664 * <p class='bcode w800'> 1665 * <jc>// Define a parameter resolver for resolving MySpecialObject objects.</jc> 1666 * <jk>public class</jk> MyRestParam <jk>extends</jk> RestMethodParam { 1667 * 1668 * <jc>// Must have no-arg constructor!</jc> 1669 * <jk>public</jk> MyRestParam() { 1670 * <jc>// First two parameters help with Swagger doc generation.</jc> 1671 * <jk>super</jk>(<jsf>QUERY</jsf>, <js>"myparam"</js>, MySpecialObject.<jk>class</jk>); 1672 * } 1673 * 1674 * <jc>// The method that creates our object. 1675 * // In this case, we're taking in a query parameter and converting it to our object.</jc> 1676 * <jk>public</jk> Object resolve(RestRequest req, RestResponse res) <jk>throws</jk> Exception { 1677 * <jk>return new</jk> MySpecialObject(req.getQuery().get(<js>"myparam"</js>)); 1678 * } 1679 * } 1680 * 1681 * <jc>// Option #1 - Registered via annotation.</jc> 1682 * <ja>@Rest</ja>(paramResolvers=MyRestParam.<jk>class</jk>) 1683 * <jk>public class</jk> MyResource { 1684 * 1685 * <jc>// Option #2 - Registered via builder passed in through resource constructor.</jc> 1686 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 1687 * 1688 * <jc>// Using method on builder.</jc> 1689 * builder.paramResolvers(MyRestParam.<jk>class</jk>); 1690 * 1691 * <jc>// Same, but using property.</jc> 1692 * builder.addTo(<jsf>REST_paramResolver</jsf>, MyRestParam.<jk>class</jk>); 1693 * } 1694 * 1695 * <jc>// Option #3 - Registered via builder passed in through init method.</jc> 1696 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 1697 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 1698 * builder.paramResolvers(MyRestParam.<jk>class</jk>); 1699 * } 1700 * 1701 * <jc>// Now pass it into your method.</jc> 1702 * <ja>@RestMethod</ja>(...) 1703 * <jk>public</jk> Object doMyMethod(MySpecialObject mySpeciaObject) { 1704 * <jc>// Do something with it.</jc> 1705 * } 1706 * } 1707 * </p> 1708 * 1709 * <ul class='notes'> 1710 * <li> 1711 * When defined as a class, the implementation must have one of the following constructors: 1712 * <ul> 1713 * <li><code><jk>public</jk> T(BeanContext)</code> 1714 * <li><code><jk>public</jk> T()</code> 1715 * <li><code><jk>public static</jk> T <jsm>create</jsm>(RestContext)</code> 1716 * <li><code><jk>public static</jk> T <jsm>create</jsm>()</code> 1717 * </ul> 1718 * <li> 1719 * Inner classes of the REST resource class are allowed. 1720 * <li> 1721 * Refer to {@link RestMethodParam} for the list of predefined parameter resolvers. 1722 * </ul> 1723 */ 1724 public static final String REST_paramResolvers = PREFIX + ".paramResolvers.lo"; 1725 1726 /** 1727 * Configuration property: Parsers. 1728 * 1729 * <h5 class='section'>Property:</h5> 1730 * <ul class='spaced-list'> 1731 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_parsers REST_parsers} 1732 * <li><b>Name:</b> <js>"RestContext.parsers.lo"</js> 1733 * <li><b>Data type:</b> <c>List<{@link org.apache.juneau.parser.Parser}|Class<{@link org.apache.juneau.parser.Parser}>></c> 1734 * <li><b>Default:</b> empty list 1735 * <li><b>Session property:</b> <jk>false</jk> 1736 * <li><b>Annotations:</b> 1737 * <ul> 1738 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#parsers()} 1739 * <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#parsers()} 1740 * </ul> 1741 * <li><b>Methods:</b> 1742 * <ul> 1743 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#parsers(Object...)} 1744 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#parsers(Class...)} 1745 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#parsersReplace(Object...)} 1746 * </ul> 1747 * </ul> 1748 * 1749 * <h5 class='section'>Description:</h5> 1750 * <p> 1751 * Adds class-level parsers to this resource. 1752 * 1753 * <p> 1754 * Parsers are used to convert the body of HTTP requests into POJOs. 1755 * <br>Any of the Juneau framework parsers can be used in this setting. 1756 * <br>The parser selected is based on the request <c>Content-Type</c> header matched against the values returned by the following method 1757 * using a best-match algorithm: 1758 * <ul class='javatree'> 1759 * <li class='jm'>{@link Parser#getMediaTypes()} 1760 * </ul> 1761 * 1762 * <h5 class='section'>Example:</h5> 1763 * <p class='bcode w800'> 1764 * <jc>// Option #1 - Defined via annotation.</jc> 1765 * <ja>@Rest</ja>(parsers={JsonParser.<jk>class</jk>, XmlParser.<jk>class</jk>}) 1766 * <jk>public class</jk> MyResource { 1767 * 1768 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 1769 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 1770 * 1771 * <jc>// Using method on builder.</jc> 1772 * builder.parsers(JsonParser.<jk>class</jk>, XmlParser.<jk>class</jk>); 1773 * 1774 * <jc>// Same, but use pre-instantiated parsers.</jc> 1775 * builder.parsers(JsonParser.<jsf>DEFAULT</jsf>, XmlParser.<jsf>DEFAULT</jsf>); 1776 * 1777 * <jc>// Same, but using property.</jc> 1778 * builder.set(<jsf>REST_parsers</jsf>, JsonParser.<jk>class</jk>, XmlParser.<jk>class</jk>); 1779 * } 1780 * 1781 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 1782 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 1783 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 1784 * builder.parsers(JsonParser.<jk>class</jk>, XmlParser.<jk>class</jk>); 1785 * } 1786 * 1787 * <jc>// Override at the method level.</jc> 1788 * <ja>@RestMethod</ja>(parsers={HtmlParser.<jk>class</jk>}) 1789 * <jk>public</jk> Object myMethod(<ja>@Body</ja> MyPojo myPojo) { 1790 * <jc>// Do something with your parsed POJO.</jc> 1791 * } 1792 * } 1793 * </p> 1794 * 1795 * <ul class='notes'> 1796 * <li> 1797 * When defined as a class, properties/transforms defined on the resource/method are inherited. 1798 * <li> 1799 * When defined as an instance, properties/transforms defined on the resource/method are NOT inherited. 1800 * <li> 1801 * Typically, you'll want your resource to extend directly from {@link BasicRestServlet} which comes 1802 * preconfigured with the following parsers: 1803 * <ul> 1804 * <li class='jc'>{@link JsonParser} 1805 * <li class='jc'>{@link XmlParser} 1806 * <li class='jc'>{@link HtmlParser} 1807 * <li class='jc'>{@link UonParser} 1808 * <li class='jc'>{@link UrlEncodingParser} 1809 * <li class='jc'>{@link MsgPackParser} 1810 * <li class='jc'>{@link PlainTextParser} 1811 * </ul> 1812 * </ul> 1813 * 1814 * <ul class='seealso'> 1815 * <li class='link'>{@doc RestParsers} 1816 * </ul> 1817 */ 1818 public static final String REST_parsers = PREFIX + ".parsers.lo"; 1819 1820 /** 1821 * Configuration property: HTTP part parser. 1822 * 1823 * <h5 class='section'>Property:</h5> 1824 * <ul class='spaced-list'> 1825 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_partParser REST_partParser} 1826 * <li><b>Name:</b> <js>"RestContext.partParser.o"</js> 1827 * <li><b>Data type:</b> <c>{@link org.apache.juneau.httppart.HttpPartParser}|Class<{@link org.apache.juneau.httppart.HttpPartParser}></c> 1828 * <li><b>Default:</b> {@link org.apache.juneau.oapi.OpenApiParser} 1829 * <li><b>Session property:</b> <jk>false</jk> 1830 * <li><b>Annotations:</b> 1831 * <ul> 1832 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#partParser()} 1833 * </ul> 1834 * <li><b>Methods:</b> 1835 * <ul> 1836 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#partParser(Class)} 1837 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#partParser(HttpPartParser)} 1838 * </ul> 1839 * </ul> 1840 * 1841 * <h5 class='section'>Description:</h5> 1842 * <p> 1843 * Specifies the {@link HttpPartParser} to use for parsing headers, query/form parameters, and URI parts. 1844 * 1845 * <p> 1846 * The default value is {@link OpenApiParser} which allows for both plain-text and URL-Encoded-Object-Notation values. 1847 * <br>If your parts contain text that can be confused with UON (e.g. <js>"(foo)"</js>), you can switch to 1848 * {@link SimplePartParser} which treats everything as plain text. 1849 * 1850 * <h5 class='section'>Example:</h5> 1851 * <p class='bcode w800'> 1852 * <jc>// Option #1 - Defined via annotation.</jc> 1853 * <ja>@Rest</ja>(partParser=SimplePartParser.<jk>class</jk>) 1854 * <jk>public class</jk> MyResource { 1855 * 1856 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 1857 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 1858 * 1859 * <jc>// Using method on builder.</jc> 1860 * builder.partParser(SimplePartParser.<jk>class</jk>); 1861 * 1862 * <jc>// Same, but using property.</jc> 1863 * builder.set(<jsf>REST_partParser</jsf>, SimplePartParser.<jk>class</jk>); 1864 * } 1865 * 1866 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 1867 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 1868 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 1869 * builder.partParser(SimplePartParser.<jk>class</jk>); 1870 * } 1871 * 1872 * <ja>@RestMethod</ja>(...) 1873 * <jk>public</jk> Object myMethod(<ja>@Header</ja>(<js>"My-Header"</js>) MyParsedHeader h, <ja>@Query</ja>(<js>"myquery"</js>) MyParsedQuery q) { 1874 * <jc>// Do something with your parsed parts.</jc> 1875 * } 1876 * } 1877 * </p> 1878 * 1879 * <ul class='notes'> 1880 * <li> 1881 * When defined as a class, properties/transforms defined on the resource/method are inherited. 1882 * <li> 1883 * When defined as an instance, properties/transforms defined on the resource/method are NOT inherited. 1884 * </ul> 1885 */ 1886 public static final String REST_partParser = PREFIX + ".partParser.o"; 1887 1888 /** 1889 * Configuration property: HTTP part serializer. 1890 * 1891 * <h5 class='section'>Property:</h5> 1892 * <ul class='spaced-list'> 1893 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_partSerializer REST_partSerializer} 1894 * <li><b>Name:</b> <js>"RestContext.partSerializer.o"</js> 1895 * <li><b>Data type:</b> 1896 * <ul> 1897 * <li>{@link org.apache.juneau.httppart.HttpPartSerializer} 1898 * <li><c>Class<{@link org.apache.juneau.httppart.HttpPartSerializer}></c> 1899 * </ul> 1900 * <li><b>Default:</b> {@link org.apache.juneau.oapi.OpenApiSerializer} 1901 * <li><b>Session property:</b> <jk>false</jk> 1902 * <li><b>Annotations:</b> 1903 * <ul> 1904 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#partSerializer()} 1905 * </ul> 1906 * <li><b>Methods:</b> 1907 * <ul> 1908 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#partSerializer(Class)} 1909 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#partSerializer(HttpPartSerializer)} 1910 * </ul> 1911 * </ul> 1912 * 1913 * <h5 class='section'>Description:</h5> 1914 * <p> 1915 * Specifies the {@link HttpPartSerializer} to use for serializing headers, query/form parameters, and URI parts. 1916 * 1917 * <p> 1918 * The default value is {@link OpenApiSerializer} which serializes based on OpenAPI rules, but defaults to UON notation for beans and maps, and 1919 * plain text for everything else. 1920 * <br>Other options include: 1921 * <ul> 1922 * <li class='jc'>{@link SimplePartSerializer} - Always serializes to plain text. 1923 * <li class='jc'>{@link UonSerializer} - Always serializers to UON. 1924 * </ul> 1925 * 1926 * <h5 class='section'>Example:</h5> 1927 * <p class='bcode w800'> 1928 * <jc>// Option #1 - Defined via annotation.</jc> 1929 * <ja>@Rest</ja>(partSerializer=SimplePartSerializer.<jk>class</jk>) 1930 * <jk>public class</jk> MyResource { 1931 * 1932 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 1933 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 1934 * 1935 * <jc>// Using method on builder.</jc> 1936 * builder.partSerializer(SimplePartSerializer.<jk>class</jk>); 1937 * 1938 * <jc>// Same, but using property.</jc> 1939 * builder.set(<jsf>REST_partSerializer</jsf>, SimplePartSerializer.<jk>class</jk>); 1940 * } 1941 * 1942 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 1943 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 1944 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 1945 * builder.partSerializer(SimplePartSerializer.<jk>class</jk>); 1946 * } 1947 * 1948 * <ja>@RestMethod</ja>(...) 1949 * <jk>public</jk> Object myMethod(RestResponse res) { 1950 * <jc>// Set a header to a POJO.</jc> 1951 * res.setHeader(<js>"My-Header"</js>, <jk>new</jk> MyPojo()); 1952 * } 1953 * } 1954 * </p> 1955 * 1956 * <ul class='notes'> 1957 * <li> 1958 * When defined as a class, properties/transforms defined on the resource/method are inherited. 1959 * <li> 1960 * When defined as an instance, properties/transforms defined on the resource/method are NOT inherited. 1961 * </ul> 1962 */ 1963 public static final String REST_partSerializer = PREFIX + ".partSerializer.o"; 1964 1965 /** 1966 * Configuration property: Resource path. 1967 * 1968 * <h5 class='section'>Property:</h5> 1969 * <ul class='spaced-list'> 1970 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_path REST_path} 1971 * <li><b>Name:</b> <js>"RestContext.path.s"</js> 1972 * <li><b>Data type:</b> <c>String</c> 1973 * <li><b>System property:</b> <c>RestContext.path.</c> 1974 * <li><b>Environment variable:</b> <c>RESTCONTEXT_PATH</c> 1975 * <li><b>Default:</b> <jk>null</jk> 1976 * <li><b>Session property:</b> <jk>false</jk> 1977 * <li><b>Annotations:</b> 1978 * <ul> 1979 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#path()} 1980 * </ul> 1981 * <li><b>Methods:</b> 1982 * <ul> 1983 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#path(String)} 1984 * </ul> 1985 * </ul> 1986 * 1987 * <h5 class='section'>Description:</h5> 1988 * <p> 1989 * Identifies the URL subpath relative to the ascendant resource. 1990 * 1991 * <p> 1992 * This setting is critical for the routing of HTTP requests from ascendant to child resources. 1993 * 1994 * <h5 class='section'>Example:</h5> 1995 * <p class='bcode w800'> 1996 * <jc>// Option #1 - Defined via annotation.</jc> 1997 * <ja>@Rest</ja>(path=<js>"/myResource"</js>) 1998 * <jk>public class</jk> MyResource { 1999 * 2000 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 2001 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 2002 * 2003 * <jc>// Using method on builder.</jc> 2004 * builder.path(<js>"/myResource"</js>); 2005 * 2006 * <jc>// Same, but using property.</jc> 2007 * builder.set(<jsf>REST_path</jsf>, <js>"/myResource"</js>); 2008 * } 2009 * 2010 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 2011 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 2012 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 2013 * builder.path(<js>"/myResource"</js>); 2014 * } 2015 * } 2016 * </p> 2017 * 2018 * <p> 2019 * <ul class='notes'> 2020 * <li> 2021 * This annotation is ignored on top-level servlets (i.e. servlets defined in <c>web.xml</c> files). 2022 * <br>Therefore, implementers can optionally specify a path value for documentation purposes. 2023 * <li> 2024 * Typically, this setting is only applicable to resources defined as children through the 2025 * {@link Rest#children() @Rest(children)} annotation. 2026 * <br>However, it may be used in other ways (e.g. defining paths for top-level resources in microservices). 2027 * <li> 2028 * Slashes are trimmed from the path ends. 2029 * <br>As a convention, you may want to start your path with <js>'/'</js> simple because it make it easier to read. 2030 * <li> 2031 * This path is available through the following method: 2032 * <ul> 2033 * <li class='jm'>{@link RestContext#getPath() RestContext.getPath()} 2034 * </ul> 2035 * </ul> 2036 */ 2037 public static final String REST_path = PREFIX + ".path.s"; 2038 2039 /** 2040 * Configuration property: Render response stack traces in responses. 2041 * 2042 * <h5 class='section'>Property:</h5> 2043 * <ul class='spaced-list'> 2044 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_renderResponseStackTraces REST_renderResponseStackTraces} 2045 * <li><b>Name:</b> <js>"RestContext.renderResponseStackTraces.b"</js> 2046 * <li><b>Data type:</b> <jk>boolean</jk> 2047 * <li><b>System property:</b> <c>RestContext.renderResponseStackTraces</c> 2048 * <li><b>Environment variable:</b> <c>RESTCONTEXT_RENDERRESPONSESTACKTRACES</c> 2049 * <li><b>Default:</b> <jk>false</jk> 2050 * <li><b>Session property:</b> <jk>false</jk> 2051 * <li><b>Annotations:</b> 2052 * <ul> 2053 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#renderResponseStackTraces()} 2054 * </ul> 2055 * <li><b>Methods:</b> 2056 * <ul> 2057 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#renderResponseStackTraces(boolean)} 2058 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#renderResponseStackTraces()} 2059 * </ul> 2060 * </ul> 2061 * 2062 * <h5 class='section'>Description:</h5> 2063 * <p> 2064 * Render stack traces in HTTP response bodies when errors occur. 2065 * 2066 * <h5 class='section'>Example:</h5> 2067 * <p class='bcode w800'> 2068 * <jc>// Option #1 - Defined via annotation.</jc> 2069 * <ja>@Rest</ja>(renderResponseStackTraces=<jk>true</jk>) 2070 * <jk>public class</jk> MyResource { 2071 * 2072 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 2073 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 2074 * 2075 * <jc>// Using method on builder.</jc> 2076 * builder.renderResponseStackTraces(); 2077 * 2078 * <jc>// Same, but using property.</jc> 2079 * builder.set(<jsf>REST_renderResponseStackTraces</jsf>, <jk>true</jk>); 2080 * } 2081 * 2082 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 2083 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 2084 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 2085 * builder.renderResponseStackTraces(); 2086 * } 2087 * } 2088 * </p> 2089 * 2090 * <ul class='notes'> 2091 * <li> 2092 * Useful for debugging, although allowing stack traces to be rendered may cause security concerns so use 2093 * caution when enabling. 2094 * <li> 2095 * This setting is available through the following method: 2096 * <ul> 2097 * <li class='jm'>{@link RestContext#isRenderResponseStackTraces() RestContext.isRenderResponseStackTraces()} 2098 * </ul> 2099 * That method is used by {@link #handleError(RestCall, Throwable)}. 2100 * </ul> 2101 */ 2102 public static final String REST_renderResponseStackTraces = PREFIX + ".renderResponseStackTraces.b"; 2103 2104 /** 2105 * Configuration property: Default request attributes. 2106 * 2107 * <h5 class='section'>Property:</h5> 2108 * <ul class='spaced-list'> 2109 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_reqAttrs REST_reqAttrs} 2110 * <li><b>Name:</b> <js>"RestContext.reqAttrs.smo"</js> 2111 * <li><b>Data type:</b> <c>Map<String,Object></c> 2112 * <li><b>System property:</b> <c>RestContext.reqAttrs</c> 2113 * <li><b>Environment variable:</b> <c>RESTCONTEXT_REQATTRS</c> 2114 * <li><b>Default:</b> empty map 2115 * <li><b>Session property:</b> <jk>false</jk> 2116 * <li><b>Annotations:</b> 2117 * <ul> 2118 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#reqAttrs()} 2119 * <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#reqAttrs()} 2120 * </ul> 2121 * <li><b>Methods:</b> 2122 * <ul> 2123 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#reqAttrs(String...)} 2124 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#reqAttr(String,Object)} 2125 * </ul> 2126 * </ul> 2127 * 2128 * <h5 class='section'>Description:</h5> 2129 * <p> 2130 * Specifies default values for request attributes if they're not already set on the request. 2131 * 2132 * <ul class='notes'> 2133 * <li> 2134 * Strings are in the format <js>"Name: value"</js>. 2135 * <li> 2136 * Affects values returned by the following methods: 2137 * <ul> 2138 * <li class='jm'>{@link RestRequest#getAttribute(String)}. 2139 * <li class='jm'>{@link RestRequest#getAttributes()}. 2140 * </ul> 2141 * </ul> 2142 * 2143 * <h5 class='section'>Example:</h5> 2144 * <p class='bcode w800'> 2145 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 2146 * <ja>@Rest</ja>(reqAttrs={<js>"Foo: bar"</js>, <js>"Baz: $C{REST/myAttributeValue}"</js>}) 2147 * <jk>public class</jk> MyResource { 2148 * 2149 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 2150 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 2151 * 2152 * <jc>// Using method on builder.</jc> 2153 * builder 2154 * .attr(<js>"Foo"</js>, <js>"bar"</js>); 2155 * .attr(<js>"Baz: true"</js>); 2156 * 2157 * <jc>// Same, but using property.</jc> 2158 * builder.addTo(<jsf>REST_reqAttrs</jsf>, <js>"Foo"</js>, <js>"bar"</js>); 2159 * } 2160 * 2161 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 2162 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 2163 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 2164 * builder.reqAttr(<js>"Foo"</js>, <js>"bar"</js>); 2165 * } 2166 * 2167 * <jc>// Override at the method level.</jc> 2168 * <ja>@RestMethod</ja>(reqAttrs={<js>"Foo: bar"</js>}) 2169 * public Object myMethod() {...} 2170 * } 2171 * </p> 2172 */ 2173 public static final String REST_reqAttrs = PREFIX + ".reqAttrs.smo"; 2174 2175 /** 2176 * Configuration property: Default request headers. 2177 * 2178 * <h5 class='section'>Property:</h5> 2179 * <ul class='spaced-list'> 2180 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_reqHeaders REST_reqHeaders} 2181 * <li><b>Name:</b> <js>"RestContext.reqHeaders.smo"</js> 2182 * <li><b>Data type:</b> <c>Map<String,String></c> 2183 * <li><b>System property:</b> <c>RestContext.reqHeaders</c> 2184 * <li><b>Environment variable:</b> <c>RESTCONTEXT_REQHEADERS</c> 2185 * <li><b>Default:</b> empty map 2186 * <li><b>Session property:</b> <jk>false</jk> 2187 * <li><b>Annotations:</b> 2188 * <ul> 2189 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#reqHeaders()} 2190 * <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#reqHeaders()} 2191 * </ul> 2192 * <li><b>Methods:</b> 2193 * <ul> 2194 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#reqHeader(String,Object)} 2195 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#reqHeaders(String...)} 2196 * </ul> 2197 * </ul> 2198 * 2199 * <h5 class='section'>Description:</h5> 2200 * <p> 2201 * Specifies default values for request headers if they're not passed in through the request. 2202 * 2203 * <ul class='notes'> 2204 * <li> 2205 * Strings are in the format <js>"Header-Name: header-value"</js>. 2206 * <li> 2207 * Affects values returned by {@link RestRequest#getHeader(String)} when the header is not present on the request. 2208 * <li> 2209 * The most useful reason for this annotation is to provide a default <c>Accept</c> header when one is not 2210 * specified so that a particular default {@link Serializer} is picked. 2211 * </ul> 2212 * 2213 * <h5 class='section'>Example:</h5> 2214 * <p class='bcode w800'> 2215 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 2216 * <ja>@Rest</ja>(reqHeaders={<js>"Accept: application/json"</js>, <js>"My-Header: $C{REST/myHeaderValue}"</js>}) 2217 * <jk>public class</jk> MyResource { 2218 * 2219 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 2220 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 2221 * 2222 * <jc>// Using method on builder.</jc> 2223 * builder 2224 * .reqHeader(<js>"Accept"</js>, <js>"application/json"</js>); 2225 * .reqHeaders(<js>"My-Header: foo"</js>); 2226 * 2227 * <jc>// Same, but using property.</jc> 2228 * builder.addTo(<jsf>REST_reqHeaders</jsf>, <js>"Accept"</js>, <js>"application/json"</js>); 2229 * } 2230 * 2231 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 2232 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 2233 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 2234 * builder.reqHeader(<js>"Accept"</js>, <js>"application/json"</js>); 2235 * } 2236 * 2237 * <jc>// Override at the method level.</jc> 2238 * <ja>@RestMethod</ja>(reqHeaders={<js>"Accept: text/xml"</js>}) 2239 * public Object myMethod() {...} 2240 * } 2241 * </p> 2242 */ 2243 public static final String REST_reqHeaders = PREFIX + ".reqHeaders.smo"; 2244 2245 /** 2246 * Configuration property: Default response headers. 2247 * 2248 * <h5 class='section'>Property:</h5> 2249 * <ul class='spaced-list'> 2250 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_resHeaders REST_resHeaders} 2251 * <li><b>Name:</b> <js>"RestContext.resHeaders.omo"</js> 2252 * <li><b>Data type:</b> <c>Map<String,String></c> 2253 * <li><b>System property:</b> <c>RestContext.resHeaders</c> 2254 * <li><b>Environment variable:</b> <c>RESTCONTEXT_RESHEADERS</c> 2255 * <li><b>Default:</b> empty map 2256 * <li><b>Session property:</b> <jk>false</jk> 2257 * <li><b>Annotations:</b> 2258 * <ul> 2259 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#resHeaders()} 2260 * </ul> 2261 * <li><b>Methods:</b> 2262 * <ul> 2263 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#resHeader(String,Object)} 2264 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#resHeaders(String...)} 2265 * </ul> 2266 * </ul> 2267 * 2268 * <h5 class='section'>Description:</h5> 2269 * <p> 2270 * Specifies default values for response headers if they're not set after the Java REST method is called. 2271 * 2272 * <ul class='notes'> 2273 * <li> 2274 * Strings are in the format <js>"Header-Name: header-value"</js>. 2275 * <li> 2276 * This is equivalent to calling {@link RestResponse#setHeader(String, String)} programmatically in each of 2277 * the Java methods. 2278 * <li> 2279 * The header value will not be set if the header value has already been specified (hence the 'default' in the name). 2280 * </ul> 2281 * 2282 * <h5 class='section'>Example:</h5> 2283 * <p class='bcode w800'> 2284 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 2285 * <ja>@Rest</ja>(resHeaders={<js>"Content-Type: $C{REST/defaultContentType,text/plain}"</js>,<js>"My-Header: $C{REST/myHeaderValue}"</js>}) 2286 * <jk>public class</jk> MyResource { 2287 * 2288 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 2289 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 2290 * 2291 * <jc>// Using method on builder.</jc> 2292 * builder 2293 * .resHeader(<js>"Content-Type"</js>, <js>"text/plain"</js>); 2294 * .resHeaders(<js>"My-Header: foo"</js>); 2295 * 2296 * <jc>// Same, but using property.</jc> 2297 * builder 2298 * .addTo(<jsf>REST_resHeaders</jsf>, <js>"Accept"</js>, <js>"application/json"</js>); 2299 * .addTo(<jsf>REST_resHeaders</jsf>, <js>"My-Header"</js>, <js>"foo"</js>); 2300 * } 2301 * 2302 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 2303 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 2304 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 2305 * builder.resHeader(<js>"Content-Type"</js>, <js>"text/plain"</js>); 2306 * } 2307 * } 2308 * </p> 2309 */ 2310 public static final String REST_resHeaders = PREFIX + ".resHeaders.omo"; 2311 2312 /** 2313 * Configuration property: REST resource resolver. 2314 * 2315 * <h5 class='section'>Property:</h5> 2316 * <ul class='spaced-list'> 2317 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_resourceResolver REST_resourceResolver} 2318 * <li><b>Name:</b> <js>"RestContext.resourceResolver.o"</js> 2319 * <li><b>Data type:</b> 2320 * <ul> 2321 * <li>{@link org.apache.juneau.rest.RestResourceResolver} 2322 * <li><c>Class<{@link org.apache.juneau.rest.RestResourceResolver}></c> 2323 * </ul> 2324 * <li><b>Default:</b> {@link org.apache.juneau.rest.BasicRestResourceResolver} 2325 * <li><b>Session property:</b> <jk>false</jk> 2326 * <li><b>Annotations:</b> 2327 * <ul> 2328 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#resourceResolver()} 2329 * </ul> 2330 * <li><b>Methods:</b> 2331 * <ul> 2332 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#resourceResolver(Class)} 2333 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#resourceResolver(RestResourceResolver)} 2334 * </ul> 2335 * </ul> 2336 * 2337 * <h5 class='section'>Description:</h5> 2338 * <p> 2339 * The resolver used for resolving instances of child resources. 2340 * 2341 * <p> 2342 * Can be used to provide customized resolution of REST resource class instances (e.g. resources retrieve from Spring). 2343 * 2344 * <h5 class='section'>Example:</h5> 2345 * <p class='bcode w800'> 2346 * <jc>// Our custom resource resolver. </jc> 2347 * <jk>public class</jk> MyResourceResolver <jk>extends</jk> RestResourceResolverSimple { 2348 * 2349 * <ja>@Override</ja> 2350 * <jk>public</jk> Object resolve(Class<?> resourceType, RestContextBuilder builder) <jk>throws</jk> Exception { 2351 * Object resource = <jsm>findOurResourceSomehow</jsm>(resourceType); 2352 * 2353 * <jc>// If we can't resolve it, use default resolution.</jc> 2354 * <jk>if</jk> (resource == <jk>null</jk>) 2355 * resource = <jk>super</jk>.resolve(resourceType, builder); 2356 * 2357 * <jk>return</jk> resource; 2358 * } 2359 * } 2360 * 2361 * <jc>// Option #1 - Defined via annotation.</jc> 2362 * <ja>@Rest</ja>(resourceResolver=MyResourceResolver.<jk>class</jk>) 2363 * <jk>public class</jk> MyResource { 2364 * 2365 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 2366 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 2367 * 2368 * <jc>// Using method on builder.</jc> 2369 * builder.resourceResolver(MyResourceResolver.<jk>class</jk>); 2370 * 2371 * <jc>// Same, but using property.</jc> 2372 * builder.set(<jsf>REST_resourceResolver</jsf>, MyResourceResolver.<jk>class</jk>); 2373 * } 2374 * 2375 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 2376 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 2377 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 2378 * builder.resourceResolver(MyResourceResolver.<jk>class</jk>); 2379 * } 2380 * } 2381 * </p> 2382 * 2383 * <ul class='notes'> 2384 * <li> 2385 * Unless overridden, resource resolvers are inherited from ascendant resources. 2386 * <li> 2387 * The resource class itself will be used if it implements the {@link RestResourceResolver} interface and not 2388 * explicitly overridden via this annotation. 2389 * <li> 2390 * The {@link RestServlet} and {@link BasicRest} classes implement the {@link RestResourceResolver} interface with the same 2391 * functionality as {@link BasicRestResourceResolver} that gets used if not overridden by this annotation. 2392 * <br>Subclasses can also alter the behavior by overriding these methods. 2393 * <li> 2394 * When defined as a class, the implementation must have one of the following constructors: 2395 * <ul> 2396 * <li><code><jk>public</jk> T(RestContext)</code> 2397 * <li><code><jk>public</jk> T()</code> 2398 * <li><code><jk>public static</jk> T <jsm>create</jsm>(RestContext)</code> 2399 * <li><code><jk>public static</jk> T <jsm>create</jsm>()</code> 2400 * </ul> 2401 * <li> 2402 * Inner classes of the REST resource class are allowed. 2403 * </ul> 2404 * 2405 * <ul class='seealso'> 2406 * <li class='link'>{@doc RestResourceResolvers} 2407 * <li class='link'>{@doc RestInjection} 2408 * </ul> 2409 */ 2410 public static final String REST_resourceResolver = PREFIX + ".resourceResolver.o"; 2411 2412 /** 2413 * Configuration property: Response handlers. 2414 * 2415 * <h5 class='section'>Property:</h5> 2416 * <ul class='spaced-list'> 2417 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_responseHandlers REST_responseHandlers} 2418 * <li><b>Name:</b> <js>"RestContext.responseHandlers.lo"</js> 2419 * <li><b>Data type:</b> <c>List<{@link org.apache.juneau.rest.ResponseHandler}|Class<{@link org.apache.juneau.rest.ResponseHandler}>></c> 2420 * <li><b>Default:</b> empty list 2421 * <li><b>Session property:</b> <jk>false</jk> 2422 * <li><b>Annotations:</b> 2423 * <ul> 2424 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#responseHandlers()} 2425 * </ul> 2426 * <li><b>Methods:</b> 2427 * <ul> 2428 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#responseHandlers(Class...)} 2429 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#responseHandlers(ResponseHandler...)} 2430 * </ul> 2431 * </ul> 2432 * 2433 * <h5 class='section'>Description:</h5> 2434 * <p> 2435 * Specifies a list of {@link ResponseHandler} classes that know how to convert POJOs returned by REST methods or 2436 * set via {@link RestResponse#setOutput(Object)} into appropriate HTTP responses. 2437 * 2438 * <p> 2439 * By default, the following response handlers are provided out-of-the-box: 2440 * <ul> 2441 * <li class='jc'>{@link ReaderHandler} - {@link Reader} objects. 2442 * <li class='jc'>{@link InputStreamHandler} - {@link InputStream} objects. 2443 * <li class='jc'>{@link DefaultHandler} - All other POJOs. 2444 * </ul> 2445 * 2446 * <h5 class='section'>Example:</h5> 2447 * <p class='bcode w800'> 2448 * <jc>// Our custom response handler for MySpecialObject objects. </jc> 2449 * <jk>public class</jk> MyResponseHandler <jk>implements</jk> ResponseHandler { 2450 * 2451 * <ja>@Override</ja> 2452 * <jk>public boolean</jk> handle(RestRequest req, RestResponse res, Object output) <jk>throws</jk> IOException, RestException { 2453 * <jk>if</jk> (output <jk>instanceof</jk> MySpecialObject) { 2454 * <jk>try</jk> (Writer w = res.getNegotiatedWriter()) { 2455 * <jc>//Pipe it to the writer ourselves.</jc> 2456 * } 2457 * <jk>return true</jk>; <jc>// We handled it.</jc> 2458 * } 2459 * <jk>return false</jk>; <jc>// We didn't handle it.</jc> 2460 * } 2461 * } 2462 * 2463 * <jc>// Option #1 - Defined via annotation.</jc> 2464 * <ja>@Rest</ja>(responseHandlers=MyResponseHandler.<jk>class</jk>) 2465 * <jk>public class</jk> MyResource { 2466 * 2467 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 2468 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 2469 * 2470 * <jc>// Using method on builder.</jc> 2471 * builder.responseHandlers(MyResponseHandler.<jk>class</jk>); 2472 * 2473 * <jc>// Same, but using property.</jc> 2474 * builder.addTo(<jsf>REST_responseHandlers</jsf>, MyResponseHandler.<jk>class</jk>); 2475 * } 2476 * 2477 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 2478 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 2479 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 2480 * builder.responseHandlers(MyResponseHandler.<jk>class</jk>); 2481 * } 2482 * 2483 * <ja>@RestMethod</ja>(...) 2484 * <jk>public</jk> Object myMethod() { 2485 * <jc>// Return a special object for our handler.</jc> 2486 * <jk>return new</jk> MySpecialObject(); 2487 * } 2488 * } 2489 * </p> 2490 * 2491 * <ul class='notes'> 2492 * <li> 2493 * Response handlers resolvers are always inherited from ascendant resources. 2494 * <li> 2495 * When defined as a class, the implementation must have one of the following constructors: 2496 * <ul> 2497 * <li><code><jk>public</jk> T(RestContext)</code> 2498 * <li><code><jk>public</jk> T()</code> 2499 * <li><code><jk>public static</jk> T <jsm>create</jsm>(RestContext)</code> 2500 * <li><code><jk>public static</jk> T <jsm>create</jsm>()</code> 2501 * </ul> 2502 * <li> 2503 * Inner classes of the REST resource class are allowed. 2504 * </ul> 2505 */ 2506 public static final String REST_responseHandlers = PREFIX + ".responseHandlers.lo"; 2507 2508 /** 2509 * Configuration property: Declared roles. 2510 * 2511 * <h5 class='section'>Property:</h5> 2512 * <ul class='spaced-list'> 2513 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_rolesDeclared REST_rolesDeclared} 2514 * <li><b>Name:</b> <js>"RestContext.rolesDeclared.ss"</js> 2515 * <li><b>Data type:</b> <c>Set<String></c> 2516 * <li><b>System property:</b> <c>RestContext.rolesDeclared</c> 2517 * <li><b>Environment variable:</b> <c>RESTCONTEXT_ROLESDECLARED</c> 2518 * <li><b>Default:</b> empty list 2519 * <li><b>Session property:</b> <jk>false</jk> 2520 * <li><b>Annotations:</b> 2521 * <ul> 2522 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#rolesDeclared()} 2523 * </ul> 2524 * <li><b>Methods:</b> 2525 * <ul> 2526 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#rolesDeclared(String...)} 2527 * </ul> 2528 * </ul> 2529 * 2530 * 2531 * <h5 class='section'>Description:</h5> 2532 * <p> 2533 * A comma-delimited list of all possible user roles. 2534 * 2535 * <p> 2536 * Used in conjunction with {@link RestContextBuilder#roleGuard(String)} is used with patterns. 2537 * 2538 * <h5 class='section'>Example:</h5> 2539 * <p class='bcode w800'> 2540 * <ja>@Rest</ja>( 2541 * rolesDeclared=<js>"ROLE_ADMIN,ROLE_READ_WRITE,ROLE_READ_ONLY,ROLE_SPECIAL"</js>, 2542 * roleGuard=<js>"ROLE_ADMIN || (ROLE_READ_WRITE && ROLE_SPECIAL)"</js> 2543 * ) 2544 * <jk>public class</jk> MyResource <jk>extends</jk> RestServlet { 2545 * ... 2546 * } 2547 * </p> 2548 * 2549 * <ul class='seealso'> 2550 * <li class='jf'>{@link RestContext#REST_rolesDeclared} 2551 * </ul> 2552 */ 2553 public static final String REST_rolesDeclared = PREFIX + ".rolesDeclared.ss"; 2554 2555 /** 2556 * Configuration property: Role guard. 2557 * 2558 * <h5 class='section'>Property:</h5> 2559 * <ul class='spaced-list'> 2560 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_roleGuard REST_roleGuard} 2561 * <li><b>Name:</b> <js>"RestContext.roleGuard.ss"</js> 2562 * <li><b>Data type:</b> <c>Set<String></c> 2563 * <li><b>System property:</b> <c>RestContext.roleGuard</c> 2564 * <li><b>Environment variable:</b> <c>RESTCONTEXT_ROLEGUARD</c> 2565 * <li><b>Default:</b> empty set 2566 * <li><b>Session property:</b> <jk>false</jk> 2567 * <li><b>Annotations:</b> 2568 * <ul> 2569 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#roleGuard()} 2570 * <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#roleGuard()} 2571 * </ul> 2572 * <li><b>Methods:</b> 2573 * <ul> 2574 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#roleGuard(String)} 2575 * </ul> 2576 * </ul> 2577 * 2578 * <h5 class='section'>Description:</h5> 2579 * <p> 2580 * An expression defining if a user with the specified roles are allowed to access methods on this class. 2581 * 2582 * <h5 class='section'>Example:</h5> 2583 * <p class='bcode w800'> 2584 * <ja>@Rest</ja>( 2585 * path=<js>"/foo"</js>, 2586 * roleGuard=<js>"ROLE_ADMIN || (ROLE_READ_WRITE && ROLE_SPECIAL)"</js> 2587 * ) 2588 * <jk>public class</jk> MyResource <jk>extends</jk> RestServlet { 2589 * ... 2590 * } 2591 * </p> 2592 * 2593 * <ul class='notes'> 2594 * <li> 2595 * Supports any of the following expression constructs: 2596 * <ul> 2597 * <li><js>"foo"</js> - Single arguments. 2598 * <li><js>"foo,bar,baz"</js> - Multiple OR'ed arguments. 2599 * <li><js>"foo | bar | bqz"</js> - Multiple OR'ed arguments, pipe syntax. 2600 * <li><js>"foo || bar || bqz"</js> - Multiple OR'ed arguments, Java-OR syntax. 2601 * <li><js>"fo*"</js> - Patterns including <js>'*'</js> and <js>'?'</js>. 2602 * <li><js>"fo* & *oo"</js> - Multiple AND'ed arguments, ampersand syntax. 2603 * <li><js>"fo* && *oo"</js> - Multiple AND'ed arguments, Java-AND syntax. 2604 * <li><js>"fo* || (*oo || bar)"</js> - Parenthesis. 2605 * </ul> 2606 * <li> 2607 * AND operations take precedence over OR operations (as expected). 2608 * <li> 2609 * Whitespace is ignored. 2610 * <li> 2611 * <jk>null</jk> or empty expressions always match as <jk>false</jk>. 2612 * <li> 2613 * If patterns are used, you must specify the list of declared roles using {@link Rest#rolesDeclared()} or {@link RestContext#REST_rolesDeclared}. 2614 * <li> 2615 * Supports {@doc RestSvlVariables} 2616 * (e.g. <js>"$L{my.localized.variable}"</js>). 2617 * <li> 2618 * Role guards defined at both the class and method level must both pass. 2619 * </ul> 2620 * 2621 * <ul class='seealso'> 2622 * <li class='jf'>{@link RestContext#REST_roleGuard} 2623 * </ul> 2624 */ 2625 public static final String REST_roleGuard = PREFIX + ".roleGuard.ss"; 2626 2627 /** 2628 * Configuration property: Serializers. 2629 * 2630 * <h5 class='section'>Property:</h5> 2631 * <ul class='spaced-list'> 2632 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_serializers REST_serializers} 2633 * <li><b>Name:</b> <js>"RestContext.serializers.lo"</js> 2634 * <li><b>Data type:</b> <c>List<{@link org.apache.juneau.serializer.Serializer}|Class<{@link org.apache.juneau.serializer.Serializer}>></c> 2635 * <li><b>Default:</b> empty list 2636 * <li><b>Session property:</b> <jk>false</jk> 2637 * <li><b>Annotations:</b> 2638 * <ul> 2639 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#serializers()} 2640 * <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#serializers()} 2641 * </ul> 2642 * <li><b>Methods:</b> 2643 * <ul> 2644 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#serializers(Object...)} 2645 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#serializers(Class...)} 2646 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#serializersReplace(Object...)} 2647 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#serializersReplace(Class...)} 2648 * </ul> 2649 * </ul> 2650 * 2651 * <h5 class='section'>Description:</h5> 2652 * <p> 2653 * Adds class-level serializers to this resource. 2654 * 2655 * <p> 2656 * Serializer are used to convert POJOs to HTTP response bodies. 2657 * <br>Any of the Juneau framework serializers can be used in this setting. 2658 * <br>The serializer selected is based on the request <c>Accept</c> header matched against the values returned by the following method 2659 * using a best-match algorithm: 2660 * <ul class='javatree'> 2661 * <li class='jm'>{@link Serializer#getMediaTypeRanges()} 2662 * </ul> 2663 * 2664 * <h5 class='section'>Example:</h5> 2665 * <p class='bcode w800'> 2666 * <jc>// Option #1 - Defined via annotation.</jc> 2667 * <ja>@Rest</ja>(serializers={JsonSerializer.<jk>class</jk>, XmlSerializer.<jk>class</jk>}) 2668 * <jk>public class</jk> MyResource { 2669 * 2670 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 2671 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 2672 * 2673 * <jc>// Using method on builder.</jc> 2674 * builder.serializers(JsonSerializer.<jk>class</jk>, XmlSerializer.<jk>class</jk>); 2675 * 2676 * <jc>// Same, but use pre-instantiated parsers.</jc> 2677 * builder.serializers(JsonSerializer.<jsf>DEFAULT</jsf>, XmlSerializer.<jsf>DEFAULT</jsf>); 2678 * 2679 * <jc>// Same, but using property.</jc> 2680 * builder.set(<jsf>REST_serializers</jsf>, JsonSerializer.<jk>class</jk>, XmlSerializer.<jk>class</jk>); 2681 * } 2682 * 2683 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 2684 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 2685 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 2686 * builder.serializers(JsonSerializer.<jk>class</jk>, XmlSerializer.<jk>class</jk>); 2687 * } 2688 * 2689 * <jc>// Override at the method level.</jc> 2690 * <ja>@RestMethod</ja>(serializers={HtmlSerializer.<jk>class</jk>}) 2691 * <jk>public</jk> MyPojo myMethod() { 2692 * <jc>// Return a POJO to be serialized.</jc> 2693 * <jk>return new</jk> MyPojo(); 2694 * } 2695 * } 2696 * </p> 2697 * 2698 * <ul class='notes'> 2699 * <li> 2700 * When defined as a class, properties/transforms defined on the resource/method are inherited. 2701 * <li> 2702 * When defined as an instance, properties/transforms defined on the resource/method are NOT inherited. 2703 * <li> 2704 * Typically, you'll want your resource to extend directly from {@link BasicRestServlet} which comes 2705 * preconfigured with the following serializers: 2706 * <ul> 2707 * <li class='jc'>{@link HtmlDocSerializer} 2708 * <li class='jc'>{@link HtmlStrippedDocSerializer} 2709 * <li class='jc'>{@link HtmlSchemaDocSerializer} 2710 * <li class='jc'>{@link JsonSerializer} 2711 * <li class='jc'>{@link SimpleJsonSerializer} 2712 * <li class='jc'>{@link JsonSchemaSerializer} 2713 * <li class='jc'>{@link XmlDocSerializer} 2714 * <li class='jc'>{@link UonSerializer} 2715 * <li class='jc'>{@link UrlEncodingSerializer} 2716 * <li class='jc'>{@link MsgPackSerializer} 2717 * <li class='jc'>{@link SoapXmlSerializer} 2718 * <li class='jc'>{@link PlainTextSerializer} 2719 * </ul> 2720 * </ul> 2721 * 2722 * <ul class='seealso'> 2723 * <li class='link'>{@doc RestSerializers} 2724 * </ul> 2725 * <p> 2726 */ 2727 public static final String REST_serializers = PREFIX + ".serializers.lo"; 2728 2729 /** 2730 * Configuration property: Static file response headers. 2731 * 2732 * <h5 class='section'>Property:</h5> 2733 * <ul class='spaced-list'> 2734 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_staticFileResponseHeaders REST_staticFileResponseHeaders} 2735 * <li><b>Name:</b> <js>"RestContext.staticFileResponseHeaders.omo"</js> 2736 * <li><b>Data type:</b> <c>Map<String,String></c> 2737 * <li><b>System property:</b> <c>RestContext.staticFileResponseHeaders</c> 2738 * <li><b>Environment variable:</b> <c>RESTCONTEXT_STATICFILERESPONSEHEADERS</c> 2739 * <li><b>Default:</b> <code>{<js>'Cache-Control'</js>: <js>'max-age=86400, public</js>}</code> 2740 * <li><b>Session property:</b> <jk>false</jk> 2741 * <li><b>Annotations:</b> 2742 * <ul> 2743 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#staticFileResponseHeaders()} 2744 * </ul> 2745 * <li><b>Methods:</b> 2746 * <ul> 2747 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#staticFileResponseHeaders(Map)} 2748 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#staticFileResponseHeaders(String...)} 2749 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#staticFileResponseHeader(String,String)} 2750 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#staticFileResponseHeadersReplace(Map)} 2751 * </ul> 2752 * </ul> 2753 * 2754 * <h5 class='section'>Description:</h5> 2755 * <p> 2756 * Used to customize the headers on responses returned for statically-served files. 2757 * 2758 * <h5 class='section'>Example:</h5> 2759 * <p class='bcode w800'> 2760 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 2761 * <ja>@Rest</ja>( 2762 * staticFileResponseHeaders={ 2763 * <js>"Cache-Control: $C{REST/cacheControl,nocache}"</js>, 2764 * <js>"My-Header: $C{REST/myHeaderValue}"</js> 2765 * } 2766 * ) 2767 * <jk>public class</jk> MyResource { 2768 * 2769 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 2770 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 2771 * 2772 * <jc>// Using method on builder.</jc> 2773 * builder 2774 * .staticFileResponseHeader(<js>"Cache-Control"</js>, <js>"nocache"</js>); 2775 * .staticFileResponseHeaders(<js>"My-Header: foo"</js>); 2776 * 2777 * <jc>// Same, but using property.</jc> 2778 * builder 2779 * .addTo(<jsf>REST_staticFileResponseHeaders</jsf>, <js>"Cache-Control"</js>, <js>"nocache"</js>); 2780 * .addTo(<jsf>REST_staticFileResponseHeaders</jsf>, <js>"My-Header"</js>, <js>"foo"</js>); 2781 * } 2782 * 2783 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 2784 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 2785 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 2786 * builder.staticFileResponseHeader(<js>"Cache-Control"</js>, <js>"nocache"</js>); 2787 * } 2788 * } 2789 * </p> 2790 * 2791 * <p> 2792 * Note that headers can also be specified per path-mapping via the {@link Rest#staticFiles() @Rest(staticFiles)} annotation. 2793 * <p class='bcode w800'> 2794 * <ja>@Rest</ja>( 2795 * staticFiles={ 2796 * <js>"htdocs:docs:{'Cache-Control':'max-age=86400, public'}"</js> 2797 * } 2798 * ) 2799 * </p> 2800 * 2801 * <ul class='seealso'> 2802 * <li class='jf'>{@link #REST_staticFiles} for information about statically-served files. 2803 * </ul> 2804 */ 2805 public static final String REST_staticFileResponseHeaders = PREFIX + ".staticFileResponseHeaders.omo"; 2806 2807 /** 2808 * Configuration property: Static file mappings. 2809 * 2810 * <h5 class='section'>Property:</h5> 2811 * <ul class='spaced-list'> 2812 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_staticFiles REST_staticFiles} 2813 * <li><b>Name:</b> <js>"RestContext.staticFiles.lo"</js> 2814 * <li><b>Data type:</b> <c>List<{@link org.apache.juneau.rest.StaticFileMapping}></c> 2815 * <li><b>Default:</b> <jk>null</jk> 2816 * <li><b>Session property:</b> <jk>false</jk> 2817 * <li><b>Annotations:</b> 2818 * <ul> 2819 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#staticFiles()} 2820 * </ul> 2821 * <li><b>Methods:</b> 2822 * <ul> 2823 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#staticFiles(String)}, 2824 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#staticFiles(Class,String)} 2825 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#staticFiles(String,String)} 2826 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#staticFiles(Class,String,String)} 2827 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#staticFiles(StaticFileMapping...)} 2828 * </ul> 2829 * </ul> 2830 * 2831 * <h5 class='section'>Description:</h5> 2832 * <p> 2833 * Used to define paths and locations of statically-served files such as images or HTML documents 2834 * from the classpath or file system. 2835 * 2836 * <p> 2837 * The format of the value is one of the following: 2838 * <ol class='spaced-list'> 2839 * <li><js>"path:location"</js> 2840 * <li><js>"path:location:headers"</js> 2841 * </ol> 2842 * 2843 * <p> 2844 * An example where this class is used is in the {@link Rest#staticFiles} annotation: 2845 * <p class='bcode w800'> 2846 * <jk>package</jk> com.foo.mypackage; 2847 * 2848 * <ja>@Rest</ja>( 2849 * path=<js>"/myresource"</js>, 2850 * staticFiles={ 2851 * <js>"htdocs:docs"</js>, 2852 * <js>"styles:styles"</js> 2853 * } 2854 * ) 2855 * <jk>public class</jk> MyResource <jk>extends</jk> BasicRestServlet {...} 2856 * </p> 2857 * 2858 * <p> 2859 * In the example above, given a GET request to the following URL... 2860 * <p class='bcode w800'> 2861 * /myresource/htdocs/foobar.html 2862 * </p> 2863 * <br>...the servlet will attempt to find the <c>foobar.html</c> file in the following location: 2864 * <ol class='spaced-list'> 2865 * <li><c>com.foo.mypackage.docs</c> package. 2866 * </ol> 2867 * 2868 * <p> 2869 * The location is interpreted as an absolute path if it starts with <js>'/'</js>. 2870 * <p class='bcode w800'> 2871 * <ja>@Rest</ja>( 2872 * staticFiles={ 2873 * <js>"htdocs:/docs"</js> 2874 * } 2875 * ) 2876 * </p> 2877 * <p> 2878 * In the example above, given a GET request to the following URL... 2879 * <p class='bcode w800'> 2880 * /myresource/htdocs/foobar.html 2881 * </p> 2882 * <br>...the servlet will attempt to find the <c>foobar.html</c> file in the following location: 2883 * <ol class='spaced-list'> 2884 * <li><c>docs</c> package (typically under <c>src/main/resources/docs</c> in your workspace). 2885 * <li><c>[working-dir]/docs</c> directory at runtime. 2886 * </ol> 2887 * 2888 * <p> 2889 * Response headers can be specified for served files by adding a 3rd section that consists of a {@doc SimplifiedJson} object. 2890 * <p class='bcode w800'> 2891 * <ja>@Rest</ja>( 2892 * staticFiles={ 2893 * <js>"htdocs:docs:{'Cache-Control':'max-age=86400, public'}"</js> 2894 * } 2895 * ) 2896 * </p> 2897 * 2898 * <p> 2899 * The same path can map to multiple locations. Files are searched in the order 2900 * <p class='bcode w800'> 2901 * <ja>@Rest</ja>( 2902 * staticFiles={ 2903 * <jc>// Search in absolute location '/htdocs/folder' before location 'htdocs.package' relative to servlet package.</jc> 2904 * <js>"htdocs:/htdocs/folder,htdocs:htdocs.package"</js> 2905 * } 2906 * ) 2907 * </p> 2908 * 2909 * <ul class='seealso'> 2910 * <li class='jf'>{@link #REST_classpathResourceFinder} for configuring how classpath resources are located and retrieved. 2911 * <li class='jf'>{@link #REST_mimeTypes} for configuring the media types based on file extension. 2912 * <li class='jf'>{@link #REST_staticFileResponseHeaders} for configuring response headers on statically served files. 2913 * <li class='jf'>{@link #REST_useClasspathResourceCaching} for configuring static file caching. 2914 * <li class='jm'>{@link RestContext#getClasspathResource(String,Locale)} for retrieving static files. 2915 * </ul> 2916 * 2917 * <ul class='notes'> 2918 * <li> 2919 * Mappings are cumulative from super classes. 2920 * <li> 2921 * Child resources can override mappings made on parent class resources. 2922 * <br>When both parent and child resources map against the same path, files will be search in the child location 2923 * and then the parent location. 2924 * </ul> 2925 */ 2926 public static final String REST_staticFiles = PREFIX + ".staticFiles.lo"; 2927 2928 /** 2929 * Configuration property: Supported accept media types. 2930 * 2931 * <h5 class='section'>Property:</h5> 2932 * <ul class='spaced-list'> 2933 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_produces REST_produces} 2934 * <li><b>Name:</b> <js>"RestContext.produces.ls"</js> 2935 * <li><b>Data type:</b> <c>List<String></c> 2936 * <li><b>System property:</b> <c>RestContext.produces</c> 2937 * <li><b>Environment variable:</b> <c>RESTCONTEXT_PRODUCES</c> 2938 * <li><b>Default:</b> empty list 2939 * <li><b>Session property:</b> <jk>false</jk> 2940 * <li><b>Annotations:</b> 2941 * <ul> 2942 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#produces()} 2943 * <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#produces()} 2944 * </ul> 2945 * <li><b>Methods:</b> 2946 * <ul> 2947 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#produces(String...)} 2948 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#produces(MediaType...)} 2949 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#producesReplace(String...)} 2950 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#producesReplace(MediaType...)} 2951 * </ul> 2952 * </ul> 2953 * 2954 * <h5 class='section'>Description:</h5> 2955 * <p> 2956 * Overrides the media types inferred from the serializers that identify what media types can be produced by the resource. 2957 * <br>An example where this might be useful if you have serializers registered that handle media types that you 2958 * don't want exposed in the Swagger documentation. 2959 * 2960 * <h5 class='section'>Example:</h5> 2961 * <p class='bcode w800'> 2962 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 2963 * <ja>@Rest</ja>(produces={<js>"$C{REST/supportedProduces,application/json}"</js>}) 2964 * <jk>public class</jk> MyResource { 2965 * 2966 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 2967 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 2968 * 2969 * <jc>// Using method on builder.</jc> 2970 * builder.produces(<jk>false</jk>, <js>"application/json"</js>) 2971 * 2972 * <jc>// Same, but using property.</jc> 2973 * builder.set(<jsf>REST_produces</jsf>, <js>"application/json"</js>); 2974 * } 2975 * 2976 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 2977 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 2978 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 2979 * builder.produces(<jk>false</jk>, <js>"application/json"</js>); 2980 * } 2981 * } 2982 * </p> 2983 * 2984 * <p> 2985 * This affects the returned values from the following: 2986 * <ul class='javatree'> 2987 * <li class='jm'>{@link RestContext#getProduces() RestContext.getProduces()} 2988 * <li class='jm'>{@link RestRequest#getProduces()} 2989 * <li class='jm'>{@link RestInfoProvider#getSwagger(RestRequest)} - Affects produces field. 2990 * </ul> 2991 */ 2992 public static final String REST_produces = PREFIX + ".produces.ls"; 2993 2994 /** 2995 * Configuration property: Properties. 2996 * 2997 * <h5 class='section'>Property:</h5> 2998 * <ul class='spaced-list'> 2999 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_properties REST_properties} 3000 * <li><b>Name:</b> <js>"RestContext.properties.sms"</js> 3001 * <li><b>Data type:</b> <c>Map<String,String></c> 3002 * <li><b>System property:</b> <c>RestContext.properties</c> 3003 * <li><b>Environment variable:</b> <c>RESTCONTEXT_PROPERTIES</c> 3004 * <li><b>Default:</b> empty map 3005 * <li><b>Session property:</b> <jk>false</jk> 3006 * <li><b>Annotations:</b> 3007 * <ul> 3008 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#properties()} 3009 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#flags()} 3010 * <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#properties()} 3011 * <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#flags()} 3012 * </ul> 3013 * <li><b>Methods:</b> 3014 * <ul> 3015 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#property(String,Object)} 3016 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#properties(Map)} 3017 * </ul> 3018 * </ul> 3019 * 3020 * <h5 class='section'>Description:</h5> 3021 * <p> 3022 * Shortcut to add properties to the bean contexts of all serializers and parsers on all methods in the class. 3023 * 3024 * <p> 3025 * Any of the properties defined on {@link RestContext} or any of the serializers and parsers can be specified. 3026 * 3027 * <p> 3028 * Property values will be converted to the appropriate type. 3029 * 3030 * <ul class='notes'> 3031 * <li> 3032 * Supports {@doc RestSvlVariables} 3033 * (e.g. <js>"$L{my.localized.variable}"</js>). 3034 * </ul> 3035 * 3036 * <ul class='seealso'> 3037 * <li class='jm'>{@link RestContextBuilder#set(String,Object)} 3038 * <li class='jm'>{@link RestContextBuilder#set(java.util.Map)} 3039 * </ul> 3040 */ 3041 public static final String REST_properties = PREFIX + ".properties.sms"; 3042 3043 /** 3044 * Configuration property: Supported content media types. 3045 * 3046 * <h5 class='section'>Property:</h5> 3047 * <ul class='spaced-list'> 3048 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_consumes REST_consumes} 3049 * <li><b>Name:</b> <js>"RestContext.consumes.ls"</js> 3050 * <li><b>Data type:</b> <c>List<String></c> 3051 * <li><b>System property:</b> <c>RestContext.consumes</c> 3052 * <li><b>Environment variable:</b> <c>RESTCONTEXT_CONSUMES</c> 3053 * <li><b>Default:</b> empty list 3054 * <li><b>Session property:</b> <jk>false</jk> 3055 * <li><b>Annotations:</b> 3056 * <ul> 3057 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#consumes()} 3058 * <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#consumes()} 3059 * </ul> 3060 * <li><b>Methods:</b> 3061 * <ul> 3062 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#consumes(String...)} 3063 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#consumes(MediaType...)} 3064 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#consumesReplace(String...)} 3065 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#consumesReplace(MediaType...)} 3066 * </ul> 3067 * </ul> 3068 * 3069 * <h5 class='section'>Description:</h5> 3070 * <p> 3071 * Overrides the media types inferred from the parsers that identify what media types can be consumed by the resource. 3072 * <br>An example where this might be useful if you have parsers registered that handle media types that you 3073 * don't want exposed in the Swagger documentation. 3074 * 3075 * <h5 class='section'>Example:</h5> 3076 * <p class='bcode w800'> 3077 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 3078 * <ja>@Rest</ja>(consumes={<js>"$C{REST/supportedConsumes,application/json}"</js>}) 3079 * <jk>public class</jk> MyResource { 3080 * 3081 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 3082 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 3083 * 3084 * <jc>// Using method on builder.</jc> 3085 * builder.consumes(<jk>false</jk>, <js>"application/json"</js>) 3086 * 3087 * <jc>// Same, but using property.</jc> 3088 * builder.set(<jsf>REST_consumes</jsf>, <js>"application/json"</js>); 3089 * } 3090 * 3091 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 3092 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 3093 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 3094 * builder.consumes(<jk>false</jk>, <js>"application/json"</js>); 3095 * } 3096 * } 3097 * </p> 3098 * 3099 * <p> 3100 * This affects the returned values from the following: 3101 * <ul class='javatree'> 3102 * <li class='jm'>{@link RestContext#getConsumes() RestContext.getConsumes()} 3103 * <li class='jm'>{@link RestRequest#getConsumes()} 3104 * <li class='jm'>{@link RestInfoProvider#getSwagger(RestRequest)} - Affects consumes field. 3105 * </ul> 3106 */ 3107 public static final String REST_consumes = PREFIX + ".consumes.ls"; 3108 3109 /** 3110 * Configuration property: REST context class. 3111 * 3112 * <review>NEEDS REVIEW</review> 3113 * 3114 * <h5 class='section'>Property:</h5> 3115 * <ul class='spaced-list'> 3116 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_context REST_context} 3117 * <li><b>Name:</b> <js>"RestContext.context.c"</js> 3118 * <li><b>Data type:</b> <c>Class<? extends {@link org.apache.juneau.rest.RestContext}></c> 3119 * <li><b>Default:</b> {@link org.apache.juneau.rest.RestContext} 3120 * <li><b>Session property:</b> <jk>false</jk> 3121 * <li><b>Annotations:</b> 3122 * <ul> 3123 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#context()} 3124 * </ul> 3125 * <li><b>Methods:</b> 3126 * <ul> 3127 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#context(Class)} 3128 * </ul> 3129 * </ul> 3130 * 3131 * <h5 class='section'>Description:</h5> 3132 * <p> 3133 * Allows you to extend the {@link RestContext} class to modify how any of the methods are implemented. 3134 * 3135 * <p> 3136 * The subclass must provide the following: 3137 * <ul> 3138 * <li>A public constructor that takes in one parameter that should be passed to the super constructor: {@link RestContextBuilder}. 3139 * </ul> 3140 * 3141 * <h5 class='section'>Example:</h5> 3142 * <p class='bcode w800'> 3143 * <jc>// Our extended context class</jc> 3144 * <jk>public</jk> MyRestContext <jk>extends</jk> RestContext { 3145 * <jk>public</jk> MyRestContext(RestContextBuilder <jv>builder</jv>) { 3146 * <jk>super</jk>(<jv>builder</jv>); 3147 * } 3148 * 3149 * <jc>// Override any methods.</jc> 3150 * } 3151 * </p> 3152 * <p class='bcode w800'> 3153 * <jc>// Option #1 - Defined via annotation.</jc> 3154 * <ja>@Rest</ja>(context=MyRestContext.<jk>class</jk>) 3155 * <jk>public class</jk> MyResource { 3156 * ... 3157 * 3158 * <jc>// Option #2 - Defined via builder passed in through init method.</jc> 3159 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 3160 * <jk>public void</jk> init(RestContextBuilder <jv>builder</jv>) <jk>throws</jk> Exception { 3161 * <jv>builder</jv>.context(MyRestContext.<jk>class</jk>); 3162 * } 3163 * } 3164 * </p> 3165 */ 3166 public static final String REST_context = PREFIX + ".context.c"; 3167 3168 /** 3169 * Configuration property: Use classpath resource caching. 3170 * 3171 * <h5 class='section'>Property:</h5> 3172 * <ul class='spaced-list'> 3173 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_useClasspathResourceCaching REST_useClasspathResourceCaching} 3174 * <li><b>Name:</b> <js>"RestContext.useClasspathResourceCaching.b"</js> 3175 * <li><b>Data type:</b> <jk>boolean</jk> 3176 * <li><b>System property:</b> <c>RestContext.useClasspathResourceCaching</c> 3177 * <li><b>Environment variable:</b> <c>RESTCONTEXT_USECLASSPATHRESOURCECACHING</c> 3178 * <li><b>Default:</b> <jk>true</jk> 3179 * <li><b>Session property:</b> <jk>false</jk> 3180 * <li><b>Annotations:</b> 3181 * <ul> 3182 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#useClasspathResourceCaching()} 3183 * </ul> 3184 * <li><b>Methods:</b> 3185 * <ul> 3186 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#useClasspathResourceCaching(boolean)} 3187 * </ul> 3188 * </ul> 3189 * 3190 * <h5 class='section'>Description:</h5> 3191 * <p> 3192 * When enabled, resources retrieved via {@link RestContext#getClasspathResource(String, Locale)} (and related 3193 * methods) will be cached in memory to speed subsequent lookups. 3194 * 3195 * <h5 class='section'>Example:</h5> 3196 * <p class='bcode w800'> 3197 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 3198 * <ja>@Rest</ja>(useClasspathResourceCaching=<js>"$C{REST/useClasspathResourceCaching,false}"</js>) 3199 * <jk>public class</jk> MyResource { 3200 * 3201 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 3202 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 3203 * 3204 * <jc>// Using method on builder.</jc> 3205 * builder.useClasspathResourceCaching(<jk>false</jk>) 3206 * 3207 * <jc>// Same, but using property.</jc> 3208 * builder.set(<jsf>REST_useClasspathResourceCaching</jsf>, <jk>false</jk>); 3209 * } 3210 * 3211 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 3212 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 3213 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 3214 * builder.useClasspathResourceCaching(<jk>false</jk>) 3215 * } 3216 * } 3217 * </p> 3218 * 3219 * <ul class='seealso'> 3220 * <li class='jf'>{@link #REST_staticFiles} for information about static files. 3221 * </ul> 3222 */ 3223 public static final String REST_useClasspathResourceCaching = PREFIX + ".useClasspathResourceCaching.b"; 3224 3225 /** 3226 * Configuration property: Use stack trace hashes. 3227 * 3228 * <div class='warn'> 3229 * <b>Deprecated</b> - Use {@link Logging#useStackTraceHashing} 3230 * </div> 3231 */ 3232 @Deprecated 3233 public static final String REST_useStackTraceHashes = PREFIX + ".useStackTraceHashes.b"; 3234 3235 /** 3236 * Configuration property: Resource URI authority path. 3237 * 3238 * <h5 class='section'>Property:</h5> 3239 * <ul class='spaced-list'> 3240 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_uriAuthority REST_uriAuthority} 3241 * <li><b>Name:</b> <js>"RestContext.uriAuthority.s"</js> 3242 * <li><b>Data type:</b> <c>String</c> 3243 * <li><b>System property:</b> <c>RestContext.uriAuthority</c> 3244 * <li><b>Environment variable:</b> <c>RESTCONTEXT_URIAUTHORITY</c> 3245 * <li><b>Default:</b> <jk>null</jk> 3246 * <li><b>Session property:</b> <jk>false</jk> 3247 * <li><b>Annotations:</b> 3248 * <ul> 3249 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#uriAuthority()} 3250 * </ul> 3251 * <li><b>Methods:</b> 3252 * <ul> 3253 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#uriAuthority(String)} 3254 * </ul> 3255 * </ul> 3256 * 3257 * <h5 class='section'>Description:</h5> 3258 * <p> 3259 * Overrides the authority path value for this resource and any child resources. 3260 * 3261 * <p> 3262 * Affects the following methods: 3263 * <ul class='javatree'> 3264 * <li class='jm'>{@link RestRequest#getAuthorityPath()} 3265 * </ul> 3266 * 3267 * <p> 3268 * If you do not specify the authority, it is automatically calculated via the following: 3269 * 3270 * <p class='bcode w800'> 3271 * String scheme = request.getScheme(); 3272 * <jk>int</jk> port = request.getServerPort(); 3273 * StringBuilder sb = <jk>new</jk> StringBuilder(request.getScheme()).append(<js>"://"</js>).append(request.getServerName()); 3274 * <jk>if</jk> (! (port == 80 && <js>"http"</js>.equals(scheme) || port == 443 && <js>"https"</js>.equals(scheme))) 3275 * sb.append(<js>':'</js>).append(port); 3276 * authorityPath = sb.toString(); 3277 * </p> 3278 * 3279 * <h5 class='section'>Example:</h5> 3280 * <p class='bcode w800'> 3281 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 3282 * <ja>@Rest</ja>( 3283 * path=<js>"/servlet"</js>, 3284 * uriAuthority=<js>"$C{REST/authorityPathOverride,http://localhost:10000}"</js> 3285 * ) 3286 * <jk>public class</jk> MyResource { 3287 * 3288 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 3289 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 3290 * 3291 * <jc>// Using method on builder.</jc> 3292 * builder.uriAuthority(<js>"http://localhost:10000"</js>); 3293 * 3294 * <jc>// Same, but using property.</jc> 3295 * builder.set(<jsf>REST_uriAuthority</jsf>, <js>"http://localhost:10000"</js>); 3296 * } 3297 * 3298 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 3299 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 3300 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 3301 * builder.uriAuthority(<js>"http://localhost:10000"</js>); 3302 * } 3303 * } 3304 * </p> 3305 */ 3306 public static final String REST_uriAuthority = PREFIX + ".uriAuthority.s"; 3307 3308 /** 3309 * Configuration property: Resource URI context path. 3310 * 3311 * <h5 class='section'>Property:</h5> 3312 * <ul class='spaced-list'> 3313 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_uriContext REST_uriContext} 3314 * <li><b>Name:</b> <js>"RestContext.uriContext.s"</js> 3315 * <li><b>Data type:</b> <c>String</c> 3316 * <li><b>System property:</b> <c>RestContext.uriContext</c> 3317 * <li><b>Environment variable:</b> <c>RESTCONTEXT_URICONTEXT</c> 3318 * <li><b>Default:</b> <jk>null</jk> 3319 * <li><b>Session property:</b> <jk>false</jk> 3320 * <li><b>Annotations:</b> 3321 * <ul> 3322 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#uriContext()} 3323 * </ul> 3324 * <li><b>Methods:</b> 3325 * <ul> 3326 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#uriContext(String)} 3327 * </ul> 3328 * </ul> 3329 * 3330 * <h5 class='section'>Description:</h5> 3331 * <p> 3332 * Overrides the context path value for this resource and any child resources. 3333 * 3334 * <p> 3335 * This setting is useful if you want to use <js>"context:/child/path"</js> URLs in child resource POJOs but 3336 * the context path is not actually specified on the servlet container. 3337 * 3338 * <p> 3339 * Affects the following methods: 3340 * <ul class='javatree'> 3341 * <li class='jm'>{@link RestRequest#getContextPath()} - Returns the overridden context path for the resource. 3342 * <li class='jm'>{@link RestRequest#getServletPath()} - Includes the overridden context path for the resource. 3343 * </ul> 3344 * 3345 * <h5 class='section'>Example:</h5> 3346 * <p class='bcode w800'> 3347 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 3348 * <ja>@Rest</ja>( 3349 * path=<js>"/servlet"</js>, 3350 * uriContext=<js>"$C{REST/contextPathOverride,/foo}"</js> 3351 * ) 3352 * <jk>public class</jk> MyResource { 3353 * 3354 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 3355 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 3356 * 3357 * <jc>// Using method on builder.</jc> 3358 * builder.uriContext(<js>"/foo"</js>); 3359 * 3360 * <jc>// Same, but using property.</jc> 3361 * builder.set(<jsf>REST_uriContext</jsf>, <js>"/foo"</js>); 3362 * } 3363 * 3364 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 3365 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 3366 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 3367 * builder.uriContext(<js>"/foo"</js>); 3368 * } 3369 * } 3370 * </p> 3371 */ 3372 public static final String REST_uriContext = PREFIX + ".uriContext.s"; 3373 3374 /** 3375 * Configuration property: URI resolution relativity. 3376 * 3377 * <h5 class='section'>Property:</h5> 3378 * <ul class='spaced-list'> 3379 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_uriRelativity REST_uriRelativity} 3380 * <li><b>Name:</b> <js>"RestContext.uriRelativity.s"</js> 3381 * <li><b>Data type:</b> <c>String</c> 3382 * <li><b>System property:</b> <c>RestContext.uriRelativity</c> 3383 * <li><b>Environment variable:</b> <c>RESTCONTEXT_URIRELATIVITY</c> 3384 * <li><b>Default:</b> <js>"RESOURCE"</js> 3385 * <li><b>Session property:</b> <jk>false</jk> 3386 * <li><b>Annotations:</b> 3387 * <ul> 3388 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#uriRelativity()} 3389 * </ul> 3390 * <li><b>Methods:</b> 3391 * <ul> 3392 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#uriRelativity(String)} 3393 * </ul> 3394 * </ul> 3395 * 3396 * <h5 class='section'>Description:</h5> 3397 * <p> 3398 * Specifies how relative URIs should be interpreted by serializers. 3399 * 3400 * <p> 3401 * See {@link UriResolution} for possible values. 3402 * 3403 * <p> 3404 * Affects the following methods: 3405 * <ul class='javatree'> 3406 * <li class='jm'>{@link RestRequest#getUriResolver()} 3407 * </ul> 3408 * 3409 * <h5 class='section'>Example:</h5> 3410 * <p class='bcode w800'> 3411 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 3412 * <ja>@Rest</ja>( 3413 * path=<js>"/servlet"</js>, 3414 * uriRelativity=<js>"$C{REST/uriRelativity,PATH_INFO}"</js> 3415 * ) 3416 * <jk>public class</jk> MyResource { 3417 * 3418 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 3419 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 3420 * 3421 * <jc>// Using method on builder.</jc> 3422 * builder.uriRelativity(<js>"PATH_INFO"</js>); 3423 * 3424 * <jc>// Same, but using property.</jc> 3425 * builder.set(<jsf>REST_uriRelativity</jsf>, <js>"PATH_INFO"</js>); 3426 * } 3427 * 3428 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 3429 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 3430 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 3431 * builder.uriRelativity(<js>"PATH_INFO"</js>); 3432 * } 3433 * } 3434 * </p> 3435 */ 3436 public static final String REST_uriRelativity = PREFIX + ".uriRelativity.s"; 3437 3438 /** 3439 * Configuration property: URI resolution. 3440 * 3441 * <h5 class='section'>Property:</h5> 3442 * <ul class='spaced-list'> 3443 * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_uriResolution REST_uriResolution} 3444 * <li><b>Name:</b> <js>"RestContext.uriResolution.s"</js> 3445 * <li><b>Data type:</b> <c>String</c> 3446 * <li><b>System property:</b> <c>RestContext.uriResolution</c> 3447 * <li><b>Environment variable:</b> <c>RESTCONTEXT_URIRESOLUTION</c> 3448 * <li><b>Default:</b> <js>"ROOT_RELATIVE"</js> 3449 * <li><b>Session property:</b> <jk>false</jk> 3450 * <li><b>Annotations:</b> 3451 * <ul> 3452 * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#uriResolution()} 3453 * </ul> 3454 * <li><b>Methods:</b> 3455 * <ul> 3456 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#uriResolution(String)} 3457 * </ul> 3458 * </ul> 3459 * 3460 * <h5 class='section'>Description:</h5> 3461 * <p> 3462 * Specifies how relative URIs should be interpreted by serializers. 3463 * 3464 * <p> 3465 * See {@link UriResolution} for possible values. 3466 * 3467 * <p> 3468 * Affects the following methods: 3469 * <ul class='javatree'> 3470 * <li class='jm'>{@link RestRequest#getUriResolver()} 3471 * </ul> 3472 * 3473 * <h5 class='section'>Example:</h5> 3474 * <p class='bcode w800'> 3475 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 3476 * <ja>@Rest</ja>( 3477 * path=<js>"/servlet"</js>, 3478 * uriResolution=<js>"$C{REST/uriResolution,ABSOLUTE}"</js> 3479 * ) 3480 * <jk>public class</jk> MyResource { 3481 * 3482 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 3483 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 3484 * 3485 * <jc>// Using method on builder.</jc> 3486 * builder.uriResolution(<js>"ABSOLUTE"</js>); 3487 * 3488 * <jc>// Same, but using property.</jc> 3489 * builder.set(<jsf>REST_uriResolution</jsf>, <js>"ABSOLUTE"</js>); 3490 * } 3491 * 3492 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 3493 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 3494 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 3495 * builder.uriResolution(<js>"ABSOLUTE"</js>); 3496 * } 3497 * } 3498 * </p> 3499 */ 3500 public static final String REST_uriResolution = PREFIX + ".uriResolution.s"; 3501 3502 /** 3503 * Configuration property: HTML Widgets. 3504 * 3505 * <div class='warn'> 3506 * <b>Deprecated</b> - Use {@link HtmlDocSerializer#HTMLDOC_widgets} 3507 * </div> 3508 */ 3509 @Deprecated 3510 public static final String REST_widgets = PREFIX + ".widgets.lo"; 3511 3512 3513 //------------------------------------------------------------------------------------------------------------------- 3514 // Static 3515 //------------------------------------------------------------------------------------------------------------------- 3516 3517 private static final Map<Class<?>, RestContext> REGISTRY = new ConcurrentHashMap<>(); 3518 3519 /** 3520 * Returns a registry of all created {@link RestContext} objects. 3521 * 3522 * @return An unmodifiable map of resource classes to {@link RestContext} objects. 3523 */ 3524 public static final Map<Class<?>, RestContext> getGlobalRegistry() { 3525 return Collections.unmodifiableMap(REGISTRY); 3526 } 3527 3528// public static final Set<MethodExecStats> getGlobalExecStats() { 3529// Set<MethodExecStats> s = new TreeSet<>(); 3530// for (RestContext rc : REGISTRY.values()) 3531// s.addAll(rc.getMethodExecStats()); 3532// return s; 3533// } 3534// 3535//// public static final Set<ExceptionStats> getGlobalExceptions() { 3536//// Set<ExceptionStats> s = new TreeSet<>(); 3537//// for (RestContext rc : REGISTRY.values()) 3538//// s.addAll(rc.getStackTraceDb().getClonedStackTraceInfos()); 3539//// return s; 3540//// } 3541// 3542// public static final Set<StatusStats> getGlobalStatusStats() { 3543// Set<StatusStats> s = new TreeSet<>(); 3544// for (RestContext rc : REGISTRY.values()) { 3545// StatusStats ss = StatusStats.create(rc.getResource().getClass()); 3546// s.add(ss); 3547// for (RestMethodContext rmc : rc.getCallMethods().values()) { 3548// StatusStats.Method ssm = ss.getMethod(rmc.method); 3549// for (Map.Entry<Integer,Integer> e : rmc.getStatusCodes().entrySet()) { 3550// ssm.status(e.getKey(), e.getValue()); 3551// } 3552// } 3553// } 3554// return s; 3555// } 3556 3557 //------------------------------------------------------------------------------------------------------------------- 3558 // Instance 3559 //------------------------------------------------------------------------------------------------------------------- 3560 3561 private final Object resource; 3562 final RestContextBuilder builder; 3563 private final boolean 3564 allowBodyParam, 3565 renderResponseStackTraces, 3566 useClasspathResourceCaching; 3567 private final Enablement debug; 3568 @Deprecated private final boolean 3569 useStackTraceHashes; 3570 private final String 3571 clientVersionHeader, 3572 uriAuthority, 3573 uriContext; 3574 final String fullPath; 3575 final UrlPathPattern pathPattern; 3576 3577 private final Set<String> allowedMethodParams, allowedHeaderParams, allowedMethodHeaders; 3578 3579 private final RestContextProperties properties; 3580 private final Map<Class<?>,RestMethodParam> paramResolvers; 3581 private final SerializerGroup serializers; 3582 private final ParserGroup parsers; 3583 private final HttpPartSerializer partSerializer; 3584 private final HttpPartParser partParser; 3585 private final JsonSchemaGenerator jsonSchemaGenerator; 3586 private final List<MediaType> 3587 consumes, 3588 produces; 3589 private final Map<String,Object> 3590 reqHeaders, 3591 resHeaders, 3592 staticFileResponseHeaders; 3593 private final OMap reqAttrs; 3594 private final ResponseHandler[] responseHandlers; 3595 private final MimetypesFileTypeMap mimetypesFileTypeMap; 3596 private final StaticFiles[] staticFiles; 3597 private final String[] staticFilesPaths; 3598 private final Messages msgs; 3599 private final Config config; 3600 private final VarResolver varResolver; 3601 private final Map<String,List<RestMethodContext>> methodMap; 3602 private final List<RestMethodContext> methods; 3603 private final Map<String,RestContext> childResources; 3604 @SuppressWarnings("deprecation") private final RestLogger logger; 3605 private final RestCallLogger callLogger; 3606 private final RestCallLoggerConfig callLoggerConfig; 3607 private final StackTraceDatabase stackTraceDb; 3608 private final RestInfoProvider infoProvider; 3609 private final HttpException initException; 3610 private final RestContext parentContext; 3611 private final RestResourceResolver resourceResolver; 3612 private final UriResolution uriResolution; 3613 private final UriRelativity uriRelativity; 3614 private final ConcurrentHashMap<String,MethodExecStats> methodExecStats = new ConcurrentHashMap<>(); 3615 private final Instant startTime; 3616 private final Map<Class<?>,ResponseBeanMeta> responseBeanMetas = new ConcurrentHashMap<>(); 3617 3618 // Lifecycle methods 3619 private final MethodInvoker[] 3620 postInitMethods, 3621 postInitChildFirstMethods, 3622 preCallMethods, 3623 postCallMethods, 3624 startCallMethods, 3625 endCallMethods, 3626 destroyMethods; 3627 private final RestMethodParam[][] 3628 preCallMethodParams, 3629 postCallMethodParams; 3630 private final Class<?>[][] 3631 postInitMethodParams, 3632 postInitChildFirstMethodParams, 3633 startCallMethodParams, 3634 endCallMethodParams, 3635 destroyMethodParams; 3636 3637 // In-memory cache of images and stylesheets in the org.apache.juneau.rest.htdocs package. 3638 private final Map<String,StaticFile> staticFilesCache = new ConcurrentHashMap<>(); 3639 3640 private final ResourceManager staticResourceManager; 3641 @Deprecated private final ConcurrentHashMap<Integer,AtomicInteger> stackTraceHashes = new ConcurrentHashMap<>(); 3642 3643 private final ThreadLocal<RestCall> call = new ThreadLocal<>(); 3644 3645 private final ReflectionMap<Enablement> debugEnablement; 3646 3647 /** 3648 * Constructor. 3649 * 3650 * @param resource The resource annotated with <ja>@Rest</ja>. 3651 * @return A new builder object. 3652 * @throws ServletException Something bad happened. 3653 */ 3654 public static RestContextBuilder create(Object resource) throws ServletException { 3655 return new RestContextBuilder(null, resource.getClass(), null).init(resource); 3656 } 3657 3658 /** 3659 * Constructor. 3660 * 3661 * @param servletConfig The servlet config passed into the servlet by the servlet container. 3662 * @param resourceClass The class annotated with <ja>@Rest</ja>. 3663 * @param parentContext The parent context, or <jk>null</jk> if there is no parent context. 3664 * @return A new builder object. 3665 * @throws ServletException Something bad happened. 3666 */ 3667 static RestContextBuilder create(ServletConfig servletConfig, Class<?> resourceClass, RestContext parentContext) throws ServletException { 3668 return new RestContextBuilder(servletConfig, resourceClass, parentContext); 3669 } 3670 3671 /** 3672 * Constructor. 3673 * 3674 * @param builder The servlet configuration object. 3675 * @throws Exception If any initialization problems were encountered. 3676 */ 3677 @SuppressWarnings("deprecation") 3678 public RestContext(RestContextBuilder builder) throws Exception { 3679 super(builder.getPropertyStore()); 3680 3681 startTime = Instant.now(); 3682 3683 REGISTRY.put(builder.resourceClass, this); 3684 3685 HttpException _initException = null; 3686 3687 try { 3688 ServletContext servletContext = builder.servletContext; 3689 3690 this.resource = builder.resource; 3691 this.builder = builder; 3692 this.parentContext = builder.parentContext; 3693 3694 Object defaultResourceResolver = parentContext == null ? (resource instanceof RestResourceResolver ? resource : BasicRestResourceResolver.class) : parentContext.resourceResolver; 3695 resourceResolver = getInstanceProperty(REST_resourceResolver, resource, RestResourceResolver.class, defaultResourceResolver, ResourceResolver.FUZZY, this); 3696 3697 varResolver = builder.varResolverBuilder 3698 .vars( 3699 FileVar.class, 3700 LocalizationVar.class, 3701 RequestAttributeVar.class, 3702 RequestFormDataVar.class, 3703 RequestHeaderVar.class, 3704 RequestPathVar.class, 3705 RequestQueryVar.class, 3706 RequestVar.class, 3707 RestInfoVar.class, 3708 SerializedRequestAttrVar.class, 3709 ServletInitParamVar.class, 3710 SwaggerVar.class, 3711 UrlVar.class, 3712 UrlEncodeVar.class, 3713 WidgetVar.class 3714 ) 3715 .build() 3716 ; 3717 3718 VarResolverSession vrs = this.varResolver.createSession(); 3719 config = builder.config.resolving(vrs); 3720 3721 ClassInfo rci = ClassInfo.of(resource).resolved(); 3722 3723 PropertyStore ps = getPropertyStore(); 3724 3725 uriContext = nullIfEmpty(getStringProperty(REST_uriContext, null)); 3726 uriAuthority = nullIfEmpty(getStringProperty(REST_uriAuthority, null)); 3727 uriResolution = getProperty(REST_uriResolution, UriResolution.class, UriResolution.ROOT_RELATIVE); 3728 uriRelativity = getProperty(REST_uriRelativity, UriRelativity.class, UriRelativity.RESOURCE); 3729 3730 allowBodyParam = getBooleanProperty(REST_allowBodyParam, true); 3731 allowedHeaderParams = newUnmodifiableSortedCaseInsensitiveSet(getStringPropertyWithNone(REST_allowedHeaderParams, "Accept,Content-Type")); 3732 allowedMethodParams = newUnmodifiableSortedCaseInsensitiveSet(getStringPropertyWithNone(REST_allowedMethodParams, "HEAD,OPTIONS")); 3733 allowedMethodHeaders = newUnmodifiableSortedCaseInsensitiveSet(getStringPropertyWithNone(REST_allowedMethodHeaders, "")); 3734 renderResponseStackTraces = getBooleanProperty(REST_renderResponseStackTraces, false); 3735 useStackTraceHashes = getBooleanProperty(REST_useStackTraceHashes, true); 3736 clientVersionHeader = getStringProperty(REST_clientVersionHeader, "X-Client-Version"); 3737 3738 ReflectionMap.Builder<Enablement> deb = ReflectionMap.create(Enablement.class); 3739 for (String s : split(getStringProperty(REST_debugOn, ""))) { 3740 s = s.trim(); 3741 if (! s.isEmpty()) { 3742 int i = s.indexOf('='); 3743 if (i == -1) 3744 deb.append(s.trim(), Enablement.TRUE); 3745 else 3746 deb.append(s.substring(0, i).trim(), Enablement.fromString(s.substring(i+1).trim())); 3747 } 3748 } 3749 3750 boolean debug = isDebug(); 3751 3752 Enablement de = getInstanceProperty(REST_debug, Enablement.class, debug ? Enablement.TRUE : Enablement.FALSE); 3753 if (de != null) 3754 deb.append(rci.getFullName(), de); 3755 for (MethodInfo mi : rci.getPublicMethods()) 3756 for (RestMethod a : mi.getAnnotations(RestMethod.class)) 3757 if (a != null && ! a.debug().isEmpty()) 3758 deb.append(mi.getFullName(), Enablement.fromString(a.debug())); 3759 3760 this.debugEnablement = deb.build(); 3761 3762 this.debug = debugEnablement.find(rci.inner(), Enablement.class).orElse(Enablement.FALSE); 3763 3764 responseHandlers = getInstanceArrayProperty(REST_responseHandlers, resource, ResponseHandler.class, new ResponseHandler[0], resourceResolver, this); 3765 3766 AMap<Class<?>,RestMethodParam> _paramResolvers = AMap.of(); 3767 for (RestMethodParam rp : getInstanceArrayProperty(REST_paramResolvers, RestMethodParam.class, new RestMethodParam[0], resourceResolver, this)) 3768 _paramResolvers.put(rp.forClass(), rp); 3769 paramResolvers = _paramResolvers.unmodifiable(); 3770 3771 Map<String,Object> _reqHeaders = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); 3772 _reqHeaders.putAll(getMapProperty(REST_reqHeaders, String.class)); 3773 reqHeaders = AMap.unmodifiable(_reqHeaders); 3774 3775 reqAttrs = new OMap(getMapProperty(REST_reqAttrs, Object.class)).unmodifiable(); 3776 resHeaders = getMapProperty(REST_resHeaders, Object.class); 3777 staticFileResponseHeaders = getMapProperty(REST_staticFileResponseHeaders, Object.class); 3778 3779 logger = getInstanceProperty(REST_logger, resource, RestLogger.class, NoOpRestLogger.class, resourceResolver, this); 3780 3781 Object clc = getProperty(REST_callLoggerConfig); 3782 if (this.debug == TRUE) 3783 this.callLoggerConfig = RestCallLoggerConfig.DEFAULT_DEBUG; 3784 else if (clc instanceof RestCallLoggerConfig) 3785 this.callLoggerConfig = (RestCallLoggerConfig)clc; 3786 else if (clc instanceof OMap) 3787 this.callLoggerConfig = RestCallLoggerConfig.create().apply((OMap)clc).build(); 3788 else 3789 this.callLoggerConfig = RestCallLoggerConfig.DEFAULT_NOOP; 3790 3791 this.stackTraceDb = new StackTraceDatabase(callLoggerConfig.getStackTraceHashingTimeout(), RestMethodContext.class); 3792 3793 Object defaultRestCallLogger = resource instanceof RestCallLogger ? resource : BasicRestCallLogger.class; 3794 callLogger = getInstanceProperty(REST_callLogger, resource, RestCallLogger.class, defaultRestCallLogger, resourceResolver, this); 3795 3796 properties = builder.properties; 3797 serializers = 3798 SerializerGroup 3799 .create() 3800 .append(getInstanceArrayProperty(REST_serializers, Serializer.class, new Serializer[0], resourceResolver, resource, ps)) 3801 .build(); 3802 parsers = 3803 ParserGroup 3804 .create() 3805 .append(getInstanceArrayProperty(REST_parsers, Parser.class, new Parser[0], resourceResolver, resource, ps)) 3806 .build(); 3807 partSerializer = getInstanceProperty(REST_partSerializer, HttpPartSerializer.class, OpenApiSerializer.class, resourceResolver, resource, ps); 3808 partParser = getInstanceProperty(REST_partParser, HttpPartParser.class, OpenApiParser.class, resourceResolver, resource, ps); 3809 jsonSchemaGenerator = 3810 JsonSchemaGenerator 3811 .create() 3812 .apply(ps) 3813 .build(); 3814 3815 mimetypesFileTypeMap = new ExtendedMimetypesFileTypeMap(); 3816 for (String mimeType : getArrayProperty(REST_mimeTypes, String.class)) 3817 mimetypesFileTypeMap.addMimeTypes(mimeType); 3818 3819 Object defaultResourceFinder = resource instanceof ResourceFinder ? resource : BasicResourceFinder.class; 3820 ResourceFinder rf = getInstanceProperty(REST_classpathResourceFinder, ResourceFinder.class, defaultResourceFinder, resourceResolver, this); 3821 3822 useClasspathResourceCaching = getProperty(REST_useClasspathResourceCaching, boolean.class, true); 3823 staticResourceManager = new ResourceManager(rci.inner(), rf, useClasspathResourceCaching); 3824 3825 consumes = getListProperty(REST_consumes, MediaType.class, parsers.getSupportedMediaTypes()); 3826 produces = getListProperty(REST_produces, MediaType.class, serializers.getSupportedMediaTypes()); 3827 3828 StaticFileMapping[] staticFileMappings = getArrayProperty(REST_staticFiles, StaticFileMapping.class, new StaticFileMapping[0]); 3829 staticFiles = new StaticFiles[staticFileMappings.length]; 3830 for (int i = 0; i < staticFiles.length; i++) 3831 staticFiles[i] = new StaticFiles(staticFileMappings[i], staticResourceManager, mimetypesFileTypeMap, staticFileResponseHeaders); 3832 3833 Set<String> s = new TreeSet<>(); 3834 for (StaticFiles sf : staticFiles) 3835 s.add(sf.getPath()); 3836 staticFilesPaths = s.toArray(new String[s.size()]); 3837 3838 Tuple2<Class<?>,String>[] mbl = getInstanceArrayProperty(REST_messages, Tuple2.class, new Tuple2[0]); 3839 Messages msgs = null; 3840 for (int i = mbl.length-1; i >= 0; i--) 3841 msgs = Messages.create(firstNonNull(mbl[i].getA(), rci.inner())).name(mbl[i].getB()).parent(msgs).build(); 3842 this.msgs = msgs; 3843 3844 this.fullPath = (builder.parentContext == null ? "" : (builder.parentContext.fullPath + '/')) + builder.getPath(); 3845 3846 String p = builder.getPath(); 3847 if (! p.endsWith("/*")) 3848 p += "/*"; 3849 this.pathPattern = new UrlPathPattern(p); 3850 3851 this.childResources = Collections.synchronizedMap(new LinkedHashMap<String,RestContext>()); // Not unmodifiable on purpose so that children can be replaced. 3852 3853 //---------------------------------------------------------------------------------------------------- 3854 // Initialize the child resources. 3855 // Done after initializing fields above since we pass this object to the child resources. 3856 //---------------------------------------------------------------------------------------------------- 3857 List<String> methodsFound = new LinkedList<>(); // Temporary to help debug transient duplicate method issue. 3858 MethodMapBuilder methodMapBuilder = new MethodMapBuilder(); 3859 AMap<String,Method> 3860 _startCallMethods = AMap.of(), 3861 _preCallMethods = AMap.of(), 3862 _postCallMethods = AMap.of(), 3863 _endCallMethods = AMap.of(), 3864 _postInitMethods = AMap.of(), 3865 _postInitChildFirstMethods = AMap.of(), 3866 _destroyMethods = AMap.of(); 3867 AList<RestMethodParam[]> 3868 _preCallMethodParams = AList.of(), 3869 _postCallMethodParams = AList.of(); 3870 AList<Class<?>[]> 3871 _startCallMethodParams = AList.of(), 3872 _endCallMethodParams = AList.of(), 3873 _postInitMethodParams = AList.of(), 3874 _postInitChildFirstMethodParams = AList.of(), 3875 _destroyMethodParams = AList.of(); 3876 3877 for (MethodInfo mi : rci.getPublicMethods()) { 3878 RestMethod a = mi.getLastAnnotation(RestMethod.class); 3879 3880 // Also include methods on @Rest-annotated interfaces. 3881 if (a == null) { 3882 for (Method mi2 : mi.getMatching()) { 3883 Class<?> ci2 = mi2.getDeclaringClass(); 3884 if (ci2.isInterface() && ci2.getAnnotation(Rest.class) != null) { 3885 a = new RestMethodAnnotation(); 3886 } 3887 } 3888 } 3889 if (a != null) { 3890 methodsFound.add(mi.getSimpleName() + "," + emptyIfNull(firstNonEmpty(a.name(), a.method())) + "," + fixMethodPath(a.path())); 3891 try { 3892 if (mi.isNotPublic()) 3893 throw new RestServletException("@RestMethod method {0}.{1} must be defined as public.", rci.inner().getName(), mi.getSimpleName()); 3894 3895 RestMethodContextBuilder rmcb = new RestMethodContextBuilder(resource, mi.inner(), this); 3896 RestMethodContext sm = new RestMethodContext(rmcb); 3897 String httpMethod = sm.getHttpMethod(); 3898 3899 // RRPC is a special case where a method returns an interface that we 3900 // can perform REST calls against. 3901 // We override the CallMethod.invoke() method to insert our logic. 3902 if ("RRPC".equals(httpMethod)) { 3903 3904 final ClassMeta<?> interfaceClass = getClassMeta(mi.inner().getGenericReturnType()); 3905 final RrpcInterfaceMeta rim = new RrpcInterfaceMeta(interfaceClass.getInnerClass(), null); 3906 if (rim.getMethodsByPath().isEmpty()) 3907 throw new RestException(SC_INTERNAL_SERVER_ERROR, "Method {0} returns an interface {1} that doesn't define any remote methods.", mi.getSignature(), interfaceClass.getFullName()); 3908 3909 RestMethodContextBuilder smb = new RestMethodContextBuilder(resource, mi.inner(), this); 3910 smb.dotAll(); 3911 sm = new RestMethodContext(smb) { 3912 3913 @Override 3914 void invoke(RestCall call) throws Throwable { 3915 3916 super.invoke(call); 3917 3918 final Object o = call.getOutput(); 3919 3920 if ("GET".equals(call.getMethod())) { 3921 call.output(rim.getMethodsByPath().keySet()); 3922 return; 3923 3924 } else if ("POST".equals(call.getMethod())) { 3925 String pip = call.getUrlPathInfo().getPath(); 3926 if (pip.indexOf('/') != -1) 3927 pip = pip.substring(pip.lastIndexOf('/')+1); 3928 pip = urlDecode(pip); 3929 RrpcInterfaceMethodMeta rmm = rim.getMethodMetaByPath(pip); 3930 if (rmm != null) { 3931 Method m = rmm.getJavaMethod(); 3932 try { 3933 RestRequest req = call.getRestRequest(); 3934 // Parse the args and invoke the method. 3935 Parser p = req.getBody().getParser(); 3936 Object[] args = null; 3937 if (m.getGenericParameterTypes().length == 0) 3938 args = new Object[0]; 3939 else { 3940 try (Closeable in = p.isReaderParser() ? req.getReader() : req.getInputStream()) { 3941 args = p.parseArgs(in, m.getGenericParameterTypes()); 3942 } 3943 } 3944 Object output = m.invoke(o, args); 3945 call.output(output); 3946 return; 3947 } catch (Exception e) { 3948 throw toHttpException(e, InternalServerError.class); 3949 } 3950 } 3951 } 3952 throw new NotFound(); 3953 } 3954 }; 3955 3956 methodMapBuilder.add("GET", sm).add("POST", sm); 3957 3958 } else { 3959 methodMapBuilder.add(httpMethod, sm); 3960 } 3961 } catch (Throwable e) { 3962 throw new RestServletException(e, "Problem occurred trying to initialize methods on class {0}, methods={1}", rci.inner().getName(), SimpleJsonSerializer.DEFAULT.serialize(methodsFound)); 3963 } 3964 } 3965 } 3966 3967 for (MethodInfo m : rci.getAllMethodsParentFirst()) { 3968 if (m.isPublic() && m.hasAnnotation(RestHook.class)) { 3969 HookEvent he = m.getLastAnnotation(RestHook.class).value(); 3970 String sig = m.getSignature(); 3971 switch(he) { 3972 case PRE_CALL: { 3973 if (! _preCallMethods.containsKey(sig)) { 3974 m.setAccessible(); 3975 _preCallMethods.put(sig, m.inner()); 3976 _preCallMethodParams.add(findParams(m, true, null)); 3977 } 3978 break; 3979 } 3980 case POST_CALL: { 3981 if (! _postCallMethods.containsKey(sig)) { 3982 m.setAccessible(); 3983 _postCallMethods.put(sig, m.inner()); 3984 _postCallMethodParams.add(findParams(m, true, null)); 3985 } 3986 break; 3987 } 3988 case START_CALL: { 3989 if (! _startCallMethods.containsKey(sig)) { 3990 m.setAccessible(); 3991 _startCallMethods.put(sig, m.inner()); 3992 _startCallMethodParams.add((Class<?>[])m.getRawParamTypes().toArray()); 3993 assertArgsOnlyOfType(m, HttpServletRequest.class, HttpServletResponse.class); 3994 } 3995 break; 3996 } 3997 case END_CALL: { 3998 if (! _endCallMethods.containsKey(sig)) { 3999 m.setAccessible(); 4000 _endCallMethods.put(sig, m.inner()); 4001 _endCallMethodParams.add((Class<?>[])m.getRawParamTypes().toArray()); 4002 assertArgsOnlyOfType(m, HttpServletRequest.class, HttpServletResponse.class); 4003 } 4004 break; 4005 } 4006 case POST_INIT: { 4007 if (! _postInitMethods.containsKey(sig)) { 4008 m.setAccessible(); 4009 _postInitMethods.put(sig, m.inner()); 4010 _postInitMethodParams.add((Class<?>[])m.getRawParamTypes().toArray()); 4011 assertArgsOnlyOfType(m, RestContext.class); 4012 } 4013 break; 4014 } 4015 case POST_INIT_CHILD_FIRST: { 4016 if (! _postInitChildFirstMethods.containsKey(sig)) { 4017 m.setAccessible(); 4018 _postInitChildFirstMethods.put(sig, m.inner()); 4019 _postInitChildFirstMethodParams.add((Class<?>[])m.getRawParamTypes().toArray()); 4020 assertArgsOnlyOfType(m, RestContext.class); 4021 } 4022 break; 4023 } 4024 case DESTROY: { 4025 if (! _destroyMethods.containsKey(sig)) { 4026 m.setAccessible(); 4027 _destroyMethods.put(sig, m.inner()); 4028 _destroyMethodParams.add((Class<?>[])m.getRawParamTypes().toArray()); 4029 assertArgsOnlyOfType(m, RestContext.class); 4030 } 4031 break; 4032 } 4033 default: // Ignore INIT 4034 } 4035 } 4036 } 4037 4038 this.preCallMethods = _preCallMethods.values().stream().map(x->new MethodInvoker(x, getMethodExecStats(x))).collect(Collectors.toList()).toArray(new MethodInvoker[_preCallMethods.size()]); 4039 this.postCallMethods = _postCallMethods.values().stream().map(x->new MethodInvoker(x, getMethodExecStats(x))).collect(Collectors.toList()).toArray(new MethodInvoker[_postCallMethods.size()]); 4040 this.startCallMethods = _startCallMethods.values().stream().map(x->new MethodInvoker(x, getMethodExecStats(x))).collect(Collectors.toList()).toArray(new MethodInvoker[_startCallMethods.size()]); 4041 this.endCallMethods = _endCallMethods.values().stream().map(x->new MethodInvoker(x, getMethodExecStats(x))).collect(Collectors.toList()).toArray(new MethodInvoker[_endCallMethods.size()]); 4042 this.postInitMethods = _postInitMethods.values().stream().map(x->new MethodInvoker(x, getMethodExecStats(x))).collect(Collectors.toList()).toArray(new MethodInvoker[_postInitMethods.size()]); 4043 this.postInitChildFirstMethods = _postInitChildFirstMethods.values().stream().map(x->new MethodInvoker(x, getMethodExecStats(x))).collect(Collectors.toList()).toArray(new MethodInvoker[_postInitChildFirstMethods.size()]); 4044 this.destroyMethods = _destroyMethods.values().stream().map(x->new MethodInvoker(x, getMethodExecStats(x))).collect(Collectors.toList()).toArray(new MethodInvoker[_destroyMethods.size()]); 4045 this.preCallMethodParams = _preCallMethodParams.toArray(new RestMethodParam[_preCallMethodParams.size()][]); 4046 this.postCallMethodParams = _postCallMethodParams.toArray(new RestMethodParam[_postCallMethodParams.size()][]); 4047 this.startCallMethodParams = _startCallMethodParams.toArray(new Class[_startCallMethodParams.size()][]); 4048 this.endCallMethodParams = _endCallMethodParams.toArray(new Class[_endCallMethodParams.size()][]); 4049 this.postInitMethodParams = _postInitMethodParams.toArray(new Class[_postInitMethodParams.size()][]); 4050 this.postInitChildFirstMethodParams = _postInitChildFirstMethodParams.toArray(new Class[_postInitChildFirstMethodParams.size()][]); 4051 this.destroyMethodParams = _destroyMethodParams.toArray(new Class[_destroyMethodParams.size()][]); 4052 4053 this.methodMap = methodMapBuilder.getMap(); 4054 this.methods = methodMapBuilder.getList(); 4055 4056 // Initialize our child resources. 4057 for (Object o : getArrayProperty(REST_children, Object.class)) { 4058 String path = null; 4059 Object r = null; 4060 if (o instanceof RestChild) { 4061 RestChild rc = (RestChild)o; 4062 path = rc.path; 4063 r = rc.resource; 4064 } else if (o instanceof Class<?>) { 4065 Class<?> c = (Class<?>)o; 4066 // Don't allow specifying yourself as a child. Causes an infinite loop. 4067 if (c == builder.resourceClass) 4068 continue; 4069 r = c; 4070 } else { 4071 r = o; 4072 } 4073 4074 RestContextBuilder childBuilder = null; 4075 4076 if (o instanceof Class) { 4077 Class<?> oc = (Class<?>)o; 4078 childBuilder = RestContext.create(builder.inner, oc, this); 4079 r = resourceResolver.resolve(resource, oc, childBuilder); 4080 } else { 4081 r = o; 4082 childBuilder = RestContext.create(builder.inner, o.getClass(), this); 4083 } 4084 4085 childBuilder.init(r); 4086 if (r instanceof RestServlet) 4087 ((RestServlet)r).innerInit(childBuilder); 4088 childBuilder.servletContext(servletContext); 4089 RestContext rc2 = childBuilder.build(); 4090 if (r instanceof RestServlet) 4091 ((RestServlet)r).setContext(rc2); 4092 path = childBuilder.getPath(); 4093 childResources.put(path, rc2); 4094 } 4095 4096 Object defaultRestInfoProvider = resource instanceof RestInfoProvider ? resource : BasicRestInfoProvider.class; 4097 infoProvider = getInstanceProperty(REST_infoProvider, resource, RestInfoProvider.class, defaultRestInfoProvider, resourceResolver, this); 4098 4099 } catch (HttpException e) { 4100 _initException = e; 4101 throw e; 4102 } catch (Exception e) { 4103 _initException = new InternalServerError(e); 4104 throw e; 4105 } finally { 4106 initException = _initException; 4107 } 4108 } 4109 4110 /** 4111 * Returns the resource resolver associated with this context. 4112 * 4113 * <p> 4114 * The resource resolver is used for instantiating child resource classes. 4115 * 4116 * <ul class='seealso'> 4117 * <li class='jf'>{@link #REST_resourceResolver} 4118 * </ul> 4119 * 4120 * @return The resource resolver associated with this context. 4121 */ 4122 protected RestResourceResolver getResourceResolver() { 4123 return resourceResolver; 4124 } 4125 4126 /** 4127 * Returns the time statistics gatherer for the specified method. 4128 * 4129 * @param m The method to get statistics for. 4130 * @return The cached time-stats object. 4131 */ 4132 protected MethodExecStats getMethodExecStats(Method m) { 4133 String n = MethodInfo.of(m).getSimpleName(); 4134 MethodExecStats ts = methodExecStats.get(n); 4135 if (ts == null) { 4136 methodExecStats.putIfAbsent(n, new MethodExecStats(m)); 4137 ts = methodExecStats.get(n); 4138 } 4139 return ts; 4140 } 4141 4142 /** 4143 * Returns the variable resolver for this servlet. 4144 * 4145 * <p> 4146 * Variable resolvers are used to replace variables in property values. 4147 * They can be nested arbitrarily deep. 4148 * They can also return values that themselves contain other variables. 4149 * 4150 * <h5 class='figure'>Example:</h5> 4151 * <p class='bcode w800'> 4152 * <ja>@Rest</ja>( 4153 * messages=<js>"nls/Messages"</js>, 4154 * properties={ 4155 * <ja>@Property</ja>(name=<js>"title"</js>,value=<js>"$L{title}"</js>), <jc>// Localized variable in Messages.properties</jc> 4156 * <ja>@Property</ja>(name=<js>"javaVendor"</js>,value=<js>"$S{java.vendor,Oracle}"</js>), <jc>// System property with default value</jc> 4157 * <ja>@Property</ja>(name=<js>"foo"</js>,value=<js>"bar"</js>), 4158 * <ja>@Property</ja>(name=<js>"bar"</js>,value=<js>"baz"</js>), 4159 * <ja>@Property</ja>(name=<js>"v1"</js>,value=<js>"$R{foo}"</js>), <jc>// Request variable. value="bar"</jc> 4160 * <ja>@Property</ja>(name=<js>"v1"</js>,value=<js>"$R{foo,bar}"</js>), <jc>// Request variable. value="bar"</jc> 4161 * } 4162 * ) 4163 * <jk>public class</jk> MyRestResource <jk>extends</jk> BasicRestServlet { 4164 * </p> 4165 * 4166 * <p> 4167 * A typical usage pattern involves using variables inside the {@link HtmlDocConfig @HtmlDocConfig} annotation: 4168 * <p class='bcode w800'> 4169 * <ja>@RestMethod</ja>( 4170 * name=<jsf>GET</jsf>, path=<js>"/{name}/*"</js> 4171 * ) 4172 * <ja>@HtmlDocConfig</ja>( 4173 * navlinks={ 4174 * <js>"up: $R{requestParentURI}"</js>, 4175 * <js>"options: servlet:/?method=OPTIONS"</js>, 4176 * <js>"stats: servlet:/stats"</js>, 4177 * <js>"editLevel: servlet:/editLevel?logger=$A{attribute.name, OFF}"</js> 4178 * } 4179 * header={ 4180 * <js>"<h1>$L{MyLocalizedPageTitle}</h1>"</js> 4181 * }, 4182 * aside={ 4183 * <js>"$F{resources/AsideText.html}"</js> 4184 * } 4185 * ) 4186 * <jk>public</jk> LoggerEntry getLogger(RestRequest req, <ja>@Path</ja> String name) <jk>throws</jk> Exception { 4187 * </p> 4188 * 4189 * <ul class='seealso'> 4190 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#vars(Class...)} - For adding custom vars. 4191 * <li class='link'>{@doc RestSvlVariables} 4192 * <li class='link'>{@doc RestSvlVariables} 4193 * </ul> 4194 * 4195 * @return The var resolver in use by this resource. 4196 */ 4197 public VarResolver getVarResolver() { 4198 return varResolver; 4199 } 4200 4201 /** 4202 * Returns the config file associated with this servlet. 4203 * 4204 * <p> 4205 * The config file is identified via one of the following: 4206 * <ul class='javatree'> 4207 * <li class='ja'>{@link Rest#config()} 4208 * <li class='jm'>{@link RestContextBuilder#config(Config)} 4209 * </ul> 4210 * 4211 * @return 4212 * The resolving config file associated with this servlet. 4213 * <br>Never <jk>null</jk>. 4214 */ 4215 public Config getConfig() { 4216 return config; 4217 } 4218 4219 /** 4220 * Resolve a static resource file. 4221 * 4222 * <p> 4223 * The location of static resources are defined via: 4224 * <ul class='javatree'> 4225 * <li class='jf'>{@link RestContext#REST_staticFiles RestContext.REST_staticFiles} 4226 * </ul> 4227 * 4228 * @param pathInfo The unencoded path info. 4229 * @return The wrapped resource, never <jk>null</jk>. 4230 * @throws NotFound Invalid path. 4231 * @throws IOException Thrown by underlying stream. 4232 */ 4233 protected StaticFile getStaticFile(String pathInfo) throws NotFound, IOException { 4234 if (! staticFilesCache.containsKey(pathInfo)) { 4235 String p = urlDecode(trimSlashes(pathInfo)); 4236 if (p.indexOf("..") != -1) 4237 throw new NotFound("Invalid path"); 4238 StaticFile sf = null; 4239 for (StaticFiles sfs : staticFiles) { 4240 sf = sfs.resolve(p); 4241 if (sf != null) 4242 break; 4243 } 4244 if (sf == null) 4245 sf = new StaticFile(null,null,null); 4246 if (useClasspathResourceCaching) { 4247 if (staticFilesCache.size() > 100) 4248 staticFilesCache.clear(); 4249 staticFilesCache.put(pathInfo, sf); 4250 } 4251 return sf; 4252 } 4253 return staticFilesCache.get(pathInfo); 4254 } 4255 4256 /** 4257 * Same as {@link Class#getResourceAsStream(String)} except if it doesn't find the resource on this class, searches 4258 * up the parent hierarchy chain. 4259 * 4260 * <p> 4261 * If the resource cannot be found in the classpath, then an attempt is made to look in the JVM working directory. 4262 * 4263 * <p> 4264 * If the <c>locale</c> is specified, then we look for resources whose name matches that locale. 4265 * <br>For example, if looking for the resource <js>"MyResource.txt"</js> for the Japanese locale, we will look for 4266 * files in the following order: 4267 * <ol> 4268 * <li><js>"MyResource_ja_JP.txt"</js> 4269 * <li><js>"MyResource_ja.txt"</js> 4270 * <li><js>"MyResource.txt"</js> 4271 * </ol> 4272 * 4273 * <h5 class='section'>Example:</h5> 4274 * <p class='bcode w800'> 4275 * <jc>// A rest method that (unsafely!) returns the contents of a localized file </jc> 4276 * <jc>// from the classpath.</jc> 4277 * <ja>@RestMethod</ja>(path=<js>"/foo"</js>) 4278 * <jk>public</jk> Object myMethod(RestRequest req, <ja>@Query</ja>(<js>"file"</js>) String file) { 4279 * <jk>return</jk> getContext().getClasspathResource(file, req.getLocale()); 4280 * } 4281 * </p> 4282 * 4283 * <ul class='seealso'> 4284 * <li class='jf'>{@link #REST_classpathResourceFinder} 4285 * </ul> 4286 * 4287 * @param name The resource name. 4288 * @param locale 4289 * Optional locale. 4290 * <br>If <jk>null</jk>, won't look for localized file names. 4291 * @return An input stream of the resource, or <jk>null</jk> if the resource could not be found. 4292 * @throws IOException Thrown by underlying stream. 4293 */ 4294 public InputStream getClasspathResource(String name, Locale locale) throws IOException { 4295 return staticResourceManager.getStream(name, locale); 4296 } 4297 4298 /** 4299 * Reads the input stream from {@link #getClasspathResource(String, Locale)} into a String. 4300 * 4301 * <h5 class='section'>Example:</h5> 4302 * <p class='bcode w800'> 4303 * <jc>// A rest method that (unsafely!) returns the contents of a localized file </jc> 4304 * <jc>// from the classpath.</jc> 4305 * <ja>@RestMethod</ja>(path=<js>"/foo"</js>) 4306 * <jk>public</jk> String myMethod(RestRequest req, <ja>@Query</ja>(<js>"file"</js>) String file) { 4307 * <jk>return</jk> getContext().getClasspathResourceAsString(file, req.getLocale()); 4308 * } 4309 * </p> 4310 * 4311 * <ul class='seealso'> 4312 * <li class='jf'>{@link #REST_classpathResourceFinder} 4313 * </ul> 4314 * 4315 * @param name The resource name. 4316 * @param locale 4317 * Optional locale. 4318 * <br>If <jk>null</jk>, won't look for localized file names. 4319 * @return The contents of the stream as a string, or <jk>null</jk> if the resource could not be found. 4320 * @throws IOException If resource could not be found. 4321 */ 4322 public String getClasspathResourceAsString(String name, Locale locale) throws IOException { 4323 return staticResourceManager.getString(name, locale); 4324 } 4325 4326 4327 /** 4328 * Reads the input stream from {@link #getClasspathResource(String, Locale)} and parses it into a POJO using the parser 4329 * matched by the specified media type. 4330 * 4331 * <p> 4332 * Useful if you want to load predefined POJOs from JSON files in your classpath. 4333 * 4334 * <h5 class='section'>Example:</h5> 4335 * <p class='bcode w800'> 4336 * <jc>// A rest method that (unsafely!) returns the contents of a localized file </jc> 4337 * <jc>// from the classpath parsed as an array of beans.</jc> 4338 * <ja>@RestMethod</ja>(path=<js>"/foo"</js>) 4339 * <jk>public</jk> MyBean[] myMethod(RestRequest req, <ja>@Query</ja>(<js>"file"</js>) String file) { 4340 * <jk>return</jk> getContext().getClasspathResource(MyBean[].<jk>class</jk>, <jsf>JSON</jsf>, file, req.getLocale()); 4341 * } 4342 * </p> 4343 * 4344 * <ul class='seealso'> 4345 * <li class='jf'>{@link #REST_classpathResourceFinder} 4346 * </ul> 4347 * 4348 * @param c The class type of the POJO to create. 4349 * @param mediaType The media type of the data in the stream (e.g. <js>"text/json"</js>) 4350 * @param name The resource name (e.g. "htdocs/styles.css"). 4351 * @param locale 4352 * Optional locale. 4353 * <br>If <jk>null</jk>, won't look for localized file names. 4354 * @return The parsed resource, or <jk>null</jk> if the resource could not be found. 4355 * @throws IOException Thrown by underlying stream. 4356 * @throws ServletException If the media type was unknown or the input could not be parsed into a POJO. 4357 */ 4358 public <T> T getClasspathResource(Class<T> c, MediaType mediaType, String name, Locale locale) throws IOException, ServletException { 4359 InputStream is = getClasspathResource(name, locale); 4360 if (is == null) 4361 return null; 4362 try { 4363 Parser p = parsers.getParser(mediaType); 4364 if (p == null) { 4365 if (mediaType == MediaType.JSON) 4366 p = JsonParser.DEFAULT; 4367 if (mediaType == MediaType.XML) 4368 p = XmlParser.DEFAULT; 4369 if (mediaType == MediaType.HTML) 4370 p = HtmlParser.DEFAULT; 4371 if (mediaType == MediaType.UON) 4372 p = UonParser.DEFAULT; 4373 if (mediaType == MediaType.URLENCODING) 4374 p = UrlEncodingParser.DEFAULT; 4375 if (mediaType == MediaType.MSGPACK) 4376 p = MsgPackParser.DEFAULT; 4377 } 4378 if (p != null) { 4379 try { 4380 try (Closeable in = p.isReaderParser() ? new InputStreamReader(is, UTF8) : is) { 4381 return p.parse(in, c); 4382 } 4383 } catch (ParseException e) { 4384 throw new ServletException("Could not parse resource '"+name+" as media type '"+mediaType+"'.", e); 4385 } 4386 } 4387 throw new ServletException("Unknown media type '"+mediaType+"'"); 4388 } catch (Exception e) { 4389 throw new ServletException("Could not parse resource with name '"+name+"'", e); 4390 } 4391 } 4392 4393 /** 4394 * Returns the path for this resource as defined by the {@link Rest#path() @Rest(path)} annotation or 4395 * {@link RestContextBuilder#path(String)} method concatenated with those on all parent classes. 4396 * 4397 * <p> 4398 * If path is not specified, returns <js>""</js>. 4399 * 4400 * <ul class='seealso'> 4401 * <li class='jf'>{@link #REST_path} 4402 * </ul> 4403 * 4404 * @return The servlet path. 4405 */ 4406 public String getPath() { 4407 return fullPath; 4408 } 4409 4410 /** 4411 * Returns the logger to use for this resource. 4412 * 4413 * <div class='warn'> 4414 * <b>Deprecated</b> - Use {@link #getCallLogger()} 4415 * </div> 4416 * 4417 * <ul class='seealso'> 4418 * <li class='jf'>{@link #REST_logger} 4419 * </ul> 4420 * 4421 * @return 4422 * The logger to use for this resource. 4423 * <br>Never <jk>null</jk>. 4424 */ 4425 @Deprecated 4426 public RestLogger getLogger() { 4427 return logger; 4428 } 4429 4430 /** 4431 * Returns the call logger to use for this resource. 4432 * 4433 * <ul class='seealso'> 4434 * <li class='jf'>{@link #REST_callLogger} 4435 * </ul> 4436 * 4437 * @return 4438 * The call logger to use for this resource. 4439 * <br>Never <jk>null</jk>. 4440 */ 4441 public RestCallLogger getCallLogger() { 4442 return callLogger; 4443 } 4444 4445 /** 4446 * Returns the call logger config to use for this resource. 4447 * 4448 * <ul class='seealso'> 4449 * <li class='jf'>{@link #REST_callLoggerConfig} 4450 * </ul> 4451 * 4452 * @return 4453 * The call logger config to use for this resource. 4454 * <br>Never <jk>null</jk>. 4455 */ 4456 public RestCallLoggerConfig getCallLoggerConfig() { 4457 return callLoggerConfig; 4458 } 4459 4460 /** 4461 * Returns the resource bundle used by this resource. 4462 * 4463 * <ul class='seealso'> 4464 * <li class='jf'>{@link #REST_messages} 4465 * </ul> 4466 * 4467 * @return 4468 * The resource bundle for this resource. 4469 * <br>Never <jk>null</jk>. 4470 */ 4471 public Messages getMessages() { 4472 return msgs; 4473 } 4474 4475 /** 4476 * Returns the REST information provider used by this resource. 4477 * 4478 * <ul class='seealso'> 4479 * <li class='jf'>{@link RestContext#REST_infoProvider} 4480 * </ul> 4481 * 4482 * @return 4483 * The information provider for this resource. 4484 * <br>Never <jk>null</jk>. 4485 */ 4486 public RestInfoProvider getInfoProvider() { 4487 return infoProvider; 4488 } 4489 4490 /** 4491 * Returns the resource object. 4492 * 4493 * <p> 4494 * This is the instance of the class annotated with the {@link Rest @Rest} annotation, usually 4495 * an instance of {@link RestServlet}. 4496 * 4497 * @return 4498 * The resource object. 4499 * <br>Never <jk>null</jk>. 4500 */ 4501 public Object getResource() { 4502 return resource; 4503 } 4504 4505 /** 4506 * Returns the resource object as a {@link RestServlet}. 4507 * 4508 * @return 4509 * The resource object cast to {@link RestServlet}, or <jk>null</jk> if the resource doesn't subclass from 4510 * {@link RestServlet}. 4511 */ 4512 public RestServlet getRestServlet() { 4513 return resource instanceof RestServlet ? (RestServlet)resource : null; 4514 } 4515 4516 /** 4517 * Throws a {@link HttpException} if an exception occurred in the constructor of this object. 4518 * 4519 * @throws HttpException The initialization exception wrapped in a {@link HttpException}. 4520 */ 4521 protected void checkForInitException() throws HttpException { 4522 if (initException != null) 4523 throw initException; 4524 } 4525 4526 /** 4527 * Returns the parent resource context (if this resource was initialized from a parent). 4528 * 4529 * <p> 4530 * From this object, you can get access to the parent resource class itself using {@link #getResource()} or 4531 * {@link #getRestServlet()} 4532 * 4533 * @return The parent resource context, or <jk>null</jk> if there is no parent context. 4534 */ 4535 public RestContext getParentContext() { 4536 return parentContext; 4537 } 4538 4539 /** 4540 * Returns the class-level properties associated with this servlet. 4541 * 4542 * <p> 4543 * Properties at the class level are defined via the following: 4544 * <ul class='javatree'> 4545 * <li class='ja'>{@link Rest#properties()} 4546 * <li class='jm'>{@link RestContextBuilder#set(String, Object)} 4547 * <li class='jm'>{@link RestContextBuilder#set(Map)} 4548 * </ul> 4549 * 4550 * <ul class='notes'> 4551 * <li> 4552 * The returned {@code Map} is mutable. 4553 * <br>Therefore, subclasses are free to override or set additional initialization parameters in their {@code init()} method. 4554 * </ul> 4555 * 4556 * @return The resource properties as a {@link RestContextProperties}. 4557 */ 4558 public RestContextProperties getProperties() { 4559 return properties; 4560 } 4561 4562 /** 4563 * Returns the servlet init parameter returned by {@link ServletConfig#getInitParameter(String)}. 4564 * 4565 * @param name The init parameter name. 4566 * @return The servlet init parameter, or <jk>null</jk> if not found. 4567 */ 4568 public String getServletInitParameter(String name) { 4569 return builder.getInitParameter(name); 4570 } 4571 4572 /** 4573 * Returns the child resources associated with this servlet. 4574 * 4575 * @return 4576 * An unmodifiable map of child resources. 4577 * Keys are the {@link Rest#path() @Rest(path)} annotation defined on the child resource. 4578 */ 4579 public Map<String,RestContext> getChildResources() { 4580 return Collections.unmodifiableMap(childResources); 4581 } 4582 4583 /** 4584 * Returns the number of times this exception was thrown based on a hash of its stacktrace. 4585 * 4586 * <div class='warn'> 4587 * <b>Deprecated</b> - Not used by new logging API. 4588 * </div> 4589 * 4590 * <ul class='seealso'> 4591 * <li class='jf'>{@link RestContext#REST_useStackTraceHashes} 4592 * </ul> 4593 * 4594 * @param e The exception to check. 4595 * @return 4596 * The number of times this exception was thrown, or <c>0</c> if {@link #REST_useStackTraceHashes} 4597 * setting is not enabled. 4598 */ 4599 @Deprecated 4600 public int getStackTraceOccurrence(Throwable e) { 4601 if (! useStackTraceHashes) 4602 return 0; 4603 int h = e.hashCode(); 4604 stackTraceHashes.putIfAbsent(h, new AtomicInteger()); 4605 return stackTraceHashes.get(h).incrementAndGet(); 4606 } 4607 4608 /** 4609 * Returns whether it's safe to render stack traces in HTTP responses. 4610 * 4611 * <ul class='seealso'> 4612 * <li class='jf'>{@link RestContext#REST_useStackTraceHashes} 4613 * </ul> 4614 * 4615 * @return <jk>true</jk> if setting is enabled. 4616 */ 4617 public boolean isRenderResponseStackTraces() { 4618 return renderResponseStackTraces; 4619 } 4620 4621 /** 4622 * Returns whether it's safe to pass the HTTP body as a <js>"body"</js> GET parameter. 4623 * 4624 * <ul class='seealso'> 4625 * <li class='jf'>{@link RestContext#REST_allowBodyParam} 4626 * </ul> 4627 * 4628 * @return <jk>true</jk> if setting is enabled. 4629 */ 4630 public boolean isAllowBodyParam() { 4631 return allowBodyParam; 4632 } 4633 4634 /** 4635 * Allowed header URL parameters. 4636 * 4637 * <ul class='seealso'> 4638 * <li class='jf'>{@link RestContext#REST_allowedHeaderParams} 4639 * </ul> 4640 * 4641 * @return 4642 * The header names allowed to be passed as URL parameters. 4643 * <br>The set is case-insensitive ordered. 4644 */ 4645 public Set<String> getAllowedHeaderParams() { 4646 return allowedHeaderParams; 4647 } 4648 4649 /** 4650 * Allowed method headers. 4651 * 4652 * <ul class='seealso'> 4653 * <li class='jf'>{@link RestContext#REST_allowedMethodHeaders} 4654 * </ul> 4655 * 4656 * @return 4657 * The method names allowed to be passed as <c>X-Method</c> headers. 4658 * <br>The set is case-insensitive ordered. 4659 */ 4660 public Set<String> getAllowedMethodHeaders() { 4661 return allowedMethodHeaders; 4662 } 4663 4664 /** 4665 * Allowed method URL parameters. 4666 * 4667 * <ul class='seealso'> 4668 * <li class='jf'>{@link RestContext#REST_allowedMethodParams} 4669 * </ul> 4670 * 4671 * @return 4672 * The method names allowed to be passed as <c>method</c> URL parameters. 4673 * <br>The set is case-insensitive ordered. 4674 */ 4675 public Set<String> getAllowedMethodParams() { 4676 return allowedMethodParams; 4677 } 4678 4679 /** 4680 * Returns the debug setting on this context for the specified method. 4681 * 4682 * @param method The java method. 4683 * @return The debug setting on this context or the debug value of the servlet context if not specified for this method. 4684 */ 4685 public Enablement getDebug(Method method) { 4686 if (method == null) 4687 return null; 4688 return debugEnablement.find(method).orElse(debug); 4689 } 4690 4691 /** 4692 * Returns the name of the client version header name used by this resource. 4693 * 4694 * <ul class='seealso'> 4695 * <li class='jf'>{@link RestContext#REST_clientVersionHeader} 4696 * </ul> 4697 * 4698 * @return 4699 * The name of the client version header used by this resource. 4700 * <br>Never <jk>null</jk>. 4701 */ 4702 public String getClientVersionHeader() { 4703 return clientVersionHeader; 4704 } 4705 4706 /** 4707 * Returns the HTTP-part parser associated with this resource. 4708 * 4709 * <ul class='seealso'> 4710 * <li class='jf'>{@link RestContext#REST_partParser} 4711 * </ul> 4712 * 4713 * @return 4714 * The HTTP-part parser associated with this resource. 4715 * <br>Never <jk>null</jk>. 4716 */ 4717 public HttpPartParser getPartParser() { 4718 return partParser; 4719 } 4720 4721 /** 4722 * Returns the HTTP-part serializer associated with this resource. 4723 * 4724 * <ul class='seealso'> 4725 * <li class='jf'>{@link RestContext#REST_partSerializer} 4726 * </ul> 4727 * 4728 * @return 4729 * The HTTP-part serializer associated with this resource. 4730 * <br>Never <jk>null</jk>. 4731 */ 4732 public HttpPartSerializer getPartSerializer() { 4733 return partSerializer; 4734 } 4735 4736 /** 4737 * Returns the JSON-Schema generator associated with this resource. 4738 * 4739 * @return 4740 * The HTTP-part serializer associated with this resource. 4741 * <br>Never <jk>null</jk>. 4742 */ 4743 public JsonSchemaGenerator getJsonSchemaGenerator() { 4744 return jsonSchemaGenerator; 4745 } 4746 4747 /** 4748 * Returns the explicit list of supported accept types for this resource. 4749 * 4750 * <ul class='seealso'> 4751 * <li class='jf'>{@link RestContext#REST_serializers} 4752 * <li class='jf'>{@link RestContext#REST_produces} 4753 * </ul> 4754 * 4755 * @return 4756 * The supported <c>Accept</c> header values for this resource. 4757 * <br>Never <jk>null</jk>. 4758 */ 4759 public List<MediaType> getProduces() { 4760 return produces; 4761 } 4762 4763 /** 4764 * Returns the explicit list of supported content types for this resource. 4765 * 4766 * <ul class='seealso'> 4767 * <li class='jf'>{@link RestContext#REST_parsers} 4768 * <li class='jf'>{@link RestContext#REST_consumes} 4769 * </ul> 4770 * 4771 * @return 4772 * The supported <c>Content-Type</c> header values for this resource. 4773 * <br>Never <jk>null</jk>. 4774 */ 4775 public List<MediaType> getConsumes() { 4776 return consumes; 4777 } 4778 4779 /** 4780 * Returns the default request headers for this resource. 4781 * 4782 * <ul class='seealso'> 4783 * <li class='jf'>{@link RestContext#REST_reqHeaders} 4784 * </ul> 4785 * 4786 * @return 4787 * The default request headers for this resource. 4788 * <br>Never <jk>null</jk>. 4789 */ 4790 public Map<String,Object> getReqHeaders() { 4791 return reqHeaders; 4792 } 4793 4794 /** 4795 * Returns the default request attributes for this resource. 4796 * 4797 * <ul class='seealso'> 4798 * <li class='jf'>{@link RestContext#REST_reqAttrs} 4799 * </ul> 4800 * 4801 * @return 4802 * The default request headers for this resource. 4803 * <br>Never <jk>null</jk>. 4804 */ 4805 public OMap getReqAttrs() { 4806 return reqAttrs; 4807 } 4808 4809 /** 4810 * Returns the default response headers for this resource. 4811 * 4812 * <ul class='seealso'> 4813 * <li class='jf'>{@link RestContext#REST_resHeaders} 4814 * </ul> 4815 * 4816 * @return 4817 * The default response headers for this resource. 4818 * <br>Never <jk>null</jk>. 4819 */ 4820 public Map<String,Object> getResHeaders() { 4821 return resHeaders; 4822 } 4823 4824 /** 4825 * Returns the response handlers associated with this resource. 4826 * 4827 * <ul class='seealso'> 4828 * <li class='jf'>{@link RestContext#REST_responseHandlers} 4829 * </ul> 4830 * 4831 * @return 4832 * The response handlers associated with this resource. 4833 * <br>Never <jk>null</jk>. 4834 */ 4835 protected ResponseHandler[] getResponseHandlers() { 4836 return responseHandlers; 4837 } 4838 4839 /** 4840 * Returns the authority path of the resource. 4841 * 4842 * <ul class='seealso'> 4843 * <li class='jf'>{@link RestContext#REST_uriAuthority} 4844 * </ul> 4845 * 4846 * @return 4847 * The authority path of this resource. 4848 * <br>If not specified, returns the context path of the ascendant resource. 4849 */ 4850 public String getUriAuthority() { 4851 if (uriAuthority != null) 4852 return uriAuthority; 4853 if (parentContext != null) 4854 return parentContext.getUriAuthority(); 4855 return null; 4856 } 4857 4858 /** 4859 * Returns the context path of the resource. 4860 * 4861 * <ul class='seealso'> 4862 * <li class='jf'>{@link RestContext#REST_uriContext} 4863 * </ul> 4864 * 4865 * @return 4866 * The context path of this resource. 4867 * <br>If not specified, returns the context path of the ascendant resource. 4868 */ 4869 public String getUriContext() { 4870 if (uriContext != null) 4871 return uriContext; 4872 if (parentContext != null) 4873 return parentContext.getUriContext(); 4874 return null; 4875 } 4876 4877 /** 4878 * Returns the setting on how relative URIs should be interpreted as relative to. 4879 * 4880 * <ul class='seealso'> 4881 * <li class='jf'>{@link RestContext#REST_uriRelativity} 4882 * </ul> 4883 * 4884 * @return 4885 * The URI-resolution relativity setting value. 4886 * <br>Never <jk>null</jk>. 4887 */ 4888 public UriRelativity getUriRelativity() { 4889 return uriRelativity; 4890 } 4891 4892 /** 4893 * Returns the setting on how relative URIs should be resolved. 4894 * 4895 * <ul class='seealso'> 4896 * <li class='jf'>{@link RestContext#REST_uriResolution} 4897 * </ul> 4898 * 4899 * @return 4900 * The URI-resolution setting value. 4901 * <br>Never <jk>null</jk>. 4902 */ 4903 public UriResolution getUriResolution() { 4904 return uriResolution; 4905 } 4906 4907 /** 4908 * Returns the media type for the specified file name. 4909 * 4910 * <ul class='seealso'> 4911 * <li class='jf'>{@link RestContext#REST_mimeTypes} 4912 * </ul> 4913 * 4914 * @param name The file name. 4915 * @return The MIME-type, or <jk>null</jk> if it could not be determined. 4916 */ 4917 public String getMediaTypeForName(String name) { 4918 return mimetypesFileTypeMap.getContentType(name); 4919 } 4920 4921 /** 4922 * Returns <jk>true</jk> if the specified path refers to a static file. 4923 * 4924 * <p> 4925 * Static files are files pulled from the classpath and served up directly to the browser. 4926 * 4927 * <ul class='seealso'> 4928 * <li class='jf'>{@link RestContext#REST_staticFiles} 4929 * </ul> 4930 * 4931 * @param p The URL path remainder after the servlet match. 4932 * @return <jk>true</jk> if the specified path refers to a static file. 4933 */ 4934 public boolean isStaticFile(String p) { 4935 return pathStartsWith(p, staticFilesPaths); 4936 } 4937 4938 /** 4939 * Returns the REST Java methods defined in this resource. 4940 * 4941 * <p> 4942 * These are the methods annotated with the {@link RestMethod @RestMethod} annotation. 4943 * 4944 * @return 4945 * An unmodifiable map of Java method names to call method objects. 4946 */ 4947 public List<RestMethodContext> getMethodContexts() { 4948 return methods; 4949 } 4950 4951 /** 4952 * Gives access to the internal stack trace database. 4953 * 4954 * @return The stack trace database. 4955 */ 4956 public StackTraceDatabase getStackTraceDb() { 4957 return stackTraceDb; 4958 } 4959 4960 /** 4961 * Returns timing information on all method executions on this class. 4962 * 4963 * <p> 4964 * Timing information is maintained for any <ja>@RestResource</ja>-annotated and hook methods. 4965 * 4966 * @return A list of timing statistics ordered by average execution time descending. 4967 */ 4968 public List<MethodExecStats> getMethodExecStats() { 4969 return methodExecStats.values().stream().sorted().collect(Collectors.toList()); 4970 } 4971 4972 /** 4973 * Gives access to the internal stack trace database. 4974 * 4975 * @return The stack trace database. 4976 */ 4977 public RestContextStats getStats() { 4978 return new RestContextStats(startTime, getMethodExecStats()); 4979 } 4980 4981 /** 4982 * Returns the timing information returned by {@link #getMethodExecStats()} in a readable format. 4983 * 4984 * @return A report of all method execution times ordered by . 4985 */ 4986 public String getMethodExecStatsReport() { 4987 StringBuilder sb = new StringBuilder() 4988 .append(" Method Runs Running Errors Avg Total \n") 4989 .append("------------------------------ --------- --------- -------- ------------ -----------\n"); 4990 getMethodExecStats() 4991 .stream() 4992 .sorted(Comparator.comparingDouble(MethodExecStats::getTotalTime).reversed()) 4993 .forEach(x -> sb.append(String.format("%30s %9d %9d %9d %10dms %10dms\n", x.getMethod(), x.getRuns(), x.getRunning(), x.getErrors(), x.getAvgTime(), x.getTotalTime()))); 4994 return sb.toString(); 4995 } 4996 4997 /** 4998 * Finds the {@link RestMethodParam} instances to handle resolving objects on the calls to the specified Java method. 4999 * 5000 * @param mi The Java method being called. 5001 * @param isPreOrPost Whether this is a {@link HookEvent#PRE_CALL} or {@link HookEvent#POST_CALL}. 5002 * @param pathPattern The path pattern to match against. 5003 * @return The array of resolvers. 5004 * @throws ServletException If an annotation usage error was detected. 5005 */ 5006 protected RestMethodParam[] findParams(MethodInfo mi, boolean isPreOrPost, UrlPathPattern pathPattern) throws ServletException { 5007 5008 List<ClassInfo> pt = mi.getParamTypes(); 5009 RestMethodParam[] rp = new RestMethodParam[pt.size()]; 5010 PropertyStore ps = getPropertyStore(); 5011 5012 for (int i = 0; i < pt.size(); i++) { 5013 5014 ClassInfo t = pt.get(i); 5015 if (t.inner() != null) { 5016 Class<?> c = t.inner(); 5017 rp[i] = paramResolvers.get(c); 5018 if (rp[i] == null) 5019 rp[i] = RestParamDefaults.STANDARD_RESOLVERS.get(c); 5020 } 5021 5022 ParamInfo mpi = mi.getParam(i); 5023 5024 if (mpi.hasAnnotation(Header.class)) { 5025 rp[i] = new RestParamDefaults.HeaderObject(mpi, ps); 5026 } else if (mpi.hasAnnotation(Attr.class)) { 5027 rp[i] = new RestParamDefaults.AttributeObject(mpi, ps); 5028 } else if (mpi.hasAnnotation(Query.class)) { 5029 rp[i] = new RestParamDefaults.QueryObject(mpi, ps); 5030 } else if (mpi.hasAnnotation(FormData.class)) { 5031 rp[i] = new RestParamDefaults.FormDataObject(mpi, ps); 5032 } else if (mpi.hasAnnotation(Path.class)) { 5033 rp[i] = new RestParamDefaults.PathObject(mpi, ps, pathPattern); 5034 } else if (mpi.hasAnnotation(Body.class)) { 5035 rp[i] = new RestParamDefaults.BodyObject(mpi, ps); 5036 } else if (mpi.hasAnnotation(Request.class)) { 5037 rp[i] = new RestParamDefaults.RequestObject(mpi, ps); 5038 } else if (mpi.hasAnnotation(Response.class)) { 5039 rp[i] = new RestParamDefaults.ResponseObject(mpi, ps); 5040 } else if (mpi.hasAnnotation(ResponseHeader.class)) { 5041 rp[i] = new RestParamDefaults.ResponseHeaderObject(mpi, ps); 5042 } else if (mpi.hasAnnotation(ResponseStatus.class)) { 5043 rp[i] = new RestParamDefaults.ResponseStatusObject(t); 5044 } else if (mpi.hasAnnotation(HasFormData.class)) { 5045 rp[i] = new RestParamDefaults.HasFormDataObject(mpi); 5046 } else if (mpi.hasAnnotation(HasQuery.class)) { 5047 rp[i] = new RestParamDefaults.HasQueryObject(mpi); 5048 } else if (mpi.hasAnnotation(org.apache.juneau.rest.annotation.Method.class)) { 5049 rp[i] = new RestParamDefaults.MethodObject(mi, t, mpi); 5050 } 5051 5052 if (rp[i] == null && ! isPreOrPost) 5053 throw new RestServletException("Invalid parameter specified for method ''{0}'' at index position {1}", mi.inner(), i); 5054 } 5055 5056 return rp; 5057 } 5058 5059 5060 //------------------------------------------------------------------------------------------------------------------ 5061 // Call handling 5062 //------------------------------------------------------------------------------------------------------------------ 5063 5064 /** 5065 * Wraps an incoming servlet request/response pair into a single {@link RestCall} object. 5066 * 5067 * <p> 5068 * This is the first method called by {@link #execute(HttpServletRequest, HttpServletResponse)}. 5069 * 5070 * @param req The rest request. 5071 * @param res The rest response. 5072 * @return The wrapped request/response pair. 5073 */ 5074 protected RestCall createCall(HttpServletRequest req, HttpServletResponse res) { 5075 return new RestCall(this, req, res).logger(getCallLogger()).loggerConfig(getCallLoggerConfig()); 5076 } 5077 5078 /** 5079 * Creates a {@link RestRequest} object based on the specified incoming {@link HttpServletRequest} object. 5080 * 5081 * <p> 5082 * This method is called immediately after {@link #startCall(RestCall)} has been called. 5083 * 5084 * @param call The current REST call. 5085 * @return The wrapped request object. 5086 * @throws ServletException If any errors occur trying to interpret the request. 5087 */ 5088 public RestRequest createRequest(RestCall call) throws ServletException { 5089 return new RestRequest(call); 5090 } 5091 5092 /** 5093 * Creates a {@link RestResponse} object based on the specified incoming {@link HttpServletResponse} object 5094 * and the request returned by {@link #createRequest(RestCall)}. 5095 * 5096 * @param call The current REST call. 5097 * @return The wrapped response object. 5098 * @throws ServletException If any errors occur trying to interpret the request or response. 5099 */ 5100 public RestResponse createResponse(RestCall call) throws ServletException { 5101 return new RestResponse(call); 5102 } 5103 5104 /** 5105 * The main service method. 5106 * 5107 * <p> 5108 * Subclasses can optionally override this method if they want to tailor the behavior of requests. 5109 * 5110 * @param r1 The incoming HTTP servlet request object. 5111 * @param r2 The incoming HTTP servlet response object. 5112 * @throws ServletException General servlet exception. 5113 * @throws IOException Thrown by underlying stream. 5114 */ 5115 public void execute(HttpServletRequest r1, HttpServletResponse r2) throws ServletException, IOException { 5116 5117 RestCall call = createCall(r1, r2); 5118 5119 // Must be careful not to bleed thread-locals. 5120 if (this.call.get() != null) 5121 System.err.println("WARNING: Thread-local call object was not cleaned up from previous request. " + this + ", thread=["+Thread.currentThread().getId()+"]"); 5122 this.call.set(call); 5123 5124 try { 5125 checkForInitException(); 5126 5127 // If the resource path contains variables (e.g. @Rest(path="/f/{a}/{b}"), then we want to resolve 5128 // those variables and push the servletPath to include the resolved variables. The new pathInfo will be 5129 // the remainder after the new servletPath. 5130 // Only do this for the top-level resource because the logic for child resources are processed next. 5131 if (pathPattern.hasVars() && getParentContext() == null) { 5132 String sp = call.getServletPath(); 5133 String pi = call.getPathInfoUndecoded(); 5134 UrlPathInfo upi2 = new UrlPathInfo(pi == null ? sp : sp + pi); 5135 UrlPathPatternMatch uppm = pathPattern.match(upi2); 5136 if (uppm != null && ! uppm.hasEmptyVars()) { 5137 call.addPathVars(uppm.getVars()); 5138 call.request( 5139 new OverrideableHttpServletRequest(call.getRequest()) 5140 .pathInfo(nullIfEmpty(urlDecode(uppm.getSuffix()))) 5141 .servletPath(uppm.getPrefix()) 5142 ); 5143 } else { 5144 call.debug(isDebug(call)).status(SC_NOT_FOUND).finish(); 5145 return; 5146 } 5147 } 5148 5149 // If this resource has child resources, try to recursively call them. 5150 String pi = call.getPathInfoUndecoded(); 5151 if ((! childResources.isEmpty()) && pi != null && ! pi.equals("/")) { 5152 for (RestContext rc : getChildResources().values()) { 5153 UrlPathPattern upp = rc.pathPattern; 5154 UrlPathPatternMatch uppm = upp.match(call.getUrlPathInfo()); 5155 if (uppm != null) { 5156 if (! uppm.hasEmptyVars()) { 5157 call.addPathVars(uppm.getVars()); 5158 HttpServletRequest childRequest = new OverrideableHttpServletRequest(call.getRequest()) 5159 .pathInfo(nullIfEmpty(urlDecode(uppm.getSuffix()))) 5160 .servletPath(call.getServletPath() + uppm.getPrefix()); 5161 rc.execute(childRequest, call.getResponse()); 5162 } else { 5163 call.debug(isDebug(call)).status(SC_NOT_FOUND).finish(); 5164 } 5165 return; 5166 } 5167 } 5168 } 5169 5170 if (isDebug(call)) 5171 call.debug(true); 5172 5173 startCall(call); 5174 5175 createRequest(call); 5176 createResponse(call); 5177 5178 StaticFile r = null; 5179 if (call.getPathInfoUndecoded() != null) { 5180 String p = call.getPathInfoUndecoded().substring(1); 5181 if (isStaticFile(p)) { 5182 r = getStaticFile(p); 5183 if (! r.exists()) { 5184 call.output(null); 5185 r = null; 5186 } 5187 } else if (p.equals("favicon.ico")) { 5188 call.output(null); 5189 } 5190 } 5191 5192 if (r != null) { 5193 call.status(SC_OK); 5194 call.output(r); 5195 } else { 5196 5197 // If the specified method has been defined in a subclass, invoke it. 5198 try { 5199 findMethod(call).invoke(call); 5200 } catch (NotFound e) { 5201 if (call.getStatus() == 0) 5202 call.status(404); 5203 handleNotFound(call); 5204 } 5205 } 5206 5207 if (call.hasOutput()) { 5208 // Now serialize the output if there was any. 5209 // Some subclasses may write to the OutputStream or Writer directly. 5210 handleResponse(call); 5211 } 5212 5213 5214 } catch (Throwable e) { 5215 handleError(call, convertThrowable(e)); 5216 } finally { 5217 clearState(); 5218 } 5219 5220 call.finish(); 5221 finishCall(call); 5222 } 5223 5224 private RestMethodContext findMethod(RestCall call) throws Throwable { 5225 String m = call.getMethod(); 5226 5227 int rc = 0; 5228 if (methodMap.containsKey(m)) { 5229 for (RestMethodContext mc : methodMap.get(m)) { 5230 int mrc = mc.match(call); 5231 if (mrc == 2) 5232 return mc; 5233 rc = Math.max(rc, mrc); 5234 } 5235 } 5236 5237 if (methodMap.containsKey("*")) { 5238 for (RestMethodContext mc : methodMap.get("*")) { 5239 int mrc = mc.match(call); 5240 if (mrc == 2) 5241 return mc; 5242 rc = Math.max(rc, mrc); 5243 } 5244 } 5245 5246 // If no paths matched, see if the path matches any other methods. 5247 // Note that we don't want to match against "/*" patterns such as getOptions(). 5248 if (rc == 0) { 5249 for (RestMethodContext mc : methods) { 5250 if (! mc.getPathPattern().endsWith("/*")) { 5251 int mrc = mc.match(call); 5252 if (mrc == 2) 5253 throw new MethodNotAllowed(); 5254 } 5255 } 5256 } 5257 5258 if (rc == 1) 5259 throw new PreconditionFailed("Method ''{0}'' not found on resource on path ''{1}'' with matching matcher.", m, call.getPathInfo()); 5260 5261 throw new NotFound(); 5262 } 5263 5264 private boolean isDebug(RestCall call) { 5265 Enablement e = null; 5266 RestMethodContext mc = call.getRestMethodContext(); 5267 if (mc != null) 5268 e = mc.getDebug(); 5269 if (e == null) 5270 e = getDebug(); 5271 if (e == TRUE) 5272 return true; 5273 if (e == FALSE) 5274 return false; 5275 if (e == PER_REQUEST) 5276 return "true".equalsIgnoreCase(call.getRequest().getHeader("X-Debug")); 5277 return false; 5278 } 5279 5280 /** 5281 * The main method for serializing POJOs passed in through the {@link RestResponse#setOutput(Object)} method or 5282 * returned by the Java method. 5283 * 5284 * <p> 5285 * Subclasses may override this method if they wish to modify the way the output is rendered or support other output 5286 * formats. 5287 * 5288 * <p> 5289 * The default implementation simply iterates through the response handlers on this resource 5290 * looking for the first one whose {@link ResponseHandler#handle(RestRequest,RestResponse)} method returns 5291 * <jk>true</jk>. 5292 * 5293 * @param call The HTTP call. 5294 * @throws IOException Thrown by underlying stream. 5295 * @throws HttpException Non-200 response. 5296 * @throws NotImplemented No registered response handlers could handle the call. 5297 */ 5298 public void handleResponse(RestCall call) throws IOException, HttpException, NotImplemented { 5299 5300 RestRequest req = call.getRestRequest(); 5301 RestResponse res = call.getRestResponse(); 5302 5303 // Loop until we find the correct handler for the POJO. 5304 for (ResponseHandler h : getResponseHandlers()) 5305 if (h.handle(req, res)) 5306 return; 5307 5308 Object output = res.getOutput(); 5309 throw new NotImplemented("No response handlers found to process output of type '"+(output == null ? null : output.getClass().getName())+"'"); 5310 } 5311 5312 /** 5313 * Method that can be subclassed to allow uncaught throwables to be treated as other types of throwables. 5314 * 5315 * <p> 5316 * The default implementation looks at the throwable class name to determine whether it can be converted to another type: 5317 * 5318 * <ul> 5319 * <li><js>"*AccessDenied*"</js> - Converted to {@link Unauthorized}. 5320 * <li><js>"*Empty*"</js>,<js>"*NotFound*"</js> - Converted to {@link NotFound}. 5321 * </ul> 5322 * 5323 * @param t The thrown object. 5324 * @return The converted thrown object. 5325 */ 5326 @SuppressWarnings("deprecation") 5327 public Throwable convertThrowable(Throwable t) { 5328 5329 ClassInfo ci = ClassInfo.ofc(t); 5330 if (ci.is(InvocationTargetException.class)) { 5331 t = ((InvocationTargetException)t).getTargetException(); 5332 ci = ClassInfo.ofc(t); 5333 } 5334 5335 if (ci.is(HttpRuntimeException.class)) { 5336 t = ((HttpRuntimeException)t).getInner(); 5337 ci = ClassInfo.ofc(t); 5338 } 5339 5340 if (ci.isChildOf(RestException.class) || ci.hasAnnotation(Response.class)) 5341 return t; 5342 5343 if (t instanceof ParseException || t instanceof InvalidDataConversionException) 5344 return new BadRequest(t); 5345 5346 String n = t.getClass().getName(); 5347 5348 if (n.contains("AccessDenied") || n.contains("Unauthorized")) 5349 return new Unauthorized(t); 5350 5351 if (n.contains("Empty") || n.contains("NotFound")) 5352 return new NotFound(t); 5353 5354 return t; 5355 } 5356 5357 /** 5358 * Handle the case where a matching method was not found. 5359 * 5360 * <p> 5361 * Subclasses can override this method to provide a 2nd-chance for specifying a response. 5362 * The default implementation will simply throw an exception with an appropriate message. 5363 * 5364 * @param call The HTTP call. 5365 * @throws Exception Any exception can be thrown. 5366 */ 5367 public void handleNotFound(RestCall call) throws Exception { 5368 String pathInfo = call.getPathInfo(); 5369 String methodUC = call.getMethod(); 5370 int rc = call.getStatus(); 5371 String onPath = pathInfo == null ? " on no pathInfo" : String.format(" on path '%s'", pathInfo); 5372 if (rc == SC_NOT_FOUND) 5373 throw new NotFound("Method ''{0}'' not found on resource with matching pattern{1}.", methodUC, onPath); 5374 else if (rc == SC_PRECONDITION_FAILED) 5375 throw new PreconditionFailed("Method ''{0}'' not found on resource{1} with matching matcher.", methodUC, onPath); 5376 else if (rc == SC_METHOD_NOT_ALLOWED) 5377 throw new MethodNotAllowed("Method ''{0}'' not found on resource{1}.", methodUC, onPath); 5378 else 5379 throw new ServletException("Invalid method response: " + rc); 5380 } 5381 5382 /** 5383 * Method for handling response errors. 5384 * 5385 * <p> 5386 * Subclasses can override this method to provide their own custom error response handling. 5387 * 5388 * @param call The rest call. 5389 * @param e The exception that occurred. 5390 * @throws IOException Can be thrown if a problem occurred trying to write to the output stream. 5391 */ 5392 @SuppressWarnings("deprecation") 5393 public synchronized void handleError(RestCall call, Throwable e) throws IOException { 5394 5395 call.exception(e); 5396 5397 if (call.isDebug()) 5398 e.printStackTrace(); 5399 5400 int occurrence = getStackTraceOccurrence(e); 5401 5402 int code = 500; 5403 5404 ClassInfo ci = ClassInfo.ofc(e); 5405 Response r = ci.getLastAnnotation(Response.class); 5406 if (r != null) 5407 if (r.code().length > 0) 5408 code = r.code()[0]; 5409 5410 RestException e2 = (e instanceof RestException ? (RestException)e : new RestException(e, code)).setOccurrence(occurrence); 5411 5412 HttpServletRequest req = call.getRequest(); 5413 HttpServletResponse res = call.getResponse(); 5414 5415 Throwable t = null; 5416 if (e instanceof HttpRuntimeException) 5417 t = ((HttpRuntimeException)e).getInner(); 5418 if (t == null) 5419 t = e2.getRootCause(); 5420 if (t != null) { 5421 res.setHeader("Exception-Name", stripInvalidHttpHeaderChars(t.getClass().getName())); 5422 res.setHeader("Exception-Message", stripInvalidHttpHeaderChars(t.getMessage())); 5423 } 5424 5425 try { 5426 res.setContentType("text/plain"); 5427 res.setHeader("Content-Encoding", "identity"); 5428 res.setStatus(e2.getStatus()); 5429 5430 PrintWriter w = null; 5431 try { 5432 w = res.getWriter(); 5433 } catch (IllegalStateException x) { 5434 w = new PrintWriter(new OutputStreamWriter(res.getOutputStream(), UTF8)); 5435 } 5436 5437 try (PrintWriter w2 = w) { 5438 String httpMessage = RestUtils.getHttpResponseText(e2.getStatus()); 5439 if (httpMessage != null) 5440 w2.append("HTTP ").append(String.valueOf(e2.getStatus())).append(": ").append(httpMessage).append("\n\n"); 5441 if (isRenderResponseStackTraces()) 5442 e.printStackTrace(w2); 5443 else 5444 w2.append(e2.getFullStackMessage(true)); 5445 } 5446 5447 } catch (Exception e1) { 5448 req.setAttribute("Exception", e1); 5449 } 5450 } 5451 5452 /** 5453 * Returns the session objects for the specified request. 5454 * 5455 * <p> 5456 * The default implementation simply returns a single map containing <c>{'req':req,'res',res}</c>. 5457 * 5458 * @param call The current REST call. 5459 * @return The session objects for that request. 5460 */ 5461 public Map<String,Object> getSessionObjects(RestCall call) { 5462 Map<String,Object> m = new HashMap<>(); 5463 m.put("req", call.getRequest()); 5464 m.put("res", call.getResponse()); 5465 return m; 5466 } 5467 5468 /** 5469 * Called at the start of a request to invoke all {@link HookEvent#START_CALL} methods. 5470 * 5471 * @param call The current request. 5472 */ 5473 protected void startCall(RestCall call) { 5474 for (int i = 0; i < startCallMethods.length; i++) 5475 startOrFinish(resource, startCallMethods[i], startCallMethodParams[i], call.getRequest(), call.getResponse()); 5476 } 5477 5478 /** 5479 * Called during a request to invoke all {@link HookEvent#PRE_CALL} methods. 5480 * 5481 * @param call The current request. 5482 * @throws HttpException If thrown from call methods. 5483 */ 5484 protected void preCall(RestCall call) throws HttpException { 5485 for (int i = 0; i < preCallMethods.length; i++) 5486 preOrPost(resource, preCallMethods[i], preCallMethodParams[i], call); 5487 } 5488 5489 /** 5490 * Called during a request to invoke all {@link HookEvent#POST_CALL} methods. 5491 * 5492 * @param call The current request. 5493 * @throws HttpException If thrown from call methods. 5494 */ 5495 protected void postCall(RestCall call) throws HttpException { 5496 for (int i = 0; i < postCallMethods.length; i++) 5497 preOrPost(resource, postCallMethods[i], postCallMethodParams[i], call); 5498 } 5499 5500 private static void preOrPost(Object resource, MethodInvoker m, RestMethodParam[] mp, RestCall call) throws HttpException { 5501 if (m != null) { 5502 Object[] args = new Object[mp.length]; 5503 for (int i = 0; i < mp.length; i++) { 5504 try { 5505 args[i] = mp[i].resolve(call.getRestRequest(), call.getRestResponse()); 5506 } catch (Exception e) { 5507 throw toHttpException(e, BadRequest.class, "Invalid data conversion. Could not convert {0} ''{1}'' to type ''{2}'' on method ''{3}.{4}''.", mp[i].getParamType().name(), mp[i].getName(), mp[i].getType(), m.getDeclaringClass().getName(), m.getName()); 5508 } 5509 } 5510 try { 5511 m.invoke(resource, args); 5512 } catch (Exception e) { 5513 throw toHttpException(e, InternalServerError.class); 5514 } 5515 } 5516 } 5517 5518 /** 5519 * Called at the end of a request to invoke all {@link HookEvent#END_CALL} methods. 5520 * 5521 * <p> 5522 * This is the very last method called in {@link #execute(HttpServletRequest, HttpServletResponse)}. 5523 * 5524 * @param call The current request. 5525 */ 5526 protected void finishCall(RestCall call) { 5527 for (int i = 0; i < endCallMethods.length; i++) 5528 startOrFinish(resource, endCallMethods[i], endCallMethodParams[i], call.getRequest(), call.getResponse()); 5529 } 5530 5531 private static void startOrFinish(Object resource, MethodInvoker m, Class<?>[] p, HttpServletRequest req, HttpServletResponse res) throws HttpException, InternalServerError { 5532 if (m != null) { 5533 Object[] args = new Object[p.length]; 5534 for (int i = 0; i < p.length; i++) { 5535 if (p[i] == HttpServletRequest.class) 5536 args[i] = req; 5537 else if (p[i] == HttpServletResponse.class) 5538 args[i] = res; 5539 } 5540 try { 5541 m.invoke(resource, args); 5542 } catch (Exception e) { 5543 throw toHttpException(e, InternalServerError.class); 5544 } 5545 } 5546 } 5547 5548 /** 5549 * Called during servlet initialization to invoke all {@link HookEvent#POST_INIT} methods. 5550 * 5551 * @return This object (for method chaining). 5552 * @throws ServletException Error occurred. 5553 */ 5554 public RestContext postInit() throws ServletException { 5555 for (int i = 0; i < postInitMethods.length; i++) 5556 postInitOrDestroy(resource, postInitMethods[i], postInitMethodParams[i]); 5557 for (RestContext childContext : this.childResources.values()) 5558 childContext.postInit(); 5559 return this; 5560 } 5561 5562 /** 5563 * Called during servlet initialization to invoke all {@link HookEvent#POST_INIT_CHILD_FIRST} methods. 5564 * 5565 * @return This object (for method chaining). 5566 * @throws ServletException Error occurred. 5567 */ 5568 public RestContext postInitChildFirst() throws ServletException { 5569 for (RestContext childContext : this.childResources.values()) 5570 childContext.postInitChildFirst(); 5571 for (int i = 0; i < postInitChildFirstMethods.length; i++) 5572 postInitOrDestroy(resource, postInitChildFirstMethods[i], postInitChildFirstMethodParams[i]); 5573 return this; 5574 } 5575 5576 private void postInitOrDestroy(Object r, MethodInvoker m, Class<?>[] p) { 5577 if (m != null) { 5578 Object[] args = new Object[p.length]; 5579 for (int i = 0; i < p.length; i++) { 5580 if (p[i] == RestContext.class) 5581 args[i] = this; 5582 else if (p[i] == RestContextBuilder.class) 5583 args[i] = this.builder; 5584 else if (p[i] == ServletConfig.class) 5585 args[i] = this.builder.inner; 5586 } 5587 try { 5588 m.invoke(r, args); 5589 } catch (Exception e) { 5590 if (e instanceof RuntimeException && ClassInfo.of(e).hasAnnotation(Response.class)) 5591 throw (RuntimeException)e; 5592 throw new InternalServerError(e); 5593 } 5594 } 5595 } 5596 5597 /** 5598 * Called during servlet initialization to invoke all {@link HookEvent#DESTROY} methods. 5599 */ 5600 protected void destroy() { 5601 for (int i = 0; i < destroyMethods.length; i++) { 5602 try { 5603 postInitOrDestroy(resource, destroyMethods[i], destroyMethodParams[i]); 5604 } catch (Exception e) { 5605 e.printStackTrace(); 5606 } 5607 } 5608 5609 for (RestContext r : childResources.values()) { 5610 r.destroy(); 5611 if (r.resource instanceof Servlet) 5612 ((Servlet)r.resource).destroy(); 5613 } 5614 } 5615 5616 /** 5617 * Returns the HTTP request object for the current request. 5618 * 5619 * @return The HTTP request object, or <jk>null</jk> if it hasn't been created. 5620 */ 5621 public RestRequest getRequest() { 5622 RestCall rc = call.get(); 5623 return rc == null ? null : rc.getRestRequest(); 5624 } 5625 5626 /** 5627 * Returns the HTTP response object for the current request. 5628 * 5629 * @return The HTTP response object, or <jk>null</jk> if it hasn't been created. 5630 */ 5631 public RestResponse getResponse() { 5632 RestCall rc = call.get(); 5633 return rc == null ? null : rc.getRestResponse(); 5634 } 5635 5636 /** 5637 * If the specified object is annotated with {@link Response}, this returns the response metadata about that object. 5638 * 5639 * @param o The object to check. 5640 * @return The response metadata, or <jk>null</jk> if it wasn't annotated with {@link Response}. 5641 */ 5642 public ResponseBeanMeta getResponseBeanMeta(Object o) { 5643 if (o == null) 5644 return null; 5645 Class<?> c = o.getClass(); 5646 ResponseBeanMeta rbm = responseBeanMetas.get(c); 5647 if (rbm == null) { 5648 rbm = ResponseBeanMeta.create(c, serializers.getPropertyStore()); 5649 if (rbm == null) 5650 rbm = ResponseBeanMeta.NULL; 5651 responseBeanMetas.put(c, rbm); 5652 } 5653 if (rbm == ResponseBeanMeta.NULL) 5654 return null; 5655 return rbm; 5656 } 5657 5658 Enablement getDebug() { 5659 return debug; 5660 } 5661 5662 /** 5663 * Clear any request state information on this context. 5664 * This should always be called in a finally block in the RestServlet. 5665 */ 5666 void clearState() { 5667 call.remove(); 5668 } 5669 5670 //----------------------------------------------------------------------------------------------------------------- 5671 // Other methods. 5672 //----------------------------------------------------------------------------------------------------------------- 5673 5674 @Override /* Context */ 5675 public OMap toMap() { 5676 return super.toMap() 5677 .a("RestContext", new DefaultFilteringOMap() 5678 .a("allowBodyParam", allowBodyParam) 5679 .a("allowedMethodHeader", allowedMethodHeaders) 5680 .a("allowedMethodParams", allowedMethodParams) 5681 .a("allowedHeaderParams", allowedHeaderParams) 5682 .a("clientVersionHeader", clientVersionHeader) 5683 .a("consumes", consumes) 5684 .a("infoProvider", infoProvider) 5685 .a("logger", logger) 5686 .a("paramResolvers", paramResolvers) 5687 .a("parsers", parsers) 5688 .a("partParser", partParser) 5689 .a("partSerializer", partSerializer) 5690 .a("produces", produces) 5691 .a("properties", properties) 5692 .a("renderResponseStackTraces", renderResponseStackTraces) 5693 .a("reqHeaders", reqHeaders) 5694 .a("resHeaders", resHeaders) 5695 .a("resourceResolver", resourceResolver) 5696 .a("responseHandlers", responseHandlers) 5697 .a("serializers", serializers) 5698 .a("staticFileResponseHeaders", staticFileResponseHeaders) 5699 .a("staticFiles", staticFiles) 5700 .a("uriAuthority", uriAuthority) 5701 .a("uriContext", uriContext) 5702 .a("uriRelativity", uriRelativity) 5703 .a("uriResolution", uriResolution) 5704 .a("useClasspathResourceCaching", useClasspathResourceCaching) 5705 ); 5706 } 5707 5708 //----------------------------------------------------------------------------------------------------------------- 5709 // Helpers. 5710 //----------------------------------------------------------------------------------------------------------------- 5711 5712 static class MethodMapBuilder { 5713 TreeMap<String,TreeSet<RestMethodContext>> map = new TreeMap<>(); 5714 Set<RestMethodContext> set = ASet.of(); 5715 5716 5717 MethodMapBuilder add(String httpMethodName, RestMethodContext mc) { 5718 httpMethodName = httpMethodName.toUpperCase(); 5719 if (! map.containsKey(httpMethodName)) 5720 map.put(httpMethodName, new TreeSet<>()); 5721 map.get(httpMethodName).add(mc); 5722 set.add(mc); 5723 return this; 5724 } 5725 5726 Map<String,List<RestMethodContext>> getMap() { 5727 AMap<String,List<RestMethodContext>> m = AMap.of(); 5728 for (Map.Entry<String,TreeSet<RestMethodContext>> e : map.entrySet()) 5729 m.put(e.getKey(), AList.of(e.getValue())); 5730 return m.unmodifiable(); 5731 } 5732 5733 List<RestMethodContext> getList() { 5734 return AList.of(set).unmodifiable(); 5735 } 5736 } 5737}