From 86ee859eb161c17cb175068daf622fa4bed62262 Mon Sep 17 00:00:00 2001 From: jmehrens Date: Thu, 25 Jan 2024 01:29:51 -0600 Subject: [PATCH] Test failure with java-21 on LogManagerPropertiesTest #700 (#704) Signed-off-by: jmehrens --- doc/release/CHANGES.txt | 10 +- logging/src/main/java/FileErrorManager.java | 14 +- logging/src/main/java/MailHandlerDemo.java | 17 +- .../util/logging/LogManagerProperties.java | 119 +- .../sun/mail/util/logging/MailHandler.java | 9 +- .../mail/util/logging/AbstractLogging.java | 870 +++++++------- .../logging/LogManagerPropertiesTest.java | 220 ++-- .../mail/util/logging/MailHandlerTest.java | 1053 ++--------------- 8 files changed, 770 insertions(+), 1542 deletions(-) diff --git a/doc/release/CHANGES.txt b/doc/release/CHANGES.txt index 376ab076a..c561f0751 100644 --- a/doc/release/CHANGES.txt +++ b/doc/release/CHANGES.txt @@ -19,6 +19,12 @@ Bug IDs that start with "G" can be found in the GlassFish Issue Tracker Seven digit bug numbers are from the old Sun bug database, which is no longer available. + CHANGES IN THE 1.6.8 RELEASE + ---------------------------- +The following bugs have been fixed in the 1.6.8 release. + +E 700 Test failure with java-21 on LogManagerPropertiesTest + CHANGES IN THE 1.6.7 RELEASE ---------------------------- @@ -110,7 +116,7 @@ GH 334 gimap set labels error with some non english characters The following bugs have been fixed in the 1.6.1 release. GH 262 Some IMAP servers send EXPUNGE responses for unknown messages -GH 278 BODYSTRUCTURE Parser fails on specific IMAP Server response +GH 278 BODYSTRUCTURE Parser fails on specific IMAP Server response GH 283 clean up connections when closing IMAPStore GH 287 Allow relaxed Content-Disposition parsing GH 289 use a different IMAP tag prefix for each connection @@ -831,7 +837,7 @@ The following bugs have been fixed in the 1.1.2 release. fix bug in SMTP output that sometimes duplicated "." close SMTP transport on I/O error 4230541 don't send SMTP NOOP unnecessarily -4216666 IMAP provider INTERNALDATE formatter error, causing +4216666 IMAP provider INTERNALDATE formatter error, causing problems during appendMessages() 4227888 IMAP provider does not honor the UID item in its FetchProfile diff --git a/logging/src/main/java/FileErrorManager.java b/logging/src/main/java/FileErrorManager.java index 2a96a7fcb..5722d17c7 100644 --- a/logging/src/main/java/FileErrorManager.java +++ b/logging/src/main/java/FileErrorManager.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 2009, 2019 Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2009, 2019 Jason Mehrens. All Rights Reserved. + * Copyright (c) 2009, 2024 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2024 Jason Mehrens. All Rights Reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0, which is available at @@ -11,8 +11,6 @@ import java.io.*; import java.lang.reflect.Constructor; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.logging.ErrorManager; import java.util.logging.Level; import java.util.logging.LogManager; @@ -262,13 +260,7 @@ private File getEmailStore() { String dir = manager.getProperty( getClass().getName().concat(".pattern")); if (dir == null) { - dir = AccessController.doPrivileged(new PrivilegedAction() { - - @Override - public String run() { - return System.getProperty("java.io.tmpdir", "."); - } - }); + dir = System.getProperty("java.io.tmpdir", "."); } return new File(dir); } diff --git a/logging/src/main/java/MailHandlerDemo.java b/logging/src/main/java/MailHandlerDemo.java index 28892e5c9..bff5ba6a5 100644 --- a/logging/src/main/java/MailHandlerDemo.java +++ b/logging/src/main/java/MailHandlerDemo.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 2009, 2019 Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2009, 2018 Jason Mehrens. All Rights Reserved. + * Copyright (c) 2009, 2024 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2024 Jason Mehrens. All Rights Reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0, which is available at @@ -139,19 +139,6 @@ private static void checkConfig(String prefix, PrintStream err) { + ManagementFactory.getRuntimeMXBean().getName()); err.println(prefix + ": java.security.debug=" + System.getProperty("java.security.debug")); - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - err.println(prefix + ": SecurityManager.class=" - + sm.getClass().getName()); - err.println(prefix + ": SecurityManager classLoader=" - + toString(sm.getClass().getClassLoader())); - err.println(prefix + ": SecurityManager.toString=" + sm); - } else { - err.println(prefix + ": SecurityManager.class=null"); - err.println(prefix + ": SecurityManager.toString=null"); - err.println(prefix + ": SecurityManager classLoader=null"); - } - String policy = System.getProperty("java.security.policy"); if (policy != null) { File f = new File(policy); diff --git a/mail/src/main/java/com/sun/mail/util/logging/LogManagerProperties.java b/mail/src/main/java/com/sun/mail/util/logging/LogManagerProperties.java index b84fb3d32..f9ca121d4 100644 --- a/mail/src/main/java/com/sun/mail/util/logging/LogManagerProperties.java +++ b/mail/src/main/java/com/sun/mail/util/logging/LogManagerProperties.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 2009, 2021 Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2009, 2021 Jason Mehrens. All rights reserved. + * Copyright (c) 2009, 2024 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2024 Jason Mehrens. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -21,7 +21,6 @@ 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.*; @@ -81,7 +80,7 @@ final class LogManagerProperties extends Properties { private static final Method ZDT_OF_INSTANT; /** - * MethodHandle is available starting at JDK7 and Andriod API 26. + * MethodHandle is available starting at JDK7 and Android API 26. */ static { //Added in JDK16 see JDK-8245302 Method lrtid = null; @@ -94,7 +93,7 @@ final class LogManagerProperties extends Properties { LR_GET_LONG_TID = lrtid; } - static { + static { //Added in JDK9 see JDK-8072645 Method lrgi = null; Method zisd = null; Method zdtoi = null; @@ -105,7 +104,6 @@ final class LogManagerProperties extends Properties { zisd = findClass("java.time.ZoneId") .getMethod("systemDefault"); if (!Modifier.isStatic(zisd.getModifiers())) { - zisd = null; throw new NoSuchMethodException(zisd.toString()); } @@ -115,7 +113,6 @@ final class LogManagerProperties extends Properties { if (!Modifier.isStatic(zdtoi.getModifiers()) || !Comparable.class.isAssignableFrom( zdtoi.getReturnType())) { - zdtoi = null; throw new NoSuchMethodException(zdtoi.toString()); } } catch (final RuntimeException ignore) { @@ -155,7 +152,7 @@ final class LogManagerProperties extends Properties { */ private static Object loadLogManager() { Object m; - try { + try { //GAE will forbid access to LogManager m = LogManager.getLogManager(); } catch (final LinkageError restricted) { m = readConfiguration(); @@ -247,8 +244,23 @@ static void checkLogManagerAccess() { if (m != null) { try { if (m instanceof LogManager) { - checked = true; - ((LogManager) m).checkAccess(); + try { + LogManager.class.getMethod("checkAccess").invoke(m); + checked = true; + } catch (InvocationTargetException ite) { + Throwable cause = ite.getCause(); + if (cause instanceof SecurityException) { + checked = true; + throw (SecurityException) cause; + } + + if (cause instanceof UnsupportedOperationException) { + checked = true; + } + } catch (NoSuchMethodException removed) { + checked = true; + } catch (ReflectiveOperationException fallthrough) { + } } } catch (final SecurityException notAllowed) { if (checked) { @@ -256,7 +268,7 @@ static void checkLogManagerAccess() { } } catch (final LinkageError restricted) { } catch (final RuntimeException unexpected) { - } + } //GAE will forbid access to LogManager } if (!checked) { @@ -279,25 +291,15 @@ private static void checkLoggingAccess() { * 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. + * defined behavior. Contractually, Logger::remove will check + * permission before checking if the argument is null. + * See JDK-8023168 */ - boolean checked = false; final Logger global = Logger.getLogger("global"); try { - if (Logger.class == global.getClass()) { - global.removeHandler((Handler) null); - checked = true; - } + global.removeHandler((Handler) null); } catch (final NullPointerException unexpected) { } - - if (!checked) { - final SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - sm.checkPermission(new LoggingPermission("control", null)); - } - } } /** @@ -784,18 +786,12 @@ static T newObjectFrom(String name, Class type) throws Exception { * @param ite any invocation target. * @return the exception. * @throws VirtualMachineError if present as cause. - * @throws ThreadDeath if present as cause. * @since JavaMail 1.4.5 */ private static Exception paramOrError(InvocationTargetException ite) { final Throwable cause = ite.getCause(); - if (cause != null) { - //Bitwise inclusive OR produces tighter bytecode for instanceof - //and matches with multicatch syntax. - if (cause instanceof VirtualMachineError - | cause instanceof ThreadDeath) { - throw (Error) cause; - } + if (cause instanceof VirtualMachineError) { + throw (Error) cause; } return ite; } @@ -871,7 +867,7 @@ private static Class tryLoad(String name, ClassLoader l) throws ClassNotFound * @return any array of class loaders. Indexes may be null. */ private static ClassLoader[] getClassLoaders() { - return AccessController.doPrivileged(new PrivilegedAction() { + return runOrDoPrivileged(new PrivilegedAction() { @SuppressWarnings("override") //JDK-6954234 public ClassLoader[] run() { @@ -891,6 +887,61 @@ public ClassLoader[] run() { } }); } + + /** + * Executes a PrivilegedAction without permissions then falling back to + * running with elevated permissions. + * + * Any unchecked exceptions from the action are passed through this API. + * + * @param the action return type. + * @param a the PrivilegedAction object. + * @return the result. + * @throws NullPointerException if the given action is null. + * @throws UndeclaredThrowableException if a checked exception is thrown. + * @since JavaMail 1.6.8 + */ + static T runOrDoPrivileged(final PrivilegedAction a) { + if (a == null) { + throw new NullPointerException(); + } + try { + return a.run(); + } catch (SecurityException sandbox) { + return invokeAccessController(a); + } + } + + /** + * Reflective call to access controller for sandbox environments. + * Any unchecked exceptions from the action are passed through this API. + * + * @param the return type of the action. + * @param a a non-null action. + * @return the result. + * @throws UnsupportedOperationException if not allowed. + * @throws UndeclaredThrowableException if a checked exception is thrown. + * @since JavaMail 1.6.8 + */ + @SuppressWarnings("unchecked") + private static T invokeAccessController(final PrivilegedAction a) { + assert a != null; + try { + Class c = Class.forName("java.security.AccessController"); + return (T) c.getMethod("doPrivileged", PrivilegedAction.class) + .invoke((Object) null, a); + } catch (ReflectiveOperationException roe) { + Throwable cause = roe.getCause(); + if (cause instanceof RuntimeException) { + throw (RuntimeException) cause; + } else if (cause instanceof Error) { + throw (Error) cause; + } else { + throw new UndeclaredThrowableException(roe); + } + } + } + /** * The namespace prefix to search LogManager and defaults. */ diff --git a/mail/src/main/java/com/sun/mail/util/logging/MailHandler.java b/mail/src/main/java/com/sun/mail/util/logging/MailHandler.java index ecfe0684a..cf5b65844 100644 --- a/mail/src/main/java/com/sun/mail/util/logging/MailHandler.java +++ b/mail/src/main/java/com/sun/mail/util/logging/MailHandler.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 2009, 2020 Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2009, 2020 Jason Mehrens. All rights reserved. + * Copyright (c) 2009, 2024 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2024 Jason Mehrens. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -24,7 +24,6 @@ import java.net.URLConnection; import java.net.UnknownHostException; import java.nio.charset.Charset; -import java.security.AccessController; import java.security.PrivilegedAction; import java.util.*; import java.util.logging.*; @@ -578,7 +577,7 @@ public boolean isLoggable(final LogRecord record) { if (record == null) { //JDK-8233979 return false; } - + int levelValue = getLevel().intValue(); if (record.getLevel().intValue() < levelValue || levelValue == offValue) { return false; @@ -4139,7 +4138,7 @@ private Object getAndSetContextClassLoader(final Object ccl) { } else { pa = new GetAndSetContext(ccl); } - return AccessController.doPrivileged(pa); + return LogManagerProperties.runOrDoPrivileged(pa); } catch (final SecurityException ignore) { } } diff --git a/mail/src/test/java/com/sun/mail/util/logging/AbstractLogging.java b/mail/src/test/java/com/sun/mail/util/logging/AbstractLogging.java index 9c11c59c6..b875b5da2 100644 --- a/mail/src/test/java/com/sun/mail/util/logging/AbstractLogging.java +++ b/mail/src/test/java/com/sun/mail/util/logging/AbstractLogging.java @@ -1,436 +1,436 @@ /* - * Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016, 2021 Jason Mehrens. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0, which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * This Source Code may also be made available under the following Secondary - * Licenses when the conditions for such availability set forth in the - * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, - * version 2 with the GNU Classpath Exception, which is available at - * https://www.gnu.org/software/classpath/license.html. - * - * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 - */ - -package com.sun.mail.util.logging; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.lang.annotation.Annotation; -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Properties; -import java.util.logging.Level; -import java.util.logging.LogManager; -import java.util.logging.LogRecord; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -/** - * Common super class for the logging test suite. - * - * @author Jason Mehrens - * @since JavaMail 1.5.6 - */ -abstract class AbstractLogging { - - /** - * Used to print debug information about the given throwable. - * - * @param t the throwable. - * @throws NullPointerException if given throwable is null. - */ - @SuppressWarnings({"CallToThreadDumpStack", "CallToPrintStackTrace"}) - static void dump(final Throwable t) { - t.printStackTrace(); - } - - /** - * Gets all of the predefined Levels. - * - * @return an array of log levels. - */ - static Level[] getAllLevels() { - final Field[] fields = Level.class.getFields(); - List a = new ArrayList<>(fields.length); - for (Field field : fields) { - if (Modifier.isStatic(field.getModifiers()) - && Level.class.isAssignableFrom(field.getType())) { - try { - a.add((Level) field.get((Object) null)); - } catch (IllegalArgumentException | IllegalAccessException ex) { - fail(ex.toString()); - } - } - } - return a.toArray(new Level[a.size()]); - } - - /** - * Determines if the given class is from the Jakarta Mail API Reference - * Implementation {@code com.sun.mail} package. - * - * @param k the type to test. - * @return true if this is part of reference implementation but not part of - * the official API spec. - * @throws Exception if there is a problem. - */ - final boolean isPrivateSpec(final Class k) throws Exception { - return isFromJakartaMail(k, false); - } - - /** - * Reinitialize the logging properties using the given properties. - * - * @param manager the log manager. - * @param props the properties. - * @throws IOException if there is a problem. - * @throws NullPointerException if either argument is null. - */ - final void read(LogManager manager, Properties props) throws IOException { - //JDK-4810637 - ByteArrayOutputStream out = new ByteArrayOutputStream(512); - props.store(out, getClass().getName()); - manager.readConfiguration(new ByteArrayInputStream(out.toByteArray())); - } - - /** - * Sets the log record time using milliseconds from the epoch of - * 1970-01-01T00:00:00Z. Any nanosecond information is set to zero. This - * method is used to support JDK8 when running on JDK9 or newer. - * - * @param record the log record to adjust. - * @param epochMilli the time in milliseconds from epoch. - * @throws NullPointerException if the given record is null. - */ - @SuppressWarnings("deprecation") //See JDK-8144262 and K7091. - static void setEpochMilli(final LogRecord record, final long epochMilli) { - record.setMillis(epochMilli); - } - - /** - * Sets the log record time using the seconds and nanoseconds of the epoch - * from 1970-01-01T00:00:00Z. - * - * @param record the log record. - * @param epochSecond the seconds. - * @param nanoAdjustment the nano seconds. - * @throws ClassNotFoundException if running on pre JDK 8. - * @throws NoSuchMethodException if running on JDK 8. - * @throws Exception if there is a problem. - */ - static void setEpochSecond(final LogRecord record, final long epochSecond, - final long nanoAdjustment) throws Exception { - final Class k = Class.forName("java.time.Instant"); - Method instant = k.getMethod("ofEpochSecond", long.class, long.class); - Method set = LogRecord.class.getMethod("setInstant", k); - set.invoke(record, instant.invoke(null, epochSecond, nanoAdjustment)); - } - - /** - * Sets the int thread id for the given log record. - * - * @param record a non null log record. - * @param id the thread id. - * @throws NullPointerException if the given record is null. - */ - @SuppressWarnings("deprecation") //See JDK-8245302 - static void setIntThreadID(final LogRecord record, int id) { - record.setThreadID(id); - } - - /** - * Gets the int thread id for the given log record. - * - * @param record a non null log record. - * @param id the thread id. - * @throws NullPointerException if the given record is null. - */ - @SuppressWarnings("deprecation") //See JDK-8245302 - static int getIntThreadID(final LogRecord record) { - return record.getThreadID(); - } - - /** - * Sets the long thread id for the given log record if it is supported. - * - * @param record a non-null record. - * @param id the long thread id. - * @throws Exception if there is a problem. - * @throws NoSuchMethodException if JDK is older than JDK 16. - * @throws NullPointerException if the given record is null. - */ - static void setLongThreadID(final LogRecord record, long id) - throws Exception { - if (record == null) { - throw new NullPointerException(); - } - LogRecord.class.getMethod("setLongThreadID", Long.TYPE) - .invoke(record, id); - } - - /** - * Determines if the {@code java.time} APIs are available for this JVM. - * - * @return true if the time classes can be loaded. - */ - static boolean hasJavaTimeModule() { - try { - Class.forName("java.time.Duration"); - Class.forName("java.time.Instant"); - Class.forName("java.time.ZonedDateTime"); - Class.forName("java.time.ZoneId"); - return true; - } catch (final ClassNotFoundException | LinkageError notSupported) { - } - return false; - } - - /** - * Fails if any declared types are outside of the logging-mailhandler.jar. - * This includes classes from the Jakarta Mail spec. - * - * @param k the type to check for dependencies. - * @throws Exception if there is a problem. - */ - final void testLinkage(final Class k) throws Exception { - testLinkage(k, true); - } - - /** - * Fails if any declared types are outside of the logging-mailhandler.jar. - * - * @param k the type to check for dependencies. - * @param includeSpec if true this includes official JakartaMail spec classes. - * @throws Exception if there is a problem. - */ - final void testLinkage(final Class k, final boolean includeSpec) - throws Exception { - assertFalse(k.getName(), isFromJakartaMail(k, includeSpec)); - for (Annotation an : k.getDeclaredAnnotations()) { - assertFalse(an.toString(), - isFromJakartaMail(an.annotationType(), includeSpec)); - } - - for (Method m : k.getDeclaredMethods()) { - assertFalse(m.getReturnType().getName(), - isFromJakartaMail(m.getReturnType(), includeSpec)); - for (Class p : m.getParameterTypes()) { - assertFalse(p.getName(), isFromJakartaMail(p, includeSpec)); - } - - for (Class e : m.getExceptionTypes()) { - assertFalse(e.getName(), isFromJakartaMail(e, includeSpec)); - } - - for (Annotation an : m.getDeclaredAnnotations()) { - assertFalse(an.toString(), - isFromJakartaMail(an.annotationType(), includeSpec)); - } - } - - for (Constructor c : k.getDeclaredConstructors()) { - for (Class p : c.getParameterTypes()) { - assertFalse(p.getName(), isFromJakartaMail(p, includeSpec)); - } - - for (Class e : c.getExceptionTypes()) { - assertFalse(e.getName(), isFromJakartaMail(e, includeSpec)); - } - - for (Annotation an : c.getDeclaredAnnotations()) { - assertFalse(an.toString(), - isFromJakartaMail(an.annotationType(), includeSpec)); - } - } - - for (Field f : k.getDeclaredFields()) { - for (Annotation an : k.getDeclaredAnnotations()) { - assertFalse(an.toString(), - isFromJakartaMail(an.annotationType(), includeSpec)); - } - assertFalse(f.getName(), isFromJakartaMail(f.getType(), includeSpec)); - } - } - - /** - * Tests that the private static loadDeclaredClasses method of the given - * type. Objects used by the MailHandler during a push might require - * declaring classes to be loaded on create since a push may happen after a - * class loader is shutdown. - * - * @param k the type to check never null. - * @throws Exception if there is a problem. - */ - final void testLoadDeclaredClasses(Class k) throws Exception { - Method m = k.getDeclaredMethod("loadDeclaredClasses"); - assertTrue(Modifier.isStatic(m.getModifiers())); - assertTrue(Modifier.isPrivate(m.getModifiers())); - assertEquals(Class[].class, m.getReturnType()); - m.setAccessible(true); - Class[] named = (Class[]) m.invoke((Object) null); - assertTrue(named.length != 0); - HashSet> declared = new HashSet<>( - Arrays.>asList(k.getDeclaredClasses())); - for (Class c : named) { - assertEquals(c.toString(), k, c.getEnclosingClass()); - assertTrue(c.getDeclaredClasses().length == 0); - declared.remove(c); - } - assertTrue(declared.toString(), declared.isEmpty()); - } - - /** - * Checks that the given class is visible to the LogManager. - * - * @param c the class to check. - * @throws Exception if there is a problem. - */ - final void testLogManagerModifiers(final Class c) throws Exception { - assertTrue(Modifier.isPublic(c.getModifiers())); - assertTrue(Modifier.isPublic(c.getConstructor().getModifiers())); - } - - /** - * Checks that the given class is not dependent on the - * {@code javax.annotation} classes as they are not present in all - * environments. - * - * @param k the class to inspect. - * @throws Exception if there is a problem. - */ - final void testNoDependencyOnJavaxAnnotations(Class k) throws Exception { - for (Method m : k.getDeclaredMethods()) { - testNoJavaxAnnotation(m); - } - - for (Field f : k.getDeclaredFields()) { - testNoJavaxAnnotation(f); - } - - for (Constructor c : k.getDeclaredConstructors()) { - testNoJavaxAnnotation(c); - } - - for (Class i : k.getInterfaces()) { - testNoJavaxAnnotation(i); - } - - for (Class d : k.getDeclaredClasses()) { - testNoDependencyOnJavaxAnnotations(d); - } - } - - /** - * WebappClassLoader.clearReferencesStaticFinal() method will ignore fields - * that have type names that start with 'java.' or 'javax.'. This test - * checks that the given class conforms to this rule so it doesn't become a - * target that will be nullified by the WebappClassLoader. - * - * @param c the class to check. - * @throws Exception if there is a problem. - */ - final void testWebappClassLoaderFieldNames(Class c) throws Exception { - for (Field f : c.getDeclaredFields()) { - Class k = f.getType(); - while (k.isArray()) { - k = k.getComponentType(); - } - - /** - * The WebappClassLoader ignores primitives, non-static, and - * synthetic fields. For the logging API, this test is stricter than - * what the WebappClassLoader actually clears. This restricts the - * logging API to standard field types for both static and - * non-static fields. The idea is to try to stay forward compatible - * with WebappClassLoader. - */ - if (f.getName().indexOf('$') < 0 && !k.isPrimitive() - && !k.getName().startsWith("java.") - && !k.getName().startsWith("javax.")) { - fail(f.toString()); - } - } - - for (Class ic : c.getDeclaredClasses()) { - testWebappClassLoaderFieldNames(ic); - } - } - - /** - * Blocks the current thread until current time has elapsed by one - * millisecond. - * - * @throws InterruptedException if the current thread is interrupted. - */ - static void tickMilli() throws InterruptedException { - tickMilli(1L); - } - - /** - * Blocks the current thread until current time has elapsed by the given - * delay in milliseconds. - * - * @param delay the number of milliseconds that have to elapse. - * @throws IllegalArgumentException if the given delay is zero or less. - * @throws InterruptedException if the current thread is interrupted. - */ - @SuppressWarnings("SleepWhileInLoop") - static void tickMilli(long delay) throws InterruptedException { - if (delay <= 0L) { - throw new IllegalArgumentException(Long.toString(delay)); - } - long then = System.currentTimeMillis(); - for (int i = 0; i < Short.MAX_VALUE; i++) { - long now = System.currentTimeMillis(); - long delta = (now - then); - if (delta >= delay) { - return; - } - Thread.sleep(delay - delta); - } - throw new AssertionError(then + " " + System.currentTimeMillis()); - } - - private boolean isFromJakartaMail(Class k, boolean include) throws Exception { - for (Class t = k; t != null; t = t.getSuperclass()) { - final String n = t.getName(); - if (n.startsWith("javax.mail.")) { - return include; - } - - //Not included with logging-mailhandler.jar. - if (n.startsWith("com.sun.mail.") - && !n.startsWith("com.sun.mail.util.logging.")) { - return true; - } - } - return false; - } - - private void testNoJavaxAnnotation(AccessibleObject fm) throws Exception { - for (Annotation a : fm.getAnnotations()) { - testNoJavaxAnnotation(a.annotationType()); - } - } - - private void testNoJavaxAnnotation(Class k) throws Exception { - for (; k != null; k = k.getSuperclass()) { - assertFalse(k.toString(), - k.getName().startsWith("javax.annotation")); - } - } -} + * Copyright (c) 2016, 2024 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 Jason Mehrens. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.mail.util.logging; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.LogRecord; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * Common super class for the logging test suite. + * + * @author Jason Mehrens + * @since JavaMail 1.5.6 + */ +abstract class AbstractLogging { + + /** + * Used to print debug information about the given throwable. + * + * @param t the throwable. + * @throws NullPointerException if given throwable is null. + */ + @SuppressWarnings({"CallToThreadDumpStack", "CallToPrintStackTrace"}) + static void dump(final Throwable t) { + t.printStackTrace(); + } + + /** + * Gets all of the predefined Levels. + * + * @return an array of log levels. + */ + static Level[] getAllLevels() { + final Field[] fields = Level.class.getFields(); + List a = new ArrayList<>(fields.length); + for (Field field : fields) { + if (Modifier.isStatic(field.getModifiers()) + && Level.class.isAssignableFrom(field.getType())) { + try { + a.add((Level) field.get((Object) null)); + } catch (IllegalArgumentException | IllegalAccessException ex) { + fail(ex.toString()); + } + } + } + return a.toArray(new Level[a.size()]); + } + + /** + * Determines if the given class is from the Jakarta Mail API Reference + * Implementation {@code com.sun.mail} package. + * + * @param k the type to test. + * @return true if this is part of reference implementation but not part of + * the official API spec. + * @throws Exception if there is a problem. + */ + final boolean isPrivateSpec(final Class k) throws Exception { + return isFromJakartaMail(k, false); + } + + /** + * Reinitialize the logging properties using the given properties. + * + * @param manager the log manager. + * @param props the properties. + * @throws IOException if there is a problem. + * @throws NullPointerException if either argument is null. + */ + final void read(LogManager manager, Properties props) throws IOException { + //JDK-4810637 + ByteArrayOutputStream out = new ByteArrayOutputStream(512); + props.store(out, getClass().getName()); + manager.readConfiguration(new ByteArrayInputStream(out.toByteArray())); + } + + /** + * Sets the log record time using milliseconds from the epoch of + * 1970-01-01T00:00:00Z. Any nanosecond information is set to zero. This + * method is used to support JDK8 when running on JDK9 or newer. + * + * @param record the log record to adjust. + * @param epochMilli the time in milliseconds from epoch. + * @throws NullPointerException if the given record is null. + */ + @SuppressWarnings("deprecation") //See JDK-8144262 and K7091. + static void setEpochMilli(final LogRecord record, final long epochMilli) { + record.setMillis(epochMilli); + } + + /** + * Sets the log record time using the seconds and nanoseconds of the epoch + * from 1970-01-01T00:00:00Z. + * + * @param record the log record. + * @param epochSecond the seconds. + * @param nanoAdjustment the nano seconds. + * @throws ClassNotFoundException if running on pre JDK 8. + * @throws NoSuchMethodException if running on JDK 8. + * @throws Exception if there is a problem. + */ + static void setEpochSecond(final LogRecord record, final long epochSecond, + final long nanoAdjustment) throws Exception { + final Class k = Class.forName("java.time.Instant"); + Method instant = k.getMethod("ofEpochSecond", long.class, long.class); + Method set = LogRecord.class.getMethod("setInstant", k); + set.invoke(record, instant.invoke(null, epochSecond, nanoAdjustment)); + } + + /** + * Sets the int thread id for the given log record. + * + * @param record a non null log record. + * @param id the thread id. + * @throws NullPointerException if the given record is null. + */ + @SuppressWarnings("deprecation") //See JDK-8245302 + static void setIntThreadID(final LogRecord record, int id) { + record.setThreadID(id); + } + + /** + * Gets the int thread id for the given log record. + * + * @param record a non null log record. + * @param id the thread id. + * @throws NullPointerException if the given record is null. + */ + @SuppressWarnings("deprecation") //See JDK-8245302 + static int getIntThreadID(final LogRecord record) { + return record.getThreadID(); + } + + /** + * Sets the long thread id for the given log record if it is supported. + * + * @param record a non-null record. + * @param id the long thread id. + * @throws Exception if there is a problem. + * @throws NoSuchMethodException if JDK is older than JDK 16. + * @throws NullPointerException if the given record is null. + */ + static void setLongThreadID(final LogRecord record, long id) + throws Exception { + if (record == null) { + throw new NullPointerException(); + } + LogRecord.class.getMethod("setLongThreadID", Long.TYPE) + .invoke(record, id); + } + + /** + * Determines if the {@code java.time} APIs are available for this JVM. + * + * @return true if the time classes can be loaded. + */ + static boolean hasJavaTimeModule() { + try { + Class.forName("java.time.Duration"); + Class.forName("java.time.Instant"); + Class.forName("java.time.ZonedDateTime"); + Class.forName("java.time.ZoneId"); + return true; + } catch (final ClassNotFoundException | LinkageError notSupported) { + } + return false; + } + + /** + * Fails if any declared types are outside of the logging-mailhandler.jar. + * This includes classes from the Jakarta Mail spec. + * + * @param k the type to check for dependencies. + * @throws Exception if there is a problem. + */ + final void testLinkage(final Class k) throws Exception { + testLinkage(k, true); + } + + /** + * Fails if any declared types are outside of the logging-mailhandler.jar. + * + * @param k the type to check for dependencies. + * @param includeSpec if true this includes official JakartaMail spec classes. + * @throws Exception if there is a problem. + */ + final void testLinkage(final Class k, final boolean includeSpec) + throws Exception { + assertFalse(k.getName(), isFromJakartaMail(k, includeSpec)); + for (Annotation an : k.getDeclaredAnnotations()) { + assertFalse(an.toString(), + isFromJakartaMail(an.annotationType(), includeSpec)); + } + + for (Method m : k.getDeclaredMethods()) { + assertFalse(m.getReturnType().getName(), + isFromJakartaMail(m.getReturnType(), includeSpec)); + for (Class p : m.getParameterTypes()) { + assertFalse(p.getName(), isFromJakartaMail(p, includeSpec)); + } + + for (Class e : m.getExceptionTypes()) { + assertFalse(e.getName(), isFromJakartaMail(e, includeSpec)); + } + + for (Annotation an : m.getDeclaredAnnotations()) { + assertFalse(an.toString(), + isFromJakartaMail(an.annotationType(), includeSpec)); + } + } + + for (Constructor c : k.getDeclaredConstructors()) { + for (Class p : c.getParameterTypes()) { + assertFalse(p.getName(), isFromJakartaMail(p, includeSpec)); + } + + for (Class e : c.getExceptionTypes()) { + assertFalse(e.getName(), isFromJakartaMail(e, includeSpec)); + } + + for (Annotation an : c.getDeclaredAnnotations()) { + assertFalse(an.toString(), + isFromJakartaMail(an.annotationType(), includeSpec)); + } + } + + for (Field f : k.getDeclaredFields()) { + for (Annotation an : k.getDeclaredAnnotations()) { + assertFalse(an.toString(), + isFromJakartaMail(an.annotationType(), includeSpec)); + } + assertFalse(f.getName(), isFromJakartaMail(f.getType(), includeSpec)); + } + } + + /** + * Tests that the private static loadDeclaredClasses method of the given + * type. Objects used by the MailHandler during a push might require + * declaring classes to be loaded on create since a push may happen after a + * class loader is shutdown. + * + * @param k the type to check never null. + * @throws Exception if there is a problem. + */ + final void testLoadDeclaredClasses(Class k) throws Exception { + Method m = k.getDeclaredMethod("loadDeclaredClasses"); + assertTrue(Modifier.isStatic(m.getModifiers())); + assertTrue(Modifier.isPrivate(m.getModifiers())); + assertEquals(Class[].class, m.getReturnType()); + m.setAccessible(true); + Class[] named = (Class[]) m.invoke((Object) null); + assertTrue(named.length != 0); + HashSet> declared = new HashSet<>( + Arrays.>asList(k.getDeclaredClasses())); + for (Class c : named) { + assertEquals(c.toString(), k, c.getEnclosingClass()); + assertTrue(c.getDeclaredClasses().length == 0); + declared.remove(c); + } + assertTrue(declared.toString(), declared.isEmpty()); + } + + /** + * Checks that the given class is visible to the LogManager. + * + * @param c the class to check. + * @throws Exception if there is a problem. + */ + final void testLogManagerModifiers(final Class c) throws Exception { + assertTrue(Modifier.isPublic(c.getModifiers())); + assertTrue(Modifier.isPublic(c.getConstructor().getModifiers())); + } + + /** + * Checks that the given class is not dependent on the + * {@code javax.annotation} classes as they are not present in all + * environments and were present in JDK. + * + * @param k the class to inspect. + * @throws Exception if there is a problem. + */ + final void testNoDependencyOnJavaxAnnotations(Class k) throws Exception { + for (Method m : k.getDeclaredMethods()) { + testNoJavaxAnnotation(m); + } + + for (Field f : k.getDeclaredFields()) { + testNoJavaxAnnotation(f); + } + + for (Constructor c : k.getDeclaredConstructors()) { + testNoJavaxAnnotation(c); + } + + for (Class i : k.getInterfaces()) { + testNoJavaxAnnotation(i); + } + + for (Class d : k.getDeclaredClasses()) { + testNoDependencyOnJavaxAnnotations(d); + } + } + + /** + * WebappClassLoader.clearReferencesStaticFinal() method will ignore fields + * that have type names that start with 'java.' or 'javax.'. This test + * checks that the given class conforms to this rule so it doesn't become a + * target that will be nullified by the WebappClassLoader. + * + * @param c the class to check. + * @throws Exception if there is a problem. + */ + final void testWebappClassLoaderFieldNames(Class c) throws Exception { + for (Field f : c.getDeclaredFields()) { + Class k = f.getType(); + while (k.isArray()) { + k = k.getComponentType(); + } + + /** + * The WebappClassLoader ignores primitives, non-static, and + * synthetic fields. For the logging API, this test is stricter than + * what the WebappClassLoader actually clears. This restricts the + * logging API to standard field types for both static and + * non-static fields. The idea is to try to stay forward compatible + * with WebappClassLoader. + */ + if (f.getName().indexOf('$') < 0 && !k.isPrimitive() + && !k.getName().startsWith("java.") + && !k.getName().startsWith("javax.")) { + fail(f.toString()); + } + } + + for (Class ic : c.getDeclaredClasses()) { + testWebappClassLoaderFieldNames(ic); + } + } + + /** + * Blocks the current thread until current time has elapsed by one + * millisecond. + * + * @throws InterruptedException if the current thread is interrupted. + */ + static void tickMilli() throws InterruptedException { + tickMilli(1L); + } + + /** + * Blocks the current thread until current time has elapsed by the given + * delay in milliseconds. + * + * @param delay the number of milliseconds that have to elapse. + * @throws IllegalArgumentException if the given delay is zero or less. + * @throws InterruptedException if the current thread is interrupted. + */ + @SuppressWarnings("SleepWhileInLoop") + static void tickMilli(long delay) throws InterruptedException { + if (delay <= 0L) { + throw new IllegalArgumentException(Long.toString(delay)); + } + long then = System.currentTimeMillis(); + for (int i = 0; i < Short.MAX_VALUE; i++) { + long now = System.currentTimeMillis(); + long delta = (now - then); + if (delta >= delay) { + return; + } + Thread.sleep(delay - delta); + } + throw new AssertionError(then + " " + System.currentTimeMillis()); + } + + private boolean isFromJakartaMail(Class k, boolean include) throws Exception { + for (Class t = k; t != null; t = t.getSuperclass()) { + final String n = t.getName(); + if (n.startsWith("javax.mail.")) { + return include; + } + + //Not included with logging-mailhandler.jar. + if (n.startsWith("com.sun.mail.") + && !n.startsWith("com.sun.mail.util.logging.")) { + return true; + } + } + return false; + } + + private void testNoJavaxAnnotation(AccessibleObject fm) throws Exception { + for (Annotation a : fm.getAnnotations()) { + testNoJavaxAnnotation(a.annotationType()); + } + } + + private void testNoJavaxAnnotation(Class k) throws Exception { + for (; k != null; k = k.getSuperclass()) { + assertFalse(k.toString(), + k.getName().startsWith("javax.annotation")); + } + } +} diff --git a/mail/src/test/java/com/sun/mail/util/logging/LogManagerPropertiesTest.java b/mail/src/test/java/com/sun/mail/util/logging/LogManagerPropertiesTest.java index d9fe233e2..60961df60 100644 --- a/mail/src/test/java/com/sun/mail/util/logging/LogManagerPropertiesTest.java +++ b/mail/src/test/java/com/sun/mail/util/logging/LogManagerPropertiesTest.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 2009, 2021 Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2009, 2021 Jason Mehrens. All rights reserved. + * Copyright (c) 2009, 2024 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2024 Jason Mehrens. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -21,6 +21,7 @@ import java.lang.management.CompilationMXBean; import java.lang.management.ManagementFactory; import java.lang.reflect.*; +import java.security.PrivilegedAction; import java.util.Arrays; import java.util.Comparator; import java.util.Locale; @@ -45,11 +46,6 @@ public class LogManagerPropertiesTest extends AbstractLogging { */ private final static ThreadLocal PENDING = new ThreadLocal<>(); - @BeforeClass - public static void setUpClass() throws Exception { - Assert.assertNull(System.getSecurityManager()); - } - private static void fullFence() { LogManager.getLogManager().getProperty(""); } @@ -79,67 +75,125 @@ public void testDeclaredClasses() throws Exception { } @Test - public void testCheckAccessPresent() { - LogManager m = LogManager.getLogManager(); - m.checkAccess(); - LogManagerProperties.checkLogManagerAccess(); - - LogPermSecurityManager sm = new LogPermSecurityManager(); - sm.secure = false; - System.setSecurityManager(sm); + public void testCheckLogManagerAccess() { try { - sm.secure = true; - try { - m.checkAccess(); - fail(m.toString()); - } catch (SecurityException expect) { + LogManagerProperties.checkLogManagerAccess(); + //okay if we return normally, this is a weak test. + } catch (SecurityException allowed) { + } + } + + private static boolean isInvokeAccessController() { + for (StackTraceElement frame : new Throwable().getStackTrace()) { + if ("invokeAccessController".equals(frame.getMethodName()) + && LogManagerProperties.class.getName().equals(frame.getClassName())) { + return true; } + } + return false; + } - try { - LogManagerProperties.checkLogManagerAccess(); - fail(LogManagerProperties.class.getName()); - } catch (SecurityException expect) { + private static boolean isAccessController() { + for (StackTraceElement frame : new Throwable().getStackTrace()) { + if ("doPrivileged".equals(frame.getMethodName()) + && "java.security.AccessController".equals( + frame.getClassName())) { + return true; } - } finally { - sm.secure = false; - System.setSecurityManager((SecurityManager) null); } + return false; } - @Ignore - public void testCheckAccessAbsent() throws Exception { - assumeNoJit(); - final Class k = LogManagerProperties.class; - final Field f = k.getDeclaredField("LOG_MANAGER"); - Field mod = setAccessible(f); + @Test + public void testRun() { + PrivilegedAction p = new PrivilegedAction() { + @SuppressWarnings("override") //JDK-6954234 + public Boolean run() { + assertFalse(isInvokeAccessController()); + assertFalse(isAccessController()); + return true; + } + }; + + assertTrue(LogManagerProperties.runOrDoPrivileged(p)); + } + + @Test + public void testDoPrivileged() { + PrivilegedAction p = new PrivilegedAction() { + @SuppressWarnings("override") //JDK-6954234 + public Boolean run() { + if (isInvokeAccessController()) { + return isAccessController(); + } + throw new SecurityException(); + } + }; + try { - final Object lm = f.get(null); - f.set(null, null); - try { - fullFence(); - LogManagerProperties.checkLogManagerAccess(); + assertTrue(LogManagerProperties.runOrDoPrivileged(p)); + } catch (UndeclaredThrowableException removed) { + assertTrue(removed.getCause() instanceof ClassNotFoundException); + } catch (UnsupportedOperationException terminal) { + } + } - LogPermSecurityManager sm = new LogPermSecurityManager(); - sm.secure = false; - System.setSecurityManager(sm); - try { - sm.secure = true; - try { - LogManagerProperties.checkLogManagerAccess(); - fail(LogManagerProperties.class.getName()); - } catch (SecurityException expect) { - } - } finally { - sm.secure = false; - System.setSecurityManager((SecurityManager) null); + @Test + public void testDoPrivilegedRuntimeException() { + RuntimeException cause = new RuntimeException(); + PrivilegedAction p = new PrivilegedAction() { + @SuppressWarnings("override") //JDK-6954234 + public Boolean run() { + if (isInvokeAccessController()) { + throw cause; } - } finally { - f.set(null, lm); - fullFence(); + throw new SecurityException(); } - } finally { - mod.setInt(f, f.getModifiers() | Modifier.FINAL); + }; + + boolean ran = false; + try { + LogManagerProperties.runOrDoPrivileged(p); + ran = true; + } catch (UndeclaredThrowableException removed) { + assertTrue(removed.getCause() instanceof ClassNotFoundException); + } catch (UnsupportedOperationException terminal) { + } catch (RuntimeException re) { + assertSame(re, cause); } + assertFalse(ran); + } + + @Test + public void testDoPrivilegedError() { + Error cause = new Error(); + PrivilegedAction p = new PrivilegedAction() { + @SuppressWarnings("override") //JDK-6954234 + public Boolean run() { + if (isInvokeAccessController()) { + throw cause; + } + throw new SecurityException(); + } + }; + + boolean ran = false; + try { + LogManagerProperties.runOrDoPrivileged(p); + ran = true; + } catch (UndeclaredThrowableException removed) { + assertTrue(removed.getCause() instanceof ClassNotFoundException); + } catch (UnsupportedOperationException terminal) { + } catch (Error e) { + assertSame(e, cause); + } + assertFalse(ran); + } + + @Test(expected=NullPointerException.class) + public void testRunOrDoPrivilegedNull() { + + LogManagerProperties.runOrDoPrivileged((PrivilegedAction) null); } @Test @@ -1055,11 +1109,11 @@ public void testEscapingAuthenticator() throws Exception { a = LogManagerProperties.newObjectFrom(k.getName(), Authenticator.class); assertEquals(k, a.getClass()); - setPending(new ThreadDeath()); + setPending(new StackOverflowError()); try { a = LogManagerProperties.newObjectFrom(k.getName(), Authenticator.class); fail(String.valueOf(a)); - } catch (ThreadDeath expect) { + } catch (StackOverflowError expect) { } setPending(new OutOfMemoryError()); @@ -1082,11 +1136,11 @@ public void testEscapingComparator() throws Exception { c = LogManagerProperties.newComparator(k.getName()); assertEquals(k, c.getClass()); - setPending(new ThreadDeath()); + setPending(new StackOverflowError()); try { c = LogManagerProperties.newComparator(k.getName()); fail(String.valueOf(c)); - } catch (ThreadDeath expect) { + } catch (StackOverflowError expect) { } setPending(new OutOfMemoryError()); @@ -1109,11 +1163,11 @@ public void testEscapingErrorErrorManager() throws Exception { f = LogManagerProperties.newErrorManager(k.getName()); assertEquals(k, f.getClass()); - setPending(new ThreadDeath()); + setPending(new StackOverflowError()); try { f = LogManagerProperties.newErrorManager(k.getName()); fail(String.valueOf(f)); - } catch (ThreadDeath expect) { + } catch (StackOverflowError expect) { } setPending(new OutOfMemoryError()); @@ -1136,11 +1190,11 @@ public void testEscapingFilter() throws Exception { f = LogManagerProperties.newFilter(k.getName()); assertEquals(k, f.getClass()); - setPending(new ThreadDeath()); + setPending(new StackOverflowError()); try { f = LogManagerProperties.newFilter(k.getName()); fail(String.valueOf(f)); - } catch (ThreadDeath expect) { + } catch (StackOverflowError expect) { } setPending(new OutOfMemoryError()); @@ -1163,11 +1217,11 @@ public void testEscapingFormatter() throws Exception { f = LogManagerProperties.newFormatter(k.getName()); assertEquals(k, f.getClass()); - setPending(new ThreadDeath()); + setPending(new StackOverflowError()); try { f = LogManagerProperties.newFormatter(k.getName()); fail(String.valueOf(f)); - } catch (ThreadDeath expect) { + } catch (StackOverflowError expect) { } setPending(new OutOfMemoryError()); @@ -1422,38 +1476,4 @@ public String getLocalHost() { throw new SecurityException(); } } - - private static final class LogPermSecurityManager extends SecurityManager { - - volatile boolean secure = false; - - LogPermSecurityManager() { - } - - @Override - public void checkPermission(java.security.Permission perm) { - try { //Call super class always for java.security.debug tracing. - super.checkPermission(perm); - checkPermission(perm, new SecurityException(perm.toString())); - } catch (SecurityException se) { - checkPermission(perm, se); - } - } - - @Override - public void checkPermission(java.security.Permission perm, Object context) { - try { //Call super class always for java.security.debug tracing. - super.checkPermission(perm, context); - checkPermission(perm, new SecurityException(perm.toString())); - } catch (SecurityException se) { - checkPermission(perm, se); - } - } - - private void checkPermission(java.security.Permission perm, SecurityException se) { - if (secure && perm instanceof LoggingPermission) { - throw se; - } - } - } } diff --git a/mail/src/test/java/com/sun/mail/util/logging/MailHandlerTest.java b/mail/src/test/java/com/sun/mail/util/logging/MailHandlerTest.java index cf056cda0..33b985013 100644 --- a/mail/src/test/java/com/sun/mail/util/logging/MailHandlerTest.java +++ b/mail/src/test/java/com/sun/mail/util/logging/MailHandlerTest.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 2009, 2021 Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2009, 2021 Jason Mehrens. All rights reserved. + * Copyright (c) 2009, 2024 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2024 Jason Mehrens. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -25,6 +25,8 @@ import java.lang.reflect.Modifier; import java.net.*; import java.util.*; +import java.util.concurrent.Callable; +import java.util.concurrent.FutureTask; import java.util.logging.*; import java.util.logging.Formatter; import javax.activation.*; @@ -90,14 +92,6 @@ public static void tearDownClass() throws Exception { anyClassPathDir = null; } - private static void assumeNoJit() { - CompilationMXBean c = ManagementFactory.getCompilationMXBean(); - if (c != null) { //-Xint - Assume.assumeNoException(new IllegalArgumentException( - c.getName() + " must be disabled.")); - } - } - private static void fullFence() { LogManager.getLogManager().getProperty(""); } @@ -153,24 +147,6 @@ private static void setPending(final Throwable t) { } } - private static Field setAccessible(Field f) { - f.setAccessible(true); - try { - assumeNoJit(); - if (Modifier.isFinal(f.getModifiers())) { - Field mod = Field.class.getDeclaredField("modifiers"); - mod.setAccessible(true); - mod.setInt(f, f.getModifiers() & ~Modifier.FINAL); - return mod; - } - } catch (RuntimeException re) { - Assume.assumeNoException(re); - } catch (Exception e) { - Assume.assumeNoException(e); - } - throw new AssertionError(); - } - private static void set(ClassLoader expect) { if (expect == null) { LOADER.remove(); @@ -218,41 +194,47 @@ static void checkContextClassLoader(ClassLoader expect) { Object ccl = Thread.currentThread().getContextClassLoader(); if (expect != ccl) { AssertionError ae = new AssertionError(expect + " != " + ccl - + ", sm=" + System.getSecurityManager()); + + ", thread=" + Thread.currentThread()); dump(ae); throw ae; } } @Test - public void testChildClassLoader() { - assertNull(System.getSecurityManager()); - final Thread thread = Thread.currentThread(); - final ClassLoader ccl = thread.getContextClassLoader(); - try { - URLClassLoader child = new URLClassLoader(new URL[0], ccl); + public void testChildClassLoader() throws Exception { + Callable c = new Callable() { + @SuppressWarnings("override") //JDK-6954234 + public Void call() throws Exception { + final Thread thread = Thread.currentThread(); + final ClassLoader ccl = thread.getContextClassLoader(); + try { + URLClassLoader child = new URLClassLoader(new URL[0], ccl); - thread.setContextClassLoader(child); - testCallingClassLoader((ClassLoaderSecurityManager) null, child); + thread.setContextClassLoader(child); + testCallingClassLoader(false, child); - thread.setContextClassLoader(child); - testCallingClassLoader(new ClassLoaderSecurityManager(), child); - } finally { - thread.setContextClassLoader(ccl); - } - assertNull(System.getSecurityManager()); + thread.setContextClassLoader(child); + testCallingClassLoader(true, child); + } finally { + thread.setContextClassLoader(ccl); + } + return null; + } + }; + + FutureTask f = new FutureTask<>(c); + Thread t = new ClassLoaderThread(f); + t.start(); + assertNull(f.get()); } - private void testCallingClassLoader( - ClassLoaderSecurityManager sm, ClassLoader expect) { + private void testCallingClassLoader(boolean secure, ClassLoader expect) { + ClassLoaderThread.checkThread(); InternalErrorManager em = new ClassLoaderErrorManager(expect); try { MailHandler instance = new MailHandler(createInitProperties("")); try { - if (sm != null) { - System.setSecurityManager(sm); - sm.secure = true; - } + ClassLoaderThread.setSecure(secure); instance.setErrorManager(em); instance.setLevel(Level.ALL); instance.setPushLevel(Level.SEVERE); @@ -271,10 +253,7 @@ private void testCallingClassLoader( instance.close(); } } finally { - if (sm != null) { - sm.secure = false; - System.setSecurityManager((SecurityManager) null); - } + ClassLoaderThread.setSecure(false); } assert em != null; @@ -290,21 +269,30 @@ private void testCallingClassLoader( @Test public void testVerifyClassLoader() throws Exception { - assertNull(System.getSecurityManager()); - final Thread thread = Thread.currentThread(); - final ClassLoader ccl = thread.getContextClassLoader(); - try { - URLClassLoader child = new URLClassLoader(new URL[0], ccl); + Callable c = new Callable() { + @SuppressWarnings("override") //JDK-6954234 + public Void call() throws Exception { + final Thread thread = Thread.currentThread(); + final ClassLoader ccl = thread.getContextClassLoader(); + try { + URLClassLoader child = new URLClassLoader(new URL[0], ccl); - thread.setContextClassLoader(child); - testVerify((ClassLoaderSecurityManager) null, child); + thread.setContextClassLoader(child); + testVerify(false, child); - thread.setContextClassLoader(child); - testVerify(new ClassLoaderSecurityManager(), child); - } finally { - thread.setContextClassLoader(ccl); - } - assertNull(System.getSecurityManager()); + thread.setContextClassLoader(child); + testVerify(true, child); + } finally { + thread.setContextClassLoader(ccl); + } + return null; + } + }; + + FutureTask f = new FutureTask<>(c); + Thread t = new ClassLoaderThread(f); + t.start(); + assertNull(f.get()); } @Test @@ -327,7 +315,8 @@ public void testWebappClassLoaderFieldNames() throws Exception { testWebappClassLoaderFieldNames(MailHandler.class); } - private void testVerify(ClassLoaderSecurityManager sm, ClassLoader expect) throws Exception { + private void testVerify(boolean secure, ClassLoader expect) throws Exception { + ClassLoaderThread.checkThread(); final LogManager manager = LogManager.getLogManager(); InternalErrorManager em = null; set(expect); @@ -342,11 +331,7 @@ private void testVerify(ClassLoaderSecurityManager sm, ClassLoader expect) throw read(manager, props); - if (sm != null) { - System.setSecurityManager(sm); - sm.secure = true; - } - + ClassLoaderThread.setSecure(secure); MailHandler instance = new MailHandler(); try { em = internalErrorManagerFrom(instance); @@ -354,10 +339,7 @@ private void testVerify(ClassLoaderSecurityManager sm, ClassLoader expect) throw instance.close(); } } finally { - if (sm != null) { - sm.secure = false; - System.setSecurityManager((SecurityManager) null); - } + ClassLoaderThread.setSecure(false); set((ClassLoader) null); manager.reset(); } @@ -372,35 +354,43 @@ private void testVerify(ClassLoaderSecurityManager sm, ClassLoader expect) throw assertFalse(em.exceptions.isEmpty()); } + @Test public void testSetMailPropertiesClassLoader() throws Exception { - assertNull(System.getSecurityManager()); - final Thread thread = Thread.currentThread(); - final ClassLoader ccl = thread.getContextClassLoader(); - try { - URLClassLoader child = new URLClassLoader(new URL[0], ccl); + Callable c = new Callable() { + @SuppressWarnings("override") //JDK-6954234 + public Void call() throws Exception { + final Thread thread = Thread.currentThread(); + final ClassLoader ccl = thread.getContextClassLoader(); + try { + URLClassLoader child = new URLClassLoader(new URL[0], ccl); - thread.setContextClassLoader(child); - testSetMailProperties((ClassLoaderSecurityManager) null, child); + thread.setContextClassLoader(child); + testSetMailProperties(false, child); - thread.setContextClassLoader(child); - testSetMailProperties(new ClassLoaderSecurityManager(), child); - } finally { - thread.setContextClassLoader(ccl); - } - assertNull(System.getSecurityManager()); + thread.setContextClassLoader(child); + testSetMailProperties(true, child); + } finally { + thread.setContextClassLoader(ccl); + } + return null; + } + }; + + FutureTask f = new FutureTask<>(c); + Thread t = new ClassLoaderThread(f); + t.start(); + assertNull(f.get()); } - private void testSetMailProperties(ClassLoaderSecurityManager sm, ClassLoader expect) throws Exception { + private void testSetMailProperties(boolean secure, ClassLoader expect) throws Exception { + ClassLoaderThread.checkThread(); InternalErrorManager em = new ClassLoaderErrorManager(expect); try { Properties props = createInitProperties(""); props.put("verify", "local"); - if (sm != null) { - System.setSecurityManager(sm); - sm.secure = true; - } + ClassLoaderThread.setSecure(secure); MailHandler instance = new MailHandler(); try { @@ -416,10 +406,7 @@ private void testSetMailProperties(ClassLoaderSecurityManager sm, ClassLoader ex instance.close(); } } finally { - if (sm != null) { - sm.secure = false; - System.setSecurityManager((SecurityManager) null); - } + ClassLoaderThread.setSecure(false); } assert em != null; @@ -2266,7 +2253,6 @@ private void testLinkageErrorEmptyStack(String method) throws IOException { @Test public void testCloseContextClassLoader() { - assertNull(System.getSecurityManager()); final Thread thread = Thread.currentThread(); final ClassLoader ccl = thread.getContextClassLoader(); try { @@ -2274,7 +2260,6 @@ public void testCloseContextClassLoader() { } finally { thread.setContextClassLoader(ccl); } - assertNull(System.getSecurityManager()); } private void testCloseContextClassLoader0() { @@ -2884,12 +2869,6 @@ public void testContentTypeOverride() throws Exception { assertEquals(expected, type); } - private ErrorManager getSuperErrorManager(MailHandler h) throws Exception { - Method hem = MailHandler.class.getDeclaredMethod("defaultErrorManager"); - hem.setAccessible(true); - return (ErrorManager) hem.invoke(h); - } - private String getInlineContentType(Formatter f) throws Exception { final String[] value = new String[1]; MailHandler instance = new MailHandler(createInitProperties("")); @@ -4796,85 +4775,6 @@ private String toString(Address o) { instance.close(); } - @Test - public void testReportErrorSuper() throws Exception { - assertNull(System.getSecurityManager()); - Field mhem = MailHandler.class.getDeclaredField("errorManager"); - mhem.setAccessible(true); - - InternalErrorManager superEm = new InternalErrorManager(); - InternalErrorManager em = new InternalErrorManager(); - MailHandler h = new MailHandler(); - try { - Exception tester = new Exception(); - synchronized (h) { - assertSame(h.getErrorManager(), getSuperErrorManager(h)); - - h.setErrorManager(superEm); - assertSame(superEm, getSuperErrorManager(h)); - assertSame(superEm, mhem.get(h)); - - mhem.set(h, em); - assertSame(em, h.getErrorManager()); - assertSame(superEm, getSuperErrorManager(h)); - assertNotSame(h.getErrorManager(), getSuperErrorManager(h)); - h.reportError("", tester, ErrorManager.GENERIC_FAILURE); - } - - assertEquals(1, em.exceptions.size()); - assertSame(tester, em.exceptions.get(0)); - assertTrue(superEm.exceptions.toString(), - superEm.exceptions.isEmpty()); - } finally { - h.close(); - } - } - - @Test - public void testGaeReportErrorSuper() throws Exception { - Field mhem = MailHandler.class.getDeclaredField("errorManager"); - mhem.setAccessible(true); - - InternalErrorManager em = new InternalErrorManager(); - GaeSecurityManager sm = new GaeSecurityManager(); - System.setSecurityManager(sm); - sm.secure = true; - try { - MailHandler h = new MailHandler(); - try { - Exception tester = new Exception(); - synchronized (h) { - sm.secure = false; - final Object def = getSuperErrorManager(h); - assertSame(def, getSuperErrorManager(h)); - sm.secure = true; - - assertEquals(h.getErrorManager().getClass(), def.getClass()); - assertNotSame(h.getErrorManager(), def); - - h.setErrorManager(em); - sm.secure = false; - final Object sem = getSuperErrorManager(h); - sm.secure = true; - assertSame(def, sem); - assertNotSame(h.getErrorManager(), def); - assertNotSame(h.getErrorManager(), sem); - assertSame(h.getErrorManager(), em); - - h.reportError("", tester, ErrorManager.GENERIC_FAILURE); - } - - assertEquals(1, em.exceptions.size()); - assertSame(tester, em.exceptions.get(0)); - } finally { - h.close(); - } - } finally { - sm.secure = false; - System.setSecurityManager((SecurityManager) null); - } - } - @Test public void testReportErrorLinkageWithStack() throws Exception { testReportErrorLinkageWithStack(new LinkageErrorStream()); @@ -4973,618 +4873,6 @@ private void testReportErrorLinkageEmptyStack(PrintStream ps) throws Throwable { } } - @Ignore - public void testGaeForbiddenHeaders() throws Exception { - assumeNoJit(); - assertNull(System.getSecurityManager()); - assertTrue(LogManagerProperties.hasLogManager()); - final Class k = LogManagerProperties.class; - final Field f = k.getDeclaredField("LOG_MANAGER"); - final Field mod = setAccessible(f); - try { - final Object lm = f.get(null); - f.set(null, new Properties()); - try { - fullFence(); - assertFalse(LogManagerProperties.hasLogManager()); - MailHandler instance = createHandlerWithRecords(); - instance.setErrorManager(new GaeErrorManager(instance)); - instance.close(); - } finally { - f.set(null, lm); - fullFence(); - } - } finally { - mod.setInt(f, f.getModifiers() | Modifier.FINAL); - } - assertTrue(LogManagerProperties.hasLogManager()); - } - - @Ignore - public void testGaeSecurityManager() throws Exception { - assumeNoJit(); - InternalErrorManager em; - MailHandler h = null; - final GaeSecurityManager manager = new GaeSecurityManager(); - System.setSecurityManager(manager); - try { - manager.secure = false; - h = new MailHandler(createInitProperties("")); - em = new InternalErrorManager(); - h.setErrorManager(em); - manager.secure = true; - assertEquals(manager, System.getSecurityManager()); - - //GAE allows access to loggers. - Logger global = Logger.getLogger("global"); - hardRef = global; - global.addHandler(h); - global.removeHandler(h); - global.removeHandler((Handler) null); - hardRef = null; - - h.postConstruct(); - h.setAttachmentFormatters(new Formatter[]{new ThrowFormatter()}); - assertEquals(1, h.getAttachmentFormatters().length); - - h.setAttachmentFilters(new Filter[]{new ThrowFilter()}); - assertEquals(1, h.getAttachmentFormatters().length); - - assertEquals(1, h.getAttachmentFormatters().length); - h.setAttachmentNames(new String[]{"error.txt"}); - - assertEquals(1, h.getAttachmentFormatters().length); - h.setAttachmentNames(new Formatter[]{new ThrowFormatter()}); - - h.setAuthenticator((Authenticator) null); - h.setComparator((Comparator) null); - - h.setLevel(Level.ALL); - h.setFilter(BooleanFilter.FALSE); - h.setFilter((Filter) null); - h.setFormatter(new EmptyFormatter()); - - assertNotNull(h.getErrorManager()); - h.setErrorManager(new ErrorManager()); - - h.setEncoding((String) null); - - h.flush(); - h.push(); - - h.setMailProperties(new Properties()); - - h.setPushFilter((Filter) null); - h.setPushLevel(Level.OFF); - - h.setSubject(new ThrowFormatter()); - h.setSubject("test"); - - h.getAuthenticator(); - h.getMailProperties(); - - h.preDestroy(); - h.close(); - - //check for internal exceptions caused by security manager. - for (Exception e : em.exceptions) { - dump(e); - } - assertTrue(em.exceptions.isEmpty()); - - hardRef = h = new MailHandler(); - h.close(); - - hardRef = h = new MailHandler(100); - assertEquals(100, h.getCapacity()); - h.close(); - - Properties props = new Properties(); - props.put("test", "test"); - hardRef = h = new MailHandler(props); - assertEquals(props, h.getMailProperties()); - } finally { - hardRef = null; - manager.secure = false; - System.setSecurityManager((SecurityManager) null); - if (h != null) { - h.close(); - } - } - } - - /** - * Test logging permissions of the MailHandler. Must run by itself or run in - * isolated VM. Use system property java.security.debug=all to troubleshoot - * failures. - */ - @Test - public void testSecurityManager() { - InternalErrorManager em; - MailHandler h = null; - final ThrowSecurityManager manager = new ThrowSecurityManager(); - System.setSecurityManager(manager); - try { - manager.secure = false; - h = new MailHandler(createInitProperties("")); - em = new InternalErrorManager(); - h.setErrorManager(em); - manager.secure = true; - assertEquals(manager, System.getSecurityManager()); - - try { - assertEquals(0, h.getAttachmentFormatters().length); - h.setAttachmentNames(new String[]{"error.txt"}); - fail("Missing secure check."); - } catch (SecurityException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - assertEquals(0, h.getAttachmentFormatters().length); - h.setAttachmentNames(new Formatter[]{new ThrowFormatter()}); - fail("Missing secure check."); - } catch (SecurityException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - assertEquals(0, h.getAttachmentFormatters().length); - h.setAttachmentFilters(new Filter[]{new ThrowFilter()}); - fail("Missing secure check."); - } catch (SecurityException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.setAttachmentFormatters(new Formatter[]{new ThrowFormatter()}); - fail("Missing secure check."); - } catch (SecurityException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - manager.secure = false; - try { - h.setAttachmentFormatters(new Formatter[]{new ThrowFormatter()}); - } catch (SecurityException fail) { - fail("Unexpected secure check."); - } catch (Exception fail) { - fail(fail.toString()); - } finally { - manager.secure = true; - } - - try { - assertEquals(1, h.getAttachmentFormatters().length); - h.setAttachmentFilters(new Filter[]{new ThrowFilter()}); - fail("Missing secure check."); - } catch (SecurityException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - assertEquals(1, h.getAttachmentFormatters().length); - h.setAttachmentFilters((Filter[]) null); - fail("Missing secure check."); - } catch (SecurityException | NullPointerException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - assertEquals(1, h.getAttachmentFormatters().length); - h.setAttachmentNames(new String[]{"error.txt"}); - fail("Missing secure check."); - } catch (SecurityException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - assertEquals(1, h.getAttachmentFormatters().length); - h.setAttachmentNames((String[]) null); - fail("Missing secure check."); - } catch (SecurityException | NullPointerException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - assertEquals(1, h.getAttachmentFormatters().length); - h.setAttachmentNames((Formatter[]) null); - fail("Missing secure check."); - } catch (SecurityException | NullPointerException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - assertEquals(1, h.getAttachmentFormatters().length); - h.setAttachmentNames(new Formatter[]{new ThrowFormatter()}); - fail("Missing secure check."); - } catch (SecurityException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - manager.secure = false; - try { - assertEquals(1, h.getAttachmentFormatters().length); - h.setAttachmentFormatters(new Formatter[0]); - } catch (SecurityException fail) { - fail(fail.toString()); - } catch (Exception fail) { - fail(fail.toString()); - } finally { - manager.secure = true; - } - - try { - assertEquals(0, h.getAttachmentFormatters().length); - assertEquals(0, h.getAttachmentFilters().length); - assertEquals(0, h.getAttachmentNames().length); - } catch (SecurityException fail) { - fail(fail.toString()); - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.setAuthenticator((Authenticator) null); - fail("Missing secure check."); - } catch (SecurityException | NullPointerException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.setComparator((Comparator) null); - fail("Missing secure check."); - } catch (SecurityException | NullPointerException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.getComparator(); - } catch (SecurityException fail) { - fail(fail.toString()); - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.setLevel(Level.ALL); - fail("Missing secure check."); - } catch (SecurityException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.setLevel((Level) null); - fail("Missing secure check."); - } catch (SecurityException | NullPointerException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.getLevel(); - } catch (SecurityException fail) { - fail(fail.toString()); - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.setFilter(BooleanFilter.FALSE); - fail("Missing secure check."); - } catch (SecurityException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.setFilter((Filter) null); - fail("Missing secure check."); - } catch (SecurityException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.getFilter(); - } catch (SecurityException fail) { - fail(fail.toString()); - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.setFormatter(new EmptyFormatter()); - fail("Missing secure check."); - } catch (SecurityException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.setFormatter((Formatter) null); - fail("Missing secure check."); - } catch (SecurityException | NullPointerException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.getFormatter(); - } catch (SecurityException fail) { - fail(fail.toString()); - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - assertNotNull(h.getErrorManager()); - fail("Missing secure check."); - } catch (SecurityException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.setErrorManager(new ErrorManager()); - fail("Missing secure check."); - } catch (SecurityException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.setErrorManager((ErrorManager) null); - fail("Missing secure check."); - } catch (SecurityException | NullPointerException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.setEncoding((String) null); - fail("Missing secure check."); - } catch (SecurityException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.getEncoding(); - } catch (SecurityException fail) { - fail(fail.toString()); - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.flush(); - } catch (SecurityException fail) { - fail(fail.toString()); - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.push(); - } catch (SecurityException fail) { - fail(fail.toString()); - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.setMailProperties(new Properties()); - fail("Missing secure check."); - } catch (SecurityException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.setMailProperties((Properties) null); - fail("Missing secure check."); - } catch (SecurityException | NullPointerException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.setPushFilter((Filter) null); - fail("Missing secure check."); - } catch (SecurityException | NullPointerException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.getPushFilter(); - } catch (SecurityException fail) { - fail(fail.toString()); - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.setPushLevel(Level.OFF); - fail("Missing secure check."); - } catch (SecurityException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.setPushLevel((Level) null); - fail("Missing secure check."); - } catch (SecurityException | NullPointerException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.getPushLevel(); - } catch (SecurityException fail) { - fail(fail.toString()); - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.setSubject((Formatter) null); - fail("Missing secure check."); - } catch (SecurityException | NullPointerException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.setSubject((String) null); - fail("Missing secure check."); - } catch (SecurityException | NullPointerException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.setSubject(new ThrowFormatter()); - fail("Missing secure check."); - } catch (SecurityException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.setSubject("test"); - fail("Missing secure check."); - } catch (SecurityException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.getSubject(); - } catch (SecurityException fail) { - fail(fail.toString()); - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - assertTrue(h.getCapacity() > 0); - } catch (SecurityException fail) { - fail(fail.toString()); - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.getAuthenticator(); - fail("Missing secure check."); - } catch (SecurityException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.getMailProperties(); - fail("Missing secure check."); - } catch (SecurityException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.close(); - fail("Missing secure check."); - } catch (SecurityException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - h.publish(new LogRecord(Level.SEVERE, "")); - h.flush(); - } catch (SecurityException fail) { - fail(fail.toString()); - } catch (Exception fail) { - fail(fail.toString()); - } - - //check for internal exceptions caused by security manager. - next: - for (Exception e : em.exceptions) { - for (Throwable t = e; t != null; t = t.getCause()) { - if (t instanceof SecurityException) { - continue next; //expected - } else if (t instanceof RuntimeException) { - throw (RuntimeException) t; //fail - } - } - } - em.exceptions.clear(); - - try { - hardRef = new MailHandler(); - fail("Missing secure check."); - } catch (SecurityException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - hardRef = new MailHandler(100); - fail("Missing secure check."); - } catch (SecurityException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - hardRef = new MailHandler(new Properties()); - fail("Missing secure check."); - } catch (SecurityException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - hardRef = new MailHandler(-100); - fail("Missing secure check."); - } catch (SecurityException | IllegalArgumentException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - - try { - hardRef = new MailHandler((Properties) null); - fail("Missing secure check."); - } catch (SecurityException | NullPointerException pass) { - } catch (Exception fail) { - fail(fail.toString()); - } - } finally { - hardRef = null; - manager.secure = false; - System.setSecurityManager((SecurityManager) null); - if (h != null) { - h.close(); - } - } - } - @Test public void testVerifyErrorManager() throws Exception { LogManager manager = LogManager.getLogManager(); @@ -5679,7 +4967,6 @@ public void testIsMissingContent() throws Exception { @Test public void testIntern() throws Exception { - assertNull(System.getSecurityManager()); final String p = MailHandler.class.getName(); Properties props = createInitProperties(p); props.put(p.concat(".errorManager"), @@ -5778,7 +5065,6 @@ private MailHandler testIntern(String p, Properties props) throws Exception { @Test public void testInternNonDiscriminating() throws Exception { - assertNull(System.getSecurityManager()); final String p = MailHandler.class.getName(); Properties props = createInitProperties(p); props.put(p.concat(".attachment.formatters"), @@ -7892,116 +7178,6 @@ public String format(LogRecord r) { } } - public static final class ThrowSecurityManager extends SecurityManager { - - boolean secure = false; - private final boolean debug; - - public ThrowSecurityManager() { - debug = isSecurityDebug(); - } - - @Override - public void checkPermission(java.security.Permission perm) { - try { //Call super class always for java.security.debug tracing. - super.checkPermission(perm); - checkPermission(perm, new SecurityException(perm.toString())); - } catch (SecurityException se) { - checkPermission(perm, se); - } - } - - @Override - public void checkPermission(java.security.Permission perm, Object context) { - try { //Call super class always for java.security.debug tracing. - super.checkPermission(perm, context); - checkPermission(perm, new SecurityException(perm.toString())); - } catch (SecurityException se) { - checkPermission(perm, se); - } - } - - private void checkPermission(java.security.Permission perm, SecurityException se) { - if (secure && perm instanceof LoggingPermission) { - throw se; - } else { - if (debug) { - securityDebugPrint(se); - } - } - } - } - - public static final class GaeErrorManager extends MessageErrorManager { - - public GaeErrorManager(MailHandler h) { - super(h.getMailProperties()); - } - - @Override - protected void error(MimeMessage message, Throwable t, int code) { - try { - assertFalse(LogManagerProperties.hasLogManager()); - String[] a = message.getHeader("auto-submitted"); - assertTrue(Arrays.toString(a), a == null || a.length == 0); - message.saveChanges(); - } catch (RuntimeException RE) { - dump(RE); - fail(RE.toString()); - } catch (Exception ME) { - dump(ME); - fail(ME.toString()); - } - } - } - - public static final class GaeSecurityManager extends SecurityManager { - - boolean secure = false; - private final boolean debug; - - public GaeSecurityManager() { - debug = isSecurityDebug(); - } - - @Override - public void checkPermission(java.security.Permission perm) { - try { //Call super class always for java.security.debug tracing. - super.checkPermission(perm); - checkPermission(perm, new SecurityException(perm.toString())); - } catch (SecurityException se) { - checkPermission(perm, se); - } - } - - @Override - public void checkPermission(java.security.Permission perm, Object context) { - try { //Call super class always for java.security.debug tracing. - super.checkPermission(perm, context); - checkPermission(perm, new SecurityException(perm.toString())); - } catch (SecurityException se) { - checkPermission(perm, se); - } - } - - private void checkPermission(java.security.Permission perm, SecurityException se) { - if (secure && perm instanceof LoggingPermission) { - final StackTraceElement[] stack = se.getStackTrace(); - if (stack.length == 0) { - Assume.assumeNoException(se); - } - for (StackTraceElement e : stack) { - if (Handler.class.getName().equals(e.getClassName())) { - throw se; - } - } - } - if (debug) { - securityDebugPrint(se); - } - } - } - public static class ErrorFormatter extends Formatter { @Override @@ -8715,53 +7891,50 @@ public boolean isLoggable(LogRecord record) { } } - private final static class ClassLoaderSecurityManager extends SecurityManager { - - volatile boolean secure = false; + private final static class ClassLoaderThread extends Thread { + volatile boolean secure; private final boolean debug; - public ClassLoaderSecurityManager() { + public ClassLoaderThread(Runnable r) { + super(r); debug = isSecurityDebug(); } - @Override - public void checkPermission(java.security.Permission perm) { - try { //Call super class always for java.security.debug tracing. - super.checkPermission(perm); - checkPermission(perm, new SecurityException(perm.toString())); - } catch (SecurityException se) { - checkPermission(perm, se); + public static void checkThread() { + Thread clt = Thread.currentThread(); + if (!(clt instanceof ClassLoaderThread)) { + throw new IllegalThreadStateException(clt.toString()); } } - @Override - public void checkPermission(java.security.Permission perm, Object context) { - try { //Call super class always for java.security.debug tracing. - super.checkPermission(perm, context); - checkPermission(perm, new SecurityException(perm.toString())); - } catch (SecurityException se) { - checkPermission(perm, se); + public static void setSecure(boolean s) { + Thread clt = Thread.currentThread(); + if (clt instanceof ClassLoaderThread) { + ((ClassLoaderThread) clt).secure = s; + } else { + throw new IllegalThreadStateException(clt.toString()); } } @Override - public void checkRead(String file, Object context) { - } + public void setContextClassLoader(ClassLoader cl) { + if (secure) { + throw new SecurityException(); + } - @Override - public void checkRead(String file) { + if (debug) { + securityDebugPrint(new SecurityException()); + } + super.setContextClassLoader(cl); } - private void checkPermission(java.security.Permission perm, SecurityException se) { - //Check for set and get context class loader. - String name = perm.getName(); - if (secure && name.contains("ContextClassLoader")) { - throw se; - } else { - if (debug) { - securityDebugPrint(se); - } + @Override + public ClassLoader getContextClassLoader() { + //It is too strict to enforce security here + if (debug) { + securityDebugPrint(new SecurityException()); } + return super.getContextClassLoader(); } }