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