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}