1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.river.tool;
20
21 import java.io.BufferedInputStream;
22 import java.io.BufferedReader;
23 import java.io.ByteArrayInputStream;
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.io.FileNotFoundException;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.InputStreamReader;
30 import java.io.PrintStream;
31 import java.io.Reader;
32 import java.lang.reflect.Array;
33 import java.lang.reflect.Constructor;
34 import java.lang.reflect.InvocationTargetException;
35 import java.lang.reflect.Method;
36 import java.net.MalformedURLException;
37 import java.net.URL;
38 import java.net.URLClassLoader;
39 import java.text.MessageFormat;
40 import java.util.Arrays;
41 import java.util.Enumeration;
42 import java.util.MissingResourceException;
43 import java.util.Properties;
44 import java.util.ResourceBundle;
45 import java.util.Set;
46 import java.util.StringTokenizer;
47 import net.jini.config.Configuration;
48 import net.jini.config.ConfigurationException;
49 import net.jini.config.ConfigurationFile;
50 import net.jini.config.ConfigurationProvider;
51 import org.apache.river.api.net.RFC3986URLClassLoader;
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
142
143
144
145 public class CheckConfigurationFile {
146
147
148 private static final Class[] primitives = {
149 Boolean.TYPE, Byte.TYPE, Character.TYPE, Short.TYPE, Integer.TYPE,
150 Long.TYPE, Float.TYPE, Double.TYPE
151 };
152
153 private static final String RESOURCE =
154 "META-INF/services/" + Configuration.class.getName();
155
156 private static ResourceBundle resources;
157 private static boolean resinit = false;
158
159
160 private CheckConfigurationFile() { }
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220 public static void main(String[] args) {
221 if (args.length == 0) {
222 usage();
223 } else if (args.length == 1 && "-help".equals(args[0])) {
224 print(System.err, "checkconfig.usage", File.pathSeparator);
225 return;
226 }
227 String classPath = null;
228 String entriesPath = null;
229 boolean stdin = false;
230 int i = 0;
231 while (i < args.length) {
232 if ("-cp".equals(args[i])) {
233 if (args.length < i + 2) {
234 usage();
235 }
236 classPath = args[i + 1];
237 i += 2;
238 } else if ("-entries".equals(args[i])) {
239 if (args.length < i + 2) {
240 usage();
241 }
242 entriesPath = args[i + 1];
243 i += 2;
244 } else if ("-stdin".equals(args[i])) {
245 stdin = true;
246 i++;
247 } else {
248 break;
249 }
250 }
251 if (!stdin && i == args.length) {
252 usage();
253 }
254 ClassLoader loader = ClassLoader.getSystemClassLoader();
255 if (classPath != null) {
256 loader = loader.getParent();
257 }
258 String[] configOptions = new String[args.length - i];
259 System.arraycopy(args, i, configOptions, 0, configOptions.length);
260 boolean ok = check(classPath, loader, stdin, configOptions,
261 entriesPath, System.err);
262 if (!ok) {
263 System.exit(1);
264 }
265 }
266
267 private static void usage() {
268 print(System.err, "checkconfig.usage", File.pathSeparator);
269 System.exit(1);
270 }
271
272
273
274
275
276 private static Properties getEntries(String files, PrintStream err) {
277 Properties entries = new Properties();
278 StringTokenizer tokens =
279 new StringTokenizer(files, File.pathSeparator);
280 while (tokens.hasMoreTokens()) {
281 String file = tokens.nextToken();
282 InputStream in = null;
283 try {
284 in = new BufferedInputStream(new FileInputStream(file));
285 entries.load(in);
286 } catch (FileNotFoundException e) {
287 print(err, "checkconfig.notfound", file);
288 return null;
289 } catch (Throwable t) {
290 print(err, "checkconfig.read.err",
291 new String[]{file, t.getClass().getName(),
292 t.getLocalizedMessage()});
293 t.printStackTrace(err);
294 return null;
295 } finally {
296 if (in != null) {
297 try {
298 in.close();
299 } catch (IOException e) {
300 }
301 }
302 }
303 }
304 return entries;
305 }
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349 public static boolean check(String classPath,
350 ClassLoader loader,
351 String[] configOptions,
352 String entriesPath,
353 PrintStream err)
354 {
355 if (loader == null || configOptions == null || err == null) {
356 throw new NullPointerException();
357 }
358 return check(classPath, loader, false, configOptions, entriesPath,
359 err);
360 }
361
362
363
364
365
366
367
368 private static boolean check(String classPath,
369 ClassLoader loader,
370 boolean stdin,
371 String[] configOptions,
372 String entriesPath,
373 PrintStream err)
374 {
375 if (classPath != null) {
376 StringTokenizer st = new StringTokenizer(classPath,
377 File.pathSeparator);
378 URL[] urls = new URL[st.countTokens()];
379 for (int i = 0; st.hasMoreTokens(); i++) {
380 String elt = st.nextToken();
381 try {
382 urls[i] = new File(elt).toURI().toURL();
383 } catch (MalformedURLException e) {
384 print(err, "checkconfig.classpath", elt);
385 return false;
386 }
387 }
388 loader = RFC3986URLClassLoader.newInstance(urls, loader);
389 }
390 Properties entries = null;
391 if (entriesPath != null) {
392 entries = getEntries(entriesPath, err);
393 if (entries == null) {
394 return false;
395 }
396 }
397 String location;
398 if (configOptions.length == 0) {
399 location = "(stdin)";
400 } else {
401 configOptions = (String[]) configOptions.clone();
402 location = configOptions[0];
403 configOptions[0] = "-";
404 }
405 Constructor configCons = getProviderConstructor(loader, err);
406 if (configCons == null) {
407 return false;
408 }
409 try {
410 InputStream in;
411 if (stdin) {
412 in = System.in;
413 } else if ("-".equals(location)) {
414 in = new ByteArrayInputStream(new byte[0]);
415 } else {
416 try {
417 URL url = new URL(location);
418 in = url.openStream();
419 } catch (MalformedURLException e) {
420 in = new FileInputStream(location);
421 }
422 }
423 Object config;
424 try {
425 config = configCons.newInstance(
426 new Object[]{new InputStreamReader(in),
427 configOptions,
428 loader});
429 } finally {
430 if (!stdin) {
431 try {
432 in.close();
433 } catch (IOException e) {
434 }
435 }
436 }
437 return check(config, entries, loader, err);
438 } catch (FileNotFoundException e) {
439 print(err, "checkconfig.notfound", location);
440 } catch (Throwable t) {
441 print(err, "checkconfig.read", location, t);
442 }
443 return false;
444 }
445
446
447
448
449
450 private static Constructor getProviderConstructor(ClassLoader loader,
451 PrintStream err)
452 {
453 URL resource = null;
454 try {
455 for (Enumeration providers = loader.getResources(RESOURCE);
456 providers.hasMoreElements(); )
457 {
458 resource = (URL) providers.nextElement();
459 }
460 } catch (IOException e) {
461 print(err, "checkconfig.resources", "", e);
462 }
463 String classname = (resource == null ?
464 ConfigurationFile.class.getName() :
465 getProviderName(resource, err));
466 if (classname == null) {
467 return null;
468 }
469 try {
470 Class provider = Class.forName(classname, false, loader);
471 try {
472 if (!Class.forName(ConfigurationFile.class.getName(),
473 false, loader).isAssignableFrom(provider))
474 {
475 print(err, "checkconfig.notsubclass", classname);
476 return null;
477 }
478 return provider.getConstructor(new Class[]{Reader.class,
479 String[].class,
480 ClassLoader.class});
481 } catch (ClassNotFoundException e) {
482 print(err, "checkconfig.notsubclass", classname);
483 } catch (NoSuchMethodException e) {
484 print(err, "checkconfig.noconstructor", classname);
485 }
486 } catch (ClassNotFoundException e) {
487 print(err, "checkconfig.noprovider", classname);
488 } catch (Throwable t) {
489 print(err, "checkconfig.provider", classname, t);
490 }
491 return null;
492 }
493
494
495
496
497
498 private static String getProviderName(URL url, PrintStream err) {
499 InputStream in = null;
500 try {
501 in = url.openStream();
502 BufferedReader reader =
503 new BufferedReader(new InputStreamReader(in, "utf-8"));
504 String result = null;
505 while (true) {
506 String line = reader.readLine();
507 if (line == null) {
508 break;
509 }
510 int commentPos = line.indexOf('#');
511 if (commentPos >= 0) {
512 line = line.substring(0, commentPos);
513 }
514 line = line.trim();
515 int len = line.length();
516 if (len != 0) {
517 if (result != null) {
518 print(err, "checkconfig.multiproviders",
519 url.toString());
520 return null;
521 }
522 result = line;
523 }
524 }
525 if (result == null) {
526 print(err, "checkconfig.missingprovider", url.toString());
527 return null;
528 }
529 return result;
530 } catch (IOException e) {
531 print(err, "configconfig.read", url.toString(), e);
532 return null;
533 } finally {
534 if (in != null) {
535 try {
536 in.close();
537 } catch (IOException e) {
538 }
539 }
540 }
541 }
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561 public static boolean check(ConfigurationFile config,
562 Properties entries,
563 ClassLoader loader,
564 PrintStream err)
565 {
566 if (config == null || loader == null || err == null) {
567 throw new NullPointerException();
568 }
569 return check((Object) config, entries, loader, err);
570 }
571
572
573
574
575
576 private static boolean check(Object config,
577 Properties entries,
578 ClassLoader loader,
579 PrintStream err)
580 {
581 Method getType;
582 String[] entryNames;
583 try {
584 getType = config.getClass().getMethod("getEntryType",
585 new Class[]{String.class,
586 String.class});
587 Method getNames = config.getClass().getMethod("getEntryNames",
588 null);
589 Set entrySet = (Set) getNames.invoke(config, new Object[0]);
590 entryNames =
591 (String[]) entrySet.toArray(new String[entrySet.size()]);
592 } catch (Throwable t) {
593 print(err, "checkconfig.unexpected", "", t);
594 return false;
595 }
596 Arrays.sort(entryNames);
597 boolean ok = true;
598 for (int i = 0; i < entryNames.length; i++) {
599 String entryName = entryNames[i];
600 String expectedTypeName = entries != null
601 ? entries.getProperty(entryName) : null;
602 if (entries != null && expectedTypeName == null) {
603 print(err, "checkconfig.unknown", entryName);
604 ok = false;
605 }
606 try {
607 int dot = entryName.lastIndexOf('.');
608 String component = entryName.substring(0, dot);
609 String name = entryName.substring(dot + 1);
610 Class type = (Class) getType.invoke(config,
611 new Object[]{component,
612 name});
613 if (expectedTypeName != null) {
614 try {
615 Class expectedType =
616 findClass(expectedTypeName, loader);
617 if (!isAssignableFrom(expectedType, type)) {
618 print(err, "checkconfig.mismatch",
619 new String[]{entryName, typeName(type),
620 typeName(expectedType)});
621 ok = false;
622 }
623 } catch (ClassNotFoundException e) {
624 print(err, "checkconfig.expect.fail",
625 new String[]{entryName, e.getMessage()});
626 ok = false;
627 } catch (Throwable t) {
628 print(err, "checkconfig.expect.err",
629 new String[]{entryName, expectedTypeName,
630 t.getClass().getName(),
631 t.getLocalizedMessage()});
632 t.printStackTrace(err);
633 ok = false;
634 }
635 }
636 } catch (Throwable t) {
637 print(err, "checkconfig.actual", entryName, t);
638 ok = false;
639 }
640 }
641 return ok;
642 }
643
644
645
646
647
648
649
650
651 private static Class findClass(String name, ClassLoader loader)
652 throws ClassNotFoundException
653 {
654 if (name.indexOf('.') < 0) {
655 for (int i = primitives.length; --i >= 0; ) {
656 if (name.equals(primitives[i].getName())) {
657 return primitives[i];
658 }
659 }
660 }
661 try {
662 return Class.forName(name, false, loader);
663 } catch (ClassNotFoundException notFound) {
664 int bracket = name.indexOf('[');
665 if (bracket > 0) {
666
667 int dims = 0;
668 int len = name.length();
669 for (int i = bracket; i < len; i += 2, dims++) {
670 if (name.charAt(i) != '['
671 || i + 1 >= len
672 || name.charAt(i + 1) != ']')
673 {
674
675 throw notFound;
676 }
677 }
678 try {
679 Class base = findClass(name.substring(0, bracket), loader);
680 return Array.newInstance(base, new int[dims]).getClass();
681 } catch (ClassNotFoundException e) {
682 }
683 } else if (name.indexOf('.') < 0) {
684
685 try {
686 return findClass("java.lang." + name, loader);
687 } catch (ClassNotFoundException e) {
688 }
689 } else {
690
691 int dot;
692 while ((dot = name.lastIndexOf('.')) >= 0) {
693 name = name.substring(0, dot) + '$' +
694 name.substring(dot + 1);
695 try {
696 return Class.forName(name, false, loader);
697 } catch (ClassNotFoundException e) {
698 }
699 }
700 }
701 throw notFound;
702 }
703 }
704
705
706 private static String typeName(Class type) {
707 if (type == null) {
708 return "null";
709 }
710 StringBuffer buf = new StringBuffer();
711 if (type.isArray()) {
712 Class component;
713 while ((component = type.getComponentType()) != null) {
714 buf.append("[]");
715 type = component;
716 }
717 }
718 return type.getName().replace('$', '.') + buf;
719 }
720
721
722
723
724
725 private static boolean isAssignableFrom(Class dest, Class source) {
726 if (dest.isPrimitive()) {
727 return source == dest;
728 } else {
729 return source == null || dest.isAssignableFrom(source);
730 }
731 }
732
733
734
735
736 private static synchronized String getString(String key, PrintStream err) {
737 if (!resinit) {
738 try {
739 resinit = true;
740 resources = ResourceBundle.getBundle(
741 "org.apache.river.tool.resources.checkconfig");
742 } catch (MissingResourceException e) {
743 e.printStackTrace(err);
744 }
745 }
746 try {
747 return resources != null ? resources.getString(key) : null;
748 } catch (MissingResourceException e) {
749 return null;
750 }
751 }
752
753
754
755
756
757
758
759
760 private static void print(PrintStream err,
761 String keyPrefix,
762 String source,
763 Throwable t)
764 {
765 if (t instanceof InvocationTargetException) {
766 t = t.getCause();
767 }
768 if (t.getClass().getName().equals(
769 ConfigurationException.class.getName()))
770 {
771 if (t.getCause() == null) {
772 print(err, keyPrefix + ".fail",
773 new String[]{source, t.getLocalizedMessage()});
774 return;
775 } else {
776 t = t.getCause();
777 }
778 }
779 print(err, keyPrefix + ".err",
780 new String[]{source, t.getClass().getName(),
781 t.getLocalizedMessage()});
782 t.printStackTrace(err);
783 }
784
785 private static void print(PrintStream err, String key, String val) {
786 print(err, key, new String[]{val});
787 }
788
789 private static void print(PrintStream err, String key, String[] vals) {
790 String fmt = getString(key, err);
791 if (fmt == null)
792 fmt = "no text found: \"" + key + "\" {0}";
793 err.println(MessageFormat.format(fmt, vals));
794 }
795 }