001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.camel.component.properties; 018 019 import java.io.Serializable; 020 import java.util.ArrayList; 021 import java.util.Arrays; 022 import java.util.List; 023 import java.util.Map; 024 import java.util.Properties; 025 026 import org.apache.camel.Endpoint; 027 import org.apache.camel.impl.DefaultComponent; 028 import org.apache.camel.util.FilePathResolver; 029 import org.apache.camel.util.LRUSoftCache; 030 import org.apache.camel.util.ObjectHelper; 031 import org.slf4j.Logger; 032 import org.slf4j.LoggerFactory; 033 034 /** 035 * The <a href="http://camel.apache.org/properties">properties</a> component. 036 * 037 * @version 038 */ 039 public class PropertiesComponent extends DefaultComponent { 040 041 /** 042 * The default prefix token. 043 */ 044 public static final String DEFAULT_PREFIX_TOKEN = "{{"; 045 046 /** 047 * The default suffix token. 048 */ 049 public static final String DEFAULT_SUFFIX_TOKEN = "}}"; 050 051 /** 052 * The default prefix token. 053 * @deprecated Use {@link #DEFAULT_PREFIX_TOKEN} instead. 054 */ 055 @Deprecated 056 public static final String PREFIX_TOKEN = DEFAULT_PREFIX_TOKEN; 057 058 /** 059 * The default suffix token. 060 * @deprecated Use {@link #DEFAULT_SUFFIX_TOKEN} instead. 061 */ 062 @Deprecated 063 public static final String SUFFIX_TOKEN = DEFAULT_SUFFIX_TOKEN; 064 065 /** 066 * Key for stores special override properties that containers such as OSGi can store 067 * in the OSGi service registry 068 */ 069 public static final String OVERRIDE_PROPERTIES = PropertiesComponent.class.getName() + ".OverrideProperties"; 070 071 private static final Logger LOG = LoggerFactory.getLogger(PropertiesComponent.class); 072 private final Map<CacheKey, Properties> cacheMap = new LRUSoftCache<CacheKey, Properties>(1000); 073 private PropertiesResolver propertiesResolver = new DefaultPropertiesResolver(); 074 private PropertiesParser propertiesParser = new DefaultPropertiesParser(); 075 private String[] locations; 076 private boolean ignoreMissingLocation; 077 private boolean cache = true; 078 private String propertyPrefix; 079 private String propertySuffix; 080 private boolean fallbackToUnaugmentedProperty = true; 081 private String prefixToken = DEFAULT_PREFIX_TOKEN; 082 private String suffixToken = DEFAULT_SUFFIX_TOKEN; 083 private Properties overrideProperties; 084 085 public PropertiesComponent() { 086 } 087 088 public PropertiesComponent(String location) { 089 setLocation(location); 090 } 091 092 public PropertiesComponent(String... locations) { 093 setLocations(locations); 094 } 095 096 @Override 097 protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception { 098 String[] paths = locations; 099 100 // override default locations 101 String locations = getAndRemoveParameter(parameters, "locations", String.class); 102 Boolean ignoreMissingLocationLoc = getAndRemoveParameter(parameters, "ignoreMissingLocation", Boolean.class); 103 if (locations != null) { 104 LOG.trace("Overriding default locations with location: {}", locations); 105 paths = locations.split(","); 106 } 107 if (ignoreMissingLocationLoc != null) { 108 ignoreMissingLocation = ignoreMissingLocationLoc; 109 } 110 111 String endpointUri = parseUri(remaining, paths); 112 LOG.debug("Endpoint uri parsed as: {}", endpointUri); 113 return getCamelContext().getEndpoint(endpointUri); 114 } 115 116 public String parseUri(String uri) throws Exception { 117 return parseUri(uri, locations); 118 } 119 120 public String parseUri(String uri, String... paths) throws Exception { 121 Properties prop = null; 122 if (paths != null) { 123 // location may contain JVM system property or OS environment variables 124 // so we need to parse those 125 String[] locations = parseLocations(paths); 126 127 // check cache first 128 CacheKey key = new CacheKey(locations); 129 prop = cache ? cacheMap.get(key) : null; 130 if (prop == null) { 131 prop = propertiesResolver.resolveProperties(getCamelContext(), ignoreMissingLocation, locations); 132 if (cache) { 133 cacheMap.put(key, prop); 134 } 135 } 136 } 137 138 // use override properties 139 if (prop != null && overrideProperties != null) { 140 // make a copy to avoid affecting the original properties 141 Properties override = new Properties(); 142 override.putAll(prop); 143 override.putAll(overrideProperties); 144 prop = override; 145 } 146 147 // enclose tokens if missing 148 if (!uri.contains(prefixToken) && !uri.startsWith(prefixToken)) { 149 uri = prefixToken + uri; 150 } 151 if (!uri.contains(suffixToken) && !uri.endsWith(suffixToken)) { 152 uri = uri + suffixToken; 153 } 154 155 LOG.trace("Parsing uri {} with properties: {}", uri, prop); 156 157 if (propertiesParser instanceof AugmentedPropertyNameAwarePropertiesParser) { 158 return ((AugmentedPropertyNameAwarePropertiesParser) propertiesParser).parseUri(uri, prop, prefixToken, suffixToken, 159 propertyPrefix, propertySuffix, fallbackToUnaugmentedProperty); 160 } else { 161 return propertiesParser.parseUri(uri, prop, prefixToken, suffixToken); 162 } 163 } 164 165 public String[] getLocations() { 166 return locations; 167 } 168 169 public void setLocations(String[] locations) { 170 this.locations = locations; 171 } 172 173 public void setLocation(String location) { 174 setLocations(location.split(",")); 175 } 176 177 public PropertiesResolver getPropertiesResolver() { 178 return propertiesResolver; 179 } 180 181 public void setPropertiesResolver(PropertiesResolver propertiesResolver) { 182 this.propertiesResolver = propertiesResolver; 183 } 184 185 public PropertiesParser getPropertiesParser() { 186 return propertiesParser; 187 } 188 189 public void setPropertiesParser(PropertiesParser propertiesParser) { 190 this.propertiesParser = propertiesParser; 191 } 192 193 public boolean isCache() { 194 return cache; 195 } 196 197 public void setCache(boolean cache) { 198 this.cache = cache; 199 } 200 201 public String getPropertyPrefix() { 202 return propertyPrefix; 203 } 204 205 public void setPropertyPrefix(String propertyPrefix) { 206 this.propertyPrefix = propertyPrefix; 207 } 208 209 public String getPropertySuffix() { 210 return propertySuffix; 211 } 212 213 public void setPropertySuffix(String propertySuffix) { 214 this.propertySuffix = propertySuffix; 215 } 216 217 public boolean isFallbackToUnaugmentedProperty() { 218 return fallbackToUnaugmentedProperty; 219 } 220 221 public void setFallbackToUnaugmentedProperty(boolean fallbackToUnaugmentedProperty) { 222 this.fallbackToUnaugmentedProperty = fallbackToUnaugmentedProperty; 223 } 224 225 public boolean isIgnoreMissingLocation() { 226 return ignoreMissingLocation; 227 } 228 229 public void setIgnoreMissingLocation(boolean ignoreMissingLocation) { 230 this.ignoreMissingLocation = ignoreMissingLocation; 231 } 232 233 public String getPrefixToken() { 234 return prefixToken; 235 } 236 237 /** 238 * Sets the value of the prefix token used to identify properties to replace. Setting a value of 239 * {@code null} restores the default token (@link {@link #DEFAULT_PREFIX_TOKEN}). 240 */ 241 public void setPrefixToken(String prefixToken) { 242 if (prefixToken == null) { 243 this.prefixToken = DEFAULT_PREFIX_TOKEN; 244 } else { 245 this.prefixToken = prefixToken; 246 } 247 } 248 249 public String getSuffixToken() { 250 return suffixToken; 251 } 252 253 /** 254 * Sets the value of the suffix token used to identify properties to replace. Setting a value of 255 * {@code null} restores the default token (@link {@link #DEFAULT_SUFFIX_TOKEN}). 256 */ 257 public void setSuffixToken(String suffixToken) { 258 if (suffixToken == null) { 259 this.suffixToken = DEFAULT_SUFFIX_TOKEN; 260 } else { 261 this.suffixToken = suffixToken; 262 } 263 } 264 265 public Properties getOverrideProperties() { 266 return overrideProperties; 267 } 268 269 /** 270 * Sets a special list of override properties that take precedence 271 * and will use first, if a property exist. 272 * 273 * @param overrideProperties properties that is used first 274 */ 275 public void setOverrideProperties(Properties overrideProperties) { 276 this.overrideProperties = overrideProperties; 277 } 278 279 @Override 280 protected void doStop() throws Exception { 281 cacheMap.clear(); 282 super.doStop(); 283 } 284 285 private String[] parseLocations(String[] locations) { 286 List<String> answer = new ArrayList<String>(); 287 288 for (String location : locations) { 289 LOG.trace("Parsing location: {} ", location); 290 291 try { 292 location = FilePathResolver.resolvePath(location); 293 LOG.debug("Parsed location: {} ", location); 294 if (ObjectHelper.isNotEmpty(location)) { 295 answer.add(location); 296 } 297 } catch (IllegalArgumentException e) { 298 if (!ignoreMissingLocation) { 299 throw e; 300 } else { 301 LOG.debug("Ignored missing location: {}", location); 302 } 303 } 304 } 305 306 // must return a not-null answer 307 return answer.toArray(new String[answer.size()]); 308 } 309 310 /** 311 * Key used in the locations cache 312 */ 313 private static final class CacheKey implements Serializable { 314 private static final long serialVersionUID = 1L; 315 private final String[] locations; 316 317 private CacheKey(String[] locations) { 318 this.locations = locations; 319 } 320 321 @Override 322 public boolean equals(Object o) { 323 if (this == o) { 324 return true; 325 } 326 if (o == null || getClass() != o.getClass()) { 327 return false; 328 } 329 330 CacheKey that = (CacheKey) o; 331 332 if (!Arrays.equals(locations, that.locations)) { 333 return false; 334 } 335 336 return true; 337 } 338 339 @Override 340 public int hashCode() { 341 return locations != null ? Arrays.hashCode(locations) : 0; 342 } 343 344 @Override 345 public String toString() { 346 return "LocationKey[" + Arrays.asList(locations).toString() + "]"; 347 } 348 } 349 350 }