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