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 }