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