/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2009-2016 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2009-2016 Jason Mehrens. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package com.sun.mail.util.logging; import java.io.*; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.UndeclaredThrowableException; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.*; import java.util.logging.*; import java.util.logging.Formatter; /** * An adapter class to allow the Mail API to access the LogManager properties. * The LogManager properties are treated as the root of all properties. First, * the parent properties are searched. If no value is found, then, the * LogManager is searched with prefix value. If not found, then, just the key * itself is searched in the LogManager. If a value is found in the LogManager * it is then copied to this properties object with no key prefix. If no value * is found in the LogManager or the parent properties, then this properties * object is searched only by passing the key value. * *
* This class also emulates the LogManager functions for creating new objects * from string class names. This is to support initial setup of objects such as * log filters, formatters, error managers, etc. * *
* This class should never be exposed outside of this package. Keep this class
* package private (default access).
*
* @author Jason Mehrens
* @since JavaMail 1.4.3
*/
final class LogManagerProperties extends Properties {
/**
* Generated serial id.
*/
private static final long serialVersionUID = -2239983349056806252L;
/**
* Holds the method used to get the LogRecord instant if running on JDK 9 or
* later.
*/
private static final Method LR_GET_INSTANT;
/**
* Holds the method used to get the default time zone if running on JDK 9 or
* later.
*/
private static final Method ZI_SYSTEM_DEFAULT;
/**
* Holds the method used to convert and instant to a zoned date time if
* running on JDK 9 later.
*/
private static final Method ZDT_OF_INSTANT;
static {
Method lrgi = null;
Method zisd = null;
Method zdtoi = null;
try {
lrgi = LogRecord.class.getMethod("getInstant");
assert Comparable.class
.isAssignableFrom(lrgi.getReturnType()) : lrgi;
zisd = findClass("java.time.ZoneId")
.getMethod("systemDefault");
if (!Modifier.isStatic(zisd.getModifiers())) {
throw new NoSuchMethodException(zisd.toString());
}
zdtoi = findClass("java.time.ZonedDateTime")
.getMethod("ofInstant", findClass("java.time.Instant"),
findClass("java.time.ZoneId"));
if (!Modifier.isStatic(zdtoi.getModifiers())) {
throw new NoSuchMethodException(zdtoi.toString());
}
if (!Comparable.class.isAssignableFrom(zdtoi.getReturnType())) {
throw new NoSuchMethodException(zdtoi.toString());
}
} catch (final RuntimeException ignore) {
} catch (final Exception ignore) { //No need for specific catch.
} catch (final LinkageError ignore) {
} finally {
if (lrgi == null || zisd == null || zdtoi == null) {
lrgi = null; //If any are null then clear all.
zisd = null;
zdtoi = null;
}
}
LR_GET_INSTANT = lrgi;
ZI_SYSTEM_DEFAULT = zisd;
ZDT_OF_INSTANT = zdtoi;
}
/**
* Caches the read only reflection class names string array. Declared
* volatile for safe publishing only. The VO_VOLATILE_REFERENCE_TO_ARRAY
* warning is a false positive.
*/
@SuppressWarnings("VolatileArrayField")
private static volatile String[] REFLECT_NAMES;
/**
* Caches the LogManager or Properties so we only read the configuration
* once.
*/
private static final Object LOG_MANAGER = loadLogManager();
/**
* Get the LogManager or loads a Properties object to use as the LogManager.
*
* @return the LogManager or a loaded Properties object.
* @since JavaMail 1.5.3
*/
private static Object loadLogManager() {
Object m;
try {
m = LogManager.getLogManager();
} catch (final LinkageError restricted) {
m = readConfiguration();
} catch (final RuntimeException unexpected) {
m = readConfiguration();
}
return m;
}
/**
* Create a properties object from the default logging configuration file.
* Since the LogManager is not available in restricted environments, only
* the default configuration is applicable.
*
* @return a properties object loaded with the default configuration.
* @since JavaMail 1.5.3
*/
private static Properties readConfiguration() {
/**
* Load the properties file so the default settings are available when
* user code creates a logging object. The class loader for the
* restricted LogManager can't access these classes to attach them to a
* logger or handler on startup. Creating logging objects at this point
* is both useless and risky.
*/
final Properties props = new Properties();
try {
String n = System.getProperty("java.util.logging.config.file");
if (n != null) {
final File f = new File(n).getCanonicalFile();
final InputStream in = new FileInputStream(f);
try {
props.load(in);
} finally {
in.close();
}
}
} catch (final RuntimeException permissionsOrMalformed) {
} catch (final Exception ioe) {
} catch (final LinkageError unexpected) {
}
return props;
}
/**
* Gets LogManger property for the running JVM. If the LogManager doesn't
* exist then the default LogManger properties are used.
*
* @param name the property name.
* @return the LogManager.
* @throws NullPointerException if the given name is null.
* @since JavaMail 1.5.3
*/
static String fromLogManager(final String name) {
if (name == null) {
throw new NullPointerException();
}
final Object m = LOG_MANAGER;
try {
if (m instanceof Properties) {
return ((Properties) m).getProperty(name);
}
} catch (final RuntimeException unexpected) {
}
if (m != null) {
try {
if (m instanceof LogManager) {
return ((LogManager) m).getProperty(name);
}
} catch (final LinkageError restricted) {
} catch (final RuntimeException unexpected) {
}
}
return null;
}
/**
* Check that the current context is trusted to modify the logging
* configuration. This requires LoggingPermission("control").
* @throws SecurityException if a security manager exists and the caller
* does not have {@code LoggingPermission("control")}.
* @since JavaMail 1.5.3
*/
static void checkLogManagerAccess() {
boolean checked = false;
final Object m = LOG_MANAGER;
if (m != null) {
try {
if (m instanceof LogManager) {
checked = true;
((LogManager) m).checkAccess();
}
} catch (final SecurityException notAllowed) {
if (checked) {
throw notAllowed;
}
} catch (final LinkageError restricted) {
} catch (final RuntimeException unexpected) {
}
}
if (!checked) {
checkLoggingAccess();
}
}
/**
* Check that the current context is trusted to modify the logging
* configuration when the LogManager is not present. This requires
* LoggingPermission("control").
* @throws SecurityException if a security manager exists and the caller
* does not have {@code LoggingPermission("control")}.
* @since JavaMail 1.5.3
*/
private static void checkLoggingAccess() {
/**
* Some environments selectively enforce logging permissions by allowing
* access to loggers but not allowing access to handlers. This is an
* indirect way of checking for LoggingPermission when the LogManager is
* not present. The root logger will lazy create handlers so the global
* logger is used instead as it is a known named logger with well
* defined behavior. If the global logger is a subclass then fallback to
* using the SecurityManager.
*/
boolean checked = false;
final Logger global = Logger.getLogger("global");
try {
if (Logger.class == global.getClass()) {
global.removeHandler((Handler) null);
checked = true;
}
} catch (final NullPointerException unexpected) {
}
if (!checked) {
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new LoggingPermission("control", null));
}
}
}
/**
* Determines if access to the {@code java.util.logging.LogManager} class is
* restricted by the class loader.
*
* @return true if a LogManager is present.
* @since JavaMail 1.5.3
*/
static boolean hasLogManager() {
final Object m = LOG_MANAGER;
return m != null && !(m instanceof Properties);
}
/**
* Gets the ZonedDateTime from the given log record.
*
* @param record used to generate the zoned date time.
* @return null if LogRecord doesn't support nanoseconds otherwise a new
* zoned date time is returned.
* @throws NullPointerException if record is null.
* @since JavaMail 1.5.6
*/
@SuppressWarnings("UseSpecificCatch")
static Comparable> getZonedDateTime(LogRecord record) {
if (record == null) {
throw new NullPointerException();
}
final Method m = ZDT_OF_INSTANT;
if (m != null) {
try {
return (Comparable>) m.invoke((Object) null,
LR_GET_INSTANT.invoke(record),
ZI_SYSTEM_DEFAULT.invoke((Object) null));
} catch (final RuntimeException ignore) {
assert LR_GET_INSTANT != null
&& ZI_SYSTEM_DEFAULT != null : ignore;
} catch (final InvocationTargetException ite) {
final Throwable cause = ite.getCause();
if (cause instanceof Error) {
throw (Error) cause;
} else if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else { //Should never happen.
throw new UndeclaredThrowableException(ite);
}
} catch (final Exception ignore) {
}
}
return null;
}
/**
* Gets the local host name from the given service.
*
* @param s the service to examine.
* @return the local host name or null.
* @throws IllegalAccessException if the method is inaccessible.
* @throws InvocationTargetException if the method throws an exception.
* @throws LinkageError if the linkage fails.
* @throws NullPointerException if the given service is null.
* @throws ExceptionInInitializerError if the static initializer fails.
* @throws Exception if there is a problem.
* @throws NoSuchMethodException if the given service does not have a method
* to get the local host name as a string.
* @throws SecurityException if unable to inspect properties of object.
* @since JavaMail 1.5.3
*/
static String getLocalHost(final Object s) throws Exception {
try {
final Method m = s.getClass().getMethod("getLocalHost");
if (!Modifier.isStatic(m.getModifiers())
&& m.getReturnType() == String.class) {
return (String) m.invoke(s);
} else {
throw new NoSuchMethodException(m.toString());
}
} catch (final ExceptionInInitializerError EIIE) {
throw wrapOrThrow(EIIE);
} catch (final InvocationTargetException ite) {
throw paramOrError(ite);
}
}
/**
* Used to parse an ISO-8601 duration format of {@code PnDTnHnMn.nS}.
*
* @param value an ISO-8601 duration character sequence.
* @return the number of milliseconds parsed from the duration.
* @throws ClassNotFoundException if the java.time classes are not present.
* @throws IllegalAccessException if the method is inaccessible.
* @throws InvocationTargetException if the method throws an exception.
* @throws LinkageError if the linkage fails.
* @throws NullPointerException if the given duration is null.
* @throws ExceptionInInitializerError if the static initializer fails.
* @throws Exception if there is a problem.
* @throws NoSuchMethodException if the correct time methods are missing.
* @throws SecurityException if reflective access to the java.time classes
* are not allowed.
* @since JavaMail 1.5.5
*/
static long parseDurationToMillis(final CharSequence value) throws Exception {
try {
final Class> k = findClass("java.time.Duration");
final Method parse = k.getMethod("parse", CharSequence.class);
if (!k.isAssignableFrom(parse.getReturnType())
|| !Modifier.isStatic(parse.getModifiers())) {
throw new NoSuchMethodException(parse.toString());
}
final Method toMillis = k.getMethod("toMillis");
if (!Long.TYPE.isAssignableFrom(toMillis.getReturnType())
|| Modifier.isStatic(toMillis.getModifiers())) {
throw new NoSuchMethodException(toMillis.toString());
}
return (Long) toMillis.invoke(parse.invoke(null, value));
} catch (final ExceptionInInitializerError EIIE) {
throw wrapOrThrow(EIIE);
} catch (final InvocationTargetException ite) {
throw paramOrError(ite);
}
}
/**
* Converts a locale to a language tag.
*
* @param locale the locale to convert.
* @return the language tag.
* @throws NullPointerException if the given locale is null.
* @since JavaMail 1.4.5
*/
static String toLanguageTag(final Locale locale) {
final String l = locale.getLanguage();
final String c = locale.getCountry();
final String v = locale.getVariant();
final char[] b = new char[l.length() + c.length() + v.length() + 2];
int count = l.length();
l.getChars(0, count, b, 0);
if (c.length() != 0 || (l.length() != 0 && v.length() != 0)) {
b[count] = '-';
++count; //be nice to the client compiler.
c.getChars(0, c.length(), b, count);
count += c.length();
}
if (v.length() != 0 && (l.length() != 0 || c.length() != 0)) {
b[count] = '-';
++count; //be nice to the client compiler.
v.getChars(0, v.length(), b, count);
count += v.length();
}
return String.valueOf(b, 0, count);
}
/**
* Creates a new filter from the given class name.
*
* @param name the fully qualified class name.
* @return a new filter.
* @throws ClassCastException if class name does not match the type.
* @throws ClassNotFoundException if the class name was not found.
* @throws IllegalAccessException if the constructor is inaccessible.
* @throws InstantiationException if the given class name is abstract.
* @throws InvocationTargetException if the constructor throws an exception.
* @throws LinkageError if the linkage fails.
* @throws ExceptionInInitializerError if the static initializer fails.
* @throws Exception to match the error method of the ErrorManager.
* @throws NoSuchMethodException if the class name does not have a no
* argument constructor.
* @since JavaMail 1.4.5
*/
static Filter newFilter(String name) throws Exception {
return newObjectFrom(name, Filter.class);
}
/**
* Creates a new formatter from the given class name.
*
* @param name the fully qualified class name.
* @return a new formatter.
* @throws ClassCastException if class name does not match the type.
* @throws ClassNotFoundException if the class name was not found.
* @throws IllegalAccessException if the constructor is inaccessible.
* @throws InstantiationException if the given class name is abstract.
* @throws InvocationTargetException if the constructor throws an exception.
* @throws LinkageError if the linkage fails.
* @throws ExceptionInInitializerError if the static initializer fails.
* @throws Exception to match the error method of the ErrorManager.
* @throws NoSuchMethodException if the class name does not have a no
* argument constructor.
* @since JavaMail 1.4.5
*/
static Formatter newFormatter(String name) throws Exception {
return newObjectFrom(name, Formatter.class);
}
/**
* Creates a new log record comparator from the given class name.
*
* @param name the fully qualified class name.
* @return a new comparator.
* @throws ClassCastException if class name does not match the type.
* @throws ClassNotFoundException if the class name was not found.
* @throws IllegalAccessException if the constructor is inaccessible.
* @throws InstantiationException if the given class name is abstract.
* @throws InvocationTargetException if the constructor throws an exception.
* @throws LinkageError if the linkage fails.
* @throws ExceptionInInitializerError if the static initializer fails.
* @throws Exception to match the error method of the ErrorManager.
* @throws NoSuchMethodException if the class name does not have a no
* argument constructor.
* @since JavaMail 1.4.5
* @see java.util.logging.LogRecord
*/
@SuppressWarnings("unchecked")
static Comparator super LogRecord> newComparator(String name) throws Exception {
return newObjectFrom(name, Comparator.class);
}
/**
* Returns a comparator that imposes the reverse ordering of the specified
* {@link Comparator}. If the given comparator declares a public
* reverseOrder method that method is called first and the return value is
* used. If that method is not declared or the caller does not have access
* then a comparator wrapping the given comparator is returned.
*
* @param