undefect. CWE-407 — 63 sites patched across 27 ecosystems

Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
This commit is contained in:
russell@unturf.com 2026-03-26 17:11:57 -04:00
commit 0a580b313d
70422 changed files with 17213626 additions and 0 deletions

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8191053 8270380
* @summary Test that the allow/disallow options are ignored
* @library /test/lib
* @run main/othervm AllowSecurityManager
* @run main/othervm -Djava.security.manager=disallow AllowSecurityManager
* @run main/othervm AllowSecurityManager extra
*/
import jdk.test.lib.process.ProcessTools;
public class AllowSecurityManager {
public static void main(String args[]) throws Exception {
if (args.length > 0) {
// Any other system property value would fail
if (args[0].equals("extra")) {
ProcessTools.executeTestJava("-Djava.security.manager=other",
"AllowSecurityManager", "test")
.shouldHaveExitValue(1)
.shouldContain("Enabling a Security Manager is not supported.");
} else {
// The sub-process is here
}
} else {
try {
System.setSecurityManager(new SecurityManager());
throw new Exception("System.setSecurityManager did not " +
"throw UnsupportedOperationException");
} catch (UnsupportedOperationException uoe) {
// Will always happen
}
}
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 1998, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* This class tests to see if System.in.available starts
* with an appropriate value
*
* @test
* @bug 4104888
* @summary Test for System.in.available
*/
public class Available {
public static void main(String args[]) throws Exception {
int bytesAvailable = System.in.available();
if (bytesAvailable != 0)
throw new RuntimeException("System.in.available returned non-zero");
}
}

View file

@ -0,0 +1,94 @@
/*
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8260265 8282042
* @summary Test file.encoding system property
* @library /test/lib
* @build jdk.test.lib.process.*
* @run testng FileEncodingTest
*/
import java.nio.charset.Charset;
import java.util.List;
import java.util.Locale;
import jdk.test.lib.process.ProcessTools;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class FileEncodingTest {
private static final String OS_NAME = System.getProperty("os.name");
@DataProvider
public Object[][] fileEncodingToDefault() {
return new Object[][] {
{"UTF-8", "UTF-8"},
{"ISO-8859-1", "ISO-8859-1"},
{"", "UTF-8"},
{"dummy", "UTF-8"},
{"COMPAT", "<should_be_replaced>"}
};
};
@Test(dataProvider = "fileEncodingToDefault")
public void testFileEncodingToDefault(String fileEncoding, String expected) throws Exception {
if (fileEncoding.equals("COMPAT")) {
if (OS_NAME.startsWith("Windows")) {
// Only tests on English locales
if (Locale.getDefault().getLanguage().equals("en")) {
expected = "windows-1252";
} else {
System.out.println("Tests only run on Windows with English locales");
return;
}
} else if (OS_NAME.startsWith("AIX")) {
expected = "ISO-8859-1";
} else {
expected = "US-ASCII";
}
}
var cmds = fileEncoding.isEmpty()
? List.of(FileEncodingTest.class.getName(), expected)
: List.of("-Dfile.encoding=" + fileEncoding, FileEncodingTest.class.getName(), expected);
var pb = ProcessTools.createTestJavaProcessBuilder(cmds);
var env = pb.environment();
env.put("LANG", "C");
env.put("LC_ALL", "C");
ProcessTools.executeProcess(pb)
.outputTo(System.out)
.errorTo(System.err)
.shouldHaveExitValue(0);
}
public static void main(String... args) {
var def = Charset.defaultCharset().name();
var expected = args[0];
System.out.println("Default Charset: " + def + ", expected: " + expected);
if (!def.equals(expected)) {
throw new RuntimeException("default charset is not the one expected.");
}
}
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 4228188
* @summary test System.identityHashCode(null)
*/
public class IHashCode {
public static void main (String argv[]) throws Exception {
int test = System.identityHashCode(null);
if (test != 0)
throw new RuntimeException("identityHashCode(null) is "+test);
}
}

View file

@ -0,0 +1,87 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.lang.System.Logger.Level;
import java.util.EnumSet;
import java.util.Objects;
import java.util.Set;
/**
* @test
* @bug 8140364
* @summary Tests System.Logger.Level names and severity.
* @author danielfuchs
* @modules java.logging
*/
public class LoggerLevelTest {
public static void main(String[] args) {
Set<Level> untested = EnumSet.allOf(Level.class);
testLevel(untested, Level.ALL, java.util.logging.Level.ALL);
testLevel(untested, Level.TRACE, java.util.logging.Level.FINER);
testLevel(untested, Level.DEBUG, java.util.logging.Level.FINE);
testLevel(untested, Level.INFO, java.util.logging.Level.INFO);
testLevel(untested, Level.WARNING, java.util.logging.Level.WARNING);
testLevel(untested, Level.ERROR, java.util.logging.Level.SEVERE);
testLevel(untested, Level.OFF, java.util.logging.Level.OFF);
if (!untested.isEmpty()) {
throw new RuntimeException("Some level values were not tested: " + untested);
}
}
private static void testLevel(Set<Level> untested, Level systemLevel, java.util.logging.Level julLevel) {
untested.remove(systemLevel);
assertEquals(systemLevel.getName(), systemLevel.name(),
"System.Logger.Level." + systemLevel.name() + ".getName()");
assertEquals(systemLevel.getSeverity(), julLevel.intValue(),
"System.Logger.Level." + systemLevel.name() + ".getSeverity");
}
private static void assertEquals(Object actual, Object expected, String what) {
if (!Objects.equals(actual, expected)) {
throw new RuntimeException("Bad value for " + what
+ "\n\t expected: " + expected
+ "\n\t actual: " + actual);
} else {
System.out.println("Got expected value for " + what + ": " + actual);
}
}
private static void assertEquals(int actual, int expected, String what) {
if (!Objects.equals(actual, expected)) {
throw new RuntimeException("Bad value for " + what
+ "\n\t expected: " + toString(expected)
+ "\n\t actual: " + toString(actual));
} else {
System.out.println("Got expected value for " + what + ": " + toString(actual));
}
}
private static String toString(int value) {
switch (value) {
case Integer.MAX_VALUE: return "Integer.MAX_VALUE";
case Integer.MIN_VALUE: return "Integer.MIN_VALUE";
default:
return Integer.toString(value);
}
}
}

View file

@ -0,0 +1,78 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.IOException;
import java.lang.System.Logger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ResourceBundle;
/**
*
* @author danielfuchs
*/
public final class AccessSystemLogger {
public AccessSystemLogger() {
this(check());
}
private AccessSystemLogger(Void unused) {
}
private static Void check() {
if (AccessSystemLogger.class.getClassLoader() != null) {
throw new RuntimeException("AccessSystemLogger should be loaded by the null classloader");
}
return null;
}
public Logger getLogger(String name) {
Logger logger = System.getLogger(name);
System.out.println("System.getLogger(\"" + name + "\"): " + logger);
return logger;
}
public Logger getLogger(String name, ResourceBundle bundle) {
Logger logger = System.getLogger(name, bundle);
System.out.println("System.getLogger(\"" + name + "\", bundle): " + logger);
return logger;
}
// copy AccessSystemLogger.class to ./boot
public static void main(String[] args) throws IOException {
Path testDir = Paths.get(System.getProperty("user.dir", "."));
Path bootDir = Paths.get(testDir.toString(), "boot");
Path classes = Paths.get(System.getProperty("test.classes", "build/classes"));
Path thisClass = Paths.get(classes.toString(),
AccessSystemLogger.class.getSimpleName()+".class");
if (Files.notExists(bootDir)) {
Files.createDirectory(bootDir);
}
Path dest = Paths.get(bootDir.toString(),
AccessSystemLogger.class.getSimpleName()+".class");
Files.copy(thisClass, dest, StandardCopyOption.REPLACE_EXISTING);
}
}

View file

@ -0,0 +1,577 @@
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.ResourceBundle;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
import java.lang.System.LoggerFinder;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
/**
* @test
* @bug 8140364
* @summary Tests loggers returned by System.getLogger with a naive implementation
* of LoggerFinder, and in particular the default body of
* System.Logger methods.
* @build CustomLoggerTest AccessSystemLogger
* @run driver AccessSystemLogger
* @run main/othervm -Xbootclasspath/a:boot CustomLoggerTest
* @author danielfuchs
*/
public class CustomLoggerTest {
final static AtomicLong sequencer = new AtomicLong();
final static boolean VERBOSE = false;
public static class MyBundle extends ResourceBundle {
final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
@Override
protected Object handleGetObject(String key) {
if (key.contains(" (translated)")) {
throw new RuntimeException("Unexpected key: " + key);
}
return map.computeIfAbsent(key, k -> k + " (translated)");
}
@Override
public Enumeration<String> getKeys() {
return Collections.enumeration(map.keySet());
}
}
public static class MyLoggerBundle extends MyBundle {
}
public static class BaseLoggerFinder extends LoggerFinder {
final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>();
final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>();
public Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
// changing this to true requires changing the logic in the
// test in order to load this class with a protection domain
// that has the CONTROL_PERMISSION (e.g. by using a custom
// system class loader.
final boolean doChecks = false;
public static final class LogEvent {
public LogEvent() {
this(sequencer.getAndIncrement());
}
LogEvent(long sequenceNumber) {
this.sequenceNumber = sequenceNumber;
}
long sequenceNumber;
boolean isLoggable;
String loggerName;
Level level;
ResourceBundle bundle;
Throwable thrown;
Object[] args;
Supplier<String> supplier;
String msg;
Object[] toArray() {
return new Object[] {
sequenceNumber,
isLoggable,
loggerName,
level,
bundle,
thrown,
args,
supplier,
msg,
};
}
@Override
public String toString() {
return Arrays.deepToString(toArray());
}
@Override
public boolean equals(Object obj) {
return obj instanceof LogEvent
&& Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
}
@Override
public int hashCode() {
return Objects.hash(toArray());
}
public static LogEvent of(boolean isLoggable, String name,
Level level, ResourceBundle bundle,
String key, Throwable thrown) {
LogEvent evt = new LogEvent();
evt.isLoggable = isLoggable;
evt.loggerName = name;
evt.level = level;
evt.args = null;
evt.bundle = bundle;
evt.thrown = thrown;
evt.supplier = null;
evt.msg = key;
return evt;
}
public static LogEvent of(boolean isLoggable, String name,
Level level, ResourceBundle bundle,
String key, Object... params) {
LogEvent evt = new LogEvent();
evt.isLoggable = isLoggable;
evt.loggerName = name;
evt.level = level;
evt.args = params;
evt.bundle = bundle;
evt.thrown = null;
evt.supplier = null;
evt.msg = key;
return evt;
}
public static LogEvent of(long sequenceNumber,
boolean isLoggable, String name,
Level level, ResourceBundle bundle,
String key, Supplier<String> supplier,
Throwable thrown, Object... params) {
LogEvent evt = new LogEvent(sequenceNumber);
evt.loggerName = name;
evt.level = level;
evt.args = params;
evt.bundle = bundle;
evt.thrown = thrown;
evt.supplier = supplier;
evt.msg = key;
evt.isLoggable = isLoggable;
return evt;
}
}
public class LoggerImpl implements Logger {
private final String name;
private Level level = Level.INFO;
public LoggerImpl(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public boolean isLoggable(Level level) {
return this.level != Level.OFF && this.level.getSeverity() <= level.getSeverity();
}
@Override
public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {
log(LogEvent.of(isLoggable(level), this.name, level, bundle, key, thrown));
}
@Override
public void log(Level level, ResourceBundle bundle, String format, Object... params) {
log(LogEvent.of(isLoggable(level), name, level, bundle, format, params));
}
void log(LogEvent event) {
eventQueue.add(event);
}
}
@Override
public Logger getLogger(String name, Module caller) {
final ClassLoader callerLoader = caller.getClassLoader();
if (callerLoader == null) {
return system.computeIfAbsent(name, (n) -> new LoggerImpl(n));
} else {
return user.computeIfAbsent(name, (n) -> new LoggerImpl(n));
}
}
}
static final AccessSystemLogger accessSystemLogger = new AccessSystemLogger();
public static void main(String[] args) {
// 1. Obtain destination loggers directly from the LoggerFinder
// - LoggerFinder.getLogger("foo", type)
BaseLoggerFinder provider =
BaseLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
BaseLoggerFinder.LoggerImpl appSink =
BaseLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", CustomLoggerTest.class.getModule()));
BaseLoggerFinder.LoggerImpl sysSink =
BaseLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", Thread.class.getModule()));
test(provider, true, appSink, sysSink);
System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
}
public static void test(BaseLoggerFinder provider, boolean hasRequiredPermissions,
BaseLoggerFinder.LoggerImpl appSink, BaseLoggerFinder.LoggerImpl sysSink) {
ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
final Map<Logger, String> loggerDescMap = new HashMap<>();
// 1. Test loggers returned by:
// - System.getLogger("foo")
// - and AccessSystemLogger.getLogger("foo")
Logger appLogger1 = System.getLogger("foo");
loggerDescMap.put(appLogger1, "System.getLogger(\"foo\");");
Logger sysLogger1 = null;
sysLogger1 = accessSystemLogger.getLogger("foo");
loggerDescMap.put(sysLogger1, "AccessSystemLogger.getLogger(\"foo\")");
if (appLogger1 == sysLogger1) {
throw new RuntimeException("identical loggers");
}
if (provider.system.contains(appLogger1)) {
throw new RuntimeException("app logger in system map");
}
if (provider.user.contains(sysLogger1)) {
throw new RuntimeException("sys logger in appplication map");
}
if (provider.system.contains(sysLogger1)) {
// sysLogger should be a LazyLoggerWrapper
throw new RuntimeException("sys logger is in system map (should be wrapped)");
}
// 2. Test loggers returned by:
// - System.getLogger(\"foo\", loggerBundle)
// - and AccessSystemLogger.getLogger(\"foo\", loggerBundle)
Logger appLogger2 =
System.getLogger("foo", loggerBundle);
loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)");
Logger sysLogger2 = null;
sysLogger2 = accessSystemLogger.getLogger("foo", loggerBundle);
loggerDescMap.put(sysLogger2, "AccessSystemLogger.getLogger(\"foo\", loggerBundle)");
if (appLogger2 == sysLogger2) {
throw new RuntimeException("identical loggers");
}
if (appLogger2 == appSink) {
throw new RuntimeException("identical loggers");
}
if (sysLogger2 == sysSink) {
throw new RuntimeException("identical loggers");
}
if (provider.system.contains(appLogger2)) {
throw new RuntimeException("localized app logger in system map");
}
if (provider.user.contains(appLogger2)) {
throw new RuntimeException("localized app logger in appplication map");
}
if (provider.user.contains(sysLogger2)) {
throw new RuntimeException("localized sys logger in appplication map");
}
if (provider.system.contains(sysLogger2)) {
throw new RuntimeException("localized sys logger not in system map");
}
testLogger(provider, loggerDescMap, "foo", null, appLogger1, appSink);
testLogger(provider, loggerDescMap, "foo", null, sysLogger1, sysSink);
testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger2, appSink);
testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger2, sysSink);
}
public static class Foo {
}
static void verbose(String msg) {
if (VERBOSE) {
System.out.println(msg);
}
}
// Calls the 8 methods defined on Logger and verify the
// parameters received by the underlying BaseLoggerFinder.LoggerImpl
// logger.
private static void testLogger(BaseLoggerFinder provider,
Map<Logger, String> loggerDescMap,
String name,
ResourceBundle loggerBundle,
Logger logger,
BaseLoggerFinder.LoggerImpl sink) {
System.out.println("Testing " + loggerDescMap.get(logger));
Foo foo = new Foo();
String fooMsg = foo.toString();
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, foo): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
BaseLoggerFinder.LogEvent expected =
BaseLoggerFinder.LogEvent.of(
sequencer.get(),
messageLevel.compareTo(loggerLevel) >= 0,
name, messageLevel, (ResourceBundle)null,
fooMsg, null, (Throwable)null, (Object[])null);
logger.log(messageLevel, foo);
if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (provider.eventQueue.poll() != null) {
throw new RuntimeException("unexpected event in queue for " + desc);
}
} else {
BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
}
String msg = "blah";
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
BaseLoggerFinder.LogEvent expected =
BaseLoggerFinder.LogEvent.of(
sequencer.get(),
messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
name, messageLevel, loggerBundle,
msg, null, (Throwable)null, (Object[])null);
logger.log(messageLevel, msg);
BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
Supplier<String> fooSupplier = new Supplier<String>() {
@Override
public String get() {
return this.toString();
}
};
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
BaseLoggerFinder.LogEvent expected =
BaseLoggerFinder.LogEvent.of(
sequencer.get(),
messageLevel.compareTo(loggerLevel) >= 0,
name, messageLevel, (ResourceBundle)null,
fooSupplier.get(), null,
(Throwable)null, (Object[])null);
logger.log(messageLevel, fooSupplier);
if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (provider.eventQueue.poll() != null) {
throw new RuntimeException("unexpected event in queue for " + desc);
}
} else {
BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
}
String format = "two params [{1} {2}]";
Object arg1 = foo;
Object arg2 = msg;
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, format, params...): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
BaseLoggerFinder.LogEvent expected =
BaseLoggerFinder.LogEvent.of(
sequencer.get(),
messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
name, messageLevel, loggerBundle,
format, null, (Throwable)null, new Object[] {arg1, arg2});
logger.log(messageLevel, format, arg1, arg2);
BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
Throwable thrown = new Exception("OK: log me!");
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
BaseLoggerFinder.LogEvent expected =
BaseLoggerFinder.LogEvent.of(
sequencer.get(),
messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
name, messageLevel, loggerBundle,
msg, null, thrown, (Object[]) null);
logger.log(messageLevel, msg, thrown);
BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
BaseLoggerFinder.LogEvent expected =
BaseLoggerFinder.LogEvent.of(
sequencer.get(),
messageLevel.compareTo(loggerLevel) >= 0,
name, messageLevel, (ResourceBundle)null,
fooSupplier.get(), null,
(Throwable)thrown, (Object[])null);
logger.log(messageLevel, fooSupplier, thrown);
if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (provider.eventQueue.poll() != null) {
throw new RuntimeException("unexpected event in queue for " + desc);
}
} else {
BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
}
ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
BaseLoggerFinder.LogEvent expected =
BaseLoggerFinder.LogEvent.of(
sequencer.get(),
messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
name, messageLevel, bundle,
format, null, (Throwable)null, new Object[] {foo, msg});
logger.log(messageLevel, bundle, format, foo, msg);
BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
BaseLoggerFinder.LogEvent expected =
BaseLoggerFinder.LogEvent.of(
sequencer.get(),
messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
name, messageLevel, bundle,
msg, null, thrown, (Object[]) null);
logger.log(messageLevel, bundle, msg, thrown);
BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
}
}

View file

@ -0,0 +1 @@
CustomLoggerTest$BaseLoggerFinder

View file

@ -0,0 +1,83 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.IOException;
import java.lang.System.Logger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ResourceBundle;
import java.util.logging.LogManager;
/**
*
* @author danielfuchs
*/
public final class AccessSystemLogger {
public AccessSystemLogger() {
this(check());
}
private AccessSystemLogger(Void unused) {
}
private static Void check() {
if (AccessSystemLogger.class.getClassLoader() != null) {
throw new RuntimeException("AccessSystemLogger should be loaded by the null classloader");
}
return null;
}
public Logger getLogger(String name) {
Logger logger = System.getLogger(name);
System.out.println("System.getLogger(\"" + name + "\"): " + logger);
return logger;
}
public Logger getLogger(String name, ResourceBundle bundle) {
Logger logger = System.getLogger(name, bundle);
System.out.println("System.getLogger(\"" + name + "\", bundle): " + logger);
return logger;
}
public java.util.logging.Logger demandSystemLogger(String name) {
return java.util.logging.Logger.getLogger(name);
}
// copy AccessSystemLogger.class to ./boot
public static void main(String[] args) throws IOException {
Path testDir = Paths.get(System.getProperty("user.dir", "."));
Path bootDir = Paths.get(testDir.toString(), "boot");
Path classes = Paths.get(System.getProperty("test.classes", "build/classes"));
Path thisClass = Paths.get(classes.toString(),
AccessSystemLogger.class.getSimpleName()+".class");
if (Files.notExists(bootDir)) {
Files.createDirectory(bootDir);
}
Path dest = Paths.get(bootDir.toString(),
AccessSystemLogger.class.getSimpleName()+".class");
Files.copy(thisClass, dest, StandardCopyOption.REPLACE_EXISTING);
}
}

View file

@ -0,0 +1,762 @@
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.ResourceBundle;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
import java.lang.System.LoggerFinder;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Function;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.util.stream.Stream;
/**
* @test
* @bug 8140364 8145686
* @summary Tests default loggers returned by System.getLogger, and in
* particular the implementation of the System.Logger method
* performed by the default binding.
* @modules java.logging
* @build DefaultLoggerTest AccessSystemLogger
* @run driver AccessSystemLogger
* @run main/othervm -Xbootclasspath/a:boot DefaultLoggerTest DEFAULTS
* @run main/othervm -Xbootclasspath/a:boot DefaultLoggerTest WITHCUSTOMWRAPPERS
* @run main/othervm -Xbootclasspath/a:boot DefaultLoggerTest WITHREFLECTION
*/
public class DefaultLoggerTest {
final static AtomicLong sequencer = new AtomicLong();
final static boolean VERBOSE = false;
public static final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
public static final class LogEvent {
public LogEvent() {
this(sequencer.getAndIncrement());
}
LogEvent(long sequenceNumber) {
this.sequenceNumber = sequenceNumber;
}
long sequenceNumber;
boolean isLoggable;
String loggerName;
java.util.logging.Level level;
ResourceBundle bundle;
Throwable thrown;
Object[] args;
String msg;
String className;
String methodName;
Object[] toArray() {
return new Object[] {
sequenceNumber,
isLoggable,
loggerName,
level,
bundle,
thrown,
args,
msg,
className,
methodName,
};
}
@Override
public String toString() {
return Arrays.deepToString(toArray());
}
@Override
public boolean equals(Object obj) {
return obj instanceof LogEvent
&& Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
}
@Override
public int hashCode() {
return Objects.hash(toArray());
}
public static LogEvent of(long sequenceNumber,
boolean isLoggable, String name,
java.util.logging.Level level, ResourceBundle bundle,
String key, Throwable thrown, Object... params) {
return LogEvent.of(sequenceNumber, isLoggable, name,
DefaultLoggerTest.class.getName(),
"testLogger", level, bundle, key,
thrown, params);
}
public static LogEvent of(long sequenceNumber,
boolean isLoggable, String name,
String className, String methodName,
java.util.logging.Level level, ResourceBundle bundle,
String key, Throwable thrown, Object... params) {
LogEvent evt = new LogEvent(sequenceNumber);
evt.loggerName = name;
evt.level = level;
evt.args = params;
evt.bundle = bundle;
evt.thrown = thrown;
evt.msg = key;
evt.isLoggable = isLoggable;
evt.className = className;
evt.methodName = methodName;
return evt;
}
}
static java.util.logging.Level mapToJul(Level level) {
switch (level) {
case ALL: return java.util.logging.Level.ALL;
case TRACE: return java.util.logging.Level.FINER;
case DEBUG: return java.util.logging.Level.FINE;
case INFO: return java.util.logging.Level.INFO;
case WARNING: return java.util.logging.Level.WARNING;
case ERROR: return java.util.logging.Level.SEVERE;
case OFF: return java.util.logging.Level.OFF;
}
throw new InternalError("No such level: " + level);
}
static void setLevel(java.util.logging.Logger sink, java.util.logging.Level loggerLevel) {
sink.setLevel(loggerLevel);
}
public static class MyHandler extends Handler {
@Override
public java.util.logging.Level getLevel() {
return java.util.logging.Level.ALL;
}
@Override
public void publish(LogRecord record) {
eventQueue.add(LogEvent.of(sequencer.getAndIncrement(),
true, record.getLoggerName(),
record.getSourceClassName(),
record.getSourceMethodName(),
record.getLevel(),
record.getResourceBundle(), record.getMessage(),
record.getThrown(), record.getParameters()));
}
@Override
public void flush() {
}
@Override
public void close() throws SecurityException {
}
}
public static class MyBundle extends ResourceBundle {
final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
@Override
protected Object handleGetObject(String key) {
if (key.contains(" (translated)")) {
throw new RuntimeException("Unexpected key: " + key);
}
return map.computeIfAbsent(key, k -> k + " (translated)");
}
@Override
public Enumeration<String> getKeys() {
return Collections.enumeration(map.keySet());
}
}
public static class MyLoggerBundle extends MyBundle {
}
static final AccessSystemLogger accessSystemLogger = new AccessSystemLogger();
static enum TestCases {DEFAULTS, WITHCUSTOMWRAPPERS, WITHREFLECTION};
/**
* The CustomLoggerWrapper makes it possible to verify that classes
* which implements System.Logger will be skipped when looking for
* the calling method.
*/
static class CustomLoggerWrapper implements Logger {
Logger impl;
public CustomLoggerWrapper(Logger logger) {
this.impl = Objects.requireNonNull(logger);
}
@Override
public String getName() {
return impl.getName();
}
@Override
public boolean isLoggable(Level level) {
return impl.isLoggable(level);
}
@Override
public void log(Level level, ResourceBundle rb, String string, Throwable thrwbl) {
impl.log(level, rb, string, thrwbl);
}
@Override
public void log(Level level, ResourceBundle rb, String string, Object... os) {
impl.log(level, rb, string, os);
}
@Override
public void log(Level level, Object o) {
impl.log(level, o);
}
@Override
public void log(Level level, String string) {
impl.log(level, string);
}
@Override
public void log(Level level, Supplier<String> splr) {
impl.log(level, splr);
}
@Override
public void log(Level level, String string, Object... os) {
impl.log(level, string, os);
}
@Override
public void log(Level level, String string, Throwable thrwbl) {
impl.log(level, string, thrwbl);
}
@Override
public void log(Level level, Supplier<String> splr, Throwable thrwbl) {
Logger.super.log(level, splr, thrwbl);
}
@Override
public String toString() {
return super.toString() + "(impl=" + impl + ")";
}
}
/**
* The ReflectionLoggerWrapper additionally makes it possible to verify
* that code which use reflection to call System.Logger will be skipped
* when looking for the calling method.
*/
static class ReflectionLoggerWrapper implements Logger {
Logger impl;
public ReflectionLoggerWrapper(Logger logger) {
this.impl = Objects.requireNonNull(logger);
}
private Object invoke(Method m, Object... params) {
try {
return m.invoke(impl, params);
} catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException ex) {
throw new RuntimeException(ex);
}
}
@Override
public String getName() {
return impl.getName();
}
@Override
public boolean isLoggable(Level level) {
return impl.isLoggable(level);
}
@Override
public void log(Level level, ResourceBundle rb, String string, Throwable thrwbl) {
try {
invoke(System.Logger.class.getMethod(
"log", Level.class, ResourceBundle.class, String.class, Throwable.class),
level, rb, string, thrwbl);
} catch (NoSuchMethodException ex) {
throw new RuntimeException(ex);
}
}
@Override
public void log(Level level, ResourceBundle rb, String string, Object... os) {
try {
invoke(System.Logger.class.getMethod(
"log", Level.class, ResourceBundle.class, String.class, Object[].class),
level, rb, string, os);
} catch (NoSuchMethodException ex) {
throw new RuntimeException(ex);
}
}
@Override
public void log(Level level, String string) {
try {
invoke(System.Logger.class.getMethod(
"log", Level.class, String.class),
level, string);
} catch (NoSuchMethodException ex) {
throw new RuntimeException(ex);
}
}
@Override
public void log(Level level, String string, Object... os) {
try {
invoke(System.Logger.class.getMethod(
"log", Level.class, String.class, Object[].class),
level, string, os);
} catch (NoSuchMethodException ex) {
throw new RuntimeException(ex);
}
}
@Override
public void log(Level level, String string, Throwable thrwbl) {
try {
invoke(System.Logger.class.getMethod(
"log", Level.class, String.class, Throwable.class),
level, string, thrwbl);
} catch (NoSuchMethodException ex) {
throw new RuntimeException(ex);
}
}
@Override
public String toString() {
return super.toString() + "(impl=" + impl + ")";
}
}
public static void main(String[] args) {
if (args.length == 0)
args = new String[] {
"DEFAULTS",
"WITHCUSTOMWRAPPERS",
"WITHREFLECTION"
};
// 1. Obtain destination loggers directly from the LoggerFinder
// - LoggerFinder.getLogger("foo", type)
Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
switch (testCase) {
case DEFAULTS:
System.out.println("\n*** Using defaults\n");
test();
System.out.println("Tetscase count: " + sequencer.get());
break;
case WITHCUSTOMWRAPPERS:
System.out.println("\n*** Using custom Wrappers\n");
test(CustomLoggerWrapper::new);
System.out.println("Tetscase count: " + sequencer.get());
break;
case WITHREFLECTION:
System.out.println("\n*** Using reflection while logging\n");
test(ReflectionLoggerWrapper::new);
System.out.println("Tetscase count: " + sequencer.get());
break;
default:
throw new RuntimeException("Unknown test case: " + testCase);
}
});
System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
}
public static void test() {
test(Function.identity());
}
public static void test(Function<Logger, Logger> wrapper) {
ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
final Map<Logger, String> loggerDescMap = new HashMap<>();
// 1. Test loggers returned by:
// - System.getLogger("foo")
// - and AccessSystemLogger.getLogger("foo")
Logger sysLogger1 = null;
sysLogger1 = wrapper.apply(accessSystemLogger.getLogger("foo"));
loggerDescMap.put(sysLogger1, "AccessSystemLogger.getLogger(\"foo\")");
Logger appLogger1 = wrapper.apply(System.getLogger("foo"));
loggerDescMap.put(appLogger1, "System.getLogger(\"foo\");");
if (appLogger1 == sysLogger1) {
throw new RuntimeException("identical loggers");
}
// 2. Test loggers returned by:
// - System.getLogger(\"foo\", loggerBundle)
// - and AccessSystemLogger.getLogger(\"foo\", loggerBundle)
Logger appLogger2 = wrapper.apply(
System.getLogger("foo", loggerBundle));
loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)");
Logger sysLogger2 = null;
sysLogger2 = wrapper.apply(accessSystemLogger.getLogger("foo", loggerBundle));
loggerDescMap.put(sysLogger2, "AccessSystemLogger.getLogger(\"foo\", loggerBundle)");
if (appLogger2 == sysLogger2) {
throw new RuntimeException("identical loggers");
}
final java.util.logging.Logger sink;
final java.util.logging.Logger appSink;
final java.util.logging.Logger sysSink;
final java.util.logging.Handler appHandler;
final java.util.logging.Handler sysHandler;
final LoggerFinder provider;
appSink = java.util.logging.Logger.getLogger("foo");
sysSink = accessSystemLogger.demandSystemLogger("foo");
sink = java.util.logging.Logger.getLogger("foo");
sink.addHandler(appHandler = sysHandler = new MyHandler());
sink.setUseParentHandlers(false);
provider = LoggerFinder.getLoggerFinder();
try {
testLogger(provider, loggerDescMap, "foo", null, sysLogger1, sysSink);
testLogger(provider, loggerDescMap, "foo", null, appLogger1, appSink);
testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger2, sysSink);
testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger2, appSink);
} finally {
appSink.removeHandler(appHandler);
sysSink.removeHandler(sysHandler);
sysSink.setLevel(null);
appSink.setLevel(null);
}
}
public static class Foo {
}
static void verbose(String msg) {
if (VERBOSE) {
System.out.println(msg);
}
}
// Calls the 8 methods defined on Logger and verify the
// parameters received by the underlying BaseLoggerFinder.LoggerImpl
// logger.
private static void testLogger(LoggerFinder provider,
Map<Logger, String> loggerDescMap,
String name,
ResourceBundle loggerBundle,
Logger logger,
java.util.logging.Logger sink) {
System.out.println("Testing " + loggerDescMap.get(logger));
Foo foo = new Foo();
String fooMsg = foo.toString();
for (Level loggerLevel : Level.values()) {
setLevel(sink, mapToJul(loggerLevel));
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, foo): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
messageLevel.compareTo(loggerLevel) >= 0,
name, mapToJul(messageLevel), (ResourceBundle)null,
fooMsg, (Throwable)null, (Object[])null);
logger.log(messageLevel, foo);
if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (eventQueue.poll() != null) {
throw new RuntimeException("unexpected event in queue for " + desc);
}
} else {
LogEvent actual = eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
}
String msg = "blah";
for (Level loggerLevel : Level.values()) {
setLevel(sink, mapToJul(loggerLevel));
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
name, mapToJul(messageLevel), loggerBundle,
msg, (Throwable)null, (Object[])null);
logger.log(messageLevel, msg);
if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (eventQueue.poll() != null) {
throw new RuntimeException("unexpected event in queue for " + desc);
}
} else {
LogEvent actual = eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
}
Supplier<String> fooSupplier = new Supplier<String>() {
@Override
public String get() {
return this.toString();
}
};
for (Level loggerLevel : Level.values()) {
setLevel(sink, mapToJul(loggerLevel));
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
messageLevel.compareTo(loggerLevel) >= 0,
name, mapToJul(messageLevel), (ResourceBundle)null,
fooSupplier.get(),
(Throwable)null, (Object[])null);
logger.log(messageLevel, fooSupplier);
if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (eventQueue.poll() != null) {
throw new RuntimeException("unexpected event in queue for " + desc);
}
} else {
LogEvent actual = eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
}
String format = "two params [{1} {2}]";
Object arg1 = foo;
Object arg2 = msg;
for (Level loggerLevel : Level.values()) {
setLevel(sink, mapToJul(loggerLevel));
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, format, params...): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
name, mapToJul(messageLevel), loggerBundle,
format, (Throwable)null, new Object[] {arg1, arg2});
logger.log(messageLevel, format, arg1, arg2);
if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (eventQueue.poll() != null) {
throw new RuntimeException("unexpected event in queue for " + desc);
}
} else {
LogEvent actual = eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
}
Throwable thrown = new Exception("OK: log me!");
for (Level loggerLevel : Level.values()) {
setLevel(sink, mapToJul(loggerLevel));
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
name, mapToJul(messageLevel), loggerBundle,
msg, thrown, (Object[]) null);
logger.log(messageLevel, msg, thrown);
if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (eventQueue.poll() != null) {
throw new RuntimeException("unexpected event in queue for " + desc);
}
} else {
LogEvent actual = eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
}
for (Level loggerLevel : Level.values()) {
setLevel(sink, mapToJul(loggerLevel));
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
messageLevel.compareTo(loggerLevel) >= 0,
name, mapToJul(messageLevel), (ResourceBundle)null,
fooSupplier.get(),
(Throwable)thrown, (Object[])null);
logger.log(messageLevel, fooSupplier, thrown);
if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (eventQueue.poll() != null) {
throw new RuntimeException("unexpected event in queue for " + desc);
}
} else {
LogEvent actual = eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
}
ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
for (Level loggerLevel : Level.values()) {
setLevel(sink, mapToJul(loggerLevel));
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
name, mapToJul(messageLevel), bundle,
format, (Throwable)null, new Object[] {foo, msg});
logger.log(messageLevel, bundle, format, foo, msg);
if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (eventQueue.poll() != null) {
throw new RuntimeException("unexpected event in queue for " + desc);
}
} else {
LogEvent actual = eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
}
for (Level loggerLevel : Level.values()) {
setLevel(sink, mapToJul(loggerLevel));
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
name, mapToJul(messageLevel), bundle,
msg, thrown, (Object[]) null);
logger.log(messageLevel, bundle, msg, thrown);
if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (eventQueue.poll() != null) {
throw new RuntimeException("unexpected event in queue for " + desc);
}
} else {
LogEvent actual = eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
}
}
}

View file

@ -0,0 +1,592 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.ResourceBundle;
import java.util.function.Consumer;
import java.lang.System.Logger.Level;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Objects;
import java.util.Queue;
import java.util.function.Supplier;
/**
* @test
* @bug 8140364
* @summary Tests the default body of the System.Logger interface.
* @author danielfuchs
*/
public class LoggerInterfaceTest {
public static class LoggerImpl implements System.Logger {
public static class LogEvent implements Cloneable {
Level level;
ResourceBundle bundle;
String msg;
Throwable thrown;
Object[] params;
StackTraceElement[] callStack;
@Override
protected LogEvent clone() {
try {
return (LogEvent)super.clone();
} catch (CloneNotSupportedException x) {
throw new RuntimeException(x);
}
}
}
public static class LogEventBuilder {
private LogEvent event = new LogEvent();
public LogEventBuilder level(Level level) {
event.level = level;
return this;
}
public LogEventBuilder stack(StackTraceElement... stack) {
event.callStack = stack;
return this;
}
public LogEventBuilder bundle(ResourceBundle bundle) {
event.bundle = bundle;
return this;
}
public LogEventBuilder msg(String msg) {
event.msg = msg;
return this;
}
public LogEventBuilder thrown(Throwable thrown) {
event.thrown = thrown;
return this;
}
public LogEventBuilder params(Object... params) {
event.params = params;
return this;
}
public LogEvent build() {
return event.clone();
}
public LogEventBuilder clear() {
event = new LogEvent();
return this;
}
}
Level level = Level.WARNING;
Consumer<LogEvent> consumer;
final LogEventBuilder builder = new LogEventBuilder();
@Override
public String getName() {
return "noname";
}
@Override
public boolean isLoggable(Level level) {
return level.getSeverity() >= this.level.getSeverity();
}
@Override
public void log(Level level, ResourceBundle bundle, String msg, Throwable thrown) {
builder.clear().level(level).bundle(bundle).msg(msg).thrown(thrown)
.stack(new Exception().getStackTrace());
consumer.accept(builder.build());
}
@Override
public void log(Level level, ResourceBundle bundle, String format, Object... params) {
builder.clear().level(level).bundle(bundle).msg(format).params(params)
.stack(new Exception().getStackTrace());
consumer.accept(builder.build());
}
}
static class Throwing {
@Override
public String toString() {
throw new RuntimeException("should not have been called");
}
}
static class NotTrowing {
private final String toString;
private int count = 0;
public NotTrowing(String toString) {
this.toString = toString;
}
@Override
public String toString() {
return toString + "[" + (++count) + "]";
}
}
public static void main(String[] args) {
final LoggerImpl loggerImpl = new LoggerImpl();
final System.Logger logger = loggerImpl;
final Queue<LoggerImpl.LogEvent> events = new LinkedList<>();
loggerImpl.consumer = (x) -> events.add(x);
System.out.println("\nlogger.isLoggable(Level)");
assertTrue(logger.isLoggable(Level.WARNING), "logger.isLoggable(Level.WARNING)"," ");
assertFalse(logger.isLoggable(Level.INFO), "logger.isLoggable(Level.INFO)", " ");
System.out.println("\nlogger.log(Level, Object)");
for (Level l : Level.values()) {
boolean logged = l.compareTo(Level.WARNING) >= 0;
Object[][] cases = new Object[][] {
{null}, {"baz"}
};
for (Object[] p : cases) {
String msg = (String)p[0];
final Object obj = msg == null ? null : logged ? new NotTrowing(msg) : new Throwing();
String par1 = msg == null ? "(Object)null"
: logged ? "new NotTrowing(\""+ msg+"\")" : "new Throwing()";
System.out.println(" logger.log(" + l + ", " + par1 + ")");
try {
logger.log(l, obj);
if (obj == null) {
throw new RuntimeException("Expected NullPointerException not thrown for"
+ " logger.log(" + l + ", " + par1 + ")");
}
} catch (NullPointerException x) {
if (obj == null) {
System.out.println(" Got expected exception: " + x);
continue;
} else {
throw x;
}
}
LoggerImpl.LogEvent e = events.poll();
if (logged) {
assertNonNull(e, "e", " ");
assertEquals(l, e.level, "e.level", " ");
assertToString(e.msg, msg, 1, "e.msg", " ");
assertEquals(e.bundle, null, "e.bundle", " ");
assertEquals(e.params, null, "e.params", " ");
assertEquals(e.thrown, null, "e.thrown", " ");
assertEquals(e.bundle, null, "e.bundle", " ");
assertEquals(e.callStack[0].getMethodName(), "log",
"e.callStack[0].getMethodName()", " ");
assertEquals(e.callStack[0].getClassName(),
logger.getClass().getName(),
"e.callStack[0].getClassName() ", " ");
assertEquals(e.callStack[1].getMethodName(), "log",
"e.callStack[1].getMethodName()", " ");
assertEquals(e.callStack[1].getClassName(),
System.Logger.class.getName(),
"e.callStack[1].getClassName() ", " ");
assertEquals(e.callStack[2].getMethodName(), "main",
"e.callStack[2].getMethodName()", " ");
} else {
assertEquals(e, null, "e", " ");
}
}
}
System.out.println(" logger.log(" + null + ", " +
"new NotThrowing(\"foobar\")" + ")");
try {
logger.log(null, new NotTrowing("foobar"));
throw new RuntimeException("Expected NullPointerException not thrown for"
+ " logger.log(" + null + ", "
+ "new NotThrowing(\"foobar\")" + ")");
} catch (NullPointerException x) {
System.out.println(" Got expected exception: " + x);
}
System.out.println("\nlogger.log(Level, String)");
for (Level l : Level.values()) {
boolean logged = l.compareTo(Level.WARNING) >= 0;
String par = "bar";
System.out.println(" logger.log(" + l + ", \"" + par +"\");");
logger.log(l, par);
LoggerImpl.LogEvent e = events.poll();
assertNonNull(e, "e", " ");
assertEquals(e.level, l, "e.level", " ");
assertEquals(e.msg, "bar", "e.msg", " ");
assertEquals(e.bundle, null, "e.bundle", " ");
assertEquals(e.params, null, "e.params", " ");
assertEquals(e.thrown, null, "e.thrown", " ");
assertEquals(e.bundle, null, "e.bundle", " ");
assertEquals(e.callStack[0].getMethodName(), "log",
"e.callStack[0].getMethodName()", " ");
assertEquals(e.callStack[0].getClassName(),
logger.getClass().getName(),
"e.callStack[0].getClassName() ", " ");
assertEquals(e.callStack[1].getMethodName(), "log",
"e.callStack[1].getMethodName()", " ");
assertEquals(e.callStack[1].getClassName(),
System.Logger.class.getName(),
"e.callStack[1].getClassName() ", " ");
assertEquals(e.callStack[2].getMethodName(), "main",
"e.callStack[2].getMethodName()", " ");
System.out.println(" logger.log(" + l + ", (String)null);");
logger.log(l, (String)null);
e = events.poll();
assertNonNull(e, "e", " ");
assertEquals(e.level, l, "e.level", " ");
assertEquals(e.msg, null, "e.msg", " ");
assertEquals(e.bundle, null, "e.bundle", " ");
assertEquals(e.params, null, "e.params", " ");
assertEquals(e.thrown, null, "e.thrown", " ");
assertEquals(e.bundle, null, "e.bundle", " ");
assertEquals(e.callStack[0].getMethodName(), "log",
"e.callStack[0].getMethodName()", " ");
assertEquals(e.callStack[0].getClassName(),
logger.getClass().getName(),
"e.callStack[0].getClassName() ", " ");
assertEquals(e.callStack[1].getMethodName(), "log",
"e.callStack[1].getMethodName()", " ");
assertEquals(e.callStack[1].getClassName(),
System.Logger.class.getName(),
"e.callStack[1].getClassName() ", " ");
assertEquals(e.callStack[2].getMethodName(), "main",
"e.callStack[2].getMethodName()", " ");
}
System.out.println("\nlogger.log(Level, Supplier<String>)");
for (Level l : Level.values()) {
boolean logged = l.compareTo(Level.WARNING) >= 0;
Object[][] cases = new Object[][] {
{null}, {"baz"}
};
for (Object[] p : cases) {
String msg = (String)p[0];
final Object obj = msg == null ? null : logged ? new NotTrowing(msg) : new Throwing();
final Supplier<String> s = msg == null ? null : () -> obj.toString();
String par1 = msg == null ? "(Supplier<String>)null"
: logged ? "() -> new NotTrowing(\""+ msg+"\").toString()" : "new Throwing()";
System.out.println(" logger.log(" + l + ", " + par1 + ")");
try {
logger.log(l, s);
if (s == null) {
throw new RuntimeException("Expected NullPointerException not thrown for"
+ " logger.log(" + l + ", " + par1 + ")");
}
} catch (NullPointerException x) {
if (s == null) {
System.out.println(" Got expected exception: " + x);
continue;
} else {
throw x;
}
}
LoggerImpl.LogEvent e = events.poll();
if (logged) {
assertNonNull(e, "e", " ");
assertEquals(l, e.level, "e.level", " ");
assertToString(e.msg, msg, 1, "e.msg", " ");
assertEquals(e.bundle, null, "e.bundle", " ");
assertEquals(e.params, null, "e.params", " ");
assertEquals(e.thrown, null, "e.thrown", " ");
assertEquals(e.bundle, null, "e.bundle", " ");
assertEquals(e.callStack[0].getMethodName(), "log",
"e.callStack[0].getMethodName()", " ");
assertEquals(e.callStack[0].getClassName(),
logger.getClass().getName(),
"e.callStack[0].getClassName() ", " ");
assertEquals(e.callStack[1].getMethodName(), "log",
"e.callStack[1].getMethodName()", " ");
assertEquals(e.callStack[1].getClassName(),
System.Logger.class.getName(),
"e.callStack[1].getClassName() ", " ");
assertEquals(e.callStack[2].getMethodName(), "main",
"e.callStack[2].getMethodName()", " ");
} else {
assertEquals(e, null, "e", " ");
}
}
}
System.out.println(" logger.log(" + null + ", " + "() -> \"biz\"" + ")");
try {
logger.log(null, () -> "biz");
throw new RuntimeException("Expected NullPointerException not thrown for"
+ " logger.log(" + null + ", "
+ "() -> \"biz\"" + ")");
} catch (NullPointerException x) {
System.out.println(" Got expected exception: " + x);
}
System.out.println("\nlogger.log(Level, String, Object...)");
for (Level l : Level.values()) {
boolean logged = l.compareTo(Level.WARNING) >= 0;
String par = "bam";
Object[] params = null;
System.out.println(" logger.log(" + l + ", \"" + par +"\", null);");
logger.log(l, par, params);
LoggerImpl.LogEvent e = events.poll();
assertNonNull(e, "e", " ");
assertEquals(l, e.level, "e.level", " ");
assertEquals(e.msg, "bam", "e.msg", " ");
assertEquals(e.bundle, null, "e.bundle", " ");
assertEquals(e.params, null, "e.params", " ");
assertEquals(e.thrown, null, "e.thrown", " ");
assertEquals(e.bundle, null, "e.bundle", " ");
assertEquals(e.callStack[0].getMethodName(), "log",
"e.callStack[0].getMethodName()", " ");
assertEquals(e.callStack[0].getClassName(),
logger.getClass().getName(),
"e.callStack[0].getClassName() ", " ");
assertEquals(e.callStack[1].getMethodName(), "log",
"e.callStack[1].getMethodName()", " ");
assertEquals(e.callStack[1].getClassName(),
System.Logger.class.getName(),
"e.callStack[1].getClassName() ", " ");
assertEquals(e.callStack[2].getMethodName(), "main",
"e.callStack[2].getMethodName()", " ");
params = new Object[] {new NotTrowing("one")};
par = "bam {0}";
System.out.println(" logger.log(" + l + ", \"" + par
+ "\", new NotTrowing(\"one\"));");
logger.log(l, par, params[0]);
e = events.poll();
assertNonNull(e, "e", " ");
assertEquals(l, e.level, "e.level", " ");
assertEquals(e.msg, par, "e.msg", " ");
assertEquals(e.bundle, null, "e.bundle", " ");
assertArrayEquals(e.params, params, "e.params", " ");
assertEquals(e.thrown, null, "e.thrown", " ");
assertEquals(e.bundle, null, "e.bundle", " ");
assertEquals(e.callStack[0].getMethodName(), "log",
"e.callStack[0].getMethodName()", " ");
assertEquals(e.callStack[0].getClassName(),
logger.getClass().getName(),
"e.callStack[0].getClassName() ", " ");
assertEquals(e.callStack[1].getMethodName(), "log",
"e.callStack[1].getMethodName()", " ");
assertEquals(e.callStack[1].getClassName(),
System.Logger.class.getName(),
"e.callStack[1].getClassName() ", " ");
assertEquals(e.callStack[2].getMethodName(), "main",
"e.callStack[2].getMethodName()", " ");
params = new Object[] {new NotTrowing("fisrt"), new NotTrowing("second")};
par = "bam {0} {1}";
System.out.println(" logger.log(" + l + ", \"" + par
+ "\", new NotTrowing(\"fisrt\"),"
+ " new NotTrowing(\"second\"));");
logger.log(l, par, params[0], params[1]);
e = events.poll();
assertNonNull(e, "e", " ");
assertEquals(l, e.level, "e.level", " ");
assertEquals(e.msg, par, "e.msg", " ");
assertEquals(e.bundle, null, "e.bundle", " ");
assertArrayEquals(e.params, params, "e.params", " ");
assertEquals(e.thrown, null, "e.thrown", " ");
assertEquals(e.bundle, null, "e.bundle", " ");
assertEquals(e.callStack[0].getMethodName(), "log",
"e.callStack[0].getMethodName()", " ");
assertEquals(e.callStack[0].getClassName(),
logger.getClass().getName(),
"e.callStack[0].getClassName() ", " ");
assertEquals(e.callStack[1].getMethodName(), "log",
"e.callStack[1].getMethodName()", " ");
assertEquals(e.callStack[1].getClassName(),
System.Logger.class.getName(),
"e.callStack[1].getClassName() ", " ");
assertEquals(e.callStack[2].getMethodName(), "main",
"e.callStack[2].getMethodName()", " ");
params = new Object[] {new NotTrowing("third"), new NotTrowing("fourth")};
par = "bam {2}";
System.out.println(" logger.log(" + l + ", \"" + par
+ "\", new Object[] {new NotTrowing(\"third\"),"
+ " new NotTrowing(\"fourth\")});");
logger.log(l, par, params);
e = events.poll();
assertNonNull(e, "e", " ");
assertEquals(l, e.level, "e.level", " ");
assertEquals(e.msg, par, "e.msg", " ");
assertEquals(e.bundle, null, "e.bundle", " ");
assertArrayEquals(e.params, params, "e.params", " ");
assertEquals(e.thrown, null, "e.thrown", " ");
assertEquals(e.bundle, null, "e.bundle", " ");
assertEquals(e.callStack[0].getMethodName(), "log",
"e.callStack[0].getMethodName()", " ");
assertEquals(e.callStack[0].getClassName(), logger.getClass().getName(),
"e.callStack[0].getClassName() ", " ");
assertEquals(e.callStack[1].getMethodName(), "log",
"e.callStack[1].getMethodName()", " ");
assertEquals(e.callStack[1].getClassName(),
System.Logger.class.getName(),
"e.callStack[1].getClassName() ", " ");
assertEquals(e.callStack[2].getMethodName(), "main",
"e.callStack[2].getMethodName()", " ");
}
System.out.println("\nlogger.log(Level, String, Throwable)");
for (Level l : Level.values()) {
boolean logged = l.compareTo(Level.WARNING) >= 0;
Object[][] cases = new Object[][] {
{null, null}, {null, new Throwable()}, {"biz", null}, {"boz", new Throwable()}
};
for (Object[] p : cases) {
String msg = (String)p[0];
Throwable thrown = (Throwable)p[1];
String par1 = msg == null ? "(String)null" : "\"" + msg + "\"";
String par2 = thrown == null ? "(Throwable)null" : "new Throwable()";
System.out.println(" logger.log(" + l + ", " + par1 +", " + par2 + ")");
logger.log(l, msg, thrown);
LoggerImpl.LogEvent e = events.poll();
assertNonNull(e, "e", " ");
assertEquals(e.level, l, "e.level", " ");
assertEquals(e.msg, msg, "e.msg", " ");
assertEquals(e.bundle, null, "e.bundle", " ");
assertEquals(e.params, null, "e.params", " ");
assertEquals(e.thrown, thrown, "e.thrown", " ");
assertEquals(e.bundle, null, "e.bundle", " ");
assertEquals(e.callStack[0].getMethodName(),
"log", "e.callStack[0].getMethodName()", " ");
assertEquals(e.callStack[0].getClassName(),
logger.getClass().getName(),
"e.callStack[0].getClassName() ", " ");
assertEquals(e.callStack[1].getMethodName(), "log",
"e.callStack[1].getMethodName()", " ");
assertEquals(e.callStack[1].getClassName(),
System.Logger.class.getName(),
"e.callStack[1].getClassName() ", " ");
assertEquals(e.callStack[2].getMethodName(), "main",
"e.callStack[2].getMethodName()", " ");
}
}
System.out.println("\nlogger.log(Level, Supplier<String>, Throwable)");
for (Level l : Level.values()) {
boolean logged = l.compareTo(Level.WARNING) >= 0;
Object[][] cases = new Object[][] {
{null, null}, {null, new Throwable()}, {"biz", null}, {"boz", new Throwable()}
};
for (Object[] p : cases) {
String msg = (String)p[0];
Throwable thrown = (Throwable)p[1];
final Object obj = msg == null ? null : logged ? new NotTrowing(msg) : new Throwing();
final Supplier<String> s = msg == null ? null : () -> obj.toString();
String par1 = msg == null ? "(Supplier<String>)null"
: logged ? "() -> new NotTrowing(\""+ msg+"\").toString()" : "new Throwing()";
String par2 = thrown == null ? "(Throwable)null" : "new Throwable()";
System.out.println(" logger.log(" + l + ", " + par1 +", " + par2 + ")");
try {
logger.log(l, s, thrown);
if (s== null) {
throw new RuntimeException("Expected NullPointerException not thrown for"
+ " logger.log(" + l + ", " + par1 +", " + par2 + ")");
}
} catch (NullPointerException x) {
if (s == null) {
System.out.println(" Got expected exception: " + x);
continue;
} else {
throw x;
}
}
LoggerImpl.LogEvent e = events.poll();
if (logged) {
assertNonNull(e, "e", " ");
assertEquals(l, e.level, "e.level", " ");
assertToString(e.msg, msg, 1, "e.msg", " ");
assertEquals(e.bundle, null, "e.bundle", " ");
assertEquals(e.params, null, "e.params", " ");
assertEquals(e.thrown, thrown, "e.thrown", " ");
assertEquals(e.bundle, null, "e.bundle", " ");
assertEquals(e.callStack[0].getMethodName(), "log",
"e.callStack[0].getMethodName()", " ");
assertEquals(e.callStack[0].getClassName(),
logger.getClass().getName(),
"e.callStack[0].getClassName() ", " ");
assertEquals(e.callStack[1].getMethodName(), "log",
"e.callStack[1].getMethodName()", " ");
assertEquals(e.callStack[1].getClassName(),
System.Logger.class.getName(),
"e.callStack[1].getClassName() ", " ");
assertEquals(e.callStack[2].getMethodName(), "main",
"e.callStack[2].getMethodName()", " ");
} else {
assertEquals(e, null, "e", " ");
}
}
}
System.out.println(" logger.log(" + null + ", " + "() -> \"biz\""
+ ", " + "new Throwable()" + ")");
try {
logger.log(null, () -> "biz", new Throwable());
throw new RuntimeException("Expected NullPointerException not thrown for"
+ " logger.log(" + null + ", "
+ "() -> \"biz\"" + ", "
+ "new Throwable()" + ")");
} catch (NullPointerException x) {
System.out.println(" Got expected exception: " + x);
}
System.out.println("Checking that we have no spurious events in the queue");
assertEquals(events.poll(), null, "events.poll()", " ");
}
static void assertTrue(boolean test, String what, String prefix) {
if (!test) {
throw new RuntimeException("Expected true for " + what);
}
System.out.println(prefix + "Got expected " + what + ": " + test);
}
static void assertFalse(boolean test, String what, String prefix) {
if (test) {
throw new RuntimeException("Expected false for " + what);
}
System.out.println(prefix + "Got expected " + what + ": " + test);
}
static void assertToString(String actual, String expected, int count, String what, String prefix) {
assertEquals(actual, expected + "["+count+"]", what, prefix);
}
static void assertEquals(Object actual, Object expected, String what, String prefix) {
if (!Objects.equals(actual, expected)) {
throw new RuntimeException("Bad " + what + ":"
+ "\n\t expected: " + expected
+ "\n\t actual: " + actual);
}
System.out.println(prefix + "Got expected " + what + ": " + actual);
}
static void assertArrayEquals(Object[] actual, Object[] expected, String what, String prefix) {
if (!Objects.deepEquals(actual, expected)) {
throw new RuntimeException("Bad " + what + ":"
+ "\n\t expected: " + expected == null ? "null" : Arrays.deepToString(expected)
+ "\n\t actual: " + actual == null ? "null" : Arrays.deepToString(actual));
}
System.out.println(prefix + "Got expected " + what + ": " + Arrays.deepToString(actual));
}
static void assertNonNull(Object actual, String what, String prefix) {
if (Objects.equals(actual, null)) {
throw new RuntimeException("Bad " + what + ":"
+ "\n\t expected: non null"
+ "\n\t actual: " + actual);
}
System.out.println(prefix + "Got expected " + what + ": " + "non null");
}
}

View file

@ -0,0 +1,78 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.IOException;
import java.lang.System.Logger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ResourceBundle;
/**
*
* @author danielfuchs
*/
public final class AccessSystemLogger {
public AccessSystemLogger() {
this(check());
}
private AccessSystemLogger(Void unused) {
}
private static Void check() {
if (AccessSystemLogger.class.getClassLoader() != null) {
throw new RuntimeException("AccessSystemLogger should be loaded by the null classloader");
}
return null;
}
public Logger getLogger(String name) {
Logger logger = System.getLogger(name);
System.out.println("System.getLogger(\"" + name + "\"): " + logger);
return logger;
}
public Logger getLogger(String name, ResourceBundle bundle) {
Logger logger = System.getLogger(name, bundle);
System.out.println("System.getLogger(\"" + name + "\", bundle): " + logger);
return logger;
}
// copy AccessSystemLogger.class to ./boot
public static void main(String[] args) throws IOException {
Path testDir = Paths.get(System.getProperty("user.dir", "."));
Path bootDir = Paths.get(testDir.toString(), "boot");
Path classes = Paths.get(System.getProperty("test.classes", "build/classes"));
Path thisClass = Paths.get(classes.toString(),
AccessSystemLogger.class.getSimpleName()+".class");
if (Files.notExists(bootDir)) {
Files.createDirectory(bootDir);
}
Path dest = Paths.get(bootDir.toString(),
AccessSystemLogger.class.getSimpleName()+".class");
Files.copy(thisClass, dest, StandardCopyOption.REPLACE_EXISTING);
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.lang.System.LoggerFinder;
import java.lang.System.Logger;
public class BaseLoggerFinder extends LoggerFinder implements TestLoggerFinder {
static final RuntimePermission LOGGERFINDER_PERMISSION =
new RuntimePermission("loggerFinder");
@Override
public Logger getLogger(String name, Module caller) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(LOGGERFINDER_PERMISSION);
}
PrivilegedAction<ClassLoader> pa = () -> caller.getClassLoader();
ClassLoader callerLoader = AccessController.doPrivileged(pa);
if (callerLoader == null) {
return system.computeIfAbsent(name, (n) -> new LoggerImpl(n));
} else {
return user.computeIfAbsent(name, (n) -> new LoggerImpl(n));
}
}
}

View file

@ -0,0 +1,458 @@
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
import java.lang.System.LoggerFinder;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
/**
* @test
* @bug 8140364
* @summary Tests a naive implementation of LoggerFinder, and in particular
* the default body of System.Logger methods.
* @build AccessSystemLogger BaseLoggerFinderTest CustomSystemClassLoader BaseLoggerFinder TestLoggerFinder
* @run driver AccessSystemLogger
* @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerFinderTest
*/
public class BaseLoggerFinderTest {
final static boolean VERBOSE = false;
final static AccessSystemLogger accessSystemLogger = new AccessSystemLogger();
static final Class<?> providerClass;
static {
try {
providerClass = ClassLoader.getSystemClassLoader().loadClass("BaseLoggerFinder");
} catch (ClassNotFoundException ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static class MyBundle extends ResourceBundle {
final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
@Override
protected Object handleGetObject(String key) {
if (key.contains(" (translated)")) {
throw new RuntimeException("Unexpected key: " + key);
}
return map.computeIfAbsent(key, k -> k + " (translated)");
}
@Override
public Enumeration<String> getKeys() {
return Collections.enumeration(map.keySet());
}
}
public static class MyLoggerBundle extends MyBundle {
}
public static void main(String[] args) {
System.out.println("Using provider class: " + providerClass + "[" + providerClass.getClassLoader() + "]");
TestLoggerFinder provider;
System.out.println("\n*** Without Security Manager\n");
provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
test(provider);
System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get());
System.out.println("\nPASSED: Tested " + TestLoggerFinder.sequencer.get() + " cases.");
}
public static void test(TestLoggerFinder provider) {
ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
final Map<Logger, String> loggerDescMap = new HashMap<>();
// 1. Test loggers returned by LoggerFinder, both for system callers
// and not system callers.
TestLoggerFinder.LoggerImpl appLogger1 = null;
appLogger1 =
TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", BaseLoggerFinderTest.class.getModule()));
loggerDescMap.put(appLogger1, "provider.getLogger(\"foo\", BaseLoggerFinderTest.class.getModule())");
TestLoggerFinder.LoggerImpl sysLogger1 =
TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", Thread.class.getModule()));
loggerDescMap.put(sysLogger1, "provider.getLogger(\"foo\", Thread.class.getModule())");
if (appLogger1 == sysLogger1) {
throw new RuntimeException("identical loggers");
}
if (provider.system.contains(appLogger1)) {
throw new RuntimeException("app logger in system map");
}
if (!provider.user.contains(appLogger1)) {
throw new RuntimeException("app logger not in appplication map");
}
if (provider.user.contains(sysLogger1)) {
throw new RuntimeException("sys logger in appplication map");
}
if (!provider.system.contains(sysLogger1)) {
throw new RuntimeException("sys logger not in system map");
}
testLogger(provider, loggerDescMap, "foo", null, appLogger1, appLogger1);
testLogger(provider, loggerDescMap, "foo", null, sysLogger1, sysLogger1);
// 2. Test localized loggers returned LoggerFinder, both for system
// callers and non system callers
Logger appLogger2 =
provider.getLocalizedLogger("foo", loggerBundle, BaseLoggerFinderTest.class.getModule());
loggerDescMap.put(appLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, BaseLoggerFinderTest.class.getModule())");
Logger sysLogger2 =
provider.getLocalizedLogger("foo", loggerBundle, Thread.class.getModule());
loggerDescMap.put(sysLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, Thread.class.getModule())");
if (appLogger2 == sysLogger2) {
throw new RuntimeException("identical loggers");
}
if (appLogger2 == appLogger1) {
throw new RuntimeException("identical loggers");
}
if (sysLogger2 == sysLogger1) {
throw new RuntimeException("identical loggers");
}
if (provider.system.contains(appLogger2)) {
throw new RuntimeException("localized app logger in system map");
}
if (provider.user.contains(appLogger2)) {
throw new RuntimeException("localized app logger in appplication map");
}
if (provider.user.contains(sysLogger2)) {
throw new RuntimeException("localized sys logger in appplication map");
}
if (provider.system.contains(sysLogger2)) {
throw new RuntimeException("localized sys logger not in system map");
}
testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger2, appLogger1);
testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger2, sysLogger1);
// 3 Test loggers returned by:
// 3.1: System.getLogger("foo")
Logger appLogger3 = System.getLogger("foo");
loggerDescMap.put(appLogger3, "System.getLogger(\"foo\")");
testLogger(provider, loggerDescMap, "foo", null, appLogger3, appLogger1);
// 3.2: System.getLogger("foo")
// Emulate what System.getLogger() does when the caller is a
// platform classes
Logger sysLogger3 = accessSystemLogger.getLogger("foo");
loggerDescMap.put(sysLogger3, "AccessSystemLogger.getLogger(\"foo\")");
if (appLogger3 == sysLogger3) {
throw new RuntimeException("identical loggers");
}
testLogger(provider, loggerDescMap, "foo", null, sysLogger3, sysLogger1);
// 4. Test loggers returned by:
// 4.1 System.getLogger("foo", loggerBundle)
Logger appLogger4 =
System.getLogger("foo", loggerBundle);
loggerDescMap.put(appLogger4, "System.getLogger(\"foo\", loggerBundle)");
if (appLogger4 == appLogger1) {
throw new RuntimeException("identical loggers");
}
testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger4, appLogger1);
// 4.2: System.getLogger("foo", loggerBundle)
// Emulate what System.getLogger() does when the caller is a
// platform classes
Logger sysLogger4 = accessSystemLogger.getLogger("foo", loggerBundle);
loggerDescMap.put(sysLogger4, "AccessSystemLogger.getLogger(\"foo\", loggerBundle)");
if (appLogger4 == sysLogger4) {
throw new RuntimeException("identical loggers");
}
testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger4, sysLogger1);
}
public static class Foo {
}
static void verbose(String msg) {
if (VERBOSE) {
System.out.println(msg);
}
}
// Calls the 8 methods defined on Logger and verify the
// parameters received by the underlying TestProvider.LoggerImpl
// logger.
private static void testLogger(TestLoggerFinder provider,
Map<Logger, String> loggerDescMap,
String name,
ResourceBundle loggerBundle,
Logger logger,
TestLoggerFinder.LoggerImpl sink) {
System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger +"]");
AtomicLong sequencer = TestLoggerFinder.sequencer;
Foo foo = new Foo();
String fooMsg = foo.toString();
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, foo): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
TestLoggerFinder.LogEvent expected =
TestLoggerFinder.LogEvent.of(
sequencer.get(),
messageLevel.compareTo(loggerLevel) >= 0,
name, messageLevel, (ResourceBundle)null,
fooMsg, null, (Throwable)null, (Object[])null);
logger.log(messageLevel, foo);
if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (provider.eventQueue.poll() != null) {
throw new RuntimeException("unexpected event in queue for " + desc);
}
} else {
TestLoggerFinder.LogEvent actual = provider.eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
}
String msg = "blah";
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
TestLoggerFinder.LogEvent expected =
TestLoggerFinder.LogEvent.of(
sequencer.get(),
messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
name, messageLevel, loggerBundle,
msg, null, (Throwable)null, (Object[])null);
logger.log(messageLevel, msg);
TestLoggerFinder.LogEvent actual = provider.eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
Supplier<String> fooSupplier = new Supplier<String>() {
@Override
public String get() {
return this.toString();
}
};
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
TestLoggerFinder.LogEvent expected =
TestLoggerFinder.LogEvent.of(
sequencer.get(),
messageLevel.compareTo(loggerLevel) >= 0,
name, messageLevel, (ResourceBundle)null,
fooSupplier.get(), null,
(Throwable)null, (Object[])null);
logger.log(messageLevel, fooSupplier);
if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (provider.eventQueue.poll() != null) {
throw new RuntimeException("unexpected event in queue for " + desc);
}
} else {
TestLoggerFinder.LogEvent actual = provider.eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
}
String format = "two params [{1} {2}]";
Object arg1 = foo;
Object arg2 = msg;
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, format, params...): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
TestLoggerFinder.LogEvent expected =
TestLoggerFinder.LogEvent.of(
sequencer.get(),
messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
name, messageLevel, loggerBundle,
format, null, (Throwable)null, new Object[] {foo, msg});
logger.log(messageLevel, format, foo, msg);
TestLoggerFinder.LogEvent actual = provider.eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
Throwable thrown = new Exception("OK: log me!");
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
TestLoggerFinder.LogEvent expected =
TestLoggerFinder.LogEvent.of(
sequencer.get(),
messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
name, messageLevel, loggerBundle,
msg, null, thrown, (Object[]) null);
logger.log(messageLevel, msg, thrown);
TestLoggerFinder.LogEvent actual = provider.eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
TestLoggerFinder.LogEvent expected =
TestLoggerFinder.LogEvent.of(
sequencer.get(),
messageLevel.compareTo(loggerLevel) >= 0,
name, messageLevel, (ResourceBundle)null,
fooSupplier.get(), null,
(Throwable)thrown, (Object[])null);
logger.log(messageLevel, fooSupplier, thrown);
if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (provider.eventQueue.poll() != null) {
throw new RuntimeException("unexpected event in queue for " + desc);
}
} else {
TestLoggerFinder.LogEvent actual = provider.eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
}
ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
TestLoggerFinder.LogEvent expected =
TestLoggerFinder.LogEvent.of(
sequencer.get(),
messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
name, messageLevel, bundle,
format, null, (Throwable)null, new Object[] {foo, msg});
logger.log(messageLevel, bundle, format, foo, msg);
TestLoggerFinder.LogEvent actual = provider.eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
TestLoggerFinder.LogEvent expected =
TestLoggerFinder.LogEvent.of(
sequencer.get(),
messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
name, messageLevel, bundle,
msg, null, thrown, (Object[]) null);
logger.log(messageLevel, bundle, msg, thrown);
TestLoggerFinder.LogEvent actual = provider.eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
}
}

View file

@ -0,0 +1,101 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.security.AllPermission;
import java.security.Permissions;
import java.security.ProtectionDomain;
/**
* A custom ClassLoader to load the concrete LoggerFinder class
* with all permissions.
*
* @author danielfuchs
*/
public class CustomSystemClassLoader extends ClassLoader {
Class<?> finderClass = null;
public CustomSystemClassLoader() {
super();
}
public CustomSystemClassLoader(ClassLoader parent) {
super(parent);
}
private Class<?> defineFinderClass(String name)
throws ClassNotFoundException {
final Object obj = getClassLoadingLock(name);
synchronized(obj) {
if (finderClass != null) return finderClass;
URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation();
File file = new File(url.getPath(), name+".class");
if (file.canRead()) {
try {
byte[] b = Files.readAllBytes(file.toPath());
Permissions perms = new Permissions();
perms.add(new AllPermission());
finderClass = defineClass(
name, b, 0, b.length, new ProtectionDomain(
this.getClass().getProtectionDomain().getCodeSource(),
perms));
System.out.println("Loaded " + name);
return finderClass;
} catch (Throwable ex) {
ex.printStackTrace();
throw new ClassNotFoundException(name, ex);
}
} else {
throw new ClassNotFoundException(name,
new IOException(file.toPath() + ": can't read"));
}
}
}
@Override
public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (name.equals("BaseLoggerFinder")) {
Class<?> c = defineFinderClass(name);
if (resolve) {
resolveClass(c);
}
return c;
}
return super.loadClass(name, resolve);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (name.equals("BaseLoggerFinder")) {
return defineFinderClass(name);
}
return super.findClass(name);
}
}

View file

@ -0,0 +1,181 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.Arrays;
import java.util.Objects;
import java.util.Queue;
import java.util.ResourceBundle;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
import java.lang.System.Logger;
/**
* What our test provider needs to implement.
* @author danielfuchs
*/
public interface TestLoggerFinder {
public final static AtomicLong sequencer = new AtomicLong();
public final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>();
public final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>();
public final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
public static final class LogEvent {
public LogEvent() {
this(sequencer.getAndIncrement());
}
LogEvent(long sequenceNumber) {
this.sequenceNumber = sequenceNumber;
}
long sequenceNumber;
boolean isLoggable;
String loggerName;
Logger.Level level;
ResourceBundle bundle;
Throwable thrown;
Object[] args;
Supplier<String> supplier;
String msg;
Object[] toArray() {
return new Object[] {
sequenceNumber,
isLoggable,
loggerName,
level,
bundle,
thrown,
args,
supplier,
msg,
};
}
@Override
public String toString() {
return Arrays.deepToString(toArray());
}
@Override
public boolean equals(Object obj) {
return obj instanceof LogEvent
&& Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
}
@Override
public int hashCode() {
return Objects.hash(toArray());
}
public static LogEvent of(boolean isLoggable, String name,
Logger.Level level, ResourceBundle bundle,
String key, Throwable thrown) {
LogEvent evt = new LogEvent();
evt.isLoggable = isLoggable;
evt.loggerName = name;
evt.level = level;
evt.args = null;
evt.bundle = bundle;
evt.thrown = thrown;
evt.supplier = null;
evt.msg = key;
return evt;
}
public static LogEvent of(boolean isLoggable, String name,
Logger.Level level, ResourceBundle bundle,
String key, Object... params) {
LogEvent evt = new LogEvent();
evt.isLoggable = isLoggable;
evt.loggerName = name;
evt.level = level;
evt.args = params;
evt.bundle = bundle;
evt.thrown = null;
evt.supplier = null;
evt.msg = key;
return evt;
}
public static LogEvent of(long sequenceNumber,
boolean isLoggable, String name,
Logger.Level level, ResourceBundle bundle,
String key, Supplier<String> supplier,
Throwable thrown, Object... params) {
LogEvent evt = new LogEvent(sequenceNumber);
evt.loggerName = name;
evt.level = level;
evt.args = params;
evt.bundle = bundle;
evt.thrown = thrown;
evt.supplier = supplier;
evt.msg = key;
evt.isLoggable = isLoggable;
return evt;
}
}
public class LoggerImpl implements Logger {
final String name;
Logger.Level level = Logger.Level.INFO;
public LoggerImpl(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public boolean isLoggable(Logger.Level level) {
return this.level != Logger.Level.OFF && this.level.getSeverity() <= level.getSeverity();
}
@Override
public void log(Logger.Level level, ResourceBundle bundle, String key, Throwable thrown) {
log(LogEvent.of(isLoggable(level), this.name, level, bundle, key, thrown));
}
@Override
public void log(Logger.Level level, ResourceBundle bundle, String format, Object... params) {
log(LogEvent.of(isLoggable(level), name, level, bundle, format, params));
}
void log(LogEvent event) {
eventQueue.add(event);
}
}
public Logger getLogger(String name, Module caller);
public Logger getLocalizedLogger(String name, ResourceBundle bundle, Module caller);
}

View file

@ -0,0 +1,82 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.IOException;
import java.lang.System.Logger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ResourceBundle;
/**
*
* @author danielfuchs
*/
public final class AccessSystemLogger {
public AccessSystemLogger() {
this(check());
}
private AccessSystemLogger(Void unused) {
}
private static Void check() {
if (AccessSystemLogger.class.getClassLoader() != null) {
throw new RuntimeException("AccessSystemLogger should be loaded by the null classloader");
}
return null;
}
public Logger getLogger(String name) {
Logger logger = System.getLogger(name);
System.out.println("System.getLogger(\"" + name + "\"): " + logger);
return logger;
}
public Logger getLogger(String name, ResourceBundle bundle) {
Logger logger = System.getLogger(name, bundle);
System.out.println("System.getLogger(\"" + name + "\", bundle): " + logger);
return logger;
}
public java.util.logging.Logger demandSystemLogger(String name) {
return java.util.logging.Logger.getLogger(name);
}
// copy AccessSystemLogger.class to ./boot
public static void main(String[] args) throws IOException {
Path testDir = Paths.get(System.getProperty("user.dir", "."));
Path bootDir = Paths.get(testDir.toString(), "boot");
Path classes = Paths.get(System.getProperty("test.classes", "build/classes"));
Path thisClass = Paths.get(classes.toString(),
AccessSystemLogger.class.getSimpleName()+".class");
if (Files.notExists(bootDir)) {
Files.createDirectory(bootDir);
}
Path dest = Paths.get(bootDir.toString(),
AccessSystemLogger.class.getSimpleName()+".class");
Files.copy(thisClass, dest, StandardCopyOption.REPLACE_EXISTING);
}
}

View file

@ -0,0 +1,637 @@
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.ResourceBundle;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.lang.System.LoggerFinder;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
/**
* @test
* @bug 8140364
* @summary Tests the default implementation of System.Logger, when
* JUL is the default backend.
* @modules java.logging
* @build AccessSystemLogger DefaultLoggerFinderTest
* @run driver AccessSystemLogger
* @run main/othervm -Xbootclasspath/a:boot DefaultLoggerFinderTest
*/
public class DefaultLoggerFinderTest {
final static AtomicLong sequencer = new AtomicLong();
final static boolean VERBOSE = false;
static final AccessSystemLogger accessSystemLogger = new AccessSystemLogger();
public static final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
public static final class LogEvent {
public LogEvent() {
this(sequencer.getAndIncrement());
}
LogEvent(long sequenceNumber) {
this.sequenceNumber = sequenceNumber;
}
long sequenceNumber;
boolean isLoggable;
String loggerName;
java.util.logging.Level level;
ResourceBundle bundle;
Throwable thrown;
Object[] args;
String msg;
String className;
String methodName;
Object[] toArray() {
return new Object[] {
sequenceNumber,
isLoggable,
loggerName,
level,
bundle,
thrown,
args,
msg,
className,
methodName,
};
}
@Override
public String toString() {
return Arrays.deepToString(toArray());
}
@Override
public boolean equals(Object obj) {
return obj instanceof LogEvent
&& Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
}
@Override
public int hashCode() {
return Objects.hash(toArray());
}
public static LogEvent of(long sequenceNumber,
boolean isLoggable, String name,
java.util.logging.Level level, ResourceBundle bundle,
String key, Throwable thrown, Object... params) {
return LogEvent.of(sequenceNumber, isLoggable, name,
DefaultLoggerFinderTest.class.getName(),
"testLogger", level, bundle, key,
thrown, params);
}
public static LogEvent of(long sequenceNumber,
boolean isLoggable, String name,
String className, String methodName,
java.util.logging.Level level, ResourceBundle bundle,
String key, Throwable thrown, Object... params) {
LogEvent evt = new LogEvent(sequenceNumber);
evt.loggerName = name;
evt.level = level;
evt.args = params;
evt.bundle = bundle;
evt.thrown = thrown;
evt.msg = key;
evt.isLoggable = isLoggable;
evt.className = className;
evt.methodName = methodName;
return evt;
}
}
static java.util.logging.Level mapToJul(Level level) {
switch (level) {
case ALL: return java.util.logging.Level.ALL;
case TRACE: return java.util.logging.Level.FINER;
case DEBUG: return java.util.logging.Level.FINE;
case INFO: return java.util.logging.Level.INFO;
case WARNING: return java.util.logging.Level.WARNING;
case ERROR: return java.util.logging.Level.SEVERE;
case OFF: return java.util.logging.Level.OFF;
}
throw new InternalError("No such level: " + level);
}
static final java.util.logging.Level[] julLevels = {
java.util.logging.Level.ALL,
new java.util.logging.Level("FINER_THAN_FINEST", java.util.logging.Level.FINEST.intValue() - 10) {},
java.util.logging.Level.FINEST,
new java.util.logging.Level("FINER_THAN_FINER", java.util.logging.Level.FINER.intValue() - 10) {},
java.util.logging.Level.FINER,
new java.util.logging.Level("FINER_THAN_FINE", java.util.logging.Level.FINE.intValue() - 10) {},
java.util.logging.Level.FINE,
new java.util.logging.Level("FINER_THAN_CONFIG", java.util.logging.Level.FINE.intValue() + 10) {},
java.util.logging.Level.CONFIG,
new java.util.logging.Level("FINER_THAN_INFO", java.util.logging.Level.INFO.intValue() - 10) {},
java.util.logging.Level.INFO,
new java.util.logging.Level("FINER_THAN_WARNING", java.util.logging.Level.INFO.intValue() + 10) {},
java.util.logging.Level.WARNING,
new java.util.logging.Level("FINER_THAN_SEVERE", java.util.logging.Level.SEVERE.intValue() - 10) {},
java.util.logging.Level.SEVERE,
new java.util.logging.Level("FATAL", java.util.logging.Level.SEVERE.intValue() + 10) {},
java.util.logging.Level.OFF,
};
static final Level[] mappedLevels = {
Level.ALL, // ALL
Level.DEBUG, // FINER_THAN_FINEST
Level.DEBUG, // FINEST
Level.DEBUG, // FINER_THAN_FINER
Level.TRACE, // FINER
Level.TRACE, // FINER_THAN_FINE
Level.DEBUG, // FINE
Level.DEBUG, // FINER_THAN_CONFIG
Level.DEBUG, // CONFIG
Level.DEBUG, // FINER_THAN_INFO
Level.INFO, // INFO
Level.INFO, // FINER_THAN_WARNING
Level.WARNING, // WARNING
Level.WARNING, // FINER_THAN_SEVERE
Level.ERROR, // SEVERE
Level.ERROR, // FATAL
Level.OFF, // OFF
};
final static Map<java.util.logging.Level, Level> julToSpiMap;
static {
Map<java.util.logging.Level, Level> map = new HashMap<>();
if (mappedLevels.length != julLevels.length) {
throw new ExceptionInInitializerError("Array lengths differ"
+ "\n\tjulLevels=" + Arrays.deepToString(julLevels)
+ "\n\tmappedLevels=" + Arrays.deepToString(mappedLevels));
}
for (int i=0; i<julLevels.length; i++) {
map.put(julLevels[i], mappedLevels[i]);
}
julToSpiMap = Collections.unmodifiableMap(map);
}
public static class MyBundle extends ResourceBundle {
final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
@Override
protected Object handleGetObject(String key) {
if (key.contains(" (translated)")) {
throw new RuntimeException("Unexpected key: " + key);
}
return map.computeIfAbsent(key, k -> k + " (translated)");
}
@Override
public Enumeration<String> getKeys() {
return Collections.enumeration(map.keySet());
}
}
public static class MyHandler extends Handler {
@Override
public java.util.logging.Level getLevel() {
return java.util.logging.Level.ALL;
}
@Override
public void publish(LogRecord record) {
eventQueue.add(LogEvent.of(sequencer.getAndIncrement(),
true, record.getLoggerName(),
record.getSourceClassName(),
record.getSourceMethodName(),
record.getLevel(),
record.getResourceBundle(), record.getMessage(),
record.getThrown(), record.getParameters()));
}
@Override
public void flush() {
}
@Override
public void close() throws SecurityException {
}
}
public static class MyLoggerBundle extends MyBundle {
}
public static void main(String[] args) {
final java.util.logging.Logger appSink = java.util.logging.Logger.getLogger("foo");
final java.util.logging.Logger sysSink = accessSystemLogger.demandSystemLogger("foo");
final java.util.logging.Logger sink = java.util.logging.Logger.getLogger("foo");
sink.addHandler(new MyHandler());
sink.setUseParentHandlers(VERBOSE);
System.out.println("\n*** Running test\n");
LoggerFinder provider = LoggerFinder.getLoggerFinder();
test(provider, appSink, sysSink);
System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
}
public static void test(LoggerFinder provider,
java.util.logging.Logger appSink,
java.util.logging.Logger sysSink) {
ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
final Map<Logger, String> loggerDescMap = new HashMap<>();
Logger appLogger1 = provider.getLogger("foo", DefaultLoggerFinderTest.class.getModule());
loggerDescMap.put(appLogger1, "provider.getLogger(\"foo\", DefaultLoggerFinderTest.class.getModule())");
Logger sysLogger1 = provider.getLogger("foo", Thread.class.getModule());
loggerDescMap.put(sysLogger1, "provider.getLogger(\"foo\", Thread.class.getModule())");
if (appLogger1 == sysLogger1) {
throw new RuntimeException("identical loggers");
}
Logger appLogger2 = provider.getLocalizedLogger("foo", loggerBundle, DefaultLoggerFinderTest.class.getModule());
loggerDescMap.put(appLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, DefaultLoggerFinderTest.class.getModule())");
Logger sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class.getModule());
loggerDescMap.put(sysLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, Thread.class.getModule())");
if (appLogger2 == sysLogger2) {
throw new RuntimeException("identical loggers");
}
if (appLogger2 == appLogger1) {
throw new RuntimeException("identical loggers");
}
if (sysLogger2 == sysLogger1) {
throw new RuntimeException("identical loggers");
}
testLogger(provider, loggerDescMap, "foo", null, appLogger1, appSink);
testLogger(provider, loggerDescMap, "foo", null, sysLogger1, sysSink);
testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger2, appSink);
testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger2, sysSink);
Logger appLogger3 = System.getLogger("foo");
loggerDescMap.put(appLogger3, "System.getLogger(\"foo\")");
testLogger(provider, loggerDescMap, "foo", null, appLogger3, appSink);
Logger appLogger4 =
System.getLogger("foo", loggerBundle);
loggerDescMap.put(appLogger4, "System.getLogger(\"foo\", loggerBundle)");
if (appLogger4 == appLogger1) {
throw new RuntimeException("identical loggers");
}
testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger4, appSink);
Logger sysLogger3 = accessSystemLogger.getLogger("foo");
loggerDescMap.put(sysLogger3, "AccessSystemLogger.getLogger(\"foo\")");
testLogger(provider, loggerDescMap, "foo", null, sysLogger3, sysSink);
Logger sysLogger4 =
accessSystemLogger.getLogger("foo", loggerBundle);
loggerDescMap.put(appLogger4, "AccessSystemLogger.getLogger(\"foo\", loggerBundle)");
if (sysLogger4 == sysLogger1) {
throw new RuntimeException("identical loggers");
}
testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger4, sysSink);
}
public static class Foo {
}
static void verbose(String msg) {
if (VERBOSE) {
System.out.println(msg);
}
}
static void setLevel(java.util.logging.Logger sink, java.util.logging.Level loggerLevel) {
sink.setLevel(loggerLevel);
}
// Calls the 8 methods defined on Logger and verify the
// parameters received by the underlying Logger Impl
// logger.
private static void testLogger(LoggerFinder provider,
Map<Logger, String> loggerDescMap,
String name,
ResourceBundle loggerBundle,
Logger logger,
java.util.logging.Logger sink) {
System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger + "]");
final java.util.logging.Level OFF = java.util.logging.Level.OFF;
Foo foo = new Foo();
String fooMsg = foo.toString();
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (Level messageLevel : Level.values()) {
java.util.logging.Level julLevel = mapToJul(messageLevel);
String desc = "logger.log(messageLevel, foo): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
julLevel.intValue() >= loggerLevel.intValue(),
name, julLevel, (ResourceBundle)null,
fooMsg, (Throwable)null, (Object[])null);
logger.log(messageLevel, foo);
if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
if (eventQueue.poll() != null) {
throw new RuntimeException("unexpected event in queue for " + desc);
}
} else {
LogEvent actual = eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
}
String msg = "blah";
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (Level messageLevel : Level.values()) {
java.util.logging.Level julLevel = mapToJul(messageLevel);
String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
julLevel.intValue() >= loggerLevel.intValue(),
name, julLevel, loggerBundle,
msg, (Throwable)null, (Object[])null);
logger.log(messageLevel, msg);
if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
if (eventQueue.poll() != null) {
throw new RuntimeException("unexpected event in queue for " + desc);
}
} else {
LogEvent actual = eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
}
Supplier<String> fooSupplier = new Supplier<String>() {
@Override
public String get() {
return this.toString();
}
};
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (Level messageLevel : Level.values()) {
java.util.logging.Level julLevel = mapToJul(messageLevel);
String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
julLevel.intValue() >= loggerLevel.intValue(),
name, julLevel, (ResourceBundle)null,
fooSupplier.get(),
(Throwable)null, (Object[])null);
logger.log(messageLevel, fooSupplier);
if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
if (eventQueue.poll() != null) {
throw new RuntimeException("unexpected event in queue for " + desc);
}
} else {
LogEvent actual = eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
}
String format = "two params [{1} {2}]";
Object arg1 = foo;
Object arg2 = msg;
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (Level messageLevel : Level.values()) {
java.util.logging.Level julLevel = mapToJul(messageLevel);
String desc = "logger.log(messageLevel, format, params...): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
julLevel.intValue() >= loggerLevel.intValue(),
name, julLevel, loggerBundle,
format, (Throwable)null, new Object[] {arg1, arg2});
logger.log(messageLevel, format, arg1, arg2);
if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
if (eventQueue.poll() != null) {
throw new RuntimeException("unexpected event in queue for " + desc);
}
} else {
LogEvent actual = eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
}
Throwable thrown = new Exception("OK: log me!");
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (Level messageLevel : Level.values()) {
java.util.logging.Level julLevel = mapToJul(messageLevel);
String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
julLevel.intValue() >= loggerLevel.intValue(),
name, julLevel, loggerBundle,
msg, thrown, (Object[]) null);
logger.log(messageLevel, msg, thrown);
if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
if (eventQueue.poll() != null) {
throw new RuntimeException("unexpected event in queue for " + desc);
}
} else {
LogEvent actual = eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
}
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (Level messageLevel : Level.values()) {
java.util.logging.Level julLevel = mapToJul(messageLevel);
String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
julLevel.intValue() >= loggerLevel.intValue(),
name, julLevel, (ResourceBundle)null,
fooSupplier.get(),
(Throwable)thrown, (Object[])null);
logger.log(messageLevel, fooSupplier, thrown);
if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
if (eventQueue.poll() != null) {
throw new RuntimeException("unexpected event in queue for " + desc);
}
} else {
LogEvent actual = eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
}
ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (Level messageLevel : Level.values()) {
java.util.logging.Level julLevel = mapToJul(messageLevel);
String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
julLevel.intValue() >= loggerLevel.intValue(),
name, julLevel, bundle,
format, (Throwable)null, new Object[] {foo, msg});
logger.log(messageLevel, bundle, format, foo, msg);
if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
if (eventQueue.poll() != null) {
throw new RuntimeException("unexpected event in queue for " + desc);
}
} else {
LogEvent actual = eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
}
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (Level messageLevel : Level.values()) {
java.util.logging.Level julLevel = mapToJul(messageLevel);
String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
julLevel.intValue() >= loggerLevel.intValue(),
name, julLevel, bundle,
msg, thrown, (Object[]) null);
logger.log(messageLevel, bundle, msg, thrown);
if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
if (eventQueue.poll() != null) {
throw new RuntimeException("unexpected event in queue for " + desc);
}
} else {
LogEvent actual = eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
}
}
}
}

View file

@ -0,0 +1,283 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.lang.System.LoggerFinder;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.ResourceBundle;
// Can't use testng because testng requires java.logging
//import org.testng.annotations.Test;
/**
* @test
* @bug 8177835 8179222
* @summary Checks that the DefaultLoggerFinder and LoggingProviderImpl
* implementations of the System.LoggerFinder conform to the
* LoggerFinder specification, in particular with respect to
* throwing NullPointerException. The test uses --limit-module
* to force the selection of one or the other.
* @author danielfuchs
* @requires !vm.graal.enabled
* @build LoggerFinderAPI
* @run main/othervm --limit-modules java.base,java.logging
* -Djava.util.logging.SimpleFormatter.format=LOG-%4$s:-[%2$s]-%5$s%6$s%n
* LoggerFinderAPI
* @run main/othervm -Djdk.system.logger.format=LOG-%4$s:-[%2$s]-%5$s%6$s%n
* --limit-modules java.base
* LoggerFinderAPI
*/
public class LoggerFinderAPI {
// Simplified log format string. No white space for main/othervm line
static final String TEST_FORMAT = "LOG-%4$s:-[%2$s]-%5$s%6$s%n";
static final String JDK_FORMAT_PROP_KEY = "jdk.system.logger.format";
static final String JUL_FORMAT_PROP_KEY =
"java.util.logging.SimpleFormatter.format";
static final String MESSAGE = "{0} with {1}: PASSED";
static final String LOCALIZED = "[localized] ";
static class RecordStream extends OutputStream {
static final Object LOCK = new Object[0];
final PrintStream out;
final PrintStream err;
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
boolean record;
RecordStream(PrintStream out, PrintStream err) {
this.out = out;
this.err = err;
}
@Override
public void write(int i) throws IOException {
if (record) {
bos.write(i);
out.write(i);
} else {
err.write(i);
}
}
void startRecording() {
out.flush();
err.flush();
bos.reset();
record = true;
}
byte[] stopRecording() {
out.flush();
err.flush();
record = false;
return bos.toByteArray();
}
}
static final PrintStream ERR = System.err;
static final PrintStream OUT = System.out;
static final RecordStream LOG_STREAM = new RecordStream(OUT, ERR);
static {
Locale.setDefault(Locale.US);
PrintStream perr = new PrintStream(LOG_STREAM);
System.setErr(perr);
}
public static class MyResourceBundle extends ResourceBundle {
final Map<String, String> map = Map.of(MESSAGE, LOCALIZED + MESSAGE);
@Override
protected Object handleGetObject(String string) {
return map.get(string);
}
@Override
public Enumeration<String> getKeys() {
return Collections.enumeration(map.keySet());
}
}
public static class EmptyResourceBundle extends ResourceBundle {
@Override
protected Object handleGetObject(String string) {
return null;
}
@Override
public Enumeration<String> getKeys() {
return Collections.emptyEnumeration();
}
}
public static void main(String[] args) {
// Set on the command line, to ensure that the test will fail if
// the 'wrong' provider gets selected.
// System.setProperty(JDK_FORMAT_PROP_KEY, TEST_FORMAT);
// System.setProperty(JUL_FORMAT_PROP_KEY, TEST_FORMAT);
LoggerFinder finder = System.LoggerFinder.getLoggerFinder();
System.out.println("LoggerFinder is " + finder.getClass().getName());
LoggerFinderAPI apiTest = new LoggerFinderAPI();
for (Object[] params : getLoggerDataProvider()) {
@SuppressWarnings("unchecked")
Class<? extends Throwable> throwableClass =
Throwable.class.getClass().cast(params[3]);
apiTest.testGetLogger((String)params[0],
(String)params[1],
(Module)params[2],
throwableClass);
}
for (Object[] params : getLocalizedLoggerDataProvider()) {
@SuppressWarnings("unchecked")
Class<? extends Throwable> throwableClass =
Throwable.class.getClass().cast(params[4]);
apiTest.testGetLocalizedLogger((String)params[0],
(String)params[1],
(ResourceBundle)params[2],
(Module)params[3],
throwableClass);
}
}
//Can't use testng because testng requires java.logging
//@Test(dataProvider = "testGetLoggerDataProvider")
void testGetLogger(String desc, String name, Module mod, Class<? extends Throwable> thrown) {
try {
LoggerFinder finder = System.LoggerFinder.getLoggerFinder();
Logger logger = finder.getLogger(name, mod);
if (thrown != null) {
throw new AssertionError("Exception " + thrown.getName()
+ " not thrown for "
+ "LoggerFinder.getLogger"
+ " with " + desc);
}
// Make sure we don't fail if tests are run in parallel
synchronized(RecordStream.LOCK) {
LOG_STREAM.startRecording();
byte[] logged = null;
try {
logger.log(Level.INFO, "{0} with {1}: PASSED",
"LoggerFinder.getLogger",
desc);
} finally {
logged = LOG_STREAM.stopRecording();
}
check(logged, "testGetLogger", desc, null,
"LoggerFinder.getLogger");
}
} catch (Throwable x) {
if (thrown != null && thrown.isInstance(x)) {
System.out.printf("Got expected exception for %s with %s: %s\n",
"LoggerFinder.getLogger", desc, String.valueOf(x));
} else throw x;
}
}
//Can't use testng because testng requires java.logging
//@Test(dataProvider = "getLocalizedLoggerDataProvider")
void testGetLocalizedLogger(String desc, String name, ResourceBundle bundle,
Module mod, Class<? extends Throwable> thrown) {
try {
LoggerFinder finder = System.LoggerFinder.getLoggerFinder();
Logger logger = finder.getLocalizedLogger(name, bundle, mod);
if (thrown != null) {
throw new AssertionError("Exception " + thrown.getName()
+ " not thrown for "
+ "LoggerFinder.getLocalizedLogger"
+ " with " + desc);
}
// Make sure we don't fail if tests are run in parallel
synchronized(RecordStream.LOCK) {
LOG_STREAM.startRecording();
byte[] logged = null;
try {
logger.log(Level.INFO, MESSAGE,
"LoggerFinder.getLocalizedLogger",
desc);
} finally {
logged = LOG_STREAM.stopRecording();
}
check(logged, "testGetLocalizedLogger", desc, bundle,
"LoggerFinder.getLocalizedLogger");
}
} catch (Throwable x) {
if (thrown != null && thrown.isInstance(x)) {
System.out.printf("Got expected exception for %s with %s: %s\n",
"LoggerFinder.getLocalizedLogger", desc, String.valueOf(x));
} else throw x;
}
}
private void check(byte[] logged, String test, String desc,
ResourceBundle bundle, String meth) {
String msg = new String(logged);
String localizedPrefix =
((bundle==null || bundle==EMPTY_BUNDLE)?"":LOCALIZED);
String expected = String.format(TEST_FORMAT, null,
"LoggerFinderAPI " + test, null, Level.INFO.name(),
localizedPrefix + meth + " with " + desc + ": PASSED",
"");
if (!Objects.equals(msg, expected)) {
throw new AssertionError("Expected log message not found: "
+ "\n\texpected: " + expected
+ "\n\tretrieved: " + msg);
}
}
static final Module MODULE = LoggerFinderAPI.class.getModule();
static final ResourceBundle BUNDLE = new MyResourceBundle();
static final ResourceBundle EMPTY_BUNDLE = new EmptyResourceBundle();
static final Object[][] GET_LOGGER = {
{"null name", null, MODULE , NullPointerException.class},
{"null module", "foo", null, NullPointerException.class},
{"null name and module", null, null, NullPointerException.class},
{"non null name and module", "foo", MODULE, null},
};
static final Object[][] GET_LOCALIZED_LOGGER = {
{"null name", null, BUNDLE, MODULE , NullPointerException.class},
{"null module", "foo", BUNDLE, null, NullPointerException.class},
{"null name and module, non null bundle", null, BUNDLE, null, NullPointerException.class},
{"non null name, module, and bundle", "foo", BUNDLE, MODULE, null},
{"null name and bundle", null, null, MODULE , NullPointerException.class},
{"null module and bundle", "foo", null, null, NullPointerException.class},
{"null name and module and bundle", null, null, null, NullPointerException.class},
{"non null name and module, null bundle", "foo", null, MODULE, null},
// tests that MissingResourceBundle is not propagated to the caller of
// logger.log() if the key is not found in the resource bundle
{"non null name, module, and empty bundle", "foo", EMPTY_BUNDLE, MODULE, null},
};
public static Object[][] getLoggerDataProvider() {
return GET_LOGGER;
}
public static Object[][] getLocalizedLoggerDataProvider() {
return GET_LOCALIZED_LOGGER;
}
}

View file

@ -0,0 +1 @@
loggerfinder.SimpleLoggerFinder

View file

@ -0,0 +1,94 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8314263
* @summary Creating a logger while loading the Logger finder
* triggers recursion and StackOverflowError
* @modules java.base/sun.util.logging java.base/jdk.internal.logger:+open
* @library ../lib
* @compile RecursiveLoadingTest.java SimpleLoggerFinder.java
* @run main/othervm PlatformRecursiveLoadingTest
*/
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.logging.LogRecord;
import sun.util.logging.PlatformLogger;
public class PlatformRecursiveLoadingTest {
/**
* This test triggers recursion by calling `System.getLogger` in the class init and constructor
* of a custom LoggerFinder. Without the fix, this is expected to throw
* java.lang.NoClassDefFoundError: Could not initialize class jdk.internal.logger.LoggerFinderLoader$ErrorPolicy
* caused by: java.lang.StackOverflowError
*/
public static void main(String[] args) throws Throwable {
PlatformLogger.getLogger("main").info("in main");
// allow time to let bootstrap logger flush data
BootstrapLoggerUtils.awaitPending();
List<Object> logs = loggerfinder.SimpleLoggerFinder.LOGS;
logs.stream().map(SimpleLogRecord::of).forEach(System.out::println);
logs.stream().map(SimpleLogRecord::of).forEach(SimpleLogRecord::check);
assertEquals(String.valueOf(logs.size()), String.valueOf(3));
}
static List<Object> asList(Object[] params) {
return params == null ? null : Arrays.asList(params);
}
record SimpleLogRecord(String message, Instant instant, String loggerName,
java.util.logging.Level level, List<Object> params,
String resourceBundleName, long seqNumber,
String sourceClassName, String methodName, Throwable thrown) {
SimpleLogRecord(LogRecord record) {
this(record.getMessage(), record.getInstant(), record.getLoggerName(), record.getLevel(),
asList(record.getParameters()), record.getResourceBundleName(), record.getSequenceNumber(),
record.getSourceClassName(), record.getSourceMethodName(), record.getThrown());
}
static SimpleLogRecord of(Object o) {
return (o instanceof LogRecord record) ? new SimpleLogRecord(record) : null;
}
static SimpleLogRecord check(SimpleLogRecord record) {
if (record.loggerName.equals("dummy")) {
assertEquals(record.sourceClassName, "jdk.internal.logger.BootstrapLogger$LogEvent");
assertEquals(record.methodName(), "log");
}
if (record.loggerName.equals("main")) {
assertEquals(record.sourceClassName, PlatformRecursiveLoadingTest.class.getName());
assertEquals(record.methodName, "main");
}
return record;
}
}
private static void assertEquals(String received, String expected) {
if (!expected.equals(received)) {
throw new RuntimeException("Received: " + received);
}
}
}

View file

@ -0,0 +1,92 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8314263
* @summary Creating a logger while loading the Logger finder
* triggers recursion and StackOverflowError
* @modules java.base/jdk.internal.logger:+open
* @library ../lib
* @compile RecursiveLoadingTest.java SimpleLoggerFinder.java
* @run main/othervm RecursiveLoadingTest
*/
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.logging.LogRecord;
public class RecursiveLoadingTest {
/**
* This test triggers recursion by calling `System.getLogger` in the class init and constructor
* of a custom LoggerFinder. Without the fix, this is expected to throw
* java.lang.NoClassDefFoundError: Could not initialize class jdk.internal.logger.LoggerFinderLoader$ErrorPolicy
* caused by: java.lang.StackOverflowError
*/
public static void main(String[] args) throws Throwable {
System.getLogger("main").log(System.Logger.Level.INFO, "in main");
// allow time to let bootstrap logger flush data
BootstrapLoggerUtils.awaitPending();
List<Object> logs = loggerfinder.SimpleLoggerFinder.LOGS;
logs.stream().map(SimpleLogRecord::of).forEach(System.out::println);
logs.stream().map(SimpleLogRecord::of).forEach(SimpleLogRecord::check);
assertEquals(String.valueOf(logs.size()), String.valueOf(3));
}
static List<Object> asList(Object[] params) {
return params == null ? null : Arrays.asList(params);
}
record SimpleLogRecord(String message, Instant instant, String loggerName,
java.util.logging.Level level, List<Object> params,
String resourceBundleName, long seqNumber,
String sourceClassName, String methodName, Throwable thrown) {
SimpleLogRecord(LogRecord record) {
this(record.getMessage(), record.getInstant(), record.getLoggerName(), record.getLevel(),
asList(record.getParameters()), record.getResourceBundleName(), record.getSequenceNumber(),
record.getSourceClassName(), record.getSourceMethodName(), record.getThrown());
}
static SimpleLogRecord of(Object o) {
return (o instanceof LogRecord record) ? new SimpleLogRecord(record) : null;
}
static SimpleLogRecord check(SimpleLogRecord record) {
if (record.loggerName.equals("dummy")) {
assertEquals(record.sourceClassName, "jdk.internal.logger.BootstrapLogger$LogEvent");
assertEquals(record.methodName(), "log");
}
if (record.loggerName.equals("main")) {
assertEquals(record.sourceClassName, RecursiveLoadingTest.class.getName());
assertEquals(record.methodName, "main");
}
return record;
}
}
private static void assertEquals(String received, String expected) {
if (!expected.equals(received)) {
throw new RuntimeException("Received: " + received);
}
}
}

View file

@ -0,0 +1,132 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package loggerfinder;
import java.lang.*;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
public class SimpleLoggerFinder extends System.LoggerFinder {
public static final CopyOnWriteArrayList<Object> LOGS = new CopyOnWriteArrayList<>();
static {
try {
long sleep = new Random().nextLong(1000L) + 1L;
// simulate a slow load service
Thread.sleep(sleep);
System.getLogger("dummy")
.log(System.Logger.Level.INFO,
"Logger finder service load sleep value: " + sleep);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
private final Map<String, SimpleLogger> loggers = new ConcurrentHashMap<>();
public SimpleLoggerFinder() {
System.getLogger("dummy")
.log(System.Logger.Level.INFO,
"Logger finder service created");
}
@Override
public System.Logger getLogger(String name, Module module) {
return loggers.computeIfAbsent(name, SimpleLogger::new);
}
private static class SimpleLogger implements System.Logger {
private final java.util.logging.Logger logger;
private static final class SimpleHandler extends Handler {
@Override
public void publish(LogRecord record) {
LOGS.add(record);
}
@Override public void flush() { }
@Override public void close() { }
}
public SimpleLogger(String name) {
logger = Logger.getLogger(name);
logger.addHandler(new SimpleHandler());
}
@Override
public String getName() {
return logger.getName();
}
java.util.logging.Level level(Level level) {
return switch (level) {
case ALL -> java.util.logging.Level.ALL;
case DEBUG -> java.util.logging.Level.FINE;
case TRACE -> java.util.logging.Level.FINER;
case INFO -> java.util.logging.Level.INFO;
case WARNING -> java.util.logging.Level.WARNING;
case ERROR -> java.util.logging.Level.SEVERE;
case OFF -> java.util.logging.Level.OFF;
};
}
@Override
public boolean isLoggable(Level level) {
return logger.isLoggable(level(level));
}
@Override
public void log(Level level, ResourceBundle bundle, String msg, Throwable thrown) {
var julLevel = level(level);
if (!logger.isLoggable(julLevel)) return;
if (bundle != null) {
logger.logrb(julLevel, bundle, msg, thrown);
} else {
logger.log(julLevel, msg, thrown);
}
}
@Override
public void log(Level level, ResourceBundle bundle, String format, Object... params) {
var julLevel = level(level);
if (!logger.isLoggable(julLevel)) return;
if (params == null) {
if (bundle == null) {
logger.log(julLevel, format);
} else {
logger.logrb(julLevel, bundle, format);
}
} else {
if (bundle == null) {
logger.log(julLevel, format, params);
} else {
logger.logrb(julLevel, bundle, format, params);
}
}
}
}
}

View file

@ -0,0 +1,307 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8314263
* @summary Signed jars triggering Logger finder recursion and StackOverflowError
* @library /test/lib ../lib
* @compile/module=java.base share/classes/jdk/internal/event/EventHelper.java
* @modules java.base/jdk.internal.logger:+open
* @build jdk.test.lib.compiler.CompilerUtils
* jdk.test.lib.process.*
* jdk.test.lib.util.JarUtils
* jdk.test.lib.JDKToolLauncher
* @compile SignedLoggerFinderTest.java SimpleLoggerFinder.java
* @run main SignedLoggerFinderTest init
* @run main SignedLoggerFinderTest init sign
*/
import java.io.File;
import java.nio.file.*;
import java.security.*;
import java.util.*;
import java.util.function.*;
import java.util.jar.*;
import jdk.test.lib.JDKToolFinder;
import jdk.test.lib.JDKToolLauncher;
import jdk.test.lib.Utils;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.util.JarUtils;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static java.util.Arrays.asList;
public class SignedLoggerFinderTest {
/**
* This test triggers recursion in the broken JDK. The error can
* manifest in a few different ways.
* One error seen is "java.lang.NoClassDefFoundError:
* Could not initialize class jdk.internal.logger.LoggerFinderLoader$ErrorPolicy"
*
* The original reported error was a StackOverflow (also seen in different iterations
* of this run). Running test in signed and unsigned jar mode for sanity coverage.
* The current bug only manifests when jars are signed.
*/
private static boolean init = false;
private static boolean signJars = false;
private static boolean mutliThreadLoad = false;
private static volatile boolean testComplete = false;
private static final String KEYSTORE = "8314263.keystore";
private static final String ALIAS = "JavaTest";
private static final String STOREPASS = "changeit";
private static final String KEYPASS = "changeit";
private static final String DNAME = "CN=sample";
private static final String CUSTOM_LOGGER_FINDER_NAME =
"loggerfinder.SimpleLoggerFinder";
private static final String CUSTOM_LOGGER_NAME =
"loggerfinder.SimpleLoggerFinder$SimpleLogger";
private static final String INTERNAL_LOGGER_FINDER_NAME =
"sun.util.logging.internal.LoggingProviderImpl";
private static final String INTERNAL_LOGGER_NAME =
"sun.util.logging.internal.LoggingProviderImpl$JULWrapper";
private static final Path jarPath1 =
Path.of(System.getProperty("test.classes", "."), "SimpleLoggerFinder.jar");
private static final Path jarPath2 =
Path.of(System.getProperty("test.classes", "."), "SimpleLoggerFinder2.jar");
public static void main(String[] args) throws Throwable {
init = args.length >=1 && args[0].equals("init");
signJars = args.length >=2 && args[1].equals("sign");
// init only passed in by jtreg test run, initialize the environment
// for the subsequent test run
if (init) {
initialize();
launchTest(false, false);
launchTest(false, true);
launchTest(true, false);
launchTest(true, true);
} else {
// set up complete. Run the code to trigger the recursion
// We're in the JVM launched by ProcessTools.executeCommand
boolean multiThreadLoad = Boolean.getBoolean("multiThreadLoad");
boolean withCustomLoggerFinder = Boolean.getBoolean("withCustomLoggerFinder");
if (multiThreadLoad) {
long sleep = new Random().nextLong(100L) + 1L;
System.out.println("multi thread load sleep value: " + sleep);
new Thread(runnableWithSleep(
() -> System.getLogger("logger" + System.currentTimeMillis()),
sleep, "System.getLogger type: ")).start();
new Thread(runnableWithSleep(
() -> System.LoggerFinder.getLoggerFinder(),
sleep, "System.getLoggerFinder type: ")).start();
}
if (withCustomLoggerFinder) {
JarFile jf = new JarFile(jarPath1.toString(), true);
jf.getInputStream(jf.getJarEntry("loggerfinder/SimpleLoggerFinder.class"));
JarFile jf2 = new JarFile(jarPath2.toString(), true);
jf2.getInputStream(jf.getJarEntry("loggerfinder/SimpleLoggerFinder.class"));
} else {
// some other call to prod LoggerFinder loading
System.getLogger("random" + System.currentTimeMillis());
System.LoggerFinder.getLoggerFinder();
}
Security.setProperty("test_1", "test");
// some extra sanity checks
if (withCustomLoggerFinder) {
assertEquals(System.LoggerFinder.getLoggerFinder().getClass().getName(),
CUSTOM_LOGGER_FINDER_NAME);
System.Logger testLogger = System.getLogger("jdk.event.security");
assertEquals(testLogger.getClass().getName(), CUSTOM_LOGGER_NAME);
} else {
assertEquals(System.LoggerFinder.getLoggerFinder().getClass().getName(),
INTERNAL_LOGGER_FINDER_NAME);
System.Logger testLogger = System.getLogger("jdk.event.security");
assertEquals(testLogger.getClass().getName(), INTERNAL_LOGGER_NAME);
}
testComplete = true;
// LoggerFinder should be initialized, trigger a simple log call
Security.setProperty("test_2", "test");
// allow time to let bootstrap logger flush data
BootstrapLoggerUtils.awaitPending();
}
}
// helper to create the inner test. Run config variations with the LoggerFinder jars
// on the classpath and with other threads running System.Logger calls during load
private static void launchTest(boolean multiThreadLoad, boolean withCustomLoggerFinder) {
List<String> cmds = new ArrayList<>();
cmds.add(JDKToolFinder.getJDKTool("java"));
cmds.addAll(asList(Utils.getTestJavaOpts()));
if (withCustomLoggerFinder) {
cmds.addAll(List.of("-classpath",
System.getProperty("test.classes") + File.pathSeparator +
jarPath1.toString() + File.pathSeparator + jarPath2.toString(),
"-Dtest.classes=" + System.getProperty("test.classes")));
} else {
cmds.addAll(List.of("-classpath",
System.getProperty("test.classes")));
}
Path patches = Paths.get(System.getProperty("test.classes"), "patches", "java.base");
cmds.addAll(List.of(
// patch of EventHelper to log at INFO level (for bootstrap logger)
"--patch-module", "java.base=" + patches.toString(),
// allow test to access internal bootstrap logger functionality
"--add-opens=java.base/jdk.internal.logger=ALL-UNNAMED",
// following debug property seems useful to tickle the issue
"-Dsun.misc.URLClassPath.debug=true",
// console logger level to capture event output
"-Djdk.system.logger.level=DEBUG",
// useful for debug purposes
"-Djdk.logger.finder.error=DEBUG",
// enable logging to verify correct output
"-Djava.util.logging.config.file=" +
Path.of(System.getProperty("test.src", "."), "logging.properties")));
if (multiThreadLoad) {
cmds.add("-DmultiThreadLoad=true");
}
if (withCustomLoggerFinder) {
cmds.add("-DwithCustomLoggerFinder=true");
}
cmds.addAll(List.of(
"SignedLoggerFinderTest",
"no-init"));
try {
OutputAnalyzer outputAnalyzer = ProcessTools.executeCommand(cmds.stream()
.filter(t -> !t.isEmpty())
.toArray(String[]::new))
.shouldHaveExitValue(0);
if (withCustomLoggerFinder) {
outputAnalyzer
.shouldContain("TEST LOGGER: [test_1, test]")
.shouldContain("TEST LOGGER: [test_2, test]");
} else {
outputAnalyzer
.shouldContain("SecurityPropertyModification: key:test_1")
.shouldContain("SecurityPropertyModification: key:test_2");
}
if (withCustomLoggerFinder && signJars) {
// X509 cert generated during verification of signed jar file
outputAnalyzer
.shouldContain(DNAME);
}
} catch (Throwable t) {
throw new RuntimeException("Unexpected fail.", t);
}
}
private static Runnable runnableWithSleep(Supplier s, long sleep, String desc) {
return () -> {
while(!testComplete) {
System.out.println(desc + s.get().getClass().getName());
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
};
}
private static void initialize() throws Throwable {
if (signJars) {
genKey();
}
Path classes = Paths.get(System.getProperty("test.classes", ""));
JarUtils.createJarFile(jarPath1,
classes,
classes.resolve("loggerfinder/SimpleLoggerFinder.class"),
classes.resolve("loggerfinder/SimpleLoggerFinder$SimpleLogger.class"));
JarUtils.updateJarFile(jarPath1, Path.of(System.getProperty("test.src")),
Path.of("META-INF", "services", "java.lang.System$LoggerFinder"));
if (signJars) {
signJar(jarPath1.toString());
}
// multiple signed jars with services to have ServiceLoader iteration
Files.copy(jarPath1, jarPath2, REPLACE_EXISTING);
}
private static void genKey() throws Throwable {
String keytool = JDKToolFinder.getJDKTool("keytool");
Files.deleteIfExists(Paths.get(KEYSTORE));
ProcessTools.executeCommand(keytool,
"-J-Duser.language=en",
"-J-Duser.country=US",
"-genkey",
"-keyalg", "rsa",
"-alias", ALIAS,
"-keystore", KEYSTORE,
"-keypass", KEYPASS,
"-dname", DNAME,
"-storepass", STOREPASS
).shouldHaveExitValue(0);
}
private static OutputAnalyzer signJar(String jarName) throws Throwable {
List<String> args = new ArrayList<>();
args.add("-verbose");
args.add(jarName);
args.add(ALIAS);
return jarsigner(args);
}
private static OutputAnalyzer jarsigner(List<String> extra)
throws Throwable {
JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jarsigner")
.addVMArg("-Duser.language=en")
.addVMArg("-Duser.country=US")
.addToolArg("-keystore")
.addToolArg(KEYSTORE)
.addToolArg("-storepass")
.addToolArg(STOREPASS)
.addToolArg("-keypass")
.addToolArg(KEYPASS);
for (String s : extra) {
if (s.startsWith("-J")) {
launcher.addVMArg(s.substring(2));
} else {
launcher.addToolArg(s);
}
}
return ProcessTools.executeCommand(launcher.getCommand());
}
private static void assertEquals(String received, String expected) {
if (!expected.equals(received)) {
throw new RuntimeException("Received: " + received);
}
}
}

View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package loggerfinder;
import java.lang.*;
import java.util.*;
public class SimpleLoggerFinder extends System.LoggerFinder {
static {
try {
long sleep = new Random().nextLong(1000L) + 1L;
System.out.println("Logger finder service load sleep value: " + sleep);
// simulate a slow load service
Thread.sleep(sleep);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
@Override
public System.Logger getLogger(String name, Module module) {
return new SimpleLogger(name);
}
private static class SimpleLogger implements System.Logger {
private final String name;
public SimpleLogger(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public boolean isLoggable(Level level) {
return true;
}
@Override
public void log(Level level, ResourceBundle bundle, String msg, Throwable thrown) {
System.out.println("TEST LOGGER: " + msg);
}
@Override
public void log(Level level, ResourceBundle bundle, String format, Object... params) {
System.out.println("TEST LOGGER: " + Arrays.asList(params));
}
}
}

View file

@ -0,0 +1,155 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.event;
import jdk.internal.access.JavaUtilJarAccess;
import jdk.internal.access.SharedSecrets;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.stream.Collectors;
import java.util.stream.LongStream;
/**
* A helper class to have events logged to a JDK Event Logger.
*/
public final class EventHelper {
private static final JavaUtilJarAccess JUJA = SharedSecrets.javaUtilJarAccess();
private static volatile boolean loggingSecurity;
private static volatile System.Logger securityLogger;
private static final VarHandle LOGGER_HANDLE;
static {
System.err.println("Patch version of EventHelper loaded");
try {
LOGGER_HANDLE =
MethodHandles.lookup().findStaticVarHandle(
EventHelper.class, "securityLogger", System.Logger.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
// a patch module to help this test ensure events are logged when
// bootstrap logger is in use
private static final System.Logger.Level LOG_LEVEL = System.Logger.Level.INFO;
// helper class used for logging security related events for now
private static final String SECURITY_LOGGER_NAME = "jdk.event.security";
public static void logTLSHandshakeEvent(Instant start,
String peerHost,
int peerPort,
String cipherSuite,
String protocolVersion,
long peerCertId) {
assert securityLogger != null;
String prepend = getDurationString(start);
securityLogger.log(LOG_LEVEL, prepend +
" TLSHandshake: {0}:{1,number,#}, {2}, {3}, {4,number,#}",
peerHost, peerPort, protocolVersion, cipherSuite, peerCertId);
}
public static void logSecurityPropertyEvent(String key,
String value) {
assert securityLogger != null;
securityLogger.log(LOG_LEVEL,
"SecurityPropertyModification: key:{0}, value:{1}", key, value);
}
public static void logX509ValidationEvent(long anchorCertId,
long[] certIds) {
assert securityLogger != null;
String codes = LongStream.of(certIds)
.mapToObj(Long::toString)
.collect(Collectors.joining(", "));
securityLogger.log(LOG_LEVEL,
"ValidationChain: {0,number,#}, {1}", anchorCertId, codes);
}
public static void logX509CertificateEvent(String algId,
String serialNum,
String subject,
String issuer,
String keyType,
int length,
long certId,
long beginDate,
long endDate) {
assert securityLogger != null;
securityLogger.log(LOG_LEVEL, "X509Certificate: Alg:{0}, Serial:{1}" +
", Subject:{2}, Issuer:{3}, Key type:{4}, Length:{5,number,#}" +
", Cert Id:{6,number,#}, Valid from:{7}, Valid until:{8}",
algId, serialNum, subject, issuer, keyType, length,
certId, new Date(beginDate), new Date(endDate));
}
/**
* Method to calculate a duration timestamp for events which measure
* the start and end times of certain operations.
* @param start Instant indicating when event started recording
* @return A string representing duraction from start time to
* time of this method call. Empty string is start is null.
*/
private static String getDurationString(Instant start) {
if (start != null) {
if (start.equals(Instant.MIN)) {
return "N/A";
}
Duration duration = Duration.between(start, Instant.now());
long micros = duration.toNanos() / 1_000;
if (micros < 1_000_000) {
return "duration = " + (micros / 1_000.0) + " ms:";
} else {
return "duration = " + ((micros / 1_000) / 1_000.0) + " s:";
}
} else {
return "";
}
}
/**
* Helper to determine if security events are being logged
* at a preconfigured logging level. The configuration value
* is read once at class initialization.
*
* @return boolean indicating whether an event should be logged
*/
public static boolean isLoggingSecurity() {
// Avoid a bootstrap issue where the commitEvent attempts to
// trigger early loading of System Logger but where
// the verification process still has JarFiles locked
if (securityLogger == null && !JUJA.isInitializing()) {
LOGGER_HANDLE.compareAndSet( null, System.getLogger(SECURITY_LOGGER_NAME));
loggingSecurity = securityLogger.isLoggable(LOG_LEVEL);
}
return loggingSecurity;
}
}

View file

@ -0,0 +1,13 @@
############################################################
# Configuration file for log testing
#
############################################################
handlers= java.util.logging.ConsoleHandler
.level= FINE
java.util.logging.ConsoleHandler.level = FINE
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
jdk.event.security.level = FINE

View file

@ -0,0 +1,82 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.IOException;
import java.lang.System.Logger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ResourceBundle;
/**
*
* @author danielfuchs
*/
public final class AccessSystemLogger {
public AccessSystemLogger() {
this(check());
}
private AccessSystemLogger(Void unused) {
}
private static Void check() {
if (AccessSystemLogger.class.getClassLoader() != null) {
throw new RuntimeException("AccessSystemLogger should be loaded by the null classloader");
}
return null;
}
public Logger getLogger(String name) {
Logger logger = System.getLogger(name);
System.out.println("System.getLogger(\"" + name + "\"): " + logger);
return logger;
}
public Logger getLogger(String name, ResourceBundle bundle) {
Logger logger = System.getLogger(name, bundle);
System.out.println("System.getLogger(\"" + name + "\", bundle): " + logger);
return logger;
}
static final Class<?>[] toCopy = { AccessSystemLogger.class, CustomSystemClassLoader.class };
// copy AccessSystemLogger.class to ./boot
public static void main(String[] args) throws IOException {
Path testDir = Paths.get(System.getProperty("user.dir", "."));
Path bootDir = Paths.get(testDir.toString(), "boot");
Path classes = Paths.get(System.getProperty("test.classes", "build/classes"));
if (Files.notExists(bootDir)) {
Files.createDirectory(bootDir);
}
for (Class<?> c : toCopy) {
Path thisClass = Paths.get(classes.toString(),
c.getSimpleName()+".class");
Path dest = Paths.get(bootDir.toString(),
c.getSimpleName()+".class");
Files.copy(thisClass, dest, StandardCopyOption.REPLACE_EXISTING);
}
}
}

View file

@ -0,0 +1,768 @@
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.UncheckedIOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.stream.Stream;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
import java.lang.System.LoggerFinder;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import jdk.internal.logger.DefaultLoggerFinder;
import jdk.internal.logger.SimpleConsoleLogger;
import sun.util.logging.PlatformLogger;
/**
* @test
* @bug 8140364 8145686 8189291
* @summary JDK implementation specific unit test for the base DefaultLoggerFinder.
* Tests the behavior of DefaultLoggerFinder and SimpleConsoleLogger
* implementation.
* @modules java.base/sun.util.logging
* java.base/jdk.internal.logger
* @build AccessSystemLogger BaseDefaultLoggerFinderTest CustomSystemClassLoader BaseLoggerFinder
* @run driver AccessSystemLogger
* @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseDefaultLoggerFinderTest DEFAULTS
* @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseDefaultLoggerFinderTest WITHCUSTOMWRAPPERS
* @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseDefaultLoggerFinderTest WITHREFLECTION
* @author danielfuchs
*/
public class BaseDefaultLoggerFinderTest {
final static boolean VERBOSE = false;
final static AccessSystemLogger accessSystemLogger = new AccessSystemLogger();
static final Class<?>[] providerClass;
static {
try {
providerClass = new Class<?>[] {
ClassLoader.getSystemClassLoader().loadClass("BaseLoggerFinder"),
};
} catch (ClassNotFoundException ex) {
throw new ExceptionInInitializerError(ex);
}
}
/**
* What our test provider needs to implement.
*/
public static interface TestLoggerFinder {
public final static AtomicBoolean fails = new AtomicBoolean();
public final static AtomicReference<String> conf = new AtomicReference<>("");
public final static AtomicLong sequencer = new AtomicLong();
public Logger getLogger(String name, Module caller);
public Logger getLocalizedLogger(String name, ResourceBundle bundle, Module caller);
void setLevel(Logger logger, Level level, Module caller);
void setLevel(Logger logger, PlatformLogger.Level level, Module caller);
PlatformLogger.Bridge asPlatformLoggerBridge(Logger logger);
}
public static class MyBundle extends ResourceBundle {
final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
@Override
protected Object handleGetObject(String key) {
if (key.contains(" (translated)")) {
throw new RuntimeException("Unexpected key: " + key);
}
return map.computeIfAbsent(key, k -> k.toUpperCase(Locale.ROOT) + " (translated)");
}
@Override
public Enumeration<String> getKeys() {
return Collections.enumeration(map.keySet());
}
}
public static class MyLoggerBundle extends MyBundle {
}
static enum TestCases {DEFAULTS, WITHCUSTOMWRAPPERS, WITHREFLECTION};
static TestLoggerFinder getLoggerFinder(Class<?> expectedClass) {
TestLoggerFinder.sequencer.incrementAndGet();
LoggerFinder provider = LoggerFinder.getLoggerFinder();
ErrorStream.errorStream.store();
System.out.println("*** Actual LoggerFinder class is: " + provider.getClass().getName());
expectedClass.cast(provider);
return TestLoggerFinder.class.cast(provider);
}
static class ErrorStream extends PrintStream {
static AtomicBoolean forward = new AtomicBoolean();
ByteArrayOutputStream out;
String saved = "";
public ErrorStream(ByteArrayOutputStream out) {
super(out);
this.out = out;
}
@Override
public void write(int b) {
super.write(b);
if (forward.get()) err.write(b);
}
@Override
public void write(byte[] b) throws IOException {
super.write(b);
if (forward.get()) err.write(b);
}
@Override
public void write(byte[] buf, int off, int len) {
super.write(buf, off, len);
if (forward.get()) err.write(buf, off, len);
}
public String peek() {
flush();
return out.toString();
}
public String drain() {
flush();
String res = out.toString();
out.reset();
return res;
}
public void store() {
flush();
saved = out.toString();
out.reset();
}
public void restore() {
out.reset();
try {
out.write(saved.getBytes());
} catch(IOException io) {
throw new UncheckedIOException(io);
}
}
static final PrintStream err = System.err;
static final ErrorStream errorStream = new ErrorStream(new ByteArrayOutputStream());
}
private static StringBuilder appendProperty(StringBuilder b, String name) {
String value = System.getProperty(name);
if (value == null) return b;
return b.append(name).append("=").append(value).append('\n');
}
static class CustomLoggerWrapper implements Logger {
Logger impl;
public CustomLoggerWrapper(Logger logger) {
this.impl = Objects.requireNonNull(logger);
}
@Override
public String getName() {
return impl.getName();
}
@Override
public boolean isLoggable(Level level) {
return impl.isLoggable(level);
}
@Override
public void log(Level level, ResourceBundle rb, String string, Throwable thrwbl) {
impl.log(level, rb, string, thrwbl);
}
@Override
public void log(Level level, ResourceBundle rb, String string, Object... os) {
impl.log(level, rb, string, os);
}
@Override
public void log(Level level, Object o) {
impl.log(level, o);
}
@Override
public void log(Level level, String string) {
impl.log(level, string);
}
@Override
public void log(Level level, Supplier<String> splr) {
impl.log(level, splr);
}
@Override
public void log(Level level, String string, Object... os) {
impl.log(level, string, os);
}
@Override
public void log(Level level, String string, Throwable thrwbl) {
impl.log(level, string, thrwbl);
}
@Override
public void log(Level level, Supplier<String> splr, Throwable thrwbl) {
Logger.super.log(level, splr, thrwbl);
}
@Override
public String toString() {
return super.toString() + "(impl=" + impl + ")";
}
}
/**
* The ReflectionLoggerWrapper additionally makes it possible to verify
* that code which use reflection to call System.Logger will be skipped
* when looking for the calling method.
*/
static class ReflectionLoggerWrapper implements Logger {
Logger impl;
public ReflectionLoggerWrapper(Logger logger) {
this.impl = Objects.requireNonNull(logger);
}
private Object invoke(Method m, Object... params) {
try {
return m.invoke(impl, params);
} catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException ex) {
throw new RuntimeException(ex);
}
}
@Override
public String getName() {
return impl.getName();
}
@Override
public boolean isLoggable(Level level) {
return impl.isLoggable(level);
}
@Override
public void log(Level level, ResourceBundle rb, String string, Throwable thrwbl) {
try {
invoke(System.Logger.class.getMethod(
"log", Level.class, ResourceBundle.class, String.class, Throwable.class),
level, rb, string, thrwbl);
} catch (NoSuchMethodException ex) {
throw new RuntimeException(ex);
}
}
@Override
public void log(Level level, ResourceBundle rb, String string, Object... os) {
try {
invoke(System.Logger.class.getMethod(
"log", Level.class, ResourceBundle.class, String.class, Object[].class),
level, rb, string, os);
} catch (NoSuchMethodException ex) {
throw new RuntimeException(ex);
}
}
@Override
public void log(Level level, String string) {
try {
invoke(System.Logger.class.getMethod(
"log", Level.class, String.class),
level, string);
} catch (NoSuchMethodException ex) {
throw new RuntimeException(ex);
}
}
@Override
public void log(Level level, String string, Object... os) {
try {
invoke(System.Logger.class.getMethod(
"log", Level.class, String.class, Object[].class),
level, string, os);
} catch (NoSuchMethodException ex) {
throw new RuntimeException(ex);
}
}
@Override
public void log(Level level, String string, Throwable thrwbl) {
try {
invoke(System.Logger.class.getMethod(
"log", Level.class, String.class, Throwable.class),
level, string, thrwbl);
} catch (NoSuchMethodException ex) {
throw new RuntimeException(ex);
}
}
@Override
public String toString() {
return super.toString() + "(impl=" + impl + ")";
}
}
public static void main(String[] args) {
if (args.length == 0) {
args = new String[] {
"DEFAULTS",
"WITHCUSTOMWRAPPERS",
"WITHREFLECTION"
};
}
Locale.setDefault(Locale.ENGLISH);
System.setErr(ErrorStream.errorStream);
//System.setProperty("jdk.logger.finder.error", "ERROR");
//System.setProperty("jdk.logger.finder.singleton", "true");
//System.setProperty("test.fails", "true");
TestLoggerFinder.fails.set(Boolean.getBoolean("test.fails"));
StringBuilder c = new StringBuilder();
appendProperty(c, "jdk.logger.packages");
appendProperty(c, "jdk.logger.finder.error");
appendProperty(c, "jdk.logger.finder.singleton");
appendProperty(c, "test.fails");
TestLoggerFinder.conf.set(c.toString());
try {
test(args);
} finally {
try {
System.setErr(ErrorStream.err);
} catch (Error | RuntimeException x) {
x.printStackTrace(ErrorStream.err);
}
}
}
public static void test(String[] args) {
final Class<?> expectedClass = jdk.internal.logger.DefaultLoggerFinder.class;
System.out.println("Declared provider class: " + providerClass[0]
+ "[" + providerClass[0].getClassLoader() + "]");
Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
TestLoggerFinder provider;
ErrorStream.errorStream.restore();
switch (testCase) {
case DEFAULTS:
System.out.println("\n*** Defaults\n");
System.out.println(TestLoggerFinder.conf.get());
provider = getLoggerFinder(expectedClass);
if (!provider.getClass().getName().equals("BaseLoggerFinder")) {
throw new RuntimeException("Unexpected provider: " + provider.getClass().getName());
}
test(provider);
System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get());
break;
case WITHCUSTOMWRAPPERS:
System.out.println("\n*** With custom Wrapper\n");
System.out.println(TestLoggerFinder.conf.get());
provider = getLoggerFinder(expectedClass);
if (!provider.getClass().getName().equals("BaseLoggerFinder")) {
throw new RuntimeException("Unexpected provider: " + provider.getClass().getName());
}
test(provider, CustomLoggerWrapper::new);
break;
case WITHREFLECTION:
System.out.println("\n*** Using reflection while logging\n");
System.out.println(TestLoggerFinder.conf.get());
provider = getLoggerFinder(expectedClass);
if (!provider.getClass().getName().equals("BaseLoggerFinder")) {
throw new RuntimeException("Unexpected provider: " + provider.getClass().getName());
}
test(provider, ReflectionLoggerWrapper::new);
break;
default:
throw new RuntimeException("Unknown test case: " + testCase);
}
});
System.out.println("\nPASSED: Tested " + TestLoggerFinder.sequencer.get() + " cases.");
}
public static void test(TestLoggerFinder provider) {
test(provider, Function.identity());
}
public static void test(TestLoggerFinder provider, Function<Logger, Logger> wrapper) {
ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
final Map<Logger, String> loggerDescMap = new HashMap<>();
System.Logger sysLogger = wrapper.apply(accessSystemLogger.getLogger("foo"));
loggerDescMap.put(sysLogger, "accessSystemLogger.getLogger(\"foo\")");
System.Logger localizedSysLogger = wrapper.apply(accessSystemLogger.getLogger("fox", loggerBundle));
loggerDescMap.put(localizedSysLogger, "accessSystemLogger.getLogger(\"fox\", loggerBundle)");
System.Logger appLogger = wrapper.apply(System.getLogger("bar"));
loggerDescMap.put(appLogger,"System.getLogger(\"bar\")");
System.Logger localizedAppLogger = wrapper.apply(System.getLogger("baz", loggerBundle));
loggerDescMap.put(localizedAppLogger,"System.getLogger(\"baz\", loggerBundle)");
testLogger(provider, loggerDescMap, "foo", null, sysLogger, accessSystemLogger.getClass());
testLogger(provider, loggerDescMap, "foo", loggerBundle, localizedSysLogger, accessSystemLogger.getClass());
testLogger(provider, loggerDescMap, "foo", null, appLogger, BaseDefaultLoggerFinderTest.class);
testLogger(provider, loggerDescMap, "foo", loggerBundle, localizedAppLogger, BaseDefaultLoggerFinderTest.class);
}
public static class Foo {
}
static void verbose(String msg) {
if (VERBOSE) {
System.out.println(msg);
}
}
// Calls the 8 methods defined on Logger and verify the
// parameters received by the underlying TestProvider.LoggerImpl
// logger.
private static void testLogger(TestLoggerFinder provider,
Map<Logger, String> loggerDescMap,
String name,
ResourceBundle loggerBundle,
Logger logger,
Class<?> callerClass) {
System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger +"]");
AtomicLong sequencer = TestLoggerFinder.sequencer;
Module caller = callerClass.getModule();
Foo foo = new Foo();
String fooMsg = foo.toString();
for (Level loggerLevel : Level.values()) {
provider.setLevel(logger, loggerLevel, caller);
for (Level messageLevel : Level.values()) {
ErrorStream.errorStream.drain();
String desc = "logger.log(messageLevel, foo): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
sequencer.incrementAndGet();
logger.log(messageLevel, foo);
if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (!ErrorStream.errorStream.peek().isEmpty()) {
throw new RuntimeException("unexpected event in queue for "
+ desc +": " + "\n\t" + ErrorStream.errorStream.drain());
}
} else {
String logged = ErrorStream.errorStream.drain();
if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
|| !logged.contains(messageLevel.getName() + ": " + fooMsg)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected:" + "\n<<<<\n"
+ "[date] BaseDefaultLoggerFinderTest testLogger\n"
+ messageLevel.getName() + " " + fooMsg
+ "\n>>>>"
+ "\n\t actual:"
+ "\n<<<<\n" + logged + ">>>>\n");
} else {
verbose("Got expected results for "
+ desc + "\n<<<<\n" + logged + ">>>>\n");
}
}
}
}
String msg = "blah";
for (Level loggerLevel : Level.values()) {
provider.setLevel(logger, loggerLevel, caller);
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
sequencer.incrementAndGet();
logger.log(messageLevel, msg);
if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (!ErrorStream.errorStream.peek().isEmpty()) {
throw new RuntimeException("unexpected event in queue for "
+ desc +": " + "\n\t" + ErrorStream.errorStream.drain());
}
} else {
String logged = ErrorStream.errorStream.drain();
String msgText = loggerBundle == null ? msg : loggerBundle.getString(msg);
if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
|| !logged.contains(messageLevel.getName() + ": " + msgText)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected:" + "\n<<<<\n"
+ "[date] BaseDefaultLoggerFinderTest testLogger\n"
+ messageLevel.getName() + " " + msgText
+ "\n>>>>"
+ "\n\t actual:"
+ "\n<<<<\n" + logged + ">>>>\n");
} else {
verbose("Got expected results for "
+ desc + "\n<<<<\n" + logged + ">>>>\n");
}
}
}
}
Supplier<String> fooSupplier = new Supplier<String>() {
@Override
public String get() {
return this.toString();
}
};
for (Level loggerLevel : Level.values()) {
provider.setLevel(logger, loggerLevel, caller);
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
sequencer.incrementAndGet();
logger.log(messageLevel, fooSupplier);
if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (!ErrorStream.errorStream.peek().isEmpty()) {
throw new RuntimeException("unexpected event in queue for "
+ desc +": " + "\n\t" + ErrorStream.errorStream.drain());
}
} else {
String logged = ErrorStream.errorStream.drain();
if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
|| !logged.contains(messageLevel.getName() + ": " + fooSupplier.get())) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected:" + "\n<<<<\n"
+ "[date] BaseDefaultLoggerFinderTest testLogger\n"
+ messageLevel.getName() + " " + fooSupplier.get()
+ "\n>>>>"
+ "\n\t actual:"
+ "\n<<<<\n" + logged + ">>>>\n");
} else {
verbose("Got expected results for "
+ desc + "\n<<<<\n" + logged + ">>>>\n");
}
}
}
}
String format = "two params [{1} {2}]";
Object arg1 = foo;
Object arg2 = msg;
for (Level loggerLevel : Level.values()) {
provider.setLevel(logger, loggerLevel, caller);
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, format, params...): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
sequencer.incrementAndGet();
logger.log(messageLevel, format, foo, msg);
if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (!ErrorStream.errorStream.peek().isEmpty()) {
throw new RuntimeException("unexpected event in queue for "
+ desc +": " + "\n\t" + ErrorStream.errorStream.drain());
}
} else {
String logged = ErrorStream.errorStream.drain();
String msgFormat = loggerBundle == null ? format : loggerBundle.getString(format);
String text = java.text.MessageFormat.format(msgFormat, foo, msg);
if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
|| !logged.contains(messageLevel.getName() + ": " + text)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected:" + "\n<<<<\n"
+ "[date] BaseDefaultLoggerFinderTest testLogger\n"
+ messageLevel.getName() + " " + text
+ "\n>>>>"
+ "\n\t actual:"
+ "\n<<<<\n" + logged + ">>>>\n");
} else {
verbose("Got expected results for "
+ desc + "\n<<<<\n" + logged + ">>>>\n");
}
}
}
}
Throwable thrown = new Exception("OK: log me!");
for (Level loggerLevel : Level.values()) {
provider.setLevel(logger, loggerLevel, caller);
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
sequencer.incrementAndGet();
logger.log(messageLevel, msg, thrown);
if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (!ErrorStream.errorStream.peek().isEmpty()) {
throw new RuntimeException("unexpected event in queue for "
+ desc +": " + "\n\t" + ErrorStream.errorStream.drain());
}
} else {
String logged = ErrorStream.errorStream.drain();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
thrown.printStackTrace(new PrintStream(baos));
String text = baos.toString();
String msgText = loggerBundle == null ? msg : loggerBundle.getString(msg);
if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
|| !logged.contains(messageLevel.getName() + ": " + msgText)
|| !logged.contains(text)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected:" + "\n<<<<\n"
+ "[date] BaseDefaultLoggerFinderTest testLogger\n"
+ messageLevel.getName() + " " + msgText +"\n"
+ text
+ ">>>>"
+ "\n\t actual:"
+ "\n<<<<\n" + logged + ">>>>\n");
} else {
verbose("Got expected results for "
+ desc + "\n<<<<\n" + logged + ">>>>\n");
}
}
}
}
for (Level loggerLevel : Level.values()) {
provider.setLevel(logger, loggerLevel, caller);
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
sequencer.incrementAndGet();
logger.log(messageLevel, fooSupplier, thrown);
if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (!ErrorStream.errorStream.peek().isEmpty()) {
throw new RuntimeException("unexpected event in queue for "
+ desc +": " + "\n\t" + ErrorStream.errorStream.drain());
}
} else {
String logged = ErrorStream.errorStream.drain();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
thrown.printStackTrace(new PrintStream(baos));
String text = baos.toString();
if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
|| !logged.contains(messageLevel.getName() + ": " + fooSupplier.get())
|| !logged.contains(text)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected:" + "\n<<<<\n"
+ "[date] BaseDefaultLoggerFinderTest testLogger\n"
+ messageLevel.getName() + " " + fooSupplier.get() +"\n"
+ text
+ ">>>>"
+ "\n\t actual:"
+ "\n<<<<\n" + logged + ">>>>\n");
} else {
verbose("Got expected results for "
+ desc + "\n<<<<\n" + logged + ">>>>\n");
}
}
}
}
ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
for (Level loggerLevel : Level.values()) {
provider.setLevel(logger, loggerLevel, caller);
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
sequencer.incrementAndGet();
logger.log(messageLevel, bundle, format, foo, msg);
if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (!ErrorStream.errorStream.peek().isEmpty()) {
throw new RuntimeException("unexpected event in queue for "
+ desc +": " + "\n\t" + ErrorStream.errorStream.drain());
}
} else {
String logged = ErrorStream.errorStream.drain();
String text = java.text.MessageFormat.format(bundle.getString(format), foo, msg);
if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
|| !logged.contains(messageLevel.getName() + ": " + text)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected:" + "\n<<<<\n"
+ "[date] BaseDefaultLoggerFinderTest testLogger\n"
+ messageLevel.getName() + " " + text
+ "\n>>>>"
+ "\n\t actual:"
+ "\n<<<<\n" + logged + ">>>>\n");
} else {
verbose("Got expected results for "
+ desc + "\n<<<<\n" + logged + ">>>>\n");
}
}
}
}
for (Level loggerLevel : Level.values()) {
provider.setLevel(logger, loggerLevel, caller);
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
sequencer.incrementAndGet();
logger.log(messageLevel, bundle, msg, thrown);
if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (!ErrorStream.errorStream.peek().isEmpty()) {
throw new RuntimeException("unexpected event in queue for "
+ desc +": " + "\n\t" + ErrorStream.errorStream.drain());
}
} else {
String logged = ErrorStream.errorStream.drain();
String textMsg = bundle.getString(msg);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
thrown.printStackTrace(new PrintStream(baos));
String text = baos.toString();
if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
|| !logged.contains(messageLevel.getName() + ": " + textMsg)
|| !logged.contains(text)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected:" + "\n<<<<\n"
+ "[date] BaseDefaultLoggerFinderTest testLogger\n"
+ messageLevel.getName() + " " + textMsg +"\n"
+ text
+ ">>>>"
+ "\n\t actual:"
+ "\n<<<<\n" + logged + ">>>>\n");
} else {
verbose("Got expected results for "
+ desc + "\n<<<<\n" + logged + ">>>>\n");
}
}
}
}
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import jdk.internal.logger.DefaultLoggerFinder;
import jdk.internal.logger.SimpleConsoleLogger;
import sun.util.logging.PlatformLogger;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.security.AccessController;
import java.security.PrivilegedAction;
public class BaseLoggerFinder extends DefaultLoggerFinder
implements BaseDefaultLoggerFinderTest.TestLoggerFinder {
public BaseLoggerFinder() {
if (fails.get()) {
throw new RuntimeException("Simulate exception while loading provider");
}
}
@Override
public void setLevel(Logger logger, Level level, Module caller) {
PrivilegedAction<Void> pa = () -> {
setLevel(logger, PlatformLogger.toPlatformLevel(level), caller);
return null;
};
AccessController.doPrivileged(pa);
}
@Override
public void setLevel(Logger logger, PlatformLogger.Level level, Module caller) {
PrivilegedAction<Logger> pa = () -> demandLoggerFor(logger.getName(), caller);
Logger impl = AccessController.doPrivileged(pa);
SimpleConsoleLogger.class.cast(impl)
.getLoggerConfiguration()
.setPlatformLevel(level);
}
@Override
public PlatformLogger.Bridge asPlatformLoggerBridge(Logger logger) {
PrivilegedAction<PlatformLogger.Bridge> pa = () ->
PlatformLogger.Bridge.convert(logger);
return AccessController.doPrivileged(pa);
}
}

View file

@ -0,0 +1,127 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.security.AllPermission;
import java.security.Permissions;
import java.security.ProtectionDomain;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* A custom ClassLoader to load the concrete LoggerFinder class
* with all permissions. The CustomSystemClassLoader class must be
* in the BCL, otherwise when system classes - such as
* ZoneDateTime try to load their resource bundle a MissingResourceBundle
* caused by a SecurityException may be thrown, as the CustomSystemClassLoader
* code base will be found in the stack called by doPrivileged.
*
* @author danielfuchs
*/
public class CustomSystemClassLoader extends ClassLoader {
final List<String> finderClassNames =
Arrays.asList("BaseLoggerFinder");
final Map<String, Class<?>> finderClasses = new ConcurrentHashMap<>();
Class<?> testLoggerFinderClass;
public CustomSystemClassLoader() {
super();
}
public CustomSystemClassLoader(ClassLoader parent) {
super(parent);
}
private Class<?> defineFinderClass(String name)
throws ClassNotFoundException {
Class<?> finderClass = finderClasses.get(name);
if (finderClass != null) return finderClass;
final Object obj = getClassLoadingLock(name);
synchronized(obj) {
finderClasses.get(name);
if (finderClass != null) return finderClass;
if (testLoggerFinderClass == null) {
// Hack: we load testLoggerFinderClass to get its code source.
// we can't use this.getClass() since we are in the boot.
testLoggerFinderClass = super.loadClass("BaseDefaultLoggerFinderTest$TestLoggerFinder");
}
URL url = testLoggerFinderClass.getProtectionDomain().getCodeSource().getLocation();
File file = new File(url.getPath(), name+".class");
if (file.canRead()) {
try {
byte[] b = Files.readAllBytes(file.toPath());
Permissions perms = new Permissions();
perms.add(new AllPermission());
finderClass = defineClass(
name, b, 0, b.length, new ProtectionDomain(
this.getClass().getProtectionDomain().getCodeSource(),
perms));
System.out.println("Loaded " + name);
finderClasses.put(name, finderClass);
return finderClass;
} catch (Throwable ex) {
ex.printStackTrace();
throw new ClassNotFoundException(name, ex);
}
} else {
throw new ClassNotFoundException(name,
new IOException(file.toPath() + ": can't read"));
}
}
}
private static boolean matches(String prefix, String name) {
return prefix.equals(name) || name.startsWith(prefix + "$");
}
@Override
public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (finderClassNames.stream().anyMatch(n -> matches(n, name))) {
Class<?> c = defineFinderClass(name);
if (resolve) {
resolveClass(c);
}
return c;
}
return super.loadClass(name, resolve);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (finderClassNames.stream().anyMatch(n -> matches(n, name))) {
return defineFinderClass(name);
}
return super.findClass(name);
}
}

View file

@ -0,0 +1,768 @@
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.ResourceBundle;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
import sun.util.logging.PlatformLogger;
import java.lang.System.LoggerFinder;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
/**
* @test
* @bug 8140364
* @summary JDK implementation specific unit test for JDK internal artifacts.
* Tests a naive implementation of System.Logger, and in particular
* the default mapping provided by PlatformLogger.Bridge.
* @modules java.base/sun.util.logging java.base/jdk.internal.logger
* @build CustomSystemClassLoader BaseLoggerFinder BaseLoggerBridgeTest
* @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerBridgeTest
*/
public class BaseLoggerBridgeTest {
static final RuntimePermission LOGGERFINDER_PERMISSION =
new RuntimePermission("loggerFinder");
final static AtomicLong sequencer = new AtomicLong();
final static boolean VERBOSE = false;
// whether the implementation of Logger try to do a best
// effort for logp... Our base logger finder stub doesn't
// support logp, and thus the logp() implementation comes from
// LoggerWrapper - which does a best effort.
static final boolean BEST_EFFORT_FOR_LOGP = true;
static final Class<?> providerClass;
static {
try {
providerClass = ClassLoader.getSystemClassLoader().loadClass("BaseLoggerFinder");
} catch (ClassNotFoundException ex) {
throw new ExceptionInInitializerError(ex);
}
}
static final sun.util.logging.PlatformLogger.Level[] julLevels = {
sun.util.logging.PlatformLogger.Level.ALL,
sun.util.logging.PlatformLogger.Level.FINEST,
sun.util.logging.PlatformLogger.Level.FINER,
sun.util.logging.PlatformLogger.Level.FINE,
sun.util.logging.PlatformLogger.Level.CONFIG,
sun.util.logging.PlatformLogger.Level.INFO,
sun.util.logging.PlatformLogger.Level.WARNING,
sun.util.logging.PlatformLogger.Level.SEVERE,
sun.util.logging.PlatformLogger.Level.OFF,
};
static final Level[] mappedLevels = {
Level.ALL, // ALL
Level.TRACE, // FINEST
Level.TRACE, // FINER
Level.DEBUG, // FINE
Level.DEBUG, // CONFIG
Level.INFO, // INFO
Level.WARNING, // WARNING
Level.ERROR, // SEVERE
Level.OFF, // OFF
};
final static Map<sun.util.logging.PlatformLogger.Level, Level> julToSpiMap;
static {
Map<sun.util.logging.PlatformLogger.Level, Level> map = new HashMap<>();
if (mappedLevels.length != julLevels.length) {
throw new ExceptionInInitializerError("Array lengths differ"
+ "\n\tjulLevels=" + Arrays.deepToString(julLevels)
+ "\n\tmappedLevels=" + Arrays.deepToString(mappedLevels));
}
for (int i=0; i<julLevels.length; i++) {
map.put(julLevels[i], mappedLevels[i]);
}
julToSpiMap = Collections.unmodifiableMap(map);
}
public static class MyBundle extends ResourceBundle {
final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
@Override
protected Object handleGetObject(String key) {
if (key.contains(" (translated)")) {
throw new RuntimeException("Unexpected key: " + key);
}
return map.computeIfAbsent(key, k -> k + " (translated)");
}
@Override
public Enumeration<String> getKeys() {
return Collections.enumeration(map.keySet());
}
}
public static class MyLoggerBundle extends MyBundle {
}
public static interface TestLoggerFinder {
final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>();
final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>();
public Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
public static final class LogEvent implements Cloneable {
public LogEvent() {
this(sequencer.getAndIncrement());
}
LogEvent(long sequenceNumber) {
this.sequenceNumber = sequenceNumber;
}
boolean callSupplier = false;
long sequenceNumber;
boolean isLoggable;
String loggerName;
Level level;
ResourceBundle bundle;
Throwable thrown;
Object[] args;
Supplier<String> supplier;
String msg;
Object[] toArray(boolean callSupplier) {
return new Object[] {
sequenceNumber,
isLoggable,
loggerName,
level,
bundle,
thrown,
args,
callSupplier && supplier != null ? supplier.get() : supplier,
msg,
};
}
boolean callSupplier(Object obj) {
return callSupplier || ((LogEvent)obj).callSupplier;
}
@Override
public String toString() {
return Arrays.deepToString(toArray(false));
}
@Override
public boolean equals(Object obj) {
return obj instanceof LogEvent
&& Objects.deepEquals(toArray(callSupplier(obj)), ((LogEvent)obj).toArray(callSupplier(obj)));
}
@Override
public int hashCode() {
return Objects.hash(toArray(true));
}
public LogEvent cloneWith(long sequenceNumber)
throws CloneNotSupportedException {
LogEvent cloned = (LogEvent)super.clone();
cloned.sequenceNumber = sequenceNumber;
return cloned;
}
public static LogEvent of(boolean isLoggable, String name,
Level level, ResourceBundle bundle,
String key, Throwable thrown) {
LogEvent evt = new LogEvent();
evt.isLoggable = isLoggable;
evt.loggerName = name;
evt.level = level;
evt.args = null;
evt.bundle = bundle;
evt.thrown = thrown;
evt.supplier = null;
evt.msg = key;
return evt;
}
public static LogEvent of(boolean isLoggable, String name,
Level level, Throwable thrown, Supplier<String> supplier) {
LogEvent evt = new LogEvent();
evt.isLoggable = isLoggable;
evt.loggerName = name;
evt.level = level;
evt.args = null;
evt.bundle = null;
evt.thrown = thrown;
evt.supplier = supplier;
evt.msg = null;
return evt;
}
public static LogEvent of(boolean isLoggable, String name,
Level level, ResourceBundle bundle,
String key, Object... params) {
LogEvent evt = new LogEvent();
evt.isLoggable = isLoggable;
evt.loggerName = name;
evt.level = level;
evt.args = params;
evt.bundle = bundle;
evt.thrown = null;
evt.supplier = null;
evt.msg = key;
return evt;
}
public static LogEvent of(long sequenceNumber,
boolean isLoggable, String name,
Level level, ResourceBundle bundle,
String key, Supplier<String> supplier,
Throwable thrown, Object... params) {
LogEvent evt = new LogEvent(sequenceNumber);
evt.loggerName = name;
evt.level = level;
evt.args = params;
evt.bundle = bundle;
evt.thrown = thrown;
evt.supplier = supplier;
evt.msg = key;
evt.isLoggable = isLoggable;
return evt;
}
public static LogEvent ofp(boolean callSupplier, LogEvent evt) {
evt.callSupplier = callSupplier;
return evt;
}
}
public class LoggerImpl implements Logger {
private final String name;
private Level level = Level.INFO;
public LoggerImpl(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public boolean isLoggable(Level level) {
return this.level != Level.OFF && this.level.getSeverity() <= level.getSeverity();
}
@Override
public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {
log(LogEvent.of(isLoggable(level), this.name, level, bundle, key, thrown));
}
@Override
public void log(Level level, ResourceBundle bundle, String format, Object... params) {
log(LogEvent.of(isLoggable(level), name, level, bundle, format, params));
}
void log(LogEvent event) {
eventQueue.add(event);
}
@Override
public void log(Level level, Supplier<String> msgSupplier) {
log(LogEvent.of(isLoggable(level), name, level, null, msgSupplier));
}
@Override
public void log(Level level, Supplier<String> msgSupplier, Throwable thrown) {
log(LogEvent.of(isLoggable(level), name, level, thrown, msgSupplier));
}
}
public Logger getLogger(String name, Module caller);
public Logger getLocalizedLogger(String name, ResourceBundle bundle, Module caller);
}
static PlatformLogger.Bridge convert(Logger logger) {
return PlatformLogger.Bridge.convert(logger);
}
static Logger getLogger(String name, Module caller) {
return jdk.internal.logger.LazyLoggers.getLogger(name, caller);
}
public static void main(String[] args) {
System.out.println("\n*** Starting test\n");
TestLoggerFinder provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
test(provider);
System.out.println("Tetscase count: " + sequencer.get());
System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
}
public static void test(TestLoggerFinder provider) {
ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
final Map<Object, String> loggerDescMap = new HashMap<>();
TestLoggerFinder.LoggerImpl appSink = TestLoggerFinder.LoggerImpl.class.cast(
provider.getLogger("foo", BaseLoggerBridgeTest.class.getModule()));
TestLoggerFinder.LoggerImpl sysSink = TestLoggerFinder.LoggerImpl.class.cast(
provider.getLogger("foo", Thread.class.getModule()));
if (provider.system.contains(appSink)) {
throw new RuntimeException("app logger in system map");
}
if (!provider.user.contains(appSink)) {
throw new RuntimeException("app logger not in appplication map");
}
if (provider.user.contains(sysSink)) {
throw new RuntimeException("sys logger in appplication map");
}
if (!provider.system.contains(sysSink)) {
throw new RuntimeException("sys logger not in system map");
}
Logger appLogger1 = System.getLogger("foo");
loggerDescMap.put(appLogger1, "System.getLogger(\"foo\")");
PlatformLogger.Bridge bridge = convert(appLogger1);
loggerDescMap.putIfAbsent(bridge, "PlatformLogger.Bridge.convert(System.getLogger(\"foo\"))");
testLogger(provider, loggerDescMap, "foo", null, bridge, appSink);
Logger sysLogger1 = getLogger("foo", Thread.class.getModule());
loggerDescMap.put(sysLogger1,
"jdk.internal.logger.LazyLoggers.getLogger(\"foo\", Thread.class.getModule())");
testLogger(provider, loggerDescMap, "foo", null,
PlatformLogger.Bridge.convert(sysLogger1), sysSink);
Logger appLogger2 =
System.getLogger("foo", loggerBundle);
loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)");
if (appLogger2 == appLogger1) {
throw new RuntimeException("identical loggers");
}
if (provider.system.contains(appLogger2)) {
throw new RuntimeException("localized app logger in system map");
}
if (provider.user.contains(appLogger2)) {
throw new RuntimeException("localized app logger in appplication map");
}
testLogger(provider, loggerDescMap, "foo", loggerBundle,
PlatformLogger.Bridge.convert(appLogger2), appSink);
Logger sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class.getModule());
loggerDescMap.put(sysLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, Thread.class.getModule())");
if (appLogger2 == sysLogger2) {
throw new RuntimeException("identical loggers");
}
if (sysLogger2 == sysLogger1) {
throw new RuntimeException("identical loggers");
}
if (provider.user.contains(sysLogger2)) {
throw new RuntimeException("localized sys logger in appplication map");
}
if (provider.system.contains(sysLogger2)) {
throw new RuntimeException("localized sys logger not in system map");
}
testLogger(provider, loggerDescMap, "foo", loggerBundle,
PlatformLogger.Bridge.convert(sysLogger2), sysSink);
}
public static class Foo {
}
static void verbose(String msg) {
if (VERBOSE) {
System.out.println(msg);
}
}
static void checkLogEvent(TestLoggerFinder provider, String desc,
TestLoggerFinder.LogEvent expected) {
TestLoggerFinder.LogEvent actual = provider.eventQueue.poll();
if (!Objects.equals(expected, actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
static void checkLogEvent(TestLoggerFinder provider, String desc,
TestLoggerFinder.LogEvent expected, boolean expectNotNull) {
TestLoggerFinder.LogEvent actual = provider.eventQueue.poll();
if (actual == null && !expectNotNull) return;
if (actual != null && !expectNotNull) {
throw new RuntimeException("Unexpected log event found for " + desc
+ "\n\tgot: " + actual);
}
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
static Supplier<String> logpMessage(ResourceBundle bundle,
String className, String methodName, Supplier<String> msg) {
if (BEST_EFFORT_FOR_LOGP && bundle == null
&& (className != null || methodName != null)) {
final String cName = className == null ? "" : className;
final String mName = methodName == null ? "" : methodName;
return () -> String.format("[%s %s] %s", cName, mName, msg.get());
} else {
return msg;
}
}
static String logpMessage(ResourceBundle bundle,
String className, String methodName, String msg) {
if (BEST_EFFORT_FOR_LOGP && bundle == null
&& (className != null || methodName != null)) {
final String cName = className == null ? "" : className;
final String mName = methodName == null ? "" : methodName;
return String.format("[%s %s] %s", cName, mName, msg == null ? "" : msg);
} else {
return msg;
}
}
// Calls the methods defined on LogProducer and verify the
// parameters received by the underlying TestLoggerFinder.LoggerImpl
// logger.
private static void testLogger(TestLoggerFinder provider,
Map<Object, String> loggerDescMap,
String name,
ResourceBundle loggerBundle,
PlatformLogger.Bridge logger,
TestLoggerFinder.LoggerImpl sink) {
if (loggerDescMap.get(logger) == null) {
throw new RuntimeException("Test bug: Missing description");
}
System.out.println("Testing " + loggerDescMap.get(logger) +" [" + logger + "]");
Foo foo = new Foo();
String fooMsg = foo.toString();
System.out.println("\tlogger.log(messageLevel, fooMsg)");
System.out.println("\tlogger.<level>(fooMsg)");
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.log(messageLevel, fooMsg): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
Level expectedMessageLevel = julToSpiMap.get(messageLevel);
TestLoggerFinder.LogEvent expected =
TestLoggerFinder.LogEvent.of(
sequencer.get(),
loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
name, expectedMessageLevel, loggerBundle,
fooMsg, null, (Throwable)null, (Object[])null);
logger.log(messageLevel, fooMsg);
checkLogEvent(provider, desc, expected);
}
}
Supplier<String> supplier = new Supplier<String>() {
@Override
public String get() {
return this.toString();
}
};
System.out.println("\tlogger.log(messageLevel, supplier)");
System.out.println("\tlogger.<level>(supplier)");
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.log(messageLevel, supplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
Level expectedMessageLevel = julToSpiMap.get(messageLevel);
TestLoggerFinder.LogEvent expected =
TestLoggerFinder.LogEvent.of(
sequencer.get(),
loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
name, expectedMessageLevel, (ResourceBundle) null,
null, supplier, (Throwable)null, (Object[])null);
logger.log(messageLevel, supplier);
checkLogEvent(provider, desc, expected);
}
}
String format = "two params [{1} {2}]";
Object arg1 = foo;
Object arg2 = fooMsg;
System.out.println("\tlogger.log(messageLevel, format, arg1, arg2)");
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.log(messageLevel, format, foo, fooMsg): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
Level expectedMessageLevel = julToSpiMap.get(messageLevel);
TestLoggerFinder.LogEvent expected =
TestLoggerFinder.LogEvent.of(
sequencer.get(),
loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
name, expectedMessageLevel, loggerBundle,
format, null, (Throwable)null, arg1, arg2);
logger.log(messageLevel, format, arg1, arg2);
checkLogEvent(provider, desc, expected);
}
}
Throwable thrown = new Exception("OK: log me!");
System.out.println("\tlogger.log(messageLevel, fooMsg, thrown)");
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.log(messageLevel, fooMsg, thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
Level expectedMessageLevel = julToSpiMap.get(messageLevel);
TestLoggerFinder.LogEvent expected =
TestLoggerFinder.LogEvent.of(
sequencer.get(),
loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
name, expectedMessageLevel, loggerBundle,
fooMsg, null, thrown, (Object[])null);
logger.log(messageLevel, fooMsg, thrown);
checkLogEvent(provider, desc, expected);
}
}
System.out.println("\tlogger.log(messageLevel, thrown, supplier)");
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.log(messageLevel, thrown, supplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
Level expectedMessageLevel = julToSpiMap.get(messageLevel);
TestLoggerFinder.LogEvent expected =
TestLoggerFinder.LogEvent.of(
sequencer.get(),
loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
name, expectedMessageLevel, (ResourceBundle)null,
null, supplier, thrown, (Object[])null);
logger.log(messageLevel, thrown, supplier);
checkLogEvent(provider, desc, expected);
}
}
String sourceClass = "blah.Blah";
String sourceMethod = "blih";
System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg)");
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
Level expectedMessageLevel = julToSpiMap.get(messageLevel);
boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
TestLoggerFinder.LogEvent expected =
isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP?
TestLoggerFinder.LogEvent.of(
sequencer.get(),
isLoggable,
name, expectedMessageLevel, loggerBundle,
logpMessage(loggerBundle, sourceClass, sourceMethod, fooMsg),
null, (Throwable)null, (Object[]) null) : null;
logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg);
checkLogEvent(provider, desc, expected);
}
}
System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, supplier)");
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, supplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
Level expectedMessageLevel = julToSpiMap.get(messageLevel);
boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
TestLoggerFinder.LogEvent expected = isLoggable ?
TestLoggerFinder.LogEvent.ofp(BEST_EFFORT_FOR_LOGP,
TestLoggerFinder.LogEvent.of(
sequencer.get(),
isLoggable,
name, expectedMessageLevel, null, null,
logpMessage(null, sourceClass, sourceMethod, supplier),
(Throwable)null, (Object[]) null)) : null;
logger.logp(messageLevel, sourceClass, sourceMethod, supplier);
checkLogEvent(provider, desc, expected);
}
}
System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2)");
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
Level expectedMessageLevel = julToSpiMap.get(messageLevel);
boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
TestLoggerFinder.LogEvent expected =
isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP?
TestLoggerFinder.LogEvent.of(
sequencer.get(),
loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
name, expectedMessageLevel, loggerBundle,
logpMessage(loggerBundle, sourceClass, sourceMethod, format),
null, (Throwable)null, arg1, arg2) : null;
logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2);
checkLogEvent(provider, desc, expected);
}
}
System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown)");
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
Level expectedMessageLevel = julToSpiMap.get(messageLevel);
boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
TestLoggerFinder.LogEvent expected =
isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP ?
TestLoggerFinder.LogEvent.of(
sequencer.get(),
loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
name, expectedMessageLevel, loggerBundle,
logpMessage(loggerBundle, sourceClass, sourceMethod, fooMsg),
null, thrown, (Object[])null) : null;
logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown);
checkLogEvent(provider, desc, expected);
}
}
System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier)");
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
Level expectedMessageLevel = julToSpiMap.get(messageLevel);
boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
TestLoggerFinder.LogEvent expected = isLoggable ?
TestLoggerFinder.LogEvent.ofp(BEST_EFFORT_FOR_LOGP,
TestLoggerFinder.LogEvent.of(
sequencer.get(),
loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
name, expectedMessageLevel, null, null,
logpMessage(null, sourceClass, sourceMethod, supplier),
thrown, (Object[])null)) : null;
logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier);
checkLogEvent(provider, desc, expected);
}
}
ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
System.out.println("\tlogger.logrb(messageLevel, bundle, format, arg1, arg2)");
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.logrb(messageLevel, bundle, format, arg1, arg2): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
Level expectedMessageLevel = julToSpiMap.get(messageLevel);
TestLoggerFinder.LogEvent expected =
TestLoggerFinder.LogEvent.of(
sequencer.get(),
loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
name, expectedMessageLevel, bundle,
format, null, (Throwable)null, arg1, arg2);
logger.logrb(messageLevel, bundle, format, arg1, arg2);
checkLogEvent(provider, desc, expected);
}
}
System.out.println("\tlogger.logrb(messageLevel, bundle, msg, thrown)");
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.logrb(messageLevel, bundle, msg, thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
Level expectedMessageLevel = julToSpiMap.get(messageLevel);
TestLoggerFinder.LogEvent expected =
TestLoggerFinder.LogEvent.of(
sequencer.get(),
loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
name, expectedMessageLevel, bundle,
fooMsg, null, thrown, (Object[])null);
logger.logrb(messageLevel, bundle, fooMsg, thrown);
checkLogEvent(provider, desc, expected);
}
}
System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2)");
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
Level expectedMessageLevel = julToSpiMap.get(messageLevel);
TestLoggerFinder.LogEvent expected =
TestLoggerFinder.LogEvent.of(
sequencer.get(),
loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
name, expectedMessageLevel, bundle,
format, null, (Throwable)null, arg1, arg2);
logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2);
checkLogEvent(provider, desc, expected);
}
}
System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown)");
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
Level expectedMessageLevel = julToSpiMap.get(messageLevel);
TestLoggerFinder.LogEvent expected =
TestLoggerFinder.LogEvent.of(
sequencer.get(),
loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
name, expectedMessageLevel, bundle,
fooMsg, null, thrown, (Object[])null);
logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, fooMsg, thrown);
checkLogEvent(provider, desc, expected);
}
}
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.lang.System.LoggerFinder;
import java.lang.System.Logger;
public class BaseLoggerFinder extends LoggerFinder
implements BaseLoggerBridgeTest.TestLoggerFinder {
static final RuntimePermission LOGGERFINDER_PERMISSION =
new RuntimePermission("loggerFinder");
@Override
public Logger getLogger(String name, Module caller) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(LOGGERFINDER_PERMISSION);
}
PrivilegedAction<ClassLoader> pa = () -> caller.getClassLoader();
ClassLoader callerLoader = AccessController.doPrivileged(pa);
if (callerLoader == null) {
return system.computeIfAbsent(name, (n) -> new LoggerImpl(n));
} else {
return user.computeIfAbsent(name, (n) -> new LoggerImpl(n));
}
}
}

View file

@ -0,0 +1,107 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.security.AllPermission;
import java.security.Permissions;
import java.security.ProtectionDomain;
import java.util.concurrent.ConcurrentHashMap;
/**
* A custom ClassLoader to load the concrete LoggerFinder class
* with all permissions.
*
* @author danielfuchs
*/
public class CustomSystemClassLoader extends ClassLoader {
private final ConcurrentHashMap<String, Class<?>> classes = new ConcurrentHashMap<>();
public CustomSystemClassLoader() {
super();
}
public CustomSystemClassLoader(ClassLoader parent) {
super(parent);
}
private Class<?> defineFinderClass(String name)
throws ClassNotFoundException {
Class<?> finderClass = classes.get(name);
if (finderClass != null) return finderClass;
final Object obj = getClassLoadingLock(name);
synchronized(obj) {
finderClass = classes.get(name);
if (finderClass != null) return finderClass;
URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation();
File file = new File(url.getPath(), name+".class");
if (file.canRead()) {
try {
byte[] b = Files.readAllBytes(file.toPath());
Permissions perms = new Permissions();
perms.add(new AllPermission());
finderClass = defineClass(
name, b, 0, b.length, new ProtectionDomain(
this.getClass().getProtectionDomain().getCodeSource(),
perms));
System.out.println("Loaded " + name);
classes.put(name, finderClass);
return finderClass;
} catch (Throwable ex) {
ex.printStackTrace();
throw new ClassNotFoundException(name, ex);
}
} else {
throw new ClassNotFoundException(name,
new IOException(file.toPath() + ": can't read"));
}
}
}
@Override
public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (name.equals("BaseLoggerFinder") || name.startsWith("BaseLoggerFinder$")) {
Class<?> c = defineFinderClass(name);
if (resolve) {
resolveClass(c);
}
return c;
}
return super.loadClass(name, resolve);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (name.equals("BaseLoggerFinder") || name.startsWith("BaseLoggerFinder$")) {
return defineFinderClass(name);
}
return super.findClass(name);
}
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.lang.System.Logger;
import java.lang.System.LoggerFinder;
import java.security.AccessController;
import java.security.PrivilegedAction;
public class BaseLoggerFinder extends LoggerFinder
implements BasePlatformLoggerTest.TestLoggerFinder {
public static final RuntimePermission LOGGERFINDER_PERMISSION =
new RuntimePermission("loggerFinder");
@Override
public Logger getLogger(String name, Module caller) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(LOGGERFINDER_PERMISSION);
}
PrivilegedAction<ClassLoader> pa = () -> caller.getClassLoader();
ClassLoader callerLoader = AccessController.doPrivileged(pa);
if (callerLoader == null) {
return system.computeIfAbsent(name, (n) -> new LoggerImpl(n));
} else {
return user.computeIfAbsent(name, (n) -> new LoggerImpl(n));
}
}
}

View file

@ -0,0 +1,520 @@
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.ResourceBundle;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
import java.lang.System.LoggerFinder;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import sun.util.logging.PlatformLogger;
/**
* @test
* @bug 8140364
* @summary JDK implementation specific unit test for JDK internal API.
* Tests a naive implementation of System.Logger, and in particular
* the default mapping provided by PlatformLogger.
* @modules java.base/sun.util.logging
* @build CustomSystemClassLoader BaseLoggerFinder BasePlatformLoggerTest
* @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader BasePlatformLoggerTest
*/
public class BasePlatformLoggerTest {
public static final RuntimePermission LOGGERFINDER_PERMISSION =
new RuntimePermission("loggerFinder");
final static AtomicLong sequencer = new AtomicLong();
final static boolean VERBOSE = false;
static final Class<?> providerClass;
static {
try {
providerClass = ClassLoader.getSystemClassLoader().loadClass("BaseLoggerFinder");
} catch (ClassNotFoundException ex) {
throw new ExceptionInInitializerError(ex);
}
}
static final PlatformLogger.Level[] julLevels = {
PlatformLogger.Level.ALL,
PlatformLogger.Level.FINEST,
PlatformLogger.Level.FINER,
PlatformLogger.Level.FINE,
PlatformLogger.Level.CONFIG,
PlatformLogger.Level.INFO,
PlatformLogger.Level.WARNING,
PlatformLogger.Level.SEVERE,
PlatformLogger.Level.OFF,
};
static final Level[] mappedLevels = {
Level.ALL, // ALL
Level.TRACE, // FINEST
Level.TRACE, // FINER
Level.DEBUG, // FINE
Level.DEBUG, // CONFIG
Level.INFO, // INFO
Level.WARNING, // WARNING
Level.ERROR, // SEVERE
Level.OFF, // OFF
};
final static Map<PlatformLogger.Level, Level> julToSpiMap;
static {
Map<PlatformLogger.Level, Level> map = new HashMap<>();
if (mappedLevels.length != julLevels.length) {
throw new ExceptionInInitializerError("Array lengths differ"
+ "\n\tjulLevels=" + Arrays.deepToString(julLevels)
+ "\n\tmappedLevels=" + Arrays.deepToString(mappedLevels));
}
for (int i=0; i<julLevels.length; i++) {
map.put(julLevels[i], mappedLevels[i]);
}
julToSpiMap = Collections.unmodifiableMap(map);
}
public static class MyBundle extends ResourceBundle {
final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
@Override
protected Object handleGetObject(String key) {
if (key.contains(" (translated)")) {
throw new RuntimeException("Unexpected key: " + key);
}
return map.computeIfAbsent(key, k -> k + " (translated)");
}
@Override
public Enumeration<String> getKeys() {
return Collections.enumeration(map.keySet());
}
}
public static class MyLoggerBundle extends MyBundle {
}
public static interface TestLoggerFinder {
final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>();
final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>();
public Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
public static final class LogEvent implements Cloneable {
public LogEvent() {
this(sequencer.getAndIncrement());
}
LogEvent(long sequenceNumber) {
this.sequenceNumber = sequenceNumber;
}
long sequenceNumber;
boolean isLoggable;
String loggerName;
Level level;
ResourceBundle bundle;
Throwable thrown;
Object[] args;
Supplier<String> supplier;
String msg;
Object[] toArray() {
return new Object[] {
sequenceNumber,
isLoggable,
loggerName,
level,
bundle,
thrown,
args,
supplier,
msg,
};
}
@Override
public String toString() {
return Arrays.deepToString(toArray());
}
@Override
public boolean equals(Object obj) {
return obj instanceof LogEvent
&& Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
}
@Override
public int hashCode() {
return Objects.hash(toArray());
}
public LogEvent cloneWith(long sequenceNumber)
throws CloneNotSupportedException {
LogEvent cloned = (LogEvent)super.clone();
cloned.sequenceNumber = sequenceNumber;
return cloned;
}
public static LogEvent of(boolean isLoggable, String name,
Level level, ResourceBundle bundle,
String key, Throwable thrown) {
LogEvent evt = new LogEvent();
evt.isLoggable = isLoggable;
evt.loggerName = name;
evt.level = level;
evt.args = null;
evt.bundle = bundle;
evt.thrown = thrown;
evt.supplier = null;
evt.msg = key;
return evt;
}
public static LogEvent of(boolean isLoggable, String name,
Level level, Throwable thrown, Supplier<String> supplier) {
LogEvent evt = new LogEvent();
evt.isLoggable = isLoggable;
evt.loggerName = name;
evt.level = level;
evt.args = null;
evt.bundle = null;
evt.thrown = thrown;
evt.supplier = supplier;
evt.msg = null;
return evt;
}
public static LogEvent of(boolean isLoggable, String name,
Level level, ResourceBundle bundle,
String key, Object... params) {
LogEvent evt = new LogEvent();
evt.isLoggable = isLoggable;
evt.loggerName = name;
evt.level = level;
evt.args = params;
evt.bundle = bundle;
evt.thrown = null;
evt.supplier = null;
evt.msg = key;
return evt;
}
public static LogEvent of(long sequenceNumber,
boolean isLoggable, String name,
Level level, ResourceBundle bundle,
String key, Supplier<String> supplier,
Throwable thrown, Object... params) {
LogEvent evt = new LogEvent(sequenceNumber);
evt.loggerName = name;
evt.level = level;
evt.args = params;
evt.bundle = bundle;
evt.thrown = thrown;
evt.supplier = supplier;
evt.msg = key;
evt.isLoggable = isLoggable;
return evt;
}
}
public class LoggerImpl implements Logger {
private final String name;
private Level level = Level.INFO;
public LoggerImpl(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public boolean isLoggable(Level level) {
return this.level != Level.OFF && this.level.getSeverity() <= level.getSeverity();
}
@Override
public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {
log(LogEvent.of(isLoggable(level), this.name, level, bundle, key, thrown));
}
@Override
public void log(Level level, ResourceBundle bundle, String format, Object... params) {
log(LogEvent.of(isLoggable(level), name, level, bundle, format, params));
}
void log(LogEvent event) {
eventQueue.add(event);
}
@Override
public void log(Level level, Supplier<String> msgSupplier) {
log(LogEvent.of(isLoggable(level), name, level, null, msgSupplier));
}
@Override
public void log(Level level, Supplier<String> msgSupplier, Throwable thrown) {
log(LogEvent.of(isLoggable(level), name, level, thrown, msgSupplier));
}
}
public Logger getLogger(String name, Module caller);
}
static PlatformLogger getPlatformLogger(String name) {
return PlatformLogger.getLogger(name);
}
public static void main(String[] args) {
System.out.println("\n*** Running test\n");
TestLoggerFinder provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
test(provider);
System.out.println("Tetscase count: " + sequencer.get());
System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
}
public static void test(TestLoggerFinder provider) {
final Map<PlatformLogger, String> loggerDescMap = new HashMap<>();
TestLoggerFinder.LoggerImpl appSink = TestLoggerFinder.LoggerImpl.class.cast(
provider.getLogger("foo", BasePlatformLoggerTest.class.getModule()));
TestLoggerFinder.LoggerImpl sysSink = TestLoggerFinder.LoggerImpl.class.cast(
provider.getLogger("foo", Thread.class.getModule()));
if (appSink == sysSink) {
throw new RuntimeException("identical loggers");
}
if (provider.system.contains(appSink)) {
throw new RuntimeException("app logger in system map");
}
if (!provider.user.contains(appSink)) {
throw new RuntimeException("app logger not in appplication map");
}
if (provider.user.contains(sysSink)) {
throw new RuntimeException("sys logger in appplication map");
}
if (!provider.system.contains(sysSink)) {
throw new RuntimeException("sys logger not in system map");
}
PlatformLogger platform = getPlatformLogger("foo");
loggerDescMap.put(platform, "PlatformLogger.getLogger(\"foo\")");
testLogger(provider, loggerDescMap, "foo", null, platform, sysSink);
}
public static class Foo {
}
static void verbose(String msg) {
if (VERBOSE) {
System.out.println(msg);
}
}
static void checkLogEvent(TestLoggerFinder provider, String desc,
TestLoggerFinder.LogEvent expected) {
TestLoggerFinder.LogEvent actual = provider.eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
static void checkLogEvent(TestLoggerFinder provider, String desc,
TestLoggerFinder.LogEvent expected, boolean expectNotNull) {
TestLoggerFinder.LogEvent actual = provider.eventQueue.poll();
if (actual == null && !expectNotNull) return;
if (actual != null && !expectNotNull) {
throw new RuntimeException("Unexpected log event found for " + desc
+ "\n\tgot: " + actual);
}
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
// Calls the methods defined on LogProducer and verify the
// parameters received by the underlying TestLoggerFinder.LoggerImpl
// logger.
private static void testLogger(TestLoggerFinder provider,
Map<PlatformLogger, String> loggerDescMap,
String name,
ResourceBundle loggerBundle,
PlatformLogger logger,
TestLoggerFinder.LoggerImpl sink) {
System.out.println("Testing " + loggerDescMap.get(logger));
Foo foo = new Foo();
String fooMsg = foo.toString();
System.out.println("\tlogger.<level>(fooMsg)");
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (PlatformLogger.Level messageLevel :julLevels) {
Level expectedMessageLevel = julToSpiMap.get(messageLevel);
TestLoggerFinder.LogEvent expected =
TestLoggerFinder.LogEvent.of(
sequencer.get(),
loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
name, expectedMessageLevel, loggerBundle,
fooMsg, null, (Throwable)null, (Object[])null);
String desc2 = "logger." + messageLevel.toString().toLowerCase()
+ "(fooMsg): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
if (messageLevel == PlatformLogger.Level.FINEST) {
logger.finest(fooMsg);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == PlatformLogger.Level.FINER) {
logger.finer(fooMsg);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == PlatformLogger.Level.FINE) {
logger.fine(fooMsg);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == PlatformLogger.Level.CONFIG) {
logger.config(fooMsg);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == PlatformLogger.Level.INFO) {
logger.info(fooMsg);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == PlatformLogger.Level.WARNING) {
logger.warning(fooMsg);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == PlatformLogger.Level.SEVERE) {
logger.severe(fooMsg);
checkLogEvent(provider, desc2, expected);
}
}
}
Throwable thrown = new Exception("OK: log me!");
System.out.println("\tlogger.<level>(msg, thrown)");
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (PlatformLogger.Level messageLevel :julLevels) {
Level expectedMessageLevel = julToSpiMap.get(messageLevel);
TestLoggerFinder.LogEvent expected =
TestLoggerFinder.LogEvent.of(
sequencer.get(),
loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
name, expectedMessageLevel, (ResourceBundle) null,
fooMsg, null, (Throwable)thrown, (Object[])null);
String desc2 = "logger." + messageLevel.toString().toLowerCase()
+ "(msg, thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
if (messageLevel == PlatformLogger.Level.FINEST) {
logger.finest(fooMsg, thrown);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == PlatformLogger.Level.FINER) {
logger.finer(fooMsg, thrown);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == PlatformLogger.Level.FINE) {
logger.fine(fooMsg, thrown);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == PlatformLogger.Level.CONFIG) {
logger.config(fooMsg, thrown);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == PlatformLogger.Level.INFO) {
logger.info(fooMsg, thrown);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == PlatformLogger.Level.WARNING) {
logger.warning(fooMsg, thrown);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == PlatformLogger.Level.SEVERE) {
logger.severe(fooMsg, thrown);
checkLogEvent(provider, desc2, expected);
}
}
}
String format = "two params [{1} {2}]";
Object arg1 = foo;
Object arg2 = fooMsg;
System.out.println("\tlogger.<level>(format, arg1, arg2)");
for (Level loggerLevel : Level.values()) {
sink.level = loggerLevel;
for (PlatformLogger.Level messageLevel :julLevels) {
Level expectedMessageLevel = julToSpiMap.get(messageLevel);
TestLoggerFinder.LogEvent expected =
TestLoggerFinder.LogEvent.of(
sequencer.get(),
loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
name, expectedMessageLevel, (ResourceBundle) null,
format, null, (Throwable)null, foo, fooMsg);
String desc2 = "logger." + messageLevel.toString().toLowerCase()
+ "(format, foo, fooMsg): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
if (messageLevel == PlatformLogger.Level.FINEST) {
logger.finest(format, foo, fooMsg);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == PlatformLogger.Level.FINER) {
logger.finer(format, foo, fooMsg);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == PlatformLogger.Level.FINE) {
logger.fine(format, foo, fooMsg);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == PlatformLogger.Level.CONFIG) {
logger.config(format, foo, fooMsg);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == PlatformLogger.Level.INFO) {
logger.info(format, foo, fooMsg);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == PlatformLogger.Level.WARNING) {
logger.warning(format, foo, fooMsg);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == PlatformLogger.Level.SEVERE) {
logger.severe(format, foo, fooMsg);
checkLogEvent(provider, desc2, expected);
}
}
}
}
}

View file

@ -0,0 +1,107 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.security.AllPermission;
import java.security.Permissions;
import java.security.ProtectionDomain;
import java.util.concurrent.ConcurrentHashMap;
/**
* A custom ClassLoader to load the concrete LoggerFinder class
* with all permissions.
*
* @author danielfuchs
*/
public class CustomSystemClassLoader extends ClassLoader {
private final ConcurrentHashMap<String, Class<?>> classes = new ConcurrentHashMap<>();
public CustomSystemClassLoader() {
super();
}
public CustomSystemClassLoader(ClassLoader parent) {
super(parent);
}
private Class<?> defineFinderClass(String name)
throws ClassNotFoundException {
Class<?> finderClass = classes.get(name);
if (finderClass != null) return finderClass;
final Object obj = getClassLoadingLock(name);
synchronized(obj) {
finderClass = classes.get(name);
if (finderClass != null) return finderClass;
URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation();
File file = new File(url.getPath(), name+".class");
if (file.canRead()) {
try {
byte[] b = Files.readAllBytes(file.toPath());
Permissions perms = new Permissions();
perms.add(new AllPermission());
finderClass = defineClass(
name, b, 0, b.length, new ProtectionDomain(
this.getClass().getProtectionDomain().getCodeSource(),
perms));
System.out.println("Loaded " + name);
classes.put(name, finderClass);
return finderClass;
} catch (Throwable ex) {
ex.printStackTrace();
throw new ClassNotFoundException(name, ex);
}
} else {
throw new ClassNotFoundException(name,
new IOException(file.toPath() + ": can't read"));
}
}
}
@Override
public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (name.endsWith("BaseLoggerFinder") || name.startsWith("BaseLoggerFinder$")) {
Class<?> c = defineFinderClass(name);
if (resolve) {
resolveClass(c);
}
return c;
}
return super.loadClass(name, resolve);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (name.endsWith("BaseLoggerFinder") || name.startsWith("BaseLoggerFinder$")) {
return defineFinderClass(name);
}
return super.findClass(name);
}
}

View file

@ -0,0 +1,287 @@
/*
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.PrintStream;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.Enumeration;
import java.util.List;
import java.util.ResourceBundle;
import jdk.internal.logger.BootstrapLogger;
import jdk.internal.logger.LazyLoggers;
/*
* @test
* @bug 8144460 8144214
* @summary Cover the logXX and LogEvent.valueOf APIs of BootstrapLogger
* and logXX APIs of SimpleConsoleLogger.
* @library ../../lib
* @modules java.base/jdk.internal.logger:+open
* java.base/sun.util.logging
* @build LogStream
* @run main/othervm BootstrapLoggerAPIsTest
*/
public class BootstrapLoggerAPIsTest {
private static final LogStream ERR = new LogStream();
public static void main(String[] args) throws Exception {
final ContentManager MGR = new ContentManager();
// private reflection hook that allows us to simulate a non booted VM
final AtomicBoolean VM_BOOTED = new AtomicBoolean(false);
BootstrapLoggerUtils.setBootedHook(() -> VM_BOOTED.get());
// We replace System.err to check the messages that have been logged
// by the JUL ConsoleHandler and default SimpleConsoleLogger
// implementaion
System.setErr(new PrintStream(ERR));
VM_BOOTED.getAndSet(false);
if (BootstrapLogger.isBooted()) {
throw new RuntimeException("VM should not be booted!");
}
final Logger LOGGER =
LazyLoggers.getLogger("foo.bar", Thread.class.getModule());
final sun.util.logging.PlatformLogger.Level PLATFORM_LEVEL =
sun.util.logging.PlatformLogger.Level.SEVERE;
final MyResources BUNDLE = new MyResources();
/*
* Test logXX APIs for interface java.lang.System.Logger. Log content
* before VM is booted should be retained. Log content after VM was
* booted should be flushed instantly. VM is not booted in first round
* of loop, VM is booted in second round of loop.
*/
for (int i = 0; i < 2; i++) {
boolean booted = BootstrapLogger.isBooted();
// make sure there is no [remaining] content in the LogStream.
MGR.failLog("xyz", "throwable #", "MyClass_#", "MyMethod_#");
/*
* test logXX APIs for interface java.lang.System.Logger.
*/
// void log(java.lang.System$Logger$Level,java.util.ResourceBundle,
// java.lang.String,java.lang.Throwable)
LOGGER.log(Level.ERROR, BUNDLE, "abc #0", new RuntimeException("throwable #0"));
MGR.checkLog(booted, "xyz #0", "throwable #0");
// void log(java.lang.System$Logger$Level,java.util.ResourceBundle,
// java.lang.String,java.lang.Object[])
LOGGER.log(Level.ERROR, BUNDLE, "abc #1");
MGR.checkLog(booted, "xyz #1");
// void log(java.lang.System$Logger$Level,java.lang.String,java.lang.Object[])
LOGGER.log(Level.ERROR, BUNDLE, "abc {0}", "#2");
MGR.checkLog(booted, "xyz #2");
// void log(java.lang.System$Logger$Level,java.lang.String,java.lang.Throwable)
LOGGER.log(Level.ERROR, "xyz #3", new RuntimeException("throwable #3"));
MGR.checkLog(booted, "xyz #3", "throwable #3");
// void log(java.lang.System$Logger$Level,java.util.function.Supplier)
LOGGER.log(Level.ERROR, () -> "xyz #4");
MGR.checkLog(booted, "xyz #4");
// void log(java.lang.System$Logger$Level,java.lang.Object)
LOGGER.log(Level.ERROR, new MyObject("xyz #5"));
MGR.checkLog(booted, "xyz #5");
// void log(java.lang.System$Logger$Level,java.util.function.Supplier,
// java.lang.Throwable)
LOGGER.log(Level.ERROR, () -> "xyz #6", new RuntimeException("throwable #6"));
MGR.checkLog(booted, "xyz #6", "throwable #6");
/*
* test logXX APIs for interface
* sun.util.logging.PlatformLogger.Bridge.
*/
sun.util.logging.PlatformLogger.Bridge bridge =
(sun.util.logging.PlatformLogger.Bridge) LOGGER;
// void log(sun.util.logging.PlatformLogger$Level,java.lang.String)
bridge.log(PLATFORM_LEVEL, "xyz #7");
MGR.checkLog(booted, "xyz #7");
// void log(sun.util.logging.PlatformLogger$Level,java.lang.String,java.lang.Throwable)
bridge.log(PLATFORM_LEVEL, "xyz #8", new RuntimeException("throwable #8"));
MGR.checkLog(booted, "xyz #8", "throwable #8");
// void log(sun.util.logging.PlatformLogger$Level,java.lang.String,java.lang.Object[])
bridge.log(PLATFORM_LEVEL, "xyz {0}", "#9");
MGR.checkLog(booted, "xyz #9");
// void log(sun.util.logging.PlatformLogger$Level,java.util.function.Supplier)
bridge.log(PLATFORM_LEVEL, () -> "xyz #10");
MGR.checkLog(booted, "xyz #10");
// void log(sun.util.logging.PlatformLogger$Level,
// java.lang.Throwable,java.util.function.Supplier)
bridge.log(PLATFORM_LEVEL, new RuntimeException("throwable #11"), () -> "xyz #11");
MGR.checkLog(booted, "xyz #11", "throwable #11");
// void logp(sun.util.logging.PlatformLogger$Level,java.lang.String,
// java.lang.String,java.lang.String)
bridge.logp(PLATFORM_LEVEL, "MyClass_#12", "MyMethod_#12", "xyz #12");
MGR.checkLog(booted, "xyz #12", "MyClass_#12", "MyMethod_#12");
// void logp(sun.util.logging.PlatformLogger$Level,java.lang.String,
// java.lang.String,java.util.function.Supplier)
bridge.logp(PLATFORM_LEVEL, "MyClass_#13", "MyMethod_#13", () -> "xyz #13");
MGR.checkLog(booted, "xyz #13", "MyClass_#13", "MyMethod_#13");
// void logp(sun.util.logging.PlatformLogger$Level,java.lang.String,
// java.lang.String,java.lang.String,java.lang.Object[])
bridge.logp(PLATFORM_LEVEL, "MyClass_#14", "MyMethod_#14", "xyz {0}", "#14");
MGR.checkLog(booted, "xyz #14", "MyClass_#14", "MyMethod_#14");
// void logp(sun.util.logging.PlatformLogger$Level,java.lang.String,
// java.lang.String,java.lang.String,java.lang.Throwable)
bridge.logp(PLATFORM_LEVEL, "MyClass_#15", "MyMethod_#15",
"xyz #15", new RuntimeException("throwable #15"));
MGR.checkLog(booted, "xyz #15", "throwable #15", "MyClass_#15", "MyMethod_#15");
// void logp(sun.util.logging.PlatformLogger$Level,java.lang.String,
// java.lang.String,java.lang.Throwable,java.util.function.Supplier)
bridge.logp(PLATFORM_LEVEL, "MyClass_#16", "MyMethod_#16",
new RuntimeException("throwable #16"), () -> "xyz #16");
MGR.checkLog(booted, "xyz #16", "throwable #16", "MyClass_#16", "MyMethod_#16");
// void logrb(sun.util.logging.PlatformLogger$Level,java.lang.String,java.lang.String,
// java.util.ResourceBundle,java.lang.String,java.lang.Object[])
bridge.logrb(PLATFORM_LEVEL, "MyClass_#17", "MyMethod_#17",
BUNDLE, "abc {0}", "#17");
MGR.checkLog(booted, "xyz #17", "MyClass_#17", "MyMethod_#17");
// void logrb(sun.util.logging.PlatformLogger$Level,java.lang.String,java.lang.String,
// java.util.ResourceBundle,java.lang.String,java.lang.Throwable)
bridge.logrb(PLATFORM_LEVEL, "MyClass_#18", "MyMethod_#18",
BUNDLE, "abc #18", new RuntimeException("throwable #18"));
MGR.checkLog(booted, "xyz #18", "throwable #18", "MyClass_#18", "MyMethod_#18");
// void logrb(sun.util.logging.PlatformLogger$Level,java.util.ResourceBundle,
// java.lang.String,java.lang.Object[])
bridge.logrb(PLATFORM_LEVEL, BUNDLE, "abc {0}", "#19");
MGR.checkLog(booted, "xyz #19");
// void logrb(sun.util.logging.PlatformLogger$Level,java.util.ResourceBundle,
// java.lang.String,java.lang.Throwable)
bridge.logrb(PLATFORM_LEVEL, BUNDLE, "abc #20",
new RuntimeException("throwable #20"));
MGR.checkLog(booted, "xyz #20", "throwable #20");
/*
* retained log content should be flushed after VM is booted.
*/
if (!booted) {
VM_BOOTED.getAndSet(true);
// trigger the flush, make sure to call LOGGER.log(...)
// after VM_BOOTED.getAndSet(true) and before MGR.assertCachedLog()
LOGGER.log(Level.ERROR, "VM was just booted! This log should flush the cached logs.");
MGR.assertCachedLog();
}
}
}
private static class ContentManager {
final List<String[]> cached = new ArrayList<String[]>();
String[] last;
public void cache() {
cached.add(last);
}
public ContentManager failLog(String... nonexistent) {
last = nonexistent;
for (String c : nonexistent) {
if (ERR.drain().contains(c)) {
throw new RuntimeException("Content \"" + nonexistent
+ "\" should not exist in the log!");
}
}
return this;
}
public void assertLog(String... logs) {
String log = ERR.drain();
for (String str : logs) {
if (!log.contains(str)) {
throw new RuntimeException("Content \"" + str + "\" does not exist in the log!");
}
}
}
public void checkLog(boolean booted, String... logs) {
if (!booted) {
failLog(logs).cache();
} else {
assertLog(logs);
}
}
public void assertCachedLog() {
String log = ERR.drain();
for (String[] arr : cached) {
for (String c : arr) {
if (!log.contains(c)) {
throw new RuntimeException("Content \"" + c + "\" does not exist in the log!");
}
}
}
}
}
private static class MyObject {
String str;
public MyObject(String str) {
this.str = str;
}
public String toString() {
return str;
}
}
private static class MyResources extends ResourceBundle {
public Object handleGetObject(String key) {
if (key.contains("abc #") || key.contains("abc {")) {
return key.replaceAll("abc ", "xyz ");
}
return null;
}
public Enumeration<String> getKeys() {
return null;
}
}
}

View file

@ -0,0 +1,360 @@
/*
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.PrintStream;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.internal.logger.BootstrapLogger;
import jdk.internal.logger.LazyLoggers;
/*
* @test
* @bug 8140364 8189291
* @summary JDK implementation specific unit test for JDK internal artifacts.
Tests the behavior of bootstrap loggers (and SimpleConsoleLoggers
* too).
* @library ../../lib
* @modules java.base/jdk.internal.logger:+open
* java.logging
* @build LogStream
* @run main/othervm BootstrapLoggerTest
* @run main/othervm/timeout=120 BootstrapLoggerTest RUN_AND_WAIT
*/
public class BootstrapLoggerTest {
static final Method isAlive;
static final Field logManagerInitialized;
static {
try {
// private reflection hook that allows us to test whether
// the BootstrapExecutor is alive.
isAlive = BootstrapLogger.class
.getDeclaredMethod("isAlive");
isAlive.setAccessible(true);
// private reflection hook that allows us to test whether the LogManager
// has initialized and registered with the BootstrapLogger class
logManagerInitialized = BootstrapLogger.class
.getDeclaredField("logManagerConfigured");
logManagerInitialized.setAccessible(true);
} catch (Exception ex) {
throw new ExceptionInInitializerError(ex);
}
}
static enum TestCase {
DEFAULTS, RUN_AND_WAIT
}
public static void main(String[] args) throws Exception {
Locale savedLocale = Locale.getDefault();
Locale.setDefault(Locale.US);
if (args == null || args.length == 0) {
args = new String[] { TestCase.RUN_AND_WAIT.name() };
}
if (args.length > 1) throw new RuntimeException("Only one argument allowed");
TestCase test = TestCase.valueOf(args[0]);
System.err.println("Testing: " + test);
// private reflection hook that allows us to simulate a non booted VM
final AtomicBoolean vmBooted = new AtomicBoolean(false);
BootstrapLoggerUtils.setBootedHook(() -> vmBooted.get());
// We replace System.err to check the messages that have been logged
// by the JUL ConsoleHandler and default SimpleConsoleLogger
// implementaion
final LogStream err = new LogStream();
System.setErr(new PrintStream(err));
if (BootstrapLogger.isBooted()) {
throw new RuntimeException("VM should not be booted!");
}
Logger logger = LazyLoggers.getLogger("foo.bar", Thread.class.getModule());
Level[] levels = {Level.INFO, Level.WARNING, Level.INFO};
int index = 0;
logger.log(levels[index], "Early message #" + (index+1)); index++;
logger.log(levels[index], "Early message #" + (index+1)); index++;
LogStream.err.println("VM Booted: " + vmBooted.get());
LogStream.err.println("LogManager initialized: " + logManagerInitialized.get(null));
logger.log(levels[index], "Early message #" + (index+1)); index++;
if (err.drain().contains("Early message")) {
// We're expecting that logger will be a LazyLogger wrapping a
// BootstrapLogger. The Bootstrap logger will stack the log messages
// it receives until the VM is booted.
// Since our private hook pretend that the VM is not booted yet,
// the logged messages shouldn't have reached System.err yet.
throw new RuntimeException("Early message logged while VM is not booted!");
}
// Now pretend that the VM is booted. Nothing should happen yet, until
// we try to log a new message.
vmBooted.getAndSet(true);
LogStream.err.println("VM Booted: " + vmBooted.get());
LogStream.err.println("LogManager initialized: " + logManagerInitialized.get(null));
if (!BootstrapLogger.isBooted()) {
throw new RuntimeException("VM should now be booted!");
}
if (((Boolean)logManagerInitialized.get(null)).booleanValue()) {
throw new RuntimeException("LogManager shouldn't be initialized yet!");
}
// Logging a message should cause the BootstrapLogger to replace itself
// by a 'real' logger in the LazyLogger. But since the LogManager isn't
// initialized yet, this should be a SimpleConsoleLogger...
logger.log(Level.INFO, "LOG#4: VM now booted: {0}", vmBooted.get());
logger.log(Level.DEBUG, "LOG#5: hi!");
WeakReference<Thread> threadRef = null;
ReferenceQueue<Thread> queue = new ReferenceQueue<>();
Set<Thread> set = Thread.getAllStackTraces().keySet().stream()
.filter((t) -> t.getName().startsWith("BootstrapMessageLoggerTask-"))
.collect(Collectors.toSet());
try {
set.stream().forEach(t -> LogStream.err.println("Found: " + t));
if (set.size() > 1) {
throw new RuntimeException("Too many bootstrap threads found");
}
Optional<Thread> firstThread = set.stream().findFirst();
if (firstThread.isPresent()) {
threadRef = new WeakReference<>(firstThread.get(), queue);
}
} finally {
set.clear();
set = null;
}
if (!BootstrapLogger.isBooted()) {
throw new RuntimeException("VM should still be booted!");
}
if (((Boolean)logManagerInitialized.get(null)).booleanValue()) {
throw new RuntimeException("LogManager shouldn't be initialized yet!");
}
// Now check that the early messages we had printed before the VM was
// booted have appeared on System.err...
String afterBoot = err.drain();
for (int i=0; i<levels.length; i++) {
String m = levels[i].getName()+": Early message #"+(i+1);
if (!afterBoot.contains(m)) {
throw new RuntimeException("System.err does not contain: "+m);
}
}
// check that the message logged *after* the VM was booted also printed.
if (!afterBoot.contains("INFO: LOG#4")) {
throw new RuntimeException("System.err does not contain: "
+ "INFO: LOG#4");
}
// check that the debug message was not printed.
if (afterBoot.contains("LOG#5")) {
throw new RuntimeException("System.err contain: " + "LOG#5");
}
LogStream.err.println("VM Booted: " + vmBooted.get());
LogStream.err.println("LogManager initialized: " + logManagerInitialized.get(null));
if (!BootstrapLogger.isBooted()) {
throw new RuntimeException("VM should still be booted!");
}
if (((Boolean)logManagerInitialized.get(null)).booleanValue()) {
throw new RuntimeException("LogManager shouldn't be initialized yet!");
}
// Now we're going to use reflection to access JUL, and change
// the level of the "foo" logger.
// We're using reflection so that the test can also run in
// configurations where java.util.logging is not present.
boolean hasJUL = false;
try {
Class<?> loggerClass = Class.forName("java.util.logging.Logger");
Class<?> levelClass = Class.forName("java.util.logging.Level");
Class<?> handlerClass = Class.forName("java.util.logging.Handler");
// java.util.logging.Logger.getLogger("foo")
// .setLevel(java.util.logging.Level.FINEST);
Object fooLogger = loggerClass.getMethod("getLogger", String.class)
.invoke(null, "foo");
loggerClass.getMethod("setLevel", levelClass)
.invoke(fooLogger, levelClass.getField("FINEST").get(null));
// java.util.logging.Logger.getLogger("").getHandlers()[0]
// .setLevel(java.util.logging.Level.ALL);
Object rootLogger = loggerClass.getMethod("getLogger", String.class)
.invoke(null, "");
Object handlers = loggerClass.getMethod("getHandlers").
invoke(rootLogger);
handlerClass.getMethod("setLevel", levelClass)
.invoke(Array.get(handlers, 0), levelClass.getField("ALL")
.get(null));
hasJUL = true;
} catch (ClassNotFoundException x) {
LogStream.err.println("JUL is not present: class " + x.getMessage()
+ " not found");
hasJUL = false;
}
logger.log(Level.DEBUG, "hi now!");
String debug = err.drain();
if (hasJUL) {
if (!((Boolean)logManagerInitialized.get(null)).booleanValue()) {
throw new RuntimeException("LogManager should be initialized now!");
}
if (!debug.contains("FINE: hi now!")) {
throw new RuntimeException("System.err does not contain: "
+ "FINE: hi now!");
}
} else {
if (debug.contains("hi now!")) {
throw new RuntimeException("System.err contains: " + "hi now!");
}
if (((Boolean)logManagerInitialized.get(null)).booleanValue()) {
throw new RuntimeException("LogManager shouldn't be initialized yet!");
}
Logger baz = System.getLogger("foo.bar.baz");
if (((Boolean)logManagerInitialized.get(null)).booleanValue()) {
throw new RuntimeException("LogManager shouldn't be initialized yet!");
}
}
Logger bazbaz = java.lang.System.LoggerFinder
.getLoggerFinder().getLogger("foo.bar.baz.baz", BootstrapLoggerTest.class.getModule());
if (!((Boolean)logManagerInitialized.get(null)).booleanValue()) {
throw new RuntimeException("LogManager should be initialized now!");
}
Logger bazbaz2 = System.getLogger("foo.bar.baz.baz");
if (bazbaz2.getClass() != bazbaz.getClass()) {
throw new RuntimeException("bazbaz2.class != bazbaz.class ["
+ bazbaz2.getClass() + " != "
+ bazbaz.getClass() + "]");
}
if (hasJUL != bazbaz2.getClass().getName()
.equals("sun.util.logging.internal.LoggingProviderImpl$JULWrapper")) {
throw new RuntimeException("Unexpected class for bazbaz: "
+ bazbaz.getClass().getName()
+ "\n\t expected: "
+ "sun.util.logging.internal.LoggingProviderImpl$JULWrapper");
}
// Now we're going to check that the thread of the BootstrapLogger
// executor terminates, and that the Executor is GC'ed after that.
// This will involve a bit of waiting, hence the timeout=120 in
// the @run line.
// If this test fails in timeout - we could envisage skipping this part,
// or adding some System property to configure the keep alive delay
// of the executor.
//
// Though unlikely, it is not impossible that the bootstrap logger
// executor may have released its first thread and spawned a new one.
// If that happened then the executor itself might have been GC'ed
// as well and a new one might have been created.
// The code below will lookup the executor threads again and
// join them.
// Only one may be active at a given time, but that might not
// be the one referenced by threadRef.
// We're just making sure all of them have stopped running
// before verifying that the executor is eventually GC'ed.
final WeakReference<Thread> previous = threadRef;
Stream<WeakReference<Thread>> stream = Thread.getAllStackTraces().keySet().stream()
.filter((t) -> t.getName().startsWith("BootstrapMessageLoggerTask-"))
.filter((t) -> previous == null ? true : t != previous.get())
.map((t) -> new WeakReference<>(t, queue));
List<WeakReference<Thread>> threads = stream.collect(Collectors.toList());
if (previous != null) threads.add(previous);
threads.forEach(t -> LogStream.err.println(t.get()));
stream = null;
if (test == TestCase.RUN_AND_WAIT) {
// First wait for all executor threads to terminate
for (var ref : threads) {
Thread t = ref.get();
if (t != null) {
if (!(Boolean)isAlive.invoke(null) && t.isAlive()) {
throw new RuntimeException("Executor already terminated");
} else {
LogStream.err.println("Executor still alive as expected: " + t.getName());
}
LogStream.err.println("Waiting for " + t.getName() + " to terminate (join)");
t.join(60_000);
t = null;
} else {
LogStream.err.println("WeakReference<Thread> is already cleared.");
long count = Thread.getAllStackTraces().keySet().stream()
.filter((tr) -> tr.getName().startsWith("BootstrapMessageLoggerTask-"))
.count();
if (count != 0) {
LogStream.err.println("There are " + count + " threads still lingering.");
}
}
}
// Then wait until all the executor threads are GC'ed
while (!threads.isEmpty()) {
LogStream.err.println("Calling System.gc()");
System.gc();
LogStream.err.println("Waiting for BootstrapMessageLoggerTask to be gc'ed");
Reference<?> tref;
while ((tref = queue.remove(1000)) == null) {
LogStream.err.println("Calling System.gc()");
System.gc();
}
threads.remove(tref);
LogStream.err.println("BootstrapMessageLoggerTask has been gc'ed: "
+ threads.size() + " remaining...");
}
// Then wait for the executor to be gc'ed...
LogStream.err.println("Waiting for the executor to be gc'ed: Calling System.gc()");
System.gc();
for (int i=0; i<10; i++) {
if (!(Boolean)isAlive.invoke(null)) break;
// It would be unexpected that we reach here...
Thread.sleep(1000);
LogStream.err.println("Calling System.gc()");
System.gc();
}
if ((Boolean)isAlive.invoke(null)) {
throw new RuntimeException("Executor still alive");
} else {
LogStream.err.println("Executor terminated as expected.");
}
} else {
LogStream.err.println("Not checking executor termination for " + test);
}
LogStream.err.println(test.name() + ": PASSED");
Locale.setDefault(savedLocale);
}
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
/**
* We use an instance of this class to check what the logging system has printed
* on System.err.
*/
class LogStream extends OutputStream {
static final PrintStream err = System.err;
private final ByteArrayOutputStream BAOS = new ByteArrayOutputStream();
LogStream() {
super();
}
@Override
public synchronized void write(int b) {
BAOS.write(b);
err.write(b);
}
String drain() {
BootstrapLoggerUtils.awaitPending();
synchronized (this) {
String txt = BAOS.toString();
BAOS.reset();
return txt;
}
}
}

View file

@ -0,0 +1,108 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.security.AllPermission;
import java.security.Permissions;
import java.security.ProtectionDomain;
import java.util.concurrent.ConcurrentHashMap;
/**
* A custom ClassLoader to load the concrete LoggerFinder class
* with all permissions.
*
* @author danielfuchs
*/
public class CustomSystemClassLoader extends ClassLoader {
private final ConcurrentHashMap<String, Class<?>> classes = new ConcurrentHashMap<>();
public CustomSystemClassLoader() {
super();
}
public CustomSystemClassLoader(ClassLoader parent) {
super(parent);
}
private Class<?> defineFinderClass(String name)
throws ClassNotFoundException {
Class<?> loggerFinderClass = classes.get(name);
if (loggerFinderClass != null) return loggerFinderClass;
final Object obj = getClassLoadingLock(name);
synchronized(obj) {
loggerFinderClass = classes.get(name);
if (loggerFinderClass != null) return loggerFinderClass;
URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation();
File file = new File(url.getPath(), name+".class");
if (file.canRead()) {
try {
byte[] b = Files.readAllBytes(file.toPath());
Permissions perms = new Permissions();
perms.add(new AllPermission());
loggerFinderClass = defineClass(
name, b, 0, b.length, new ProtectionDomain(
this.getClass().getProtectionDomain().getCodeSource(),
perms));
classes.put(name, loggerFinderClass);
System.out.println("Loaded " + name);
return loggerFinderClass;
} catch (Throwable ex) {
ex.printStackTrace();
throw new ClassNotFoundException(name, ex);
}
} else {
throw new ClassNotFoundException(name,
new IOException(file.toPath() + ": can't read"));
}
}
}
@Override
public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (name.equals("LogProducerFinder") || name.startsWith("LogProducerFinder$")) {
Class<?> c = defineFinderClass(name);
if (resolve) {
resolveClass(c);
}
return c;
}
return super.loadClass(name, resolve);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (name.equals("LogProducerFinder") || name.startsWith("LogProducerFinder$")) {
return defineFinderClass(name);
}
return super.findClass(name);
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.lang.System.Logger;
import java.lang.System.LoggerFinder;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.concurrent.ConcurrentHashMap;
public class LogProducerFinder extends LoggerFinder {
public static final RuntimePermission LOGGERFINDER_PERMISSION =
new RuntimePermission("loggerFinder");
final ConcurrentHashMap<String, LoggerBridgeTest.LoggerImpl> system = new ConcurrentHashMap<>();
final ConcurrentHashMap<String, LoggerBridgeTest.LoggerImpl> user = new ConcurrentHashMap<>();
@Override
public Logger getLogger(String name, Module caller) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(LOGGERFINDER_PERMISSION);
}
PrivilegedAction<ClassLoader> pa = () -> caller.getClassLoader();
ClassLoader callerLoader = AccessController.doPrivileged(pa);
if (callerLoader == null) {
return system.computeIfAbsent(name, (n) -> new LoggerBridgeTest.LoggerImpl(n));
} else {
return user.computeIfAbsent(name, (n) -> new LoggerBridgeTest.LoggerImpl(n));
}
}
}

View file

@ -0,0 +1,861 @@
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.ResourceBundle;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.lang.System.LoggerFinder;
import java.lang.System.Logger;
import sun.util.logging.PlatformLogger;
/**
* @test
* @bug 8140364
* @summary JDK implementation specific unit test for JDK internal artifacts.
* Tests all bridge methods with a custom backend whose
* loggers implement PlatformLogger.Bridge.
* @modules java.base/sun.util.logging
* java.base/jdk.internal.logger
* java.logging
* @build CustomSystemClassLoader LogProducerFinder LoggerBridgeTest
* @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader LoggerBridgeTest
*/
public class LoggerBridgeTest {
public static final RuntimePermission LOGGERFINDER_PERMISSION =
new RuntimePermission("loggerFinder");
final static AtomicLong sequencer = new AtomicLong();
final static boolean VERBOSE = false;
public static final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
public static final class LogEvent implements Cloneable {
public LogEvent() {
this(sequencer.getAndIncrement());
}
LogEvent(long sequenceNumber) {
this.sequenceNumber = sequenceNumber;
}
long sequenceNumber;
boolean isLoggable;
String loggerName;
sun.util.logging.PlatformLogger.Level level;
ResourceBundle bundle;
Throwable thrown;
Object[] args;
String msg;
Supplier<String> supplier;
String className;
String methodName;
Object[] toArray() {
return new Object[] {
sequenceNumber,
loggerName,
level,
isLoggable,
bundle,
msg,
supplier,
thrown,
args,
className,
methodName,
};
}
@Override
public String toString() {
return Arrays.deepToString(toArray());
}
@Override
public boolean equals(Object obj) {
return obj instanceof LogEvent
&& Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
}
@Override
public int hashCode() {
return Objects.hash(toArray());
}
public LogEvent cloneWith(long sequenceNumber)
throws CloneNotSupportedException {
LogEvent cloned = (LogEvent)super.clone();
cloned.sequenceNumber = sequenceNumber;
return cloned;
}
public static LogEvent of(long sequenceNumber,
boolean isLoggable, String name,
sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
String key, Throwable thrown, Object... params) {
return LogEvent.of(sequenceNumber, isLoggable, name,
null, null, level, bundle, key,
thrown, params);
}
public static LogEvent of(long sequenceNumber,
boolean isLoggable, String name,
sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
Supplier<String> supplier, Throwable thrown, Object... params) {
return LogEvent.of(sequenceNumber, isLoggable, name,
null, null, level, bundle, supplier,
thrown, params);
}
public static LogEvent of(long sequenceNumber,
boolean isLoggable, String name,
String className, String methodName,
sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
String key, Throwable thrown, Object... params) {
LogEvent evt = new LogEvent(sequenceNumber);
evt.loggerName = name;
evt.level = level;
evt.args = params;
evt.bundle = bundle;
evt.thrown = thrown;
evt.msg = key;
evt.isLoggable = isLoggable;
evt.className = className;
evt.methodName = methodName;
return evt;
}
public static LogEvent of(boolean isLoggable, String name,
String className, String methodName,
sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
String key, Throwable thrown, Object... params) {
return LogEvent.of(sequencer.getAndIncrement(), isLoggable, name,
className, methodName, level, bundle, key, thrown, params);
}
public static LogEvent of(long sequenceNumber,
boolean isLoggable, String name,
String className, String methodName,
sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
Supplier<String> supplier, Throwable thrown, Object... params) {
LogEvent evt = new LogEvent(sequenceNumber);
evt.loggerName = name;
evt.level = level;
evt.args = params;
evt.bundle = bundle;
evt.thrown = thrown;
evt.supplier = supplier;
evt.isLoggable = isLoggable;
evt.className = className;
evt.methodName = methodName;
return evt;
}
public static LogEvent of(boolean isLoggable, String name,
String className, String methodName,
sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
Supplier<String> supplier, Throwable thrown, Object... params) {
return LogEvent.of(sequencer.getAndIncrement(), isLoggable, name,
className, methodName, level, bundle, supplier, thrown, params);
}
}
static final Class<?> providerClass;
static {
try {
// Preload classes before the security manager is on.
providerClass = ClassLoader.getSystemClassLoader().loadClass("LogProducerFinder");
((LoggerFinder)providerClass.newInstance()).getLogger("foo", providerClass.getModule());
} catch (Exception ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static class LoggerImpl implements System.Logger, PlatformLogger.Bridge {
private final String name;
private PlatformLogger.Level level = PlatformLogger.Level.INFO;
private PlatformLogger.Level OFF = PlatformLogger.Level.OFF;
private PlatformLogger.Level FINE = PlatformLogger.Level.FINE;
private PlatformLogger.Level FINER = PlatformLogger.Level.FINER;
private PlatformLogger.Level FINEST = PlatformLogger.Level.FINEST;
private PlatformLogger.Level CONFIG = PlatformLogger.Level.CONFIG;
private PlatformLogger.Level INFO = PlatformLogger.Level.INFO;
private PlatformLogger.Level WARNING = PlatformLogger.Level.WARNING;
private PlatformLogger.Level SEVERE = PlatformLogger.Level.SEVERE;
public LoggerImpl(String name) {
this.name = name;
}
public void configureLevel(PlatformLogger.Level level) {
this.level = level;
}
@Override
public String getName() {
return name;
}
@Override
public boolean isLoggable(Level level) {
return this.level != OFF && this.level.intValue() <= level.getSeverity();
}
@Override
public void log(Level level, ResourceBundle bundle,
String key, Throwable thrown) {
throw new UnsupportedOperationException();
}
@Override
public void log(Level level, ResourceBundle bundle,
String format, Object... params) {
throw new UnsupportedOperationException();
}
void log(LogEvent event) {
eventQueue.add(event);
}
@Override
public void log(Level level, Supplier<String> msgSupplier) {
throw new UnsupportedOperationException();
}
@Override
public void log(Level level, Supplier<String> msgSupplier,
Throwable thrown) {
throw new UnsupportedOperationException();
}
@Override
public void log(PlatformLogger.Level level, String msg) {
log(LogEvent.of(isLoggable(level), name, null, null,
level, null, msg, null, (Object[]) null));
}
@Override
public void log(PlatformLogger.Level level,
Supplier<String> msgSupplier) {
log(LogEvent.of(isLoggable(level), name, null, null,
level, null, msgSupplier, null, (Object[]) null));
}
@Override
public void log(PlatformLogger.Level level, String msg,
Object... params) {
log(LogEvent.of(isLoggable(level), name, null, null,
level, null, msg, null, params));
}
@Override
public void log(PlatformLogger.Level level, String msg,
Throwable thrown) {
log(LogEvent.of(isLoggable(level), name, null, null,
level, null, msg, thrown, (Object[]) null));
}
@Override
public void log(PlatformLogger.Level level, Throwable thrown,
Supplier<String> msgSupplier) {
log(LogEvent.of(isLoggable(level), name, null, null,
level, null, msgSupplier, thrown, (Object[]) null));
}
@Override
public void logp(PlatformLogger.Level level, String sourceClass,
String sourceMethod, String msg) {
log(LogEvent.of(isLoggable(level), name,
sourceClass, sourceMethod,
level, null, msg, null, (Object[]) null));
}
@Override
public void logp(PlatformLogger.Level level, String sourceClass,
String sourceMethod, Supplier<String> msgSupplier) {
log(LogEvent.of(isLoggable(level), name,
sourceClass, sourceMethod,
level, null, msgSupplier, null, (Object[]) null));
}
@Override
public void logp(PlatformLogger.Level level, String sourceClass,
String sourceMethod, String msg, Object... params) {
log(LogEvent.of(isLoggable(level), name,
sourceClass, sourceMethod,
level, null, msg, null, params));
}
@Override
public void logp(PlatformLogger.Level level, String sourceClass,
String sourceMethod, String msg, Throwable thrown) {
log(LogEvent.of(isLoggable(level), name,
sourceClass, sourceMethod,
level, null, msg, thrown, (Object[]) null));
}
@Override
public void logp(PlatformLogger.Level level, String sourceClass,
String sourceMethod, Throwable thrown,
Supplier<String> msgSupplier) {
log(LogEvent.of(isLoggable(level), name,
sourceClass, sourceMethod,
level, null, msgSupplier, thrown, (Object[]) null));
}
@Override
public void logrb(PlatformLogger.Level level, String sourceClass,
String sourceMethod, ResourceBundle bundle, String msg,
Object... params) {
log(LogEvent.of(isLoggable(level), name,
sourceClass, sourceMethod,
level, bundle, msg, null, params));
}
@Override
public void logrb(PlatformLogger.Level level, ResourceBundle bundle,
String msg, Object... params) {
log(LogEvent.of(isLoggable(level), name, null, null,
level, bundle, msg, null, params));
}
@Override
public void logrb(PlatformLogger.Level level, String sourceClass,
String sourceMethod, ResourceBundle bundle, String msg,
Throwable thrown) {
log(LogEvent.of(isLoggable(level), name,
sourceClass, sourceMethod,
level, bundle, msg, thrown, (Object[]) null));
}
@Override
public void logrb(PlatformLogger.Level level, ResourceBundle bundle,
String msg, Throwable thrown) {
log(LogEvent.of(isLoggable(level), name, null, null,
level, bundle, msg, thrown, (Object[]) null));
}
@Override
public boolean isLoggable(PlatformLogger.Level level) {
return this.level != OFF && level.intValue()
>= this.level.intValue();
}
@Override
public boolean isEnabled() {
return this.level != OFF;
}
}
static ClassLoader getClassLoader(Module m) {
return m.getClassLoader();
}
static final sun.util.logging.PlatformLogger.Level[] julLevels = {
sun.util.logging.PlatformLogger.Level.ALL,
sun.util.logging.PlatformLogger.Level.FINEST,
sun.util.logging.PlatformLogger.Level.FINER,
sun.util.logging.PlatformLogger.Level.FINE,
sun.util.logging.PlatformLogger.Level.CONFIG,
sun.util.logging.PlatformLogger.Level.INFO,
sun.util.logging.PlatformLogger.Level.WARNING,
sun.util.logging.PlatformLogger.Level.SEVERE,
sun.util.logging.PlatformLogger.Level.OFF,
};
public static class MyBundle extends ResourceBundle {
final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
@Override
protected Object handleGetObject(String key) {
if (key.contains(" (translated)")) {
throw new RuntimeException("Unexpected key: " + key);
}
return map.computeIfAbsent(key, k -> k + " (translated)");
}
@Override
public Enumeration<String> getKeys() {
return Collections.enumeration(map.keySet());
}
}
public static class MyHandler extends Handler {
@Override
public java.util.logging.Level getLevel() {
return java.util.logging.Level.ALL;
}
@Override
public void publish(LogRecord record) {
eventQueue.add(LogEvent.of(sequencer.getAndIncrement(),
true, record.getLoggerName(),
record.getSourceClassName(),
record.getSourceMethodName(),
PlatformLogger.Level.valueOf(record.getLevel().getName()),
record.getResourceBundle(), record.getMessage(),
record.getThrown(), record.getParameters()));
}
@Override
public void flush() {
}
@Override
public void close() throws SecurityException {
}
}
public static class MyLoggerBundle extends MyBundle {
}
final static Method lazyGetLogger;
static {
// jdk.internal.logging.LoggerBridge.getLogger(name, caller)
try {
Class<?> bridgeClass = Class.forName("jdk.internal.logger.LazyLoggers");
lazyGetLogger = bridgeClass.getDeclaredMethod("getLogger",
String.class, Module.class);
lazyGetLogger.setAccessible(true);
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
static Logger getLogger(LoggerFinder provider, String name, Module caller) {
Logger logger;
try {
logger = Logger.class.cast(lazyGetLogger.invoke(null, name, caller));
} catch (Throwable x) {
Throwable t = (x instanceof InvocationTargetException) ?
((InvocationTargetException)x).getTargetException() : x;
if (t instanceof RuntimeException) {
throw (RuntimeException)t;
} else if (t instanceof Exception) {
throw new RuntimeException(t);
} else {
throw (Error)t;
}
}
// The method above does not throw exception...
// call the provider here to verify that an exception would have
// been thrown by the provider.
if (logger != null && caller == Thread.class.getModule()) {
Logger log = provider.getLogger(name, caller);
}
return logger;
}
static Logger getLogger(LoggerFinder provider, String name, ResourceBundle bundle, Module caller) {
if (getClassLoader(caller) != null) {
return System.getLogger(name,bundle);
} else {
return provider.getLocalizedLogger(name, bundle, caller);
}
}
static PlatformLogger.Bridge convert(Logger logger) {
return PlatformLogger.Bridge.convert(logger);
}
public static void main(String[] args) {
System.out.println("\n*** Running test\n");
LoggerFinder provider = LoggerFinder.getLoggerFinder();
test(provider);
System.out.println("Tetscase count: " + sequencer.get());
System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
}
public static void test(LoggerFinder provider) {
ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
final Map<Object, String> loggerDescMap = new HashMap<>();
Logger appLogger1 = System.getLogger("foo");
loggerDescMap.put(appLogger1, "System.getLogger(\"foo\")");
Logger sysLogger1 = getLogger(provider, "foo", Thread.class.getModule());
loggerDescMap.put(sysLogger1, "provider.getLogger(\"foo\", Thread.class.getModule())");
Logger appLogger2 = System.getLogger("foo", loggerBundle);
loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)");
Logger sysLogger2 = getLogger(provider, "foo", loggerBundle, Thread.class.getModule());
loggerDescMap.put(sysLogger2, "provider.getLogger(\"foo\", loggerBundle, Thread.class.getModule())");
if (appLogger2 == sysLogger2) {
throw new RuntimeException("identical loggers");
}
if (appLogger2 == appLogger1) {
throw new RuntimeException("identical loggers");
}
if (sysLogger2 == sysLogger1) {
throw new RuntimeException("identical loggers");
}
final LoggerImpl appSink = LoggerImpl.class.cast(
provider.getLogger("foo", LoggerBridgeTest.class.getModule()));
final LoggerImpl sysSink = LoggerImpl.class.cast(
provider.getLogger("foo", Thread.class.getModule()));
testLogger(provider, loggerDescMap, "foo", null, convert(appLogger1), appSink);
testLogger(provider, loggerDescMap, "foo", null, convert(sysLogger1), sysSink);
testLogger(provider, loggerDescMap, "foo", loggerBundle, convert(appLogger2), appSink);
testLogger(provider, loggerDescMap, "foo", loggerBundle, convert(sysLogger2), sysSink);
}
public static class Foo {
}
static void verbose(String msg) {
if (VERBOSE) {
System.out.println(msg);
}
}
static void checkLogEvent(LoggerFinder provider, String desc,
LogEvent expected) {
LogEvent actual = eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
static void checkLogEvent(LoggerFinder provider, String desc,
LogEvent expected, boolean expectNotNull) {
LogEvent actual = eventQueue.poll();
if (actual == null && !expectNotNull) return;
if (actual != null && !expectNotNull) {
throw new RuntimeException("Unexpected log event found for " + desc
+ "\n\tgot: " + actual);
}
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
static void setLevel(LoggerImpl sink,
sun.util.logging.PlatformLogger.Level loggerLevel) {
sink.configureLevel(loggerLevel);
}
// Calls the methods defined on PlatformLogger.Bridge and verify the
// parameters received by the underlying LoggerImpl
// logger.
private static void testLogger(LoggerFinder provider,
Map<Object, String> loggerDescMap,
String name,
ResourceBundle loggerBundle,
PlatformLogger.Bridge logger,
LoggerImpl sink) {
System.out.println("Testing " + loggerDescMap.get(logger) + "[" + logger + "]");
final sun.util.logging.PlatformLogger.Level OFF = sun.util.logging.PlatformLogger.Level.OFF;
Foo foo = new Foo();
String fooMsg = foo.toString();
System.out.println("\tlogger.log(messageLevel, fooMsg)");
System.out.println("\tlogger.<level>(fooMsg)");
for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.log(messageLevel, fooMsg): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, messageLevel, loggerBundle,
fooMsg, (Throwable)null, (Object[])null);
logger.log(messageLevel, fooMsg);
checkLogEvent(provider, desc, expected);
}
}
Supplier<String> supplier = new Supplier<String>() {
@Override
public String get() {
return this.toString();
}
};
System.out.println("\tlogger.log(messageLevel, supplier)");
System.out.println("\tlogger.<level>(supplier)");
for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.log(messageLevel, supplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, messageLevel, null,
supplier, (Throwable)null, (Object[])null);
logger.log(messageLevel, supplier);
checkLogEvent(provider, desc, expected);
}
}
String format = "two params [{1} {2}]";
Object arg1 = foo;
Object arg2 = fooMsg;
System.out.println("\tlogger.log(messageLevel, format, arg1, arg2)");
for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.log(messageLevel, format, foo, fooMsg): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, messageLevel, loggerBundle,
format, (Throwable)null, arg1, arg2);
logger.log(messageLevel, format, arg1, arg2);
checkLogEvent(provider, desc, expected);
}
}
Throwable thrown = new Exception("OK: log me!");
System.out.println("\tlogger.log(messageLevel, fooMsg, thrown)");
for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.log(messageLevel, fooMsg, thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, messageLevel, loggerBundle,
fooMsg, thrown, (Object[])null);
logger.log(messageLevel, fooMsg, thrown);
checkLogEvent(provider, desc, expected);
}
}
System.out.println("\tlogger.log(messageLevel, thrown, supplier)");
for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.log(messageLevel, thrown, supplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, messageLevel, null,
supplier, thrown, (Object[])null);
logger.log(messageLevel, thrown, supplier);
checkLogEvent(provider, desc, expected);
}
}
String sourceClass = "blah.Blah";
String sourceMethod = "blih";
System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg)");
for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, sourceClass, sourceMethod, messageLevel, loggerBundle,
fooMsg, (Throwable)null, (Object[])null);
logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg);
checkLogEvent(provider, desc, expected);
}
}
System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, supplier)");
for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, supplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, sourceClass, sourceMethod, messageLevel, null,
supplier, (Throwable)null, (Object[])null);
logger.logp(messageLevel, sourceClass, sourceMethod, supplier);
checkLogEvent(provider, desc, expected);
}
}
System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2)");
for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, sourceClass, sourceMethod, messageLevel, loggerBundle,
format, (Throwable)null, arg1, arg2);
logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2);
checkLogEvent(provider, desc, expected);
}
}
System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown)");
for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, sourceClass, sourceMethod, messageLevel, loggerBundle,
fooMsg, thrown, (Object[])null);
logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown);
checkLogEvent(provider, desc, expected);
}
}
System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier)");
for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, sourceClass, sourceMethod, messageLevel, null,
supplier, thrown, (Object[])null);
logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier);
checkLogEvent(provider, desc, expected);
}
}
ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
System.out.println("\tlogger.logrb(messageLevel, bundle, format, arg1, arg2)");
for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.logrb(messageLevel, bundle, format, arg1, arg2): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, messageLevel, bundle,
format, (Throwable)null, arg1, arg2);
logger.logrb(messageLevel, bundle, format, arg1, arg2);
checkLogEvent(provider, desc, expected);
}
}
System.out.println("\tlogger.logrb(messageLevel, bundle, msg, thrown)");
for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.logrb(messageLevel, bundle, msg, thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, messageLevel, bundle,
fooMsg, thrown, (Object[])null);
logger.logrb(messageLevel, bundle, fooMsg, thrown);
checkLogEvent(provider, desc, expected);
}
}
System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2)");
for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, sourceClass, sourceMethod, messageLevel, bundle,
format, (Throwable)null, arg1, arg2);
logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2);
checkLogEvent(provider, desc, expected);
}
}
System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown)");
for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, sourceClass, sourceMethod, messageLevel, bundle,
fooMsg, thrown, (Object[])null);
logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, fooMsg, thrown);
checkLogEvent(provider, desc, expected);
}
}
}
}

View file

@ -0,0 +1,82 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.IOException;
import java.lang.System.Logger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ResourceBundle;
/**
*
* @author danielfuchs
*/
public final class AccessSystemLogger {
public AccessSystemLogger() {
this(check());
}
private AccessSystemLogger(Void unused) {
}
private static Void check() {
if (AccessSystemLogger.class.getClassLoader() != null) {
throw new RuntimeException("AccessSystemLogger should be loaded by the null classloader");
}
return null;
}
public Logger getLogger(String name) {
Logger logger = System.getLogger(name);
System.out.println("System.getLogger(\"" + name + "\"): " + logger);
return logger;
}
public Logger getLogger(String name, ResourceBundle bundle) {
Logger logger = System.getLogger(name, bundle);
System.out.println("System.getLogger(\"" + name + "\", bundle): " + logger);
return logger;
}
static final Class<?>[] toCopy = { AccessSystemLogger.class, CustomSystemClassLoader.class };
// copy AccessSystemLogger.class to ./boot
public static void main(String[] args) throws IOException {
Path testDir = Paths.get(System.getProperty("user.dir", "."));
Path bootDir = Paths.get(testDir.toString(), "boot");
Path classes = Paths.get(System.getProperty("test.classes", "build/classes"));
if (Files.notExists(bootDir)) {
Files.createDirectory(bootDir);
}
for (Class<?> c : toCopy) {
Path thisClass = Paths.get(classes.toString(),
c.getSimpleName()+".class");
Path dest = Paths.get(bootDir.toString(),
c.getSimpleName()+".class");
Files.copy(thisClass, dest, StandardCopyOption.REPLACE_EXISTING);
}
}
}

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import jdk.internal.logger.SimpleConsoleLogger;
import java.lang.System.Logger;
import java.lang.System.LoggerFinder;
import java.security.AccessController;
import java.security.PrivilegedAction;
public class BaseLoggerFinder extends LoggerFinder
implements LoggerFinderLoaderTest.TestLoggerFinder {
static final RuntimePermission LOGGERFINDER_PERMISSION =
new RuntimePermission("loggerFinder");
public BaseLoggerFinder() {
if (fails.get()) {
throw new RuntimeException("Simulate exception while loading provider");
}
}
Logger createSimpleLogger(String name) {
PrivilegedAction<Logger> pa = () -> SimpleConsoleLogger.makeSimpleLogger(name);
return AccessController.doPrivileged(pa);
}
@Override
public Logger getLogger(String name, Module caller) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(LOGGERFINDER_PERMISSION);
}
PrivilegedAction<ClassLoader> pa = () -> caller.getClassLoader();
ClassLoader callerLoader = AccessController.doPrivileged(pa);
if (callerLoader == null) {
return system.computeIfAbsent(name, (n) -> new LoggerImpl(n, createSimpleLogger(name)));
} else {
return user.computeIfAbsent(name, (n) -> new LoggerImpl(n, createSimpleLogger(name)));
}
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.lang.System.Logger;
import java.lang.System.LoggerFinder;
import java.util.ServiceConfigurationError;
public class BaseLoggerFinder2 extends LoggerFinder
implements LoggerFinderLoaderTest.TestLoggerFinder {
public BaseLoggerFinder2() {
throw new ServiceConfigurationError("Should not come here");
}
@Override
public Logger getLogger(String name, Module caller) {
throw new ServiceConfigurationError("Should not come here");
}
}

View file

@ -0,0 +1,126 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.security.AllPermission;
import java.security.Permissions;
import java.security.ProtectionDomain;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* A custom ClassLoader to load the concrete LoggerFinder class
* with all permissions. The CustomSystemClassLoader class must be
* in the BCL, otherwise when system classes - such as
* ZoneDateTime try to load their resource bundle a MissingResourceBundle
* caused by a SecurityException may be thrown, as the CustomSystemClassLoader
* code base will be found in the stack called by doPrivileged.
*
* @author danielfuchs
*/
public class CustomSystemClassLoader extends ClassLoader {
final List<String> finderClassNames =
Arrays.asList("BaseLoggerFinder", "BaseLoggerFinder2");
final Map<String, Class<?>> finderClasses = new ConcurrentHashMap<>();
Class<?> testLoggerFinderClass;
public CustomSystemClassLoader() {
super();
}
public CustomSystemClassLoader(ClassLoader parent) {
super(parent);
}
private Class<?> defineFinderClass(String name)
throws ClassNotFoundException {
Class<?> finderClass = finderClasses.get(name);
if (finderClass != null) return finderClass;
final Object obj = getClassLoadingLock(name);
synchronized(obj) {
finderClass = finderClasses.get(name);
if (finderClass != null) return finderClass;
if (testLoggerFinderClass == null) {
// Hack: we load testLoggerFinderClass to get its code source.
// we can't use this.getClass() since we are in the boot.
testLoggerFinderClass = super.loadClass("LoggerFinderLoaderTest$TestLoggerFinder");
}
URL url = testLoggerFinderClass.getProtectionDomain().getCodeSource().getLocation();
File file = new File(url.getPath(), name+".class");
if (file.canRead()) {
try {
byte[] b = Files.readAllBytes(file.toPath());
Permissions perms = new Permissions();
perms.add(new AllPermission());
finderClass = defineClass(
name, b, 0, b.length, new ProtectionDomain(
this.getClass().getProtectionDomain().getCodeSource(),
perms));
System.out.println("Loaded " + name);
finderClasses.put(name, finderClass);
return finderClass;
} catch (Throwable ex) {
ex.printStackTrace();
throw new ClassNotFoundException(name, ex);
}
} else {
throw new ClassNotFoundException(name,
new IOException(file.toPath() + ": can't read"));
}
}
}
private static boolean matches(String prefix, String name) {
return prefix.equals(name) || name.startsWith(prefix + "$");
}
@Override
public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (finderClassNames.stream().anyMatch(n -> matches(n, name))) {
Class<?> c = defineFinderClass(name);
if (resolve) {
resolveClass(c);
}
return c;
}
return super.loadClass(name, resolve);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (finderClassNames.stream().anyMatch(n -> matches(n, name))) {
return defineFinderClass(name);
}
return super.findClass(name);
}
}

View file

@ -0,0 +1,671 @@
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.UncheckedIOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.stream.Collectors;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
import java.lang.System.LoggerFinder;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.ServiceLoader;
import java.util.concurrent.atomic.AtomicReference;
/**
* @test
* @bug 8140364 8189291 8283049
* @summary JDK implementation specific unit test for LoggerFinderLoader.
* Tests the behavior of LoggerFinderLoader with respect to the
* value of the internal diagnosability switches. Also test the
* DefaultLoggerFinder and SimpleConsoleLogger implementation.
* @modules java.base/sun.util.logging
* java.base/jdk.internal.logger
* @build AccessSystemLogger LoggerFinderLoaderTest CustomSystemClassLoader BaseLoggerFinder BaseLoggerFinder2
* @run driver AccessSystemLogger
* @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader LoggerFinderLoaderTest
* @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true LoggerFinderLoaderTest
* @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest
* @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest
* @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest
* @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true LoggerFinderLoaderTest
* @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest
* @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest
* @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest
*/
public class LoggerFinderLoaderTest {
final static boolean VERBOSE = false;
final static AccessSystemLogger accessSystemLogger = new AccessSystemLogger();
static final Class<?>[] providerClass;
static {
try {
providerClass = new Class<?>[] {
ClassLoader.getSystemClassLoader().loadClass("BaseLoggerFinder"),
ClassLoader.getSystemClassLoader().loadClass("BaseLoggerFinder2")
};
} catch (ClassNotFoundException ex) {
throw new ExceptionInInitializerError(ex);
}
}
/**
* What our test provider needs to implement.
*/
public static interface TestLoggerFinder {
public final static AtomicBoolean fails = new AtomicBoolean();
public final static AtomicReference<String> conf = new AtomicReference<>("");
public final static AtomicLong sequencer = new AtomicLong();
public final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>();
public final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>();
public class LoggerImpl implements System.Logger {
final String name;
final Logger logger;
public LoggerImpl(String name, Logger logger) {
this.name = name;
this.logger = logger;
}
@Override
public String getName() {
return name;
}
@Override
public boolean isLoggable(Logger.Level level) {
return logger.isLoggable(level);
}
@Override
public void log(Logger.Level level, ResourceBundle bundle, String key, Throwable thrown) {
logger.log(level, bundle, key, thrown);
}
@Override
public void log(Logger.Level level, ResourceBundle bundle, String format, Object... params) {
logger.log(level, bundle, format, params);
}
}
public Logger getLogger(String name, Module caller);
public Logger getLocalizedLogger(String name, ResourceBundle bundle, Module caller);
}
public static class MyBundle extends ResourceBundle {
final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
@Override
protected Object handleGetObject(String key) {
if (key.contains(" (translated)")) {
throw new RuntimeException("Unexpected key: " + key);
}
return map.computeIfAbsent(key, k -> k.toUpperCase(Locale.ROOT) + " (translated)");
}
@Override
public Enumeration<String> getKeys() {
return Collections.enumeration(map.keySet());
}
}
public static class MyLoggerBundle extends MyBundle {
}
private static String withoutWarning(String in) {
return in.lines().filter(s -> !s.startsWith("WARNING:")).collect(Collectors.joining());
}
static LoggerFinder getLoggerFinder(Class<?> expectedClass,
String errorPolicy, boolean singleton) {
LoggerFinder provider = null;
try {
TestLoggerFinder.sequencer.incrementAndGet();
provider = LoggerFinder.getLoggerFinder();
if (TestLoggerFinder.fails.get() || singleton) {
if ("ERROR".equals(errorPolicy.toUpperCase(Locale.ROOT))) {
throw new RuntimeException("Expected exception not thrown");
} else if ("WARNING".equals(errorPolicy.toUpperCase(Locale.ROOT))) {
String warning = ErrorStream.errorStream.peek();
if (!warning.contains("WARNING: Failed to instantiate LoggerFinder provider; Using default.")) {
throw new RuntimeException("Expected message not found. Error stream contained: " + warning);
}
} else if ("DEBUG".equals(errorPolicy.toUpperCase(Locale.ROOT))) {
String warning = ErrorStream.errorStream.peek();
if (!warning.contains("WARNING: Failed to instantiate LoggerFinder provider; Using default.")) {
throw new RuntimeException("Expected message not found. Error stream contained: " + warning);
}
if (!warning.contains("WARNING: Exception raised trying to instantiate LoggerFinder")) {
throw new RuntimeException("Expected message not found. Error stream contained: " + warning);
}
if (TestLoggerFinder.fails.get()) {
if (!warning.contains("java.util.ServiceConfigurationError: java.lang.System$LoggerFinder: Provider BaseLoggerFinder could not be instantiated")) {
throw new RuntimeException("Expected message not found. Error stream contained: " + warning);
}
} else if (singleton) {
if (!warning.contains("java.util.ServiceConfigurationError: More than one LoggerFinder implementation")) {
throw new RuntimeException("Expected message not found. Error stream contained: " + warning);
}
}
} else if ("QUIET".equals(errorPolicy.toUpperCase(Locale.ROOT))) {
String warning = ErrorStream.errorStream.peek();
warning = withoutWarning(warning);
if (!warning.isEmpty()) {
throw new RuntimeException("Unexpected error message found: "
+ ErrorStream.errorStream.peek());
}
}
}
} catch(Throwable t) {
if (TestLoggerFinder.fails.get() || singleton) {
// must check System.err
if ("ERROR".equals(errorPolicy.toUpperCase(Locale.ROOT))) {
provider = LoggerFinder.getLoggerFinder();
} else {
Throwable orig = t.getCause();
while (orig != null && orig.getCause() != null) orig = orig.getCause();
if (orig != null) orig.printStackTrace(ErrorStream.err);
throw new RuntimeException("Unexpected exception: " + t, t);
}
} else {
throw new RuntimeException("Unexpected exception: " + t, t);
}
}
expectedClass.cast(provider);
ErrorStream.errorStream.store();
System.out.println("*** Actual LoggerFinder class is: " + provider.getClass().getName());
return provider;
}
static class ErrorStream extends PrintStream {
static AtomicBoolean forward = new AtomicBoolean();
ByteArrayOutputStream out;
String saved = "";
public ErrorStream(ByteArrayOutputStream out) {
super(out);
this.out = out;
}
@Override
public void write(int b) {
super.write(b);
if (forward.get()) err.write(b);
}
@Override
public void write(byte[] b) throws IOException {
super.write(b);
if (forward.get()) err.write(b);
}
@Override
public void write(byte[] buf, int off, int len) {
super.write(buf, off, len);
if (forward.get()) err.write(buf, off, len);
}
public String peek() {
flush();
return out.toString();
}
public String drain() {
flush();
String res = out.toString();
out.reset();
return res;
}
public void store() {
flush();
saved = out.toString();
out.reset();
}
public void restore() {
out.reset();
try {
out.write(saved.getBytes());
} catch(IOException io) {
throw new UncheckedIOException(io);
}
}
static final PrintStream err = System.err;
static final ErrorStream errorStream = new ErrorStream(new ByteArrayOutputStream());
}
private static StringBuilder appendProperty(StringBuilder b, String name) {
String value = System.getProperty(name);
if (value == null) return b;
return b.append(name).append("=").append(value).append('\n');
}
public static void main(String[] args) {
if (args.length == 0) {
args = new String[] {
"NOSECURITY",
"NOPERMISSIONS",
"WITHPERMISSIONS"
};
}
Locale.setDefault(Locale.ENGLISH);
System.setErr(ErrorStream.errorStream);
System.setProperty("jdk.logger.packages", TestLoggerFinder.LoggerImpl.class.getName());
//System.setProperty("jdk.logger.finder.error", "ERROR");
//System.setProperty("jdk.logger.finder.singleton", "true");
//System.setProperty("test.fails", "true");
TestLoggerFinder.fails.set(Boolean.getBoolean("test.fails"));
StringBuilder c = new StringBuilder();
appendProperty(c, "jdk.logger.packages");
appendProperty(c, "jdk.logger.finder.error");
appendProperty(c, "jdk.logger.finder.singleton");
appendProperty(c, "test.fails");
TestLoggerFinder.conf.set(c.toString());
try {
test(args);
} finally {
try {
System.setErr(ErrorStream.err);
} catch (Error | RuntimeException x) {
x.printStackTrace(ErrorStream.err);
}
}
}
public static void test(String[] args) {
final String errorPolicy = System.getProperty("jdk.logger.finder.error", "WARNING");
final Boolean ensureSingleton = Boolean.getBoolean("jdk.logger.finder.singleton");
final Class<?> expectedClass =
TestLoggerFinder.fails.get() || ensureSingleton
? jdk.internal.logger.DefaultLoggerFinder.class
: TestLoggerFinder.class;
System.out.println("Declared provider class: " + providerClass[0]
+ "[" + providerClass[0].getClassLoader() + "]");
if (!TestLoggerFinder.fails.get()) {
ServiceLoader<LoggerFinder> serviceLoader =
ServiceLoader.load(LoggerFinder.class, ClassLoader.getSystemClassLoader());
Iterator<LoggerFinder> iterator = serviceLoader.iterator();
Object firstProvider = iterator.next();
if (!firstProvider.getClass().getName().equals("BaseLoggerFinder")) {
throw new RuntimeException("Unexpected provider: " + firstProvider.getClass().getName());
}
if (!iterator.hasNext()) {
throw new RuntimeException("Expected two providers");
}
}
LoggerFinder provider;
ErrorStream.errorStream.restore();
System.out.println("\n*** Test starting\n");
System.out.println(TestLoggerFinder.conf.get());
provider = getLoggerFinder(expectedClass, errorPolicy, ensureSingleton);
test(provider);
System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get());
System.out.println("\nPASSED: Tested " + TestLoggerFinder.sequencer.get() + " cases.");
}
public static void test(LoggerFinder provider) {
ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
final Map<Logger, String> loggerDescMap = new HashMap<>();
System.Logger sysLogger = accessSystemLogger.getLogger("foo");
loggerDescMap.put(sysLogger, "accessSystemLogger.getLogger(\"foo\")");
System.Logger localizedSysLogger = accessSystemLogger.getLogger("fox", loggerBundle);
loggerDescMap.put(localizedSysLogger, "accessSystemLogger.getLogger(\"fox\", loggerBundle)");
System.Logger appLogger = System.getLogger("bar");
loggerDescMap.put(appLogger,"System.getLogger(\"bar\")");
System.Logger localizedAppLogger = System.getLogger("baz", loggerBundle);
loggerDescMap.put(localizedAppLogger,"System.getLogger(\"baz\", loggerBundle)");
testLogger(provider, loggerDescMap, "foo", null, sysLogger);
testLogger(provider, loggerDescMap, "foo", loggerBundle, localizedSysLogger);
testLogger(provider, loggerDescMap, "foo", null, appLogger);
testLogger(provider, loggerDescMap, "foo", loggerBundle, localizedAppLogger);
}
public static class Foo {
}
static void verbose(String msg) {
if (VERBOSE) {
System.out.println(msg);
}
}
// Calls the 8 methods defined on Logger and verify the
// parameters received by the underlying TestProvider.LoggerImpl
// logger.
private static void testLogger(LoggerFinder provider,
Map<Logger, String> loggerDescMap,
String name,
ResourceBundle loggerBundle,
Logger logger) {
System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger +"]");
AtomicLong sequencer = TestLoggerFinder.sequencer;
Foo foo = new Foo();
String fooMsg = foo.toString();
for (Level loggerLevel : EnumSet.of(Level.INFO)) {
for (Level messageLevel : Level.values()) {
ErrorStream.errorStream.drain();
String desc = "logger.log(messageLevel, foo): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
sequencer.incrementAndGet();
logger.log(messageLevel, foo);
if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (!ErrorStream.errorStream.peek().isEmpty()) {
throw new RuntimeException("unexpected event in queue for "
+ desc +": " + "\n\t" + ErrorStream.errorStream.drain());
}
} else {
String logged = ErrorStream.errorStream.drain();
if (!logged.contains("LoggerFinderLoaderTest testLogger")
|| !logged.contains(messageLevel.getName() + ": " + fooMsg)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected:" + "\n<<<<\n"
+ "[date] LoggerFinderLoaderTest testLogger\n"
+ messageLevel.getName() + " " + fooMsg
+ "\n>>>>"
+ "\n\t actual:"
+ "\n<<<<\n" + logged + ">>>>\n");
} else {
verbose("Got expected results for "
+ desc + "\n<<<<\n" + logged + ">>>>\n");
}
}
}
}
String msg = "blah";
for (Level loggerLevel : EnumSet.of(Level.INFO)) {
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
sequencer.incrementAndGet();
logger.log(messageLevel, msg);
if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (!ErrorStream.errorStream.peek().isEmpty()) {
throw new RuntimeException("unexpected event in queue for "
+ desc +": " + "\n\t" + ErrorStream.errorStream.drain());
}
} else {
String logged = ErrorStream.errorStream.drain();
String msgText = loggerBundle == null ? msg : loggerBundle.getString(msg);
if (!logged.contains("LoggerFinderLoaderTest testLogger")
|| !logged.contains(messageLevel.getName() + ": " + msgText)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected:" + "\n<<<<\n"
+ "[date] LoggerFinderLoaderTest testLogger\n"
+ messageLevel.getName() + " " + msgText
+ "\n>>>>"
+ "\n\t actual:"
+ "\n<<<<\n" + logged + ">>>>\n");
} else {
verbose("Got expected results for "
+ desc + "\n<<<<\n" + logged + ">>>>\n");
}
}
}
}
Supplier<String> fooSupplier = new Supplier<String>() {
@Override
public String get() {
return this.toString();
}
};
for (Level loggerLevel : EnumSet.of(Level.INFO)) {
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
sequencer.incrementAndGet();
logger.log(messageLevel, fooSupplier);
if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (!ErrorStream.errorStream.peek().isEmpty()) {
throw new RuntimeException("unexpected event in queue for "
+ desc +": " + "\n\t" + ErrorStream.errorStream.drain());
}
} else {
String logged = ErrorStream.errorStream.drain();
if (!logged.contains("LoggerFinderLoaderTest testLogger")
|| !logged.contains(messageLevel.getName() + ": " + fooSupplier.get())) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected:" + "\n<<<<\n"
+ "[date] LoggerFinderLoaderTest testLogger\n"
+ messageLevel.getName() + " " + fooSupplier.get()
+ "\n>>>>"
+ "\n\t actual:"
+ "\n<<<<\n" + logged + ">>>>\n");
} else {
verbose("Got expected results for "
+ desc + "\n<<<<\n" + logged + ">>>>\n");
}
}
}
}
String format = "two params [{1} {2}]";
Object arg1 = foo;
Object arg2 = msg;
for (Level loggerLevel : EnumSet.of(Level.INFO)) {
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, format, params...): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
sequencer.incrementAndGet();
logger.log(messageLevel, format, foo, msg);
if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (!ErrorStream.errorStream.peek().isEmpty()) {
throw new RuntimeException("unexpected event in queue for "
+ desc +": " + "\n\t" + ErrorStream.errorStream.drain());
}
} else {
String logged = ErrorStream.errorStream.drain();
String msgFormat = loggerBundle == null ? format : loggerBundle.getString(format);
String text = java.text.MessageFormat.format(msgFormat, foo, msg);
if (!logged.contains("LoggerFinderLoaderTest testLogger")
|| !logged.contains(messageLevel.getName() + ": " + text)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected:" + "\n<<<<\n"
+ "[date] LoggerFinderLoaderTest testLogger\n"
+ messageLevel.getName() + " " + text
+ "\n>>>>"
+ "\n\t actual:"
+ "\n<<<<\n" + logged + ">>>>\n");
} else {
verbose("Got expected results for "
+ desc + "\n<<<<\n" + logged + ">>>>\n");
}
}
}
}
Throwable thrown = new Exception("OK: log me!");
for (Level loggerLevel : EnumSet.of(Level.INFO)) {
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
sequencer.incrementAndGet();
logger.log(messageLevel, msg, thrown);
if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (!ErrorStream.errorStream.peek().isEmpty()) {
throw new RuntimeException("unexpected event in queue for "
+ desc +": " + "\n\t" + ErrorStream.errorStream.drain());
}
} else {
String logged = ErrorStream.errorStream.drain();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
thrown.printStackTrace(new PrintStream(baos));
String text = baos.toString();
String msgText = loggerBundle == null ? msg : loggerBundle.getString(msg);
if (!logged.contains("LoggerFinderLoaderTest testLogger")
|| !logged.contains(messageLevel.getName() + ": " + msgText)
|| !logged.contains(text)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected:" + "\n<<<<\n"
+ "[date] LoggerFinderLoaderTest testLogger\n"
+ messageLevel.getName() + " " + msgText +"\n"
+ text
+ ">>>>"
+ "\n\t actual:"
+ "\n<<<<\n" + logged + ">>>>\n");
} else {
verbose("Got expected results for "
+ desc + "\n<<<<\n" + logged + ">>>>\n");
}
}
}
}
for (Level loggerLevel : EnumSet.of(Level.INFO)) {
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
sequencer.incrementAndGet();
logger.log(messageLevel, fooSupplier, thrown);
if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (!ErrorStream.errorStream.peek().isEmpty()) {
throw new RuntimeException("unexpected event in queue for "
+ desc +": " + "\n\t" + ErrorStream.errorStream.drain());
}
} else {
String logged = ErrorStream.errorStream.drain();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
thrown.printStackTrace(new PrintStream(baos));
String text = baos.toString();
if (!logged.contains("LoggerFinderLoaderTest testLogger")
|| !logged.contains(messageLevel.getName() + ": " + fooSupplier.get())
|| !logged.contains(text)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected:" + "\n<<<<\n"
+ "[date] LoggerFinderLoaderTest testLogger\n"
+ messageLevel.getName() + " " + fooSupplier.get() +"\n"
+ text
+ ">>>>"
+ "\n\t actual:"
+ "\n<<<<\n" + logged + ">>>>\n");
} else {
verbose("Got expected results for "
+ desc + "\n<<<<\n" + logged + ">>>>\n");
}
}
}
}
ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
for (Level loggerLevel : EnumSet.of(Level.INFO)) {
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
sequencer.incrementAndGet();
logger.log(messageLevel, bundle, format, foo, msg);
if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (!ErrorStream.errorStream.peek().isEmpty()) {
throw new RuntimeException("unexpected event in queue for "
+ desc +": " + "\n\t" + ErrorStream.errorStream.drain());
}
} else {
String logged = ErrorStream.errorStream.drain();
String text = java.text.MessageFormat.format(bundle.getString(format), foo, msg);
if (!logged.contains("LoggerFinderLoaderTest testLogger")
|| !logged.contains(messageLevel.getName() + ": " + text)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected:" + "\n<<<<\n"
+ "[date] LoggerFinderLoaderTest testLogger\n"
+ messageLevel.getName() + " " + text
+ "\n>>>>"
+ "\n\t actual:"
+ "\n<<<<\n" + logged + ">>>>\n");
} else {
verbose("Got expected results for "
+ desc + "\n<<<<\n" + logged + ">>>>\n");
}
}
}
}
for (Level loggerLevel : EnumSet.of(Level.INFO)) {
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
sequencer.incrementAndGet();
logger.log(messageLevel, bundle, msg, thrown);
if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (!ErrorStream.errorStream.peek().isEmpty()) {
throw new RuntimeException("unexpected event in queue for "
+ desc +": " + "\n\t" + ErrorStream.errorStream.drain());
}
} else {
String logged = ErrorStream.errorStream.drain();
String textMsg = bundle.getString(msg);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
thrown.printStackTrace(new PrintStream(baos));
String text = baos.toString();
if (!logged.contains("LoggerFinderLoaderTest testLogger")
|| !logged.contains(messageLevel.getName() + ": " + textMsg)
|| !logged.contains(text)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected:" + "\n<<<<\n"
+ "[date] LoggerFinderLoaderTest testLogger\n"
+ messageLevel.getName() + " " + textMsg +"\n"
+ text
+ ">>>>"
+ "\n\t actual:"
+ "\n<<<<\n" + logged + ">>>>\n");
} else {
verbose("Got expected results for "
+ desc + "\n<<<<\n" + logged + ">>>>\n");
}
}
}
}
}
}

View file

@ -0,0 +1,107 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.security.AllPermission;
import java.security.Permissions;
import java.security.ProtectionDomain;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* A custom ClassLoader to load the concrete LoggerFinder class
* with all permissions.
*
* @author danielfuchs
*/
public class CustomSystemClassLoader extends ClassLoader {
final ConcurrentHashMap<String, Class<?>> classes = new ConcurrentHashMap<>();
public CustomSystemClassLoader() {
super();
}
public CustomSystemClassLoader(ClassLoader parent) {
super(parent);
}
private Class<?> defineFinderClass(String name)
throws ClassNotFoundException {
Class<?> loggerFinderClass = classes.get(name);
if (loggerFinderClass != null) return loggerFinderClass;
final Object obj = getClassLoadingLock(name);
synchronized(obj) {
loggerFinderClass = classes.get(name);
if (loggerFinderClass != null) return loggerFinderClass;
URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation();
File file = new File(url.getPath(), name+".class");
if (file.canRead()) {
try {
byte[] b = Files.readAllBytes(file.toPath());
Permissions perms = new Permissions();
perms.add(new AllPermission());
loggerFinderClass = defineClass(
name, b, 0, b.length, new ProtectionDomain(
this.getClass().getProtectionDomain().getCodeSource(),
perms));
System.out.println("Loaded " + name);
classes.put(name, loggerFinderClass);
return loggerFinderClass;
} catch (Throwable ex) {
ex.printStackTrace();
throw new ClassNotFoundException(name, ex);
}
} else {
throw new ClassNotFoundException(name,
new IOException(file.toPath() + ": can't read"));
}
}
}
@Override
public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (name.equals("LogProducerFinder") || name.startsWith("LogProducerFinder$")) {
Class<?> c = defineFinderClass(name);
if (resolve) {
resolveClass(c);
}
return c;
}
return super.loadClass(name, resolve);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (name.equals("LogProducerFinder") || name.startsWith("LogProducerFinder$")) {
return defineFinderClass(name);
}
return super.findClass(name);
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Queue;
import java.util.ResourceBundle;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import java.lang.System.LoggerFinder;
import java.lang.System.Logger;
public class LogProducerFinder extends LoggerFinder {
static final RuntimePermission LOGGERFINDER_PERMISSION =
new RuntimePermission("loggerFinder");
final ConcurrentHashMap<String, PlatformLoggerBridgeTest.LoggerImpl>
system = new ConcurrentHashMap<>();
final ConcurrentHashMap<String, PlatformLoggerBridgeTest.LoggerImpl>
user = new ConcurrentHashMap<>();
private static PlatformLoggerBridgeTest.LoggerImpl newLoggerImpl(String name) {
return new PlatformLoggerBridgeTest.LoggerImpl(name);
}
@Override
public Logger getLogger(String name, Module caller) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(LOGGERFINDER_PERMISSION);
}
PrivilegedAction<ClassLoader> pa = () -> caller.getClassLoader();
ClassLoader callerLoader = AccessController.doPrivileged(pa);
if (callerLoader == null) {
return system.computeIfAbsent(name, (n) -> newLoggerImpl(n));
} else {
return user.computeIfAbsent(name, (n) -> newLoggerImpl(n));
}
}
}

View file

@ -0,0 +1,648 @@
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.ResourceBundle;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.lang.System.LoggerFinder;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import sun.util.logging.PlatformLogger;
/**
* @test
* @bug 8140364
* @summary JDK implementation specific unit test for JDK internal artifacts.
* Tests all bridge methods from PlatformLogger with a custom
* backend whose loggers implement PlatformLogger.Bridge.
* @modules java.base/sun.util.logging
* java.logging
* @build CustomSystemClassLoader LogProducerFinder PlatformLoggerBridgeTest
* @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader PlatformLoggerBridgeTest
*/
public class PlatformLoggerBridgeTest {
final static AtomicLong sequencer = new AtomicLong();
final static boolean VERBOSE = false;
public static final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
static final Class<?> providerClass;
static {
try {
// Preload classes before the security manager is on.
providerClass = ClassLoader.getSystemClassLoader().loadClass("LogProducerFinder");
((LoggerFinder)providerClass.newInstance()).getLogger("foo", providerClass.getModule());
} catch (Exception ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static final class LogEvent implements Cloneable {
public LogEvent() {
this(sequencer.getAndIncrement());
}
LogEvent(long sequenceNumber) {
this.sequenceNumber = sequenceNumber;
}
long sequenceNumber;
boolean isLoggable;
String loggerName;
sun.util.logging.PlatformLogger.Level level;
ResourceBundle bundle;
Throwable thrown;
Object[] args;
String msg;
Supplier<String> supplier;
String className;
String methodName;
Object[] toArray() {
return new Object[] {
sequenceNumber,
loggerName,
level,
isLoggable,
bundle,
msg,
supplier,
thrown,
args,
className,
methodName,
};
}
@Override
public String toString() {
return Arrays.deepToString(toArray());
}
@Override
public boolean equals(Object obj) {
return obj instanceof LogEvent
&& Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
}
@Override
public int hashCode() {
return Objects.hash(toArray());
}
public LogEvent cloneWith(long sequenceNumber)
throws CloneNotSupportedException {
LogEvent cloned = (LogEvent)super.clone();
cloned.sequenceNumber = sequenceNumber;
return cloned;
}
public static LogEvent of(long sequenceNumber,
boolean isLoggable, String name,
sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
String key, Throwable thrown, Object... params) {
return LogEvent.of(sequenceNumber, isLoggable, name,
null, null, level, bundle, key,
thrown, params);
}
public static LogEvent of(long sequenceNumber,
boolean isLoggable, String name,
sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
Supplier<String> supplier, Throwable thrown, Object... params) {
return LogEvent.of(sequenceNumber, isLoggable, name,
null, null, level, bundle, supplier,
thrown, params);
}
public static LogEvent of(long sequenceNumber,
boolean isLoggable, String name,
String className, String methodName,
sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
String key, Throwable thrown, Object... params) {
LogEvent evt = new LogEvent(sequenceNumber);
evt.loggerName = name;
evt.level = level;
evt.args = params;
evt.bundle = bundle;
evt.thrown = thrown;
evt.msg = key;
evt.isLoggable = isLoggable;
evt.className = className;
evt.methodName = methodName;
return evt;
}
public static LogEvent of(boolean isLoggable, String name,
String className, String methodName,
sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
String key, Throwable thrown, Object... params) {
return LogEvent.of(sequencer.getAndIncrement(), isLoggable, name,
className, methodName, level, bundle, key, thrown, params);
}
public static LogEvent of(long sequenceNumber,
boolean isLoggable, String name,
String className, String methodName,
sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
Supplier<String> supplier, Throwable thrown, Object... params) {
LogEvent evt = new LogEvent(sequenceNumber);
evt.loggerName = name;
evt.level = level;
evt.args = params;
evt.bundle = bundle;
evt.thrown = thrown;
evt.supplier = supplier;
evt.isLoggable = isLoggable;
evt.className = className;
evt.methodName = methodName;
return evt;
}
public static LogEvent of(boolean isLoggable, String name,
String className, String methodName,
sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
Supplier<String> supplier, Throwable thrown, Object... params) {
return LogEvent.of(sequencer.getAndIncrement(), isLoggable, name,
className, methodName, level, bundle, supplier, thrown, params);
}
}
public static class LoggerImpl implements System.Logger, PlatformLogger.Bridge {
private final String name;
private sun.util.logging.PlatformLogger.Level level = sun.util.logging.PlatformLogger.Level.INFO;
private sun.util.logging.PlatformLogger.Level OFF = sun.util.logging.PlatformLogger.Level.OFF;
private sun.util.logging.PlatformLogger.Level FINE = sun.util.logging.PlatformLogger.Level.FINE;
private sun.util.logging.PlatformLogger.Level FINER = sun.util.logging.PlatformLogger.Level.FINER;
private sun.util.logging.PlatformLogger.Level FINEST = sun.util.logging.PlatformLogger.Level.FINEST;
private sun.util.logging.PlatformLogger.Level CONFIG = sun.util.logging.PlatformLogger.Level.CONFIG;
private sun.util.logging.PlatformLogger.Level INFO = sun.util.logging.PlatformLogger.Level.INFO;
private sun.util.logging.PlatformLogger.Level WARNING = sun.util.logging.PlatformLogger.Level.WARNING;
private sun.util.logging.PlatformLogger.Level SEVERE = sun.util.logging.PlatformLogger.Level.SEVERE;
public LoggerImpl(String name) {
this.name = name;
}
public void configureLevel(sun.util.logging.PlatformLogger.Level level) {
this.level = level;
}
@Override
public String getName() {
return name;
}
@Override
public boolean isLoggable(Level level) {
return this.level != OFF && this.level.intValue() <= level.getSeverity();
}
@Override
public void log(Level level, ResourceBundle bundle,
String key, Throwable thrown) {
throw new UnsupportedOperationException();
}
@Override
public void log(Level level, ResourceBundle bundle,
String format, Object... params) {
throw new UnsupportedOperationException();
}
void log(PlatformLoggerBridgeTest.LogEvent event) {
eventQueue.add(event);
}
@Override
public void log(Level level, Supplier<String> msgSupplier) {
throw new UnsupportedOperationException();
}
@Override
public void log(Level level, Supplier<String> msgSupplier,
Throwable thrown) {
throw new UnsupportedOperationException();
}
@Override
public void log(sun.util.logging.PlatformLogger.Level level, String msg) {
log(LogEvent.of(isLoggable(level), name, null, null,
level, null, msg, null, (Object[]) null));
}
@Override
public void log(sun.util.logging.PlatformLogger.Level level,
Supplier<String> msgSupplier) {
log(LogEvent.of(isLoggable(level), name, null, null,
level, null, msgSupplier, null, (Object[]) null));
}
@Override
public void log(sun.util.logging.PlatformLogger.Level level, String msg,
Object... params) {
log(LogEvent.of(isLoggable(level), name, null, null,
level, null, msg, null, params));
}
@Override
public void log(sun.util.logging.PlatformLogger.Level level, String msg,
Throwable thrown) {
log(LogEvent.of(isLoggable(level), name, null, null,
level, null, msg, thrown, (Object[]) null));
}
@Override
public void log(sun.util.logging.PlatformLogger.Level level, Throwable thrown,
Supplier<String> msgSupplier) {
log(LogEvent.of(isLoggable(level), name, null, null,
level, null, msgSupplier, thrown, (Object[]) null));
}
@Override
public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
String sourceMethod, String msg) {
log(LogEvent.of(isLoggable(level), name,
sourceClass, sourceMethod,
level, null, msg, null, (Object[]) null));
}
@Override
public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
String sourceMethod, Supplier<String> msgSupplier) {
log(LogEvent.of(isLoggable(level), name,
sourceClass, sourceMethod,
level, null, msgSupplier, null, (Object[]) null));
}
@Override
public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
String sourceMethod, String msg, Object... params) {
log(LogEvent.of(isLoggable(level), name,
sourceClass, sourceMethod,
level, null, msg, null, params));
}
@Override
public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
String sourceMethod, String msg, Throwable thrown) {
log(LogEvent.of(isLoggable(level), name,
sourceClass, sourceMethod,
level, null, msg, thrown, (Object[]) null));
}
@Override
public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
String sourceMethod, Throwable thrown,
Supplier<String> msgSupplier) {
log(LogEvent.of(isLoggable(level), name,
sourceClass, sourceMethod,
level, null, msgSupplier, thrown, (Object[]) null));
}
@Override
public void logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass,
String sourceMethod, ResourceBundle bundle, String msg,
Object... params) {
log(LogEvent.of(isLoggable(level), name,
sourceClass, sourceMethod,
level, bundle, msg, null, params));
}
@Override
public void logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
String msg, Object... params) {
log(LogEvent.of(isLoggable(level), name, null, null,
level, bundle, msg, null, params));
}
@Override
public void logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass,
String sourceMethod, ResourceBundle bundle, String msg,
Throwable thrown) {
log(LogEvent.of(isLoggable(level), name,
sourceClass, sourceMethod,
level, bundle, msg, thrown, (Object[]) null));
}
@Override
public void logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
String msg, Throwable thrown) {
log(LogEvent.of(isLoggable(level), name, null, null,
level, bundle, msg, thrown, (Object[]) null));
}
@Override
public boolean isLoggable(sun.util.logging.PlatformLogger.Level level) {
return this.level != OFF && level.intValue()
>= this.level.intValue();
}
@Override
public boolean isEnabled() {
return this.level != OFF;
}
}
static final sun.util.logging.PlatformLogger.Level[] julLevels = {
sun.util.logging.PlatformLogger.Level.ALL,
sun.util.logging.PlatformLogger.Level.FINEST,
sun.util.logging.PlatformLogger.Level.FINER,
sun.util.logging.PlatformLogger.Level.FINE,
sun.util.logging.PlatformLogger.Level.CONFIG,
sun.util.logging.PlatformLogger.Level.INFO,
sun.util.logging.PlatformLogger.Level.WARNING,
sun.util.logging.PlatformLogger.Level.SEVERE,
sun.util.logging.PlatformLogger.Level.OFF,
};
public static class MyBundle extends ResourceBundle {
final ConcurrentHashMap map = new ConcurrentHashMap();
@Override
protected Object handleGetObject(String key) {
if (key.contains(" (translated)")) {
throw new RuntimeException("Unexpected key: " + key);
}
return map.computeIfAbsent(key, k -> k + " (translated)");
}
@Override
public Enumeration<String> getKeys() {
return Collections.enumeration(map.keySet());
}
}
public static class MyHandler extends Handler {
@Override
public java.util.logging.Level getLevel() {
return java.util.logging.Level.ALL;
}
@Override
public void publish(LogRecord record) {
eventQueue.add(LogEvent.of(sequencer.getAndIncrement(),
true, record.getLoggerName(),
record.getSourceClassName(),
record.getSourceMethodName(),
PlatformLogger.Level.valueOf(record.getLevel().getName()),
record.getResourceBundle(), record.getMessage(),
record.getThrown(), record.getParameters()));
}
@Override
public void flush() {
}
@Override
public void close() throws SecurityException {
}
}
public static class MyLoggerBundle extends MyBundle {
}
public static void main(String[] args) {
System.out.println("\n*** Without Security Manager\n");
LoggerFinder provider = LoggerFinder.getLoggerFinder();
test(provider);
System.out.println("Tetscase count: " + sequencer.get());
System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
}
public static void test(LoggerFinder provider) {
final Map<PlatformLogger, String> loggerDescMap = new HashMap<>();
PlatformLogger sysLogger1 = PlatformLogger.getLogger("foo");
loggerDescMap.put(sysLogger1, "PlatformLogger.getLogger(\"foo\")");
final LoggerImpl sysSink = LoggerImpl.class.cast(
provider.getLogger("foo", Thread.class.getModule()));
testLogger(provider, loggerDescMap, "foo", null, sysLogger1, sysSink);
}
public static class Foo {
}
static void verbose(String msg) {
if (VERBOSE) {
System.out.println(msg);
}
}
static void checkLogEvent(LoggerFinder provider, String desc,
LogEvent expected) {
LogEvent actual = eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
static void checkLogEvent(LoggerFinder provider, String desc,
LogEvent expected, boolean expectNotNull) {
LogEvent actual = eventQueue.poll();
if (actual == null && !expectNotNull) return;
if (actual != null && !expectNotNull) {
throw new RuntimeException("Unexpected log event found for " + desc
+ "\n\tgot: " + actual);
}
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
static void setLevel(LoggerImpl sink,
sun.util.logging.PlatformLogger.Level loggerLevel) {
sink.configureLevel(loggerLevel);
}
// Calls the methods defined on LogProducer and verify the
// parameters received by the underlying LoggerImpl
// logger.
private static void testLogger(LoggerFinder provider,
Map<PlatformLogger, String> loggerDescMap,
String name,
ResourceBundle loggerBundle,
PlatformLogger logger,
LoggerImpl sink) {
System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger +"]");
final sun.util.logging.PlatformLogger.Level OFF = sun.util.logging.PlatformLogger.Level.OFF;
final sun.util.logging.PlatformLogger.Level ALL = sun.util.logging.PlatformLogger.Level.OFF;
Foo foo = new Foo();
String fooMsg = foo.toString();
System.out.println("\tlogger.log(messageLevel, fooMsg)");
System.out.println("\tlogger.<level>(fooMsg)");
for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
if (messageLevel == ALL || messageLevel == OFF) continue;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, messageLevel, loggerBundle,
fooMsg, (Throwable)null, (Object[])null);
String desc2 = "logger." + messageLevel.toString().toLowerCase()
+ "(fooMsg): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
if (messageLevel == sun.util.logging.PlatformLogger.Level.FINEST) {
logger.finest(fooMsg);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == sun.util.logging.PlatformLogger.Level.FINER) {
logger.finer(fooMsg);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == sun.util.logging.PlatformLogger.Level.FINE) {
logger.fine(fooMsg);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == sun.util.logging.PlatformLogger.Level.CONFIG) {
logger.config(fooMsg);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == sun.util.logging.PlatformLogger.Level.INFO) {
logger.info(fooMsg);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == sun.util.logging.PlatformLogger.Level.WARNING) {
logger.warning(fooMsg);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == sun.util.logging.PlatformLogger.Level.SEVERE) {
logger.severe(fooMsg);
checkLogEvent(provider, desc2, expected);
}
}
}
String format = "two params [{1} {2}]";
Object arg1 = foo;
Object arg2 = fooMsg;
System.out.println("\tlogger.log(messageLevel, format, arg1, arg2)");
for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
if (messageLevel == ALL || messageLevel == OFF) continue;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, messageLevel, loggerBundle,
format, (Throwable)null, arg1, arg2);
String desc2 = "logger." + messageLevel.toString().toLowerCase()
+ "(format, foo, fooMsg): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
if (messageLevel == sun.util.logging.PlatformLogger.Level.FINEST) {
logger.finest(format, arg1, arg2);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == sun.util.logging.PlatformLogger.Level.FINER) {
logger.finer(format, arg1, arg2);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == sun.util.logging.PlatformLogger.Level.FINE) {
logger.fine(format, arg1, arg2);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == sun.util.logging.PlatformLogger.Level.CONFIG) {
logger.config(format, arg1, arg2);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == sun.util.logging.PlatformLogger.Level.INFO) {
logger.info(format, arg1, arg2);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == sun.util.logging.PlatformLogger.Level.WARNING) {
logger.warning(format, arg1, arg2);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == sun.util.logging.PlatformLogger.Level.SEVERE) {
logger.severe(format, arg1, arg2);
checkLogEvent(provider, desc2, expected);
}
}
}
Throwable thrown = new Exception("OK: log me!");
System.out.println("\tlogger.log(messageLevel, fooMsg, thrown)");
for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
if (messageLevel == ALL || messageLevel == OFF) continue;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, messageLevel, loggerBundle,
fooMsg, thrown, (Object[])null);
String desc2 = "logger." + messageLevel.toString().toLowerCase()
+ "(fooMsg, thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
if (messageLevel == sun.util.logging.PlatformLogger.Level.FINEST) {
logger.finest(fooMsg, thrown);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == sun.util.logging.PlatformLogger.Level.FINER) {
logger.finer(fooMsg, thrown);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == sun.util.logging.PlatformLogger.Level.FINE) {
logger.fine(fooMsg, thrown);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == sun.util.logging.PlatformLogger.Level.CONFIG) {
logger.config(fooMsg, thrown);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == sun.util.logging.PlatformLogger.Level.INFO) {
logger.info(fooMsg, thrown);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == sun.util.logging.PlatformLogger.Level.WARNING) {
logger.warning(fooMsg, thrown);
checkLogEvent(provider, desc2, expected);
} else if (messageLevel == sun.util.logging.PlatformLogger.Level.SEVERE) {
logger.severe(fooMsg, thrown);
checkLogEvent(provider, desc2, expected);
}
}
}
}
}

View file

@ -0,0 +1,561 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.UncheckedIOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.ResourceBundle;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import jdk.internal.logger.SimpleConsoleLogger;
import jdk.internal.logger.SurrogateLogger;
import sun.util.logging.PlatformLogger;
/**
* @test
* @bug 8140364
* @summary JDK implementation specific unit test for SimpleConsoleLogger.
* Tests the behavior of SimpleConsoleLogger.
* @modules java.base/sun.util.logging
* java.base/jdk.internal.logger
* @build SimpleConsoleLoggerTest
* @run main/othervm SimpleConsoleLoggerTest
* @run main/othervm -Djdk.system.logger.level=OFF SimpleConsoleLoggerTest
* @run main/othervm -Djdk.system.logger.level=ERROR SimpleConsoleLoggerTest
* @run main/othervm -Djdk.system.logger.level=WARNING SimpleConsoleLoggerTest
* @run main/othervm -Djdk.system.logger.level=INFO SimpleConsoleLoggerTest
* @run main/othervm -Djdk.system.logger.level=DEBUG SimpleConsoleLoggerTest
* @run main/othervm -Djdk.system.logger.level=TRACE SimpleConsoleLoggerTest
* @run main/othervm -Djdk.system.logger.level=ALL SimpleConsoleLoggerTest
* @run main/othervm -Djdk.system.logger.level=WOMBAT SimpleConsoleLoggerTest
* @run main/othervm -Djdk.system.logger.level SimpleConsoleLoggerTest
* @run main/othervm -Djdk.system.logger.level=FINEST SimpleConsoleLoggerTest
* @run main/othervm -Djdk.system.logger.level=DEBUG -Djava.util.logging.SimpleFormatter.format=++++_%2$s%n%4$s:_%5$s%6$s%n SimpleConsoleLoggerTest
* @run main/othervm -Djdk.system.logger.level=DEBUG -Djdk.system.logger.format=++++_%2$s%n%4$s:_%5$s%6$s%n SimpleConsoleLoggerTest
*
* @author danielfuchs
*/
public class SimpleConsoleLoggerTest {
static final RuntimePermission LOGGERFINDER_PERMISSION =
new RuntimePermission("loggerFinder");
final static boolean VERBOSE = false;
public static class MyBundle extends ResourceBundle {
final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
@Override
protected Object handleGetObject(String key) {
if (key.contains(" (translated)")) {
throw new RuntimeException("Unexpected key: " + key);
}
return map.computeIfAbsent(key, k -> k.toUpperCase(Locale.ROOT) + " (translated)");
}
@Override
public Enumeration<String> getKeys() {
return Collections.enumeration(map.keySet());
}
}
public static class MyLoggerBundle extends MyBundle {
}
static class ErrorStream extends PrintStream {
static AtomicBoolean forward = new AtomicBoolean();
ByteArrayOutputStream out;
String saved = "";
public ErrorStream(ByteArrayOutputStream out) {
super(out);
this.out = out;
}
@Override
public void write(int b) {
super.write(b);
if (forward.get()) err.write(b);
}
@Override
public void write(byte[] b) throws IOException {
super.write(b);
if (forward.get()) err.write(b);
}
@Override
public void write(byte[] buf, int off, int len) {
super.write(buf, off, len);
if (forward.get()) err.write(buf, off, len);
}
public String peek() {
flush();
return out.toString();
}
public String drain() {
flush();
String res = out.toString();
out.reset();
return res;
}
public void store() {
flush();
saved = out.toString();
out.reset();
}
public void restore() {
out.reset();
try {
out.write(saved.getBytes());
} catch(IOException io) {
throw new UncheckedIOException(io);
}
}
static final PrintStream err = System.err;
static final ErrorStream errorStream = new ErrorStream(new ByteArrayOutputStream());
}
private static StringBuilder appendProperty(StringBuilder b, String name) {
String value = System.getProperty(name);
if (value == null) return b;
return b.append(name).append("=").append(value).append('\n');
}
public static void main(String[] args) {
Locale.setDefault(Locale.ENGLISH);
System.setErr(ErrorStream.errorStream);
try {
test(args);
} finally {
try {
System.setErr(ErrorStream.err);
} catch (Error | RuntimeException x) {
x.printStackTrace(ErrorStream.err);
}
}
}
public static void test(String[] args) {
ErrorStream.errorStream.restore();
String l = System.getProperty("jdk.system.logger.level");
String f = System.getProperty("jdk.system.logger.format");
String jf = System.getProperty("java.util.logging.SimpleFormatter.format");
System.out.println("Running test: "
+ "\n\tjdk.system.logger.level=\"" + l + "\""
+ "\n\tjdk.system.logger.format=\"" + f + "\""
+ "\n\tjava.util.logging.SimpleFormatter.format=\"" + jf + "\"");
test(l,f,jf);
System.out.println("\nPASSED: tested " + SEQUENCER.get() + " test cases");
}
static final AtomicLong SEQUENCER = new AtomicLong();
public static void test(String defaultLevel, String defaultFormat, String julFormat) {
final Map<Logger, String> loggerDescMap = new HashMap<>();
SimpleConsoleLogger simple = SimpleConsoleLogger.makeSimpleLogger("test.logger");
loggerDescMap.put(simple, "SimpleConsoleLogger.makeSimpleLogger(\"test.logger\")");
SimpleConsoleLogger temporary = SurrogateLogger.makeSurrogateLogger("test.logger");
loggerDescMap.put(temporary, "SurrogateLogger.makeSimpleLogger(\"test.logger\")");
Level level;
try {
level = defaultLevel == null ? null : Level.valueOf(defaultLevel);
} catch (IllegalArgumentException ex) {
level = null;
}
testLogger(loggerDescMap, simple, level, false, defaultFormat);
testLogger(loggerDescMap, temporary, null, true, julFormat);
}
public static class Foo {
}
static void verbose(String msg) {
if (VERBOSE) {
System.out.println(msg);
}
}
static String getName(Level level, boolean usePlatformLevel) {
if (usePlatformLevel) {
return PlatformLogger.toPlatformLevel(level).name();
} else {
return level.getName();
}
}
// Calls the 8 methods defined on Logger and verify the
// parameters received by the underlying TestProvider.LoggerImpl
// logger.
private static void testLogger(Map<Logger, String> loggerDescMap,
SimpleConsoleLogger simple,
Level defaultLevel,
boolean usePlatformLevel,
String defaultFormat) {
System.out.println("Testing " + loggerDescMap.get(simple) + " [" + simple +"]");
String formatStrMarker = defaultFormat == null ? ""
: defaultFormat.startsWith("++++") ? "++++" : "";
String unexpectedMarker = defaultFormat == null ? "++++"
: defaultFormat.startsWith("++++") ? "????" : "++++";
String formatStrSpec = defaultFormat == null ? "[date]"
: defaultFormat.startsWith("++++") ? "++++" : "????";
String sep = defaultFormat == null ? ": " : ":_";
String sepw = defaultFormat == null ? " " : "_";
Foo foo = new Foo();
String fooMsg = foo.toString();
for (Level loggerLevel : defaultLevel == null
? EnumSet.of(Level.INFO) : EnumSet.of(defaultLevel)) {
for (Level messageLevel : Level.values()) {
ErrorStream.errorStream.drain();
String desc = "logger.log(messageLevel, foo): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
SEQUENCER.incrementAndGet();
simple.log(messageLevel, foo);
if (loggerLevel == Level.OFF || messageLevel == Level.OFF
|| messageLevel.compareTo(loggerLevel) < 0) {
if (!ErrorStream.errorStream.peek().isEmpty()) {
throw new RuntimeException("unexpected event in queue for "
+ desc +": " + "\n\t" + ErrorStream.errorStream.drain());
}
} else {
String logged = ErrorStream.errorStream.drain();
String expected = getName(messageLevel, usePlatformLevel) + sep + fooMsg;
if (!logged.contains("SimpleConsoleLoggerTest testLogger")
|| !logged.contains(formatStrMarker)
|| logged.contains(unexpectedMarker)
|| !logged.contains(expected)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected:" + "\n<<<<\n"
+ formatStrSpec + sepw + "SimpleConsoleLoggerTest testLogger\n"
+ expected
+ "\n>>>>"
+ "\n\t actual:"
+ "\n<<<<\n" + logged + ">>>>\n");
} else {
verbose("Got expected results for "
+ desc + "\n<<<<\n" + logged + ">>>>\n");
}
}
}
}
String msg = "blah";
for (Level loggerLevel : defaultLevel == null
? EnumSet.of(Level.INFO) : EnumSet.of(defaultLevel)) {
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
SEQUENCER.incrementAndGet();
simple.log(messageLevel, msg);
if (loggerLevel == Level.OFF || messageLevel == Level.OFF
|| messageLevel.compareTo(loggerLevel) < 0) {
if (!ErrorStream.errorStream.peek().isEmpty()) {
throw new RuntimeException("unexpected event in queue for "
+ desc +": " + "\n\t" + ErrorStream.errorStream.drain());
}
} else {
String logged = ErrorStream.errorStream.drain();
String expected = getName(messageLevel, usePlatformLevel) + sep + msg;
if (!logged.contains("SimpleConsoleLoggerTest testLogger")
|| !logged.contains(formatStrMarker)
|| logged.contains(unexpectedMarker)
|| !logged.contains(expected)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected:" + "\n<<<<\n"
+ formatStrSpec + sepw + "SimpleConsoleLoggerTest testLogger\n"
+ expected
+ "\n>>>>"
+ "\n\t actual:"
+ "\n<<<<\n" + logged + ">>>>\n");
} else {
verbose("Got expected results for "
+ desc + "\n<<<<\n" + logged + ">>>>\n");
}
}
}
}
Supplier<String> fooSupplier = new Supplier<String>() {
@Override
public String get() {
return this.toString();
}
};
for (Level loggerLevel : defaultLevel == null
? EnumSet.of(Level.INFO) : EnumSet.of(defaultLevel)) {
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
SEQUENCER.incrementAndGet();
simple.log(messageLevel, fooSupplier);
if (loggerLevel == Level.OFF || messageLevel == Level.OFF
|| messageLevel.compareTo(loggerLevel) < 0) {
if (!ErrorStream.errorStream.peek().isEmpty()) {
throw new RuntimeException("unexpected event in queue for "
+ desc +": " + "\n\t" + ErrorStream.errorStream.drain());
}
} else {
String logged = ErrorStream.errorStream.drain();
String expected = getName(messageLevel, usePlatformLevel) + sep + fooSupplier.get();
if (!logged.contains("SimpleConsoleLoggerTest testLogger")
|| !logged.contains(formatStrMarker)
|| logged.contains(unexpectedMarker)
|| !logged.contains(expected)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected:" + "\n<<<<\n"
+ formatStrSpec + sepw + "SimpleConsoleLoggerTest testLogger\n"
+ expected
+ "\n>>>>"
+ "\n\t actual:"
+ "\n<<<<\n" + logged + ">>>>\n");
} else {
verbose("Got expected results for "
+ desc + "\n<<<<\n" + logged + ">>>>\n");
}
}
}
}
String format = "two params [{1} {2}]";
Object arg1 = foo;
Object arg2 = msg;
for (Level loggerLevel : defaultLevel == null
? EnumSet.of(Level.INFO) : EnumSet.of(defaultLevel)) {
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, format, params...): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
SEQUENCER.incrementAndGet();
simple.log(messageLevel, format, foo, msg);
if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (!ErrorStream.errorStream.peek().isEmpty()) {
throw new RuntimeException("unexpected event in queue for "
+ desc +": " + "\n\t" + ErrorStream.errorStream.drain());
}
} else {
String logged = ErrorStream.errorStream.drain();
String msgFormat = format;
String text = java.text.MessageFormat.format(msgFormat, foo, msg);
String expected = getName(messageLevel, usePlatformLevel) + sep + text;
if (!logged.contains("SimpleConsoleLoggerTest testLogger")
|| !logged.contains(formatStrMarker)
|| !logged.contains(expected)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected:" + "\n<<<<\n"
+ formatStrSpec + sepw + "SimpleConsoleLoggerTest testLogger\n"
+ expected
+ "\n>>>>"
+ "\n\t actual:"
+ "\n<<<<\n" + logged + ">>>>\n");
} else {
verbose("Got expected results for "
+ desc + "\n<<<<\n" + logged + ">>>>\n");
}
}
}
}
Throwable thrown = new Exception("OK: log me!");
for (Level loggerLevel : defaultLevel == null
? EnumSet.of(Level.INFO) : EnumSet.of(defaultLevel)) {
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
SEQUENCER.incrementAndGet();
simple.log(messageLevel, msg, thrown);
if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (!ErrorStream.errorStream.peek().isEmpty()) {
throw new RuntimeException("unexpected event in queue for "
+ desc +": " + "\n\t" + ErrorStream.errorStream.drain());
}
} else {
String logged = ErrorStream.errorStream.drain();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
thrown.printStackTrace(new PrintStream(baos));
String text = baos.toString();
String expected = getName(messageLevel, usePlatformLevel) + sep + msg;
if (!logged.contains("SimpleConsoleLoggerTest testLogger")
|| !logged.contains(formatStrMarker)
|| !logged.contains(expected)
|| logged.contains(unexpectedMarker)
|| !logged.contains(text)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected:" + "\n<<<<\n"
+ formatStrSpec + sepw + "SimpleConsoleLoggerTest testLogger\n"
+ msg +"\n"
+ text
+ ">>>>"
+ "\n\t actual:"
+ "\n<<<<\n" + logged + ">>>>\n");
} else {
verbose("Got expected results for "
+ desc + "\n<<<<\n" + logged + ">>>>\n");
}
}
}
}
for (Level loggerLevel : defaultLevel == null
? EnumSet.of(Level.INFO) : EnumSet.of(defaultLevel)) {
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
SEQUENCER.incrementAndGet();
simple.log(messageLevel, fooSupplier, thrown);
if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (!ErrorStream.errorStream.peek().isEmpty()) {
throw new RuntimeException("unexpected event in queue for "
+ desc +": " + "\n\t" + ErrorStream.errorStream.drain());
}
} else {
String logged = ErrorStream.errorStream.drain();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
thrown.printStackTrace(new PrintStream(baos));
String text = baos.toString();
String expected = getName(messageLevel, usePlatformLevel) + sep + fooSupplier.get();
if (!logged.contains("SimpleConsoleLoggerTest testLogger")
|| !logged.contains(formatStrMarker)
|| !logged.contains(expected)
|| logged.contains(unexpectedMarker)
|| !logged.contains(text)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected:" + "\n<<<<\n"
+ formatStrSpec + sepw + "SimpleConsoleLoggerTest testLogger\n"
+ expected +"\n"
+ text
+ ">>>>"
+ "\n\t actual:"
+ "\n<<<<\n" + logged + ">>>>\n");
} else {
verbose("Got expected results for "
+ desc + "\n<<<<\n" + logged + ">>>>\n");
}
}
}
}
ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
for (Level loggerLevel : defaultLevel == null
? EnumSet.of(Level.INFO) : EnumSet.of(defaultLevel)) {
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
SEQUENCER.incrementAndGet();
simple.log(messageLevel, bundle, format, foo, msg);
if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (!ErrorStream.errorStream.peek().isEmpty()) {
throw new RuntimeException("unexpected event in queue for "
+ desc +": " + "\n\t" + ErrorStream.errorStream.drain());
}
} else {
String logged = ErrorStream.errorStream.drain();
String text = java.text.MessageFormat.format(bundle.getString(format), foo, msg);
if (!logged.contains("SimpleConsoleLoggerTest testLogger")
|| !logged.contains(formatStrMarker)
|| logged.contains(unexpectedMarker)
|| !logged.contains(getName(messageLevel, usePlatformLevel) + sep + text)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected:" + "\n<<<<\n"
+ formatStrSpec + sepw + "SimpleConsoleLoggerTest testLogger\n"
+ getName(messageLevel, usePlatformLevel) + " " + text
+ "\n>>>>"
+ "\n\t actual:"
+ "\n<<<<\n" + logged + ">>>>\n");
} else {
verbose("Got expected results for "
+ desc + "\n<<<<\n" + logged + ">>>>\n");
}
}
}
}
for (Level loggerLevel : defaultLevel == null
? EnumSet.of(Level.INFO) : EnumSet.of(defaultLevel)) {
for (Level messageLevel : Level.values()) {
String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
SEQUENCER.incrementAndGet();
simple.log(messageLevel, bundle, msg, thrown);
if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
if (!ErrorStream.errorStream.peek().isEmpty()) {
throw new RuntimeException("unexpected event in queue for "
+ desc +": " + "\n\t" + ErrorStream.errorStream.drain());
}
} else {
String logged = ErrorStream.errorStream.drain();
String textMsg = bundle.getString(msg);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
thrown.printStackTrace(new PrintStream(baos));
String text = baos.toString();
String expected = getName(messageLevel, usePlatformLevel) + sep + textMsg;
if (!logged.contains("SimpleConsoleLoggerTest testLogger")
|| !logged.contains(formatStrMarker)
|| !logged.contains(expected)
|| logged.contains(unexpectedMarker)
|| !logged.contains(text)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected:" + "\n<<<<\n"
+ formatStrSpec + sepw + "SimpleConsoleLoggerTest testLogger\n"
+ expected +"\n"
+ text
+ ">>>>"
+ "\n\t actual:"
+ "\n<<<<\n" + logged + ">>>>\n");
} else {
verbose("Got expected results for "
+ desc + "\n<<<<\n" + logged + ">>>>\n");
}
}
}
}
}
}

View file

@ -0,0 +1,132 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/*
* @test
* @bug 8163162
* @summary Checks that LazyLoggers are returned for System.Logger instances
* created by modules in the platform class loader.
* @modules java.base/java.lang:open
* @build systempkg.log.SystemLoggerAccessor SystemLoggerInPlatformLoader
* @run main/othervm SystemLoggerInPlatformLoader
* @author danielfuchs
*/
public class SystemLoggerInPlatformLoader {
static final class PlatformClassLoaderChild extends ClassLoader {
private PlatformClassLoaderChild() {
super(ClassLoader.getPlatformClassLoader());
}
public Class<?> definePlatformClass(String name) throws IOException {
String testClasses = System.getProperty("test.classes", "./build/classes");
String fname = name.replace('.', '/').concat(".class");
try (InputStream is = new FileInputStream(new File(testClasses, fname))) {
byte[] b = is.readAllBytes();
ClassLoader parent = getParent();
try {
Method m = ClassLoader.class.getDeclaredMethod("defineClass",
String.class, byte[].class, int.class, int.class);
m.setAccessible(true);
return (Class<?>)m.invoke(parent, name, b, 0, b.length);
} catch (NoSuchMethodException
| IllegalAccessException
| InvocationTargetException ex) {
throw new IOException(ex);
}
}
}
static final PlatformClassLoaderChild INSTANCE = new PlatformClassLoaderChild();
static Class<?> loadLoggerAccessor() throws IOException {
return INSTANCE.definePlatformClass("systempkg.log.SystemLoggerAccessor");
}
}
static final Class<?> LOGGER_ACCESSOR_CLASS;
static {
try {
LOGGER_ACCESSOR_CLASS = PlatformClassLoaderChild.loadLoggerAccessor();
ClassLoader platformCL = ClassLoader.getPlatformClassLoader();
if (LOGGER_ACCESSOR_CLASS.getClassLoader() != platformCL) {
throw new ExceptionInInitializerError(
"Could not load accessor class in platform class loader: "
+ LOGGER_ACCESSOR_CLASS.getClassLoader());
}
} catch (IOException ex) {
throw new ExceptionInInitializerError(ex);
}
}
// Returns a system logger created on behalf of a class loaded by the
// Platform ClassLoader
static System.Logger getSystemLogger(String name) {
try {
return (System.Logger)LOGGER_ACCESSOR_CLASS.getMethod(
"getSystemLogger", String.class).invoke(null, name);
} catch (NoSuchMethodException
| IllegalAccessException
| InvocationTargetException ex) {
throw new RuntimeException("Failed to invoke LoggerAccessor.getJULLogger", ex);
}
}
public static void main(String[] args) {
System.Logger platformLogger = getSystemLogger("bar"); // for a platform class
System.Logger appLogger = System.getLogger("bar"); // for an application class
if (appLogger == platformLogger) {
throw new RuntimeException("Same loggers");
}
Class<?> platformLoggerType = platformLogger.getClass();
System.out.println("platformLogger: " + platformLoggerType);
boolean simpleConsoleOnly = !ModuleLayer.boot().findModule("java.logging").isPresent();
if (simpleConsoleOnly) {
/* Happens if the test is called with custom JDK without java.logging module
or in case usage commandline option --limit-modules java.base */
if (!platformLoggerType.getSimpleName().equals("SimpleConsoleLogger")) {
throw new RuntimeException(platformLoggerType.getSimpleName()
+ ": unexpected class for platform logger"
+ " (expected a simple console logger class)");
}
} else {
if (!platformLoggerType.getSimpleName().equals("JdkLazyLogger")) {
throw new RuntimeException(platformLoggerType.getSimpleName()
+ ": unexpected class for platform logger"
+ " (expected a lazy logger for a platform class)");
}
Class<?> appLoggerType = appLogger.getClass();
System.out.println("appLogger: " + appLoggerType);
if (appLoggerType.equals(platformLoggerType)) {
throw new RuntimeException(appLoggerType
+ ": unexpected class for application logger"
+ " (a lazy logger was not expected"
+ " for a non platform class)");
}
}
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package systempkg.log;
/**
* Utility class that returns loggers created for classes in the
* systempkg.log package;
* This class should be loaded in the {@linkplain
* ClassLoader#getPlatformClassLoader() platform class loader}
* for the purpose of the test.
* @author danielfuchs
*/
public class SystemLoggerAccessor {
public static System.Logger getSystemLogger(String name) {
return System.getLogger(name);
}
}

View file

@ -0,0 +1,498 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8140364
* @author danielfuchs
* @summary JDK implementation specific unit test for JDK internal artifacts.
* Tests the consistency of the LoggerFinder and JDK extensions.
* @modules java.base/sun.util.logging
* java.base/jdk.internal.logger
* java.logging
* @run main LoggerFinderAPITest
*/
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.function.Supplier;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import sun.util.logging.PlatformLogger;
public class LoggerFinderAPITest {
static final Class<java.lang.System.Logger> spiLoggerClass
= java.lang.System.Logger.class;
static final Class<java.lang.System.Logger> jdkLoggerClass
= java.lang.System.Logger.class;
static final Class<sun.util.logging.PlatformLogger.Bridge> bridgeLoggerClass
= sun.util.logging.PlatformLogger.Bridge.class;
static final Class<java.util.logging.Logger> julLoggerClass
= java.util.logging.Logger.class;
static final Class<sun.util.logging.PlatformLogger.Bridge> julLogProducerClass
= PlatformLogger.Bridge.class;
static final Pattern julLogNames = Pattern.compile(
"^((log(p|rb)?)|severe|warning|info|config|fine|finer|finest|isLoggable)$");
static final Collection<Method> julLoggerIgnores;
static {
List<Method> ignores = new ArrayList<>();
try {
ignores.add(julLoggerClass.getDeclaredMethod("log", LogRecord.class));
} catch (NoSuchMethodException | SecurityException ex) {
throw new ExceptionInInitializerError(ex);
}
julLoggerIgnores = Collections.unmodifiableList(ignores);
}
// Don't require LoggerBridge to have a body for those methods
interface LoggerBridgeMethodsWithNoBody extends
PlatformLogger.Bridge, java.lang.System.Logger {
@Override
public default String getName() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public default boolean isLoggable(PlatformLogger.Level level) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public default void log(sun.util.logging.PlatformLogger.Level level,
String msg, Throwable thrown) {
}
@Override
public default void log(sun.util.logging.PlatformLogger.Level level,
Throwable thrown, Supplier<String> msgSupplier) {
}
@Override
public default void log(sun.util.logging.PlatformLogger.Level level,
Supplier<String> msgSupplier) {
}
@Override
public default void log(sun.util.logging.PlatformLogger.Level level, String msg) {
}
@Override
public default void log(sun.util.logging.PlatformLogger.Level level,
String format, Object... params) {
}
@Override
public default void logrb(sun.util.logging.PlatformLogger.Level level,
ResourceBundle bundle, String key, Throwable thrown) {
}
@Override
public default void logrb(sun.util.logging.PlatformLogger.Level level,
ResourceBundle bundle, String format, Object... params) {
}
@Override
public default void logrb(PlatformLogger.Level level,
String sourceClass, String sourceMethod,
ResourceBundle bundle, String msg, Throwable thrown) {
}
@Override
public default void logrb(PlatformLogger.Level level, String sourceClass,
String sourceMethod, ResourceBundle bundle, String msg,
Object... params) {
}
@Override
public default void logp(PlatformLogger.Level level, String sourceClass,
String sourceMethod, Supplier<String> msgSupplier) {
}
@Override
public default void logp(PlatformLogger.Level level, String sourceClass,
String sourceMethod, String msg, Object... params) {
}
@Override
public default void logp(PlatformLogger.Level level, String sourceClass,
String sourceMethod, String msg, Throwable thrown) {
}
@Override
public default void logp(PlatformLogger.Level level, String sourceClass,
String sourceMethod, String msg) {
}
@Override
public default void logp(PlatformLogger.Level level, String sourceClass,
String sourceMethod, Throwable thrown,
Supplier<String> msgSupplier) {
}
static boolean requiresDefaultBodyFor(Method m) {
try {
Method m2 = LoggerBridgeMethodsWithNoBody.class
.getDeclaredMethod(m.getName(),
m.getParameterTypes());
return !m2.isDefault();
} catch (NoSuchMethodException x) {
return true;
}
}
}
final boolean warnDuplicateMappings;
public LoggerFinderAPITest(boolean verbose) {
this.warnDuplicateMappings = verbose;
for (Handler h : Logger.getLogger("").getHandlers()) {
if (h instanceof ConsoleHandler) {
Logger.getLogger("").removeHandler(h);
}
}
Logger.getLogger("").addHandler( new Handler() {
@Override
public void publish(LogRecord record) {
StringBuilder builder = new StringBuilder();
builder.append("GOT LogRecord: ")
.append(record.getLevel().getLocalizedName())
.append(": [").append(record.getLoggerName())
.append("] ").append(record.getSourceClassName())
.append('.')
.append(record.getSourceMethodName()).append(" -> ")
.append(record.getMessage())
.append(' ')
.append(record.getParameters() == null ? ""
: Arrays.toString(record.getParameters()))
;
System.out.println(builder);
if (record.getThrown() != null) {
record.getThrown().printStackTrace(System.out);
}
}
@Override public void flush() {}
@Override public void close() {}
});
}
public Stream<Method> getJulLogMethodStream(Class<?> loggerClass) {
return Stream.of(loggerClass.getMethods()).filter((x) -> {
final Matcher m = julLogNames.matcher(x.getName());
return m.matches() ? x.getAnnotation(Deprecated.class) == null : false;
});
}
/**
* Tells whether a method invocation of 'origin' can be transformed in a
* method invocation of 'target'.
* This method only look at the parameter signatures, it doesn't look at
* the name, nor does it look at the return types.
* <p>
* Example:
* <ul>
* <li>java.util.logging.Logger.log(Level, String, Object) can be invoked as<br>
java.util.logging.spi.Logger.log(Level, String, Object...) because the
last parameter in 'target' is a varargs.</li>
* <li>java.util.logging.Logger.log(Level, String) can also be invoked as<br>
java.util.logging.spi.Logger.log(Level, String, Object...) for the
same reason.</li>
* </ul>
* <p>
* The algorithm is tailored for our needs: when the last parameter in the
* target is a vararg, and when origin & target have the same number of
* parameters, then we consider that the types of the last parameter *must*
* match.
* <p>
* Similarly - we do not consider that o(X x, Y y, Y y) matches t(X x, Y... y)
* although strictly speaking, it should...
*
* @param origin The method in the original class
* @param target The correspondent candidate in the target class
* @return true if a method invocation of 'origin' can be transformed in a
* method invocation of 'target'.
*/
public boolean canBeInvokedAs(Method origin, Method target,
Map<Class<?>,Class<?>> substitutes) {
final Class<?>[] xParams = target.getParameterTypes();
final Class<?>[] mParams = Stream.of(origin.getParameterTypes())
.map((x) -> substitutes.getOrDefault(x, x))
.collect(Collectors.toList()).toArray(new Class<?>[0]);
if (Arrays.deepEquals(xParams, mParams)) return true;
if (target.isVarArgs()) {
if (xParams.length == mParams.length) {
if (xParams[xParams.length-1].isArray()) {
return mParams[mParams.length -1].equals(
xParams[xParams.length -1].getComponentType());
}
} else if (xParams.length == mParams.length + 1) {
return Arrays.deepEquals(
Arrays.copyOfRange(xParams, 0, xParams.length-1), mParams);
}
}
return false;
}
/**
* Look whether {@code otherClass} has a public method similar to m
* @param m
* @param otherClass
* @return
*/
public Stream<Method> findInvokable(Method m, Class<?> otherClass) {
final Map<Class<?>,Class<?>> substitues =
Collections.singletonMap(java.util.logging.Level.class,
sun.util.logging.PlatformLogger.Level.class);
return Stream.of(otherClass.getMethods())
.filter((x) -> m.getName().equals(x.getName()))
.filter((x) -> canBeInvokedAs(m, x, substitues));
}
/**
* Test that the concrete Logger implementation passed as parameter
* overrides all the methods defined by its interface.
* @param julLogger A concrete implementation of System.Logger
* whose backend is a JUL Logger.
*/
StringBuilder testDefaultJULLogger(java.lang.System.Logger julLogger) {
final StringBuilder errors = new StringBuilder();
if (!bridgeLoggerClass.isInstance(julLogger)) {
final String errorMsg =
"Logger returned by LoggerFactory.getLogger(\"foo\") is not a "
+ bridgeLoggerClass + "\n\t" + julLogger;
System.err.println(errorMsg);
errors.append(errorMsg).append('\n');
}
final Class<? extends java.lang.System.Logger> xClass = julLogger.getClass();
List<Method> notOverridden =
Stream.of(bridgeLoggerClass.getDeclaredMethods()).filter((m) -> {
try {
Method x = xClass.getDeclaredMethod(m.getName(), m.getParameterTypes());
return x == null;
} catch (NoSuchMethodException ex) {
return !Modifier.isStatic(m.getModifiers());
}
}).collect(Collectors.toList());
notOverridden.stream().filter((x) -> {
boolean shouldOverride = true;
try {
final Method m = xClass.getMethod(x.getName(), x.getParameterTypes());
Method m2 = null;
try {
m2 = jdkLoggerClass.getDeclaredMethod(x.getName(), x.getParameterTypes());
} catch (Exception e) {
}
shouldOverride = m.isDefault() || m2 == null;
} catch (Exception e) {
// should override.
}
return shouldOverride;
}).forEach(x -> {
final String errorMsg = xClass.getName() + " should override\n\t" + x.toString();
System.err.println(errorMsg);
errors.append(errorMsg).append('\n');
});
if (notOverridden.isEmpty()) {
System.out.println(xClass + " overrides all methods from " + bridgeLoggerClass);
}
return errors;
}
public static class ResourceBundeParam extends ResourceBundle {
Map<String, String> map = Collections.synchronizedMap(new LinkedHashMap<>());
@Override
protected Object handleGetObject(String key) {
map.putIfAbsent(key, "${"+key+"}");
return map.get(key);
}
@Override
public Enumeration<String> getKeys() {
return Collections.enumeration(new LinkedHashSet<>(map.keySet()));
}
}
final ResourceBundle bundleParam =
ResourceBundle.getBundle(ResourceBundeParam.class.getName());
public static class ResourceBundeLocalized extends ResourceBundle {
Map<String, String> map = Collections.synchronizedMap(new LinkedHashMap<>());
@Override
protected Object handleGetObject(String key) {
map.putIfAbsent(key, "Localized:${"+key+"}");
return map.get(key);
}
@Override
public Enumeration<String> getKeys() {
return Collections.enumeration(new LinkedHashSet<>(map.keySet()));
}
}
final static ResourceBundle bundleLocalized =
ResourceBundle.getBundle(ResourceBundeLocalized.class.getName());
final Map<Class<?>, Object> params = new HashMap<>();
{
params.put(String.class, "TestString");
params.put(sun.util.logging.PlatformLogger.Level.class, sun.util.logging.PlatformLogger.Level.WARNING);
params.put(java.lang.System.Logger.Level.class, java.lang.System.Logger.Level.WARNING);
params.put(ResourceBundle.class, bundleParam);
params.put(Throwable.class, new Throwable("TestThrowable (Please ignore it!)"));
params.put(Object[].class, new Object[] {"One", "Two"});
params.put(Object.class, new Object() {
@Override public String toString() { return "I am an object!"; }
});
}
public Object[] getParamsFor(Method m) {
final Object[] res = new Object[m.getParameterCount()];
final Class<?>[] sig = m.getParameterTypes();
if (res.length == 0) {
return res;
}
for (int i=0; i<res.length; i++) {
Object p = params.get(sig[i]);
if (p == null && sig[i].equals(Supplier.class)) {
final String msg = "SuppliedMsg["+i+"]";
p = (Supplier<String>) () -> msg;
}
if (p instanceof String) {
res[i] = String.valueOf(p)+"["+i+"]";
} else {
res[i] = p;
}
}
return res;
}
public void invokeOn(java.lang.System.Logger logger, Method m) {
Object[] p = getParamsFor(m);
try {
m.invoke(logger, p);
} catch (Exception e) {
throw new RuntimeException("Failed to invoke "+m.toString(), e);
}
}
public void testAllJdkExtensionMethods(java.lang.System.Logger logger) {
Stream.of(jdkLoggerClass.getDeclaredMethods())
.filter(m -> !Modifier.isStatic(m.getModifiers()))
.forEach((m) -> invokeOn(logger, m));
}
public void testAllAPIMethods(java.lang.System.Logger logger) {
Stream.of(spiLoggerClass.getDeclaredMethods())
.filter(m -> !Modifier.isStatic(m.getModifiers()))
.forEach((m) -> invokeOn(logger, m));
}
public void testAllBridgeMethods(java.lang.System.Logger logger) {
Stream.of(bridgeLoggerClass.getDeclaredMethods())
.filter(m -> !Modifier.isStatic(m.getModifiers()))
.forEach((m) -> invokeOn(logger, m));
}
public void testAllLogProducerMethods(java.lang.System.Logger logger) {
Stream.of(julLogProducerClass.getDeclaredMethods())
.filter(m -> !Modifier.isStatic(m.getModifiers()))
.forEach((m) -> invokeOn(logger, m));
}
public StringBuilder testGetLoggerOverriddenOnSpi() {
final StringBuilder errors = new StringBuilder();
Stream.of(jdkLoggerClass.getDeclaredMethods())
.filter(m -> Modifier.isStatic(m.getModifiers()))
.filter(m -> Modifier.isPublic(m.getModifiers()))
.filter(m -> !m.getName().equals("getLoggerFinder"))
.filter(m -> {
try {
final Method x = bridgeLoggerClass.getDeclaredMethod(m.getName(), m.getParameterTypes());
return x == null;
} catch (NoSuchMethodException ex) {
return true;
}
}).forEach(m -> {
final String errorMsg = bridgeLoggerClass.getName() + " should override\n\t" + m.toString();
System.err.println(errorMsg);
errors.append(errorMsg).append('\n');
});
if (errors.length() == 0) {
System.out.println(bridgeLoggerClass + " overrides all static methods from " + jdkLoggerClass);
} else {
if (errors.length() > 0) throw new RuntimeException(errors.toString());
}
return errors;
}
public static void main(String argv[]) throws Exception {
final LoggerFinderAPITest test = new LoggerFinderAPITest(false);
final StringBuilder errors = new StringBuilder();
errors.append(test.testGetLoggerOverriddenOnSpi());
java.lang.System.Logger julLogger =
java.lang.System.LoggerFinder.getLoggerFinder()
.getLogger("foo", LoggerFinderAPITest.class.getModule());
errors.append(test.testDefaultJULLogger(julLogger));
if (errors.length() > 0) throw new RuntimeException(errors.toString());
java.lang.System.Logger julSystemLogger =
java.lang.System.LoggerFinder.getLoggerFinder()
.getLogger("bar", Thread.class.getModule());
errors.append(test.testDefaultJULLogger(julSystemLogger));
if (errors.length() > 0) throw new RuntimeException(errors.toString());
java.lang.System.Logger julLocalizedLogger =
(java.lang.System.Logger)
System.getLogger("baz", bundleLocalized);
java.lang.System.Logger julLocalizedSystemLogger =
java.lang.System.LoggerFinder.getLoggerFinder()
.getLocalizedLogger("oof", bundleLocalized, Thread.class.getModule());
final String error = errors.toString();
if (!error.isEmpty()) throw new RuntimeException(error);
for (java.lang.System.Logger logger : new java.lang.System.Logger[] {
julLogger, julSystemLogger, julLocalizedLogger, julLocalizedSystemLogger
}) {
test.testAllJdkExtensionMethods(logger);
test.testAllAPIMethods(logger);
test.testAllBridgeMethods(logger);
test.testAllLogProducerMethods(logger);
}
}
}

View file

@ -0,0 +1,2 @@
LoggerFinderBackendTest$CustomLoggerFinder

View file

@ -0,0 +1,98 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.lang.System.LoggerFinder;
/**
* A custom class loader which can hide the registered LoggerProvider
* depending on the value of a test.logger.hidesProvider system property.
* @author danielfuchs
*/
public class SystemClassLoader extends ClassLoader {
final public boolean hidesProvider;
public SystemClassLoader() {
hidesProvider = Boolean.getBoolean("test.logger.hidesProvider");
}
public SystemClassLoader(ClassLoader parent) {
super(parent);
hidesProvider = Boolean.getBoolean("test.logger.hidesProvider");
}
boolean accept(String name) {
final boolean res = !name.endsWith(LoggerFinder.class.getName());
if (res == false) {
System.out.println("Hiding " + name);
}
return res;
}
@Override
public URL getResource(String name) {
if (hidesProvider && !accept(name)) {
return null;
} else {
return super.getResource(name);
}
}
class Enumerator implements Enumeration<URL> {
final Enumeration<URL> enumeration;
volatile URL next;
Enumerator(Enumeration<URL> enumeration) {
this.enumeration = enumeration;
}
@Override
public boolean hasMoreElements() {
if (next != null) return true;
if (!enumeration.hasMoreElements()) return false;
if (hidesProvider == false) return true;
next = enumeration.nextElement();
if (accept(next.getPath())) return true;
next = null;
return hasMoreElements();
}
@Override
public URL nextElement() {
final URL res = next == null ? enumeration.nextElement() : next;
next = null;
if (hidesProvider == false || accept(res.getPath())) return res;
return nextElement();
}
}
@Override
public Enumeration<URL> getResources(String name) throws IOException {
final Enumeration<URL> enumeration = super.getResources(name);
return hidesProvider ? new Enumerator(enumeration) : enumeration;
}
}

View file

@ -0,0 +1,614 @@
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.ResourceBundle;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
import java.util.logging.Handler;
import java.util.logging.LogManager;
import sun.util.logging.PlatformLogger;
import java.util.logging.LogRecord;
import java.lang.System.LoggerFinder;
import java.lang.System.Logger;
import sun.util.logging.internal.LoggingProviderImpl;
/**
* @test
* @bug 8140364
* @summary JDK implementation specific unit test for JDK internal artifacts.
* Tests all internal bridge methods with the default LoggerFinder
* JUL backend.
* @modules java.base/sun.util.logging
* java.base/jdk.internal.logger
* java.logging/sun.util.logging.internal
* @run main/othervm DefaultLoggerBridgeTest NOSECURITY
*/
public class DefaultLoggerBridgeTest {
final static AtomicLong sequencer = new AtomicLong();
final static boolean VERBOSE = false;
public static final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
public static final class LogEvent implements Cloneable {
public LogEvent() {
this(sequencer.getAndIncrement());
}
LogEvent(long sequenceNumber) {
this.sequenceNumber = sequenceNumber;
}
long sequenceNumber;
boolean isLoggable;
String loggerName;
java.util.logging.Level level;
ResourceBundle bundle;
Throwable thrown;
Object[] args;
String msg;
String className;
String methodName;
Object[] toArray() {
return new Object[] {
sequenceNumber,
isLoggable,
loggerName,
level,
bundle,
thrown,
args,
msg,
className,
methodName,
};
}
@Override
public String toString() {
return Arrays.deepToString(toArray());
}
@Override
public boolean equals(Object obj) {
return obj instanceof LogEvent
&& Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
}
@Override
public int hashCode() {
return Objects.hash(toArray());
}
public LogEvent cloneWith(long sequenceNumber)
throws CloneNotSupportedException {
LogEvent cloned = (LogEvent)super.clone();
cloned.sequenceNumber = sequenceNumber;
return cloned;
}
public static LogEvent of(long sequenceNumber,
boolean isLoggable, String name,
java.util.logging.Level level, ResourceBundle bundle,
String key, Throwable thrown, Object... params) {
return LogEvent.of(sequenceNumber, isLoggable, name,
DefaultLoggerBridgeTest.class.getName(),
"testLogger", level, bundle, key,
thrown, params);
}
public static LogEvent of(long sequenceNumber,
boolean isLoggable, String name,
String className, String methodName,
java.util.logging.Level level, ResourceBundle bundle,
String key, Throwable thrown, Object... params) {
LogEvent evt = new LogEvent(sequenceNumber);
evt.loggerName = name;
evt.level = level;
evt.args = params;
evt.bundle = bundle;
evt.thrown = thrown;
evt.msg = key;
evt.isLoggable = isLoggable;
evt.className = className;
evt.methodName = methodName;
return evt;
}
}
static final java.util.logging.Level[] julLevels = {
java.util.logging.Level.ALL,
java.util.logging.Level.FINEST,
java.util.logging.Level.FINER,
java.util.logging.Level.FINE,
java.util.logging.Level.CONFIG,
java.util.logging.Level.INFO,
java.util.logging.Level.WARNING,
java.util.logging.Level.SEVERE,
java.util.logging.Level.OFF,
};
public static class MyBundle extends ResourceBundle {
final ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
@Override
protected Object handleGetObject(String key) {
if (key.contains(" (translated)")) {
throw new RuntimeException("Unexpected key: " + key);
}
return map.computeIfAbsent(key, k -> k + " (translated)");
}
@Override
public Enumeration<String> getKeys() {
return Collections.enumeration(map.keySet());
}
}
public static class MyHandler extends Handler {
@Override
public java.util.logging.Level getLevel() {
return java.util.logging.Level.ALL;
}
@Override
public void publish(LogRecord record) {
eventQueue.add(LogEvent.of(sequencer.getAndIncrement(),
true, record.getLoggerName(),
record.getSourceClassName(),
record.getSourceMethodName(),
record.getLevel(),
record.getResourceBundle(), record.getMessage(),
record.getThrown(), record.getParameters()));
}
@Override
public void flush() {
}
@Override
public void close() throws SecurityException {
}
}
public static class MyLoggerBundle extends MyBundle {
}
static PlatformLogger.Bridge convert(Logger logger) {
return PlatformLogger.Bridge.convert(logger);
}
static Logger getLogger(String name, Module caller) {
return jdk.internal.logger.LazyLoggers.getLogger(name, caller);
}
public static void main(String[] args) {
System.out.println("\n*** Starting test\n");
test();
System.out.println("Tetscase count: " + sequencer.get());
System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
}
public static void test() {
ResourceBundle loggerBundle =
ResourceBundle.getBundle(MyLoggerBundle.class.getName());
final Map<Object, String> loggerDescMap = new HashMap<>();
Logger sysLogger1a = getLogger("foo", Thread.class.getModule());
loggerDescMap.put(sysLogger1a, "jdk.internal.logger.LazyLoggers.getLogger(\"foo\", Thread.class.getModule())");
Logger appLogger1 = System.getLogger("foo");
loggerDescMap.put(appLogger1, "System.getLogger(\"foo\")");
LoggerFinder provider = LoggerFinder.getLoggerFinder();
Logger sysLogger1b = provider.getLogger("foo", Thread.class.getModule());
if (sysLogger1b != sysLogger1a) {
loggerDescMap.put(sysLogger1b, "provider.getLogger(\"foo\", Thread.class.getModule())");
}
Logger appLogger2 = System.getLogger("foo", loggerBundle);
loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)");
if (appLogger2 == appLogger1) {
throw new RuntimeException("identical loggers");
}
Logger sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class.getModule());
loggerDescMap.put(sysLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, Thread.class.getModule())");
if (appLogger2 == sysLogger2) {
throw new RuntimeException("identical loggers");
}
if (sysLogger2 == sysLogger1a) {
throw new RuntimeException("identical loggers");
}
final MyHandler appHandler;
final MyHandler sysHandler;
final java.util.logging.Logger appSink = LoggingProviderImpl.getLogManagerAccess().demandLoggerFor(
LogManager.getLogManager(), "foo", DefaultLoggerBridgeTest.class.getModule());
final java.util.logging.Logger sysSink = LoggingProviderImpl.getLogManagerAccess().demandLoggerFor(
LogManager.getLogManager(), "foo", Thread.class.getModule());
if (appSink == sysSink) {
throw new RuntimeException("identical backend loggers");
}
final java.util.logging.Logger sink = java.util.logging.Logger.getLogger("foo");
if (appSink != sink) {
throw new RuntimeException("expected same application logger");
}
sink.addHandler(appHandler = sysHandler = new MyHandler());
sink.setUseParentHandlers(VERBOSE);
try {
testLogger(provider, loggerDescMap, "foo", null, convert(sysLogger1a), sysSink);
testLogger(provider, loggerDescMap, "foo", null, convert(appLogger1), appSink);
testLogger(provider, loggerDescMap, "foo", loggerBundle, convert(appLogger2), appSink);
if (sysLogger1b != null && sysLogger1b != sysLogger1a) {
testLogger(provider, loggerDescMap, "foo", null, convert(sysLogger1b), sysSink);
}
if (sysLogger2 != null) {
testLogger(provider, loggerDescMap, "foo", loggerBundle, convert(sysLogger2), sysSink);
}
} finally {
appSink.removeHandler(appHandler);
sysSink.removeHandler(sysHandler);
}
}
public static class Foo {
}
static void verbose(String msg) {
if (VERBOSE) {
System.out.println(msg);
}
}
static void checkLogEvent(LoggerFinder provider, String desc,
LogEvent expected) {
LogEvent actual = eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
static void checkLogEvent(LoggerFinder provider, String desc,
LogEvent expected, boolean expectNotNull) {
LogEvent actual = eventQueue.poll();
if (actual == null && !expectNotNull) return;
if (actual != null && !expectNotNull) {
throw new RuntimeException("Unexpected log event found for " + desc
+ "\n\tgot: " + actual);
}
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
static void setLevel(java.util.logging.Logger sink, java.util.logging.Level loggerLevel) {
sink.setLevel(loggerLevel);
}
static sun.util.logging.PlatformLogger.Level toPlatformLevel(java.util.logging.Level level) {
return sun.util.logging.PlatformLogger.Level.valueOf(level.getName());
}
// Calls the methods defined on LogProducer and verify the
// parameters received by the underlying logger.
private static void testLogger(LoggerFinder provider,
Map<Object, String> loggerDescMap,
String name,
ResourceBundle loggerBundle,
PlatformLogger.Bridge logger,
java.util.logging.Logger sink) {
if (loggerDescMap.get(logger) == null) {
throw new RuntimeException("Missing description for " + logger);
}
System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger + "]");
final java.util.logging.Level OFF = java.util.logging.Level.OFF;
Foo foo = new Foo();
String fooMsg = foo.toString();
System.out.println("\tlogger.log(messageLevel, fooMsg)");
System.out.println("\tlogger.<level>(fooMsg)");
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (java.util.logging.Level messageLevel :julLevels) {
String desc = "logger.log(messageLevel, fooMsg): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, messageLevel, loggerBundle,
fooMsg, (Throwable)null, (Object[])null);
logger.log(toPlatformLevel(messageLevel), fooMsg);
checkLogEvent(provider, desc, expected, expected.isLoggable);
}
}
Supplier<String> supplier = new Supplier<String>() {
@Override
public String get() {
return this.toString();
}
};
System.out.println("\tlogger.log(messageLevel, supplier)");
System.out.println("\tlogger.<level>(supplier)");
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (java.util.logging.Level messageLevel :julLevels) {
String desc = "logger.log(messageLevel, supplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, messageLevel, null,
supplier.get(), (Throwable)null, (Object[])null);
logger.log(toPlatformLevel(messageLevel), supplier);
checkLogEvent(provider, desc, expected, expected.isLoggable);
}
}
String format = "two params [{1} {2}]";
Object arg1 = foo;
Object arg2 = fooMsg;
System.out.println("\tlogger.log(messageLevel, format, arg1, arg2)");
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (java.util.logging.Level messageLevel :julLevels) {
String desc = "logger.log(messageLevel, format, foo, fooMsg): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, messageLevel, loggerBundle,
format, (Throwable)null, arg1, arg2);
logger.log(toPlatformLevel(messageLevel), format, arg1, arg2);
checkLogEvent(provider, desc, expected, expected.isLoggable);
}
}
Throwable thrown = new Exception("OK: log me!");
System.out.println("\tlogger.log(messageLevel, fooMsg, thrown)");
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (java.util.logging.Level messageLevel :julLevels) {
String desc = "logger.log(messageLevel, fooMsg, thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, messageLevel, loggerBundle,
fooMsg, thrown, (Object[])null);
logger.log(toPlatformLevel(messageLevel), fooMsg, thrown);
checkLogEvent(provider, desc, expected, expected.isLoggable);
}
}
System.out.println("\tlogger.log(messageLevel, thrown, supplier)");
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (java.util.logging.Level messageLevel :julLevels) {
String desc = "logger.log(messageLevel, thrown, supplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, messageLevel, null,
supplier.get(), thrown, (Object[])null);
logger.log(toPlatformLevel(messageLevel), thrown, supplier);
checkLogEvent(provider, desc, expected, expected.isLoggable);
}
}
String sourceClass = "blah.Blah";
String sourceMethod = "blih";
System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg)");
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (java.util.logging.Level messageLevel :julLevels) {
String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, sourceClass, sourceMethod, messageLevel, loggerBundle,
fooMsg, (Throwable)null, (Object[])null);
logger.logp(toPlatformLevel(messageLevel), sourceClass, sourceMethod, fooMsg);
checkLogEvent(provider, desc, expected, expected.isLoggable);
}
}
System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, supplier)");
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (java.util.logging.Level messageLevel :julLevels) {
String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, supplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, sourceClass, sourceMethod, messageLevel, null,
supplier.get(), (Throwable)null, (Object[])null);
logger.logp(toPlatformLevel(messageLevel), sourceClass, sourceMethod, supplier);
checkLogEvent(provider, desc, expected, expected.isLoggable);
}
}
System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2)");
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (java.util.logging.Level messageLevel :julLevels) {
String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, sourceClass, sourceMethod, messageLevel, loggerBundle,
format, (Throwable)null, arg1, arg2);
logger.logp(toPlatformLevel(messageLevel), sourceClass, sourceMethod, format, arg1, arg2);
checkLogEvent(provider, desc, expected, expected.isLoggable);
}
}
System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown)");
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (java.util.logging.Level messageLevel :julLevels) {
String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, sourceClass, sourceMethod, messageLevel, loggerBundle,
fooMsg, thrown, (Object[])null);
logger.logp(toPlatformLevel(messageLevel), sourceClass, sourceMethod, fooMsg, thrown);
checkLogEvent(provider, desc, expected, expected.isLoggable);
}
}
System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier)");
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (java.util.logging.Level messageLevel :julLevels) {
String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, sourceClass, sourceMethod, messageLevel, null,
supplier.get(), thrown, (Object[])null);
logger.logp(toPlatformLevel(messageLevel), sourceClass, sourceMethod, thrown, supplier);
checkLogEvent(provider, desc, expected, expected.isLoggable);
}
}
ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
System.out.println("\tlogger.logrb(messageLevel, bundle, format, arg1, arg2)");
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (java.util.logging.Level messageLevel :julLevels) {
String desc = "logger.logrb(messageLevel, bundle, format, arg1, arg2): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, messageLevel, bundle,
format, (Throwable)null, arg1, arg2);
logger.logrb(toPlatformLevel(messageLevel), bundle, format, arg1, arg2);
checkLogEvent(provider, desc, expected, expected.isLoggable);
}
}
System.out.println("\tlogger.logrb(messageLevel, bundle, msg, thrown)");
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (java.util.logging.Level messageLevel :julLevels) {
String desc = "logger.logrb(messageLevel, bundle, msg, thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, messageLevel, bundle,
fooMsg, thrown, (Object[])null);
logger.logrb(toPlatformLevel(messageLevel), bundle, fooMsg, thrown);
checkLogEvent(provider, desc, expected, expected.isLoggable);
}
}
System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2)");
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (java.util.logging.Level messageLevel :julLevels) {
String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, sourceClass, sourceMethod, messageLevel, bundle,
format, (Throwable)null, arg1, arg2);
logger.logrb(toPlatformLevel(messageLevel), sourceClass, sourceMethod, bundle, format, arg1, arg2);
checkLogEvent(provider, desc, expected, expected.isLoggable);
}
}
System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown)");
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (java.util.logging.Level messageLevel :julLevels) {
String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, sourceClass, sourceMethod, messageLevel, bundle,
fooMsg, thrown, (Object[])null);
logger.logrb(toPlatformLevel(messageLevel), sourceClass, sourceMethod, bundle, fooMsg, thrown);
checkLogEvent(provider, desc, expected, expected.isLoggable);
}
}
}
}

View file

@ -0,0 +1,439 @@
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.ResourceBundle;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Handler;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.lang.System.LoggerFinder;
import java.util.logging.Logger;
import sun.util.logging.PlatformLogger;
import sun.util.logging.internal.LoggingProviderImpl;
/*
* @test
* @bug 8140364
* @summary Tests all PlatformLogger methods with the default LoggerFinder JUL backend.
* @modules java.base/sun.util.logging java.logging/sun.util.logging.internal
* @run main/othervm DefaultPlatformLoggerTest
*/
public class DefaultPlatformLoggerTest {
final static AtomicLong sequencer = new AtomicLong();
final static boolean VERBOSE = false;
public static final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
public static final class LogEvent implements Cloneable {
public LogEvent() {
this(sequencer.getAndIncrement());
}
LogEvent(long sequenceNumber) {
this.sequenceNumber = sequenceNumber;
}
long sequenceNumber;
boolean isLoggable;
String loggerName;
java.util.logging.Level level;
ResourceBundle bundle;
Throwable thrown;
Object[] args;
String msg;
String className;
String methodName;
Object[] toArray() {
return new Object[] {
sequenceNumber,
isLoggable,
loggerName,
level,
bundle,
thrown,
args,
msg,
className,
methodName,
};
}
@Override
public String toString() {
return Arrays.deepToString(toArray());
}
@Override
public boolean equals(Object obj) {
return obj instanceof LogEvent
&& Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
}
@Override
public int hashCode() {
return Objects.hash(toArray());
}
public LogEvent cloneWith(long sequenceNumber)
throws CloneNotSupportedException {
LogEvent cloned = (LogEvent)super.clone();
cloned.sequenceNumber = sequenceNumber;
return cloned;
}
public static LogEvent of(long sequenceNumber,
boolean isLoggable, String name,
java.util.logging.Level level, ResourceBundle bundle,
String key, Throwable thrown, Object... params) {
return LogEvent.of(sequenceNumber, isLoggable, name,
DefaultPlatformLoggerTest.class.getName(),
"testLogger", level, bundle, key,
thrown, params);
}
public static LogEvent of(long sequenceNumber,
boolean isLoggable, String name,
String className, String methodName,
java.util.logging.Level level, ResourceBundle bundle,
String key, Throwable thrown, Object... params) {
LogEvent evt = new LogEvent(sequenceNumber);
evt.loggerName = name;
evt.level = level;
evt.args = params;
evt.bundle = bundle;
evt.thrown = thrown;
evt.msg = key;
evt.isLoggable = isLoggable;
evt.className = className;
evt.methodName = methodName;
return evt;
}
}
static final java.util.logging.Level[] julLevels = {
java.util.logging.Level.ALL,
new java.util.logging.Level("FINER_THAN_FINEST", java.util.logging.Level.FINEST.intValue() - 10) {},
java.util.logging.Level.FINEST,
new java.util.logging.Level("FINER_THAN_FINER", java.util.logging.Level.FINER.intValue() - 10) {},
java.util.logging.Level.FINER,
new java.util.logging.Level("FINER_THAN_FINE", java.util.logging.Level.FINE.intValue() - 10) {},
java.util.logging.Level.FINE,
new java.util.logging.Level("FINER_THAN_CONFIG", java.util.logging.Level.FINE.intValue() + 10) {},
java.util.logging.Level.CONFIG,
new java.util.logging.Level("FINER_THAN_INFO", java.util.logging.Level.INFO.intValue() - 10) {},
java.util.logging.Level.INFO,
new java.util.logging.Level("FINER_THAN_WARNING", java.util.logging.Level.INFO.intValue() + 10) {},
java.util.logging.Level.WARNING,
new java.util.logging.Level("FINER_THAN_SEVERE", java.util.logging.Level.SEVERE.intValue() - 10) {},
java.util.logging.Level.SEVERE,
new java.util.logging.Level("FATAL", java.util.logging.Level.SEVERE.intValue() + 10) {},
java.util.logging.Level.OFF,
};
static final java.util.logging.Level[] julPlatformLevels = {
java.util.logging.Level.FINEST,
java.util.logging.Level.FINER,
java.util.logging.Level.FINE,
java.util.logging.Level.CONFIG,
java.util.logging.Level.INFO,
java.util.logging.Level.WARNING,
java.util.logging.Level.SEVERE,
};
public static class MyBundle extends ResourceBundle {
final ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
@Override
protected Object handleGetObject(String key) {
if (key.contains(" (translated)")) {
throw new RuntimeException("Unexpected key: " + key);
}
return map.computeIfAbsent(key, k -> k + " (translated)");
}
@Override
public Enumeration<String> getKeys() {
return Collections.enumeration(map.keySet());
}
}
public static class MyHandler extends Handler {
@Override
public java.util.logging.Level getLevel() {
return java.util.logging.Level.ALL;
}
@Override
public void publish(LogRecord record) {
eventQueue.add(LogEvent.of(sequencer.getAndIncrement(),
true, record.getLoggerName(),
record.getSourceClassName(),
record.getSourceMethodName(),
record.getLevel(),
record.getResourceBundle(), record.getMessage(),
record.getThrown(), record.getParameters()));
}
@Override
public void flush() {
}
@Override
public void close() throws SecurityException {
}
}
public static class MyLoggerBundle extends MyBundle {
}
public static void main(String[] args) throws Exception {
LoggerFinder provider = LoggerFinder.getLoggerFinder();
java.util.logging.Logger appSink = LoggingProviderImpl.getLogManagerAccess()
.demandLoggerFor(LogManager.getLogManager(), "foo",
DefaultPlatformLoggerTest.class.getModule());
java.util.logging.Logger sysSink = LoggingProviderImpl.getLogManagerAccess()
.demandLoggerFor(LogManager.getLogManager(),"foo", Thread.class.getModule());
java.util.logging.Logger sink = Logger.getLogger("foo");
sink.addHandler(new MyHandler());
sink.setUseParentHandlers(VERBOSE);
System.out.println("\n*** Without Security Manager\n");
test(provider, true, appSink, sysSink);
System.out.println("Tetscase count: " + sequencer.get());
System.out.println("\n*** With Security Manager, without permissions\n");
test(provider, false, appSink, sysSink);
System.out.println("Tetscase count: " + sequencer.get());
System.out.println("\n*** With Security Manager, with control permission\n");
test(provider, true, appSink, sysSink);
System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
}
public static void test(LoggerFinder provider, boolean hasRequiredPermissions,
java.util.logging.Logger appSink, java.util.logging.Logger sysSink) throws Exception {
// No way to give a resource bundle to a platform logger.
// ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
final Map<PlatformLogger, String> loggerDescMap = new HashMap<>();
PlatformLogger platform = PlatformLogger.getLogger("foo");
loggerDescMap.put(platform, "PlatformLogger.getLogger(\"foo\")");
testLogger(provider, loggerDescMap, "foo", null, platform, sysSink);
}
public static class Foo {
}
static void verbose(String msg) {
if (VERBOSE) {
System.out.println(msg);
}
}
static void checkLogEvent(LoggerFinder provider, String desc,
LogEvent expected) {
LogEvent actual = eventQueue.poll();
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
static void checkLogEvent(LoggerFinder provider, String desc,
LogEvent expected, boolean expectNotNull) {
LogEvent actual = eventQueue.poll();
if (actual == null && !expectNotNull) return;
if (actual != null && !expectNotNull) {
throw new RuntimeException("Unexpected log event found for " + desc
+ "\n\tgot: " + actual);
}
if (!expected.equals(actual)) {
throw new RuntimeException("mismatch for " + desc
+ "\n\texpected=" + expected
+ "\n\t actual=" + actual);
} else {
verbose("Got expected results for "
+ desc + "\n\t" + expected);
}
}
static void setLevel(java.util.logging.Logger sink, java.util.logging.Level loggerLevel) {
sink.setLevel(loggerLevel);
}
// Calls the methods defined on LogProducer and verify the
// parameters received by the underlying logger.
private static void testLogger(LoggerFinder provider,
Map<PlatformLogger, String> loggerDescMap,
String name,
ResourceBundle loggerBundle,
PlatformLogger logger,
java.util.logging.Logger sink) throws Exception {
System.out.println("Testing " + loggerDescMap.get(logger));
final java.util.logging.Level OFF = java.util.logging.Level.OFF;
Foo foo = new Foo();
String fooMsg = foo.toString();
System.out.println("\tlogger.<level>(fooMsg)");
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (java.util.logging.Level messageLevel : julPlatformLevels) {
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, messageLevel, loggerBundle,
fooMsg, (Throwable)null, (Object[])null);
String desc2 = "logger." + messageLevel.toString().toLowerCase()
+ "(fooMsg): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
if (messageLevel == java.util.logging.Level.FINEST) {
logger.finest(fooMsg);
checkLogEvent(provider, desc2, expected, expected.isLoggable);
} else if (messageLevel == java.util.logging.Level.FINER) {
logger.finer(fooMsg);
checkLogEvent(provider, desc2, expected, expected.isLoggable);
} else if (messageLevel == java.util.logging.Level.FINE) {
logger.fine(fooMsg);
checkLogEvent(provider, desc2, expected, expected.isLoggable);
} else if (messageLevel == java.util.logging.Level.CONFIG) {
logger.config(fooMsg);
checkLogEvent(provider, desc2, expected, expected.isLoggable);
} else if (messageLevel == java.util.logging.Level.INFO) {
logger.info(fooMsg);
checkLogEvent(provider, desc2, expected, expected.isLoggable);
} else if (messageLevel == java.util.logging.Level.WARNING) {
logger.warning(fooMsg);
checkLogEvent(provider, desc2, expected, expected.isLoggable);
} else if (messageLevel == java.util.logging.Level.SEVERE) {
logger.severe(fooMsg);
checkLogEvent(provider, desc2, expected, expected.isLoggable);
}
}
}
Throwable thrown = new Exception("OK: log me!");
System.out.println("\tlogger.<level>(msg, thrown)");
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (java.util.logging.Level messageLevel :julPlatformLevels) {
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, messageLevel, loggerBundle,
fooMsg, thrown, (Object[])null);
String desc2 = "logger." + messageLevel.toString().toLowerCase()
+ "(msg, thrown): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
if (messageLevel == java.util.logging.Level.FINEST) {
logger.finest(fooMsg, thrown);
checkLogEvent(provider, desc2, expected, expected.isLoggable);
} else if (messageLevel == java.util.logging.Level.FINER) {
logger.finer(fooMsg, thrown);
checkLogEvent(provider, desc2, expected, expected.isLoggable);
} else if (messageLevel == java.util.logging.Level.FINE) {
logger.fine(fooMsg, thrown);
checkLogEvent(provider, desc2, expected, expected.isLoggable);
} else if (messageLevel == java.util.logging.Level.CONFIG) {
logger.config(fooMsg, thrown);
checkLogEvent(provider, desc2, expected, expected.isLoggable);
} else if (messageLevel == java.util.logging.Level.INFO) {
logger.info(fooMsg, thrown);
checkLogEvent(provider, desc2, expected, expected.isLoggable);
} else if (messageLevel == java.util.logging.Level.WARNING) {
logger.warning(fooMsg, thrown);
checkLogEvent(provider, desc2, expected, expected.isLoggable);
} else if (messageLevel == java.util.logging.Level.SEVERE) {
logger.severe(fooMsg, thrown);
checkLogEvent(provider, desc2, expected, expected.isLoggable);
}
}
}
String format = "two params [{1} {2}]";
Object arg1 = foo;
Object arg2 = fooMsg;
System.out.println("\tlogger.<level>(format, arg1, arg2)");
for (java.util.logging.Level loggerLevel : julLevels) {
setLevel(sink, loggerLevel);
for (java.util.logging.Level messageLevel : julPlatformLevels) {
LogEvent expected =
LogEvent.of(
sequencer.get(),
loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
name, messageLevel, loggerBundle,
format, (Throwable)null, foo, fooMsg);
String desc2 = "logger." + messageLevel.toString().toLowerCase()
+ "(format, foo, fooMsg): loggerLevel="
+ loggerLevel+", messageLevel="+messageLevel;
if (messageLevel == java.util.logging.Level.FINEST) {
logger.finest(format, foo, fooMsg);
checkLogEvent(provider, desc2, expected, expected.isLoggable);
} else if (messageLevel == java.util.logging.Level.FINER) {
logger.finer(format, foo, fooMsg);
checkLogEvent(provider, desc2, expected, expected.isLoggable);
} else if (messageLevel == java.util.logging.Level.FINE) {
logger.fine(format, foo, fooMsg);
checkLogEvent(provider, desc2, expected, expected.isLoggable);
} else if (messageLevel == java.util.logging.Level.CONFIG) {
logger.config(format, foo, fooMsg);
checkLogEvent(provider, desc2, expected, expected.isLoggable);
} else if (messageLevel == java.util.logging.Level.INFO) {
logger.info(format, foo, fooMsg);
checkLogEvent(provider, desc2, expected, expected.isLoggable);
} else if (messageLevel == java.util.logging.Level.WARNING) {
logger.warning(format, foo, fooMsg);
checkLogEvent(provider, desc2, expected, expected.isLoggable);
} else if (messageLevel == java.util.logging.Level.SEVERE) {
logger.severe(format, foo, fooMsg);
checkLogEvent(provider, desc2, expected, expected.isLoggable);
}
}
}
}
}

View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.BooleanSupplier;
import jdk.internal.logger.BootstrapLogger;
public final class BootstrapLoggerUtils {
private static final Field IS_BOOTED;
private static final Method AWAIT_PENDING;
static {
try {
IS_BOOTED = BootstrapLogger.class.getDeclaredField("isBooted");
IS_BOOTED.setAccessible(true);
// private reflection hook that allows us to test wait until all
// the tasks pending in the BootstrapExecutor are finished.
AWAIT_PENDING = BootstrapLogger.class.getDeclaredMethod("awaitPendingTasks");
AWAIT_PENDING.setAccessible(true);
} catch (Exception ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static void setBootedHook(BooleanSupplier supplier) throws IllegalAccessException {
IS_BOOTED.set(null, supplier);
}
public static void awaitPending() {
try {
AWAIT_PENDING.invoke(null);
} catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException ex) {
ex.printStackTrace();
}
}
}

View file

@ -0,0 +1,272 @@
/*
* Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import jdk.test.lib.JDKToolFinder;
import jdk.test.lib.compiler.CompilerUtils;
import static jdk.test.lib.process.ProcessTools.executeCommand;
/*
* Base class for tests.
* The tests focuse on that LoggerFinder works well in jigsaw environment,
* i.e. make sure correct Logger can be retrieved,
* also verify that basic functionality of retrieved Logger's works well.
*
* Note: As the test will take long time, to avoid timeout,
* split it as several tests, this class is the base class for tests.
*/
public class Base {
protected static final String JAVA_HOME = System.getProperty("java.home");
protected static final Path JDK_IMAGE = Paths.get(JAVA_HOME);
protected static final Path JMODS = Paths.get(JAVA_HOME, "jmods");
protected static final String TEST_SRC = System.getProperty("test.src");
// logger client to get logger from java.base module, it should get a lazy logger
// which wraps the underlying real logger implementation
protected static final Path SRC_PATCHED_USAGE =
Paths.get(TEST_SRC, "patched_usage", "java.base");
protected static final Path DEST_PATCHED_USAGE = Paths.get("patched_usage", "java.base");
protected static final Path SRC_PATCHED_CLIENT = Paths.get(TEST_SRC, "patched_client");
protected static final Path DEST_PATCHED_CLIENT = Paths.get("patched_client");
// logger client to get logger from bootclasspath/a, it should get a lazy logger
// which wraps the underlying real logger implementation
protected static final Path SRC_BOOT_USAGE = Paths.get(TEST_SRC, "boot_usage");
protected static final Path DEST_BOOT_USAGE = Paths.get("boot_usage");
protected static final Path SRC_BOOT_CLIENT = Paths.get(TEST_SRC, "boot_client");
protected static final Path DEST_BOOT_CLIENT = Paths.get("boot_client");
// logger provider in named module m.l.a
protected static final Path SRC_NAMED_LOGGER = Paths.get(TEST_SRC, "named_logger");
protected static final Path DEST_NAMED_LOGGER = Paths.get("mods_named_logger");
// logger provider in unnamed module
protected static final Path SRC_UNNAMED_LOGGER = Paths.get(TEST_SRC, "unnamed_logger");
protected static final Path DEST_UNNAMED_LOGGER = Paths.get("cp_unnamed_logger");
protected static final Path SRC_UNNAMED_LOGGER_SERVICE_FILE =
SRC_UNNAMED_LOGGER.resolve("META-INF/services/java.lang.System$LoggerFinder");
protected static final Path DEST_UNNAMED_LOGGER_SERVICE_DIR =
DEST_UNNAMED_LOGGER.resolve("META-INF/services");
protected static final Path DEST_UNNAMED_LOGGER_SERVICE_FILE =
DEST_UNNAMED_LOGGER.resolve("META-INF/services/java.lang.System$LoggerFinder");
// logger client in named module m.t.a
protected static final Path SRC_NAMED_CLIENT = Paths.get(TEST_SRC, "named_client");
protected static final Path DEST_NAMED_CLIENT = Paths.get("mods_named_client");
// logger client in unnamed module
protected static final Path SRC_UNNAMED_CLIENT = Paths.get(TEST_SRC, "unnamed_client");
protected static final Path DEST_UNNAMED_CLIENT = Paths.get("cp_unnamed_client");
// customized image with only module java.base
protected static final Path IMAGE = Paths.get("image");
// customized image with java.base and logger provider module m.l.a
protected static final Path IMAGE_LOGGER = Paths.get("image_logger");
// customized image with module java.base and logger client module m.t.a
protected static final Path IMAGE_CLIENT = Paths.get("image_client");
// customized image with module java.base, logger provider module m.l.a
// and logger client module m.t.a
protected static final Path IMAGE_CLIENT_LOGGER = Paths.get("image_all");
// lazy logger class which wraps the underlying real logger implementation
protected static final String LAZY_LOGGER =
"jdk.internal.logger.LazyLoggers$JdkLazyLogger";
// JUL logger class which wraps java.util.logging.Logger
protected static final String JUL_LOGGER =
"sun.util.logging.internal.LoggingProviderImpl$JULWrapper";
// default simple logger class when no logger provider can be found
protected static final String SIMPLE_LOGGER =
"jdk.internal.logger.SimpleConsoleLogger";
// logger class in named module m.l.a
protected static final String LOGGER_A = "pkg.a.l.LoggerA";
// logger class in unnamed module m.l.b
protected static final String LOGGER_B = "pkg.b.l.LoggerB";
// logger client in named module
protected static final String CLIENT_A = "m.t.a/pkg.a.t.TestA";
// logger client in unnamed module
protected static final String CLIENT_B = "pkg.b.t.TestB";
// logger client which gets logger through boot class BootUsage
protected static final String BOOT_CLIENT = "BootClient";
// logger client which gets logger through patched class
// java.base/java.lang.PatchedUsage
protected static final String PATCHED_CLIENT = "PatchedClient";
protected void setupAllClient() throws Throwable {
// compiles logger client which will get logger through patched
// class java.base/java.lang.PatchedUsage
compile(SRC_BOOT_USAGE, DEST_BOOT_USAGE);
compile(SRC_BOOT_CLIENT, DEST_BOOT_CLIENT,
"--class-path", DEST_BOOT_USAGE.toString());
// compiles logger client which will get logger through boot
// class BootUsage
compile(SRC_PATCHED_USAGE, DEST_PATCHED_USAGE,
"--patch-module", "java.base=" + SRC_PATCHED_USAGE.toString());
compile(SRC_PATCHED_CLIENT, DEST_PATCHED_CLIENT,
"--patch-module", "java.base=" + DEST_PATCHED_USAGE.toString());
// compiles logger client in unnamed module
compile(SRC_UNNAMED_CLIENT, DEST_UNNAMED_CLIENT,
"--source-path", SRC_UNNAMED_CLIENT.toString());
// compiles logger client in named module m.t.a
compile(SRC_NAMED_CLIENT, DEST_NAMED_CLIENT,
"--module-source-path", SRC_NAMED_CLIENT.toString());
}
protected void setupNamedLogger() throws Throwable {
// compiles logger provider in named module m.l.a
compile(SRC_NAMED_LOGGER, DEST_NAMED_LOGGER,
"--module-source-path", SRC_NAMED_LOGGER.toString());
}
protected void setupUnnamedLogger() throws Throwable {
// compiles logger provider in unnamed module
compile(SRC_UNNAMED_LOGGER, DEST_UNNAMED_LOGGER,
"--source-path", SRC_UNNAMED_LOGGER.toString());
Files.createDirectories(DEST_UNNAMED_LOGGER_SERVICE_DIR);
Files.copy(SRC_UNNAMED_LOGGER_SERVICE_FILE, DEST_UNNAMED_LOGGER_SERVICE_FILE,
StandardCopyOption.REPLACE_EXISTING);
}
protected boolean checkJMODS() throws Throwable {
// if $JAVA_HOME/jmods does not exist, skip below steps
// as there is no way to build customized images by jlink
if (Files.notExists(JMODS)) {
System.err.println("Skip tests which require image");
return false;
}
return true;
}
protected void setupJavaBaseImage() throws Throwable {
if (!checkJMODS()) {
return;
}
// build image with just java.base module
String mpath = JMODS.toString();
execTool("jlink",
"--module-path", mpath,
"--add-modules", "java.base",
"--output", IMAGE.toString());
}
protected void setupLoggerImage() throws Throwable {
if (!checkJMODS()) {
return;
}
// build image with java.base + m.l.a modules
String mpath = DEST_NAMED_LOGGER.toString() + File.pathSeparator + JMODS.toString();
execTool("jlink",
"--module-path", mpath,
"--add-modules", "m.l.a",
"--output", IMAGE_LOGGER.toString());
}
protected void setupClientImage() throws Throwable {
if (!checkJMODS()) {
return;
}
// build image with java.base + m.t.a modules
String mpath = DEST_NAMED_CLIENT.toString() + File.pathSeparator + JMODS.toString();
execTool("jlink",
"--module-path", mpath,
"--add-modules", "m.t.a",
"--output", IMAGE_CLIENT.toString());
}
protected void setupFullImage() throws Throwable {
if (!checkJMODS()) {
return;
}
// build image with java.base + m.l.a + m.t.a modules
String mpath = DEST_NAMED_LOGGER.toString() + File.pathSeparator
+ DEST_NAMED_CLIENT.toString() + File.pathSeparator + JMODS.toString();
execTool("jlink",
"--module-path", mpath,
"--add-modules", "m.l.a,m.t.a",
"--output", IMAGE_CLIENT_LOGGER.toString());
}
protected static void assertTrue(boolean b) {
if (!b) {
throw new RuntimeException("expected true, but get false.");
}
}
/*
* run test with supplied java image which could be jdk image or customized image
*/
protected void runTest(Path image, String... opts) throws Throwable {
String[] options = Stream.concat(Stream.of(getJava(image)), Stream.of(opts))
.toArray(String[]::new);
ProcessBuilder pb = new ProcessBuilder(options);
int exitValue = executeCommand(pb).outputTo(System.out)
.errorTo(System.err)
.getExitValue();
assertTrue(exitValue == 0);
}
private void compile(Path src, Path dest, String... params) throws Throwable {
assertTrue(CompilerUtils.compile(src, dest, params));
}
private String getJava(Path image) {
boolean isWindows = System.getProperty("os.name").startsWith("Windows");
Path java = image.resolve("bin").resolve(isWindows ? "java.exe" : "java");
if (Files.notExists(java))
throw new RuntimeException(java + " not found");
return java.toAbsolutePath().toString();
}
private void execTool(String tool, String... args) throws Throwable {
String path = JDKToolFinder.getJDKTool(tool);
List<String> commands = new ArrayList<>();
commands.add(path);
Stream.of(args).forEach(commands::add);
ProcessBuilder pb = new ProcessBuilder(commands);
int exitValue = executeCommand(pb).outputTo(System.out)
.errorTo(System.out)
.shouldNotContain("no module is recorded in hash")
.getExitValue();
assertTrue(exitValue == 0);
}
}

View file

@ -0,0 +1,78 @@
/*
* Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @modules jdk.compiler
* @summary Test cases which run against customized image, check the situation where
* 1. logger provider is the default one supplied by java.base,
* 2. clients are in named/unnamed module,
* patched system module, or Xbootclasspath
* This test does not require existence of java.logging module,
* but require jdk.compiler module
* @requires vm.flagless
* @library /test/lib
* @build Base jdk.test.lib.compiler.CompilerUtils
* @run main/othervm JDKLoggerForImageTest
*/
public class JDKLoggerForImageTest extends Base {
public static void main(String args[]) throws Throwable {
JDKLoggerForImageTest t = new JDKLoggerForImageTest();
t.setup();
t.test();
}
private void setup() throws Throwable {
setupAllClient();
setupJavaBaseImage();
}
private void test() throws Throwable {
if (!checkJMODS()) {
return;
}
// logger client is in named module m.t.a
runTest(IMAGE,
"--module-path", DEST_NAMED_CLIENT.toString(),
"-m", CLIENT_A, "system", SIMPLE_LOGGER);
// logger client is in unnamed module
runTest(IMAGE,
"--class-path", DEST_UNNAMED_CLIENT.toString(),
CLIENT_B, "system", SIMPLE_LOGGER);
// logger client gets logger through boot class BootUsage
runTest(IMAGE,
"-Xbootclasspath/a:" + DEST_BOOT_USAGE.toString(),
"--class-path", DEST_BOOT_CLIENT.toString(),
BOOT_CLIENT, "system", SIMPLE_LOGGER);
// logger client gets logger through patched class
// java.base/java.lang.PatchedUsage
runTest(IMAGE,
"--patch-module", "java.base=" + DEST_PATCHED_USAGE.toString(),
"--class-path", DEST_PATCHED_CLIENT.toString(),
PATCHED_CLIENT, "system", SIMPLE_LOGGER);
}
}

View file

@ -0,0 +1,72 @@
/*
* Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @modules java.logging
* @modules jdk.compiler
* @summary Test cases which run against the JDK image, check the situation where
* 1. logger provider is the default one supplied by the JDK,
* 2. clients are in named/unnamed module,
* patched system module, or Xbootclasspath
* This test DOES require existence of java.logging module
* @requires vm.flagless
* @library /test/lib
* @build Base jdk.test.lib.compiler.CompilerUtils
* @run main/othervm JDKLoggerForJDKTest
*/
public class JDKLoggerForJDKTest extends Base {
public static void main(String args[]) throws Throwable {
JDKLoggerForJDKTest t = new JDKLoggerForJDKTest();
t.setup();
t.test();
}
private void setup() throws Throwable {
setupAllClient();
}
private void test() throws Throwable {
// logger client is in named module m.t.a
runTest(JDK_IMAGE,
"--module-path", DEST_NAMED_CLIENT.toString(),
"-m", CLIENT_A, "system", JUL_LOGGER);
// logger client is in unnamed module
runTest(JDK_IMAGE,
"--class-path", DEST_UNNAMED_CLIENT.toString(),
CLIENT_B, "system", JUL_LOGGER);
// logger client gets logger through boot class BootUsage
runTest(JDK_IMAGE,
"-Xbootclasspath/a:" + DEST_BOOT_USAGE.toString(),
"--class-path", DEST_BOOT_CLIENT.toString(),
BOOT_CLIENT, "system", LAZY_LOGGER, JUL_LOGGER);
// logger client gets logger through patched class
// java.base/java.lang.PatchedUsage
runTest(JDK_IMAGE,
"--patch-module", "java.base=" + DEST_PATCHED_USAGE.toString(),
"--class-path", DEST_PATCHED_CLIENT.toString(),
PATCHED_CLIENT, "system", LAZY_LOGGER, JUL_LOGGER);
}
}

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @modules jdk.compiler
* @summary Test cases which run against customized image, check the situation where
* 1. logger providers are in the customized image too,
* 2. clients are in named/unnamed module, image,
* patched system module, or Xbootclasspath
* This test does not require existence of java.logging module,
* but require jdk.compiler module
* @requires vm.flagless
* @library /test/lib
* @build Base jdk.test.lib.compiler.CompilerUtils
* @run main/othervm LoggerInImageTest
*/
public class LoggerInImageTest extends Base {
public static void main(String args[]) throws Throwable {
LoggerInImageTest t = new LoggerInImageTest();
t.setup();
t.test();
}
private void setup() throws Throwable {
setupAllClient();
setupNamedLogger();
setupLoggerImage();
setupFullImage();
}
private void test() throws Throwable {
if (!checkJMODS()) {
return;
}
// logger client is in named module m.t.a which is also in customized image
runTest(IMAGE_CLIENT_LOGGER,
"-m", CLIENT_A, "named", LOGGER_A);
// logger client in named module m.t.a
runTest(IMAGE_LOGGER,
"--module-path", DEST_NAMED_CLIENT.toString(),
"-m", CLIENT_A, "named", LOGGER_A);
// logger client is in unnamed module
runTest(IMAGE_LOGGER,
"--class-path", DEST_UNNAMED_CLIENT.toString(),
CLIENT_B, "named", LOGGER_A);
// logger client gets logger through boot class BootUsage
runTest(IMAGE_LOGGER,
"-Xbootclasspath/a:" + DEST_BOOT_USAGE.toString(),
"--class-path", DEST_BOOT_CLIENT.toString(),
BOOT_CLIENT, "system", LAZY_LOGGER, LOGGER_A);
// logger client gets logger through patched class
// java.base/java.lang.PatchedUsage
runTest(IMAGE_LOGGER,
"--patch-module", "java.base=" + DEST_PATCHED_USAGE.toString(),
"--class-path", DEST_PATCHED_CLIENT.toString(),
PATCHED_CLIENT, "system", LAZY_LOGGER, LOGGER_A);
}
}

View file

@ -0,0 +1,91 @@
/*
* Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.File;
/*
* @test
* @modules jdk.compiler
* @summary Test cases which run against customized image, check the situation where
* 1. logger providers are in named module,
* 2. clients are in named/unnamed module, image,
* patched system module, or Xbootclasspath
* This test does not require existence of java.logging module,
* but require jdk.compiler module
* @requires vm.flagless
* @library /test/lib
* @build Base jdk.test.lib.compiler.CompilerUtils
* @run main/othervm NamedLoggerForImageTest
*/
public class NamedLoggerForImageTest extends Base {
public static void main(String args[]) throws Throwable {
NamedLoggerForImageTest t = new NamedLoggerForImageTest();
t.setup();
t.test();
}
private void setup() throws Throwable {
setupAllClient();
setupNamedLogger();
setupJavaBaseImage();
setupClientImage();
}
private void test() throws Throwable {
if (!checkJMODS()) {
return;
}
// logger client is in named module m.t.a
runTest(IMAGE,
"--module-path", DEST_NAMED_LOGGER.toString()
+ File.pathSeparator + DEST_NAMED_CLIENT.toString(),
"-m", CLIENT_A, "named", LOGGER_A);
// logger client is in unnamed module
runTest(IMAGE,
"--module-path", DEST_NAMED_LOGGER.toString(),
"--class-path", DEST_UNNAMED_CLIENT.toString(),
CLIENT_B, "named", LOGGER_A);
// logger client is in named module m.t.a which is in customized image
runTest(IMAGE_CLIENT,
"--module-path", DEST_NAMED_LOGGER.toString(),
"-m", CLIENT_A, "named", LOGGER_A);
// logger client gets logger through boot class BootUsage
runTest(IMAGE,
"--module-path", DEST_NAMED_LOGGER.toString(),
"-Xbootclasspath/a:" + DEST_BOOT_USAGE.toString(),
"--class-path", DEST_BOOT_CLIENT.toString(),
BOOT_CLIENT, "system", LAZY_LOGGER, LOGGER_A);
// logger client gets logger through patched class
// java.base/java.lang.PatchedUsage
runTest(IMAGE,
"--module-path", DEST_NAMED_LOGGER.toString(),
"--patch-module", "java.base=" + DEST_PATCHED_USAGE.toString(),
"--class-path", DEST_PATCHED_CLIENT.toString(),
PATCHED_CLIENT, "system", LAZY_LOGGER, LOGGER_A);
}
}

View file

@ -0,0 +1,80 @@
/*
* Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.File;
/*
* @test
* @modules jdk.compiler
* @summary Test cases which run against the JDK image, check the situation where
* 1. logger providers are in named module,
* 2. clients are in named/unnamed module,
* patched system module, or Xbootclasspath
* This test does not require existence of java.logging module,
* but require jdk.compiler module
* @requires vm.flagless
* @library /test/lib
* @build Base jdk.test.lib.compiler.CompilerUtils
* @run main/othervm NamedLoggerForJDKTest
*/
public class NamedLoggerForJDKTest extends Base {
public static void main(String args[]) throws Throwable {
NamedLoggerForJDKTest t = new NamedLoggerForJDKTest();
t.setup();
t.test();
}
private void setup() throws Throwable {
setupAllClient();
setupNamedLogger();
}
private void test() throws Throwable {
// logger client is in named module m.t.a
runTest(JDK_IMAGE,
"--module-path", DEST_NAMED_LOGGER.toString()
+ File.pathSeparator + DEST_NAMED_CLIENT.toString(),
"-m", CLIENT_A, "named", LOGGER_A);
// logger client is in unnamed module
runTest(JDK_IMAGE,
"--module-path", DEST_NAMED_LOGGER.toString(),
"--class-path", DEST_UNNAMED_CLIENT.toString(),
CLIENT_B, "named", LOGGER_A);
// logger client gets logger through boot class BootUsage
runTest(JDK_IMAGE,
"--module-path", DEST_NAMED_LOGGER.toString(),
"-Xbootclasspath/a:" + DEST_BOOT_USAGE.toString(),
"--class-path", DEST_BOOT_CLIENT.toString(),
BOOT_CLIENT, "system", LAZY_LOGGER, LOGGER_A);
// logger client gets logger through patched class
// java.base/java.lang.PatchedUsage
runTest(JDK_IMAGE,
"--module-path", DEST_NAMED_LOGGER.toString(),
"--patch-module", "java.base=" + DEST_PATCHED_USAGE.toString(),
"--class-path", DEST_PATCHED_CLIENT.toString(),
PATCHED_CLIENT, "system", LAZY_LOGGER, LOGGER_A);
}
}

View file

@ -0,0 +1,91 @@
/*
* Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.File;
/*
* @test
* @modules jdk.compiler
* @summary Test cases which run against customized image, check the situation where
* 1. logger providers are in unnamed module,
* 2. clients are in named/unnamed module, image,
* patched system module, or Xbootclasspath
* This test does not require existence of java.logging module,
* but require jdk.compiler module
* @requires vm.flagless
* @library /test/lib
* @build Base jdk.test.lib.compiler.CompilerUtils
* @run main/othervm UnnamedLoggerForImageTest
*/
public class UnnamedLoggerForImageTest extends Base {
public static void main(String args[]) throws Throwable {
UnnamedLoggerForImageTest t = new UnnamedLoggerForImageTest();
t.setup();
t.test();
}
private void setup() throws Throwable {
setupAllClient();
setupUnnamedLogger();
setupJavaBaseImage();
setupClientImage();
}
private void test() throws Throwable {
if (!checkJMODS()) {
return;
}
// logger client is in unnamed module
runTest(IMAGE,
"--class-path", DEST_UNNAMED_LOGGER.toString()
+ File.pathSeparator + DEST_UNNAMED_CLIENT.toString(),
CLIENT_B, "unnamed", LOGGER_B);
// logger client is in named module m.t.a
runTest(IMAGE,
"--class-path", DEST_UNNAMED_LOGGER.toString(),
"--module-path", DEST_NAMED_CLIENT.toString(),
"-m", CLIENT_A, "unnamed", LOGGER_B);
// logger client is in named module m.t.a which is in customized image
runTest(IMAGE_CLIENT,
"--class-path", DEST_UNNAMED_LOGGER.toString(),
"-m", CLIENT_A, "unnamed", LOGGER_B);
// logger client gets logger through boot class BootUsage
runTest(IMAGE,
"--class-path", DEST_UNNAMED_LOGGER.toString()
+ File.pathSeparator + DEST_BOOT_CLIENT.toString(),
"-Xbootclasspath/a:" + DEST_BOOT_USAGE.toString(),
BOOT_CLIENT, "system", LAZY_LOGGER, LOGGER_B);
// logger client gets logger through patched class
// java.base/java.lang.PatchedUsage
runTest(IMAGE,
"--class-path", DEST_UNNAMED_LOGGER.toString()
+ File.pathSeparator + DEST_PATCHED_CLIENT.toString(),
"--patch-module", "java.base=" + DEST_PATCHED_USAGE.toString(),
PATCHED_CLIENT, "system", LAZY_LOGGER, LOGGER_B);
}
}

View file

@ -0,0 +1,80 @@
/*
* Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.File;
/*
* @test
* @modules jdk.compiler
* @summary Test cases which run against the JDK image, check the situation where
* 1. logger providers are in unnamed module,
* 2. clients are in named/unnamed module,
* patched system module, or Xbootclasspath
* This test does not require existence of java.logging module,
* but require jdk.compiler module
* @requires vm.flagless
* @library /test/lib
* @build Base jdk.test.lib.compiler.CompilerUtils
* @run main/othervm UnnamedLoggerForJDKTest
*/
public class UnnamedLoggerForJDKTest extends Base {
public static void main(String args[]) throws Throwable {
UnnamedLoggerForJDKTest t = new UnnamedLoggerForJDKTest();
t.setup();
t.test();
}
private void setup() throws Throwable {
setupAllClient();
setupUnnamedLogger();
}
private void test() throws Throwable {
// logger client is in named module m.t.a
runTest(JDK_IMAGE,
"--class-path", DEST_UNNAMED_LOGGER.toString(),
"--module-path", DEST_NAMED_CLIENT.toString(),
"-m", CLIENT_A, "unnamed", LOGGER_B);
// logger client is also in unnamed module
runTest(JDK_IMAGE,
"--class-path", DEST_UNNAMED_LOGGER.toString()
+ File.pathSeparator + DEST_UNNAMED_CLIENT.toString(),
CLIENT_B, "unnamed", LOGGER_B);
// logger client gets logger through boot class BootUsage
runTest(JDK_IMAGE,
"--class-path", DEST_UNNAMED_LOGGER.toString()
+ File.pathSeparator + DEST_BOOT_CLIENT.toString(),
"-Xbootclasspath/a:" + DEST_BOOT_USAGE.toString(),
BOOT_CLIENT, "system", LAZY_LOGGER, LOGGER_B);
// logger client gets logger through patched class
// java.base/java.lang.PatchedUsage
runTest(JDK_IMAGE,
"--class-path", DEST_UNNAMED_LOGGER.toString()
+ File.pathSeparator + DEST_PATCHED_CLIENT.toString(),
"--patch-module", "java.base=" + DEST_PATCHED_USAGE.toString(),
PATCHED_CLIENT, "system", LAZY_LOGGER, LOGGER_B);
}
}

View file

@ -0,0 +1,164 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.lang.reflect.Method;
import java.lang.System.Logger;
import java.time.Instant;
import java.util.ResourceBundle;
import java.util.ListResourceBundle;
/*
* Tests when logger client is in Xbootclasspath
*/
public final class BootClient {
public static void main(String[] args) throws Exception {
assertTrue(args.length >= 2);
String loggerMode = args[0];
String loggerClassName = args[1];
String underlyingLoggerClassName = args.length >= 3 ? args[2] : null;
System.err.println("BootClient starting at " + Instant.now());
try {
testLogger(loggerMode, loggerClassName, underlyingLoggerClassName);
testLog(underlyingLoggerClassName);
} finally {
System.err.println("BootClient finished at " + Instant.now());
}
}
/*
* Tests System.getLogger(String) get expected logger.
*/
private static void testLogger(String loggerMode, String loggerClassName,
String underlyingLoggerClassName) {
String name = "test.boot";
Logger logger = getLogger(name);
printLogger(logger);
final Module lm = logger.getClass().getModule();
final ClassLoader loggerCL = lm.getClassLoader();
if (loggerMode.equals("system")) {
assertTrue(lm.isNamed());
assertTrue(loggerCL == null);
} else if(loggerMode.equals("unnamed")) {
assertTrue(!lm.isNamed());
assertTrue(loggerCL != null);
} else {
throw new RuntimeException("wrong parameter");
}
assertTrue(loggerClassName.equals(logger.getClass().getName()));
if (underlyingLoggerClassName != null) {
String loggerName = logger.getName();
if (underlyingLoggerClassName.equals(
"sun.util.logging.internal.LoggingProviderImpl$JULWrapper")) {
assertTrue(loggerName.equals(name));
} else {
assertTrue(loggerName.equals(underlyingLoggerClassName));
}
}
}
/*
* Tests Logger retrieved by System.getLogger(String, ResourceBundle) and
* System.getLogger(String) works well.
*/
private static void testLog(String underlyingLoggerClassName) throws Exception {
if (underlyingLoggerClassName == null) {
return;
}
if (underlyingLoggerClassName.equals("pkg.a.l.LoggerA")
|| underlyingLoggerClassName.equals("pkg.b.l.LoggerB")) {
String name = "test.boot.logger";
String plainMsg = "this is test log message #1";
ResourceBundle rb = new MyResourcesBoot();
Throwable ex = new Throwable("this is an expected exception to be logged");
Class<?> clazz = Class.forName(underlyingLoggerClassName);
Method method = clazz.getMethod("checkLog", String.class,
System.Logger.Level.class,
ResourceBundle.class, String.class,
Throwable.class, Object[].class);
Logger logger = getLogger(name);
printLogger(logger);
assertTrue(logger.getClass().getName()
.equals("jdk.internal.logger.LazyLoggers$JdkLazyLogger"));
assertTrue(logger.getName().equals(underlyingLoggerClassName));
logger.log(Logger.Level.WARNING, plainMsg);
boolean pass = (boolean)method.invoke(null, name, Logger.Level.WARNING,
null, plainMsg, ex, (Object)null);
assertTrue(pass);
pass = (boolean)method.invoke(null, name, Logger.Level.INFO,
rb, MyResourcesBoot.VALUE, (Throwable)null,
(Object)null);
assertTrue(!pass);
logger = getLogger(name, rb);
printLogger(logger);
assertTrue(logger.getClass().getName()
.equals("jdk.internal.logger.LocalizedLoggerWrapper"));
assertTrue(logger.getName().equals(underlyingLoggerClassName));
logger.log(Logger.Level.INFO, MyResourcesBoot.KEY);
pass = (boolean)method.invoke(null, name, Logger.Level.INFO,
rb, MyResourcesBoot.VALUE, (Throwable)null,
(Object)null);
assertTrue(pass);
pass = (boolean)method.invoke(null, name, Logger.Level.WARNING,
null, plainMsg, ex, (Object)null);
assertTrue(pass);
}
}
private static class MyResourcesBoot extends ListResourceBundle {
static final String KEY = "this is the key in MyResourcesBoot";
static final String VALUE = "THIS IS THE VALUE IN MyResourcesBoot";
@Override
protected Object[][] getContents() {
return new Object[][] {
{KEY, VALUE}
};
}
}
private static Logger getLogger(String name) {
return BootUsage.getLogger(name);
}
private static Logger getLogger(String name, ResourceBundle rb) {
return BootUsage.getLogger(name, rb);
}
private static void printLogger(Logger logger) {
System.err.println("logger name: " + logger.getName()
+ ", logger class: " + logger.getClass());
}
private static void assertTrue(boolean b) {
if (!b) {
throw new RuntimeException("expected true, but get false.");
}
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.lang.System.Logger;
import java.util.ResourceBundle;
/*
* BootUsage is put to Xbootclasspath, it will be used by
* BootClient to test when logger client is in boot classpath
*/
public final class BootUsage {
public static Logger getLogger(String name) {
check();
return System.getLogger(name);
}
public static Logger getLogger(String name, ResourceBundle rb) {
check();
return System.getLogger(name, rb);
}
private static void check() {
final Module m = BootUsage.class.getModule();
final ClassLoader moduleCL = m.getClassLoader();
assertTrue(!m.isNamed());
assertTrue(moduleCL == null);
}
private static void assertTrue(boolean b) {
if (!b) {
throw new RuntimeException("expected true, but get false.");
}
}
}

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
module m.t.a {
exports pkg.a.t;
uses System.LoggerFinder;
}

View file

@ -0,0 +1,160 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package pkg.a.t;
import java.lang.reflect.Method;
import java.lang.System.Logger;
import java.time.Instant;
import java.util.ResourceBundle;
import java.util.ListResourceBundle;
/*
* Tests when logger client is in named module m.l.a
*/
public class TestA {
public static void main(String[] args) throws Exception {
assertTrue(args.length == 2);
String loggerMode = args[0];
String loggerClassName = args[1];
System.err.println("TestA starting at " + Instant.now());
try {
testLogger(loggerMode, loggerClassName);
testLog(loggerClassName);
} finally {
System.err.println("TestA finished at " + Instant.now());
}
}
/*
* Tests System.getLogger(String) get expected logger.
*/
private static void testLogger(String loggerMode, String loggerClassName) {
final Module m = TestA.class.getModule();
final ClassLoader moduleCL = m.getClassLoader();
assertTrue(m.isNamed());
assertTrue(moduleCL != null);
String name = "test.a";
Logger logger = getLogger(name);
printLogger(logger);
final Module lm = logger.getClass().getModule();
final ClassLoader loggerCL = lm.getClassLoader();
if (loggerMode.equals("system")) {
assertTrue(lm.isNamed());
assertTrue(loggerCL == null);
} else if(loggerMode.equals("named")) {
assertTrue(lm.isNamed());
assertTrue(loggerCL != null);
} else if(loggerMode.equals("unnamed")) {
assertTrue(!lm.isNamed());
assertTrue(loggerCL != null);
} else {
throw new RuntimeException("wrong parameter");
}
assertTrue(loggerClassName.equals(logger.getClass().getName()));
assertTrue(!loggerClassName.equals("jdk.internal.logger.LazyLoggers$JdkLazyLogger"));
}
/*
* Tests Logger retrieved by System.getLogger(String, ResourceBundle) and
* System.getLogger(String) works well.
*/
private static void testLog(String loggerClassName) throws Exception {
if (loggerClassName.equals("pkg.a.l.LoggerA")
|| loggerClassName.equals("pkg.b.l.LoggerB")) {
String name = "test.a.A";
String plainMsg = "this is test log message #1";
ResourceBundle rb = new MyResourcesA();
Throwable ex = new Throwable("this is an expected exception to be logged");
Class<?> clazz = Class.forName(loggerClassName);
Method method = clazz.getMethod("checkLog", String.class,
System.Logger.Level.class,
ResourceBundle.class, String.class,
Throwable.class, Object[].class);
Logger logger = getLogger(name);
printLogger(logger);
assertTrue(logger.getClass().getName().equals(loggerClassName));
assertTrue(logger.getName().equals(loggerClassName));
logger.log(Logger.Level.WARNING, plainMsg);
boolean pass = (boolean)method.invoke(null, name,
Logger.Level.WARNING,
null, plainMsg, ex, (Object)null);
assertTrue(pass);
pass = (boolean)method.invoke(null, name, Logger.Level.INFO,
rb, MyResourcesA.VALUE, (Throwable)null,
(Object)null);
assertTrue(!pass);
logger = getLogger(name, rb);
printLogger(logger);
assertTrue(logger.getClass().getName()
.equals("jdk.internal.logger.LocalizedLoggerWrapper"));
assertTrue(logger.getName().equals(loggerClassName));
logger.log(Logger.Level.INFO, MyResourcesA.KEY);
pass = (boolean)method.invoke(null, name, Logger.Level.INFO,
rb, MyResourcesA.VALUE, (Throwable)null,
(Object)null);
assertTrue(pass);
pass = (boolean)method.invoke(null, name, Logger.Level.WARNING,
null, plainMsg, ex, (Object)null);
assertTrue(pass);
}
}
private static class MyResourcesA extends ListResourceBundle {
static final String KEY = "this is the key in MyResourcesA";
static final String VALUE = "THIS IS THE VALUE IN MyResourcesA";
@Override
protected Object[][] getContents() {
return new Object[][] {
{KEY, VALUE}
};
}
}
private static Logger getLogger(String name) {
return System.getLogger(name);
}
private static Logger getLogger(String name, ResourceBundle rb) {
return System.getLogger(name, rb);
}
private static void printLogger(Logger logger) {
System.err.println("logger name: " + logger.getName()
+ ", logger class: " + logger.getClass());
}
private static void assertTrue(boolean b) {
if (!b) {
throw new RuntimeException("expected true, but get false.");
}
}
}

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
module m.l.a {
opens pkg.a.l;
provides java.lang.System.LoggerFinder with pkg.a.p.LoggerFinderA;
}

View file

@ -0,0 +1,127 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package pkg.a.l;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.ResourceBundle;
public class LoggerA implements System.Logger {
// ---- test utility fields and methods ----
private static Map<String, LoggerA> map = new HashMap<>();
public static LoggerA getLogger(String name) {
return map.computeIfAbsent(name, (n) -> new LoggerA());
}
public static boolean checkLog(String name, Level level, ResourceBundle bundle,
String format, Throwable throwable, Object... params) {
LoggerA logger = map.get(name);
LogEvent event = new LogEvent(level, bundle, format, null, params);
for (LogEvent l : logger.queue) {
if (l.equals(event)) {
return true;
}
}
return false;
}
// ---- logger implementation ----
private Queue<LogEvent> queue = new LinkedList<>();
@Override
public String getName() {
return this.getClass().getName();
}
@Override
public boolean isLoggable(Level level) {
return true;
}
@Override
public void log(Level level, ResourceBundle bundle, String format, Object... params) {
String msg = bundle != null ? bundle.getString(format) : format;
log(new LogEvent(level, bundle, msg, null, params));
}
@Override
public void log(Level level, ResourceBundle bundle, String format, Throwable throwable) {
String msg = bundle != null ? bundle.getString(format) : format;
log(new LogEvent(level, bundle, msg, throwable, (Object)null));
}
void log(LogEvent l) {
print(l);
queue.add(l);
}
private void print(LogEvent l) {
System.err.println("LoggerA Message"+ l);
}
public Queue<LogEvent> getLogEvent() {
return queue;
}
public static class LogEvent {
public LogEvent(Level level, ResourceBundle bundle, String format,
Throwable throwable, Object... params) {
this.level = level;
this.bundle = bundle;
this.format = format;
this.throwable = throwable;
this.params = params;
}
@Override
public boolean equals(Object o) {
if (o instanceof LogEvent) {
LogEvent e = (LogEvent)o;
return level == e.level
&& bundle == e.bundle
&& format == e.format
&& params == e.params;
}
return false;
}
@Override
public String toString() {
return String.format("[level: %s, bundle: %s, format: %s, throwable: %s, object: %s]",
level, bundle, format, throwable, params);
}
private Level level;
private ResourceBundle bundle;
private String format;
private Throwable throwable;
private Object[] params;
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package pkg.a.p;
import pkg.a.l.LoggerA;
public class LoggerFinderA extends System.LoggerFinder {
@Override
public System.Logger getLogger(String name, Module module) {
return LoggerA.getLogger(name);
}
}

View file

@ -0,0 +1,165 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.lang.reflect.Method;
import java.lang.System.Logger;
import java.time.Instant;
import java.util.ResourceBundle;
import java.util.ListResourceBundle;
/*
* Tests when logger client is in patched module
*/
public class PatchedClient {
public static void main(String[] args) throws Exception {
assertTrue(args.length >= 2);
String loggerMode = args[0];
String loggerClassName = args[1];
String underlyingLoggerClassName = args.length >= 3 ? args[2] : null;
System.err.println("PatchedClient starting at " + Instant.now());
try {
testLogger(loggerMode, loggerClassName, underlyingLoggerClassName);
testLog(underlyingLoggerClassName);
} finally {
System.err.println("PatchedClient finished at " + Instant.now());
}
}
/*
* Tests System.getLogger(String) get expected logger.
*/
private static void testLogger(String loggerMode, String loggerClassName,
String underlyingLoggerClassName) {
String name = "test.patched";
Logger logger = getLogger(name);
printLogger(logger);
final Module lm = logger.getClass().getModule();
final ClassLoader loggerCL = lm.getClassLoader();
if (loggerMode.equals("system")) {
assertTrue(lm.isNamed());
assertTrue(loggerCL == null);
} else if(loggerMode.equals("unnamed")) {
assertTrue(!lm.isNamed());
assertTrue(loggerCL != null);
} else {
throw new RuntimeException("wrong parameter");
}
assertTrue(loggerClassName.equals(logger.getClass().getName()));
if (underlyingLoggerClassName != null) {
String loggerName = logger.getName();
if (underlyingLoggerClassName.equals(
"sun.util.logging.internal.LoggingProviderImpl$JULWrapper")) {
assertTrue(loggerName.equals(name));
} else {
assertTrue(loggerName.equals(underlyingLoggerClassName));
}
}
}
/*
* Tests Logger retrieved by System.getLogger(String, ResourceBundle) and
* System.getLogger(String) works well.
*/
private static void testLog(String underlyingLoggerClassName) throws Exception {
if (underlyingLoggerClassName == null) {
return;
}
if (underlyingLoggerClassName.equals("pkg.a.l.LoggerA")
|| underlyingLoggerClassName.equals("pkg.b.l.LoggerB")) {
String name = "test.patched.logger";
String plainMsg = "this is test log message #1";
ResourceBundle rb = new MyResourcesPatched();
Throwable ex = new Throwable("this is an expected exception to be logged");
Class<?> clazz = Class.forName(underlyingLoggerClassName);
Method method = clazz.getMethod("checkLog", String.class,
System.Logger.Level.class,
ResourceBundle.class, String.class,
Throwable.class, Object[].class);
Logger logger = getLogger(name);
printLogger(logger);
assertTrue(logger.getClass().getName()
.equals("jdk.internal.logger.LazyLoggers$JdkLazyLogger"));
assertTrue(logger.getName().equals(underlyingLoggerClassName));
logger.log(Logger.Level.WARNING, plainMsg);
boolean pass = (boolean)method.invoke(null, name, Logger.Level.WARNING,
null, plainMsg, ex, (Object)null);
assertTrue(pass);
pass = (boolean)method.invoke(null, name, Logger.Level.INFO,
rb, MyResourcesPatched.VALUE, (Throwable)null,
(Object)null);
assertTrue(!pass);
logger = getLogger(name, rb);
printLogger(logger);
assertTrue(logger.getClass().getName()
.equals("jdk.internal.logger.LocalizedLoggerWrapper"));
assertTrue(logger.getName().equals(underlyingLoggerClassName));
logger.log(Logger.Level.INFO, MyResourcesPatched.KEY);
pass = (boolean)method.invoke(null, name, Logger.Level.INFO,
rb, MyResourcesPatched.VALUE, (Throwable)null,
(Object)null);
assertTrue(pass);
pass = (boolean)method.invoke(null, name, Logger.Level.WARNING,
null, plainMsg, ex, (Object)null);
assertTrue(pass);
}
}
private static class MyResourcesPatched extends ListResourceBundle {
static final String KEY = "this is the key in MyResourcesPatched";
static final String VALUE = "THIS IS THE VALUE IN MyResourcesPatched";
@Override
protected Object[][] getContents() {
return new Object[][] {
{KEY, VALUE}
};
}
}
private static Logger getLogger(String name) {
return PatchedUsage.getLogger(name);
}
private static Logger getLogger(String name, ResourceBundle rb) {
return PatchedUsage.getLogger(name, rb);
}
private static void printLogger(Logger logger) {
System.err.println("logger name: " + logger.getName()
+ ", logger class: " + logger.getClass());
}
private static void assertTrue(boolean b) {
if (!b) {
throw new RuntimeException("expected true, but get false.");
}
}
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang;
import java.lang.System.Logger;
import java.util.ResourceBundle;
/*
* PatchedUsage is patched into java.base, it will be used by
* PatchedClient to test when logger client is in patched module
*/
public class PatchedUsage {
public static Logger getLogger(String name) {
check();
return System.getLogger(name);
}
public static Logger getLogger(String name, ResourceBundle rb) {
check();
return System.getLogger(name, rb);
}
private static void check() {
final Module m = PatchedUsage.class.getModule();
final ClassLoader moduleCL = m.getClassLoader();
assertTrue(m.isNamed());
assertTrue(moduleCL == null);
}
private static void assertTrue(boolean b) {
if (!b) {
throw new RuntimeException("expected true, but get false.");
}
}
}

View file

@ -0,0 +1,159 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package pkg.b.t;
import java.lang.reflect.Method;
import java.lang.System.Logger;
import java.time.Instant;
import java.util.ResourceBundle;
import java.util.ListResourceBundle;
/*
* Tests when logger client is in unnamed module
*/
public class TestB {
public static void main(String[] args) throws Exception {
assertTrue(args.length == 2);
String loggerMode = args[0];
String loggerClassName = args[1];
System.err.println("TestB starting at " + Instant.now());
try {
testLogger(loggerMode, loggerClassName);
testLog(loggerClassName);
} finally {
System.err.println("TestB finished at " + Instant.now());
}
}
/*
* Tests System.getLogger(String) get expected logger.
*/
private static void testLogger(String loggerMode, String loggerClassName) {
final Module m = TestB.class.getModule();
final ClassLoader moduleCL = m.getClassLoader();
assertTrue(!m.isNamed());
assertTrue(moduleCL != null);
String name = "test.b";
Logger logger = getLogger(name);
printLogger(logger);
final Module lm = logger.getClass().getModule();
final ClassLoader loggerCL = lm.getClassLoader();
if (loggerMode.equals("system")) {
assertTrue(lm.isNamed());
assertTrue(loggerCL == null);
} else if(loggerMode.equals("named")) {
assertTrue(lm.isNamed());
assertTrue(loggerCL != null);
} else if(loggerMode.equals("unnamed")) {
assertTrue(!lm.isNamed());
assertTrue(loggerCL != null);
} else {
throw new RuntimeException("wrong parameter");
}
assertTrue(loggerClassName.equals(logger.getClass().getName()));
assertTrue(!loggerClassName.equals("jdk.internal.logger.LazyLoggers$JdkLazyLogger"));
}
/*
* Tests Logger retrieved by System.getLogger(String, ResourceBundle) and
* System.getLogger(String) works well.
*/
private static void testLog(String loggerClassName) throws Exception {
if (loggerClassName.equals("pkg.a.l.LoggerA")
|| loggerClassName.equals("pkg.b.l.LoggerB")) {
String name = "test.b.B";
String plainMsg = "this is test log message #1";
ResourceBundle rb = new MyResourcesB();
Throwable ex = new Throwable("this is an expected exception to be logged");
Class<?> clazz = Class.forName(loggerClassName);
Method method = clazz.getMethod("checkLog", String.class,
System.Logger.Level.class,
ResourceBundle.class, String.class,
Throwable.class, Object[].class);
Logger logger = getLogger(name);
printLogger(logger);
assertTrue(logger.getClass().getName().equals(loggerClassName));
assertTrue(logger.getName().equals(loggerClassName));
logger.log(Logger.Level.WARNING, plainMsg);
boolean pass = (boolean)method.invoke(null, name, Logger.Level.WARNING,
null, plainMsg, ex, (Object)null);
assertTrue(pass);
pass = (boolean)method.invoke(null, name, Logger.Level.INFO,
rb, MyResourcesB.VALUE, (Throwable)null,
(Object)null);
assertTrue(!pass);
logger = getLogger(name, rb);
printLogger(logger);
assertTrue(logger.getClass().getName()
.equals("jdk.internal.logger.LocalizedLoggerWrapper"));
assertTrue(logger.getName().equals(loggerClassName));
logger.log(Logger.Level.INFO, MyResourcesB.KEY);
pass = (boolean)method.invoke(null, name, Logger.Level.INFO,
rb, MyResourcesB.VALUE, (Throwable)null,
(Object)null);
assertTrue(pass);
pass = (boolean)method.invoke(null, name, Logger.Level.WARNING,
null, plainMsg, ex, (Object)null);
assertTrue(pass);
}
}
private static class MyResourcesB extends ListResourceBundle {
static final String KEY = "this is the key in MyResourcesB";
static final String VALUE = "THIS IS THE VALUE IN MyResourcesB";
@Override
protected Object[][] getContents() {
return new Object[][] {
{KEY, VALUE}
};
}
}
private static Logger getLogger(String name) {
return System.getLogger(name);
}
private static Logger getLogger(String name, ResourceBundle rb) {
return System.getLogger(name, rb);
}
private static void printLogger(Logger logger) {
System.err.println("logger name: " + logger.getName()
+ ", logger class: " + logger.getClass());
}
private static void assertTrue(boolean b) {
if (!b) {
throw new RuntimeException("expected true, but get false.");
}
}
}

View file

@ -0,0 +1,121 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package pkg.b.l;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.ResourceBundle;
public class LoggerB implements System.Logger {
// ---- test utility fields and methods ----
private static Map<String, LoggerB> map = new HashMap<>();
public static LoggerB getLogger(String name) {
return map.computeIfAbsent(name, (n) -> new LoggerB());
}
public static boolean checkLog(String name, Level level, ResourceBundle bundle,
String format, Throwable throwable, Object... params) {
LoggerB logger = map.get(name);
LogEvent event = new LogEvent(level, bundle, format, null, params);
for (LogEvent l : logger.queue) {
if (l.equals(event)) {
return true;
}
}
return false;
}
// ---- logger implementation ----
private Queue<LogEvent> queue = new LinkedList<>();
@Override
public String getName() {
return this.getClass().getName();
}
@Override
public boolean isLoggable(Level level) {
return true;
}
@Override
public void log(Level level, ResourceBundle bundle, String format, Object... params) {
String msg = bundle != null ? bundle.getString(format) : format;
log(new LogEvent(level, bundle, msg, null, params));
}
@Override
public void log(Level level, ResourceBundle bundle, String format, Throwable throwable) {
String msg = bundle != null ? bundle.getString(format) : format;
log(new LogEvent(level, bundle, msg, throwable, (Object)null));
}
void log(LogEvent l) {
print(l);
queue.add(l);
}
private void print(LogEvent l) {
System.err.println("LoggerB Message"+ l);
}
public Queue<LogEvent> getLogEvent() {
return queue;
}
public static class LogEvent {
public LogEvent(Level level, ResourceBundle bundle, String format,
Throwable throwable, Object... params) {
this.level = level;
this.bundle = bundle;
this.format = format;
this.throwable = throwable;
this.params = params;
}
@Override
public boolean equals(Object o) {
if (o instanceof LogEvent) {
LogEvent e = (LogEvent)o;
return level == e.level
&& bundle == e.bundle
&& format == e.format
&& params == e.params;
}
return false;
}
private Level level;
private ResourceBundle bundle;
private String format;
private Throwable throwable;
private Object[] params;
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package pkg.b.p;
import pkg.b.l.LoggerB;
public class LoggerFinderB extends System.LoggerFinder {
@Override
public System.Logger getLogger(String name, Module module) {
return LoggerB.getLogger(name);
}
}

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* Check that the value of file.encoding and sun.jnu.encoding match the expected
* values passed in on the command-line.
*/
public class ExpectedEncoding {
public static void main(String[] args) {
boolean failed = false;
if (args.length != 2) {
System.out.println("Usage:");
System.out.println("$ java ExpectedEncoding <expected file.encoding> <expected sun.jnu.encoding>");
System.out.println("$ use \"skip\" to skip checking property's value");
System.exit(1);
}
String expectFileEnc = args[0];
String expectSunJnuEnc = args[1];
String fileEnc = System.getProperty("file.encoding");
String jnuEnc = System.getProperty("sun.jnu.encoding");
if ("skip".equals(expectFileEnc)) {
System.err.println("Expected file.encoding is \"skip\", ignoring");
} else {
if (fileEnc == null || !fileEnc.equals(expectFileEnc)) {
System.err.println("Expected file.encoding: " + expectFileEnc);
System.err.println("Actual file.encoding: " + fileEnc);
failed = true;
}
}
if ("skip".equals(expectSunJnuEnc)) {
System.err.println("Expected sun.jnu.encoding is \"skip\", ignoring");
} else {
if (jnuEnc == null || !jnuEnc.equals(expectSunJnuEnc)) {
System.err.println("Expected sun.jnu.encoding: " + expectSunJnuEnc);
System.err.println("Actual sun.jnu.encoding: " + jnuEnc);
failed = true;
}
}
if (failed) {
throw new RuntimeException("Test Failed");
}
}
}

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8003228 8260265
* @summary Test the value of sun.jnu.encoding on Mac
* @requires (os.family == "mac")
* @library /test/lib
* @build jdk.test.lib.process.*
* ExpectedEncoding
* @run main MacJNUEncoding UTF-8 UTF-8 C
* @run main MacJNUEncoding US-ASCII UTF-8 C COMPAT
* @run main MacJNUEncoding UTF-8 UTF-8 en_US.UTF-8
* @run main MacJNUEncoding UTF-8 UTF-8 en_US.UTF-8 COMPAT
*/
import java.util.List;
import java.util.Map;
import jdk.test.lib.process.ProcessTools;
public class MacJNUEncoding {
public static void main(String[] args) throws Exception {
if (args.length != 3 && args.length != 4) {
System.out.println("Usage:");
System.out.println(" java MacJNUEncoding"
+ " <expected file.encoding> <expected sun.jnu.encoding> <locale> [<user's file.encoding>]");
throw new IllegalArgumentException("missing arguments");
}
final String locale = args[2];
System.out.println("Running test for locale: " + locale);
var cmds = (args.length == 4)
? List.of("-Dfile.encoding=" + args[3], ExpectedEncoding.class.getName(), args[0], args[1])
: List.of(ExpectedEncoding.class.getName(), args[0], args[1]);
ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(cmds);
Map<String, String> env = pb.environment();
env.put("LANG", locale);
env.put("LC_ALL", locale);
ProcessTools.executeProcess(pb)
.outputTo(System.out)
.errorTo(System.err)
.shouldHaveExitValue(0);
}
}

View file

@ -0,0 +1,172 @@
/*
* Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.*;
/*
* @test
* @bug 8011194 8260265
* @summary Test value of file.encoding for corresponding value of LANG, etc
* @library ../../../../tools/launcher/ ../
* @modules jdk.compiler
* @build TestHelper TestFileEncoding ExpectedEncoding
* @run main TestFileEncoding UTF-8
* @run main/othervm -Dfile.encoding=MyEncoding -DuserEncoding=MyEncoding TestFileEncoding MyEncoding
* @run main/othervm -DuserEncoding=COMPAT TestFileEncoding UTF-8
* @run main TestFileEncoding UTF-8 en_US.UTF-8
* @run main/othervm -Dfile.encoding=MyEncoding -DuserEncoding=MyEncoding TestFileEncoding MyEncoding en_US.UTF-8
* @run main/othervm -DuserEncoding=COMPAT TestFileEncoding UTF-8 en_US.UTF-8
* @run main TestFileEncoding UTF-8 C
* @run main/othervm -Dfile.encoding=MyEncoding -DuserEncoding=MyEncoding TestFileEncoding MyEncoding C
* @run main/othervm -DuserEncoding=COMPAT TestFileEncoding US-ASCII C
* @author Brent Christian
*/
/**
* Setup the environment and run a sub-test to check the expected value of
* file.encoding, based on the value(s) of encoding-related environment vars
* (LANG, LC_ALL, LC_CTYPE).
*
* The first argument (required) is the expected value of the
* file.encoding System property.
* The second argument (optional) is the value to set to the LANG/etc env vars.
*/
public class TestFileEncoding {
private static final String TEST_NAME = "ExpectedEncoding";
private String expectedEncoding; // Expected value for file.encoding
private String langVar = null; // Value to set for LANG, etc
private static Set<String> envToRm = HashSet.newHashSet(3);
static {
// Take these vars out of the test's run environment, possibly adding
// our own value back in.
envToRm.add("LANG");
envToRm.add("LC_ALL");
envToRm.add("LC_CTYPE");
}
public TestFileEncoding(String expectedEncoding) {
this.expectedEncoding = expectedEncoding;
}
public TestFileEncoding(String expectedEncoding, String langVar) {
this.expectedEncoding = expectedEncoding;
this.langVar = langVar;
}
/*
* Launch ExpectedEncoding with the given parameters, check for the
* expected file.encoding.
*/
private void run() {
String testClasses = System.getProperty("test.classes");
// Pick up VM opts
String vmOptsStr = System.getProperty("test.vm.opts");
System.out.println("test.vm.opts: " + vmOptsStr);
String[] vmOpts = new String[0];
if (vmOptsStr != null && !"".equals(vmOptsStr)) {
vmOpts = vmOptsStr.split(" ");
System.out.println("found vm options:");
for (String opt : vmOpts) {
System.out.println(" <" + opt + ">");
}
}
// Build java cmd
LinkedList<String> cmdList = new LinkedList<>();
cmdList.add(TestHelper.javaCmd);
for (String vmOpt : vmOpts) {
if (vmOpt != null && !vmOpt.equals("")) {
cmdList.add(vmOpt);
}
}
// See if the user specified a file.encoding that we should pass through
String userEncoding = System.getProperty("userEncoding");
if (userEncoding != null) {
cmdList.add("-Dfile.encoding="+userEncoding);
}
cmdList.add("-cp");
cmdList.add(testClasses);
cmdList.add(TEST_NAME);
cmdList.add(expectedEncoding);
cmdList.add("skip"); // ignore sun.jnu.encoding for this test
String cmdArray[] = new String[cmdList.size()];
cmdList.toArray(cmdArray);
// Run the test(s)
if (langVar == null) {
System.out.println("TestFileEncoding: Running with no envvars set");
TestHelper.TestResult tr = TestHelper.doExec(null, envToRm,
cmdArray);
checkResult(tr);
} else {
runWithEnvVar("LANG", cmdArray);
runWithEnvVar("LC_ALL", cmdArray);
runWithEnvVar("LC_CTYPE", cmdArray);
}
}
/*
* Run the test, setting the environment named by envVarName to the value
* in langVar.
*/
private void runWithEnvVar(String envVarName, String[] cmdArray) {
Map<String, String> envToAdd = new HashMap<>(1);
TestHelper.TestResult tr = null;
System.out.println("TestFileEncoding: Running with " + envVarName + "=" + langVar);
envToAdd.put(envVarName, langVar);
tr = TestHelper.doExec(envToAdd, envToRm, cmdArray);
checkResult(tr);
}
private void checkResult(TestHelper.TestResult tr) {
System.out.println(tr);
if (!tr.isOK()) {
throw new RuntimeException("TEST FAILED: !tr.isOK()");
}
}
public static void main(String[] args) {
TestFileEncoding cfe = null;
if (!TestHelper.isMacOSX) {
System.out.println("Test is currently only for Mac OS X - pass.");
return;
}
if (args.length == 1) {
cfe = new TestFileEncoding(args[0]);
} else if (args.length == 2) {
cfe = new TestFileEncoding(args[0], args[1]);
} else {
System.out.println("Usage: TestFileEncoding <expected file.encoding>");
System.out.println(" TestFileEncoding <expected file.encoding> <value for LANG/etc env var>");
return;
}
cfe.run();
}
}

View file

@ -0,0 +1,93 @@
/*
* Copyright (c) 2015, 2021 SAP SE. All rights reserved.
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import jdk.test.lib.Platform;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
import jtreg.SkippedException;
/*
* @test
* @bug 8132374 8359830
* @summary Check that the value of the os.version property is equal
* to the value of the corresponding OS provided tools.
* @library /test/lib
* @build jtreg.SkippedException
* @run main OsVersionTest
* @author Volker Simonis
*/
public class OsVersionTest {
public static void main(String[] args) throws Throwable {
final String osVersion = System.getProperty("os.version");
if (osVersion == null) {
throw new Error("Missing value for os.version system property");
}
if (Platform.isLinux()) {
OutputAnalyzer output = ProcessTools.executeProcess("uname", "-r");
output.shouldHaveExitValue(0);
if (!osVersion.equals(output.getOutput().trim())) {
throw new Error(osVersion + " != " + output.getOutput().trim());
}
} else if (Platform.isOSX()) {
testMacOS(osVersion);
} else if (Platform.isAix()) {
OutputAnalyzer output1 = ProcessTools.executeProcess("uname", "-v");
output1.shouldHaveExitValue(0);
OutputAnalyzer output2 = ProcessTools.executeProcess("uname", "-r");
output2.shouldHaveExitValue(0);
String version = output1.getOutput().trim() + "." + output2.getOutput().trim();
if (!osVersion.equals(version)) {
throw new Error(osVersion + " != " + version);
}
} else if (Platform.isWindows()) {
OutputAnalyzer output = ProcessTools.executeProcess("cmd", "/c", "ver");
output.shouldHaveExitValue(0);
String version = output.firstMatch(".+\\[Version ([0-9.]+)\\]", 1);
if (version == null || !version.startsWith(osVersion)) {
throw new Error(osVersion + " != " + version);
}
} else {
throw new jtreg.SkippedException("This test is currently not supported on " +
Platform.getOsName());
}
}
private static void testMacOS(final String sysPropOsVersion) throws Exception {
final ProcessBuilder pb = new ProcessBuilder("sw_vers", "-productVersion");
// if the test was launched with SYSTEM_VERSION_COMPAT environment variable set,
// then propagate that to the sw_vers too
final String versionCompat = System.getenv().get("SYSTEM_VERSION_COMPAT");
if (versionCompat != null) {
pb.environment().put("SYSTEM_VERSION_COMPAT", versionCompat);
}
final OutputAnalyzer output = ProcessTools.executeCommand(pb);
output.shouldHaveExitValue(0);
final String swVersOutput = output.getOutput().trim();
if (!sysPropOsVersion.equals(swVersOutput)) {
throw new Error("sw_vers reports macOS version: " + swVersOutput
+ " but os.version system property reports version: " + sysPropOsVersion);
}
}
}

View file

@ -0,0 +1,264 @@
/*
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.lang.System;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.stream.Collectors;
import org.testng.Assert;
import org.testng.IMethodInstance;
import org.testng.IMethodInterceptor;
import org.testng.TestListenerAdapter;
import org.testng.TestNG;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
/*
* @test
* @bug 4463345 4244670 8030781 8265989 8283620
* @summary Simple test of System getProperty, setProperty, clearProperty,
* getProperties, and setProperties
* @run testng/othervm PropertyTest
*/
@Test
public class PropertyTest {
@DataProvider(name = "requiredProperties")
static Object[][] requiredProperties() {
return new Object[][]{
{"java.version"},
{"java.version.date"},
{"java.vendor"},
{"java.vendor.url"},
{"java.home"},
{"java.vm.specification.version"},
{"java.vm.specification.vendor"},
{"java.vm.specification.name"},
{"java.vm.version"},
{"java.vm.vendor"},
{"java.vm.name"},
{"java.specification.version"},
{"java.specification.vendor"},
{"java.specification.name"},
{"java.class.version"},
{"java.class.path"},
{"java.library.path"},
{"java.io.tmpdir"},
{"os.arch"},
{"os.version"},
{"file.separator"},
{"path.separator"},
{"line.separator"},
{"user.name"},
{"user.home"},
{"user.dir"},
{"java.runtime.version"},
{"java.runtime.name"},
{"native.encoding"},
{"stdin.encoding"},
{"stdout.encoding"},
{"stderr.encoding"},
};
}
@Test
static void getTest() {
System.setProperty("blah", "blech");
Assert.assertEquals(System.getProperty("blah"), "blech");
try {
System.getProperty(null);
Assert.fail("Failed: expected NullPointerException");
} catch (NullPointerException npe) {
// Correct result
}
try {
System.getProperty("");
Assert.fail("Failed: expected IllegalArgumentException");
} catch (IllegalArgumentException iae) {
// Correct result
}
}
@Test
static void clearTest() {
System.setProperty("blah", "blech");
Assert.assertEquals(System.getProperty("blah"), "blech");
System.clearProperty("blah");
Assert.assertNull(System.getProperty("blah"));
try {
System.clearProperty(null);
Assert.fail("Failed: expected NullPointerException");
} catch (NullPointerException npe) {
// Correct result
}
try {
System.clearProperty("");
Assert.fail("Failed: expected IllegalArgumentException");
} catch (IllegalArgumentException iae) {
// Correct result
}
}
@Test
static void setTest() {
System.setProperty("blah", "blech");
Assert.assertEquals(System.getProperty("blah"), "blech");
System.setProperty("blah", "");
Assert.assertEquals(System.getProperty("blah"), "");
try {
System.setProperty(null, null);
Assert.fail("Failed: expected NullPointerException");
} catch (NullPointerException npe) {
// Correct result
}
try {
System.setProperty("blah", null);
Assert.fail("Failed: expected NullPointerException");
} catch (NullPointerException npe) {
// Correct result
}
try {
System.setProperty(null, "blech");
Assert.fail("Failed: expected NullPointerException");
} catch (NullPointerException npe) {
// Correct result
}
try {
System.setProperty("", "blech");
Assert.fail("Failed: expected IllegalArgumentException");
} catch (IllegalArgumentException iae) {
// Correct result
}
try {
System.setProperty("", "");
Assert.fail("Failed: expected IllegalArgumentException");
} catch (IllegalArgumentException iae) {
// Correct result
}
}
@Test
static void replaceSetProperties() {
Properties oldProps = System.getProperties();
Properties newProps = new Properties();
oldProps.forEach( (k,v) -> newProps.put(k,v));
System.setProperties(newProps);
Assert.assertSame(System.getProperties(), newProps,
"getProperties not the same as setProperties");
final String KEY = "blah";
final String VALUE = "blech";
// Set via Property instance; get via System methods
newProps.setProperty(KEY, VALUE);
Assert.assertEquals(System.getProperty(KEY), VALUE, KEY);
String s = (String)newProps.remove(KEY);
Assert.assertEquals(s, VALUE);
Assert.assertNull(System.getProperty(KEY), KEY);
// Set via System methods; Get via Property instance;
System.setProperty(KEY, VALUE);
Assert.assertEquals(newProps.getProperty(KEY), VALUE);
String t = System.clearProperty(KEY);
Assert.assertEquals(t, VALUE, KEY);
Assert.assertNull(newProps.getProperty(KEY), KEY);
}
@Test
static void setNullProperties() {
Properties oldProps = System.getProperties();
Properties savedProps = new Properties();
oldProps.forEach((k,v) -> {
if (v == null) {
throw new RuntimeException("null value, key: " + k);
}
savedProps.put(k,v);
});
// Re-initialize properties
System.setProperties(null);
Properties newProps = System.getProperties();
Object[][] propnames = requiredProperties();
for (Object[] p : propnames) {
String name = (String)p[0];
Assert.assertEquals(System.getProperty(name), savedProps.getProperty(name), name);
Assert.assertEquals(newProps.getProperty(name), savedProps.getProperty(name), name);
}
}
// Verify all the required properties have values from System.getProperty and
// System.getProperties()
@Test(dataProvider = "requiredProperties")
static void checkRequiredProperties(String name) {
Assert.assertNotNull(System.getProperty(name), name);
Properties props = System.getProperties();
Assert.assertNotNull(props.getProperty(name), name);
}
@SuppressWarnings("raw_types")
@Test(enabled=false)
public static void main(String[] args) {
TestListenerAdapter tla = new TestListenerAdapter();
Class<?>[] testclass = {PropertyTest.class};
TestNG testng = new TestNG();
testng.setTestClasses(testclass);
testng.addListener(tla);
if (args.length > 0) {
IMethodInterceptor intercept = (m, c) -> {
List<IMethodInstance> x = m.stream()
.filter(m1 -> m1.getMethod().getMethodName().contains(args[0]))
.collect(Collectors.toList());
return x;
};
testng.setMethodInterceptor(intercept);
}
testng.run();
tla.getPassedTests()
.stream().forEach(t -> System.out.printf("Passed: %s%s%n", t.getName(),
List.of(t.getParameters())));
tla.getFailedTests()
.stream().forEach(t -> System.out.printf("Failed: %s%s%n", t.getName(),
List.of(t.getParameters())));
}
}

View file

@ -0,0 +1,115 @@
/*
* Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8266459 8268349 8269543 8270380
* @summary check various warnings
* @library /test/lib
*/
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.util.JarUtils;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.nio.file.Path;
public class SecurityManagerWarnings {
public static void main(String args[]) throws Exception {
if (args.length == 0) {
System.setProperty("test.noclasspath", "true");
String testClasses = System.getProperty("test.classes");
failLateTest(null, testClasses);
failLateTest("disallow", testClasses);
failEarlyTest("allow", testClasses);
failEarlyTest("", testClasses);
failEarlyTest("default", testClasses);
failEarlyTest("java.lang.SecurityManager", testClasses);
JarUtils.createJarFile(Path.of("a.jar"),
Path.of(testClasses),
Path.of("SecurityManagerWarnings.class"),
Path.of("A.class"));
failLateTest(null, "a.jar");
} else {
PrintStream oldErr = System.err;
// Modify System.err, thus make sure warnings are always printed
// to the original System.err and will not be swallowed.
System.setErr(new PrintStream(new ByteArrayOutputStream()));
try {
A.run(); // System.setSecurityManager(null);
} catch (Exception e) {
// Exception messages must show in original stderr
e.printStackTrace(oldErr);
throw e;
}
}
}
// When -Djava.security.manager is not set, or set to "allow",
// or "disallow", JVM starts but setSecurityManager will fail.
static void failLateTest(String prop, String cp) throws Exception {
run(prop, cp)
.shouldNotHaveExitValue(0)
.stderrShouldContain("at SecurityManagerWarnings.main")
.stderrShouldContain("UnsupportedOperationException: Setting a Security Manager is not supported");
}
// When -Djava.security.manager is set to any other values,
// JVM will not start.
static void failEarlyTest(String prop, String cp) throws Exception {
run(prop, cp)
.shouldNotHaveExitValue(0)
.shouldNotContain("SecurityManagerWarnings.main")
.shouldContain("at java.lang.System.initPhase3")
.shouldContain("Error: A command line option has attempted to allow or enable the Security Manager.");
}
static OutputAnalyzer run(String prop, String cp) throws Exception {
ProcessBuilder pb;
if (prop == null) {
pb = ProcessTools.createTestJavaProcessBuilder(
"-cp", cp,
"SecurityManagerWarnings", "run");
} else {
pb = ProcessTools.createTestJavaProcessBuilder(
"-cp", cp,
"-Djava.security.manager=" + prop,
"SecurityManagerWarnings", "run");
}
return ProcessTools.executeProcess(pb)
.stderrShouldNotContain("AccessControlException");
}
}
class A {
static void run() {
System.setSecurityManager(null);
}
}

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import jdk.internal.util.SystemProps.Raw;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/*
* @test
* @summary Test to verify that the SystemProps.Raw _xxx_NDX indices are unique and without gaps.
* @modules java.base/jdk.internal.util:+open
* @run testng VerifyRawIndexesTest
*/
@Test
public class VerifyRawIndexesTest {
/**
* Check that the Raw._*_NDX indexes are sequential and followed by
* the FIXED_LENGTH value.
* It verifies there there are no gaps or duplication in the declarations.
*/
@Test
void verifyIndexes() {
Field[] fields = Raw.class.getDeclaredFields();
int expectedModifiers = Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL;
int next = 0; // indexes start at zero
int fixedLength = -1;
for (Field f : fields) {
try {
int mods = f.getModifiers();
String name = f.getName();
if (((mods & expectedModifiers) == expectedModifiers) &&
(name.endsWith("_NDX") || name.equals("FIXED_LENGTH"))) {
f.setAccessible(true);
int ndx = f.getInt(null);
System.out.printf("%s: %s%n", name, ndx);
Assert.assertEquals(ndx, next, "index value wrong");
if (name.equals("FIXED_LENGTH")) {
fixedLength = next; // remember for final check
}
next++;
} else {
System.out.printf("Ignoring field: " + f);
}
} catch (IllegalAccessException iae) {
Assert.fail("unexpected exception", iae);
}
}
Assert.assertEquals(next - 1, fixedLength,
"FIXED_LENGTH should be 1 greater than max of _NDX indexes");
}
}

View file

@ -0,0 +1,105 @@
/*
* Copyright (c) 2004, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 4989690 6259855 6706299 8204565
* @summary Check that version-related system property invariants hold.
* @author Martin Buchholz
*/
import java.io.*;
import java.net.URLClassLoader;
import java.net.URL;
public class Versions {
static String getProperty(String prop) throws Exception {
String value = System.getProperty(prop);
if (value == null)
throw new Exception("No such system property: " + prop);
System.out.printf("%s=%s%n", prop, value);
return value;
}
static ClassLoader cl;
static void checkClassVersion(int major, int minor, boolean expectSupported)
throws Exception
{
final String className = "ClassVersionTest";
final String classFile = className + ".class";
// We create an invalid class file, (only magic and version info),
// but the version info must be checked before the body.
final DataOutputStream dos =
new DataOutputStream(new FileOutputStream(classFile));
dos.writeLong((0xCafeBabel << 32) + (minor << 16) + major);
dos.close();
boolean supported = true;
try {
Class.forName(className, false, cl);
} catch (UnsupportedClassVersionError e) {
supported = false;
} catch (Throwable t) {
// We expect an Exception indicating invalid class file
}
new File(classFile).delete();
if (supported != expectSupported)
throw new Exception("Forgot to update java.class.version?");
}
public static void main(String [] args) throws Exception {
String classVersion = getProperty("java.class.version");
String javaVersion = getProperty("java.version");
String runtimeVersion = getProperty("java.runtime.version");
String specVersion = getProperty("java.specification.version");
String vmSpecVersion = getProperty("java.vm.specification.version");
String featureVersion = Integer.toString(Runtime.version().feature());
if (! (javaVersion.startsWith(specVersion) &&
runtimeVersion.startsWith(specVersion) &&
specVersion.equals(featureVersion) &&
vmSpecVersion.equals(featureVersion))) {
throw new Exception("Invalid version-related system properties");
}
//----------------------------------------------------------------
// Check that java.class.version is correct.
// Injecting a larger major or minor version number into a
// .class file should result in UnsupportedClassVersionError.
//----------------------------------------------------------------
String[] versions = classVersion.split("\\.");
int majorVersion = Integer.parseInt(versions[0]);
int minorVersion = Integer.parseInt(versions[1]);
System.out.printf("majorVersion=%s%n",majorVersion);
System.out.printf("minorVersion=%s%n",minorVersion);
// Look in ".", and *not* in CLASSPATH
cl = new URLClassLoader(new URL[]{new File("./").toURL()}, null);
checkClassVersion(majorVersion , minorVersion , true );
checkClassVersion(majorVersion + 1, minorVersion , false);
checkClassVersion(majorVersion , minorVersion + 1, false);
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 4354680
* @summary runFinalization() should not clear or ignore interrupt bit
* @run main FinInterrupt
*/
public class FinInterrupt {
public static void main(String[] args) throws Exception {
Thread.currentThread().interrupt();
System.runFinalization();
if (Thread.interrupted()) {
System.out.println("Passed: interrupt bit was still set.");
} else {
throw new AssertionError("interrupt bit was cleared");
}
}
}

View file

@ -0,0 +1,131 @@
/*
* Copyright (c) 1998, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
@bug 4026895
@summary Ensure that System.runFinalization does not run finalizers in the
thread that invokes it
*/
public class FinThreads {
static Thread mainThread;
static Object lock = new Object(); /* Protects following two fields */
static Thread finalizerThread = null;
static Thread finalizedBy = null;
static class Foo {
boolean catchFinalizer = false;
/* Instances are only created in an auxiliary thread, in order to
guard against stray references from the current thread's stack
*/
public static void create(final boolean catchFinalizer)
throws InterruptedException
{
Thread t = new Thread(new Runnable() {
public void run() {
new Foo(catchFinalizer);
}});
t.start();
t.join();
}
public Foo(boolean catchFinalizer) {
this.catchFinalizer = catchFinalizer;
}
public void finalize() throws InterruptedException {
if (catchFinalizer) {
boolean gotFinalizer = false;
synchronized (lock) {
if (finalizerThread == null) {
finalizerThread = Thread.currentThread();
gotFinalizer = true;
}
}
if (gotFinalizer) {
System.err.println("Caught finalizer thread; sleeping...");
Thread.sleep(Long.MAX_VALUE);
}
} else {
synchronized (lock) {
finalizedBy = Thread.currentThread();
}
System.err.println("Test object finalized by " + finalizedBy);
}
}
}
static void alarm(final Thread sleeper, final long delay)
throws InterruptedException
{
Thread t = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(delay);
System.err.println("Waking " + sleeper);
sleeper.interrupt();
} catch (InterruptedException x) { }
}});
t.setDaemon(true);
t.start();
}
public static void main(String[] args) throws Exception {
mainThread = Thread.currentThread();
/* Find the finalizer thread and put it to sleep */
for (;;) {
Foo.create(true);
System.gc();
synchronized (lock) {
if (finalizerThread != null) break;
}
}
/* Now create a finalizable object and run finalization */
alarm(finalizerThread, 5000);
Foo.create(false);
for (;;) {
System.gc();
System.runFinalization();
synchronized (lock) {
if (finalizedBy != null) break;
}
}
if (finalizedBy == mainThread)
throw new Exception("Finalizer run in main thread");
}
}

Some files were not shown because too many files have changed in this diff Show more