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