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.rest.RestContext.*;
016import static org.apache.juneau.rest.util.RestUtils.*;
017
018import java.util.logging.*;
019
020import static org.apache.juneau.internal.StringUtils.*;
021import static org.apache.juneau.internal.ArrayUtils.*;
022
023import org.apache.juneau.*;
024import org.apache.juneau.collections.*;
025import org.apache.juneau.cp.*;
026import org.apache.juneau.httppart.*;
027import org.apache.juneau.internal.*;
028import org.apache.juneau.parser.*;
029import org.apache.juneau.reflect.*;
030import org.apache.juneau.rest.*;
031import org.apache.juneau.rest.annotation.AnnotationUtils;
032import org.apache.juneau.rest.annotation.Logging;
033import org.apache.juneau.rest.util.*;
034import org.apache.juneau.svl.*;
035import org.apache.juneau.utils.*;
036
037/**
038 * Applies {@link Rest} annotations to a {@link PropertyStoreBuilder}.
039 */
040public class RestConfigApply extends ConfigApply<Rest> {
041
042   /**
043    * Constructor.
044    *
045    * @param c The annotation class.
046    * @param r The resolver for resolving values in annotations.
047    */
048   public RestConfigApply(Class<Rest> c, VarResolverSession r) {
049      super(c, r);
050   }
051
052   @SuppressWarnings("deprecation")
053   @Override
054   public void apply(AnnotationInfo<Rest> ai, PropertyStoreBuilder psb) {
055      Rest a = ai.getAnnotation();
056      String s = null;
057      ClassInfo c = ai.getClassOn();
058
059      for (Property p1 : a.properties()) {
060         psb.set(p1.name(), string(p1.value()));  // >>> DEPRECATED - Remove in 9.0 <<<
061         psb.putTo(REST_properties, string(p1.name()), string(p1.value()));
062      }
063
064      for (String p1 : a.flags()) {
065         psb.set(p1, true);  // >>> DEPRECATED - Remove in 9.0 <<<
066         psb.putTo(REST_properties, string(p1), true);
067      }
068
069      if (a.serializers().length > 0)
070         psb.set(REST_serializers, merge(ConverterUtils.toType(psb.peek(REST_serializers), Object[].class), a.serializers()));
071
072      if (a.parsers().length > 0)
073         psb.set(REST_parsers, merge(ConverterUtils.toType(psb.peek(REST_parsers), Object[].class), a.parsers()));
074
075      if (a.partSerializer() != HttpPartSerializer.Null.class)
076         psb.set(REST_partSerializer, a.partSerializer());
077
078      if (a.partParser() != HttpPartParser.Null.class)
079         psb.set(REST_partParser, a.partParser());
080
081      psb.prependTo(REST_encoders, a.encoders());
082
083      if (a.produces().length > 0)
084         psb.set(REST_produces, strings(a.produces()));
085
086      if (a.consumes().length > 0)
087         psb.set(REST_consumes, strings(a.consumes()));
088
089      for (String ra : strings(a.attrs())) {
090         String[] ra2 = RestUtils.parseKeyValuePair(ra);
091         if (ra2 == null)
092            throw new BasicRuntimeException("Invalid default request attribute specified: ''{0}''.  Must be in the format: ''Name: value''", ra);
093         if (isNotEmpty(ra2[1]))
094            psb.putTo(REST_attrs, ra2[0], ra2[1]);
095      }
096
097      for (String ra : strings(a.reqAttrs())) {
098         String[] ra2 = RestUtils.parseKeyValuePair(ra);
099         if (ra2 == null)
100            throw new BasicRuntimeException("Invalid default request attribute specified: ''{0}''.  Must be in the format: ''Name: value''", ra);
101         if (isNotEmpty(ra2[1]))
102            psb.putTo(REST_reqAttrs, ra2[0], ra2[1]);
103      }
104
105      for (String header : strings(a.defaultRequestHeaders())) {
106         String[] h = RestUtils.parseHeader(header);
107         if (h == null)
108            throw new BasicRuntimeException("Invalid default request header specified: ''{0}''.  Must be in the format: ''Header-Name: header-value''", header);
109         if (isNotEmpty(h[1]))
110            psb.putTo(REST_defaultRequestHeaders, h[0], h[1]);
111      }
112
113      for (String header : strings(a.reqHeaders())) {
114         String[] h = RestUtils.parseHeader(header);
115         if (h == null)
116            throw new BasicRuntimeException("Invalid default request header specified: ''{0}''.  Must be in the format: ''Header-Name: header-value''", header);
117         if (isNotEmpty(h[1]))
118            psb.putTo(REST_reqHeaders, h[0], h[1]);
119      }
120
121      if (a.defaultAccept().length() > 0) {
122         s = string(a.defaultAccept());
123         if (isNotEmpty(s))
124            psb.putTo(REST_reqHeaders, "Accept", s);
125      }
126
127      if (a.defaultContentType().length() > 0) {
128         s = string(a.defaultContentType());
129         if (isNotEmpty(s))
130            psb.putTo(REST_reqHeaders, "Content-Type", s);
131
132      }
133
134      for (String header : strings(a.defaultResponseHeaders())) {
135         String[] h = parseHeader(header);
136         if (h == null)
137            throw new BasicRuntimeException("Invalid default response header specified: ''{0}''.  Must be in the format: ''Header-Name: header-value''", header);
138         if (isNotEmpty(h[1]))
139            psb.putTo(REST_defaultResponseHeaders, h[0], h[1]);
140      }
141
142      for (String header : strings(a.resHeaders())) {
143         String[] h = parseHeader(header);
144         if (h == null)
145            throw new BasicRuntimeException("Invalid default response header specified: ''{0}''.  Must be in the format: ''Header-Name: header-value''", header);
146         if (isNotEmpty(h[1]))
147            psb.putTo(REST_resHeaders, h[0], h[1]);
148      }
149
150      psb.prependTo(REST_responseHandlers, a.responseHandlers());
151
152      psb.prependTo(REST_converters, a.converters());
153
154      psb.prependTo(REST_guards, reverse(a.guards()));
155
156      psb.prependTo(REST_children, a.children());
157
158      psb.prependTo(REST_paramResolvers, a.paramResolvers());
159
160      Class<?> cc = a.context();
161      if (! cc.equals(RestContext.Null.class))
162         psb.set(REST_context, cc);
163
164      s = string(a.uriContext());
165      if (isNotEmpty(s))
166         psb.set(REST_uriContext, s);
167
168      s = string(a.uriAuthority());
169      if (isNotEmpty(s))
170         psb.set(REST_uriAuthority, s);
171
172      s = string(a.uriRelativity());
173      if (isNotEmpty(s))
174         psb.set(REST_uriRelativity, s);
175
176      s = string(a.uriResolution());
177      if (isNotEmpty(s))
178         psb.set(REST_uriResolution, s);
179
180      for (String mapping : a.staticFiles()) {
181         try {
182            for (StaticFileMapping sfm : StaticFileMapping.parse(c.inner(), string(mapping)).riterable())
183               psb.prependTo(REST_staticFiles, sfm);
184         } catch (ParseException e) {
185            throw new ConfigException(e, "Invalid @Resource(staticFiles) value on class ''{0}''", c);
186         }
187      }
188
189      psb.prependTo(REST_messages, Tuple2.of(c.inner(), string(a.messages())));
190
191      for (String header : strings(a.staticFileResponseHeaders())) {
192         String[] h = RestUtils.parseHeader(header);
193         if (h == null)
194            throw new BasicRuntimeException("Invalid static file response header specified: ''{0}''.  Must be in the format: ''Header-Name: header-value''", header);
195         if (isNotEmpty(h[1]))
196            psb.putTo(REST_staticFileResponseHeaders, h[0], h[1]);
197      }
198
199      if (! a.useClasspathResourceCaching().isEmpty())
200         psb.set(REST_useClasspathResourceCaching, bool(a.useClasspathResourceCaching()));
201
202      if (a.classpathResourceFinder() != ResourceFinder.Null.class)
203         psb.set(REST_classpathResourceFinder, a.classpathResourceFinder());
204
205      if (! a.path().isEmpty())
206         psb.set(REST_path, trimLeadingSlash(string(a.path())));
207
208      if (! a.clientVersionHeader().isEmpty())
209         psb.set(REST_clientVersionHeader, string(a.clientVersionHeader()));
210
211      if (a.resourceResolver() != RestResourceResolver.Null.class)
212         psb.set(REST_resourceResolver, a.resourceResolver());
213
214      if (a.callLogger() != RestCallLogger.Null.class)
215         psb.set(REST_callLogger, a.callLogger());
216
217      if (! AnnotationUtils.empty(a.logging())) {
218         Logging al = a.logging();
219         OMap m = new OMap(psb.peek(OMap.class, REST_callLoggerConfig));
220
221         if (! al.useStackTraceHashing().isEmpty())
222            m.append("useStackTraceHashing", bool(al.useStackTraceHashing()));
223
224         if (! al.stackTraceHashingTimeout().isEmpty())
225            m.append("stackTraceHashingTimeout", integer(al.stackTraceHashingTimeout(), "@Logging(stackTraceHashingTimeout)"));
226
227         if (! al.disabled().isEmpty())
228            m.append("disabled", enablement(al.disabled()));
229
230         if (! al.level().isEmpty())
231            m.append("level", level(al.level(), "@Logging(level)"));
232
233         if (al.rules().length > 0) {
234            OList ol = new OList();
235            for (LoggingRule a2 : al.rules()) {
236               OMap m2 = new OMap();
237
238               if (! a2.codes().isEmpty())
239                  m2.append("codes", string(a2.codes()));
240
241               if (! a2.exceptions().isEmpty())
242                  m2.append("exceptions", string(a2.exceptions()));
243
244               if (! a2.debugOnly().isEmpty())
245                   m2.append("debugOnly", bool(a2.debugOnly()));
246
247               if (! a2.level().isEmpty())
248                  m2.append("level", level(a2.level(), "@LoggingRule(level)"));
249
250               if (! a2.req().isEmpty())
251                  m2.append("req", string(a2.req()));
252
253               if (! a2.res().isEmpty())
254                  m2.append("res", string(a2.res()));
255
256               if (! a2.verbose().isEmpty())
257                  m2.append("verbose", bool(a2.verbose()));
258
259               if (! a2.disabled().isEmpty())
260                  m2.append("disabled", bool(a2.disabled()));
261
262               ol.add(m2);
263            }
264            m.put("rules", ol.appendAll(m.getList("rules")));
265         }
266
267         psb.set(REST_callLoggerConfig, m);
268      }
269
270      if (a.callHandler() != RestCallHandler.Null.class)
271         psb.set(REST_callHandler, a.callHandler());
272
273      if (a.infoProvider() != RestInfoProvider.Null.class)
274         psb.set(REST_infoProvider, a.infoProvider());
275
276      if (! a.allowBodyParam().isEmpty())
277         psb.set(REST_allowBodyParam, bool(a.allowBodyParam()));
278
279      if (! a.allowedHeaderParams().isEmpty())
280         psb.set(REST_allowedHeaderParams, string(a.allowedHeaderParams()));
281
282      if (! a.allowedMethodHeaders().isEmpty())
283         psb.set(REST_allowedMethodHeaders, string(a.allowedMethodHeaders()));
284
285      if (! a.allowedMethodParams().isEmpty())
286         psb.set(REST_allowedMethodParams, string(a.allowedMethodParams()));
287
288      if (! a.renderResponseStackTraces().isEmpty())
289         psb.set(REST_renderResponseStackTraces, bool(a.renderResponseStackTraces()));
290
291      if (! a.defaultCharset().isEmpty())
292         psb.set(REST_defaultCharset, string(a.defaultCharset()));
293
294      if (! a.maxInput().isEmpty())
295         psb.set(REST_maxInput, string(a.maxInput()));
296
297      if (! a.debug().isEmpty())
298         psb.set(REST_debug, string(a.debug()));
299
300      if (! a.debugOn().isEmpty())
301         psb.set(REST_debugOn, string(a.debugOn()));
302
303      psb.addTo(REST_mimeTypes, strings(a.mimeTypes()));
304
305      if (! a.rolesDeclared().isEmpty())
306         psb.addTo(REST_rolesDeclared, strings(a.rolesDeclared()));
307
308      if (! a.roleGuard().isEmpty())
309         psb.addTo(REST_roleGuard, string(a.roleGuard()));
310   }
311
312   private String trimLeadingSlash(String value) {
313      if (startsWith(value, '/'))
314         return value.substring(1);
315      return value;
316   }
317
318   private Enablement enablement(String in) {
319      return Enablement.fromString(string(in));
320   }
321
322   private Level level(String in, String loc) {
323      try {
324         return Level.parse(string(in).toUpperCase());
325      } catch (Exception e) {
326         throw new ConfigException("Invalid syntax for level on annotation @Rest({0}): {1}", loc, in);
327      }
328   }
329}