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