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 */
017package org.apache.camel.spring.spi;
018
019import java.util.Properties;
020
021import org.apache.camel.CamelContext;
022import org.apache.camel.component.properties.AugmentedPropertyNameAwarePropertiesParser;
023import org.apache.camel.component.properties.PropertiesParser;
024import org.apache.camel.component.properties.PropertiesResolver;
025import org.springframework.beans.BeansException;
026import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
027import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
028import org.springframework.core.Constants;
029import org.springframework.util.PropertyPlaceholderHelper;
030
031/**
032 * A {@link PropertyPlaceholderConfigurer} that bridges Camel's <a href="http://camel.apache.org/using-propertyplaceholder.html">
033 * property placeholder</a> with the Spring property placeholder mechanism.
034 */
035public class BridgePropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer implements PropertiesResolver, AugmentedPropertyNameAwarePropertiesParser {
036
037    // NOTE: this class must be in the spi package as if its in the root package, then Spring fails to parse the XML
038    // files due some weird spring issue. But that is okay as having this class in the spi package is fine anyway.
039
040    private final Properties properties = new Properties();
041    private PropertiesResolver resolver;
042    private PropertiesParser parser;
043    private String id;
044    private PropertyPlaceholderHelper helper;
045
046    // to support both Spring 3.0 / 3.1+ we need to keep track of these as they have private modified in Spring 3.0
047    private String configuredPlaceholderPrefix;
048    private String configuredPlaceholderSuffix;
049    private String configuredValueSeparator;
050    private Boolean configuredIgnoreUnresolvablePlaceholders;
051    private int systemPropertiesMode = SYSTEM_PROPERTIES_MODE_FALLBACK;
052    private Boolean ignoreResourceNotFound;
053
054    @Override
055    protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props) throws BeansException {
056        super.processProperties(beanFactoryToProcess, props);
057        // store all the spring properties so we can refer to them later
058        properties.putAll(props);
059        // create helper
060        helper = new PropertyPlaceholderHelper(
061                configuredPlaceholderPrefix != null ? configuredPlaceholderPrefix : DEFAULT_PLACEHOLDER_PREFIX,
062                configuredPlaceholderSuffix != null ? configuredPlaceholderSuffix : DEFAULT_PLACEHOLDER_SUFFIX,
063                configuredValueSeparator != null ? configuredValueSeparator : DEFAULT_VALUE_SEPARATOR,
064                configuredIgnoreUnresolvablePlaceholders != null ? configuredIgnoreUnresolvablePlaceholders : false);
065    }
066
067    @Override
068    public void setBeanName(String beanName) {
069        this.id = beanName;
070        super.setBeanName(beanName);
071    }
072
073    @Override
074    public void setSystemPropertiesModeName(String constantName) throws IllegalArgumentException {
075        super.setSystemPropertiesModeName(constantName);
076        Constants constants = new Constants(PropertyPlaceholderConfigurer.class);
077        this.systemPropertiesMode = constants.asNumber(constantName).intValue();
078    }
079
080    @Override
081    public void setSystemPropertiesMode(int systemPropertiesMode) {
082        super.setSystemPropertiesMode(systemPropertiesMode);
083        this.systemPropertiesMode = systemPropertiesMode;
084    }
085
086    @Override
087    public void setPlaceholderPrefix(String placeholderPrefix) {
088        super.setPlaceholderPrefix(placeholderPrefix);
089        this.configuredPlaceholderPrefix = placeholderPrefix;
090    }
091
092    @Override
093    public void setPlaceholderSuffix(String placeholderSuffix) {
094        super.setPlaceholderSuffix(placeholderSuffix);
095        this.configuredPlaceholderSuffix = placeholderSuffix;
096    }
097
098    @Override
099    public void setValueSeparator(String valueSeparator) {
100        super.setValueSeparator(valueSeparator);
101        this.configuredValueSeparator = valueSeparator;
102    }
103
104    @Override
105    public void setIgnoreUnresolvablePlaceholders(boolean ignoreUnresolvablePlaceholders) {
106        super.setIgnoreUnresolvablePlaceholders(ignoreUnresolvablePlaceholders);
107        this.configuredIgnoreUnresolvablePlaceholders = ignoreUnresolvablePlaceholders;
108    }
109    
110    @Override
111    public void setIgnoreResourceNotFound(boolean ignoreResourceNotFound) {
112        super.setIgnoreResourceNotFound(ignoreResourceNotFound);
113        this.ignoreResourceNotFound = ignoreResourceNotFound;
114    }
115    
116    @Override
117    protected String resolvePlaceholder(String placeholder, Properties props) {
118        String value = props.getProperty(placeholder);
119        if (parser != null) {
120            // Just apply the parser to the place holder value to avoid configuring the other placeholder configure twice for the inside and outside camel context
121            return parser.parseProperty(placeholder, value, props);
122        } else {
123            return value;
124        }
125    }
126
127    @Override
128    public Properties resolveProperties(CamelContext context, boolean ignoreMissingLocation, String... uri) throws Exception {
129        // return the spring properties, if it
130        Properties answer = new Properties();
131        for (String u : uri) {
132            String ref = "ref:" + id;
133            if (ref.equals(u)) {
134                answer.putAll(properties);
135            } else if (resolver != null) {
136                boolean flag = ignoreMissingLocation;
137                // Override the setting by using ignoreResourceNotFound
138                if (ignoreResourceNotFound != null) {
139                    flag = ignoreResourceNotFound;
140                }
141                Properties p = resolver.resolveProperties(context, flag, u);
142                if (p != null) {
143                    answer.putAll(p);
144                }
145            }
146        }
147        // must not return null
148        return answer;
149    }
150
151    @Override
152    public String parseUri(String text, Properties properties, String prefixToken, String suffixToken,
153                           String propertyPrefix, String propertySuffix, boolean fallbackToUnaugmentedProperty) throws IllegalArgumentException {
154
155        // first let Camel parse the text as it may contain Camel placeholders
156        String answer;
157        if (parser instanceof AugmentedPropertyNameAwarePropertiesParser) {
158            answer = ((AugmentedPropertyNameAwarePropertiesParser) parser).parseUri(text, properties, prefixToken, suffixToken,
159                    propertyPrefix, propertySuffix, fallbackToUnaugmentedProperty);
160        } else {
161            answer = parser.parseUri(text, properties, prefixToken, suffixToken);
162        }
163
164        // then let Spring parse it to resolve any Spring placeholders
165        if (answer != null) {
166            answer = springResolvePlaceholders(answer, properties);
167        } else {
168            answer = springResolvePlaceholders(text, properties);
169        }
170        return answer;
171    }
172
173    @Override
174    public String parseUri(String text, Properties properties, String prefixToken, String suffixToken) throws IllegalArgumentException {
175        String answer = parser.parseUri(text, properties, prefixToken, suffixToken);
176        if (answer != null) {
177            answer = springResolvePlaceholders(answer, properties);
178        } else {
179            answer = springResolvePlaceholders(text, properties);
180        }
181        return answer;
182    }
183
184    @Override
185    public String parseProperty(String key, String value, Properties properties) {
186        String answer = parser.parseProperty(key, value, properties);
187        if (answer != null) {
188            answer = springResolvePlaceholders(answer, properties);
189        } else {
190            answer = springResolvePlaceholders(value, properties);
191        }
192        return answer;
193    }
194
195    /**
196     * Resolves the placeholders using Spring's property placeholder functionality.
197     *
198     * @param text   the text which may contain spring placeholders
199     * @param properties the properties
200     * @return the parsed text with replaced placeholders, or the original text as is
201     */
202    protected String springResolvePlaceholders(String text, Properties properties) {
203        return helper.replacePlaceholders(text, new BridgePropertyPlaceholderResolver(properties));
204    }
205
206    public void setResolver(PropertiesResolver resolver) {
207        this.resolver = resolver;
208    }
209
210    public void setParser(PropertiesParser parser) {
211        if (this.parser != null) {
212            // use a bridge if there is already a parser configured
213            this.parser = new BridgePropertiesParser(this.parser, parser);
214        } else {
215            this.parser = parser;
216        }
217    }
218
219    private class BridgePropertyPlaceholderResolver implements PropertyPlaceholderHelper.PlaceholderResolver {
220
221        private final Properties properties;
222
223        public BridgePropertyPlaceholderResolver(Properties properties) {
224            this.properties = properties;
225        }
226
227        public String resolvePlaceholder(String placeholderName) {
228            String propVal = null;
229            if (systemPropertiesMode  == SYSTEM_PROPERTIES_MODE_OVERRIDE) {
230                propVal = resolveSystemProperty(placeholderName);
231            }
232            if (propVal == null) {
233                propVal = (String) properties.get(placeholderName);
234            }
235            if (propVal == null && systemPropertiesMode == SYSTEM_PROPERTIES_MODE_FALLBACK) {
236                propVal = resolveSystemProperty(placeholderName);
237            }
238            return propVal;
239        }
240    }
241
242    private final class BridgePropertiesParser implements PropertiesParser {
243
244        private final PropertiesParser delegate;
245        private final PropertiesParser parser;
246
247        private BridgePropertiesParser(PropertiesParser delegate, PropertiesParser parser) {
248            this.delegate = delegate;
249            this.parser = parser;
250        }
251
252        @Override
253        public String parseUri(String text, Properties properties, String prefixToken, String suffixToken) throws IllegalArgumentException {
254            String answer = null;
255            if (delegate != null) {
256                answer = delegate.parseUri(text, properties, prefixToken, suffixToken);
257            }
258            if (answer != null) {
259                text = answer;
260            }
261            return parser.parseUri(text, properties, prefixToken, suffixToken);
262        }
263
264        @Override
265        public String parseProperty(String key, String value, Properties properties) {
266            String answer = null;
267            if (delegate != null) {
268                answer = delegate.parseProperty(key, value, properties);
269            }
270            if (answer != null) {
271                value = answer;
272            }
273            return parser.parseProperty(key, value, properties);
274        }
275    }
276
277}