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}