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}