1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.eclipse.jetty.webapp;
21
22 import java.io.File;
23 import java.net.URL;
24 import java.nio.file.Path;
25 import java.util.AbstractSet;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.function.Predicate;
34
35 import org.eclipse.jetty.util.ArrayTernaryTrie;
36 import org.eclipse.jetty.util.IncludeExcludeSet;
37 import org.eclipse.jetty.util.TypeUtil;
38 import org.eclipse.jetty.util.URIUtil;
39 import org.eclipse.jetty.util.log.Log;
40 import org.eclipse.jetty.util.log.Logger;
41 import org.eclipse.jetty.util.resource.Resource;
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 public class ClasspathPattern extends AbstractSet<String>
61 {
62 private static final Logger LOG = Log.getLogger(ClasspathPattern.class);
63
64 enum Type { PACKAGE, CLASSNAME, LOCATION }
65
66 private static class Entry
67 {
68 private final String _pattern;
69 private final String _name;
70 private final boolean _inclusive;
71 private final Type _type;
72
73 Entry(String pattern)
74 {
75 _pattern=pattern;
76 _inclusive = !pattern.startsWith("-");
77 _name = _inclusive ? pattern : pattern.substring(1).trim();
78 _type = (_name.startsWith("file:"))?Type.LOCATION:(_name.endsWith(".")?Type.PACKAGE:Type.CLASSNAME);
79 }
80
81 Entry(String name, boolean include)
82 {
83 _pattern=include?name:("-"+name);
84 _inclusive = include;
85 _name = name;
86 _type = (_name.startsWith("file:"))?Type.LOCATION:(_name.endsWith(".")?Type.PACKAGE:Type.CLASSNAME);
87 }
88
89
90 public String getPattern()
91 {
92 return _pattern;
93 }
94
95 public boolean isPackage()
96 {
97 return _type==Type.PACKAGE;
98 }
99
100 public boolean isClassName()
101 {
102 return _type==Type.CLASSNAME;
103 }
104
105 public boolean isLocation()
106 {
107 return _type==Type.LOCATION;
108 }
109
110 public String getName()
111 {
112 return _name;
113 }
114
115 @Override
116 public String toString()
117 {
118 return _pattern;
119 }
120
121 @Override
122 public int hashCode()
123 {
124 return _pattern.hashCode();
125 }
126
127 @Override
128 public boolean equals(Object o)
129 {
130 return (o instanceof Entry)
131 && _pattern.equals(((Entry)o)._pattern);
132 }
133
134 public boolean isInclusive()
135 {
136 return _inclusive;
137 }
138 }
139
140
141 public static class ByPackage extends AbstractSet<Entry> implements Predicate<String>
142 {
143 private final ArrayTernaryTrie.Growing<Entry> _entries = new ArrayTernaryTrie.Growing<>(false,512,512);
144
145 @Override
146 public boolean test(String name)
147 {
148 return _entries.getBest(name)!=null;
149 }
150
151 @Override
152 public Iterator<Entry> iterator()
153 {
154 return _entries.keySet().stream().map(_entries::get).iterator();
155 }
156
157 @Override
158 public int size()
159 {
160 return _entries.size();
161 }
162
163 @Override
164 public boolean isEmpty()
165 {
166 return _entries.isEmpty();
167 }
168
169 @Override
170 public boolean add(Entry entry)
171 {
172 String name = entry.getName();
173 if (entry.isClassName())
174 name+="$";
175 else if (entry.isLocation())
176 throw new IllegalArgumentException(entry.toString());
177 else if (".".equals(name))
178 name="";
179
180 if (_entries.get(name)!=null)
181 return false;
182
183 _entries.put(name,entry);
184 return true;
185 }
186
187 @Override
188 public boolean remove(Object entry)
189 {
190 if (!(entry instanceof Entry))
191 return false;
192
193 return _entries.remove(((Entry)entry).getName())!=null;
194 }
195
196 @Override
197 public void clear()
198 {
199 _entries.clear();
200 }
201 }
202
203 @SuppressWarnings("serial")
204 public static class ByName extends HashSet<Entry> implements Predicate<String>
205 {
206 private final Map<String,Entry> _entries = new HashMap<>();
207
208 @Override
209 public boolean test(String name)
210 {
211 return _entries.containsKey(name);
212 }
213
214 @Override
215 public Iterator<Entry> iterator()
216 {
217 return _entries.values().iterator();
218 }
219
220 @Override
221 public int size()
222 {
223 return _entries.size();
224 }
225
226 @Override
227 public boolean add(Entry entry)
228 {
229 if (!entry.isClassName())
230 throw new IllegalArgumentException(entry.toString());
231 return _entries.put(entry.getName(),entry)==null;
232 }
233
234 @Override
235 public boolean remove(Object entry)
236 {
237 if (!(entry instanceof Entry))
238 return false;
239
240 return _entries.remove(((Entry)entry).getName())!=null;
241 }
242 }
243
244 public static class ByPackageOrName extends AbstractSet<Entry> implements Predicate<String>
245 {
246 private final ByName _byName = new ByName();
247 private final ByPackage _byPackage = new ByPackage();
248
249 @Override
250 public boolean test(String name)
251 {
252 return _byPackage.test(name)
253 || _byName.test(name) ;
254 }
255
256 @Override
257 public Iterator<Entry> iterator()
258 {
259
260 return _byPackage.iterator();
261 }
262
263 @Override
264 public int size()
265 {
266 return _byPackage.size();
267 }
268
269 @Override
270 public boolean add(Entry e)
271 {
272 if (e.isLocation())
273 throw new IllegalArgumentException();
274
275 if (e.isPackage())
276 return _byPackage.add(e);
277
278
279
280 boolean added = _byPackage.add(e);
281 added = _byName.add(e) || added;
282 return added;
283 }
284
285 @Override
286 public boolean remove(Object o)
287 {
288 if (!(o instanceof Entry))
289 return false;
290
291 boolean removed = _byPackage.remove(o);
292
293 if (!((Entry)o).isPackage())
294 removed = _byName.remove(o) || removed;
295
296 return removed;
297 }
298
299 @Override
300 public void clear()
301 {
302 _byPackage.clear();
303 _byName.clear();
304 }
305 }
306
307 @SuppressWarnings("serial")
308 public static class ByLocation extends HashSet<File> implements Predicate<Path>
309 {
310 @Override
311 public boolean test(Path path)
312 {
313 for (File file: this)
314 {
315 if (file.isDirectory())
316 {
317 if (path.startsWith(file.toPath()))
318 return true;
319 }
320 else
321 {
322 if (path.equals(file.toPath()))
323 return true;
324 }
325 }
326
327 return false;
328 }
329 }
330
331
332 Map<String,Entry> _entries = new HashMap<>();
333 IncludeExcludeSet<Entry,String> _patterns = new IncludeExcludeSet<>(ByPackageOrName.class);
334 IncludeExcludeSet<File,Path> _locations = new IncludeExcludeSet<>(ByLocation.class);
335
336 public ClasspathPattern()
337 {
338 }
339
340 public ClasspathPattern(String[] patterns)
341 {
342 setAll(patterns);
343 }
344
345 public ClasspathPattern(String pattern)
346 {
347 add(pattern);
348 }
349
350 public boolean include(String name)
351 {
352 if (name==null)
353 return false;
354 return add(new Entry(name,true));
355 }
356
357 public boolean include(String... name)
358 {
359 boolean added = false;
360 for (String n:name)
361 if (n!=null)
362 added = add(new Entry(n,true)) || added;
363 return added;
364 }
365
366 public boolean exclude(String name)
367 {
368 if (name==null)
369 return false;
370 return add(new Entry(name,false));
371 }
372
373 public boolean exclude(String... name)
374 {
375 boolean added = false;
376 for (String n:name)
377 if (n!=null)
378 added = add(new Entry(n,false)) || added;
379 return added;
380 }
381
382 @Override
383 public boolean add(String pattern)
384 {
385 if (pattern==null)
386 return false;
387 return add(new Entry(pattern));
388 }
389
390 public boolean add(String... pattern)
391 {
392 boolean added = false;
393 for (String p:pattern)
394 if (p!=null)
395 added = add(new Entry(p)) || added;
396 return added;
397 }
398
399 protected boolean add(Entry entry)
400 {
401 if (_entries.containsKey(entry.getPattern()))
402 return false;
403 _entries.put(entry.getPattern(),entry);
404
405 if (entry.isLocation())
406 {
407 try
408 {
409 File file = Resource.newResource(entry.getName()).getFile().getAbsoluteFile().getCanonicalFile();
410 if (entry.isInclusive())
411 _locations.include(file);
412 else
413 _locations.exclude(file);
414 }
415 catch (Exception e)
416 {
417 throw new IllegalArgumentException(e);
418 }
419 }
420 else
421 {
422 if (entry.isInclusive())
423 _patterns.include(entry);
424 else
425 _patterns.exclude(entry);
426 }
427 return true;
428 }
429
430 @Override
431 public boolean remove(Object o)
432 {
433 if (!(o instanceof String))
434 return false;
435 String pattern = (String)o;
436
437 Entry entry = _entries.remove(pattern);
438 if (entry==null)
439 return false;
440
441 List<Entry> saved = new ArrayList<>(_entries.values());
442 clear();
443 for (Entry e:saved)
444 add(e);
445 return true;
446 }
447
448 @Override
449 public void clear()
450 {
451 _entries.clear();
452 _patterns.clear();
453 _locations.clear();
454 }
455
456 @Override
457 public Iterator<String> iterator()
458 {
459 return _entries.keySet().iterator();
460 }
461
462 @Override
463 public int size()
464 {
465 return _entries.size();
466 }
467
468
469
470
471
472
473 private void setAll(String[] classes)
474 {
475 _entries.clear();
476 addAll(classes);
477 }
478
479
480
481
482 private void addAll(String[] classes)
483 {
484 if (classes!=null)
485 addAll(Arrays.asList(classes));
486 }
487
488
489
490
491 public String[] getPatterns()
492 {
493 return toArray(new String[_entries.size()]);
494 }
495
496
497
498
499
500
501
502
503 public boolean match(String name)
504 {
505 return _patterns.test(name);
506 }
507
508
509
510
511
512
513
514 public boolean match(Class<?> clazz)
515 {
516 try
517 {
518 Boolean byName = _patterns.isIncludedAndNotExcluded(clazz.getName());
519 Resource resource = TypeUtil.getLoadedFrom(clazz);
520 Boolean byLocation = resource == null || resource.getFile() == null
521 ? null
522 : _locations.isIncludedAndNotExcluded(resource.getFile().toPath());
523
524
525 boolean included = byName==Boolean.TRUE || byLocation==Boolean.TRUE
526 || (byName==null && !_patterns.hasIncludes() && byLocation==null && !_locations.hasIncludes());
527 boolean excluded = byName==Boolean.FALSE || byLocation==Boolean.FALSE;
528 return included && !excluded;
529 }
530 catch (Exception e)
531 {
532 LOG.warn(e);
533 }
534 return false;
535 }
536
537 public boolean match(String name, URL url)
538 {
539
540 if (name.endsWith(".class"))
541 name=name.substring(0,name.length()-6);
542
543
544 name=name.replace("/",".");
545
546 Boolean byName = _patterns.isIncludedAndNotExcluded(name);
547
548
549 Boolean byLocation = null;
550 try
551 {
552 Resource resource = Resource.newResource(URIUtil.getJarSource(url.toURI()));
553 File file = resource.getFile();
554 byLocation = _locations.isIncludedAndNotExcluded(file.toPath());
555 }
556 catch(Exception e)
557 {
558 LOG.ignore(e);
559 }
560
561
562 boolean included = byName==Boolean.TRUE || byLocation==Boolean.TRUE
563 || (byName==null && !_patterns.hasIncludes() && byLocation==null && !_locations.hasIncludes());
564 boolean excluded = byName==Boolean.FALSE || byLocation==Boolean.FALSE;
565 return included && !excluded;
566 }
567 }