PropertyUtils.java
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 valuethrows 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 = (Stringj.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 }