Skip to content

Method: run()

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