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