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