1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.quickstart;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.net.URI;
24 import java.net.URL;
25 import java.nio.file.FileSystems;
26 import java.nio.file.Files;
27 import java.nio.file.Path;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.Comparator;
31 import java.util.List;
32 import java.util.Stack;
33 import java.util.regex.Matcher;
34 import java.util.regex.Pattern;
35
36 import org.eclipse.jetty.util.URIUtil;
37 import org.eclipse.jetty.util.log.Log;
38 import org.eclipse.jetty.util.log.Logger;
39 import org.eclipse.jetty.util.resource.Resource;
40
41
42
43
44
45
46
47
48
49
50
51
52
53 public class AttributeNormalizer
54 {
55 private static final Logger LOG = Log.getLogger(AttributeNormalizer.class);
56 private static final Pattern __propertyPattern = Pattern.compile("(?<=[^$]|^)\\$\\{([^}]*)\\}");
57
58 private static class PathAttribute
59 {
60 public final Path path;
61 public final String key;
62 private int weight = -1;
63
64 public PathAttribute(String key, Path path) throws IOException
65 {
66 this.key = key;
67 this.path = toCanonicalPath(path);
68
69 }
70
71 public PathAttribute(String key, String systemPropertyKey) throws IOException
72 {
73 this(key, toCanonicalPath(System.getProperty(systemPropertyKey)));
74 }
75
76 private static Path toCanonicalPath(String path) throws IOException
77 {
78 if (path == null)
79 {
80 return null;
81 }
82 return toCanonicalPath(FileSystems.getDefault().getPath(path));
83 }
84
85 private static Path toCanonicalPath(Path path) throws IOException
86 {
87 if (path == null)
88 {
89 return null;
90 }
91 if (Files.exists(path))
92 {
93 return path.toRealPath();
94 }
95 return path.toAbsolutePath();
96 }
97
98 public PathAttribute weight(int newweight)
99 {
100 this.weight = newweight;
101 return this;
102 }
103
104 @Override
105 public String toString()
106 {
107 return String.format("PathAttribute[%s=>%s,%d]",key,path,weight);
108 }
109 }
110
111 private static class PathAttributeComparator implements Comparator<PathAttribute>
112 {
113 @Override
114 public int compare(PathAttribute o1, PathAttribute o2)
115 {
116 if( (o1.path == null) && (o2.path != null) )
117 {
118 return -1;
119 }
120
121 if( (o1.path != null) && (o2.path == null) )
122 {
123 return 1;
124 }
125
126 if( (o1.path == null) && (o2.path == null) )
127 {
128 return 0;
129 }
130
131
132 int diff = o2.path.getNameCount() - o1.path.getNameCount();
133 if(diff != 0)
134 {
135 return diff;
136 }
137
138
139 diff = o2.path.compareTo(o1.path);
140 if(diff != 0)
141 {
142 return diff;
143 }
144
145
146 return o2.weight - o1.weight;
147 }
148 }
149
150 public static String uriSeparators(String path)
151 {
152 StringBuilder ret = new StringBuilder();
153 for (char c : path.toCharArray())
154 {
155 if ((c == '/') || (c == '\\'))
156 {
157 ret.append('/');
158 }
159 else
160 {
161 ret.append(c);
162 }
163 }
164 return ret.toString();
165 }
166
167 private URI warURI;
168 private List<PathAttribute> attributes = new ArrayList<>();
169
170 public AttributeNormalizer(Resource baseResource)
171 {
172
173 warURI = baseResource == null ? null : baseResource.getURI();
174
175 if (!warURI.isAbsolute())
176 throw new IllegalArgumentException("WAR URI is not absolute: " + warURI);
177 try
178 {
179
180 attributes.add(new PathAttribute("jetty.base", "jetty.base").weight(9));
181 attributes.add(new PathAttribute("jetty.home", "jetty.home").weight(8));
182 attributes.add(new PathAttribute("user.home", "user.home").weight(7));
183 attributes.add(new PathAttribute("user.dir", "user.dir").weight(6));
184
185 Collections.sort(attributes, new PathAttributeComparator());
186
187 if (LOG.isDebugEnabled())
188 {
189 int i = 0;
190 for (PathAttribute attr : attributes)
191 {
192 LOG.debug(" [{}] {}", i++, attr);
193 }
194 }
195 }
196 catch (Exception e)
197 {
198 throw new IllegalArgumentException(e);
199 }
200 }
201
202 public String normalize(Object o)
203 {
204 try
205 {
206
207 URI uri = null;
208 if (o instanceof URI)
209 uri = (URI)o;
210 else if (o instanceof URL)
211 uri = ((URL)o).toURI();
212 else if (o instanceof File)
213 uri = ((File)o).toURI();
214 else
215 {
216 String s = o.toString();
217 uri = new URI(s);
218 if (uri.getScheme() == null)
219 return s;
220 }
221
222 if ("jar".equalsIgnoreCase(uri.getScheme()))
223 {
224 String raw = uri.getRawSchemeSpecificPart();
225 int bang = raw.indexOf("!/");
226 String normal = normalize(raw.substring(0,bang));
227 String suffix = raw.substring(bang);
228 return "jar:" + normal + suffix;
229 }
230 else if ("file".equalsIgnoreCase(uri.getScheme()))
231 {
232 return "file:" + normalizePath(new File(uri.getRawSchemeSpecificPart()).toPath());
233 }
234 else
235 {
236 if(uri.isAbsolute())
237 {
238 return normalizeUri(uri);
239 }
240 }
241 }
242 catch (Exception e)
243 {
244 LOG.warn(e);
245 }
246 return String.valueOf(o);
247 }
248
249 public String normalizeUri(URI uri)
250 {
251 String uriStr = uri.toASCIIString();
252 String warStr = warURI.toASCIIString();
253 if (uriStr.startsWith(warStr))
254 {
255 return "${WAR}" + uriStr.substring(warStr.length());
256 }
257 return uriStr;
258 }
259
260 public String normalizePath(Path path)
261 {
262 for (PathAttribute attr : attributes)
263 {
264 if (attr.path == null)
265 continue;
266
267 try
268 {
269 if (path.startsWith(attr.path) || path.equals(attr.path) || Files.isSameFile(path,attr.path))
270 {
271 return uriSeparators(URIUtil.addPaths("${" + attr.key + "}",attr.path.relativize(path).toString()));
272 }
273 }
274 catch (IOException ignore)
275 {
276 LOG.ignore(ignore);
277 }
278 }
279
280 return uriSeparators(path.toString());
281 }
282
283 public String expand(String str)
284 {
285 return expand(str,new Stack<String>());
286 }
287
288 public String expand(String str, Stack<String> seenStack)
289 {
290 if (str == null)
291 {
292 return str;
293 }
294
295 if (str.indexOf("${") < 0)
296 {
297
298 return str;
299 }
300
301 Matcher mat = __propertyPattern.matcher(str);
302 StringBuilder expanded = new StringBuilder();
303 int offset = 0;
304 String property;
305 String value;
306
307 while (mat.find(offset))
308 {
309 property = mat.group(1);
310
311
312 if (seenStack.contains(property))
313 {
314 StringBuilder err = new StringBuilder();
315 err.append("Property expansion loop detected: ");
316 int idx = seenStack.lastIndexOf(property);
317 for (int i = idx; i < seenStack.size(); i++)
318 {
319 err.append(seenStack.get(i));
320 err.append(" -> ");
321 }
322 err.append(property);
323 throw new RuntimeException(err.toString());
324 }
325
326 seenStack.push(property);
327
328
329 expanded.append(str.subSequence(offset,mat.start()));
330
331 value = getString(property);
332 if (value == null)
333 {
334 if(LOG.isDebugEnabled())
335 LOG.debug("Unable to expand: {}",property);
336 expanded.append(mat.group());
337 }
338 else
339 {
340
341 value = expand(value,seenStack);
342 expanded.append(value);
343 }
344
345 offset = mat.end();
346 }
347
348
349 expanded.append(str.substring(offset));
350
351
352 if (expanded.indexOf("$$") >= 0)
353 {
354 return expanded.toString().replaceAll("\\$\\$","\\$");
355 }
356
357 return expanded.toString();
358 }
359
360 private String getString(String property)
361 {
362 if(property == null)
363 {
364 return null;
365 }
366
367
368 if("WAR".equalsIgnoreCase(property))
369 {
370 return warURI.toASCIIString();
371 }
372
373
374 for (PathAttribute attr : attributes)
375 {
376 if (attr.key.equalsIgnoreCase(property))
377 {
378 return uriSeparators(attr.path.toString());
379 }
380 }
381
382
383 return System.getProperty(property);
384 }
385 }