View Javadoc
1 /* 2 * ==================================================================== 3 * License: 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 1. Redistributions of source code must retain the above copyright notice, 8 * this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. The end-user documentation included with the redistribution, if any, 13 * must include the following acknowledgment: "This product includes software 14 * developed by Robert Half International (http://www.rhi.com/)." Alternately, 15 * this acknowledgment may appear in the software itself, if and wherever such 16 * third-party acknowledgments normally appear. 17 * 4. The names "Parc", "RHI", and "Robert Half International" must not be 18 * used to endorse or promote products derived from this software without prior 19 * written permission. For written permission, please contact 20 * pete.mckinstry@rhi.com. 21 * 5. Products derived from this software may not be called "PARC", nor may 22 * "PARC" appear in their name, without prior written permission of Robert Half 23 * International. 24 * 25 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, 26 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 27 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 28 * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 32 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 * ==================================================================== 36 * 37 */ 38 package com.rhi.architecture.lang; 39 40 import com.rhi.architecture.logging.*; 41 42 import java.io.IOException; 43 import java.io.InputStream; 44 import java.util.Enumeration; 45 import java.util.Properties; 46 import java.util.StringTokenizer; 47 48 /*** 49 * <pre> 50 * This class resolves references in a properties file to other properties 51 * files. Properties files are categorized in 2 classes for the explanation of 52 * this class. 53 * <ol> 54 * <li>Base properties - these are like base classes in OOP, and properties 55 * from these files are taken as defaults. These properties can be overridden 56 * by specifying in a more specific file. Base property files are identified 57 * by "base.include.files" entry. Multiple files can be specified separated by 58 * comma. 59 * <li>Referenced properties - these are like object references from a class in 60 * OOP, properties from these files will be read and added to main properties 61 * file. These files are identified by entries starting with "include." 62 * *</ol> 63 * Base properties file can have referenced properties file but not the other 64 * way round. This is done to limit features provided by this class to the 65 * necessary ones and to keep the class simple. 66 * 67 * @see PropertyUtils, PropertiesException 68 * @author <a href="mailto:vairan01@rhi.com">Vaibhav Ranjangaonkar</a> 69 * 70 */ 71 public class CompositeProperties extends Properties { 72 73 private static Logger log = null; 74 75 private static final String BASE_PROP_FILE = "base.include.files"; 76 private static final String PROP_KEY = "include."; 77 78 /*** 79 * default constructor 80 */ 81 public CompositeProperties() { 82 super(); 83 } 84 85 /*** 86 * constructor - loads a Property file of the provided name from the 87 * classpath. If it references any other properties files, they too are 88 * resolved. 89 * 90 * @param fileName 91 * @throws PropertiesException 92 */ 93 public CompositeProperties(String fileName) throws PropertiesException { 94 Properties orig = PropertyUtils.loadProperties(fileName); 95 resolve(orig); 96 } 97 98 /*** 99 * This constructor can be used if a Properties object is already existing, 100 * but the user would like to resolve any file inclusions it may contain. 101 * It can be used to wrap Properties file from other components & present a 102 * <code>Properties</code> interface to a client. 103 * 104 * @param plain 105 * @throws PropertiesException 106 */ 107 public CompositeProperties(Properties plain) throws PropertiesException { 108 resolve(plain); 109 } 110 111 /*** 112 * load propery name/value pairs from an input stream. This is overridden 113 * by the CompositePropeties obj so that references can be resolved before 114 * the client sees the Properties object. 115 * 116 * @param is - 117 * InputStream containing properties. 118 * @throws IOException 119 */ 120 public synchronized void load(InputStream is) throws IOException { 121 Properties tmp = new Properties(); 122 tmp.load(is); 123 try { 124 resolve(tmp); 125 } 126 catch (PropertiesException e) { 127 log().error( 128 "PropertiesException thrown while resolving nested files" 129 + "e = " 130 + e.getMessage(), 131 e); 132 // can't nest exception in JDK1.3, so we'll just put the error 133 // message 134 // on the new exception & leave it at that. 135 throw new IOException(e.getMessage()); 136 } 137 } 138 139 /*** 140 * Given a properties object, resolve any base property file references or 141 * included property references. 142 * 143 * @param p - Properties object. 144 * @throws PropertiesException 145 */ 146 private void resolve(Properties p) throws PropertiesException { 147 // pass 1 for resolving base properties 148 Enumeration entries = p.propertyNames(); 149 while (entries.hasMoreElements()) { 150 String name = (String)entries.nextElement(); 151 String value = p.getProperty(name); 152 if (name.equals(BASE_PROP_FILE)) { 153 String fileNames[] = toStringArray(value, ", "); 154 155 for (int i = 0; i < fileNames.length; i++) { 156 log().debug("base property file: " + fileNames[i]); 157 Properties x = PropertyUtils.loadProperties(fileNames[i]); 158 resolve(x); 159 } 160 } 161 } 162 163 // pass 2 for resolving referenced properties 164 entries = p.propertyNames(); 165 while (entries.hasMoreElements()) { 166 String name = (String)entries.nextElement(); 167 String value = p.getProperty(name); 168 169 if (name.startsWith(PROP_KEY)) { 170 resolve(value, name); 171 } 172 else { 173 log().debug("entry { name=" + name + ", value=" + value + " }"); 174 setProperty(name, value); 175 } 176 } 177 } 178 179 /*** 180 * Given a filename, and a parent key, resolve any references. and prefix 181 * the parent name. 182 * 183 * @param fileName 184 * @param parentProperty 185 * @throws PropertiesException 186 */ 187 private void resolve(String fileName, String parentProperty) 188 throws PropertiesException { 189 Properties q = PropertyUtils.loadProperties(fileName); 190 Enumeration entries = q.propertyNames(); 191 192 while (entries.hasMoreElements()) { 193 String name = (String)entries.nextElement(); 194 String value = q.getProperty(name); 195 196 setProperty(formName(afterPropKey(parentProperty), name), value); 197 } 198 } 199 200 /*** 201 * The name of the key after resolving it from sub-property files. The 202 * prefix used in the include line will be prepended to the property name 203 * so that multiple properties files with identical key values can be 204 * loaded assuming different prefixs are provided during inclusion. E.g. 205 * you may need to load multiple data sources through the configuration 206 * files and would like the files to look identically. You can do this & 207 * provide the data source name (prefix) in the include statement to 208 * different all the child keys 209 * 210 * @param x - 211 * prefix 212 * @param y - 213 * key in sub-file. 214 * @return 215 */ 216 private String formName(String x, String y) { 217 return (x + "." + y); 218 } 219 220 /*** 221 * Return the string part following the include tag. 222 * 223 * @param pname 224 * @return The string part following the include tag. 225 */ 226 private String afterPropKey(String pname) { 227 int pos = pname.indexOf(PROP_KEY); 228 if (pos == -1) 229 return pname; 230 return pname.substring(pos + PROP_KEY.length()); 231 } 232 233 /* 234 * not needed in JDK 1.4 source.split( ",//s*" ) will do the same 235 */ 236 private String[] toStringArray(String source, String delim) { 237 StringTokenizer stz = new StringTokenizer(source, delim); 238 int tokenCount = stz.countTokens(); 239 240 String arrRetStr[] = new String[tokenCount]; 241 for (int i = 0; i < tokenCount; i++) { 242 arrRetStr[i] = stz.nextToken(); 243 } 244 245 return arrRetStr; 246 } 247 248 /*** 249 * lazy initialization 250 * 251 * @return Logger 252 */ 253 private Logger log() { 254 if (log != null) { 255 return log; 256 } 257 else { 258 try { 259 log = LogUtil.getLogger(CompositeProperties.class); 260 } 261 catch (Exception e) { 262 // ignore, follow log==null path. 263 } 264 if (log == null) { 265 return new DefaultLogger(); 266 } 267 else { 268 return log; 269 } 270 } 271 } 272 }

This page was automatically generated by Maven