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