001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.rest; 014 015import static org.apache.juneau.internal.CollectionUtils.*; 016import static org.apache.juneau.internal.ObjectUtils.*; 017import static org.apache.juneau.collections.JsonMap.*; 018import static org.apache.juneau.common.internal.StringUtils.*; 019import static org.apache.juneau.common.internal.ThrowableUtils.*; 020import static org.apache.juneau.http.HttpHeaders.*; 021import static org.apache.juneau.http.HttpParts.*; 022import static org.apache.juneau.httppart.HttpPartType.*; 023import static org.apache.juneau.rest.util.RestUtils.*; 024import java.lang.annotation.*; 025import java.lang.reflect.Method; 026import java.nio.charset.*; 027import java.util.*; 028import java.util.concurrent.*; 029import java.util.function.*; 030 031import jakarta.servlet.*; 032import jakarta.servlet.http.*; 033 034import org.apache.http.*; 035import org.apache.juneau.*; 036import org.apache.juneau.annotation.*; 037import org.apache.juneau.collections.*; 038import org.apache.juneau.common.internal.*; 039import org.apache.juneau.cp.*; 040import org.apache.juneau.encoders.*; 041import org.apache.juneau.http.annotation.*; 042import org.apache.juneau.http.annotation.Header; 043import org.apache.juneau.http.header.*; 044import org.apache.juneau.http.part.*; 045import org.apache.juneau.http.remote.*; 046import org.apache.juneau.httppart.*; 047import org.apache.juneau.httppart.HttpPartSerializer.Creator; 048import org.apache.juneau.httppart.bean.*; 049import org.apache.juneau.internal.*; 050import org.apache.juneau.jsonschema.*; 051import org.apache.juneau.parser.*; 052import org.apache.juneau.parser.ParseException; 053import org.apache.juneau.reflect.*; 054import org.apache.juneau.rest.annotation.*; 055import org.apache.juneau.rest.converter.*; 056import org.apache.juneau.rest.debug.*; 057import org.apache.juneau.rest.guard.*; 058import org.apache.juneau.rest.httppart.*; 059import org.apache.juneau.rest.logger.*; 060import org.apache.juneau.http.response.*; 061import org.apache.juneau.rest.matcher.*; 062import org.apache.juneau.rest.swagger.*; 063import org.apache.juneau.rest.util.*; 064import org.apache.juneau.serializer.*; 065import org.apache.juneau.svl.*; 066import org.apache.juneau.utils.*; 067 068/** 069 * Represents a single Java servlet/resource method annotated with {@link RestOp @RestOp}. 070 * 071 * <h5 class='section'>Notes:</h5><ul> 072 * <li class='note'>This class is thread safe and reusable. 073 * </ul> 074 * 075 * <h5 class='section'>See Also:</h5><ul> 076 * <li class='link'><a class="doclink" href="../../../../index.html#jrs.RestOpContext">RestOpContext</a> 077 * </ul> 078 */ 079public class RestOpContext extends Context implements Comparable<RestOpContext> { 080 081 //------------------------------------------------------------------------------------------------------------------- 082 // Static 083 //------------------------------------------------------------------------------------------------------------------- 084 085 /** 086 * Creates a new builder for this object. 087 * 088 * @param method The Java method this context belongs to. 089 * @param context The Java class context. 090 * @return A new builder. 091 */ 092 public static Builder create(java.lang.reflect.Method method, RestContext context) { 093 return new Builder(method, context); 094 } 095 096 //------------------------------------------------------------------------------------------------------------------- 097 // Builder 098 //------------------------------------------------------------------------------------------------------------------- 099 100 /** 101 * Builder class. 102 */ 103 @FluentSetters 104 public static final class Builder extends Context.Builder { 105 106 RestContext restContext; 107 RestContext.Builder parent; 108 Method restMethod; 109 String httpMethod, clientVersion; 110 Enablement debug; 111 List<String> path; 112 113 private RestConverterList.Builder converters; 114 private BeanContext.Builder beanContext; 115 private RestGuardList.Builder guards; 116 private EncoderSet.Builder encoders; 117 private SerializerSet.Builder serializers; 118 private ParserSet.Builder parsers; 119 private HttpPartSerializer.Creator partSerializer; 120 private HttpPartParser.Creator partParser; 121 private RestMatcherList.Builder matchers; 122 private JsonSchemaGenerator.Builder jsonSchemaGenerator; 123 124 PartList defaultRequestFormData, defaultRequestQueryData; 125 NamedAttributeMap defaultRequestAttributes; 126 HeaderList defaultRequestHeaders, defaultResponseHeaders; 127 RestMatcherList.Builder restMatchers; 128 List<MediaType> produces, consumes; 129 Set<String> roleGuard, rolesDeclared; 130 boolean dotAll; 131 132 Charset defaultCharset; 133 Long maxInput; 134 135 private BeanStore beanStore; 136 137 @Override /* Context.Builder */ 138 public Builder copy() { 139 throw new NoSuchMethodError("Not implemented."); 140 } 141 142 @Override /* BeanContext.Builder */ 143 public RestOpContext build() { 144 try { 145 return beanStore.createBean(RestOpContext.class).type(getType().orElse(getDefaultImplClass())).builder(RestOpContext.Builder.class, this).run(); 146 } catch (Exception e) { 147 throw new InternalServerError(e); 148 } 149 } 150 151 /** 152 * Specifies the default implementation class if not specified via {@link #type(Class)}. 153 * 154 * @return The default implementation class if not specified via {@link #type(Class)}. 155 */ 156 protected Class<? extends RestOpContext> getDefaultImplClass() { 157 return RestOpContext.class; 158 } 159 160 Builder(java.lang.reflect.Method method, RestContext context) { 161 162 this.restContext = context; 163 this.parent = context.builder; 164 this.restMethod = method; 165 166 this.beanStore = BeanStore 167 .of(context.getBeanStore(), context.builder.resource().get()) 168 .addBean(java.lang.reflect.Method.class, method); 169 170 MethodInfo mi = MethodInfo.of(context.getResourceClass(), method); 171 172 try { 173 174 VarResolver vr = context.getVarResolver(); 175 VarResolverSession vrs = vr.createSession(); 176 AnnotationWorkList work = AnnotationWorkList.of(vrs, mi.getAnnotationList(CONTEXT_APPLY_FILTER)); 177 178 apply(work); 179 180 if (context.builder.beanContext().canApply(work)) 181 beanContext().apply(work); 182 if (context.builder.serializers().canApply(work)) 183 serializers().apply(work); 184 if (context.builder.parsers().canApply(work)) 185 parsers().apply(work); 186 if (context.builder.partSerializer().canApply(work)) 187 partSerializer().apply(work); 188 if (context.builder.partParser().canApply(work)) 189 partParser().apply(work); 190 if (context.builder.jsonSchemaGenerator().canApply(work)) 191 jsonSchemaGenerator().apply(work); 192 193 processParameterAnnotations(); 194 195 } catch (Exception e) { 196 throw new InternalServerError(e); 197 } 198 } 199 200 /** 201 * Returns the REST servlet/bean instance that this context is defined against. 202 * 203 * @return The REST servlet/bean instance that this context is defined against. 204 */ 205 public Supplier<?> resource() { 206 return restContext.builder.resource(); 207 } 208 209 /** 210 * Returns the default classes list. 211 * 212 * <p> 213 * This defines the implementation classes for a variety of bean types. 214 * 215 * <p> 216 * Default classes are inherited from the parent REST object. 217 * Typically used on the top-level {@link RestContext.Builder} to affect class types for that REST object and all children. 218 * 219 * <p> 220 * Modifying the default class list on this builder does not affect the default class list on the parent builder, but changes made 221 * here are inherited by child builders. 222 * 223 * @return The default classes list for this builder. 224 */ 225 public DefaultClassList defaultClasses() { 226 return restContext.builder.defaultClasses(); 227 } 228 229 //----------------------------------------------------------------------------------------------------------------- 230 // beanStore 231 //----------------------------------------------------------------------------------------------------------------- 232 233 /** 234 * Returns access to the bean store being used by this builder. 235 * 236 * <p> 237 * Can be used to add more beans to the bean store. 238 * 239 * @return The bean store being used by this builder. 240 */ 241 public BeanStore beanStore() { 242 return beanStore; 243 } 244 245 /** 246 * Specifies a {@link BeanStore} to use when resolving constructor arguments. 247 * 248 * @param beanStore The bean store to use for resolving constructor arguments. 249 * @return This object. 250 */ 251 protected Builder beanStore(BeanStore beanStore) { 252 this.beanStore = beanStore; 253 return this; 254 } 255 256 /** 257 * Adds a bean to the bean store of this operation. 258 * 259 * <p> 260 * Equivalent to calling: 261 * <p class='bjava'> 262 * <jv>builder</jv>.beanStore().add(<jv>beanType</jv>, <jv>bean</jv>); 263 * </p> 264 * 265 * @param <T> The class to associate this bean with. 266 * @param beanType The class to associate this bean with. 267 * @param bean The bean. Can be <jk>null</jk>. 268 * @return This object. 269 */ 270 public <T> Builder beanStore(Class<T> beanType, T bean) { 271 beanStore().addBean(beanType, bean); 272 return this; 273 } 274 275 /** 276 * Adds a bean to the bean store of this operation. 277 * 278 * <p> 279 * Equivalent to calling: 280 * <p class='bjava'> 281 * <jv>builder</jv>.beanStore().add(<jv>beanType</jv>, <jv>bean</jv>, <jv>name</jv>); 282 * </p> 283 * 284 * @param <T> The class to associate this bean with. 285 * @param beanType The class to associate this bean with. 286 * @param bean The bean. Can be <jk>null</jk>. 287 * @param name The bean name if this is a named bean. Can be <jk>null</jk>. 288 * @return This object. 289 */ 290 public <T> Builder beanStore(Class<T> beanType, T bean, String name) { 291 beanStore().addBean(beanType, bean, name); 292 return this; 293 } 294 295 //----------------------------------------------------------------------------------------------------------------- 296 // beanContext 297 //----------------------------------------------------------------------------------------------------------------- 298 299 /** 300 * Returns the bean context sub-builder. 301 * 302 * @return The bean context sub-builder. 303 */ 304 public BeanContext.Builder beanContext() { 305 if (beanContext == null) 306 beanContext = createBeanContext(beanStore(), parent, resource()); 307 return beanContext; 308 } 309 310 /** 311 * Instantiates the bean context sub-builder. 312 * 313 * @param beanStore 314 * The factory used for creating beans and retrieving injected beans. 315 * @param parent 316 * The builder for the REST resource class. 317 * @param resource 318 * The REST servlet/bean instance that this context is defined against. 319 * @return A new bean context sub-builder. 320 */ 321 protected BeanContext.Builder createBeanContext(BeanStore beanStore, RestContext.Builder parent, Supplier<?> resource) { 322 323 // Default value. 324 Value<BeanContext.Builder> v = Value.of( 325 parent.beanContext().copy() 326 ); 327 328 // Replace with bean from: @RestInject(methodScope="foo") public [static] BeanContext xxx(<args>) 329 BeanStore 330 .of(beanStore, resource) 331 .addBean(BeanContext.Builder.class, v.get()) 332 .createMethodFinder(BeanContext.class, resource) 333 .find(this::matches) 334 .run(x -> v.get().impl(x)); 335 336 return v.get(); 337 } 338 339 Optional<BeanContext> getBeanContext() { 340 return optional(beanContext).map(BeanContext.Builder::build); 341 } 342 343 //----------------------------------------------------------------------------------------------------------------- 344 // encoders 345 //----------------------------------------------------------------------------------------------------------------- 346 347 /** 348 * Returns the encoder group sub-builder. 349 * 350 * @return The encoder group sub-builder. 351 */ 352 public EncoderSet.Builder encoders() { 353 if (encoders == null) 354 encoders = createEncoders(beanStore(), parent, resource()); 355 return encoders; 356 } 357 358 /** 359 * Adds one or more encoders to this operation. 360 * 361 * <p> 362 * Equivalent to calling: 363 * <p class='bjava'> 364 * <jv>builder</jv>.encoders().add(<jv>value</jv>); 365 * </p> 366 * 367 * @param value The values to add. 368 * @return This object. 369 */ 370 @SafeVarargs 371 public final Builder encoders(Class<? extends Encoder>...value) { 372 encoders().add(value); 373 return this; 374 } 375 376 /** 377 * Adds one or more encoders to this operation. 378 * 379 * <p> 380 * Equivalent to calling: 381 * <p class='bjava'> 382 * <jv>builder</jv>.encoders().add(<jv>value</jv>); 383 * </p> 384 * 385 * @param value The values to add. 386 * @return This object. 387 */ 388 public Builder encoders(Encoder...value) { 389 encoders().add(value); 390 return this; 391 } 392 393 /** 394 * Instantiates the encoder group sub-builder. 395 * 396 * @param beanStore 397 * The factory used for creating beans and retrieving injected beans. 398 * @param parent 399 * The builder for the REST resource class. 400 * @param resource 401 * The REST servlet/bean instance that this context is defined against. 402 * @return A new encoder group sub-builder. 403 */ 404 protected EncoderSet.Builder createEncoders(BeanStore beanStore, RestContext.Builder parent, Supplier<?> resource) { 405 406 // Default value. 407 Value<EncoderSet.Builder> v = Value.of( 408 parent.encoders().copy() 409 ); 410 411 // Replace with bean from: @RestInject(methodScope="foo") public [static] EncoderSet xxx(<args>) 412 BeanStore 413 .of(beanStore, resource) 414 .addBean(EncoderSet.Builder.class, v.get()) 415 .createMethodFinder(EncoderSet.class, resource) 416 .find(this::matches) 417 .run(x -> v.get().impl(x)); 418 419 return v.get(); 420 } 421 422 Optional<EncoderSet> getEncoders() { 423 return optional(encoders).map(EncoderSet.Builder::build); 424 } 425 426 //----------------------------------------------------------------------------------------------------------------- 427 // serializers 428 //----------------------------------------------------------------------------------------------------------------- 429 430 /** 431 * Returns the serializer group sub-builder. 432 * 433 * @return The serializer group sub-builder. 434 */ 435 public SerializerSet.Builder serializers() { 436 if (serializers == null) 437 serializers = createSerializers(beanStore(), parent, resource()); 438 return serializers; 439 } 440 441 /** 442 * Adds one or more serializers to this operation. 443 * 444 * <p> 445 * Equivalent to calling: 446 * <p class='bjava'> 447 * <jv>builder</jv>.serializers().add(<jv>value</jv>); 448 * </p> 449 * 450 * @param value The values to add. 451 * @return This object. 452 */ 453 @SafeVarargs 454 public final Builder serializers(Class<? extends Serializer>...value) { 455 serializers().add(value); 456 return this; 457 } 458 459 /** 460 * Adds one or more serializers to this operation. 461 * 462 * <p> 463 * Equivalent to calling: 464 * <p class='bjava'> 465 * <jv>builder</jv>.serializers().add(<jv>value</jv>); 466 * </p> 467 * 468 * @param value The values to add. 469 * @return This object. 470 */ 471 public Builder serializers(Serializer...value) { 472 serializers().add(value); 473 return this; 474 } 475 476 /** 477 * Instantiates the serializer group sub-builder. 478 * 479 * @param beanStore 480 * The factory used for creating beans and retrieving injected beans. 481 * @param parent 482 * The builder for the REST resource class. 483 * @param resource 484 * The REST servlet/bean instance that this context is defined against. 485 * @return A new serializer group sub-builder. 486 */ 487 protected SerializerSet.Builder createSerializers(BeanStore beanStore, RestContext.Builder parent, Supplier<?> resource) { 488 489 // Default value. 490 Value<SerializerSet.Builder> v = Value.of( 491 parent.serializers().copy() 492 ); 493 494 // Replace with bean from: @RestInject(methodScope="foo") public [static] SerializerSet xxx(<args>) 495 BeanStore 496 .of(beanStore, resource) 497 .addBean(SerializerSet.Builder.class, v.get()) 498 .createMethodFinder(SerializerSet.class, resource) 499 .find(this::matches) 500 .run(x -> v.get().impl(x)); 501 502 return v.get(); 503 } 504 505 Optional<SerializerSet> getSerializers() { 506 return optional(serializers).map(SerializerSet.Builder::build); 507 } 508 509 //----------------------------------------------------------------------------------------------------------------- 510 // parsers 511 //----------------------------------------------------------------------------------------------------------------- 512 513 /** 514 * Returns the parser group sub-builder. 515 * 516 * @return The parser group sub-builder. 517 */ 518 public ParserSet.Builder parsers() { 519 if (parsers == null) 520 parsers = createParsers(beanStore(), parent, resource()); 521 return parsers; 522 } 523 524 /** 525 * Adds one or more parsers to this operation. 526 * 527 * <p> 528 * Equivalent to calling: 529 * <p class='bjava'> 530 * <jv>builder</jv>.parsers().add(<jv>value</jv>); 531 * </p> 532 * 533 * @param value The values to add. 534 * @return This object. 535 */ 536 @SafeVarargs 537 public final Builder parsers(Class<? extends Parser>...value) { 538 parsers().add(value); 539 return this; 540 } 541 542 /** 543 * Adds one or more parsers to this operation. 544 * 545 * <p> 546 * Equivalent to calling: 547 * <p class='bjava'> 548 * <jv>builder</jv>.parsers().add(<jv>value</jv>); 549 * </p> 550 * 551 * @param value The values to add. 552 * @return This object. 553 */ 554 public Builder parsers(Parser...value) { 555 parsers().add(value); 556 return this; 557 } 558 559 /** 560 * Instantiates the parser group sub-builder. 561 * 562 * @param beanStore 563 * The factory used for creating beans and retrieving injected beans. 564 * @param parent 565 * The builder for the REST resource class. 566 * @param resource 567 * The REST servlet/bean instance that this context is defined against. 568 * @return A new parser group sub-builder. 569 */ 570 protected ParserSet.Builder createParsers(BeanStore beanStore, RestContext.Builder parent, Supplier<?> resource) { 571 572 // Default value. 573 Value<ParserSet.Builder> v = Value.of( 574 parent.parsers().copy() 575 ); 576 577 // Replace with bean from: @RestInject(methodScope="foo") public [static] ParserSet xxx(<args>) 578 BeanStore 579 .of(beanStore, resource) 580 .addBean(ParserSet.Builder.class, v.get()) 581 .createMethodFinder(ParserSet.class, resource) 582 .find(this::matches) 583 .run(x -> v.get().impl(x)); 584 585 return v.get(); 586 } 587 588 Optional<ParserSet> getParsers() { 589 return optional(parsers).map(ParserSet.Builder::build); 590 } 591 592 //----------------------------------------------------------------------------------------------------------------- 593 // partSerializer 594 //----------------------------------------------------------------------------------------------------------------- 595 596 /** 597 * Returns the part serializer sub-builder. 598 * 599 * @return The part serializer sub-builder. 600 */ 601 public HttpPartSerializer.Creator partSerializer() { 602 if (partSerializer == null) 603 partSerializer = createPartSerializer(beanStore(), parent, resource()); 604 return partSerializer; 605 } 606 607 /** 608 * Specifies the part serializer to use for serializing HTTP parts for this operation. 609 * 610 * <p> 611 * Equivalent to calling: 612 * <p class='bjava'> 613 * <jv>builder</jv>.partSerializer().type(<jv>value</jv>); 614 * </p> 615 * 616 * @param value The new value. 617 * @return This object. 618 */ 619 public Builder partSerializer(Class<? extends HttpPartSerializer> value) { 620 partSerializer().type(value); 621 return this; 622 } 623 624 /** 625 * Specifies the part serializer to use for serializing HTTP parts for this operation. 626 * 627 * <p> 628 * Equivalent to calling: 629 * <p class='bjava'> 630 * <jv>builder</jv>.partSerializer().impl(<jv>value</jv>); 631 * </p> 632 * 633 * @param value The new value. 634 * @return This object. 635 */ 636 public Builder partSerializer(HttpPartSerializer value) { 637 partSerializer().impl(value); 638 return this; 639 } 640 641 /** 642 * Instantiates the part serializer sub-builder. 643 * 644 * @param beanStore 645 * The factory used for creating beans and retrieving injected beans. 646 * @param parent 647 * The builder for the REST resource class. 648 * @param resource 649 * The REST servlet/bean instance that this context is defined against. 650 * @return A new part serializer sub-builder. 651 */ 652 protected HttpPartSerializer.Creator createPartSerializer(BeanStore beanStore, RestContext.Builder parent, Supplier<?> resource) { 653 654 // Default value. 655 Value<HttpPartSerializer.Creator> v = Value.of( 656 parent.partSerializer().copy() 657 ); 658 659 // Replace with bean from: @RestInject(methodScope="foo") public [static] HttpPartSerializer xxx(<args>) 660 BeanStore 661 .of(beanStore, resource) 662 .addBean(HttpPartSerializer.Creator.class, v.get()) 663 .createMethodFinder(HttpPartSerializer.class, resource) 664 .find(this::matches) 665 .run(x -> v.get().impl(x)); 666 667 return v.get(); 668 } 669 670 Optional<HttpPartSerializer> getPartSerializer() { 671 return optional(partSerializer).map(Creator::create); 672 } 673 674 //----------------------------------------------------------------------------------------------------------------- 675 // partParser 676 //----------------------------------------------------------------------------------------------------------------- 677 678 /** 679 * Returns the part parser sub-builder. 680 * 681 * @return The part parser sub-builder. 682 */ 683 public HttpPartParser.Creator partParser() { 684 if (partParser == null) 685 partParser = createPartParser(beanStore(), parent, resource()); 686 return partParser; 687 } 688 689 /** 690 * Specifies the part parser to use for parsing HTTP parts for this operation. 691 * 692 * <p> 693 * Equivalent to calling: 694 * <p class='bjava'> 695 * <jv>builder</jv>.partParser().type(<jv>value</jv>); 696 * </p> 697 * 698 * @param value The new value. 699 * @return This object. 700 */ 701 public Builder partParser(Class<? extends HttpPartParser> value) { 702 partParser().type(value); 703 return this; 704 } 705 706 /** 707 * Specifies the part parser to use for parsing HTTP parts for this operation. 708 * 709 * <p> 710 * Equivalent to calling: 711 * <p class='bjava'> 712 * <jv>builder</jv>.partParser().impl(<jv>value</jv>); 713 * </p> 714 * 715 * @param value The new value. 716 * @return This object. 717 */ 718 public Builder partParser(HttpPartParser value) { 719 partParser().impl(value); 720 return this; 721 } 722 723 /** 724 * Instantiates the part parser sub-builder. 725 * 726 * @param beanStore 727 * The factory used for creating beans and retrieving injected beans. 728 * @param parent 729 * The builder for the REST resource class. 730 * @param resource 731 * The REST servlet/bean instance that this context is defined against. 732 * @return A new part parser sub-builder. 733 */ 734 protected HttpPartParser.Creator createPartParser(BeanStore beanStore, RestContext.Builder parent, Supplier<?> resource) { 735 736 // Default value. 737 Value<HttpPartParser.Creator> v = Value.of( 738 parent.partParser().copy() 739 ); 740 741 // Replace with bean from: @RestInject(methodScope="foo") public [static] HttpPartParser xxx(<args>) 742 BeanStore 743 .of(beanStore, resource) 744 .addBean(HttpPartParser.Creator.class, v.get()) 745 .createMethodFinder(HttpPartParser.class, resource) 746 .find(this::matches) 747 .run(x -> v.get().impl(x)); 748 749 return v.get(); 750 } 751 752 Optional<HttpPartParser> getPartParser() { 753 return optional(partParser).map(org.apache.juneau.httppart.HttpPartParser.Creator::create); 754 } 755 756 //----------------------------------------------------------------------------------------------------------------- 757 // jsonSchemaGenerator 758 //----------------------------------------------------------------------------------------------------------------- 759 760 /** 761 * Returns the JSON schema generator sub-builder. 762 * 763 * @return The JSON schema generator sub-builder. 764 */ 765 public JsonSchemaGenerator.Builder jsonSchemaGenerator() { 766 if (jsonSchemaGenerator == null) 767 jsonSchemaGenerator = createJsonSchemaGenerator(beanStore(), parent, resource()); 768 return jsonSchemaGenerator; 769 } 770 771 /** 772 * Specifies the JSON schema generator for this operation. 773 * 774 * <p> 775 * Equivalent to calling: 776 * <p class='bjava'> 777 * <jv>builder</jv>.jsonSchemaGenerator().type(<jv>value</jv>); 778 * </p> 779 * 780 * @param value The new value. 781 * @return This object. 782 */ 783 public Builder jsonSchemaGenerator(Class<? extends JsonSchemaGenerator> value) { 784 jsonSchemaGenerator().type(value); 785 return this; 786 } 787 788 /** 789 * Specifies the JSON schema generator for this operation. 790 * 791 * <p> 792 * Equivalent to calling: 793 * <p class='bjava'> 794 * <jv>builder</jv>.jsonSchemaGenerator().impl(<jv>value</jv>); 795 * </p> 796 * 797 * @param value The new value. 798 * @return This object. 799 */ 800 public Builder jsonSchemaGenerator(JsonSchemaGenerator value) { 801 jsonSchemaGenerator().impl(value); 802 return this; 803 } 804 805 /** 806 * Instantiates the JSON schema generator sub-builder. 807 * 808 * @param beanStore 809 * The factory used for creating beans and retrieving injected beans. 810 * @param parent 811 * The builder for the REST resource class. 812 * @param resource 813 * The REST servlet/bean instance that this context is defined against. 814 * @return A new JSON schema generator sub-builder. 815 */ 816 protected JsonSchemaGenerator.Builder createJsonSchemaGenerator(BeanStore beanStore, RestContext.Builder parent, Supplier<?> resource) { 817 818 // Default value. 819 Value<JsonSchemaGenerator.Builder> v = Value.of( 820 parent.jsonSchemaGenerator().copy() 821 ); 822 823 // Replace with bean from: @RestInject(methodScope="foo") public [static] JsonSchemaGenerator xxx(<args>) 824 BeanStore 825 .of(beanStore, resource) 826 .addBean(JsonSchemaGenerator.Builder.class, v.get()) 827 .createMethodFinder(JsonSchemaGenerator.class, resource) 828 .find(this::matches) 829 .run(x -> v.get().impl(x)); 830 831 return v.get(); 832 } 833 834 Optional<JsonSchemaGenerator> getJsonSchemaGenerator() { 835 return optional(jsonSchemaGenerator).map(JsonSchemaGenerator.Builder::build); 836 } 837 838 //----------------------------------------------------------------------------------------------------------------- 839 // converters 840 //----------------------------------------------------------------------------------------------------------------- 841 842 /** 843 * Returns the response converter list sub-builder. 844 * 845 * @return The response converter list sub-builder. 846 */ 847 public RestConverterList.Builder converters() { 848 if (converters == null) 849 converters = createConverters(beanStore(), resource()); 850 return converters; 851 } 852 853 /** 854 * Adds one or more converters to use to convert response objects for this operation. 855 * 856 * <p> 857 * Equivalent to calling: 858 * <p class='bjava'> 859 * <jv>builder</jv>.converters().append(<jv>value</jv>); 860 * </p> 861 * 862 * @param value The new value. 863 * @return This object. 864 */ 865 @SafeVarargs 866 public final Builder converters(Class<? extends RestConverter>...value) { 867 converters().append(value); 868 return this; 869 } 870 871 /** 872 * Adds one or more converters to this operation. 873 * 874 * <p> 875 * Equivalent to calling: 876 * <p class='bjava'> 877 * <jv>builder</jv>.converters().append(<jv>value</jv>); 878 * </p> 879 * 880 * @param value The new value. 881 * @return This object. 882 */ 883 public Builder converters(RestConverter...value) { 884 converters().append(value); 885 return this; 886 } 887 888 /** 889 * Instantiates the response converter list sub-builder. 890 * 891 * <p> 892 * Associates one or more {@link RestConverter converters} with a resource class. 893 * <br>These converters get called immediately after execution of the REST method in the same order specified in the 894 * annotation. 895 * <br>The object passed into this converter is the object returned from the Java method or passed into 896 * the {@link RestResponse#setContent(Object)} method. 897 * 898 * <p> 899 * Can be used for performing post-processing on the response object before serialization. 900 * 901 * <p> 902 * When multiple converters are specified, they're executed in the order they're specified in the annotation 903 * (e.g. first the results will be traversed, then the resulting node will be searched/sorted). 904 * 905 * <h5 class='section'>Example:</h5> 906 * <p class='bjava'> 907 * <jc>// Our converter.</jc> 908 * <jk>public class</jk> MyConverter <jk>implements</jk> RestConverter { 909 * <ja>@Override</ja> 910 * <jk>public</jk> Object convert(RestRequest <jv>req</jv>, Object <jv>object</jv>) { 911 * <jc>// Do something with object and return another object.</jc> 912 * <jc>// Or just return the same object for a no-op.</jc> 913 * } 914 * } 915 * 916 * <jc>// Option #1 - Registered via annotation resolving to a config file setting with default value.</jc> 917 * <ja>@Rest</ja>(converters={MyConverter.<jk>class</jk>}) 918 * <jk>public class</jk> MyResource { 919 * 920 * <jc>// Option #2 - Registered via builder passed in through resource constructor.</jc> 921 * <jk>public</jk> MyResource(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 922 * 923 * <jc>// Using method on builder.</jc> 924 * <jv>builder</jv>.converters(MyConverter.<jk>class</jk>); 925 * 926 * <jc>// Pass in an instance instead.</jc> 927 * <jv>builder</jv>.converters(<jk>new</jk> MyConverter()); 928 * } 929 * 930 * <jc>// Option #3 - Registered via builder passed in through init method.</jc> 931 * <ja>@RestInit</ja> 932 * <jk>public void</jk> init(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 933 * <jv>builder</jv>.converters(MyConverter.<jk>class</jk>); 934 * } 935 * } 936 * </p> 937 * 938 * <h5 class='section'>Notes:</h5><ul> 939 * <li class='note'> 940 * When defined as a class, the implementation must have one of the following constructors: 941 * <ul> 942 * <li><code><jk>public</jk> T(BeanContext)</code> 943 * <li><code><jk>public</jk> T()</code> 944 * <li><code><jk>public static</jk> T <jsm>create</jsm>(RestContext)</code> 945 * <li><code><jk>public static</jk> T <jsm>create</jsm>()</code> 946 * </ul> 947 * <li class='note'> 948 * Inner classes of the REST resource class are allowed. 949 * </ul> 950 * 951 * <h5 class='section'>See Also:</h5><ul> 952 * <li class='jc'>{@link Traversable} - Allows URL additional path info to address individual elements in a POJO tree. 953 * <li class='jc'>{@link Queryable} - Allows query/view/sort functions to be performed on POJOs. 954 * <li class='jc'>{@link Introspectable} - Allows Java public methods to be invoked on the returned POJOs. 955 * <li class='ja'>{@link Rest#converters()} 956 * <li class='link'><a class="doclink" href="../../../../index.html#jrs.Converters">Converters</a> 957 * </ul> 958 * 959 * @param beanStore 960 * The factory used for creating beans and retrieving injected beans. 961 * @param resource 962 * The REST servlet/bean instance that this context is defined against. 963 * @return A new response converter list sub-builder. 964 */ 965 protected RestConverterList.Builder createConverters(BeanStore beanStore, Supplier<?> resource) { 966 967 // Default value. 968 Value<RestConverterList.Builder> v = Value.of( 969 RestConverterList 970 .create(beanStore) 971 ); 972 973 // Specify the implementation class if its set as a default. 974 defaultClasses() 975 .get(RestConverterList.class) 976 .ifPresent(x -> v.get().type(x)); 977 978 // Replace with bean from bean store. 979 beanStore 980 .getBean(RestConverterList.class) 981 .ifPresent(x->v.get().impl(x)); 982 983 // Replace with bean from: @RestInject(methodScope="foo") public [static] RestConverterList xxx(<args>) 984 beanStore 985 .createMethodFinder(RestConverterList.class) 986 .addBean(RestConverterList.Builder.class, v.get()) 987 .find(this::matches) 988 .run(x -> v.get().impl(x)); 989 990 return v.get(); 991 } 992 993 //----------------------------------------------------------------------------------------------------------------- 994 // guards 995 //----------------------------------------------------------------------------------------------------------------- 996 997 /** 998 * Returns the guard list sub-builder. 999 * 1000 * @return The guard list sub-builder. 1001 */ 1002 public RestGuardList.Builder guards() { 1003 if (guards == null) 1004 guards = createGuards(beanStore(), resource()); 1005 return guards; 1006 } 1007 1008 /** 1009 * Adds one or more guards to this operation. 1010 * 1011 * <p> 1012 * Equivalent to calling: 1013 * <p class='bjava'> 1014 * <jv>builder</jv>.guards().append(<jv>value</jv>); 1015 * </p> 1016 * 1017 * @param value The values to add. 1018 * @return This object. 1019 */ 1020 @SafeVarargs 1021 public final Builder guards(Class<? extends RestGuard>...value) { 1022 guards().append(value); 1023 return this; 1024 } 1025 1026 /** 1027 * Adds one or more guards to this operation. 1028 * 1029 * <p> 1030 * Equivalent to calling: 1031 * <p class='bjava'> 1032 * <jv>builder</jv>.guards().append(<jv>value</jv>); 1033 * </p> 1034 * 1035 * @param value The values to add. 1036 * @return This object. 1037 */ 1038 public Builder guards(RestGuard...value) { 1039 guards().append(value); 1040 return this; 1041 } 1042 1043 /** 1044 * Instantiates the guard list sub-builder. 1045 * 1046 * <p> 1047 * Instantiates based on the following logic: 1048 * <ul> 1049 * <li>Looks for guards set via any of the following: 1050 * <ul> 1051 * <li>{@link RestOpContext.Builder#guards()}} 1052 * <li>{@link RestOp#guards()}. 1053 * <li>{@link Rest#guards()}. 1054 * </ul> 1055 * <li>Looks for a static or non-static <c>createGuards()</c> method that returns <c>{@link RestGuard}[]</c> on the 1056 * resource class with any of the following arguments: 1057 * <ul> 1058 * <li>{@link Method} - The Java method this context belongs to. 1059 * <li>{@link RestContext} 1060 * <li>{@link BeanStore} 1061 * <li>Any <a class="doclink" href="../../../../index.html#juneau-rest-server-springboot">injected beans</a>. 1062 * </ul> 1063 * <li>Resolves it via the bean store registered in this context. 1064 * <li>Instantiates a <c>RestGuard[0]</c>. 1065 * </ul> 1066 * 1067 * @param beanStore 1068 * The factory used for creating beans and retrieving injected beans. 1069 * @param resource 1070 * The REST servlet/bean instance that this context is defined against. 1071 * @return A new guard list sub-builder. 1072 */ 1073 protected RestGuardList.Builder createGuards(BeanStore beanStore, Supplier<?> resource) { 1074 1075 // Default value. 1076 Value<RestGuardList.Builder> v = Value.of( 1077 RestGuardList 1078 .create(beanStore) 1079 ); 1080 1081 // Specify the implementation class if its set as a default. 1082 defaultClasses() 1083 .get(RestGuardList.class) 1084 .ifPresent(x -> v.get().type(x)); 1085 1086 // Replace with bean from bean store. 1087 beanStore 1088 .getBean(RestGuardList.class) 1089 .ifPresent(x->v.get().impl(x)); 1090 1091 // Replace with bean from: @RestInject(methodScope="foo") public [static] RestGuardList xxx(<args>) 1092 beanStore 1093 .createMethodFinder(RestGuardList.class) 1094 .addBean(RestGuardList.Builder.class, v.get()) 1095 .find(this::matches) 1096 .run(x -> v.get().impl(x)); 1097 1098 return v.get(); 1099 } 1100 1101 RestGuardList getGuards() { 1102 RestGuardList.Builder b = guards(); 1103 Set<String> roleGuard = optional(this.roleGuard).orElseGet(CollectionUtils::set); 1104 1105 for (String rg : roleGuard) { 1106 try { 1107 b.append(new RoleBasedRestGuard(rolesDeclared, rg)); 1108 } catch (java.text.ParseException e1) { 1109 throw asRuntimeException(e1); 1110 } 1111 } 1112 1113 return guards.build(); 1114 } 1115 1116 //----------------------------------------------------------------------------------------------------------------- 1117 // matchers 1118 //----------------------------------------------------------------------------------------------------------------- 1119 1120 /** 1121 * Returns the matcher list sub-builder. 1122 * 1123 * @return The matcher list sub-builder. 1124 */ 1125 public RestMatcherList.Builder matchers() { 1126 if (matchers == null) 1127 matchers = createMatchers(beanStore(), resource()); 1128 return matchers; 1129 } 1130 1131 /** 1132 * Adds one or more matchers to this operation. 1133 * 1134 * <p> 1135 * Equivalent to calling: 1136 * <p class='bjava'> 1137 * <jv>builder</jv>.matchers().append(<jv>value</jv>); 1138 * </p> 1139 * 1140 * @param value The values to add. 1141 * @return This object. 1142 */ 1143 @SafeVarargs 1144 public final Builder matchers(Class<? extends RestMatcher>...value) { 1145 matchers().append(value); 1146 return this; 1147 } 1148 1149 /** 1150 * Adds one or more matchers to this operation. 1151 * 1152 * <p> 1153 * Equivalent to calling: 1154 * <p class='bjava'> 1155 * <jv>builder</jv>.matchers().append(<jv>value</jv>); 1156 * </p> 1157 * 1158 * @param value The values to add. 1159 * @return This object. 1160 */ 1161 public Builder matchers(RestMatcher...value) { 1162 matchers().append(value); 1163 return this; 1164 } 1165 1166 /** 1167 * Instantiates the matcher list sub-builder. 1168 * 1169 * <p> 1170 * Associates one or more {@link RestMatcher RestMatchers} with the specified method. 1171 * 1172 * <p> 1173 * If multiple matchers are specified, <b>ONE</b> matcher must pass. 1174 * <br>Note that this is different than guards where <b>ALL</b> guards needs to pass. 1175 * 1176 * <h5 class='section'>Notes:</h5><ul> 1177 * <li class='note'> 1178 * When defined as a class, the implementation must have one of the following constructors: 1179 * <ul> 1180 * <li><code><jk>public</jk> T(RestContext)</code> 1181 * <li><code><jk>public</jk> T()</code> 1182 * <li><code><jk>public static</jk> T <jsm>create</jsm>(RestContext)</code> 1183 * <li><code><jk>public static</jk> T <jsm>create</jsm>()</code> 1184 * </ul> 1185 * <li class='note'> 1186 * Inner classes of the REST resource class are allowed. 1187 * </ul> 1188 * 1189 * <h5 class='section'>See Also:</h5><ul> 1190 * <li class='ja'>{@link RestOp#matchers()} 1191 * <li class='ja'>{@link RestGet#matchers()} 1192 * <li class='ja'>{@link RestPut#matchers()} 1193 * <li class='ja'>{@link RestPost#matchers()} 1194 * <li class='ja'>{@link RestDelete#matchers()} 1195 * </ul> 1196 * 1197 * <p> 1198 * Instantiates based on the following logic: 1199 * <ul> 1200 * <li>Looks for matchers set via any of the following: 1201 * <ul> 1202 * <li>{@link RestOp#matchers()}. 1203 * </ul> 1204 * <li>Looks for a static or non-static <c>createMatchers()</c> method that returns <c>{@link RestMatcher}[]</c> on the 1205 * resource class with any of the following arguments: 1206 * <ul> 1207 * <li>{@link java.lang.reflect.Method} - The Java method this context belongs to. 1208 * <li>{@link RestContext} 1209 * <li>{@link BeanStore} 1210 * <li>Any <a class="doclink" href="../../../../index.html#juneau-rest-server-springboot">injected beans</a>. 1211 * </ul> 1212 * <li>Resolves it via the bean store registered in this context. 1213 * <li>Instantiates a <c>RestMatcher[0]</c>. 1214 * </ul> 1215 * 1216 * @param beanStore 1217 * The factory used for creating beans and retrieving injected beans. 1218 * @param resource 1219 * The REST servlet/bean instance that this context is defined against. 1220 * @return A new matcher list sub-builder. 1221 */ 1222 protected RestMatcherList.Builder createMatchers(BeanStore beanStore, Supplier<?> resource) { 1223 1224 // Default value. 1225 Value<RestMatcherList.Builder> v = Value.of( 1226 RestMatcherList 1227 .create(beanStore) 1228 ); 1229 1230 // Specify the implementation class if its set as a default. 1231 defaultClasses() 1232 .get(RestMatcherList.class) 1233 .ifPresent(x -> v.get().type(x)); 1234 1235 // Replace with bean from bean store. 1236 beanStore 1237 .getBean(RestMatcherList.class) 1238 .ifPresent(x->v.get().impl(x)); 1239 1240 // Replace with bean from: @RestInject(methodScope="foo") public [static] RestMatcherList xxx(<args>) 1241 beanStore 1242 .createMethodFinder(RestMatcherList.class) 1243 .addBean(RestMatcherList.Builder.class, v.get()) 1244 .find(this::matches) 1245 .run(x -> v.get().impl(x)); 1246 1247 return v.get(); 1248 } 1249 1250 RestMatcherList getMatchers(RestContext restContext) { 1251 RestMatcherList.Builder b = matchers(); 1252 if (clientVersion != null) 1253 b.append(new ClientVersionMatcher(restContext.getClientVersionHeader(), MethodInfo.of(restMethod))); 1254 1255 return b.build(); 1256 } 1257 1258 //----------------------------------------------------------------------------------------------------------------- 1259 // pathMatchers 1260 //----------------------------------------------------------------------------------------------------------------- 1261 1262 /** 1263 * Instantiates the path matchers for this method. 1264 * 1265 * @return The path matchers for this method. 1266 */ 1267 protected UrlPathMatcherList getPathMatchers() { 1268 1269 Value<UrlPathMatcherList> v = Value.of( 1270 UrlPathMatcherList.create() 1271 ); 1272 1273 if (path != null) { 1274 for (String p : path) { 1275 if (dotAll && ! p.endsWith("/*")) 1276 p += "/*"; 1277 v.get().add(UrlPathMatcher.of(p)); 1278 } 1279 } 1280 1281 if (v.get().isEmpty()) { 1282 MethodInfo mi = MethodInfo.of(restMethod); 1283 String p = null; 1284 String httpMethod = null; 1285 if (mi.hasAnnotation(RestGet.class)) 1286 httpMethod = "get"; 1287 else if (mi.hasAnnotation(RestPut.class)) 1288 httpMethod = "put"; 1289 else if (mi.hasAnnotation(RestPost.class)) 1290 httpMethod = "post"; 1291 else if (mi.hasAnnotation(RestDelete.class)) 1292 httpMethod = "delete"; 1293 else if (mi.hasAnnotation(RestOp.class)) { 1294 Value<String> _httpMethod = Value.empty(); 1295 mi.forEachAnnotation(RestOp.class, x -> isNotEmpty(x.method()), x -> _httpMethod.set(x.method())); 1296 httpMethod = _httpMethod.orElse(null); 1297 } 1298 1299 p = HttpUtils.detectHttpPath(restMethod, httpMethod); 1300 1301 if (dotAll && ! p.endsWith("/*")) 1302 p += "/*"; 1303 1304 v.get().add(UrlPathMatcher.of(p)); 1305 } 1306 1307 // Replace with bean from: @RestInject(methodScope="foo") public [static] UrlPathMatcherList xxx(<args>) 1308 beanStore 1309 .createMethodFinder(UrlPathMatcherList.class, resource().get()) 1310 .addBean(UrlPathMatcherList.class, v.get()) 1311 .find(this::matches) 1312 .run(x -> v.set(x)); 1313 1314 return v.get(); 1315 } 1316 1317 /** 1318 * When enabled, append <js>"/*"</js> to path patterns if not already present. 1319 * 1320 * @return This object. 1321 */ 1322 public Builder dotAll() { 1323 dotAll = true; 1324 return this; 1325 } 1326 1327 //----------------------------------------------------------------------------------------------------------------- 1328 // defaultRequestHeaders 1329 //----------------------------------------------------------------------------------------------------------------- 1330 1331 /** 1332 * Returns the default request headers. 1333 * 1334 * @return The default request headers. 1335 */ 1336 public HeaderList defaultRequestHeaders() { 1337 if (defaultRequestHeaders == null) 1338 defaultRequestHeaders = createDefaultRequestHeaders(beanStore(), parent, resource()); 1339 return defaultRequestHeaders; 1340 } 1341 1342 /** 1343 * Adds one or more default request headers to this operation. 1344 * 1345 * <p> 1346 * Equivalent to calling: 1347 * <p class='bjava'> 1348 * <jv>builder</jv>.defaultRequestHeaders().append(<jv>value</jv>); 1349 * </p> 1350 * 1351 * @param value The values to add. 1352 * @return This object. 1353 */ 1354 public Builder defaultRequestHeaders(org.apache.http.Header...value) { 1355 defaultRequestHeaders().append(value); 1356 return this; 1357 } 1358 1359 /** 1360 * Instantiates the default request headers. 1361 * 1362 * @param beanStore 1363 * The factory used for creating beans and retrieving injected beans. 1364 * @param parent 1365 * The builder for the REST resource class. 1366 * @param resource 1367 * The REST servlet/bean instance that this context is defined against. 1368 * @return A new default request headers sub-builder. 1369 */ 1370 protected HeaderList createDefaultRequestHeaders(BeanStore beanStore, RestContext.Builder parent, Supplier<?> resource) { 1371 1372 Value<HeaderList> v = Value.of( 1373 parent.defaultRequestHeaders().copy() 1374 ); 1375 1376 // Replace with bean from: @RestInject(name="defaultRequestHeaders",methodScope="foo") public [static] HeaderList xxx(<args>) 1377 BeanStore 1378 .of(beanStore, resource) 1379 .addBean(HeaderList.class, v.get()) 1380 .createMethodFinder(HeaderList.class, resource) 1381 .find(x -> matches(x, "defaultRequestHeaders")) 1382 .run(x -> v.set(x)); 1383 1384 return v.get(); 1385 } 1386 1387 //----------------------------------------------------------------------------------------------------------------- 1388 // defaultResponseHeaders 1389 //----------------------------------------------------------------------------------------------------------------- 1390 1391 /** 1392 * Returns the default response headers. 1393 * 1394 * @return The default response headers. 1395 */ 1396 public HeaderList defaultResponseHeaders() { 1397 if (defaultResponseHeaders == null) 1398 defaultResponseHeaders = createDefaultResponseHeaders(beanStore(), parent, resource()); 1399 return defaultResponseHeaders; 1400 } 1401 1402 /** 1403 * Adds one or more default response headers to this operation. 1404 * 1405 * <p> 1406 * Equivalent to calling: 1407 * <p class='bjava'> 1408 * <jv>builder</jv>.defaultResponseHeaders().append(<jv>value</jv>); 1409 * </p> 1410 * 1411 * @param value The values to add. 1412 * @return This object. 1413 */ 1414 public Builder defaultResponseHeaders(org.apache.http.Header...value) { 1415 defaultResponseHeaders().append(value); 1416 return this; 1417 } 1418 1419 /** 1420 * Instantiates the default response headers. 1421 * 1422 * @param beanStore 1423 * The factory used for creating beans and retrieving injected beans. 1424 * @param parent 1425 * The builder for the REST resource class. 1426 * @param resource 1427 * The REST servlet/bean instance that this context is defined against. 1428 * @return A new default response headers sub-builder. 1429 */ 1430 protected HeaderList createDefaultResponseHeaders(BeanStore beanStore, RestContext.Builder parent, Supplier<?> resource) { 1431 1432 Value<HeaderList> v = Value.of( 1433 parent.defaultResponseHeaders().copy() 1434 ); 1435 1436 // Replace with bean from: @RestInject(name="defaultResponseHeaders",methodScope="foo") public [static] HeaderList xxx(<args>) 1437 BeanStore 1438 .of(beanStore, resource) 1439 .addBean(HeaderList.class, v.get()) 1440 .createMethodFinder(HeaderList.class, resource) 1441 .find(x -> matches(x, "defaultResponseHeaders")) 1442 .run(x -> v.set(x)); 1443 1444 return v.get(); 1445 } 1446 1447 //----------------------------------------------------------------------------------------------------------------- 1448 // defaultRequestAttributes 1449 //----------------------------------------------------------------------------------------------------------------- 1450 1451 /** 1452 * Returns the default request attributes sub-builder. 1453 * 1454 * @return The default request attributes sub-builder. 1455 */ 1456 public NamedAttributeMap defaultRequestAttributes() { 1457 if (defaultRequestAttributes == null) 1458 defaultRequestAttributes = createDefaultRequestAttributes(beanStore(), parent, resource()); 1459 return defaultRequestAttributes; 1460 } 1461 1462 /** 1463 * Adds one or more default request attributes to this operation. 1464 * 1465 * <p> 1466 * Equivalent to calling: 1467 * <p class='bjava'> 1468 * <jv>builder</jv>.defaultRequestAttributes().append(<jv>value</jv>); 1469 * </p> 1470 * 1471 * @param value The values to add. 1472 * @return This object. 1473 */ 1474 public Builder defaultRequestAttributes(NamedAttribute...value) { 1475 defaultRequestAttributes().add(value); 1476 return this; 1477 } 1478 1479 /** 1480 * Instantiates the default request attributes sub-builder. 1481 * 1482 * @param beanStore 1483 * The factory used for creating beans and retrieving injected beans. 1484 * @param parent 1485 * The builder for the REST resource class. 1486 * @param resource 1487 * The REST servlet/bean instance that this context is defined against. 1488 * @return A new default request attributes sub-builder. 1489 */ 1490 protected NamedAttributeMap createDefaultRequestAttributes(BeanStore beanStore, RestContext.Builder parent, Supplier<?> resource) { 1491 1492 Value<NamedAttributeMap> v = Value.of( 1493 parent.defaultRequestAttributes().copy() 1494 ); 1495 1496 // Replace with bean from: @RestInject(name="defaultRequestAttributes",methodScope="foo") public [static] NamedAttributeMap xxx(<args>) 1497 BeanStore 1498 .of(beanStore, resource) 1499 .addBean(NamedAttributeMap.class, v.get()) 1500 .createMethodFinder(NamedAttributeMap.class, resource) 1501 .find(x -> matches(x, "defaultRequestAttributes")) 1502 .run(x -> v.set(x)); 1503 1504 return v.get(); 1505 } 1506 1507 //----------------------------------------------------------------------------------------------------------------- 1508 // defaultRequestQuery 1509 //----------------------------------------------------------------------------------------------------------------- 1510 1511 /** 1512 * Returns the default request query data. 1513 * 1514 * @return The default request query data. 1515 */ 1516 public PartList defaultRequestQueryData() { 1517 if (defaultRequestQueryData == null) 1518 defaultRequestQueryData = createDefaultRequestQueryData(beanStore(), parent, resource()); 1519 return defaultRequestQueryData; 1520 } 1521 1522 /** 1523 * Adds one or more default request query data to this operation. 1524 * 1525 * <p> 1526 * Equivalent to calling: 1527 * <p class='bjava'> 1528 * <jv>builder</jv>.defaultRequestQueryData().append(<jv>value</jv>); 1529 * </p> 1530 * 1531 * @param value The values to add. 1532 * @return This object. 1533 */ 1534 public Builder defaultRequestQueryData(NameValuePair...value) { 1535 defaultRequestQueryData().append(value); 1536 return this; 1537 } 1538 1539 /** 1540 * Instantiates the default request query data. 1541 * 1542 * @param beanStore 1543 * The factory used for creating beans and retrieving injected beans. 1544 * @param parent 1545 * The builder for the REST resource class. 1546 * @param resource 1547 * The REST servlet/bean instance that this context is defined against. 1548 * @return A new default request query data sub-builder. 1549 */ 1550 protected PartList createDefaultRequestQueryData(BeanStore beanStore, RestContext.Builder parent, Supplier<?> resource) { 1551 1552 Value<PartList> v = Value.of( 1553 PartList.create() 1554 ); 1555 1556 // Replace with bean from: @RestInject(name="defaultRequestQueryData",methodScope="foo") public [static] PartList xxx(<args>) 1557 BeanStore 1558 .of(beanStore, resource) 1559 .addBean(PartList.class, v.get()) 1560 .createMethodFinder(PartList.class, resource) 1561 .find(x -> matches(x, "defaultRequestQueryData")) 1562 .run(x -> v.set(x)); 1563 1564 return v.get(); 1565 } 1566 1567 //----------------------------------------------------------------------------------------------------------------- 1568 // defaultRequestFormData 1569 //----------------------------------------------------------------------------------------------------------------- 1570 1571 /** 1572 * Returns the default request form data. 1573 * 1574 * @return The default request form data. 1575 */ 1576 public PartList defaultRequestFormData() { 1577 if (defaultRequestFormData == null) 1578 defaultRequestFormData = createDefaultRequestFormData(beanStore(), parent, resource()); 1579 return defaultRequestFormData; 1580 } 1581 1582 /** 1583 * Adds one or more default request form data to this operation. 1584 * 1585 * <p> 1586 * Equivalent to calling: 1587 * <p class='bjava'> 1588 * <jv>builder</jv>.defaultRequestFormData().append(<jv>value</jv>); 1589 * </p> 1590 * 1591 * @param value The values to add. 1592 * @return This object. 1593 */ 1594 public Builder defaultRequestFormData(NameValuePair...value) { 1595 defaultRequestFormData().append(value); 1596 return this; 1597 } 1598 1599 /** 1600 * Instantiates the default request form data. 1601 * 1602 * @param beanStore 1603 * The factory used for creating beans and retrieving injected beans. 1604 * @param parent 1605 * The builder for the REST resource class. 1606 * @param resource 1607 * The REST servlet/bean instance that this context is defined against. 1608 * @return A new default request form data sub-builder. 1609 */ 1610 protected PartList createDefaultRequestFormData(BeanStore beanStore, RestContext.Builder parent, Supplier<?> resource) { 1611 1612 Value<PartList> v = Value.of( 1613 PartList.create() 1614 ); 1615 1616 // Replace with bean from: @RestInject(name="defaultRequestFormData",methodScope="foo") public [static] PartList xxx(<args>) 1617 BeanStore 1618 .of(beanStore, resource) 1619 .addBean(PartList.class, v.get()) 1620 .createMethodFinder(PartList.class, resource) 1621 .find(x -> matches(x, "defaultRequestFormData")) 1622 .run(x -> v.set(x)); 1623 1624 return v.get(); 1625 } 1626 1627 //----------------------------------------------------------------------------------------------------------------- 1628 // Parameter annotations 1629 //----------------------------------------------------------------------------------------------------------------- 1630 1631 /** 1632 * Handles processing of any annotations on parameters. 1633 * 1634 * <p> 1635 * This includes: {@link Header}, {@link Query}, {@link FormData}. 1636 */ 1637 protected void processParameterAnnotations() { 1638 for (Annotation[] aa : restMethod.getParameterAnnotations()) { 1639 1640 String def = null; 1641 for (Annotation a : aa) { 1642 if (a instanceof Schema) { 1643 Schema s = (Schema)a; 1644 def = joinnlFirstNonEmptyArray(s._default(), s.df()); 1645 } 1646 } 1647 1648 for (Annotation a : aa) { 1649 if (a instanceof Header) { 1650 Header h = (Header)a; 1651 if (def != null) { 1652 try { 1653 defaultRequestHeaders().set(basicHeader(firstNonEmpty(h.name(), h.value()), parseAnything(def))); 1654 } catch (ParseException e) { 1655 throw new ConfigException(e, "Malformed @Header annotation"); 1656 } 1657 } 1658 } 1659 if (a instanceof Query) { 1660 Query h = (Query)a; 1661 if (def != null) { 1662 try { 1663 defaultRequestQueryData().setDefault(basicPart(firstNonEmpty(h.name(), h.value()), parseAnything(def))); 1664 } catch (ParseException e) { 1665 throw new ConfigException(e, "Malformed @Query annotation"); 1666 } 1667 } 1668 } 1669 if (a instanceof FormData) { 1670 FormData h = (FormData)a; 1671 if (def != null) { 1672 try { 1673 defaultRequestFormData().setDefault(basicPart(firstNonEmpty(h.name(), h.value()), parseAnything(def))); 1674 } catch (ParseException e) { 1675 throw new ConfigException(e, "Malformed @FormData annotation"); 1676 } 1677 } 1678 } 1679 } 1680 } 1681 } 1682 1683 //---------------------------------------------------------------------------------------------------- 1684 // Properties 1685 //---------------------------------------------------------------------------------------------------- 1686 1687 /** 1688 * Client version pattern matcher. 1689 * 1690 * <p> 1691 * Specifies whether this method can be called based on the client version. 1692 * 1693 * <p> 1694 * The client version is identified via the HTTP request header identified by 1695 * {@link Rest#clientVersionHeader() @Rest(clientVersionHeader)} which by default is <js>"Client-Version"</js>. 1696 * 1697 * <p> 1698 * This is a specialized kind of {@link RestMatcher} that allows you to invoke different Java methods for the same 1699 * method/path based on the client version. 1700 * 1701 * <p> 1702 * The format of the client version range is similar to that of OSGi versions. 1703 * 1704 * <p> 1705 * In the following example, the Java methods are mapped to the same HTTP method and URL <js>"/foobar"</js>. 1706 * <p class='bjava'> 1707 * <jc>// Call this method if Client-Version is at least 2.0. 1708 * // Note that this also matches 2.0.1.</jc> 1709 * <ja>@RestGet</ja>(path=<js>"/foobar"</js>, clientVersion=<js>"2.0"</js>) 1710 * <jk>public</jk> Object method1() {...} 1711 * 1712 * <jc>// Call this method if Client-Version is at least 1.1, but less than 2.0.</jc> 1713 * <ja>@RestGet</ja>(path=<js>"/foobar"</js>, clientVersion=<js>"[1.1,2.0)"</js>) 1714 * <jk>public</jk> Object method2() {...} 1715 * 1716 * <jc>// Call this method if Client-Version is less than 1.1.</jc> 1717 * <ja>@RestGet</ja>(path=<js>"/foobar"</js>, clientVersion=<js>"[0,1.1)"</js>) 1718 * <jk>public</jk> Object method3() {...} 1719 * </p> 1720 * 1721 * <p> 1722 * It's common to combine the client version with transforms that will convert new POJOs into older POJOs for 1723 * backwards compatibility. 1724 * <p class='bjava'> 1725 * <jc>// Call this method if Client-Version is at least 2.0.</jc> 1726 * <ja>@RestGet</ja>(path=<js>"/foobar"</js>, clientVersion=<js>"2.0"</js>) 1727 * <jk>public</jk> NewPojo newMethod() {...} 1728 * 1729 * <jc>// Call this method if Client-Version is at least 1.1, but less than 2.0.</jc> 1730 * <ja>@RestGet</ja>(path=<js>"/foobar"</js>, clientVersion=<js>"[1.1,2.0)"</js>) 1731 * <ja>@BeanConfig(swaps=NewToOldSwap.<jk>class</jk>) 1732 * <jk>public</jk> NewPojo oldMethod() { 1733 * <jk>return</jk> newMethod(); 1734 * } 1735 * 1736 * <p> 1737 * Note that in the previous example, we're returning the exact same POJO, but using a transform to convert it into 1738 * an older form. 1739 * The old method could also just return back a completely different object. 1740 * The range can be any of the following: 1741 * <ul> 1742 * <li><js>"[0,1.0)"</js> = Less than 1.0. 1.0 and 1.0.0 does not match. 1743 * <li><js>"[0,1.0]"</js> = Less than or equal to 1.0. Note that 1.0.1 will match. 1744 * <li><js>"1.0"</js> = At least 1.0. 1.0 and 2.0 will match. 1745 * </ul> 1746 * 1747 * <h5 class='section'>See Also:</h5><ul> 1748 * <li class='ja'>{@link RestOp#clientVersion} 1749 * <li class='ja'>{@link RestGet#clientVersion} 1750 * <li class='ja'>{@link RestPut#clientVersion} 1751 * <li class='ja'>{@link RestPost#clientVersion} 1752 * <li class='ja'>{@link RestDelete#clientVersion} 1753 * <li class='jm'>{@link RestContext.Builder#clientVersionHeader(String)} 1754 * </ul> 1755 * 1756 * @param value The new value for this setting. 1757 * @return This object. 1758 */ 1759 @FluentSetter 1760 public Builder clientVersion(String value) { 1761 clientVersion = value; 1762 return this; 1763 } 1764 1765 /** 1766 * Debug mode. 1767 * 1768 * <p> 1769 * Enables the following: 1770 * <ul class='spaced-list'> 1771 * <li> 1772 * HTTP request/response bodies are cached in memory for logging purposes. 1773 * </ul> 1774 * 1775 * <p> 1776 * If not sppecified, the debug enablement is inherited from the class context. 1777 * 1778 * @param value The new value for this setting. 1779 * @return This object. 1780 */ 1781 @FluentSetter 1782 public Builder debug(Enablement value) { 1783 debug = value; 1784 return this; 1785 } 1786 1787 /** 1788 * Default character encoding. 1789 * 1790 * <p> 1791 * The default character encoding for the request and response if not specified on the request. 1792 * 1793 * <p> 1794 * This overrides the value defined on the {@link RestContext}. 1795 * 1796 * <h5 class='section'>See Also:</h5><ul> 1797 * <li class='jm'>{@link RestContext.Builder#defaultCharset(Charset)} 1798 * <li class='ja'>{@link Rest#defaultCharset} 1799 * <li class='ja'>{@link RestOp#defaultCharset} 1800 * </ul> 1801 * 1802 * @param value 1803 * The new value for this setting. 1804 * <br>The default is the first value found: 1805 * <ul> 1806 * <li>System property <js>"RestContext.defaultCharset" 1807 * <li>Environment variable <js>"RESTCONTEXT_defaultCharset" 1808 * <li><js>"utf-8"</js> 1809 * </ul> 1810 * @return This object. 1811 */ 1812 @FluentSetter 1813 public Builder defaultCharset(Charset value) { 1814 defaultCharset = value; 1815 return this; 1816 } 1817 1818 /** 1819 * HTTP method name. 1820 * 1821 * <p> 1822 * Typically <js>"GET"</js>, <js>"PUT"</js>, <js>"POST"</js>, <js>"DELETE"</js>, or <js>"OPTIONS"</js>. 1823 * 1824 * <p> 1825 * Method names are case-insensitive (always folded to upper-case). 1826 * 1827 * <p> 1828 * Note that you can use {@link org.apache.juneau.http.HttpMethod} for constant values. 1829 * 1830 * <p> 1831 * Besides the standard HTTP method names, the following can also be specified: 1832 * <ul class='spaced-list'> 1833 * <li> 1834 * <js>"*"</js> 1835 * - Denotes any method. 1836 * <br>Use this if you want to capture any HTTP methods in a single Java method. 1837 * <br>The {@link org.apache.juneau.rest.annotation.Method @Method} annotation and/or {@link RestRequest#getMethod()} method can be used to 1838 * distinguish the actual HTTP method name. 1839 * <li> 1840 * <js>""</js> 1841 * - Auto-detect. 1842 * <br>The method name is determined based on the Java method name. 1843 * <br>For example, if the method is <c>doPost(...)</c>, then the method name is automatically detected 1844 * as <js>"POST"</js>. 1845 * <br>Otherwise, defaults to <js>"GET"</js>. 1846 * <li> 1847 * <js>"RRPC"</js> 1848 * - Remote-proxy interface. 1849 * <br>This denotes a Java method that returns an object (usually an interface, often annotated with the 1850 * {@link Remote @Remote} annotation) to be used as a remote proxy using 1851 * <c>RestClient.getRemoteInterface(Class<T> interfaceClass, String url)</c>. 1852 * <br>This allows you to construct client-side interface proxies using REST as a transport medium. 1853 * <br>Conceptually, this is simply a fancy <c>POST</c> against the url <js>"/{path}/{javaMethodName}"</js> 1854 * where the arguments are marshalled from the client to the server as an HTTP content containing an array of 1855 * objects, passed to the method as arguments, and then the resulting object is marshalled back to the client. 1856 * <li> 1857 * Anything else 1858 * - Overloaded non-HTTP-standard names that are passed in through a <c>&method=methodName</c> URL 1859 * parameter. 1860 * </ul> 1861 * 1862 * <h5 class='section'>See Also:</h5><ul> 1863 * <li class='ja'>{@link RestOp#method()} 1864 * <li class='ja'>{@link RestGet} 1865 * <li class='ja'>{@link RestPut} 1866 * <li class='ja'>{@link RestPost} 1867 * <li class='ja'>{@link RestDelete} 1868 * </ul> 1869 * 1870 * @param value The new value for this setting. 1871 * @return This object. 1872 */ 1873 @FluentSetter 1874 public Builder httpMethod(String value) { 1875 this.httpMethod = value; 1876 return this; 1877 } 1878 1879 /** 1880 * The maximum allowed input size (in bytes) on HTTP requests. 1881 * 1882 * <p> 1883 * Useful for alleviating DoS attacks by throwing an exception when too much input is received instead of resulting 1884 * in out-of-memory errors which could affect system stability. 1885 * 1886 * <h5 class='section'>Example:</h5> 1887 * <p class='bjava'> 1888 * <jc>// Option #1 - Defined via annotation resolving to a config file setting with default value.</jc> 1889 * <ja>@Rest</ja>(maxInput=<js>"$C{REST/maxInput,10M}"</js>) 1890 * <jk>public class</jk> MyResource { 1891 * 1892 * <jc>// Option #2 - Defined via builder passed in through resource constructor.</jc> 1893 * <jk>public</jk> MyResource(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 1894 * 1895 * <jc>// Using method on builder.</jc> 1896 * <jv>builder</jv>.maxInput(<js>"10M"</js>); 1897 * } 1898 * 1899 * <jc>// Option #3 - Defined via builder passed in through init method.</jc> 1900 * <ja>@RestInit</ja> 1901 * <jk>public void</jk> init(RestContext.Builder <jv>builder</jv>) <jk>throws</jk> Exception { 1902 * <jv>builder</jv>.maxInput(<js>"10M"</js>); 1903 * } 1904 * 1905 * <jc>// Override at the method level.</jc> 1906 * <ja>@RestPost</ja>(maxInput=<js>"10M"</js>) 1907 * <jk>public</jk> Object myMethod() {...} 1908 * } 1909 * </p> 1910 * 1911 * <h5 class='section'>Notes:</h5><ul> 1912 * <li class='note'> 1913 * String value that gets resolved to a <jk>long</jk>. 1914 * <li class='note'> 1915 * Can be suffixed with any of the following representing kilobytes, megabytes, and gigabytes: 1916 * <js>'K'</js>, <js>'M'</js>, <js>'G'</js>. 1917 * <li class='note'> 1918 * A value of <js>"-1"</js> can be used to represent no limit. 1919 * </ul> 1920 * 1921 * <h5 class='section'>See Also:</h5><ul> 1922 * <li class='ja'>{@link Rest#maxInput} 1923 * <li class='ja'>{@link RestOp#maxInput} 1924 * <li class='jm'>{@link RestOpContext.Builder#maxInput(String)} 1925 * </ul> 1926 * 1927 * @param value 1928 * The new value for this setting. 1929 * <br>The default is the first value found: 1930 * <ul> 1931 * <li>System property <js>"RestContext.maxInput" 1932 * <li>Environment variable <js>"RESTCONTEXT_MAXINPUT" 1933 * <li><js>"100M"</js> 1934 * </ul> 1935 * <br>The default is <js>"100M"</js>. 1936 * @return This object. 1937 */ 1938 @FluentSetter 1939 public Builder maxInput(String value) { 1940 maxInput = StringUtils.parseLongWithSuffix(value); 1941 return this; 1942 } 1943 1944 /** 1945 * Resource method paths. 1946 * 1947 * <p> 1948 * Identifies the URL subpath relative to the servlet class. 1949 * 1950 * <p> 1951 * <h5 class='section'>Notes:</h5><ul> 1952 * <li class='note'> 1953 * This method is only applicable for Java methods. 1954 * <li class='note'> 1955 * Slashes are trimmed from the path ends. 1956 * <br>As a convention, you may want to start your path with <js>'/'</js> simple because it make it easier to read. 1957 * </ul> 1958 * 1959 * @param values The new values for this setting. 1960 * @return This object. 1961 */ 1962 @FluentSetter 1963 public Builder path(String...values) { 1964 path = prependAll(path, values); 1965 return this; 1966 } 1967 1968 /** 1969 * Supported accept media types. 1970 * 1971 * <p> 1972 * Overrides the media types inferred from the serializers that identify what media types can be produced by the resource. 1973 * <br>An example where this might be useful if you have serializers registered that handle media types that you 1974 * don't want exposed in the Swagger documentation. 1975 * 1976 * <p> 1977 * This affects the returned values from the following: 1978 * <ul class='javatree'> 1979 * <li class='jm'>{@link RestContext#getProduces() RestContext.getProduces()} 1980 * <li class='jm'>{@link SwaggerProvider#getSwagger(RestContext,Locale)} - Affects produces field. 1981 * </ul> 1982 * 1983 * <h5 class='section'>See Also:</h5><ul> 1984 * <li class='ja'>{@link Rest#produces} 1985 * <li class='ja'>{@link RestOp#produces} 1986 * <li class='ja'>{@link RestGet#produces} 1987 * <li class='ja'>{@link RestPut#produces} 1988 * <li class='ja'>{@link RestPost#produces} 1989 * </ul> 1990 * 1991 * @param values The values to add to this setting. 1992 * @return This object. 1993 */ 1994 @FluentSetter 1995 public Builder produces(MediaType...values) { 1996 produces = addAll(produces, values); 1997 return this; 1998 } 1999 2000 /** 2001 * Declared roles. 2002 * 2003 * <p> 2004 * A comma-delimited list of all possible user roles. 2005 * 2006 * <p> 2007 * Used in conjunction with {@link RestOpContext.Builder#roleGuard(String)} is used with patterns. 2008 * 2009 * <h5 class='section'>Example:</h5> 2010 * <p class='bjava'> 2011 * <ja>@Rest</ja>( 2012 * rolesDeclared=<js>"ROLE_ADMIN,ROLE_READ_WRITE,ROLE_READ_ONLY,ROLE_SPECIAL"</js>, 2013 * roleGuard=<js>"ROLE_ADMIN || (ROLE_READ_WRITE && ROLE_SPECIAL)"</js> 2014 * ) 2015 * <jk>public class</jk> MyResource <jk>extends</jk> BasicRestServlet { 2016 * ... 2017 * } 2018 * </p> 2019 * 2020 * <h5 class='section'>See Also:</h5><ul> 2021 * <li class='ja'>{@link Rest#rolesDeclared} 2022 * </ul> 2023 * 2024 * @param values The values to add to this setting. 2025 * @return This object. 2026 */ 2027 @FluentSetter 2028 public Builder rolesDeclared(String...values) { 2029 rolesDeclared = addAll(rolesDeclared, values); 2030 return this; 2031 } 2032 2033 /** 2034 * Role guard. 2035 * 2036 * <p> 2037 * An expression defining if a user with the specified roles are allowed to access methods on this class. 2038 * 2039 * <h5 class='section'>Example:</h5> 2040 * <p class='bjava'> 2041 * <ja>@Rest</ja>( 2042 * path=<js>"/foo"</js>, 2043 * roleGuard=<js>"ROLE_ADMIN || (ROLE_READ_WRITE && ROLE_SPECIAL)"</js> 2044 * ) 2045 * <jk>public class</jk> MyResource <jk>extends</jk> BasicRestServlet { 2046 * ... 2047 * } 2048 * </p> 2049 * 2050 * <h5 class='section'>Notes:</h5><ul> 2051 * <li class='note'> 2052 * Supports any of the following expression constructs: 2053 * <ul> 2054 * <li><js>"foo"</js> - Single arguments. 2055 * <li><js>"foo,bar,baz"</js> - Multiple OR'ed arguments. 2056 * <li><js>"foo | bar | bqz"</js> - Multiple OR'ed arguments, pipe syntax. 2057 * <li><js>"foo || bar || bqz"</js> - Multiple OR'ed arguments, Java-OR syntax. 2058 * <li><js>"fo*"</js> - Patterns including <js>'*'</js> and <js>'?'</js>. 2059 * <li><js>"fo* & *oo"</js> - Multiple AND'ed arguments, ampersand syntax. 2060 * <li><js>"fo* && *oo"</js> - Multiple AND'ed arguments, Java-AND syntax. 2061 * <li><js>"fo* || (*oo || bar)"</js> - Parenthesis. 2062 * </ul> 2063 * <li class='note'> 2064 * AND operations take precedence over OR operations (as expected). 2065 * <li class='note'> 2066 * Whitespace is ignored. 2067 * <li class='note'> 2068 * <jk>null</jk> or empty expressions always match as <jk>false</jk>. 2069 * <li class='note'> 2070 * If patterns are used, you must specify the list of declared roles using {@link Rest#rolesDeclared()} or {@link RestOpContext.Builder#rolesDeclared(String...)}. 2071 * <li class='note'> 2072 * Supports <a class="doclink" href="../../../../index.html#jrs.SvlVariables">SVL Variables</a> 2073 * (e.g. <js>"$L{my.localized.variable}"</js>). 2074 * </ul> 2075 * 2076 * @param value The values to add to this setting. 2077 * @return This object. 2078 */ 2079 @FluentSetter 2080 public Builder roleGuard(String value) { 2081 if (roleGuard == null) 2082 roleGuard = set(value); 2083 else 2084 roleGuard.add(value); 2085 return this; 2086 } 2087 2088 /** 2089 * Supported content media types. 2090 * 2091 * <p> 2092 * Overrides the media types inferred from the parsers that identify what media types can be consumed by the resource. 2093 * <br>An example where this might be useful if you have parsers registered that handle media types that you 2094 * don't want exposed in the Swagger documentation. 2095 * 2096 * <p> 2097 * This affects the returned values from the following: 2098 * <ul class='javatree'> 2099 * <li class='jm'>{@link RestContext#getConsumes() RestContext.getConsumes()} 2100 * </ul> 2101 * 2102 * <h5 class='section'>See Also:</h5><ul> 2103 * <li class='ja'>{@link Rest#consumes} 2104 * <li class='ja'>{@link RestOp#consumes} 2105 * <li class='ja'>{@link RestPut#consumes} 2106 * <li class='ja'>{@link RestPost#consumes} 2107 * </ul> 2108 * 2109 * @param values The values to add to this setting. 2110 * @return This object. 2111 */ 2112 @FluentSetter 2113 public Builder consumes(MediaType...values) { 2114 consumes = addAll(consumes, values); 2115 return this; 2116 } 2117 2118 // <FluentSetters> 2119 2120 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 2121 public Builder annotations(Annotation...values) { 2122 super.annotations(values); 2123 return this; 2124 } 2125 2126 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 2127 public Builder apply(AnnotationWorkList work) { 2128 super.apply(work); 2129 return this; 2130 } 2131 2132 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 2133 public Builder applyAnnotations(java.lang.Class<?>...fromClasses) { 2134 super.applyAnnotations(fromClasses); 2135 return this; 2136 } 2137 2138 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 2139 public Builder applyAnnotations(Method...fromMethods) { 2140 super.applyAnnotations(fromMethods); 2141 return this; 2142 } 2143 2144 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 2145 public Builder cache(Cache<HashKey,? extends org.apache.juneau.Context> value) { 2146 super.cache(value); 2147 return this; 2148 } 2149 2150 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 2151 public Builder debug() { 2152 super.debug(); 2153 return this; 2154 } 2155 2156 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 2157 public Builder debug(boolean value) { 2158 super.debug(value); 2159 return this; 2160 } 2161 2162 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 2163 public Builder impl(Context value) { 2164 super.impl(value); 2165 return this; 2166 } 2167 2168 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 2169 public Builder type(Class<? extends org.apache.juneau.Context> value) { 2170 super.type(value); 2171 return this; 2172 } 2173 2174 // </FluentSetters> 2175 2176 //----------------------------------------------------------------------------------------------------------------- 2177 // Helper methods. 2178 //----------------------------------------------------------------------------------------------------------------- 2179 2180 private boolean matches(MethodInfo annotated) { 2181 RestInject a = annotated.getAnnotation(RestInject.class); 2182 if (a != null) { 2183 for (String n : a.methodScope()) { 2184 if ("*".equals(n) || restMethod.getName().equals(n)) 2185 return true; 2186 } 2187 } 2188 return false; 2189 } 2190 2191 private boolean matches(MethodInfo annotated, String beanName) { 2192 RestInject a = annotated.getAnnotation(RestInject.class); 2193 if (a != null) { 2194 if (! a.name().equals(beanName)) 2195 return false; 2196 for (String n : a.methodScope()) { 2197 if ("*".equals(n) || restMethod.getName().equals(n)) 2198 return true; 2199 } 2200 } 2201 return false; 2202 } 2203 2204 private String joinnlFirstNonEmptyArray(String[]...s) { 2205 for (String[] ss : s) 2206 if (ss.length > 0) 2207 return joinnl(ss); 2208 return null; 2209 } 2210 2211 } 2212 2213 //------------------------------------------------------------------------------------------------------------------- 2214 // Instance 2215 //------------------------------------------------------------------------------------------------------------------- 2216 2217 private final String httpMethod; 2218 private final UrlPathMatcher[] pathMatchers; 2219 private final RestGuard[] guards; 2220 private final RestMatcher[] requiredMatchers, optionalMatchers; 2221 private final RestConverter[] converters; 2222 private final RestContext context; 2223 private final Method method; 2224 private final RestOpInvoker methodInvoker; 2225 private final RestOpInvoker[] 2226 preCallMethods, 2227 postCallMethods; 2228 private final MethodInfo mi; 2229 private final BeanContext beanContext; 2230 private final SerializerSet serializers; 2231 private final ParserSet parsers; 2232 private final EncoderSet encoders; 2233 private final HttpPartSerializer partSerializer; 2234 private final HttpPartParser partParser; 2235 private final JsonSchemaGenerator jsonSchemaGenerator; 2236 private final HeaderList defaultRequestHeaders, defaultResponseHeaders; 2237 private final PartList defaultRequestQueryData, defaultRequestFormData; 2238 private final NamedAttributeMap defaultRequestAttributes; 2239 private final Charset defaultCharset; 2240 private final long maxInput; 2241 private final List<MediaType> 2242 supportedAcceptTypes, 2243 supportedContentTypes; 2244 private final CallLogger callLogger; 2245 2246 private final Map<Class<?>,ResponseBeanMeta> responseBeanMetas = new ConcurrentHashMap<>(); 2247 private final Map<Class<?>,ResponsePartMeta> headerPartMetas = new ConcurrentHashMap<>(); 2248 private final ResponseBeanMeta responseMeta; 2249 private final int hierarchyDepth; 2250 private final DebugEnablement debug; 2251 2252 /** 2253 * Context constructor. 2254 * 2255 * @param builder The builder for this object. 2256 * @throws ServletException If context could not be created. 2257 */ 2258 protected RestOpContext(Builder builder) throws ServletException { 2259 super(builder); 2260 2261 try { 2262 context = builder.restContext; 2263 method = builder.restMethod; 2264 2265 if (builder.debug == null) 2266 debug = context.getDebugEnablement(); 2267 else 2268 debug = DebugEnablement.create(context.getBeanStore()).enable(builder.debug, "*").build(); 2269 2270 mi = MethodInfo.of(method).accessible(); 2271 Object r = context.getResource(); 2272 2273 BeanStore bs = BeanStore.of(context.getRootBeanStore(), r) 2274 .addBean(RestOpContext.class, this) 2275 .addBean(Method.class, method) 2276 .addBean(AnnotationWorkList.class, builder.getApplied()); 2277 bs.addBean(BeanStore.class, bs); 2278 2279 beanContext = bs.add(BeanContext.class, builder.getBeanContext().orElse(context.getBeanContext())); 2280 encoders = bs.add(EncoderSet.class, builder.getEncoders().orElse(context.getEncoders())); 2281 serializers = bs.add(SerializerSet.class, builder.getSerializers().orElse(context.getSerializers())); 2282 parsers = bs.add(ParserSet.class, builder.getParsers().orElse(context.getParsers())); 2283 partSerializer = bs.add(HttpPartSerializer.class, builder.getPartSerializer().orElse(context.getPartSerializer())); 2284 partParser = bs.add(HttpPartParser.class, builder.getPartParser().orElse(context.getPartParser())); 2285 jsonSchemaGenerator = bs.add(JsonSchemaGenerator.class, builder.getJsonSchemaGenerator().orElse(context.getJsonSchemaGenerator())); 2286 converters = bs.add(RestConverter[].class, builder.converters().build().asArray()); 2287 guards = bs.add(RestGuard[].class, builder.getGuards().asArray()); 2288 2289 RestMatcherList matchers = builder.getMatchers(context); 2290 optionalMatchers = matchers.getOptionalEntries(); 2291 requiredMatchers = matchers.getRequiredEntries(); 2292 2293 pathMatchers = bs.add(UrlPathMatcher[].class, builder.getPathMatchers().asArray()); 2294 bs.addBean(UrlPathMatcher.class, pathMatchers.length > 0 ? pathMatchers[0] : null); 2295 2296 supportedAcceptTypes = unmodifiable(builder.produces != null ? builder.produces : serializers.getSupportedMediaTypes()); 2297 supportedContentTypes = unmodifiable(builder.consumes != null ? builder.consumes : parsers.getSupportedMediaTypes()); 2298 2299 defaultRequestHeaders = builder.defaultRequestHeaders(); 2300 defaultResponseHeaders = builder.defaultResponseHeaders(); 2301 defaultRequestQueryData = builder.defaultRequestQueryData(); 2302 defaultRequestFormData = builder.defaultRequestFormData(); 2303 defaultRequestAttributes = builder.defaultRequestAttributes(); 2304 2305 int _hierarchyDepth = 0; 2306 Class<?> sc = method.getDeclaringClass().getSuperclass(); 2307 while (sc != null) { 2308 _hierarchyDepth++; 2309 sc = sc.getSuperclass(); 2310 } 2311 hierarchyDepth = _hierarchyDepth; 2312 2313 String _httpMethod = builder.httpMethod; 2314 if (_httpMethod == null) 2315 _httpMethod = HttpUtils.detectHttpMethod(method, true, "GET"); 2316 if ("METHOD".equals(_httpMethod)) 2317 _httpMethod = "*"; 2318 httpMethod = _httpMethod.toUpperCase(Locale.ENGLISH); 2319 2320 defaultCharset = builder.defaultCharset != null ? builder.defaultCharset : context.defaultCharset; 2321 maxInput = builder.maxInput != null ? builder.maxInput : context.maxInput; 2322 2323 responseMeta = ResponseBeanMeta.create(mi, builder.getApplied()); 2324 2325 preCallMethods = context.getPreCallMethods().stream().map(x -> new RestOpInvoker(x, context.findRestOperationArgs(x, bs), context.getMethodExecStats(x))).toArray(RestOpInvoker[]::new); 2326 postCallMethods = context.getPostCallMethods().stream().map(x -> new RestOpInvoker(x, context.findRestOperationArgs(x, bs), context.getMethodExecStats(x))).toArray(RestOpInvoker[]::new); 2327 methodInvoker = new RestOpInvoker(method, context.findRestOperationArgs(method, bs), context.getMethodExecStats(method)); 2328 2329 this.callLogger = context.getCallLogger(); 2330 } catch (Exception e) { 2331 throw new ServletException(e); 2332 } 2333 } 2334 2335 /** 2336 * Creates a new REST operation session. 2337 * 2338 * @param session The REST session. 2339 * @return A new REST operation session. 2340 * @throws Exception If op session could not be created. 2341 */ 2342 public RestOpSession.Builder createSession(RestSession session) throws Exception { 2343 return RestOpSession.create(this, session).logger(callLogger).debug(debug.isDebug(this, session.getRequest())); 2344 } 2345 2346 /** 2347 * Creates a {@link RestRequest} object based on the specified incoming {@link HttpServletRequest} object. 2348 * 2349 * @param session The current REST call. 2350 * @return The wrapped request object. 2351 * @throws Exception If any errors occur trying to interpret the request. 2352 */ 2353 public RestRequest createRequest(RestSession session) throws Exception { 2354 return new RestRequest(this, session); 2355 } 2356 2357 /** 2358 * Creates a {@link RestResponse} object based on the specified incoming {@link HttpServletResponse} object 2359 * and the request returned by {@link #createRequest(RestSession)}. 2360 * 2361 * @param session The current REST call. 2362 * @param req The REST request. 2363 * @return The wrapped response object. 2364 * @throws Exception If any errors occur trying to interpret the request or response. 2365 */ 2366 public RestResponse createResponse(RestSession session, RestRequest req) throws Exception { 2367 return new RestResponse(this, session, req); 2368 } 2369 2370 /** 2371 * Returns the bean context associated with this context. 2372 * 2373 * @return The bean context associated with this context. 2374 */ 2375 public BeanContext getBeanContext() { 2376 return beanContext; 2377 } 2378 2379 /** 2380 * Returns metadata about the specified response object if it's annotated with {@link Response @Response}. 2381 * 2382 * @param o The response POJO. 2383 * @return Metadata about the specified response object, or <jk>null</jk> if it's not annotated with {@link Response @Response}. 2384 */ 2385 public ResponseBeanMeta getResponseBeanMeta(Object o) { 2386 if (o == null) 2387 return null; 2388 Class<?> c = o.getClass(); 2389 ResponseBeanMeta rbm = responseBeanMetas.get(c); 2390 if (rbm == null) { 2391 rbm = ResponseBeanMeta.create(c, AnnotationWorkList.create()); 2392 if (rbm == null) 2393 rbm = ResponseBeanMeta.NULL; 2394 responseBeanMetas.put(c, rbm); 2395 } 2396 if (rbm == ResponseBeanMeta.NULL) 2397 return null; 2398 return rbm; 2399 } 2400 2401 /** 2402 * Returns metadata about the specified response object if it's annotated with {@link Header @Header}. 2403 * 2404 * @param o The response POJO. 2405 * @return Metadata about the specified response object, or <jk>null</jk> if it's not annotated with {@link Header @Header}. 2406 */ 2407 public ResponsePartMeta getResponseHeaderMeta(Object o) { 2408 if (o == null) 2409 return null; 2410 Class<?> c = o.getClass(); 2411 ResponsePartMeta pm = headerPartMetas.get(c); 2412 if (pm == null) { 2413 Header a = c.getAnnotation(Header.class); 2414 if (a != null) { 2415 HttpPartSchema schema = HttpPartSchema.create(a); 2416 HttpPartSerializer serializer = createPartSerializer(schema.getSerializer(), partSerializer); 2417 pm = new ResponsePartMeta(HEADER, schema, serializer); 2418 } 2419 if (pm == null) 2420 pm = ResponsePartMeta.NULL; 2421 headerPartMetas.put(c, pm); 2422 } 2423 if (pm == ResponsePartMeta.NULL) 2424 return null; 2425 return pm; 2426 } 2427 2428 /** 2429 * Returns the HTTP method name (e.g. <js>"GET"</js>). 2430 * 2431 * @return The HTTP method name. 2432 */ 2433 public String getHttpMethod() { 2434 return httpMethod; 2435 } 2436 2437 /** 2438 * Returns the path pattern for this method. 2439 * 2440 * @return The path pattern. 2441 */ 2442 public String getPathPattern() { 2443 return pathMatchers[0].toString(); 2444 } 2445 2446 /** 2447 * Returns the serializers to use for this method. 2448 * 2449 * @return The serializers to use for this method. 2450 */ 2451 public SerializerSet getSerializers() { 2452 return serializers; 2453 } 2454 2455 /** 2456 * Returns the parsers to use for this method. 2457 * 2458 * @return The parsers to use for this method. 2459 */ 2460 public ParserSet getParsers() { 2461 return parsers; 2462 } 2463 2464 /** 2465 * Returns the compression encoders to use for this method. 2466 * 2467 * @return The compression encoders to use for this method. 2468 */ 2469 public EncoderSet getEncoders() { 2470 return encoders; 2471 } 2472 2473 /** 2474 * Bean property getter: <property>partSerializer</property>. 2475 * 2476 * @return The value of the <property>partSerializer</property> property on this bean, or <jk>null</jk> if it is not set. 2477 */ 2478 public HttpPartSerializer getPartSerializer() { 2479 return partSerializer; 2480 } 2481 2482 /** 2483 * Bean property getter: <property>partParser</property>. 2484 * 2485 * @return The value of the <property>partParser</property> property on this bean, or <jk>null</jk> if it is not set. 2486 */ 2487 public HttpPartParser getPartParser() { 2488 return partParser; 2489 } 2490 2491 /** 2492 * Returns the JSON-Schema generator applicable to this Java method. 2493 * 2494 * @return The JSON-Schema generator applicable to this Java method. 2495 */ 2496 public JsonSchemaGenerator getJsonSchemaGenerator() { 2497 return jsonSchemaGenerator; 2498 } 2499 2500 /** 2501 * Returns the underlying Java method that this context belongs to. 2502 * 2503 * @return The underlying Java method that this context belongs to. 2504 */ 2505 public Method getJavaMethod() { 2506 return method; 2507 } 2508 2509 /** 2510 * Returns the default request headers. 2511 * 2512 * @return The default request headers. Never <jk>null</jk>. 2513 */ 2514 public HeaderList getDefaultRequestHeaders() { 2515 return defaultRequestHeaders; 2516 } 2517 2518 /** 2519 * Returns the default response headers. 2520 * 2521 * @return The default response headers. Never <jk>null</jk>. 2522 */ 2523 public HeaderList getDefaultResponseHeaders() { 2524 return defaultResponseHeaders; 2525 } 2526 2527 /** 2528 * Returns the default request query parameters. 2529 * 2530 * @return The default request query parameters. Never <jk>null</jk>. 2531 */ 2532 public PartList getDefaultRequestQueryData() { 2533 return defaultRequestQueryData; 2534 } 2535 2536 /** 2537 * Returns the default form data parameters. 2538 * 2539 * @return The default form data parameters. Never <jk>null</jk>. 2540 */ 2541 public PartList getDefaultRequestFormData() { 2542 return defaultRequestFormData; 2543 } 2544 2545 /** 2546 * Returns the default request attributes. 2547 * 2548 * @return The default request attributes. Never <jk>null</jk>. 2549 */ 2550 public NamedAttributeMap getDefaultRequestAttributes() { 2551 return defaultRequestAttributes; 2552 } 2553 2554 /** 2555 * Returns the default charset. 2556 * 2557 * @return The default charset. Never <jk>null</jk>. 2558 */ 2559 public Charset getDefaultCharset() { 2560 return defaultCharset; 2561 } 2562 2563 /** 2564 * Returns the max number of bytes to process in the input content. 2565 * 2566 * @return The max number of bytes to process in the input content. 2567 */ 2568 public long getMaxInput() { 2569 return maxInput; 2570 } 2571 2572 /** 2573 * Returns the list of supported content types. 2574 * 2575 * @return An unmodifiable list. 2576 */ 2577 public List<MediaType> getSupportedContentTypes() { 2578 return supportedContentTypes; 2579 } 2580 2581 /** 2582 * Returns a list of supported accept types. 2583 * 2584 * @return An unmodifiable list. 2585 */ 2586 public List<MediaType> getSupportedAcceptTypes() { 2587 return supportedAcceptTypes; 2588 } 2589 2590 /** 2591 * Returns the response bean meta if this method returns a {@link Response}-annotated bean. 2592 * 2593 * @return The response bean meta or <jk>null</jk> if it's not a {@link Response}-annotated bean. 2594 */ 2595 public ResponseBeanMeta getResponseMeta() { 2596 return responseMeta; 2597 } 2598 2599 /** 2600 * Identifies if this method can process the specified call. 2601 * 2602 * <p> 2603 * To process the call, the following must be true: 2604 * <ul> 2605 * <li>Path pattern must match. 2606 * <li>Matchers (if any) must match. 2607 * </ul> 2608 * 2609 * @param session The call to check. 2610 * @return 2611 * One of the following values: 2612 * <ul> 2613 * <li><c>0</c> - Path doesn't match. 2614 * <li><c>1</c> - Path matched but matchers did not. 2615 * <li><c>2</c> - Matches. 2616 * </ul> 2617 */ 2618 protected int match(RestSession session) { 2619 2620 UrlPathMatch pm = matchPattern(session); 2621 2622 if (pm == null) 2623 return 0; 2624 2625 if (requiredMatchers.length == 0 && optionalMatchers.length == 0) { 2626 session.urlPathMatch(pm); // Cache so we don't have to recalculate. 2627 return 2; 2628 } 2629 2630 try { 2631 HttpServletRequest req = session.getRequest(); 2632 2633 // If the method implements matchers, test them. 2634 for (RestMatcher m : requiredMatchers) 2635 if (! m.matches(req)) 2636 return 1; 2637 if (optionalMatchers.length > 0) { 2638 boolean matches = false; 2639 for (RestMatcher m : optionalMatchers) 2640 matches |= m.matches(req); 2641 if (! matches) 2642 return 1; 2643 } 2644 2645 session.urlPathMatch(pm); // Cache so we don't have to recalculate. 2646 return 2; 2647 } catch (Exception e) { 2648 throw new InternalServerError(e); 2649 } 2650 } 2651 2652 RestOpInvoker getMethodInvoker() { 2653 return methodInvoker; 2654 } 2655 2656 RestGuard[] getGuards() { 2657 return guards; 2658 } 2659 2660 RestConverter[] getConverters() { 2661 return converters; 2662 } 2663 2664 RestOpInvoker[] getPreCallMethods() { 2665 return preCallMethods; 2666 } 2667 2668 RestOpInvoker[] getPostCallMethods() { 2669 return postCallMethods; 2670 } 2671 2672 //----------------------------------------------------------------------------------------------------------------- 2673 // Other methods 2674 //----------------------------------------------------------------------------------------------------------------- 2675 2676 @Override /* Context */ 2677 public Context.Builder copy() { 2678 throw new UnsupportedOperationException("Method not implemented."); 2679 } 2680 2681 /* 2682 * compareTo() method is used to keep SimpleMethods ordered in the RestCallRouter list. 2683 * It maintains the order in which matches are made during requests. 2684 */ 2685 @Override /* Comparable */ 2686 public int compareTo(RestOpContext o) { 2687 int c; 2688 2689 for (int i = 0; i < Math.min(pathMatchers.length, o.pathMatchers.length); i++) { 2690 c = pathMatchers[i].compareTo(o.pathMatchers[i]); 2691 if (c != 0) 2692 return c; 2693 } 2694 2695 c = compare(o.hierarchyDepth, hierarchyDepth); 2696 if (c != 0) 2697 return c; 2698 2699 c = compare(o.requiredMatchers.length, requiredMatchers.length); 2700 if (c != 0) 2701 return c; 2702 2703 c = compare(o.optionalMatchers.length, optionalMatchers.length); 2704 if (c != 0) 2705 return c; 2706 2707 c = compare(o.guards.length, guards.length); 2708 2709 if (c != 0) 2710 return c; 2711 2712 c = compare(method.getName(), o.method.getName()); 2713 if (c != 0) 2714 return c; 2715 2716 c = compare(method.getParameterCount(), o.method.getParameterCount()); 2717 if (c != 0) 2718 return c; 2719 2720 for (int i = 0; i < method.getParameterCount(); i++) { 2721 c = compare(method.getParameterTypes()[i].getName(), o.method.getParameterTypes()[i].getName()); 2722 if (c != 0) 2723 return c; 2724 } 2725 2726 c = compare(method.getReturnType().getName(), o.method.getReturnType().getName()); 2727 if (c != 0) 2728 return c; 2729 2730 return 0; 2731 } 2732 2733 @Override /* Object */ 2734 public boolean equals(Object o) { 2735 return (o instanceof RestOpContext) && eq(this, (RestOpContext)o, (x,y)->x.method.equals(y.method)); 2736 } 2737 2738 @Override /* Object */ 2739 public int hashCode() { 2740 return method.hashCode(); 2741 } 2742 2743 @Override /* Context */ 2744 protected JsonMap properties() { 2745 return filteredMap() 2746 .append("defaultRequestFormData", defaultRequestFormData) 2747 .append("defaultRequestHeaders", defaultRequestHeaders) 2748 .append("defaultRequestQueryData", defaultRequestQueryData) 2749 .append("httpMethod", httpMethod); 2750 } 2751 2752 //----------------------------------------------------------------------------------------------------------------- 2753 // Helper methods. 2754 //----------------------------------------------------------------------------------------------------------------- 2755 2756 private static HttpPartSerializer createPartSerializer(Class<? extends HttpPartSerializer> c, HttpPartSerializer _default) { 2757 return BeanCreator.of(HttpPartSerializer.class).type(c).orElse(_default); 2758 } 2759 2760 private UrlPathMatch matchPattern(RestSession call) { 2761 UrlPathMatch pm = null; 2762 for (UrlPathMatcher pp : pathMatchers) 2763 if (pm == null) 2764 pm = pp.match(call.getUrlPath()); 2765 return pm; 2766 } 2767}