001 /*
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements. See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership. The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License. You may obtain a copy of the License at
010 *
011 * http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied. See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 *
020 */
021 package org.apache.qpid.configuration;
022
023 import java.util.ArrayList;
024 import java.util.Iterator;
025
026 /**
027 * PropertyUtils provides helper methods for dealing with Java properties.
028 *
029 * <p/><table id="crc"><caption>CRC Card</caption>
030 * <tr><th> Responsibilities <th> Collaborations
031 * <tr><td> Expand system properties into strings with named expansions.
032 * </table>
033 *
034 * @todo Make the lookup method generic by passing in the properties to use for the expansion, rather than hard coding
035 * as system properties. The expansion code has greater potential for re-use that way.
036 *
037 * @todo Some more property related code could be added to this utils class, which might more appropriately reside under
038 * org.apache.qpid.util. For example standardised code to load properties from a resource name, currently found in
039 * QpidProperties and possibly other places could be moved here.
040 */
041 public class PropertyUtils
042 {
043 /**
044 * Given a string that contains substrings of the form <code>${xxx}</code>, looks up the valuea of 'xxx' as a
045 * system properties and substitutes tham back into the original string, to provide a property value expanded
046 * string.
047 *
048 * @param value The string to be scanned for property references. May be <code>null</code>, in which case this
049 * method returns immediately with no effect.
050 *
051 * @return The original string with the properties replaced, or <code>null</code> if the original string is
052 * <code>null</code>.
053 *
054 * @throws PropertyException If the string contains an opening <code>${</code> without a balancing <code>}</code>,
055 * or if the property to expand does not exist as a system property.
056 */
057 public static String replaceProperties(String value) throws PropertyException
058 {
059 if (value == null)
060 {
061 return null;
062 }
063
064 ArrayList<String> fragments = new ArrayList<String>();
065 ArrayList<String> propertyRefs = new ArrayList<String>();
066 parsePropertyString(value, fragments, propertyRefs);
067
068 StringBuffer sb = new StringBuffer();
069 Iterator j = propertyRefs.iterator();
070
071 for (String fragment : fragments)
072 {
073 if (fragment == null)
074 {
075 String propertyName = (String) j.next();
076
077 // try to get it from the project or keys
078 // Backward compatibility
079 String replacement = System.getProperty(propertyName);
080
081 if (replacement == null)
082 {
083 throw new PropertyException("Property ${" + propertyName + "} has not been set", null);
084 }
085
086 fragment = replacement;
087 }
088
089 sb.append(fragment);
090 }
091
092 return sb.toString();
093 }
094
095 /**
096 * Parses the supplied value for properties which are specified using ${foo} syntax. $X is left as is, and $$
097 * specifies a single $.
098 *
099 * @param value The property string to parse.
100 * @param fragments Is populated with the string fragments. A null means "insert a property value here. The number
101 * of nulls in the list when populated is equal to the size of the propertyRefs list.
102 * @param propertyRefs Populated with the property names to be added into the final string.
103 */
104 private static void parsePropertyString(String value, ArrayList<String> fragments, ArrayList<String> propertyRefs)
105 throws PropertyException
106 {
107 int prev = 0;
108 int pos;
109 // search for the next instance of $ from the 'prev' position
110 while ((pos = value.indexOf("$", prev)) >= 0)
111 {
112
113 // if there was any text before this, add it as a fragment
114 if (pos > 0)
115 {
116 fragments.add(value.substring(prev, pos));
117 }
118 // if we are at the end of the string, we tack on a $
119 // then move past it
120 if (pos == (value.length() - 1))
121 {
122 fragments.add("$");
123 prev = pos + 1;
124 }
125 else if (value.charAt(pos + 1) != '{')
126 {
127 // peek ahead to see if the next char is a property or not
128 // not a property: insert the char as a literal
129 if (value.charAt(pos + 1) == '$')
130 {
131 // two $ map to one $
132 fragments.add("$");
133 prev = pos + 2;
134 }
135 else
136 {
137 // $X maps to $X for all values of X!='$'
138 fragments.add(value.substring(pos, pos + 2));
139 prev = pos + 2;
140 }
141 }
142 else
143 {
144 // property found, extract its name or bail on a typo
145 int endName = value.indexOf('}', pos);
146 if (endName < 0)
147 {
148 throw new PropertyException("Syntax error in property: " + value, null);
149 }
150
151 String propertyName = value.substring(pos + 2, endName);
152 fragments.add(null);
153 propertyRefs.add(propertyName);
154 prev = endName + 1;
155 }
156 }
157 // no more $ signs found
158 // if there is any tail to the file, append it
159 if (prev < value.length())
160 {
161 fragments.add(value.substring(prev));
162 }
163 }
164 }
|