Skip to content

Package: LogManagerProperties$1

LogManagerProperties$1

nameinstructionbranchcomplexitylinemethod
run()
M: 26 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 10 C: 0
0%
M: 1 C: 0
0%
{...}
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%

Coverage

1: /*
2: * Copyright (c) 2009, 2024 Oracle and/or its affiliates. All rights reserved.
3: * Copyright (c) 2009, 2024 Jason Mehrens. All rights reserved.
4: *
5: * This program and the accompanying materials are made available under the
6: * terms of the Eclipse Public License v. 2.0, which is available at
7: * http://www.eclipse.org/legal/epl-2.0.
8: *
9: * This Source Code may also be made available under the following Secondary
10: * Licenses when the conditions for such availability set forth in the
11: * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
12: * version 2 with the GNU Classpath Exception, which is available at
13: * https://www.gnu.org/software/classpath/license.html.
14: *
15: * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
16: */
17:
18: package org.eclipse.angus.mail.util.logging;
19:
20: import java.io.File;
21: import java.io.FileInputStream;
22: import java.io.InputStream;
23: import java.io.ObjectStreamException;
24: import java.lang.reflect.InvocationTargetException;
25: import java.lang.reflect.Method;
26: import java.lang.reflect.Modifier;
27: import java.lang.reflect.UndeclaredThrowableException;
28: import java.security.AccessController;
29: import java.security.PrivilegedAction;
30: import java.util.Collections;
31: import java.util.Comparator;
32: import java.util.Enumeration;
33: import java.util.HashSet;
34: import java.util.Locale;
35: import java.util.Properties;
36: import java.util.logging.ErrorManager;
37: import java.util.logging.Filter;
38: import java.util.logging.Formatter;
39: import java.util.logging.Handler;
40: import java.util.logging.LogManager;
41: import java.util.logging.LogRecord;
42: import java.util.logging.Logger;
43: import java.util.logging.LoggingPermission;
44:
45: /**
46: * An adapter class to allow the Mail API to access the LogManager properties.
47: * The LogManager properties are treated as the root of all properties. First,
48: * the parent properties are searched. If no value is found, then, the
49: * LogManager is searched with prefix value. If not found, then, just the key
50: * itself is searched in the LogManager. If a value is found in the LogManager
51: * it is then copied to this properties object with no key prefix. If no value
52: * is found in the LogManager or the parent properties, then this properties
53: * object is searched only by passing the key value.
54: *
55: * <p>
56: * This class also emulates the LogManager functions for creating new objects
57: * from string class names. This is to support initial setup of objects such as
58: * log filters, formatters, error managers, etc.
59: *
60: * <p>
61: * This class should never be exposed outside of this package. Keep this class
62: * package private (default access).
63: *
64: * @author Jason Mehrens
65: * @since JavaMail 1.4.3
66: */
67: final class LogManagerProperties extends Properties {
68:
69: /**
70: * Generated serial id.
71: */
72: private static final long serialVersionUID = -2239983349056806252L;
73: /**
74: * Holds the method used to get the LogRecord instant if running on JDK 9 or
75: * later.
76: */
77: private static final Method LR_GET_INSTANT;
78:
79: /**
80: * Holds the method used to get the long thread id if running on JDK 16 or
81: * later.
82: */
83: private static final Method LR_GET_LONG_TID;
84:
85: /**
86: * Holds the method used to get the default time zone if running on JDK 9 or
87: * later.
88: */
89: private static final Method ZI_SYSTEM_DEFAULT;
90:
91: /**
92: * Holds the method used to convert and instant to a zoned date time if
93: * running on JDK 9 later.
94: */
95: private static final Method ZDT_OF_INSTANT;
96:
97: /**
98: * MethodHandle is available starting at JDK7 and Android API 26.
99: */
100: static { //Added in JDK16 see JDK-8245302
101: Method lrtid = null;
102: try {
103: lrtid = LogRecord.class.getMethod("getLongThreadID");
104: } catch (final RuntimeException ignore) {
105: } catch (final Exception ignore) { //No need for specific catch.
106: } catch (final LinkageError ignore) {
107: }
108: LR_GET_LONG_TID = lrtid;
109: }
110:
111: static { //Added in JDK9 see JDK-8072645
112: Method lrgi = null;
113: Method zisd = null;
114: Method zdtoi = null;
115: try {
116: lrgi = LogRecord.class.getMethod("getInstant");
117: assert Comparable.class
118: .isAssignableFrom(lrgi.getReturnType()) : lrgi;
119: zisd = findClass("java.time.ZoneId")
120: .getMethod("systemDefault");
121: if (!Modifier.isStatic(zisd.getModifiers())) {
122: throw new NoSuchMethodException(zisd.toString());
123: }
124:
125: zdtoi = findClass("java.time.ZonedDateTime")
126: .getMethod("ofInstant", findClass("java.time.Instant"),
127: findClass("java.time.ZoneId"));
128: if (!Modifier.isStatic(zdtoi.getModifiers())) {
129: throw new NoSuchMethodException(zdtoi.toString());
130: }
131:
132: if (!Comparable.class.isAssignableFrom(zdtoi.getReturnType())) {
133: throw new NoSuchMethodException(zdtoi.toString());
134: }
135: } catch (final RuntimeException | LinkageError ignore) {
136: } catch (final Exception ignore) { //No need for specific catch.
137: } finally {
138: if (lrgi == null || zisd == null || zdtoi == null) {
139: lrgi = null; //If any are null then clear all.
140: zisd = null;
141: zdtoi = null;
142: }
143: }
144:
145: LR_GET_INSTANT = lrgi;
146: ZI_SYSTEM_DEFAULT = zisd;
147: ZDT_OF_INSTANT = zdtoi;
148: }
149:
150: /**
151: * Caches the read only reflection class names string array. Declared
152: * volatile for safe publishing only. The VO_VOLATILE_REFERENCE_TO_ARRAY
153: * warning is a false positive.
154: */
155: @SuppressWarnings("VolatileArrayField")
156: private static volatile String[] REFLECT_NAMES;
157: /**
158: * Caches the LogManager or Properties so we only read the configuration
159: * once.
160: */
161: private static final Object LOG_MANAGER = loadLogManager();
162:
163: /**
164: * Get the LogManager or loads a Properties object to use as the LogManager.
165: *
166: * @return the LogManager or a loaded Properties object.
167: * @since JavaMail 1.5.3
168: */
169: private static Object loadLogManager() {
170: Object m;
171: try {
172: m = LogManager.getLogManager();
173: } catch (final LinkageError | RuntimeException restricted) {
174: m = readConfiguration();
175: }
176: return m;
177: }
178:
179: /**
180: * Create a properties object from the default logging configuration file.
181: * Since the LogManager is not available in restricted environments, only
182: * the default configuration is applicable.
183: *
184: * @return a properties object loaded with the default configuration.
185: * @since JavaMail 1.5.3
186: */
187: private static Properties readConfiguration() {
188: /**
189: * Load the properties file so the default settings are available when
190: * user code creates a logging object. The class loader for the
191: * restricted LogManager can't access these classes to attach them to a
192: * logger or handler on startup. Creating logging objects at this point
193: * is both useless and risky.
194: */
195: final Properties props = new Properties();
196: try {
197: String n = System.getProperty("java.util.logging.config.file");
198: if (n != null) {
199: final File f = new File(n).getCanonicalFile();
200: final InputStream in = new FileInputStream(f);
201: try {
202: props.load(in);
203: } finally {
204: in.close();
205: }
206: }
207: } catch (final LinkageError | Exception permissionsOrMalformed) {
208: }
209: return props;
210: }
211:
212: /**
213: * Gets LogManger property for the running JVM. If the LogManager doesn't
214: * exist then the default LogManger properties are used.
215: *
216: * @param name the property name.
217: * @return the LogManager.
218: * @throws NullPointerException if the given name is null.
219: * @since JavaMail 1.5.3
220: */
221: static String fromLogManager(final String name) {
222: if (name == null) {
223: throw new NullPointerException();
224: }
225:
226: final Object m = LOG_MANAGER;
227: try {
228: if (m instanceof Properties) {
229: return ((Properties) m).getProperty(name);
230: }
231: } catch (final RuntimeException unexpected) {
232: }
233:
234: if (m != null) {
235: try {
236: if (m instanceof LogManager) {
237: return ((LogManager) m).getProperty(name);
238: }
239: } catch (final LinkageError | RuntimeException restricted) {
240: }
241: }
242: return null;
243: }
244:
245: /**
246: * Check that the current context is trusted to modify the logging
247: * configuration. This requires LoggingPermission("control").
248: *
249: * @throws SecurityException if a security manager exists and the caller
250: * does not have {@code LoggingPermission("control")}.
251: * @since JavaMail 1.5.3
252: */
253: static void checkLogManagerAccess() {
254: boolean checked = false;
255: final Object m = LOG_MANAGER;
256: if (m != null) {
257: try {
258: if (m instanceof LogManager) {
259: checked = true;
260: ((LogManager) m).checkAccess();
261: }
262: } catch (final SecurityException notAllowed) {
263: if (checked) {
264: throw notAllowed;
265: }
266: } catch (final LinkageError | RuntimeException restricted) {
267: }
268: }
269:
270: if (!checked) {
271: checkLoggingAccess();
272: }
273: }
274:
275: /**
276: * Check that the current context is trusted to modify the logging
277: * configuration when the LogManager is not present. This requires
278: * LoggingPermission("control").
279: *
280: * @throws SecurityException if a security manager exists and the caller
281: * does not have {@code LoggingPermission("control")}.
282: * @since JavaMail 1.5.3
283: */
284: private static void checkLoggingAccess() {
285: /**
286: * Some environments selectively enforce logging permissions by allowing
287: * access to loggers but not allowing access to handlers. This is an
288: * indirect way of checking for LoggingPermission when the LogManager is
289: * not present. The root logger will lazy create handlers so the global
290: * logger is used instead as it is a known named logger with well
291: * defined behavior. If the global logger is a subclass then fallback to
292: * using the SecurityManager.
293: */
294: boolean checked = false;
295: final Logger global = Logger.getLogger("global");
296: try {
297: if (Logger.class == global.getClass()) {
298: global.removeHandler((Handler) null);
299: checked = true;
300: }
301: } catch (final NullPointerException unexpected) {
302: }
303:
304: if (!checked) {
305: final SecurityManager sm = System.getSecurityManager();
306: if (sm != null) {
307: sm.checkPermission(new LoggingPermission("control", null));
308: }
309: }
310: }
311:
312: /**
313: * Determines if access to the {@code java.util.logging.LogManager} class is
314: * restricted by the class loader.
315: *
316: * @return true if a LogManager is present.
317: * @since JavaMail 1.5.3
318: */
319: static boolean hasLogManager() {
320: final Object m = LOG_MANAGER;
321: return m != null && !(m instanceof Properties);
322: }
323:
324: /**
325: * Gets the ZonedDateTime from the given log record.
326: *
327: * @param record used to generate the zoned date time.
328: * @return null if LogRecord doesn't support nanoseconds otherwise a new
329: * zoned date time is returned.
330: * @throws NullPointerException if record is null.
331: * @since JavaMail 1.5.6
332: */
333: @SuppressWarnings("UseSpecificCatch")
334: static Comparable<?> getZonedDateTime(LogRecord record) {
335: if (record == null) {
336: throw new NullPointerException();
337: }
338: final Method m = ZDT_OF_INSTANT;
339: if (m != null) {
340: try {
341: return (Comparable<?>) m.invoke((Object) null,
342: LR_GET_INSTANT.invoke(record),
343: ZI_SYSTEM_DEFAULT.invoke((Object) null));
344: } catch (final RuntimeException ignore) {
345: assert LR_GET_INSTANT != null
346: && ZI_SYSTEM_DEFAULT != null : ignore;
347: } catch (final InvocationTargetException ite) {
348: final Throwable cause = ite.getCause();
349: if (cause instanceof Error) {
350: throw (Error) cause;
351: } else if (cause instanceof RuntimeException) {
352: throw (RuntimeException) cause;
353: } else { //Should never happen.
354: throw new UndeclaredThrowableException(ite);
355: }
356: } catch (final Exception ignore) {
357: }
358: }
359: return null;
360: }
361:
362: /**
363: * Gets the long thread id from the given log record.
364: *
365: * @param record used to get the long thread id.
366: * @return null if LogRecord doesn't support long thread ids.
367: * @throws NullPointerException if record is null.
368: * @since Angus Mail 2.0.3
369: */
370: static Long getLongThreadID(final LogRecord record) {
371: if (record == null) {
372: throw new NullPointerException();
373: }
374:
375: final Method m = LR_GET_LONG_TID;
376: if (m != null) {
377: try {
378: return (Long) m.invoke(record);
379: } catch (final InvocationTargetException ite) {
380: final Throwable cause = ite.getCause();
381: if (cause instanceof Error) {
382: throw (Error) cause;
383: } else if (cause instanceof RuntimeException) {
384: throw (RuntimeException) cause;
385: } else { //Should never happen.
386: throw new UndeclaredThrowableException(ite);
387: }
388: } catch (final RuntimeException ignore) {
389: } catch (final Exception ignore) {
390: }
391: }
392: return null;
393: }
394:
395: /**
396: * Gets the local host name from the given service.
397: *
398: * @param s the service to examine.
399: * @return the local host name or null.
400: * @throws IllegalAccessException if the method is inaccessible.
401: * @throws InvocationTargetException if the method throws an exception.
402: * @throws LinkageError if the linkage fails.
403: * @throws NullPointerException if the given service is null.
404: * @throws ExceptionInInitializerError if the static initializer fails.
405: * @throws Exception if there is a problem.
406: * @throws NoSuchMethodException if the given service does not have a method
407: * to get the local host name as a string.
408: * @throws SecurityException if unable to inspect properties of object.
409: * @since JavaMail 1.5.3
410: */
411: static String getLocalHost(final Object s) throws Exception {
412: try {
413: final Method m = s.getClass().getMethod("getLocalHost");
414: if (!Modifier.isStatic(m.getModifiers())
415: && m.getReturnType() == String.class) {
416: return (String) m.invoke(s);
417: } else {
418: throw new NoSuchMethodException(m.toString());
419: }
420: } catch (final ExceptionInInitializerError EIIE) {
421: throw wrapOrThrow(EIIE);
422: } catch (final InvocationTargetException ite) {
423: throw paramOrError(ite);
424: }
425: }
426:
427: /**
428: * Used to parse an ISO-8601 duration format of {@code PnDTnHnMn.nS}.
429: *
430: * @param value an ISO-8601 duration character sequence.
431: * @return the number of milliseconds parsed from the duration.
432: * @throws ArithmeticException if the duration is too large or too small.
433: * @throws ClassNotFoundException if the java.time classes are not present.
434: * @throws IllegalAccessException if the method is inaccessible.
435: * @throws InvocationTargetException if the method throws an exception.
436: * @throws LinkageError if the linkage fails.
437: * @throws NullPointerException if the given duration is null.
438: * @throws ExceptionInInitializerError if the static initializer fails.
439: * @throws Exception if there is a problem.
440: * @throws NoSuchMethodException if the correct time methods are missing.
441: * @throws SecurityException if reflective access to the java.time classes
442: * are not allowed.
443: * @since JavaMail 1.5.5
444: */
445: static long parseDurationToMillis(final CharSequence value) throws Exception {
446: if (value == null) {
447: throw new NullPointerException();
448: }
449:
450: try {
451: final Class<?> k = findClass("java.time.Duration");
452: final Method parse = k.getMethod("parse", CharSequence.class);
453: if (!k.isAssignableFrom(parse.getReturnType())
454: || !Modifier.isStatic(parse.getModifiers())) {
455: throw new NoSuchMethodException(parse.toString());
456: }
457:
458: final Method toMillis = k.getMethod("toMillis");
459: if (!Long.TYPE.isAssignableFrom(toMillis.getReturnType())
460: || Modifier.isStatic(toMillis.getModifiers())) {
461: throw new NoSuchMethodException(toMillis.toString());
462: }
463: return (Long) toMillis.invoke(parse.invoke(null, value));
464: } catch (final ExceptionInInitializerError EIIE) {
465: throw wrapOrThrow(EIIE);
466: } catch (final InvocationTargetException ite) {
467: final Throwable cause = ite.getCause();
468: if (cause instanceof ArithmeticException) {
469: throw (ArithmeticException) cause;
470: } else {
471: throw paramOrError(ite);
472: }
473: }
474: }
475:
476: /**
477: * Converts a locale to a language tag.
478: *
479: * @param locale the locale to convert.
480: * @return the language tag.
481: * @throws NullPointerException if the given locale is null.
482: * @since JavaMail 1.4.5
483: */
484: static String toLanguageTag(final Locale locale) {
485: final String l = locale.getLanguage();
486: final String c = locale.getCountry();
487: final String v = locale.getVariant();
488: final char[] b = new char[l.length() + c.length() + v.length() + 2];
489: int count = l.length();
490: l.getChars(0, count, b, 0);
491: if (c.length() != 0 || (l.length() != 0 && v.length() != 0)) {
492: b[count] = '-';
493: ++count; //be nice to the client compiler.
494: c.getChars(0, c.length(), b, count);
495: count += c.length();
496: }
497:
498: if (v.length() != 0 && (l.length() != 0 || c.length() != 0)) {
499: b[count] = '-';
500: ++count; //be nice to the client compiler.
501: v.getChars(0, v.length(), b, count);
502: count += v.length();
503: }
504: return String.valueOf(b, 0, count);
505: }
506:
507: /**
508: * Creates a new filter from the given class name.
509: *
510: * @param name the fully qualified class name.
511: * @return a new filter.
512: * @throws ClassCastException if class name does not match the type.
513: * @throws ClassNotFoundException if the class name was not found.
514: * @throws IllegalAccessException if the constructor is inaccessible.
515: * @throws InstantiationException if the given class name is abstract.
516: * @throws InvocationTargetException if the constructor throws an exception.
517: * @throws LinkageError if the linkage fails.
518: * @throws ExceptionInInitializerError if the static initializer fails.
519: * @throws Exception to match the error method of the ErrorManager.
520: * @throws NoSuchMethodException if the class name does not have a no
521: * argument constructor.
522: * @since JavaMail 1.4.5
523: */
524: static Filter newFilter(String name) throws Exception {
525: return newObjectFrom(name, Filter.class);
526: }
527:
528: /**
529: * Creates a new formatter from the given class name.
530: *
531: * @param name the fully qualified class name.
532: * @return a new formatter.
533: * @throws ClassCastException if class name does not match the type.
534: * @throws ClassNotFoundException if the class name was not found.
535: * @throws IllegalAccessException if the constructor is inaccessible.
536: * @throws InstantiationException if the given class name is abstract.
537: * @throws InvocationTargetException if the constructor throws an exception.
538: * @throws LinkageError if the linkage fails.
539: * @throws ExceptionInInitializerError if the static initializer fails.
540: * @throws Exception to match the error method of the ErrorManager.
541: * @throws NoSuchMethodException if the class name does not have a no
542: * argument constructor.
543: * @since JavaMail 1.4.5
544: */
545: static Formatter newFormatter(String name) throws Exception {
546: return newObjectFrom(name, Formatter.class);
547: }
548:
549: /**
550: * Creates a new log record comparator from the given class name.
551: *
552: * @param name the fully qualified class name.
553: * @return a new comparator.
554: * @throws ClassCastException if class name does not match the type.
555: * @throws ClassNotFoundException if the class name was not found.
556: * @throws IllegalAccessException if the constructor is inaccessible.
557: * @throws InstantiationException if the given class name is abstract.
558: * @throws InvocationTargetException if the constructor throws an exception.
559: * @throws LinkageError if the linkage fails.
560: * @throws ExceptionInInitializerError if the static initializer fails.
561: * @throws Exception to match the error method of the ErrorManager.
562: * @throws NoSuchMethodException if the class name does not have a no
563: * argument constructor.
564: * @see java.util.logging.LogRecord
565: * @since JavaMail 1.4.5
566: */
567: @SuppressWarnings("unchecked")
568: static Comparator<? super LogRecord> newComparator(String name) throws Exception {
569: return newObjectFrom(name, Comparator.class);
570: }
571:
572: /**
573: * Returns a comparator that imposes the reverse ordering of the specified
574: * {@link Comparator}. If the given comparator declares a public
575: * reverseOrder method that method is called first and the return value is
576: * used. If that method is not declared or the caller does not have access
577: * then a comparator wrapping the given comparator is returned.
578: *
579: * @param <T> the element type to be compared
580: * @param c a comparator whose ordering is to be reversed by the returned
581: * comparator
582: * @return A comparator that imposes the reverse ordering of the specified
583: * comparator.
584: * @throws NullPointerException if the given comparator is null.
585: * @since JavaMail 1.5.0
586: */
587: @SuppressWarnings({"unchecked", "ThrowableResultIgnored"})
588: static <T> Comparator<T> reverseOrder(final Comparator<T> c) {
589: if (c == null) {
590: throw new NullPointerException();
591: }
592:
593: Comparator<T> reverse = null;
594: //Comparator in JDK8 has 'reversed' as a default method.
595: //This code calls that method first to allow custom
596: //code to define what reverse order means in versions older than JDK8.
597: try {
598: //assert Modifier.isPublic(c.getClass().getModifiers()) :
599: // Modifier.toString(c.getClass().getModifiers());
600: final Method m = c.getClass().getMethod("reversed");
601: if (!Modifier.isStatic(m.getModifiers())
602: && Comparator.class.isAssignableFrom(m.getReturnType())) {
603: try {
604: reverse = (Comparator<T>) m.invoke(c);
605: } catch (final ExceptionInInitializerError eiie) {
606: throw wrapOrThrow(eiie);
607: }
608: }
609: } catch (final NoSuchMethodException | RuntimeException | IllegalAccessException ignore) {
610: } catch (final InvocationTargetException ite) {
611: paramOrError(ite); //Ignore invocation bugs (returned values).
612: }
613:
614: if (reverse == null) {
615: reverse = Collections.reverseOrder(c);
616: }
617: return reverse;
618: }
619:
620: /**
621: * Creates a new error manager from the given class name.
622: *
623: * @param name the fully qualified class name.
624: * @return a new error manager.
625: * @throws ClassCastException if class name does not match the type.
626: * @throws ClassNotFoundException if the class name was not found.
627: * @throws IllegalAccessException if the constructor is inaccessible.
628: * @throws InstantiationException if the given class name is abstract.
629: * @throws InvocationTargetException if the constructor throws an exception.
630: * @throws LinkageError if the linkage fails.
631: * @throws ExceptionInInitializerError if the static initializer fails.
632: * @throws Exception to match the error method of the ErrorManager.
633: * @throws NoSuchMethodException if the class name does not have a no
634: * argument constructor.
635: * @since JavaMail 1.4.5
636: */
637: static ErrorManager newErrorManager(String name) throws Exception {
638: return newObjectFrom(name, ErrorManager.class);
639: }
640:
641: /**
642: * Determines if the given class name identifies a utility class.
643: *
644: * @param name the fully qualified class name.
645: * @return true if the given class name
646: * @throws ClassNotFoundException if the class name was not found.
647: * @throws IllegalAccessException if the constructor is inaccessible.
648: * @throws LinkageError if the linkage fails.
649: * @throws ExceptionInInitializerError if the static initializer fails.
650: * @throws Exception to match the error method of the ErrorManager.
651: * @throws SecurityException if unable to inspect properties of class.
652: * @since JavaMail 1.5.2
653: */
654: static boolean isStaticUtilityClass(String name) throws Exception {
655: final Class<?> c = findClass(name);
656: final Class<?> obj = Object.class;
657: Method[] methods;
658: boolean util;
659: if (c != obj && (methods = c.getMethods()).length != 0) {
660: util = true;
661: for (Method m : methods) {
662: if (m.getDeclaringClass() != obj
663: && !Modifier.isStatic(m.getModifiers())) {
664: util = false;
665: break;
666: }
667: }
668: } else {
669: util = false;
670: }
671: return util;
672: }
673:
674: /**
675: * Determines if the given class name is a reflection class name responsible
676: * for invoking methods and or constructors.
677: *
678: * @param name the fully qualified class name.
679: * @return true if the given class name
680: * @throws ClassNotFoundException if the class name was not found.
681: * @throws IllegalAccessException if the constructor is inaccessible.
682: * @throws LinkageError if the linkage fails.
683: * @throws ExceptionInInitializerError if the static initializer fails.
684: * @throws Exception to match the error method of the ErrorManager.
685: * @throws SecurityException if unable to inspect properties of class.
686: * @since JavaMail 1.5.2
687: */
688: static boolean isReflectionClass(String name) throws Exception {
689: String[] names = REFLECT_NAMES;
690: if (names == null) { //Benign data race.
691: REFLECT_NAMES = names = reflectionClassNames();
692: }
693:
694: for (String rf : names) { //The set of names is small.
695: if (name.equals(rf)) {
696: return true;
697: }
698: }
699:
700: findClass(name); //Fail late instead of normal return.
701: return false;
702: }
703:
704: /**
705: * Determines all of the reflection class names used to invoke methods.
706: *
707: * This method performs indirect and direct calls on a throwable to capture
708: * the standard class names and the implementation class names.
709: *
710: * @return a string array containing the fully qualified class names.
711: * @throws Exception if there is a problem.
712: */
713: private static String[] reflectionClassNames() throws Exception {
714: final Class<?> thisClass = LogManagerProperties.class;
715: assert Modifier.isFinal(thisClass.getModifiers()) : thisClass;
716: try {
717: //This code must use reflection to capture extra frames.
718: //The invoke API doesn't produce the frames needed.
719: final HashSet<String> traces = new HashSet<>();
720: Throwable t = Throwable.class.getConstructor().newInstance();
721: for (StackTraceElement ste : t.getStackTrace()) {
722: if (!thisClass.getName().equals(ste.getClassName())) {
723: traces.add(ste.getClassName());
724: } else {
725: break;
726: }
727: }
728:
729: //This code must use reflection to capture extra frames.
730: //The invoke API doesn't produce the frames needed.
731: Throwable.class.getMethod("fillInStackTrace").invoke(t);
732: for (StackTraceElement ste : t.getStackTrace()) {
733: if (!thisClass.getName().equals(ste.getClassName())) {
734: traces.add(ste.getClassName());
735: } else {
736: break;
737: }
738: }
739: return traces.toArray(new String[0]);
740: } catch (final InvocationTargetException ITE) {
741: throw paramOrError(ITE);
742: }
743: }
744:
745: /**
746: * Creates a new object from the given class name.
747: *
748: * @param <T> The generic class type.
749: * @param name the fully qualified class name.
750: * @param type the assignable type for the given name.
751: * @return a new object assignable to the given type.
752: * @throws ClassCastException if class name does not match the type.
753: * @throws ClassNotFoundException if the class name was not found.
754: * @throws IllegalAccessException if the constructor is inaccessible.
755: * @throws InstantiationException if the given class name is abstract.
756: * @throws InvocationTargetException if the constructor throws an exception.
757: * @throws LinkageError if the linkage fails.
758: * @throws ExceptionInInitializerError if the static initializer fails.
759: * @throws Exception to match the error method of the ErrorManager.
760: * @throws NoSuchMethodException if the class name does not have a no
761: * argument constructor.
762: * @since JavaMail 1.4.5
763: */
764: static <T> T newObjectFrom(String name, Class<T> type) throws Exception {
765: try {
766: final Class<?> clazz = LogManagerProperties.findClass(name);
767: //This check avoids additional side effects when the name parameter
768: //is a literal name and not a class name.
769: if (type.isAssignableFrom(clazz)) {
770: try {
771: return type.cast(clazz.getConstructor().newInstance());
772: } catch (final InvocationTargetException ITE) {
773: throw paramOrError(ITE);
774: }
775: } else {
776: throw new ClassCastException(clazz.getName()
777: + " cannot be cast to " + type.getName());
778: }
779: } catch (final NoClassDefFoundError NCDFE) {
780: //No class def found can occur on filesystems that are
781: //case insensitive (BUG ID 6196068). In some cases, we allow class
782: //names or literal names, this code guards against the case where a
783: //literal name happens to match a class name in a different case.
784: //This is also a nice way to adapt this error for the error manager.
785: throw new ClassNotFoundException(NCDFE.toString(), NCDFE);
786: } catch (final ExceptionInInitializerError EIIE) {
787: throw wrapOrThrow(EIIE);
788: }
789: }
790:
791: /**
792: * Returns the given exception or throws the escaping cause.
793: *
794: * @param ite any invocation target.
795: * @return the exception.
796: * @throws VirtualMachineError if present as cause.
797: * @throws ThreadDeath if present as cause.
798: * @since JavaMail 1.4.5
799: */
800: private static Exception paramOrError(InvocationTargetException ite) {
801: final Throwable cause = ite.getCause();
802: if (cause != null) {
803: //Bitwise inclusive OR produces tighter bytecode for instanceof
804: //and matches with multicatch syntax.
805: if (cause instanceof VirtualMachineError
806: | cause instanceof ThreadDeath) {
807: throw (Error) cause;
808: }
809: }
810: return ite;
811: }
812:
813: /**
814: * Throws the given error if the cause is an error otherwise the given error
815: * is wrapped.
816: *
817: * @param eiie the error.
818: * @return an InvocationTargetException.
819: * @since JavaMail 1.5.0
820: */
821: private static InvocationTargetException wrapOrThrow(
822: ExceptionInInitializerError eiie) {
823: //This linkage error will escape the constructor new instance call.
824: //If the cause is an error, rethrow to skip any error manager.
825: if (eiie.getCause() instanceof Error) {
826: throw eiie;
827: } else {
828: //Considered a bug in the code, wrap the error so it can be
829: //reported to the error manager.
830: return new InvocationTargetException(eiie);
831: }
832: }
833:
834: /**
835: * This code is modified from the LogManager, which explicitly states
836: * searching the system class loader first, then the context class loader.
837: * There is resistance (compatibility) to change this behavior to simply
838: * searching the context class loader. See JDK-6878454.
839: *
840: * @param name full class name
841: * @return the class.
842: * @throws LinkageError if the linkage fails.
843: * @throws ClassNotFoundException if the class name was not found.
844: * @throws ExceptionInInitializerError if static initializer fails.
845: */
846: private static Class<?> findClass(String name) throws ClassNotFoundException {
847: ClassLoader[] loaders = getClassLoaders();
848: assert loaders.length == 2 : loaders.length;
849: Class<?> clazz;
850: if (loaders[0] != null) {
851: try {
852: clazz = Class.forName(name, false, loaders[0]);
853: } catch (ClassNotFoundException tryContext) {
854: clazz = tryLoad(name, loaders[1]);
855: }
856: } else {
857: clazz = tryLoad(name, loaders[1]);
858: }
859: return clazz;
860: }
861:
862: /**
863: * Loads a class using the given loader or the class loader of this class.
864: *
865: * @param name the class name.
866: * @param l any class loader or null.
867: * @return the raw class.
868: * @throws ClassNotFoundException if not found.
869: */
870: private static Class<?> tryLoad(String name, ClassLoader l) throws ClassNotFoundException {
871: if (l != null) {
872: return Class.forName(name, false, l);
873: } else {
874: return Class.forName(name);
875: }
876: }
877:
878: /**
879: * Gets the class loaders using elevated privileges.
880: *
881: * @return any array of class loaders. Indexes may be null.
882: */
883: private static ClassLoader[] getClassLoaders() {
884: return AccessController.doPrivileged(new PrivilegedAction<ClassLoader[]>() {
885:
886: @SuppressWarnings("override") //JDK-6954234
887: public ClassLoader[] run() {
888: final ClassLoader[] loaders = new ClassLoader[2];
889: try {
890: loaders[0] = ClassLoader.getSystemClassLoader();
891: } catch (SecurityException ignore) {
892: loaders[0] = null;
893: }
894:
895: try {
896: loaders[1] = Thread.currentThread().getContextClassLoader();
897: } catch (SecurityException ignore) {
898: loaders[1] = null;
899: }
900: return loaders;
901: }
902: });
903: }
904:
905: /**
906: * The namespace prefix to search LogManager and defaults.
907: */
908: private final String prefix;
909:
910: /**
911: * Creates a log manager properties object.
912: *
913: * @param parent the parent properties.
914: * @param prefix the namespace prefix.
915: * @throws NullPointerException if <code>prefix</code> or
916: * <code>parent</code> is <code>null</code>.
917: */
918: LogManagerProperties(final Properties parent, final String prefix) {
919: super(parent);
920: if (parent == null || prefix == null) {
921: throw new NullPointerException();
922: }
923: this.prefix = prefix;
924: }
925:
926: /**
927: * Returns a properties object that contains a snapshot of the current
928: * state. This method violates the clone contract so that no instances of
929: * LogManagerProperties is exported for public use.
930: *
931: * @return the snapshot.
932: * @since JavaMail 1.4.4
933: */
934: @Override
935: @SuppressWarnings("CloneDoesntCallSuperClone")
936: public synchronized Object clone() {
937: return exportCopy(defaults);
938: }
939:
940: /**
941: * Searches defaults, then searches the log manager if available or the
942: * system properties by the prefix property, and then by the key itself.
943: *
944: * @param key a non null key.
945: * @return the value for that key.
946: */
947: @Override
948: public synchronized String getProperty(final String key) {
949: String value = defaults.getProperty(key);
950: if (value == null) {
951: if (key.length() > 0) {
952: value = fromLogManager(prefix + '.' + key);
953: }
954:
955: if (value == null) {
956: value = fromLogManager(key);
957: }
958:
959: /**
960: * Copy the log manager properties as we read them. If a value is no
961: * longer present in the LogManager read it from here. The reason
962: * this works is because LogManager.reset() closes all attached
963: * handlers therefore, stale values only exist in closed handlers.
964: */
965: if (value != null) {
966: super.put(key, value);
967: } else {
968: Object v = super.get(key); //defaults are not used.
969: value = v instanceof String ? (String) v : null;
970: }
971: }
972: return value;
973: }
974:
975: /**
976: * Calls getProperty directly. If getProperty returns null the default value
977: * is returned.
978: *
979: * @param key a key to search for.
980: * @param def the default value to use if not found.
981: * @return the value for the key.
982: * @since JavaMail 1.4.4
983: */
984: @Override
985: public String getProperty(final String key, final String def) {
986: final String value = this.getProperty(key);
987: return value == null ? def : value;
988: }
989:
990: /**
991: * Required to work with PropUtil. Calls getProperty directly if the given
992: * key is a string. Otherwise, performs a get operation on the defaults
993: * followed by the normal hash table get.
994: *
995: * @param key any key.
996: * @return the value for the key or null.
997: * @since JavaMail 1.4.5
998: */
999: @Override
1000: public synchronized Object get(final Object key) {
1001: Object value;
1002: if (key instanceof String) {
1003: value = getProperty((String) key);
1004: } else {
1005: value = null;
1006: }
1007:
1008: //Search for non-string value.
1009: if (value == null) {
1010: value = defaults.get(key);
1011: if (value == null && !defaults.containsKey(key)) {
1012: value = super.get(key);
1013: }
1014: }
1015: return value;
1016: }
1017:
1018: /**
1019: * Required to work with PropUtil. An updated copy of the key is fetched
1020: * from the log manager if the key doesn't exist in this properties.
1021: *
1022: * @param key any key.
1023: * @return the value for the key or the default value for the key.
1024: * @since JavaMail 1.4.5
1025: */
1026: @Override
1027: public synchronized Object put(final Object key, final Object value) {
1028: if (key instanceof String && value instanceof String) {
1029: final Object def = preWrite(key);
1030: final Object man = super.put(key, value);
1031: return man == null ? def : man;
1032: } else {
1033: return super.put(key, value);
1034: }
1035: }
1036:
1037: /**
1038: * Calls the put method directly.
1039: *
1040: * @param key any key.
1041: * @return the value for the key or the default value for the key.
1042: * @since JavaMail 1.4.5
1043: */
1044: @Override
1045: public Object setProperty(String key, String value) {
1046: return this.put(key, value);
1047: }
1048:
1049: /**
1050: * Required to work with PropUtil. An updated copy of the key is fetched
1051: * from the log manager prior to returning.
1052: *
1053: * @param key any key.
1054: * @return the value for the key or null.
1055: * @since JavaMail 1.4.5
1056: */
1057: @Override
1058: public synchronized boolean containsKey(final Object key) {
1059: boolean found = key instanceof String
1060: && getProperty((String) key) != null;
1061: if (!found) {
1062: found = defaults.containsKey(key) || super.containsKey(key);
1063: }
1064: return found;
1065: }
1066:
1067: /**
1068: * Required to work with PropUtil. An updated copy of the key is fetched
1069: * from the log manager if the key doesn't exist in this properties.
1070: *
1071: * @param key any key.
1072: * @return the value for the key or the default value for the key.
1073: * @since JavaMail 1.4.5
1074: */
1075: @Override
1076: public synchronized Object remove(final Object key) {
1077: final Object def = preWrite(key);
1078: final Object man = super.remove(key);
1079: return man == null ? def : man;
1080: }
1081:
1082: /**
1083: * It is assumed that this method will never be called. No way to get the
1084: * property names from LogManager.
1085: *
1086: * @return the property names
1087: */
1088: @Override
1089: public Enumeration<?> propertyNames() {
1090: assert false;
1091: return super.propertyNames();
1092: }
1093:
1094: /**
1095: * It is assumed that this method will never be called. The prefix value is
1096: * not used for the equals method.
1097: *
1098: * @param o any object or null.
1099: * @return true if equal, otherwise false.
1100: */
1101: @Override
1102: public boolean equals(final Object o) {
1103: if (o == null) {
1104: return false;
1105: }
1106: if (o == this) {
1107: return true;
1108: }
1109: if (o instanceof Properties == false) {
1110: return false;
1111: }
1112: assert false : prefix;
1113: return super.equals(o);
1114: }
1115:
1116: /**
1117: * It is assumed that this method will never be called. See equals.
1118: *
1119: * @return the hash code.
1120: */
1121: @Override
1122: public int hashCode() {
1123: assert false : prefix.hashCode();
1124: return super.hashCode();
1125: }
1126:
1127: /**
1128: * Called before a write operation of a key. Caches a key read from the log
1129: * manager in this properties object. The key is only cached if it is an
1130: * instance of a String and this properties doesn't contain a copy of the
1131: * key.
1132: *
1133: * @param key the key to search.
1134: * @return the default value for the key.
1135: */
1136: private Object preWrite(final Object key) {
1137: assert Thread.holdsLock(this);
1138: return get(key);
1139: }
1140:
1141: /**
1142: * Creates a public snapshot of this properties object using the given
1143: * parent properties.
1144: *
1145: * @param parent the defaults to use with the snapshot.
1146: * @return the safe snapshot.
1147: */
1148: private Properties exportCopy(final Properties parent) {
1149: Thread.holdsLock(this);
1150: final Properties child = new Properties(parent);
1151: child.putAll(this);
1152: return child;
1153: }
1154:
1155: /**
1156: * It is assumed that this method will never be called. We return a safe
1157: * copy for export to avoid locking this properties object or the defaults
1158: * during write.
1159: *
1160: * @return the parent properties.
1161: * @throws ObjectStreamException if there is a problem.
1162: */
1163: private synchronized Object writeReplace() throws ObjectStreamException {
1164: assert false;
1165: return exportCopy((Properties) defaults.clone());
1166: }
1167: }