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.ClassUtils.*; 017import static org.apache.juneau.internal.CollectionUtils.*; 018import static org.apache.juneau.internal.IOUtils.*; 019import static org.apache.juneau.internal.StringUtils.*; 020 021import java.io.*; 022import java.lang.reflect.*; 023import java.lang.reflect.Method; 024import java.nio.charset.*; 025import java.util.*; 026import java.util.concurrent.*; 027import java.util.concurrent.atomic.*; 028import java.util.logging.*; 029 030import javax.activation.*; 031import javax.servlet.*; 032import javax.servlet.http.*; 033 034import org.apache.juneau.*; 035import org.apache.juneau.config.*; 036import org.apache.juneau.encoders.*; 037import org.apache.juneau.html.*; 038import org.apache.juneau.http.*; 039import org.apache.juneau.http.StreamResource; 040import org.apache.juneau.http.annotation.*; 041import org.apache.juneau.http.annotation.Body; 042import org.apache.juneau.http.annotation.FormData; 043import org.apache.juneau.http.annotation.HasFormData; 044import org.apache.juneau.http.annotation.HasQuery; 045import org.apache.juneau.http.annotation.Header; 046import org.apache.juneau.http.annotation.Path; 047import org.apache.juneau.http.annotation.Query; 048import org.apache.juneau.http.annotation.Response; 049import org.apache.juneau.httppart.*; 050import org.apache.juneau.httppart.bean.*; 051import org.apache.juneau.internal.*; 052import org.apache.juneau.json.*; 053import org.apache.juneau.msgpack.*; 054import org.apache.juneau.oapi.*; 055import org.apache.juneau.parser.*; 056import org.apache.juneau.plaintext.*; 057import org.apache.juneau.remote.*; 058import org.apache.juneau.rest.annotation.*; 059import org.apache.juneau.rest.converters.*; 060import org.apache.juneau.rest.exception.*; 061import org.apache.juneau.rest.reshandlers.*; 062import org.apache.juneau.rest.util.UrlPathPattern; 063import org.apache.juneau.rest.vars.*; 064import org.apache.juneau.rest.widget.*; 065import org.apache.juneau.serializer.*; 066import org.apache.juneau.soap.*; 067import org.apache.juneau.svl.*; 068import org.apache.juneau.uon.*; 069import org.apache.juneau.urlencoding.*; 070import org.apache.juneau.utils.*; 071import org.apache.juneau.xml.*; 072import org.apache.juneau.xmlschema.XmlSchemaDocSerializer; 073 074/** 075 * Contains all the configuration on a REST resource and the entry points for handling REST calls. 076 * 077 * <h5 class='section'>See Also:</h5> 078 * <ul> 079 * <li class='link'>{@doc juneau-rest-server.RestContext} 080 * </ul> 081 */ 082public final class RestContext extends BeanContext { 083 084 //------------------------------------------------------------------------------------------------------------------- 085 // Configurable properties 086 //------------------------------------------------------------------------------------------------------------------- 087 088 private static final String PREFIX = "RestContext."; 089 090 /** 091 * Configuration property: Allow body URL parameter. 092 * 093 * <h5 class='section'>Property:</h5> 094 * <ul> 095 * <li><b>Name:</b> <js>"RestContext.allowBodyParam.b"</js> 096 * <li><b>Data type:</b> <code>Boolean</code> 097 * <li><b>Default:</b> <jk>true</jk> 098 * <li><b>Session property:</b> <jk>false</jk> 099 * <li><b>Annotations:</b> 100 * <ul> 101 * <li class='ja'>{@link RestResource#allowBodyParam()} 102 * </ul> 103 * <li><b>Methods:</b> 104 * <ul> 105 * <li class='jm'>{@link RestContextBuilder#allowBodyParam(boolean)} 106 * </ul> 107 * </ul> 108 * 109 * <h5 class='section'>Description:</h5> 110 * <p> 111 * When enabled, the HTTP body content on PUT and POST requests can be passed in as text using the <js>"body"</js> 112 * URL parameter. 113 * <br> 114 * For example: 115 * <p class='bcode w800'> 116 * ?body=(name='John%20Smith',age=45) 117 * </p> 118 * 119 * <h5 class='section'>Example:</h5> 120 * <p class='bcode w800'> 121 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 122 * <ja>@RestResource</ja>(allowBodyParam=<js>"$C{REST/allowBodyParam,false}"</js>) 123 * <jk>public class</jk> MyResource { 124 * 125 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 126 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 127 * 128 * <jc>// Using method on builder.</jc> 129 * builder.allowBodyParam(<jk>false</jk>); 130 * 131 * <jc>// Same, but using property.</jc> 132 * builder.set(<jsf>REST_allowBodyParam</jsf>, <jk>false</jk>); 133 * } 134 * 135 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 136 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 137 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 138 * builder.allowBodyParam(<jk>false</jk>); 139 * } 140 * } 141 * </p> 142 * 143 * <h5 class='section'>Notes:</h5> 144 * <ul class='spaced-list'> 145 * <li> 146 * <js>'body'</js> parameter name is case-insensitive. 147 * <li> 148 * Useful for debugging PUT and POST methods using only a browser. 149 * </ul> 150 */ 151 public static final String REST_allowBodyParam = PREFIX + "allowBodyParam.b"; 152 153 /** 154 * Configuration property: Allowed method parameters. 155 * 156 * <h5 class='section'>Property:</h5> 157 * <ul> 158 * <li><b>Name:</b> <js>"RestContext.allowedMethodParams.s"</js> 159 * <li><b>Data type:</b> <code>String</code> 160 * <li><b>Default:</b> <js>"HEAD,OPTIONS"</js> 161 * <li><b>Session property:</b> <jk>false</jk> 162 * <li><b>Annotations:</b> 163 * <ul> 164 * <li class='ja'>{@link RestResource#allowedMethodParams()} 165 * </ul> 166 * <li><b>Methods:</b> 167 * <ul> 168 * <li class='jm'>{@link RestContextBuilder#allowedMethodParams(String...)} 169 * </ul> 170 * </ul> 171 * 172 * <h5 class='section'>Description:</h5> 173 * <p> 174 * When specified, the HTTP method can be overridden by passing in a <js>"method"</js> URL parameter on a regular 175 * GET request. 176 * <br> 177 * For example: 178 * <p class='bcode w800'> 179 * ?method=OPTIONS 180 * </p> 181 * 182 * <h5 class='section'>Example:</h5> 183 * <p class='bcode w800'> 184 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 185 * <ja>@RestResource</ja>(allowMethodParams=<js>"$C{REST/allowMethodParams,HEAD\,OPTIONS\,PUT}"</js>) 186 * <jk>public class</jk> MyResource { 187 * 188 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 189 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 190 * 191 * <jc>// Using method on builder.</jc> 192 * builder.allowMethodParams(<js>"HEAD,OPTIONS,PUT"</js>); 193 * 194 * <jc>// Same, but using property.</jc> 195 * builder.set(<jsf>REST_allowMethodParams</jsf>, <js>"HEAD,OPTIONS,PUT"</js>); 196 * } 197 * 198 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 199 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 200 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 201 * builder.allowMethodParams(<js>"HEAD"</js>, <js>"OPTIONS"</js>, <js>"PUT"</js>); 202 * } 203 * } 204 * </p> 205 * 206 * <h5 class='section'>Notes:</h5> 207 * <ul class='spaced-list'> 208 * <li> 209 * Format is a comma-delimited list of HTTP method names that can be passed in as a method parameter. 210 * <li> 211 * <js>'method'</js> parameter name is case-insensitive. 212 * <li> 213 * Use <js>"*"</js> to represent all methods. 214 * </ul> 215 * 216 * <p> 217 * Note that per the {@doc RFC2616.section9 HTTP specification}, special care should 218 * be taken when allowing non-safe (POST, PUT, DELETE) methods to be invoked through GET requests. 219 */ 220 public static final String REST_allowedMethodParams = PREFIX + "allowedMethodParams.s"; 221 222 /** 223 * Configuration property: Allow header URL parameters. 224 * 225 * <h5 class='section'>Property:</h5> 226 * <ul> 227 * <li><b>Name:</b> <js>"RestContext.allowHeaderParams.b"</js> 228 * <li><b>Data type:</b> <code>Boolean</code> 229 * <li><b>Default:</b> <jk>true</jk> 230 * <li><b>Session property:</b> <jk>false</jk> 231 * <li><b>Annotations:</b> 232 * <ul> 233 * <li class='ja'>{@link RestResource#allowHeaderParams()} 234 * </ul> 235 * <li><b>Methods:</b> 236 * <ul> 237 * <li class='jm'>{@link RestContextBuilder#allowHeaderParams(boolean)} 238 * </ul> 239 * </ul> 240 * 241 * <h5 class='section'>Description:</h5> 242 * <p> 243 * When enabled, headers such as <js>"Accept"</js> and <js>"Content-Type"</js> to be passed in as URL query 244 * parameters. 245 * <br> 246 * For example: 247 * <p class='bcode w800'> 248 * ?Accept=text/json&Content-Type=text/json 249 * </p> 250 * 251 * <h5 class='section'>Example:</h5> 252 * <p class='bcode w800'> 253 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 254 * <ja>@RestResource</ja>(allowMethodParams=<js>"$C{REST/allowHeaderParams,false}"</js>) 255 * <jk>public class</jk> MyResource { 256 * 257 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 258 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 259 * 260 * <jc>// Using method on builder.</jc> 261 * builder.allowHeaderParams(<jk>false</jk>); 262 * 263 * <jc>// Same, but using property.</jc> 264 * builder.set(<jsf>REST_allowHeaderParams</jsf>, <jk>false</jk>); 265 * } 266 * 267 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 268 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 269 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 270 * builder.allowHeaderParams(<jk>false</jk>); 271 * } 272 * } 273 * </p> 274 * 275 * <h5 class='section'>Notes:</h5> 276 * <ul class='spaced-list'> 277 * <li> 278 * Header names are case-insensitive. 279 * <li> 280 * Useful for debugging REST interface using only a browser. 281 * </ul> 282 */ 283 public static final String REST_allowHeaderParams = PREFIX + "allowHeaderParams.b"; 284 285 /** 286 * Configuration property: REST call handler. 287 * 288 * <h5 class='section'>Property:</h5> 289 * <ul> 290 * <li><b>Name:</b> <js>"RestContext.callHandler.o"</js> 291 * <li><b>Data type:</b> {@link RestCallHandler} | <code>Class<? <jk>extends</jk> {@link RestCallHandler}></code> 292 * <li><b>Default:</b> {@link BasicRestCallHandler} 293 * <li><b>Session property:</b> <jk>false</jk> 294 * <li><b>Annotations:</b> 295 * <ul> 296 * <li class='ja'>{@link RestResource#callHandler()} 297 * </ul> 298 * <li><b>Methods:</b> 299 * <ul> 300 * <li class='jm'>{@link RestContextBuilder#callHandler(Class)} 301 * <li class='jm'>{@link RestContextBuilder#callHandler(RestCallHandler)} 302 * </ul> 303 * </ul> 304 * 305 * <h5 class='section'>Description:</h5> 306 * <p> 307 * This class handles the basic lifecycle of an HTTP REST call. 308 * <br>Subclasses can be used to customize how these HTTP calls are handled. 309 * 310 * <h5 class='section'>Example:</h5> 311 * <p class='bcode w800'> 312 * <jc>// Our customized call handler.</jc> 313 * <jk>public class</jk> MyRestCallHandler <jk>extends</jk> BasicRestCallHandler { 314 * 315 * <jc>// Must provide this constructor!</jc> 316 * <jk>public</jk> MyRestCallHandler(RestContext context) { 317 * <jk>super</jk>(context); 318 * } 319 * 320 * <ja>@Override</ja> 321 * <jk>public</jk> RestRequest createRequest(HttpServletRequest req) <jk>throws</jk> ServletException { 322 * <jc>// Low-level handling of requests.</jc> 323 * ... 324 * } 325 * 326 * <ja>@Override</ja> 327 * <jk>public void</jk> handleResponse(RestRequest req, RestResponse res, Object output) <jk>throws</jk> IOException, RestException { 328 * <jc>// Low-level handling of responses.</jc> 329 * ... 330 * } 331 * 332 * <ja>@Override</ja> 333 * <jk>public void</jk> handleNotFound(int rc, RestRequest req, RestResponse res) <jk>throws</jk> Exception { 334 * <jc>// Low-level handling of various error conditions.</jc> 335 * ... 336 * } 337 * } 338 * 339 * <jc>// Option #1 - Registered via annotation resolving to a config file setting with default value.</jc> 340 * <ja>@RestResource</ja>(callHandler=MyRestCallHandler.<jk>class</jk>) 341 * <jk>public class</jk> MyResource { 342 * 343 * <jc>// Option #2 - Registered via builder passed in through resource constructor.</jc> 344 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 345 * 346 * <jc>// Using method on builder.</jc> 347 * builder.callHandler(MyRestCallHandler.<jk>class</jk>); 348 * 349 * <jc>// Same, but using property.</jc> 350 * builder.set(<jsf>REST_callHandler</jsf>, MyRestCallHandler.<jk>class</jk>); 351 * } 352 * 353 * <jc>// Option #3 - Registered via builder passed in through init method.</jc> 354 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 355 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 356 * builder.callHandler(MyRestCallHandler.<jk>class</jk>); 357 * } 358 * } 359 * </p> 360 * 361 * <h5 class='section'>Notes:</h5> 362 * <ul class='spaced-list'> 363 * <li> 364 * When defined as a class, the implementation must have one of the following constructors: 365 * <ul> 366 * <li><code><jk>public</jk> T(RestContext)</code> 367 * <li><code><jk>public</jk> T()</code> 368 * </ul> 369 * <li> 370 * Inner classes of the REST resource class are allowed. 371 * </ul> 372 */ 373 public static final String REST_callHandler = PREFIX + "callHandler.o"; 374 375 /** 376 * Configuration property: Children. 377 * 378 * <h5 class='section'>Property:</h5> 379 * <ul> 380 * <li><b>Name:</b> <js>"RestContext.children.lo"</js> 381 * <li><b>Data type:</b> <code>List<Class | Object | {@link RestChild}></code> 382 * <li><b>Default:</b> empty list 383 * <li><b>Session property:</b> <jk>false</jk> 384 * <li><b>Annotations:</b> 385 * <ul> 386 * <li class='ja'>{@link RestResource#children()} 387 * </ul> 388 * <li><b>Methods:</b> 389 * <ul> 390 * <li class='jm'>{@link RestContextBuilder#child(String,Object)} 391 * <li class='jm'>{@link RestContextBuilder#children(Class...)} 392 * <li class='jm'>{@link RestContextBuilder#children(Object...)} 393 * </ul> 394 * </ul> 395 * 396 * <h5 class='section'>Description:</h5> 397 * <p> 398 * Defines children of this resource. 399 * 400 * <p> 401 * A REST child resource is simply another servlet or object that is initialized as part of the ascendant resource and has a 402 * servlet path directly under the ascendant resource object path. 403 * <br>The main advantage to defining servlets as REST children is that you do not need to define them in the 404 * <code>web.xml</code> file of the web application. 405 * <br>This can cut down on the number of entries that show up in the <code>web.xml</code> file if you are defining 406 * large numbers of servlets. 407 * 408 * <p> 409 * Child resources must specify a value for {@link RestResource#path() @RestResource(path)} that identifies the subpath of the child resource 410 * relative to the ascendant path UNLESS you use the {@link RestContextBuilder#child(String, Object)} method to register it. 411 * 412 * <p> 413 * Child resources can be nested arbitrarily deep using this technique (i.e. children can also have children). 414 * 415 * <dl> 416 * <dt>Servlet initialization:</dt> 417 * <dd> 418 * <p> 419 * A child resource will be initialized immediately after the ascendant servlet/resource is initialized. 420 * <br>The child resource receives the same servlet config as the ascendant servlet/resource. 421 * <br>This allows configuration information such as servlet initialization parameters to filter to child 422 * resources. 423 * </p> 424 * </dd> 425 * <dt>Runtime behavior:</dt> 426 * <dd> 427 * <p> 428 * As a rule, methods defined on the <code>HttpServletRequest</code> object will behave as if the child 429 * servlet were deployed as a top-level resource under the child's servlet path. 430 * <br>For example, the <code>getServletPath()</code> and <code>getPathInfo()</code> methods on the 431 * <code>HttpServletRequest</code> object will behave as if the child resource were deployed using the 432 * child's servlet path. 433 * <br>Therefore, the runtime behavior should be equivalent to deploying the child servlet in the 434 * <code>web.xml</code> file of the web application. 435 * </p> 436 * </dd> 437 * </dl> 438 * 439 * <h5 class='section'>Example:</h5> 440 * <p class='bcode w800'> 441 * <jc>// Our child resource.</jc> 442 * <ja>@RestResource</ja>(path=<js>"/child"</js>) 443 * <jk>public class</jk> MyChildResource {...} 444 * 445 * <jc>// Option #1 - Registered via annotation.</jc> 446 * <ja>@RestResource</ja>(children={MyChildResource.<jk>class</jk>}) 447 * <jk>public class</jk> MyResource { 448 * 449 * <jc>// Option #2 - Registered via builder passed in through resource constructor.</jc> 450 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 451 * 452 * <jc>// Using method on builder.</jc> 453 * builder.children(MyChildResource.<jk>class</jk>); 454 * 455 * <jc>// Same, but using property.</jc> 456 * builder.addTo(<jsf>REST_children</jsf>, MyChildResource.<jk>class</jk>)); 457 * 458 * <jc>// Use a pre-instantiated object instead.</jc> 459 * builder.child(<js>"/child"</js>, <jk>new</jk> MyChildResource()); 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.children(MyChildResource.<jk>class</jk>); 466 * } 467 * } 468 * 469 * <h5 class='section'>Notes:</h5> 470 * <ul class='spaced-list'> 471 * <li> 472 * When defined as classes, instances are resolved using the registered {@link #REST_resourceResolver} which 473 * by default is {@link BasicRestResourceResolver} which requires the class have one of the following 474 * constructors: 475 * <ul> 476 * <li><code><jk>public</jk> T(RestContextBuilder)</code> 477 * <li><code><jk>public</jk> T()</code> 478 * </ul> 479 * </ul> 480 * 481 * <h5 class='section'>See Also:</h5> 482 * <ul> 483 * <li class='link'>{@doc juneau-rest-server.Instantiation.Children} 484 * </ul> 485 */ 486 public static final String REST_children = PREFIX + "children.lo"; 487 488 /** 489 * Configuration property: Classpath resource finder. 490 * 491 * <h5 class='section'>Property:</h5> 492 * <ul> 493 * <li><b>Name:</b> <js>"RestContext.classpathResourceFinder.o"</js> 494 * <li><b>Data type:</b> {@link ClasspathResourceFinder} 495 * <li><b>Default:</b> {@link ClasspathResourceFinderBasic} 496 * <li><b>Session property:</b> <jk>false</jk> 497 * <li><b>Annotations:</b> 498 * <ul> 499 * <li class='ja'>{@link RestResource#classpathResourceFinder()} 500 * </ul> 501 * <li><b>Methods:</b> 502 * <ul> 503 * <li class='jm'>{@link RestContextBuilder#classpathResourceFinder(Class)} 504 * <li class='jm'>{@link RestContextBuilder#classpathResourceFinder(ClasspathResourceFinder)} 505 * </ul> 506 * </ul> 507 * 508 * <h5 class='section'>Description:</h5> 509 * <p> 510 * Used to retrieve localized files from the classpath. 511 * 512 * <p> 513 * Used by the following methods: 514 * <ul> 515 * <li class='jc'>{@link RestContext} 516 * <ul> 517 * <li class='jm'>{@link #getClasspathResource(String,Locale) getClasspathResource(String,Locale)} 518 * <li class='jm'>{@link #getClasspathResource(Class,String,Locale) getClasspathResource(Class,String,Locale)} 519 * <li class='jm'>{@link #getClasspathResource(Class,MediaType,String,Locale) getClasspathResource(Class,MediaType,String,Locale)} 520 * <li class='jm'>{@link #getClasspathResource(Class,Class,MediaType,String,Locale) getClasspathResource(Class,Class,MediaType,String,Locale)} 521 * <li class='jm'>{@link #getClasspathResourceAsString(String,Locale) getClasspathResourceAsString(String,Locale)} 522 * <li class='jm'>{@link #getClasspathResourceAsString(Class,String,Locale) getClasspathResourceAsString(Class,String,Locale)} 523 * <li class='jm'>{@link #resolveStaticFile(String) resolveStaticFile(String)} 524 * </ul> 525 * <li class='jc'>{@link RestRequest} 526 * <ul> 527 * <li class='jm'>{@link RestRequest#getClasspathReaderResource(String) getClasspathReaderResource(String)} 528 * <li class='jm'>{@link RestRequest#getClasspathReaderResource(String,boolean) getClasspathReaderResource(String,boolean)} 529 * <li class='jm'>{@link RestRequest#getClasspathReaderResource(String,boolean,MediaType,boolean) getClasspathReaderResource(String,boolean,MediaType,boolean)} 530 * </ul> 531 * </ul> 532 * 533 * <p> 534 * It also affects the behavior of the {@link #REST_staticFiles} property. 535 * 536 * <h5 class='section'>Example:</h5> 537 * <p class='bcode w800'> 538 * <jc>// Our customized classpath resource finder.</jc> 539 * <jk>public class</jk> MyClasspathResourceFinder <jk>extends</jk> ClasspathResourceFinderBasic { 540 * <ja>@Override</ja> 541 * <jk>public</jk> InputStream findResource(Class<?> baseClass, String name, Locale locale) <jk>throws</jk> IOException { 542 * <jc>// Do your own resolution.</jc> 543 * } 544 * } 545 * 546 * <jc>// Option #1 - Registered via annotation.</jc> 547 * <ja>@RestResource</ja>(classpathResourceFinder=MyClasspathResourceFinder.<jk>class</jk>) 548 * <jk>public class</jk> MyResource { 549 * 550 * <jc>// Option #2 - Registered via builder passed in through resource constructor.</jc> 551 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 552 * 553 * <jc>// Using method on builder.</jc> 554 * builder.classpathResourceFinder(MyClasspathResourceFinder.<jk>class</jk>); 555 * 556 * <jc>// Same, but using property.</jc> 557 * builder.set(<jsf>REST_classpathResourceFinder</jsf>, MyClasspathResourceFinder.<jk>class</jk>)); 558 * 559 * <jc>// Use a pre-instantiated object instead.</jc> 560 * builder.classpathResourceFinder(<jk>new</jk> MyClasspathResourceFinder()); 561 * } 562 * 563 * <jc>// Option #3 - Registered via builder passed in through init method.</jc> 564 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 565 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 566 * builder.classpathResourceFinder(MyClasspathResourceFinder.<jk>class</jk>); 567 * } 568 * } 569 * 570 * <h5 class='section'>Notes:</h5> 571 * <ul class='spaced-list'> 572 * <li> 573 * The default value is {@link ClasspathResourceFinderBasic} which provides basic support for finding localized 574 * resources on the classpath and JVM working directory. 575 * <br>The {@link ClasspathResourceFinderRecursive} is another option that also recursively searches for resources 576 * up the class-hierarchy. 577 * <br>Each of these classes can be extended to provide customized handling of resource retrieval. 578 * <li> 579 * When defined as a class, the implementation must have one of the following constructors: 580 * <ul> 581 * <li><code><jk>public</jk> T(RestContext)</code> 582 * <li><code><jk>public</jk> T()</code> 583 * </ul> 584 * <li> 585 * Inner classes of the REST resource class are allowed. 586 * </ul> 587 */ 588 public static final String REST_classpathResourceFinder = PREFIX + "classpathResourceFinder.o"; 589 590 /** 591 * Configuration property: Client version header. 592 * 593 * <h5 class='section'>Property:</h5> 594 * <ul> 595 * <li><b>Name:</b> <js>"RestContext.clientVersionHeader.s"</js> 596 * <li><b>Data type:</b> <code>String</code> 597 * <li><b>Default:</b> <js>"X-Client-Version"</js> 598 * <li><b>Session property:</b> <jk>false</jk> 599 * <li><b>Annotations:</b> 600 * <ul> 601 * <li class='ja'>{@link RestResource#clientVersionHeader()} 602 * </ul> 603 * <li><b>Methods:</b> 604 * <ul> 605 * <li class='jm'>{@link RestContextBuilder#clientVersionHeader(String)} 606 * </ul> 607 * </ul> 608 * 609 * <h5 class='section'>Description:</h5> 610 * <p> 611 * Specifies the name of the header used to denote the client version on HTTP requests. 612 * 613 * <p> 614 * The client version is used to support backwards compatibility for breaking REST interface changes. 615 * <br>Used in conjunction with {@link RestMethod#clientVersion() @RestMethod(clientVersion)} annotation. 616 * 617 * <h5 class='section'>Example:</h5> 618 * <p class='bcode w800'> 619 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 620 * <ja>@RestResource</ja>(clientVersionHeader=<js>"$C{REST/clientVersionHeader,Client-Version}"</js>) 621 * <jk>public class</jk> MyResource { 622 * 623 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 624 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 625 * 626 * <jc>// Using method on builder.</jc> 627 * builder.clientVersionHeader(<js>"Client-Version"</js>); 628 * 629 * <jc>// Same, but using property.</jc> 630 * builder.set(<jsf>REST_clientVersionHeader</jsf>, <js>"Client-Version"</js>); 631 * } 632 * 633 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 634 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 635 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 636 * builder.clientVersionHeader(<js>"Client-Version"</js>); 637 * } 638 * } 639 * </p> 640 * <p class='bcode w800'> 641 * <jc>// Call this method if Client-Version is at least 2.0. 642 * // Note that this also matches 2.0.1.</jc> 643 * <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/foobar"</js>, clientVersion=<js>"2.0"</js>) 644 * <jk>public</jk> Object method1() { 645 * ... 646 * } 647 * 648 * <jc>// Call this method if Client-Version is at least 1.1, but less than 2.0.</jc> 649 * <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/foobar"</js>, clientVersion=<js>"[1.1,2.0)"</js>) 650 * <jk>public</jk> Object method2() { 651 * ... 652 * } 653 * 654 * <jc>// Call this method if Client-Version is less than 1.1.</jc> 655 * <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/foobar"</js>, clientVersion=<js>"[0,1.1)"</js>) 656 * <jk>public</jk> Object method3() { 657 * ... 658 * } 659 * </p> 660 */ 661 public static final String REST_clientVersionHeader = PREFIX + "clientVersionHeader.s"; 662 663 /** 664 * Configuration property: Class-level response converters. 665 * 666 * <h5 class='section'>Property:</h5> 667 * <ul> 668 * <li><b>Name:</b> <js>"RestContext.converters.lo"</js> 669 * <li><b>Data type:</b> <code>List<{@link RestConverter} | Class<? <jk>extends</jk> {@link RestConverter}>></code> 670 * <li><b>Default:</b> empty list 671 * <li><b>Session property:</b> <jk>false</jk> 672 * <li><b>Annotations:</b> 673 * <ul> 674 * <li class='ja'>{@link RestResource#converters()} 675 * <li class='ja'>{@link RestMethod#converters()} 676 * </ul> 677 * <li><b>Methods:</b> 678 * <ul> 679 * <li class='jm'>{@link RestContextBuilder#converters(Class...)} 680 * <li class='jm'>{@link RestContextBuilder#converters(RestConverter...)} 681 * </ul> 682 * </ul> 683 * 684 * <h5 class='section'>Description:</h5> 685 * <p> 686 * Associates one or more {@link RestConverter converters} with a resource class. 687 * <br>These converters get called immediately after execution of the REST method in the same order specified in the 688 * annotation. 689 * <br>The object passed into this converter is the object returned from the Java method or passed into 690 * the {@link RestResponse#setOutput(Object)} method. 691 * 692 * <p> 693 * Can be used for performing post-processing on the response object before serialization. 694 * 695 * <p> 696 * When multiple converters are specified, they're executed in the order they're specified in the annotation 697 * (e.g. first the results will be traversed, then the resulting node will be searched/sorted). 698 * 699 * <h5 class='section'>Example:</h5> 700 * <p class='bcode w800'> 701 * <jc>// Our converter.</jc> 702 * <jk>public class</jk> MyConverter <jk>implements</jk> RestConverter { 703 * <ja>@Override</ja> 704 * <jk>public</jk> Object convert(RestRequest req, Object o) { 705 * <jc>// Do something with object and return another object.</jc> 706 * <jc>// Or just return the same object for a no-op.</jc> 707 * } 708 * } 709 * 710 * <jc>// Option #1 - Registered via annotation resolving to a config file setting with default value.</jc> 711 * <ja>@RestResource</ja>(converters={MyConverter.<jk>class</jk>}) 712 * <jk>public class</jk> MyResource { 713 * 714 * <jc>// Option #2 - Registered via builder passed in through resource constructor.</jc> 715 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 716 * 717 * <jc>// Using method on builder.</jc> 718 * builder.converters(MyConverter.<jk>class</jk>); 719 * 720 * <jc>// Same, but using property.</jc> 721 * builder.set(<jsf>REST_converters</jsf>, MyConverter.<jk>class</jk>); 722 * 723 * <jc>// Pass in an instance instead.</jc> 724 * builder.converters(<jk>new</jk> MyConverter()); 725 * } 726 * 727 * <jc>// Option #3 - Registered via builder passed in through init method.</jc> 728 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 729 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 730 * builder.converters(MyConverter.<jk>class</jk>); 731 * } 732 * } 733 * </p> 734 * 735 * <h5 class='section'>See Also:</h5> 736 * <ul> 737 * <li class='jc'>{@link Traversable} - Allows URL additional path info to address individual elements in a POJO tree. 738 * <li class='jc'>{@link Queryable} - Allows query/view/sort functions to be performed on POJOs. 739 * <li class='jc'>{@link Introspectable} - Allows Java public methods to be invoked on the returned POJOs. 740 * </ul> 741 * 742 * <h5 class='section'>See Also:</h5> 743 * <ul> 744 * <li class='link'>{@doc juneau-rest-server.Converters} 745 * </ul> 746 * 747 * <h5 class='section'>Notes:</h5> 748 * <ul class='spaced-list'> 749 * <li> 750 * When defined as a class, the implementation must have one of the following constructors: 751 * <ul> 752 * <li><code><jk>public</jk> T(BeanContext)</code> 753 * <li><code><jk>public</jk> T()</code> 754 * </ul> 755 * <li> 756 * Inner classes of the REST resource class are allowed. 757 * </ul> 758 */ 759 public static final String REST_converters = PREFIX + "converters.lo"; 760 761 /** 762 * Configuration property: Debug mode. 763 * 764 * <h5 class='section'>Property:</h5> 765 * <ul> 766 * <li><b>Name:</b> <js>"RestContext.debug.b"</js> 767 * <li><b>Data type:</b> <code>Boolean</code> 768 * <li><b>Default:</b> <jk>false</jk> 769 * <li><b>Session property:</b> <jk>false</jk> 770 * <li><b>Annotations:</b> 771 * <ul> 772 * <li class='ja'>{@link RestResource#debug()} 773 * </ul> 774 * <li><b>Methods:</b> 775 * <ul> 776 * <li class='jm'>{@link RestContextBuilder#debug(boolean)} 777 * </ul> 778 * </ul> 779 * 780 * <h5 class='section'>Description:</h5> 781 * <p> 782 * Enables the following: 783 * <ul> 784 * <li>A message and stack trace is printed to STDERR when {@link BasicRestCallHandler#handleError(HttpServletRequest, HttpServletResponse, Throwable)} is called. 785 * </ul> 786 */ 787 public static final String REST_debug = PREFIX + "debug.b"; 788 789 /** 790 * Configuration property: Default character encoding. 791 * 792 * <h5 class='section'>Property:</h5> 793 * <ul> 794 * <li><b>Name:</b> <js>"RestContext.defaultCharset.s"</js> 795 * <li><b>Data type:</b> <code>String</code> 796 * <li><b>Default:</b> <js>"utf-8"</js> 797 * <li><b>Session property:</b> <jk>false</jk> 798 * <li><b>Annotations:</b> 799 * <ul> 800 * <li class='ja'>{@link RestResource#defaultCharset()} 801 * <li class='ja'>{@link RestMethod#defaultCharset()} 802 * </ul> 803 * <li><b>Methods:</b> 804 * <ul> 805 * <li class='jm'>{@link RestContextBuilder#defaultCharset(String)} 806 * <li class='jm'>{@link RestContextBuilder#defaultCharset(Charset)} 807 * </ul> 808 * </ul> 809 * 810 * <h5 class='section'>Description:</h5> 811 * <p> 812 * The default character encoding for the request and response if not specified on the request. 813 * 814 * <h5 class='section'>Example:</h5> 815 * <p class='bcode w800'> 816 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 817 * <ja>@RestResource</ja>(defaultCharset=<js>"$C{REST/defaultCharset,US-ASCII}"</js>) 818 * <jk>public class</jk> MyResource { 819 * 820 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 821 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 822 * 823 * <jc>// Using method on builder.</jc> 824 * builder.defaultCharset(<js>"US-ASCII"</js>); 825 * 826 * <jc>// Same, but using property.</jc> 827 * builder.set(<jsf>REST_defaultCharset</jsf>, <js>"US-ASCII"</js>); 828 * } 829 * 830 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 831 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 832 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 833 * builder.defaultCharset(<js>"US-ASCII"</js>); 834 * } 835 * 836 * <jc>// Override at the method level.</jc> 837 * <ja>@RestMethod</ja>(defaultCharset=<js>"UTF-16"</js>) 838 * public Object myMethod() {...} 839 * } 840 * </p> 841 */ 842 public static final String REST_defaultCharset = PREFIX + "defaultCharset.s"; 843 844 /** 845 * Configuration property: Default request headers. 846 * 847 * <h5 class='section'>Property:</h5> 848 * <ul> 849 * <li><b>Name:</b> <js>"RestContext.defaultRequestHeaders.smo"</js> 850 * <li><b>Data type:</b> <code>Map<String,String></code> 851 * <li><b>Default:</b> empty map 852 * <li><b>Session property:</b> <jk>false</jk> 853 * <li><b>Annotations:</b> 854 * <ul> 855 * <li class='ja'>{@link RestResource#defaultRequestHeaders()} 856 * <li class='ja'>{@link RestMethod#defaultRequestHeaders()} 857 * </ul> 858 * <li><b>Methods:</b> 859 * <ul> 860 * <li class='jm'>{@link RestContextBuilder#defaultRequestHeader(String,Object)} 861 * <li class='jm'>{@link RestContextBuilder#defaultRequestHeaders(String...)} 862 * </ul> 863 * </ul> 864 * 865 * <h5 class='section'>Description:</h5> 866 * <p> 867 * Specifies default values for request headers if they're not passed in through the request. 868 * 869 * <h5 class='section'>Notes:</h5> 870 * <ul class='spaced-list'> 871 * <li> 872 * Strings are in the format <js>"Header-Name: header-value"</js>. 873 * <li> 874 * Affects values returned by {@link RestRequest#getHeader(String)} when the header is not present on the request. 875 * <li> 876 * The most useful reason for this annotation is to provide a default <code>Accept</code> header when one is not 877 * specified so that a particular default {@link Serializer} is picked. 878 * </ul> 879 * 880 * <h5 class='section'>Example:</h5> 881 * <p class='bcode w800'> 882 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 883 * <ja>@RestResource</ja>(defaultRequestHeaders={<js>"Accept: application/json"</js>, <js>"My-Header: $C{REST/myHeaderValue}"</js>}) 884 * <jk>public class</jk> MyResource { 885 * 886 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 887 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 888 * 889 * <jc>// Using method on builder.</jc> 890 * builder 891 * .defaultRequestHeader(<js>"Accept"</js>, <js>"application/json"</js>); 892 * .defaultRequestHeaders(<js>"My-Header: foo"</js>); 893 * 894 * <jc>// Same, but using property.</jc> 895 * builder.addTo(<jsf>REST_defaultRequestHeaders</jsf>, <js>"Accept"</js>, <js>"application/json"</js>); 896 * } 897 * 898 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 899 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 900 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 901 * builder.defaultRequestHeader(<js>"Accept"</js>, <js>"application/json"</js>); 902 * } 903 * 904 * <jc>// Override at the method level.</jc> 905 * <ja>@RestMethod</ja>(defaultRequestHeaders={<js>"Accept: text/xml"</js>}) 906 * public Object myMethod() {...} 907 * } 908 * </p> 909 */ 910 public static final String REST_defaultRequestHeaders = PREFIX + "defaultRequestHeaders.smo"; 911 912 /** 913 * Configuration property: Default response headers. 914 * 915 * <h5 class='section'>Property:</h5> 916 * <ul> 917 * <li><b>Name:</b> <js>"RestContext.defaultResponseHeaders.omo"</js> 918 * <li><b>Data type:</b> <code>Map<String,String></code> 919 * <li><b>Default:</b> empty map 920 * <li><b>Session property:</b> <jk>false</jk> 921 * <li><b>Annotations:</b> 922 * <ul> 923 * <li class='ja'>{@link RestResource#defaultResponseHeaders()} 924 * </ul> 925 * <li><b>Methods:</b> 926 * <ul> 927 * <li class='jm'>{@link RestContextBuilder#defaultResponseHeader(String,Object)} 928 * <li class='jm'>{@link RestContextBuilder#defaultResponseHeaders(String...)} 929 * </ul> 930 * </ul> 931 * 932 * <h5 class='section'>Description:</h5> 933 * <p> 934 * Specifies default values for response headers if they're not set after the Java REST method is called. 935 * 936 * <h5 class='section'>Notes:</h5> 937 * <ul class='spaced-list'> 938 * <li> 939 * Strings are in the format <js>"Header-Name: header-value"</js>. 940 * <li> 941 * This is equivalent to calling {@link RestResponse#setHeader(String, String)} programmatically in each of 942 * the Java methods. 943 * <li> 944 * The header value will not be set if the header value has already been specified (hence the 'default' in the name). 945 * </ul> 946 * 947 * <h5 class='section'>Example:</h5> 948 * <p class='bcode w800'> 949 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 950 * <ja>@RestResource</ja>(defaultResponseHeaders={<js>"Content-Type: $C{REST/defaultContentType,text/plain}"</js>,<js>"My-Header: $C{REST/myHeaderValue}"</js>}) 951 * <jk>public class</jk> MyResource { 952 * 953 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 954 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 955 * 956 * <jc>// Using method on builder.</jc> 957 * builder 958 * .defaultResponseHeader(<js>"Content-Type"</js>, <js>"text/plain"</js>); 959 * .defaultResponseHeaders(<js>"My-Header: foo"</js>); 960 * 961 * <jc>// Same, but using property.</jc> 962 * builder 963 * .addTo(<jsf>REST_defaultRequestHeaders</jsf>, <js>"Accept"</js>, <js>"application/json"</js>); 964 * .addTo(<jsf>REST_defaultRequestHeaders</jsf>, <js>"My-Header"</js>, <js>"foo"</js>); 965 * } 966 * 967 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 968 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 969 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 970 * builder.defaultResponseHeader(<js>"Content-Type"</js>, <js>"text/plain"</js>); 971 * } 972 * } 973 * </p> 974 */ 975 public static final String REST_defaultResponseHeaders = PREFIX + "defaultResponseHeaders.omo"; 976 977 /** 978 * Configuration property: Compression encoders. 979 * 980 * <h5 class='section'>Property:</h5> 981 * <ul> 982 * <li><b>Name:</b> <js>"RestContext.encoders.o"</js> 983 * <li><b>Data type:</b> <code>List<{@link Encoder} | Class<? <jk>extends</jk> {@link Encoder}>></code> 984 * <li><b>Default:</b> empty list 985 * <li><b>Session property:</b> <jk>false</jk> 986 * <li><b>Annotations:</b> 987 * <ul> 988 * <li class='ja'>{@link RestResource#encoders()} 989 * <li class='ja'>{@link RestMethod#encoders()} 990 * </ul> 991 * <li><b>Methods:</b> 992 * <ul> 993 * <li class='jm'>{@link RestContextBuilder#encoders(Class...)} 994 * <li class='jm'>{@link RestContextBuilder#encoders(Encoder...)} 995 * </ul> 996 * </ul> 997 * 998 * <h5 class='section'>Description:</h5> 999 * <p> 1000 * These can be used to enable various kinds of compression (e.g. <js>"gzip"</js>) on requests and responses. 1001 * 1002 * <h5 class='section'>Example:</h5> 1003 * <p class='bcode w800'> 1004 * <jc>// Option #1 - Registered via annotation.</jc> 1005 * <ja>@RestResource</ja>(encoders={GzipEncoder.<jk>class</jk>}) 1006 * <jk>public class</jk> MyResource { 1007 * 1008 * <jc>// Option #2 - Registered via builder passed in through resource constructor.</jc> 1009 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 1010 * 1011 * <jc>// Using method on builder.</jc> 1012 * builder.encoders(GzipEncoder.<jk>class</jk>); 1013 * 1014 * <jc>// Same, but using property.</jc> 1015 * builder.addTo(<jsf>REST_encoders</jsf>, GzipEncoder.<jk>class</jk>); 1016 * } 1017 * 1018 * <jc>// Option #3 - Registered via builder passed in through init method.</jc> 1019 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 1020 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 1021 * builder.encoders(GzipEncoder.<jk>class</jk>); 1022 * } 1023 * 1024 * <jc>// Override at the method level.</jc> 1025 * <ja>@RestMethod</ja>(encoders={MySpecialEncoder.<jk>class</jk>}, inherit={<js>"ENCODERS"</js>}) 1026 * public Object myMethod() {...} 1027 * } 1028 * </p> 1029 * 1030 * <h5 class='section'>Notes:</h5> 1031 * <ul class='spaced-list'> 1032 * <li> 1033 * When defined as a class, the implementation must have one of the following constructors: 1034 * <ul> 1035 * <li><code><jk>public</jk> T(BeanContext)</code> 1036 * <li><code><jk>public</jk> T()</code> 1037 * </ul> 1038 * <li> 1039 * Inner classes of the REST resource class are allowed. 1040 * </ul> 1041 * 1042 * <h5 class='section'>See Also:</h5> 1043 * <ul> 1044 * <li class='link'>{@doc juneau-rest-server.Encoders} 1045 * </ul> 1046 */ 1047 public static final String REST_encoders = PREFIX + "encoders.lo"; 1048 1049 /** 1050 * Configuration property: Class-level guards. 1051 * 1052 * <h5 class='section'>Property:</h5> 1053 * <ul> 1054 * <li><b>Name:</b> <js>"RestContext.guards.lo"</js> 1055 * <li><b>Data type:</b> <code>List<{@link RestGuard} | Class<? <jk>extends</jk> {@link RestGuard}>></code> 1056 * <li><b>Default:</b> empty list 1057 * <li><b>Session property:</b> <jk>false</jk> 1058 * <li><b>Annotations:</b> 1059 * <ul> 1060 * <li class='ja'>{@link RestResource#guards()} 1061 * <li class='ja'>{@link RestMethod#guards()} 1062 * </ul> 1063 * <li><b>Methods:</b> 1064 * <ul> 1065 * <li class='jm'>{@link RestContextBuilder#guards(Class...)} 1066 * <li class='jm'>{@link RestContextBuilder#guards(RestGuard...)} 1067 * </ul> 1068 * </ul> 1069 * 1070 * <h5 class='section'>Description:</h5> 1071 * <p> 1072 * Associates one or more {@link RestGuard RestGuards} with all REST methods defined in this class. 1073 * <br>These guards get called immediately before execution of any REST method in this class. 1074 * 1075 * <p> 1076 * If multiple guards are specified, <b>ALL</b> guards must pass. 1077 * <br>Note that this is different than matchers were only ONE matcher needs to pass. 1078 * 1079 * <h5 class='section'>Example:</h5> 1080 * <p class='bcode w800'> 1081 * <jc>// Define a guard that only lets Billy make a request.</jc> 1082 * <jk>public</jk> BillyGuard <jk>extends</jk> RestGuard { 1083 * <ja>@Override</ja> 1084 * <jk>public boolean</jk> isRequestAllowed(RestRequest req) { 1085 * <jk>return</jk> req.getUserPrincipal().getName().equals(<js>"Billy"</js>); 1086 * } 1087 * } 1088 * 1089 * <jc>// Option #1 - Registered via annotation.</jc> 1090 * <ja>@RestResource</ja>(guards={BillyGuard.<jk>class</jk>}) 1091 * <jk>public class</jk> MyResource { 1092 * 1093 * <jc>// Option #2 - Registered via builder passed in through resource constructor.</jc> 1094 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 1095 * 1096 * <jc>// Using method on builder.</jc> 1097 * builder.guards(BillyGuard.<jk>class</jk>); 1098 * 1099 * <jc>// Same, but using property.</jc> 1100 * builder.addTo(<jsf>REST_guards</jsf>, BillyGuard.<jk>class</jk>); 1101 * } 1102 * 1103 * <jc>// Option #3 - Registered via builder passed in through init method.</jc> 1104 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 1105 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 1106 * builder.guards(BillyGuard.<jk>class</jk>); 1107 * } 1108 * 1109 * <jc>// Override at the method level.</jc> 1110 * <ja>@RestMethod</ja>(guards={SomeOtherGuard.<jk>class</jk>}) 1111 * public Object myMethod() {...} 1112 * } 1113 * </p> 1114 * 1115 * <h5 class='section'>Notes:</h5> 1116 * <ul class='spaced-list'> 1117 * <li> 1118 * When defined as a class, the implementation must have one of the following constructors: 1119 * <ul> 1120 * <li><code><jk>public</jk> T(RestContext)</code> 1121 * <li><code><jk>public</jk> T()</code> 1122 * </ul> 1123 * <li> 1124 * Inner classes of the REST resource class are allowed. 1125 * </ul> 1126 * 1127 * <h5 class='section'>See Also:</h5> 1128 * <ul> 1129 * <li class='link'>{@doc juneau-rest-server.Guards} 1130 * </ul> 1131 */ 1132 public static final String REST_guards = PREFIX + "guards.lo"; 1133 1134 /** 1135 * Configuration property: REST info provider. 1136 * 1137 * <h5 class='section'>Property:</h5> 1138 * <ul> 1139 * <li><b>Name:</b> <js>"RestContext.infoProvider.o"</js> 1140 * <li><b>Data type:</b> <code>{@link RestInfoProvider} | Class<? <jk>extends</jk> {@link RestInfoProvider}></code> 1141 * <li><b>Default:</b> {@link BasicRestInfoProvider} 1142 * <li><b>Session property:</b> <jk>false</jk> 1143 * <li><b>Annotations:</b> 1144 * <ul> 1145 * <li class='ja'>{@link RestResource#infoProvider()} 1146 * </ul> 1147 * <li><b>Methods:</b> 1148 * <ul> 1149 * <li class='jm'>{@link RestContextBuilder#infoProvider(Class)} 1150 * <li class='jm'>{@link RestContextBuilder#infoProvider(RestInfoProvider)} 1151 * </ul> 1152 * </ul> 1153 * 1154 * <h5 class='section'>Description:</h5> 1155 * <p> 1156 * Class used to retrieve title/description/swagger information about a resource. 1157 * 1158 * <h5 class='section'>Example:</h5> 1159 * <p class='bcode w800'> 1160 * <jc>// Our customized info provider.</jc> 1161 * <jc>// Extend from the default implementation and selectively override values.</jc> 1162 * <jk>public class</jk> MyRestInfoProvider <jk>extends</jk> BasicRestInfoProvider { 1163 * 1164 * <jc>// Must provide this constructor!</jc> 1165 * <jk>public</jk> MyRestInfoProvider(RestContext context) { 1166 * <jk>super</jk>(context); 1167 * } 1168 * 1169 * <ja>@Override</ja> 1170 * <jk>public</jk> Swagger getSwaggerFromFile(RestRequest req) <jk>throws</jk> RestException { 1171 * <jc>// Provide our own method of retrieving swagger from file system.</jc> 1172 * } 1173 * 1174 * <ja>@Override</ja> 1175 * <jk>public</jk> Swagger getSwagger(RestRequest req) <jk>throws</jk> RestException { 1176 * Swagger s = <jk>super</jk>.getSwagger(req); 1177 * <jc>// Made inline modifications to generated swagger.</jc> 1178 * <jk>return</jk> s; 1179 * } 1180 * 1181 * <ja>@Override</ja> 1182 * <jk>public</jk> String getSiteName(RestRequest req) { 1183 * <jc>// Override the site name.</jc> 1184 * } 1185 * } 1186 * 1187 * <jc>// Option #1 - Registered via annotation resolving to a config file setting with default value.</jc> 1188 * <ja>@RestResource</ja>(infoProvider=MyRestInfoProvider.<jk>class</jk>) 1189 * <jk>public class</jk> MyResource { 1190 * 1191 * <jc>// Option #2 - Registered via builder passed in through resource constructor.</jc> 1192 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 1193 * 1194 * <jc>// Using method on builder.</jc> 1195 * builder.infoProvider(MyRestInfoProvider.<jk>class</jk>); 1196 * 1197 * <jc>// Same, but using property.</jc> 1198 * builder.set(<jsf>REST_infoProvider</jsf>, MyRestInfoProvider.<jk>class</jk>); 1199 * } 1200 * 1201 * <jc>// Option #3 - Registered via builder passed in through init method.</jc> 1202 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 1203 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 1204 * builder.infoProvider(MyRestInfoProvider.<jk>class</jk>); 1205 * } 1206 * } 1207 * </p> 1208 * 1209 * <h5 class='section'>Notes:</h5> 1210 * <ul class='spaced-list'> 1211 * <li> 1212 * When defined as a class, the implementation must have one of the following constructors: 1213 * <ul> 1214 * <li><code><jk>public</jk> T(RestContext)</code> 1215 * <li><code><jk>public</jk> T()</code> 1216 * </ul> 1217 * <li> 1218 * Inner classes of the REST resource class are allowed. 1219 * </ul> 1220 */ 1221 public static final String REST_infoProvider = PREFIX + "infoProvider.o"; 1222 1223 /** 1224 * Configuration property: REST logger. 1225 * 1226 * <h5 class='section'>Property:</h5> 1227 * <ul> 1228 * <li><b>Name:</b> <js>"RestContext.logger.o"</js> 1229 * <li><b>Data type:</b> <code>{@link RestLogger} | Class<? <jk>extends</jk> {@link RestLogger}></code> 1230 * <li><b>Default:</b> {@link BasicRestLogger} 1231 * <li><b>Session property:</b> <jk>false</jk> 1232 * <li><b>Annotations:</b> 1233 * <ul> 1234 * <li class='ja'>{@link RestResource#logger()} 1235 * </ul> 1236 * <li><b>Methods:</b> 1237 * <ul> 1238 * <li class='jm'>{@link RestContextBuilder#logger(Class)} 1239 * <li class='jm'>{@link RestContextBuilder#logger(RestLogger)} 1240 * </ul> 1241 * </ul> 1242 * 1243 * <h5 class='section'>Description:</h5> 1244 * <p> 1245 * Specifies the logger to use for logging. 1246 * 1247 * <p> 1248 * Two implementations are provided by default: 1249 * <ul> 1250 * <li class='jc'>{@link BasicRestLogger} - Default logging. 1251 * <li class='jc'>{@link NoOpRestLogger} - Logging disabled. 1252 * </ul> 1253 * 1254 * <p> 1255 * Loggers are accessible through the following: 1256 * <ul> 1257 * <li class='jm'>{@link RestContext#getLogger() RestContext.getLogger()} 1258 * <li class='jm'>{@link RestRequest#getLogger() RestRequest.getLogger()} 1259 * </ul> 1260 * 1261 * <h5 class='section'>Example:</h5> 1262 * <p class='bcode w800'> 1263 * <jc>// Our customized logger.</jc> 1264 * <jk>public class</jk> MyRestLogger <jk>extends</jk> BasicRestLogger { 1265 * 1266 * <ja>@Override</ja> 1267 * <jk>public void</jk> log(Level level, Throwable cause, String msg, Object...args) { 1268 * <jc>// Handle logging ourselves.</jc> 1269 * } 1270 * } 1271 * 1272 * <jc>// Option #1 - Registered via annotation resolving to a config file setting with default value.</jc> 1273 * <ja>@RestResource</ja>(logger=MyRestLogger.<jk>class</jk>) 1274 * <jk>public class</jk> MyResource { 1275 * 1276 * <jc>// Option #2 - Registered via builder passed in through resource constructor.</jc> 1277 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 1278 * 1279 * <jc>// Using method on builder.</jc> 1280 * builder.logger(MyRestLogger.<jk>class</jk>); 1281 * 1282 * <jc>// Same, but using property.</jc> 1283 * builder.set(<jsf>REST_logger</jsf>, MyRestLogger.<jk>class</jk>); 1284 * } 1285 * 1286 * <jc>// Option #3 - Registered via builder passed in through init method.</jc> 1287 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 1288 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 1289 * builder.logger(MyRestLogger.<jk>class</jk>); 1290 * } 1291 * } 1292 * </p> 1293 * 1294 * <h5 class='section'>See Also:</h5> 1295 * <ul> 1296 * <li class='link'>{@doc juneau-rest-server.LoggingAndErrorHandling} 1297 * </ul> 1298 */ 1299 public static final String REST_logger = PREFIX + "logger.o"; 1300 1301 /** 1302 * Configuration property: The maximum allowed input size (in bytes) on HTTP requests. 1303 * 1304 * <h5 class='section'>Property:</h5> 1305 * <ul> 1306 * <li><b>Name:</b> <js>"RestContext.maxInput.s"</js> 1307 * <li><b>Data type:</b> <code>String</code> 1308 * <li><b>Default:</b> <js>"100M"</js> 1309 * <li><b>Session property:</b> <jk>false</jk> 1310 * <li><b>Annotations:</b> 1311 * <ul> 1312 * <li class='ja'>{@link RestResource#maxInput()} 1313 * <li class='ja'>{@link RestMethod#maxInput()} 1314 * </ul> 1315 * <li><b>Methods:</b> 1316 * <ul> 1317 * <li class='jm'>{@link RestContextBuilder#maxInput(String)} 1318 * </ul> 1319 * </ul> 1320 * 1321 * <h5 class='section'>Description:</h5> 1322 * <p> 1323 * Useful for alleviating DoS attacks by throwing an exception when too much input is received instead of resulting 1324 * in out-of-memory errors which could affect system stability. 1325 * 1326 * <h5 class='section'>Example:</h5> 1327 * <p class='bcode w800'> 1328 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 1329 * <ja>@RestResource</ja>(maxInput=<js>"$C{REST/maxInput,10M}"</js>) 1330 * <jk>public class</jk> MyResource { 1331 * 1332 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 1333 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 1334 * 1335 * <jc>// Using method on builder.</jc> 1336 * builder.maxInput(<js>"10M"</js>); 1337 * 1338 * <jc>// Same, but using property.</jc> 1339 * builder.set(<jsf>REST_maxInput</jsf>, <js>"10M"</js>); 1340 * } 1341 * 1342 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 1343 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 1344 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 1345 * builder.maxInput(<js>"10M"</js>); 1346 * } 1347 * 1348 * <jc>// Override at the method level.</jc> 1349 * <ja>@RestMethod</ja>(maxInput=<js>"10M"</js>) 1350 * public Object myMethod() {...} 1351 * } 1352 * </p> 1353 * 1354 * <h5 class='section'>Notes:</h5> 1355 * <ul class='spaced-list'> 1356 * <li> 1357 * String value that gets resolved to a <jk>long</jk>. 1358 * <li> 1359 * Can be suffixed with any of the following representing kilobytes, megabytes, and gigabytes: 1360 * <js>'K'</js>, <js>'M'</js>, <js>'G'</js>. 1361 * <li> 1362 * A value of <js>"-1"</js> can be used to represent no limit. 1363 * </ul> 1364 */ 1365 public static final String REST_maxInput = PREFIX + "maxInput.s"; 1366 1367 /** 1368 * Configuration property: Messages. 1369 * 1370 * <h5 class='section'>Property:</h5> 1371 * <ul> 1372 * <li><b>Name:</b> <js>"RestContext.messages.lo"</js> 1373 * <li><b>Data type:</b> <code>List<{@link MessageBundleLocation}></code> 1374 * <li><b>Default:</b> <jk>null</jk> 1375 * <li><b>Session property:</b> <jk>false</jk> 1376 * <li><b>Annotations:</b> 1377 * <ul> 1378 * <li class='ja'>{@link RestResource#messages()} 1379 * </ul> 1380 * <li><b>Methods:</b> 1381 * <ul> 1382 * <li class='jm'>{@link RestContextBuilder#messages(String)}, 1383 * <li class='jm'>{@link RestContextBuilder#messages(Class,String)} 1384 * <li class='jm'>{@link RestContextBuilder#messages(MessageBundleLocation...)} 1385 * </ul> 1386 * </ul> 1387 * 1388 * <h5 class='section'>Description:</h5> 1389 * <p> 1390 * Identifies the location of the resource bundle for this class. 1391 * 1392 * <p> 1393 * This annotation is used to provide localized messages for the following methods: 1394 * <ul> 1395 * <li class='jm'>{@link RestRequest#getMessage(String, Object...)} 1396 * <li class='jm'>{@link RestContext#getMessages() RestContext.getMessages()} 1397 * </ul> 1398 * 1399 * <p> 1400 * Messages are also available by passing either of the following parameter types into your Java method: 1401 * <ul> 1402 * <li class='jc'>{@link ResourceBundle} - Basic Java resource bundle. 1403 * <li class='jc'>{@link MessageBundle} - Extended resource bundle with several convenience methods. 1404 * </ul> 1405 * 1406 * <p> 1407 * Messages passed into Java methods already have their locale set to that of the incoming request. 1408 * 1409 * <p> 1410 * The value can be a relative path like <js>"nls/Messages"</js>, indicating to look for the resource bundle 1411 * <js>"com.foo.sample.nls.Messages"</js> if the resource class is in <js>"com.foo.sample"</js>, or it can be an 1412 * absolute path like <js>"com.foo.sample.nls.Messages"</js> 1413 * 1414 * <h5 class='section'>Examples:</h5> 1415 * <p class='bcode w800'> 1416 * <jk>package</jk> org.apache.foo; 1417 * 1418 * <jc>// Resolve messages to org/apache/foo/nls/MyMessages.properties</jc> 1419 * <ja>@RestResource</ja>(messages=<js>"nls/MyMessages"</js>) 1420 * <jk>public class</jk> MyResource {...} 1421 * 1422 * <ja>@RestMethod</ja>(name=<js>"GET"</js>, path=<js>"/hello/{you}"</js>) 1423 * <jk>public</jk> Object helloYou(RestRequest req, MessageBundle messages, <ja>@Path</ja>(<js>"name"</js>) String you)) { 1424 * String s; 1425 * 1426 * <jc>// Get it from the RestRequest object.</jc> 1427 * s = req.getMessage(<js>"HelloMessage"</js>, you); 1428 * 1429 * <jc>// Or get it from the method parameter.</jc> 1430 * s = messages.getString(<js>"HelloMessage"</js>, you); 1431 * 1432 * <jc>// Or get the message in a locale different from the request.</jc> 1433 * s = messages.getString(Locale.<jsf>UK</jsf>, <js>"HelloMessage"</js>, you); 1434 * 1435 * <jk>return</jk> s; 1436 * } 1437 * } 1438 * </p> 1439 * 1440 * <h5 class='section'>Notes:</h5> 1441 * <ul class='spaced-list'> 1442 * <li 1443 * >Mappings are cumulative from super classes. 1444 * <br>Therefore, you can find and retrieve messages up the class-hierarchy chain. 1445 * </ul> 1446 * 1447 * <h5 class='section'>See Also:</h5> 1448 * <ul> 1449 * <li class='link'>{@doc juneau-rest-server.Messages} 1450 * </ul> 1451 */ 1452 public static final String REST_messages = PREFIX + "messages.lo"; 1453 1454 /** 1455 * Configuration property: MIME types. 1456 * 1457 * <h5 class='section'>Property:</h5> 1458 * <ul> 1459 * <li><b>Name:</b> <js>"RestContext.mimeTypes.ss"</js> 1460 * <li><b>Data type:</b> <code>Set<String></code> 1461 * <li><b>Default:</b> empty list 1462 * <li><b>Session property:</b> <jk>false</jk> 1463 * <li><b>Annotations:</b> 1464 * <ul> 1465 * <li class='ja'>{@link RestResource#mimeTypes()} 1466 * </ul> 1467 * <li><b>Methods:</b> 1468 * <ul> 1469 * <li class='jm'>{@link RestContextBuilder#mimeTypes(String...)} 1470 * </ul> 1471 * </ul> 1472 * 1473 * <h5 class='section'>Description:</h5> 1474 * <p> 1475 * Defines MIME-type file type mappings. 1476 * 1477 * <p> 1478 * Used for specifying the content type on file resources retrieved through the following methods: 1479 * <ul> 1480 * <li class='jm'>{@link RestContext#resolveStaticFile(String) RestContext.resolveStaticFile(String)} 1481 * <li class='jm'>{@link RestRequest#getClasspathReaderResource(String,boolean,MediaType,boolean)} 1482 * <li class='jm'>{@link RestRequest#getClasspathReaderResource(String,boolean)} 1483 * <li class='jm'>{@link RestRequest#getClasspathReaderResource(String)} 1484 * </ul> 1485 * 1486 * <p> 1487 * This list appends to the existing list provided by {@link ExtendedMimetypesFileTypeMap}. 1488 * 1489 * <h5 class='section'>Example:</h5> 1490 * <p class='bcode w800'> 1491 * <jc>// Option #1 - Defined via annotation.</jc> 1492 * <ja>@RestResource</ja>(mimeTypes={<js>"text/plain txt text TXT"</js>}) 1493 * <jk>public class</jk> MyResource { 1494 * 1495 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 1496 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 1497 * 1498 * <jc>// Using method on builder.</jc> 1499 * builder.mimeTypes(<js>"text/plain txt text TXT"</js>); 1500 * 1501 * <jc>// Same, but using property.</jc> 1502 * builder.addTo(<jsf>REST_mimeTypes</jsf>, <js>"text/plain txt text TXT"</js>); 1503 * } 1504 * 1505 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 1506 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 1507 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 1508 * builder.mimeTypes(<js>"text/plain txt text TXT"</js>); 1509 * } 1510 * } 1511 * </p> 1512 * 1513 * <h5 class='section'>Notes:</h5> 1514 * <ul class='spaced-list'> 1515 * <li> 1516 * Values are .mime.types formatted entry string. 1517 * <br>Example: <js>"image/svg+xml svg"</js> 1518 * </ul> 1519 */ 1520 public static final String REST_mimeTypes = PREFIX + "mimeTypes.ss"; 1521 1522 /** 1523 * Configuration property: Java method parameter resolvers. 1524 * 1525 * <h5 class='section'>Property:</h5> 1526 * <ul> 1527 * <li><b>Name:</b> <js>"RestContext.paramResolvers.lo"</js> 1528 * <li><b>Data type:</b> <code>List<{@link RestMethodParam} | Class<? <jk>extends</jk> {@link RestMethodParam}>></code> 1529 * <li><b>Default:</b> empty list 1530 * <li><b>Session property:</b> <jk>false</jk> 1531 * <li><b>Annotations:</b> 1532 * <ul> 1533 * <li class='ja'>{@link RestResource#paramResolvers()} 1534 * </ul> 1535 * <li><b>Methods:</b> 1536 * <ul> 1537 * <li class='jm'>{@link RestContextBuilder#paramResolvers(Class...)} 1538 * <li class='jm'>{@link RestContextBuilder#paramResolvers(RestMethodParam...)} 1539 * </ul> 1540 * </ul> 1541 * 1542 * <h5 class='section'>Description:</h5> 1543 * <p> 1544 * By default, the Juneau framework will automatically Java method parameters of various types (e.g. 1545 * <code>RestRequest</code>, <code>Accept</code>, <code>Reader</code>). 1546 * This setting allows you to provide your own resolvers for your own class types that you want resolved. 1547 * 1548 * <p> 1549 * For example, if you want to pass in instances of <code>MySpecialObject</code> to your Java method, define 1550 * the following resolver: 1551 * <p class='bcode w800'> 1552 * <jc>// Define a parameter resolver for resolving MySpecialObject objects.</jc> 1553 * <jk>public class</jk> MyRestParam <jk>extends</jk> RestMethodParam { 1554 * 1555 * <jc>// Must have no-arg constructor!</jc> 1556 * <jk>public</jk> MyRestParam() { 1557 * <jc>// First two parameters help with Swagger doc generation.</jc> 1558 * <jk>super</jk>(<jsf>QUERY</jsf>, <js>"myparam"</js>, MySpecialObject.<jk>class</jk>); 1559 * } 1560 * 1561 * <jc>// The method that creates our object. 1562 * // In this case, we're taking in a query parameter and converting it to our object.</jc> 1563 * <jk>public</jk> Object resolve(RestRequest req, RestResponse res) <jk>throws</jk> Exception { 1564 * <jk>return new</jk> MySpecialObject(req.getQuery().get(<js>"myparam"</js>)); 1565 * } 1566 * } 1567 * 1568 * <jc>// Option #1 - Registered via annotation.</jc> 1569 * <ja>@RestResource</ja>(paramResolvers=MyRestParam.<jk>class</jk>) 1570 * <jk>public class</jk> MyResource { 1571 * 1572 * <jc>// Option #2 - Registered via builder passed in through resource constructor.</jc> 1573 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 1574 * 1575 * <jc>// Using method on builder.</jc> 1576 * builder.paramResolvers(MyRestParam.<jk>class</jk>); 1577 * 1578 * <jc>// Same, but using property.</jc> 1579 * builder.addTo(<jsf>REST_paramResolver</jsf>, MyRestParam.<jk>class</jk>); 1580 * } 1581 * 1582 * <jc>// Option #3 - Registered via builder passed in through init method.</jc> 1583 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 1584 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 1585 * builder.paramResolvers(MyRestParam.<jk>class</jk>); 1586 * } 1587 * 1588 * <jc>// Now pass it into your method.</jc> 1589 * <ja>@RestMethod</ja>(...) 1590 * <jk>public</jk> Object doMyMethod(MySpecialObject mySpeciaObject) { 1591 * <jc>// Do something with it.</jc> 1592 * } 1593 * } 1594 * </p> 1595 * 1596 * <h5 class='section'>Notes:</h5> 1597 * <ul class='spaced-list'> 1598 * <li> 1599 * When defined as a class, the implementation must have one of the following constructors: 1600 * <ul> 1601 * <li><code><jk>public</jk> T(BeanContext)</code> 1602 * <li><code><jk>public</jk> T()</code> 1603 * </ul> 1604 * <li> 1605 * Inner classes of the REST resource class are allowed. 1606 * <li> 1607 * Refer to {@link RestMethodParam} for the list of predefined parameter resolvers. 1608 * </ul> 1609 */ 1610 public static final String REST_paramResolvers = PREFIX + "paramResolvers.lo"; 1611 1612 /** 1613 * Configuration property: Parsers. 1614 * 1615 * <h5 class='section'>Property:</h5> 1616 * <ul> 1617 * <li><b>Name:</b> <js>"RestContext.parsers.lo"</js> 1618 * <li><b>Data type:</b> <code>List<{@link Parser} | Class<? <jk>extends</jk> {@link Parser}>></code> 1619 * <li><b>Default:</b> empty list 1620 * <li><b>Session property:</b> <jk>false</jk> 1621 * <li><b>Annotations:</b> 1622 * <ul> 1623 * <li class='ja'>{@link RestResource#parsers()} 1624 * <li class='ja'>{@link RestMethod#parsers()} 1625 * </ul> 1626 * <li><b>Methods:</b> 1627 * <ul> 1628 * <li class='jm'>{@link RestContextBuilder#parsers(Object...)} 1629 * <li class='jm'>{@link RestContextBuilder#parsers(Class...)} 1630 * <li class='jm'>{@link RestContextBuilder#parsers(boolean,Object...)} 1631 * </ul> 1632 * </ul> 1633 * 1634 * <h5 class='section'>Description:</h5> 1635 * <p> 1636 * Adds class-level parsers to this resource. 1637 * 1638 * <p> 1639 * Parsers are used to convert the body of HTTP requests into POJOs. 1640 * <br>Any of the Juneau framework parsers can be used in this setting. 1641 * <br>The parser selected is based on the request <code>Content-Type</code> header matched against the values returned by the following method 1642 * using a best-match algorithm: 1643 * <ul> 1644 * <li class='jm'>{@link Parser#getMediaTypes()} 1645 * </ul> 1646 * 1647 * <h5 class='section'>Example:</h5> 1648 * <p class='bcode w800'> 1649 * <jc>// Option #1 - Defined via annotation.</jc> 1650 * <ja>@RestResource</ja>(parsers={JsonParser.<jk>class</jk>, XmlParser.<jk>class</jk>}) 1651 * <jk>public class</jk> MyResource { 1652 * 1653 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 1654 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 1655 * 1656 * <jc>// Using method on builder.</jc> 1657 * builder.parsers(JsonParser.<jk>class</jk>, XmlParser.<jk>class</jk>); 1658 * 1659 * <jc>// Same, but use pre-instantiated parsers.</jc> 1660 * builder.parsers(JsonParser.<jsf>DEFAULT</jsf>, XmlParser.<jsf>DEFAULT</jsf>); 1661 * 1662 * <jc>// Same, but using property.</jc> 1663 * builder.set(<jsf>REST_parsers</jsf>, JsonParser.<jk>class</jk>, XmlParser.<jk>class</jk>); 1664 * } 1665 * 1666 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 1667 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 1668 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 1669 * builder.parsers(JsonParser.<jk>class</jk>, XmlParser.<jk>class</jk>); 1670 * } 1671 * 1672 * <jc>// Override at the method level.</jc> 1673 * <ja>@RestMethod</ja>(parsers={HtmlParser.<jk>class</jk>}) 1674 * <jk>public</jk> Object myMethod(<ja>@Body</ja> MyPojo myPojo) { 1675 * <jc>// Do something with your parsed POJO.</jc> 1676 * } 1677 * } 1678 * </p> 1679 * 1680 * <h5 class='section'>Notes:</h5> 1681 * <ul class='spaced-list'> 1682 * <li> 1683 * When defined as a class, properties/transforms defined on the resource/method are inherited. 1684 * <li> 1685 * When defined as an instance, properties/transforms defined on the resource/method are NOT inherited. 1686 * <li> 1687 * Typically, you'll want your resource to extend directly from {@link BasicRestServlet} which comes 1688 * preconfigured with the following parsers: 1689 * <ul> 1690 * <li class='jc'>{@link JsonParser} 1691 * <li class='jc'>{@link XmlParser} 1692 * <li class='jc'>{@link HtmlParser} 1693 * <li class='jc'>{@link UonParser} 1694 * <li class='jc'>{@link UrlEncodingParser} 1695 * <li class='jc'>{@link MsgPackParser} 1696 * <li class='jc'>{@link PlainTextParser} 1697 * </ul> 1698 * </ul> 1699 * 1700 * <h5 class='section'>See Also:</h5> 1701 * <ul> 1702 * <li class='link'>{@doc juneau-rest-server.Parsers} 1703 * </ul> 1704 */ 1705 public static final String REST_parsers = PREFIX + "parsers.lo"; 1706 1707 /** 1708 * Configuration property: HTTP part parser. 1709 * 1710 * <h5 class='section'>Property:</h5> 1711 * <ul> 1712 * <li><b>Name:</b> <js>"RestContext.partParser.o"</js> 1713 * <li><b>Data type:</b> <code>{@link HttpPartParser} | Class<? <jk>extends</jk> {@link HttpPartParser}></code> 1714 * <li><b>Default:</b> {@link OpenApiParser} 1715 * <li><b>Session property:</b> <jk>false</jk> 1716 * <li><b>Annotations:</b> 1717 * <ul> 1718 * <li class='ja'>{@link RestResource#partParser()} 1719 * </ul> 1720 * <li><b>Methods:</b> 1721 * <ul> 1722 * <li class='jm'>{@link RestContextBuilder#partParser(Class)} 1723 * <li class='jm'>{@link RestContextBuilder#partParser(HttpPartParser)} 1724 * </ul> 1725 * </ul> 1726 * 1727 * <h5 class='section'>Description:</h5> 1728 * <p> 1729 * Specifies the {@link HttpPartParser} to use for parsing headers, query/form parameters, and URI parts. 1730 * 1731 * <p> 1732 * The default value is {@link OpenApiParser} which allows for both plain-text and URL-Encoded-Object-Notation values. 1733 * <br>If your parts contain text that can be confused with UON (e.g. <js>"(foo)"</js>), you can switch to 1734 * {@link SimplePartParser} which treats everything as plain text. 1735 * 1736 * <h5 class='section'>Example:</h5> 1737 * <p class='bcode w800'> 1738 * <jc>// Option #1 - Defined via annotation.</jc> 1739 * <ja>@RestResource</ja>(partParser=SimplePartParser.<jk>class</jk>) 1740 * <jk>public class</jk> MyResource { 1741 * 1742 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 1743 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 1744 * 1745 * <jc>// Using method on builder.</jc> 1746 * builder.partParser(SimplePartParser.<jk>class</jk>); 1747 * 1748 * <jc>// Same, but using property.</jc> 1749 * builder.set(<jsf>REST_partParser</jsf>, SimplePartParser.<jk>class</jk>); 1750 * } 1751 * 1752 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 1753 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 1754 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 1755 * builder.partParser(SimplePartParser.<jk>class</jk>); 1756 * } 1757 * 1758 * <ja>@RestMethod</ja>(...) 1759 * <jk>public</jk> Object myMethod(<ja>@Header</ja>(<js>"My-Header"</js>) MyParsedHeader h, <ja>@Query</ja>(<js>"myquery"</js>) MyParsedQuery q) { 1760 * <jc>// Do something with your parsed parts.</jc> 1761 * } 1762 * } 1763 * </p> 1764 * 1765 * <h5 class='section'>Notes:</h5> 1766 * <ul class='spaced-list'> 1767 * <li> 1768 * When defined as a class, properties/transforms defined on the resource/method are inherited. 1769 * <li> 1770 * When defined as an instance, properties/transforms defined on the resource/method are NOT inherited. 1771 * </ul> 1772 */ 1773 public static final String REST_partParser = PREFIX + "partParser.o"; 1774 1775 /** 1776 * Configuration property: HTTP part serializer. 1777 * 1778 * <h5 class='section'>Property:</h5> 1779 * <ul> 1780 * <li><b>Name:</b> <js>"RestContext.partSerializer.o"</js> 1781 * <li><b>Data type:</b> <code>{@link HttpPartSerializer} | Class<? <jk>extends</jk> {@link HttpPartSerializer}></code> 1782 * <li><b>Default:</b> {@link OpenApiSerializer} 1783 * <li><b>Session property:</b> <jk>false</jk> 1784 * <li><b>Annotations:</b> 1785 * <ul> 1786 * <li class='ja'>{@link RestResource#partSerializer()} 1787 * </ul> 1788 * <li><b>Methods:</b> 1789 * <ul> 1790 * <li class='jm'>{@link RestContextBuilder#partSerializer(Class)} 1791 * <li class='jm'>{@link RestContextBuilder#partSerializer(HttpPartSerializer)} 1792 * </ul> 1793 * </ul> 1794 * 1795 * <h5 class='section'>Description:</h5> 1796 * <p> 1797 * Specifies the {@link HttpPartSerializer} to use for serializing headers, query/form parameters, and URI parts. 1798 * 1799 * <p> 1800 * The default value is {@link OpenApiSerializer} which serializes based on OpenAPI rules, but defaults to UON notation for beans and maps, and 1801 * plain text for everything else. 1802 * <br>Other options include: 1803 * <ul> 1804 * <li class='jc'>{@link SimplePartSerializer} - Always serializes to plain text. 1805 * <li class='jc'>{@link UonSerializer} - Always serializers to UON. 1806 * </ul> 1807 * 1808 * <h5 class='section'>Example:</h5> 1809 * <p class='bcode w800'> 1810 * <jc>// Option #1 - Defined via annotation.</jc> 1811 * <ja>@RestResource</ja>(partSerializer=SimplePartSerializer.<jk>class</jk>) 1812 * <jk>public class</jk> MyResource { 1813 * 1814 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 1815 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 1816 * 1817 * <jc>// Using method on builder.</jc> 1818 * builder.partSerializer(SimplePartSerializer.<jk>class</jk>); 1819 * 1820 * <jc>// Same, but using property.</jc> 1821 * builder.set(<jsf>REST_partSerializer</jsf>, SimplePartSerializer.<jk>class</jk>); 1822 * } 1823 * 1824 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 1825 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 1826 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 1827 * builder.partSerializer(SimplePartSerializer.<jk>class</jk>); 1828 * } 1829 * 1830 * <ja>@RestMethod</ja>(...) 1831 * <jk>public</jk> Object myMethod(RestResponse res) { 1832 * <jc>// Set a header to a POJO.</jc> 1833 * res.setHeader(<js>"My-Header"</js>, <jk>new</jk> MyPojo()); 1834 * } 1835 * } 1836 * </p> 1837 * 1838 * <h5 class='section'>Notes:</h5> 1839 * <ul class='spaced-list'> 1840 * <li> 1841 * When defined as a class, properties/transforms defined on the resource/method are inherited. 1842 * <li> 1843 * When defined as an instance, properties/transforms defined on the resource/method are NOT inherited. 1844 * </ul> 1845 */ 1846 public static final String REST_partSerializer = PREFIX + "partSerializer.o"; 1847 1848 /** 1849 * Configuration property: Resource path. 1850 * 1851 * <h5 class='section'>Property:</h5> 1852 * <ul> 1853 * <li><b>Name:</b> <js>"RestContext.path.s"</js> 1854 * <li><b>Data type:</b> <code>String</code> 1855 * <li><b>Default:</b> <jk>null</jk> 1856 * <li><b>Session property:</b> <jk>false</jk> 1857 * <li><b>Annotations:</b> 1858 * <ul> 1859 * <li class='ja'>{@link RestResource#path()} 1860 * </ul> 1861 * <li><b>Methods:</b> 1862 * <ul> 1863 * <li class='jm'>{@link RestContextBuilder#path(String)} 1864 * </ul> 1865 * </ul> 1866 * 1867 * <h5 class='section'>Description:</h5> 1868 * <p> 1869 * Identifies the URL subpath relative to the ascendant resource. 1870 * 1871 * <p> 1872 * This setting is critical for the routing of HTTP requests from ascendant to child resources. 1873 * 1874 * <h5 class='section'>Example:</h5> 1875 * <p class='bcode w800'> 1876 * <jc>// Option #1 - Defined via annotation.</jc> 1877 * <ja>@RestResource</ja>(path=<js>"/myResource"</js>) 1878 * <jk>public class</jk> MyResource { 1879 * 1880 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 1881 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 1882 * 1883 * <jc>// Using method on builder.</jc> 1884 * builder.path(<js>"/myResource"</js>); 1885 * 1886 * <jc>// Same, but using property.</jc> 1887 * builder.set(<jsf>REST_path</jsf>, <js>"/myResource"</js>); 1888 * } 1889 * 1890 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 1891 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 1892 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 1893 * builder.path(<js>"/myResource"</js>); 1894 * } 1895 * } 1896 * </p> 1897 * 1898 * <p> 1899 * <h5 class='section'>Notes:</h5> 1900 * <ul class='spaced-list'> 1901 * <li> 1902 * This annotation is ignored on top-level servlets (i.e. servlets defined in <code>web.xml</code> files). 1903 * <br>Therefore, implementers can optionally specify a path value for documentation purposes. 1904 * <li> 1905 * Typically, this setting is only applicable to resources defined as children through the 1906 * {@link RestResource#children() @RestResource(children)} annotation. 1907 * <br>However, it may be used in other ways (e.g. defining paths for top-level resources in microservices). 1908 * <li> 1909 * Slashes are trimmed from the path ends. 1910 * <br>As a convention, you may want to start your path with <js>'/'</js> simple because it make it easier to read. 1911 * <li> 1912 * This path is available through the following method: 1913 * <ul> 1914 * <li class='jm'>{@link RestContext#getPath() RestContext.getPath()} 1915 * </ul> 1916 * </ul> 1917 */ 1918 public static final String REST_path = PREFIX + "path.s"; 1919 1920 /** 1921 * Configuration property: Render response stack traces in responses. 1922 * 1923 * <h5 class='section'>Property:</h5> 1924 * <ul> 1925 * <li><b>Name:</b> <js>"RestContext.renderResponseStackTraces.b"</js> 1926 * <li><b>Data type:</b> <code>Boolean</code> 1927 * <li><b>Default:</b> <jk>false</jk> 1928 * <li><b>Session property:</b> <jk>false</jk> 1929 * <li><b>Annotations:</b> 1930 * <ul> 1931 * <li class='ja'>{@link RestResource#renderResponseStackTraces()} 1932 * </ul> 1933 * <li><b>Methods:</b> 1934 * <ul> 1935 * <li class='jm'>{@link RestContextBuilder#renderResponseStackTraces(boolean)} 1936 * <li class='jm'>{@link RestContextBuilder#renderResponseStackTraces()} 1937 * </ul> 1938 * </ul> 1939 * 1940 * <h5 class='section'>Description:</h5> 1941 * <p> 1942 * Render stack traces in HTTP response bodies when errors occur. 1943 * 1944 * <h5 class='section'>Example:</h5> 1945 * <p class='bcode w800'> 1946 * <jc>// Option #1 - Defined via annotation.</jc> 1947 * <ja>@RestResource</ja>(renderResponseStackTraces=<jk>true</jk>) 1948 * <jk>public class</jk> MyResource { 1949 * 1950 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 1951 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 1952 * 1953 * <jc>// Using method on builder.</jc> 1954 * builder.renderResponseStackTraces(); 1955 * 1956 * <jc>// Same, but using property.</jc> 1957 * builder.set(<jsf>REST_renderResponseStackTraces</jsf>, <jk>true</jk>); 1958 * } 1959 * 1960 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 1961 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 1962 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 1963 * builder.renderResponseStackTraces(); 1964 * } 1965 * } 1966 * </p> 1967 * 1968 * <h5 class='section'>Notes:</h5> 1969 * <ul class='spaced-list'> 1970 * <li> 1971 * Useful for debugging, although allowing stack traces to be rendered may cause security concerns so use 1972 * caution when enabling. 1973 * <li> 1974 * This setting is available through the following method: 1975 * <ul> 1976 * <li class='jm'>{@link RestContext#isRenderResponseStackTraces() RestContext.isRenderResponseStackTraces()} 1977 * </ul> 1978 * That method is used by {@link BasicRestCallHandler#handleError(HttpServletRequest, HttpServletResponse, Throwable)}. 1979 * </ul> 1980 */ 1981 public static final String REST_renderResponseStackTraces = PREFIX + "renderResponseStackTraces.b"; 1982 1983 /** 1984 * Configuration property: REST resource resolver. 1985 * 1986 * <h5 class='section'>Property:</h5> 1987 * <ul> 1988 * <li><b>Name:</b> <js>"RestContext.resourceResolver.o"</js> 1989 * <li><b>Data type:</b> <code>{@link RestResourceResolver} | Class<? <jk>extends</jk> {@link RestResourceResolver}></code> 1990 * <li><b>Default:</b> {@link BasicRestResourceResolver} 1991 * <li><b>Session property:</b> <jk>false</jk> 1992 * <li><b>Annotations:</b> 1993 * <ul> 1994 * <li class='ja'>{@link RestResource#resourceResolver()} 1995 * </ul> 1996 * <li><b>Methods:</b> 1997 * <ul> 1998 * <li class='jm'>{@link RestContextBuilder#resourceResolver(Class)} 1999 * <li class='jm'>{@link RestContextBuilder#resourceResolver(RestResourceResolver)} 2000 * </ul> 2001 * </ul> 2002 * 2003 * <h5 class='section'>Description:</h5> 2004 * <p> 2005 * The resolver used for resolving instances of child resources. 2006 * 2007 * <p> 2008 * Can be used to provide customized resolution of REST resource class instances (e.g. resources retrieve from Spring). 2009 * 2010 * <h5 class='section'>Example:</h5> 2011 * <p class='bcode w800'> 2012 * <jc>// Our custom resource resolver. </jc> 2013 * <jk>public class</jk> MyResourceResolver <jk>extends</jk> RestResourceResolverSimple { 2014 * 2015 * <ja>@Override</ja> 2016 * <jk>public</jk> Object resolve(Class<?> resourceType, RestContextBuilder builder) <jk>throws</jk> Exception { 2017 * Object resource = <jsm>findOurResourceSomehow</jsm>(resourceType); 2018 * 2019 * <jc>// If we can't resolve it, use default resolution.</jc> 2020 * <jk>if</jk> (resource == <jk>null</jk>) 2021 * resource = <jk>super</jk>.resolve(resourceType, builder); 2022 * 2023 * <jk>return</jk> resource; 2024 * } 2025 * } 2026 * 2027 * <jc>// Option #1 - Defined via annotation.</jc> 2028 * <ja>@RestResource</ja>(resourceResolver=MyResourceResolver.<jk>class</jk>) 2029 * <jk>public class</jk> MyResource { 2030 * 2031 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 2032 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 2033 * 2034 * <jc>// Using method on builder.</jc> 2035 * builder.resourceResolver(MyResourceResolver.<jk>class</jk>); 2036 * 2037 * <jc>// Same, but using property.</jc> 2038 * builder.set(<jsf>REST_resourceResolver</jsf>, MyResourceResolver.<jk>class</jk>); 2039 * } 2040 * 2041 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 2042 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 2043 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 2044 * builder.resourceResolver(MyResourceResolver.<jk>class</jk>); 2045 * } 2046 * } 2047 * </p> 2048 * 2049 * <h5 class='section'>Notes:</h5> 2050 * <ul class='spaced-list'> 2051 * <li> 2052 * Unless overridden, resource resolvers are inherited from ascendant resources. 2053 * <li> 2054 * When defined as a class, the implementation must have one of the following constructors: 2055 * <ul> 2056 * <li><code><jk>public</jk> T(RestContext)</code> 2057 * <li><code><jk>public</jk> T()</code> 2058 * </ul> 2059 * <li> 2060 * Inner classes of the REST resource class are allowed. 2061 * </ul> 2062 * 2063 * <h5 class='section'>See Also:</h5> 2064 * <ul> 2065 * <li class='link'>{@doc juneau-rest-server.Instantiation.ResourceResolvers} 2066 * <li class='link'>{@doc juneau-rest-server.Injection} 2067 * </ul> 2068 */ 2069 public static final String REST_resourceResolver = PREFIX + "resourceResolver.o"; 2070 2071 /** 2072 * Configuration property: Response handlers. 2073 * 2074 * <h5 class='section'>Property:</h5> 2075 * <ul> 2076 * <li><b>Name:</b> <js>"RestContext.responseHandlers.lo"</js> 2077 * <li><b>Data type:</b> <code>List<{@link ResponseHandler} | Class<? <jk>extends</jk> {@link ResponseHandler}>></code> 2078 * <li><b>Default:</b> empty list 2079 * <li><b>Session property:</b> <jk>false</jk> 2080 * <li><b>Annotations:</b> 2081 * <ul> 2082 * <li class='ja'>{@link RestResource#responseHandlers()} 2083 * </ul> 2084 * <li><b>Methods:</b> 2085 * <ul> 2086 * <li class='jm'>{@link RestContextBuilder#responseHandlers(Class...)} 2087 * <li class='jm'>{@link RestContextBuilder#responseHandlers(ResponseHandler...)} 2088 * </ul> 2089 * </ul> 2090 * 2091 * <h5 class='section'>Description:</h5> 2092 * <p> 2093 * Specifies a list of {@link ResponseHandler} classes that know how to convert POJOs returned by REST methods or 2094 * set via {@link RestResponse#setOutput(Object)} into appropriate HTTP responses. 2095 * 2096 * <p> 2097 * By default, the following response handlers are provided out-of-the-box: 2098 * <ul> 2099 * <li class='jc'>{@link ReaderHandler} - {@link Reader} objects. 2100 * <li class='jc'>{@link InputStreamHandler} - {@link InputStream} objects. 2101 * <li class='jc'>{@link DefaultHandler} - All other POJOs. 2102 * </ul> 2103 * 2104 * <h5 class='section'>Example:</h5> 2105 * <p class='bcode w800'> 2106 * <jc>// Our custom response handler for MySpecialObject objects. </jc> 2107 * <jk>public class</jk> MyResponseHandler <jk>implements</jk> ResponseHandler { 2108 * 2109 * <ja>@Override</ja> 2110 * <jk>public boolean</jk> handle(RestRequest req, RestResponse res, Object output) <jk>throws</jk> IOException, RestException { 2111 * <jk>if</jk> (output <jk>instanceof</jk> MySpecialObject) { 2112 * <jk>try</jk> (Writer w = res.getNegotiatedWriter()) { 2113 * <jc>//Pipe it to the writer ourselves.</jc> 2114 * } 2115 * <jk>return true</jk>; <jc>// We handled it.</jc> 2116 * } 2117 * <jk>return false</jk>; <jc>// We didn't handle it.</jc> 2118 * } 2119 * } 2120 * 2121 * <jc>// Option #1 - Defined via annotation.</jc> 2122 * <ja>@RestResource</ja>(responseHandlers=MyResponseHandler.<jk>class</jk>) 2123 * <jk>public class</jk> MyResource { 2124 * 2125 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 2126 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 2127 * 2128 * <jc>// Using method on builder.</jc> 2129 * builder.responseHandlers(MyResponseHandler.<jk>class</jk>); 2130 * 2131 * <jc>// Same, but using property.</jc> 2132 * builder.addTo(<jsf>REST_responseHandlers</jsf>, MyResponseHandler.<jk>class</jk>); 2133 * } 2134 * 2135 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 2136 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 2137 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 2138 * builder.responseHandlers(MyResponseHandler.<jk>class</jk>); 2139 * } 2140 * 2141 * <ja>@RestMethod</ja>(...) 2142 * <jk>public</jk> Object myMethod() { 2143 * <jc>// Return a special object for our handler.</jc> 2144 * <jk>return new</jk> MySpecialObject(); 2145 * } 2146 * } 2147 * </p> 2148 * 2149 * <h5 class='section'>Notes:</h5> 2150 * <ul class='spaced-list'> 2151 * <li> 2152 * Response handlers resolvers are always inherited from ascendant resources. 2153 * <li> 2154 * When defined as a class, the implementation must have one of the following constructors: 2155 * <ul> 2156 * <li><code><jk>public</jk> T(RestContext)</code> 2157 * <li><code><jk>public</jk> T()</code> 2158 * </ul> 2159 * <li> 2160 * Inner classes of the REST resource class are allowed. 2161 * </ul> 2162 */ 2163 public static final String REST_responseHandlers = PREFIX + "responseHandlers.lo"; 2164 2165 /** 2166 * Configuration property: Serializers. 2167 * 2168 * <h5 class='section'>Property:</h5> 2169 * <ul> 2170 * <li><b>Name:</b> <js>"RestContext.serializers.lo"</js> 2171 * <li><b>Data type:</b> <code>List<{@link Serializer} | Class<? <jk>extends</jk> {@link Serializer}>></code> 2172 * <li><b>Default:</b> empty list 2173 * <li><b>Session property:</b> <jk>false</jk> 2174 * <li><b>Annotations:</b> 2175 * <ul> 2176 * <li class='ja'>{@link RestResource#serializers()} 2177 * <li class='ja'>{@link RestMethod#serializers()} 2178 * </ul> 2179 * <li><b>Methods:</b> 2180 * <ul> 2181 * <li class='jm'>{@link RestContextBuilder#serializers(Object...)} 2182 * <li class='jm'>{@link RestContextBuilder#serializers(Class...)} 2183 * <li class='jm'>{@link RestContextBuilder#serializers(boolean,Object...)} 2184 * </ul> 2185 * </ul> 2186 * 2187 * <h5 class='section'>Description:</h5> 2188 * <p> 2189 * Adds class-level serializers to this resource. 2190 * 2191 * <p> 2192 * Serializer are used to convert POJOs to HTTP response bodies. 2193 * <br>Any of the Juneau framework serializers can be used in this setting. 2194 * <br>The serializer selected is based on the request <code>Accept</code> header matched against the values returned by the following method 2195 * using a best-match algorithm: 2196 * <ul> 2197 * <li class='jm'>{@link Serializer#getMediaTypeRanges()} 2198 * </ul> 2199 * 2200 * <h5 class='section'>Example:</h5> 2201 * <p class='bcode w800'> 2202 * <jc>// Option #1 - Defined via annotation.</jc> 2203 * <ja>@RestResource</ja>(serializers={JsonSerializer.<jk>class</jk>, XmlSerializer.<jk>class</jk>}) 2204 * <jk>public class</jk> MyResource { 2205 * 2206 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 2207 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 2208 * 2209 * <jc>// Using method on builder.</jc> 2210 * builder.serializers(JsonSerializer.<jk>class</jk>, XmlSerializer.<jk>class</jk>); 2211 * 2212 * <jc>// Same, but use pre-instantiated parsers.</jc> 2213 * builder.serializers(JsonSerializer.<jsf>DEFAULT</jsf>, XmlSerializer.<jsf>DEFAULT</jsf>); 2214 * 2215 * <jc>// Same, but using property.</jc> 2216 * builder.set(<jsf>REST_serializers</jsf>, JsonSerializer.<jk>class</jk>, XmlSerializer.<jk>class</jk>); 2217 * } 2218 * 2219 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 2220 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 2221 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 2222 * builder.serializers(JsonSerializer.<jk>class</jk>, XmlSerializer.<jk>class</jk>); 2223 * } 2224 * 2225 * <jc>// Override at the method level.</jc> 2226 * <ja>@RestMethod</ja>(serializers={HtmlSerializer.<jk>class</jk>}) 2227 * <jk>public</jk> MyPojo myMethod() { 2228 * <jc>// Return a POJO to be serialized.</jc> 2229 * <jk>return new</jk> MyPojo(); 2230 * } 2231 * } 2232 * </p> 2233 * 2234 * <h5 class='section'>Notes:</h5> 2235 * <ul class='spaced-list'> 2236 * <li> 2237 * When defined as a class, properties/transforms defined on the resource/method are inherited. 2238 * <li> 2239 * When defined as an instance, properties/transforms defined on the resource/method are NOT inherited. 2240 * <li> 2241 * Typically, you'll want your resource to extend directly from {@link BasicRestServlet} which comes 2242 * preconfigured with the following serializers: 2243 * <ul> 2244 * <li class='jc'>{@link HtmlDocSerializer} 2245 * <li class='jc'>{@link HtmlStrippedDocSerializer} 2246 * <li class='jc'>{@link HtmlSchemaDocSerializer} 2247 * <li class='jc'>{@link JsonSerializer} 2248 * <li class='jc'>{@link SimpleJsonSerializer} 2249 * <li class='jc'>{@link JsonSchemaSerializer} 2250 * <li class='jc'>{@link XmlDocSerializer} 2251 * <li class='jc'>{@link XmlSchemaDocSerializer} 2252 * <li class='jc'>{@link UonSerializer} 2253 * <li class='jc'>{@link UrlEncodingSerializer} 2254 * <li class='jc'>{@link MsgPackSerializer} 2255 * <li class='jc'>{@link SoapXmlSerializer} 2256 * <li class='jc'>{@link PlainTextSerializer} 2257 * </ul> 2258 * </ul> 2259 * 2260 * <h5 class='section'>See Also:</h5> 2261 * <ul> 2262 * <li class='link'>{@doc juneau-rest-server.Serializers} 2263 * </ul> 2264 * <p> 2265 */ 2266 public static final String REST_serializers = PREFIX + "serializers.lo"; 2267 2268 /** 2269 * Configuration property: Static file response headers. 2270 * 2271 * <h5 class='section'>Property:</h5> 2272 * <ul> 2273 * <li><b>Name:</b> <js>"RestContext.staticFileResponseHeaders.omo"</js> 2274 * <li><b>Data type:</b> <code>Map<String,String></code> 2275 * <li><b>Default:</b> <code>{<js>'Cache-Control'</js>: <js>'max-age=86400, public</js>}</code> 2276 * <li><b>Session property:</b> <jk>false</jk> 2277 * <li><b>Annotations:</b> 2278 * <ul> 2279 * <li class='ja'>{@link RestResource#staticFileResponseHeaders()} 2280 * </ul> 2281 * <li><b>Methods:</b> 2282 * <ul> 2283 * <li class='jm'>{@link RestContextBuilder#staticFileResponseHeaders(boolean,Map)} 2284 * <li class='jm'>{@link RestContextBuilder#staticFileResponseHeaders(String...)} 2285 * <li class='jm'>{@link RestContextBuilder#staticFileResponseHeader(String,String)} 2286 * </ul> 2287 * </ul> 2288 * 2289 * <h5 class='section'>Description:</h5> 2290 * <p> 2291 * Used to customize the headers on responses returned for statically-served files. 2292 * 2293 * <h5 class='section'>Example:</h5> 2294 * <p class='bcode w800'> 2295 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 2296 * <ja>@RestResource</ja>( 2297 * staticFileResponseHeaders={ 2298 * <js>"Cache-Control: $C{REST/cacheControl,nocache}"</js>, 2299 * <js>"My-Header: $C{REST/myHeaderValue}"</js> 2300 * } 2301 * ) 2302 * <jk>public class</jk> MyResource { 2303 * 2304 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 2305 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 2306 * 2307 * <jc>// Using method on builder.</jc> 2308 * builder 2309 * .staticFileResponseHeader(<js>"Cache-Control"</js>, <js>"nocache"</js>); 2310 * .staticFileResponseHeaders(<js>"My-Header: foo"</js>); 2311 * 2312 * <jc>// Same, but using property.</jc> 2313 * builder 2314 * .addTo(<jsf>REST_staticFileResponseHeaders</jsf>, <js>"Cache-Control"</js>, <js>"nocache"</js>); 2315 * .addTo(<jsf>REST_staticFileResponseHeaders</jsf>, <js>"My-Header"</js>, <js>"foo"</js>); 2316 * } 2317 * 2318 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 2319 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 2320 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 2321 * builder.staticFileResponseHeader(<js>"Cache-Control"</js>, <js>"nocache"</js>); 2322 * } 2323 * } 2324 * </p> 2325 * 2326 * <h5 class='section'>See Also:</h5> 2327 * <ul> 2328 * <li class='jf'>{@link #REST_staticFiles} for information about statically-served files. 2329 * </ul> 2330 */ 2331 public static final String REST_staticFileResponseHeaders = PREFIX + "staticFileResponseHeaders.omo"; 2332 2333 /** 2334 * Configuration property: Static file mappings. 2335 * 2336 * <h5 class='section'>Property:</h5> 2337 * <ul> 2338 * <li><b>Name:</b> <js>"RestContext.staticFiles.lo"</js> 2339 * <li><b>Data type:</b> <code>List<StaticFileMapping></code> 2340 * <li><b>Default:</b> <jk>null</jk> 2341 * <li><b>Session property:</b> <jk>false</jk> 2342 * <li><b>Annotations:</b> 2343 * <ul> 2344 * <li class='ja'>{@link RestResource#staticFiles()} 2345 * </ul> 2346 * <li><b>Methods:</b> 2347 * <ul> 2348 * <li class='jm'>{@link RestContextBuilder#staticFiles(String)}, 2349 * <li class='jm'>{@link RestContextBuilder#staticFiles(Class,String)} 2350 * <li class='jm'>{@link RestContextBuilder#staticFiles(String,String)} 2351 * <li class='jm'>{@link RestContextBuilder#staticFiles(Class,String,String)} 2352 * <li class='jm'>{@link RestContextBuilder#staticFiles(StaticFileMapping...)} 2353 * </ul> 2354 * </ul> 2355 * 2356 * <h5 class='section'>Description:</h5> 2357 * <p> 2358 * Used to define paths and locations of statically-served files such as images or HTML documents 2359 * from the classpath or file system. 2360 * 2361 * <p> 2362 * An example where this class is used is in the {@link RestResource#staticFiles} annotation: 2363 * <p class='bcode w800'> 2364 * <jk>package</jk> com.foo.mypackage; 2365 * 2366 * <ja>@RestResource</ja>( 2367 * path=<js>"/myresource"</js>, 2368 * staticFiles={ 2369 * <js>"htdocs:docs"</js>, 2370 * <js>"styles:styles"</js> 2371 * } 2372 * ) 2373 * <jk>public class</jk> MyResource <jk>extends</jk> BasicRestServlet {...} 2374 * </p> 2375 * 2376 * <p> 2377 * In the example above, given a GET request to the following URL... 2378 * <p class='bcode w800'> 2379 * /myresource/htdocs/foobar.html 2380 * </p> 2381 * <br>...the servlet will attempt to find the <code>foobar.html</code> file in the following ordered locations: 2382 * <ol class='spaced-list'> 2383 * <li><code>com.foo.mypackage.docs</code> package. 2384 * <li><code>[working-dir]/docs</code> directory. 2385 * </ol> 2386 * 2387 * <h5 class='section'>See Also:</h5> 2388 * <ul> 2389 * <li class='jf'>{@link #REST_classpathResourceFinder} for configuring how classpath resources are located and retrieved. 2390 * <li class='jf'>{@link #REST_mimeTypes} for configuring the media types based on file extension. 2391 * <li class='jf'>{@link #REST_staticFileResponseHeaders} for configuring response headers on statically served files. 2392 * <li class='jf'>{@link #REST_useClasspathResourceCaching} for configuring static file caching. 2393 * <li class='jm'>{@link RestContext#getClasspathResource(String,Locale)} for retrieving static files. 2394 * </ul> 2395 * 2396 * <h5 class='section'>Notes:</h5> 2397 * <ul class='spaced-list'> 2398 * <li> 2399 * Mappings are cumulative from super classes. 2400 * <li> 2401 * Child resources can override mappings made on parent class resources. 2402 * </ul> 2403 */ 2404 public static final String REST_staticFiles = PREFIX + "staticFiles.lo"; 2405 2406 /** 2407 * Configuration property: Supported accept media types. 2408 * 2409 * <h5 class='section'>Property:</h5> 2410 * <ul> 2411 * <li><b>Name:</b> <js>"RestContext.produces.ls"</js> 2412 * <li><b>Data type:</b> <code>List<String></code> 2413 * <li><b>Default:</b> empty list 2414 * <li><b>Session property:</b> <jk>false</jk> 2415 * <li><b>Annotations:</b> 2416 * <ul> 2417 * <li class='ja'>{@link RestResource#produces()} 2418 * <li class='ja'>{@link RestMethod#produces()} 2419 * </ul> 2420 * <li><b>Methods:</b> 2421 * <ul> 2422 * <li class='jm'>{@link RestContextBuilder#produces(boolean,String...)} 2423 * <li class='jm'>{@link RestContextBuilder#produces(boolean,MediaType...)} 2424 * </ul> 2425 * </ul> 2426 * 2427 * <h5 class='section'>Description:</h5> 2428 * <p> 2429 * Overrides the media types inferred from the serializers that identify what media types can be produced by the resource. 2430 * <br>An example where this might be useful if you have serializers registered that handle media types that you 2431 * don't want exposed in the Swagger documentation. 2432 * 2433 * <h5 class='section'>Example:</h5> 2434 * <p class='bcode w800'> 2435 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 2436 * <ja>@RestResource</ja>(produces={<js>"$C{REST/supportedProduces,application/json}"</js>}) 2437 * <jk>public class</jk> MyResource { 2438 * 2439 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 2440 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 2441 * 2442 * <jc>// Using method on builder.</jc> 2443 * builder.produces(<jk>false</jk>, <js>"application/json"</js>) 2444 * 2445 * <jc>// Same, but using property.</jc> 2446 * builder.set(<jsf>REST_produces</jsf>, <js>"application/json"</js>); 2447 * } 2448 * 2449 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 2450 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 2451 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 2452 * builder.produces(<jk>false</jk>, <js>"application/json"</js>); 2453 * } 2454 * } 2455 * </p> 2456 * 2457 * <p> 2458 * This affects the returned values from the following: 2459 * <ul> 2460 * <li class='jm'>{@link RestContext#getProduces() RestContext.getProduces()} 2461 * <li class='jm'>{@link RestRequest#getProduces()} 2462 * <li class='jm'>{@link RestInfoProvider#getSwagger(RestRequest)} - Affects produces field. 2463 * </ul> 2464 */ 2465 public static final String REST_produces = PREFIX + "produces.ls"; 2466 2467 /** 2468 * Configuration property: Supported content media types. 2469 * 2470 * <h5 class='section'>Property:</h5> 2471 * <ul> 2472 * <li><b>Name:</b> <js>"RestContext.consumes.ls"</js> 2473 * <li><b>Data type:</b> <code>List<String></code> 2474 * <li><b>Default:</b> empty list 2475 * <li><b>Session property:</b> <jk>false</jk> 2476 * <li><b>Annotations:</b> 2477 * <ul> 2478 * <li class='ja'>{@link RestResource#consumes()} 2479 * <li class='ja'>{@link RestMethod#consumes()} 2480 * </ul> 2481 * <li><b>Methods:</b> 2482 * <ul> 2483 * <li class='jm'>{@link RestContextBuilder#consumes(boolean,String...)} 2484 * <li class='jm'>{@link RestContextBuilder#consumes(boolean,MediaType...)} 2485 * </ul> 2486 * </ul> 2487 * 2488 * <h5 class='section'>Description:</h5> 2489 * <p> 2490 * Overrides the media types inferred from the parsers that identify what media types can be consumed by the resource. 2491 * <br>An example where this might be useful if you have parsers registered that handle media types that you 2492 * don't want exposed in the Swagger documentation. 2493 * 2494 * <h5 class='section'>Example:</h5> 2495 * <p class='bcode w800'> 2496 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 2497 * <ja>@RestResource</ja>(consumes={<js>"$C{REST/supportedConsumes,application/json}"</js>}) 2498 * <jk>public class</jk> MyResource { 2499 * 2500 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 2501 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 2502 * 2503 * <jc>// Using method on builder.</jc> 2504 * builder.consumes(<jk>false</jk>, <js>"application/json"</js>) 2505 * 2506 * <jc>// Same, but using property.</jc> 2507 * builder.set(<jsf>REST_consumes</jsf>, <js>"application/json"</js>); 2508 * } 2509 * 2510 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 2511 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 2512 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 2513 * builder.consumes(<jk>false</jk>, <js>"application/json"</js>); 2514 * } 2515 * } 2516 * </p> 2517 * 2518 * <p> 2519 * This affects the returned values from the following: 2520 * <ul> 2521 * <li class='jm'>{@link RestContext#getConsumes() RestContext.getConsumes()} 2522 * <li class='jm'>{@link RestRequest#getConsumes()} 2523 * <li class='jm'>{@link RestInfoProvider#getSwagger(RestRequest)} - Affects consumes field. 2524 * </ul> 2525 */ 2526 public static final String REST_consumes = PREFIX + "consumes.ls"; 2527 2528 /** 2529 * Configuration property: Use classpath resource caching. 2530 * 2531 * <h5 class='section'>Property:</h5> 2532 * <ul> 2533 * <li><b>Name:</b> <js>"RestContext.useClasspathResourceCaching.b"</js> 2534 * <li><b>Data type:</b> <code>Boolean</code> 2535 * <li><b>Default:</b> <jk>true</jk> 2536 * <li><b>Session property:</b> <jk>false</jk> 2537 * <li><b>Annotations:</b> 2538 * <ul> 2539 * <li class='ja'>{@link RestResource#useClasspathResourceCaching()} 2540 * </ul> 2541 * <li><b>Methods:</b> 2542 * <ul> 2543 * <li class='jm'>{@link RestContextBuilder#useClasspathResourceCaching(boolean)} 2544 * </ul> 2545 * </ul> 2546 * 2547 * <h5 class='section'>Description:</h5> 2548 * <p> 2549 * When enabled, resources retrieved via {@link RestContext#getClasspathResource(String, Locale)} (and related 2550 * methods) will be cached in memory to speed subsequent lookups. 2551 * 2552 * <h5 class='section'>Example:</h5> 2553 * <p class='bcode w800'> 2554 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 2555 * <ja>@RestResource</ja>(useClasspathResourceCaching=<js>"$C{REST/useClasspathResourceCaching,false}"</js>) 2556 * <jk>public class</jk> MyResource { 2557 * 2558 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 2559 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 2560 * 2561 * <jc>// Using method on builder.</jc> 2562 * builder.useClasspathResourceCaching(<jk>false</jk>) 2563 * 2564 * <jc>// Same, but using property.</jc> 2565 * builder.set(<jsf>REST_useClasspathResourceCaching</jsf>, <jk>false</jk>); 2566 * } 2567 * 2568 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 2569 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 2570 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 2571 * builder.useClasspathResourceCaching(<jk>false</jk>) 2572 * } 2573 * } 2574 * </p> 2575 * 2576 * <h5 class='section'>See Also:</h5> 2577 * <ul> 2578 * <li class='jf'>{@link #REST_staticFiles} for information about static files. 2579 * </ul> 2580 */ 2581 public static final String REST_useClasspathResourceCaching = PREFIX + "useClasspathResourceCaching.b"; 2582 2583 /** 2584 * Configuration property: Use stack trace hashes. 2585 * 2586 * <h5 class='section'>Property:</h5> 2587 * <ul> 2588 * <li><b>Name:</b> <js>"RestContext.useStackTraceHashes.b"</js> 2589 * <li><b>Data type:</b> <code>Boolean</code> 2590 * <li><b>Default:</b> <jk>true</jk> 2591 * <li><b>Session property:</b> <jk>false</jk> 2592 * <li><b>Annotations:</b> 2593 * <ul> 2594 * <li class='ja'>{@link RestResource#useStackTraceHashes()} 2595 * </ul> 2596 * <li><b>Methods:</b> 2597 * <ul> 2598 * <li class='jm'>{@link RestContextBuilder#useStackTraceHashes(boolean)} 2599 * </ul> 2600 * </ul> 2601 * 2602 * <h5 class='section'>Description:</h5> 2603 * <p> 2604 * When enabled, the number of times an exception has occurred will be tracked based on stack trace hashsums. 2605 * 2606 * <p> 2607 * Affects the following methods: 2608 * <ul> 2609 * <li class='jm'>{@link RestContext#getStackTraceOccurrence(Throwable) RestContext.getStackTraceOccurrance(Throwable)} 2610 * <li class='jm'>{@link RestCallHandler#handleError(HttpServletRequest, HttpServletResponse, Throwable)} 2611 * <li class='jm'>{@link RestException#getOccurrence()} - Returns the number of times this exception occurred. 2612 * </ul> 2613 * 2614 * <h5 class='section'>Example:</h5> 2615 * <p class='bcode w800'> 2616 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 2617 * <ja>@RestResource</ja>(useStackTraceHashes=<js>"$C{REST/useStackTraceHashes,false}"</js>) 2618 * <jk>public class</jk> MyResource { 2619 * 2620 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 2621 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 2622 * 2623 * <jc>// Using method on builder.</jc> 2624 * builder.useStackTraceHashes(<jk>false</jk>) 2625 * 2626 * <jc>// Same, but using property.</jc> 2627 * builder.set(<jsf>REST_useStackTraceHashes</jsf>, <jk>false</jk>); 2628 * } 2629 * 2630 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 2631 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 2632 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 2633 * builder.useStackTraceHashes(<jk>false</jk>) 2634 * } 2635 * } 2636 * </p> 2637 */ 2638 public static final String REST_useStackTraceHashes = PREFIX + "useStackTraceHashes.b"; 2639 2640 /** 2641 * Configuration property: Resource URI authority path. 2642 * 2643 * <h5 class='section'>Property:</h5> 2644 * <ul> 2645 * <li><b>Name:</b> <js>"RestContext.uriAuthority.s"</js> 2646 * <li><b>Data type:</b> <code>String</code> 2647 * <li><b>Default:</b> <jk>null</jk> 2648 * <li><b>Session property:</b> <jk>false</jk> 2649 * <li><b>Annotations:</b> 2650 * <ul> 2651 * <li class='ja'>{@link RestResource#uriAuthority()} 2652 * </ul> 2653 * <li><b>Methods:</b> 2654 * <ul> 2655 * <li class='jm'>{@link RestContextBuilder#uriAuthority(String)} 2656 * </ul> 2657 * </ul> 2658 * 2659 * <h5 class='section'>Description:</h5> 2660 * <p> 2661 * Overrides the authority path value for this resource and any child resources. 2662 * 2663 * <p> 2664 * Affects the following methods: 2665 * <ul> 2666 * <li class='jm'>{@link RestRequest#getAuthorityPath()} 2667 * </ul> 2668 * 2669 * <p> 2670 * If you do not specify the authority, it is automatically calculated via the following: 2671 * 2672 * <p class='bcode w800'> 2673 * String scheme = request.getScheme(); 2674 * <jk>int</jk> port = request.getServerPort(); 2675 * StringBuilder sb = <jk>new</jk> StringBuilder(request.getScheme()).append(<js>"://"</js>).append(request.getServerName()); 2676 * <jk>if</jk> (! (port == 80 && <js>"http"</js>.equals(scheme) || port == 443 && <js>"https"</js>.equals(scheme))) 2677 * sb.append(<js>':'</js>).append(port); 2678 * authorityPath = sb.toString(); 2679 * </p> 2680 * 2681 * <h5 class='section'>Example:</h5> 2682 * <p class='bcode w800'> 2683 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 2684 * <ja>@RestResource</ja>( 2685 * path=<js>"/servlet"</js>, 2686 * uriAuthority=<js>"$C{REST/authorityPathOverride,http://localhost:10000}"</js> 2687 * ) 2688 * <jk>public class</jk> MyResource { 2689 * 2690 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 2691 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 2692 * 2693 * <jc>// Using method on builder.</jc> 2694 * builder.uriAuthority(<js>"http://localhost:10000"</js>); 2695 * 2696 * <jc>// Same, but using property.</jc> 2697 * builder.set(<jsf>REST_uriAuthority</jsf>, <js>"http://localhost:10000"</js>); 2698 * } 2699 * 2700 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 2701 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 2702 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 2703 * builder.uriAuthority(<js>"http://localhost:10000"</js>); 2704 * } 2705 * } 2706 * </p> 2707 */ 2708 public static final String REST_uriAuthority = PREFIX + "uriAuthority.s"; 2709 2710 /** 2711 * Configuration property: Resource URI context path. 2712 * 2713 * <h5 class='section'>Property:</h5> 2714 * <ul> 2715 * <li><b>Name:</b> <js>"RestContext.uriContext.s"</js> 2716 * <li><b>Data type:</b> <code>String</code> 2717 * <li><b>Default:</b> <jk>null</jk> 2718 * <li><b>Session property:</b> <jk>false</jk> 2719 * <li><b>Annotations:</b> 2720 * <ul> 2721 * <li class='ja'>{@link RestResource#uriContext()} 2722 * </ul> 2723 * <li><b>Methods:</b> 2724 * <ul> 2725 * <li class='jm'>{@link RestContextBuilder#uriContext(String)} 2726 * </ul> 2727 * </ul> 2728 * 2729 * <h5 class='section'>Description:</h5> 2730 * <p> 2731 * Overrides the context path value for this resource and any child resources. 2732 * 2733 * <p> 2734 * This setting is useful if you want to use <js>"context:/child/path"</js> URLs in child resource POJOs but 2735 * the context path is not actually specified on the servlet container. 2736 * 2737 * <p> 2738 * Affects the following methods: 2739 * <ul> 2740 * <li class='jm'>{@link RestRequest#getContextPath()} - Returns the overridden context path for the resource. 2741 * <li class='jm'>{@link RestRequest#getServletPath()} - Includes the overridden context path for the resource. 2742 * </ul> 2743 * 2744 * <h5 class='section'>Example:</h5> 2745 * <p class='bcode w800'> 2746 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 2747 * <ja>@RestResource</ja>( 2748 * path=<js>"/servlet"</js>, 2749 * uriContext=<js>"$C{REST/contextPathOverride,/foo}"</js> 2750 * ) 2751 * <jk>public class</jk> MyResource { 2752 * 2753 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 2754 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 2755 * 2756 * <jc>// Using method on builder.</jc> 2757 * builder.uriContext(<js>"/foo"</js>); 2758 * 2759 * <jc>// Same, but using property.</jc> 2760 * builder.set(<jsf>REST_uriContext</jsf>, <js>"/foo"</js>); 2761 * } 2762 * 2763 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 2764 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 2765 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 2766 * builder.uriContext(<js>"/foo"</js>); 2767 * } 2768 * } 2769 * </p> 2770 */ 2771 public static final String REST_uriContext = PREFIX + "uriContext.s"; 2772 2773 /** 2774 * Configuration property: URI resolution relativity. 2775 * 2776 * <h5 class='section'>Property:</h5> 2777 * <ul> 2778 * <li><b>Name:</b> <js>"RestContext.uriRelativity.s"</js> 2779 * <li><b>Data type:</b> <code>String</code> 2780 * <li><b>Default:</b> <js>"RESOURCE"</js> 2781 * <li><b>Session property:</b> <jk>false</jk> 2782 * <li><b>Annotations:</b> 2783 * <ul> 2784 * <li class='ja'>{@link RestResource#uriRelativity()} 2785 * </ul> 2786 * <li><b>Methods:</b> 2787 * <ul> 2788 * <li class='jm'>{@link RestContextBuilder#uriRelativity(String)} 2789 * </ul> 2790 * </ul> 2791 * 2792 * <h5 class='section'>Description:</h5> 2793 * <p> 2794 * Specifies how relative URIs should be interpreted by serializers. 2795 * 2796 * <p> 2797 * See {@link UriResolution} for possible values. 2798 * 2799 * <p> 2800 * Affects the following methods: 2801 * <ul> 2802 * <li class='jm'>{@link RestRequest#getUriResolver()} 2803 * </ul> 2804 * 2805 * <h5 class='section'>Example:</h5> 2806 * <p class='bcode w800'> 2807 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 2808 * <ja>@RestResource</ja>( 2809 * path=<js>"/servlet"</js>, 2810 * uriRelativity=<js>"$C{REST/uriRelativity,PATH_INFO}"</js> 2811 * ) 2812 * <jk>public class</jk> MyResource { 2813 * 2814 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 2815 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 2816 * 2817 * <jc>// Using method on builder.</jc> 2818 * builder.uriRelativity(<js>"PATH_INFO"</js>); 2819 * 2820 * <jc>// Same, but using property.</jc> 2821 * builder.set(<jsf>REST_uriRelativity</jsf>, <js>"PATH_INFO"</js>); 2822 * } 2823 * 2824 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 2825 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 2826 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 2827 * builder.uriRelativity(<js>"PATH_INFO"</js>); 2828 * } 2829 * } 2830 * </p> 2831 */ 2832 public static final String REST_uriRelativity = PREFIX + "uriRelativity.s"; 2833 2834 /** 2835 * Configuration property: URI resolution. 2836 * 2837 * <h5 class='section'>Property:</h5> 2838 * <ul> 2839 * <li><b>Name:</b> <js>"RestContext.uriResolution.s"</js> 2840 * <li><b>Data type:</b> <code>String</code> 2841 * <li><b>Default:</b> <js>"ROOT_RELATIVE"</js> 2842 * <li><b>Session property:</b> <jk>false</jk> 2843 * <li><b>Annotations:</b> 2844 * <ul> 2845 * <li class='ja'>{@link RestResource#uriResolution()} 2846 * </ul> 2847 * <li><b>Methods:</b> 2848 * <ul> 2849 * <li class='jm'>{@link RestContextBuilder#uriResolution(String)} 2850 * </ul> 2851 * </ul> 2852 * 2853 * <h5 class='section'>Description:</h5> 2854 * <p> 2855 * Specifies how relative URIs should be interpreted by serializers. 2856 * 2857 * <p> 2858 * See {@link UriResolution} for possible values. 2859 * 2860 * <p> 2861 * Affects the following methods: 2862 * <ul> 2863 * <li class='jm'>{@link RestRequest#getUriResolver()} 2864 * </ul> 2865 * 2866 * <h5 class='section'>Example:</h5> 2867 * <p class='bcode w800'> 2868 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 2869 * <ja>@RestResource</ja>( 2870 * path=<js>"/servlet"</js>, 2871 * uriResolution=<js>"$C{REST/uriResolution,ABSOLUTE}"</js> 2872 * ) 2873 * <jk>public class</jk> MyResource { 2874 * 2875 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 2876 * <jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception { 2877 * 2878 * <jc>// Using method on builder.</jc> 2879 * builder.uriResolution(<js>"ABSOLUTE"</js>); 2880 * 2881 * <jc>// Same, but using property.</jc> 2882 * builder.set(<jsf>REST_uriResolution</jsf>, <js>"ABSOLUTE"</js>); 2883 * } 2884 * 2885 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 2886 * <ja>@RestHook</ja>(<jsf>INIT</jsf>) 2887 * <jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception { 2888 * builder.uriResolution(<js>"ABSOLUTE"</js>); 2889 * } 2890 * } 2891 * </p> 2892 */ 2893 public static final String REST_uriResolution = PREFIX + "uriResolution.s"; 2894 2895 /** 2896 * Configuration property: HTML Widgets. 2897 * 2898 * <h5 class='section'>Property:</h5> 2899 * <ul> 2900 * <li><b>Name:</b> <js>"RestContext.widgets.lo"</js> 2901 * <li><b>Data type:</b> <code>List<{@link Widget} | Class<? <jk>extends</jk> {@link Widget}>></code> 2902 * <li><b>Default:</b> empty list 2903 * <li><b>Session property:</b> <jk>false</jk> 2904 * <li><b>Annotations:</b> 2905 * <ul> 2906 * <li class='ja'>{@link HtmlDoc#widgets()} 2907 * </ul> 2908 * <li><b>Methods:</b> 2909 * <ul> 2910 * <li class='jm'>{@link RestContextBuilder#widgets(Class...)} 2911 * <li class='jm'>{@link RestContextBuilder#widgets(Widget...)} 2912 * <li class='jm'>{@link RestContextBuilder#widgets(boolean,Widget...)} 2913 * </ul> 2914 * </ul> 2915 * 2916 * <h5 class='section'>Description:</h5> 2917 * <p> 2918 * Defines widgets that can be used in conjunction with string variables of the form <js>"$W{name}"</js>to quickly 2919 * generate arbitrary replacement text. 2920 * 2921 * Widgets resolve the following variables: 2922 * <ul class='spaced-list'> 2923 * <li><js>"$W{name}"</js> - Contents returned by {@link Widget#getHtml(RestRequest)}. 2924 * <li><js>"$W{name.script}"</js> - Contents returned by {@link Widget#getScript(RestRequest)}. 2925 * <br>The script contents are automatically inserted into the <xt><head/script></xt> section 2926 * in the HTML page. 2927 * <li><js>"$W{name.style}"</js> - Contents returned by {@link Widget#getStyle(RestRequest)}. 2928 * <br>The styles contents are automatically inserted into the <xt><head/style></xt> section 2929 * in the HTML page. 2930 * </ul> 2931 * 2932 * <p> 2933 * The following examples shows how to associate a widget with a REST method and then have it rendered in the links 2934 * and aside section of the page: 2935 * 2936 * <p class='bcode w800'> 2937 * <ja>@RestMethod</ja>( 2938 * widgets={ 2939 * MyWidget.<jk>class</jk> 2940 * } 2941 * htmldoc=<ja>@HtmlDoc</ja>( 2942 * navlinks={ 2943 * <js>"$W{MyWidget}"</js> 2944 * }, 2945 * aside={ 2946 * <js>"Check out this widget: $W{MyWidget}"</js> 2947 * } 2948 * ) 2949 * ) 2950 * </p> 2951 * 2952 * <h5 class='section'>Notes:</h5> 2953 * <ul class='spaced-list'> 2954 * <li> 2955 * Widgets are inherited from super classes, but can be overridden by reusing the widget name. 2956 * </ul> 2957 * 2958 * <h5 class='section'>See Also:</h5> 2959 * <ul> 2960 * <li class='link'>{@doc juneau-rest-server.HtmlDocAnnotation.Widgets} 2961 * </ul> 2962 */ 2963 public static final String REST_widgets = PREFIX + "widgets.lo"; 2964 2965 2966 //------------------------------------------------------------------------------------------------------------------- 2967 // Instance 2968 //------------------------------------------------------------------------------------------------------------------- 2969 2970 private final Object resource; 2971 final RestContextBuilder builder; 2972 private final boolean 2973 allowHeaderParams, 2974 allowBodyParam, 2975 renderResponseStackTraces, 2976 useStackTraceHashes, 2977 useClasspathResourceCaching, 2978 debug; 2979 private final String 2980 defaultCharset, 2981 clientVersionHeader, 2982 uriAuthority, 2983 uriContext; 2984 private final long 2985 maxInput; 2986 2987 final String fullPath; 2988 2989 private final Map<String,Widget> widgets; 2990 2991 private final Set<String> allowedMethodParams; 2992 2993 private final RestContextProperties properties; 2994 private final Map<Class<?>,RestMethodParam> paramResolvers; 2995 private final SerializerGroup serializers; 2996 private final ParserGroup parsers; 2997 private final HttpPartSerializer partSerializer; 2998 private final HttpPartParser partParser; 2999 private final EncoderGroup encoders; 3000 private final List<MediaType> 3001 consumes, 3002 produces; 3003 private final Map<String,Object> 3004 defaultRequestHeaders, 3005 defaultResponseHeaders, 3006 staticFileResponseHeaders; 3007 private final BeanContext beanContext; 3008 private final RestConverter[] converters; 3009 private final RestGuard[] guards; 3010 private final ResponseHandler[] responseHandlers; 3011 private final MimetypesFileTypeMap mimetypesFileTypeMap; 3012 private final StaticFileMapping[] staticFiles; 3013 private final String[] staticFilesPaths; 3014 private final MessageBundle msgs; 3015 private final Config config; 3016 private final VarResolver varResolver; 3017 private final Map<String,RestCallRouter> callRouters; 3018 private final Map<String,RestJavaMethod> callMethods; 3019 private final Map<String,RestContext> childResources; 3020 private final RestLogger logger; 3021 private final RestCallHandler callHandler; 3022 private final RestInfoProvider infoProvider; 3023 private final RestException initException; 3024 private final RestContext parentContext; 3025 private final RestResourceResolver resourceResolver; 3026 private final UriResolution uriResolution; 3027 private final UriRelativity uriRelativity; 3028 3029 // Lifecycle methods 3030 private final Method[] 3031 postInitMethods, 3032 postInitChildFirstMethods, 3033 preCallMethods, 3034 postCallMethods, 3035 startCallMethods, 3036 endCallMethods, 3037 destroyMethods; 3038 private final RestMethodParam[][] 3039 preCallMethodParams, 3040 postCallMethodParams; 3041 private final Class<?>[][] 3042 postInitMethodParams, 3043 postInitChildFirstMethodParams, 3044 startCallMethodParams, 3045 endCallMethodParams, 3046 destroyMethodParams; 3047 3048 // In-memory cache of images and stylesheets in the org.apache.juneau.rest.htdocs package. 3049 private final Map<String,StaticFile> staticFilesCache = new ConcurrentHashMap<>(); 3050 3051 private final ClasspathResourceManager staticResourceManager; 3052 private final ConcurrentHashMap<Integer,AtomicInteger> stackTraceHashes = new ConcurrentHashMap<>(); 3053 3054 private final ThreadLocal<RestRequest> req = new ThreadLocal<>(); 3055 private final ThreadLocal<RestResponse> res = new ThreadLocal<>(); 3056 3057 /** 3058 * Constructor. 3059 * 3060 * @param resource The resource annotated with <ja>@RestResource</ja>. 3061 * @return A new builder object. 3062 * @throws ServletException Something bad happened. 3063 */ 3064 public static RestContextBuilder create(Object resource) throws ServletException { 3065 return new RestContextBuilder(null, resource.getClass(), null).init(resource); 3066 } 3067 3068 /** 3069 * Constructor. 3070 * 3071 * @param servletConfig The servlet config passed into the servlet by the servlet container. 3072 * @param resourceClass The class annotated with <ja>@RestResource</ja>. 3073 * @param parentContext The parent context, or <jk>null</jk> if there is no parent context. 3074 * @return A new builder object. 3075 * @throws ServletException Something bad happened. 3076 */ 3077 static RestContextBuilder create(ServletConfig servletConfig, Class<?> resourceClass, RestContext parentContext) throws ServletException { 3078 return new RestContextBuilder(servletConfig, resourceClass, parentContext); 3079 } 3080 3081 /** 3082 * Constructor. 3083 * 3084 * @param builder The servlet configuration object. 3085 * @throws Exception If any initialization problems were encountered. 3086 */ 3087 RestContext(RestContextBuilder builder) throws Exception { 3088 super(builder.getPropertyStore()); 3089 3090 RestException _initException = null; 3091 3092 try { 3093 ServletContext servletContext = builder.servletContext; 3094 3095 this.resource = builder.resource; 3096 this.builder = builder; 3097 this.parentContext = builder.parentContext; 3098 resourceResolver = getInstanceProperty(REST_resourceResolver, resource, RestResourceResolver.class, parentContext == null ? BasicRestResourceResolver.class : parentContext.resourceResolver, ResourceResolver.FUZZY, this); 3099 3100 PropertyStore ps = getPropertyStore().builder().add(builder.properties).build(); 3101 Class<?> resourceClass = resource.getClass(); 3102 3103 uriContext = nullIfEmpty(getStringProperty(REST_uriContext, null)); 3104 uriAuthority = nullIfEmpty(getStringProperty(REST_uriAuthority, null)); 3105 uriResolution = getProperty(REST_uriResolution, UriResolution.class, UriResolution.ROOT_RELATIVE); 3106 uriRelativity = getProperty(REST_uriRelativity, UriRelativity.class, UriRelativity.RESOURCE); 3107 3108 allowHeaderParams = getBooleanProperty(REST_allowHeaderParams, true); 3109 allowBodyParam = getBooleanProperty(REST_allowBodyParam, true); 3110 allowedMethodParams = Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(StringUtils.split(getStringProperty(REST_allowedMethodParams, "HEAD,OPTIONS"))))); 3111 renderResponseStackTraces = getBooleanProperty(REST_renderResponseStackTraces, false); 3112 useStackTraceHashes = getBooleanProperty(REST_useStackTraceHashes, true); 3113 debug = getBooleanProperty(REST_debug, super.isDebug()); 3114 defaultCharset = getStringProperty(REST_defaultCharset, "utf-8"); 3115 maxInput = getLongProperty(REST_maxInput, 100_000_000l); 3116 clientVersionHeader = getStringProperty(REST_clientVersionHeader, "X-Client-Version"); 3117 3118 converters = getInstanceArrayProperty(REST_converters, resource, RestConverter.class, new RestConverter[0], resourceResolver, this); 3119 guards = getInstanceArrayProperty(REST_guards, resource, RestGuard.class, new RestGuard[0], resourceResolver, this); 3120 responseHandlers = getInstanceArrayProperty(REST_responseHandlers, resource, ResponseHandler.class, new ResponseHandler[0], resourceResolver, this); 3121 3122 Map<Class<?>,RestMethodParam> _paramResolvers = new HashMap<>(); 3123 for (RestMethodParam rp : getInstanceArrayProperty(REST_paramResolvers, RestMethodParam.class, new RestMethodParam[0], resourceResolver, this)) 3124 _paramResolvers.put(rp.forClass(), rp); 3125 paramResolvers = unmodifiableMap(_paramResolvers); 3126 3127 Map<String,Object> _defaultRequestHeaders = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); 3128 _defaultRequestHeaders.putAll(getMapProperty(REST_defaultRequestHeaders, String.class)); 3129 defaultRequestHeaders = unmodifiableMap(new LinkedHashMap<>(_defaultRequestHeaders)); 3130 3131 defaultResponseHeaders = getMapProperty(REST_defaultResponseHeaders, Object.class); 3132 staticFileResponseHeaders = getMapProperty(REST_staticFileResponseHeaders, Object.class); 3133 3134 logger = getInstanceProperty(REST_logger, resource, RestLogger.class, NoOpRestLogger.class, resourceResolver, this); 3135 if (debug) 3136 logger.setLevel(Level.FINE); 3137 3138 varResolver = builder.varResolverBuilder 3139 .vars( 3140 FileVar.class, 3141 LocalizationVar.class, 3142 RequestAttributeVar.class, 3143 RequestFormDataVar.class, 3144 RequestHeaderVar.class, 3145 RequestPathVar.class, 3146 RequestQueryVar.class, 3147 RequestVar.class, 3148 RestInfoVar.class, 3149 SerializedRequestAttrVar.class, 3150 ServletInitParamVar.class, 3151 SwaggerVar.class, 3152 UrlVar.class, 3153 UrlEncodeVar.class, 3154 WidgetVar.class 3155 ) 3156 .build() 3157 ; 3158 3159 config = builder.config.resolving(this.varResolver.createSession()); 3160 3161 properties = builder.properties; 3162 serializers = SerializerGroup.create().append(getInstanceArrayProperty(REST_serializers, Serializer.class, new Serializer[0], resourceResolver, resource, ps)).build(); 3163 parsers = ParserGroup.create().append(getInstanceArrayProperty(REST_parsers, Parser.class, new Parser[0], resourceResolver, resource, ps)).build(); 3164 partSerializer = getInstanceProperty(REST_partSerializer, HttpPartSerializer.class, OpenApiSerializer.class, resourceResolver, resource, ps); 3165 partParser = getInstanceProperty(REST_partParser, HttpPartParser.class, OpenApiParser.class, resourceResolver, resource, ps); 3166 encoders = new EncoderGroupBuilder().append(getInstanceArrayProperty(REST_encoders, Encoder.class, new Encoder[0], resourceResolver, resource, ps)).build(); 3167 beanContext = BeanContext.create().apply(ps).build(); 3168 3169 mimetypesFileTypeMap = new ExtendedMimetypesFileTypeMap(); 3170 for (String mimeType : getArrayProperty(REST_mimeTypes, String.class)) 3171 mimetypesFileTypeMap.addMimeTypes(mimeType); 3172 3173 ClasspathResourceFinder rf = getInstanceProperty(REST_classpathResourceFinder, ClasspathResourceFinder.class, ClasspathResourceFinderBasic.class, resourceResolver, this); 3174 useClasspathResourceCaching = getProperty(REST_useClasspathResourceCaching, boolean.class, true); 3175 staticResourceManager = new ClasspathResourceManager(resourceClass, rf, useClasspathResourceCaching); 3176 3177 consumes = getListProperty(REST_consumes, MediaType.class, parsers.getSupportedMediaTypes()); 3178 produces = getListProperty(REST_produces, MediaType.class, serializers.getSupportedMediaTypes()); 3179 3180 staticFiles = ArrayUtils.reverse(getArrayProperty(REST_staticFiles, StaticFileMapping.class)); 3181 Set<String> s = new TreeSet<>(); 3182 for (StaticFileMapping sfm : staticFiles) 3183 s.add(sfm.path); 3184 staticFilesPaths = s.toArray(new String[s.size()]); 3185 3186 MessageBundleLocation[] mbl = getInstanceArrayProperty(REST_messages, MessageBundleLocation.class, new MessageBundleLocation[0]); 3187 if (mbl.length == 0) 3188 msgs = new MessageBundle(resourceClass, ""); 3189 else { 3190 msgs = new MessageBundle(mbl[0] != null ? mbl[0].baseClass : resourceClass, mbl[0].bundlePath); 3191 for (int i = 1; i < mbl.length; i++) 3192 msgs.addSearchPath(mbl[i] != null ? mbl[i].baseClass : resourceClass, mbl[i].bundlePath); 3193 } 3194 3195 fullPath = (builder.parentContext == null ? "" : (builder.parentContext.fullPath + '/')) + builder.path; 3196 3197 this.childResources = Collections.synchronizedMap(new LinkedHashMap<String,RestContext>()); // Not unmodifiable on purpose so that children can be replaced. 3198 3199 Map<String,Widget> _widgets = new LinkedHashMap<>(); 3200 for (Widget w : getInstanceArrayProperty(REST_widgets, resource, Widget.class, new Widget[0], resourceResolver, ps)) 3201 _widgets.put(w.getName(), w); 3202 this.widgets = unmodifiableMap(_widgets); 3203 3204 //---------------------------------------------------------------------------------------------------- 3205 // Initialize the child resources. 3206 // Done after initializing fields above since we pass this object to the child resources. 3207 //---------------------------------------------------------------------------------------------------- 3208 List<String> methodsFound = new LinkedList<>(); // Temporary to help debug transient duplicate method issue. 3209 Map<String,RestCallRouter.Builder> routers = new LinkedHashMap<>(); 3210 Map<String,RestJavaMethod> _javaRestMethods = new LinkedHashMap<>(); 3211 Map<String,Method> 3212 _startCallMethods = new LinkedHashMap<>(), 3213 _preCallMethods = new LinkedHashMap<>(), 3214 _postCallMethods = new LinkedHashMap<>(), 3215 _endCallMethods = new LinkedHashMap<>(), 3216 _postInitMethods = new LinkedHashMap<>(), 3217 _postInitChildFirstMethods = new LinkedHashMap<>(), 3218 _destroyMethods = new LinkedHashMap<>(); 3219 List<RestMethodParam[]> 3220 _preCallMethodParams = new ArrayList<>(), 3221 _postCallMethodParams = new ArrayList<>(); 3222 List<Class<?>[]> 3223 _startCallMethodParams = new ArrayList<>(), 3224 _endCallMethodParams = new ArrayList<>(), 3225 _postInitMethodParams = new ArrayList<>(), 3226 _postInitChildFirstMethodParams = new ArrayList<>(), 3227 _destroyMethodParams = new ArrayList<>(); 3228 3229 for (java.lang.reflect.Method method : resourceClass.getMethods()) { 3230 RestMethod a = ClassUtils.getAnnotation(RestMethod.class, method); 3231 if (a != null) { 3232 methodsFound.add(method.getName() + "," + emptyIfNull(firstNonEmpty(a.name(), a.method())) + "," + a.path()); 3233 try { 3234 if (! isPublic(method)) 3235 throw new RestServletException("@RestMethod method {0}.{1} must be defined as public.", resourceClass.getName(), method.getName()); 3236 3237 RestJavaMethod sm = new RestJavaMethod(resource, method, this); 3238 String httpMethod = sm.getHttpMethod(); 3239 3240 // RRPC is a special case where a method returns an interface that we 3241 // can perform REST calls against. 3242 // We override the CallMethod.invoke() method to insert our logic. 3243 if ("RRPC".equals(httpMethod)) { 3244 3245 final ClassMeta<?> interfaceClass = beanContext.getClassMeta(method.getGenericReturnType()); 3246 final RemoteInterfaceMeta rim = new RemoteInterfaceMeta(interfaceClass.getInnerClass(), null); 3247 if (rim.getMethodsByPath().isEmpty()) 3248 throw new RestException(SC_INTERNAL_SERVER_ERROR, "Method {0} returns an interface {1} that doesn't define any remote methods.", getMethodSignature(method), interfaceClass.getReadableName()); 3249 3250 sm = new RestJavaMethod(resource, method, this) { 3251 3252 @Override 3253 int invoke(String pathInfo, RestRequest req, RestResponse res) throws Throwable { 3254 3255 int rc = super.invoke(pathInfo, req, res); 3256 if (rc != SC_OK) 3257 return rc; 3258 3259 final Object o = res.getOutput(); 3260 3261 if ("GET".equals(req.getMethod())) { 3262 res.setOutput(rim.getMethodsByPath().keySet()); 3263 return SC_OK; 3264 3265 } else if ("POST".equals(req.getMethod())) { 3266 if (pathInfo.indexOf('/') != -1) 3267 pathInfo = pathInfo.substring(pathInfo.lastIndexOf('/')+1); 3268 pathInfo = urlDecode(pathInfo); 3269 RemoteInterfaceMethod rmm = rim.getMethodMetaByPath(pathInfo); 3270 if (rmm != null) { 3271 Method m = rmm.getJavaMethod(); 3272 try { 3273 // Parse the args and invoke the method. 3274 Parser p = req.getBody().getParser(); 3275 Object[] args = null; 3276 if (m.getGenericParameterTypes().length == 0) 3277 args = new Object[0]; 3278 else { 3279 try (Closeable in = p.isReaderParser() ? req.getReader() : req.getInputStream()) { 3280 args = p.parseArgs(in, m.getGenericParameterTypes()); 3281 } 3282 } 3283 Object output = m.invoke(o, args); 3284 res.setOutput(output); 3285 return SC_OK; 3286 } catch (Exception e) { 3287 throw new InternalServerError(e); 3288 } 3289 } 3290 } 3291 return SC_NOT_FOUND; 3292 } 3293 }; 3294 3295 _javaRestMethods.put(method.getName(), sm); 3296 addToRouter(routers, "GET", sm); 3297 addToRouter(routers, "POST", sm); 3298 3299 } else { 3300 _javaRestMethods.put(method.getName(), sm); 3301 addToRouter(routers, httpMethod, sm); 3302 } 3303 } catch (Throwable e) { 3304 throw new RestServletException("Problem occurred trying to serialize methods on class {0}, methods={1}", resourceClass.getName(), SimpleJsonSerializer.DEFAULT.serialize(methodsFound)).initCause(e); 3305 } 3306 } 3307 } 3308 3309 for (Method m : ClassUtils.getAllMethods(resourceClass, true)) { 3310 if (ClassUtils.isPublic(m) && m.isAnnotationPresent(RestHook.class)) { 3311 HookEvent he = m.getAnnotation(RestHook.class).value(); 3312 String sig = ClassUtils.getMethodSignature(m); 3313 switch(he) { 3314 case PRE_CALL: { 3315 if (! _preCallMethods.containsKey(sig)) { 3316 setAccessible(m, false); 3317 _preCallMethods.put(sig, m); 3318 _preCallMethodParams.add(findParams(m, true, null)); 3319 } 3320 break; 3321 } 3322 case POST_CALL: { 3323 if (! _postCallMethods.containsKey(sig)) { 3324 setAccessible(m, false); 3325 _postCallMethods.put(sig, m); 3326 _postCallMethodParams.add(findParams(m, true, null)); 3327 } 3328 break; 3329 } 3330 case START_CALL: { 3331 if (! _startCallMethods.containsKey(sig)) { 3332 setAccessible(m, false); 3333 _startCallMethods.put(sig, m); 3334 _startCallMethodParams.add(m.getParameterTypes()); 3335 ClassUtils.assertArgsOfType(m, HttpServletRequest.class, HttpServletResponse.class); 3336 } 3337 break; 3338 } 3339 case END_CALL: { 3340 if (! _endCallMethods.containsKey(sig)) { 3341 setAccessible(m, false); 3342 _endCallMethods.put(sig, m); 3343 _endCallMethodParams.add(m.getParameterTypes()); 3344 ClassUtils.assertArgsOfType(m, HttpServletRequest.class, HttpServletResponse.class); 3345 } 3346 break; 3347 } 3348 case POST_INIT: { 3349 if (! _postInitMethods.containsKey(sig)) { 3350 setAccessible(m, false); 3351 _postInitMethods.put(sig, m); 3352 _postInitMethodParams.add(m.getParameterTypes()); 3353 ClassUtils.assertArgsOfType(m, RestContext.class); 3354 } 3355 break; 3356 } 3357 case POST_INIT_CHILD_FIRST: { 3358 if (! _postInitChildFirstMethods.containsKey(sig)) { 3359 setAccessible(m, false); 3360 _postInitChildFirstMethods.put(sig, m); 3361 _postInitChildFirstMethodParams.add(m.getParameterTypes()); 3362 ClassUtils.assertArgsOfType(m, RestContext.class); 3363 } 3364 break; 3365 } 3366 case DESTROY: { 3367 if (! _destroyMethods.containsKey(sig)) { 3368 setAccessible(m, false); 3369 _destroyMethods.put(sig, m); 3370 _destroyMethodParams.add(m.getParameterTypes()); 3371 ClassUtils.assertArgsOfType(m, RestContext.class); 3372 } 3373 break; 3374 } 3375 default: // Ignore INIT 3376 } 3377 } 3378 } 3379 3380 this.callMethods = unmodifiableMap(_javaRestMethods); 3381 this.preCallMethods = _preCallMethods.values().toArray(new Method[_preCallMethods.size()]); 3382 this.postCallMethods = _postCallMethods.values().toArray(new Method[_postCallMethods.size()]); 3383 this.startCallMethods = _startCallMethods.values().toArray(new Method[_startCallMethods.size()]); 3384 this.endCallMethods = _endCallMethods.values().toArray(new Method[_endCallMethods.size()]); 3385 this.postInitMethods = _postInitMethods.values().toArray(new Method[_postInitMethods.size()]); 3386 this.postInitChildFirstMethods = _postInitChildFirstMethods.values().toArray(new Method[_postInitChildFirstMethods.size()]); 3387 this.destroyMethods = _destroyMethods.values().toArray(new Method[_destroyMethods.size()]); 3388 this.preCallMethodParams = _preCallMethodParams.toArray(new RestMethodParam[_preCallMethodParams.size()][]); 3389 this.postCallMethodParams = _postCallMethodParams.toArray(new RestMethodParam[_postCallMethodParams.size()][]); 3390 this.startCallMethodParams = _startCallMethodParams.toArray(new Class[_startCallMethodParams.size()][]); 3391 this.endCallMethodParams = _endCallMethodParams.toArray(new Class[_endCallMethodParams.size()][]); 3392 this.postInitMethodParams = _postInitMethodParams.toArray(new Class[_postInitMethodParams.size()][]); 3393 this.postInitChildFirstMethodParams = _postInitChildFirstMethodParams.toArray(new Class[_postInitChildFirstMethodParams.size()][]); 3394 this.destroyMethodParams = _destroyMethodParams.toArray(new Class[_destroyMethodParams.size()][]); 3395 3396 Map<String,RestCallRouter> _callRouters = new LinkedHashMap<>(); 3397 for (RestCallRouter.Builder crb : routers.values()) 3398 _callRouters.put(crb.getHttpMethodName(), crb.build()); 3399 this.callRouters = unmodifiableMap(_callRouters); 3400 3401 // Initialize our child resources. 3402 for (Object o : getArrayProperty(REST_children, Object.class)) { 3403 String path = null; 3404 Object r = null; 3405 if (o instanceof RestChild) { 3406 RestChild rc = (RestChild)o; 3407 path = rc.path; 3408 r = rc.resource; 3409 } else if (o instanceof Class<?>) { 3410 Class<?> c = (Class<?>)o; 3411 // Don't allow specifying yourself as a child. Causes an infinite loop. 3412 if (c == builder.resourceClass) 3413 continue; 3414 r = c; 3415 } else { 3416 r = o; 3417 } 3418 3419 RestContextBuilder childBuilder = null; 3420 3421 if (o instanceof Class) { 3422 Class<?> oc = (Class<?>)o; 3423 childBuilder = RestContext.create(builder.inner, oc, this); 3424 r = resourceResolver.resolve(resource, oc, childBuilder); 3425 } else { 3426 r = o; 3427 childBuilder = RestContext.create(builder.inner, o.getClass(), this); 3428 } 3429 3430 childBuilder.init(r); 3431 if (r instanceof RestServlet) 3432 ((RestServlet)r).innerInit(childBuilder); 3433 childBuilder.servletContext(servletContext); 3434 RestContext rc2 = childBuilder.build(); 3435 if (r instanceof RestServlet) 3436 ((RestServlet)r).setContext(rc2); 3437 path = childBuilder.path; 3438 childResources.put(path, rc2); 3439 } 3440 3441 callHandler = getInstanceProperty(REST_callHandler, resource, RestCallHandler.class, BasicRestCallHandler.class, resourceResolver, this); 3442 infoProvider = getInstanceProperty(REST_infoProvider, resource, RestInfoProvider.class, BasicRestInfoProvider.class, resourceResolver, this); 3443 3444 } catch (RestException e) { 3445 _initException = e; 3446 throw e; 3447 } catch (Exception e) { 3448 _initException = new RestException(e, SC_INTERNAL_SERVER_ERROR); 3449 throw e; 3450 } finally { 3451 initException = _initException; 3452 } 3453 } 3454 3455 private static void addToRouter(Map<String, RestCallRouter.Builder> routers, String httpMethodName, RestJavaMethod cm) throws RestServletException { 3456 if (! routers.containsKey(httpMethodName)) 3457 routers.put(httpMethodName, new RestCallRouter.Builder(httpMethodName)); 3458 routers.get(httpMethodName).add(cm); 3459 } 3460 3461 /** 3462 * Returns the resource resolver associated with this context. 3463 * 3464 * <p> 3465 * The resource resolver is used for instantiating child resource classes. 3466 * 3467 * <h5 class='section'>See Also:</h5> 3468 * <ul> 3469 * <li class='jf'>{@link #REST_resourceResolver} 3470 * </ul> 3471 * 3472 * @return The resource resolver associated with this context. 3473 */ 3474 protected RestResourceResolver getResourceResolver() { 3475 return resourceResolver; 3476 } 3477 3478 /** 3479 * Returns the variable resolver for this servlet. 3480 * 3481 * <p> 3482 * Variable resolvers are used to replace variables in property values. 3483 * They can be nested arbitrarily deep. 3484 * They can also return values that themselves contain other variables. 3485 * 3486 * <h5 class='figure'>Example:</h5> 3487 * <p class='bcode w800'> 3488 * <ja>@RestResource</ja>( 3489 * messages=<js>"nls/Messages"</js>, 3490 * properties={ 3491 * <ja>@Property</ja>(name=<js>"title"</js>,value=<js>"$L{title}"</js>), <jc>// Localized variable in Messages.properties</jc> 3492 * <ja>@Property</ja>(name=<js>"javaVendor"</js>,value=<js>"$S{java.vendor,Oracle}"</js>), <jc>// System property with default value</jc> 3493 * <ja>@Property</ja>(name=<js>"foo"</js>,value=<js>"bar"</js>), 3494 * <ja>@Property</ja>(name=<js>"bar"</js>,value=<js>"baz"</js>), 3495 * <ja>@Property</ja>(name=<js>"v1"</js>,value=<js>"$R{foo}"</js>), <jc>// Request variable. value="bar"</jc> 3496 * <ja>@Property</ja>(name=<js>"v1"</js>,value=<js>"$R{foo,bar}"</js>), <jc>// Request variable. value="bar"</jc> 3497 * } 3498 * ) 3499 * <jk>public class</jk> MyRestResource <jk>extends</jk> BasicRestServlet { 3500 * </p> 3501 * 3502 * <p> 3503 * A typical usage pattern involves using variables inside the {@link HtmlDoc @HtmlDoc} annotation: 3504 * <p class='bcode w800'> 3505 * <ja>@RestMethod</ja>( 3506 * name=<jsf>GET</jsf>, path=<js>"/{name}/*"</js>, 3507 * htmldoc=@HtmlDoc( 3508 * navlinks={ 3509 * <js>"up: $R{requestParentURI}"</js>, 3510 * <js>"options: servlet:/?method=OPTIONS"</js>, 3511 * <js>"editLevel: servlet:/editLevel?logger=$A{attribute.name, OFF}"</js> 3512 * } 3513 * header={ 3514 * <js>"<h1>$L{MyLocalizedPageTitle}</h1>"</js> 3515 * }, 3516 * aside={ 3517 * <js>"$F{resources/AsideText.html}"</js> 3518 * } 3519 * ) 3520 * ) 3521 * <jk>public</jk> LoggerEntry getLogger(RestRequest req, <ja>@Path</ja> String name) <jk>throws</jk> Exception { 3522 * </p> 3523 * 3524 * <h5 class='section'>See Also:</h5> 3525 * <ul> 3526 * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#vars(Class...)} - For adding custom vars. 3527 * <li class='link'>{@doc juneau-rest-server.SvlVariables} 3528 * <li class='link'>{@doc DefaultRestSvlVariables} 3529 * </ul> 3530 * 3531 * @return The var resolver in use by this resource. 3532 */ 3533 public VarResolver getVarResolver() { 3534 return varResolver; 3535 } 3536 3537 /** 3538 * Returns the config file associated with this servlet. 3539 * 3540 * <p> 3541 * The config file is identified via one of the following: 3542 * <ul> 3543 * <li class='ja'>{@link RestResource#config()} 3544 * <li class='jm'>{@link RestContextBuilder#config(Config)} 3545 * </ul> 3546 * 3547 * @return 3548 * The resolving config file associated with this servlet. 3549 * <br>Never <jk>null</jk>. 3550 */ 3551 public Config getConfig() { 3552 return config; 3553 } 3554 3555 /** 3556 * Resolve a static resource file. 3557 * 3558 * <p> 3559 * The location of static resources are defined via: 3560 * <ul> 3561 * <li class='jf'>{@link RestContext#REST_staticFiles RestContext.REST_staticFiles} 3562 * </ul> 3563 * 3564 * @param pathInfo The unencoded path info. 3565 * @return The wrapped resource, never <jk>null</jk>. 3566 * @throws NotFound Invalid path. 3567 * @throws IOException 3568 */ 3569 protected StaticFile resolveStaticFile(String pathInfo) throws NotFound, IOException { 3570 if (! staticFilesCache.containsKey(pathInfo)) { 3571 String p = urlDecode(trimSlashes(pathInfo)); 3572 if (p.indexOf("..") != -1) 3573 throw new NotFound("Invalid path"); 3574 StreamResource sr = null; 3575 for (StaticFileMapping sfm : staticFiles) { 3576 String path = sfm.path; 3577 if (p.startsWith(path)) { 3578 String remainder = (p.equals(path) ? "" : p.substring(path.length())); 3579 if (remainder.isEmpty() || remainder.startsWith("/")) { 3580 String p2 = sfm.location + remainder; 3581 try (InputStream is = getClasspathResource(sfm.resourceClass, p2, null)) { 3582 if (is != null) { 3583 int i = p2.lastIndexOf('/'); 3584 String name = (i == -1 ? p2 : p2.substring(i+1)); 3585 String mediaType = mimetypesFileTypeMap.getContentType(name); 3586 Map<String,Object> responseHeaders = sfm.responseHeaders != null ? sfm.responseHeaders : staticFileResponseHeaders; 3587 sr = new StreamResource(MediaType.forString(mediaType), responseHeaders, true, is); 3588 break; 3589 } 3590 } 3591 } 3592 } 3593 } 3594 StaticFile sf = new StaticFile(sr); 3595 if (useClasspathResourceCaching) { 3596 if (staticFilesCache.size() > 100) 3597 staticFilesCache.clear(); 3598 staticFilesCache.put(pathInfo, sf); 3599 } 3600 return sf; 3601 } 3602 return staticFilesCache.get(pathInfo); 3603 } 3604 3605 /** 3606 * A cached static file instance. 3607 */ 3608 protected class StaticFile { 3609 StreamResource resource; 3610 ResponseBeanMeta meta; 3611 3612 /** 3613 * Constructor. 3614 * 3615 * @param resource 3616 */ 3617 protected StaticFile(StreamResource resource) { 3618 this.resource = resource; 3619 this.meta = resource == null ? null : ResponseBeanMeta.create(resource.getClass(), getPropertyStore()); 3620 } 3621 } 3622 3623 /** 3624 * Same as {@link Class#getResourceAsStream(String)} except if it doesn't find the resource on this class, searches 3625 * up the parent hierarchy chain. 3626 * 3627 * <p> 3628 * If the resource cannot be found in the classpath, then an attempt is made to look in the JVM working directory. 3629 * 3630 * <p> 3631 * If the <code>locale</code> is specified, then we look for resources whose name matches that locale. 3632 * <br>For example, if looking for the resource <js>"MyResource.txt"</js> for the Japanese locale, we will look for 3633 * files in the following order: 3634 * <ol> 3635 * <li><js>"MyResource_ja_JP.txt"</js> 3636 * <li><js>"MyResource_ja.txt"</js> 3637 * <li><js>"MyResource.txt"</js> 3638 * </ol> 3639 * 3640 * <h5 class='section'>Example:</h5> 3641 * <p class='bcode w800'> 3642 * <jc>// A rest method that (unsafely!) returns the contents of a localized file </jc> 3643 * <jc>// from the classpath.</jc> 3644 * <ja>@RestMethod</ja>(path=<js>"/foo"</js>) 3645 * <jk>public</jk> Object myMethod(RestRequest req, <ja>@Query</ja>(<js>"file"</js>) String file) { 3646 * <jk>return</jk> getContext().getClasspathResource(file, req.getLocale()); 3647 * } 3648 * </p> 3649 * 3650 * <h5 class='section'>See Also:</h5> 3651 * <ul> 3652 * <li class='jf'>{@link #REST_classpathResourceFinder} 3653 * </ul> 3654 * 3655 * @param name The resource name. 3656 * @param locale 3657 * Optional locale. 3658 * <br>If <jk>null</jk>, won't look for localized file names. 3659 * @return An input stream of the resource, or <jk>null</jk> if the resource could not be found. 3660 * @throws IOException 3661 */ 3662 public InputStream getClasspathResource(String name, Locale locale) throws IOException { 3663 return staticResourceManager.getStream(name, locale); 3664 } 3665 3666 /** 3667 * Same as {@link #getClasspathResource(String, Locale)}, but allows you to override the class used for looking 3668 * up the classpath resource. 3669 * 3670 * <h5 class='section'>Example:</h5> 3671 * <p class='bcode w800'> 3672 * <jc>// A rest method that (unsafely!) returns the contents of a localized file </jc> 3673 * <jc>// from the classpath.</jc> 3674 * <ja>@RestMethod</ja>(path=<js>"/foo"</js>) 3675 * <jk>public</jk> Object myMethod(RestRequest req, <ja>@Query</ja>(<js>"file"</js>) String file) { 3676 * <jk>return</jk> getContext().getClasspathResource(SomeOtherClass.<jk>class</jk>, file, req.getLocale()); 3677 * } 3678 * </p> 3679 * 3680 * <h5 class='section'>See Also:</h5> 3681 * <ul> 3682 * <li class='jf'>{@link #REST_classpathResourceFinder} 3683 * </ul> 3684 * 3685 * @param baseClass 3686 * Overrides the default class to use for retrieving the classpath resource. 3687 * <br>If <jk>null</jk>, uses the REST resource class. 3688 * @param name The resource name. 3689 * @param locale 3690 * Optional locale. 3691 * <br>If <jk>null</jk>, won't look for localized file names. 3692 * @return An input stream of the resource, or <jk>null</jk> if the resource could not be found. 3693 * @throws IOException 3694 */ 3695 public InputStream getClasspathResource(Class<?> baseClass, String name, Locale locale) throws IOException { 3696 return staticResourceManager.getStream(baseClass, name, locale); 3697 } 3698 3699 /** 3700 * Reads the input stream from {@link #getClasspathResource(String, Locale)} into a String. 3701 * 3702 * <h5 class='section'>Example:</h5> 3703 * <p class='bcode w800'> 3704 * <jc>// A rest method that (unsafely!) returns the contents of a localized file </jc> 3705 * <jc>// from the classpath.</jc> 3706 * <ja>@RestMethod</ja>(path=<js>"/foo"</js>) 3707 * <jk>public</jk> String myMethod(RestRequest req, <ja>@Query</ja>(<js>"file"</js>) String file) { 3708 * <jk>return</jk> getContext().getClasspathResourceAsString(file, req.getLocale()); 3709 * } 3710 * </p> 3711 * 3712 * <h5 class='section'>See Also:</h5> 3713 * <ul> 3714 * <li class='jf'>{@link #REST_classpathResourceFinder} 3715 * </ul> 3716 * 3717 * @param name The resource name. 3718 * @param locale 3719 * Optional locale. 3720 * <br>If <jk>null</jk>, won't look for localized file names. 3721 * @return The contents of the stream as a string, or <jk>null</jk> if the resource could not be found. 3722 * @throws IOException If resource could not be found. 3723 */ 3724 public String getClasspathResourceAsString(String name, Locale locale) throws IOException { 3725 return staticResourceManager.getString(name, locale); 3726 } 3727 3728 /** 3729 * Same as {@link #getClasspathResourceAsString(String, Locale)}, but allows you to override the class used for looking 3730 * up the classpath resource. 3731 * 3732 * <h5 class='section'>Example:</h5> 3733 * <p class='bcode w800'> 3734 * <jc>// A rest method that (unsafely!) returns the contents of a localized file </jc> 3735 * <jc>// from the classpath.</jc> 3736 * <ja>@RestMethod</ja>(path=<js>"/foo"</js>) 3737 * <jk>public</jk> String myMethod(RestRequest req, <ja>@Query</ja>(<js>"file"</js>) String file) { 3738 * <jk>return</jk> getContext().getClasspathResourceAsString(SomeOtherClass.<jk>class</jk>, file, req.getLocale()); 3739 * } 3740 * </p> 3741 * 3742 * <h5 class='section'>See Also:</h5> 3743 * <ul> 3744 * <li class='jf'>{@link #REST_classpathResourceFinder} 3745 * </ul> 3746 * 3747 * @param baseClass 3748 * Overrides the default class to use for retrieving the classpath resource. 3749 * <br>If <jk>null</jk>, uses the REST resource class. 3750 * @param name The resource name. 3751 * @param locale 3752 * Optional locale. 3753 * <br>If <jk>null</jk>, won't look for localized file names. 3754 * @return The contents of the stream as a string, or <jk>null</jk> if the resource could not be found. 3755 * @throws IOException If resource could not be found. 3756 */ 3757 public String getClasspathResourceAsString(Class<?> baseClass, String name, Locale locale) throws IOException { 3758 return staticResourceManager.getString(baseClass, name, locale); 3759 } 3760 3761 /** 3762 * Reads the input stream from {@link #getClasspathResource(String, Locale)} and parses it into a POJO using the parser 3763 * matched by the specified media type. 3764 * 3765 * <p> 3766 * Useful if you want to load predefined POJOs from JSON files in your classpath. 3767 * 3768 * <h5 class='section'>Example:</h5> 3769 * <p class='bcode w800'> 3770 * <jc>// A rest method that (unsafely!) returns the contents of a localized file </jc> 3771 * <jc>// from the classpath parsed as an array of beans.</jc> 3772 * <ja>@RestMethod</ja>(path=<js>"/foo"</js>) 3773 * <jk>public</jk> MyBean[] myMethod(RestRequest req, <ja>@Query</ja>(<js>"file"</js>) String file) { 3774 * <jk>return</jk> getContext().getClasspathResource(MyBean[].<jk>class</jk>, <jsf>JSON</jsf>, file, req.getLocale()); 3775 * } 3776 * </p> 3777 * 3778 * <h5 class='section'>See Also:</h5> 3779 * <ul> 3780 * <li class='jf'>{@link #REST_classpathResourceFinder} 3781 * </ul> 3782 * 3783 * @param c The class type of the POJO to create. 3784 * @param mediaType The media type of the data in the stream (e.g. <js>"text/json"</js>) 3785 * @param name The resource name (e.g. "htdocs/styles.css"). 3786 * @param locale 3787 * Optional locale. 3788 * <br>If <jk>null</jk>, won't look for localized file names. 3789 * @return The parsed resource, or <jk>null</jk> if the resource could not be found. 3790 * @throws IOException 3791 * @throws ServletException If the media type was unknown or the input could not be parsed into a POJO. 3792 */ 3793 public <T> T getClasspathResource(Class<T> c, MediaType mediaType, String name, Locale locale) throws IOException, ServletException { 3794 return getClasspathResource(null, c, mediaType, name, locale); 3795 } 3796 3797 /** 3798 * Same as {@link #getClasspathResource(Class, MediaType, String, Locale)}, except overrides the class used 3799 * for retrieving the classpath resource. 3800 * 3801 * <h5 class='section'>See Also:</h5> 3802 * <ul> 3803 * <li class='jf'>{@link #REST_classpathResourceFinder} 3804 * </ul> 3805 * 3806 * <h5 class='section'>Example:</h5> 3807 * <p class='bcode w800'> 3808 * <jc>// A rest method that (unsafely!) returns the contents of a localized file </jc> 3809 * <jc>// from the classpath parsed as an array of beans.</jc> 3810 * <ja>@RestMethod</ja>(path=<js>"/foo"</js>) 3811 * <jk>public</jk> MyBean[] myMethod(RestRequest req, <ja>@Query</ja>(<js>"file"</js>) String file) { 3812 * <jk>return</jk> getContext().getClasspathResource(SomeOtherClass.<jk>class</jk>, MyBean[].<jk>class</jk>, <jsf>JSON</jsf>, file, req.getLocale()); 3813 * } 3814 * </p> 3815 * 3816 * @param baseClass 3817 * Overrides the default class to use for retrieving the classpath resource. 3818 * <br>If <jk>null<jk>, uses the REST resource class. 3819 * @param c The class type of the POJO to create. 3820 * @param mediaType The media type of the data in the stream (e.g. <js>"text/json"</js>) 3821 * @param name The resource name (e.g. "htdocs/styles.css"). 3822 * @param locale 3823 * Optional locale. 3824 * <br>If <jk>null</jk>, won't look for localized file names. 3825 * @return The parsed resource, or <jk>null</jk> if the resource could not be found. 3826 * @throws IOException 3827 * @throws ServletException If the media type was unknown or the input could not be parsed into a POJO. 3828 */ 3829 public <T> T getClasspathResource(Class<?> baseClass, Class<T> c, MediaType mediaType, String name, Locale locale) throws IOException, ServletException { 3830 InputStream is = getClasspathResource(baseClass, name, locale); 3831 if (is == null) 3832 return null; 3833 try { 3834 Parser p = parsers.getParser(mediaType); 3835 if (p == null) { 3836 if (mediaType == MediaType.JSON) 3837 p = JsonParser.DEFAULT; 3838 if (mediaType == MediaType.XML) 3839 p = XmlParser.DEFAULT; 3840 if (mediaType == MediaType.HTML) 3841 p = HtmlParser.DEFAULT; 3842 if (mediaType == MediaType.UON) 3843 p = UonParser.DEFAULT; 3844 if (mediaType == MediaType.URLENCODING) 3845 p = UrlEncodingParser.DEFAULT; 3846 if (mediaType == MediaType.MSGPACK) 3847 p = MsgPackParser.DEFAULT; 3848 } 3849 if (p != null) { 3850 try { 3851 try (Closeable in = p.isReaderParser() ? new InputStreamReader(is, UTF8) : is) { 3852 return p.parse(in, c); 3853 } 3854 } catch (ParseException e) { 3855 throw new ServletException("Could not parse resource '"+name+" as media type '"+mediaType+"'.", e); 3856 } 3857 } 3858 throw new ServletException("Unknown media type '"+mediaType+"'"); 3859 } catch (Exception e) { 3860 throw new ServletException("Could not parse resource with name '"+name+"'", e); 3861 } 3862 } 3863 3864 /** 3865 * Returns the path for this resource as defined by the {@link RestResource#path() @RestResource(path)} annotation or 3866 * {@link RestContextBuilder#path(String)} method concatenated with those on all parent classes. 3867 * 3868 * <p> 3869 * If path is not specified, returns <js>""</js>. 3870 * 3871 * <h5 class='section'>See Also:</h5> 3872 * <ul> 3873 * <li class='jf'>{@link #REST_path} 3874 * </ul> 3875 * 3876 * @return The servlet path. 3877 */ 3878 public String getPath() { 3879 return fullPath; 3880 } 3881 3882 /** 3883 * The widgets used for resolving <js>"$W{...}"<js> variables. 3884 * 3885 * <h5 class='section'>See Also:</h5> 3886 * <ul> 3887 * <li class='jf'>{@link #REST_widgets} 3888 * </ul> 3889 * 3890 * @return The var resolver widgets as a map with keys being the name returned by {@link Widget#getName()}. 3891 */ 3892 public Map<String,Widget> getWidgets() { 3893 return widgets; 3894 } 3895 3896 /** 3897 * Returns the logger to use for this resource. 3898 * 3899 * <h5 class='section'>See Also:</h5> 3900 * <ul> 3901 * <li class='jf'>{@link #REST_logger} 3902 * </ul> 3903 * 3904 * @return 3905 * The logger to use for this resource. 3906 * <br>Never <jk>null</jk>. 3907 */ 3908 public RestLogger getLogger() { 3909 return logger; 3910 } 3911 3912 /** 3913 * Returns the resource bundle used by this resource. 3914 * 3915 * <h5 class='section'>See Also:</h5> 3916 * <ul> 3917 * <li class='jf'>{@link #REST_messages} 3918 * </ul> 3919 * 3920 * @return 3921 * The resource bundle for this resource. 3922 * <br>Never <jk>null</jk>. 3923 */ 3924 public MessageBundle getMessages() { 3925 return msgs; 3926 } 3927 3928 /** 3929 * Returns the REST information provider used by this resource. 3930 * 3931 * <h5 class='section'>See Also:</h5> 3932 * <ul> 3933 * <li class='jf'>{@link RestContext#REST_infoProvider} 3934 * </ul> 3935 * 3936 * @return 3937 * The information provider for this resource. 3938 * <br>Never <jk>null</jk>. 3939 */ 3940 public RestInfoProvider getInfoProvider() { 3941 return infoProvider; 3942 } 3943 3944 /** 3945 * Returns the REST call handler used by this resource. 3946 * 3947 * <h5 class='section'>See Also:</h5> 3948 * <ul> 3949 * <li class='jf'>{@link RestContext#REST_callHandler} 3950 * </ul> 3951 * 3952 * @return 3953 * The call handler for this resource. 3954 * <br>Never <jk>null</jk>. 3955 */ 3956 public RestCallHandler getCallHandler() { 3957 return callHandler; 3958 } 3959 3960 /** 3961 * Returns a map of HTTP method names to call routers. 3962 * 3963 * @return A map with HTTP method names upper-cased as the keys, and call routers as the values. 3964 */ 3965 protected Map<String,RestCallRouter> getCallRouters() { 3966 return callRouters; 3967 } 3968 3969 /** 3970 * Returns the resource object. 3971 * 3972 * <p> 3973 * This is the instance of the class annotated with the {@link RestResource @RestResource} annotation, usually 3974 * an instance of {@link RestServlet}. 3975 * 3976 * @return 3977 * The resource object. 3978 * <br>Never <jk>null</jk>. 3979 */ 3980 public Object getResource() { 3981 return resource; 3982 } 3983 3984 /** 3985 * Returns the resource object as a {@link RestServlet}. 3986 * 3987 * @return 3988 * The resource object cast to {@link RestServlet}, or <jk>null</jk> if the resource doesn't subclass from 3989 * {@link RestServlet}. 3990 */ 3991 public RestServlet getRestServlet() { 3992 return resource instanceof RestServlet ? (RestServlet)resource : null; 3993 } 3994 3995 /** 3996 * Throws a {@link RestException} if an exception occurred in the constructor of this object. 3997 * 3998 * @throws RestException The initialization exception wrapped in a {@link RestException}. 3999 */ 4000 protected void checkForInitException() throws RestException { 4001 if (initException != null) 4002 throw initException; 4003 } 4004 4005 /** 4006 * Returns the parent resource context (if this resource was initialized from a parent). 4007 * 4008 * <p> 4009 * From this object, you can get access to the parent resource class itself using {@link #getResource()} or 4010 * {@link #getRestServlet()} 4011 * 4012 * @return The parent resource context, or <jk>null</jk> if there is no parent context. 4013 */ 4014 public RestContext getParentContext() { 4015 return parentContext; 4016 } 4017 4018 /** 4019 * Returns the {@link BeanContext} object used for parsing path variables and header values. 4020 * 4021 * @return The bean context used for parsing path variables and header values. 4022 */ 4023 public BeanContext getBeanContext() { 4024 return beanContext; 4025 } 4026 4027 /** 4028 * Returns the class-level properties associated with this servlet. 4029 * 4030 * <p> 4031 * Properties at the class level are defined via the following: 4032 * <ul> 4033 * <li class='ja'>{@link RestResource#properties()} 4034 * <li class='jm'>{@link RestContextBuilder#set(String, Object)} 4035 * <li class='jm'>{@link RestContextBuilder#set(Map)} 4036 * </ul> 4037 * 4038 * <h5 class='section'>Notes:</h5> 4039 * <ul class='spaced-list'> 4040 * <li> 4041 * The returned {@code Map} is mutable. 4042 * <br>Therefore, subclasses are free to override or set additional initialization parameters in their {@code init()} method. 4043 * </ul> 4044 * 4045 * @return The resource properties as a {@link RestContextProperties}. 4046 */ 4047 public RestContextProperties getProperties() { 4048 return properties; 4049 } 4050 4051 /** 4052 * Returns the serializers registered with this resource. 4053 * 4054 * <h5 class='section'>See Also:</h5> 4055 * <ul> 4056 * <li class='jf'>{@link RestContext#REST_serializers} 4057 * </ul> 4058 * 4059 * @return The serializers registered with this resource. 4060 */ 4061 public SerializerGroup getSerializers() { 4062 return serializers; 4063 } 4064 4065 /** 4066 * Returns the parsers registered with this resource. 4067 * 4068 * <p> 4069 * Parsers at the class level are defined via the following: 4070 * <ul> 4071 * <li class='jf'>{@link RestContext#REST_parsers} 4072 * </ul> 4073 * 4074 * @return The parsers registered with this resource. 4075 */ 4076 public ParserGroup getParsers() { 4077 return parsers; 4078 } 4079 4080 /** 4081 * Returns the servlet init parameter returned by {@link ServletConfig#getInitParameter(String)}. 4082 * 4083 * @param name The init parameter name. 4084 * @return The servlet init parameter, or <jk>null</jk> if not found. 4085 */ 4086 public String getServletInitParameter(String name) { 4087 return builder.getInitParameter(name); 4088 } 4089 4090 /** 4091 * Returns the child resources associated with this servlet. 4092 * 4093 * @return 4094 * An unmodifiable map of child resources. 4095 * Keys are the {@link RestResource#path() @RestResource(path)} annotation defined on the child resource. 4096 */ 4097 public Map<String,RestContext> getChildResources() { 4098 return Collections.unmodifiableMap(childResources); 4099 } 4100 4101 /** 4102 * Returns the number of times this exception was thrown based on a hash of its stacktrace. 4103 * 4104 * <h5 class='section'>See Also:</h5> 4105 * <ul> 4106 * <li class='jf'>{@link RestContext#REST_useStackTraceHashes} 4107 * </ul> 4108 * 4109 * @param e The exception to check. 4110 * @return 4111 * The number of times this exception was thrown, or <code>0</code> if {@link #REST_useStackTraceHashes} 4112 * setting is not enabled. 4113 */ 4114 public int getStackTraceOccurrence(Throwable e) { 4115 if (! useStackTraceHashes) 4116 return 0; 4117 int h = e.hashCode(); 4118 stackTraceHashes.putIfAbsent(h, new AtomicInteger()); 4119 return stackTraceHashes.get(h).incrementAndGet(); 4120 } 4121 4122 /** 4123 * Returns whether it's safe to render stack traces in HTTP responses. 4124 * 4125 * <h5 class='section'>See Also:</h5> 4126 * <ul> 4127 * <li class='jf'>{@link RestContext#REST_useStackTraceHashes} 4128 * </ul> 4129 * 4130 * @return <jk>true</jk> if setting is enabled. 4131 */ 4132 public boolean isRenderResponseStackTraces() { 4133 return renderResponseStackTraces; 4134 } 4135 4136 /** 4137 * Returns whether it's safe to pass header values in as GET parameters. 4138 * 4139 * <h5 class='section'>See Also:</h5> 4140 * <ul> 4141 * <li class='jf'>{@link RestContext#REST_allowHeaderParams} 4142 * </ul> 4143 * 4144 * @return <jk>true</jk> if setting is enabled. 4145 */ 4146 public boolean isAllowHeaderParams() { 4147 return allowHeaderParams; 4148 } 4149 4150 /** 4151 * Returns whether it's safe to pass the HTTP body as a <js>"body"</js> GET parameter. 4152 * 4153 * <h5 class='section'>See Also:</h5> 4154 * <ul> 4155 * <li class='jf'>{@link RestContext#REST_allowBodyParam} 4156 * </ul> 4157 * 4158 * @return <jk>true</jk> if setting is enabled. 4159 */ 4160 public boolean isAllowBodyParam() { 4161 return allowBodyParam; 4162 } 4163 4164 /** 4165 * Returns <jk>true</jk> if debug mode is enabled on this resource. 4166 * 4167 * <h5 class='section'>See Also:</h5> 4168 * <ul> 4169 * <li class='jf'>{@link RestContext#REST_debug} 4170 * </ul> 4171 * 4172 * @return <jk>true</jk> if setting is enabled. 4173 */ 4174 @Override 4175 public boolean isDebug() { 4176 return debug; 4177 } 4178 4179 /** 4180 * Returns the default charset to use on requests and responses when not specified on the request. 4181 * 4182 * <h5 class='section'>See Also:</h5> 4183 * <ul> 4184 * <li class='jf'>{@link RestContext#REST_defaultCharset} 4185 * </ul> 4186 * 4187 * @return 4188 * The default charset. 4189 * <br>Never <jk>null</jk>. 4190 */ 4191 public String getDefaultCharset() { 4192 return defaultCharset; 4193 } 4194 4195 /** 4196 * Returns the maximum request input size in bytes. 4197 * 4198 * <h5 class='section'>See Also:</h5> 4199 * <ul> 4200 * <li class='jf'>{@link RestContext#REST_maxInput} 4201 * </ul> 4202 * 4203 * @return The maximum request input size in bytes. 4204 */ 4205 public long getMaxInput() { 4206 return maxInput; 4207 } 4208 4209 /** 4210 * Returns the name of the client version header name used by this resource. 4211 * 4212 * <h5 class='section'>See Also:</h5> 4213 * <ul> 4214 * <li class='jf'>{@link RestContext#REST_clientVersionHeader} 4215 * </ul> 4216 * 4217 * @return 4218 * The name of the client version header used by this resource. 4219 * <br>Never <jk>null</jk>. 4220 */ 4221 public String getClientVersionHeader() { 4222 return clientVersionHeader; 4223 } 4224 4225 /** 4226 * Returns <jk>true</jk> if the specified <code>Method</code> GET parameter value can be used to override 4227 * the method name in the HTTP header. 4228 * 4229 * <h5 class='section'>See Also:</h5> 4230 * <ul> 4231 * <li class='jf'>{@link RestContext#REST_allowedMethodParams} 4232 * </ul> 4233 * 4234 * @param m The method name, upper-cased. 4235 * @return <jk>true</jk> if this resource allows the specified method to be overridden. 4236 */ 4237 public boolean allowMethodParam(String m) { 4238 return (isNotEmpty(m) && (allowedMethodParams.contains(m) || allowedMethodParams.contains("*"))); 4239 } 4240 4241 /** 4242 * Returns the HTTP-part parser associated with this resource. 4243 * 4244 * <h5 class='section'>See Also:</h5> 4245 * <ul> 4246 * <li class='jf'>{@link RestContext#REST_partParser} 4247 * </ul> 4248 * 4249 * @return 4250 * The HTTP-part parser associated with this resource. 4251 * <br>Never <jk>null</jk>. 4252 */ 4253 public HttpPartParser getPartParser() { 4254 return partParser; 4255 } 4256 4257 /** 4258 * Returns the HTTP-part serializer associated with this resource. 4259 * 4260 * <h5 class='section'>See Also:</h5> 4261 * <ul> 4262 * <li class='jf'>{@link RestContext#REST_partSerializer} 4263 * </ul> 4264 * 4265 * @return 4266 * The HTTP-part serializer associated with this resource. 4267 * <br>Never <jk>null</jk>. 4268 */ 4269 public HttpPartSerializer getPartSerializer() { 4270 return partSerializer; 4271 } 4272 4273 /** 4274 * Returns the encoders associated with this resource. 4275 * 4276 * <h5 class='section'>See Also:</h5> 4277 * <ul> 4278 * <li class='jf'>{@link RestContext#REST_encoders} 4279 * </ul> 4280 * 4281 * @return 4282 * The encoders associated with this resource. 4283 * <br>Never <jk>null</jk>. 4284 */ 4285 public EncoderGroup getEncoders() { 4286 return encoders; 4287 } 4288 4289 /** 4290 * Returns the explicit list of supported accept types for this resource. 4291 * 4292 * <h5 class='section'>See Also:</h5> 4293 * <ul> 4294 * <li class='jf'>{@link RestContext#REST_serializers} 4295 * <li class='jf'>{@link RestContext#REST_produces} 4296 * </ul> 4297 * 4298 * @return 4299 * The supported <code>Accept</code> header values for this resource. 4300 * <br>Never <jk>null</jk>. 4301 */ 4302 public List<MediaType> getProduces() { 4303 return produces; 4304 } 4305 4306 /** 4307 * Returns the explicit list of supported content types for this resource. 4308 * 4309 * <h5 class='section'>See Also:</h5> 4310 * <ul> 4311 * <li class='jf'>{@link RestContext#REST_parsers} 4312 * <li class='jf'>{@link RestContext#REST_consumes} 4313 * </ul> 4314 * 4315 * @return 4316 * The supported <code>Content-Type</code> header values for this resource. 4317 * <br>Never <jk>null</jk>. 4318 */ 4319 public List<MediaType> getConsumes() { 4320 return consumes; 4321 } 4322 4323 /** 4324 * Returns the default request headers for this resource. 4325 * 4326 * <h5 class='section'>See Also:</h5> 4327 * <ul> 4328 * <li class='jf'>{@link RestContext#REST_defaultRequestHeaders} 4329 * </ul> 4330 * 4331 * @return 4332 * The default request headers for this resource. 4333 * <br>Never <jk>null</jk>. 4334 */ 4335 public Map<String,Object> getDefaultRequestHeaders() { 4336 return defaultRequestHeaders; 4337 } 4338 4339 /** 4340 * Returns the default response headers for this resource. 4341 * 4342 * <h5 class='section'>See Also:</h5> 4343 * <ul> 4344 * <li class='jf'>{@link RestContext#REST_defaultResponseHeaders} 4345 * </ul> 4346 * 4347 * @return 4348 * The default response headers for this resource. 4349 * <br>Never <jk>null</jk>. 4350 */ 4351 public Map<String,Object> getDefaultResponseHeaders() { 4352 return defaultResponseHeaders; 4353 } 4354 4355 /** 4356 * Returns the converters associated with this resource at the class level. 4357 * 4358 * <h5 class='section'>See Also:</h5> 4359 * <ul> 4360 * <li class='jf'>{@link RestContext#REST_converters} 4361 * </ul> 4362 * 4363 * @return 4364 * The converters associated with this resource. 4365 * <br>Never <jk>null</jk>. 4366 */ 4367 public RestConverter[] getConverters() { 4368 return converters; 4369 } 4370 4371 /** 4372 * Returns the guards associated with this resource at the class level. 4373 * 4374 * <h5 class='section'>See Also:</h5> 4375 * <ul> 4376 * <li class='jf'>{@link RestContext#REST_guards} 4377 * </ul> 4378 * 4379 * @return 4380 * The guards associated with this resource. 4381 * <br>Never <jk>null</jk>. 4382 */ 4383 public RestGuard[] getGuards() { 4384 return guards; 4385 } 4386 4387 /** 4388 * Returns the response handlers associated with this resource. 4389 * 4390 * <h5 class='section'>See Also:</h5> 4391 * <ul> 4392 * <li class='jf'>{@link RestContext#REST_responseHandlers} 4393 * </ul> 4394 * 4395 * @return 4396 * The response handlers associated with this resource. 4397 * <br>Never <jk>null</jk>. 4398 */ 4399 protected ResponseHandler[] getResponseHandlers() { 4400 return responseHandlers; 4401 } 4402 4403 /** 4404 * Returns <jk>true</jk> if this resource has any child resources associated with it. 4405 * 4406 * <h5 class='section'>See Also:</h5> 4407 * <ul> 4408 * <li class='jf'>{@link RestContext#REST_children} 4409 * </ul> 4410 * 4411 * @return <jk>true</jk> if this resource has any child resources associated with it. 4412 */ 4413 public boolean hasChildResources() { 4414 return ! childResources.isEmpty(); 4415 } 4416 4417 /** 4418 * Returns the context of the child resource associated with the specified path. 4419 * 4420 * <h5 class='section'>See Also:</h5> 4421 * <ul> 4422 * <li class='jf'>{@link RestContext#REST_children} 4423 * </ul> 4424 * 4425 * @param path The path of the child resource to resolve. 4426 * @return The resolved context, or <jk>null</jk> if it could not be resolved. 4427 */ 4428 public RestContext getChildResource(String path) { 4429 return childResources.get(path); 4430 } 4431 4432 /** 4433 * Returns the authority path of the resource. 4434 * 4435 * <h5 class='section'>See Also:</h5> 4436 * <ul> 4437 * <li class='jf'>{@link RestContext#REST_uriAuthority} 4438 * </ul> 4439 * 4440 * @return 4441 * The authority path of this resource. 4442 * <br>If not specified, returns the context path of the ascendant resource. 4443 */ 4444 public String getUriAuthority() { 4445 if (uriAuthority != null) 4446 return uriAuthority; 4447 if (parentContext != null) 4448 return parentContext.getUriAuthority(); 4449 return null; 4450 } 4451 4452 /** 4453 * Returns the context path of the resource. 4454 * 4455 * <h5 class='section'>See Also:</h5> 4456 * <ul> 4457 * <li class='jf'>{@link RestContext#REST_uriContext} 4458 * </ul> 4459 * 4460 * @return 4461 * The context path of this resource. 4462 * <br>If not specified, returns the context path of the ascendant resource. 4463 */ 4464 public String getUriContext() { 4465 if (uriContext != null) 4466 return uriContext; 4467 if (parentContext != null) 4468 return parentContext.getUriContext(); 4469 return null; 4470 } 4471 4472 /** 4473 * Returns the setting on how relative URIs should be interpreted as relative to. 4474 * 4475 * <h5 class='section'>See Also:</h5> 4476 * <ul> 4477 * <li class='jf'>{@link RestContext#REST_uriRelativity} 4478 * </ul> 4479 * 4480 * @return 4481 * The URI-resolution relativity setting value. 4482 * <br>Never <jk>null<jk>. 4483 */ 4484 public UriRelativity getUriRelativity() { 4485 return uriRelativity; 4486 } 4487 4488 /** 4489 * Returns the setting on how relative URIs should be resolved. 4490 * 4491 * <h5 class='section'>See Also:</h5> 4492 * <ul> 4493 * <li class='jf'>{@link RestContext#REST_uriResolution} 4494 * </ul> 4495 * 4496 * @return 4497 * The URI-resolution setting value. 4498 * <br>Never <jk>null<jk>. 4499 */ 4500 public UriResolution getUriResolution() { 4501 return uriResolution; 4502 } 4503 4504 /** 4505 * Returns the parameters defined on the specified Java method. 4506 * 4507 * @param method The Java method to check. 4508 * @return The parameters defined on the Java method. 4509 */ 4510 public RestMethodParam[] getRestMethodParams(Method method) { 4511 return callMethods.get(method.getName()).methodParams; 4512 } 4513 4514 /** 4515 * Returns the media type for the specified file name. 4516 * 4517 * <h5 class='section'>See Also:</h5> 4518 * <ul> 4519 * <li class='jf'>{@link RestContext#REST_mimeTypes} 4520 * </ul> 4521 * 4522 * @param name The file name. 4523 * @return The MIME-type, or <jk>null</jk> if it could not be determined. 4524 */ 4525 public String getMediaTypeForName(String name) { 4526 return mimetypesFileTypeMap.getContentType(name); 4527 } 4528 4529 /** 4530 * Returns <jk>true</jk> if the specified path refers to a static file. 4531 * 4532 * <p> 4533 * Static files are files pulled from the classpath and served up directly to the browser. 4534 * 4535 * <h5 class='section'>See Also:</h5> 4536 * <ul> 4537 * <li class='jf'>{@link RestContext#REST_staticFiles} 4538 * </ul> 4539 * 4540 * @param p The URL path remainder after the servlet match. 4541 * @return <jk>true</jk> if the specified path refers to a static file. 4542 */ 4543 public boolean isStaticFile(String p) { 4544 return pathStartsWith(p, staticFilesPaths); 4545 } 4546 4547 /** 4548 * Returns the REST Java methods defined in this resource. 4549 * 4550 * <p> 4551 * These are the methods annotated with the {@link RestMethod @RestMethod} annotation. 4552 * 4553 * @return 4554 * An unmodifiable map of Java method names to call method objects. 4555 */ 4556 public Map<String,RestJavaMethod> getCallMethods() { 4557 return callMethods; 4558 } 4559 4560 /** 4561 * Finds the {@link RestMethodParam} instances to handle resolving objects on the calls to the specified Java method. 4562 * 4563 * @param method The Java method being called. 4564 * @param isPreOrPost Whether this is a {@link HookEvent#PRE_CALL} or {@link HookEvent#POST_CALL}. 4565 * @param pathPattern 4566 * @return The array of resolvers. 4567 * @throws ServletException If an annotation usage error was detected. 4568 */ 4569 protected RestMethodParam[] findParams(Method method, boolean isPreOrPost, UrlPathPattern pathPattern) throws ServletException { 4570 4571 Type[] pt = method.getGenericParameterTypes(); 4572 RestMethodParam[] rp = new RestMethodParam[pt.length]; 4573 PropertyStore ps = getPropertyStore(); 4574 4575 for (int i = 0; i < pt.length; i++) { 4576 4577 Type t = pt[i]; 4578 if (t instanceof Class) { 4579 Class<?> c = (Class<?>)t; 4580 rp[i] = paramResolvers.get(c); 4581 if (rp[i] == null) 4582 rp[i] = RestParamDefaults.STANDARD_RESOLVERS.get(c); 4583 } 4584 4585 if (hasAnnotation(Header.class, method, i)) { 4586 rp[i] = new RestParamDefaults.HeaderObject(method, i, ps); 4587 } else if (hasAnnotation(Query.class, method, i)) { 4588 rp[i] = new RestParamDefaults.QueryObject(method, i, ps); 4589 } else if (hasAnnotation(FormData.class, method, i)) { 4590 rp[i] = new RestParamDefaults.FormDataObject(method, i, ps); 4591 } else if (hasAnnotation(Path.class, method, i)) { 4592 rp[i] = new RestParamDefaults.PathObject(method, i, ps, pathPattern); 4593 } else if (hasAnnotation(Body.class, method, i)) { 4594 rp[i] = new RestParamDefaults.BodyObject(method, i, ps); 4595 } else if (hasAnnotation(Request.class, method, i)) { 4596 rp[i] = new RestParamDefaults.RequestObject(method, i, ps); 4597 } else if (hasAnnotation(Response.class, method, i)) { 4598 rp[i] = new RestParamDefaults.ResponseObject(method, i, ps); 4599 } else if (hasAnnotation(ResponseHeader.class, method, i)) { 4600 rp[i] = new RestParamDefaults.ResponseHeaderObject(method, i, ps); 4601 } else if (hasAnnotation(ResponseStatus.class, method, i)) { 4602 rp[i] = new RestParamDefaults.ResponseStatusObject(method, t); 4603 } else if (hasAnnotation(HasFormData.class, method, i)) { 4604 rp[i] = new RestParamDefaults.HasFormDataObject(method, i); 4605 } else if (hasAnnotation(HasQuery.class, method, i)) { 4606 rp[i] = new RestParamDefaults.HasQueryObject(method, i); 4607 } else if (hasAnnotation(org.apache.juneau.rest.annotation.Method.class, method, i)) { 4608 rp[i] = new RestParamDefaults.MethodObject(method, t); 4609 } 4610 4611 if (rp[i] == null && ! isPreOrPost) 4612 throw new RestServletException("Invalid parameter specified for method ''{0}'' at index position {1}", method, i); 4613 } 4614 4615 return rp; 4616 } 4617 4618 /* 4619 * Calls all @RestHook(PRE) methods. 4620 */ 4621 void preCall(RestRequest req, RestResponse res) throws RestException { 4622 for (int i = 0; i < preCallMethods.length; i++) 4623 preOrPost(resource, preCallMethods[i], preCallMethodParams[i], req, res); 4624 } 4625 4626 /* 4627 * Calls all @RestHook(POST) methods. 4628 */ 4629 void postCall(RestRequest req, RestResponse res) throws RestException { 4630 for (int i = 0; i < postCallMethods.length; i++) 4631 preOrPost(resource, postCallMethods[i], postCallMethodParams[i], req, res); 4632 } 4633 4634 private static void preOrPost(Object resource, Method m, RestMethodParam[] mp, RestRequest req, RestResponse res) throws RestException { 4635 if (m != null) { 4636 Object[] args = new Object[mp.length]; 4637 for (int i = 0; i < mp.length; i++) { 4638 try { 4639 args[i] = mp[i].resolve(req, res); 4640 } catch (RestException e) { 4641 throw e; 4642 } catch (Exception e) { 4643 throw new BadRequest(e, 4644 "Invalid data conversion. Could not convert {0} ''{1}'' to type ''{2}'' on method ''{3}.{4}''.", 4645 mp[i].getParamType().name(), mp[i].getName(), mp[i].getType(), m.getDeclaringClass().getName(), m.getName() 4646 ); 4647 } 4648 } 4649 try { 4650 m.invoke(resource, args); 4651 } catch (RestException e) { 4652 throw e; 4653 } catch (Exception e) { 4654 throw new InternalServerError(e); 4655 } 4656 } 4657 } 4658 4659 /* 4660 * Calls all @RestHook(START) methods. 4661 */ 4662 void startCall(HttpServletRequest req, HttpServletResponse res) { 4663 for (int i = 0; i < startCallMethods.length; i++) 4664 startOrFinish(resource, startCallMethods[i], startCallMethodParams[i], req, res); 4665 } 4666 4667 /* 4668 * Calls all @RestHook(FINISH) methods. 4669 */ 4670 void finishCall(HttpServletRequest req, HttpServletResponse res) { 4671 for (int i = 0; i < endCallMethods.length; i++) 4672 startOrFinish(resource, endCallMethods[i], endCallMethodParams[i], req, res); 4673 } 4674 4675 private static void startOrFinish(Object resource, Method m, Class<?>[] p, HttpServletRequest req, HttpServletResponse res) throws RestException, InternalServerError { 4676 if (m != null) { 4677 Object[] args = new Object[p.length]; 4678 for (int i = 0; i < p.length; i++) { 4679 if (p[i] == HttpServletRequest.class) 4680 args[i] = req; 4681 else if (p[i] == HttpServletResponse.class) 4682 args[i] = res; 4683 } 4684 try { 4685 m.invoke(resource, args); 4686 } catch (RestException e) { 4687 throw e; 4688 } catch (Exception e) { 4689 throw new InternalServerError(e); 4690 } 4691 } 4692 } 4693 4694 /** 4695 * Calls all @RestHook(POST_INIT) methods in parent-to-child order. 4696 * 4697 * @return This object (for method chaining). 4698 * @throws ServletException 4699 */ 4700 public RestContext postInit() throws ServletException { 4701 for (int i = 0; i < postInitMethods.length; i++) 4702 postInitOrDestroy(resource, postInitMethods[i], postInitMethodParams[i]); 4703 for (RestContext childContext : this.childResources.values()) 4704 childContext.postInit(); 4705 return this; 4706 } 4707 4708 /** 4709 * Calls all @RestHook(POST_INIT_CHILD_FIRST) methods in child-to-parent order. 4710 * 4711 * @return This object (for method chaining). 4712 * @throws ServletException 4713 */ 4714 public RestContext postInitChildFirst() throws ServletException { 4715 for (RestContext childContext : this.childResources.values()) 4716 childContext.postInitChildFirst(); 4717 for (int i = 0; i < postInitChildFirstMethods.length; i++) 4718 postInitOrDestroy(resource, postInitChildFirstMethods[i], postInitChildFirstMethodParams[i]); 4719 return this; 4720 } 4721 4722 private void postInitOrDestroy(Object r, Method m, Class<?>[] p) { 4723 if (m != null) { 4724 Object[] args = new Object[p.length]; 4725 for (int i = 0; i < p.length; i++) { 4726 if (p[i] == RestContext.class) 4727 args[i] = this; 4728 else if (p[i] == RestContextBuilder.class) 4729 args[i] = this.builder; 4730 else if (p[i] == ServletConfig.class) 4731 args[i] = this.builder.inner; 4732 } 4733 try { 4734 m.invoke(r, args); 4735 } catch (RestException e) { 4736 throw e; 4737 } catch (Exception e) { 4738 throw new InternalServerError(e); 4739 } 4740 } 4741 } 4742 4743 /** 4744 * Calls {@link Servlet#destroy()} on any child resources defined on this resource. 4745 */ 4746 protected void destroy() { 4747 for (int i = 0; i < destroyMethods.length; i++) { 4748 try { 4749 postInitOrDestroy(resource, destroyMethods[i], destroyMethodParams[i]); 4750 } catch (Exception e) { 4751 e.printStackTrace(); 4752 } 4753 } 4754 4755 for (RestContext r : childResources.values()) { 4756 r.destroy(); 4757 if (r.resource instanceof Servlet) 4758 ((Servlet)r.resource).destroy(); 4759 } 4760 } 4761 4762 /** 4763 * Unused. 4764 */ 4765 @Override /* BeanContextBuilder */ 4766 public BeanSession createSession(BeanSessionArgs args) { 4767 throw new NoSuchMethodError(); 4768 } 4769 4770 /** 4771 * Unused. 4772 */ 4773 @Override /* BeanContextBuilder */ 4774 public BeanSessionArgs createDefaultSessionArgs() { 4775 throw new NoSuchMethodError(); 4776 } 4777 4778 /** 4779 * Returns the HTTP request object for the current request. 4780 * 4781 * @return The HTTP request object, or <jk>null</jk> if it hasn't been created. 4782 */ 4783 public RestRequest getRequest() { 4784 return req.get(); 4785 } 4786 4787 void setRequest(RestRequest req) { 4788 // Must be careful not to bleed thread-locals. 4789 if (this.req.get() != null) 4790 System.err.println("WARNING: Thread-local request object was not cleaned up from previous request. " + this + ", thread=["+Thread.currentThread().getId()+"]"); 4791 this.req.set(req); 4792 } 4793 4794 /** 4795 * Returns the HTTP response object for the current request. 4796 * 4797 * @return The HTTP response object, or <jk>null</jk> if it hasn't been created. 4798 */ 4799 public RestResponse getResponse() { 4800 return res.get(); 4801 } 4802 4803 void setResponse(RestResponse res) { 4804 // Must be careful not to bleed thread-locals. 4805 if (this.res.get() != null) 4806 System.err.println("WARNING: Thread-local response object was not cleaned up from previous request. " + this + ", thread=["+Thread.currentThread().getId()+"]"); 4807 this.res.set(res); 4808 } 4809 4810 /** 4811 * Clear any request state information on this context. 4812 * This should always be called in a finally block in the RestServlet. 4813 */ 4814 void clearState() { 4815 req.remove(); 4816 res.remove(); 4817 } 4818 4819 @Override 4820 public String toString() { 4821 Object r = getResource(); 4822 return "RestContext: hashCode=["+System.identityHashCode(this)+"], resource=["+(r == null ? null : r.getClass()+","+System.identityHashCode(r))+"]"; 4823 } 4824}