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}