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