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