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:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
59
test/jdk/java/lang/System/AllowSecurityManager.java
Normal file
59
test/jdk/java/lang/System/AllowSecurityManager.java
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
39
test/jdk/java/lang/System/Available.java
Normal file
39
test/jdk/java/lang/System/Available.java
Normal 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");
|
||||
}
|
||||
}
|
||||
94
test/jdk/java/lang/System/FileEncodingTest.java
Normal file
94
test/jdk/java/lang/System/FileEncodingTest.java
Normal 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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
36
test/jdk/java/lang/System/IHashCode.java
Normal file
36
test/jdk/java/lang/System/IHashCode.java
Normal 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);
|
||||
}
|
||||
}
|
||||
87
test/jdk/java/lang/System/Logger/Level/LoggerLevelTest.java
Normal file
87
test/jdk/java/lang/System/Logger/Level/LoggerLevelTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
577
test/jdk/java/lang/System/Logger/custom/CustomLoggerTest.java
Normal file
577
test/jdk/java/lang/System/Logger/custom/CustomLoggerTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
CustomLoggerTest$BaseLoggerFinder
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
762
test/jdk/java/lang/System/Logger/default/DefaultLoggerTest.java
Normal file
762
test/jdk/java/lang/System/Logger/default/DefaultLoggerTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
BaseLoggerFinder
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
loggerfinder.SimpleLoggerFinder
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
loggerfinder.SimpleLoggerFinder
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
BaseLoggerFinder
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
BaseLoggerFinder
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
BaseLoggerFinder
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
LogProducerFinder
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
BaseLoggerFinder
|
||||
BaseLoggerFinder2
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
LogProducerFinder
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,2 @@
|
|||
LoggerFinderBackendTest$CustomLoggerFinder
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
272
test/jdk/java/lang/System/LoggerFinder/modules/Base.java
Normal file
272
test/jdk/java/lang/System/LoggerFinder/modules/Base.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
pkg.b.p.LoggerFinderB
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
66
test/jdk/java/lang/System/MacEncoding/ExpectedEncoding.java
Normal file
66
test/jdk/java/lang/System/MacEncoding/ExpectedEncoding.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
67
test/jdk/java/lang/System/MacEncoding/MacJNUEncoding.java
Normal file
67
test/jdk/java/lang/System/MacEncoding/MacJNUEncoding.java
Normal 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);
|
||||
}
|
||||
}
|
||||
172
test/jdk/java/lang/System/MacEncoding/TestFileEncoding.java
Normal file
172
test/jdk/java/lang/System/MacEncoding/TestFileEncoding.java
Normal 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();
|
||||
}
|
||||
}
|
||||
93
test/jdk/java/lang/System/OsVersionTest.java
Normal file
93
test/jdk/java/lang/System/OsVersionTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
264
test/jdk/java/lang/System/PropertyTest.java
Normal file
264
test/jdk/java/lang/System/PropertyTest.java
Normal 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())));
|
||||
}
|
||||
}
|
||||
115
test/jdk/java/lang/System/SecurityManagerWarnings.java
Normal file
115
test/jdk/java/lang/System/SecurityManagerWarnings.java
Normal 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);
|
||||
}
|
||||
}
|
||||
79
test/jdk/java/lang/System/VerifyRawIndexesTest.java
Normal file
79
test/jdk/java/lang/System/VerifyRawIndexesTest.java
Normal 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");
|
||||
}
|
||||
}
|
||||
105
test/jdk/java/lang/System/Versions.java
Normal file
105
test/jdk/java/lang/System/Versions.java
Normal 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);
|
||||
}
|
||||
}
|
||||
41
test/jdk/java/lang/System/finalization/FinInterrupt.java
Normal file
41
test/jdk/java/lang/System/finalization/FinInterrupt.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
131
test/jdk/java/lang/System/finalization/FinThreads.java
Normal file
131
test/jdk/java/lang/System/finalization/FinThreads.java
Normal 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
Loading…
Add table
Add a link
Reference in a new issue