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.component.properties.PropertiesLookup;
022import org.apache.camel.component.properties.PropertiesParser;
023import org.apache.camel.spi.PropertiesSource;
024import org.springframework.beans.BeansException;
025import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
026import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
027import org.springframework.core.Constants;
028import org.springframework.util.PropertyPlaceholderHelper;
029
030/**
031 * A {@link PropertyPlaceholderConfigurer} that bridges Camel's
032 * <a href="http://camel.apache.org/using-propertyplaceholder.html"> property placeholder</a> with the Spring property
033 * placeholder mechanism.
034 */
035public class BridgePropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer
036        implements PropertiesParser, PropertiesSource {
037
038    // NOTE: this class must be in the spi package as if its in the root package, then Spring fails to parse the XML
039    // files due some weird spring issue. But that is okay as having this class in the spi package is fine anyway.
040
041    private final Properties properties = new Properties();
042    private PropertiesParser parser;
043    private PropertyPlaceholderHelper helper;
044    private int systemPropertiesMode = SYSTEM_PROPERTIES_MODE_FALLBACK;
045
046    @Override
047    protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
048            throws BeansException {
049        super.processProperties(beanFactoryToProcess, props);
050        // store all the spring properties so we can refer to them later
051        properties.putAll(props);
052        // create helper
053        helper = new PropertyPlaceholderHelper(
054                placeholderPrefix, placeholderSuffix, valueSeparator, ignoreUnresolvablePlaceholders);
055    }
056
057    public int getSystemPropertiesMode() {
058        return systemPropertiesMode;
059    }
060
061    @Override
062    public void setSystemPropertiesModeName(String constantName) throws IllegalArgumentException {
063        super.setSystemPropertiesModeName(constantName);
064        Constants constants = new Constants(PropertyPlaceholderConfigurer.class);
065        this.systemPropertiesMode = constants.asNumber(constantName).intValue();
066    }
067
068    @Override
069    public void setSystemPropertiesMode(int systemPropertiesMode) {
070        super.setSystemPropertiesMode(systemPropertiesMode);
071        this.systemPropertiesMode = systemPropertiesMode;
072    }
073
074    @Override
075    protected String resolvePlaceholder(String placeholder, Properties props) {
076        String value = props.getProperty(placeholder);
077        if (parser != null) {
078            // Just apply the parser to the place holder value to avoid configuring the other placeholder configure twice for the inside and outside camel context
079            return parser.parseProperty(placeholder, value, props::getProperty);
080        } else {
081            return value;
082        }
083    }
084
085    @Override
086    public String parseUri(String text, PropertiesLookup properties, boolean fallback) throws IllegalArgumentException {
087        // first let Camel parse the text as it may contain Camel placeholders
088        String answer = parser.parseUri(text, properties, fallback);
089
090        // then let Spring parse it to resolve any Spring placeholders
091        if (answer != null) {
092            answer = springResolvePlaceholders(answer, properties);
093        } else {
094            answer = springResolvePlaceholders(text, properties);
095        }
096        return answer;
097    }
098
099    @Override
100    public String parseProperty(String key, String value, PropertiesLookup properties) {
101        String answer = parser.parseProperty(key, value, properties);
102        if (answer != null) {
103            answer = springResolvePlaceholders(answer, properties);
104        } else {
105            answer = springResolvePlaceholders(value, properties);
106        }
107        return answer;
108    }
109
110    /**
111     * Resolves the placeholders using Spring's property placeholder functionality.
112     *
113     * @param  text       the text which may contain spring placeholders
114     * @param  properties the properties
115     * @return            the parsed text with replaced placeholders, or the original text as is
116     */
117    protected String springResolvePlaceholders(String text, PropertiesLookup properties) {
118        return helper.replacePlaceholders(text, new BridgePropertyPlaceholderResolver(properties));
119    }
120
121    public void setParser(PropertiesParser parser) {
122        if (this.parser != null) {
123            // use a bridge if there is already a parser configured
124            this.parser = new BridgePropertiesParser(this.parser, parser);
125        } else {
126            this.parser = parser;
127        }
128    }
129
130    @Override
131    public String getName() {
132        return "BridgePropertyPlaceholderConfigurer";
133    }
134
135    @Override
136    public String getProperty(String name) {
137        return properties.getProperty(name);
138    }
139
140    private class BridgePropertyPlaceholderResolver implements PropertyPlaceholderHelper.PlaceholderResolver {
141
142        private final PropertiesLookup properties;
143
144        BridgePropertyPlaceholderResolver(PropertiesLookup properties) {
145            this.properties = properties;
146        }
147
148        @Override
149        public String resolvePlaceholder(String placeholderName) {
150            String propVal = null;
151            if (systemPropertiesMode == SYSTEM_PROPERTIES_MODE_OVERRIDE) {
152                propVal = resolveSystemProperty(placeholderName);
153            }
154            if (propVal == null) {
155                propVal = properties.lookup(placeholderName);
156            }
157            if (propVal == null && systemPropertiesMode == SYSTEM_PROPERTIES_MODE_FALLBACK) {
158                propVal = resolveSystemProperty(placeholderName);
159            }
160            return propVal;
161        }
162    }
163
164    private final class BridgePropertiesParser implements PropertiesParser {
165
166        private final PropertiesParser delegate;
167        private final PropertiesParser parser;
168
169        private BridgePropertiesParser(PropertiesParser delegate, PropertiesParser parser) {
170            this.delegate = delegate;
171            this.parser = parser;
172        }
173
174        @Override
175        public String parseUri(String text, PropertiesLookup properties, boolean fallback) throws IllegalArgumentException {
176            String answer = null;
177            if (delegate != null) {
178                answer = delegate.parseUri(text, properties, fallback);
179            }
180            if (answer != null) {
181                text = answer;
182            }
183            return parser.parseUri(text, properties, fallback);
184        }
185
186        @Override
187        public String parseProperty(String key, String value, PropertiesLookup properties) {
188            String answer = null;
189            if (delegate != null) {
190                answer = delegate.parseProperty(key, value, properties);
191            }
192            if (answer != null) {
193                value = answer;
194            }
195            return parser.parseProperty(key, value, properties);
196        }
197    }
198
199}