1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.start;
20
21 import static org.eclipse.jetty.start.UsageException.ERR_BAD_ARG;
22
23 import java.io.IOException;
24 import java.io.OutputStream;
25 import java.util.ArrayList;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Properties;
30 import java.util.Stack;
31 import java.util.TreeMap;
32 import java.util.regex.Matcher;
33 import java.util.regex.Pattern;
34
35 import org.eclipse.jetty.start.Props.Prop;
36
37
38
39
40
41
42
43 public final class Props implements Iterable<Prop>
44 {
45 private static final Pattern __propertyPattern = Pattern.compile("(?<=[^$]|^)\\$\\{([^}]*)\\}");
46
47 public static class Prop
48 {
49 public String key;
50 public String value;
51 public String origin;
52 public Prop overrides;
53
54 public Prop(String key, String value, String origin)
55 {
56 this.key = key;
57 this.value = value;
58 this.origin = origin;
59 }
60
61 public Prop(String key, String value, String origin, Prop overrides)
62 {
63 this(key,value,origin);
64 this.overrides = overrides;
65 }
66
67 @Override
68 public String toString()
69 {
70 StringBuilder builder = new StringBuilder();
71 builder.append("Prop [key=");
72 builder.append(key);
73 builder.append(", value=");
74 builder.append(value);
75 builder.append(", origin=");
76 builder.append(origin);
77 builder.append(", overrides=");
78 builder.append(overrides);
79 builder.append("]");
80 return builder.toString();
81 }
82 }
83
84 public static final String ORIGIN_SYSPROP = "<system-property>";
85
86 public static String getValue(String arg)
87 {
88 int idx = arg.indexOf('=');
89 if (idx == (-1))
90 {
91 throw new UsageException(ERR_BAD_ARG,"Argument is missing a required value: %s",arg);
92 }
93 String value = arg.substring(idx + 1).trim();
94 if (value.length() <= 0)
95 {
96 throw new UsageException(ERR_BAD_ARG,"Argument is missing a required value: %s",arg);
97 }
98 return value;
99 }
100
101 public static List<String> getValues(String arg)
102 {
103 String v = getValue(arg);
104 ArrayList<String> l = new ArrayList<>();
105 for (String s : v.split(","))
106 {
107 if (s != null)
108 {
109 s = s.trim();
110 if (s.length() > 0)
111 {
112 l.add(s);
113 }
114 }
115 }
116 return l;
117 }
118
119 private Map<String, Prop> props = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
120 private List<String> sysPropTracking = new ArrayList<>();
121
122 public void addAll(Props other)
123 {
124 this.props.putAll(other.props);
125 this.sysPropTracking.addAll(other.sysPropTracking);
126 }
127
128
129
130
131
132
133
134
135
136 public boolean addPossibleProperty(String arg, String source)
137 {
138
139 if (arg.startsWith("-D"))
140 {
141 String[] assign = arg.substring(2).split("=",2);
142 switch (assign.length)
143 {
144 case 2:
145 setSystemProperty(assign[0],assign[1]);
146 setProperty(assign[0],assign[1],source);
147 return true;
148 case 1:
149 setSystemProperty(assign[0],"");
150 setProperty(assign[0],"",source);
151 return true;
152 default:
153 return false;
154 }
155 }
156
157
158 int idx = arg.indexOf('=');
159 if (idx >= 0)
160 {
161 String key = arg.substring(0,idx);
162 String value = arg.substring(idx + 1);
163
164 setProperty(key,value,source);
165 return true;
166 }
167
168
169 return false;
170 }
171
172 public String cleanReference(String property)
173 {
174 String name = property.trim();
175 if (name.startsWith("${") && name.endsWith("}"))
176 {
177 name = name.substring(2,name.length() - 1);
178 }
179 return name.trim();
180 }
181
182 public boolean containsKey(String key)
183 {
184 return props.containsKey(key);
185 }
186
187 public String expand(String str)
188 {
189 return expand(str,new Stack<String>());
190 }
191
192 public String expand(String str, Stack<String> seenStack)
193 {
194 if (str == null)
195 {
196 return str;
197 }
198
199 if (str.indexOf("${") < 0)
200 {
201
202 return str;
203 }
204
205 Matcher mat = __propertyPattern.matcher(str);
206 StringBuilder expanded = new StringBuilder();
207 int offset = 0;
208 String property;
209 String value;
210
211 while (mat.find(offset))
212 {
213 property = mat.group(1);
214
215
216 if (seenStack.contains(property))
217 {
218 StringBuilder err = new StringBuilder();
219 err.append("Property expansion loop detected: ");
220 int idx = seenStack.lastIndexOf(property);
221 for (int i = idx; i < seenStack.size(); i++)
222 {
223 err.append(seenStack.get(i));
224 err.append(" -> ");
225 }
226 err.append(property);
227 throw new PropsException(err.toString());
228 }
229
230 seenStack.push(property);
231
232
233 expanded.append(str.subSequence(offset,mat.start()));
234
235 value = getString(property);
236 if (value == null)
237 {
238 StartLog.trace("Unable to expand: %s",property);
239 expanded.append(mat.group());
240 }
241 else
242 {
243
244 value = expand(value,seenStack);
245 expanded.append(value);
246 }
247
248 offset = mat.end();
249 }
250
251
252 expanded.append(str.substring(offset));
253
254
255 if (expanded.indexOf("$$") >= 0)
256 {
257 return expanded.toString().replaceAll("\\$\\$","\\$");
258 }
259
260 return expanded.toString();
261 }
262
263 public Prop getProp(String key)
264 {
265 return getProp(key,true);
266 }
267
268 public Prop getProp(String key, boolean searchSystemProps)
269 {
270 Prop prop = props.get(key);
271 if ((prop == null) && searchSystemProps)
272 {
273
274 prop = getSystemProperty(key);
275 }
276 return prop;
277 }
278
279 public String getString(String key)
280 {
281 if (key == null)
282 {
283 throw new PropsException("Cannot get value for null key");
284 }
285
286 String name = cleanReference(key);
287
288 if (name.length() == 0)
289 {
290 throw new PropsException("Cannot get value for empty key");
291 }
292
293 Prop prop = getProp(name);
294 if (prop == null)
295 {
296 return null;
297 }
298 return prop.value;
299 }
300
301 public String getString(String key, String defVal)
302 {
303 String val = getString(key);
304 if (val == null)
305 {
306 return defVal;
307 }
308 return val;
309 }
310
311 private Prop getSystemProperty(String key)
312 {
313 String value = System.getProperty(key);
314 if (value == null)
315 {
316 return null;
317 }
318 return new Prop(key,value,ORIGIN_SYSPROP);
319 }
320
321 public static boolean hasPropertyKey(String name)
322 {
323 return __propertyPattern.matcher(name).find();
324 }
325
326 @Override
327 public Iterator<Prop> iterator()
328 {
329 return props.values().iterator();
330 }
331
332 public void reset()
333 {
334 props.clear();
335 }
336
337 public void setProperty(Prop prop)
338 {
339 props.put(prop.key,prop);
340 }
341
342 public void setProperty(String key, String value, String origin)
343 {
344 Prop prop = props.get(key);
345 if (prop == null)
346 {
347 prop = new Prop(key,value,origin);
348 }
349 else
350 {
351 prop = new Prop(key,value,origin,prop);
352 }
353 props.put(key,prop);
354 }
355
356 public int size()
357 {
358 return props.size();
359 }
360
361 public void store(OutputStream stream, String comments) throws IOException
362 {
363 Properties props = new Properties();
364
365 for (Prop prop : this)
366 {
367 props.setProperty(prop.key,expand(prop.value));
368 }
369
370 props.store(stream,comments);
371 }
372
373 public void setSystemProperty(String key, String value)
374 {
375 System.setProperty(key,value);
376 sysPropTracking.add(key);
377 }
378 }