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 */
017 package org.apache.camel.component.properties;
018
019 import java.util.ArrayList;
020 import java.util.List;
021 import java.util.Properties;
022
023 import org.apache.camel.util.StringHelper;
024 import org.slf4j.Logger;
025 import org.slf4j.LoggerFactory;
026
027 /**
028 * A parser to parse a string which contains property placeholders
029 *
030 * @version
031 */
032 public class DefaultPropertiesParser implements AugmentedPropertyNameAwarePropertiesParser {
033 protected final transient Logger log = LoggerFactory.getLogger(getClass());
034
035 @Override
036 public String parseUri(String text, Properties properties, String prefixToken, String suffixToken) throws IllegalArgumentException {
037 return parseUri(text, properties, prefixToken, suffixToken, null, null, false);
038 }
039
040 public String parseUri(String text, Properties properties, String prefixToken, String suffixToken,
041 String propertyPrefix, String propertySuffix, boolean fallbackToUnaugmentedProperty) throws IllegalArgumentException {
042 String answer = text;
043 boolean done = false;
044
045 // the placeholders can contain nested placeholders so we need to do recursive parsing
046 // we must therefore also do circular reference check and must keep a list of visited keys
047 List<String> visited = new ArrayList<String>();
048 while (!done) {
049 List<String> replaced = new ArrayList<String>();
050 answer = doParseUri(answer, properties, replaced, prefixToken, suffixToken, propertyPrefix, propertySuffix, fallbackToUnaugmentedProperty);
051
052 // check the replaced with the visited to avoid circular reference
053 for (String replace : replaced) {
054 if (visited.contains(replace)) {
055 throw new IllegalArgumentException("Circular reference detected with key [" + replace + "] from text: " + text);
056 }
057 }
058 // okay all okay so add the replaced as visited
059 visited.addAll(replaced);
060
061 // we are done when we can no longer find any prefix tokens in the answer
062 done = findTokenPosition(answer, 0, prefixToken) == -1;
063 }
064 return answer;
065 }
066
067 public String parseProperty(String key, String value, Properties properties) {
068 return value;
069 }
070
071 private String doParseUri(String uri, Properties properties, List<String> replaced, String prefixToken, String suffixToken,
072 String propertyPrefix, String propertySuffix, boolean fallbackToUnaugmentedProperty) {
073 StringBuilder sb = new StringBuilder();
074
075 int pivot = 0;
076 int size = uri.length();
077 while (pivot < size) {
078 int idx = findTokenPosition(uri, pivot, prefixToken);
079 if (idx < 0) {
080 sb.append(createConstantPart(uri, pivot, size));
081 break;
082 } else {
083 if (pivot < idx) {
084 sb.append(createConstantPart(uri, pivot, idx));
085 }
086 pivot = idx + prefixToken.length();
087 int endIdx = findTokenPosition(uri, pivot, suffixToken);
088 if (endIdx < 0) {
089 throw new IllegalArgumentException("Expecting " + suffixToken + " but found end of string from text: " + uri);
090 }
091 String key = uri.substring(pivot, endIdx);
092 String augmentedKey = key;
093
094 if (propertyPrefix != null) {
095 log.debug("Augmenting property key [{}] with prefix: {}", key, propertyPrefix);
096 augmentedKey = propertyPrefix + augmentedKey;
097 }
098
099 if (propertySuffix != null) {
100 log.debug("Augmenting property key [{}] with suffix: {}", key, propertySuffix);
101 augmentedKey = augmentedKey + propertySuffix;
102 }
103
104 String part = createPlaceholderPart(augmentedKey, properties, replaced);
105
106 // Note: Only fallback to unaugmented when the original key was actually augmented
107 if (part == null && fallbackToUnaugmentedProperty && (propertyPrefix != null || propertySuffix != null)) {
108 log.debug("Property wth key [{}] not found, attempting with unaugmented key: {}", augmentedKey, key);
109 part = createPlaceholderPart(key, properties, replaced);
110 }
111
112 if (part == null) {
113 StringBuilder esb = new StringBuilder();
114 esb.append("Property with key [").append(augmentedKey).append("] ");
115 if (fallbackToUnaugmentedProperty && (propertyPrefix != null || propertySuffix != null)) {
116 esb.append("(and original key [").append(key).append("]) ");
117 }
118 esb.append("not found in properties from text: ").append(uri);
119 throw new IllegalArgumentException(esb.toString());
120 }
121 sb.append(part);
122 pivot = endIdx + suffixToken.length();
123 }
124 }
125 return sb.toString();
126 }
127
128 private int findTokenPosition(String uri, int pivot, String token) {
129 int idx = uri.indexOf(token, pivot);
130 while (idx > 0) {
131 // grab part as the previous char + token + next char, to test if the token is quoted
132 String part = null;
133 int len = idx + token.length() + 1;
134 if (uri.length() >= len) {
135 part = uri.substring(idx - 1, len);
136 }
137 if (StringHelper.isQuoted(part)) {
138 // the token was quoted, so regard it as a literal
139 // and then try to find from next position
140 pivot = idx + token.length() + 1;
141 idx = uri.indexOf(token, pivot);
142 } else {
143 // found token
144 return idx;
145 }
146 }
147 return idx;
148 }
149
150 private String createConstantPart(String uri, int start, int end) {
151 return uri.substring(start, end);
152 }
153
154 private String createPlaceholderPart(String key, Properties properties, List<String> replaced) {
155 // keep track of which parts we have replaced
156 replaced.add(key);
157
158 String propertyValue = System.getProperty(key);
159 if (propertyValue != null) {
160 log.debug("Found a JVM system property: {} with value: {} to be used.", key, propertyValue);
161 } else if (properties != null) {
162 propertyValue = properties.getProperty(key);
163 }
164
165 return parseProperty(key, propertyValue, properties);
166 }
167
168 }