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