001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.rest; 018 019import static jakarta.servlet.http.HttpServletResponse.*; 020import static java.util.Collections.*; 021import static org.apache.juneau.commons.reflect.ReflectionUtils.*; 022import static org.apache.juneau.commons.utils.AssertionUtils.*; 023import static org.apache.juneau.commons.utils.ClassUtils.*; 024import static org.apache.juneau.commons.utils.CollectionUtils.*; 025import static org.apache.juneau.commons.utils.PredicateUtils.*; 026import static org.apache.juneau.commons.utils.IoUtils.*; 027import static org.apache.juneau.commons.utils.StringUtils.*; 028import static org.apache.juneau.commons.utils.Utils.*; 029import static org.apache.juneau.http.HttpHeaders.*; 030import static org.apache.juneau.rest.annotation.RestOpAnnotation.*; 031import static org.apache.juneau.rest.processor.ResponseProcessor.*; 032import static org.apache.juneau.rest.util.RestUtils.*; 033 034import java.io.*; 035import java.lang.annotation.*; 036import java.lang.reflect.*; 037import java.lang.reflect.Method; 038import java.nio.charset.*; 039import java.time.*; 040import java.util.*; 041import java.util.concurrent.*; 042import java.util.concurrent.atomic.*; 043import java.util.function.*; 044import java.util.logging.*; 045import java.util.stream.*; 046 047import org.apache.http.Header; 048import org.apache.juneau.*; 049import org.apache.juneau.bean.swagger.Swagger; 050import org.apache.juneau.commons.collections.*; 051import org.apache.juneau.commons.collections.FluentMap; 052import org.apache.juneau.commons.lang.*; 053import org.apache.juneau.commons.reflect.*; 054import org.apache.juneau.commons.utils.*; 055import org.apache.juneau.config.*; 056import org.apache.juneau.config.vars.*; 057import org.apache.juneau.cp.*; 058import org.apache.juneau.encoders.*; 059import org.apache.juneau.html.*; 060import org.apache.juneau.html.annotation.*; 061import org.apache.juneau.http.annotation.*; 062import org.apache.juneau.http.header.*; 063import org.apache.juneau.http.response.*; 064import org.apache.juneau.httppart.*; 065import org.apache.juneau.httppart.HttpPartSerializer.*; 066import org.apache.juneau.jsonschema.*; 067import org.apache.juneau.oapi.*; 068import org.apache.juneau.parser.*; 069import org.apache.juneau.parser.ParseException; 070import org.apache.juneau.rest.annotation.*; 071import org.apache.juneau.rest.arg.*; 072import org.apache.juneau.rest.debug.*; 073import org.apache.juneau.rest.httppart.*; 074import org.apache.juneau.rest.logger.*; 075import org.apache.juneau.rest.processor.*; 076import org.apache.juneau.rest.rrpc.*; 077import org.apache.juneau.rest.servlet.*; 078import org.apache.juneau.rest.staticfile.*; 079import org.apache.juneau.rest.stats.*; 080import org.apache.juneau.rest.swagger.*; 081import org.apache.juneau.rest.util.*; 082import org.apache.juneau.rest.vars.*; 083import org.apache.juneau.serializer.*; 084import org.apache.juneau.svl.*; 085import org.apache.juneau.svl.vars.*; 086import jakarta.servlet.*; 087import jakarta.servlet.http.*; 088 089/** 090 * Defines the initial configuration of a <c>RestServlet</c> or <c>@Rest</c> annotated object. 091 * 092 * <p> 093 * An extension of the {@link ServletConfig} object used during servlet initialization. 094 * 095 * <p> 096 * Methods are provided for overriding or augmenting the information provided by the <ja>@Rest</ja> annotation. 097 * In general, most information provided in the <ja>@Rest</ja> annotation can be specified programmatically 098 * through calls on this object. 099 * 100 * <p> 101 * To interact with this object, simply pass it in as a constructor argument or in an INIT hook. 102 * <p class='bjava'> 103 * <jc>// Option #1 - Pass in through constructor.</jc> 104 * <jk>public</jk> MyResource(RestContext.Builder <jv>builder</jv>) { 105 * <jv>builder</jv> 106 * .swaps(TemporalCalendarSwap.IsoLocalDateTime.<jk>class</jk>); 107 * } 108 * 109 * <jc>// Option #2 - Use an init hook.</jc> 110 * <ja>@RestInit</ja> 111 * <jk>public void</jk> init(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 112 * <jv>builder</jv> 113 * .swaps(TemporalCalendarSwap.IsoLocalDateTime.<jk>class</jk>); 114 * } 115 * </p> 116 * 117 * <h5 class='section'>Notes:</h5><ul> 118 * <li class='note'>This class is thread safe and reusable. 119 * </ul> 120 * 121 * <h5 class='section'>See Also:</h5><ul> 122 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestContext">RestContext</a> 123 124 * </ul> 125 */ 126public class RestContext extends Context { 127 /** 128 * Builder class. 129 */ 130 public static class Builder extends Context.Builder implements ServletConfig { 131 132 private static final AnnotationProvider AP = AnnotationProvider.INSTANCE; 133 134 // @formatter:off 135 private static final Set<Class<?>> DELAYED_INJECTION = set( 136 BeanContext.Builder.class, 137 BeanStore.Builder.class, 138 BeanStore.class, 139 CallLogger.Builder.class, 140 CallLogger.class, 141 Config.class, 142 DebugEnablement.Builder.class, 143 DebugEnablement.class, 144 EncoderSet.Builder.class, 145 EncoderSet.class, 146 FileFinder.Builder.class, 147 FileFinder.class, 148 HttpPartParser.class, 149 HttpPartParser.Creator.class, 150 HttpPartSerializer.class, 151 HttpPartSerializer.Creator.class, 152 JsonSchemaGenerator.Builder.class, 153 JsonSchemaGenerator.class, 154 Logger.class, 155 Messages.Builder.class, 156 Messages.class, 157 MethodExecStore.Builder.class, 158 MethodExecStore.class, 159 ParserSet.Builder.class, 160 ParserSet.class, 161 ResponseProcessorList.Builder.class, 162 ResponseProcessorList.class, 163 RestChildren.Builder.class, 164 RestChildren.class, 165 RestOpArgList.Builder.class, 166 RestOpArgList.class, 167 RestOperations.Builder.class, 168 RestOperations.class, 169 SerializerSet.Builder.class, 170 SerializerSet.class, 171 StaticFiles.Builder.class, 172 StaticFiles.class, 173 SwaggerProvider.Builder.class, 174 SwaggerProvider.class, 175 ThrownStore.Builder.class, 176 ThrownStore.class, 177 VarList.class, 178 VarResolver.Builder.class, 179 VarResolver.class 180 ); 181 182 private static final Set<String> DELAYED_INJECTION_NAMES = set( 183 "defaultRequestAttributes", 184 "defaultRequestHeaders", 185 "defaultResponseHeaders", 186 "destroyMethods", 187 "endCallMethods", 188 "postCallMethods", 189 "postInitChildFirstMethods", 190 "postInitMethods", 191 "preCallMethods", 192 "startCallMethods" 193 ); 194 // @formatter:on 195 196 //----------------------------------------------------------------------------------------------------------------- 197 // The following fields are meant to be modifiable. 198 // They should not be declared final. 199 // Read-only snapshots of these will be made in RestServletContext. 200 //----------------------------------------------------------------------------------------------------------------- 201 202 private static <T extends Annotation> Stream<Method> getAnnotatedMethods(Supplier<?> resource, Class<T> annotation) { 203 return ClassInfo.ofProxy(resource.get()).getAllMethodsTopDown().stream() 204 .filter(y -> y.hasAnnotation(annotation)) 205 .filter(distinctByKey(MethodInfo::getSignature)) 206 .map(y -> y.accessible().inner()); 207 } 208 209 private static boolean isRestInjectMethod(MethodInfo mi) { 210 return isRestInjectMethod(mi, null); 211 } 212 213 private static boolean isRestInjectMethod(MethodInfo mi, String name) { 214 return mi.getAnnotations(RestInject.class) 215 .map(AnnotationInfo::inner) 216 .filter(x -> nn(x) && x.methodScope().length == 0 && (n(name) || eq(x.name(), name))) 217 .findFirst() 218 .isPresent(); 219 } 220 221 private BeanContext.Builder beanContext; 222 private BeanCreator<CallLogger> callLogger; 223 private BeanCreator<DebugEnablement> debugEnablement; 224 private BeanCreator<StaticFiles> staticFiles; 225 private BeanCreator<SwaggerProvider> swaggerProvider; 226 private BeanStore beanStore; 227 private BeanStore rootBeanStore; 228 private boolean disableContentParam = env("RestContext.disableContentParam", false); 229 private boolean initialized; 230 private boolean renderResponseStackTraces = env("RestContext.renderResponseStackTraces", false); 231 private Charset defaultCharset = env("RestContext.defaultCharset").map(Charset::forName).orElse(UTF8); 232 private Class<? extends RestChildren> childrenClass = RestChildren.class; 233 private Class<? extends RestOpContext> opContextClass = RestOpContext.class; 234 private final Class<?> resourceClass; 235 private Config config; 236 private DefaultClassList defaultClasses; 237 private DefaultSettingsMap defaultSettings; 238 private EncoderSet.Builder encoders; 239 private HeaderList defaultRequestHeaders; 240 private HeaderList defaultResponseHeaders; 241 private HttpPartParser.Creator partParser; 242 private HttpPartSerializer.Creator partSerializer; 243 private JsonSchemaGenerator.Builder jsonSchemaGenerator; 244 private List<MediaType> consumes; 245 private List<MediaType> produces; 246 private List<Object> children = list(); 247 private Logger logger; 248 private long maxInput = env("RestContext.maxInput").map(StringUtils::parseLongWithSuffix).orElse(100_000_000l); 249 private MethodExecStore.Builder methodExecStore; 250 private MethodList destroyMethods; 251 private MethodList endCallMethods; 252 private MethodList postCallMethods; 253 private MethodList postInitChildFirstMethods; 254 private MethodList postInitMethods; 255 private MethodList preCallMethods; 256 private MethodList startCallMethods; 257 private Messages.Builder messages; 258 private NamedAttributeMap defaultRequestAttributes; 259 private ParserSet.Builder parsers; 260 private final RestContext parentContext; 261 private RestChildren.Builder restChildren; 262 private RestOpArgList.Builder restOpArgs; 263 private RestOperations.Builder restOperations; 264 private ResponseProcessorList.Builder responseProcessors; 265 private ResourceSupplier resource; 266 private SerializerSet.Builder serializers; 267 private final ServletConfig inner; 268 private String allowedHeaderParams = env("RestContext.allowedHeaderParams", "Accept,Content-Type"); 269 private String allowedMethodHeaders = env("RestContext.allowedMethodHeaders", ""); 270 private String allowedMethodParams = env("RestContext.allowedMethodParams", "HEAD,OPTIONS"); 271 private String clientVersionHeader = env("RestContext.clientVersionHeader", "Client-Version"); 272 private String path = null; 273 private String uriAuthority = env("RestContext.uriAuthority").orElse(null); 274 private String uriContext = env("RestContext.uriContext").orElse(null); 275 private ThrownStore.Builder thrownStore; 276 private UriRelativity uriRelativity = env("RestContext.uriRelativity", UriRelativity.RESOURCE); 277 private UriResolution uriResolution = env("RestContext.uriResolution", UriResolution.ROOT_RELATIVE); 278 private VarResolver.Builder varResolver; 279 280 /** 281 * Constructor. 282 * 283 * @param resourceClass 284 * The REST servlet/bean type that this context is defined against. 285 * @param parentContext The parent context if this is a child of another resource. 286 * @param servletConfig The servlet config if available. 287 */ 288 protected Builder(Class<?> resourceClass, RestContext parentContext, ServletConfig servletConfig) { 289 290 this.resourceClass = resourceClass; 291 this.inner = servletConfig; 292 this.parentContext = parentContext; 293 294 // Pass-through default values. 295 if (nn(parentContext)) { 296 defaultClasses = parentContext.defaultClasses.copy(); 297 defaultSettings = parentContext.defaultSettings.copy(); 298 rootBeanStore = parentContext.rootBeanStore; 299 } else { 300 defaultClasses = DefaultClassList.create(); 301 defaultSettings = DefaultSettingsMap.create(); 302 } 303 } 304 305 /** 306 * Allowed header URL parameters. 307 * 308 * <p> 309 * When specified, allows headers such as <js>"Accept"</js> and <js>"Content-Type"</js> to be passed in as URL query 310 * parameters. 311 * <br> 312 * For example: 313 * <p class='burlenc'> 314 * ?Accept=text/json&Content-Type=text/json 315 * </p> 316 * 317 * <h5 class='section'>Notes:</h5><ul> 318 * <li class='note'> 319 * Useful for debugging REST interface using only a browser so that you can quickly simulate header values 320 * in the URL bar. 321 * <li class='note'> 322 * Header names are case-insensitive. 323 * <li class='note'> 324 * Use <js>"*"</js> to allow any headers to be specified as URL parameters. 325 * <li class='note'> 326 * Use <js>"NONE"</js> (case insensitive) to suppress inheriting a value from a parent class. 327 * </ul> 328 * 329 * <h5 class='section'>See Also:</h5><ul> 330 * <li class='ja'>{@link Rest#allowedHeaderParams} 331 * </ul> 332 * 333 * @param value 334 * The new value for this setting. 335 * <br>The default is the first value found: 336 * <ul> 337 * <li>System property <js>"RestContext.allowedHeaderParams" 338 * <li>Environment variable <js>"RESTCONTEXT_ALLOWEDHEADERPARAMS" 339 * <li><js>"Accept,Content-Type"</js> 340 * </ul> 341 * <br>Cannot be <jk>null</jk>. 342 * @return This object. 343 */ 344 public Builder allowedHeaderParams(String value) { 345 allowedHeaderParams = assertArgNotNull("value", value); 346 return this; 347 } 348 349 /** 350 * Allowed method headers. 351 * 352 * <p> 353 * A comma-delimited list of HTTP method names that are allowed to be passed as values in an <c>X-Method</c> HTTP header 354 * to override the real HTTP method name. 355 * 356 * <p> 357 * Allows you to override the actual HTTP method with a simulated method. 358 * <br>For example, if an HTTP Client API doesn't support <c>PATCH</c> but does support <c>POST</c> (because 359 * <c>PATCH</c> is not part of the original HTTP spec), you can add a <c>X-Method: PATCH</c> header on a normal 360 * <c>HTTP POST /foo</c> request call which will make the HTTP call look like a <c>PATCH</c> request in any of the REST APIs. 361 * 362 * <h5 class='section'>Example:</h5> 363 * <p class='bjava'> 364 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 365 * <ja>@Rest</ja>(allowedMethodHeaders=<js>"PATCH"</js>) 366 * <jk>public class</jk> MyResource { 367 * 368 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 369 * <jk>public</jk> MyResource(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 370 * 371 * <jc>// Using method on builder.</jc> 372 * <jv>builder</jv>.allowedMethodHeaders(<js>"PATCH"</js>); 373 * } 374 * 375 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 376 * <ja>@RestInit</ja> 377 * <jk>public void</jk> init(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 378 * <jv>builder</jv>.allowedMethodHeaders(<js>"PATCH"</js>); 379 * } 380 * } 381 * </p> 382 * 383 * <h5 class='section'>Notes:</h5><ul> 384 * <li class='note'> 385 * Method names are case-insensitive. 386 * <li class='note'> 387 * Use <js>"*"</js> to represent all methods. 388 * <li class='note'> 389 * Use <js>"NONE"</js> (case insensitive) to suppress inheriting a value from a parent class. 390 * </ul> 391 * 392 * <h5 class='section'>See Also:</h5><ul> 393 * <li class='ja'>{@link Rest#allowedMethodHeaders} 394 * </ul> 395 * 396 * @param value 397 * The new value for this setting. 398 * <br>The default is the first value found: 399 * <ul> 400 * <li>System property <js>"RestContext.allowedMethodHeaders" 401 * <li>Environment variable <js>"RESTCONTEXT_ALLOWEDMETHODHEADERS" 402 * <li><js>""</js> 403 * </ul> 404 * <br>Cannot be <jk>null</jk>. 405 * @return This object. 406 */ 407 public Builder allowedMethodHeaders(String value) { 408 allowedMethodHeaders = assertArgNotNull("value", value); 409 return this; 410 } 411 412 /** 413 * Allowed method parameters. 414 * 415 * <p> 416 * When specified, the HTTP method can be overridden by passing in a <js>"method"</js> URL parameter on a regular 417 * GET request. 418 * <br> 419 * For example: 420 * <p class='burlenc'> 421 * ?method=OPTIONS 422 * </p> 423 * 424 * <p> 425 * Useful in cases where you want to simulate a non-GET request in a browser by simply adding a parameter. 426 * <br>Also useful if you want to construct hyperlinks to non-GET REST endpoints such as links to <c>OPTIONS</c> 427 * pages. 428 * 429 * <p> 430 * Note that per the <a class="doclink" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html">HTTP specification</a>, special care should 431 * be taken when allowing non-safe (<c>POST</c>, <c>PUT</c>, <c>DELETE</c>) methods to be invoked through GET requests. 432 * 433 * <h5 class='section'>Example:</h5> 434 * <p class='bjava'> 435 * <jc>// Option #1 - Defined via annotation.</jc> 436 * <ja>@Rest</ja>(allowedMethodParams=<js>"HEAD,OPTIONS,PUT"</js>) 437 * <jk>public class</jk> MyResource { 438 * 439 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 440 * <jk>public</jk> MyResource(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 441 * 442 * <jc>// Using method on builder.</jc> 443 * <jv>builder</jv>.allowedMethodParams(<js>"HEAD,OPTIONS,PUT"</js>); 444 * } 445 * 446 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 447 * <ja>@RestInit</ja> 448 * <jk>public void</jk> init(RestContext.Builder builder) <jk>throws</jk> Exception { 449 * <jv>builder</jv>.allowedMethodParams(<js>"HEAD,OPTIONS,PUT"</js>); 450 * } 451 * } 452 * </p> 453 * 454 * <h5 class='section'>Notes:</h5><ul> 455 * <li class='note'> 456 * Format is a comma-delimited list of HTTP method names that can be passed in as a method parameter. 457 * <li class='note'> 458 * <js>'method'</js> parameter name is case-insensitive. 459 * <li class='note'> 460 * Use <js>"*"</js> to represent all methods. 461 * <li class='note'> 462 * Use <js>"NONE"</js> (case insensitive) to suppress inheriting a value from a parent class. 463 * </ul> 464 * 465 * <h5 class='section'>See Also:</h5><ul> 466 * <li class='ja'>{@link Rest#allowedMethodParams} 467 * </ul> 468 * 469 * @param value 470 * The new value for this setting. 471 * <br>The default is the first value found: 472 * <ul> 473 * <li>System property <js>"RestContext.allowedMethodParams" 474 * <li>Environment variable <js>"RESTCONTEXT_ALLOWEDMETHODPARAMS" 475 * <li><js>"HEAD,OPTIONS"</js> 476 * </ul> 477 * <br>Cannot be <jk>null</jk>. 478 * @return This object. 479 */ 480 public Builder allowedMethodParams(String value) { 481 allowedMethodParams = assertArgNotNull("value", value); 482 return this; 483 } 484 485 @Override /* Overridden from Builder */ 486 public Builder annotations(Annotation...values) { 487 super.annotations(values); 488 return this; 489 } 490 491 @Override /* Overridden from Builder */ 492 public Builder apply(AnnotationWorkList work) { 493 super.apply(work); 494 return this; 495 } 496 497 @Override /* Overridden from Builder */ 498 public Builder applyAnnotations(Class<?>...from) { 499 super.applyAnnotations(from); 500 return this; 501 } 502 503 @Override /* Overridden from Builder */ 504 public Builder applyAnnotations(Object...from) { 505 super.applyAnnotations(from); 506 return this; 507 } 508 509 /** 510 * Returns the bean context sub-builder. 511 * 512 * <p> 513 * The bean context is used to retrieve metadata on Java beans. 514 * 515 * <p> 516 * The default bean context can overridden via any of the following: 517 * <ul class='spaced-list'> 518 * <li>Injected via bean store. 519 * </ul> 520 * 521 * @return The bean context sub-builder. 522 */ 523 public BeanContext.Builder beanContext() { 524 if (beanContext == null) 525 beanContext = createBeanContext(beanStore(), resource()); 526 return beanContext; 527 } 528 529 /** 530 * Returns the bean store in this builder. 531 * 532 * <p> 533 * The bean store is a simple storage database for beans keyed by type and name. 534 * 535 * <p> 536 * The bean store is created with the parent root bean store as the parent, allowing any beans in the root bean store to be available 537 * in this builder. The root bean store typically pulls from an injection framework such as Spring to allow injected beans to be used. 538 * 539 * <p> 540 * The default bean store can be overridden via any of the following: 541 * <ul class='spaced-list'> 542 * <li>Class annotation: {@link Rest#beanStore() @Rest(beanStore)} 543 * <li>{@link RestInject @RestInject}-annotated methods: 544 * <p class='bjava'> 545 * <ja>@RestInject</ja> <jk>public</jk> [<jk>static</jk>] BeanStore myMethod(<i><args></i>) {...} 546 * </p> 547 * Args can be any injected bean including {@link org.apache.juneau.cp.BeanStore.Builder}, the default builder. 548 * </ul> 549 * 550 * @return The bean store in this builder. 551 */ 552 public BeanStore beanStore() { 553 return beanStore; 554 } 555 556 /** 557 * Adds a bean to the bean store of this class. 558 * 559 * <p> 560 * Equivalent to calling: 561 * <p class='bjava'> 562 * <jv>builder</jv>.beanStore().add(<jv>beanType</jv>, <jv>bean</jv>); 563 * </p> 564 * 565 * <h5 class='section'>See Also:</h5><ul> 566 * <li class='jm'>{@link #beanStore()} 567 * </ul> 568 * 569 * @param <T> The class to associate this bean with. 570 * @param beanType The class to associate this bean with. 571 * @param bean The bean. 572 * <br>Can be <jk>null</jk> (a null bean will be stored in the bean store). 573 * @return This object. 574 */ 575 public <T> Builder beanStore(Class<T> beanType, T bean) { 576 beanStore().addBean(beanType, bean); 577 return this; 578 } 579 580 /** 581 * Adds a bean to the bean store of this class. 582 * 583 * <p> 584 * Equivalent to calling: 585 * <p class='bjava'> 586 * <jv>builder</jv>.beanStore().add(<jv>beanType</jv>, <jv>bean</jv>, <jv>name</jv>); 587 * </p> 588 * 589 * <h5 class='section'>See Also:</h5><ul> 590 * <li class='jm'>{@link #beanStore()} 591 * </ul> 592 * 593 * @param <T> The class to associate this bean with. 594 * @param beanType The class to associate this bean with. 595 * @param bean The bean. 596 * <br>Can be <jk>null</jk> (a null bean will be stored in the bean store). 597 * @param name The bean name if this is a named bean. 598 * <br>Can be <jk>null</jk> (bean will be stored as an unnamed bean). 599 * @return This object. 600 */ 601 public <T> Builder beanStore(Class<T> beanType, T bean, String name) { 602 beanStore().addBean(beanType, bean, name); 603 return this; 604 } 605 606 @Override /* Overridden from BeanContext.Builder */ 607 public RestContext build() { 608 try { 609 return beanStore().createBean(RestContext.class).type(getType().orElse(RestContext.class)).builder(RestContext.Builder.class, this).run(); 610 } catch (Exception e) { 611 e.printStackTrace(); // NOSONAR 612 throw new InternalServerError(e, "Could not instantiate RestContext."); 613 } 614 } 615 616 @Override /* Overridden from Builder */ 617 public Builder cache(Cache<HashKey,? extends org.apache.juneau.Context> value) { 618 super.cache(value); 619 return this; 620 } 621 622 /** 623 * Returns the call logger bean creator. 624 * 625 * <p> 626 * Specifies the logger to use for logging of HTTP requests and responses. 627 * 628 * <h5 class='section'>Example:</h5> 629 * <p class='bjava'> 630 * <jc>// Our customized logger.</jc> 631 * <jk>public class</jk> MyLogger <jk>extends</jk> BasicCallLogger { 632 * 633 * <jk>public</jk> MyLogger(BeanStore <jv>beanStore</jv>) { 634 * <jk>super</jk>(<jv>beanStore</jv>); 635 * } 636 * 637 * <ja>@Override</ja> 638 * <jk>protected void</jk> log(Level <jv>level</jv>, String <jv>msg</jv>, Throwable <jv>e</jv>) { 639 * <jc>// Handle logging ourselves.</jc> 640 * } 641 * } 642 * 643 * <jc>// Option #1 - Registered via annotation resolving to a config file setting with default value.</jc> 644 * <ja>@Rest</ja>(callLogger=MyLogger.<jk>class</jk>) 645 * <jk>public class</jk> MyResource { 646 * 647 * <jc>// Option #2 - Registered via builder passed in through init method.</jc> 648 * <ja>@RestInit</ja> 649 * <jk>public void</jk> init(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 650 * <jv>builder</jv>.callLogger(MyLogger.<jk>class</jk>); 651 * } 652 * } 653 * </p> 654 * 655 * <p> 656 * The default call logger can overridden via any of the following: 657 * <ul class='spaced-list'> 658 * <li>Injected via bean store. 659 * <li>Class annotation: {@link Rest#callLogger() @Rest(callLogger)} 660 * <li>{@link RestInject @RestInject}-annotated method: 661 * <p class='bjava'> 662 * <ja>@RestInject</ja> <jk>public</jk> [<jk>static</jk>] CallLogger myMethod(<i><args></i>) {...} 663 * </p> 664 * Args can be any injected bean. 665 * </ul> 666 * 667 * <h5 class='section'>Notes:</h5><ul> 668 * <li class='note'> 669 * The default call logger if not specified is {@link BasicCallLogger}. 670 * <li class='note'> 671 * The resource class itself will be used if it implements the {@link CallLogger} interface and not 672 * explicitly overridden via this annotation. 673 * <li class='note'> 674 * When defined as a class, the implementation must have one of the following constructor: 675 * <ul> 676 * <li><code><jk>public</jk> T(BeanStore)</code> 677 * </ul> 678 * <li class='note'> 679 * Inner classes of the REST resource class are allowed. 680 * </ul> 681 * 682 * <h5 class='section'>See Also:</h5><ul> 683 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerLoggingAndDebugging">Logging / Debugging</a> 684 * </ul> 685 * 686 * @return The call logger sub-builder. 687 * @throws RuntimeException If {@link #init(Supplier)} has not been called. 688 */ 689 public BeanCreator<CallLogger> callLogger() { 690 if (callLogger == null) 691 callLogger = createCallLogger(beanStore, resource); 692 return callLogger; 693 } 694 695 /** 696 * Specifies the call logger for this class. 697 * 698 * <p> 699 * Equivalent to calling: 700 * <p class='bjava'> 701 * <jv>builder</jv>.callLogger().impl(<jv>value</jv>); 702 * </p> 703 * 704 * <h5 class='section'>See Also:</h5><ul> 705 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerLoggingAndDebugging">Logging / Debugging</a> 706 * <li class='jm'>{@link #callLogger()} 707 * </ul> 708 * 709 * @param value The new value. 710 * <br>Cannot be <jk>null</jk>. 711 * @return This object. 712 */ 713 public Builder callLogger(CallLogger value) { 714 callLogger().impl(assertArgNotNull("value", value)); 715 return this; 716 } 717 718 /** 719 * Specifies the call logger for this class. 720 * 721 * <p> 722 * Equivalent to calling: 723 * <p class='bjava'> 724 * <jv>builder</jv>.callLogger().type(<jv>value</jv>); 725 * </p> 726 * 727 * <h5 class='section'>See Also:</h5><ul> 728 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerLoggingAndDebugging">Logging / Debugging</a> 729 * <li class='jm'>{@link #callLogger()} 730 * </ul> 731 * 732 * @param value The new value. 733 * <br>Cannot be <jk>null</jk>. 734 * @return This object. 735 */ 736 public Builder callLogger(Class<? extends CallLogger> value) { 737 callLogger().type(assertArgNotNull("value", value)); 738 return this; 739 } 740 741 /** 742 * Add a child REST resource. 743 * 744 * <p> 745 * Shortcut for adding a single child to this resource. 746 * 747 * <p> 748 * This can be used for resources that don't have a {@link Rest#path() @Rest(path)} annotation. 749 * 750 * @param path The child path relative to the parent resource URI. 751 * <br>Cannot be <jk>null</jk>. 752 * @param child The child to add to this resource. 753 * @return This object. 754 */ 755 public Builder child(String path, Object child) { 756 children.add(new RestChild(assertArgNotNull("path", path), child)); 757 return this; 758 } 759 760 /** 761 * Child REST resources. 762 * 763 * <p> 764 * Defines children of this resource. 765 * 766 * <p> 767 * A REST child resource is simply another servlet or object that is initialized as part of the ascendant resource and has a 768 * servlet path directly under the ascendant resource object path. 769 * <br>The main advantage to defining servlets as REST children is that you do not need to define them in the 770 * <c>web.xml</c> file of the web application. 771 * <br>This can cut down on the number of entries that show up in the <c>web.xml</c> file if you are defining 772 * large numbers of servlets. 773 * 774 * <p> 775 * Child resources must specify a value for {@link Rest#path() @Rest(path)} that identifies the subpath of the child resource 776 * relative to the ascendant path UNLESS you use the {@link RestContext.Builder#child(String, Object)} method to register it. 777 * 778 * <p> 779 * Child resources can be nested arbitrarily deep using this technique (i.e. children can also have children). 780 * 781 * <dl> 782 * <dt>Servlet initialization:</dt> 783 * <dd> 784 * <p> 785 * A child resource will be initialized immediately after the ascendant servlet/resource is initialized. 786 * <br>The child resource receives the same servlet config as the ascendant servlet/resource. 787 * <br>This allows configuration information such as servlet initialization parameters to filter to child 788 * resources. 789 * </p> 790 * </dd> 791 * <dt>Runtime behavior:</dt> 792 * <dd> 793 * <p> 794 * As a rule, methods defined on the <c>HttpServletRequest</c> object will behave as if the child 795 * servlet were deployed as a top-level resource under the child's servlet path. 796 * <br>For example, the <c>getServletPath()</c> and <c>getPathInfo()</c> methods on the 797 * <c>HttpServletRequest</c> object will behave as if the child resource were deployed using the 798 * child's servlet path. 799 * <br>Therefore, the runtime behavior should be equivalent to deploying the child servlet in the 800 * <c>web.xml</c> file of the web application. 801 * </p> 802 * </dd> 803 * </dl> 804 * 805 * <h5 class='section'>Example:</h5> 806 * <p class='bjava'> 807 * <jc>// Our child resource.</jc> 808 * <ja>@Rest</ja>(path=<js>"/child"</js>) 809 * <jk>public class</jk> MyChildResource {...} 810 * 811 * <jc>// Option #1 - Registered via annotation.</jc> 812 * <ja>@Rest</ja>(children={MyChildResource.<jk>class</jk>}) 813 * <jk>public class</jk> MyResource { 814 * 815 * <jc>// Option #2 - Registered via builder passed in through resource constructor.</jc> 816 * <jk>public</jk> MyResource(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 817 * 818 * <jc>// Using method on builder.</jc> 819 * <jv>builder</jv>.children(MyChildResource.<jk>class</jk>); 820 * 821 * <jc>// Use a pre-instantiated object instead.</jc> 822 * <jv>builder</jv>.child(<js>"/child"</js>, <jk>new</jk> MyChildResource()); 823 * } 824 * 825 * <jc>// Option #3 - Registered via builder passed in through init method.</jc> 826 * <ja>@RestInit</ja> 827 * <jk>public void</jk> init(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 828 * <jv>builder</jv>.children(MyChildResource.<jk>class</jk>); 829 * } 830 * } 831 * </p> 832 * 833 * <h5 class='section'>Notes:</h5><ul> 834 * <li class='note'> 835 * When defined as classes, instances are resolved using the registered bean store which 836 * by default is {@link BeanStore} which requires the class have one of the following 837 * constructors: 838 * <ul> 839 * <li><code><jk>public</jk> T(RestContext.Builder)</code> 840 * <li><code><jk>public</jk> T()</code> 841 * </ul> 842 * </ul> 843 * 844 * <h5 class='section'>See Also:</h5><ul> 845 * <li class='ja'>{@link Rest#children()} 846 * </ul> 847 * 848 * @param values The child resources to add. 849 * <br>Cannot contain <jk>null</jk> values. 850 * <br>Objects can be any of the specified types: 851 * <ul> 852 * <li>A class that has a constructor described above. 853 * <li>An instantiated resource object (such as a servlet object instantiated by a servlet container). 854 * <li>An instance of {@link RestChild} containing an instantiated resource object and a subpath. 855 * </ul> 856 * @return This object. 857 */ 858 public Builder children(Object...values) { 859 assertArgNoNulls("values", values); 860 addAll(children, values); 861 return this; 862 } 863 864 /** 865 * Client version header. 866 * 867 * <p> 868 * Specifies the name of the header used to denote the client version on HTTP requests. 869 * 870 * <p> 871 * The client version is used to support backwards compatibility for breaking REST interface changes. 872 * <br>Used in conjunction with {@link RestOp#clientVersion() @RestOp(clientVersion)} annotation. 873 * 874 * <h5 class='section'>Example:</h5> 875 * <p class='bjava'> 876 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 877 * <ja>@Rest</ja>(clientVersionHeader=<js>"$C{REST/clientVersionHeader,Client-Version}"</js>) 878 * <jk>public class</jk> MyResource { 879 * 880 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 881 * <jk>public</jk> MyResource(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 882 * 883 * <jc>// Using method on builder.</jc> 884 * <jv>builder</jv>.clientVersionHeader(<js>"Client-Version"</js>); 885 * } 886 * 887 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 888 * <ja>@RestInit</ja> 889 * <jk>public void</jk> init(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 890 * <jv>builder</jv>.clientVersionHeader(<js>"Client-Version"</js>); 891 * } 892 * } 893 * </p> 894 * 895 * <p class='bjava'> 896 * <jc>// Call this method if Client-Version is at least 2.0. 897 * // Note that this also matches 2.0.1.</jc> 898 * <ja>@RestGet</ja>(path=<js>"/foobar"</js>, clientVersion=<js>"2.0"</js>) 899 * <jk>public</jk> Object method1() { 900 * ... 901 * } 902 * 903 * <jc>// Call this method if Client-Version is at least 1.1, but less than 2.0.</jc> 904 * <ja>@RestGet</ja>(path=<js>"/foobar"</js>, clientVersion=<js>"[1.1,2.0)"</js>) 905 * <jk>public</jk> Object method2() { 906 * ... 907 * } 908 * 909 * <jc>// Call this method if Client-Version is less than 1.1.</jc> 910 * <ja>@RestGet</ja>(path=<js>"/foobar"</js>, clientVersion=<js>"[0,1.1)"</js>) 911 * <jk>public</jk> Object method3() { 912 * ... 913 * } 914 * </p> 915 * 916 * <h5 class='section'>See Also:</h5><ul> 917 * <li class='ja'>{@link Rest#clientVersionHeader} 918 * </ul> 919 * 920 * @param value 921 * The new value for this setting. 922 * <br>The default is the first value found: 923 * <ul> 924 * <li>System property <js>"RestContext.clientVersionHeader" 925 * <li>Environment variable <js>"RESTCONTEXT_CLIENTVERSIONHEADER" 926 * <li><js>"Client-Version"</js> 927 * </ul> 928 * <br>Cannot be <jk>null</jk>. 929 * @return This object. 930 */ 931 public Builder clientVersionHeader(String value) { 932 clientVersionHeader = assertArgNotNull("value", value); 933 return this; 934 } 935 936 /** 937 * Returns the external configuration file for this resource. 938 * 939 * <p> 940 * The config file contains arbitrary configuration information that can be accessed by this class, usually 941 * via <c>$C</c> variables. 942 * 943 * <p> 944 * The default config can be overridden via any of the following: 945 * <ul class='spaced-list'> 946 * <li>Injected via bean store. 947 * <li>Class annotation: {@link Rest#config() @Rest(config)} 948 * <li>{@link RestInject @RestInject}-annotated method: 949 * <p class='bjava'> 950 * <ja>@RestInject</ja> <jk>public</jk> [<jk>static</jk>] Config myMethod(<i><args></i>) {...} 951 * </p> 952 * Args can be any injected bean. 953 * </ul> 954 * 955 * <p> 956 * If a config file is not set up, then an empty config file will be returned that is not backed by any file. 957 * 958 * <p> 959 * This bean can be accessed directly via {@link RestContext#getConfig()} or passed in as a parameter 960 * on a {@link RestOp}-annotated method. 961 * 962 * <h5 class='section'>See Also:</h5><ul> 963 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/ConfigurationFiles">Configuration Files</a> 964 * </ul> 965 * 966 * @return The external configuration file for this resource. 967 */ 968 public Config config() { 969 return config; 970 } 971 972 /** 973 * Overwrites the default config file with a custom config file. 974 * 975 * <p> 976 * By default, the config file is determined using the {@link Rest#config() @Rest(config)} 977 * annotation. 978 * This method allows you to programmatically override it with your own custom config file. 979 * 980 * <h5 class='section'>See Also:</h5><ul> 981 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/ConfigurationFiles">Configuration Files</a> 982 * <li class='jm'>{@link #config()} 983 * </ul> 984 * 985 * @param value The new config file. 986 * <br>Cannot be <jk>null</jk>. 987 * @return This object. 988 */ 989 public Builder config(Config value) { 990 config = assertArgNotNull("value", value); 991 return this; 992 } 993 994 /** 995 * Returns the media types consumed by this resource if it's manually specified. 996 * 997 * @return The media types. 998 */ 999 public Optional<List<MediaType>> consumes() { 1000 return opt(consumes); 1001 } 1002 1003 /** 1004 * Supported content media types. 1005 * 1006 * <p> 1007 * Overrides the media types inferred from the parsers that identify what media types can be consumed by the resource. 1008 * <br>An example where this might be useful if you have parsers registered that handle media types that you 1009 * don't want exposed in the Swagger documentation. 1010 * 1011 * <h5 class='section'>Example:</h5> 1012 * <p class='bjava'> 1013 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 1014 * <ja>@Rest</ja>(consumes={<js>"$C{REST/supportedConsumes,application/json}"</js>}) 1015 * <jk>public class</jk> MyResource { 1016 * 1017 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 1018 * <jk>public</jk> MyResource(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 1019 * 1020 * <jc>// Using method on builder.</jc> 1021 * <jv>builder</jv>.consumes(<jk>false</jk>, <js>"application/json"</js>) 1022 * } 1023 * 1024 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 1025 * <ja>@RestInit</ja> 1026 * <jk>public void</jk> init(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 1027 * <jv>builder</jv>.consumes(<jk>false</jk>, <js>"application/json"</js>); 1028 * } 1029 * } 1030 * </p> 1031 * 1032 * <p> 1033 * This affects the returned values from the following: 1034 * <ul class='javatree'> 1035 * <li class='jm'>{@link RestContext#getConsumes() RestContext.getConsumes()} 1036 * </ul> 1037 * 1038 * <h5 class='section'>See Also:</h5><ul> 1039 * <li class='ja'>{@link Rest#consumes} 1040 * <li class='ja'>{@link RestOp#consumes} 1041 * <li class='ja'>{@link RestPut#consumes} 1042 * <li class='ja'>{@link RestPost#consumes} 1043 * </ul> 1044 * 1045 * @param values The values to add to this setting. 1046 * <br>Cannot contain <jk>null</jk> values. 1047 * @return This object. 1048 */ 1049 public Builder consumes(MediaType...values) { 1050 assertArgNoNulls("values", values); 1051 consumes = addAll(consumes, values); 1052 return this; 1053 } 1054 1055 @Override /* Overridden from Context.Builder */ 1056 public Builder copy() { 1057 throw new NoSuchMethodError("Not implemented."); 1058 } 1059 1060 @Override /* Overridden from Builder */ 1061 public Builder debug() { 1062 super.debug(); 1063 return this; 1064 } 1065 1066 @Override /* Overridden from Builder */ 1067 public Builder debug(boolean value) { 1068 super.debug(value); 1069 return this; 1070 } 1071 1072 /** 1073 * Sets the debug default value. 1074 * 1075 * <p> 1076 * The default debug value is the enablement value if not otherwise overridden at the class or method level. 1077 * 1078 * @param value The debug default value. 1079 * <br>Cannot be <jk>null</jk>. 1080 * @return This object. 1081 */ 1082 public Builder debugDefault(Enablement value) { 1083 defaultSettings().set("RestContext.debugDefault", assertArgNotNull("value", value)); 1084 return this; 1085 } 1086 1087 /** 1088 * Returns the debug enablement bean creator. 1089 * 1090 * <p> 1091 * Enables the following: 1092 * <ul class='spaced-list'> 1093 * <li> 1094 * HTTP request/response bodies are cached in memory for logging purposes. 1095 * <li> 1096 * Request/response messages are automatically logged always or per request. 1097 * </ul> 1098 * 1099 * @return The debug enablement sub-builder. 1100 */ 1101 public BeanCreator<DebugEnablement> debugEnablement() { 1102 if (debugEnablement == null) 1103 debugEnablement = createDebugEnablement(beanStore, resource); 1104 return debugEnablement; 1105 } 1106 1107 /** 1108 * Specifies the debug enablement class to use for this REST context. 1109 * 1110 * @param value The new value for this setting. 1111 * <br>Cannot be <jk>null</jk>. 1112 * @return This object. 1113 */ 1114 public Builder debugEnablement(Class<? extends DebugEnablement> value) { 1115 debugEnablement().type(assertArgNotNull("value", value)); 1116 return this; 1117 } 1118 1119 /** 1120 * Specifies the debug enablement class to use for this REST context. 1121 * 1122 * @param value The new value for this setting. 1123 * <br>Cannot be <jk>null</jk>. 1124 * @return This object. 1125 */ 1126 public Builder debugEnablement(DebugEnablement value) { 1127 debugEnablement().impl(assertArgNotNull("value", value)); 1128 return this; 1129 } 1130 1131 /** 1132 * Specifies a default <c>Accept</c> header value if not specified on a request. 1133 * 1134 * @param value 1135 * The default value of the <c>Accept</c> header. 1136 * <br>Ignored if <jk>null</jk> or empty. 1137 * @return This object. 1138 */ 1139 public Builder defaultAccept(String value) { 1140 if (ne(value)) 1141 defaultRequestHeaders(accept(value)); 1142 return this; 1143 } 1144 1145 /** 1146 * Default character encoding. 1147 * 1148 * <p> 1149 * The default character encoding for the request and response if not specified on the request. 1150 * 1151 * <h5 class='section'>Example:</h5> 1152 * <p class='bjava'> 1153 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 1154 * <ja>@Rest</ja>(defaultCharset=<js>"$C{REST/defaultCharset,US-ASCII}"</js>) 1155 * <jk>public class</jk> MyResource { 1156 * 1157 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 1158 * <jk>public</jk> MyResource(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 1159 * 1160 * <jc>// Using method on builder.</jc> 1161 * <jv>builder</jv>.defaultCharset(<js>"US-ASCII"</js>); 1162 * } 1163 * 1164 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 1165 * <ja>@RestInit</ja> 1166 * <jk>public void</jk> init(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 1167 * <jv>builder</jv>.defaultCharset(<js>"US-ASCII"</js>); 1168 * } 1169 * 1170 * <jc>// Override at the method level.</jc> 1171 * <ja>@RestGet</ja>(defaultCharset=<js>"UTF-16"</js>) 1172 * <jk>public</jk> Object myMethod() {...} 1173 * } 1174 * </p> 1175 * 1176 * <h5 class='section'>See Also:</h5><ul> 1177 * <li class='ja'>{@link Rest#defaultCharset} 1178 * <li class='ja'>{@link RestOp#defaultCharset} 1179 * </ul> 1180 * 1181 * @param value 1182 * The new value for this setting. 1183 * <br>The default is the first value found: 1184 * <ul> 1185 * <li>System property <js>"RestContext.defaultCharset" 1186 * <li>Environment variable <js>"RESTCONTEXT_defaultCharset" 1187 * <li><js>"utf-8"</js> 1188 * </ul> 1189 * <br>Cannot be <jk>null</jk>. 1190 * @return This object. 1191 */ 1192 public Builder defaultCharset(Charset value) { 1193 defaultCharset = assertArgNotNull("value", value); 1194 return this; 1195 } 1196 1197 /** 1198 * Returns the default implementation class list. 1199 * 1200 * <p> 1201 * This defines the implementation classes for a variety of bean types. 1202 * 1203 * <p> 1204 * Default classes are inherited from the parent REST object. 1205 * Typically used on the top-level {@link RestContext.Builder} to affect class types for that REST object and all children. 1206 * 1207 * <p> 1208 * Modifying the default class list on this builder does not affect the default class list on the parent builder, but changes made 1209 * here are inherited by child builders. 1210 * 1211 * @return The default implementation class list. 1212 */ 1213 public DefaultClassList defaultClasses() { 1214 return defaultClasses; 1215 } 1216 1217 /** 1218 * Adds to the default implementation class list. 1219 * 1220 * <p> 1221 * A shortcut for the following code: 1222 * 1223 * <p class='bjava'> 1224 * <jv>builder</jv>.defaultClasses().add(<jv>values</jv>); 1225 * </p> 1226 * 1227 * @param values The values to add to the list of default classes. 1228 * <br>Cannot contain <jk>null</jk> values. 1229 * @return This object. 1230 * @see #defaultClasses() 1231 */ 1232 public Builder defaultClasses(Class<?>...values) { 1233 assertArgNoNulls("values", values); 1234 defaultClasses().add(values); 1235 return this; 1236 } 1237 1238 /** 1239 * Specifies a default <c>Content-Type</c> header value if not specified on a request. 1240 * 1241 * @param value 1242 * The default value of the <c>Content-Type</c> header. 1243 * <br>Ignored if <jk>null</jk> or empty. 1244 * @return This object. 1245 */ 1246 public Builder defaultContentType(String value) { 1247 if (ne(value)) 1248 defaultRequestHeaders(contentType(value)); 1249 return this; 1250 } 1251 1252 /** 1253 * Returns the default request attributes sub-builder. 1254 * 1255 * @return The default request attributes sub-builder. 1256 */ 1257 public NamedAttributeMap defaultRequestAttributes() { 1258 if (defaultRequestAttributes == null) 1259 defaultRequestAttributes = createDefaultRequestAttributes(beanStore(), resource()); 1260 return defaultRequestAttributes; 1261 } 1262 1263 /** 1264 * Default request attributes. 1265 * 1266 * <p> 1267 * Specifies default values for request attributes if they're not already set on the request. 1268 * 1269 * Affects values returned by the following methods: 1270 * <ul> 1271 * <li class='jm'>{@link RestRequest#getAttribute(String)}. 1272 * <li class='jm'>{@link RestRequest#getAttributes()}. 1273 * </ul> 1274 * 1275 * <h5 class='section'>Example:</h5> 1276 * <p class='bjava'> 1277 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 1278 * <ja>@Rest</ja>(defaultRequestAttributes={<js>"Foo=bar"</js>, <js>"Baz: $C{REST/myAttributeValue}"</js>}) 1279 * <jk>public class</jk> MyResource { 1280 * 1281 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 1282 * <jk>public</jk> MyResource(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 1283 * 1284 * <jc>// Using method on builder.</jc> 1285 * <jv>builder</jv> 1286 * .defaultRequestAttributes( 1287 * BasicNamedAttribute.<jsm>of</jsm>(<js>"Foo"</js>, <js>"bar"</js>), 1288 * BasicNamedAttribute.<jsm>of</jsm>(<js>"Baz"</js>, <jk>true</jk>) 1289 * ); 1290 * } 1291 * 1292 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 1293 * <ja>@RestInit</ja> 1294 * <jk>public void</jk> init(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 1295 * <jv>builder</jv>.defaultRequestAttribute(<js>"Foo"</js>, <js>"bar"</js>); 1296 * } 1297 * 1298 * <jc>// Override at the method level.</jc> 1299 * <ja>@RestGet</ja>(defaultRequestAttributes={<js>"Foo: bar"</js>}) 1300 * <jk>public</jk> Object myMethod() {...} 1301 * } 1302 * </p> 1303 * 1304 * <h5 class='section'>Notes:</h5><ul> 1305 * <li class='note'>Use {@link BasicNamedAttribute#of(String, Supplier)} to provide a dynamically changeable attribute value. 1306 * </ul> 1307 * 1308 * @param values The attributes. 1309 * <br>Cannot contain <jk>null</jk> values. 1310 * @return This object. 1311 */ 1312 public Builder defaultRequestAttributes(NamedAttribute...values) { 1313 assertArgNoNulls("values", values); 1314 defaultRequestAttributes().add(values); 1315 return this; 1316 } 1317 1318 /** 1319 * Returns the default request headers. 1320 * 1321 * @return The default request headers. 1322 */ 1323 public HeaderList defaultRequestHeaders() { 1324 if (defaultRequestHeaders == null) 1325 defaultRequestHeaders = createDefaultRequestHeaders(beanStore(), resource()); 1326 return defaultRequestHeaders; 1327 } 1328 1329 /** 1330 * Default request headers. 1331 * 1332 * <p> 1333 * Specifies default values for request headers if they're not passed in through the request. 1334 * 1335 * <h5 class='section'>Notes:</h5><ul> 1336 * <li class='note'> 1337 * Affects values returned by {@link RestRequest#getHeader(String)} when the header is not present on the request. 1338 * <li class='note'> 1339 * The most useful reason for this annotation is to provide a default <c>Accept</c> header when one is not 1340 * specified so that a particular default {@link Serializer} is picked. 1341 * </ul> 1342 * 1343 * <h5 class='section'>Example:</h5> 1344 * <p class='bjava'> 1345 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 1346 * <ja>@Rest</ja>(defaultRequestHeaders={<js>"Accept: application/json"</js>, <js>"My-Header=$C{REST/myHeaderValue}"</js>}) 1347 * <jk>public class</jk> MyResource { 1348 * 1349 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 1350 * <jk>public</jk> MyResource(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 1351 * 1352 * <jc>// Using method on builder.</jc> 1353 * <jv>builder</jv> 1354 * .defaultRequestHeaders( 1355 * Accept.<jsm>of</jsm>(<js>"application/json"</js>), 1356 * BasicHeader.<jsm>of</jsm>(<js>"My-Header"</js>, <js>"foo"</js>) 1357 * ); 1358 * } 1359 * 1360 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 1361 * <ja>@RestInit</ja> 1362 * <jk>public void</jk> init(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 1363 * <jv>builder</jv>.defaultRequestHeaders(Accept.<jsm>of</jsm>(<js>"application/json"</js>)); 1364 * } 1365 * 1366 * <jc>// Override at the method level.</jc> 1367 * <ja>@RestGet</ja>(defaultRequestHeaders={<js>"Accept: text/xml"</js>}) 1368 * <jk>public</jk> Object myMethod() {...} 1369 * } 1370 * </p> 1371 * 1372 * <h5 class='section'>See Also:</h5><ul> 1373 * <li class='ja'>{@link Rest#defaultRequestHeaders} 1374 * <li class='ja'>{@link RestOp#defaultRequestHeaders} 1375 * <li class='ja'>{@link RestGet#defaultRequestHeaders} 1376 * <li class='ja'>{@link RestPut#defaultRequestHeaders} 1377 * <li class='ja'>{@link RestPost#defaultRequestHeaders} 1378 * <li class='ja'>{@link RestDelete#defaultRequestHeaders} 1379 * </ul> 1380 * 1381 * @param values The headers to add. 1382 * <br>Cannot contain <jk>null</jk> values. 1383 * @return This object. 1384 */ 1385 public Builder defaultRequestHeaders(Header...values) { 1386 assertArgNoNulls("values", values); 1387 defaultRequestHeaders().setDefault(values); 1388 return this; 1389 } 1390 1391 /** 1392 * Returns the default response headers. 1393 * 1394 * @return The default response headers. 1395 */ 1396 public HeaderList defaultResponseHeaders() { 1397 if (defaultResponseHeaders == null) 1398 defaultResponseHeaders = createDefaultResponseHeaders(beanStore(), resource()); 1399 return defaultResponseHeaders; 1400 } 1401 1402 /** 1403 * Default response headers. 1404 * 1405 * <p> 1406 * Specifies default values for response headers if they're not set after the Java REST method is called. 1407 * 1408 * <h5 class='section'>Notes:</h5><ul> 1409 * <li class='note'> 1410 * This is equivalent to calling {@link RestResponse#setHeader(String, String)} programmatically in each of 1411 * the Java methods. 1412 * <li class='note'> 1413 * The header value will not be set if the header value has already been specified (hence the 'default' in the name). 1414 * </ul> 1415 * 1416 * <h5 class='section'>Example:</h5> 1417 * <p class='bjava'> 1418 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 1419 * <ja>@Rest</ja>(defaultResponseHeaders={<js>"Content-Type: $C{REST/defaultContentType,text/plain}"</js>,<js>"My-Header: $C{REST/myHeaderValue}"</js>}) 1420 * <jk>public class</jk> MyResource { 1421 * 1422 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 1423 * <jk>public</jk> MyResource(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 1424 * 1425 * <jc>// Using method on builder.</jc> 1426 * <jv>builder</jv> 1427 * .defaultResponseHeaders( 1428 * ContentType.<jsm>of</jsm>(<js>"text/plain"</js>), 1429 * BasicHeader.<jsm>ofPair</jsm>(<js>"My-Header: foo"</js>) 1430 * ); 1431 * } 1432 * 1433 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 1434 * <ja>@RestInit</ja> 1435 * <jk>public void</jk> init(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 1436 * <jv>builder</jv>.defaultResponseHeaders(ContentType.<jsm>of</jsm>(<js>"text/plain"</js>)); 1437 * } 1438 * } 1439 * </p> 1440 * 1441 * <h5 class='section'>See Also:</h5><ul> 1442 * <li class='ja'>{@link Rest#defaultResponseHeaders} 1443 * <li class='ja'>{@link RestOp#defaultResponseHeaders} 1444 * <li class='ja'>{@link RestGet#defaultResponseHeaders} 1445 * <li class='ja'>{@link RestPut#defaultResponseHeaders} 1446 * <li class='ja'>{@link RestPost#defaultResponseHeaders} 1447 * <li class='ja'>{@link RestDelete#defaultResponseHeaders} 1448 * </ul> 1449 * 1450 * @param values The headers to add. 1451 * <br>Cannot contain <jk>null</jk> values. 1452 * @return This object. 1453 */ 1454 public Builder defaultResponseHeaders(Header...values) { 1455 assertArgNoNulls("values", values); 1456 defaultResponseHeaders().setDefault(values); 1457 return this; 1458 } 1459 1460 /** 1461 * Sets a value in the default settings map. 1462 * 1463 * <p> 1464 * A shortcut for the following code: 1465 * 1466 * <p class='bjava'> 1467 * <jv>builder</jv>.defaultSettings().add(<jv>key</jv>, <jv>value</jv>); 1468 * 1469 * </p> 1470 * @param key The setting key. 1471 * <br>Cannot be <jk>null</jk>. 1472 * @param value The setting value. 1473 * @return This object. 1474 * @see #defaultSettings() 1475 */ 1476 public Builder defaultSetting(String key, Object value) { 1477 defaultSettings().set(assertArgNotNull("key", key), value); 1478 return this; 1479 } 1480 1481 /** 1482 * Returns the default settings map. 1483 * 1484 * <p> 1485 * Default settings are inherited from the parent REST object. 1486 * Typically used on the top-level {@link RestContext.Builder} to affect settings for that REST object and all children. 1487 * 1488 * <p> 1489 * Modifying the default settings map on this builder does not affect the default settings on the parent builder, but changes made 1490 * here are inherited by child builders. 1491 * 1492 * @return The default settings map. 1493 */ 1494 public DefaultSettingsMap defaultSettings() { 1495 return defaultSettings; 1496 } 1497 1498 /** 1499 * Returns the destroy method list. 1500 * 1501 * @return The destroy method list. 1502 */ 1503 public MethodList destroyMethods() { 1504 if (destroyMethods == null) 1505 destroyMethods = createDestroyMethods(beanStore(), resource()); 1506 return destroyMethods; 1507 } 1508 1509 /** 1510 * Disable content URL parameter. 1511 * 1512 * <p> 1513 * When enabled, the HTTP content content on PUT and POST requests can be passed in as text using the <js>"content"</js> 1514 * URL parameter. 1515 * <br> 1516 * For example: 1517 * <p class='burlenc'> 1518 * ?content=(name='John%20Smith',age=45) 1519 * </p> 1520 * 1521 * <h5 class='section'>Example:</h5> 1522 * <p class='bjava'> 1523 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 1524 * <ja>@Rest</ja>(disableContentParam=<js>"$C{REST/disableContentParam,true}"</js>) 1525 * <jk>public class</jk> MyResource { 1526 * 1527 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 1528 * <jk>public</jk> MyResource(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 1529 * 1530 * <jc>// Using method on builder.</jc> 1531 * <jv>builder</jv>.disableContentParam(); 1532 * } 1533 * 1534 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 1535 * <ja>@RestInit</ja> 1536 * <jk>public void</jk> init(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 1537 * <jv>builder</jv>.disableContentParam(); 1538 * } 1539 * } 1540 * </p> 1541 * 1542 * <h5 class='section'>Notes:</h5><ul> 1543 * <li class='note'> 1544 * <js>'content'</js> parameter name is case-insensitive. 1545 * <li class='note'> 1546 * Useful for debugging PUT and POST methods using only a browser. 1547 * </ul> 1548 * 1549 * @return This object. 1550 */ 1551 public Builder disableContentParam() { 1552 return disableContentParam(true); 1553 } 1554 1555 /** 1556 * Disable content URL parameter. 1557 * 1558 * <p> 1559 * Same as {@link #disableContentParam()} but allows you to set it as a boolean value. 1560 * 1561 * @param value The new value for this setting. 1562 * @return This object. 1563 */ 1564 public Builder disableContentParam(boolean value) { 1565 disableContentParam = value; 1566 return this; 1567 } 1568 1569 /** 1570 * Returns the encoder group sub-builder. 1571 * 1572 * <p> 1573 * Encoders are used to decode HTTP requests and encode HTTP responses based on {@code Content-Encoding} and {@code Accept-Encoding} 1574 * headers. 1575 * 1576 * <p> 1577 * The default encoder set has support for identity incoding only. 1578 * It can be overridden via any of the following: 1579 * <ul class='spaced-list'> 1580 * <li>Injected via bean store. 1581 * <li>Class annotation: {@link Rest#encoders() @Rest(encoders)} 1582 * <li>{@link RestInject @RestInject}-annotated method: 1583 * <p class='bjava'> 1584 * <ja>@RestInject</ja> <jk>public</jk> [<jk>static</jk>] EncoderSet myMethod(<i><args></i>) {...} 1585 * </p> 1586 * Args can be any injected bean including EncoderSet.Builder, the default builder. 1587 * </ul> 1588 * 1589 * <h5 class='section'>See Also:</h5><ul> 1590 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerEncoders">Encoders</a> 1591 * </ul> 1592 * 1593 * @return The builder for the {@link EncoderSet} object in the REST context. 1594 */ 1595 public EncoderSet.Builder encoders() { 1596 if (encoders == null) 1597 encoders = createEncoders(beanStore(), resource()); 1598 return encoders; 1599 } 1600 1601 /** 1602 * Adds one or more encoders to this class. 1603 * 1604 * <p> 1605 * Equivalent to calling: 1606 * <p class='bjava'> 1607 * <jv>builder</jv>.encoders().add(<jv>value</jv>); 1608 * </p> 1609 * 1610 * <h5 class='section'>See Also:</h5><ul> 1611 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerEncoders">Encoders</a> 1612 * <li class='jm'>{@link #encoders()} 1613 * </ul> 1614 * 1615 * @param value The values to add. 1616 * <br>Cannot contain <jk>null</jk> values. 1617 * @return This object. 1618 */ 1619 @SafeVarargs 1620 public final Builder encoders(Class<? extends Encoder>...value) { 1621 assertArgNoNulls("value", value); 1622 encoders().add(value); 1623 return this; 1624 } 1625 1626 /** 1627 * Adds one or more encoders to this class. 1628 * 1629 * <p> 1630 * Equivalent to calling: 1631 * <p class='bjava'> 1632 * <jv>builder</jv>.encoders().add(<jv>value</jv>); 1633 * </p> 1634 * 1635 * <h5 class='section'>See Also:</h5><ul> 1636 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerEncoders">Encoders</a> 1637 * <li class='jm'>{@link #encoders()} 1638 * </ul> 1639 * 1640 * @param value The values to add. 1641 * <br>Cannot contain <jk>null</jk> values. 1642 * @return This object. 1643 */ 1644 public Builder encoders(Encoder...value) { 1645 assertArgNoNulls("value", value); 1646 encoders().add(value); 1647 return this; 1648 } 1649 1650 /** 1651 * Returns the end call method list. 1652 * 1653 * @return The end call method list. 1654 */ 1655 public MethodList endCallMethods() { 1656 if (endCallMethods == null) 1657 endCallMethods = createEndCallMethods(beanStore(), resource()); 1658 return endCallMethods; 1659 } 1660 1661 /** 1662 * Returns the encoder group builder containing the encoders for compressing/decompressing input and output streams. 1663 * 1664 * <p> 1665 * These can be used to enable various kinds of compression (e.g. <js>"gzip"</js>) on requests and responses. 1666 * 1667 * <p> 1668 * The builder is initialized with encoders defined via the {@link Rest#encoders()} annotation. That annotation is applied 1669 * from parent-to-child order with child entries given priority over parent entries. 1670 * 1671 * <h5 class='section'>See Also:</h5><ul> 1672 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerEncoders">Encoders</a> 1673 * </ul> 1674 * 1675 * @return The encoder group builder for this context builder. 1676 */ 1677 public EncoderSet.Builder getEncoders() { return encoders; } 1678 1679 @Override /* Overridden from ServletConfig */ 1680 public String getInitParameter(String name) { 1681 return inner == null ? null : inner.getInitParameter(name); 1682 } 1683 1684 @Override /* Overridden from ServletConfig */ 1685 public Enumeration<String> getInitParameterNames() { return inner == null ? new Vector<String>().elements() : inner.getInitParameterNames(); } 1686 1687 /** 1688 * Returns the parser group builder containing the parsers for converting HTTP request bodies into POJOs. 1689 * 1690 * <p> 1691 * Parsers are used to convert the content of HTTP requests into POJOs. 1692 * <br>Any of the Juneau framework parsers can be used in this setting. 1693 * <br>The parser selected is based on the request <c>Content-Type</c> header matched against the values returned by the following method 1694 * using a best-match algorithm: 1695 * <ul class='javatree'> 1696 * <li class='jm'>{@link Parser#getMediaTypes()} 1697 * </ul> 1698 * 1699 * <p> 1700 * The builder is initialized with parsers defined via the {@link Rest#parsers()} annotation. That annotation is applied 1701 * from parent-to-child order with child entries given priority over parent entries. 1702 * 1703 * <h5 class='section'>See Also:</h5><ul> 1704 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Marshalling">Marshalling</a> 1705 * </ul> 1706 * 1707 * @return The parser group builder for this context builder. 1708 */ 1709 public ParserSet.Builder getParsers() { return parsers; } 1710 1711 /** 1712 * Returns the serializer group builder containing the serializers for marshalling POJOs into response bodies. 1713 * 1714 * <p> 1715 * Serializer are used to convert POJOs to HTTP response bodies. 1716 * <br>Any of the Juneau framework serializers can be used in this setting. 1717 * <br>The serializer selected is based on the request <c>Accept</c> header matched against the values returned by the following method 1718 * using a best-match algorithm: 1719 * <ul class='javatree'> 1720 * <li class='jm'>{@link Serializer#getMediaTypeRanges()} 1721 * </ul> 1722 * 1723 * <p> 1724 * The builder is initialized with serializers defined via the {@link Rest#serializers()} annotation. That annotation is applied 1725 * from parent-to-child order with child entries given priority over parent entries. 1726 * 1727 * <h5 class='section'>See Also:</h5><ul> 1728 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Marshalling">Marshalling</a> 1729 * </ul> 1730 * 1731 * @return The serializer group builder for this context builder. 1732 */ 1733 public SerializerSet.Builder getSerializers() { return serializers; } 1734 1735 @Override /* Overridden from ServletConfig */ 1736 public ServletContext getServletContext() { 1737 return nn(inner) ? inner.getServletContext() : nn(parentContext) ? parentContext.getBuilder().getServletContext() : null; 1738 } 1739 1740 @Override /* Overridden from ServletConfig */ 1741 public String getServletName() { return inner == null ? null : inner.getServletName(); } 1742 1743 @Override /* Overridden from Builder */ 1744 public Builder impl(Context value) { 1745 super.impl(value); 1746 return this; 1747 } 1748 1749 /** 1750 * Performs initialization on this builder against the specified REST servlet/bean instance. 1751 * 1752 * @param resource 1753 * The REST servlet/bean instance that this context is defined against. 1754 * <br>Cannot be <jk>null</jk>. 1755 * @return This object. 1756 * @throws ServletException If hook method calls failed. 1757 */ 1758 public Builder init(Supplier<?> resource) throws ServletException { 1759 1760 if (initialized) 1761 return this; 1762 initialized = true; 1763 1764 this.resource = new ResourceSupplier(resourceClass, assertArgNotNull("resource", resource)); 1765 var r = this.resource; 1766 var rc = resourceClass; 1767 1768 // @formatter:off 1769 beanStore = createBeanStore(resource) 1770 .build() 1771 .addBean(Builder.class, this) 1772 .addBean(ResourceSupplier.class, this.resource) 1773 .addBean(ServletConfig.class, nn(inner) ? inner : this) 1774 .addBean(ServletContext.class, (nn(inner) ? inner : this).getServletContext()); 1775 // @formatter:on 1776 1777 if (rootBeanStore == null) { 1778 rootBeanStore = beanStore; 1779 beanStore = BeanStore.of(rootBeanStore, r.get()); 1780 } 1781 var bs = beanStore; 1782 1783 beanStore.add(BeanStore.class, bs); 1784 varResolver = createVarResolver(bs, r, rc); 1785 beanStore.add(VarResolver.class, varResolver.build()); 1786 config = beanStore.add(Config.class, createConfig(bs, r, rc)); 1787 beanStore.add(VarResolver.class, varResolver.bean(Config.class, config).build()); 1788 1789 var rci = ClassInfo.of(resourceClass); 1790 1791 // Get @RestInject fields initialized with values. 1792 // @formatter:off 1793 rci.getAllFields().stream() 1794 .filter(x -> x.hasAnnotation(RestInject.class)) 1795 .forEach(x -> opt(x.get(resource.get())).ifPresent( 1796 y -> beanStore.add( 1797 x.getFieldType().inner(), 1798 y, 1799 RestInjectAnnotation.name(x.getAnnotations(RestInject.class).findFirst().map(AnnotationInfo::inner).orElse(null)) 1800 ) 1801 )); 1802 // @formatter:on 1803 1804 rci.getAllMethods().stream().filter(x -> x.hasAnnotation(RestInject.class)).forEach(x -> { 1805 var rt = x.getReturnType().<Object>inner(); 1806 var name = RestInjectAnnotation.name(x.getAnnotations(RestInject.class).findFirst().map(AnnotationInfo::inner).orElse(null)); 1807 if (! (DELAYED_INJECTION.contains(rt) || DELAYED_INJECTION_NAMES.contains(name))) { 1808 // @formatter:off 1809 beanStore 1810 .createMethodFinder(rt) 1811 .find(Builder::isRestInjectMethod) 1812 .run(y -> beanStore.add(rt, y, name)); 1813 // @formatter:on 1814 } 1815 }); 1816 1817 var vrs = varResolver().build().createSession(); 1818 var work = AnnotationWorkList.of(vrs, rstream(AP.find(rci)).filter(CONTEXT_APPLY_FILTER)); 1819 1820 apply(work); 1821 beanContext().apply(work); 1822 partSerializer().apply(work); 1823 partParser().apply(work); 1824 jsonSchemaGenerator().apply(work); 1825 1826 runInitHooks(bs, resource()); 1827 1828 // Set @RestInject fields not initialized with values. 1829 // @formatter:off 1830 rci.getAllFields().stream() 1831 .filter(x -> x.hasAnnotation(RestInject.class)) 1832 .forEach(x -> x.setIfNull( 1833 resource.get(), 1834 beanStore.getBean( 1835 x.getFieldType().inner(), 1836 RestInjectAnnotation.name(x.getAnnotations(RestInject.class).findFirst().map(AnnotationInfo::inner).orElse(null)) 1837 ).orElse(null) 1838 )); 1839 // @formatter:on 1840 1841 return this; 1842 } 1843 1844 /** 1845 * Returns the JSON schema generator sub-builder. 1846 * 1847 * <p> 1848 * The JSON schema generator is used for generating JSON schema in the auto-generated Swagger documentation. 1849 * 1850 * <p> 1851 * The default JSON schema generator is a default {@link JsonSchemaGenerator}. 1852 * It can overridden via any of the following: 1853 * <ul class='spaced-list'> 1854 * <li>Injected via bean store. 1855 * <li>{@link RestInject @RestInject}-annotated method: 1856 * <p class='bjava'> 1857 * <ja>@RestInject</ja> <jk>public</jk> [<jk>static</jk>] JsonSchemaGenerator myMethod(<i><args></i>) {...} 1858 * </p> 1859 * Args can be any injected bean including JsonSchemaGenerator.Builder, the default builder. 1860 * </ul> 1861 * 1862 * <h5 class='section'>See Also:</h5><ul> 1863 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanSwagger2">juneau-bean-swagger-v2</a> 1864 * </ul> 1865 * 1866 * @return The JSON schema generator sub-builder. 1867 */ 1868 public JsonSchemaGenerator.Builder jsonSchemaGenerator() { 1869 if (jsonSchemaGenerator == null) 1870 jsonSchemaGenerator = createJsonSchemaGenerator(beanStore(), resource()); 1871 return jsonSchemaGenerator; 1872 } 1873 1874 /** 1875 * Specifies the JSON schema generator for this class. 1876 * 1877 * <p> 1878 * Equivalent to calling: 1879 * <p class='bjava'> 1880 * <jv>builder</jv>.jsonSchemaGenerator().type(<jv>value</jv>); 1881 * </p> 1882 * 1883 * <h5 class='section'>See Also:</h5><ul> 1884 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanSwagger2">juneau-bean-swagger-v2</a> 1885 * <li class='jm'>{@link #jsonSchemaGenerator()} 1886 * </ul> 1887 * 1888 * @param value The new value. 1889 * <br>Cannot be <jk>null</jk>. 1890 * @return This object. 1891 */ 1892 public Builder jsonSchemaGenerator(Class<? extends JsonSchemaGenerator> value) { 1893 jsonSchemaGenerator().type(assertArgNotNull("value", value)); 1894 return this; 1895 } 1896 1897 /** 1898 * Specifies the JSON schema generator for this class. 1899 * 1900 * <p> 1901 * Equivalent to calling: 1902 * <p class='bjava'> 1903 * <jv>builder</jv>.jsonSchemaGenerator().impl(<jv>value</jv>); 1904 * <li class='jm'>{@link #jsonSchemaGenerator()} 1905 * </p> 1906 * 1907 * <h5 class='section'>See Also:</h5><ul> 1908 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanSwagger2">juneau-bean-swagger-v2</a> 1909 * </ul> 1910 * 1911 * @param value The new value. 1912 * <br>Cannot be <jk>null</jk>. 1913 * @return This object. 1914 */ 1915 public Builder jsonSchemaGenerator(JsonSchemaGenerator value) { 1916 jsonSchemaGenerator().impl(assertArgNotNull("value", value)); 1917 return this; 1918 } 1919 1920 /** 1921 * Returns the logger for this resource. 1922 * 1923 * <p> 1924 * The logger is used in the following locations: 1925 * <ul> 1926 * <li>{@link RestServlet#log(Level, Throwable, String, Object...)} and related methods. 1927 * <li>{@link RestObject#log(Level, Throwable, String, Object...)} and related methods. 1928 * <li>In the {@link #callLogger()} of this resource. 1929 * </ul> 1930 * It can also be accessed directly via {@link RestContext#getLogger()} or passed in as a parameter 1931 * on a {@link RestOp}-annotated method. 1932 * 1933 * <p> 1934 * The default config can be overridden via any of the following: 1935 * <ul class='spaced-list'> 1936 * <li>Injected via bean store. 1937 * <li>{@link RestInject @RestInject}-annotated method: 1938 * <p class='bjava'> 1939 * <ja>@RestInject</ja> <jk>public</jk> [<jk>static</jk>] Logger myMethod(<i><args></i>) {...} 1940 * </p> 1941 * Args can be any injected bean. 1942 * </ul> 1943 * 1944 * <h5 class='section'>See Also:</h5><ul> 1945 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerLoggingAndDebugging">Logging / Debugging</a> 1946 * </ul> 1947 * 1948 * @return The logger for this resource. 1949 */ 1950 public Logger logger() { 1951 if (logger == null) 1952 logger = createLogger(beanStore(), resource, resourceClass); 1953 return logger; 1954 } 1955 1956 /** 1957 * Sets the logger for this resource. 1958 * 1959 * <h5 class='section'>See Also:</h5><ul> 1960 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerLoggingAndDebugging">Logging / Debugging</a> 1961 * <li class='jm'>{@link #logger()} 1962 * </ul> 1963 * 1964 * @param value The logger to use for the REST resource. 1965 * <br>Cannot be <jk>null</jk>. 1966 * @return This object. 1967 */ 1968 public Builder logger(Logger value) { 1969 logger = assertArgNotNull("value", value); 1970 return this; 1971 } 1972 1973 /** 1974 * The maximum allowed input size (in bytes) on HTTP requests. 1975 * 1976 * <p> 1977 * Useful for alleviating DoS attacks by throwing an exception when too much input is received instead of resulting 1978 * in out-of-memory errors which could affect system stability. 1979 * 1980 * <h5 class='section'>Example:</h5> 1981 * <p class='bjava'> 1982 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 1983 * <ja>@Rest</ja>(maxInput=<js>"$C{REST/maxInput,10M}"</js>) 1984 * <jk>public class</jk> MyResource { 1985 * 1986 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 1987 * <jk>public</jk> MyResource(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 1988 * 1989 * <jc>// Using method on builder.</jc> 1990 * <jv>builder</jv>.maxInput(<js>"10M"</js>); 1991 * } 1992 * 1993 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 1994 * <ja>@RestInit</ja> 1995 * <jk>public void</jk> init(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 1996 * <jv>builder</jv>.maxInput(<js>"10M"</js>); 1997 * } 1998 * 1999 * <jc>// Override at the method level.</jc> 2000 * <ja>@RestPost</ja>(maxInput=<js>"10M"</js>) 2001 * <jk>public</jk> Object myMethod() {...} 2002 * } 2003 * </p> 2004 * 2005 * <h5 class='section'>Notes:</h5><ul> 2006 * <li class='note'> 2007 * String value that gets resolved to a <jk>long</jk>. 2008 * <li class='note'> 2009 * Can be suffixed with any of the following representing kilobytes, megabytes, and gigabytes: 2010 * <js>'K'</js>, <js>'M'</js>, <js>'G'</js>. 2011 * <li class='note'> 2012 * A value of <js>"-1"</js> can be used to represent no limit. 2013 * </ul> 2014 * 2015 * <h5 class='section'>See Also:</h5><ul> 2016 * <li class='ja'>{@link Rest#maxInput} 2017 * <li class='ja'>{@link RestOp#maxInput} 2018 * <li class='jm'>{@link RestOpContext.Builder#maxInput(String)} 2019 * </ul> 2020 * 2021 * @param value 2022 * The new value for this setting. 2023 * <br>The default is the first value found: 2024 * <ul> 2025 * <li>System property <js>"RestContext.maxInput" 2026 * <li>Environment variable <js>"RESTCONTEXT_MAXINPUT" 2027 * <li><js>"100M"</js> 2028 * </ul> 2029 * <br>The default is <js>"100M"</js>. 2030 * <br>Cannot be <jk>null</jk>. 2031 * @return This object. 2032 */ 2033 public Builder maxInput(String value) { 2034 maxInput = StringUtils.parseLongWithSuffix(assertArgNotNull("value", value)); 2035 return this; 2036 } 2037 2038 /** 2039 * Returns the messages sub-builder. 2040 * 2041 * <p> 2042 * Messages beans are wrappers around resource bundles containing localized messages. 2043 * 2044 * <p> 2045 * By default, the resource bundle name is assumed to match the class name. For example, given the class 2046 * <c>MyClass.java</c>, the resource bundle is assumed to be <c>MyClass.properties</c>. This property 2047 * allows you to override this setting to specify a different location such as <c>MyMessages.properties</c> by 2048 * specifying a value of <js>"MyMessages"</js>. 2049 * 2050 * <p> 2051 * Resource bundles are searched using the following base name patterns: 2052 * <ul> 2053 * <li><js>"{package}.{name}"</js> 2054 * <li><js>"{package}.i18n.{name}"</js> 2055 * <li><js>"{package}.nls.{name}"</js> 2056 * <li><js>"{package}.messages.{name}"</js> 2057 * </ul> 2058 * 2059 * <p> 2060 * This annotation is used to provide request-localized (based on <c>Accept-Language</c>) messages for the following methods: 2061 * <ul class='javatree'> 2062 * <li class='jm'>{@link RestRequest#getMessage(String, Object...)} 2063 * <li class='jm'>{@link RestContext#getMessages() RestContext.getMessages()} 2064 * </ul> 2065 * 2066 * <p> 2067 * Request-localized messages are also available by passing either of the following parameter types into your Java method: 2068 * <ul class='javatree'> 2069 * <li class='jc'>{@link ResourceBundle} - Basic Java resource bundle. 2070 * <li class='jc'>{@link Messages} - Extended resource bundle with several convenience methods. 2071 * </ul> 2072 * 2073 * The value can be a relative path like <js>"nls/Messages"</js>, indicating to look for the resource bundle 2074 * <js>"com.foo.sample.nls.Messages"</js> if the resource class is in <js>"com.foo.sample"</js>, or it can be an 2075 * absolute path like <js>"com.foo.sample.nls.Messages"</js> 2076 * 2077 * <h5 class='section'>Examples:</h5> 2078 * <p class='bini'> 2079 * <cc># Contents of org/apache/foo/nls/MyMessages.properties</cc> 2080 * 2081 * <ck>HelloMessage</ck> = <cv>Hello {0}!</cv> 2082 * </p> 2083 * <p class='bjava'> 2084 * <jc>// Contents of org/apache/foo/MyResource.java</jc> 2085 * 2086 * <ja>@Rest</ja>(messages=<js>"nls/MyMessages"</js>) 2087 * <jk>public class</jk> MyResource {...} 2088 * 2089 * <ja>@RestGet</ja>(<js>"/hello/{you}"</js>) 2090 * <jk>public</jk> Object helloYou(RestRequest <jv>req</jv>, Messages <jv>messages</jv>, <ja>@Path</ja>(<js>"name"</js>) String <jv>you</jv>) { 2091 * String <jv>string</jv>; 2092 * 2093 * <jc>// Get it from the RestRequest object.</jc> 2094 * <jv>string</jv> = <jv>req</jv>.getMessage(<js>"HelloMessage"</js>, <jv>you</jv>); 2095 * 2096 * <jc>// Or get it from the method parameter.</jc> 2097 * <jv>string</jv> = <jv>messages</jv>.getString(<js>"HelloMessage"</js>, <jv>you</jv>); 2098 * 2099 * <jc>// Or get the message in a locale different from the request.</jc> 2100 * <jv>string</jv> = <jv>messages</jv>.forLocale(Locale.<jsf>UK</jsf>).getString(<js>"HelloMessage"</js>, <jv>you</jv>); 2101 * 2102 * <jk>return</jk> <jv>string</jv>; 2103 * } 2104 * } 2105 * </p> 2106 * 2107 * <p> 2108 * The default messages can overridden via any of the following: 2109 * <ul class='spaced-list'> 2110 * <li>Injected via bean store. 2111 * <li>Class annotation: {@link Rest#messages() @Rest(messages)} 2112 * <li>{@link RestInject @RestInject}-annotated method: 2113 * <p class='bjava'> 2114 * <ja>@RestInject</ja> <jk>public</jk> [<jk>static</jk>] Messages myMethod(<i><args></i>) {...} 2115 * </p> 2116 * Args can be any injected bean including Messages.Builder, the default builder. 2117 * </ul> 2118 * 2119 * <h5 class='section'>Notes:</h5><ul> 2120 * <li class='note'>Mappings are cumulative from super classes. 2121 * <br>Therefore, you can find and retrieve messages up the class-hierarchy chain. 2122 * </ul> 2123 * 2124 * <h5 class='section'>See Also:</h5><ul> 2125 * <li class='jc'>{@link Messages} 2126 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/LocalizedMessages">Localized Messages</a> 2127 * </ul> 2128 * 2129 * @return The messages sub-builder. 2130 */ 2131 public Messages.Builder messages() { 2132 if (messages == null) 2133 messages = createMessages(beanStore(), resource()); 2134 return messages; 2135 } 2136 2137 /** 2138 * Specifies the messages bundle for this class. 2139 * 2140 * <p> 2141 * Equivalent to calling: 2142 * <p class='bjava'> 2143 * <jv>builder</jv>.messages().type(<jv>value</jv>); 2144 * </p> 2145 * 2146 * <h5 class='section'>See Also:</h5><ul> 2147 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/LocalizedMessages">Localized Messages</a> 2148 * <li class='jm'>{@link #messages()} 2149 * </ul> 2150 * 2151 * @param value The new value. 2152 * <br>Cannot be <jk>null</jk>. 2153 * @return This object. 2154 */ 2155 public Builder messages(Class<? extends Messages> value) { 2156 messages().type(assertArgNotNull("value", value)); 2157 return this; 2158 } 2159 2160 /** 2161 * Specifies the messages bundle for this class. 2162 * 2163 * <p> 2164 * Equivalent to calling: 2165 * <p class='bjava'> 2166 * <jv>builder</jv>.messages().impl(<jv>value</jv>); 2167 * </p> 2168 * 2169 * <h5 class='section'>See Also:</h5><ul> 2170 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/LocalizedMessages">Localized Messages</a> 2171 * <li class='jm'>{@link #messages()} 2172 * </ul> 2173 * 2174 * @param value The new value. 2175 * <br>Cannot be <jk>null</jk>. 2176 * @return This object. 2177 */ 2178 public Builder messages(Messages value) { 2179 messages().impl(assertArgNotNull("value", value)); 2180 return this; 2181 } 2182 2183 /** 2184 * Returns the method execution statistics store sub-builder. 2185 * 2186 * <p> 2187 * Used for tracking basic call statistics on Java methods in this class. 2188 * It can be accessed directly via {@link RestContext#getMethodExecStore()} or passed in as a parameter 2189 * on a {@link RestOp}-annotated method. 2190 * 2191 * <p> 2192 * The default method exec store can overridden via any of the following: 2193 * <ul class='spaced-list'> 2194 * <li>Injected via bean store. 2195 * <li>{@link RestInject @RestInject}-annotated method: 2196 * <p class='bjava'> 2197 * <ja>@RestInject</ja> <jk>public</jk> [<jk>static</jk>] MethodExecStore myMethod(<i><args></i>) {...} 2198 * </p> 2199 * Args can be any injected bean including MethodExecStore.Builder, the default builder. 2200 * </ul> 2201 * 2202 * <h5 class='section'>See Also:</h5><ul> 2203 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/ExecutionStatistics">REST method execution statistics</a> 2204 * </ul> 2205 * 2206 * @return The method execution statistics store sub-builder. 2207 */ 2208 public MethodExecStore.Builder methodExecStore() { 2209 if (methodExecStore == null) 2210 methodExecStore = createMethodExecStore(beanStore(), resource()); 2211 return methodExecStore; 2212 } 2213 2214 /** 2215 * Specifies the method execution store for this class. 2216 * 2217 * <p> 2218 * Equivalent to calling: 2219 * <p class='bjava'> 2220 * <jv>builder</jv>.methodExecStore().type(<jv>value</jv>); 2221 * </p> 2222 * 2223 * <h5 class='section'>See Also:</h5><ul> 2224 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/ExecutionStatistics">REST method execution statistics</a> 2225 * <li class='jm'>{@link #methodExecStore()} 2226 * </ul> 2227 * 2228 * @param value The new value. 2229 * <br>Cannot be <jk>null</jk>. 2230 * @return This object. 2231 */ 2232 public Builder methodExecStore(Class<? extends MethodExecStore> value) { 2233 methodExecStore().type(assertArgNotNull("value", value)); 2234 return this; 2235 } 2236 2237 /** 2238 * Specifies the method execution store for this class. 2239 * 2240 * <p> 2241 * Equivalent to calling: 2242 * <p class='bjava'> 2243 * <jv>builder</jv>.methodExecStore().impl(<jv>value</jv>); 2244 * </p> 2245 * 2246 * <h5 class='section'>See Also:</h5><ul> 2247 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/ExecutionStatistics">REST method execution statistics</a> 2248 * <li class='jm'>{@link #methodExecStore()} 2249 * </ul> 2250 * 2251 * @param value The new value. 2252 * <br>Cannot be <jk>null</jk>. 2253 * @return This object. 2254 */ 2255 public Builder methodExecStore(MethodExecStore value) { 2256 methodExecStore().impl(assertArgNotNull("value", value)); 2257 return this; 2258 } 2259 2260 /** 2261 * <i><l>RestContext</l> configuration property: </i> Parser listener. 2262 * 2263 * <p> 2264 * Specifies the parser listener class to use for listening to non-fatal parsing errors. 2265 * 2266 * <h5 class='section'>See Also:</h5><ul> 2267 * <li class='jm'>{@link org.apache.juneau.parser.Parser.Builder#listener(Class)} 2268 * </ul> 2269 * 2270 * @param value The new value for this setting. 2271 * <br>Cannot be <jk>null</jk>. 2272 * @return This object. 2273 */ 2274 public Builder parserListener(Class<? extends ParserListener> value) { 2275 if (isNotVoid(assertArgNotNull("value", value))) 2276 parsers.forEach(x -> x.listener(value)); 2277 return this; 2278 } 2279 2280 /** 2281 * Returns the parser group sub-builder. 2282 * 2283 * <p> 2284 * Parsers are used to HTTP request bodies into POJOs based on the {@code Content-Type} header. 2285 * 2286 * <p> 2287 * The default parser set is empty. 2288 * It can be overridden via any of the following: 2289 * <ul class='spaced-list'> 2290 * <li>Injected via bean store. 2291 * <li>Class annotation: {@link Rest#parsers() @Rest(parsers)} 2292 * <li>{@link RestInject @RestInject}-annotated method: 2293 * <p class='bjava'> 2294 * <ja>@RestInject</ja> <jk>public</jk> [<jk>static</jk>] ParserSet myMethod(<i><args></i>) {...} 2295 * </p> 2296 * Args can be any injected bean including ParserSet.Builder, the default builder. 2297 * </ul> 2298 * 2299 * <h5 class='section'>See Also:</h5><ul> 2300 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Marshalling">Marshalling</a> 2301 * </ul> 2302 * 2303 * @return The parser group sub-builder. 2304 */ 2305 public ParserSet.Builder parsers() { 2306 if (parsers == null) 2307 parsers = createParsers(beanStore(), resource()); 2308 return parsers; 2309 } 2310 2311 /** 2312 * Adds one or more parsers to this class. 2313 * 2314 * <p> 2315 * Equivalent to calling: 2316 * <p class='bjava'> 2317 * <jv>builder</jv>.parsers().add(<jv>value</jv>); 2318 * </p> 2319 * 2320 * <h5 class='section'>See Also:</h5><ul> 2321 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Marshalling">Marshalling</a> 2322 * <li class='jm'>{@link #parsers()} 2323 * </ul> 2324 * 2325 * @param value The values to add. 2326 * <br>Cannot contain <jk>null</jk> values. 2327 * @return This object. 2328 */ 2329 @SafeVarargs 2330 public final Builder parsers(Class<? extends Parser>...value) { 2331 assertArgNoNulls("value", value); 2332 parsers().add(value); 2333 return this; 2334 } 2335 2336 /** 2337 * Adds one or more parsers to this class. 2338 * 2339 * <p> 2340 * Equivalent to calling: 2341 * <p class='bjava'> 2342 * <jv>builder</jv>.parsers().add(<jv>value</jv>); 2343 * </p> 2344 * 2345 * <h5 class='section'>See Also:</h5><ul> 2346 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Marshalling">Marshalling</a> 2347 * <li class='jm'>{@link #parsers()} 2348 * </ul> 2349 * 2350 * @param value The values to add. 2351 * <br>Cannot contain <jk>null</jk> values. 2352 * @return This object. 2353 */ 2354 public Builder parsers(Parser...value) { 2355 assertArgNoNulls("value", value); 2356 parsers().add(value); 2357 return this; 2358 } 2359 2360 /** 2361 * Returns the part parser sub-builder. 2362 * 2363 * <p> 2364 * The part parser is used for parsing HTTP parts such as request headers and query/form/path parameters. 2365 * 2366 * <p> 2367 * The default part parser is an {@link OpenApiParser}. 2368 * It can overridden via any of the following: 2369 * <ul class='spaced-list'> 2370 * <li>Injected via bean store. 2371 * <li>{@link RestInject @RestInject}-annotated method: 2372 * <p class='bjava'> 2373 * <ja>@RestInject</ja> <jk>public</jk> [<jk>static</jk>] HttpPartParser myMethod(<i><args></i>) {...} 2374 * </p> 2375 * Args can be any injected bean including HttpPartParser.Builder, the default builder. 2376 * </ul> 2377 * 2378 * <h5 class='section'>See Also:</h5><ul> 2379 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/HttpParts">HTTP Parts</a> 2380 * </ul> 2381 * 2382 * @return The part parser sub-builder. 2383 */ 2384 public HttpPartParser.Creator partParser() { 2385 if (partParser == null) 2386 partParser = createPartParser(beanStore(), resource()); 2387 return partParser; 2388 } 2389 2390 /** 2391 * Specifies the part parser to use for parsing HTTP parts for this class. 2392 * 2393 * <p> 2394 * Equivalent to calling: 2395 * <p class='bjava'> 2396 * <jv>builder</jv>.partParser().type(<jv>value</jv>); 2397 * </p> 2398 * 2399 * <h5 class='section'>See Also:</h5><ul> 2400 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/HttpParts">HTTP Parts</a> 2401 * <li class='jm'>{@link #partParser()} 2402 * </ul> 2403 * 2404 * @param value The new value. 2405 * <br>Cannot be <jk>null</jk>. 2406 * @return This object. 2407 */ 2408 public Builder partParser(Class<? extends HttpPartParser> value) { 2409 partParser().type(assertArgNotNull("value", value)); 2410 return this; 2411 } 2412 2413 /** 2414 * Specifies the part parser to use for parsing HTTP parts for this class. 2415 * 2416 * <p> 2417 * Equivalent to calling: 2418 * <p class='bjava'> 2419 * <jv>builder</jv>.partParser().impl(<jv>value</jv>); 2420 * </p> 2421 * 2422 * <h5 class='section'>See Also:</h5><ul> 2423 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/HttpParts">HTTP Parts</a> 2424 * <li class='jm'>{@link #partParser()} 2425 * </ul> 2426 * 2427 * @param value The new value. 2428 * <br>Cannot be <jk>null</jk>. 2429 * @return This object. 2430 */ 2431 public Builder partParser(HttpPartParser value) { 2432 partParser().impl(assertArgNotNull("value", value)); 2433 return this; 2434 } 2435 2436 /** 2437 * Returns the part serializer sub-builder. 2438 * 2439 * <p> 2440 * The part serializer is used for serializing HTTP parts such as response headers. 2441 * 2442 * <p> 2443 * The default part serializer is an {@link OpenApiSerializer}. 2444 * It can overridden via any of the following: 2445 * <ul class='spaced-list'> 2446 * <li>Injected via bean store. 2447 * <li>{@link RestInject @RestInject}-annotated method: 2448 * <p class='bjava'> 2449 * <ja>@RestInject</ja> <jk>public</jk> [<jk>static</jk>] HttpPartSerializer myMethod(<i><args></i>) {...} 2450 * </p> 2451 * Args can be any injected bean including HttpPartSerializer.Builder, the default builder. 2452 * </ul> 2453 * 2454 * <h5 class='section'>See Also:</h5><ul> 2455 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/HttpParts">HTTP Parts</a> 2456 * </ul> 2457 * 2458 * @return The part serializer sub-builder. 2459 */ 2460 public HttpPartSerializer.Creator partSerializer() { 2461 if (partSerializer == null) 2462 partSerializer = createPartSerializer(beanStore(), resource()); 2463 return partSerializer; 2464 } 2465 2466 /** 2467 * Specifies the part serializer to use for serializing HTTP parts for this class. 2468 * 2469 * <p> 2470 * Equivalent to calling: 2471 * <p class='bjava'> 2472 * <jv>builder</jv>.partSerializer().type(<jv>value</jv>); 2473 * </p> 2474 * 2475 * <h5 class='section'>See Also:</h5><ul> 2476 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/HttpParts">HTTP Parts</a> 2477 * <li class='jm'>{@link #partSerializer()} 2478 * </ul> 2479 * 2480 * @param value The new value. 2481 * <br>Cannot be <jk>null</jk>. 2482 * @return This object. 2483 */ 2484 public Builder partSerializer(Class<? extends HttpPartSerializer> value) { 2485 partSerializer().type(assertArgNotNull("value", value)); 2486 return this; 2487 } 2488 2489 /** 2490 * Specifies the part serializer to use for serializing HTTP parts for this class. 2491 * 2492 * <p> 2493 * Equivalent to calling: 2494 * <p class='bjava'> 2495 * <jv>builder</jv>.partSerializer().impl(<jv>value</jv>); 2496 * </p> 2497 * 2498 * <h5 class='section'>See Also:</h5><ul> 2499 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/HttpParts">HTTP Parts</a> 2500 * <li class='jm'>{@link #partSerializer()} 2501 * </ul> 2502 * 2503 * @param value The new value. 2504 * <br>Cannot be <jk>null</jk>. 2505 * @return This object. 2506 */ 2507 public Builder partSerializer(HttpPartSerializer value) { 2508 partSerializer().impl(assertArgNotNull("value", value)); 2509 return this; 2510 } 2511 2512 /** 2513 * Resource path. 2514 * 2515 * <p> 2516 * Identifies the URL subpath relative to the parent resource. 2517 * 2518 * <p> 2519 * This setting is critical for the routing of HTTP requests from ascendant to child resources. 2520 * 2521 * <h5 class='section'>Example:</h5> 2522 * <p class='bjava'> 2523 * <jc>// Option #1 - Defined via annotation.</jc> 2524 * <ja>@Rest</ja>(path=<js>"/myResource"</js>) 2525 * <jk>public class</jk> MyResource { 2526 * 2527 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 2528 * <jk>public</jk> MyResource(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 2529 * 2530 * <jc>// Using method on builder.</jc> 2531 * <jv>builder</jv>.path(<js>"/myResource"</js>); 2532 * } 2533 * 2534 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 2535 * <ja>@RestInit</ja> 2536 * <jk>public void</jk> init(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 2537 * <jv>builder</jv>.path(<js>"/myResource"</js>); 2538 * } 2539 * } 2540 * </p> 2541 * 2542 * <p> 2543 * <h5 class='section'>Notes:</h5><ul> 2544 * <li class='note'> 2545 * This annotation is ignored on top-level servlets (i.e. servlets defined in <c>web.xml</c> files). 2546 * <br>Therefore, implementers can optionally specify a path value for documentation purposes. 2547 * <li class='note'> 2548 * Typically, this setting is only applicable to resources defined as children through the 2549 * {@link Rest#children() @Rest(children)} annotation. 2550 * <br>However, it may be used in other ways (e.g. defining paths for top-level resources in microservices). 2551 * <li class='note'> 2552 * Slashes are trimmed from the path ends. 2553 * <br>As a convention, you may want to start your path with <js>'/'</js> simple because it make it easier to read. 2554 * <li class='note'> 2555 * This path is available through the following method: 2556 * <ul> 2557 * <li class='jm'>{@link RestContext#getPath() RestContext.getPath()} 2558 * </ul> 2559 * </ul> 2560 * 2561 * <h5 class='section'>See Also:</h5><ul> 2562 * <li class='ja'>{@link Rest#path} 2563 * </ul> 2564 * 2565 * @param value The new value for this setting. 2566 * <br>Can be <jk>null</jk> or empty (path will not be set, defaults to empty string). 2567 * @return This object. 2568 */ 2569 public Builder path(String value) { 2570 value = trimLeadingSlashes(value); 2571 if (ne(value)) 2572 path = value; 2573 return this; 2574 } 2575 2576 /** 2577 * Returns the post-call method list. 2578 * 2579 * <p> 2580 * The list of methods that gets called immediately after the <ja>@RestOp</ja> annotated method gets called.. 2581 * 2582 * @return The list of methods that gets called immediately after the <ja>@RestOp</ja> annotated method gets called.. 2583 */ 2584 public MethodList postCallMethods() { 2585 if (postCallMethods == null) 2586 postCallMethods = createPostCallMethods(beanStore(), resource()); 2587 return postCallMethods; 2588 } 2589 2590 /** 2591 * Returns the post-init-child-first method list. 2592 * 2593 * @return The post-init-child-first method list. 2594 */ 2595 public MethodList postInitChildFirstMethods() { 2596 if (postInitChildFirstMethods == null) 2597 postInitChildFirstMethods = createPostInitChildFirstMethods(beanStore(), resource()); 2598 return postInitChildFirstMethods; 2599 } 2600 2601 /** 2602 * Returns the post-init method list. 2603 * 2604 * @return The post-init method list. 2605 */ 2606 public MethodList postInitMethods() { 2607 if (postInitMethods == null) 2608 postInitMethods = createPostInitMethods(beanStore(), resource()); 2609 return postInitMethods; 2610 } 2611 2612 /** 2613 * Returns the pre-call method list. 2614 * 2615 * <p> 2616 * The list of methods that gets called immediately before the <ja>@RestOp</ja> annotated method gets called. 2617 * 2618 * @return The pre-call method list. 2619 */ 2620 public MethodList preCallMethods() { 2621 if (preCallMethods == null) 2622 preCallMethods = createPreCallMethods(beanStore(), resource()); 2623 return preCallMethods; 2624 } 2625 2626 /** 2627 * Returns the media types produced by this resource if it's manually specified. 2628 * 2629 * @return The media types. 2630 */ 2631 public Optional<List<MediaType>> produces() { 2632 return opt(produces); 2633 } 2634 2635 /** 2636 * Supported accept media types. 2637 * 2638 * <p> 2639 * Overrides the media types inferred from the serializers that identify what media types can be produced by the resource. 2640 * <br>An example where this might be useful if you have serializers registered that handle media types that you 2641 * don't want exposed in the Swagger documentation. 2642 * 2643 * <h5 class='section'>Example:</h5> 2644 * <p class='bjava'> 2645 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 2646 * <ja>@Rest</ja>(produces={<js>"$C{REST/supportedProduces,application/json}"</js>}) 2647 * <jk>public class</jk> MyResource { 2648 * 2649 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 2650 * <jk>public</jk> MyResource(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 2651 * 2652 * <jc>// Using method on builder.</jc> 2653 * <jv>builder</jv>.produces(<jk>false</jk>, <js>"application/json"</js>) 2654 * } 2655 * 2656 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 2657 * <ja>@RestInit</ja> 2658 * <jk>public void</jk> init(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 2659 * <jv>builder</jv>.produces(<jk>false</jk>, <js>"application/json"</js>); 2660 * } 2661 * } 2662 * </p> 2663 * 2664 * <p> 2665 * This affects the returned values from the following: 2666 * <ul class='javatree'> 2667 * <li class='jm'>{@link RestContext#getProduces() RestContext.getProduces()} 2668 * <li class='jm'>{@link SwaggerProvider#getSwagger(RestContext,Locale)} - Affects produces field. 2669 * </ul> 2670 * 2671 * <h5 class='section'>See Also:</h5><ul> 2672 * <li class='ja'>{@link Rest#produces} 2673 * <li class='ja'>{@link RestOp#produces} 2674 * <li class='ja'>{@link RestGet#produces} 2675 * <li class='ja'>{@link RestPut#produces} 2676 * <li class='ja'>{@link RestPost#produces} 2677 * </ul> 2678 * 2679 * @param values The values to add to this setting. 2680 * <br>Cannot contain <jk>null</jk> values. 2681 * @return This object. 2682 */ 2683 public Builder produces(MediaType...values) { 2684 assertArgNoNulls("values", values); 2685 produces = addAll(produces, values); 2686 return this; 2687 } 2688 2689 /** 2690 * <i><l>RestContext</l> configuration property: </i> Render response stack traces in responses. 2691 * 2692 * <p> 2693 * Shortcut for calling <code>renderResponseStackTraces(<jk>true</jk>)</code>. 2694 * 2695 * @return This object. 2696 */ 2697 public Builder renderResponseStackTraces() { 2698 renderResponseStackTraces = true; 2699 return this; 2700 } 2701 2702 /** 2703 * <i><l>RestContext</l> configuration property: </i> Render response stack traces in responses. 2704 * 2705 * <p> 2706 * Render stack traces in HTTP response bodies when errors occur. 2707 * 2708 * @param value 2709 * The new value for this setting. 2710 * <br>The default is <jk>false</jk>. 2711 * @return This object. 2712 */ 2713 public Builder renderResponseStackTraces(boolean value) { 2714 renderResponseStackTraces = value; 2715 return this; 2716 } 2717 2718 /** 2719 * Returns the REST servlet/bean instance that this context is defined against. 2720 * 2721 * @return The REST servlet/bean instance that this context is defined against. 2722 */ 2723 public Supplier<?> resource() { 2724 return Objects.requireNonNull(resource, "Resource not available. init(Object) has not been called."); 2725 } 2726 2727 /** 2728 * Returns the REST servlet/bean instance that this context is defined against if it's the specified type. 2729 * 2730 * @param <T> The expected type of the resource bean. 2731 * @param type The expected type of the resource bean. 2732 * <br>Cannot be <jk>null</jk>. 2733 * @return The bean cast to that instance, or {@link Optional#empty()} if it's not the specified type. 2734 */ 2735 public <T> Optional<T> resourceAs(Class<T> type) { 2736 var r = resource().get(); 2737 return opt(assertArgNotNull("type", type).isInstance(r) ? type.cast(r) : null); 2738 } 2739 2740 /** 2741 * Returns the response processor list sub-builder. 2742 * 2743 * <p> 2744 * Specifies a list of {@link ResponseProcessor} classes that know how to convert POJOs returned by REST methods or 2745 * set via {@link RestResponse#setContent(Object)} into appropriate HTTP responses. 2746 * 2747 * <p> 2748 * By default, the following response handlers are provided in the specified order: 2749 * <ul class='javatreec'> 2750 * <li class='jc'>{@link ReaderProcessor} 2751 * <li class='jc'>{@link InputStreamProcessor} 2752 * <li class='jc'>{@link ThrowableProcessor} 2753 * <li class='jc'>{@link HttpResponseProcessor} 2754 * <li class='jc'>{@link HttpResourceProcessor} 2755 * <li class='jc'>{@link HttpEntityProcessor} 2756 * <li class='jc'>{@link ResponseBeanProcessor} 2757 * <li class='jc'>{@link PlainTextPojoProcessor} 2758 * <li class='jc'>{@link SerializedPojoProcessor} 2759 * </ul> 2760 * 2761 * <h5 class='section'>Example:</h5> 2762 * <p class='bjava'> 2763 * <jc>// Our custom response processor for Foo objects. </jc> 2764 * <jk>public class</jk> MyResponseProcessor <jk>implements</jk> ResponseProcessor { 2765 * 2766 * <ja>@Override</ja> 2767 * <jk>public int</jk> process(RestOpSession <jv>opSession</jv>) <jk>throws</jk> IOException { 2768 * 2769 * RestResponse <jv>res</jv> = <jv>opSession</jv>.getResponse(); 2770 * Foo <jv>foo</jv> = <jv>res</jv>.getOutput(Foo.<jk>class</jk>); 2771 * 2772 * <jk>if</jk> (<jv>foo</jv> == <jk>null</jk>) 2773 * <jk>return</jk> <jsf>NEXT</jsf>; <jc>// Let the next processor handle it.</jc> 2774 * 2775 * <jk>try</jk> (Writer <jv>writer</jv> = <jv>res</jv>.getNegotiatedWriter()) { 2776 * <jc>//Pipe it to the writer ourselves.</jc> 2777 * } 2778 * 2779 * <jk>return</jk> <jsf>FINISHED</jsf>; <jc>// We handled it.</jc> 2780 * } 2781 * } 2782 * } 2783 * 2784 * <jc>// Option #1 - Defined via annotation.</jc> 2785 * <ja>@Rest</ja>(responseProcessors=MyResponseProcessor.<jk>class</jk>) 2786 * <jk>public class</jk> MyResource { 2787 * 2788 * <jc>// Option #2 - Defined via builder passed in through init method.</jc> 2789 * <ja>@RestInit</ja> 2790 * <jk>public void</jk> init(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 2791 * <jv>builder</jv>.responseProcessors(MyResponseProcessors.<jk>class</jk>); 2792 * } 2793 * 2794 * <ja>@RestGet</ja>(...) 2795 * <jk>public</jk> Object myMethod() { 2796 * <jc>// Return a special object for our handler.</jc> 2797 * <jk>return new</jk> MySpecialObject(); 2798 * } 2799 * } 2800 * </p> 2801 * 2802 * <p> 2803 * The default response processors can overridden via any of the following: 2804 * <ul class='spaced-list'> 2805 * <li>Injected via bean store. 2806 * <li>Class annotation: {@link Rest#responseProcessors() @Rest(responseProcessors)} 2807 * <li>{@link RestInject @RestInject}-annotated method: 2808 * <p class='bjava'> 2809 * <ja>@RestInject</ja> <jk>public</jk> [<jk>static</jk>] ResponseProcessorList myMethod(<i><args></i>) {...} 2810 * </p> 2811 * Args can be any injected bean including ResponseProcessorList.Builder, the default builder. 2812 * </ul> 2813 * 2814 * <h5 class='section'>Notes:</h5><ul> 2815 * <li class='note'> 2816 * Response processors are always inherited from ascendant resources. 2817 * <li class='note'> 2818 * When defined as a class, the implementation must have one of the following constructors: 2819 * <ul> 2820 * <li><code><jk>public</jk> T(RestContext)</code> 2821 * <li><code><jk>public</jk> T()</code> 2822 * <li><code><jk>public static</jk> T <jsm>create</jsm>(RestContext)</code> 2823 * <li><code><jk>public static</jk> T <jsm>create</jsm>()</code> 2824 * </ul> 2825 * <li class='note'> 2826 * Inner classes of the REST resource class are allowed. 2827 * </ul> 2828 * 2829 * <h5 class='section'>See Also:</h5><ul> 2830 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/ResponseProcessors">Response Processors</a> 2831 * </ul> 2832 * 2833 * @return The response processor list sub-builder. 2834 */ 2835 public ResponseProcessorList.Builder responseProcessors() { 2836 if (responseProcessors == null) 2837 responseProcessors = createResponseProcessors(beanStore(), resource()); 2838 return responseProcessors; 2839 } 2840 2841 /** 2842 * Adds one or more response processors to this class. 2843 * 2844 * <p> 2845 * Equivalent to calling: 2846 * <p class='bjava'> 2847 * <jv>builder</jv>.responseProcessors().add(<jv>value</jv>); 2848 * </p> 2849 * 2850 * <h5 class='section'>See Also:</h5><ul> 2851 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/ResponseProcessors">Response Processors</a> 2852 * <li class='jm'>{@link #responseProcessors()} 2853 * </ul> 2854 * 2855 * @param value The values to add. 2856 * <br>Cannot contain <jk>null</jk> values. 2857 * @return This object. 2858 */ 2859 @SafeVarargs 2860 public final Builder responseProcessors(Class<? extends ResponseProcessor>...value) { 2861 assertArgNoNulls("value", value); 2862 responseProcessors().add(value); 2863 return this; 2864 } 2865 2866 /** 2867 * Adds one or more response processors to this class. 2868 * 2869 * <p> 2870 * Equivalent to calling: 2871 * <p class='bjava'> 2872 * <jv>builder</jv>.responseProcessors().add(<jv>value</jv>); 2873 * </p> 2874 * 2875 * <h5 class='section'>See Also:</h5><ul> 2876 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/ResponseProcessors">Response Processors</a> 2877 * <li class='jm'>{@link #responseProcessors()} 2878 * </ul> 2879 * 2880 * @param value The values to add. 2881 * <br>Cannot contain <jk>null</jk> values. 2882 * @return This object. 2883 */ 2884 public Builder responseProcessors(ResponseProcessor...value) { 2885 assertArgNoNulls("value", value); 2886 responseProcessors().add(value); 2887 return this; 2888 } 2889 2890 /** 2891 * Returns the REST children list. 2892 * 2893 * @param restContext The rest context. 2894 * <br>Can be <jk>null</jk> if the bean is a top-level resource. 2895 * @return The REST children list. 2896 * @throws Exception If a problem occurred instantiating one of the child rest contexts. 2897 */ 2898 public RestChildren.Builder restChildren(RestContext restContext) throws Exception { 2899 if (restChildren == null) 2900 restChildren = createRestChildren(beanStore(), resource(), restContext); 2901 return restChildren; 2902 } 2903 2904 /** 2905 * REST children class. 2906 * 2907 * <p> 2908 * Allows you to extend the {@link RestChildren} class to modify how any of the methods are implemented. 2909 * 2910 * <p> 2911 * The subclass must have a public constructor that takes in any of the following arguments: 2912 * <ul> 2913 * <li>{@link RestChildren.Builder} - The builder for the object. 2914 * <li>Any beans found in the specified bean store. 2915 * <li>Any {@link Optional} beans that may or may not be found in the specified bean store. 2916 * </ul> 2917 * 2918 * <h5 class='section'>Example:</h5> 2919 * <p class='bjava'> 2920 * <jc>// Our extended context class</jc> 2921 * <jk>public</jk> MyRestChildren <jk>extends</jk> RestChildren { 2922 * <jk>public</jk> MyRestChildren(RestChildren.Builder <jv>builder</jv>, ARequiredSpringBean <jv>bean1</jv>, Optional<AnOptionalSpringBean> <jv>bean2</jv>) { 2923 * <jk>super</jk>(<jv>builder</jv>); 2924 * } 2925 * 2926 * <jc>// Override any methods.</jc> 2927 * 2928 * <ja>@Override</ja> 2929 * <jk>public</jk> Optional<RestChildMatch> findMatch(RestCall <jv>call</jv>) { 2930 * String <jv>path</jv> = <jv>call</jv>.getPathInfo(); 2931 * <jk>if</jk> (<jv>path</jv>.endsWith(<js>"/foo"</js>)) { 2932 * <jc>// Do our own special handling.</jc> 2933 * } 2934 * <jk>return super</jk>.findMatch(<jv>call</jv>); 2935 * } 2936 * } 2937 * </p> 2938 * <p class='bjava'> 2939 * <jc>// Option #1 - Defined via annotation.</jc> 2940 * <ja>@Rest</ja>(restChildrenClass=MyRestChildren.<jk>class</jk>) 2941 * <jk>public class</jk> MyResource { 2942 * ... 2943 * 2944 * <jc>// Option #2 - Defined via builder passed in through init method.</jc> 2945 * <ja>@RestInit</ja> 2946 * <jk>public void</jk> init(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 2947 * <jv>builder</jv>.restChildrenClass(MyRestChildren.<jk>class</jk>); 2948 * } 2949 * } 2950 * </p> 2951 * 2952 * @param value The new value for this setting. 2953 * <br>Cannot be <jk>null</jk>. 2954 * @return This object. 2955 */ 2956 public Builder restChildrenClass(Class<? extends RestChildren> value) { 2957 childrenClass = assertArgNotNull("value", value); 2958 return this; 2959 } 2960 2961 /** 2962 * Returns the REST operation args sub-builder. 2963 * 2964 * @return The REST operation args sub-builder. 2965 */ 2966 public RestOpArgList.Builder restOpArgs() { 2967 if (restOpArgs == null) 2968 restOpArgs = createRestOpArgs(beanStore(), resource()); 2969 return restOpArgs; 2970 } 2971 2972 /** 2973 * Adds one or more REST operation args to this class. 2974 * 2975 * <p> 2976 * Equivalent to calling: 2977 * <p class='bjava'> 2978 * <jv>builder</jv>.restOpArgs().add(<jv>value</jv>); 2979 * </p> 2980 * 2981 * @param value The new value. 2982 * <br>Cannot contain <jk>null</jk> values. 2983 * @return This object. 2984 */ 2985 @SafeVarargs 2986 public final Builder restOpArgs(Class<? extends RestOpArg>...value) { 2987 assertArgNoNulls("value", value); 2988 restOpArgs().add(value); 2989 return this; 2990 } 2991 2992 /** 2993 * REST operation context class. 2994 * 2995 * <p> 2996 * Allows you to extend the {@link RestOpContext} class to modify how any of the methods are implemented. 2997 * 2998 * <p> 2999 * The subclass must have a public constructor that takes in any of the following arguments: 3000 * <ul> 3001 * <li>{@link RestOpContext.Builder} - The builder for the object. 3002 * <li>Any beans found in the specified bean store. 3003 * <li>Any {@link Optional} beans that may or may not be found in the specified bean store. 3004 * </ul> 3005 * 3006 * <h5 class='section'>Example:</h5> 3007 * <p class='bjava'> 3008 * <jc>// Our extended context class that adds a request attribute to all requests.</jc> 3009 * <jc>// The attribute value is provided by an injected spring bean.</jc> 3010 * <jk>public</jk> MyRestOperationContext <jk>extends</jk> RestOpContext { 3011 * 3012 * <jk>private final</jk> Optional<? <jk>extends</jk> Supplier<Object>> <jf>fooSupplier</jf>; 3013 * 3014 * <jc>// Constructor that takes in builder and optional injected attribute provider.</jc> 3015 * <jk>public</jk> MyRestOperationContext(RestOpContext.Builder <jv>builder</jv>, Optional<AnInjectedFooSupplier> <jv>fooSupplier</jv>) { 3016 * <jk>super</jk>(<jv>builder</jv>); 3017 * <jk>this</jk>.<jf>fooSupplier</jf> = <jv>fooSupplier</jv>.orElseGet(()-><jk>null</jk>); 3018 * } 3019 * 3020 * <jc>// Override the method used to create default request attributes.</jc> 3021 * <ja>@Override</ja> 3022 * <jk>protected</jk> NamedAttributeMap createDefaultRequestAttributes(Object <jv>resource</jv>, BeanStore <jv>beanStore</jv>, Method <jv>method</jv>, RestContext <jv>context</jv>) <jk>throws</jk> Exception { 3023 * <jk>return super</jk> 3024 * .createDefaultRequestAttributes(<jv>resource</jv>, <jv>beanStore</jv>, <jv>method</jv>, <jv>context</jv>) 3025 * .append(NamedAttribute.<jsm>of</jsm>(<js>"foo"</js>, ()-><jf>fooSupplier</jf>.get()); 3026 * } 3027 * } 3028 * </p> 3029 * <p class='bjava'> 3030 * <jc>// Option #1 - Defined via annotation.</jc> 3031 * <ja>@Rest</ja>(restOpContextClass=MyRestOperationContext.<jk>class</jk>) 3032 * <jk>public class</jk> MyResource { 3033 * ... 3034 * 3035 * <jc>// Option #2 - Defined via builder passed in through init method.</jc> 3036 * <ja>@RestInit</ja> 3037 * <jk>public void</jk> init(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 3038 * <jv>builder</jv>.methodContextClass(MyRestOperationContext.<jk>class</jk>); 3039 * } 3040 * 3041 * <ja>@RestGet</ja> 3042 * <jk>public</jk> Object foo(RequestAttributes <jv>attributes</jv>) { 3043 * <jk>return</jk> <jv>attributes</jv>.get(<js>"foo"</js>); 3044 * } 3045 * } 3046 * </p> 3047 * 3048 * @param value The new value for this setting. 3049 * <br>Cannot be <jk>null</jk>. 3050 * @return This object. 3051 */ 3052 public Builder restOpContextClass(Class<? extends RestOpContext> value) { 3053 opContextClass = assertArgNotNull("value", value); 3054 return this; 3055 } 3056 3057 /** 3058 * Returns the REST operations list. 3059 * 3060 * @param restContext The rest context. 3061 * <br>Cannot be <jk>null</jk>. 3062 * @return The REST operations list. 3063 * @throws ServletException If a problem occurred instantiating one of the child rest contexts. 3064 */ 3065 public RestOperations.Builder restOperations(RestContext restContext) throws ServletException { 3066 if (restOperations == null) 3067 restOperations = createRestOperations(beanStore(), resource(), assertArgNotNull("restContext", restContext)); 3068 return restOperations; 3069 } 3070 3071 /** 3072 * Returns the root bean store. 3073 * 3074 * <p> 3075 * This is the bean store inherited from the parent resource and does not include 3076 * any beans added by this class. 3077 * 3078 * @return The root bean store. 3079 */ 3080 public BeanStore rootBeanStore() { 3081 return rootBeanStore; 3082 } 3083 3084 /** 3085 * <i><l>RestContext</l> configuration property: </i> Serializer listener. 3086 * 3087 * <p> 3088 * Specifies the serializer listener class to use for listening to non-fatal serialization errors. 3089 * 3090 * <h5 class='section'>See Also:</h5><ul> 3091 * <li class='jm'>{@link org.apache.juneau.serializer.Serializer.Builder#listener(Class)} 3092 * </ul> 3093 * 3094 * @param value The new value for this setting. 3095 * <br>Cannot be <jk>null</jk>. 3096 * @return This object. 3097 */ 3098 public Builder serializerListener(Class<? extends SerializerListener> value) { 3099 if (isNotVoid(assertArgNotNull("value", value))) 3100 serializers.forEach(x -> x.listener(value)); 3101 return this; 3102 } 3103 3104 /** 3105 * Returns the serializer group sub-builder. 3106 * 3107 * <p> 3108 * Serializers are used to convert POJOs to HTTP response bodies based on the {@code Accept} header. 3109 * 3110 * <p> 3111 * The default serializer set is empty. 3112 * It can be overridden via any of the following: 3113 * <ul class='spaced-list'> 3114 * <li>Injected via bean store. 3115 * <li>Class annotation: {@link Rest#serializers() @Rest(serializers)} 3116 * <li>{@link RestInject @RestInject}-annotated method: 3117 * <p class='bjava'> 3118 * <ja>@RestInject</ja> <jk>public</jk> [<jk>static</jk>] SerializerSet myMethod(<i><args></i>) {...} 3119 * </p> 3120 * Args can be any injected bean including SerializerSet.Builder, the default builder. 3121 * </ul> 3122 * 3123 * <h5 class='section'>See Also:</h5><ul> 3124 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Marshalling">Marshalling</a> 3125 * </ul> 3126 * 3127 * @return The serializer group sub-builder. 3128 */ 3129 public SerializerSet.Builder serializers() { 3130 if (serializers == null) 3131 serializers = createSerializers(beanStore(), resource()); 3132 return serializers; 3133 } 3134 3135 /** 3136 * Adds one or more serializers to this class. 3137 * 3138 * <p> 3139 * Equivalent to calling: 3140 * <p class='bjava'> 3141 * <jv>builder</jv>.serializers().add(<jv>value</jv>); 3142 * </p> 3143 * 3144 * <h5 class='section'>See Also:</h5><ul> 3145 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Marshalling">Marshalling</a> 3146 * <li class='jm'>{@link #serializers()} 3147 * </ul> 3148 * 3149 * @param value The values to add. 3150 * <br>Cannot contain <jk>null</jk> values. 3151 * @return This object. 3152 */ 3153 @SafeVarargs 3154 public final Builder serializers(Class<? extends Serializer>...value) { 3155 assertArgNoNulls("value", value); 3156 serializers().add(value); 3157 return this; 3158 } 3159 3160 /** 3161 * Adds one or more serializers to this class. 3162 * 3163 * <p> 3164 * Equivalent to calling: 3165 * <p class='bjava'> 3166 * <jv>builder</jv>.serializers().add(<jv>value</jv>); 3167 * </p> 3168 * 3169 * <h5 class='section'>See Also:</h5><ul> 3170 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Marshalling">Marshalling</a> 3171 * <li class='jm'>{@link #serializers()} 3172 * </ul> 3173 * 3174 * @param value The values to add. 3175 * <br>Cannot contain <jk>null</jk> values. 3176 * @return This object. 3177 */ 3178 public Builder serializers(Serializer...value) { 3179 assertArgNoNulls("value", value); 3180 serializers().add(value); 3181 return this; 3182 } 3183 3184 /** 3185 * Returns the start call method list. 3186 * 3187 * @return The start call method list. 3188 */ 3189 public MethodList startCallMethods() { 3190 if (startCallMethods == null) 3191 startCallMethods = createStartCallMethods(beanStore(), resource()); 3192 return startCallMethods; 3193 } 3194 3195 /** 3196 * Returns the static files bean creator. 3197 * 3198 * <p> 3199 * Used to retrieve localized files to be served up as static files through the REST API via the following 3200 * predefined methods: 3201 * <ul class='javatree'> 3202 * <li class='jm'>{@link BasicRestObject#getHtdoc(String, Locale)}. 3203 * <li class='jm'>{@link BasicRestServlet#getHtdoc(String, Locale)}. 3204 * </ul> 3205 * 3206 * <p> 3207 * The static file finder can be accessed through the following methods: 3208 * <ul class='javatree'> 3209 * <li class='jm'>{@link RestContext#getStaticFiles()} 3210 * <li class='jm'>{@link RestRequest#getStaticFiles()} 3211 * </ul> 3212 * 3213 * <p> 3214 * The default static files finder implementation class is {@link BasicStaticFiles}. This can be overridden via the following: 3215 * <ul class='spaced-list'> 3216 * <li> 3217 * The {@link Rest#staticFiles() @Rest(staticFiles)} annotation. 3218 * <li> 3219 * Overridden {@link StaticFiles} implementation class name specified in {@link #defaultClasses()}. 3220 * <li> 3221 * Type specified via <c>{@link RestContext.Builder}.{@link #staticFiles() staticFiles()}.{@link org.apache.juneau.rest.staticfile.StaticFiles.Builder#type(Class) type(Class)}</c>. 3222 * <li> 3223 * Bean specified via <c>{@link RestContext.Builder}.{@link #staticFiles() staticFiles()}.{@link org.apache.juneau.rest.staticfile.StaticFiles.Builder#impl(Object) impl(Object)}</c>. 3224 * </ul> 3225 * 3226 * <h5 class='section'>Example:</h5> 3227 * <p class='bjava'> 3228 * <jc>// Create a static file finder that looks for files in the /files working subdirectory, but 3229 * // overrides the find() and resolve methods for special handling of special cases and adds a 3230 * // Foo header to all requests.</jc> 3231 * <jk>public class</jk> MyStaticFiles <jk>extends</jk> BasicStaticFiles { 3232 * 3233 * <jk>public</jk> MyStaticFiles() { 3234 * <jk>super</jk>( 3235 * StaticFiles 3236 * .<jsm>create</jsm>() 3237 * .dir(<js>"/files"</js>) 3238 * .headers(BasicStringHeader.<jsm>of</jsm>(<js>"Foo"</js>, <js>"bar"</js>)) 3239 * ); 3240 * } 3241 * } 3242 * </p> 3243 * <p class='bjava'> 3244 * <ja>@Rest</ja>(staticFiles=MyStaticFiles.<jk>class</jk>) 3245 * <jk>public class</jk> MyResource {...} 3246 * </p> 3247 * 3248 * <h5 class='section'>See Also:</h5><ul> 3249 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/StaticFiles">Static files</a> 3250 * </ul> 3251 * 3252 * @return The static files bean creator. 3253 */ 3254 public BeanCreator<StaticFiles> staticFiles() { 3255 if (staticFiles == null) 3256 staticFiles = createStaticFiles(beanStore, resource); 3257 return staticFiles; 3258 } 3259 3260 /** 3261 * Specifies the static files resource finder for this class. 3262 * 3263 * <p> 3264 * Equivalent to calling: 3265 * <p class='bjava'> 3266 * <jv>builder</jv>.staticFiles().type(<jv>value</jv>); 3267 * </p> 3268 * 3269 * <h5 class='section'>See Also:</h5><ul> 3270 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/StaticFiles">Static files</a> 3271 * </ul> 3272 * 3273 * @param value The new value. 3274 * <br>Cannot be <jk>null</jk>. 3275 * @return This object. 3276 */ 3277 public Builder staticFiles(Class<? extends StaticFiles> value) { 3278 staticFiles().type(assertArgNotNull("value", value)); 3279 return this; 3280 } 3281 3282 /** 3283 * Specifies the static files resource finder for this class. 3284 * 3285 * <p> 3286 * Equivalent to calling: 3287 * <p class='bjava'> 3288 * <jv>builder</jv>.staticFiles().impl(<jv>value</jv>); 3289 * </p> 3290 * 3291 * <h5 class='section'>See Also:</h5><ul> 3292 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/StaticFiles">Static files</a> 3293 * </ul> 3294 * 3295 * @param value The new value. 3296 * <br>Cannot be <jk>null</jk>. 3297 * @return This object. 3298 */ 3299 public Builder staticFiles(StaticFiles value) { 3300 staticFiles().impl(assertArgNotNull("value", value)); 3301 return this; 3302 } 3303 3304 /** 3305 * Returns the swagger provider sub-builder. 3306 * 3307 * @return The swagger provider sub-builder. 3308 */ 3309 public BeanCreator<SwaggerProvider> swaggerProvider() { 3310 if (swaggerProvider == null) 3311 swaggerProvider = createSwaggerProvider(beanStore, resource); 3312 return swaggerProvider; 3313 } 3314 3315 /** 3316 * Specifies the swagger provider for this class. 3317 * 3318 * <p> 3319 * Equivalent to calling: 3320 * <p class='bjava'> 3321 * <jv>builder</jv>.swaggerProvider().type(<jv>value</jv>); 3322 * </p> 3323 * 3324 * @param value The new value. 3325 * <br>Cannot be <jk>null</jk>. 3326 * @return This object. 3327 */ 3328 public Builder swaggerProvider(Class<? extends SwaggerProvider> value) { 3329 swaggerProvider().type(assertArgNotNull("value", value)); 3330 return this; 3331 } 3332 3333 /** 3334 * Specifies the swagger provider for this class. 3335 * 3336 * <p> 3337 * Equivalent to calling: 3338 * <p class='bjava'> 3339 * <jv>builder</jv>.swaggerProvider().impl(<jv>value</jv>); 3340 * </p> 3341 * 3342 * @param value The new value. 3343 * <br>Cannot be <jk>null</jk>. 3344 * @return This object. 3345 */ 3346 public Builder swaggerProvider(SwaggerProvider value) { 3347 swaggerProvider().impl(assertArgNotNull("value", value)); 3348 return this; 3349 } 3350 3351 /** 3352 * Returns the thrown-store sub-builder. 3353 * 3354 * <p> 3355 * The thrown store is an in-memory cache of thrown exceptions. 3356 * It is used to store thrown exceptions when {@link MethodExecStats#error(Throwable)} is called from the {@link MethodExecStore} 3357 * bean of this resource. It can also be accessed directly via {@link RestContext#getThrownStore()} or passed in as a parameter 3358 * on a {@link RestOp}-annotated method. 3359 * 3360 * <p> 3361 * The default thrown store is inherited from the parent context and can be overridden via any of the following: 3362 * <ul class='spaced-list'> 3363 * <li>Injected via bean store. 3364 * <li>{@link RestInject @RestInject}-annotated method: 3365 * <p class='bjava'> 3366 * <ja>@RestInject</ja> <jk>public</jk> [<jk>static</jk>] ThrownStore myMethod(<i><args></i>) {...} 3367 * </p> 3368 * Args can be any injected bean including ThrownStore.Builder, the default builder. 3369 * </ul> 3370 * 3371 * <h5 class='section'>See Also:</h5><ul> 3372 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/ExecutionStatistics">REST method execution statistics</a> 3373 * </ul> 3374 * 3375 * @return The builder for the {@link ThrownStore} object in the REST context. 3376 */ 3377 public ThrownStore.Builder thrownStore() { 3378 if (thrownStore == null) 3379 thrownStore = createThrownStore(beanStore(), resource(), parentContext); 3380 return thrownStore; 3381 } 3382 3383 /** 3384 * Specifies the thrown store for this class. 3385 * 3386 * <p> 3387 * Equivalent to calling: 3388 * <p class='bjava'> 3389 * <jv>builder</jv>.thrownStore().type(<jv>value</jv>); 3390 * </p> 3391 * 3392 * <h5 class='section'>See Also:</h5><ul> 3393 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/ExecutionStatistics">REST method execution statistics</a> 3394 * <li class='jm'>{@link #thrownStore()} 3395 * </ul> 3396 * 3397 * @param value The new value. 3398 * <br>Cannot be <jk>null</jk>. 3399 * @return This object. 3400 */ 3401 public Builder thrownStore(Class<? extends ThrownStore> value) { 3402 thrownStore().type(assertArgNotNull("value", value)); 3403 return this; 3404 } 3405 3406 /** 3407 * Specifies the thrown store for this class. 3408 * 3409 * <p> 3410 * Equivalent to calling: 3411 * <p class='bjava'> 3412 * <jv>builder</jv>.thrownStore().impl(<jv>value</jv>); 3413 * </p> 3414 * 3415 * <h5 class='section'>See Also:</h5><ul> 3416 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/ExecutionStatistics">REST method execution statistics</a> 3417 * <li class='jm'>{@link #thrownStore()} 3418 * </ul> 3419 * 3420 * @param value The new value. 3421 * <br>Cannot be <jk>null</jk>. 3422 * @return This object. 3423 */ 3424 public Builder thrownStore(ThrownStore value) { 3425 thrownStore().impl(assertArgNotNull("value", value)); 3426 return this; 3427 } 3428 3429 @Override /* Overridden from Builder */ 3430 public Builder type(Class<? extends org.apache.juneau.Context> value) { 3431 super.type(value); 3432 return this; 3433 } 3434 3435 /** 3436 * Resource authority path. 3437 * 3438 * <p> 3439 * Overrides the authority path value for this resource and any child resources. 3440 * 3441 * <p> 3442 * This setting is useful if you want to resolve relative URIs to absolute paths and want to explicitly specify the hostname/port. 3443 * 3444 * <p> 3445 * Affects the following methods: 3446 * <ul class='javatree'> 3447 * <li class='jm'>{@link RestRequest#getAuthorityPath()} 3448 * </ul> 3449 * 3450 * <p> 3451 * If you do not specify the authority, it is automatically calculated via the following: 3452 * 3453 * <p class='bjava'> 3454 * String <jv>scheme</jv> = <jv>request</jv>.getScheme(); 3455 * <jk>int</jk> <jv>port</jv> = <jv>request</jv>.getServerPort(); 3456 * StringBuilder <jv>sb</jv> = <jk>new</jk> StringBuilder(<jv>request</jv>.getScheme()).append(<js>"://"</js>).append(<jv>request</jv>.getServerName()); 3457 * <jk>if</jk> (! (<jv>port</jv> == 80 && <js>"http"</js>.equals(<jv>scheme</jv>) || port == 443 && <js>"https"</js>.equals(<jv>scheme</jv>))) 3458 * <jv>sb</jv>.append(<js>':'</js>).append(<jv>port</jv>); 3459 * <jv>authorityPath</jv> = <jv>sb</jv>.toString(); 3460 * </p> 3461 * 3462 * <h5 class='section'>Example:</h5> 3463 * <p class='bjava'> 3464 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 3465 * <ja>@Rest</ja>( 3466 * path=<js>"/servlet"</js>, 3467 * uriAuthority=<js>"$C{REST/authorityPathOverride,http://localhost:10000}"</js> 3468 * ) 3469 * <jk>public class</jk> MyResource { 3470 * 3471 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 3472 * <jk>public</jk> MyResource(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 3473 * 3474 * <jc>// Using method on builder.</jc> 3475 * <jv>builder</jv>.uriAuthority(<js>"http://localhost:10000"</js>); 3476 * } 3477 * 3478 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 3479 * <ja>@RestInit</ja> 3480 * <jk>public void</jk> init(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 3481 * <jv>builder</jv>.uriAuthority(<js>"http://localhost:10000"</js>); 3482 * } 3483 * } 3484 * </p> 3485 * 3486 * <h5 class='section'>See Also:</h5><ul> 3487 * <li class='ja'>{@link Rest#uriAuthority} 3488 * </ul> 3489 * 3490 * @param value 3491 * The new value for this setting. 3492 * <br>The default is the first value found: 3493 * <ul> 3494 * <li>System property <js>"RestContext.uriAuthority" 3495 * <li>Environment variable <js>"RESTCONTEXT_URIAUTHORITY" 3496 * <li><jk>null</jk> 3497 * </ul> 3498 * <br>Cannot be <jk>null</jk>. 3499 * @return This object. 3500 */ 3501 public Builder uriAuthority(String value) { 3502 uriAuthority = assertArgNotNull("value", value); 3503 return this; 3504 } 3505 3506 /** 3507 * Resource context path. 3508 * 3509 * <p> 3510 * Overrides the context path value for this resource and any child resources. 3511 * 3512 * <p> 3513 * This setting is useful if you want to use <js>"context:/child/path"</js> URLs in child resource POJOs but 3514 * the context path is not actually specified on the servlet container. 3515 * 3516 * <p> 3517 * Affects the following methods: 3518 * <ul class='javatree'> 3519 * <li class='jm'>{@link RestRequest#getContextPath()} - Returns the overridden context path for the resource. 3520 * <li class='jm'>{@link RestRequest#getServletPath()} - Includes the overridden context path for the resource. 3521 * </ul> 3522 * 3523 * <h5 class='section'>Example:</h5> 3524 * <p class='bjava'> 3525 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 3526 * <ja>@Rest</ja>( 3527 * path=<js>"/servlet"</js>, 3528 * uriContext=<js>"$C{REST/contextPathOverride,/foo}"</js> 3529 * ) 3530 * <jk>public class</jk> MyResource { 3531 * 3532 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 3533 * <jk>public</jk> MyResource(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 3534 * 3535 * <jc>// Using method on builder.</jc> 3536 * <jv>builder</jv>.uriContext(<js>"/foo"</js>); 3537 * } 3538 * 3539 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 3540 * <ja>@RestInit</ja> 3541 * <jk>public void</jk> init(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 3542 * <jv>builder</jv>.uriContext(<js>"/foo"</js>); 3543 * } 3544 * } 3545 * </p> 3546 * 3547 * <h5 class='section'>See Also:</h5><ul> 3548 * <li class='ja'>{@link Rest#uriContext} 3549 * </ul> 3550 * 3551 * @param value 3552 * The new value for this setting. 3553 * <br>The default is the first value found: 3554 * <ul> 3555 * <li>System property <js>"RestContext.uriContext" 3556 * <li>Environment variable <js>"RESTCONTEXT_URICONTEXT" 3557 * <li><jk>null</jk> 3558 * </ul> 3559 * <br>Cannot be <jk>null</jk>. 3560 * @return This object. 3561 */ 3562 public Builder uriContext(String value) { 3563 uriContext = assertArgNotNull("value", value); 3564 return this; 3565 } 3566 3567 /** 3568 * URI resolution relativity. 3569 * 3570 * <p> 3571 * Specifies how relative URIs should be interpreted by serializers. 3572 * 3573 * <p> 3574 * See {@link UriResolution} for possible values. 3575 * 3576 * <p> 3577 * Affects the following methods: 3578 * <ul class='javatree'> 3579 * <li class='jm'>{@link RestRequest#getUriResolver()} 3580 * </ul> 3581 * 3582 * <h5 class='section'>Example:</h5> 3583 * <p class='bjava'> 3584 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 3585 * <ja>@Rest</ja>( 3586 * path=<js>"/servlet"</js>, 3587 * uriRelativity=<js>"$C{REST/uriRelativity,PATH_INFO}"</js> 3588 * ) 3589 * <jk>public class</jk> MyResource { 3590 * 3591 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 3592 * <jk>public</jk> MyResource(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 3593 * 3594 * <jc>// Using method on builder.</jc> 3595 * <jv>builder</jv>.uriRelativity(<jsf>PATH_INFO</jsf>); 3596 * } 3597 * 3598 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 3599 * <ja>@RestInit</ja> 3600 * <jk>public void</jk> init(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 3601 * <jv>builder</jv>.uriRelativity(<jsf>PATH_INFO</jsf>); 3602 * } 3603 * } 3604 * </p> 3605 * 3606 * <h5 class='section'>See Also:</h5><ul> 3607 * <li class='ja'>{@link Rest#uriRelativity} 3608 * </ul> 3609 * 3610 * @param value 3611 * The new value for this setting. 3612 * <br>The default is the first value found: 3613 * <ul> 3614 * <li>System property <js>"RestContext.uriRelativity" 3615 * <li>Environment variable <js>"RESTCONTEXT_URIRELATIVITY" 3616 * <li>{@link UriRelativity#RESOURCE} 3617 * </ul> 3618 * <br>Cannot be <jk>null</jk>. 3619 * @return This object. 3620 */ 3621 public Builder uriRelativity(UriRelativity value) { 3622 uriRelativity = assertArgNotNull("value", value); 3623 return this; 3624 } 3625 3626 /** 3627 * URI resolution. 3628 * 3629 * <p> 3630 * Specifies how relative URIs should be interpreted by serializers. 3631 * 3632 * <p> 3633 * See {@link UriResolution} for possible values. 3634 * 3635 * <p> 3636 * Affects the following methods: 3637 * <ul class='javatree'> 3638 * <li class='jm'>{@link RestRequest#getUriResolver()} 3639 * </ul> 3640 * 3641 * <h5 class='section'>Example:</h5> 3642 * <p class='bjava'> 3643 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 3644 * <ja>@Rest</ja>( 3645 * path=<js>"/servlet"</js>, 3646 * uriResolution=<js>"$C{REST/uriResolution,ABSOLUTE}"</js> 3647 * ) 3648 * <jk>public class</jk> MyResource { 3649 * 3650 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 3651 * <jk>public</jk> MyResource(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 3652 * 3653 * <jc>// Using method on builder.</jc> 3654 * <jv>builder</jv>.uriResolution(<jsf>ABSOLUTE</jsf>); 3655 * } 3656 * 3657 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 3658 * <ja>@RestInit</ja> 3659 * <jk>public void</jk> init(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 3660 * <jv>builder</jv>.uriResolution(<jsf>ABSOLUTE</jsf>); 3661 * } 3662 * } 3663 * </p> 3664 * 3665 * <h5 class='section'>See Also:</h5><ul> 3666 * <li class='ja'>{@link Rest#uriResolution} 3667 * </ul> 3668 * 3669 * @param value 3670 * The new value for this setting. 3671 * <br>The default is the first value found: 3672 * <ul> 3673 * <li>System property <js>"RestContext.uriResolution" 3674 * <li>Environment variable <js>"RESTCONTEXT_URIRESOLUTION" 3675 * <li>{@link UriResolution#ROOT_RELATIVE} 3676 * </ul> 3677 * <br>Cannot be <jk>null</jk>. 3678 * @return This object. 3679 */ 3680 public Builder uriResolution(UriResolution value) { 3681 uriResolution = assertArgNotNull("value", value); 3682 return this; 3683 } 3684 3685 /** 3686 * Returns the variable resolver sub-builder. 3687 * 3688 * <p> 3689 * The variable resolver is used to resolve string variables of the form <js>"$X{...}"</js> in various places such as annotations on the REST class and methods. 3690 * 3691 * <p> 3692 * Can be used to add more variables or context objects to the variable resolver. 3693 * These variables affect the variable resolver returned by {@link RestRequest#getVarResolverSession()}. 3694 * 3695 * <p> 3696 * The var resolver is created by the constructor using the {@link #createVarResolver(BeanStore,Supplier,Class)} method and is initialized with the following variables: 3697 * <ul class='javatreec'> 3698 * <li class='jc'>{@link ArgsVar} 3699 * <li class='jc'>{@link CoalesceVar} 3700 * <li class='jc'>{@link ConfigVar} 3701 * <li class='jc'>{@link EnvVariablesVar} 3702 * <li class='jc'>{@link FileVar} 3703 * <li class='jc'>{@link HtmlWidgetVar} 3704 * <li class='jc'>{@link IfVar} 3705 * <li class='jc'>{@link LenVar} 3706 * <li class='jc'>{@link LocalizationVar} 3707 * <li class='jc'>{@link LowerCaseVar} 3708 * <li class='jc'>{@link ManifestFileVar} 3709 * <li class='jc'>{@link NotEmptyVar} 3710 * <li class='jc'>{@link PatternExtractVar} 3711 * <li class='jc'>{@link PatternMatchVar} 3712 * <li class='jc'>{@link PatternReplaceVar} 3713 * <li class='jc'>{@link RequestAttributeVar} 3714 * <li class='jc'>{@link RequestFormDataVar} 3715 * <li class='jc'>{@link RequestHeaderVar} 3716 * <li class='jc'>{@link RequestPathVar} 3717 * <li class='jc'>{@link RequestQueryVar} 3718 * <li class='jc'>{@link RequestSwaggerVar} 3719 * <li class='jc'>{@link RequestVar} 3720 * <li class='jc'>{@link SerializedRequestAttrVar} 3721 * <li class='jc'>{@link ServletInitParamVar} 3722 * <li class='jc'>{@link SubstringVar} 3723 * <li class='jc'>{@link SwaggerVar} 3724 * <li class='jc'>{@link SwitchVar} 3725 * <li class='jc'>{@link SystemPropertiesVar} 3726 * <li class='jc'>{@link UpperCaseVar} 3727 * <li class='jc'>{@link UrlEncodeVar} 3728 * <li class='jc'>{@link UrlVar} 3729 * </ul> 3730 * 3731 * <p> 3732 * The default var resolver can be overridden via any of the following: 3733 * <ul class='spaced-list'> 3734 * <li>Injected via bean store. 3735 * <li>{@link RestInject @RestInject}-annotated methods: 3736 * <p class='bjava'> 3737 * <ja>@RestInject</ja> <jk>public</jk> [<jk>static</jk>] VarResolver myMethod(<i><args></i>) {...} 3738 * </p> 3739 * Args can be any injected bean including {@link org.apache.juneau.svl.VarResolver.Builder}, the default builder. 3740 * </ul> 3741 * 3742 * <h5 class='section'>See Also:</h5><ul> 3743 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerSvlVariables">SVL Variables</a> 3744 * </ul> 3745 * 3746 * @return The variable resolver sub-builder. 3747 */ 3748 public VarResolver.Builder varResolver() { 3749 return varResolver; 3750 } 3751 3752 /** 3753 * Adds one or more variables to the var resolver of this class. 3754 * 3755 * <p> 3756 * Equivalent to calling: 3757 * <p class='bjava'> 3758 * <jv>builder</jv>.vars().add(<jv>value</jv>); 3759 * </p> 3760 * 3761 * <h5 class='section'>See Also:</h5><ul> 3762 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerSvlVariables">SVL Variables</a> 3763 * <li class='jm'>{@link #varResolver()} 3764 * </ul> 3765 * 3766 * @param value The values to add. 3767 * <br>Cannot contain <jk>null</jk> values. 3768 * @return This object. 3769 */ 3770 @SafeVarargs 3771 public final Builder vars(Class<? extends Var>...value) { 3772 assertArgNoNulls("value", value); 3773 varResolver.vars(value); 3774 return this; 3775 } 3776 3777 /** 3778 * Adds one or more variables to the var resolver of this class. 3779 * 3780 * <p> 3781 * Equivalent to calling: 3782 * <p class='bjava'> 3783 * <jv>builder</jv>.vars().add(<jv>value</jv>); 3784 * </p> 3785 * 3786 * <h5 class='section'>See Also:</h5><ul> 3787 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerSvlVariables">SVL Variables</a> 3788 * <li class='jm'>{@link #varResolver()} 3789 * </ul> 3790 * 3791 * @param value The values to add. 3792 * <br>Cannot contain <jk>null</jk> values. 3793 * @return This object. 3794 */ 3795 public Builder vars(Var...value) { 3796 assertArgNoNulls("value", value); 3797 varResolver.vars(value); 3798 return this; 3799 } 3800 3801 private static void runInitHooks(BeanStore beanStore, Supplier<?> resource) throws ServletException { 3802 3803 var r = resource.get(); 3804 3805 var map = CollectionUtils.<String,MethodInfo>map(); 3806 // @formatter:off 3807 ClassInfo.ofProxy(r).getAllMethodsTopDown().stream() 3808 .filter(y -> y.hasAnnotation(RestInit.class) && ! y.hasParameter(RestOpContext.Builder.class)) 3809 .forEach(y -> { 3810 var sig = y.getSignature(); 3811 if (! map.containsKey(sig)) 3812 map.put(sig, y.accessible()); 3813 } 3814 ); 3815 // @formatter:on 3816 3817 for (var m : map.values()) { 3818 if (! beanStore.hasAllParams(m)) 3819 throw servletException("Could not call @RestInit method {0}.{1}. Could not find prerequisites: {2}.", cns(m.getDeclaringClass()), m.getSignature(), beanStore.getMissingParams(m)); 3820 try { 3821 m.invoke(r, beanStore.getParams(m)); 3822 } catch (Exception e) { 3823 throw servletException(e, "Exception thrown from @RestInit method {0}.{1}.", cns(m.getDeclaringClass()), m.getSignature()); 3824 } 3825 } 3826 } 3827 3828 /** 3829 * Instantiates the bean context sub-builder. 3830 * 3831 * @param beanStore 3832 * The factory used for creating beans and retrieving injected beans. 3833 * @param resource 3834 * The REST servlet/bean instance that this context is defined against. 3835 * @return A new bean context sub-builder. 3836 */ 3837 protected BeanContext.Builder createBeanContext(BeanStore beanStore, Supplier<?> resource) { 3838 3839 // Default value. 3840 Value<BeanContext.Builder> v = Value.of(BeanContext.create()); 3841 3842 // Replace with builder from bean store. 3843 // @formatter:off 3844 beanStore 3845 .getBean(BeanContext.Builder.class) 3846 .map(BeanContext.Builder::copy) 3847 .ifPresent(x -> v.set(x)); 3848 // @formatter:on 3849 3850 // Replace with bean from bean store. 3851 beanStore.getBean(BeanContext.class).ifPresent(x -> v.get().impl(x)); 3852 3853 return v.get(); 3854 } 3855 3856 /** 3857 * Creates the bean store in this builder. 3858 * 3859 * @param resource 3860 * The REST servlet/bean instance that this context is defined against. 3861 * @return A new bean store builder. 3862 */ 3863 protected BeanStore.Builder createBeanStore(Supplier<?> resource) { 3864 3865 // Default value. 3866 // @formatter:off 3867 var v = Value.of( 3868 BeanStore 3869 .create() 3870 .parent(rootBeanStore()) 3871 .outer(resource.get()) 3872 ); 3873 // @formatter:on 3874 3875 // Apply @Rest(beanStore). 3876 rstream(AP.find(Rest.class, info(resourceClass))).map(x -> x.inner().beanStore()).filter(ClassUtils::isNotVoid).forEach(x -> v.get().type(x)); 3877 3878 // Replace with bean from: @RestInject public [static] BeanStore xxx(<args>) 3879 // @formatter:off 3880 v.get().build() 3881 .createMethodFinder(BeanStore.class) 3882 .find(Builder::isRestInjectMethod) 3883 .run(x -> v.get().impl(x)); 3884 // @formatter:on 3885 3886 return v.get(); 3887 } 3888 3889 /** 3890 * Instantiates the call logger sub-builder. 3891 * 3892 * <h5 class='section'>See Also:</h5><ul> 3893 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerLoggingAndDebugging">Logging / Debugging</a> 3894 * </ul> 3895 * 3896 * @param beanStore 3897 * The factory used for creating beans and retrieving injected beans. 3898 * @param resource 3899 * The REST servlet/bean instance that this context is defined against. 3900 * @return A new call logger sub-builder. 3901 */ 3902 protected BeanCreator<CallLogger> createCallLogger(BeanStore beanStore, Supplier<?> resource) { 3903 3904 var creator = beanStore.createBean(CallLogger.class).type(BasicCallLogger.class); 3905 3906 // Specify the bean type if its set as a default. 3907 defaultClasses().get(CallLogger.class).ifPresent(x -> creator.type(x)); 3908 3909 beanStore.getBean(CallLogger.class).ifPresent(x -> creator.impl(x)); 3910 3911 // Replace with bean from: @RestInject public [static] CallLogger xxx(<args>) 3912 beanStore.createMethodFinder(CallLogger.class).find(Builder::isRestInjectMethod).run(x -> creator.impl(x)); 3913 3914 return creator; 3915 } 3916 3917 /** 3918 * Creates the config for this builder. 3919 * 3920 * <h5 class='section'>See Also:</h5><ul> 3921 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/ConfigurationFiles">Configuration Files</a> 3922 * <li class='jm'>{@link #config()} 3923 * </ul> 3924 * 3925 * @param beanStore 3926 * The factory used for creating beans and retrieving injected beans. 3927 * @param resource 3928 * The REST servlet/bean instance that this context is defined against. 3929 * @param resourceClass 3930 * The REST servlet/bean type that this context is defined against. 3931 * @return A new config. 3932 */ 3933 protected Config createConfig(BeanStore beanStore, Supplier<?> resource, Class<?> resourceClass) { 3934 3935 var v = Value.<Config>empty(); 3936 3937 // Find our config file. It's the last non-empty @RestResource(config). 3938 var vr = beanStore.getBean(VarResolver.class).orElseThrow(() -> new IllegalArgumentException("VarResolver not found.")); 3939 var cfv = Value.<String>empty(); 3940 rstream(AP.find(Rest.class, info(resourceClass))).map(x -> x.inner().config()).filter(Utils::ne).forEach(x -> cfv.set(vr.resolve(x))); 3941 var cf = cfv.orElse(""); 3942 3943 // If not specified or value is set to SYSTEM_DEFAULT, use system default config. 3944 if (v.isEmpty() && "SYSTEM_DEFAULT".equals(cf)) 3945 v.set(Config.getSystemDefault()); 3946 3947 // Otherwise build one. 3948 if (v.isEmpty()) { 3949 Config.Builder cb = Config.create().varResolver(vr); 3950 if (! cf.isEmpty()) 3951 cb.name(cf); 3952 v.set(cb.build()); 3953 } 3954 3955 // Replace with bean from bean store. 3956 beanStore.getBean(Config.class).ifPresent(x -> v.set(x)); 3957 3958 // Replace with bean from: @RestInject public [static] Config xxx(<args>) 3959 beanStore.createMethodFinder(Config.class).addBean(Config.class, v.get()).find(Builder::isRestInjectMethod).run(x -> v.set(x)); 3960 3961 return v.get(); 3962 } 3963 3964 /** 3965 * Instantiates the debug enablement bean creator. 3966 * 3967 * @param beanStore 3968 * The factory used for creating beans and retrieving injected beans. 3969 * @param resource 3970 * The REST servlet/bean instance that this context is defined against. 3971 * @return A new debug enablement bean creator. 3972 */ 3973 protected BeanCreator<DebugEnablement> createDebugEnablement(BeanStore beanStore, Supplier<?> resource) { 3974 3975 var creator = beanStore.createBean(DebugEnablement.class).type(BasicDebugEnablement.class); 3976 3977 // Specify the bean type if its set as a default. 3978 defaultClasses().get(DebugEnablement.class).ifPresent(x -> creator.type(x)); 3979 3980 beanStore.getBean(DebugEnablement.class).ifPresent(x -> creator.impl(x)); 3981 3982 // Replace with bean from: @RestInject public [static] DebugEnablement xxx(<args>) 3983 beanStore.createMethodFinder(DebugEnablement.class).find(Builder::isRestInjectMethod).run(x -> creator.impl(x)); 3984 3985 return creator; 3986 } 3987 3988 /** 3989 * Instantiates the default request attributes sub-builder. 3990 * 3991 * @param beanStore 3992 * The factory used for creating beans and retrieving injected beans. 3993 * @param resource 3994 * The REST servlet/bean instance that this context is defined against. 3995 * @return A new default request attributes sub-builder. 3996 */ 3997 protected NamedAttributeMap createDefaultRequestAttributes(BeanStore beanStore, Supplier<?> resource) { 3998 3999 // Default value. 4000 var v = Value.of(NamedAttributeMap.create()); 4001 4002 beanStore.getBean(NamedAttributeMap.class, "defaultRequestAttributes").ifPresent(x -> v.set(x)); 4003 4004 // Replace with bean from: @RestInject(name="defaultRequestAttributes") public [static] NamedAttributeMap xxx(<args>) 4005 beanStore.createMethodFinder(NamedAttributeMap.class).addBean(NamedAttributeMap.class, v.get()).find(x -> isRestInjectMethod(x, "defaultRequestAttributes")).run(x -> v.set(x)); 4006 4007 return v.get(); 4008 } 4009 4010 /** 4011 * Instantiates the default request headers sub-builder. 4012 * 4013 * @param beanStore 4014 * The factory used for creating beans and retrieving injected beans. 4015 * @param resource 4016 * The REST servlet/bean instance that this context is defined against. 4017 * @return A new default request headers sub-builder. 4018 */ 4019 protected HeaderList createDefaultRequestHeaders(BeanStore beanStore, Supplier<?> resource) { 4020 4021 // Default value. 4022 var v = Value.of(HeaderList.create()); 4023 4024 // Replace with bean from bean store. 4025 beanStore.getBean(HeaderList.class, "defaultRequestHeaders").ifPresent(x -> v.set(x)); 4026 4027 // Replace with bean from: @RestInject(name="defaultRequestHeaders") public [static] HeaderList xxx(<args>) 4028 beanStore.createMethodFinder(HeaderList.class).addBean(HeaderList.class, v.get()).find(x -> isRestInjectMethod(x, "defaultRequestHeaders")).run(x -> v.set(x)); 4029 4030 return v.get(); 4031 } 4032 4033 /** 4034 * Instantiates the default response headers sub-builder. 4035 * 4036 * @param beanStore 4037 * The factory used for creating beans and retrieving injected beans. 4038 * @param resource 4039 * The REST servlet/bean instance that this context is defined against. 4040 * @return A new default response headers sub-builder. 4041 */ 4042 protected HeaderList createDefaultResponseHeaders(BeanStore beanStore, Supplier<?> resource) { 4043 4044 // Default value. 4045 var v = Value.of(HeaderList.create()); 4046 4047 // Replace with bean from bean store. 4048 beanStore.getBean(HeaderList.class, "defaultResponseHeaders").ifPresent(x -> v.set(x)); 4049 4050 // Replace with bean from: @RestInject(name="defaultResponseHeaders") public [static] HeaderList xxx(<args>) 4051 beanStore.createMethodFinder(HeaderList.class).addBean(HeaderList.class, v.get()).find(x -> isRestInjectMethod(x, "defaultResponseHeaders")).run(x -> v.set(x)); 4052 4053 return v.get(); 4054 } 4055 4056 /** 4057 * Instantiates the destroy method list. 4058 * 4059 * @param beanStore 4060 * The factory used for creating beans and retrieving injected beans. 4061 * @param resource 4062 * The REST servlet/bean instance that this context is defined against. 4063 * @return A new destroy method list. 4064 */ 4065 protected MethodList createDestroyMethods(BeanStore beanStore, Supplier<?> resource) { 4066 4067 // Default value. 4068 var v = Value.of(MethodList.of(getAnnotatedMethods(resource, RestDestroy.class).collect(Collectors.toList()))); 4069 4070 // Replace with bean from: @RestInject(name="destroyMethods") public [static] MethodList xxx(<args>) 4071 beanStore.createMethodFinder(MethodList.class).addBean(MethodList.class, v.get()).find(x -> isRestInjectMethod(x, "destroyMethods")).run(x -> v.set(x)); 4072 4073 return v.get(); 4074 } 4075 4076 /** 4077 * Instantiates the encoder group sub-builder. 4078 * 4079 * <h5 class='section'>See Also:</h5><ul> 4080 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerEncoders">Encoders</a> 4081 * <li class='jm'>{@link #encoders()} 4082 * </ul> 4083 * 4084 * @param resource 4085 * The REST servlet/bean instance that this context is defined against. 4086 * @param beanStore 4087 * The factory used for creating beans and retrieving injected beans. 4088 * <br>Created by {@link RestContext.Builder#beanStore()}. 4089 * @return A new encoder group sub-builder. 4090 */ 4091 protected EncoderSet.Builder createEncoders(BeanStore beanStore, Supplier<?> resource) { 4092 4093 // Default value. 4094 Value<EncoderSet.Builder> v = Value.of(EncoderSet.create(beanStore).add(IdentityEncoder.INSTANCE)); 4095 4096 // Specify the implementation class if its set as a default. 4097 defaultClasses().get(EncoderSet.class).ifPresent(x -> v.get().type(x)); 4098 4099 // Replace with bean from bean store. 4100 beanStore.getBean(EncoderSet.class).ifPresent(x -> v.get().impl(x)); 4101 4102 // Replace with bean from: @RestInject public [static] EncoderSet xxx(<args>) 4103 beanStore.createMethodFinder(EncoderSet.class).addBean(EncoderSet.Builder.class, v.get()).find(Builder::isRestInjectMethod).run(x -> v.get().impl(x)); 4104 4105 return v.get(); 4106 } 4107 4108 /** 4109 * Instantiates the end call method list. 4110 * 4111 * @param beanStore 4112 * The factory used for creating beans and retrieving injected beans. 4113 * @param resource 4114 * The REST servlet/bean instance that this context is defined against. 4115 * @return A new end call method list. 4116 */ 4117 protected MethodList createEndCallMethods(BeanStore beanStore, Supplier<?> resource) { 4118 4119 // Default value. 4120 Value<MethodList> v = Value.of(MethodList.of(getAnnotatedMethods(resource, RestEndCall.class).collect(Collectors.toList()))); 4121 4122 // Replace with bean from: @RestInject(name="endCallMethods") public [static] MethodList xxx(<args>) 4123 beanStore.createMethodFinder(MethodList.class).addBean(MethodList.class, v.get()).find(x -> isRestInjectMethod(x, "endCallMethods")).run(x -> v.set(x)); 4124 4125 return v.get(); 4126 } 4127 4128 /** 4129 * Instantiates the JSON schema generator sub-builder. 4130 * 4131 * <h5 class='section'>See Also:</h5><ul> 4132 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanSwagger2">juneau-bean-swagger-v2</a> 4133 * </ul> 4134 * 4135 * @param beanStore 4136 * The factory used for creating beans and retrieving injected beans. 4137 * @param resource 4138 * The REST servlet/bean instance that this context is defined against. 4139 * @return A new JSON schema generator sub-builder. 4140 */ 4141 protected JsonSchemaGenerator.Builder createJsonSchemaGenerator(BeanStore beanStore, Supplier<?> resource) { 4142 4143 // Default value. 4144 var v = Value.of(JsonSchemaGenerator.create()); 4145 4146 // Replace with builder from bean store. 4147 beanStore.getBean(JsonSchemaGenerator.Builder.class).map(JsonSchemaGenerator.Builder::copy).ifPresent(x -> v.set(x)); 4148 4149 // Replace with bean from bean store. 4150 beanStore.getBean(JsonSchemaGenerator.class).ifPresent(x -> v.get().impl(x)); 4151 4152 // Replace with bean from: @RestInject public [static] JsonSchemaGenerator xxx(<args>) 4153 beanStore.createMethodFinder(JsonSchemaGenerator.class).addBean(JsonSchemaGenerator.Builder.class, v.get()).find(Builder::isRestInjectMethod).run(x -> v.get().impl(x)); 4154 4155 return v.get(); 4156 } 4157 4158 /** 4159 * Instantiates the logger for this resource. 4160 * 4161 * <h5 class='section'>See Also:</h5><ul> 4162 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerLoggingAndDebugging">Logging / Debugging</a> 4163 * <li class='jm'>{@link #logger()} 4164 * </ul> 4165 * 4166 * @param beanStore 4167 * The factory used for creating beans and retrieving injected beans. 4168 * @param resource 4169 * The REST servlet/bean instance that this context is defined against. 4170 * @param resourceClass 4171 * The REST servlet/bean class that this context is defined against. 4172 * @return A new logger. 4173 */ 4174 protected Logger createLogger(BeanStore beanStore, Supplier<?> resource, Class<?> resourceClass) { 4175 4176 // Default value. 4177 Value<Logger> v = Value.of(Logger.getLogger(cn(resourceClass))); 4178 4179 // Replace with bean from bean store. 4180 beanStore.getBean(Logger.class).ifPresent(x -> v.set(x)); 4181 4182 // Replace with bean from: @RestInject public [static] Logger xxx(<args>) 4183 beanStore.createMethodFinder(Logger.class).addBean(Logger.class, v.get()).find(Builder::isRestInjectMethod).run(x -> v.set(x)); 4184 4185 return v.get(); 4186 } 4187 4188 /** 4189 * Instantiates the messages sub-builder. 4190 * 4191 * <h5 class='section'>See Also:</h5><ul> 4192 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/LocalizedMessages">Localized Messages</a> 4193 * </ul> 4194 * 4195 * @param beanStore 4196 * The factory used for creating beans and retrieving injected beans. 4197 * @param resource 4198 * The REST servlet/bean instance that this context is defined against. 4199 * @return A new messages sub-builder. 4200 */ 4201 protected Messages.Builder createMessages(BeanStore beanStore, Supplier<?> resource) { 4202 4203 // Default value. 4204 Value<Messages.Builder> v = Value.of(Messages.create(resourceClass)); 4205 4206 // Replace with bean from bean store. 4207 beanStore.getBean(Messages.class).ifPresent(x -> v.get().impl(x)); 4208 4209 // Replace with bean from: @RestInject public [static] Messages xxx(<args>) 4210 beanStore.createMethodFinder(Messages.class).addBean(Messages.Builder.class, v.get()).find(Builder::isRestInjectMethod).run(x -> v.get().impl(x)); 4211 4212 return v.get(); 4213 } 4214 4215 /** 4216 * Instantiates the method execution statistics store sub-builder. 4217 * 4218 * <h5 class='section'>See Also:</h5><ul> 4219 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/ExecutionStatistics">REST method execution statistics</a> 4220 * </ul> 4221 * 4222 * @param beanStore 4223 * The factory used for creating beans and retrieving injected beans. 4224 * @param resource 4225 * The REST servlet/bean instance that this context is defined against. 4226 * @return A new method execution statistics store sub-builder. 4227 */ 4228 protected MethodExecStore.Builder createMethodExecStore(BeanStore beanStore, Supplier<?> resource) { 4229 4230 // Default value. 4231 Value<MethodExecStore.Builder> v = Value.of(MethodExecStore.create(beanStore)); 4232 4233 // Specify the implementation class if its set as a default. 4234 defaultClasses().get(MethodExecStore.class).ifPresent(x -> v.get().type(x)); 4235 4236 // Replace with bean from bean store. 4237 beanStore.getBean(MethodExecStore.class).ifPresent(x -> v.get().impl(x)); 4238 4239 // Replace with bean from: @RestInject public [static] MethodExecStore xxx(<args>) 4240 beanStore.createMethodFinder(MethodExecStore.class).addBean(MethodExecStore.Builder.class, v.get()).find(Builder::isRestInjectMethod).run(x -> v.get().impl(x)); 4241 4242 return v.get(); 4243 } 4244 4245 /** 4246 * Instantiates the parser group sub-builder. 4247 * 4248 * <h5 class='section'>See Also:</h5><ul> 4249 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Marshalling">Marshalling</a> 4250 * </ul> 4251 * 4252 * @param resource 4253 * The REST servlet/bean instance that this context is defined against. 4254 * @param beanStore 4255 * The factory used for creating beans and retrieving injected beans. 4256 * <br>Created by {@link RestContext.Builder#beanStore()}. 4257 * @return A new parser group sub-builder. 4258 */ 4259 protected ParserSet.Builder createParsers(BeanStore beanStore, Supplier<?> resource) { 4260 4261 // Default value. 4262 Value<ParserSet.Builder> v = Value.of(ParserSet.create(beanStore)); 4263 4264 // Specify the implementation class if its set as a default. 4265 defaultClasses().get(ParserSet.class).ifPresent(x -> v.get().type(x)); 4266 4267 // Replace with bean from bean store. 4268 beanStore.getBean(ParserSet.class).ifPresent(x -> v.get().impl(x)); 4269 4270 // Replace with bean from: @RestInject public [static] ParserSet xxx(<args>) 4271 beanStore.createMethodFinder(ParserSet.class).addBean(ParserSet.Builder.class, v.get()).find(Builder::isRestInjectMethod).run(x -> v.get().impl(x)); 4272 4273 return v.get(); 4274 } 4275 4276 /** 4277 * Instantiates the part parser sub-builder. 4278 * 4279 * <h5 class='section'>See Also:</h5><ul> 4280 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/HttpParts">HTTP Parts</a> 4281 * </ul> 4282 * 4283 * @param beanStore 4284 * The factory used for creating beans and retrieving injected beans. 4285 * @param resource 4286 * The REST servlet/bean instance that this context is defined against. 4287 * @return A new part parser sub-builder. 4288 */ 4289 protected HttpPartParser.Creator createPartParser(BeanStore beanStore, Supplier<?> resource) { 4290 4291 // Default value. 4292 Value<HttpPartParser.Creator> v = Value.of(HttpPartParser.creator().type(OpenApiParser.class)); 4293 4294 // Replace with builder from bean store. 4295 beanStore.getBean(HttpPartParser.Creator.class).map(HttpPartParser.Creator::copy).ifPresent(x -> v.set(x)); 4296 4297 // Replace with bean from bean store. 4298 beanStore.getBean(HttpPartParser.class).ifPresent(x -> v.get().impl(x)); 4299 4300 // Replace with this bean. 4301 resourceAs(HttpPartParser.class).ifPresent(x -> v.get().impl(x)); 4302 4303 // Specify the bean type if its set as a default. 4304 defaultClasses().get(HttpPartParser.class).ifPresent(x -> v.get().type(x)); 4305 4306 // Replace with bean from: @RestInject public [static] HttpPartParser xxx(<args>) 4307 beanStore.createMethodFinder(HttpPartParser.class).addBean(HttpPartParser.Creator.class, v.get()).find(Builder::isRestInjectMethod).run(x -> v.get().impl(x)); 4308 4309 return v.get(); 4310 } 4311 4312 /** 4313 * Instantiates the part serializer sub-builder. 4314 * 4315 * <h5 class='section'>See Also:</h5><ul> 4316 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/HttpParts">HTTP Parts</a> 4317 * </ul> 4318 * 4319 * @param beanStore 4320 * The factory used for creating beans and retrieving injected beans. 4321 * @param resource 4322 * The REST servlet/bean instance that this context is defined against. 4323 * @return A new part serializer sub-builder. 4324 */ 4325 protected HttpPartSerializer.Creator createPartSerializer(BeanStore beanStore, Supplier<?> resource) { 4326 4327 // Default value. 4328 Value<HttpPartSerializer.Creator> v = Value.of(HttpPartSerializer.creator().type(OpenApiSerializer.class)); 4329 4330 // Replace with builder from bean store. 4331 beanStore.getBean(HttpPartSerializer.Creator.class).map(Creator::copy).ifPresent(x -> v.set(x)); 4332 4333 // Replace with bean from bean store. 4334 beanStore.getBean(HttpPartSerializer.class).ifPresent(x -> v.get().impl(x)); 4335 4336 // Replace with this bean. 4337 resourceAs(HttpPartSerializer.class).ifPresent(x -> v.get().impl(x)); 4338 4339 // Specify the bean type if its set as a default. 4340 defaultClasses().get(HttpPartSerializer.class).ifPresent(x -> v.get().type(x)); 4341 4342 // Replace with bean from: @RestInject public [static] HttpPartSerializer xxx(<args>) 4343 beanStore.createMethodFinder(HttpPartSerializer.class).addBean(HttpPartSerializer.Creator.class, v.get()).find(Builder::isRestInjectMethod).run(x -> v.get().impl(x)); 4344 4345 return v.get(); 4346 } 4347 4348 /** 4349 * Instantiates the post-call method list. 4350 * 4351 * @param beanStore 4352 * The factory used for creating beans and retrieving injected beans. 4353 * @param resource 4354 * The REST servlet/bean instance that this context is defined against. 4355 * @return A new post-call method list. 4356 */ 4357 protected MethodList createPostCallMethods(BeanStore beanStore, Supplier<?> resource) { 4358 4359 // Default value. 4360 Value<MethodList> v = Value.of(MethodList.of(getAnnotatedMethods(resource, RestPostCall.class).collect(Collectors.toList()))); 4361 4362 // Replace with bean from: @RestInject(name="postCallMethods") public [static] MethodList xxx(<args>) 4363 beanStore.createMethodFinder(MethodList.class).addBean(MethodList.class, v.get()).find(x -> isRestInjectMethod(x, "postCallMethods")).run(x -> v.set(x)); 4364 4365 return v.get(); 4366 } 4367 4368 /** 4369 * Instantiates the post-init-child-first method list. 4370 * 4371 * @param beanStore 4372 * The factory used for creating beans and retrieving injected beans. 4373 * @param resource 4374 * The REST servlet/bean instance that this context is defined against. 4375 * @return A new post-init-child-first method list. 4376 */ 4377 protected MethodList createPostInitChildFirstMethods(BeanStore beanStore, Supplier<?> resource) { 4378 4379 // Default value. 4380 var ap = AP; 4381 Value<MethodList> v = Value.of(MethodList.of(getAnnotatedMethods(resource, RestPostInit.class) 4382 .filter(m -> { 4383 var mi = MethodInfo.of(m); 4384 return rstream(ap.find(RestPostInit.class, mi)) 4385 .map(AnnotationInfo::inner) 4386 .anyMatch(RestPostInit::childFirst); 4387 }) 4388 .collect(Collectors.toList()))); 4389 4390 // Replace with bean from: @RestInject(name="postInitChildFirstMethods") public [static] MethodList xxx(<args>) 4391 beanStore.createMethodFinder(MethodList.class).addBean(MethodList.class, v.get()).find(x -> isRestInjectMethod(x, "postInitChildFirstMethods")).run(x -> v.set(x)); 4392 4393 return v.get(); 4394 } 4395 4396 /** 4397 * Instantiates the post-init method list. 4398 * 4399 * @param beanStore 4400 * The factory used for creating beans and retrieving injected beans. 4401 * @param resource 4402 * The REST servlet/bean instance that this context is defined against. 4403 * @return A new post-init method list. 4404 */ 4405 protected MethodList createPostInitMethods(BeanStore beanStore, Supplier<?> resource) { 4406 4407 // Default value. 4408 var ap = AP; 4409 Value<MethodList> v = Value.of(MethodList.of(getAnnotatedMethods(resource, RestPostInit.class) 4410 .filter(m -> { 4411 var mi = MethodInfo.of(m); 4412 return rstream(ap.find(RestPostInit.class, mi)) 4413 .map(AnnotationInfo::inner) 4414 .anyMatch(x -> ! x.childFirst()); 4415 }) 4416 .collect(Collectors.toList()))); 4417 4418 // Replace with bean from: @RestInject(name="postInitMethods") public [static] MethodList xxx(<args>) 4419 beanStore.createMethodFinder(MethodList.class).addBean(MethodList.class, v.get()).find(x -> isRestInjectMethod(x, "postInitMethods")).run(x -> v.set(x)); 4420 4421 return v.get(); 4422 } 4423 4424 /** 4425 * Instantiates the pre-call method list. 4426 * 4427 * @param beanStore 4428 * The factory used for creating beans and retrieving injected beans. 4429 * @param resource 4430 * The REST servlet/bean instance that this context is defined against. 4431 * @return A new pre-call method list. 4432 */ 4433 protected MethodList createPreCallMethods(BeanStore beanStore, Supplier<?> resource) { 4434 4435 // Default value. 4436 Value<MethodList> v = Value.of(MethodList.of(getAnnotatedMethods(resource, RestPreCall.class).collect(Collectors.toList()))); 4437 4438 // Replace with bean from: @RestInject(name="preCallMethods") public [static] MethodList xxx(<args>) 4439 beanStore.createMethodFinder(MethodList.class).addBean(MethodList.class, v.get()).find(x -> isRestInjectMethod(x, "preCallMethods")).run(x -> v.set(x)); 4440 4441 return v.get(); 4442 } 4443 4444 /** 4445 * Instantiates the response processor list sub-builder. 4446 * 4447 * <h5 class='section'>See Also:</h5><ul> 4448 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/ResponseProcessors">Response Processors</a> 4449 * </ul> 4450 * 4451 * @param beanStore 4452 * The factory used for creating beans and retrieving injected beans. 4453 * @param resource 4454 * The REST servlet/bean instance that this context is defined against. 4455 * @return A new response processor list sub-builder. 4456 */ 4457 protected ResponseProcessorList.Builder createResponseProcessors(BeanStore beanStore, Supplier<?> resource) { 4458 4459 // Default value. 4460 // @formatter:off 4461 Value<ResponseProcessorList.Builder> v = Value.of( 4462 ResponseProcessorList 4463 .create(beanStore) 4464 .add( 4465 ReaderProcessor.class, 4466 InputStreamProcessor.class, 4467 ThrowableProcessor.class, 4468 HttpResponseProcessor.class, 4469 HttpResourceProcessor.class, 4470 HttpEntityProcessor.class, 4471 ResponseBeanProcessor.class, 4472 PlainTextPojoProcessor.class, 4473 SerializedPojoProcessor.class 4474 ) 4475 ); 4476 // @formatter:on 4477 4478 // Replace with bean from bean store. 4479 beanStore.getBean(ResponseProcessorList.class).ifPresent(x -> v.get().impl(x)); 4480 4481 // Replace with bean from: @RestInject public [static] ResponseProcessorList xxx(<args>) 4482 beanStore.createMethodFinder(ResponseProcessorList.class).addBean(ResponseProcessorList.Builder.class, v.get()).find(Builder::isRestInjectMethod).run(x -> v.get().impl(x)); 4483 4484 return v.get(); 4485 } 4486 4487 /** 4488 * Instantiates the REST children list. 4489 * 4490 * @param restContext The rest context. 4491 * @param beanStore 4492 * The factory used for creating beans and retrieving injected beans. 4493 * @param resource 4494 * The REST servlet/bean instance that this context is defined against. 4495 * @return A new REST children list. 4496 * @throws Exception If a problem occurred instantiating one of the child rest contexts. 4497 */ 4498 protected RestChildren.Builder createRestChildren(BeanStore beanStore, Supplier<?> resource, RestContext restContext) throws Exception { 4499 4500 // Default value. 4501 Value<RestChildren.Builder> v = Value.of(RestChildren.create(beanStore).type(childrenClass)); 4502 4503 // Initialize our child resources. 4504 for (var o : children) { 4505 var path = (String)null; 4506 Supplier<?> so; 4507 4508 if (o instanceof RestChild o2) { 4509 path = o2.path; 4510 var o3 = o2.resource; 4511 so = () -> o3; 4512 } 4513 4514 var cb = (Builder)null; 4515 4516 if (o instanceof Class<?> oc) { 4517 // Don't allow specifying yourself as a child. Causes an infinite loop. 4518 if (oc == resourceClass) 4519 continue; 4520 cb = RestContext.create(oc, restContext, inner); 4521 if (beanStore.getBean(oc).isPresent()) { 4522 so = () -> beanStore.getBean(oc).get(); // If we resolved via injection, always get it this way. 4523 } else { 4524 Object o2 = beanStore.createBean(oc).builder(RestContext.Builder.class, cb).run(); 4525 so = () -> o2; 4526 } 4527 } else { 4528 cb = RestContext.create(o.getClass(), restContext, inner); 4529 so = () -> o; 4530 } 4531 4532 if (nn(path)) 4533 cb.path(path); 4534 4535 var cc = cb.init(so).build(); 4536 4537 var mi = ClassInfo.of(so.get()).getMethod(x -> x.hasName("setContext") && x.hasParameterTypes(RestContext.class)).orElse(null); 4538 if (nn(mi)) 4539 mi.accessible().invoke(so.get(), cc); 4540 4541 v.get().add(cc); 4542 } 4543 4544 // Replace with bean from: @RestInject public [static] RestChildren xxx(<args>) 4545 beanStore.createMethodFinder(RestChildren.class).addBean(RestChildren.Builder.class, v.get()).find(Builder::isRestInjectMethod).run(x -> v.get().impl(x)); 4546 4547 return v.get(); 4548 } 4549 4550 /** 4551 * Instantiates the REST operation args sub-builder. 4552 * 4553 * <p> 4554 * Instantiates based on the following logic: 4555 * <ul> 4556 * <li>Looks for REST op args set via any of the following: 4557 * <ul> 4558 * <li>{@link RestContext.Builder#restOpArgs(Class...)}/{@link RestContext.Builder#restOpArgs(Class...)} 4559 * <li>{@link Rest#restOpArgs()}. 4560 * </ul> 4561 * <li>Looks for a static or non-static <c>createRestParams()</c> method that returns <c>{@link Class}[]</c>. 4562 * <li>Resolves it via the bean store registered in this context. 4563 * <li>Instantiates a default set of parameters. 4564 * </ul> 4565 * 4566 * @param beanStore 4567 * The factory used for creating beans and retrieving injected beans. 4568 * @param resource 4569 * The REST servlet/bean instance that this context is defined against. 4570 * @return A new REST operation args sub-builder. 4571 */ 4572 protected RestOpArgList.Builder createRestOpArgs(BeanStore beanStore, Supplier<?> resource) { 4573 4574 // Default value. 4575 Value<RestOpArgList.Builder> v = Value.of(RestOpArgList.create(beanStore).add(AttributeArg.class, ContentArg.class, FormDataArg.class, HasFormDataArg.class, HasQueryArg.class, 4576 HeaderArg.class, HttpServletRequestArgs.class, HttpServletResponseArgs.class, HttpSessionArgs.class, InputStreamParserArg.class, MethodArg.class, ParserArg.class, PathArg.class, 4577 PathRemainderArg.class, QueryArg.class, ReaderParserArg.class, RequestBeanArg.class, ResponseBeanArg.class, ResponseHeaderArg.class, ResponseCodeArg.class, RestContextArgs.class, 4578 RestSessionArgs.class, RestOpContextArgs.class, RestOpSessionArgs.class, RestRequestArgs.class, RestResponseArgs.class, DefaultArg.class)); 4579 4580 // Replace with bean from bean store. 4581 beanStore.getBean(RestOpArgList.class).ifPresent(x -> v.get().impl(x)); 4582 4583 // Replace with bean from: @RestInject public [static] RestOpArgList xxx(<args>) 4584 beanStore.createMethodFinder(RestOpArgList.class).addBean(RestOpArgList.Builder.class, v.get()).find(Builder::isRestInjectMethod).run(x -> v.get().impl(x)); 4585 4586 return v.get(); 4587 } 4588 4589 /** 4590 * Instantiates the REST operations list. 4591 * 4592 * <p> 4593 * The set of {@link RestOpContext} objects that represent the methods on this resource. 4594 * 4595 * @param restContext The rest context. 4596 * @param beanStore 4597 * The factory used for creating beans and retrieving injected beans. 4598 * @param resource 4599 * The REST servlet/bean instance that this context is defined against. 4600 * @return A new REST operations list. 4601 * @throws ServletException If a problem occurred instantiating one of the child rest contexts. 4602 */ 4603 protected RestOperations.Builder createRestOperations(BeanStore beanStore, Supplier<?> resource, RestContext restContext) throws ServletException { 4604 4605 // Default value. 4606 Value<RestOperations.Builder> v = Value.of(RestOperations.create(beanStore)); 4607 var ap = restContext.getBeanContext().getAnnotationProvider(); 4608 4609 var rci = ClassInfo.of(resource.get()); 4610 4611 var initMap = CollectionUtils.<String,MethodInfo>map(); 4612 // @formatter:off 4613 ClassInfo.ofProxy(resource.get()).getAllMethodsTopDown().stream() 4614 .filter(y -> y.hasAnnotation(RestInit.class) && y.hasParameter(RestOpContext.Builder.class)) 4615 .forEach(y -> { 4616 var sig = y.getSignature(); 4617 if (! initMap.containsKey(sig)) 4618 initMap.put(sig, y.accessible()); 4619 }); 4620 // @formatter:on 4621 4622 for (var mi : rci.getPublicMethods()) { 4623 var al = rstream(ap.find(mi)).filter(REST_OP_GROUP).collect(Collectors.toList()); 4624 4625 // Also include methods on @Rest-annotated interfaces. 4626 if (al.isEmpty()) { 4627 Predicate<MethodInfo> isRestAnnotatedInterface = x -> x.getDeclaringClass().isInterface() 4628 && nn(x.getDeclaringClass().getAnnotations(Rest.class).findFirst().map(AnnotationInfo::inner).orElse(null)); 4629 mi.getMatchingMethods().stream().filter(isRestAnnotatedInterface).forEach(x -> al.add(AnnotationInfo.of(x, RestOpAnnotation.DEFAULT))); 4630 } 4631 4632 if (al.size() > 0) { 4633 try { 4634 if (mi.isNotPublic()) 4635 throw servletException("@RestOp method {0}.{1} must be defined as public.", rci.inner().getName(), mi.getSimpleName()); 4636 4637 RestOpContext.Builder rocb = RestOpContext.create(mi.inner(), restContext).beanStore(beanStore).type(opContextClass); 4638 4639 beanStore = BeanStore.of(beanStore, resource.get()).addBean(RestOpContext.Builder.class, rocb); 4640 for (var m : initMap.values()) { 4641 if (! beanStore.hasAllParams(m)) { 4642 throw servletException("Could not call @RestInit method {0}.{1}. Could not find prerequisites: {2}.", cns(m.getDeclaringClass()), m.getSignature(), 4643 beanStore.getMissingParams(m)); 4644 } 4645 try { 4646 m.invoke(resource.get(), beanStore.getParams(m)); 4647 } catch (Exception e) { 4648 throw servletException(e, "Exception thrown from @RestInit method {0}.{1}.", cns(m.getDeclaringClass()), m.getSignature()); 4649 } 4650 } 4651 4652 var roc = rocb.build(); 4653 4654 String httpMethod = roc.getHttpMethod(); 4655 4656 // RRPC is a special case where a method returns an interface that we 4657 // can perform REST calls against. 4658 // We override the CallMethod.invoke() method to insert our logic. 4659 if ("RRPC".equals(httpMethod)) { 4660 4661 // @formatter:off 4662 RestOpContext roc2 = RestOpContext 4663 .create(mi.inner(), restContext) 4664 .dotAll() 4665 .beanStore(restContext.getRootBeanStore()) 4666 .type(RrpcRestOpContext.class) 4667 .build(); 4668 v.get() 4669 .add("GET", roc2) 4670 .add("POST", roc2); 4671 // @formatter:on 4672 4673 } else { 4674 v.get().add(roc); 4675 } 4676 } catch (Throwable e) { 4677 throw servletException(e, "Problem occurred trying to initialize methods on class {0}", rci.inner().getName()); 4678 } 4679 } 4680 } 4681 4682 // Replace with bean from: @RestInject public [static] RestOperations xxx(<args>) 4683 beanStore.createMethodFinder(RestOperations.class).addBean(RestOperations.Builder.class, v.get()).find(Builder::isRestInjectMethod).run(x -> v.get().impl(x)); 4684 4685 return v.get(); 4686 } 4687 4688 /** 4689 * Instantiates the serializer group sub-builder. 4690 * 4691 * <h5 class='section'>See Also:</h5><ul> 4692 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Marshalling">Marshalling</a> 4693 * </ul> 4694 * 4695 * @param resource 4696 * The REST servlet/bean instance that this context is defined against. 4697 * @param beanStore 4698 * The factory used for creating beans and retrieving injected beans. 4699 * <br>Created by {@link RestContext.Builder#beanStore()}. 4700 * @return A new serializer group sub-builder. 4701 */ 4702 protected SerializerSet.Builder createSerializers(BeanStore beanStore, Supplier<?> resource) { 4703 4704 // Default value. 4705 Value<SerializerSet.Builder> v = Value.of(SerializerSet.create(beanStore)); 4706 4707 // Specify the implementation class if its set as a default. 4708 defaultClasses().get(SerializerSet.class).ifPresent(x -> v.get().type(x)); 4709 4710 // Replace with bean from bean store. 4711 beanStore.getBean(SerializerSet.class).ifPresent(x -> v.get().impl(x)); 4712 4713 // Replace with bean from: @RestInject public [static] SerializerSet xxx(<args>) 4714 beanStore.createMethodFinder(SerializerSet.class).addBean(SerializerSet.Builder.class, v.get()).find(Builder::isRestInjectMethod).run(x -> v.get().impl(x)); 4715 4716 return v.get(); 4717 } 4718 4719 /** 4720 * Instantiates the start call method list. 4721 * 4722 * @param beanStore 4723 * The factory used for creating beans and retrieving injected beans. 4724 * @param resource 4725 * The REST servlet/bean instance that this context is defined against. 4726 * @return A new start call method list. 4727 */ 4728 protected MethodList createStartCallMethods(BeanStore beanStore, Supplier<?> resource) { 4729 4730 // Default value. 4731 Value<MethodList> v = Value.of(MethodList.of(getAnnotatedMethods(resource, RestStartCall.class).collect(Collectors.toList()))); 4732 4733 // Replace with bean from: @RestInject(name="startCallMethods") public [static] MethodList xxx(<args>) 4734 beanStore.createMethodFinder(MethodList.class).addBean(MethodList.class, v.get()).find(x -> isRestInjectMethod(x, "startCallMethods")).run(x -> v.set(x)); 4735 4736 return v.get(); 4737 } 4738 4739 /** 4740 * Instantiates the static files bean creator. 4741 * 4742 * <h5 class='section'>See Also:</h5><ul> 4743 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/StaticFiles">Static files</a> 4744 * </ul> 4745 * 4746 * @param beanStore 4747 * The factory used for creating beans and retrieving injected beans. 4748 * @param resource 4749 * The REST servlet/bean instance that this context is defined against. 4750 * @return A new static files sub-builder. 4751 */ 4752 protected BeanCreator<StaticFiles> createStaticFiles(BeanStore beanStore, Supplier<?> resource) { 4753 4754 var creator = beanStore.createBean(StaticFiles.class).type(BasicStaticFiles.class); 4755 4756 // Specify the bean type if its set as a default. 4757 defaultClasses().get(StaticFiles.class).ifPresent(x -> creator.type(x)); 4758 4759 beanStore.getBean(StaticFiles.class).ifPresent(x -> creator.impl(x)); 4760 4761 // Replace with bean from: @RestInject public [static] StaticFiles xxx(<args>) 4762 beanStore.createMethodFinder(StaticFiles.class).find(Builder::isRestInjectMethod).run(x -> creator.impl(x)); 4763 4764 return creator; 4765 } 4766 4767 /** 4768 * Instantiates the swagger provider sub-builder. 4769 * 4770 * <p> 4771 * Instantiates based on the following logic: 4772 * <ul> 4773 * <li>Returns the resource class itself is an instance of {@link SwaggerProvider}. 4774 * <li>Looks for swagger provider set via any of the following: 4775 * <ul> 4776 * <li>{@link RestContext.Builder#swaggerProvider(Class)}/{@link RestContext.Builder#swaggerProvider(SwaggerProvider)} 4777 * <li>{@link Rest#swaggerProvider()}. 4778 * </ul> 4779 * <li>Looks for a static or non-static <c>createSwaggerProvider()</c> method that returns {@link SwaggerProvider} on the 4780 * resource class with any of the following arguments: 4781 * <ul> 4782 * <li>{@link RestContext} 4783 * <li>{@link BeanStore} 4784 * <li>Any <a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestServerSpringbootBasics">juneau-rest-server-springboot Basics</a>. 4785 * </ul> 4786 * <li>Resolves it via the bean store registered in this context. 4787 * <li>Instantiates a default {@link BasicSwaggerProvider}. 4788 * </ul> 4789 * 4790 * <h5 class='section'>See Also:</h5><ul> 4791 * <li class='jm'>{@link RestContext.Builder#swaggerProvider(Class)} 4792 * <li class='jm'>{@link RestContext.Builder#swaggerProvider(SwaggerProvider)} 4793 * </ul> 4794 * 4795 * @param beanStore 4796 * The factory used for creating beans and retrieving injected beans. 4797 * @param resource 4798 * The REST servlet/bean instance that this context is defined against. 4799 * @return A new swagger provider sub-builder. 4800 */ 4801 protected BeanCreator<SwaggerProvider> createSwaggerProvider(BeanStore beanStore, Supplier<?> resource) { 4802 4803 var creator = beanStore.createBean(SwaggerProvider.class).type(BasicSwaggerProvider.class); 4804 4805 // Specify the bean type if its set as a default. 4806 defaultClasses().get(SwaggerProvider.class).ifPresent(x -> creator.type(x)); 4807 4808 beanStore.getBean(SwaggerProvider.class).ifPresent(x -> creator.impl(x)); 4809 4810 // Replace with bean from: @RestInject public [static] SwaggerProvider xxx(<args>) 4811 beanStore.createMethodFinder(SwaggerProvider.class).find(Builder::isRestInjectMethod).run(x -> creator.impl(x)); 4812 4813 return creator; 4814 } 4815 4816 /** 4817 * Instantiates the thrown-store sub-builder. 4818 * 4819 * <h5 class='section'>See Also:</h5><ul> 4820 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/ExecutionStatistics">REST method execution statistics</a> 4821 * </ul> 4822 * 4823 * @param resource 4824 * The REST servlet/bean instance that this context is defined against. 4825 * @param parent 4826 * The parent context if the REST bean was registered via {@link Rest#children()}. 4827 * <br>Will be <jk>null</jk> if the bean is a top-level resource. 4828 * @param beanStore 4829 * The factory used for creating beans and retrieving injected beans. 4830 * <br>Created by {@link RestContext.Builder#beanStore()}. 4831 * @return A new thrown-store sub-builder. 4832 */ 4833 protected ThrownStore.Builder createThrownStore(BeanStore beanStore, Supplier<?> resource, RestContext parent) { 4834 4835 // Default value. 4836 Value<ThrownStore.Builder> v = Value.of(ThrownStore.create(beanStore).impl(parent == null ? null : parent.getThrownStore())); 4837 4838 // Specify the implementation class if its set as a default. 4839 defaultClasses().get(ThrownStore.class).ifPresent(x -> v.get().type(x)); 4840 4841 // Replace with bean from bean store. 4842 beanStore.getBean(ThrownStore.class).ifPresent(x -> v.get().impl(x)); 4843 4844 // Replace with bean from: @RestInject public [static] ThrownStore xxx(<args>) 4845 beanStore.createMethodFinder(ThrownStore.class).addBean(ThrownStore.Builder.class, v.get()).find(Builder::isRestInjectMethod).run(x -> v.get().impl(x)); 4846 4847 return v.get(); 4848 } 4849 4850 /** 4851 * Creates the variable resolver sub-builder. 4852 * 4853 * <h5 class='section'>See Also:</h5><ul> 4854 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerSvlVariables">SVL Variables</a> 4855 * </ul> 4856 * 4857 * @param beanStore 4858 * The factory used for creating beans and retrieving injected beans. 4859 * @param resource 4860 * The REST servlet/bean instance that this context is defined against. 4861 * @param resourceClass 4862 * The REST servlet/bean type that this context is defined against. 4863 * @return A new variable resolver sub-builder. 4864 */ 4865 protected VarResolver.Builder createVarResolver(BeanStore beanStore, Supplier<?> resource, Class<?> resourceClass) { 4866 4867 // Default value. 4868 // @formatter:off 4869 Value<VarResolver.Builder> v = Value.of( 4870 VarResolver 4871 .create() 4872 .defaultVars() 4873 .vars( 4874 VarList.of( 4875 ConfigVar.class, 4876 FileVar.class, 4877 LocalizationVar.class, 4878 RequestAttributeVar.class, 4879 RequestFormDataVar.class, 4880 RequestHeaderVar.class, 4881 RequestPathVar.class, 4882 RequestQueryVar.class, 4883 RequestVar.class, 4884 RequestSwaggerVar.class, 4885 SerializedRequestAttrVar.class, 4886 ServletInitParamVar.class, 4887 SwaggerVar.class, 4888 UrlVar.class, 4889 UrlEncodeVar.class, 4890 HtmlWidgetVar.class 4891 ) 4892 .addDefault() 4893 ) 4894 .bean(FileFinder.class, FileFinder.create(beanStore).cp(resourceClass,null,true).build()) 4895 ); 4896 // @formatter:on 4897 4898 // Replace with bean from bean store. 4899 beanStore.getBean(VarResolver.class).ifPresent(x -> v.get().impl(x)); 4900 4901 // Replace with bean from: @RestInject public [static] VarResolver xxx(<args>) 4902 beanStore.createMethodFinder(VarResolver.class).addBean(VarResolver.Builder.class, v.get()).find(Builder::isRestInjectMethod).run(x -> v.get().impl(x)); 4903 4904 return v.get(); 4905 } 4906 } 4907 4908 private static final Map<Class<?>,RestContext> REGISTRY = new ConcurrentHashMap<>(); 4909 4910 /** 4911 * Creates a new builder for this object. 4912 * 4913 * @param resourceClass 4914 * The class annotated with <ja>@Rest</ja>. 4915 * <br>Must not be <jk>null</jk>. 4916 * @param parentContext 4917 * The parent context if the REST bean was registered via {@link Rest#children()}. 4918 * <br>Can be <jk>null</jk> if the bean is a top-level resource. 4919 * @param servletConfig 4920 * The servlet config passed into the servlet by the servlet container. 4921 * <br>Can be <jk>null</jk> if not available. 4922 * <br>If <jk>null</jk>, then some features (such as access to servlet init params) will not be available. 4923 * 4924 * @return A new builder object. 4925 * @throws ServletException Something bad happened. 4926 */ 4927 public static Builder create(Class<?> resourceClass, RestContext parentContext, ServletConfig servletConfig) throws ServletException { 4928 return new Builder(resourceClass, parentContext, servletConfig); 4929 } 4930 4931 /** 4932 * Returns a registry of all created {@link RestContext} objects. 4933 * 4934 * @return An unmodifiable map of resource classes to {@link RestContext} objects. 4935 */ 4936 public static final Map<Class<?>,RestContext> getGlobalRegistry() { return u(REGISTRY); } 4937 4938 static ServletException servletException(String msg, Object...args) { 4939 return new ServletException(f(msg, args)); 4940 } 4941 4942 static ServletException servletException(Throwable t, String msg, Object...args) { 4943 return new ServletException(f(msg, args), t); 4944 } 4945 4946 protected final boolean allowContentParam; 4947 protected final boolean renderResponseStackTraces; 4948 protected final long maxInput; 4949 protected final AtomicBoolean initialized = new AtomicBoolean(false); 4950 protected final BasicHttpException initException; 4951 protected final BeanContext beanContext; 4952 protected final BeanStore beanStore; 4953 protected final BeanStore rootBeanStore; 4954 protected final Builder builder; 4955 protected final CallLogger callLogger; 4956 protected final Charset defaultCharset; 4957 protected final Class<?> resourceClass; 4958 protected final Class<? extends RestOpArg>[] restOpArgs; 4959 protected final ConcurrentHashMap<Locale,Swagger> swaggerCache = new ConcurrentHashMap<>(); 4960 protected final Config config; 4961 protected final DebugEnablement debugEnablement; 4962 protected final DefaultClassList defaultClasses; 4963 protected final DefaultSettingsMap defaultSettings; 4964 protected final EncoderSet encoders; 4965 protected final HeaderList defaultRequestHeaders; 4966 protected final HeaderList defaultResponseHeaders; 4967 protected final HttpPartParser partParser; 4968 protected final HttpPartSerializer partSerializer; 4969 protected final Instant startTime; 4970 protected final JsonSchemaGenerator jsonSchemaGenerator; 4971 protected final List<MediaType> consumes; 4972 protected final List<MediaType> produces; 4973 protected final Logger logger; 4974 protected final Messages messages; 4975 protected final MethodExecStore methodExecStore; 4976 protected final NamedAttributeMap defaultRequestAttributes; 4977 protected final ParserSet parsers; 4978 protected final RestChildren restChildren; 4979 protected final RestContext parentContext; 4980 protected final RestOperations restOperations; 4981 protected final ResponseProcessor[] responseProcessors; 4982 protected final SerializerSet serializers; 4983 protected final Set<String> allowedHeaderParams; 4984 protected final Set<String> allowedMethodHeaders; 4985 protected final Set<String> allowedMethodParams; 4986 protected final StaticFiles staticFiles; 4987 protected final String clientVersionHeader; 4988 protected final String fullPath; 4989 protected final String path; 4990 protected final String uriAuthority; 4991 protected final String uriContext; 4992 protected final SwaggerProvider swaggerProvider; 4993 protected final ThrownStore thrownStore; 4994 protected final ThreadLocal<RestSession> localSession = new ThreadLocal<>(); 4995 protected final UriRelativity uriRelativity; 4996 protected final UriResolution uriResolution; 4997 protected final UrlPathMatcher pathMatcher; 4998 protected final VarResolver varResolver; 4999 private final MethodInvoker[] destroyMethods; 5000 private final MethodInvoker[] endCallMethods; 5001 private final MethodInvoker[] postInitChildFirstMethods; 5002 private final MethodInvoker[] postInitMethods; 5003 private final MethodInvoker[] startCallMethods; 5004 private final MethodList postCallMethods; 5005 private final MethodList preCallMethods; 5006 private final Supplier<?> resource; 5007 5008 /** 5009 * Constructor. 5010 * 5011 * @param builder The builder containing the settings for this bean. 5012 * @throws Exception If any initialization problems were encountered. 5013 */ 5014 public RestContext(Builder builder) throws Exception { 5015 super(builder); 5016 5017 startTime = Instant.now(); 5018 5019 REGISTRY.put(builder.resourceClass, this); 5020 5021 BasicHttpException _initException = null; 5022 5023 try { 5024 this.builder = builder; 5025 5026 defaultClasses = builder.defaultClasses(); 5027 defaultSettings = builder.defaultSettings(); 5028 parentContext = builder.parentContext; 5029 resource = builder.resource; 5030 resourceClass = builder.resourceClass; 5031 rootBeanStore = builder.rootBeanStore(); 5032 5033 BeanStore bs = beanStore = builder.beanStore(); 5034 // @formatter:off 5035 beanStore 5036 .addBean(BeanStore.class, beanStore) 5037 .addBean(RestContext.class, this) 5038 .addBean(Object.class, resource.get()) 5039 .addBean(DefaultSettingsMap.class, defaultSettings) 5040 .addBean(Builder.class, builder) 5041 .addBean(AnnotationWorkList.class, builder.getApplied()); 5042 // @formatter:on 5043 5044 path = nn(builder.path) ? builder.path : ""; 5045 fullPath = (parentContext == null ? "" : (parentContext.fullPath + '/')) + path; 5046 var p = path; 5047 if (! p.endsWith("/*")) 5048 p += "/*"; 5049 pathMatcher = UrlPathMatcher.of(p); 5050 5051 allowContentParam = ! builder.disableContentParam; 5052 allowedHeaderParams = newCaseInsensitiveSet(opt(builder.allowedHeaderParams).map(x -> "NONE".equals(x) ? "" : x).orElse("")); 5053 allowedMethodHeaders = newCaseInsensitiveSet(opt(builder.allowedMethodHeaders).map(x -> "NONE".equals(x) ? "" : x).orElse("")); 5054 allowedMethodParams = newCaseInsensitiveSet(opt(builder.allowedMethodParams).map(x -> "NONE".equals(x) ? "" : x).orElse("")); 5055 clientVersionHeader = builder.clientVersionHeader; 5056 defaultCharset = builder.defaultCharset; 5057 maxInput = builder.maxInput; 5058 renderResponseStackTraces = builder.renderResponseStackTraces; 5059 uriAuthority = builder.uriAuthority; 5060 uriContext = builder.uriContext; 5061 uriRelativity = builder.uriRelativity; 5062 uriResolution = builder.uriResolution; 5063 5064 beanContext = bs.add(BeanContext.class, builder.beanContext().build()); 5065 encoders = bs.add(EncoderSet.class, builder.encoders().build()); 5066 serializers = bs.add(SerializerSet.class, builder.serializers().build()); 5067 parsers = bs.add(ParserSet.class, builder.parsers().build()); 5068 logger = bs.add(Logger.class, builder.logger()); 5069 thrownStore = bs.add(ThrownStore.class, builder.thrownStore().build()); 5070 methodExecStore = bs.add(MethodExecStore.class, builder.methodExecStore().thrownStoreOnce(thrownStore).build()); 5071 messages = bs.add(Messages.class, builder.messages().build()); 5072 varResolver = bs.add(VarResolver.class, builder.varResolver().bean(Messages.class, messages).build()); 5073 config = bs.add(Config.class, builder.config().resolving(varResolver.createSession())); 5074 responseProcessors = bs.add(ResponseProcessor[].class, builder.responseProcessors().build().toArray()); 5075 callLogger = bs.add(CallLogger.class, builder.callLogger().orElse(null)); 5076 partSerializer = bs.add(HttpPartSerializer.class, builder.partSerializer().create()); 5077 partParser = bs.add(HttpPartParser.class, builder.partParser().create()); 5078 jsonSchemaGenerator = bs.add(JsonSchemaGenerator.class, builder.jsonSchemaGenerator().build()); 5079 staticFiles = bs.add(StaticFiles.class, builder.staticFiles().orElse(null)); 5080 bs.add(FileFinder.class, staticFiles); 5081 defaultRequestHeaders = bs.add(HeaderList.class, builder.defaultRequestHeaders(), "defaultRequestHeaders"); 5082 defaultResponseHeaders = bs.add(HeaderList.class, builder.defaultResponseHeaders(), "defaultResponseHeaders"); 5083 defaultRequestAttributes = bs.add(NamedAttributeMap.class, builder.defaultRequestAttributes(), "defaultRequestAttributes"); 5084 restOpArgs = builder.restOpArgs().build().asArray(); 5085 debugEnablement = bs.add(DebugEnablement.class, builder.debugEnablement().orElse(null)); 5086 startCallMethods = builder.startCallMethods().stream().map(this::toMethodInvoker).toArray(MethodInvoker[]::new); 5087 endCallMethods = builder.endCallMethods().stream().map(this::toMethodInvoker).toArray(MethodInvoker[]::new); 5088 postInitMethods = builder.postInitMethods().stream().map(this::toMethodInvoker).toArray(MethodInvoker[]::new); 5089 postInitChildFirstMethods = builder.postInitChildFirstMethods().stream().map(this::toMethodInvoker).toArray(MethodInvoker[]::new); 5090 destroyMethods = builder.destroyMethods().stream().map(this::toMethodInvoker).toArray(MethodInvoker[]::new); 5091 preCallMethods = builder.preCallMethods(); 5092 postCallMethods = builder.postCallMethods(); 5093 restOperations = builder.restOperations(this).build(); 5094 restChildren = builder.restChildren(this).build(); 5095 swaggerProvider = bs.add(SwaggerProvider.class, builder.swaggerProvider().orElse(null)); 5096 5097 var opContexts = restOperations.getOpContexts(); 5098 5099 // @formatter:off 5100 produces = builder.produces().orElseGet( 5101 ()->{ 5102 Set<MediaType> s = opContexts.isEmpty() ? emptySet() : toSet(opContexts.get(0).getSerializers().getSupportedMediaTypes()); 5103 opContexts.forEach(x -> s.retainAll(x.getSerializers().getSupportedMediaTypes())); 5104 return u(toList(s)); 5105 } 5106 ); 5107 consumes = builder.consumes().orElseGet( 5108 ()->{ 5109 Set<MediaType> s = opContexts.isEmpty() ? emptySet() : toSet(opContexts.get(0).getParsers().getSupportedMediaTypes()); 5110 opContexts.forEach(x -> s.retainAll(x.getParsers().getSupportedMediaTypes())); 5111 return u(toList(s)); 5112 } 5113 ); 5114 // @formatter:on 5115 5116 } catch (BasicHttpException e) { 5117 _initException = e; 5118 throw e; 5119 } catch (Exception e) { 5120 _initException = new InternalServerError(e); 5121 throw e; 5122 } finally { 5123 initException = _initException; 5124 } 5125 } 5126 5127 @Override /* Overridden from Context */ 5128 public RestSession.Builder createSession() { 5129 return RestSession.create(this); 5130 } 5131 5132 /** 5133 * Called during servlet destruction to invoke all {@link RestDestroy} methods. 5134 */ 5135 public void destroy() { 5136 for (var x : destroyMethods) { 5137 try { 5138 x.invoke(beanStore, getResource()); 5139 } catch (Exception e) { 5140 getLogger().log(Level.WARNING, unwrap(e), () -> f("Error occurred invoking servlet-destroy method ''{0}''.", x.getFullName())); 5141 } 5142 } 5143 5144 restChildren.destroy(); 5145 } 5146 5147 /** 5148 * The main service method. 5149 * 5150 * <p> 5151 * Subclasses can optionally override this method if they want to tailor the behavior of requests. 5152 * 5153 * @param resource 5154 * The REST servlet or bean that this context defines. 5155 * <br>Note that this bean may not be the same bean used during initialization as it may have been replaced at runtime. 5156 * @param r1 The incoming HTTP servlet request object. 5157 * @param r2 The incoming HTTP servlet response object. 5158 * @throws ServletException General servlet exception. 5159 * @throws IOException Thrown by underlying stream. 5160 */ 5161 public void execute(Object resource, HttpServletRequest r1, HttpServletResponse r2) throws ServletException, IOException { 5162 5163 // Must be careful not to bleed thread-locals. 5164 if (nn(localSession.get())) 5165 System.err.println("WARNING: Thread-local call object was not cleaned up from previous request. " + this + ", thread=[" + Thread.currentThread().getId() + "]"); 5166 5167 RestSession.Builder sb = createSession().resource(resource).req(r1).res(r2).logger(getCallLogger()); 5168 5169 try { 5170 5171 if (nn(initException)) 5172 throw initException; 5173 5174 // If the resource path contains variables (e.g. @Rest(path="/f/{a}/{b}"), then we want to resolve 5175 // those variables and push the servletPath to include the resolved variables. The new pathInfo will be 5176 // the remainder after the new servletPath. 5177 // Only do this for the top-level resource because the logic for child resources are processed next. 5178 if (pathMatcher.hasVars() && parentContext == null) { 5179 var sp = sb.req().getServletPath(); 5180 var pi = sb.getPathInfoUndecoded(); 5181 var upi2 = UrlPath.of(pi == null ? sp : sp + pi); 5182 var uppm = pathMatcher.match(upi2); 5183 if (nn(uppm) && ! uppm.hasEmptyVars()) { 5184 sb.pathVars(uppm.getVars()); 5185 var pathInfo = opt(validatePathInfo(nullIfEmpty(urlDecode(uppm.getSuffix())))).orElse("\u0000"); 5186 var servletPath = validateServletPath(uppm.getPrefix()); 5187 sb.req(new HttpServletRequestWrapper(sb.req()) { 5188 @Override 5189 public String getPathInfo() { 5190 return pathInfo.charAt(0) == (char)0 ? null : pathInfo; 5191 } 5192 @Override 5193 public String getServletPath() { 5194 return servletPath; 5195 } 5196 }); 5197 } else { 5198 var call = sb.build(); 5199 call.debug(isDebug(call)).status(SC_NOT_FOUND).finish(); 5200 return; 5201 } 5202 } 5203 5204 // If this resource has child resources, try to recursively call them. 5205 var childMatch = restChildren.findMatch(sb); 5206 if (childMatch.isPresent()) { 5207 var uppm = childMatch.get().getPathMatch(); 5208 var rc = childMatch.get().getChildContext(); 5209 if (! uppm.hasEmptyVars()) { 5210 sb.pathVars(uppm.getVars()); 5211 var pathInfo = opt(validatePathInfo(nullIfEmpty(urlDecode(uppm.getSuffix())))).orElse("\u0000"); 5212 var servletPath = validateServletPath(sb.req().getServletPath() + uppm.getPrefix()); 5213 var childRequest = new HttpServletRequestWrapper(sb.req()) { 5214 @Override 5215 public String getPathInfo() { 5216 return pathInfo.charAt(0) == (char)0 ? null : pathInfo; 5217 } 5218 @Override 5219 public String getServletPath() { 5220 return servletPath; 5221 } 5222 }; 5223 rc.execute(rc.getResource(), childRequest, sb.res()); 5224 } else { 5225 var call = sb.build(); 5226 call.debug(isDebug(call)).status(SC_NOT_FOUND).finish(); 5227 } 5228 return; 5229 } 5230 5231 } catch (Throwable e) { 5232 handleError(sb.build(), convertThrowable(e)); 5233 } 5234 5235 var s = sb.build(); 5236 5237 try { 5238 localSession.set(s); 5239 s.debug(isDebug(s)); 5240 startCall(s); 5241 s.run(); 5242 } catch (Throwable e) { 5243 handleError(s, convertThrowable(e)); 5244 } finally { 5245 try { 5246 s.finish(); 5247 endCall(s); 5248 } finally { 5249 localSession.remove(); 5250 } 5251 } 5252 } 5253 5254 /** 5255 * Allowed header URL parameters. 5256 * 5257 * <h5 class='section'>See Also:</h5><ul> 5258 * <li class='ja'>{@link Rest#allowedHeaderParams} 5259 * <li class='jm'>{@link RestContext.Builder#allowedHeaderParams(String)} 5260 * </ul> 5261 * 5262 * @return 5263 * The header names allowed to be passed as URL parameters. 5264 * <br>The set is case-insensitive ordered and unmodifiable. 5265 */ 5266 public Set<String> getAllowedHeaderParams() { return allowedHeaderParams; } 5267 5268 /** 5269 * Allowed method headers. 5270 * 5271 * <h5 class='section'>See Also:</h5><ul> 5272 * <li class='ja'>{@link Rest#allowedMethodHeaders} 5273 * <li class='jm'>{@link RestContext.Builder#allowedMethodHeaders(String)} 5274 * </ul> 5275 * 5276 * @return 5277 * The method names allowed to be passed as <c>X-Method</c> headers. 5278 * <br>The set is case-insensitive ordered and unmodifiable. 5279 */ 5280 public Set<String> getAllowedMethodHeaders() { return allowedMethodHeaders; } 5281 5282 /** 5283 * Allowed method URL parameters. 5284 * 5285 * <h5 class='section'>See Also:</h5><ul> 5286 * <li class='ja'>{@link Rest#allowedMethodParams} 5287 * <li class='jm'>{@link RestContext.Builder#allowedMethodParams(String)} 5288 * </ul> 5289 * 5290 * @return 5291 * The method names allowed to be passed as <c>method</c> URL parameters. 5292 * <br>The set is case-insensitive ordered and unmodifiable. 5293 */ 5294 public Set<String> getAllowedMethodParams() { return allowedMethodParams; } 5295 5296 /** 5297 * Returns the annotations applied to this context. 5298 * 5299 * @return The annotations applied to this context. 5300 */ 5301 public AnnotationWorkList getAnnotations() { return builder.getApplied(); } 5302 5303 /** 5304 * Returns the bean context associated with this context. 5305 * 5306 * @return The bean store associated with this context. 5307 */ 5308 public BeanContext getBeanContext() { return beanContext; } 5309 5310 /** 5311 * Returns the bean store associated with this context. 5312 * 5313 * <p> 5314 * The bean store is used for instantiating child resource classes. 5315 * 5316 * @return The resource resolver associated with this context. 5317 */ 5318 public BeanStore getBeanStore() { return beanStore; } 5319 5320 /** 5321 * Returns the builder that created this context. 5322 * 5323 * @return The builder that created this context. 5324 */ 5325 public ServletConfig getBuilder() { return builder; } 5326 5327 /** 5328 * Returns the call logger to use for this resource. 5329 * 5330 * <h5 class='section'>See Also:</h5><ul> 5331 * <li class='jm'>{@link RestContext.Builder#callLogger()} 5332 * </ul> 5333 * 5334 * @return 5335 * The call logger to use for this resource. 5336 * <br>Never <jk>null</jk>. 5337 */ 5338 public CallLogger getCallLogger() { return callLogger; } 5339 5340 /** 5341 * Returns the name of the client version header name used by this resource. 5342 * 5343 * <h5 class='section'>See Also:</h5><ul> 5344 * <li class='ja'>{@link Rest#clientVersionHeader} 5345 * <li class='jm'>{@link RestContext.Builder#clientVersionHeader(String)} 5346 * </ul> 5347 * 5348 * @return 5349 * The name of the client version header used by this resource. 5350 * <br>Never <jk>null</jk>. 5351 */ 5352 public String getClientVersionHeader() { return clientVersionHeader; } 5353 5354 /** 5355 * Returns the config file associated with this servlet. 5356 * 5357 * <p> 5358 * The config file is identified via one of the following: 5359 * <ul class='javatree'> 5360 * <li class='ja'>{@link Rest#config()} 5361 * <li class='jm'>{@link RestContext.Builder#config(Config)} 5362 * </ul> 5363 * 5364 * @return 5365 * The resolving config file associated with this servlet. 5366 * <br>Never <jk>null</jk>. 5367 */ 5368 public Config getConfig() { return config; } 5369 5370 /** 5371 * Returns the explicit list of supported content types for this resource. 5372 * 5373 * <p> 5374 * Consists of the media types for consumption common to all operations on this class. 5375 * 5376 * <p> 5377 * Can be overridden by {@link RestContext.Builder#consumes(MediaType...)}. 5378 * 5379 * @return 5380 * An unmodifiable list of supported <c>Content-Type</c> header values for this resource. 5381 * <br>Never <jk>null</jk>. 5382 */ 5383 public List<MediaType> getConsumes() { return consumes; } 5384 5385 /** 5386 * Returns the debug enablement bean for this context. 5387 * 5388 * @return The debug enablement bean for this context. 5389 */ 5390 public DebugEnablement getDebugEnablement() { return debugEnablement; } 5391 5392 /** 5393 * Returns the default request attributes for this resource. 5394 * 5395 * <h5 class='section'>See Also:</h5><ul> 5396 * <li class='jm'>{@link RestContext.Builder#defaultRequestAttributes(NamedAttribute...)} 5397 * </ul> 5398 * 5399 * @return 5400 * The default request headers for this resource in an unmodifiable list. 5401 * <br>Never <jk>null</jk>. 5402 */ 5403 public NamedAttributeMap getDefaultRequestAttributes() { return defaultRequestAttributes; } 5404 5405 /** 5406 * Returns the default request headers for this resource. 5407 * 5408 * <h5 class='section'>See Also:</h5><ul> 5409 * <li class='jm'>{@link RestContext.Builder#defaultRequestHeaders(org.apache.http.Header...)} 5410 * </ul> 5411 * 5412 * @return 5413 * The default request headers for this resource in an unmodifiable list. 5414 * <br>Never <jk>null</jk>. 5415 */ 5416 public HeaderList getDefaultRequestHeaders() { return defaultRequestHeaders; } 5417 5418 /** 5419 * Returns the default response headers for this resource. 5420 * 5421 * <h5 class='section'>See Also:</h5><ul> 5422 * <li class='jm'>{@link RestContext.Builder#defaultResponseHeaders(org.apache.http.Header...)} 5423 * </ul> 5424 * 5425 * @return 5426 * The default response headers for this resource in an unmodifiable list. 5427 * <br>Never <jk>null</jk>. 5428 */ 5429 public HeaderList getDefaultResponseHeaders() { return defaultResponseHeaders; } 5430 5431 /** 5432 * Returns the encoders associated with this context. 5433 * 5434 * @return The encoders associated with this context. 5435 */ 5436 public EncoderSet getEncoders() { return encoders; } 5437 5438 /** 5439 * Returns the path for this resource as defined by the {@link Rest#path() @Rest(path)} annotation or 5440 * {@link RestContext.Builder#path(String)} method concatenated with those on all parent classes. 5441 * 5442 * <p> 5443 * If path is not specified, returns <js>""</js>. 5444 * 5445 * <h5 class='section'>See Also:</h5><ul> 5446 * <li class='jm'>{@link RestContext.Builder#path(String)} 5447 * </ul> 5448 * 5449 * @return The full path. 5450 */ 5451 public String getFullPath() { return fullPath; } 5452 5453 /** 5454 * Returns the JSON-Schema generator associated with this resource. 5455 * 5456 * @return 5457 * The HTTP-part serializer associated with this resource. 5458 * <br>Never <jk>null</jk>. 5459 */ 5460 public JsonSchemaGenerator getJsonSchemaGenerator() { return jsonSchemaGenerator; } 5461 5462 /** 5463 * Returns the HTTP call for the current request. 5464 * 5465 * @return The HTTP call for the current request, never <jk>null</jk>? 5466 * @throws InternalServerError If no active request exists on the current thread. 5467 */ 5468 public RestSession getLocalSession() { 5469 var rc = localSession.get(); 5470 if (rc == null) 5471 throw new InternalServerError("No active request on current thread."); 5472 return rc; 5473 } 5474 5475 /** 5476 * Returns the logger associated with this context. 5477 * 5478 * @return 5479 * The logger for this resource. 5480 * <br>Never <jk>null</jk>. 5481 */ 5482 public Logger getLogger() { return logger; } 5483 5484 /** 5485 * Returns the resource bundle used by this resource. 5486 * 5487 * @return 5488 * The resource bundle for this resource. 5489 * <br>Never <jk>null</jk>. 5490 */ 5491 public Messages getMessages() { return messages; } 5492 5493 /** 5494 * Returns the timing statistics on all method executions on this class. 5495 * 5496 * @return The timing statistics on all method executions on this class. 5497 */ 5498 public MethodExecStore getMethodExecStore() { return methodExecStore; } 5499 5500 /** 5501 * Returns the parsers associated with this context. 5502 * 5503 * @return The parsers associated with this context. 5504 */ 5505 public ParserSet getParsers() { return parsers; } 5506 5507 /** 5508 * Returns the HTTP-part parser associated with this resource. 5509 * 5510 * @return 5511 * The HTTP-part parser associated with this resource. 5512 * <br>Never <jk>null</jk>. 5513 */ 5514 public HttpPartParser getPartParser() { return partParser; } 5515 5516 /** 5517 * Returns the HTTP-part serializer associated with this resource. 5518 * 5519 * @return 5520 * The HTTP-part serializer associated with this resource. 5521 * <br>Never <jk>null</jk>. 5522 */ 5523 public HttpPartSerializer getPartSerializer() { return partSerializer; } 5524 5525 /** 5526 * Returns the path for this resource as defined by the {@link Rest#path() @Rest(path)} annotation or 5527 * {@link RestContext.Builder#path(String)} method. 5528 * 5529 * <p> 5530 * If path is not specified, returns <js>""</js>. 5531 * 5532 * <h5 class='section'>See Also:</h5><ul> 5533 * <li class='jm'>{@link RestContext.Builder#path(String)} 5534 * </ul> 5535 * 5536 * @return The servlet path. 5537 */ 5538 public String getPath() { return path; } 5539 5540 /** 5541 * Returns the path matcher for this context. 5542 * 5543 * @return The path matcher for this context. 5544 */ 5545 public UrlPathMatcher getPathMatcher() { return pathMatcher; } 5546 5547 /** 5548 * Returns the explicit list of supported accept types for this resource. 5549 * 5550 * <p> 5551 * Consists of the media types for production common to all operations on this class. 5552 * 5553 * <p> 5554 * Can be overridden by {@link RestContext.Builder#produces(MediaType...)}. 5555 * 5556 * @return 5557 * An unmodifiable list of supported <c>Accept</c> header values for this resource. 5558 * <br>Never <jk>null</jk>. 5559 */ 5560 public List<MediaType> getProduces() { return produces; } 5561 5562 /** 5563 * Returns the resource object. 5564 * 5565 * <p> 5566 * This is the instance of the class annotated with the {@link Rest @Rest} annotation, usually 5567 * an instance of {@link RestServlet}. 5568 * 5569 * @return 5570 * The resource object. 5571 * <br>Never <jk>null</jk>. 5572 */ 5573 public Object getResource() { return resource.get(); } 5574 5575 /** 5576 * Returns the resource class type. 5577 * 5578 * @return The resource class type. 5579 */ 5580 public Class<?> getResourceClass() { return resourceClass; } 5581 5582 /** 5583 * Returns the child resources associated with this servlet. 5584 * 5585 * @return 5586 * An unmodifiable map of child resources. 5587 * Keys are the {@link Rest#path() @Rest(path)} annotation defined on the child resource. 5588 */ 5589 public RestChildren getRestChildren() { return restChildren; } 5590 5591 /** 5592 * Returns the REST Java methods defined in this resource. 5593 * 5594 * <p> 5595 * These are the methods annotated with the {@link RestOp @RestOp} annotation. 5596 * 5597 * @return 5598 * An unmodifiable map of Java method names to call method objects. 5599 */ 5600 public RestOperations getRestOperations() { return restOperations; } 5601 5602 /** 5603 * Returns the root bean store for this context. 5604 * 5605 * @return The root bean store for this context. 5606 */ 5607 public BeanStore getRootBeanStore() { return rootBeanStore; } 5608 5609 /** 5610 * Returns the serializers associated with this context. 5611 * 5612 * @return The serializers associated with this context. 5613 */ 5614 public SerializerSet getSerializers() { return serializers; } 5615 5616 /** 5617 * Returns the servlet init parameter returned by {@link ServletConfig#getInitParameter(String)}. 5618 * 5619 * @param name The init parameter name. 5620 * @return The servlet init parameter, or <jk>null</jk> if not found. 5621 */ 5622 public String getServletInitParameter(String name) { 5623 return builder.getInitParameter(name); 5624 } 5625 5626 /** 5627 * Returns the static files associated with this context. 5628 * 5629 * @return 5630 * The static files for this resource. 5631 * <br>Never <jk>null</jk>. 5632 */ 5633 public StaticFiles getStaticFiles() { return staticFiles; } 5634 5635 /** 5636 * Gives access to the internal statistics on this context. 5637 * 5638 * @return The context statistics. 5639 */ 5640 public RestContextStats getStats() { return new RestContextStats(startTime, getMethodExecStore().getStatsByTotalTime()); } 5641 5642 /** 5643 * Returns the swagger for the REST resource. 5644 * 5645 * @param locale The locale of the swagger to return. 5646 * @return The swagger as an {@link Optional}. Never <jk>null</jk>. 5647 */ 5648 public Optional<Swagger> getSwagger(Locale locale) { 5649 Swagger s = swaggerCache.get(locale); 5650 if (s == null) { 5651 try { 5652 s = swaggerProvider.getSwagger(this, locale); 5653 if (nn(s)) 5654 swaggerCache.put(locale, s); 5655 } catch (Exception e) { 5656 throw new InternalServerError(e); 5657 } 5658 } 5659 return opt(s); 5660 } 5661 5662 /** 5663 * Returns the Swagger provider used by this resource. 5664 * 5665 * <h5 class='section'>See Also:</h5><ul> 5666 * <li class='jm'>{@link RestContext.Builder#swaggerProvider(Class)} 5667 * <li class='jm'>{@link RestContext.Builder#swaggerProvider(SwaggerProvider)} 5668 * </ul> 5669 * 5670 * @return 5671 * The information provider for this resource. 5672 * <br>Never <jk>null</jk>. 5673 */ 5674 public SwaggerProvider getSwaggerProvider() { return swaggerProvider; } 5675 5676 /** 5677 * Returns the stack trace database associated with this context. 5678 * 5679 * @return 5680 * The stack trace database for this resource. 5681 * <br>Never <jk>null</jk>. 5682 */ 5683 public ThrownStore getThrownStore() { return thrownStore; } 5684 5685 /** 5686 * Returns the authority path of the resource. 5687 * 5688 * <h5 class='section'>See Also:</h5><ul> 5689 * <li class='jm'>{@link RestContext.Builder#uriAuthority(String)} 5690 * </ul> 5691 * 5692 * @return 5693 * The authority path of this resource. 5694 * <br>If not specified, returns the context path of the ascendant resource. 5695 */ 5696 public String getUriAuthority() { 5697 if (nn(uriAuthority)) 5698 return uriAuthority; 5699 if (nn(parentContext)) 5700 return parentContext.getUriAuthority(); 5701 return null; 5702 } 5703 5704 /** 5705 * Returns the context path of the resource. 5706 * 5707 * <h5 class='section'>See Also:</h5><ul> 5708 * <li class='jm'>{@link RestContext.Builder#uriContext(String)} 5709 * </ul> 5710 * 5711 * @return 5712 * The context path of this resource. 5713 * <br>If not specified, returns the context path of the ascendant resource. 5714 */ 5715 public String getUriContext() { 5716 if (nn(uriContext)) 5717 return uriContext; 5718 if (nn(parentContext)) 5719 return parentContext.getUriContext(); 5720 return null; 5721 } 5722 5723 /** 5724 * Returns the setting on how relative URIs should be interpreted as relative to. 5725 * 5726 * <h5 class='section'>See Also:</h5><ul> 5727 * <li class='jm'>{@link RestContext.Builder#uriRelativity(UriRelativity)} 5728 * </ul> 5729 * 5730 * @return 5731 * The URI-resolution relativity setting value. 5732 * <br>Never <jk>null</jk>. 5733 */ 5734 public UriRelativity getUriRelativity() { return uriRelativity; } 5735 5736 /** 5737 * Returns the setting on how relative URIs should be resolved. 5738 * 5739 * <h5 class='section'>See Also:</h5><ul> 5740 * <li class='jm'>{@link RestContext.Builder#uriResolution(UriResolution)} 5741 * </ul> 5742 * 5743 * @return 5744 * The URI-resolution setting value. 5745 * <br>Never <jk>null</jk>. 5746 */ 5747 public UriResolution getUriResolution() { return uriResolution; } 5748 5749 /** 5750 * Returns the variable resolver for this servlet. 5751 * 5752 * <p> 5753 * Variable resolvers are used to replace variables in property values. 5754 * They can be nested arbitrarily deep. 5755 * They can also return values that themselves contain other variables. 5756 * 5757 * <h5 class='figure'>Example:</h5> 5758 * <p class='bjava'> 5759 * <ja>@Rest</ja>( 5760 * messages=<js>"nls/Messages"</js>, 5761 * properties={ 5762 * <ja>@Property</ja>(name=<js>"title"</js>,value=<js>"$L{title}"</js>), <jc>// Localized variable in Messages.properties</jc> 5763 * <ja>@Property</ja>(name=<js>"javaVendor"</js>,value=<js>"$S{java.vendor,Oracle}"</js>), <jc>// System property with default value</jc> 5764 * <ja>@Property</ja>(name=<js>"foo"</js>,value=<js>"bar"</js>), 5765 * <ja>@Property</ja>(name=<js>"bar"</js>,value=<js>"baz"</js>), 5766 * <ja>@Property</ja>(name=<js>"v1"</js>,value=<js>"$R{foo}"</js>), <jc>// Request variable. value="bar"</jc> 5767 * <ja>@Property</ja>(name=<js>"v1"</js>,value=<js>"$R{foo,bar}"</js>), <jc>// Request variable. value="bar"</jc> 5768 * } 5769 * ) 5770 * <jk>public class</jk> MyRestResource <jk>extends</jk> BasicRestServlet { 5771 * </p> 5772 * 5773 * <p> 5774 * A typical usage pattern involves using variables inside the {@link HtmlDocConfig @HtmlDocConfig} annotation: 5775 * <p class='bjava'> 5776 * <ja>@RestGet</ja>(<js>"/{name}/*"</js>) 5777 * <ja>@HtmlDocConfig</ja>( 5778 * navlinks={ 5779 * <js>"up: $R{requestParentURI}"</js>, 5780 * <js>"api: servlet:/api"</js>, 5781 * <js>"stats: servlet:/stats"</js>, 5782 * <js>"editLevel: servlet:/editLevel?logger=$A{attribute.name, OFF}"</js> 5783 * } 5784 * header={ 5785 * <js>"<h1>$L{MyLocalizedPageTitle}</h1>"</js> 5786 * }, 5787 * aside={ 5788 * <js>"$F{resources/AsideText.html}"</js> 5789 * } 5790 * ) 5791 * <jk>public</jk> LoggerEntry getLogger(RestRequest <jv>req</jv>, <ja>@Path</ja> String <jv>name</jv>) <jk>throws</jk> Exception { 5792 * </p> 5793 * 5794 * <h5 class='section'>See Also:</h5><ul> 5795 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerSvlVariables">SVL Variables</a> 5796 * </ul> 5797 * 5798 * @return The var resolver in use by this resource. 5799 */ 5800 public VarResolver getVarResolver() { return varResolver; } 5801 5802 /** 5803 * Returns whether it's safe to pass the HTTP content as a <js>"content"</js> GET parameter. 5804 * 5805 * <h5 class='section'>See Also:</h5><ul> 5806 * <li class='jm'>{@link RestContext.Builder#disableContentParam()} 5807 * </ul> 5808 * 5809 * @return <jk>true</jk> if setting is enabled. 5810 */ 5811 public boolean isAllowContentParam() { return allowContentParam; } 5812 5813 /** 5814 * Returns whether it's safe to render stack traces in HTTP responses. 5815 * 5816 * @return <jk>true</jk> if setting is enabled. 5817 */ 5818 public boolean isRenderResponseStackTraces() { return renderResponseStackTraces; } 5819 5820 /** 5821 * Called during servlet initialization to invoke all {@link RestPostInit} child-last methods. 5822 * 5823 * @return This object. 5824 * @throws ServletException Error occurred. 5825 */ 5826 public synchronized RestContext postInit() throws ServletException { 5827 if (initialized.get()) 5828 return this; 5829 var resource = getResource(); 5830 var mi = ClassInfo.of(getResource()).getPublicMethod(x -> x.hasName("setContext") && x.hasParameterTypes(RestContext.class)).orElse(null); 5831 if (nn(mi)) { 5832 try { 5833 mi.accessible().invoke(resource, this); 5834 } catch (ExecutableException e) { 5835 throw new ServletException(e.unwrap()); 5836 } 5837 } 5838 for (var x : postInitMethods) { 5839 try { 5840 x.invoke(beanStore, getResource()); 5841 } catch (Exception e) { 5842 throw new ServletException(unwrap(e)); 5843 } 5844 } 5845 restChildren.postInit(); 5846 return this; 5847 } 5848 5849 /** 5850 * Called during servlet initialization to invoke all {@link RestPostInit} child-first methods. 5851 * 5852 * @return This object. 5853 * @throws ServletException Error occurred. 5854 */ 5855 public RestContext postInitChildFirst() throws ServletException { 5856 if (initialized.get()) 5857 return this; 5858 restChildren.postInitChildFirst(); 5859 for (var x : postInitChildFirstMethods) { 5860 try { 5861 x.invoke(beanStore, getResource()); 5862 } catch (Exception e) { 5863 throw new ServletException(unwrap(e)); 5864 } 5865 } 5866 initialized.set(true); 5867 return this; 5868 } 5869 5870 private boolean isDebug(RestSession call) { 5871 return debugEnablement.isDebug(this, call.getRequest()); 5872 } 5873 5874 private static Set<String> newCaseInsensitiveSet(String value) { 5875 var s = new TreeSet<>(String.CASE_INSENSITIVE_ORDER) { 5876 private static final long serialVersionUID = 1L; 5877 5878 @Override 5879 public boolean contains(Object v) { 5880 return v == null ? false : super.contains(v); 5881 } 5882 }; 5883 StringUtils.split(value, x -> s.add(x)); 5884 return u(s); 5885 } 5886 5887 private MethodInvoker toMethodInvoker(Method m) { 5888 return new MethodInvoker(m, getMethodExecStats(m)); 5889 } 5890 5891 private static Throwable unwrap(Throwable t) { 5892 if (t instanceof InvocationTargetException t2) 5893 return t2.getTargetException(); 5894 return t; 5895 } 5896 5897 /** 5898 * Method that can be subclassed to allow uncaught throwables to be treated as other types of throwables. 5899 * 5900 * <p> 5901 * The default implementation looks at the throwable class name to determine whether it can be converted to another type: 5902 * 5903 * <ul> 5904 * <li><js>"*AccessDenied*"</js> - Converted to {@link Unauthorized}. 5905 * <li><js>"*Empty*"</js>,<js>"*NotFound*"</js> - Converted to {@link NotFound}. 5906 * </ul> 5907 * 5908 * @param t The thrown object. 5909 * @return The converted thrown object. 5910 */ 5911 protected Throwable convertThrowable(Throwable t) { 5912 5913 if (t instanceof InvocationTargetException t2) 5914 t = t2.getTargetException(); 5915 5916 if (t instanceof ExecutableException t3) 5917 t = t3.getTargetException(); 5918 5919 if (t instanceof BasicHttpException t2) 5920 return t2; 5921 5922 var ci = ClassInfo.of(t); 5923 5924 if (ci.hasAnnotation(Response.class)) 5925 return t; 5926 5927 if (ci.isChildOf(ParseException.class) || ci.is(InvalidDataConversionException.class)) 5928 return new BadRequest(t); 5929 5930 String n = cn(t); 5931 5932 if (co(n, "AccessDenied") || co(n, "Unauthorized")) 5933 return new Unauthorized(t); 5934 5935 if (co(n, "Empty") || co(n, "NotFound")) 5936 return new NotFound(t); 5937 5938 return t; 5939 } 5940 5941 /** 5942 * Called at the end of a request to invoke all {@link RestEndCall} methods. 5943 * 5944 * <p> 5945 * This is the very last method called in {@link #execute(Object, HttpServletRequest, HttpServletResponse)}. 5946 * 5947 * @param session The current request. 5948 */ 5949 protected void endCall(RestSession session) { 5950 for (var x : endCallMethods) { 5951 try { 5952 x.invoke(session.getBeanStore(), session.getResource()); 5953 } catch (Exception e) { 5954 getLogger().log(Level.WARNING, unwrap(e), () -> f("Error occurred invoking finish-call method ''{0}''.", x.getFullName())); 5955 } 5956 } 5957 } 5958 5959 /** 5960 * Finds the {@link RestOpArg} instances to handle resolving objects on the calls to the specified Java method. 5961 * 5962 * @param m The Java method being called. 5963 * @param beanStore 5964 * The factory used for creating beans and retrieving injected beans. 5965 * <br>Created by {@link RestContext.Builder#beanStore()}. 5966 * @return The array of resolvers. 5967 */ 5968 protected RestOpArg[] findRestOperationArgs(Method m, BeanStore beanStore) { 5969 5970 var mi = MethodInfo.of(m); 5971 var params = mi.getParameters(); 5972 var ra = new RestOpArg[params.size()]; 5973 5974 beanStore = BeanStore.of(beanStore, getResource()); 5975 5976 for (var i = 0; i < params.size(); i++) { 5977 var pi = params.get(i); 5978 beanStore.addBean(ParameterInfo.class, pi); 5979 for (var c : restOpArgs) { 5980 try { 5981 ra[i] = beanStore.createBean(RestOpArg.class).type(c).run(); 5982 if (nn(ra[i])) 5983 break; 5984 } catch (ExecutableException e) { 5985 throw new InternalServerError(e.unwrap(), "Could not resolve parameter {0} on method {1}.", i, mi.getFullName()); 5986 } 5987 } 5988 if (ra[i] == null) 5989 throw new InternalServerError("Could not resolve parameter {0} on method {1}.", i, mi.getFullName()); 5990 } 5991 5992 return ra; 5993 } 5994 5995 /** 5996 * Returns the time statistics gatherer for the specified method. 5997 * 5998 * @param m The method to get statistics for. 5999 * @return The cached time-stats object. 6000 */ 6001 protected MethodExecStats getMethodExecStats(Method m) { 6002 return this.methodExecStore.getStats(m); 6003 } 6004 6005 /** 6006 * Returns the list of methods to invoke after the actual REST method is called. 6007 * 6008 * @return The list of methods to invoke after the actual REST method is called. 6009 */ 6010 protected MethodList getPostCallMethods() { return postCallMethods; } 6011 6012 /** 6013 * Returns the list of methods to invoke before the actual REST method is called. 6014 * 6015 * @return The list of methods to invoke before the actual REST method is called. 6016 */ 6017 protected MethodList getPreCallMethods() { return preCallMethods; } 6018 6019 /** 6020 * Method for handling response errors. 6021 * 6022 * <p> 6023 * Subclasses can override this method to provide their own custom error response handling. 6024 * 6025 * @param session The rest call. 6026 * @param e The exception that occurred. 6027 * @throws IOException Can be thrown if a problem occurred trying to write to the output stream. 6028 */ 6029 protected synchronized void handleError(RestSession session, Throwable e) throws IOException { 6030 6031 session.exception(e); 6032 6033 if (session.isDebug()) 6034 e.printStackTrace(); 6035 6036 int code = 500; 6037 6038 var ci = ClassInfo.of(e); 6039 var r = ci.getAnnotations(StatusCode.class).findFirst().map(AnnotationInfo::inner).orElse(null); 6040 if (nn(r)) 6041 if (r.value().length > 0) 6042 code = r.value()[0]; 6043 6044 var e2 = (e instanceof BasicHttpException e3 ? e3 : new BasicHttpException(code, e)); 6045 6046 var req = session.getRequest(); 6047 var res = session.getResponse(); 6048 6049 Throwable t = e2.getRootCause(); 6050 if (nn(t)) { 6051 Thrown t2 = thrown(t); 6052 res.setHeader(t2.getName(), t2.getValue()); 6053 } 6054 6055 try { 6056 res.setContentType("text/plain"); 6057 res.setHeader("Content-Encoding", "identity"); 6058 var statusCode = e2.getStatusLine().getStatusCode(); 6059 res.setStatus(statusCode); 6060 6061 var w = (PrintWriter)null; 6062 try { 6063 w = res.getWriter(); 6064 } catch (@SuppressWarnings("unused") IllegalStateException x) { 6065 w = new PrintWriter(new OutputStreamWriter(res.getOutputStream(), UTF8)); 6066 } 6067 6068 try (PrintWriter w2 = w) { 6069 var httpMessage = RestUtils.getHttpResponseText(statusCode); 6070 if (nn(httpMessage)) 6071 w2.append("HTTP ").append(String.valueOf(statusCode)).append(": ").append(httpMessage).append("\n\n"); 6072 if (isRenderResponseStackTraces()) 6073 e.printStackTrace(w2); 6074 else 6075 w2.append(e2.getFullStackMessage(true)); 6076 } 6077 6078 } catch (Exception e1) { 6079 req.setAttribute("Exception", e1); 6080 } 6081 } 6082 6083 /** 6084 * Handle the case where a matching method was not found. 6085 * 6086 * <p> 6087 * Subclasses can override this method to provide a 2nd-chance for specifying a response. 6088 * The default implementation will simply throw an exception with an appropriate message. 6089 * 6090 * @param session The HTTP call. 6091 * @throws Exception Any exception can be thrown. 6092 */ 6093 protected void handleNotFound(RestSession session) throws Exception { 6094 var pathInfo = session.getPathInfo(); 6095 var methodUC = session.getMethod(); 6096 var rc = session.getStatus(); 6097 var onPath = pathInfo == null ? " on no pathInfo" : String.format(" on path '%s'", pathInfo); 6098 if (rc == SC_NOT_FOUND) 6099 throw new NotFound("Method ''{0}'' not found on resource with matching pattern{1}.", methodUC, onPath); 6100 else if (rc == SC_PRECONDITION_FAILED) 6101 throw new PreconditionFailed("Method ''{0}'' not found on resource{1} with matching matcher.", methodUC, onPath); 6102 else if (rc == SC_METHOD_NOT_ALLOWED) 6103 throw new MethodNotAllowed("Method ''{0}'' not found on resource{1}.", methodUC, onPath); 6104 else 6105 throw new ServletException("Invalid method response: " + rc, session.getException()); 6106 } 6107 6108 /** 6109 * Called during a request to invoke all {@link RestPostCall} methods. 6110 * 6111 * @param session The current request. 6112 * @throws Throwable If thrown from call methods. 6113 */ 6114 protected void postCall(RestOpSession session) throws Throwable { 6115 for (var m : session.getContext().getPostCallMethods()) 6116 m.invoke(session); 6117 } 6118 6119 /** 6120 * Called during a request to invoke all {@link RestPreCall} methods. 6121 * 6122 * @param session The current request. 6123 * @throws Throwable If thrown from call methods. 6124 */ 6125 protected void preCall(RestOpSession session) throws Throwable { 6126 for (var m : session.getContext().getPreCallMethods()) 6127 m.invoke(session); 6128 } 6129 6130 /** 6131 * The main method for serializing POJOs passed in through the {@link RestResponse#setContent(Object)} method or 6132 * returned by the Java method. 6133 * 6134 * <p> 6135 * Subclasses may override this method if they wish to modify the way the output is rendered or support other output 6136 * formats. 6137 * 6138 * <p> 6139 * The default implementation simply iterates through the response handlers on this resource 6140 * looking for the first one whose {@link ResponseProcessor#process(RestOpSession)} method returns 6141 * <jk>true</jk>. 6142 * 6143 * @param opSession The HTTP call. 6144 * @throws IOException Thrown by underlying stream. 6145 * @throws BasicHttpException Non-200 response. 6146 * @throws NotImplemented No registered response processors could handle the call. 6147 */ 6148 protected void processResponse(RestOpSession opSession) throws IOException, BasicHttpException, NotImplemented { 6149 6150 // Loop until we find the correct processor for the POJO. 6151 int loops = 5; 6152 for (var i = 0; i < responseProcessors.length; i++) { 6153 var j = responseProcessors[i].process(opSession); 6154 if (j == FINISHED) 6155 return; 6156 if (j == RESTART) { 6157 if (loops-- < 0) 6158 throw new InternalServerError("Too many processing loops."); 6159 i = -1; // Start over. 6160 } 6161 } 6162 6163 var output = opSession.getResponse().getContent().orElse(null); 6164 throw new NotImplemented("No response processors found to process output of type ''{0}''", cn(output)); 6165 } 6166 6167 @Override /* Overridden from Context */ 6168 protected FluentMap<String,Object> properties() { 6169 return super.properties() 6170 .a("allowContentParam", allowContentParam) 6171 .a("allowedHeaderParams", allowedHeaderParams) 6172 .a("allowedMethodHeader", allowedMethodHeaders) 6173 .a("allowedMethodParams", allowedMethodParams) 6174 .a("beanStore", beanStore) 6175 .a("clientVersionHeader", clientVersionHeader) 6176 .a("consumes", consumes) 6177 .a("defaultRequestHeaders", defaultRequestHeaders) 6178 .a("defaultResponseHeaders", defaultResponseHeaders) 6179 .a("partParser", partParser) 6180 .a("partSerializer", partSerializer) 6181 .a("produces", produces) 6182 .a("renderResponseStackTraces", renderResponseStackTraces) 6183 .a("responseProcessors", responseProcessors) 6184 .a("restOpArgs", restOpArgs) 6185 .a("staticFiles", staticFiles) 6186 .a("swaggerProvider", swaggerProvider) 6187 .a("uriAuthority", uriAuthority) 6188 .a("uriContext", uriContext) 6189 .a("uriRelativity", uriRelativity) 6190 .a("uriResolution", uriResolution); 6191 } 6192 6193 /** 6194 * Called at the start of a request to invoke all {@link RestStartCall} methods. 6195 * 6196 * @param session The current request. 6197 * @throws BasicHttpException If thrown from call methods. 6198 */ 6199 protected void startCall(RestSession session) throws BasicHttpException { 6200 for (var x : startCallMethods) { 6201 try { 6202 x.invoke(session.getBeanStore(), session.getContext().getResource()); 6203 } catch (IllegalAccessException | IllegalArgumentException e) { 6204 throw new InternalServerError(e, "Error occurred invoking start-call method ''{0}''.", x.getFullName()); 6205 } catch (InvocationTargetException e) { 6206 var t = e.getTargetException(); 6207 if (t instanceof BasicHttpException t2) 6208 throw t2; 6209 throw new InternalServerError(t); 6210 } 6211 } 6212 } 6213}