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