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