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