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