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