001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.rest.annotation; 014 015import static org.apache.juneau.internal.ArrayUtils.*; 016import java.lang.annotation.*; 017import java.nio.charset.*; 018import java.util.function.*; 019 020import org.apache.juneau.*; 021import org.apache.juneau.annotation.*; 022import org.apache.juneau.common.internal.*; 023import org.apache.juneau.encoders.*; 024import org.apache.juneau.http.HttpHeaders; 025import org.apache.juneau.http.HttpParts; 026import org.apache.juneau.reflect.*; 027import org.apache.juneau.rest.*; 028import org.apache.juneau.rest.converter.*; 029import org.apache.juneau.rest.guard.*; 030import org.apache.juneau.rest.httppart.*; 031import org.apache.juneau.rest.matcher.*; 032import org.apache.juneau.serializer.*; 033import org.apache.juneau.svl.*; 034 035/** 036 * Utility classes and methods for the {@link RestOp @RestOp} annotation. 037 * 038 * <h5 class='section'>See Also:</h5><ul> 039 * <li class='link'><a class="doclink" href="../../../../../index.html#jrs.RestOpAnnotatedMethods">@RestOp-Annotated Methods</a> 040 * </ul> 041 */ 042public class RestOpAnnotation { 043 044 //----------------------------------------------------------------------------------------------------------------- 045 // Static 046 //----------------------------------------------------------------------------------------------------------------- 047 048 /** Default value */ 049 public static final RestOp DEFAULT = create().build(); 050 051 /** 052 * Predicate that can be used with the {@link ClassInfo#getAnnotationList(Predicate)} and {@link MethodInfo#getAnnotationList(Predicate)} 053 */ 054 public static final Predicate<AnnotationInfo<?>> REST_OP_GROUP = x -> x.isInGroup(RestOp.class); 055 056 /** 057 * Instantiates a new builder for this class. 058 * 059 * @return A new builder object. 060 */ 061 public static Builder create() { 062 return new Builder(); 063 } 064 065 //----------------------------------------------------------------------------------------------------------------- 066 // Builder 067 //----------------------------------------------------------------------------------------------------------------- 068 069 /** 070 * Builder class. 071 * 072 * <h5 class='section'>See Also:</h5><ul> 073 * <li class='jm'>{@link org.apache.juneau.BeanContext.Builder#annotations(Annotation...)} 074 * </ul> 075 */ 076 @SuppressWarnings("unchecked") 077 public static class Builder extends TargetedAnnotationMBuilder { 078 079 Class<? extends RestConverter>[] converters = new Class[0]; 080 Class<? extends RestGuard>[] guards = new Class[0]; 081 Class<? extends RestMatcher>[] matchers = new Class[0]; 082 Class<? extends Encoder>[] encoders = new Class[0]; 083 Class<? extends Serializer>[] serializers = new Class[0]; 084 Class<?>[] parsers={}; 085 OpSwagger swagger = OpSwaggerAnnotation.DEFAULT; 086 String clientVersion="", debug="", defaultAccept="", defaultCharset="", defaultContentType="", maxInput="", method="", rolesDeclared="", roleGuard="", summary="", value=""; 087 String[] consumes={}, defaultRequestFormData={}, defaultRequestQueryData={}, defaultRequestAttributes={}, defaultRequestHeaders={}, defaultResponseHeaders={}, description={}, path={}, produces={}; 088 089 /** 090 * Constructor. 091 */ 092 protected Builder() { 093 super(RestOp.class); 094 } 095 096 /** 097 * Instantiates a new {@link RestOp @RestOp} object initialized with this builder. 098 * 099 * @return A new {@link RestOp @RestOp} object. 100 */ 101 public RestOp build() { 102 return new Impl(this); 103 } 104 105 /** 106 * Sets the {@link RestOp#clientVersion()} property on this annotation. 107 * 108 * @param value The new value for this property. 109 * @return This object. 110 */ 111 public Builder clientVersion(String value) { 112 this.clientVersion = value; 113 return this; 114 } 115 116 /** 117 * Sets the {@link RestOp#consumes()} property on this annotation. 118 * 119 * @param value The new value for this property. 120 * @return This object. 121 */ 122 public Builder consumes(String...value) { 123 this.consumes = value; 124 return this; 125 } 126 127 /** 128 * Sets the {@link RestOp#converters()} property on this annotation. 129 * 130 * @param value The new value for this property. 131 * @return This object. 132 */ 133 public Builder converters(Class<? extends RestConverter>...value) { 134 this.converters = value; 135 return this; 136 } 137 138 /** 139 * Sets the {@link RestOp#debug()} property on this annotation. 140 * 141 * @param value The new value for this property. 142 * @return This object. 143 */ 144 public Builder debug(String value) { 145 this.debug = value; 146 return this; 147 } 148 149 /** 150 * Sets the {@link RestOp#defaultAccept()} property on this annotation. 151 * 152 * @param value The new value for this property. 153 * @return This object. 154 */ 155 public Builder defaultAccept(String value) { 156 this.defaultAccept = value; 157 return this; 158 } 159 160 /** 161 * Sets the {@link RestOp#defaultCharset()} property on this annotation. 162 * 163 * @param value The new value for this property. 164 * @return This object. 165 */ 166 public Builder defaultCharset(String value) { 167 this.defaultCharset = value; 168 return this; 169 } 170 171 /** 172 * Sets the {@link RestOp#defaultContentType()} property on this annotation. 173 * 174 * @param value The new value for this property. 175 * @return This object. 176 */ 177 public Builder defaultContentType(String value) { 178 this.defaultContentType = value; 179 return this; 180 } 181 182 /** 183 * Sets the {@link RestOp#defaultRequestFormData()} property on this annotation. 184 * 185 * @param value The new value for this property. 186 * @return This object. 187 */ 188 public Builder defaultRequestFormData(String...value) { 189 this.defaultRequestFormData = value; 190 return this; 191 } 192 193 /** 194 * Sets the {@link RestOp#defaultRequestQueryData()} property on this annotation. 195 * 196 * @param value The new value for this property. 197 * @return This object. 198 */ 199 public Builder defaultRequestQueryData(String...value) { 200 this.defaultRequestQueryData = value; 201 return this; 202 } 203 204 /** 205 * Sets the {@link RestOp#defaultRequestAttributes()} property on this annotation. 206 * 207 * @param value The new value for this property. 208 * @return This object. 209 */ 210 public Builder defaultRequestAttributes(String...value) { 211 this.defaultRequestAttributes = value; 212 return this; 213 } 214 215 /** 216 * Sets the {@link RestOp#defaultRequestHeaders()} property on this annotation. 217 * 218 * @param value The new value for this property. 219 * @return This object. 220 */ 221 public Builder defaultRequestHeaders(String...value) { 222 this.defaultRequestHeaders = value; 223 return this; 224 } 225 226 /** 227 * Sets the {@link RestOp#defaultResponseHeaders()} property on this annotation. 228 * 229 * @param value The new value for this property. 230 * @return This object. 231 */ 232 public Builder defaultResponseHeaders(String...value) { 233 this.defaultResponseHeaders = value; 234 return this; 235 } 236 237 /** 238 * Sets the {@link RestOp#description()} property on this annotation. 239 * 240 * @param value The new value for this property. 241 * @return This object. 242 */ 243 public Builder description(String...value) { 244 this.description = value; 245 return this; 246 } 247 248 /** 249 * Sets the {@link RestOp#encoders()} property on this annotation. 250 * 251 * @param value The new value for this property. 252 * @return This object. 253 */ 254 public Builder encoders(Class<? extends Encoder>...value) { 255 this.encoders = value; 256 return this; 257 } 258 259 /** 260 * Sets the {@link RestOp#guards()} property on this annotation. 261 * 262 * @param value The new value for this property. 263 * @return This object. 264 */ 265 public Builder guards(Class<? extends RestGuard>...value) { 266 this.guards = value; 267 return this; 268 } 269 270 /** 271 * Sets the {@link RestOp#matchers()} property on this annotation. 272 * 273 * @param value The new value for this property. 274 * @return This object. 275 */ 276 public Builder matchers(Class<? extends RestMatcher>...value) { 277 this.matchers = value; 278 return this; 279 } 280 281 /** 282 * Sets the {@link RestOp#maxInput()} property on this annotation. 283 * 284 * @param value The new value for this property. 285 * @return This object. 286 */ 287 public Builder maxInput(String value) { 288 this.maxInput = value; 289 return this; 290 } 291 292 /** 293 * Sets the {@link RestOp#method()} property on this annotation. 294 * 295 * @param value The new value for this property. 296 * @return This object. 297 */ 298 public Builder method(String value) { 299 this.method = value; 300 return this; 301 } 302 303 /** 304 * Sets the {@link RestOp#parsers()} property on this annotation. 305 * 306 * @param value The new value for this property. 307 * @return This object. 308 */ 309 public Builder parsers(Class<?>...value) { 310 this.parsers = value; 311 return this; 312 } 313 314 /** 315 * Sets the {@link RestOp#path()} property on this annotation. 316 * 317 * @param value The new value for this property. 318 * @return This object. 319 */ 320 public Builder path(String...value) { 321 this.path = value; 322 return this; 323 } 324 325 /** 326 * Sets the {@link RestOp#produces()} property on this annotation. 327 * 328 * @param value The new value for this property. 329 * @return This object. 330 */ 331 public Builder produces(String...value) { 332 this.produces = value; 333 return this; 334 } 335 336 /** 337 * Sets the {@link RestOp#roleGuard()} property on this annotation. 338 * 339 * @param value The new value for this property. 340 * @return This object. 341 */ 342 public Builder roleGuard(String value) { 343 this.roleGuard = value; 344 return this; 345 } 346 347 /** 348 * Sets the {@link RestOp#rolesDeclared()} property on this annotation. 349 * 350 * @param value The new value for this property. 351 * @return This object. 352 */ 353 public Builder rolesDeclared(String value) { 354 this.rolesDeclared = value; 355 return this; 356 } 357 358 /** 359 * Sets the {@link RestOp#serializers()} property on this annotation. 360 * 361 * @param value The new value for this property. 362 * @return This object. 363 */ 364 public Builder serializers(Class<? extends Serializer>...value) { 365 this.serializers = value; 366 return this; 367 } 368 369 /** 370 * Sets the {@link RestOp#summary()} property on this annotation. 371 * 372 * @param value The new value for this property. 373 * @return This object. 374 */ 375 public Builder summary(String value) { 376 this.summary = value; 377 return this; 378 } 379 380 /** 381 * Sets the {@link RestOp#swagger()} property on this annotation. 382 * 383 * @param value The new value for this property. 384 * @return This object. 385 */ 386 public Builder swagger(OpSwagger value) { 387 this.swagger = value; 388 return this; 389 } 390 391 /** 392 * Sets the {@link RestOp#value()} property on this annotation. 393 * 394 * @param value The new value for this property. 395 * @return This object. 396 */ 397 public Builder value(String value) { 398 this.value = value; 399 return this; 400 } 401 402 // <FluentSetters> 403 404 @Override /* GENERATED - TargetedAnnotationBuilder */ 405 public Builder on(String...values) { 406 super.on(values); 407 return this; 408 } 409 410 @Override /* GENERATED - TargetedAnnotationTMBuilder */ 411 public Builder on(java.lang.reflect.Method...value) { 412 super.on(value); 413 return this; 414 } 415 416 // </FluentSetters> 417 } 418 419 //----------------------------------------------------------------------------------------------------------------- 420 // Implementation 421 //----------------------------------------------------------------------------------------------------------------- 422 423 private static class Impl extends TargetedAnnotationImpl implements RestOp { 424 425 private final Class<? extends RestConverter>[] converters; 426 private final Class<? extends RestGuard>[] guards; 427 private final Class<? extends RestMatcher>[] matchers; 428 private final Class<? extends Encoder>[] encoders; 429 private final Class<? extends Serializer>[] serializers; 430 private final Class<?>[] parsers; 431 private final OpSwagger swagger; 432 private final String clientVersion, debug, defaultAccept, defaultCharset, defaultContentType, maxInput, method, rolesDeclared, roleGuard, summary, value; 433 private final String[] consumes, defaultRequestFormData, defaultRequestQueryData, defaultRequestAttributes, defaultRequestHeaders, defaultResponseHeaders, description, path, produces; 434 435 Impl(Builder b) { 436 super(b); 437 this.clientVersion = b.clientVersion; 438 this.consumes = copyOf(b.consumes); 439 this.converters = copyOf(b.converters); 440 this.debug = b.debug; 441 this.defaultAccept = b.defaultAccept; 442 this.defaultCharset = b.defaultCharset; 443 this.defaultContentType = b.defaultContentType; 444 this.defaultRequestFormData = copyOf(b.defaultRequestFormData); 445 this.defaultRequestQueryData = copyOf(b.defaultRequestQueryData); 446 this.defaultRequestAttributes = copyOf(b.defaultRequestAttributes); 447 this.defaultRequestHeaders = copyOf(b.defaultRequestHeaders); 448 this.defaultResponseHeaders = copyOf(b.defaultResponseHeaders); 449 this.description = copyOf(b.description); 450 this.encoders = copyOf(b.encoders); 451 this.guards = copyOf(b.guards); 452 this.matchers = copyOf(b.matchers); 453 this.maxInput = b.maxInput; 454 this.method = b.method; 455 this.parsers = copyOf(b.parsers); 456 this.path = copyOf(b.path); 457 this.produces = copyOf(b.produces); 458 this.roleGuard = b.roleGuard; 459 this.rolesDeclared = b.rolesDeclared; 460 this.serializers = copyOf(b.serializers); 461 this.summary = b.summary; 462 this.swagger = b.swagger; 463 this.value = b.value; 464 postConstruct(); 465 } 466 467 @Override /* RestOp */ 468 public String clientVersion() { 469 return clientVersion; 470 } 471 472 @Override /* RestOp */ 473 public String[] consumes() { 474 return consumes; 475 } 476 477 @Override /* RestOp */ 478 public Class<? extends RestConverter>[] converters() { 479 return converters; 480 } 481 482 @Override /* RestOp */ 483 public String debug() { 484 return debug; 485 } 486 487 @Override /* RestOp */ 488 public String defaultAccept() { 489 return defaultAccept; 490 } 491 492 @Override /* RestOp */ 493 public String defaultCharset() { 494 return defaultCharset; 495 } 496 497 @Override /* RestOp */ 498 public String defaultContentType() { 499 return defaultContentType; 500 } 501 502 @Override /* RestOp */ 503 public String[] defaultRequestFormData() { 504 return defaultRequestFormData; 505 } 506 507 @Override /* RestOp */ 508 public String[] defaultRequestQueryData() { 509 return defaultRequestQueryData; 510 } 511 512 @Override /* RestOp */ 513 public String[] defaultRequestAttributes() { 514 return defaultRequestAttributes; 515 } 516 517 @Override /* RestOp */ 518 public String[] defaultRequestHeaders() { 519 return defaultRequestHeaders; 520 } 521 522 @Override /* RestOp */ 523 public String[] defaultResponseHeaders() { 524 return defaultResponseHeaders; 525 } 526 527 @Override /* RestOp */ 528 public String[] description() { 529 return description; 530 } 531 532 @Override /* RestOp */ 533 public Class<? extends Encoder>[] encoders() { 534 return encoders; 535 } 536 537 @Override /* RestOp */ 538 public Class<? extends RestGuard>[] guards() { 539 return guards; 540 } 541 542 @Override /* RestOp */ 543 public Class<? extends RestMatcher>[] matchers() { 544 return matchers; 545 } 546 547 @Override /* RestOp */ 548 public String maxInput() { 549 return maxInput; 550 } 551 552 @Override /* RestOp */ 553 public String method() { 554 return method; 555 } 556 557 @Override /* RestOp */ 558 public Class<?>[] parsers() { 559 return parsers; 560 } 561 562 @Override /* RestOp */ 563 public String[] path() { 564 return path; 565 } 566 567 @Override /* RestOp */ 568 public String[] produces() { 569 return produces; 570 } 571 572 @Override /* RestOp */ 573 public String roleGuard() { 574 return roleGuard; 575 } 576 577 @Override /* RestOp */ 578 public String rolesDeclared() { 579 return rolesDeclared; 580 } 581 582 @Override /* RestOp */ 583 public Class<? extends Serializer>[] serializers() { 584 return serializers; 585 } 586 587 @Override /* RestOp */ 588 public String summary() { 589 return summary; 590 } 591 592 @Override /* RestOp */ 593 public OpSwagger swagger() { 594 return swagger; 595 } 596 597 @Override /* RestOp */ 598 public String value() { 599 return value; 600 } 601 } 602 603 //----------------------------------------------------------------------------------------------------------------- 604 // Appliers 605 //----------------------------------------------------------------------------------------------------------------- 606 607 /** 608 * Applies {@link RestOp} annotations to a {@link org.apache.juneau.rest.RestOpContext.Builder}. 609 */ 610 public static class RestOpContextApply extends AnnotationApplier<RestOp,RestOpContext.Builder> { 611 612 /** 613 * Constructor. 614 * 615 * @param vr The resolver for resolving values in annotations. 616 */ 617 public RestOpContextApply(VarResolverSession vr) { 618 super(RestOp.class, RestOpContext.Builder.class, vr); 619 } 620 621 @Override 622 public void apply(AnnotationInfo<RestOp> ai, RestOpContext.Builder b) { 623 RestOp a = ai.inner(); 624 625 classes(a.serializers()).ifPresent(x -> b.serializers().set(x)); 626 classes(a.parsers()).ifPresent(x -> b.parsers().set(x)); 627 classes(a.encoders()).ifPresent(x -> b.encoders().set(x)); 628 stream(a.produces()).map(MediaType::of).forEach(x -> b.produces(x)); 629 stream(a.consumes()).map(MediaType::of).forEach(x -> b.consumes(x)); 630 stream(a.defaultRequestHeaders()).map(HttpHeaders::stringHeader).forEach(x -> b.defaultRequestHeaders().setDefault(x)); 631 stream(a.defaultResponseHeaders()).map(HttpHeaders::stringHeader).forEach(x -> b.defaultResponseHeaders().setDefault(x)); 632 stream(a.defaultRequestAttributes()).map(BasicNamedAttribute::ofPair).forEach(x -> b.defaultRequestAttributes().add(x)); 633 stream(a.defaultRequestQueryData()).map(HttpParts::basicPart).forEach(x -> b.defaultRequestQueryData().setDefault(x)); 634 stream(a.defaultRequestFormData()).map(HttpParts::basicPart).forEach(x -> b.defaultRequestFormData().setDefault(x)); 635 string(a.defaultAccept()).map(HttpHeaders::accept).ifPresent(x -> b.defaultRequestHeaders().setDefault(x)); 636 string(a.defaultContentType()).map(HttpHeaders::contentType).ifPresent(x -> b.defaultRequestHeaders().setDefault(x)); 637 b.converters().append(a.converters()); 638 b.guards().append(a.guards()); 639 b.matchers().append(a.matchers()); 640 string(a.clientVersion()).ifPresent(x -> b.clientVersion(x)); 641 string(a.defaultCharset()).map(Charset::forName).ifPresent(x -> b.defaultCharset(x)); 642 string(a.maxInput()).ifPresent(x -> b.maxInput(x)); 643 stream(a.path()).forEach(x -> b.path(x)); 644 cdl(a.rolesDeclared()).forEach(x -> b.rolesDeclared(x)); 645 string(a.roleGuard()).ifPresent(x -> b.roleGuard(x)); 646 647 string(a.method()).ifPresent(x -> b.httpMethod(x)); 648 string(a.debug()).map(Enablement::fromString).ifPresent(x -> b.debug(x)); 649 650 String v = StringUtils.trim(string(a.value()).orElse(null)); 651 if (v != null) { 652 int i = v.indexOf(' '); 653 if (i == -1) { 654 b.httpMethod(v); 655 } else { 656 b.httpMethod(v.substring(0, i).trim()); 657 b.path(v.substring(i).trim()); 658 } 659 } 660 } 661 } 662}