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