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    public Properties resolveProperties(CamelContext context, boolean ignoreMissingLocation, String... uri) throws Exception {
118        // return the spring properties, if it
119        Properties answer = new Properties();
120        for (String u : uri) {
121            String ref = "ref:" + id;
122            if (ref.equals(u)) {
123                answer.putAll(properties);
124            } else if (resolver != null) {
125                boolean flag = ignoreMissingLocation;
126                // Override the setting by using ignoreResourceNotFound
127                if (ignoreResourceNotFound != null) {
128                    flag = ignoreResourceNotFound;
129                }
130                Properties p = resolver.resolveProperties(context, flag, u);
131                if (p != null) {
132                    answer.putAll(p);
133                }
134            }
135        }
136        // must not return null
137        return answer;
138    }
139
140    @Override
141    public String parseUri(String text, Properties properties, String prefixToken, String suffixToken,
142                           String propertyPrefix, String propertySuffix, boolean fallbackToUnaugmentedProperty) throws IllegalArgumentException {
143
144        // first let Camel parse the text as it may contain Camel placeholders
145        String answer;
146        if (parser instanceof AugmentedPropertyNameAwarePropertiesParser) {
147            answer = ((AugmentedPropertyNameAwarePropertiesParser) parser).parseUri(text, properties, prefixToken, suffixToken,
148                    propertyPrefix, propertySuffix, fallbackToUnaugmentedProperty);
149        } else {
150            answer = parser.parseUri(text, properties, prefixToken, suffixToken);
151        }
152
153        // then let Spring parse it to resolve any Spring placeholders
154        if (answer != null) {
155            answer = springResolvePlaceholders(answer, properties);
156        } else {
157            answer = springResolvePlaceholders(text, properties);
158        }
159        return answer;
160    }
161
162    @Override
163    public String parseUri(String text, Properties properties, String prefixToken, String suffixToken) throws IllegalArgumentException {
164        String answer = parser.parseUri(text, properties, prefixToken, suffixToken);
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 parseProperty(String key, String value, Properties properties) {
175        String answer = parser.parseProperty(key, value, properties);
176        if (answer != null) {
177            answer = springResolvePlaceholders(answer, properties);
178        } else {
179            answer = springResolvePlaceholders(value, properties);
180        }
181        return answer;
182    }
183
184    /**
185     * Resolves the placeholders using Spring's property placeholder functionality.
186     *
187     * @param text   the text which may contain spring placeholders
188     * @param properties the properties
189     * @return the parsed text with replaced placeholders, or the original text as is
190     */
191    protected String springResolvePlaceholders(String text, Properties properties) {
192        return helper.replacePlaceholders(text, new BridgePropertyPlaceholderResolver(properties));
193    }
194
195    public void setResolver(PropertiesResolver resolver) {
196        this.resolver = resolver;
197    }
198
199    public void setParser(PropertiesParser parser) {
200        if (this.parser != null) {
201            // use a bridge if there is already a parser configured
202            this.parser = new BridgePropertiesParser(this.parser, parser);
203        } else {
204            this.parser = parser;
205        }
206    }
207
208    private class BridgePropertyPlaceholderResolver implements PropertyPlaceholderHelper.PlaceholderResolver {
209
210        private final Properties properties;
211
212        public BridgePropertyPlaceholderResolver(Properties properties) {
213            this.properties = properties;
214        }
215
216        public String resolvePlaceholder(String placeholderName) {
217            String propVal = null;
218            if (systemPropertiesMode  == SYSTEM_PROPERTIES_MODE_OVERRIDE) {
219                propVal = resolveSystemProperty(placeholderName);
220            }
221            if (propVal == null) {
222                propVal = (String) properties.get(placeholderName);
223            }
224            if (propVal == null && systemPropertiesMode == SYSTEM_PROPERTIES_MODE_FALLBACK) {
225                propVal = resolveSystemProperty(placeholderName);
226            }
227            return propVal;
228        }
229    }
230
231    private final class BridgePropertiesParser implements PropertiesParser {
232
233        private final PropertiesParser delegate;
234        private final PropertiesParser parser;
235
236        private BridgePropertiesParser(PropertiesParser delegate, PropertiesParser parser) {
237            this.delegate = delegate;
238            this.parser = parser;
239        }
240
241        @Override
242        public String parseUri(String text, Properties properties, String prefixToken, String suffixToken) throws IllegalArgumentException {
243            String answer = null;
244            if (delegate != null) {
245                answer = delegate.parseUri(text, properties, prefixToken, suffixToken);
246            }
247            if (answer != null) {
248                text = answer;
249            }
250            return parser.parseUri(text, properties, prefixToken, suffixToken);
251        }
252
253        @Override
254        public String parseProperty(String key, String value, Properties properties) {
255            String answer = null;
256            if (delegate != null) {
257                answer = delegate.parseProperty(key, value, properties);
258            }
259            if (answer != null) {
260                value = answer;
261            }
262            return parser.parseProperty(key, value, properties);
263        }
264    }
265
266}