1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.eclipse.jetty.start;
18
19 import java.io.BufferedReader;
20 import java.io.File;
21 import java.io.FileFilter;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.InputStreamReader;
25 import java.io.Reader;
26 import java.io.StringReader;
27 import java.lang.reflect.Constructor;
28 import java.lang.reflect.InvocationTargetException;
29 import java.net.URL;
30 import java.security.Policy;
31 import java.text.CollationKey;
32 import java.text.Collator;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Collection;
36 import java.util.Collections;
37 import java.util.Comparator;
38 import java.util.HashMap;
39 import java.util.HashSet;
40 import java.util.Iterator;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.Set;
44 import java.util.StringTokenizer;
45 import java.util.TreeSet;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141 public class Config
142 {
143 public static final String DEFAULT_SECTION = "";
144 static
145 {
146 Package pkg = Config.class.getPackage();
147 if (pkg != null && (pkg.getImplementationVersion() != null))
148 _version = pkg.getImplementationVersion();
149 else
150 _version = System.getProperty("jetty.version","Unknown");
151 }
152
153
154
155
156 private Comparator<String> keySorter = new Comparator<String>()
157 {
158 private Collator collator = Collator.getInstance();
159
160 public int compare(String o1, String o2)
161 {
162 CollationKey key1 = collator.getCollationKey(o1);
163 CollationKey key2 = collator.getCollationKey(o2);
164 return key1.compareTo(key2);
165 }
166 };
167
168 private static final String _version;
169 private static boolean DEBUG = false;
170 private Map<String, Classpath> _classpaths = new HashMap<String, Classpath>();
171 private List<String> _xml = new ArrayList<String>();
172 private Set<String> _policies = new HashSet<String>();
173 private String _classname = null;
174 private Set<String> _activeOptions = new TreeSet<String>(new Comparator<String>()
175 {
176
177 public int compare(String o1, String o2)
178 {
179 if ("*".equals(o1))
180 {
181 return 1;
182 }
183 if ("*".equals(o2))
184 {
185 return -1;
186 }
187 return o1.compareTo(o2);
188 }
189 });
190 private Map<String, String> _properties = new HashMap<String, String>();
191 private int argCount = 0;
192
193 private boolean addClasspathComponent(List<String> sections, String component)
194 {
195 for (String section : sections)
196 {
197 Classpath cp = _classpaths.get(section);
198 if (cp == null)
199 cp = new Classpath();
200
201 boolean added = cp.addComponent(component);
202 _classpaths.put(section,cp);
203
204 if (!added)
205 {
206
207 return false;
208 }
209 }
210
211 return true;
212 }
213
214 private boolean addClasspathPath(List<String> sections, String path)
215 {
216 for (String section : sections)
217 {
218 Classpath cp = _classpaths.get(section);
219 if (cp == null)
220 {
221 cp = new Classpath();
222 }
223 if (!cp.addClasspath(path))
224 {
225
226 return false;
227 }
228 _classpaths.put(section,cp);
229 }
230
231 return true;
232 }
233
234 private void addJars(List<String> sections, File dir, boolean recurse) throws IOException
235 {
236 List<File> entries = new ArrayList<File>();
237 File[] files = dir.listFiles();
238 if (files == null)
239 {
240
241 return;
242 }
243 entries.addAll(Arrays.asList(files));
244 Collections.sort(entries,FilenameComparator.INSTANCE);
245
246 for (File entry : entries)
247 {
248 if (entry.isDirectory())
249 {
250 if (recurse)
251 addJars(sections,entry,recurse);
252 }
253 else
254 {
255 String name = entry.getName().toLowerCase();
256 if (name.endsWith(".jar") || name.endsWith(".zip"))
257 {
258 String jar = entry.getCanonicalPath();
259 boolean added = addClasspathComponent(sections,jar);
260 debug((added?" CLASSPATH+=":" !") + jar);
261 }
262 }
263 }
264 }
265
266 private void close(InputStream stream)
267 {
268 if (stream == null)
269 return;
270
271 try
272 {
273 stream.close();
274 }
275 catch (IOException ignore)
276 {
277
278 }
279 }
280
281 private void close(Reader reader)
282 {
283 if (reader == null)
284 return;
285
286 try
287 {
288 reader.close();
289 }
290 catch (IOException ignore)
291 {
292
293 }
294 }
295
296 public static boolean isDebug()
297 {
298 return DEBUG;
299 }
300
301 public static void debug(String msg)
302 {
303 if (DEBUG)
304 {
305 System.err.println(msg);
306 }
307 }
308
309 public static void debug(Throwable t)
310 {
311 if (DEBUG)
312 {
313 t.printStackTrace(System.err);
314 }
315 }
316
317 private String expand(String s)
318 {
319 int i1 = 0;
320 int i2 = 0;
321 while (s != null)
322 {
323 i1 = s.indexOf("$(",i2);
324 if (i1 < 0)
325 break;
326 i2 = s.indexOf(")",i1 + 2);
327 if (i2 < 0)
328 break;
329 String name = s.substring(i1 + 2,i2);
330 String property = getSystemProperty(name);
331 s = s.substring(0,i1) + property + s.substring(i2 + 1);
332 }
333
334 i1 = 0;
335 i2 = 0;
336 while (s != null)
337 {
338 i1 = s.indexOf("${",i2);
339 if (i1 < 0)
340 break;
341 i2 = s.indexOf("}",i1 + 2);
342 if (i2 < 0)
343 break;
344 String name = s.substring(i1 + 2,i2);
345 String property = getProperty(name);
346 s = s.substring(0,i1) + property + s.substring(i2 + 1);
347 }
348
349 return s;
350 }
351
352
353
354
355
356
357 public Classpath getClasspath()
358 {
359 return _classpaths.get(DEFAULT_SECTION);
360 }
361
362
363
364
365
366
367
368 public Classpath getActiveClasspath()
369 {
370 return getCombinedClasspath(_activeOptions);
371 }
372
373
374
375
376
377
378
379
380
381
382
383 public Classpath getCombinedClasspath(Collection<String> sectionIds)
384 {
385 Classpath cp = new Classpath();
386
387 cp.overlay(_classpaths.get(DEFAULT_SECTION));
388 for (String sectionId : sectionIds)
389 {
390 Classpath otherCp = _classpaths.get(sectionId);
391 if (otherCp == null)
392 {
393 throw new IllegalArgumentException("No such OPTIONS: " + sectionId);
394 }
395 cp.overlay(otherCp);
396 }
397 cp.overlay(_classpaths.get("*"));
398 return cp;
399 }
400
401 public String getMainClassname()
402 {
403 return _classname;
404 }
405
406 public String getProperty(String name)
407 {
408 if ("version".equalsIgnoreCase(name))
409 return _version;
410
411 return _properties.get(name);
412 }
413
414 public String getProperty(String name, String dftValue)
415 {
416 if (_properties.containsKey(name))
417 return _properties.get(name);
418 return dftValue;
419 }
420
421
422
423
424
425
426
427 public Classpath getSectionClasspath(String sectionId)
428 {
429 return _classpaths.get(sectionId);
430 }
431
432
433
434
435
436
437 public Set<String> getSectionIds()
438 {
439 Set<String> ids = new TreeSet<String>(keySorter);
440 ids.addAll(_classpaths.keySet());
441 return ids;
442 }
443
444 private String getSystemProperty(String name)
445 {
446 if ("version".equalsIgnoreCase(name))
447 return _version;
448 if (_properties.containsKey(name))
449 return _properties.get(name);
450 return System.getProperty(name);
451 }
452
453 public List<String> getXmlConfigs()
454 {
455 return _xml;
456 }
457
458 private boolean isAvailable(List<String> sections, String classname)
459 {
460
461 try
462 {
463 Class.forName(classname);
464 return true;
465 }
466 catch (NoClassDefFoundError e)
467 {
468 debug(e);
469 }
470 catch (ClassNotFoundException e)
471 {
472 debug("ClassNotFoundException (parent class loader): " + classname);
473 }
474
475
476 ClassLoader loader;
477 Classpath classpath;
478 for (String sectionId : sections)
479 {
480 classpath = _classpaths.get(sectionId);
481 if (classpath == null)
482 {
483
484 continue;
485 }
486
487 loader = classpath.getClassLoader();
488
489 try
490 {
491 loader.loadClass(classname);
492 return true;
493 }
494 catch (NoClassDefFoundError e)
495 {
496 debug(e);
497 }
498 catch (ClassNotFoundException e)
499 {
500 debug("ClassNotFoundException (section class loader: " + sectionId + "): " + classname);
501 }
502 }
503 return false;
504 }
505
506
507
508
509
510
511
512 public void parse(CharSequence buf) throws IOException
513 {
514 parse(new StringReader(buf.toString()));
515 }
516
517
518
519
520
521
522
523 public void parse(InputStream stream) throws IOException
524 {
525 InputStreamReader reader = null;
526 try
527 {
528 reader = new InputStreamReader(stream);
529 parse(reader);
530 }
531 finally
532 {
533 close(reader);
534 }
535 }
536
537 public void parse(Reader reader) throws IOException
538 {
539 BufferedReader buf = null;
540
541 try
542 {
543 buf = new BufferedReader(reader);
544
545 List<String> sections = new ArrayList<String>();
546 sections.add(DEFAULT_SECTION);
547 _classpaths.put(DEFAULT_SECTION,new Classpath());
548 Version java_version = new Version(System.getProperty("java.version"));
549 Version ver = new Version();
550
551 String line = null;
552 while ((line = buf.readLine()) != null)
553 {
554 String trim = line.trim();
555 if (trim.length() == 0)
556 continue;
557
558 if (trim.startsWith("#"))
559 continue;
560
561
562 if (trim.startsWith("[") && trim.endsWith("]"))
563 {
564 String identifier = trim.substring(1,trim.length() - 1);
565
566
567 sections = Arrays.asList(identifier.split(","));
568 List<String> section_ids=new ArrayList<String>();
569
570
571 for (String sectionId : sections)
572 {
573 if (sectionId.charAt(0) == '=')
574 continue;
575
576 if (!_classpaths.containsKey(sectionId))
577 _classpaths.put(sectionId,new Classpath());
578
579 section_ids.add(sectionId);
580 }
581
582
583
584 for (String sectionId : sections)
585 {
586 if (sectionId.charAt(0) != '=')
587 continue;
588
589 section_ids = processDynamicSectionIdentifier(sectionId.substring(1),section_ids);
590 }
591
592 sections = section_ids;
593
594 continue;
595 }
596
597 try
598 {
599 StringTokenizer st = new StringTokenizer(line);
600 String subject = st.nextToken();
601 boolean expression = true;
602 boolean not = false;
603 String condition = null;
604
605 while (st.hasMoreTokens())
606 {
607 condition = st.nextToken();
608 if (condition.equalsIgnoreCase("!"))
609 {
610 not = true;
611 continue;
612 }
613 if (condition.equalsIgnoreCase("OR"))
614 {
615 if (expression)
616 break;
617 expression = true;
618 continue;
619 }
620 if (condition.equalsIgnoreCase("AND"))
621 {
622 if (!expression)
623 break;
624 continue;
625 }
626 boolean eval = true;
627 if (condition.equals("true") || condition.equals("always"))
628 {
629 eval = true;
630 }
631 else if (condition.equals("false") || condition.equals("never"))
632 {
633 eval = false;
634 }
635 else if (condition.equals("available"))
636 {
637 String class_to_check = st.nextToken();
638 eval = isAvailable(sections,class_to_check);
639 }
640 else if (condition.equals("exists"))
641 {
642 try
643 {
644 eval = false;
645 File file = new File(expand(st.nextToken()));
646 eval = file.exists();
647 }
648 catch (Exception e)
649 {
650 debug(e);
651 }
652 }
653 else if (condition.equals("property"))
654 {
655 String property = getProperty(st.nextToken());
656 eval = property != null && property.length() > 0;
657 }
658 else if (condition.equals("system"))
659 {
660 String property = System.getProperty(st.nextToken());
661 eval = property != null && property.length() > 0;
662 }
663 else if (condition.equals("java"))
664 {
665 String operator = st.nextToken();
666 String version = st.nextToken();
667 ver.parse(version);
668 eval = (operator.equals("<") && java_version.compare(ver) < 0) || (operator.equals(">") && java_version.compare(ver) > 0)
669 || (operator.equals("<=") && java_version.compare(ver) <= 0) || (operator.equals("=<") && java_version.compare(ver) <= 0)
670 || (operator.equals("=>") && java_version.compare(ver) >= 0) || (operator.equals(">=") && java_version.compare(ver) >= 0)
671 || (operator.equals("==") && java_version.compare(ver) == 0) || (operator.equals("!=") && java_version.compare(ver) != 0);
672 }
673 else if (condition.equals("nargs"))
674 {
675 String operator = st.nextToken();
676 int number = Integer.parseInt(st.nextToken());
677 eval = (operator.equals("<") && argCount < number) || (operator.equals(">") && argCount > number)
678 || (operator.equals("<=") && argCount <= number) || (operator.equals("=<") && argCount <= number)
679 || (operator.equals("=>") && argCount >= number) || (operator.equals(">=") && argCount >= number)
680 || (operator.equals("==") && argCount == number) || (operator.equals("!=") && argCount != number);
681 }
682 else
683 {
684 System.err.println("ERROR: Unknown condition: " + condition);
685 eval = false;
686 }
687 expression &= not?!eval:eval;
688 not = false;
689 }
690
691 String file = expand(subject);
692 debug((expression?"T ":"F ") + line);
693 if (!expression)
694 continue;
695
696
697 if (subject.indexOf("~=") > 0)
698 {
699 int i = file.indexOf("~=");
700 String property = file.substring(0,i);
701 String value = fixPath(file.substring(i + 2));
702 debug(" " + property + "~=" + value);
703 setProperty(property,value);
704 continue;
705 }
706
707
708 if (subject.indexOf("/=") > 0)
709 {
710 int i = file.indexOf("/=");
711 String property = file.substring(0,i);
712 String value = fixPath(file.substring(i + 2));
713 String canonical = new File(value).getCanonicalPath();
714 debug(" " + property + "/=" + value + "==" + canonical);
715 setProperty(property,canonical);
716 continue;
717 }
718
719
720 if (subject.indexOf("=") > 0)
721 {
722 int i = file.indexOf("=");
723 String property = file.substring(0,i);
724 String value = fixPath(file.substring(i + 1));
725 debug(" " + property + "=" + value);
726 System.setProperty(property,value);
727 continue;
728 }
729
730
731 if (subject.endsWith("/*"))
732 {
733
734 File dir = new File(fixPath(file.substring(0,file.length() - 1)));
735 addJars(sections,dir,false);
736 continue;
737 }
738
739
740 if (subject.endsWith("/**"))
741 {
742
743 File dir = new File(fixPath(file.substring(0,file.length() - 2)));
744 addJars(sections,dir,true);
745 continue;
746 }
747
748
749 if (subject.endsWith("/"))
750 {
751
752 File cd = new File(fixPath(file));
753 String d = cd.getCanonicalPath();
754 boolean added = addClasspathComponent(sections,d);
755 debug((added?" CLASSPATH+=":" !") + d);
756 continue;
757 }
758
759
760 if (subject.toLowerCase().endsWith(".xml"))
761 {
762
763 File f = new File(fixPath(file));
764 if (f.exists())
765 _xml.add(f.getCanonicalPath());
766 debug(" ARGS+=" + f);
767 continue;
768 }
769
770
771 if (subject.toLowerCase().endsWith(".class"))
772 {
773
774 String cn = expand(subject.substring(0,subject.length() - 6));
775 if (cn != null && cn.length() > 0)
776 {
777 debug(" CLASS=" + cn);
778 _classname = cn;
779 }
780 continue;
781 }
782
783
784 if (subject.toLowerCase().endsWith(".path"))
785 {
786
787 String cn = expand(subject.substring(0,subject.length() - 5));
788 if (cn != null && cn.length() > 0)
789 {
790 debug(" PATH=" + cn);
791 addClasspathPath(sections,cn);
792 }
793 continue;
794 }
795
796
797 if (subject.toLowerCase().endsWith(".policy"))
798 {
799
800 String cn = expand(subject.substring(0,subject.length()));
801 if (cn != null && cn.length() > 0)
802 {
803 debug(" POLICY=" + cn);
804 _policies.add(fixPath(cn));
805 }
806 continue;
807 }
808
809
810 File f = new File(fixPath(file));
811 if (f.exists())
812 {
813 String d = f.getCanonicalPath();
814 boolean added = addClasspathComponent(sections,d);
815 if (!added)
816 {
817 added = addClasspathPath(sections,expand(subject));
818 }
819 debug((added?" CLASSPATH+=":" !") + d);
820 }
821 }
822 catch (Exception e)
823 {
824 System.err.println("on line: '" + line + "'");
825 e.printStackTrace();
826 }
827 }
828 }
829 finally
830 {
831 close(buf);
832 }
833 }
834
835 private List<String> processDynamicSectionIdentifier(String dynamicPathId,List<String> sections) throws IOException
836 {
837 String section=null;
838 String rawPath;
839 boolean deep;
840
841 if (dynamicPathId.endsWith("/*"))
842 {
843 deep=false;
844 rawPath = fixPath(dynamicPathId.substring(0,dynamicPathId.length() - 1));
845 }
846 else if (dynamicPathId.endsWith("/**"))
847 {
848 deep=true;
849 rawPath = fixPath(dynamicPathId.substring(0,dynamicPathId.length() - 2));
850 }
851 else if (dynamicPathId.indexOf('/')>1 && !dynamicPathId.endsWith("/"))
852 {
853 section=dynamicPathId.substring(dynamicPathId.lastIndexOf('/')+1);
854 rawPath=dynamicPathId.substring(0,dynamicPathId.lastIndexOf('/'));
855 deep=true;
856 }
857 else
858 {
859 String msg = "Illegal dynamic path [" + dynamicPathId + "]";
860 throw new IOException(msg);
861 }
862
863 File parentDir = new File(expand(rawPath));
864 if (!parentDir.exists())
865 return sections;
866 debug("dynamic: " + parentDir);
867
868 File dirs[] = section!=null
869 ?new File[]{new File(parentDir,section)}
870 :parentDir.listFiles(new FileFilter()
871 {
872 public boolean accept(File path)
873 {
874 return path.isDirectory();
875 }
876 });
877
878 List<String> dyn_sections = new ArrayList<String>();
879 List<String> super_sections = new ArrayList<String>();
880 if (sections!=null)
881 super_sections.addAll(sections);
882
883 for (File dir : dirs)
884 {
885 String id = dir.getName();
886 if (_classpaths.keySet().contains(id))
887 continue;
888 _classpaths.put(id,new Classpath());
889
890 dyn_sections.clear();
891 if (sections!=null)
892 dyn_sections.addAll(sections);
893 dyn_sections.add(id);
894 super_sections.add(id);
895 debug("dynamic: " + dyn_sections);
896 addJars(dyn_sections,dir,deep);
897 }
898
899 return super_sections;
900 }
901
902 private String fixPath(String path)
903 {
904 return path.replace('/',File.separatorChar);
905 }
906
907 public void parse(URL url) throws IOException
908 {
909 InputStream stream = null;
910 InputStreamReader reader = null;
911 try
912 {
913 stream = url.openStream();
914 reader = new InputStreamReader(stream);
915 parse(reader);
916 }
917 finally
918 {
919 close(reader);
920 close(stream);
921 }
922 }
923
924 public void setArgCount(int argCount)
925 {
926 this.argCount = argCount;
927 }
928
929 public void setProperty(String name, String value)
930 {
931 if (name.equals("DEBUG"))
932 {
933 DEBUG = Boolean.parseBoolean(value);
934 if (DEBUG)
935 {
936 System.setProperty("org.eclipse.jetty.util.log.stderr.DEBUG","true");
937 System.setProperty("org.eclipse.jetty.start.DEBUG","true");
938 }
939 }
940 if (name.equals("OPTIONS"))
941 {
942 _activeOptions.clear();
943 String ids[] = value.split(",");
944 for (String id : ids)
945 {
946 addActiveOption(id);
947 }
948 }
949 _properties.put(name,value);
950 }
951
952 public Policy getPolicyInstance(ClassLoader cl) throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException,
953 InstantiationException, IllegalAccessException, InvocationTargetException
954 {
955 Class<?> jettyPolicy = cl.loadClass("org.eclipse.jetty.policy.JettyPolicy");
956 Constructor<?> c = jettyPolicy.getConstructor(new Class[]
957 { Set.class, Map.class });
958 Object policyClass = c.newInstance(_policies,_properties);
959
960 if (policyClass instanceof Policy)
961 {
962 Policy p = (Policy)policyClass;
963 p.refresh();
964 return p;
965 }
966
967 throw new ClassCastException("Unable to cast to " + Policy.class.getName() + " : " + policyClass.getClass().getName());
968 }
969
970 public void addActiveOption(String option)
971 {
972 if (!_activeOptions.contains(option))
973 {
974 _activeOptions.add(option);
975 }
976 _properties.put("OPTIONS",join(_activeOptions,","));
977 }
978
979 public Set<String> getActiveOptions()
980 {
981 return _activeOptions;
982 }
983
984 public void removeActiveOption(String option)
985 {
986 _activeOptions.remove(option);
987 _properties.put("OPTIONS",join(_activeOptions,","));
988 }
989
990 private String join(Collection<?> coll, String delim)
991 {
992 StringBuffer buf = new StringBuffer();
993
994 Iterator<?> i = coll.iterator();
995 boolean hasNext = i.hasNext();
996 while (hasNext)
997 {
998 buf.append(String.valueOf(i.next()));
999 hasNext = i.hasNext();
1000 if (hasNext)
1001 buf.append(delim);
1002 }
1003
1004 return buf.toString();
1005 }
1006 }