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

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

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

View file

@ -0,0 +1,283 @@
/*
* Copyright (c) 2012, 2013, 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 6244047
* @author Jim Gish
* @summary throw more precise IOException when pattern specifies invalid directory
*
* @run main/othervm CheckLockLocationTest
*/
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.AccessDeniedException;
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.attribute.UserPrincipal;
import java.util.UUID;
import java.util.logging.FileHandler;
public class CheckLockLocationTest {
private static final String NON_WRITABLE_DIR = "non-writable-dir";
private static final String NOT_A_DIR = "not-a-dir";
private static final String WRITABLE_DIR = "writable-dir";
private static final String NON_EXISTENT_DIR = "non-existent-dir";
private static boolean runNonWritableDirTest;
public static void main(String... args) throws IOException {
// we'll base all file creation attempts on the system temp directory,
// %t and also try specifying non-existent directories and plain files
// that should be directories, and non-writable directories,
// to exercise all code paths of checking the lock location
// Note that on platforms like Windows that don't support
// setWritable() on a directory, we'll skip the non-writable
// directory test if setWritable(false) returns false.
//
File writableDir = setup();
// we now have three files/directories to work with:
// writableDir
// notAdir
// nonWritableDir (may not be possible on some platforms)
// nonExistentDir (which doesn't exist)
runTests(writableDir);
}
/**
* @param writableDir in which log and lock file are created
* @throws SecurityException
* @throws RuntimeException
* @throws IOException
*/
private static void runTests(File writableDir) throws SecurityException,
RuntimeException, IOException {
// Test 1: make sure we can create FileHandler in writable directory
try {
new FileHandler("%t/" + WRITABLE_DIR + "/log.log");
} catch (IOException ex) {
throw new RuntimeException("Test failed: should have been able"
+ " to create FileHandler for " + "%t/" + WRITABLE_DIR
+ "/log.log in writable directory"
+ (!writableDir.canRead() // concurrent tests running or user conf issue?
? ": directory not readable.\n\tPlease check your "
+ "environment and machine configuration."
: "."), ex);
} finally {
// the above test leaves files in the directory. Get rid of the
// files created and the directory
delete(writableDir);
}
// Test 2: creating FileHandler in non-writable directory should fail
if (runNonWritableDirTest) {
try {
new FileHandler("%t/" + NON_WRITABLE_DIR + "/log.log");
throw new RuntimeException("Test failed: should not have been able"
+ " to create FileHandler for " + "%t/" + NON_WRITABLE_DIR
+ "/log.log in non-writable directory.");
} catch (AccessDeniedException ex) {
// the right exception was thrown, so continue.
} catch (IOException ex) {
throw new RuntimeException(
"Test failed: Expected exception was not an "
+ "AccessDeniedException", ex);
}
}
// Test 3: creating FileHandler in non-directory should fail
try {
new FileHandler("%t/" + NOT_A_DIR + "/log.log");
throw new RuntimeException("Test failed: should not have been able"
+ " to create FileHandler for " + "%t/" + NOT_A_DIR
+ "/log.log in non-directory.");
} catch (FileSystemException ex) {
// the right exception was thrown, so continue.
} catch (IOException ex) {
throw new RuntimeException("Test failed: exception thrown was not a "
+ "FileSystemException", ex);
}
// Test 4: make sure we can't create a FileHandler in a non-existent dir
try {
new FileHandler("%t/" + NON_EXISTENT_DIR + "/log.log");
throw new RuntimeException("Test failed: should not have been able"
+ " to create FileHandler for " + "%t/" + NON_EXISTENT_DIR
+ "/log.log in a non-existent directory.");
} catch (NoSuchFileException ex) {
// the right exception was thrown, so continue.
} catch (IOException ex) {
throw new RuntimeException("Test failed: Expected exception "
+ "was not a NoSuchFileException", ex);
}
}
/**
* Setup all the files and directories needed for the tests
*
* @return writable directory created that needs to be deleted when done
* @throws RuntimeException
*/
private static File setup() throws RuntimeException {
// First do some setup in the temporary directory (using same logic as
// FileHandler for %t pattern)
String tmpDir = System.getProperty("java.io.tmpdir"); // i.e. %t
if (tmpDir == null) {
tmpDir = System.getProperty("user.home");
}
File tmpOrHomeDir = new File(tmpDir);
// Create a writable directory here (%t/writable-dir)
File writableDir = new File(tmpOrHomeDir, WRITABLE_DIR);
if (!createFile(writableDir, true)) {
throw new RuntimeException("Test setup failed: unable to create"
+ " writable working directory "
+ writableDir.getAbsolutePath() );
}
if (!writableDir.canRead()) {
throw new RuntimeException("Test setup failed: can't read "
+ " writable working directory "
+ writableDir.getAbsolutePath() );
}
// writableDirectory and its contents will be deleted after the test
// that uses it.
// check that we can write in the new writable dir.
File dummyFile = new File(writableDir, UUID.randomUUID().toString() + ".txt" );
try {
if (!dummyFile.createNewFile()) {
throw new RuntimeException("Test setup failed: can't create "
+ " dummy file in writable working directory "
+ dummyFile.getAbsolutePath() );
}
try (OutputStream os = new FileOutputStream(dummyFile)) {
os.write('A');
} finally {
dummyFile.delete();
}
if (dummyFile.canRead()) {
throw new RuntimeException("Test setup failed: can't delete "
+ " dummy file in writable working directory "
+ dummyFile.getAbsolutePath() );
}
System.out.println("Successfully created and deleted dummy file: " +
dummyFile.getAbsolutePath());
} catch(IOException x) {
throw new RuntimeException("Test setup failed: can't write "
+ " or delete dummy file in writable working directory "
+ dummyFile.getAbsolutePath(), x);
}
// Create a plain file which we will attempt to use as a directory
// (%t/not-a-dir)
File notAdir = new File(tmpOrHomeDir, NOT_A_DIR);
if (!createFile(notAdir, false)) {
throw new RuntimeException("Test setup failed: unable to a plain"
+ " working file " + notAdir.getAbsolutePath() );
}
notAdir.deleteOnExit();
// Create a non-writable directory (%t/non-writable-dir)
File nonWritableDir = new File(tmpOrHomeDir, NON_WRITABLE_DIR);
if (!createFile(nonWritableDir, true)) {
throw new RuntimeException("Test setup failed: unable to create"
+ " a non-"
+ "writable working directory "
+ nonWritableDir.getAbsolutePath() );
}
nonWritableDir.deleteOnExit();
// make it non-writable
Path path = nonWritableDir.toPath();
final boolean nonWritable = nonWritableDir.setWritable(false);
final boolean isWritable = Files.isWritable(path);
if (nonWritable && !isWritable) {
runNonWritableDirTest = true;
System.out.println("Created non writable dir for "
+ getOwner(path) + " at: " + path.toString());
} else {
runNonWritableDirTest = false;
System.out.println( "Test Setup WARNING: unable to make"
+ " working directory " + nonWritableDir.getAbsolutePath()
+ "\n\t non-writable for " + getOwner(path)
+ " on platform " + System.getProperty("os.name"));
}
// make sure non-existent directory really doesn't exist
File nonExistentDir = new File(tmpOrHomeDir, NON_EXISTENT_DIR);
if (nonExistentDir.exists()) {
nonExistentDir.delete();
}
System.out.println("Setup completed - writableDir is: " + writableDir.getPath());
return writableDir;
}
private static String getOwner(Path path) {
UserPrincipal user = null;
try {
user = Files.getOwner(path);
} catch (Exception x) {
System.err.println("Failed to get owner of: " + path);
System.err.println("\terror is: " + x);
}
return user == null ? "???" : user.getName();
}
/**
* @param newFile
* @return true if file already exists or creation succeeded
*/
private static boolean createFile(File newFile, boolean makeDirectory) {
if (newFile.exists()) {
return true;
}
if (makeDirectory) {
return newFile.mkdir();
} else {
try {
return newFile.createNewFile();
} catch (IOException ioex) {
ioex.printStackTrace();
return false;
}
}
}
/*
* Recursively delete all files starting at specified file
*/
private static void delete(File f) {
if (f != null && f.isDirectory()) {
for (File c : f.listFiles())
delete(c);
}
if (!f.delete())
System.err.println(
"WARNING: unable to delete/cleanup writable test directory: "
+ f );
}
}

View file

@ -0,0 +1,374 @@
/*
* Copyright (c) 2014, 2022, 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 8048020
* @author Daniel Fuchs
* @summary Regression on java.util.logging.FileHandler.
* The fix is to avoid filling up the file system with zombie lock files.
*
* @run main/othervm CheckZombieLockTest WRITABLE CLOSE CLEANUP
* @run main/othervm CheckZombieLockTest CLEANUP
* @run main/othervm CheckZombieLockTest WRITABLE
* @run main/othervm CheckZombieLockTest CREATE_FIRST
* @run main/othervm CheckZombieLockTest CREATE_NEXT
* @run main/othervm CheckZombieLockTest CREATE_NEXT
* @run main/othervm CheckZombieLockTest CLEANUP
* @run main/othervm CheckZombieLockTest REUSE
* @run main/othervm CheckZombieLockTest CLEANUP
* @key randomness
*/
import java.io.File;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import static java.nio.file.StandardOpenOption.*;
public class CheckZombieLockTest {
private static final String WRITABLE_DIR = "writable-lockfile-dir";
private static volatile boolean supportsLocking = true;
static enum TestCase {
WRITABLE, // just verifies that we can create a file in our 'writable-lockfile-dir'
CLOSE, // checks that closing a FileHandler removes its lock file
CREATE_FIRST, // verifies that 'writable-lockfile-dir' contains no lock, then creates a first FileHandler.
CREATE_NEXT, // verifies that 'writable-lockfile-dir' contains a single lock, then creates the next FileHandler
REUSE, // verifies that zombie lock files can be reused
CLEANUP // removes "writable-lockfile-dir"
};
public static void main(String... args) throws IOException {
// we'll base all file creation attempts on the system temp directory,
// %t
File writableDir = setup();
System.out.println("Writable dir is: " + writableDir.getAbsolutePath());
// we now have one writable directory to work with:
// writableDir
if (args == null || args.length == 0) {
args = new String[] { "WRITABLE", "CLOSE", "CLEANUP" };
}
try {
runTests(writableDir, args);
} catch (RuntimeException | IOException | Error x) {
// some error occured: cleanup
delete(writableDir);
throw x;
}
}
/**
* @param writableDir in which log and lock file are created
* @throws SecurityException
* @throws RuntimeException
* @throws IOException
*/
private static void runTests(File writableDir, String... args) throws SecurityException,
RuntimeException, IOException {
for (String arg : args) {
switch(TestCase.valueOf(arg)) {
// Test 1: makes sure we can create FileHandler in writable directory
case WRITABLE: checkWritable(writableDir); break;
// Test 2: verifies that FileHandler.close() cleans up its lock file
case CLOSE: testFileHandlerClose(writableDir); break;
// Test 3: creates the first file handler
case CREATE_FIRST: testFileHandlerCreate(writableDir, true); break;
// Test 4, 5, ... creates the next file handler
case CREATE_NEXT: testFileHandlerCreate(writableDir, false); break;
// Checks that zombie lock files are reused appropriatly
case REUSE: testFileHandlerReuse(writableDir); break;
// Removes the writableDir
case CLEANUP: delete(writableDir); break;
default: throw new RuntimeException("No such test case: " + arg);
}
}
}
/**
* @param writableDir in which log and lock file are created
* @throws SecurityException
* @throws RuntimeException
* @throws IOException
*/
private static void checkWritable(File writableDir) throws SecurityException,
RuntimeException, IOException {
// Test 1: make sure we can create/delete files in the writable dir.
final File file = new File(writableDir, "test.txt");
if (!createFile(file, false)) {
throw new IOException("Can't create " + file + "\n\tUnable to run test");
} else {
delete(file);
}
}
private static FileHandler createFileHandler(File writableDir) throws SecurityException,
RuntimeException, IOException {
// Test 1: make sure we can create FileHandler in writable directory
try {
FileHandler handler = new FileHandler("%t/" + WRITABLE_DIR + "/log.log");
handler.publish(new LogRecord(Level.INFO, handler.toString()));
handler.flush();
return handler;
} catch (IOException ex) {
throw new RuntimeException("Test failed: should have been able"
+ " to create FileHandler for " + "%t/" + WRITABLE_DIR
+ "/log.log in writable directory.", ex);
}
}
private static List<File> listLocks(File writableDir, boolean print)
throws IOException {
List<File> locks = new ArrayList<>();
for (File f : writableDir.listFiles()) {
if (print) {
System.out.println("Found file: " + f.getName());
}
if (f.getName().endsWith(".lck")) {
locks.add(f);
}
}
return locks;
}
private static void testFileHandlerClose(File writableDir) throws IOException {
File fakeLock = new File(writableDir, "log.log.lck");
if (!createFile(fakeLock, false)) {
throw new IOException("Can't create fake lock file: " + fakeLock);
}
try {
List<File> before = listLocks(writableDir, true);
System.out.println("before: " + before.size() + " locks found");
FileHandler handler = createFileHandler(writableDir);
System.out.println("handler created: " + handler);
List<File> after = listLocks(writableDir, true);
System.out.println("after creating handler: " + after.size() + " locks found");
handler.close();
System.out.println("handler closed: " + handler);
List<File> afterClose = listLocks(writableDir, true);
System.out.println("after closing handler: " + afterClose.size() + " locks found");
afterClose.removeAll(before);
if (!afterClose.isEmpty()) {
throw new RuntimeException("Zombie lock file detected: " + afterClose);
}
} finally {
if (fakeLock.canRead()) delete(fakeLock);
}
List<File> finalLocks = listLocks(writableDir, false);
System.out.println("After cleanup: " + finalLocks.size() + " locks found");
}
private static void testFileHandlerReuse(File writableDir) throws IOException {
List<File> before = listLocks(writableDir, true);
System.out.println("before: " + before.size() + " locks found");
try {
if (!before.isEmpty()) {
throw new RuntimeException("Expected no lock file! Found: " + before);
}
} finally {
before.stream().forEach(CheckZombieLockTest::delete);
}
FileHandler handler1 = createFileHandler(writableDir);
System.out.println("handler created: " + handler1);
List<File> after = listLocks(writableDir, true);
System.out.println("after creating handler: " + after.size() + " locks found");
if (after.size() != 1) {
throw new RuntimeException("Unexpected number of lock files found for "
+ handler1 + ": " + after);
}
final File lock = after.get(0);
after.clear();
handler1.close();
after = listLocks(writableDir, true);
System.out.println("after closing handler: " + after.size() + " locks found");
if (!after.isEmpty()) {
throw new RuntimeException("Unexpected number of lock files found for "
+ handler1 + ": " + after);
}
if (!createFile(lock, false)) {
throw new IOException("Can't create fake lock file: " + lock);
}
try {
before = listLocks(writableDir, true);
System.out.println("before: " + before.size() + " locks found");
if (before.size() != 1) {
throw new RuntimeException("Unexpected number of lock files found: "
+ before + " expected [" + lock + "].");
}
FileHandler handler2 = createFileHandler(writableDir);
System.out.println("handler created: " + handler2);
after = listLocks(writableDir, true);
System.out.println("after creating handler: " + after.size() + " locks found");
after.removeAll(before);
if (!after.isEmpty()) {
throw new RuntimeException("Unexpected lock file found: " + after
+ "\n\t" + lock + " should have been reused");
}
handler2.close();
System.out.println("handler closed: " + handler2);
List<File> afterClose = listLocks(writableDir, true);
System.out.println("after closing handler: " + afterClose.size() + " locks found");
if (!afterClose.isEmpty()) {
throw new RuntimeException("Zombie lock file detected: " + afterClose);
}
if (supportsLocking) {
handler2 = null;
try (FileChannel fc = FileChannel.open(lock.toPath(), CREATE_NEW, APPEND, WRITE)) {
if (fc.tryLock() != null) {
System.out.println("locked: " + lock);
handler2 = createFileHandler(writableDir);
System.out.println("handler created: " + handler2);
after = listLocks(writableDir, true);
System.out.println("after creating handler: " + after.size()
+ " locks found");
after.removeAll(before);
if (after.size() != 1) {
throw new RuntimeException("Unexpected lock files found: " + after
+ "\n\t" + lock + " should not have been reused");
}
} else {
throw new RuntimeException("Failed to lock: " + lock);
}
} finally {
if (handler2 != null) handler2.close();
delete(lock);
}
}
} finally {
List<File> finalLocks = listLocks(writableDir, false);
System.out.println("end: " + finalLocks.size() + " locks found");
delete(writableDir);
}
}
private static void testFileHandlerCreate(File writableDir, boolean first)
throws IOException {
List<File> before = listLocks(writableDir, true);
System.out.println("before: " + before.size() + " locks found");
try {
if (first && !before.isEmpty()) {
throw new RuntimeException("Expected no lock file! Found: " + before);
} else if (!first && before.size() != 1) {
throw new RuntimeException("Expected a single lock file! Found: " + before);
}
} finally {
before.stream().forEach(CheckZombieLockTest::delete);
}
FileHandler handler = createFileHandler(writableDir);
System.out.println("handler created: " + handler);
List<File> after = listLocks(writableDir, true);
System.out.println("after creating handler: " + after.size() + " locks found");
if (after.size() != 1) {
throw new RuntimeException("Unexpected number of lock files found for "
+ handler + ": " + after);
}
}
/**
* Setup all the files and directories needed for the tests
*
* @return writable directory created that needs to be deleted when done
* @throws RuntimeException
*/
private static File setup() throws RuntimeException {
// First do some setup in the temporary directory (using same logic as
// FileHandler for %t pattern)
String tmpDir = System.getProperty("java.io.tmpdir"); // i.e. %t
if (tmpDir == null) {
tmpDir = System.getProperty("user.home");
}
File tmpOrHomeDir = new File(tmpDir);
// Create a writable directory here (%t/writable-lockfile-dir)
File writableDir = new File(tmpOrHomeDir, WRITABLE_DIR);
if (!createFile(writableDir, true)) {
throw new RuntimeException("Test setup failed: unable to create"
+ " writable working directory "
+ writableDir.getAbsolutePath() );
}
// try to determine whether file locking is supported
final String uniqueFileName = UUID.randomUUID().toString()+".lck";
try (FileChannel fc = FileChannel.open(Paths.get(writableDir.getAbsolutePath(),
uniqueFileName), CREATE_NEW, APPEND, DELETE_ON_CLOSE)) {
try {
fc.tryLock();
} catch (IOException x) {
supportsLocking = false;
}
} catch (IOException t) {
// should not happen
System.err.println("Failed to create new file " + uniqueFileName +
" in " + writableDir.getAbsolutePath());
throw new RuntimeException("Test setup failed: unable to run test", t);
}
return writableDir;
}
/**
* @param newFile
* @return true if file already exists or creation succeeded
*/
private static boolean createFile(File newFile, boolean makeDirectory) {
if (newFile.exists()) {
return true;
}
if (makeDirectory) {
return newFile.mkdir();
} else {
try {
return newFile.createNewFile();
} catch (IOException ioex) {
ioex.printStackTrace();
return false;
}
}
}
/*
* Recursively delete all files starting at specified file
*/
private static void delete(File f) {
if (f != null && f.isDirectory()) {
for (File c : f.listFiles())
delete(c);
}
if (!f.delete())
System.err.println(
"WARNING: unable to delete/cleanup writable test directory: "
+ f );
}
}

View file

@ -0,0 +1,188 @@
/*
* Copyright (c) 2009, 2012, 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 6799583
*
* @summary Test verifes that LogManager shutdown hook does not cause
* an application classloader leaks.
*
* @run main/othervm ClassLoaderLeakTest
*/
import java.io.File;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.concurrent.CountDownLatch;
import java.util.logging.Logger;
public class ClassLoaderLeakTest {
private static CountDownLatch doneSignal;
private static CountDownLatch launchSignal;
private static ThreadGroup appsThreadGroup;
private static Throwable launchFailure = null;
public static void main(String[] args) {
appsThreadGroup = new ThreadGroup("MyAppsThreadGroup");
doneSignal = new CountDownLatch(1);
launchSignal = new CountDownLatch(1);
Runnable launcher = new Runnable() {
public void run() {
try {
ClassLoader cl =
Thread.currentThread().getContextClassLoader();
Class<?> appMain = cl.loadClass("AppTest");
Method launch =
appMain.getDeclaredMethod("launch", doneSignal.getClass());
Constructor<?> c = appMain.getConstructor();
Object o = c.newInstance();
launch.invoke(o, doneSignal);
} catch (Throwable e) {
launchFailure = e;
} finally {
launchSignal.countDown();
}
}
};
/* prepare test class loader */
URL pwd = null;
try {
pwd = new File(System.getProperty("test.classes",".")).toURI().toURL();
} catch (MalformedURLException e) {
throw new RuntimeException("Test failed.", e);
}
URL[] urls = new URL[] { pwd };
MyClassLoader appClassLoader = new MyClassLoader(urls, "test0");
WeakReference<MyClassLoader> ref =
new WeakReference<>(appClassLoader);
Thread appThread = new Thread(appsThreadGroup, launcher, "AppThread-0");
appThread.setContextClassLoader(appClassLoader);
appThread.start();
appClassLoader = null;
launcher = null;
appThread = null;
/* wait for laucnh completion */
try {
launchSignal.await();
} catch (InterruptedException e) {
}
/* check if launch failed */
if (launchFailure != null) {
throw new RuntimeException("Test failed.", launchFailure);
}
/* wait for test app excution completion */
try {
doneSignal.await();
} catch (InterruptedException e) {
}
/* give a chence to GC */
waitAndGC(5);
if (ref.get() != null) {
throw new RuntimeException("Test failed: classloader is still alive");
}
System.out.println("Test passed.");
}
private static class MyClassLoader extends URLClassLoader {
private static boolean verbose =
Boolean.getBoolean("verboseClassLoading");
private String uniqClassName;
public MyClassLoader(URL[] urls, String uniq) {
super(urls);
uniqClassName = uniq;
}
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (verbose) {
System.out.printf("%s: load class %s\n", uniqClassName, name);
}
if (uniqClassName.equals(name)) {
return Object.class;
}
return super.loadClass(name);
}
public String toString() {
return "MyClassLoader(" + uniqClassName + ")";
}
}
private static void waitAndGC(int sec) {
int cnt = sec;
System.out.print("Wait ");
while (cnt-- > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
// do GC every 3 seconds
if (cnt % 3 == 2) {
System.gc();
System.out.print("+");
} else {
System.out.print(".");
}
//checkErrors();
}
System.out.println("");
}
}
class AppTest {
public AppTest() {
}
public void launch(CountDownLatch done) {
Logger log = Logger.getLogger("app_test_logger");
log.fine("Test app is launched");
done.countDown();
}
}

View file

@ -0,0 +1,207 @@
/*
* Copyright (c) 2013, 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.*;
import java.util.*;
import java.util.logging.*;
/*
* Custom LogManager implementation to verify that the implementation delegates
* to the LogManager subclass to register both system logger and user logger.
*
* The LogManager implementation is the one configuring the logger's property
* such as level, handler, etc.
*/
public class CustomLogManager extends LogManager {
static LogManager INSTANCE;
Map<String,Logger> namedLoggers = new HashMap<>();
Properties props = initConfig();
public CustomLogManager() {
if (INSTANCE != null) {
throw new RuntimeException("CustomLogManager already created");
}
INSTANCE = this;
}
private boolean useParentHandlers(String loggerName) {
String s = props.getProperty(loggerName + ".useParentHandlers");
if (s == null)
return true; // default is true
s = s.toLowerCase();
if (s.equals("true") || s.equals("1")) {
return true;
} else if (s.equals("false") || s.equals("0")) {
return false;
}
return true;
}
public synchronized boolean addLogger(Logger logger) {
String name = logger.getName();
if (namedLoggers.containsKey(name)) {
return false;
}
namedLoggers.put(name, logger);
// set level
if (props.get(name + ".level") != null) {
logger.setLevel(Level.parse(props.getProperty(name + ".level")));
}
// add handlers
if (props.get(name + ".handlers") != null && logger.getHandlers().length == 0) {
logger.addHandler(new CustomHandler());
}
if (!useParentHandlers(name)) {
logger.setUseParentHandlers(false);
}
// add parent loggers
int ix = 1;
for (;;) {
int ix2 = name.indexOf(".", ix);
if (ix2 < 0) {
break;
}
String pname = name.substring(0, ix2);
if (props.get(pname + ".level") != null ||
props.get(pname + ".handlers") != null) {
// This pname has a level/handlers definition.
// Make sure it exists.
//
// The test doesn't set the parent for simplicity.
if (!namedLoggers.containsKey(pname)) {
Logger parent = Logger.getLogger(pname);
if (!useParentHandlers(pname)) {
parent.setUseParentHandlers(false);
}
}
}
ix = ix2 + 1;
}
return true;
}
public synchronized Logger getLogger(String name) {
return namedLoggers.get(name);
}
public synchronized Enumeration<String> getLoggerNames() {
return Collections.enumeration(namedLoggers.keySet());
}
public String getProperty(String name) {
return props.getProperty(name);
}
public void readConfiguration() {
// do nothing
}
public void readConfiguration(InputStream ins) {
// do nothing
}
private Properties initConfig() {
Properties props = new Properties();
props.put(".level", "CONFIG");
props.put("CustomLogManagerTest.level", "WARNING");
props.put("CustomLogManagerTest.handlers", "CustomLogManager$CustomHandler");
props.put("SimpleLogManager.level", "INFO");
props.put("SimpleLogManager.handlers", "CustomLogManager$CustomHandler");
props.put("CustomLogManager$CustomHandler.level", "WARNING");
props.put(".handlers", "CustomLogManager$CustomHandler");
props.put("org.foo.bar.level", "SEVERE");
props.put("org.foo.bar.useParentHandlers", "true");
props.put("org.foo.handlers", "CustomLogManager$CustomHandler");
props.put("org.foo.useParentHandlers", "false");
props.put("org.openjdk.level", "SEVERE");
props.put("org.openjdk.handlers", "CustomLogManager$CustomHandler");
props.put("org.openjdk.core.level", "INFO");
props.put("org.openjdk.core.useParentHandlers", "false");
return props;
}
public static void checkLogger(String name) {
checkLogger(name, null);
}
public static void checkLogger(String name, String resourceBundleName) {
Logger logger = INSTANCE.getLogger(name);
if (logger == null) {
throw new RuntimeException("Logger \"" + name + "\" not exist");
}
System.out.format("Logger \"%s\" level=%s handlers=%s resourcebundle=%s useParentHandlers=%s%n",
name, logger.getLevel(),
Arrays.toString(logger.getHandlers()),
logger.getResourceBundleName(),
logger.getUseParentHandlers());
String rb = logger.getResourceBundleName();
if (rb != resourceBundleName && (rb == null || rb.equals(resourceBundleName))) {
throw new RuntimeException("Logger \"" + name +
"\" unexpected resource bundle: " + rb);
}
String value = INSTANCE.getProperty(name + ".level");
String level = logger.getLevel() != null ? logger.getLevel().getName() : null;
if (level != value && (level == null || level.equals(value))) {
throw new RuntimeException("Logger \"" + name + "\" unexpected level: " + level);
}
Handler[] handlers = logger.getHandlers();
String hdl = INSTANCE.getProperty(name + ".handlers");
if ((hdl == null && handlers.length != 0) ||
(hdl != null && handlers.length != 1)) {
throw new RuntimeException("Logger \"" + name + "\" unexpected handler: " +
Arrays.toString(handlers));
}
String s = INSTANCE.getProperty(name + ".useParentHandlers");
boolean uph = (s != null && s.equals("false")) ? false : true;
if (logger.getUseParentHandlers() != uph) {
throw new RuntimeException("Logger \"" + name + "\" unexpected useParentHandlers: " +
logger.getUseParentHandlers());
}
checkParents(name);
}
private static void checkParents(String name) {
int ix = 1;
for (;;) {
int ix2 = name.indexOf(".", ix);
if (ix2 < 0) {
break;
}
String pname = name.substring(0, ix2);
if (INSTANCE.getProperty(pname + ".level") != null ||
INSTANCE.getProperty(pname + ".handlers") != null) {
// This pname has a level/handlers definition.
// Make sure it exists.
checkLogger(pname);
}
ix = ix2 + 1;
}
}
// only CustomLogManager can create an instance of CustomHandler
private class CustomHandler extends StreamHandler {
}
}

View file

@ -0,0 +1,65 @@
/*
* Copyright (c) 2013, 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.*;
import java.util.*;
import java.util.logging.*;
import sun.util.logging.PlatformLogger;
/*
* @test
* @bug 8005615 8006104
* @summary Add loggers to custom log manager
*
* @modules java.base/sun.util.logging
* java.logging
* @compile -XDignore.symbol.file CustomLogManagerTest.java CustomLogManager.java
* @run main/othervm -Djava.util.logging.manager=CustomLogManager CustomLogManagerTest
*/
public class CustomLogManagerTest {
private static final String RESOURCE_BUNDLE = "sun.util.logging.resources.logging";
public static void main(String[] args) {
String mgr = System.getProperty("java.util.logging.manager");
if (!mgr.equals("CustomLogManager")) {
throw new RuntimeException("java.util.logging.manager not set");
}
Logger.getLogger(CustomLogManagerTest.class.getName());
Logger.getLogger("org.foo.Foo");
Logger.getLogger("org.foo.bar.Foo", RESOURCE_BUNDLE);
// platform logger will be set with the default system resource bundle
PlatformLogger.getLogger("org.openjdk.core.logger");
if (LogManager.getLogManager() != CustomLogManager.INSTANCE) {
throw new RuntimeException(LogManager.getLogManager() + " not CustomLogManager");
}
CustomLogManager.checkLogger(CustomLogManagerTest.class.getName());
CustomLogManager.checkLogger("org.foo.Foo");
CustomLogManager.checkLogger("org.foo.bar.Foo", RESOURCE_BUNDLE);
CustomLogManager.checkLogger(Logger.GLOBAL_LOGGER_NAME);
CustomLogManager.checkLogger("");
CustomLogManager.checkLogger("org.openjdk.core.logger", RESOURCE_BUNDLE);
}
}

View file

@ -0,0 +1,107 @@
/*
* 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.
*/
/*
* @test
* @bug 8211093
* @summary Check default configuration
* @build DefaultConfigTest
* @run main/othervm DefaultConfigTest
*/
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import java.util.stream.Collectors;
public class DefaultConfigTest {
static final java.io.PrintStream out = System.out;
public static void main(String arg[]) {
LogManager manager = LogManager.getLogManager();
if (!Set.of(Collections.list(manager.getLoggerNames()).toArray())
.equals(Set.of("", "global"))) {
throw new RuntimeException("Unexpected logger name in: "
+ Collections.list(manager.getLoggerNames()));
}
Logger global = Logger.getGlobal();
Logger root = Logger.getLogger("");
Logger foo = Logger.getLogger("com.xyz.foo");
if (!Set.of(Collections.list(manager.getLoggerNames()).toArray())
.equals(Set.of("", "global", "com.xyz.foo"))) {
throw new RuntimeException("Unexpected logger name in: "
+ Collections.list(manager.getLoggerNames()));
}
for (Logger l : List.of(global, foo)) {
if (l.getLevel() != null) {
throw new RuntimeException("Unexpected level "
+ l.getLevel() + " for " + l.getName());
}
if (l.getHandlers().length != 0) {
throw new RuntimeException("Unexpected handlers "
+ List.of(l.getHandlers()) + " for " + l.getName());
}
for (Level level : List.of(
Level.ALL, Level.FINEST, Level.FINER, Level.FINE, Level.CONFIG)) {
if (l.isLoggable(level)) {
throw new RuntimeException("Level "
+ level + " should not be loggable for " + l.getName());
}
}
for (Level level : List.of(
Level.INFO, Level.WARNING, Level.SEVERE)) {
if (!l.isLoggable(level)) {
throw new RuntimeException("Level "
+ level + " should be loggable for " + l.getName());
}
}
}
if (root.getLevel() != Level.INFO) {
throw new RuntimeException("Unexpected level "
+ root.getLevel() + " for root logger");
}
if (root.getHandlers().length != 1) {
throw new RuntimeException("Unexpected handlers "
+ List.of(root.getHandlers()) + " for root logger");
}
if (root.getHandlers()[0].getClass() != ConsoleHandler.class) {
throw new RuntimeException("Unexpected handlers "
+ List.of(root.getHandlers()) + " for root logger");
}
if (root.getHandlers()[0].getLevel() != Level.INFO) {
throw new RuntimeException("Unexpected level "
+ root.getHandlers()[0].getLevel() + " for handler "
+ root.getHandlers()[0]);
}
out.println(List.of(root, global, foo).stream()
.map(Logger::getName)
.map(s -> String.format("\"%s\"", s))
.collect(Collectors.joining(", ",
"Default configuration checked for ",
".")));
}
}

View file

@ -0,0 +1,217 @@
/*
* Copyright (c) 2013, 2020, 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.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.lang.Thread.State;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.util.Random;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import java.util.Map;
import jdk.test.lib.RandomFactory;
/**
* @test
* @bug 8010939
* @summary check for deadlock between findLogger() and drainLoggerRefQueueBounded()
* @author jim.gish@oracle.com
* @modules java.logging
* java.management
* @library /test/lib
* @build DrainFindDeadlockTest
* @run main/othervm DrainFindDeadlockTest
* @key randomness
*/
/**
* This test is checking for a deadlock between
* LogManager$LoggerContext.findLogger() and
* LogManager.drainLoggerRefQueueBounded() (which could happen by calling
* Logger.getLogger() and LogManager.readConfiguration() in different threads)
*/
public class DrainFindDeadlockTest {
private LogManager mgr = LogManager.getLogManager();
private static final int MAX_ITERATIONS = 100;
private static final Random random = RandomFactory.getRandom();
private static int preventLoopElision;
// Get a ThreadMXBean so we can check for deadlock. N.B. this may
// not be supported on all platforms, which means we will have to
// resort to the traditional test timeout method. However, if
// we have the support we'll get the deadlock details if one
// is detected.
private static final ThreadMXBean threadMXBean =
ManagementFactory.getThreadMXBean();
private final boolean threadMXBeanDeadlockSupported =
threadMXBean.isSynchronizerUsageSupported();
public static void main(String... args) throws IOException, Exception {
new DrainFindDeadlockTest().testForDeadlock();
}
public static void randomDelay() {
int runs = random.nextInt(1000000);
int c = 0;
for (int i=0; i<runs; ++i) {
c=c+i;
}
preventLoopElision = c;
}
public void testForDeadlock() throws IOException, Exception {
System.out.println("Deadlock detection "
+ (threadMXBeanDeadlockSupported ? "is" : "is not") +
" available.");
Thread setup = new Thread(new SetupLogger(), "SetupLogger");
Thread readConfig = new Thread(new ReadConfig(), "ReadConfig");
Thread check = new Thread(new DeadlockChecker(setup, readConfig),
"DeadlockChecker");
// make the threads daemon threads so they will go away when the
// test exits
setup.setDaemon(true);
readConfig.setDaemon(true);
check.setDaemon(true);
check.start(); setup.start(); readConfig.start();
try {
check.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
try {
readConfig.join();
setup.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("Test passed");
}
class SetupLogger implements Runnable {
Logger logger = null;
@Override
public void run() {
System.out.println("Running " + Thread.currentThread().getName());
try {
for (int i=0; i < MAX_ITERATIONS; i++) {
logger = Logger.getLogger("DrainFindDeadlockTest"+i);
DrainFindDeadlockTest.randomDelay();
}
} finally {
System.out.println("Completed " + Thread.currentThread().getName());
}
}
}
class ReadConfig implements Runnable {
@Override
public void run() {
System.out.println("Running " + Thread.currentThread().getName());
try {
for (int i=0; i < MAX_ITERATIONS; i++) {
try {
mgr.readConfiguration();
} catch (IOException | SecurityException ex) {
throw new RuntimeException("FAILED: test setup problem", ex);
}
DrainFindDeadlockTest.randomDelay();
}
} finally {
System.out.println("Completed " + Thread.currentThread().getName());
}
}
}
class DeadlockChecker implements Runnable {
Thread t1, t2;
DeadlockChecker(Thread t1, Thread t2) {
this.t1 = t1;
this.t2 = t2;
}
void checkState(Thread x, Thread y) {
// System.out.println("checkstate");
boolean isXblocked = x.getState().equals(State.BLOCKED);
boolean isYblocked = y.getState().equals(State.BLOCKED);
long[] deadlockedThreads = null;
if (isXblocked && isYblocked) {
System.out.println("threads blocked");
// they are both blocked, but this doesn't necessarily mean
// they are deadlocked
if (threadMXBeanDeadlockSupported) {
System.out.println("checking for deadlock");
deadlockedThreads = threadMXBean.findDeadlockedThreads();
} else {
System.out.println("Can't check for deadlock");
}
if (deadlockedThreads != null) {
System.out.println("We detected a deadlock! ");
ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(
deadlockedThreads, true, true);
for (ThreadInfo threadInfo: threadInfos) {
System.out.println(threadInfo);
}
throw new RuntimeException("TEST FAILED: Deadlock detected");
}
System.out.println("We may have a deadlock");
Map<Thread, StackTraceElement[]> threadMap =
Thread.getAllStackTraces();
dumpStack(threadMap.get(x), x);
dumpStack(threadMap.get(y), y);
}
}
private void dumpStack(StackTraceElement[] aStackElt, Thread aThread) {
if (aStackElt != null) {
System.out.println("Thread:" + aThread.getName() + ": " +
aThread.getState());
for (StackTraceElement element: aStackElt) {
System.out.println(" " + element);
}
}
}
@Override
public void run() {
System.out.println("Running " + Thread.currentThread().getName());
try {
for (int i=0; i < MAX_ITERATIONS*2; i++) {
checkState(t1, t2);
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
}
}
} finally {
System.out.println("Completed " + Thread.currentThread().getName());
}
}
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 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.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
/**
* @test
* @bug 8252883
* @summary tests the handling of AccessDeniedException due to delay in Windows deletion.
* @modules java.logging/java.util.logging:open
* @run main/othervm FileHandlerAccessTest 20
* @author evwhelan
*/
public class FileHandlerAccessTest {
public static void main(String[] args) {
var count = Integer.parseInt(args[0]);
System.out.println("Testing with " + count + " threads");
for (var i = 0; i < count; i++) {
new Thread(FileHandlerAccessTest::access).start();
}
}
private static void access() {
try {
var handler = new FileHandler("sample%g.log", 1048576, 2, true);
handler.publish(new LogRecord(Level.SEVERE, "TEST"));
handler.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View file

@ -0,0 +1,351 @@
/*
* 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
/**
* @test
* @bug 8059767
* @summary tests that FileHandler can accept a long limit.
* @modules java.logging/java.util.logging:open
* @run main/othervm --enable-final-field-mutation=ALL-UNNAMED FileHandlerLongLimit
* @author danielfuchs
* @key randomness
*/
public class FileHandlerLongLimit {
// We will test handling of limit and overflow of MeteredStream.written
public static void run(Properties propertyFile) throws Exception {
setUp(propertyFile);
test(propertyFile.getProperty("test.name"), propertyFile,
Long.parseLong(propertyFile.getProperty(FileHandler.class.getName()+".limit")));
}
private static final String PREFIX =
"FileHandler-" + UUID.randomUUID() + ".log";
private static final String userDir = System.getProperty("user.dir", ".");
private static final boolean userDirWritable = Files.isWritable(Paths.get(userDir));
private static final Field limitField;
private static final Field meterField;
private static final Field writtenField;
private static final Field outField;
private static final List<Properties> properties;
static {
Properties props1 = new Properties();
Properties props2 = new Properties();
Properties props3 = new Properties();
props1.setProperty("test.name", "with limit=Integer.MAX_VALUE");
props1.setProperty(FileHandler.class.getName() + ".pattern", PREFIX);
props1.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE));
props2.setProperty("test.name", "with limit=Integer.MAX_VALUE*4");
props2.setProperty(FileHandler.class.getName() + ".pattern", PREFIX);
props2.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(((long)Integer.MAX_VALUE)*4));
props3.setProperty("test.name", "with limit=Long.MAX_VALUE - 1024");
props3.setProperty(FileHandler.class.getName() + ".pattern", PREFIX);
props3.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Long.MAX_VALUE - 1024));
properties = Collections.unmodifiableList(Arrays.asList(
props1,
props2,
props3));
try {
Class<?> metteredStreamClass = Class.forName(FileHandler.class.getName()+"$MeteredStream");
limitField = FileHandler.class.getDeclaredField("limit");
limitField.setAccessible(true);
meterField = FileHandler.class.getDeclaredField("meter");
meterField.setAccessible(true);
writtenField = metteredStreamClass.getDeclaredField("written");
writtenField.setAccessible(true);
outField = metteredStreamClass.getDeclaredField("out");
outField.setAccessible(true);
} catch (NoSuchFieldException | ClassNotFoundException x) {
throw new ExceptionInInitializerError(x);
}
}
private static class TestOutputStream extends OutputStream {
final OutputStream delegate;
TestOutputStream(OutputStream delegate) {
this.delegate = delegate;
}
@Override
public void write(int b) throws IOException {
// do nothing - we only pretend to write something...
}
@Override
public void close() throws IOException {
delegate.close();
}
@Override
public void flush() throws IOException {
delegate.flush();
}
}
public static void main(String... args) throws Exception {
try {
for (Properties propertyFile : properties) {
run(propertyFile);
}
} finally {
if (userDirWritable) {
// cleanup - delete files that have been created
try {
Files.list(Paths.get(userDir))
.filter((f) -> f.toString().contains(PREFIX))
.forEach((f) -> {
try {
System.out.println("deleting " + f);
Files.delete(f);
} catch(Throwable t) {
System.err.println("Failed to delete " + f + ": " + t);
}
});
} catch(Throwable t) {
System.err.println("Cleanup failed to list files: " + t);
t.printStackTrace();
}
}
}
}
static void setUp(Properties propertyFile) {
try {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
propertyFile.store(bytes, propertyFile.getProperty("test.name"));
ByteArrayInputStream bais = new ByteArrayInputStream(bytes.toByteArray());
LogManager.getLogManager().readConfiguration(bais);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
@FunctionalInterface
public static interface FileHandlerSupplier {
public FileHandler test() throws Exception;
}
private static void checkException(Class<? extends Exception> type, FileHandlerSupplier test) {
Throwable t = null;
FileHandler f = null;
try {
f = test.test();
} catch (Throwable x) {
t = x;
}
try {
if (type != null && t == null) {
throw new RuntimeException("Expected " + type.getName() + " not thrown");
} else if (type != null && t != null) {
if (type.isInstance(t)) {
System.out.println("Recieved expected exception: " + t);
} else {
throw new RuntimeException("Exception type mismatch: "
+ type.getName() + " expected, "
+ t.getClass().getName() + " received.", t);
}
} else if (t != null) {
throw new RuntimeException("Unexpected exception received: " + t, t);
}
} finally {
if (f != null) {
// f should always be null when an exception is expected,
// but in case the test doesn't behave as expected we will
// want to close f.
try { f.close(); } catch (Throwable x) {};
}
}
}
static final class TestAssertException extends RuntimeException {
TestAssertException(String msg) {
super(msg);
}
}
private static void assertEquals(long expected, long received, String msg) {
if (expected != received) {
throw new TestAssertException("Unexpected result for " + msg
+ ".\n\texpected: " + expected
+ "\n\tactual: " + received);
} else {
System.out.println("Got expected " + msg + ": " + received);
}
}
private static long getLimit(FileHandler handler) throws Exception {
return limitField.getLong(handler);
}
private static OutputStream getMeteredOutput(FileHandler handler) throws Exception {
final OutputStream metered = OutputStream.class.cast(meterField.get(handler));
return metered;
}
private static TestOutputStream setTestOutputStream(OutputStream metered) throws Exception {
outField.set(metered, new TestOutputStream(OutputStream.class.cast(outField.get(metered))));
return TestOutputStream.class.cast(outField.get(metered));
}
private static long getWritten(OutputStream metered) throws Exception {
return writtenField.getLong(metered);
}
private static long setWritten(OutputStream metered, long newValue) throws Exception {
writtenField.setLong(metered, newValue);
return writtenField.getLong(metered);
}
public static FileHandler testFileHandlerLimit(FileHandlerSupplier supplier,
long limit) throws Exception {
try {
Files.deleteIfExists(Paths.get(PREFIX));
} catch (IOException x) {
throw new RuntimeException(x);
}
final FileHandler fh = supplier.test();
try {
// verify we have the expected limit
assertEquals(limit, getLimit(fh), "limit");
// get the metered output stream
OutputStream metered = getMeteredOutput(fh);
// we don't want to actually write to the file, so let's
// redirect the metered to our own TestOutputStream.
setTestOutputStream(metered);
// check that fh.meter.written is 0
assertEquals(0, getWritten(metered), "written");
// now we're going to publish a series of log records
// we're using the same log record over and over to make
// sure we get the same amount of bytes.
String msg = "this is at least 10 chars long";
LogRecord record = new LogRecord(Level.SEVERE, msg);
fh.publish(record);
fh.flush();
long w = getWritten(metered);
long offset = getWritten(metered);
System.out.println("first offset is: " + offset);
fh.publish(record);
fh.flush();
offset = getWritten(metered) - w;
w = getWritten(metered);
System.out.println("second offset is: " + offset);
fh.publish(record);
fh.flush();
offset = getWritten(metered) - w;
w = getWritten(metered);
System.out.println("third offset is: " + offset);
fh.publish(record);
fh.flush();
offset = getWritten(metered) - w;
System.out.println("fourth offset is: " + offset);
// Now set fh.meter.written to something close to the limit,
// so that we can trigger log file rotation.
assertEquals(limit-2*offset+10, setWritten(metered, limit-2*offset+10), "written");
w = getWritten(metered);
// publish one more log record. we should still be just beneath
// the limit
fh.publish(record);
fh.flush();
assertEquals(w+offset, getWritten(metered), "written");
// check that fh still has the same MeteredStream - indicating
// that the file hasn't rotated.
if (getMeteredOutput(fh) != metered) {
throw new RuntimeException("Log should not have rotated");
}
// Now publish two log record. The spec is a bit vague about when
// exactly the log will be rotated - it could happen just after
// writing the first log record or just before writing the next
// one. We publich two - so we're sure that the log must have
// rotated.
fh.publish(record);
fh.flush();
fh.publish(record);
fh.flush();
// Check that fh.meter is a different instance of MeteredStream.
if (getMeteredOutput(fh) == metered) {
throw new RuntimeException("Log should have rotated");
}
// success!
return fh;
} catch (Error | Exception x) {
// if we get an exception we need to close fh.
// if we don't get an exception, fh will be closed by the caller.
// (and that's why we dont use try-with-resources/finally here).
try { fh.close(); } catch(Throwable t) {t.printStackTrace();}
throw x;
}
}
public static void test(String name, Properties props, long limit) throws Exception {
System.out.println("Testing: " + name);
Class<? extends Exception> expectedException = null;
if (userDirWritable || expectedException != null) {
// These calls will create files in user.dir.
// The file name contain a random UUID (PREFIX) which identifies them
// and allow us to remove them cleanly at the end (see finally block
// in main()).
checkException(expectedException, () -> new FileHandler());
checkException(expectedException, () -> {
final FileHandler fh = new FileHandler();
assertEquals(limit, getLimit(fh), "limit");
return fh;
});
checkException(expectedException, () -> testFileHandlerLimit(
() -> new FileHandler(),
limit));
checkException(expectedException, () -> testFileHandlerLimit(
() -> new FileHandler(PREFIX, Long.MAX_VALUE, 1, true),
Long.MAX_VALUE));
}
System.out.println("Success for: " + name);
}
}

View file

@ -0,0 +1,115 @@
/*
* Copyright (c) 2016, 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.
*/
/*
* @test
* @bug 8153955
* @summary test the FileHandler's new property
* "java.util.logging.FileHandler.maxLocks" which will be present in
* "logging.properties" file with default value of 100. This property can be
* overriden by specifying this property in the custom config file.
* @library /test/lib
* @build jdk.test.lib.Platform
* jdk.test.lib.util.FileUtils
* @author rpatil
* @run main/othervm FileHandlerMaxLocksTest
*/
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.FileHandler;
import jdk.test.lib.util.FileUtils;
public class FileHandlerMaxLocksTest {
private static final String LOGGER_DIR = "logger-dir";
private static final String MAX_LOCK_PROPERTY = "java.util.logging.FileHandler.maxLocks = 200";
private static final String CONFIG_FILE_NAME = "logging.properties";
public static void main(String[] args) throws Exception {
File loggerDir = createLoggerDir();
String configFilePath = loggerDir.getPath() + File.separator + CONFIG_FILE_NAME;
File configFile = new File(configFilePath);
createFile(configFile, false);
System.setProperty("java.util.logging.config.file", configFilePath);
List<FileHandler> fileHandlers = new ArrayList<>();
try (FileWriter writer = new FileWriter(configFile)) {
writer.write(MAX_LOCK_PROPERTY);
writer.flush();
// 200 raises the default limit of 100, we try 102 times
for (int i = 0; i < 102; i++) {
fileHandlers.add(new FileHandler(loggerDir.getPath() + File.separator + "test_%u.log"));
}
} catch (IOException ie) {
throw new RuntimeException("Test Failed: " + ie.getMessage());
} finally {
for (FileHandler fh : fileHandlers) {
fh.close();
}
FileUtils.deleteFileTreeWithRetry(Paths.get(loggerDir.getPath()));
}
}
/**
* Create a writable directory in user directory for the test
*
* @return writable directory created that needs to be deleted when done
* @throws RuntimeException
*/
private static File createLoggerDir() throws RuntimeException {
String userDir = System.getProperty("user.dir", ".");
File loggerDir = new File(userDir, LOGGER_DIR);
if (!createFile(loggerDir, true)) {
throw new RuntimeException("Test failed: unable to create"
+ " writable working directory "
+ loggerDir.getAbsolutePath());
}
// System.out.println("Created Logger Directory: " + loggerDir.getPath());
return loggerDir;
}
/**
* @param newFile File to be created
* @param makeDirectory is File to be created is directory
* @return true if file already exists or creation succeeded
*/
private static boolean createFile(File newFile, boolean makeDirectory) {
if (newFile.exists()) {
return true;
}
if (makeDirectory) {
return newFile.mkdir();
} else {
try {
return newFile.createNewFile();
} catch (IOException ie) {
System.err.println("Not able to create file: " + newFile
+ ", IOException: " + ie.getMessage());
return false;
}
}
}
}

View file

@ -0,0 +1,168 @@
/*
* 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
import java.util.logging.FileHandler;
import java.util.logging.LogManager;
/**
* @test
* @bug 8059269
* @summary tests that using a simple (non composite) pattern does not lead
* to NPE when the lock file already exists.
* @run main/othervm/timeout=480 FileHandlerPath
* @author danielfuchs
* @key randomness
*/
public class FileHandlerPath {
// We will test the simple pattern
public static void run(Properties propertyFile) throws Exception {
setUp(propertyFile);
test(propertyFile.getProperty("test.name"), propertyFile);
}
// Use a random name provided by UUID to avoid collision with other tests
static final String logFile = FileHandlerPath.class.getSimpleName() + "_"
+ UUID.randomUUID().toString() + ".log";
static final String tmpLogFile;
static final String userDir = System.getProperty("user.dir");
static final String tmpDir = System.getProperty("java.io.tmpdir");
private static final List<Properties> properties;
static {
tmpLogFile = new File(tmpDir, logFile).toString();
Properties props1 = new Properties();
Properties props2 = new Properties();
props1.setProperty("test.name", "relative file");
props1.setProperty("test.file.name", logFile);
props1.setProperty(FileHandler.class.getName() + ".pattern", logFile);
props1.setProperty(FileHandler.class.getName() + ".count", "1");
props2.setProperty("test.name", "absoluste file");
props2.setProperty("test.file.name", tmpLogFile);
props2.setProperty(FileHandler.class.getName() + ".pattern", "%t/" + logFile);
props2.setProperty(FileHandler.class.getName() + ".count", "1");
properties = Collections.unmodifiableList(Arrays.asList(
props1,
props2));
}
public static void main(String... args) throws Exception {
// Sanity checks
if (!Files.isWritable(Paths.get(userDir))) {
throw new RuntimeException(userDir +
": user.dir is not writable - can't run test.");
}
if (!Files.isWritable(Paths.get(tmpDir))) {
throw new RuntimeException(tmpDir +
": java.io.tmpdir is not writable - can't run test.");
}
File[] files = {
new File(logFile),
new File(tmpLogFile),
new File(logFile+".1"),
new File(tmpLogFile+".1"),
new File(logFile+".lck"),
new File(tmpLogFile+".lck"),
new File(logFile+".1.lck"),
new File(tmpLogFile+".1.lck")
};
for (File log : files) {
if (log.exists()) {
throw new Exception(log +": file already exists - can't run test.");
}
}
// Now start the real test
try {
for (Properties propertyFile : properties) {
run(propertyFile);
}
} finally {
// Cleanup...
for(File log : files) {
try {
final boolean isLockFile = log.getName().endsWith(".lck");
// lock file should already be deleted, except if the
// test failed in exception.
// log file should all be present, except if the test
// failed in exception.
if (log.exists()) {
if (!isLockFile) {
System.out.println("deleting "+log.toString());
} else {
System.err.println("deleting lock file "+log.toString());
}
log.delete();
} else {
if (!isLockFile) {
System.err.println(log.toString() + ": not found.");
}
}
} catch (Throwable t) {
// should not happen
t.printStackTrace();
}
}
}
}
static void setUp(Properties propertyFile) {
try {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
propertyFile.store(bytes, propertyFile.getProperty("test.name"));
ByteArrayInputStream bais = new ByteArrayInputStream(bytes.toByteArray());
LogManager.getLogManager().readConfiguration(bais);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
public static void test(String name, Properties props) throws Exception {
System.out.println("Testing: " + name);
String file = props.getProperty("test.file.name");
// create the lock files first - in order to take the path that
// used to trigger the NPE
Files.createFile(Paths.get(file + ".lck"));
Files.createFile(Paths.get(file + ".1.lck"));
final FileHandler f1 = new FileHandler();
final FileHandler f2 = new FileHandler();
f1.close();
f2.close();
System.out.println("Success for: " + name);
}
}

View file

@ -0,0 +1,194 @@
/*
* Copyright (c) 2014, 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
import java.util.logging.FileHandler;
import java.util.logging.LogManager;
/**
* @test
* @bug 8025690
* @summary tests that an empty or null pattern always result in an exception.
* @run main/othervm FileHandlerPatternExceptions
* @author danielfuchs
* @key randomness
*/
public class FileHandlerPatternExceptions {
// We will test null/empty pattern
public static void run(Properties propertyFile) throws Exception {
setUp(propertyFile);
test(propertyFile.getProperty("test.name"));
}
private static final String PREFIX =
"FileHandler-" + UUID.randomUUID() + ".log";
private static final String userDir = System.getProperty("user.dir", ".");
private static final boolean userDirWritable = Files.isWritable(Paths.get(userDir));
private static final List<Properties> properties;
static {
Properties props1 = new Properties();
Properties props2 = new Properties();
props1.setProperty("test.name", "with count=1");
props1.setProperty(FileHandler.class.getName() + ".pattern", "");
props1.setProperty(FileHandler.class.getName() + ".count", "1");
props2.setProperty("test.name", "with count=2");
props2.setProperty(FileHandler.class.getName() + ".pattern", "");
props2.setProperty(FileHandler.class.getName() + ".count", "2");
properties = Collections.unmodifiableList(Arrays.asList(
props1,
props2));
}
public static void main(String... args) throws Exception {
try {
for (Properties propertyFile : properties) {
run(propertyFile);
}
} finally {
if (userDirWritable) {
// cleanup - delete files that have been created
try {
Files.list(Paths.get(userDir))
.filter((f) -> f.toString().contains(PREFIX))
.forEach((f) -> {
try {
System.out.println("deleting " + f);
Files.delete(f);
} catch(Throwable t) {
System.err.println("Failed to delete " + f + ": " + t);
}
});
} catch(Throwable t) {
System.err.println("Cleanup failed to list files: " + t);
t.printStackTrace();
}
}
}
}
static void setUp(Properties propertyFile) {
try {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
propertyFile.store(bytes, propertyFile.getProperty("test.name"));
ByteArrayInputStream bais = new ByteArrayInputStream(bytes.toByteArray());
LogManager.getLogManager().readConfiguration(bais);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
@FunctionalInterface
public static interface FileHandlerSupplier {
public FileHandler test() throws Exception;
}
private static void checkException(Class<? extends Exception> type, FileHandlerSupplier test) {
Throwable t = null;
FileHandler f = null;
try {
f = test.test();
} catch (Throwable x) {
t = x;
}
try {
if (type != null && t == null) {
throw new RuntimeException("Expected " + type.getName() + " not thrown");
} else if (type != null && t != null) {
if (type.isInstance(t)) {
System.out.println("Recieved expected exception: " + t);
} else {
throw new RuntimeException("Exception type mismatch: "
+ type.getName() + " expected, "
+ t.getClass().getName() + " received.", t);
}
} else if (t != null) {
throw new RuntimeException("Unexpected exception received: " + t, t);
}
} finally {
if (f != null) {
// f should always be null when an exception is expected,
// but in case the test doesn't behave as expected we will
// want to close f.
try { f.close(); } catch (Throwable x) {};
}
}
}
public static void test(String name) throws Exception {
System.out.println("Testing: " + name);
checkException(RuntimeException.class, () -> new FileHandler());
checkException(IllegalArgumentException.class, () -> new FileHandler(""));
checkException(NullPointerException.class, () -> new FileHandler(null));
checkException(IllegalArgumentException.class, () -> new FileHandler("", true));
checkException(IllegalArgumentException.class, () -> new FileHandler("", false));
checkException(NullPointerException.class, () -> new FileHandler(null, true));
checkException(NullPointerException.class, () -> new FileHandler(null, false));
checkException(IllegalArgumentException.class, () -> new FileHandler("", 1, 1));
checkException(IllegalArgumentException.class, () -> new FileHandler(PREFIX, 0, 0));
checkException(IllegalArgumentException.class, () -> new FileHandler(PREFIX, -1, 1));
checkException(IllegalArgumentException.class, () -> new FileHandler("", 0, 0));
checkException(IllegalArgumentException.class, () -> new FileHandler("", -1, 1));
checkException(IllegalArgumentException.class, () -> new FileHandler("", 1, 1, true));
checkException(IllegalArgumentException.class, () -> new FileHandler(PREFIX, 0, 0, true));
checkException(IllegalArgumentException.class, () -> new FileHandler(PREFIX, -1, 1, true));
checkException(IllegalArgumentException.class, () -> new FileHandler("", 0, 0, true));
checkException(IllegalArgumentException.class, () -> new FileHandler("", -1, 1, true));
checkException(IllegalArgumentException.class, () -> new FileHandler("", 1, 1, false));
checkException(IllegalArgumentException.class, () -> new FileHandler(PREFIX, 0, 0, false));
checkException(IllegalArgumentException.class, () -> new FileHandler(PREFIX, -1, 1, false));
checkException(IllegalArgumentException.class, () -> new FileHandler("", 0, 0, false));
checkException(IllegalArgumentException.class, () -> new FileHandler("", -1, 1, false));
if (userDirWritable) {
// These calls will create files in user.dir in the UNSECURE case.
// The file name contain a random UUID (PREFIX) which identifies them
// and allow us to remove them cleanly at the end (see finally block
// in main()).
checkException(null,
() -> new FileHandler(PREFIX, 0, 1, true));
checkException(null,
() -> new FileHandler(PREFIX, 1, 2, true));
checkException(null,
() -> new FileHandler(PREFIX, 0, 1, false));
checkException(null,
() -> new FileHandler(PREFIX, 1, 2, false));
}
System.out.println("Success for: " + name);
}
}

View file

@ -0,0 +1,232 @@
/*
* 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.FileHandler;
import java.util.logging.LogManager;
/**
* @test
* @bug 8189953
* @summary tests the pattern generation algorithm
* @modules java.logging/java.util.logging:open
* @run main/othervm FileHandlerPatternGeneration
* @author danielfuchs
*/
public class FileHandlerPatternGeneration {
/**
* An array of strings where the elements at even indices are the input
* to give to FileHandler::generate(pattern, count, generation, unique),
* and the elements at the next odd index are a partially computed expected
* output, where %t, %h, %u, %g and file separator still need to be replaced.
* The final expected output is obtained by passing the partially computed
* output to FileHandlerPatternGeneration::generateExpected
* <p>
* The test verifies that {@code
* FileHandler.generate(PATTERN[i], c, g, u).toString()
* }
* is equal to {@code
* FileHandlerPatternGeneration.generateExpected(PATTERN[i],
* PATTERN[i+1],
* c, g, u)
* }
*/
static final String[] PATTERNS = {
"C:/Workspace/hoge.log", "C:/Workspace/hoge.log",
"C:/Workspace%g/hoge.log", "C:/Workspace%g/hoge.log",
"C:/%uWorkspace/hoge.log", "C:/%uWorkspace/hoge.log",
"C:/%uWorkspace%g/hoge.log", "C:/%uWorkspace%g/hoge.log",
"C:/Workspace/%ghoge.log", "C:/Workspace/%ghoge.log",
"C:/Workspace/%ghoge%u.log", "C:/Workspace/%ghoge%u.log",
"C:/Workspace-%g/hoge.log", "C:/Workspace-%g/hoge.log",
"C:/Work%hspace/hoge.log", "%h/space/hoge.log",
"C:/Works%tpace%g/hoge.log", "%t/pace%g/hoge.log",
"C:/%uWork%hspace/hoge.log", "%h/space/hoge.log",
"C:/%uWorkspace%g/%thoge.log", "%t/hoge.log",
"C:/Workspace/%g%h%%hoge.log", "%h/%%hoge.log",
"C:/Work%h%%hspace/hoge.log", "%h/%%hspace/hoge.log",
"C:/Works%t%%hpace%g/hoge.log", "%t/%%hpace%g/hoge.log",
"C:/%uWork%h%%tspace/hoge.log", "%h/%%tspace/hoge.log",
"C:/%uWorkspace%g/%t%%hoge.log", "%t/%%hoge.log",
"C:/Workspace/%g%h%%hoge.log", "%h/%%hoge.log",
"ahaha", "ahaha",
"ahaha/ahabe", "ahaha/ahabe",
"../ahaha/ahabe", "../ahaha/ahabe",
"/x%ty/w/hoge.log", "%t/y/w/hoge.log",
"/x/%ty/w/hoge.log", "%t/y/w/hoge.log",
"/x%t/y/w/hoge.log", "%t/y/w/hoge.log",
"/x/%t/y/w/hoge.log", "%t/y/w/hoge.log",
"%ty/w/hoge.log", "%t/y/w/hoge.log",
"%t/y/w/hoge.log", "%t/y/w/hoge.log",
"/x%hy/w/hoge.log", "%h/y/w/hoge.log",
"/x/%hy/w/hoge.log", "%h/y/w/hoge.log",
"/x%h/y/w/hoge.log", "%h/y/w/hoge.log",
"/x/%h/y/w/hoge.log", "%h/y/w/hoge.log",
"%hy/w/hoge.log", "%h/y/w/hoge.log",
"%h/y/w/hoge.log", "%h/y/w/hoge.log",
"ahaha-%u-%g", "ahaha-%u-%g",
"ahaha-%g/ahabe-%u", "ahaha-%g/ahabe-%u",
"../ahaha-%u/ahabe", "../ahaha-%u/ahabe",
"/x%ty/w/hoge-%g.log", "%t/y/w/hoge-%g.log",
"/x/%ty/w/hoge-%u.log", "%t/y/w/hoge-%u.log",
"%u-%g/x%t/y/w/hoge.log", "%t/y/w/hoge.log",
"/x/%g%t%u/y/w/hoge.log", "%t/%u/y/w/hoge.log",
"%ty/w-%g/hoge.log", "%t/y/w-%g/hoge.log",
"%t/y/w-%u/hoge.log", "%t/y/w-%u/hoge.log",
"/x%hy/%u-%g-w/hoge.log", "%h/y/%u-%g-w/hoge.log",
"/x/%hy/w-%u-%g/hoge.log", "%h/y/w-%u-%g/hoge.log",
"/x%h/y/w/%u-%ghoge.log", "%h/y/w/%u-%ghoge.log",
"/x/%h/y/w/hoge-%u-%g.log", "%h/y/w/hoge-%u-%g.log",
"%hy/w/%u-%g-hoge.log", "%h/y/w/%u-%g-hoge.log",
"%h/y/w/hoge-%u-%g.log", "%h/y/w/hoge-%u-%g.log",
"/x/y/z/hoge-%u.log", "/x/y/z/hoge-%u.log",
};
// the (count, generation, unique) parameters to pass to
// FileHandler.generate(pattern, count, generation, unique)
static final int[][] GENERATIONS = {
{0, 0, 0},
{0, 1, 0},
{0, 1, 1},
{1, 1, 0},
{1, 1, 1},
{1, 1, 2},
{1, 2, 3},
{3, 4, 0},
{3, 4, 1},
{3, 4, 2},
{3, 0, 5},
{3, 1, 5},
{3, 2, 5},
};
static final Class<FileHandler> FILE_HANDLER_CLASS = FileHandler.class;
static final Method GENERATE;
static final String USER_HOME;
static final String TMP;
static {
Method generate;
try {
generate = FILE_HANDLER_CLASS.getDeclaredMethod("generate",
String.class,
int.class,
int.class,
int.class);
generate.setAccessible(true);
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
GENERATE = generate;
USER_HOME = System.getProperty("user.home");
TMP = System.getProperty("java.io.tmpdir", USER_HOME);
}
public static void main(String... args) throws Throwable {
for (int i=0; i < PATTERNS.length; i+=2) {
String s = PATTERNS[i];
String partial = PATTERNS[i+1];
System.out.println("generate: " + s);
for (int[] gen : GENERATIONS) {
String expected = generateExpected(s, partial, gen[0], gen[1], gen[2]);
String output = generate(s, gen[0], gen[1], gen[2]).toString();
System.out.println("\t" + Arrays.toString(gen)+ ": " + output);
if (!expected.equals(output)) {
throw new RuntimeException("test failed for \""
+ s +"\" " + Arrays.toString(gen) + ": "
+ "\n\tgenerated: \"" + output +"\""
+ "\n\t expected: \"" + expected +"\"");
}
}
}
}
// Strip the trailing separator from the string, if present
static String stripTrailingSeparator(String s) {
if (s.endsWith("/")) {
return s.substring(0, s.length() -1);
} else if (s.endsWith(File.separator)) {
return s.substring(0, s.length() - File.separator.length());
} else {
return s;
}
}
/**
* Compute the final expected output from a partially computed output found
* at PATTERNS[i+1]
* @param s The pattern string, found at PATTERN[i]
* (with i % 2 == 0)
* @param partial The partially computed output, found at PATTERN[i+1]
* @param count The count parameter given to FileHandler::generate
* @param generation The generation parameter given to FileHandler::generate
* @param unique The unique parameter given to FileHandler::generate
* @return The expected output that FileHandler.generate(s, count, gen, unique)
* should produce.
*/
static String generateExpected(String s, String partial,
int count, int generation, int unique)
{
boolean sawu = s.replace("%%", "$$$$").contains("%u");
boolean sawg = s.replace("%%", "$$$$").contains("%g");
String result = partial.replace("%%", "$$$$");
String tmp = stripTrailingSeparator(TMP);
String home = stripTrailingSeparator(USER_HOME);
result = result.replace("%h", home);
result = result.replace("%t", tmp);
result = result.replace("%g", String.valueOf(generation));
result = result.replace("%u", String.valueOf(unique));
result = result.replace("$$$$", "%");
result = result.replace("/", File.separator);
if (count > 1 && !sawg) {
result = result + "." + generation;
}
if (unique > 0 && !sawu) {
result = result + "." + unique;
}
return result;
}
// Calls FileHandler.generate(s, count, generation, unique) through reflection
static File generate(String s, int count, int generation, int unique)
throws Throwable
{
try {
return (File) GENERATE.invoke(null, s, count, generation, unique);
} catch (InvocationTargetException e) {
throw e.getCause();
}
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2006, 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 6476146
*
* @summary Added convenience method for deprecated Logger.global
* @author Serguei Spitsyn
*
* @build GetGlobalTest
* @run main GetGlobalTest
*/
/**
* RFE ID:
* 6476146: Add convenience method for deprecated Logger.global
* This is simple unit test for the RFE.
*
* With the deprecation of Logger.global, there is no convenient way of
* doing entry-level logging. Logger.getLogger(Logger.GLOBAL_LOGGER_NAME)
* is not an adequate substitute for Logger.global.
* Solution: A convenience method Logger.getGlobal() should be provided.
*/
import java.util.logging.*;
public class GetGlobalTest {
static final java.io.PrintStream out = System.out;
public static void main(String arg[]) {
Logger glogger1 = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
Logger glogger2 = Logger.getGlobal();
if (glogger1.equals(glogger2)) {
out.println("Test passed");
} else {
out.println("Test FAILED");
}
}
}

View file

@ -0,0 +1,105 @@
/*
* 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.
*/
/*
* @test
* @bug 8349206
* @summary j.u.l.Handler classes create deadlock risk via synchronized publish() method.
* @modules java.base/sun.util.logging
* java.logging
* @build java.logging/java.util.logging.TestStreamHandler
* @run main/othervm StreamHandlerLockingTest
*/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.TestStreamHandler;
public class StreamHandlerLockingTest {
static class TestFormatter extends Formatter {
final Handler handler;
TestFormatter(Handler handler) {
this.handler = handler;
}
@Override
public String format(LogRecord record) {
if (Thread.holdsLock(handler)) {
throw new AssertionError("format() was called with handler locked (bad).");
}
return record.getMessage() + "\n";
}
@Override
public String getHead(Handler h) {
// This is currently true, and not easy to make unsynchronized.
if (!Thread.holdsLock(handler)) {
throw new AssertionError("getHead() expected to be called with handler locked.");
}
return "--HEAD--\n";
}
@Override
public String getTail(Handler h) {
// This is currently true, and not easy to make unsynchronized.
if (!Thread.holdsLock(handler)) {
throw new AssertionError("getTail() expected to be called with handler locked.");
}
return "--TAIL--\n";
}
}
private static final String EXPECTED_LOG =
String.join("\n", "--HEAD--", "Hello World", "Some more logging...", "And we're done!", "--TAIL--", "");
public static void main(String[] args) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
TestStreamHandler handler = new TestStreamHandler(out);
TestFormatter formatter = new TestFormatter(handler);
handler.setFormatter(formatter);
handler.publish(log("Hello World"));
handler.publish(log("Some more logging..."));
handler.publish(log("And we're done!"));
handler.close();
// Post write callback should have happened once per publish call (with lock held).
if (handler.callbackCount != 3) {
throw new AssertionError("Unexpected callback count: " + handler.callbackCount);
}
String logged = out.toString("UTF-8");
if (!EXPECTED_LOG.equals(logged)) {
throw new AssertionError("Unexpected log contents: " + logged);
}
}
static LogRecord log(String msg) {
return new LogRecord(Level.INFO, msg);
}
}

View file

@ -0,0 +1,54 @@
/*
* 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.
*/
package java.util.logging;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
/**
* A trivial UTF-8 stream handler subclass class to capture whether the
* (package protected) post-write callback method is synchronized.
*/
public class TestStreamHandler extends StreamHandler {
public int callbackCount = 0;
public TestStreamHandler(OutputStream out) {
setOutputStream(out);
try {
setEncoding("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
@Override
void synchronousPostWriteHook() {
if (!Thread.holdsLock(this)) {
throw new AssertionError(
String.format("Post write callback [index=%d] was invoked without handler locked.", callbackCount));
}
callbackCount++;
}
}

View file

@ -0,0 +1,29 @@
# mandatory Default properties...
.level= INFO
handlers=HandlersConfigTest$ConfiguredHandler
java.util.logging.MemoryHandler.target=HandlersConfigTest$ConfiguredHandler
# in addition to Default, Configured adds the following...
java.util.logging.MemoryHandler.level=FINE
java.util.logging.MemoryHandler.filter=HandlersConfigTest$ConfiguredFilter
java.util.logging.MemoryHandler.formatter=HandlersConfigTest$ConfiguredFormatter
java.util.logging.MemoryHandler.size=123
java.util.logging.MemoryHandler.push=FINE
java.util.logging.ConsoleHandler.level=FINE
java.util.logging.ConsoleHandler.encoding=ASCII
java.util.logging.ConsoleHandler.filter=HandlersConfigTest$ConfiguredFilter
java.util.logging.ConsoleHandler.formatter=HandlersConfigTest$ConfiguredFormatter
java.util.logging.StreamHandler.level=FINE
java.util.logging.StreamHandler.encoding=ASCII
java.util.logging.StreamHandler.filter=HandlersConfigTest$ConfiguredFilter
java.util.logging.StreamHandler.formatter=HandlersConfigTest$ConfiguredFormatter
java.util.logging.SocketHandler.level=FINE
java.util.logging.SocketHandler.encoding=ASCII
java.util.logging.SocketHandler.filter=HandlersConfigTest$ConfiguredFilter
java.util.logging.SocketHandler.formatter=HandlersConfigTest$ConfiguredFormatter

View file

@ -0,0 +1,6 @@
# mandatory Default properties...
.level= INFO
handlers=HandlersConfigTest$ConfiguredHandler
java.util.logging.MemoryHandler.target=HandlersConfigTest$ConfiguredHandler

View file

@ -0,0 +1,323 @@
/*
* Copyright (c) 2013, 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 8029781 8030801
* @summary Test which verifies that various JDK logging Handlers are
* configured correctly from defaults and/or LogManager properties
* as specified in javadoc and that no special
* logging permission is required for instantiating them.
* @modules java.logging/java.util.logging:open
* @run main/othervm HandlersConfigTest default
* @run main/othervm HandlersConfigTest configured
*/
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.net.ServerSocket;
import java.net.URL;
import java.util.Objects;
import java.util.logging.ConsoleHandler;
import java.util.logging.Filter;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.MemoryHandler;
import java.util.logging.SimpleFormatter;
import java.util.logging.SocketHandler;
import java.util.logging.StreamHandler;
import java.util.logging.XMLFormatter;
public abstract class HandlersConfigTest implements Runnable {
public static void main(String[] args) {
switch (args.length == 1 ? args[0] : "usage") {
case "default":
new Default().run();
break;
case "configured":
new Configured().run();
break;
default:
System.err.println("Usage: HandlersConfigTest [default|configured]");
break;
}
}
static final String CONFIG_FILE_PROPERTY = "java.util.logging.config.file";
final Field memoryHandlerTarget, memoryHandlerSize, streamHandlerOutput;
final ServerSocket serverSocket;
HandlersConfigTest() {
// establish access to private fields
try {
memoryHandlerTarget = MemoryHandler.class.getDeclaredField("target");
memoryHandlerTarget.setAccessible(true);
memoryHandlerSize = MemoryHandler.class.getDeclaredField("size");
memoryHandlerSize.setAccessible(true);
streamHandlerOutput = StreamHandler.class.getDeclaredField("output");
streamHandlerOutput.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new AssertionError(e);
}
// load logging.propertes for the test
String rname = getClass().getName().replace('.', '/') + ".props";
URL url = getClass().getClassLoader().getResource(rname);
if (url == null || !"file".equals(url.getProtocol())) {
throw new IllegalStateException("Resource: " + rname + " not found or not on file: " + url);
}
System.setProperty(CONFIG_FILE_PROPERTY, url.getFile());
// create ServerSocket as a target for SocketHandler
try {
serverSocket = new ServerSocket(0); // auto allocated port
} catch (IOException e) {
throw new AssertionError(e);
}
// initialize logging system
LogManager.getLogManager();
}
// check that defaults are used as specified by javadoc
public static class Default extends HandlersConfigTest {
public static void main(String[] args) {
new Default().run();
}
@Override
public void run() {
// MemoryHandler
check(new MemoryHandler(),
Level.ALL, null, null, SimpleFormatter.class,
ConfiguredHandler.class, 1000, Level.SEVERE);
check(new MemoryHandler(new SpecifiedHandler(), 100, Level.WARNING),
Level.ALL, null, null, SimpleFormatter.class,
SpecifiedHandler.class, 100, Level.WARNING);
// StreamHandler
check(new StreamHandler(),
Level.INFO, null, null, SimpleFormatter.class,
null);
check(new StreamHandler(System.out, new SpecifiedFormatter()),
Level.INFO, null, null, SpecifiedFormatter.class,
System.out);
// ConsoleHandler
check(new ConsoleHandler(),
Level.INFO, null, null, SimpleFormatter.class,
System.err);
// SocketHandler (use the ServerSocket's port)
try {
check(new SocketHandler("localhost", serverSocket.getLocalPort()),
Level.ALL, null, null, XMLFormatter.class);
} catch (IOException e) {
throw new RuntimeException("Can't connect to localhost:" + serverSocket.getLocalPort(), e);
}
}
}
// check that LogManager properties configuration is respected
public static class Configured extends HandlersConfigTest {
public static void main(String[] args) {
new Configured().run();
}
@Override
public void run() {
// MemoryHandler
check(new MemoryHandler(),
Level.FINE, null, ConfiguredFilter.class, ConfiguredFormatter.class,
ConfiguredHandler.class, 123, Level.FINE);
check(new MemoryHandler(new SpecifiedHandler(), 100, Level.WARNING),
Level.FINE, null, ConfiguredFilter.class, ConfiguredFormatter.class,
SpecifiedHandler.class, 100, Level.WARNING);
// StreamHandler
check(new StreamHandler(),
Level.FINE, "ASCII", ConfiguredFilter.class, ConfiguredFormatter.class,
null);
check(new StreamHandler(System.out, new SpecifiedFormatter()),
Level.FINE, "ASCII", ConfiguredFilter.class, SpecifiedFormatter.class,
System.out);
// ConsoleHandler
check(new ConsoleHandler(),
Level.FINE, "ASCII", ConfiguredFilter.class, ConfiguredFormatter.class,
System.err);
// SocketHandler (use the ServerSocket's port)
try {
check(new SocketHandler("localhost", serverSocket.getLocalPort()),
Level.FINE, "ASCII", ConfiguredFilter.class, ConfiguredFormatter.class);
} catch (Exception e) {
throw new RuntimeException("Can't connect to localhost:" + serverSocket.getLocalPort(), e);
}
}
}
// test infrastructure
void check(Handler handler,
Level expectedLevel,
String expectedEncoding,
Class<? extends Filter> expectedFilterType,
Class<? extends Formatter> expectedFormatterType) {
checkEquals(handler, "level", handler.getLevel(), expectedLevel);
checkEquals(handler, "encoding", handler.getEncoding(), expectedEncoding);
checkType(handler, "filter", handler.getFilter(), expectedFilterType);
checkType(handler, "formatter", handler.getFormatter(), expectedFormatterType);
}
void check(MemoryHandler handler,
Level expectedLevel,
String expectedEncoding,
Class<? extends Filter> expectedFilterType,
Class<? extends Formatter> expectedFormatterType,
Class<? extends Handler> expextedTargetType,
int expextedSize,
Level expectedPushLevel) {
checkType(handler, "target", getTarget(handler), expextedTargetType);
checkEquals(handler, "size", getSize(handler), expextedSize);
checkEquals(handler, "pushLevel", handler.getPushLevel(), expectedPushLevel);
check(handler, expectedLevel, expectedEncoding, expectedFilterType, expectedFormatterType);
}
void check(StreamHandler handler,
Level expectedLevel,
String expectedEncoding,
Class<? extends Filter> expectedFilterType,
Class<? extends Formatter> expectedFormatterType,
OutputStream expectedOutputStream) {
checkEquals(handler, "outputStream", getOutput(handler), expectedOutputStream);
check(handler, expectedLevel, expectedEncoding, expectedFilterType, expectedFormatterType);
}
<T> void checkEquals(Handler handler, String property, T value, T expectedValue) {
if (!Objects.equals(value, expectedValue)) {
fail(handler, property + ": " + value + ", expected " + property + ": " + expectedValue);
}
}
<T> void checkType(Handler handler, String property, T value, Class<? extends T> expectedType) {
if (!(expectedType == null && value == null || expectedType != null && expectedType.isInstance(value))) {
Class<?> type = value == null ? null : value.getClass();
fail(handler, property + " type: " + type + ", expected " + property + " type: " + expectedType);
}
}
void fail(Handler handler, String message) {
throw new AssertionError("Handler: " + handler.getClass().getName() +
", configured with: " + getClass().getName() +
", " + message);
}
Handler getTarget(MemoryHandler memoryHandler) {
try {
return (Handler) memoryHandlerTarget.get(memoryHandler);
} catch (IllegalAccessException e) {
throw new IllegalAccessError(e.getMessage());
}
}
int getSize(MemoryHandler memoryHandler) {
try {
return (int) memoryHandlerSize.get(memoryHandler);
} catch (IllegalAccessException e) {
throw new IllegalAccessError(e.getMessage());
}
}
OutputStream getOutput(StreamHandler streamHandler) {
try {
return (OutputStream) streamHandlerOutput.get(streamHandler);
} catch (IllegalAccessException e) {
throw new IllegalAccessError(e.getMessage());
}
}
// various independent types of Formatters, Filters, Handlers...
public static class SpecifiedFormatter extends Formatter {
@Override
public String format(LogRecord record) {
return String.valueOf(record);
}
}
public static class SpecifiedHandler extends Handler {
@Override
public void publish(LogRecord record) { }
@Override
public void flush() { }
@Override
public void close() throws SecurityException { }
}
public static class ConfiguredFormatter extends Formatter {
@Override
public String format(LogRecord record) {
return String.valueOf(record);
}
}
public static class ConfiguredFilter implements Filter {
@Override
public boolean isLoggable(LogRecord record) {
return true;
}
}
public static class ConfiguredHandler extends Handler {
@Override
public void publish(LogRecord record) { }
@Override
public void flush() { }
@Override
public void close() throws SecurityException { }
}
}

View file

@ -0,0 +1,143 @@
/*
* 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.SimpleFormatter;
/**
* @test
* @bug 8072645
* @summary tests that LogRecord has nanos...
* @run main LogRecordWithNanos
* @author danielfuchs
*/
public class LogRecordWithNanos {
static final int MILLIS_IN_SECOND = 1000;
static final int NANOS_IN_MILLI = 1000_000;
static final int NANOS_IN_SECOND = 1000_000_000;
static final boolean verbose = false;
static final class TestAssertException extends RuntimeException {
TestAssertException(String msg) { super(msg); }
}
private static void assertEquals(long expected, long received, String msg) {
if (expected != received) {
throw new TestAssertException("Unexpected result for " + msg
+ ".\n\texpected: " + expected
+ "\n\tactual: " + received);
} else if (verbose) {
System.out.println("Got expected " + msg + ": " + received);
}
}
/**
* Serializes a log record, then deserializes it and check that both
* records match.
* @param record the log record to serialize & deserialize.
* @throws IOException Unexpected.
* @throws ClassNotFoundException Unexpected.
*/
public static void test(LogRecord record)
throws IOException, ClassNotFoundException {
// Format the given logRecord using the SimpleFormatter
SimpleFormatter formatter = new SimpleFormatter();
String str = formatter.format(record);
// Serialize the given LogRecord
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(record);
oos.flush();
oos.close();
// First checks that the log record can be deserialized
final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
final ObjectInputStream ois = new ObjectInputStream(bais);
final LogRecord record2 = (LogRecord)ois.readObject();
assertEquals(record.getMillis(), record2.getMillis(), "getMillis()");
assertEquals(record.getInstant().getEpochSecond(),
record2.getInstant().getEpochSecond(),
"getInstant().getEpochSecond()");
assertEquals(record.getInstant().getNano(),
record2.getInstant().getNano(),
"getInstant().getNano()");
assertEquals(record.getInstant().toEpochMilli(),
record2.getInstant().toEpochMilli(),
"getInstant().toEpochMilli()");
assertEquals(record.getMillis(),
record.getInstant().toEpochMilli(),
"getMillis()/getInstant().toEpochMilli()");
assertEquals(record2.getMillis(),
record2.getInstant().toEpochMilli(),
"getMillis()/getInstant().toEpochMilli()");
assertEquals((record.getMillis()%MILLIS_IN_SECOND)*NANOS_IN_MILLI
+ (record.getInstant().getNano() % NANOS_IN_MILLI),
record.getInstant().getNano(),
"record.getMillis()%1000)*1000_000"
+ " + record.getInstant().getNano()%1000_000 / getInstant().getNano()");
assertEquals((record2.getMillis()%MILLIS_IN_SECOND)*NANOS_IN_MILLI
+ (record2.getInstant().getNano() % NANOS_IN_MILLI),
record2.getInstant().getNano(),
"record2.getMillis()%1000)*1000_000"
+ " + record2.getInstant().getNano()%1000_000 / getInstant().getNano()");
// Format the deserialized LogRecord using the SimpleFormatter, and
// check that the string representation obtained matches the string
// representation of the original LogRecord
String str2 = formatter.format(record2);
if (!str.equals(str2))
throw new RuntimeException("Unexpected values in deserialized object:"
+ "\n\tExpected: " + str
+ "\n\tRetrieved: "+str);
}
public static void main(String[] args) throws Exception {
int count=0;
for (int i=0; i<1000; i++) {
LogRecord record = new LogRecord(Level.INFO, "Java Version: {0}");
record.setLoggerName("test");
record.setParameters(new Object[] {System.getProperty("java.version")});
if (record.getInstant().getNano() % 1000_000 != 0) {
count++;
}
test(record);
}
if (count == 0) {
throw new RuntimeException("Expected at least one record to have nanos");
}
System.out.println(count + "/1000 records had nano adjustment.");
}
}

View file

@ -0,0 +1,335 @@
/*
* 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.time.Instant;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.SimpleFormatter;
/**
* @test
* @bug 8072645 8144262
* @summary tests the new methods added to LogRecord.
* @run main LogRecordWithNanosAPI
* @author danielfuchs
*/
public class LogRecordWithNanosAPI {
static final int MILLIS_IN_SECOND = 1000;
static final int NANOS_IN_MILLI = 1000_000;
static final int NANOS_IN_SECOND = 1000_000_000;
static final boolean verbose = true;
static final class TestAssertException extends RuntimeException {
TestAssertException(String msg) { super(msg); }
}
private static void assertEquals(long expected, long received, String msg) {
if (expected != received) {
throw new TestAssertException("Unexpected result for " + msg
+ ".\n\texpected: " + expected
+ "\n\tactual: " + received);
} else if (verbose) {
System.out.println("Got expected " + msg + ": " + received);
}
}
private static void assertEquals(Object expected, Object received, String msg) {
if (!Objects.equals(expected, received)) {
throw new TestAssertException("Unexpected result for " + msg
+ ".\n\texpected: " + expected
+ "\n\tactual: " + received);
} else if (verbose) {
System.out.println("Got expected " + msg + ": " + received);
}
}
// The nano second fractional part of a second, contained in a time expressed
// as a number of millisecond from the epoch.
private static long nanoInSecondFromEpochMilli(long millis) {
return (((millis%MILLIS_IN_SECOND) + MILLIS_IN_SECOND)%MILLIS_IN_SECOND)*NANOS_IN_MILLI;
}
/**
* Serializes a log record, then deserializes it and check that both
* records match.
* @param record the log record to serialize & deserialize.
* @param hasExceedingNanos whether the record has a nano adjustment whose
* value exceeds 1ms.
* @throws IOException Unexpected.
* @throws ClassNotFoundException Unexpected.
*/
public static void test(LogRecord record, boolean hasExceedingNanos)
throws IOException, ClassNotFoundException {
// Format the given logRecord using the SimpleFormatter
SimpleFormatter formatter = new SimpleFormatter();
String str = formatter.format(record);
// Serialize the given LogRecord
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(record);
oos.flush();
oos.close();
// First checks that the log record can be deserialized
final ByteArrayInputStream bais =
new ByteArrayInputStream(baos.toByteArray());
final ObjectInputStream ois = new ObjectInputStream(bais);
final LogRecord record2 = (LogRecord)ois.readObject();
assertEquals(record.getMillis(), record2.getMillis(), "getMillis()");
assertEquals(record.getInstant().getEpochSecond(),
record2.getInstant().getEpochSecond(),
"getInstant().getEpochSecond()");
assertEquals(record.getInstant().getNano(),
record2.getInstant().getNano(),
"getInstant().getNano()");
assertEquals(record.getInstant().toEpochMilli(),
record2.getInstant().toEpochMilli(),
"getInstant().toEpochMilli()");
long millis = record.getMillis();
millis = hasExceedingNanos
? Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
(millis%MILLIS_IN_SECOND)*NANOS_IN_MILLI
+ record.getInstant().getNano() % NANOS_IN_MILLI).toEpochMilli()
: millis;
assertEquals(millis, record.getInstant().toEpochMilli(),
"getMillis()/getInstant().toEpochMilli()");
millis = record2.getMillis();
millis = hasExceedingNanos
? Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
(millis%MILLIS_IN_SECOND)*NANOS_IN_MILLI
+ record2.getInstant().getNano() % NANOS_IN_MILLI).toEpochMilli()
: millis;
assertEquals(millis, record2.getInstant().toEpochMilli(),
"getMillis()/getInstant().toEpochMilli()");
long nanos = nanoInSecondFromEpochMilli(record.getMillis())
+ record.getInstant().getNano() % NANOS_IN_MILLI;
assertEquals(nanos, record.getInstant().getNano(),
"nanoInSecondFromEpochMilli(record.getMillis())"
+ " + record.getInstant().getNano() % NANOS_IN_MILLI /getInstant().getNano()");
nanos = nanoInSecondFromEpochMilli(record2.getMillis())
+ record2.getInstant().getNano() % NANOS_IN_MILLI;
assertEquals(nanos, record2.getInstant().getNano(),
"nanoInSecondFromEpochMilli(record2.getMillis())"
+ " + record2.getInstant().getNano() % NANOS_IN_MILLI /getInstant().getNano()");
// Format the deserialized LogRecord using the SimpleFormatter, and
// check that the string representation obtained matches the string
// representation of the original LogRecord
String str2 = formatter.format(record2);
if (!str.equals(str2))
throw new RuntimeException("Unexpected values in deserialized object:"
+ "\n\tExpected: " + str
+ "\n\tRetrieved: "+str);
}
public static void main(String[] args) throws Exception {
int count=0;
LogRecord record = new LogRecord(Level.INFO, "Java Version: {0}");
record.setLoggerName("test");
record.setParameters(new Object[] {System.getProperty("java.version")});
final int nanos = record.getInstant().getNano() % NANOS_IN_MILLI;
final long millis = record.getMillis();
final Instant instant = record.getInstant();
if (millis != instant.toEpochMilli()) {
throw new RuntimeException("Unexpected millis: "
+ record.getMillis());
}
test(record, false);
// nano adjustment < 1ms (canonical case)
int newNanos = (nanos + 111111) % NANOS_IN_MILLI;
record.setInstant(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
(millis % MILLIS_IN_SECOND) * NANOS_IN_MILLI + newNanos));
assertEquals(newNanos, record.getInstant().getNano() % NANOS_IN_MILLI,
"record.getInstant().getNano() % NANOS_IN_MILLI");
assertEquals(millis, record.getMillis(), "record.getMillis()");
assertEquals(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
(millis%MILLIS_IN_SECOND)*NANOS_IN_MILLI + newNanos),
record.getInstant(), "record.getInstant()");
test(record, false);
assertEquals(newNanos, record.getInstant().getNano() % NANOS_IN_MILLI,
"record.getInstant().getNano() % NANOS_IN_MILLI");
assertEquals(millis, record.getMillis(), "record.getMillis()");
assertEquals(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
(millis%MILLIS_IN_SECOND)*NANOS_IN_MILLI + newNanos),
record.getInstant(), "record.getInstant()");
// nano adjustment > 1ms - non canonical - should still work
int newExceedingNanos = 2111_111;
record.setInstant(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
(millis % MILLIS_IN_SECOND) * NANOS_IN_MILLI + newExceedingNanos));
assertEquals(newExceedingNanos % NANOS_IN_MILLI,
record.getInstant().getNano() % NANOS_IN_MILLI,
"record.getInstant().getNano() % NANOS_IN_MILLI");
assertEquals(millis + newExceedingNanos / NANOS_IN_MILLI,
record.getMillis(), "record.getMillis()");
assertEquals(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
(millis%MILLIS_IN_SECOND)*NANOS_IN_MILLI + newExceedingNanos),
record.getInstant(), "record.getInstant()");
test(record, true);
assertEquals(newExceedingNanos % NANOS_IN_MILLI,
record.getInstant().getNano() % NANOS_IN_MILLI,
"record.getInstant().getNano() % NANOS_IN_MILLI");
assertEquals(millis + newExceedingNanos / NANOS_IN_MILLI,
record.getMillis(), "record.getMillis()");
assertEquals(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
(millis%MILLIS_IN_SECOND)*NANOS_IN_MILLI + newExceedingNanos),
record.getInstant(), "record.getInstant()");
// nano adjustement > 1s - non canonical - should still work
newExceedingNanos = 1111_111_111;
record.setInstant(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
(millis % MILLIS_IN_SECOND) * NANOS_IN_MILLI + newExceedingNanos));
assertEquals(newExceedingNanos % NANOS_IN_MILLI,
record.getInstant().getNano() % NANOS_IN_MILLI,
"record.getInstant().getNano() % NANOS_IN_MILLI");
assertEquals(millis + newExceedingNanos / NANOS_IN_MILLI,
record.getMillis(), "record.getMillis()");
assertEquals(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
(millis%MILLIS_IN_SECOND)*NANOS_IN_MILLI + newExceedingNanos),
record.getInstant(), "record.getInstant()");
test(record, true);
assertEquals(newExceedingNanos % NANOS_IN_MILLI,
record.getInstant().getNano() % NANOS_IN_MILLI,
"record.getInstant().getNano() % NANOS_IN_MILLI");
assertEquals(millis + newExceedingNanos / NANOS_IN_MILLI,
record.getMillis(), "record.getMillis()");
assertEquals(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
(millis%MILLIS_IN_SECOND)*NANOS_IN_MILLI + newExceedingNanos),
record.getInstant(), "record.getInstant()");
// nano adjustement < 0 - non canonical - should still work
newExceedingNanos = -1;
record.setInstant(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
(millis % MILLIS_IN_SECOND) * NANOS_IN_MILLI + newExceedingNanos));
assertEquals(newExceedingNanos + NANOS_IN_MILLI,
record.getInstant().getNano() % NANOS_IN_MILLI,
"record.getInstant().getNano() % NANOS_IN_MILLI");
assertEquals(millis -1, record.getMillis(), "record.getMillis()");
assertEquals(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
(millis%MILLIS_IN_SECOND)*NANOS_IN_MILLI + newExceedingNanos),
record.getInstant(), "record.getInstant()");
test(record, true);
record.setInstant(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
(millis % MILLIS_IN_SECOND) * NANOS_IN_MILLI + newExceedingNanos));
assertEquals(millis -1, record.getMillis(), "record.getMillis()");
assertEquals(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
(millis%MILLIS_IN_SECOND)*NANOS_IN_MILLI + newExceedingNanos),
record.getInstant(), "record.getInstant()");
// setMillis
record.setMillis(millis-1);
assertEquals(millis-1, record.getInstant().toEpochMilli(),
"record.getInstant().toEpochMilli()");
assertEquals(millis-1, record.getMillis(),
"record.getMillis()");
assertEquals(0, record.getInstant().getNano() % NANOS_IN_MILLI,
"record.getInstant().getNano() % NANOS_IN_MILLI");
test(record, false);
assertEquals(millis-1, record.getInstant().toEpochMilli(),
"record.getInstant().toEpochMilli()");
assertEquals(millis-1, record.getMillis(),
"record.getMillis()");
assertEquals(0, record.getInstant().getNano() % NANOS_IN_MILLI,
"record.getInstant().getNano() % NANOS_IN_MILLI");
// setMillis to 0
record.setMillis(0);
assertEquals(0, record.getInstant().toEpochMilli(),
"record.getInstant().toEpochMilli()");
assertEquals(0, record.getMillis(),
"record.getMillis()");
assertEquals(0, record.getInstant().getNano() % NANOS_IN_MILLI,
"record.getInstant().getNano() % NANOS_IN_MILLI");
test(record, false);
assertEquals(0, record.getInstant().toEpochMilli(),
"record.getInstant().toEpochMilli()");
assertEquals(0, record.getMillis(),
"record.getMillis()");
assertEquals(0, record.getInstant().getNano() % NANOS_IN_MILLI,
"record.getInstant().getNano() % NANOS_IN_MILLI");
// setMillis to -1
record.setMillis(-1);
assertEquals(-1, record.getInstant().toEpochMilli(),
"record.getInstant().toEpochMilli()");
assertEquals(-1, record.getMillis(),
"record.getMillis()");
assertEquals(0, record.getInstant().getNano() % NANOS_IN_MILLI,
"record.getInstant().getNano() % NANOS_IN_MILLI");
test(record, false);
assertEquals(-1, record.getInstant().toEpochMilli(),
"record.getInstant().toEpochMilli()");
assertEquals(-1, record.getMillis(),
"record.getMillis()");
assertEquals(0, record.getInstant().getNano() % NANOS_IN_MILLI,
"record.getInstant().getNano() % NANOS_IN_MILLI");
try {
record.setInstant(null);
throw new RuntimeException("Expected NullPointerException not thrown");
} catch (NullPointerException x) {
System.out.println("Got expected NPE when trying to call record.setInstant(null): " + x);
}
// This instant is the biggest for which toEpochMilli will not throw...
final Instant max = Instant.ofEpochMilli(Long.MAX_VALUE).plusNanos(999_999L);
record.setInstant(max);
assertEquals(Long.MAX_VALUE / 1000L,
record.getInstant().getEpochSecond(),
"max instant seconds [record.getInstant().getEpochSecond()]");
assertEquals(Long.MAX_VALUE,
record.getInstant().toEpochMilli(),
"max instant millis [record.getInstant().toEpochMilli()]");
assertEquals(Long.MAX_VALUE, record.getMillis(),
"max instant millis [record.getMillis()]");
assertEquals((Long.MAX_VALUE % 1000L)*1000_000L + 999_999L,
record.getInstant().getNano(),
"max instant nanos [record.getInstant().getNano()]");
// Too big by 1 ns.
final Instant tooBig = max.plusNanos(1L);
try {
record.setInstant(tooBig);
throw new RuntimeException("Expected ArithmeticException not thrown");
} catch (ArithmeticException x) {
System.out.println("Got expected ArithmeticException when trying"
+ " to call record.setInstant(Instant.ofEpochMilli(Long.MAX_VALUE)"
+ ".plusNanos(999_999L).plusNanos(1L)): " + x);
}
}
}

View file

@ -0,0 +1,363 @@
/*
* 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.time.ZoneId;
import java.util.Base64;
import java.util.Locale;
import java.util.TimeZone;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.SimpleFormatter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
/**
* @test
* @bug 8072645
* @summary tests the compatibility of LogRecord serial form between
* JDK 8 and JDK 9. Ideally this test should be run on both platforms.
* (It is designed to run on both).
* @run main/othervm SerializeLogRecord
* @author danielfuchs
*/
public class SerializeLogRecord {
/**
* Serializes a log record, encode the serialized bytes in base 64, and
* prints pseudo java code that can be cut and pasted into this test.
* @param record the log record to serialize, encode in base 64, and for
* which test data will be generated.
* @return A string containing the generated pseudo java code.
* @throws IOException Unexpected.
* @throws ClassNotFoundException Unexpected.
*/
public static String generate(LogRecord record) throws IOException, ClassNotFoundException {
// Format the given logRecord using the SimpleFormatter
SimpleFormatter formatter = new SimpleFormatter();
String str = formatter.format(record);
// Serialize the given LogRecord
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(record);
oos.flush();
oos.close();
// Now we're going to perform a number of smoke tests before
// generating the Java pseudo code.
//
// First checks that the log record can be deserialized
final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
final ObjectInputStream ois = new ObjectInputStream(bais);
final LogRecord record2 = (LogRecord)ois.readObject();
// Format the deserialized LogRecord using the SimpleFormatter, and
// check that the string representation obtained matches the string
// representation of the original LogRecord
String str2 = formatter.format(record2);
if (!str.equals(str2)) throw new RuntimeException("Unexpected values in deserialized object:"
+ "\n\tExpected: " + str
+ "\n\tRetrieved: "+str);
// Now get a Base64 string representation of the serialized bytes.
final String base64 = Base64.getEncoder().encodeToString(baos.toByteArray());
// Check that we can deserialize a log record from the Base64 string
// representation we just computed.
final ByteArrayInputStream bais2 = new ByteArrayInputStream(Base64.getDecoder().decode(base64));
final ObjectInputStream ois2 = new ObjectInputStream(bais2);
final LogRecord record3 = (LogRecord)ois2.readObject();
// Format the new deserialized LogRecord using the SimpleFormatter, and
// check that the string representation obtained matches the string
// representation of the original LogRecord
String str3 = formatter.format(record3);
if (!str.equals(str3)) throw new RuntimeException("Unexpected values in deserialized object:"
+ "\n\tExpected: " + str
+ "\n\tRetrieved: "+str);
//System.out.println(base64);
//System.out.println();
// Generates the Java Pseudo code that can be cut & pasted into
// this test (see Jdk8SerializedLog and Jdk9SerializedLog below)
final StringBuilder sb = new StringBuilder();
sb.append(" /**").append('\n');
sb.append(" * Base64 encoded string for LogRecord object.").append('\n');
sb.append(" * Java version: ").append(System.getProperty("java.version")).append('\n');
sb.append(" **/").append('\n');
sb.append(" final String base64 = ").append("\n ");
final int last = base64.length() - 1;
for (int i=0; i<base64.length();i++) {
if (i%64 == 0) sb.append("\"");
sb.append(base64.charAt(i));
if (i%64 == 63 || i == last) {
sb.append("\"");
if (i == last) sb.append(";\n");
else sb.append("\n + ");
}
}
sb.append('\n');
sb.append(" /**").append('\n');
sb.append(" * SimpleFormatter output for LogRecord object.").append('\n');
sb.append(" * Java version: ").append(System.getProperty("java.version")).append('\n');
sb.append(" **/").append('\n');
sb.append(" final String str = ").append("\n ");
sb.append("\"").append(str.replace("\n", "\\n")).append("\";\n");
return sb.toString();
}
/**
* An abstract class to test that a log record previously serialized on a
* different java version can be deserialized in the current java version.
* (see Jdk8SerializedLog and Jdk9SerializedLog below)
*/
public abstract static class SerializedLog {
public abstract String getBase64();
public abstract String getString();
/**
* Deserializes the Base64 encoded string returned by {@link
* #getBase64()}, format the obtained LogRecord using a
* SimpleFormatter, and checks that the string representation obtained
* matches the original string representation returned by {@link
* #getString()}.
*/
protected void dotest() {
try {
final String base64 = getBase64();
final ByteArrayInputStream bais =
new ByteArrayInputStream(Base64.getDecoder().decode(base64));
final ObjectInputStream ois = new ObjectInputStream(bais);
final LogRecord record = (LogRecord)ois.readObject();
final SimpleFormatter formatter = new SimpleFormatter();
String expected = getString();
String str2 = formatter.format(record);
check(expected, str2);
System.out.println(str2);
System.out.println("PASSED: "+this.getClass().getName()+"\n");
} catch (IOException | ClassNotFoundException x) {
throw new RuntimeException(x);
}
}
/**
* Check that the actual String representation obtained matches the
* expected String representation.
* @param expected Expected String representation, as returned by
* {@link #getString()}.
* @param actual Actual String representation obtained by formatting
* the LogRecord obtained by the deserialization of the
* bytes encoded in {@link #getBase64()}.
*/
protected void check(String expected, String actual) {
if (!expected.equals(actual)) {
throw new RuntimeException(this.getClass().getName()
+ " - Unexpected values in deserialized object:"
+ "\n\tExpected: " + expected
+ "\n\tRetrieved: "+ actual);
}
}
}
public static class Jdk8SerializedLog extends SerializedLog {
// Generated by generate() on JDK 8.
// --------------------------------
// BEGIN
/**
* Base64 encoded string for LogRecord object.
* Java version: 1.8.0_11
**/
final String base64 =
"rO0ABXNyABtqYXZhLnV0aWwubG9nZ2luZy5Mb2dSZWNvcmRKjVk982lRlgMACkoA"
+ "Bm1pbGxpc0oADnNlcXVlbmNlTnVtYmVySQAIdGhyZWFkSURMAAVsZXZlbHQAGUxq"
+ "YXZhL3V0aWwvbG9nZ2luZy9MZXZlbDtMAApsb2dnZXJOYW1ldAASTGphdmEvbGFu"
+ "Zy9TdHJpbmc7TAAHbWVzc2FnZXEAfgACTAAScmVzb3VyY2VCdW5kbGVOYW1lcQB+"
+ "AAJMAA9zb3VyY2VDbGFzc05hbWVxAH4AAkwAEHNvdXJjZU1ldGhvZE5hbWVxAH4A"
+ "AkwABnRocm93bnQAFUxqYXZhL2xhbmcvVGhyb3dhYmxlO3hwAAABSjUCgo0AAAAA"
+ "AAAAAAAAAAFzcgAXamF2YS51dGlsLmxvZ2dpbmcuTGV2ZWyOiHETUXM2kgIAA0kA"
+ "BXZhbHVlTAAEbmFtZXEAfgACTAAScmVzb3VyY2VCdW5kbGVOYW1lcQB+AAJ4cAAA"
+ "AyB0AARJTkZPdAAic3VuLnV0aWwubG9nZ2luZy5yZXNvdXJjZXMubG9nZ2luZ3QA"
+ "BHRlc3R0ABFKYXZhIFZlcnNpb246IHswfXBwcHB3BgEAAAAAAXQACDEuOC4wXzEx"
+ "eA==";
/**
* SimpleFormatter output for LogRecord object.
* Java version: 1.8.0_11
**/
final String str =
"Dec 10, 2014 4:22:44.621000000 PM test - INFO: Java Version: 1.8.0_11";
// ^^^
// Notice the milli second resolution above...
// END
// --------------------------------
@Override
public String getBase64() {
return base64;
}
@Override
public String getString() {
return str;
}
public static void test() {
new Jdk8SerializedLog().dotest();
}
}
public static class Jdk9SerializedLog extends SerializedLog {
// Generated by generate() on JDK 9.
// --------------------------------
// BEGIN
/**
* Base64 encoded string for LogRecord object.
* Java version: 1.9.0-internal
**/
final String base64 =
"rO0ABXNyABtqYXZhLnV0aWwubG9nZ2luZy5Mb2dSZWNvcmRKjVk982lRlgMAC0oA"
+ "Bm1pbGxpc0kADm5hbm9BZGp1c3RtZW50SgAOc2VxdWVuY2VOdW1iZXJJAAh0aHJl"
+ "YWRJREwABWxldmVsdAAZTGphdmEvdXRpbC9sb2dnaW5nL0xldmVsO0wACmxvZ2dl"
+ "ck5hbWV0ABJMamF2YS9sYW5nL1N0cmluZztMAAdtZXNzYWdlcQB+AAJMABJyZXNv"
+ "dXJjZUJ1bmRsZU5hbWVxAH4AAkwAD3NvdXJjZUNsYXNzTmFtZXEAfgACTAAQc291"
+ "cmNlTWV0aG9kTmFtZXEAfgACTAAGdGhyb3dudAAVTGphdmEvbGFuZy9UaHJvd2Fi"
+ "bGU7eHAAAAFLl3u6OAAOU/gAAAAAAAAAAAAAAAFzcgAXamF2YS51dGlsLmxvZ2dp"
+ "bmcuTGV2ZWyOiHETUXM2kgIAA0kABXZhbHVlTAAEbmFtZXEAfgACTAAScmVzb3Vy"
+ "Y2VCdW5kbGVOYW1lcQB+AAJ4cAAAAyB0AARJTkZPdAAic3VuLnV0aWwubG9nZ2lu"
+ "Zy5yZXNvdXJjZXMubG9nZ2luZ3QABHRlc3R0ABFKYXZhIFZlcnNpb246IHswfXBw"
+ "cHB3BgEAAAAAAXQADjEuOS4wLWludGVybmFseA==";
/**
* SimpleFormatter output for LogRecord object.
* Java version: 1.9.0-internal
**/
final String str =
"Feb 17, 2015 12:20:43.192939000 PM test - INFO: Java Version: 1.9.0-internal";
// ^^^
// Notice the micro second resolution above...
// END
// --------------------------------
@Override
public String getBase64() {
return base64;
}
@Override
public String getString() {
return str;
}
@Override
protected void check(String expected, String actual) {
if (System.getProperty("java.version").startsWith("1.8")) {
// If we are in JDK 8 and print a log record serialized in JDK 9,
// then we won't be able to print anything below the millisecond
// precision, since that hasn't been implemented in JDK 8.
// Therefore - we need to replace anything below millseconds by
// zeroes in the expected string (which was generated on JDK 9).
Pattern pattern = Pattern.compile("^"
+ "(.*\\.[0-9][0-9][0-9])" // group1: everything up to milliseconds
+ "([0-9][0-9][0-9][0-9][0-9][0-9])" // group 2: micros and nanos
+ "(.* - .*)$"); // group three: all the rest...
Matcher matcher = pattern.matcher(expected);
if (matcher.matches()) {
expected = matcher.group(1) + "000000" + matcher.group(3);
}
}
super.check(expected, actual);
}
public static void test() {
new Jdk9SerializedLog().dotest();
}
}
public static void generate() {
try {
LogRecord record = new LogRecord(Level.INFO, "Java Version: {0}");
record.setLoggerName("test");
record.setParameters(new Object[] {System.getProperty("java.version")});
System.out.println(generate(record));
} catch (IOException | ClassNotFoundException x) {
throw new RuntimeException(x);
}
}
static enum TestCase { GENERATE, TESTJDK8, TESTJDK9 };
public static void main(String[] args) {
// Set the locale and time zone to make sure we won't depend on the
// test env - in particular we don't want to depend on the
// time zone in which the test machine might be located.
// So we're gong to use Locale English and Time Zone UTC for this test.
// (Maybe that should be Locale.ROOT?)
Locale.setDefault(Locale.ENGLISH);
TimeZone.setDefault(TimeZone.getTimeZone(ZoneId.of("UTC")));
// Set the format property to make sure we always have the nanos, and
// to make sure it's the same format than what we used when
// computing the formatted string for Jdk8SerializedLog and
// Jdk9SerializedLog above.
//
// If you change the formatting, then you will need to regenerate
// the data for Jdk8SerializedLog and Jdk9SerializedLog.
//
// To do that - just run this test on JDK 8, and cut & paste the
// pseudo code printed by generate() into Jdk8SerializedLog.
// Then run this test again on JDK 9, and cut & paste the
// pseudo code printed by generate() into Jdk9SerializedLog.
// [Note: you can pass GENERATE as single arg to main() to avoid
// running the actual test]
// Finally run the test again to check that it still passes after
// your modifications.
//
System.setProperty("java.util.logging.SimpleFormatter.format",
"%1$tb %1$td, %1$tY %1$tl:%1$tM:%1$tS.%1$tN %1$Tp %2$s - %4$s: %5$s%6$s");
// If no args, then run everything....
if (args == null || args.length == 0) {
args = new String[] { "GENERATE", "TESTJDK8", "TESTJDK9" };
}
// Run the specified test case(s)
Stream.of(args).map(x -> TestCase.valueOf(x)).forEach((x) -> {
switch(x) {
case GENERATE: generate(); break;
case TESTJDK8: Jdk8SerializedLog.test(); break;
case TESTJDK9: Jdk9SerializedLog.test(); break;
}
});
}
}

View file

@ -0,0 +1,155 @@
/*
* 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.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.SimpleFormatter;
/**
* @test
* @bug 8072645
* @summary tests that SimpleFormatter can print out dates with the new
* nanosecond precision.
* @run main/othervm SimpleFormatterNanos
* @author danielfuchs
*/
public class SimpleFormatterNanos {
static final int MILLIS_IN_SECOND = 1000;
static final int NANOS_IN_MILLI = 1000_000;
static final int NANOS_IN_MICRO = 1000;
static final int NANOS_IN_SECOND = 1000_000_000;
static final boolean verbose = true;
static final class TestAssertException extends RuntimeException {
TestAssertException(String msg) { super(msg); }
}
private static void assertEquals(long expected, long received, String msg) {
if (expected != received) {
throw new TestAssertException("Unexpected result for " + msg
+ ".\n\texpected: " + expected
+ "\n\tactual: " + received);
} else if (verbose) {
System.out.println("Got expected " + msg + ": " + received);
}
}
static int getNanoAdjustment(LogRecord record) {
return record.getInstant().getNano() % NANOS_IN_MILLI;
}
static void setNanoAdjustment(LogRecord record, int nanos) {
record.setInstant(Instant.ofEpochSecond(record.getInstant().getEpochSecond(),
(record.getInstant().getNano() / NANOS_IN_MILLI) * NANOS_IN_MILLI + nanos));
}
public static void main(String[] args) throws Exception {
Locale.setDefault(Locale.ENGLISH);
LogRecord record = new LogRecord(Level.INFO, "Java Version: {0}");
record.setLoggerName("test");
record.setParameters(new Object[] {System.getProperty("java.version")});
int nanos = getNanoAdjustment(record);
long millis = record.getMillis();
// make sure we don't have leading zeros when printing below
// the second precision
if (millis % MILLIS_IN_SECOND < 100) millis = millis + 100;
// make sure we have some nanos
if (nanos % NANOS_IN_MICRO == 0) nanos = nanos + 999;
record.setMillis(millis);
setNanoAdjustment(record, nanos);
final Instant instant = record.getInstant();
if (nanos < 0) {
throw new RuntimeException("Unexpected negative nano adjustment: "
+ getNanoAdjustment(record));
}
if (nanos >= NANOS_IN_MILLI) {
throw new RuntimeException("Nano adjustment exceeds 1ms: "
+ getNanoAdjustment(record));
}
if (millis != record.getMillis()) {
throw new RuntimeException("Unexpected millis: " + millis + " != "
+ record.getMillis());
}
if (millis != record.getInstant().toEpochMilli()) {
throw new RuntimeException("Unexpected millis: "
+ record.getInstant().toEpochMilli());
}
ZonedDateTime zdt = ZonedDateTime.ofInstant(instant, ZoneId.systemDefault());
int zdtNanos = zdt.getNano();
long expectedNanos = (millis % MILLIS_IN_SECOND) * NANOS_IN_MILLI + nanos;
assertEquals(expectedNanos, instant.getNano(), "Instant.getNano()");
assertEquals(expectedNanos, zdtNanos, "ZonedDateTime.getNano()");
String match = "."+expectedNanos+" ";
System.out.println("Testing with default format...");
SimpleFormatter formatter = new SimpleFormatter();
String str = formatter.format(record);
if (str.contains(match)) {
throw new RuntimeException("SimpleFormatter.format()"
+ " string contains unexpected nanos: "
+ "\n\tdid not expect match for: '" + match + "'"
+ "\n\tin: " + str);
}
System.out.println("Nanos omitted as expected: no '"+match+"' in "+str);
System.out.println("Changing format to print nanos...");
System.setProperty("java.util.logging.SimpleFormatter.format",
"%1$tb %1$td, %1$tY %1$tl:%1$tM:%1$tS.%1$tN %1$Tp %2$s%n%4$s: %5$s%6$s%n");
SimpleFormatter formatter2 = new SimpleFormatter();
str = formatter2.format(record);
if (!str.contains(match)) {
throw new RuntimeException("SimpleFormatter.format()"
+ " string does not contain expected nanos: "
+ "\n\texpected match for: '" + match + "'"
+ "\n\tin: " + str);
}
System.out.println("Found expected match for '"+match+"' in "+str);
System.out.println("Changing format to omit nanos...");
System.setProperty("java.util.logging.SimpleFormatter.format",
"%1$tb %1$td, %1$tY %1$tl:%1$tM:%1$tS %1$Tp %2$s%n%4$s: %5$s%6$s%n");
SimpleFormatter formatter3 = new SimpleFormatter();
str = formatter3.format(record);
String notMatch = match;
if (str.contains(notMatch)) {
throw new RuntimeException("SimpleFormatter.format()"
+ " string contains unexpected nanos: "
+ "\n\tdid not expect match for: '" + notMatch + "'"
+ "\n\tin: " + str);
}
System.out.println("Nanos omitted as expected: no '"+notMatch+"' in "+str);
}
}

View file

@ -0,0 +1,274 @@
/*
* 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.XMLFormatter;
/**
* @test
* @bug 8072645
* @summary tests that XmlFormatter will print out dates with the new
* nanosecond precision.
* @run main/othervm XmlFormatterNanos
* @author danielfuchs
*/
public class XmlFormatterNanos {
static final int MILLIS_IN_SECOND = 1000;
static final int NANOS_IN_MILLI = 1000_000;
static final int NANOS_IN_MICRO = 1000;
static final int NANOS_IN_SECOND = 1000_000_000;
static final boolean verbose = true;
static final class TestAssertException extends RuntimeException {
TestAssertException(String msg) { super(msg); }
}
private static void assertEquals(long expected, long received, String msg) {
if (expected != received) {
throw new TestAssertException("Unexpected result for " + msg
+ ".\n\texpected: " + expected
+ "\n\tactual: " + received);
} else if (verbose) {
System.out.println("Got expected " + msg + ": " + received);
}
}
private static void assertEquals(Object expected, Object received, String msg) {
if (!Objects.equals(expected, received)) {
throw new TestAssertException("Unexpected result for " + msg
+ ".\n\texpected: " + expected
+ "\n\tactual: " + received);
} else if (verbose) {
System.out.println("Got expected " + msg + ": " + received);
}
}
static class CustomXMLFormatter extends XMLFormatter {}
static class Configuration {
final Properties propertyFile;
private Configuration(Properties properties) {
propertyFile = properties;
}
public Configuration apply() {
try {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
propertyFile.store(bytes, testName());
ByteArrayInputStream bais = new ByteArrayInputStream(bytes.toByteArray());
LogManager.getLogManager().readConfiguration(bais);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
return this;
}
public static String useInstantProperty(Class<?> type) {
return type.getName()+".useInstant";
}
public boolean useInstant(XMLFormatter formatter) {
return Boolean.parseBoolean(propertyFile.getProperty(
formatter.getClass().getName()+".useInstant", "true"));
}
public String testName() {
return propertyFile.getProperty("test.name", "????");
}
public static Configuration of(Properties props) {
return new Configuration(props);
}
public static Configuration apply(Properties props) {
return of(props).apply();
}
}
static final List<Properties> properties;
static {
Properties props1 = new Properties();
props1.setProperty("test.name", "with XML nano element (default)");
Properties props2 = new Properties();
props2.setProperty("test.name", "with XML nano element (standard=true, custom=false)");
props2.setProperty(Configuration.useInstantProperty(XMLFormatter.class), "true");
props2.setProperty(Configuration.useInstantProperty(CustomXMLFormatter.class), "false");
Properties props3 = new Properties();
props3.setProperty("test.name", "with XML nano element (standard=false, custom=true)");
props3.setProperty(Configuration.useInstantProperty(XMLFormatter.class), "false");
props3.setProperty(Configuration.useInstantProperty(CustomXMLFormatter.class), "true");
properties = Collections.unmodifiableList(Arrays.asList(
props1,
props2));
}
public static void main(String[] args) throws Exception {
Locale.setDefault(Locale.ENGLISH);
properties.stream().forEach(XmlFormatterNanos::test);
}
static int getNanoAdjustment(LogRecord record) {
return record.getInstant().getNano() % NANOS_IN_MILLI;
}
static void setNanoAdjustment(LogRecord record, int nanos) {
record.setInstant(Instant.ofEpochSecond(record.getInstant().getEpochSecond(),
(record.getInstant().getNano() / NANOS_IN_MILLI) * NANOS_IN_MILLI + nanos));
}
public static void test(Properties props) {
Configuration conf = Configuration.apply(props);
LogRecord record = new LogRecord(Level.INFO, "Test Name: {0}");
record.setLoggerName("test");
record.setParameters(new Object[] {conf.testName()});
int nanos = record.getInstant().getNano() % NANOS_IN_MILLI;
long millis = record.getMillis();
// make sure we don't have leading zeros when printing below
// the second precision
if (millis % MILLIS_IN_SECOND < 100) millis = millis + 100;
// make sure we some nanos - and make sure we don't have
// trailing zeros
if (nanos % 10 == 0) nanos = nanos + 7;
record.setMillis(millis);
setNanoAdjustment(record, nanos);
final Instant instant = record.getInstant();
if (nanos < 0) {
throw new RuntimeException("Unexpected negative nano adjustment: "
+ getNanoAdjustment(record));
}
if (nanos >= NANOS_IN_MILLI) {
throw new RuntimeException("Nano adjustment exceeds 1ms: "
+ getNanoAdjustment(record));
}
if (millis != record.getMillis()) {
throw new RuntimeException("Unexpected millis: " + millis + " != "
+ record.getMillis());
}
if (millis != record.getInstant().toEpochMilli()) {
throw new RuntimeException("Unexpected millis: "
+ record.getInstant().toEpochMilli());
}
long expectedNanos = (millis % MILLIS_IN_SECOND) * NANOS_IN_MILLI + nanos;
assertEquals(expectedNanos, instant.getNano(), "Instant.getNano()");
XMLFormatter formatter = new XMLFormatter();
testMatching(formatter, record, instant, expectedNanos, conf.useInstant(formatter));
XMLFormatter custom = new CustomXMLFormatter();
testMatching(custom, record, instant, expectedNanos, conf.useInstant(custom));
}
private static void testMatching(XMLFormatter formatter,
LogRecord record, Instant instant, long expectedNanos,
boolean useInstant) {
ZonedDateTime zdt = ZonedDateTime.ofInstant(instant, ZoneId.systemDefault());
int zdtNanos = zdt.getNano();
assertEquals(expectedNanos, zdtNanos, "ZonedDateTime.getNano()");
String str = formatter.format(record);
String match = "."+expectedNanos;
if (str.contains(match) != useInstant) {
throw new RuntimeException(formatter.getClass().getSimpleName()
+ ".format()"
+ " string does not contain expected nanos: "
+ "\n\texpected match for: '" + match + "'"
+ "\n\tin: \n" + str);
}
System.out.println("Found expected match for '"+match+"' in \n"+str);
match = "<millis>"+instant.toEpochMilli()+"</millis>";
if (!str.contains(match)) {
throw new RuntimeException(formatter.getClass().getSimpleName()
+ ".format()"
+ " string does not contain expected millis: "
+ "\n\texpected match for: '" + match + "'"
+ "\n\tin: \n" + str);
}
System.out.println("Found expected match for '"+match+"' in \n"+str);
match = "<nanos>";
if (str.contains(match) != useInstant) {
throw new RuntimeException(formatter.getClass().getSimpleName()
+ ".format()"
+ " string "
+ (useInstant
? "does not contain expected nanos: "
: "contains unexpected nanos: ")
+ "\n\t" + (useInstant ? "expected" : "unexpected")
+ " match for: '" + match + "'"
+ "\n\tin: \n" + str);
}
match = "<nanos>"+getNanoAdjustment(record)+"</nanos>";
if (str.contains(match) != useInstant) {
throw new RuntimeException(formatter.getClass().getSimpleName()
+ ".format()"
+ " string "
+ (useInstant
? "does not contain expected nanos: "
: "contains unexpected nanos: ")
+ "\n\t" + (useInstant ? "expected" : "unexpected")
+ " match for: '" + match + "'"
+ "\n\tin: \n" + str);
}
if (useInstant) {
System.out.println("Found expected match for '"+match+"' in \n"+str);
} else {
System.out.println("As expected '"+match+"' is not present in \n"+str);
}
match = useInstant ? DateTimeFormatter.ISO_INSTANT.format(instant)
: zdt.truncatedTo(ChronoUnit.SECONDS)
.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
match = "<date>"+match+"</date>";
if (!str.contains(match)) {
throw new RuntimeException(formatter.getClass().getSimpleName()
+ ".format()"
+ " string does not contain expected date: "
+ "\n\texpected match for: '" + match + "'"
+ "\n\tin: \n" + str);
}
System.out.println("Found expected match for '"+match+"' in \n"+str);
}
}

View file

@ -0,0 +1,96 @@
/*
* Copyright (c) 2019, 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 8216363
* @summary Test that Handler.isLoggable(null) returns false
* @run main/othervm IsLoggableHandlerTest
*/
import java.io.File;
import java.io.IOException;
import java.util.UUID;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.MemoryHandler;
import java.util.logging.StreamHandler;
import java.util.stream.Stream;
public class IsLoggableHandlerTest {
public static void main(String... args) throws IOException {
String userDir = System.getProperty("user.dir", ".");
File logfile = new File(userDir, "IsLoggableHandlerTest_" + UUID.randomUUID() + ".log");
try {
System.out.println("Dummy logfile: " + logfile.getAbsolutePath());
Handler h = new CustomHandler();
testIsLoggable(h);
testIsLoggable(new MemoryHandler(h, 1, Level.ALL));
testIsLoggable(new StreamHandler(System.out, new java.util.logging.SimpleFormatter()));
testIsLoggable(new FileHandler(logfile.getAbsolutePath()));
testIsLoggable(new ConsoleHandler());
} finally {
if (logfile.canRead()) {
try {
System.out.println("Deleting dummy logfile: " + logfile.getAbsolutePath());
logfile.delete();
} catch (Throwable t) {
System.out.println("Warning: failed to delete dummy logfile: " + t);
t.printStackTrace();
}
}
}
}
public static void testIsLoggable(Handler h) {
System.out.println("Testing " + h.getClass().getName());
// should not throw NPE but return false
if (h.isLoggable(null)) {
throw new AssertionError(h.getClass().getName()
+ ": null record should not be loggable");
}
h.setLevel(Level.ALL);
// should still not throw NPE but return false
if (h.isLoggable(null)) {
throw new AssertionError(h.getClass().getName()
+ ": null record should not be loggable");
}
// should not throw NPE
h.publish(null);
}
public static final class CustomHandler extends Handler {
@Override
public void publish(LogRecord record) { }
@Override
public void flush() { }
@Override
public void close() throws SecurityException { }
}
}

View file

@ -0,0 +1,305 @@
/*
* Copyright (c) 2013, 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.*;
import java.lang.management.ManagementFactory;
import java.lang.management.PlatformLoggingMXBean;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.logging.*;
/*
* @test
* @bug 8026027 6543126 8187073
* @modules java.logging
* java.management
* @summary Test Level.parse to look up custom levels by name and its
* localized name, as well as severity.
*
* @run main/othervm CustomLevel
*/
public class CustomLevel extends Level {
public CustomLevel(String name, int value, String resourceBundleName) {
super(name, value, resourceBundleName);
}
private static final List<Level> levels = new ArrayList<>();
private static final String RB_NAME = "myresource";
private static final String OTHERRB_NAME = "myresource2";
private static class CustomLevelReference extends WeakReference<Level> {
final String name;
final int value;
final String resourceBundleName;
public CustomLevelReference(Level level, ReferenceQueue<Level> queue) {
super(level, queue);
name = level.getName();
value = level.intValue();
resourceBundleName = level.getResourceBundleName();
}
@Override
public String toString() {
return "CustomLevelReference(\"" + name + "\", " + value + ", \""
+ resourceBundleName + "\")";
}
}
public static void main(String[] args) throws Exception {
setupCustomLevels();
setUpCustomLevelsOtherLoader();
PlatformLoggingMXBean mxbean = ManagementFactory.getPlatformMXBean(PlatformLoggingMXBean.class);
Logger logger = Logger.getLogger("foo.bar");
// Level.parse will return the custom Level instance
for (Level level : levels) {
final ResourceBundle rb = getResourceBundle(level);
String name = level.getName();
Level l = Level.parse(name);
if (!name.equals("WARNING") && !name.equals("INFO")
&& !name.equals("SEVERE")) {
// custom level whose name doesn't conflict with any standard one
checkCustomLevel(l, level);
} else if (l != Level.WARNING && l != Level.INFO && l != Level.SEVERE
|| !name.equals(l.getName())) {
throw new RuntimeException("Unexpected level " + formatLevel(l));
}
System.out.println("Level.parse found expected level: "
+ formatLevel(l));
String localizedName = rb.getString(level.getName());
l = Level.parse(localizedName);
if (l != level) {
throw new RuntimeException("Unexpected level " + l + " "
+ l.getClass() + " for " + localizedName
+ " in " + rb.getBaseBundleName());
}
l = Level.parse(String.valueOf(level.intValue()));
System.out.println("Level.parse(" + level.intValue() + ") returns " + l);
if (l != level) {
if (l == null || l.intValue() != level.intValue()) {
throw new RuntimeException("Unexpected level " + l
+ (l == null ? "" : (" " + l.getClass()))
+ " for " + level.intValue());
}
}
mxbean.setLoggerLevel(logger.getName(), String.valueOf(level.intValue()));
Level l2 = logger.getLevel();
if (l2 != level) {
if (l2 == null || l2.intValue() != level.intValue()) {
throw new RuntimeException("Unexpected level " + l2
+ (l2 == null ? "" : (" " + l2.getClass()))
+ " for " + level.intValue());
}
}
}
final long otherLevelCount = levels.stream()
.filter(CustomLevel::isCustomLoader)
.count();
// Now verify that custom level instances are correctly
// garbage collected when no longer referenced
ReferenceQueue<Level> queue = new ReferenceQueue<>();
List<CustomLevelReference> refs = new ArrayList<>();
List<CustomLevelReference> customRefs = new ArrayList<>();
int otherLevels = 0;
while (!levels.isEmpty()) {
Level l = levels.stream().findAny().get();
boolean isCustomLoader = isCustomLoader(l);
if (isCustomLoader) otherLevels++;
CustomLevelReference ref = new CustomLevelReference(l, queue);
if (isCustomLoader) {
customRefs.add(ref);
} else {
refs.add(ref);
}
// remove strong references to l
levels.remove(l);
l = null;
// Run gc and wait for garbage collection
if (otherLevels == otherLevelCount) {
if (customRefs.size() != otherLevelCount) {
throw new RuntimeException("Test bug: customRefs.size() != "
+ otherLevelCount);
}
waitForGC(customRefs, queue);
}
}
if (otherLevelCount != otherLevels || otherLevelCount == 0) {
throw new RuntimeException("Test bug: "
+ "no or wrong count of levels loaded from custom loader");
}
if (!customRefs.isEmpty()) {
throw new RuntimeException(
"Test bug: customRefs.size() should be empty!");
}
while (!refs.isEmpty()) {
final Reference<?> ref = refs.remove(0);
if (ref.get() == null) {
throw new RuntimeException("Unexpected garbage collection for "
+ ref);
}
}
}
private static void waitForGC(List<CustomLevelReference> customRefs,
ReferenceQueue<Level> queue)
throws InterruptedException
{
while (!customRefs.isEmpty()) {
Reference<? extends Level> ref2;
do {
System.gc();
Thread.sleep(100);
} while ((ref2 = queue.poll()) == null);
// Check garbage collected reference
if (!customRefs.contains(ref2)) {
throw new RuntimeException("Unexpected reference: " + ref2);
}
CustomLevelReference ref = customRefs.remove(customRefs.indexOf(ref2));
System.out.println(ref2 + " garbage collected");
final String name = ref.name;
Level l;
try {
l = Level.parse(name);
if (!name.equals("SEVERE")
&& !name.equals("INFO")
|| !name.equals(l.getName())) {
throw new RuntimeException("Unexpected level "
+ formatLevel(l));
} else {
if (l == Level.WARNING || l == Level.INFO
|| l == Level.SEVERE) {
System.out.println("Level.parse found expected level: "
+ formatLevel(l));
} else {
throw new RuntimeException("Unexpected level "
+ formatLevel(l));
}
}
} catch (IllegalArgumentException iae) {
if (!name.equals("WARNING")
&& !name.equals("INFO")
&& !name.equals("SEVERE")) {
System.out.println("Level.parse fired expected exception: "
+ iae);
} else {
throw iae;
}
}
}
}
private static boolean isCustomLoader(Level level) {
final ClassLoader cl = level.getClass().getClassLoader();
return cl != null
&& cl != ClassLoader.getPlatformClassLoader()
&& cl != ClassLoader.getSystemClassLoader();
}
static ResourceBundle getResourceBundle(Level level) {
return isCustomLoader(level)
? ResourceBundle.getBundle(OTHERRB_NAME, Locale.getDefault(),
level.getClass().getClassLoader())
: ResourceBundle.getBundle(RB_NAME);
}
private static void setupCustomLevels() throws IOException {
levels.add(new CustomLevel("EMERGENCY", 1090, RB_NAME));
levels.add(new CustomLevel("ALERT", 1060, RB_NAME));
levels.add(new CustomLevel("CRITICAL", 1030, RB_NAME));
levels.add(new CustomLevel("WARNING", 1010, RB_NAME));
levels.add(new CustomLevel("INFO", 1000, RB_NAME));
}
static void setUpCustomLevelsOtherLoader()
throws MalformedURLException,
ClassNotFoundException, NoSuchMethodException,
IllegalAccessException, InvocationTargetException
{
final String classes = System.getProperty("test.classes",
"build/classes");
final String sources = System.getProperty("test.src",
"src");
final URL curl = new File(classes).toURI().toURL();
final URL surl = new File(sources).toURI().toURL();
URLClassLoader loader = new URLClassLoader(new URL[] {curl, surl},
ClassLoader.getPlatformClassLoader());
Class<?> customLevelClass = Class.forName("CustomLevel", false, loader);
Method m = customLevelClass.getMethod("setUpCustomLevelsOtherLoader",
List.class);
m.invoke(null, levels);
}
public static void setUpCustomLevelsOtherLoader(List<Level> levels) {
levels.add(new CustomLevel("OTHEREMERGENCY", 1091, OTHERRB_NAME));
levels.add(new CustomLevel("OTHERALERT", 1061, OTHERRB_NAME));
levels.add(new CustomLevel("OTHERCRITICAL", 1031, OTHERRB_NAME));
levels.add(new CustomLevel("SEVERE", 1011, OTHERRB_NAME));
levels.add(new CustomLevel("INFO", 1000, OTHERRB_NAME));
}
static void checkCustomLevel(Level level, Level expected) {
// Level value must be the same
if (!level.equals(expected)) {
throw new RuntimeException(formatLevel(level) + " != "
+ formatLevel(expected));
}
if (!level.getName().equals(expected.getName())) {
throw new RuntimeException(formatLevel(level) + " != "
+ formatLevel(expected));
}
// Level.parse is expected to return the custom Level
if (level != expected) {
throw new RuntimeException(formatLevel(level) + " != "
+ formatLevel(expected));
}
final ResourceBundle rb = getResourceBundle(level);
String name = rb.getString(level.getName());
if (!level.getLocalizedName().equals(name)) {
// must have the same localized name
throw new RuntimeException(level.getLocalizedName() + " != " + name);
}
}
static String formatLevel(Level l) {
return l + ":" + l.intValue() + ":" + l.getClass().getName();
}
}

View file

@ -0,0 +1,5 @@
EMERGENCY=localized.emergency
ALERT=localized.alert
CRITICAL=localized.critical
WARNING=localized.warning
INFO=localized.info

View file

@ -0,0 +1,5 @@
OTHEREMERGENCY=localized.otheremergency
OTHERALERT=localized.otheralert
OTHERCRITICAL=localized.othercritical
SEVERE=localized.severe
INFO=localized.info.2

View file

@ -0,0 +1,108 @@
/*
* Copyright (c) 2013, 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.util.*;
import java.util.logging.*;
/*
* @test
* @bug 8016127 8024131
* @summary test logging.properties localized
* @modules java.logging/sun.util.logging.resources:open
* @run main/othervm LocalizedLevelName
*/
public class LocalizedLevelName {
private static Object[] namesMap = {
"SEVERE", Locale.ENGLISH, "Severe", Level.SEVERE,
"WARNING", Locale.JAPANESE, "\u8B66\u544A", Level.WARNING,
"INFO", Locale.SIMPLIFIED_CHINESE, "\u4FE1\u606F", Level.INFO,
"SEVERE", Locale.SIMPLIFIED_CHINESE, "\u4E25\u91CD", Level.SEVERE,
"CONFIG", Locale.forLanguageTag("zh-CN"), "\u914D\u7F6E", Level.CONFIG,
"ALL", Locale.ROOT, "All", Level.ALL,
"SEVERE", Locale.ROOT, "Severe", Level.SEVERE,
"WARNING", Locale.ROOT, "Warning", Level.WARNING,
"CONFIG", Locale.ROOT, "Config", Level.CONFIG,
"INFO", Locale.ROOT, "Info", Level.INFO,
"FINE", Locale.ROOT, "Fine", Level.FINE,
"FINER", Locale.ROOT, "Finer", Level.FINER,
"FINEST", Locale.ROOT, "Finest", Level.FINEST
};
public static void main(String args[]) throws Exception {
Locale defaultLocale = Locale.getDefault();
for (int i=0; i<namesMap.length; i += 4) {
Locale.setDefault(Locale.US);
final String key = (String) namesMap[i];
final Locale locale = (Locale) namesMap[i+1];
final String expectedTranslation = (String) namesMap[i+2];
final Level level = (Level) namesMap[i+3];
final String en = getLocalizedMessage(Locale.ENGLISH, key);
final String other = getLocalizedMessage(locale, key);
System.out.println(locale + ": " + key + "=" + expectedTranslation
+ ", (Level." + level.getName() + ")");
System.out.println(" => localized(" + Locale.ENGLISH + ", "
+ key + ")=" + en);
System.out.println(" => localized(" + locale + ", " + key
+ ")=" + other);
if (!key.equals(en.toUpperCase(Locale.ROOT))) {
throw new RuntimeException("Expect " + key
+ " equals upperCase(" + en + ")");
}
if (!Locale.ENGLISH.equals(locale) && !Locale.ROOT.equals(locale)
&& key.equals(other.toUpperCase(Locale.ROOT))) {
throw new RuntimeException("Expect " + key
+ " not equals upperCase(" + other +")");
}
if ((Locale.ENGLISH.equals(locale) || Locale.ROOT.equals(locale))
&& !key.equals(other.toUpperCase(Locale.ROOT))) {
throw new RuntimeException("Expect " + key
+ " equals upperCase(" + other +")");
}
if (!other.equals(expectedTranslation)) {
throw new RuntimeException("Expected \"" + expectedTranslation
+ "\" for '" + locale + "' but got \"" + other + "\"");
}
Locale.setDefault(locale);
final String levelName = level.getLocalizedName();
System.out.println("Level.getLocalizedName() is: " + levelName);
if (!levelName.equals(other.toUpperCase(locale))) {
throw new RuntimeException("Expected \""
+ other.toUpperCase(locale) + "\" for '"
+ locale + "' but got \"" + levelName + "\"");
}
Locale.setDefault(defaultLocale);
}
}
private static final String RBNAME = "sun.util.logging.resources.logging";
private static String getLocalizedMessage(Locale locale, String key) {
// this test verifies if the logging.properties in the java.logging module
// is localized.
Module module = java.util.logging.Level.class.getModule();
ResourceBundle rb = ResourceBundle.getBundle(RBNAME, locale, module);
return rb.getString(key);
}
}

View file

@ -0,0 +1,164 @@
/*
* 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.io.UnsupportedEncodingException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Properties;
import java.util.logging.LogManager;
/**
* @test
* @bug 8075810
* @run main/othervm InvalidEscapeConfigurationTest
* @author danielfuchs
*/
public class InvalidEscapeConfigurationTest {
public static void main(String[] args)
throws UnsupportedEncodingException, IOException {
String[] validEscapes = {
"com.f\\u006fo.level = INF\\u004f",
"com.f\\u006fo.level = INFO",
"com.foo.level = INF\\u004f"
};
String[] invalidEscapes = {
"com.fo\\u0O6f.level = INF\\u0O4f",
"com.fo\\u0O6f.level = INFO",
"com.foo.level = INF\\u0O4f"
};
for (String line : validEscapes) {
test(line, true);
}
for (String line : invalidEscapes) {
test(line, false);
}
try {
Properties props = new Properties();
props.load((InputStream)null);
throw new RuntimeException("Properties.load(null): "
+ "NullPointerException exception not raised");
} catch (NullPointerException x) {
System.out.println("Properties.load(null): "
+ "got expected exception: " + x);
}
try {
LogManager.getLogManager().readConfiguration(null);
throw new RuntimeException("LogManager.readConfiguration(null): "
+ "NullPointerException exception not raised");
} catch (NullPointerException x) {
System.out.println("LogManager.readConfiguration(null): "
+ "got expected exception: " + x);
}
}
public static void test(String line, boolean valid) throws IOException {
String test = (valid ? "valid" : "invalid")
+ " line \"" +line + "\"";
System.out.println("Testing " + test);
// First verify that we get the expected result from Properties.load()
try {
ByteArrayInputStream bais =
new ByteArrayInputStream(line.getBytes("UTF-8"));
Properties props = new Properties();
props.load(bais);
if (!valid) {
throw new RuntimeException(test
+ "\n\tProperties.load: expected exception not raised");
} else {
System.out.println("Properties.load passed for " + test);
}
} catch(IllegalArgumentException x) {
if (!valid) {
System.out.println(
"Properties.load: Got expected exception: "
+ x + "\n\tfor " + test);
} else {
throw x;
}
}
// Then verify that we get the expected result from
// LogManager.readConfiguration
try {
String content = defaultConfiguration() + '\n' + line + '\n';
ByteArrayInputStream bais =
new ByteArrayInputStream(content.getBytes("UTF-8"));
LogManager.getLogManager().readConfiguration(bais);
if (!valid) {
throw new RuntimeException(test
+ "\n\tLogManager.readConfiguration: "
+ "expected exception not raised");
} else {
System.out.println("LogManager.readConfiguration passed for "
+ test);
}
} catch(IOException x) {
if (!valid) {
System.out.println(
"LogManager.readConfiguration: Got expected exception: "
+ x + "\n\tfor " + test);
} else {
throw x;
}
}
}
static String getConfigurationFileName() {
String fname = System.getProperty("java.util.logging.config.file");
if (fname == null) {
fname = System.getProperty("java.home");
if (fname == null) {
throw new Error("Can't find java.home ??");
}
fname = Paths.get(fname, "conf", "logging.properties")
.toAbsolutePath().normalize().toString();
}
return fname;
}
static String defaultConfiguration() throws IOException {
Properties props = new Properties();
String fileName = getConfigurationFileName();
if (Files.exists(Paths.get(fileName))) {
try (InputStream is = new FileInputStream(fileName);) {
props.load(is);
} catch(IOException x) {
throw new UncheckedIOException(x);
}
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
props.store(bos, null);
return bos.toString();
}
}

View file

@ -0,0 +1,384 @@
/*
* Copyright (c) 2014, 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.LogManager;
import java.util.logging.Logger;
/**
* @test
* @bug 8060132
* @summary tests that FileHandlers configured on abstract nodes in logging.properties
* will be closed by reset().
* @run main/othervm ParentLoggerWithHandlerGC
* @author danielfuchs
* @key randomness
*/
public class ParentLoggerWithHandlerGC {
// We will test the handling of abstract logger nodes with file handlers
public static void run(Properties propertyFile) throws Exception {
Configure.setUp(propertyFile);
test(propertyFile.getProperty("test.name"), propertyFile);
}
private static final String PREFIX =
"FileHandler-" + UUID.randomUUID() + ".log";
private static final String userDir = System.getProperty("user.dir", ".");
private static final boolean userDirWritable = Files.isWritable(Paths.get(userDir));
static enum ConfigMode { DEFAULT, ENSURE_CLOSE_ON_RESET_TRUE, ENSURE_CLOSE_ON_RESET_FALSE }
private static final List<Properties> properties;
static {
Properties props1 = new Properties();
props1.setProperty("test.name", "parent logger with handler");
props1.setProperty("test.config.mode", ConfigMode.DEFAULT.name());
props1.setProperty(FileHandler.class.getName() + ".pattern", PREFIX);
props1.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE));
props1.setProperty(FileHandler.class.getName() + ".level", "ALL");
props1.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter");
props1.setProperty("com.foo.handlers", FileHandler.class.getName());
props1.setProperty("com.bar.level", "FINEST");
Properties props2 = new Properties();
props2.setProperty("test.name", "parent logger with handler");
props2.setProperty("test.config.mode", ConfigMode.ENSURE_CLOSE_ON_RESET_TRUE.name());
props2.setProperty(FileHandler.class.getName() + ".pattern", PREFIX);
props2.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE));
props2.setProperty(FileHandler.class.getName() + ".level", "ALL");
props2.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter");
props2.setProperty("com.foo.handlers", FileHandler.class.getName());
props2.setProperty("com.foo.handlers.ensureCloseOnReset", "true");
props2.setProperty("com.bar.level", "FINEST");
Properties props3 = new Properties();
props3.setProperty("test.name", "parent logger with handler");
props3.setProperty("test.config.mode", ConfigMode.ENSURE_CLOSE_ON_RESET_FALSE.name());
props3.setProperty(FileHandler.class.getName() + ".pattern", PREFIX);
props3.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE));
props3.setProperty(FileHandler.class.getName() + ".level", "ALL");
props3.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter");
props3.setProperty("com.foo.handlers", FileHandler.class.getName());
props3.setProperty("com.foo.handlers.ensureCloseOnReset", "false");
props3.setProperty("com.bar.level", "FINEST");
properties = Collections.unmodifiableList(Arrays.asList(
props1, props2, props3));
}
public static void main(String... args) throws Exception {
try {
for (Properties propertyFile : properties) {
run(propertyFile);
}
} finally {
if (userDirWritable) {
Configure.doPrivileged(() -> {
// cleanup - delete files that have been created
try {
Files.list(Paths.get(userDir))
.filter((f) -> f.toString().contains(PREFIX))
.forEach((f) -> {
try {
System.out.println("deleting " + f);
Files.delete(f);
} catch(Throwable t) {
System.err.println("Failed to delete " + f + ": " + t);
}
});
} catch(Throwable t) {
System.err.println("Cleanup failed to list files: " + t);
t.printStackTrace();
}
});
}
}
}
static class Configure {
static void setUp(Properties propertyFile) {
doPrivileged(() -> {
try {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
propertyFile.store(bytes, propertyFile.getProperty("test.name"));
ByteArrayInputStream bais = new ByteArrayInputStream(bytes.toByteArray());
LogManager.getLogManager().readConfiguration(bais);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
});
}
static void doPrivileged(Runnable run) {
run.run();
}
static <T> T callPrivileged(Callable<T> call) throws Exception {
return call.call();
}
}
@FunctionalInterface
public static interface FileHandlerSupplier {
public FileHandler test() throws Exception;
}
static final class TestAssertException extends RuntimeException {
TestAssertException(String msg) {
super(msg);
}
}
private static void assertEquals(long expected, long received, String msg) {
if (expected != received) {
throw new TestAssertException("Unexpected result for " + msg
+ ".\n\texpected: " + expected
+ "\n\tactual: " + received);
} else {
System.out.println("Got expected " + msg + ": " + received);
}
}
public static void test(String name, Properties props) throws Exception {
ConfigMode configMode = ConfigMode.valueOf(props.getProperty("test.config.mode"));
System.out.println("\nTesting: " + name + " mode=" + configMode);
if (!userDirWritable) {
throw new RuntimeException("Not writable: "+userDir);
}
switch(configMode) {
case DEFAULT:
case ENSURE_CLOSE_ON_RESET_TRUE:
testCloseOnResetTrue(name, props); break;
case ENSURE_CLOSE_ON_RESET_FALSE:
testCloseOnResetFalse(name, props); break;
default:
throw new RuntimeException("Unknwown mode: " + configMode);
}
}
// Test a configuration which has either
// com.foo.handlers.ensureCloseOnReset=true, or where
// com.foo.handlers.ensureCloseOnReset is not specified.
public static void testCloseOnResetTrue(String name, Properties props)
throws Exception {
Logger fooChild = Logger.getLogger("com.foo.child");
fooChild.info("hello world");
Logger barChild = Logger.getLogger("com.bar.child");
barChild.info("hello world");
ReferenceQueue<Logger> queue = new ReferenceQueue();
WeakReference<Logger> fooRef = new WeakReference<>(Logger.getLogger("com.foo"), queue);
if (fooRef.get() != fooChild.getParent()) {
throw new RuntimeException("Unexpected parent logger: "
+ fooChild.getParent() +"\n\texpected: " + fooRef.get());
}
WeakReference<Logger> barRef = new WeakReference<>(Logger.getLogger("com.bar"), queue);
if (barRef.get() != barChild.getParent()) {
throw new RuntimeException("Unexpected parent logger: "
+ barChild.getParent() +"\n\texpected: " + barRef.get());
}
fooChild = barChild = null;
Reference<? extends Logger> ref2 = null;
while ((ref2 = queue.poll()) == null) {
System.gc();
Thread.sleep(1000);
}
Throwable failed = null;
try {
do {
if (ref2 != barRef) {
throw new RuntimeException("Unexpected reference: "
+ ref2 +"\n\texpected: " + barRef);
}
if (ref2.get() != null) {
throw new RuntimeException("Referent not cleared: "
+ ref2.get());
}
System.out.println("Got barRef");
System.gc();
Thread.sleep(1000);
} while( (ref2 = queue.poll()) != null);
System.out.println("Parent logger GCed");
} catch(Throwable t) {
failed = t;
} finally {
final Throwable suppressed = failed;
Configure.doPrivileged(() -> LogManager.getLogManager().reset());
Configure.doPrivileged(() -> {
try {
StringBuilder builder = new StringBuilder();
Files.list(Paths.get(userDir))
.filter((f) -> f.toString().contains(PREFIX))
.filter((f) -> f.toString().endsWith(".lck"))
.forEach((f) -> {
builder.append(f.toString()).append('\n');
});
if (!builder.toString().isEmpty()) {
throw new RuntimeException("Lock files not cleaned:\n"
+ builder.toString());
}
} catch(RuntimeException | Error x) {
if (suppressed != null) x.addSuppressed(suppressed);
throw x;
} catch(Exception x) {
if (suppressed != null) x.addSuppressed(suppressed);
throw new RuntimeException(x);
}
});
while ((ref2 = queue.poll()) == null) {
System.gc();
Thread.sleep(1000);
}
if (ref2 != fooRef) {
throw new RuntimeException("Unexpected reference: "
+ ref2 +"\n\texpected: " + fooRef);
}
if (ref2.get() != null) {
throw new RuntimeException("Referent not cleared: " + ref2.get());
}
System.out.println("Got fooRef after reset()");
}
if (failed != null) {
// should rarely happen...
throw new RuntimeException(failed);
}
}
private static Handler getHandlerToClose() throws Exception {
return Configure.callPrivileged(
() -> Logger.getLogger("com.foo.child").getParent().getHandlers()[0]);
}
// Test a configuration which has com.foo.handlers.ensureCloseOnReset=false
public static void testCloseOnResetFalse(String name, Properties props)
throws Exception {
Logger fooChild = Logger.getLogger("com.foo.child");
fooChild.info("hello world");
Logger barChild = Logger.getLogger("com.bar.child");
barChild.info("hello world");
Handler toClose = getHandlerToClose();
ReferenceQueue<Logger> queue = new ReferenceQueue();
WeakReference<Logger> fooRef = new WeakReference<>(Logger.getLogger("com.foo"), queue);
if (fooRef.get() != fooChild.getParent()) {
throw new RuntimeException("Unexpected parent logger: "
+ fooChild.getParent() +"\n\texpected: " + fooRef.get());
}
WeakReference<Logger> barRef = new WeakReference<>(Logger.getLogger("com.bar"), queue);
if (barRef.get() != barChild.getParent()) {
throw new RuntimeException("Unexpected parent logger: "
+ barChild.getParent() +"\n\texpected: " + barRef.get());
}
fooChild = barChild = null;
Reference<? extends Logger> ref2 = null;
Set<WeakReference<Logger>> expectedRefs = new HashSet<>(Arrays.asList(fooRef, barRef));
Throwable failed = null;
try {
int l=0;
while (failed == null && !expectedRefs.isEmpty()) {
int max = 60;
while ((ref2 = queue.poll()) == null) {
if (l > 0 && max-- <= 0) {
throw new RuntimeException("Logger #2 not GC'ed!"
+ " max too short (max=60) or "
+ "com.foo.handlers.ensureCloseOnReset=false"
+ " does not work");
}
System.gc();
Thread.sleep(1000);
}
do {
if (!expectedRefs.contains(ref2)) {
throw new RuntimeException("Unexpected reference: "
+ ref2 +"\n\texpected: " + expectedRefs);
}
if (ref2.get() != null) {
throw new RuntimeException("Referent not cleared: "
+ ref2.get());
}
expectedRefs.remove(ref2);
System.out.println("Got "+
(ref2 == barRef ? "barRef"
: (ref2 == fooRef ? "fooRef"
: ref2.toString())));
System.gc();
Thread.sleep(1000);
System.out.println("Logger #" + (++l) + " GCed");
} while( (ref2 = queue.poll()) != null);
}
} catch(Throwable t) {
failed = t;
} finally {
final Throwable suppressed = failed;
Configure.doPrivileged(() -> LogManager.getLogManager().reset());
Configure.doPrivileged(() -> {
try {
toClose.close();
StringBuilder builder = new StringBuilder();
Files.list(Paths.get(userDir))
.filter((f) -> f.toString().contains(PREFIX))
.filter((f) -> f.toString().endsWith(".lck"))
.forEach((f) -> {
builder.append(f.toString()).append('\n');
});
if (!builder.toString().isEmpty()) {
throw new RuntimeException("Lock files not cleaned:\n" + builder.toString());
}
} catch(RuntimeException | Error x) {
if (suppressed != null) x.addSuppressed(suppressed);
throw x;
} catch(Exception x) {
if (suppressed != null) x.addSuppressed(suppressed);
throw new RuntimeException(x);
}
});
}
if (failed != null) {
// should rarely happen...
throw new RuntimeException(failed);
}
}
}

View file

@ -0,0 +1,435 @@
/*
* 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.File;
import java.io.IOException;
import java.lang.management.LockInfo;
import java.lang.management.ManagementFactory;
import java.lang.management.MonitorInfo;
import java.lang.management.ThreadInfo;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
/**
* @test
* @bug 8077846
* @key randomness
* @summary Test that using a reentrant configuration lock does not introduce
* new synchronization issues in Logger and LogManager. This test
* focuses more particularly on potential deadlock in
* drainLoggerRefQueueBounded / readConfiguration / reset
* todo: add at randomness
* @modules java.logging
* java.management
* @run main/othervm TestConfigurationLock
* @author danielfuchs
*/
// This test is a best effort to try & detect issues. The test itself will run
// for 8secs. This might be unsufficient to detect issues.
// To get a greater confidence it is recommended to run this test in a loop:
// e.g. use something like:
// $ while jtreg -jdk:$JDK -verbose:all \
// test/java/util/logging/TestConfigurationLock.java ; \
// do echo Running test again ; done
// and let it run for a few hours...
//
public class TestConfigurationLock {
static volatile Exception thrown = null;
static volatile boolean goOn = true;
static volatile boolean deadlock = false;
static final double CONFSYNCTHRESHOLD = 0.3;
static final double LOGSYNCTHRESHOLD = 0.3;
static final int RESETERS = 0;
static final int READERS = 3;
static final int LOGGERS = 4;
static final long TIME = 8 * 1000; // 8 sec.
static final long STEP = 1 * 1000; // message every 1 sec.
static final int LCOUNT = 50; // 50 loggers created in a row...
static final AtomicLong nextLogger = new AtomicLong(0);
static final AtomicLong resetCount = new AtomicLong(0);
static final AtomicLong readCount = new AtomicLong(0);
static final AtomicLong checkCount = new AtomicLong(0);
static final String BLAH = "blah";
static Object fakeConfExternalLock() {
return LogManager.getLogManager();
}
static Object fakeLogExternalLock() {
return LogManager.getLogManager();
}
/**
* The test starts a number of threads that will call
* LogManager.reset() concurrently (ResetConf), and a number of threads
* that will call readConfiguration() (ReadConf), and then starts a
* number of threads that will create new loggers concurrently
* (AddLogger), and finally two additional threads:
* - one (Stopper) that will stop the test after 4secs (TIME ms),
* - and one DeadlockDetector that will attempt to detect deadlocks.
* If after 4secs no deadlock was detected and no exception was thrown
* then the test is considered a success and passes.
*
* Note that 4sec may not be enough to detect issues if there are some.
* This is a best effort test.
*
* @param args the command line arguments
* @throws java.lang.Exception if the test fails
*/
public static void main(String[] args) throws Exception {
File conf = new File(System.getProperty("test.src", "./src"),
TestConfigurationLock.class.getSimpleName() + ".properties");
if (!conf.canRead()) {
throw new IOException("Can't read config file: " + conf.getAbsolutePath());
}
System.setProperty("java.util.logging.config.file", conf.getAbsolutePath());
test();
}
/**
* Starts all threads, wait 4secs, then stops all threads.
* @throws Exception if a deadlock was detected or an error occurred.
*/
public static void test() throws Exception {
goOn = true;
thrown = null;
long sNextLogger = nextLogger.get();
long sUpdateCount = resetCount.get();
long sReadCount = readCount.get();
long sCheckCount = checkCount.get();
List<Thread> threads = new ArrayList<>();
for (int i = 0; i<RESETERS; i++) {
threads.add(new ResetConf());
}
for (int i = 0; i<READERS; i++) {
threads.add(new ReadConf());
}
for (int i = 0; i<LOGGERS; i++) {
threads.add(new AddLogger());
}
threads.add(0, new Stopper(TIME));
threads.stream().forEach(Thread::start);
Thread deadLockDetector = new DeadlockDetector();
deadLockDetector.start();
deadLockDetector.join();
if (!deadlock) {
threads.stream().forEach(TestConfigurationLock::join);
} else {
System.err.println("Deadlock found: exiting forcibly.");
Runtime.getRuntime().halt(-1);
}
if (thrown != null) {
throw thrown;
}
System.out.println("Passed: " + (nextLogger.get() - sNextLogger)
+ " loggers created by " + LOGGERS + " Thread(s),");
System.out.println("\t LogManager.reset() called "
+ (resetCount.get() - sUpdateCount) + " times by " + RESETERS
+ " Thread(s).");
System.out.println("\t LogManager.readConfiguration() called "
+ (readCount.get() - sReadCount) + " times by " + READERS
+ " Thread(s).");
System.out.println("\t ThreadMXBean.findDeadlockedThreads called "
+ (checkCount.get() -sCheckCount) + " times by 1 Thread.");
}
static void join(Thread t) {
try {
t.join();
} catch (Exception x) {
fail(x);
}
}
static final class ResetConf extends Thread {
public ResetConf() {
setDaemon(true);
}
@Override
public void run() {
while (goOn) {
try {
if (Math.random() > CONFSYNCTHRESHOLD) {
// calling reset while holding a lock can increase
// deadlock probability...
synchronized(fakeConfExternalLock()) {
LogManager.getLogManager().reset();
}
} else {
LogManager.getLogManager().reset();
}
Logger blah = Logger.getLogger(BLAH);
blah.setLevel(Level.FINEST);
blah.fine(BLAH);
resetCount.incrementAndGet();
pause(1);
} catch (Exception x) {
fail(x);
}
}
}
}
static final class ReadConf extends Thread {
public ReadConf() {
setDaemon(true);
}
@Override
public void run() {
while (goOn) {
try {
if (Math.random() > CONFSYNCTHRESHOLD) {
// calling readConfiguration while holding a lock can
// increase deadlock probability...
synchronized(fakeConfExternalLock()) {
LogManager.getLogManager().readConfiguration();
}
} else {
LogManager.getLogManager().readConfiguration();
}
Logger blah = Logger.getLogger(BLAH);
blah.setLevel(Level.FINEST);
blah.fine(BLAH);
readCount.incrementAndGet();
pause(1);
} catch (Exception x) {
fail(x);
}
}
}
}
static final class AddLogger extends Thread {
public AddLogger() {
setDaemon(true);
}
@Override
public void run() {
try {
while (goOn) {
Logger l;
Logger foo = Logger.getLogger("foo");
Logger bar = Logger.getLogger("foo.bar");
for (int i=0; i < LCOUNT ; i++) {
LogManager manager = LogManager.getLogManager();
if (Math.random() > LOGSYNCTHRESHOLD) {
synchronized(fakeLogExternalLock()) {
l = Logger.getLogger("foo.bar.l"+nextLogger.incrementAndGet());
}
} else {
l = Logger.getLogger("foo.bar.l"+nextLogger.incrementAndGet());
}
l.setLevel(Level.FINEST);
l.fine("I'm fine");
if (!goOn) break;
pause(1);
}
}
} catch (InterruptedException | RuntimeException x ) {
fail(x);
}
}
}
static final class DeadlockDetector extends Thread {
@Override
public void run() {
boolean deadlock = false;
while(goOn) {
try {
long[] ids = ManagementFactory.getThreadMXBean().findDeadlockedThreads();
checkCount.incrementAndGet();
ids = ids == null ? new long[0] : ids;
if (ids.length == 1) {
throw new RuntimeException("Found 1 deadlocked thread: "+ids[0]);
} else if (ids.length > 0) {
deadlock = true;
ThreadInfo[] infos = ManagementFactory.getThreadMXBean()
.getThreadInfo(ids, true, true);
System.err.println("Found "+ids.length+" deadlocked threads: ");
for (ThreadInfo inf : infos) {
System.err.println(asString(inf));
}
throw new RuntimeException("Found "+ids.length+" deadlocked threads");
}
pause(100);
} catch(InterruptedException | RuntimeException x) {
if (deadlock) deadlock(x);
else fail(x);
}
}
}
}
static final class Stopper extends Thread {
long start;
long time;
Stopper(long time) {
start = System.currentTimeMillis();
this.time = time;
setDaemon(true);
}
@Override
public void run() {
try {
long rest, previous;
int msgCount = 0;
previous = time;
Logger logger = Logger.getLogger("remaining");
while (goOn && (rest = start - System.currentTimeMillis() + time) > 0) {
if (previous == time || previous - rest >= STEP) {
logger.log(Level.INFO, "{0}ms remaining...", String.valueOf(rest));
msgCount++;
previous = rest == time ? rest -1 : rest;
System.gc();
}
if (goOn == false) break;
pause(Math.min(rest, 100));
}
System.err.println(this + ": " + msgCount + " messages.");
System.err.flush();
System.out.println(System.currentTimeMillis() - start
+ " ms elapsed ("+time+ " requested)");
goOn = false;
} catch(InterruptedException | RuntimeException x) {
fail(x);
}
}
}
// ThreadInfo.toString() only prints 8 frames...
static String asString(ThreadInfo inf) {
StringBuilder sb = new StringBuilder();
sb.append("\"").append(inf.getThreadName()).append("\"")
.append(inf.isDaemon() ? " daemon" : "")
.append(" prio=").append(inf.getPriority())
.append(" Id=").append(inf.getThreadId())
.append(" ").append(inf.getThreadState());
if (inf.getLockName() != null) {
sb.append(" on ").append(inf.getLockName());
}
if (inf.getLockOwnerName() != null) {
sb.append(" owned by \"").append(inf.getLockOwnerName())
.append("\" Id=").append(inf.getLockOwnerId());
}
if (inf.isSuspended()) {
sb.append(" (suspended)");
}
if (inf.isInNative()) {
sb.append(" (in native)");
}
sb.append('\n');
int i = 0;
StackTraceElement[] stackTrace = inf.getStackTrace();
for (; i < stackTrace.length; i++) {
StackTraceElement ste = stackTrace[i];
sb.append("\tat ").append(ste.toString());
sb.append('\n');
if (i == 0 && inf.getLockInfo() != null) {
Thread.State ts = inf.getThreadState();
switch (ts) {
case BLOCKED:
sb.append("\t- blocked on ").append(inf.getLockInfo());
sb.append('\n');
break;
case WAITING:
sb.append("\t- waiting on ").append(inf.getLockInfo());
sb.append('\n');
break;
case TIMED_WAITING:
sb.append("\t- waiting on ").append(inf.getLockInfo());
sb.append('\n');
break;
default:
}
}
for (MonitorInfo mi : inf.getLockedMonitors()) {
if (mi.getLockedStackDepth() == i) {
sb.append("\t- locked ").append(mi);
sb.append('\n');
}
}
}
if (i < stackTrace.length) {
sb.append("\t...");
sb.append('\n');
}
LockInfo[] locks = inf.getLockedSynchronizers();
if (locks.length > 0) {
sb.append("\n\tNumber of locked synchronizers = ").append(locks.length);
sb.append('\n');
for (LockInfo li : locks) {
sb.append("\t- ").append(li);
sb.append('\n');
}
}
sb.append('\n');
return sb.toString();
}
static void pause(long millis) throws InterruptedException {
Thread.sleep(millis);
}
static void fail(Exception x) {
x.printStackTrace(System.err);
if (thrown == null) {
thrown = x;
}
goOn = false;
}
static void deadlock(Exception x) {
deadlock = true;
System.out.flush();
fail(x);
System.err.flush();
}
}

View file

@ -0,0 +1,20 @@
########################################################################
# Logging configuration property file for TestConfigurationLock.java #
########################################################################
handlers= java.util.logging.ConsoleHandler
.level= INFO
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
blah.level = FINE
foo.bar.l10.level = INFO
foo.bar.l100.level = INFO
foo.bar.l1000.level = INFO

View file

@ -0,0 +1,301 @@
/*
* 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.
*/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @test
* @bug 8191033
* @build custom.DotHandler custom.Handler
* @run main/othervm -Dlogging.properties=badlogging.properties -Dclz=1custom.DotHandler BadRootLoggerHandlers CUSTOM
* @run main/othervm -Dlogging.properties=badlogging.properties -Dclz=1custom.DotHandler BadRootLoggerHandlers DEFAULT
* @run main/othervm -Dlogging.properties=badglobal.properties -Dclz=1custom.GlobalHandler BadRootLoggerHandlers CUSTOM
* @run main/othervm -Dlogging.properties=badglobal.properties -Dclz=1custom.GlobalHandler BadRootLoggerHandlers DEFAULT
* @author danielfuchs
*/
public class BadRootLoggerHandlers {
public static final Path SRC_DIR =
Paths.get(System.getProperty("test.src", "src"));
public static final Path USER_DIR =
Paths.get(System.getProperty("user.dir", "."));
public static final Path CONFIG_FILE = Paths.get(
Objects.requireNonNull(System.getProperty("logging.properties")));
public static final String BAD_HANDLER_NAME =
Objects.requireNonNull(System.getProperty("clz"));
static enum TESTS { CUSTOM, DEFAULT}
public static final class CustomLogManager extends LogManager {
final ConcurrentMap<String, Logger> loggers = new ConcurrentHashMap<>();
@Override
public boolean addLogger(Logger logger) {
return loggers.putIfAbsent(logger.getName(), logger) == null;
}
@Override
public Enumeration<String> getLoggerNames() {
return Collections.enumeration(loggers.keySet());
}
@Override
public Logger getLogger(String name) {
return loggers.get(name);
}
}
public static class SystemErr extends OutputStream {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final OutputStream wrapped;
public SystemErr(OutputStream out) {
this.wrapped = out;
}
@Override
public void write(int b) throws IOException {
baos.write(b);
wrapped.write(b);
}
public void close() throws IOException {
flush();
super.close();
}
public void flush() throws IOException {
super.flush();
wrapped.flush();
}
}
// Uncomment this to run the test on Java 8. Java 8 does not have
// List.of(...)
// static final class List {
// static <T> java.util.List<T> of(T... items) {
// return Collections.unmodifiableList(Arrays.asList(items));
// }
// }
public static void main(String[] args) throws IOException {
Path initialProps = SRC_DIR.resolve(CONFIG_FILE);
Path loggingProps = USER_DIR.resolve(CONFIG_FILE);
if (args.length != 1) {
throw new IllegalArgumentException("expected (only) one of " + List.of(TESTS.values()));
}
TESTS test = TESTS.valueOf(args[0]);
System.setProperty("java.util.logging.config.file", loggingProps.toString());
if (test == TESTS.CUSTOM) {
System.setProperty("java.util.logging.manager", CustomLogManager.class.getName());
}
Files.copy(initialProps, loggingProps, StandardCopyOption.REPLACE_EXISTING);
loggingProps.toFile().setWritable(true);
SystemErr err = new SystemErr(System.err);
System.setErr(new PrintStream(err));
System.out.println("Root level is: " + Logger.getLogger("").getLevel());
if (Logger.getLogger("").getLevel() != Level.INFO) {
throw new RuntimeException("Expected root level INFO, got: "
+ Logger.getLogger("").getLevel());
}
Class<? extends LogManager> logManagerClass =
LogManager.getLogManager().getClass();
Class<? extends LogManager> expectedClass =
test == TESTS.CUSTOM ? CustomLogManager.class : LogManager.class;
if (logManagerClass != expectedClass) {
throw new RuntimeException("Bad class for log manager: " + logManagerClass
+ " expected " + expectedClass + " for " + test);
}
if (test == TESTS.DEFAULT) {
// Verify that we have two handlers. One was configured with
// handlers=custom.Handler, the other with
// .handlers=custom.DotHandler
// Verify that exactly one of the two handlers is a custom.Handler
// Verify that exactly one of the two handlers is a custom.DotHandler
// Verify that the two handlers have an id of '1'
checkHandlers(Logger.getLogger(""),
Logger.getLogger("").getHandlers(),
1L,
custom.Handler.class,
custom.DotHandler.class);
} else {
// Verify that we have one handler, configured with
// handlers=custom.Handler.
// Verify that it is a custom.Handler
// Verify that the handler have an id of '1'
checkHandlers(Logger.getLogger(""),
Logger.getLogger("").getHandlers(),
1L,
custom.Handler.class);
}
// DEFAULT: The log message "hi" should appear twice on the console.
// CUSTOM: The log message "hi" should appear twice on the console.
// We don't check that. This is just for log analysis in case
// of test failure.
Logger.getAnonymousLogger().info("hi (" + test +")");
// Change the root logger level to FINE in the properties file
// and reload the configuration.
Files.write(loggingProps,
Files.lines(initialProps)
.map((s) -> s.replace("INFO", "FINE"))
.collect(Collectors.toList()));
LogManager.getLogManager().readConfiguration();
System.out.println("Root level is: " + Logger.getLogger("").getLevel());
if (Logger.getLogger("").getLevel() != Level.FINE) {
throw new RuntimeException("Expected root level FINE, got: "
+ Logger.getLogger("").getLevel());
}
// Verify that we have now only one handler, configured with
// handlers=custom.Handler, and that the other configured with
// .handlers=custom.DotHandler was ignored.
// Verify that the handler is a custom.Handler
// Verify that the handler has an id of '2'
checkHandlers(Logger.getLogger(""),
Logger.getLogger("").getHandlers(),
2L,
custom.Handler.class);
// The log message "there" should appear only once on the console.
// We don't check that. This is just for log analysis in case
// of test failure.
Logger.getAnonymousLogger().info("there!");
// Change the root logger level to FINER in the properties file
// and reload the configuration.
Files.write(loggingProps,
Files.lines(initialProps)
.map((s) -> s.replace("INFO", "FINER"))
.collect(Collectors.toList()));
LogManager.getLogManager().readConfiguration();
System.out.println("Root level is: " + Logger.getLogger("").getLevel());
if (Logger.getLogger("").getLevel() != Level.FINER) {
throw new RuntimeException("Expected root level FINER, got: "
+ Logger.getLogger("").getLevel());
}
// Verify that we have only one handler, configured with
// handlers=custom.Handler, and that the other configured with
// .handlers=custom.DotHandler was ignored.
// Verify that the handler is a custom.Handler
// Verify that the handler has an id of '3'
checkHandlers(Logger.getLogger(""),
Logger.getLogger("").getHandlers(),
3L,
custom.Handler.class);
// The log message "done" should appear only once on the console.
// We don't check that. This is just for log analysis in case
// of test failure.
Logger.getAnonymousLogger().info("done!");
byte[] errBytes = err.baos.toByteArray();
String errText = new String(errBytes);
switch(test) {
case CUSTOM:
if (errText.contains("java.lang.ClassNotFoundException: "
+ BAD_HANDLER_NAME)) {
throw new RuntimeException("Error message found on System.err");
}
System.out.println("OK: ClassNotFoundException error message not found for " + test);
break;
case DEFAULT:
if (!errText.contains("java.lang.ClassNotFoundException: "
+ BAD_HANDLER_NAME)) {
throw new RuntimeException("Error message not found on System.err");
}
System.err.println("OK: ClassNotFoundException error message found for " + test);
break;
default:
throw new InternalError("unknown test case: " + test);
}
}
static void checkHandlers(Logger logger, Handler[] handlers, Long expectedID, Class<?>... clz) {
// Verify that we have the expected number of handlers.
if (Stream.of(handlers).count() != clz.length) {
throw new RuntimeException("Expected " + clz.length + " handlers, got: "
+ List.of(logger.getHandlers()));
}
for (Class<?> cl : clz) {
// Verify that the handlers are of the expected class.
// For each class, we should have exactly one handler
// of that class.
if (Stream.of(handlers)
.map(Object::getClass)
.filter(cl::equals)
.count() != 1) {
throw new RuntimeException("Expected one " + cl +", got: "
+ List.of(logger.getHandlers()));
}
}
// Verify that all handlers have the expected ID
if (Stream.of(logger.getHandlers())
.map(BadRootLoggerHandlers::getId)
.filter(expectedID::equals)
.count() != clz.length) {
throw new RuntimeException("Expected ids to be " + expectedID + ", got: "
+ List.of(logger.getHandlers()));
}
}
static long getId(Handler h) {
if (h instanceof custom.Handler) {
return ((custom.Handler)h).id;
}
if (h instanceof custom.DotHandler) {
return ((custom.DotHandler)h).id;
}
if (h instanceof custom.GlobalHandler) {
return ((custom.GlobalHandler)h).id;
}
return -1;
}
}

View file

@ -0,0 +1,228 @@
/*
* Copyright (c) 2017, 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.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @test
* @bug 8191033
* @build custom.DotHandler custom.Handler
* @run main/othervm RootLoggerHandlers
* @author danielfuchs
*/
public class RootLoggerHandlers {
public static final Path SRC_DIR =
Paths.get(System.getProperty("test.src", "src"));
public static final Path USER_DIR =
Paths.get(System.getProperty("user.dir", "."));
public static final Path CONFIG_FILE = Paths.get("logging.properties");
// Uncomment this to run the test on Java 8. Java 8 does not have
// List.of(...)
// static final class List {
// static <T> java.util.List<T> of(T... items) {
// return Collections.unmodifiableList(Arrays.asList(items));
// }
// }
public static void main(String[] args) throws IOException {
Path initialProps = SRC_DIR.resolve(CONFIG_FILE);
Path loggingProps = USER_DIR.resolve(CONFIG_FILE);
System.setProperty("java.util.logging.config.file", loggingProps.toString());
Files.copy(initialProps, loggingProps, StandardCopyOption.REPLACE_EXISTING);
loggingProps.toFile().setWritable(true);
System.out.println("Root level is: " + Logger.getLogger("").getLevel());
if (Logger.getLogger("").getLevel() != Level.INFO) {
throw new RuntimeException("Expected root level INFO, got: "
+ Logger.getLogger("").getLevel());
}
// Verify that we have two handlers. One was configured with
// handlers=custom.Handler, the other with
// .handlers=custom.DotHandler
// Verify that exactly one of the two handlers is a custom.Handler
// Verify that exactly one of the two handlers is a custom.DotHandler
// Verify that the two handlers has an id of '1'
checkHandlers(Logger.getLogger(""),
Logger.getLogger("").getHandlers(),
1L,
custom.Handler.class,
custom.DotHandler.class);
checkHandlers(Logger.getLogger("global"),
Logger.getGlobal().getHandlers(),
1L,
custom.GlobalHandler.class);
// The log message "hi" should appear twice on the console.
// We don't check that. This is just for log analysis in case
// of test failure.
Logger.getAnonymousLogger().info("hi");
// Change the root logger level to FINE in the properties file
// and reload the configuration.
Files.write(loggingProps,
Files.lines(initialProps)
.map((s) -> s.replace("INFO", "FINE"))
.collect(Collectors.toList()));
LogManager.getLogManager().readConfiguration();
System.out.println("Root level is: " + Logger.getLogger("").getLevel());
if (Logger.getLogger("").getLevel() != Level.FINE) {
throw new RuntimeException("Expected root level FINE, got: "
+ Logger.getLogger("").getLevel());
}
// Verify that we have now only one handler, configured with
// handlers=custom.Handler, and that the other configured with
// .handlers=custom.DotHandler was ignored.
// Verify that the handler is a custom.Handler
// Verify that the handler has an id of '2'
checkHandlers(Logger.getLogger(""),
Logger.getLogger("").getHandlers(),
2L,
custom.Handler.class);
checkHandlers(Logger.getGlobal(),
Logger.getGlobal().getHandlers(),
1L);
// The log message "there" should appear only once on the console.
// We don't check that. This is just for log analysis in case
// of test failure.
Logger.getAnonymousLogger().info("there!");
// Change the root logger level to FINER in the properties file
// and reload the configuration.
Files.write(loggingProps,
Files.lines(initialProps)
.map((s) -> s.replace("INFO", "FINER"))
.collect(Collectors.toList()));
LogManager.getLogManager().readConfiguration();
System.out.println("Root level is: " + Logger.getLogger("").getLevel());
if (Logger.getLogger("").getLevel() != Level.FINER) {
throw new RuntimeException("Expected root level FINER, got: "
+ Logger.getLogger("").getLevel());
}
// Verify that we have only one handler, configured with
// handlers=custom.Handler, and that the other configured with
// .handlers=custom.DotHandler was ignored.
// Verify that the handler is a custom.Handler
// Verify that the handler has an id of '3'
checkHandlers(Logger.getLogger(""),
Logger.getLogger("").getHandlers(),
3L,
custom.Handler.class);
checkHandlers(Logger.getGlobal(),
Logger.getGlobal().getHandlers(),
1L);
LogManager.getLogManager().reset();
LogManager.getLogManager().updateConfiguration((s) -> (o,n) -> n);
// Verify that we have only one handler, configured with
// handlers=custom.Handler, and that the other configured with
// .handlers=custom.DotHandler was ignored.
// Verify that the handler is a custom.Handler
// Verify that the handler has an id of '4'
checkHandlers(Logger.getLogger(""),
Logger.getLogger("").getHandlers(),
4L,
custom.Handler.class);
checkHandlers(Logger.getGlobal(),
Logger.getGlobal().getHandlers(),
2L,
custom.GlobalHandler.class);
LogManager.getLogManager().updateConfiguration((s) -> (o,n) -> n);
// Verify that we have only one handler, configured with
// handlers=custom.Handler, and that the other configured with
// .handlers=custom.DotHandler was ignored.
// Verify that the handler is a custom.Handler
// Verify that the handler has an id of '4'
checkHandlers(Logger.getLogger(""),
Logger.getLogger("").getHandlers(),
4L,
custom.Handler.class);
checkHandlers(Logger.getGlobal(),
Logger.getGlobal().getHandlers(),
2L,
custom.GlobalHandler.class);
// The log message "done" should appear only once on the console.
// We don't check that. This is just for log analysis in case
// of test failure.
Logger.getAnonymousLogger().info("done!");
}
static void checkHandlers(Logger logger, Handler[] handlers, Long expectedID, Class<?>... clz) {
// Verify that we have the expected number of handlers.
if (Stream.of(handlers).count() != clz.length) {
throw new RuntimeException("Expected " + clz.length + " handlers, got: "
+ List.of(logger.getHandlers()));
}
for (Class<?> cl : clz) {
// Verify that the handlers are of the expected class.
// For each class, we should have exactly one handler
// of that class.
if (Stream.of(handlers)
.map(Object::getClass)
.filter(cl::equals)
.count() != 1) {
throw new RuntimeException("Expected one " + cl +", got: "
+ List.of(logger.getHandlers()));
}
}
// Verify that all handlers have the expected ID
if (Stream.of(logger.getHandlers())
.map(RootLoggerHandlers::getId)
.filter(expectedID::equals)
.count() != clz.length) {
throw new RuntimeException("Expected ids to be " + expectedID + ", got: "
+ List.of(logger.getHandlers()));
}
}
static long getId(Handler h) {
if (h instanceof custom.Handler) {
return ((custom.Handler)h).id;
}
if (h instanceof custom.DotHandler) {
return ((custom.DotHandler)h).id;
}
if (h instanceof custom.GlobalHandler) {
return ((custom.GlobalHandler)h).id;
}
return -1;
}
}

View file

@ -0,0 +1,18 @@
############################################################
# Global properties
############################################################
# "handlers" specifies a comma separated list of log Handler
# classes. These handlers will be installed during VM startup.
#handlers= java.util.logging.ConsoleHandler
handlers= custom.Handler
.handlers= custom.DotHandler
global.handlers = 1custom.GlobalHandler, custom.GlobalHandler
# Default global logging level.
.level= INFO
# Other configuration
custom.Handler.level=ALL
custom.DotHandler.level=ALL
java.util.logging.SimpleFormatter.format=%4$s [%1$tc]: %2$s: %5$s%n

View file

@ -0,0 +1,17 @@
############################################################
# Global properties
############################################################
# "handlers" specifies a comma separated list of log Handler
# classes. These handlers will be installed during VM startup.
#handlers= java.util.logging.ConsoleHandler
handlers= custom.Handler
.handlers= 1custom.DotHandler,custom.DotHandler
# Default global logging level.
.level= INFO
# Other configuration
custom.Handler.level=ALL
custom.DotHandler.level=ALL
java.util.logging.SimpleFormatter.format=%4$s [%1$tc]: %2$s: %5$s%n

View file

@ -0,0 +1,50 @@
/*
* 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 custom;
import java.util.concurrent.atomic.AtomicLong;
/**
*
* @author danielfuchs
*/
public class DotHandler extends java.util.logging.ConsoleHandler {
public static final AtomicLong IDS = new AtomicLong();
public final long id = IDS.incrementAndGet();
public DotHandler() {
System.out.println("DotHandler(" + id + ") created");
//new Exception("DotHandler").printStackTrace();
}
@Override
public void close() {
System.out.println("DotHandler(" + id + ") closed");
super.close();
}
@Override
public String toString() {
return this.getClass().getName() + '(' + id + ')';
}
}

View file

@ -0,0 +1,50 @@
/*
* 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.
*/
package custom;
import java.util.concurrent.atomic.AtomicLong;
/**
*
* @author danielfuchs
*/
public class GlobalHandler extends java.util.logging.ConsoleHandler {
public static final AtomicLong IDS = new AtomicLong();
public final long id = IDS.incrementAndGet();
public GlobalHandler() {
System.out.println("GlobalHandler(" + id + ") created");
//new Exception("GlobalHandler").printStackTrace();
}
@Override
public void close() {
System.out.println("GlobalHandler(" + id + ") closed");
super.close();
}
@Override
public String toString() {
return this.getClass().getName() + '(' + id + ')';
}
}

View file

@ -0,0 +1,49 @@
/*
* 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 custom;
import java.util.concurrent.atomic.AtomicLong;
/**
*
* @author danielfuchs
*/
public class Handler extends java.util.logging.ConsoleHandler {
static final AtomicLong IDS = new AtomicLong();
public final long id = IDS.incrementAndGet();
public Handler() {
System.out.println("Handler(" + id + ") created");
}
@Override
public void close() {
System.out.println("Handler(" + id + ") closed");
super.close();
}
@Override
public String toString() {
return this.getClass().getName() + '(' + id + ')';
}
}

View file

@ -0,0 +1,19 @@
############################################################
# Global properties
############################################################
# "handlers" specifies a comma separated list of log Handler
# classes. These handlers will be installed during VM startup.
#handlers= java.util.logging.ConsoleHandler
handlers= custom.Handler
.handlers= custom.DotHandler
global.handlers= custom.GlobalHandler
# Default global logging level.
.level= INFO
# Other configuration
custom.Handler.level=ALL
custom.DotHandler.level=ALL
custom.GlobalHandler.level=ALL
java.util.logging.SimpleFormatter.format=%4$s [%1$tc]: %2$s: %5$s%n

View file

@ -0,0 +1,417 @@
/*
* Copyright (c) 2015, 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.logging.FileHandler;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import jdk.test.lib.Utils;
/**
* @test
* @bug 8033661
* @summary tests that FileHandlers configured on abstract nodes in logging.properties
* will be closed on reset and reopened on updateConfiguration().
* Test a complex reconfiguration where a logger with handlers
* suddenly appears in the hierarchy between a child logger and the
* root logger.
* @library /test/lib
* @run main/othervm HandlersOnComplexResetUpdate
* @author danielfuchs
*/
public class HandlersOnComplexResetUpdate {
// We will test the handling of abstract logger nodes with file handlers
public static void run(List<Properties> properties) throws Exception {
Configure.setUp(properties.get(0));
test(properties);
}
static int adjustCount(int count) {
return Math.min(count, (int) Math.ceil(Utils.TIMEOUT_FACTOR * count));
}
private static final String PREFIX =
"FileHandler-" + UUID.randomUUID() + ".log";
private static final String userDir = System.getProperty("user.dir", ".");
private static final boolean userDirWritable = Files.isWritable(Paths.get(userDir));
private static final List<Properties> properties;
static {
// The test will call reset() and updateConfiguration() with each of these
// properties in sequence. The child logger is not released between each
// configuration. What is interesting here is mostly what happens between
// props4 and props5:
//
// In step 4 (props4) the configuration defines a handler for the
// logger com.foo (the direct parent of com.foo.child - which is the
// logger we hold on to).
//
// In step 5 (props5) the configuration has nothing concerning
// 'com.foo', but the handler has been migrated to 'com'.
// Since there doesn't exist any logger for 'com' (the previous
// configuration didn't have any configuration for 'com'), then
// 'com' will not be found when we process the existing loggers named
// in the configuration.
//
// So if we didn't also process the existing loggers not named in the
// configuration (such as com.foo.child) then no logger for 'com'
// would be created, which means that com.foo.child would not be
// able to inherit its configuration for 'com' until someone explicitely
// creates a logger for 'com'.
//
// This test check that a logger for 'com' will be created because
// 'com.foo.child' still exists when updateConfiguration() is called.
Properties props1 = new Properties();
props1.setProperty("test.name", "parent logger with handler");
props1.setProperty(FileHandler.class.getName() + ".pattern", PREFIX);
props1.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE));
props1.setProperty(FileHandler.class.getName() + ".level", "ALL");
props1.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter");
props1.setProperty("com.foo.handlers", FileHandler.class.getName());
props1.setProperty("test.checkHandlersOnParent", "true");
props1.setProperty("test.checkHandlersOn", "com.foo");
props1.setProperty("com.bar.level", "FINEST");
Properties props2 = new Properties();
props2.setProperty("java.util.logging.LogManager.reconfigureHandlers", "true");
props2.setProperty(FileHandler.class.getName() + ".pattern", PREFIX);
props2.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE));
props2.setProperty(FileHandler.class.getName() + ".level", "ALL");
props2.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter");
props2.setProperty("com.foo.handlers", FileHandler.class.getName());
props2.setProperty("test.checkHandlersOnParent", "true");
props2.setProperty("test.checkHandlersOn", "com.foo");
props2.setProperty("com.bar.level", "FINEST");
Properties props3 = new Properties();
props3.setProperty("test.name", "parent logger with handler");
props3.setProperty(FileHandler.class.getName() + ".pattern", PREFIX);
props3.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE));
props3.setProperty(FileHandler.class.getName() + ".level", "ALL");
props3.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter");
props3.setProperty("com.foo.handlers", FileHandler.class.getName());
props3.setProperty("test.checkHandlersOnParent", "true");
props3.setProperty("test.checkHandlersOn", "com.foo");
props3.setProperty("com.bar.level", "FINEST");
Properties props4 = new Properties();
props4.setProperty("java.util.logging.LogManager.reconfigureHandlers", "true");
props4.setProperty(FileHandler.class.getName() + ".pattern", PREFIX);
props4.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE));
props4.setProperty(FileHandler.class.getName() + ".level", "ALL");
props4.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter");
props4.setProperty("test.checkHandlersOnParent", "true");
props4.setProperty("test.checkHandlersOn", "com.foo");
props4.setProperty("com.foo.handlers", FileHandler.class.getName());
Properties props5 = new Properties();
props5.setProperty("test.name", "parent logger with handler");
props5.setProperty(FileHandler.class.getName() + ".pattern", PREFIX);
props5.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE));
props5.setProperty(FileHandler.class.getName() + ".level", "ALL");
props5.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter");
props5.setProperty("test.checkHandlersOnParent", "false");
props5.setProperty("test.checkHandlersOn", "com");
props5.setProperty("com.handlers", FileHandler.class.getName());
properties = Collections.unmodifiableList(Arrays.asList(
props1, props2, props3, props4, props5));
}
/**
* This is the main test method. The rest is infrastructure.
* Creates a child of the 'com.foo' logger (com.foo.child) and holds on to
* it.
* <p>
* Then applies all given configurations in sequence and verifies assumptions
* about the handlers that com.foo should have, or not have.
* In the last configuration (props5) it also verifies that the
* logger 'com' has been created and has now the expected handler.
* <p>
* Finally releases the child logger after all configurations have been
* applied.
*
* @param properties
* @throws Exception
*/
static void test(List<Properties> properties)
throws Exception {
if (!userDirWritable) {
throw new RuntimeException("Not writable: "+userDir);
}
// Then create a child of the com.foo logger.
Logger fooChild = Logger.getLogger("com.foo.child");
fooChild.info("hello world");
Logger barChild = Logger.getLogger("com.bar.child");
barChild.info("hello world");
ReferenceQueue<Logger> queue = new ReferenceQueue();
WeakReference<Logger> fooRef = new WeakReference<>(Logger.getLogger("com.foo"), queue);
if (!fooRef.refersTo(fooChild.getParent())) {
throw new RuntimeException("Unexpected parent logger: "
+ fooChild.getParent() +"\n\texpected: " + fooRef.get());
}
WeakReference<Logger> barRef = new WeakReference<>(Logger.getLogger("com.bar"), queue);
if (!barRef.refersTo(barChild.getParent())) {
throw new RuntimeException("Unexpected parent logger: "
+ barChild.getParent() +"\n\texpected: " + barRef.get());
}
Reference<? extends Logger> ref2;
int max = adjustCount(6);
barChild = null;
while ((ref2 = queue.remove(500)) == null) {
System.gc();
if (--max == 0) break;
}
Throwable failed = null;
try {
if (ref2 != null) {
String refName = ref2 == fooRef ? "fooRef" : ref2 == barRef ? "barRef" : "unknown";
if (ref2 != barRef) {
throw new RuntimeException("Unexpected logger reference cleared: " + refName);
} else {
System.out.println("Reference " + refName + " cleared as expected");
}
} else if (ref2 == null) {
throw new RuntimeException("Expected 'barRef' to be cleared");
}
// Now lets try to reset, check that ref2 has no handlers, and
// attempt to configure again.
Properties previousProps = properties.get(0);
int expectedHandlersCount = 1;
boolean checkHandlersOnParent = Boolean.parseBoolean(
previousProps.getProperty("test.checkHandlersOnParent", "true"));
String checkHandlersOn = previousProps.getProperty("test.checkHandlersOn", null);
for (int i=1; i<properties.size(); i++) {
System.out.println("\n*** Reconfiguration with properties["+i+"]\n");
Properties nextProps = properties.get(i);
boolean reconfigureHandlers = true;
if (checkHandlersOnParent) {
assertEquals(expectedHandlersCount,
fooChild.getParent().getHandlers().length,
"fooChild.getParent().getHandlers().length");
}
if (checkHandlersOn != null) {
Logger loggerWithHandlers = LogManager.getLogManager().getLogger(checkHandlersOn);
if (loggerWithHandlers == null) {
throw new RuntimeException("Logger with handlers not found: " + checkHandlersOn);
}
assertEquals(expectedHandlersCount,
loggerWithHandlers.getHandlers().length,
checkHandlersOn + ".getHandlers().length");
}
// Reset
Configure.doPrivileged(() -> LogManager.getLogManager().reset());
assertEquals(0, fooChild.getParent().getHandlers().length, "fooChild.getParent().getHandlers().length");
if (checkHandlersOn != null) {
Logger loggerWithHandlers = LogManager.getLogManager().getLogger(checkHandlersOn);
if (loggerWithHandlers == null) {
throw new RuntimeException("Logger with handlers not found: " + checkHandlersOn);
}
assertEquals(0, loggerWithHandlers.getHandlers().length,
checkHandlersOn + ".getHandlers().length");
}
if (i == 4) {
System.out.println("Last configuration...");
}
// Read configuration
Configure.doPrivileged(() -> Configure.updateConfigurationWith(nextProps, false));
expectedHandlersCount = reconfigureHandlers ? 1 : 0;
checkHandlersOnParent = Boolean.parseBoolean(
nextProps.getProperty("test.checkHandlersOnParent", "true"));
checkHandlersOn = nextProps.getProperty("test.checkHandlersOn", null);
if (checkHandlersOnParent) {
assertEquals(expectedHandlersCount,
fooChild.getParent().getHandlers().length,
"fooChild.getParent().getHandlers().length");
} else {
assertEquals(0,
fooChild.getParent().getHandlers().length,
"fooChild.getParent().getHandlers().length");
}
if (checkHandlersOn != null) {
Logger loggerWithHandlers = LogManager.getLogManager().getLogger(checkHandlersOn);
if (loggerWithHandlers == null) {
throw new RuntimeException("Logger with handlers not found: " + checkHandlersOn);
}
assertEquals(expectedHandlersCount,
loggerWithHandlers.getHandlers().length,
checkHandlersOn + ".getHandlers().length");
}
}
} catch (Throwable t) {
failed = t;
} finally {
final Throwable suppressed = failed;
Configure.doPrivileged(() -> LogManager.getLogManager().reset());
Configure.doPrivileged(() -> {
try {
StringBuilder builder = new StringBuilder();
Files.list(Paths.get(userDir))
.filter((f) -> f.toString().contains(PREFIX))
.filter((f) -> f.toString().endsWith(".lck"))
.forEach((f) -> {
builder.append(f.toString()).append('\n');
});
if (!builder.toString().isEmpty()) {
throw new RuntimeException("Lock files not cleaned:\n"
+ builder.toString());
}
} catch(RuntimeException | Error x) {
if (suppressed != null) x.addSuppressed(suppressed);
throw x;
} catch(Exception x) {
if (suppressed != null) x.addSuppressed(suppressed);
throw new RuntimeException(x);
}
});
try {
fooChild = null;
System.out.println("Setting fooChild to: " + fooChild);
while ((ref2 = queue.poll()) == null) {
System.gc();
Thread.sleep(1000);
}
if (ref2 != fooRef) {
throw new RuntimeException("Unexpected reference: "
+ ref2 +"\n\texpected: " + fooRef);
}
if (!ref2.refersTo(null)) {
throw new RuntimeException("Referent not cleared: " + ref2.get());
}
System.out.println("Got fooRef after reset(), fooChild is " + fooChild);
} catch(Throwable t) {
if (failed != null) t.addSuppressed(failed);
throw t;
}
}
if (failed != null) {
// should rarely happen...
throw new RuntimeException(failed);
}
}
public static void main(String... args) throws Exception {
try {
run(properties);
} finally {
if (userDirWritable) {
Configure.doPrivileged(() -> {
// cleanup - delete files that have been created
try {
Files.list(Paths.get(userDir))
.filter((f) -> f.toString().contains(PREFIX))
.forEach((f) -> {
try {
System.out.println("deleting " + f);
Files.delete(f);
} catch(Throwable t) {
System.err.println("Failed to delete " + f + ": " + t);
}
});
} catch(Throwable t) {
System.err.println("Cleanup failed to list files: " + t);
t.printStackTrace();
}
});
}
}
}
static class Configure {
static void setUp(Properties propertyFile) {
doPrivileged(() -> {
updateConfigurationWith(propertyFile, false);
});
}
static void updateConfigurationWith(Properties propertyFile, boolean append) {
try {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
propertyFile.store(bytes, propertyFile.getProperty("test.name"));
ByteArrayInputStream bais = new ByteArrayInputStream(bytes.toByteArray());
Function<String, BiFunction<String,String,String>> remapper =
append ? (x) -> ((o, n) -> n == null ? o : n)
: (x) -> ((o, n) -> n);
LogManager.getLogManager().updateConfiguration(bais, remapper);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
static void doPrivileged(Runnable run) {
run.run();
}
static <T> T callPrivileged(Callable<T> call) throws Exception {
return call.call();
}
}
@FunctionalInterface
public static interface FileHandlerSupplier {
public FileHandler test() throws Exception;
}
static final class TestAssertException extends RuntimeException {
TestAssertException(String msg) {
super(msg);
}
}
private static void assertEquals(long expected, long received, String msg) {
if (expected != received) {
throw new TestAssertException("Unexpected result for " + msg
+ ".\n\texpected: " + expected
+ "\n\tactual: " + received);
} else {
System.out.println("Got expected " + msg + ": " + received);
}
}
}

View file

@ -0,0 +1,416 @@
/*
* Copyright (c) 2015, 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.logging.FileHandler;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import jdk.test.lib.Utils;
/**
* @test
* @bug 8033661
* @summary tests that FileHandlers configured on abstract nodes in logging.properties
* will be properly closed and reopened on updateConfiguration().
* Test a complex reconfiguration where a logger with handlers
* suddenly appears in the hierarchy between a child logger and the
* root logger.
* @library /test/lib
* @run main/othervm HandlersOnComplexUpdate
* @author danielfuchs
*/
public class HandlersOnComplexUpdate {
// We will test the handling of abstract logger nodes with file handlers
public static void run(List<Properties> properties) throws Exception {
Configure.setUp(properties.get(0));
test(properties);
}
static int adjustCount(int count) {
return Math.min(count, (int) Math.ceil(Utils.TIMEOUT_FACTOR * count));
}
private static final String PREFIX =
"FileHandler-" + UUID.randomUUID() + ".log";
private static final String userDir = System.getProperty("user.dir", ".");
private static final boolean userDirWritable = Files.isWritable(Paths.get(userDir));
private static final List<Properties> properties;
static {
// The test will call updateConfiguration() with each of these
// properties in sequence. The child logger is not released between each
// configuration. What is interesting here is mostly what happens between
// props4 and props5:
//
// In step 4 (props4) the configuration defines a handler for the
// logger com.foo (the direct parent of com.foo.child - which is the
// logger we hold on to).
//
// In step 5 (props5) the configuration has nothing concerning
// 'com.foo', but the handler has been migrated to 'com'.
// Since there doesn't exist any logger for 'com' (the previous
// configuration didn't have any configuration for 'com'), then
// 'com' will not be found when we process the existing loggers named
// in the configuration.
//
// So if we didn't also process the existing loggers not named in the
// configuration (such as com.foo.child) then no logger for 'com'
// would be created, which means that com.foo.child would not be
// able to inherit its configuration for 'com' until someone explicitely
// creates a logger for 'com'.
//
// This test check that a logger for 'com' will be created because
// 'com.foo.child' still exists when updateConfiguration() is called.
Properties props1 = new Properties();
props1.setProperty("test.name", "parent logger with handler");
props1.setProperty(FileHandler.class.getName() + ".pattern", PREFIX);
props1.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE));
props1.setProperty(FileHandler.class.getName() + ".level", "ALL");
props1.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter");
props1.setProperty("com.foo.handlers", FileHandler.class.getName());
props1.setProperty("test.checkHandlersOnParent", "true");
props1.setProperty("test.checkHandlersOn", "com.foo");
props1.setProperty("com.bar.level", "FINEST");
Properties props2 = new Properties();
props2.setProperty("test.name", "parent logger with handler");
props2.setProperty(FileHandler.class.getName() + ".pattern", PREFIX);
props2.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE));
props2.setProperty(FileHandler.class.getName() + ".level", "ALL");
props2.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter");
props2.setProperty("com.foo.handlers", FileHandler.class.getName());
props2.setProperty("test.checkHandlersOnParent", "true");
props2.setProperty("test.checkHandlersOn", "com.foo");
props2.setProperty("com.bar.level", "FINEST");
Properties props3 = new Properties();
props3.setProperty("test.name", "parent logger with handler");
props3.setProperty(FileHandler.class.getName() + ".pattern", PREFIX);
props3.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE));
props3.setProperty(FileHandler.class.getName() + ".level", "ALL");
props3.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter");
props3.setProperty("com.foo.handlers", FileHandler.class.getName());
props3.setProperty("test.checkHandlersOnParent", "true");
props3.setProperty("test.checkHandlersOn", "com.foo");
props3.setProperty("com.bar.level", "FINEST");
Properties props4 = new Properties();
props4.setProperty("test.name", "parent logger with handler");
props4.setProperty(FileHandler.class.getName() + ".pattern", PREFIX);
props4.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE));
props4.setProperty(FileHandler.class.getName() + ".level", "ALL");
props4.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter");
props4.setProperty("test.checkHandlersOnParent", "true");
props4.setProperty("test.checkHandlersOn", "com.foo");
props4.setProperty("com.foo.handlers", FileHandler.class.getName());
Properties props5 = new Properties();
props5.setProperty("test.name", "parent logger with handler");
props5.setProperty(FileHandler.class.getName() + ".pattern", PREFIX);
props5.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE));
props5.setProperty(FileHandler.class.getName() + ".level", "ALL");
props5.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter");
props5.setProperty("test.checkHandlersOnParent", "false");
props5.setProperty("test.checkHandlersOn", "com");
props5.setProperty("com.handlers", FileHandler.class.getName());
properties = Collections.unmodifiableList(Arrays.asList(
props1, props2, props3, props4, props5));
}
/**
* This is the main test method. The rest is infrastructure.
* Creates a child of the 'com.foo' logger (com.foo.child) and holds on to
* it.
* <p>
* Then applies all given configurations in sequence and verifies assumptions
* about the handlers that com.foo should have, or not have.
* In the last configuration (props5) it also verifies that the
* logger 'com' has been created and has now the expected handler.
* <p>
* Finally releases the child logger after all configurations have been
* applied.
*
* @param properties
* @throws Exception
*/
static void test(List<Properties> properties)
throws Exception {
if (!userDirWritable) {
throw new RuntimeException("Not writable: "+userDir);
}
// Then create a child of the com.foo logger.
Logger fooChild = Logger.getLogger("com.foo.child");
fooChild.info("hello world");
Logger barChild = Logger.getLogger("com.bar.child");
barChild.info("hello world");
ReferenceQueue<Logger> queue = new ReferenceQueue();
WeakReference<Logger> fooRef = new WeakReference<>(Logger.getLogger("com.foo"), queue);
if (!fooRef.refersTo(fooChild.getParent())) {
throw new RuntimeException("Unexpected parent logger: "
+ fooChild.getParent() +"\n\texpected: " + fooRef.get());
}
WeakReference<Logger> barRef = new WeakReference<>(Logger.getLogger("com.bar"), queue);
if (!barRef.refersTo(barChild.getParent())) {
throw new RuntimeException("Unexpected parent logger: "
+ barChild.getParent() +"\n\texpected: " + barRef.get());
}
Reference<? extends Logger> ref2;
int max = adjustCount(6);
barChild = null;
while ((ref2 = queue.remove(500)) == null) {
System.gc();
if (--max == 0) break;
}
Throwable failed = null;
try {
if (ref2 != null) {
String refName = ref2 == fooRef ? "fooRef" : ref2 == barRef ? "barRef" : "unknown";
if (ref2 != barRef) {
throw new RuntimeException("Unexpected logger reference cleared: " + refName);
} else {
System.out.println("Reference " + refName + " cleared as expected");
}
} else if (ref2 == null) {
throw new RuntimeException("Expected 'barRef' to be cleared");
}
// Now lets try to check handlers, and
// attempt to update the configuration again.
Properties previousProps = properties.get(0);
int expectedHandlersCount = 1;
boolean checkHandlersOnParent = Boolean.parseBoolean(
previousProps.getProperty("test.checkHandlersOnParent", "true"));
String checkHandlersOn = previousProps.getProperty("test.checkHandlersOn", null);
for (int i=1; i<properties.size(); i++) {
System.out.println("\n*** Reconfiguration with properties["+i+"]\n");
Properties nextProps = properties.get(i);
boolean reconfigureHandlers = true;
if (checkHandlersOnParent) {
assertEquals(expectedHandlersCount,
fooChild.getParent().getHandlers().length,
"fooChild.getParent().getHandlers().length");
}
if (checkHandlersOn != null) {
Logger loggerWithHandlers = LogManager.getLogManager().getLogger(checkHandlersOn);
if (loggerWithHandlers == null) {
throw new RuntimeException("Logger with handlers not found: " + checkHandlersOn);
}
assertEquals(expectedHandlersCount,
loggerWithHandlers.getHandlers().length,
checkHandlersOn + ".getHandlers().length");
}
if (i == 4) {
System.out.println("Last configuration...");
}
// Read configuration
Configure.doPrivileged(() -> Configure.updateConfigurationWith(nextProps, false));
expectedHandlersCount = reconfigureHandlers ? 1 : 0;
checkHandlersOnParent = Boolean.parseBoolean(
nextProps.getProperty("test.checkHandlersOnParent", "true"));
checkHandlersOn = nextProps.getProperty("test.checkHandlersOn", null);
if (checkHandlersOnParent) {
assertEquals(expectedHandlersCount,
fooChild.getParent().getHandlers().length,
"fooChild.getParent().getHandlers().length");
} else {
assertEquals(0,
fooChild.getParent().getHandlers().length,
"fooChild.getParent().getHandlers().length");
}
if (checkHandlersOn != null) {
Logger loggerWithHandlers = LogManager.getLogManager().getLogger(checkHandlersOn);
if (loggerWithHandlers == null) {
throw new RuntimeException("Logger with handlers not found: " + checkHandlersOn);
}
assertEquals(expectedHandlersCount,
loggerWithHandlers.getHandlers().length,
checkHandlersOn + ".getHandlers().length");
}
}
} catch (Throwable t) {
failed = t;
} finally {
final Throwable suppressed = failed;
Configure.doPrivileged(() -> LogManager.getLogManager().reset());
Configure.doPrivileged(() -> {
try {
StringBuilder builder = new StringBuilder();
Files.list(Paths.get(userDir))
.filter((f) -> f.toString().contains(PREFIX))
.filter((f) -> f.toString().endsWith(".lck"))
.forEach((f) -> {
builder.append(f.toString()).append('\n');
});
if (!builder.toString().isEmpty()) {
throw new RuntimeException("Lock files not cleaned:\n"
+ builder.toString());
}
} catch(RuntimeException | Error x) {
if (suppressed != null) x.addSuppressed(suppressed);
throw x;
} catch(Exception x) {
if (suppressed != null) x.addSuppressed(suppressed);
throw new RuntimeException(x);
}
});
try {
fooChild = null;
System.out.println("Setting fooChild to: " + fooChild);
while ((ref2 = queue.poll()) == null) {
System.gc();
Thread.sleep(1000);
}
if (ref2 != fooRef) {
throw new RuntimeException("Unexpected reference: "
+ ref2 +"\n\texpected: " + fooRef);
}
if (!ref2.refersTo(null)) {
throw new RuntimeException("Referent not cleared: " + ref2.get());
}
System.out.println("Got fooRef after reset(), fooChild is " + fooChild);
} catch (Throwable t) {
if (failed != null) t.addSuppressed(failed);
throw t;
}
}
if (failed != null) {
// should rarely happen...
throw new RuntimeException(failed);
}
}
public static void main(String... args) throws Exception {
try {
run(properties);
} finally {
if (userDirWritable) {
Configure.doPrivileged(() -> {
// cleanup - delete files that have been created
try {
Files.list(Paths.get(userDir))
.filter((f) -> f.toString().contains(PREFIX))
.forEach((f) -> {
try {
System.out.println("deleting " + f);
Files.delete(f);
} catch(Throwable t) {
System.err.println("Failed to delete " + f + ": " + t);
}
});
} catch(Throwable t) {
System.err.println("Cleanup failed to list files: " + t);
t.printStackTrace();
}
});
}
}
}
static class Configure {
static void setUp(Properties propertyFile) {
doPrivileged(() -> {
configureWith(propertyFile);
});
}
static void configureWith(Properties propertyFile) {
try {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
propertyFile.store(bytes, propertyFile.getProperty("test.name"));
ByteArrayInputStream bais = new ByteArrayInputStream(bytes.toByteArray());
LogManager.getLogManager().readConfiguration(bais);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
static void updateConfigurationWith(Properties propertyFile, boolean append) {
try {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
propertyFile.store(bytes, propertyFile.getProperty("test.name"));
ByteArrayInputStream bais = new ByteArrayInputStream(bytes.toByteArray());
Function<String, BiFunction<String,String,String>> remapper =
append ? (x) -> ((o, n) -> n == null ? o : n)
: (x) -> ((o, n) -> n);
LogManager.getLogManager().updateConfiguration(bais, remapper);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
static void doPrivileged(Runnable run) {
run.run();
}
static <T> T callPrivileged(Callable<T> call) throws Exception {
return call.call();
}
}
@FunctionalInterface
public static interface FileHandlerSupplier {
public FileHandler test() throws Exception;
}
static final class TestAssertException extends RuntimeException {
TestAssertException(String msg) {
super(msg);
}
}
private static void assertEquals(long expected, long received, String msg) {
if (expected != received) {
throw new TestAssertException("Unexpected result for " + msg
+ ".\n\texpected: " + expected
+ "\n\tactual: " + received);
} else {
System.out.println("Got expected " + msg + ": " + received);
}
}
}

View file

@ -0,0 +1,534 @@
/*
* 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
/**
* @test
* @bug 8033661 8189291
* @summary tests LogManager.updateConfiguration(InputStream, Function) method
* @run main/othervm SimpleUpdateConfigWithInputStreamTest
* @author danielfuchs
*/
public class SimpleUpdateConfigWithInputStreamTest {
// We will test updateConfiguration
public static void execute(Runnable run) {
try {
Configure.doPrivileged(run);
} finally {
Configure.doPrivileged(() -> {
try {
setSystemProperty("java.util.logging.config.file", null);
LogManager.getLogManager().readConfiguration();
System.gc();
} catch (Exception x) {
throw new RuntimeException(x);
}
});
}
}
public static class MyHandler extends Handler {
static final AtomicLong seq = new AtomicLong();
long count = seq.incrementAndGet();
@Override
public void publish(LogRecord record) {
}
@Override
public void flush() {
}
@Override
public void close() throws SecurityException {
}
@Override
public String toString() {
return super.toString() + "("+count+")";
}
}
static String storePropertyToFile(String name, Properties props)
throws Exception {
return Configure.callPrivileged(() -> {
String scratch = System.getProperty("user.dir", ".");
Path p = Paths.get(scratch, name);
try (FileOutputStream fos = new FileOutputStream(p.toFile())) {
props.store(fos, name);
}
return p.toString();
});
}
static void setSystemProperty(String name, String value)
throws Exception {
Configure.doPrivileged(() -> {
if (value == null)
System.clearProperty(name);
else
System.setProperty(name, value);
});
}
static String trim(String value) {
return value == null ? null : value.trim();
}
/**
* Tests one of the configuration defined above.
* <p>
* This is the main test method (the rest is infrastructure).
*/
static void testUpdateConfiguration() {
try {
// manager initialized with default configuration.
LogManager manager = LogManager.getLogManager();
// Test default configuration. It should not have
// any value for "com.foo.level" and "com.foo.handlers"
assertEquals(null, manager.getProperty("com.foo.level"),
"com.foo.level in default configuration");
assertEquals(null, manager.getProperty("com.foo.handlers"),
"com.foo.handlers in default configuration");
// Create a logging configuration file that contains
// com.foo.level=FINEST
// and set "java.util.logging.config.file" to this file.
Properties props = new Properties();
props.setProperty("com.foo.level", "FINEST");
// Update configuration with props
// then test that the new configuration has
// com.foo.level=FINEST
// and nothing for com.foo.handlers
Configure.updateConfigurationWith(props, null);
assertEquals("FINEST", manager.getProperty("com.foo.level"),
"com.foo.level in " + props);
assertEquals(null, manager.getProperty("com.foo.handlers"),
"com.foo.handlers in " + props);
// call updateConfiguration with an empty stream.
// check that the new configuration no longer has
// any value for com.foo.level, and still no value
// for com.foo.handlers
Configure.updateConfigurationWith(new Properties(), null);
assertEquals(null, manager.getProperty("com.foo.level"),
"com.foo.level in default configuration");
assertEquals(null, manager.getProperty("com.foo.handlers"),
"com.foo.handlers in default configuration");
// creates the com.foo logger, check it has
// the default config: no level, and no handlers
final Logger logger = Logger.getLogger("com.foo");
assertEquals(null, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertDeepEquals(new Handler[0], logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
// call updateConfiguration with 'props'
// check that the configuration has
// com.foo.level=FINEST
// and nothing for com.foo.handlers
// check that the logger has now a FINEST level and still
// no handlers
Configure.updateConfigurationWith(props, null);
assertEquals("FINEST", manager.getProperty("com.foo.level"),
"com.foo.level in " + props);
assertEquals(Level.FINEST, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertDeepEquals(new Handler[0], logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
assertEquals(null, manager.getProperty("com.foo.handlers"),
"com.foo.handlers in " + props);
// Calls updateConfiguration with a lambda whose effect should
// be to set the FINER level on the "com.foo" logger.
// Check that the new configuration has
// com.foo.level=FINER
// and nothing for com.foo.handlers
// check that the logger has now a FINER level and still
// no handlers
Configure.updateConfigurationWith(props,
(k) -> ("com.foo.level".equals(k) ? (o, n) -> "FINER" : (o, n) -> n));
assertEquals("FINER", manager.getProperty("com.foo.level"),
"com.foo.level set to FINER by updateConfiguration");
assertEquals(Level.FINER, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertDeepEquals(new Handler[0], logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
assertEquals(null, manager.getProperty("com.foo.handlers"),
"com.foo.handlers in " + props);
// Calls updateConfiguration with a lambda whose effect is a noop.
// This should not change the configuration, so
// check that the new configuration still has
// com.foo.level=FINER
// and nothing for com.foo.handlers
// check that the logger still has FINER level and still
// no handlers
Configure.updateConfigurationWith(props,
(k) -> ((o, n) -> o));
assertEquals("FINER", manager.getProperty("com.foo.level"),
"com.foo.level preserved by updateConfiguration");
assertEquals(Level.FINER, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertDeepEquals(new Handler[0], logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
assertEquals(null, manager.getProperty("com.foo.handlers"),
"com.foo.handlers in " + props);
// Calls updateConfiguration with a lambda whose effect is to
// take all values from the new configuration.
// This should update the configuration to what is in props, so
// check that the new configuration has
// com.foo.level=FINEST
// and nothing for com.foo.handlers
// check that the logger now has FINEST level and still
// no handlers
Configure.updateConfigurationWith(props,
(k) -> ((o, n) -> n));
assertEquals("FINEST", manager.getProperty("com.foo.level"),
"com.foo.level updated by updateConfiguration");
assertEquals(Level.FINEST, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertDeepEquals(new Handler[0], logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
assertEquals(null, manager.getProperty("com.foo.handlers"),
"com.foo.handlers in " + props);
// now set a handler on the com.foo logger.
MyHandler h = new MyHandler();
logger.addHandler(h);
assertDeepEquals(new Handler[] {h}, logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
// Calls updateConfiguration with a lambda whose effect should
// be to set the FINER level on the "com.foo" logger, and
// take the value from props for everything else.
// Check that the new configuration has
// com.foo.level=FINER
// and nothing for com.foo.handlers
// check that the logger has now a FINER level, but that its
// handlers are still present and have not been reset
// since neither the old nor new configuration defined them.
Configure.updateConfigurationWith(props,
(k) -> ("com.foo.level".equals(k) ? (o, n) -> "FINER" : (o, n) -> n));
assertEquals("FINER", manager.getProperty("com.foo.level"),
"com.foo.level set to FINER by updateConfiguration");
assertEquals(Level.FINER, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertDeepEquals(new Handler[] {h}, logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
assertEquals(null, manager.getProperty("com.foo.handlers"),
"com.foo.handlers in " + props);
// now add some configuration for com.foo.handlers
props.setProperty("com.foo.handlers", MyHandler.class.getName());
// we didn't call updateConfiguration, so just changing the
// content of props should have had no effect.
assertEquals("FINER", manager.getProperty("com.foo.level"),
"com.foo.level set to FINER by updateConfiguration");
assertEquals(Level.FINER, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertEquals(null,
manager.getProperty("com.foo.handlers"),
"manager.getProperty(\"com.foo.handlers\")");
assertDeepEquals(new Handler[] {h}, logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
// Calls updateConfiguration with a lambda whose effect is a noop.
// This should not change the current configuration, so
// check that the new configuration still has
// com.foo.level=FINER
// and nothing for com.foo.handlers
// check that the logger still has FINER level and still
// has its handlers and that they haven't been reset.
Configure.updateConfigurationWith(props, (k) -> ((o, n) -> o));
assertEquals("FINER", manager.getProperty("com.foo.level"),
"com.foo.level set to FINER by updateConfiguration");
assertEquals(Level.FINER, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertEquals(null,
manager.getProperty("com.foo.handlers"),
"manager.getProperty(\"com.foo.handlers\")");
assertDeepEquals(new Handler[] {h}, logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
// Calls updateConfiguration with a lambda whose effect is to
// take all values from the new configuration.
// This should update the configuration to what is in props, so
// check that the new configuration has
// com.foo.level=FINEST
// com.foo.handlers=SimpleUpdateConfigWithInputStreamTest$MyHandler
// check that the logger now has FINEST level
// and a new handler instance, since the old config
// had no handlers for com.foo and the new config has one.
Configure.updateConfigurationWith(props, (k) -> ((o, n) -> n));
assertEquals("FINEST", manager.getProperty("com.foo.level"),
"com.foo.level updated by updateConfiguration");
assertEquals(Level.FINEST, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertEquals(MyHandler.class.getName(),
manager.getProperty("com.foo.handlers"),
"manager.getProperty(\"com.foo.handlers\")");
Handler[] loggerHandlers = logger.getHandlers().clone();
assertEquals(1, loggerHandlers.length,
"Logger.getLogger(\"com.foo\").getHandlers().length");
assertEquals(MyHandler.class, loggerHandlers[0].getClass(),
"Logger.getLogger(\"com.foo\").getHandlers()[0].getClass()");
assertEquals(h.count + 1, ((MyHandler)logger.getHandlers()[0]).count,
"Logger.getLogger(\"com.foo\").getHandlers()[0].count");
// Calls updateConfiguration with a lambda whose effect is a noop.
// This should not change the current configuration, so
// check that the new configuration still has
// com.foo.level=FINEST
// com.foo.handlers=SimpleUpdateConfigWithInputStreamTest$MyHandler
// check that the logger still has FINEST level and still
// has its handlers and that they haven't been reset.
Configure.updateConfigurationWith(props, (k) -> ((o, n) -> o));
assertDeepEquals(loggerHandlers, logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
assertEquals("FINEST", manager.getProperty("com.foo.level"),
"com.foo.level updated by updateConfiguration");
assertEquals(Level.FINEST, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertEquals(MyHandler.class.getName(),
manager.getProperty("com.foo.handlers"),
"manager.getProperty(\"com.foo.handlers\")");
// Calls updateConfiguration with a lambda whose effect is to
// take all values from the new configuration.
// Because the content of the props hasn't changed, then
// it should also be a noop.
// check that the new configuration still has
// com.foo.level=FINEST
// com.foo.handlers=SimpleUpdateConfigWithInputStreamTest$MyHandler
// check that the logger still has FINEST level and still
// has its handlers and that they haven't been reset.
Configure.updateConfigurationWith(props, (k) -> ((o, n) -> n));
assertDeepEquals(loggerHandlers, logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
assertEquals("FINEST", manager.getProperty("com.foo.level"),
"com.foo.level updated by updateConfiguration");
assertEquals(Level.FINEST, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertEquals(MyHandler.class.getName(),
manager.getProperty("com.foo.handlers"),
"manager.getProperty(\"com.foo.handlers\")");
// Calls updateConfiguration with a null lambda, whose effect is to
// take all values from the new configuration.
// Because the content of the props hasn't changed, then
// it should also be a noop.
// check that the new configuration still has
// com.foo.level=FINEST
// com.foo.handlers=SimpleUpdateConfigWithInputStreamTest$MyHandler
// check that the logger still has FINEST level and still
// has its handlers and that they haven't been reset.
Configure.updateConfigurationWith(props, (k) -> ((o, n) -> n));
assertDeepEquals(loggerHandlers, logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
assertEquals("FINEST", manager.getProperty("com.foo.level"),
"com.foo.level updated by updateConfiguration");
assertEquals(Level.FINEST, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertEquals(MyHandler.class.getName(),
manager.getProperty("com.foo.handlers"),
"manager.getProperty(\"com.foo.handlers\")");
// now remove com.foo.handlers=SimpleUpdateConfigWithInputStreamTest$MyHandler
// from the configuration file.
props.remove("com.foo.handlers");
// Calls updateConfiguration with a lambda whose effect is a noop.
// This should not change the current configuration, so
// check that the new configuration still has
// com.foo.level=FINEST
// com.foo.handlers=SimpleUpdateConfigWithInputStreamTest$MyHandler
// check that the logger still has FINEST level and still
// has its handlers and that they haven't been reset.
Configure.updateConfigurationWith(props, (k) -> ((o, n) -> o));
assertDeepEquals(loggerHandlers, logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
assertEquals("FINEST", manager.getProperty("com.foo.level"),
"com.foo.level updated by updateConfiguration");
assertEquals(Level.FINEST, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertEquals(MyHandler.class.getName(),
manager.getProperty("com.foo.handlers"),
"manager.getProperty(\"com.foo.handlers\")");
// Calls updateConfiguration with a lambda whose effect is to
// take all values from the new configuration.
// This should update the configuration to what is in props, so
// check that the new configuration has
// com.foo.level=FINEST
// and nothing for com.foo.handlers
// check that the logger still has FINEST level
// and no handlers, since the old config
// had an handler for com.foo and the new config doesn't.
Configure.updateConfigurationWith(props, (k) -> ((o, n) -> n));
assertDeepEquals(new Handler[0], logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
assertEquals("FINEST", manager.getProperty("com.foo.level"),
"com.foo.level updated by updateConfiguration");
assertEquals(Level.FINEST, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertEquals(null,
manager.getProperty("com.foo.handlers"),
"manager.getProperty(\"com.foo.handlers\")");
} catch (RuntimeException | Error r) {
throw r;
} catch (Exception x) {
throw new RuntimeException(x);
}
}
public static void main(String[] args) throws Exception {
execute(SimpleUpdateConfigWithInputStreamTest::testUpdateConfiguration);
}
static class Configure {
static void updateConfigurationWith(Properties propertyFile,
Function<String,BiFunction<String,String,String>> remapper) {
try {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
propertyFile.store(bytes, propertyFile.getProperty("test.name"));
ByteArrayInputStream bais = new ByteArrayInputStream(bytes.toByteArray());
LogManager.getLogManager().updateConfiguration(bais, remapper);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
static void doPrivileged(Runnable run) {
run.run();
}
static <T> T callPrivileged(Callable<T> call) throws Exception {
return call.call();
}
}
static final class TestAssertException extends RuntimeException {
TestAssertException(String msg) {
super(msg);
}
}
private static void assertEquals(long expected, long received, String msg) {
if (expected != received) {
throw new TestAssertException("Unexpected result for " + msg
+ ".\n\texpected: " + expected
+ "\n\tactual: " + received);
} else {
System.out.println("Got expected " + msg + ": " + received);
}
}
private static void assertEquals(String expected, String received, String msg) {
if (!Objects.equals(expected, received)) {
throw new TestAssertException("Unexpected result for " + msg
+ ".\n\texpected: " + expected
+ "\n\tactual: " + received);
} else {
System.out.println("Got expected " + msg + ": " + received);
}
}
private static void assertEquals(Object expected, Object received, String msg) {
if (!Objects.equals(expected, received)) {
throw new TestAssertException("Unexpected result for " + msg
+ ".\n\texpected: " + expected
+ "\n\tactual: " + received);
} else {
System.out.println("Got expected " + msg + ": " + received);
}
}
public static String deepToString(Object o) {
if (o == null) {
return "null";
} else if (o.getClass().isArray()) {
String s;
if (o instanceof Object[])
s = Arrays.deepToString((Object[]) o);
else if (o instanceof byte[])
s = Arrays.toString((byte[]) o);
else if (o instanceof short[])
s = Arrays.toString((short[]) o);
else if (o instanceof int[])
s = Arrays.toString((int[]) o);
else if (o instanceof long[])
s = Arrays.toString((long[]) o);
else if (o instanceof char[])
s = Arrays.toString((char[]) o);
else if (o instanceof float[])
s = Arrays.toString((float[]) o);
else if (o instanceof double[])
s = Arrays.toString((double[]) o);
else if (o instanceof boolean[])
s = Arrays.toString((boolean[]) o);
else
s = o.toString();
return s;
} else {
return o.toString();
}
}
private static void assertDeepEquals(Object expected, Object received, String msg) {
if (!Objects.deepEquals(expected, received)) {
throw new TestAssertException("Unexpected result for " + msg
+ ".\n\texpected: " + deepToString(expected)
+ "\n\tactual: " + deepToString(received));
} else {
System.out.println("Got expected " + msg + ": " + deepToString(received));
}
}
}

View file

@ -0,0 +1,558 @@
/*
* 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
/**
* @test
* @bug 8033661
* @summary tests LogManager.updateConfiguration(Function) method
* @run main/othervm SimpleUpdateConfigurationTest
* @author danielfuchs
*/
public class SimpleUpdateConfigurationTest {
// We will test updateConfiguration
public static void execute(Runnable run) {
try {
Configure.doPrivileged(run);
} finally {
Configure.doPrivileged(() -> {
try {
setSystemProperty("java.util.logging.config.file", null);
LogManager.getLogManager().readConfiguration();
System.gc();
} catch (Exception x) {
throw new RuntimeException(x);
}
});
}
}
public static class MyHandler extends Handler {
static final AtomicLong seq = new AtomicLong();
long count = seq.incrementAndGet();
@Override
public void publish(LogRecord record) {
}
@Override
public void flush() {
}
@Override
public void close() throws SecurityException {
}
@Override
public String toString() {
return super.toString() + "("+count+")";
}
}
static String storePropertyToFile(String name, Properties props)
throws Exception {
return Configure.callPrivileged(() -> {
String scratch = System.getProperty("user.dir", ".");
Path p = Paths.get(scratch, name);
try (FileOutputStream fos = new FileOutputStream(p.toFile())) {
props.store(fos, name);
}
return p.toString();
});
}
static void setSystemProperty(String name, String value)
throws Exception {
Configure.doPrivileged(() -> {
if (value == null)
System.clearProperty(name);
else
System.setProperty(name, value);
});
}
static String trim(String value) {
return value == null ? null : value.trim();
}
/**
* Tests one of the configuration defined above.
* <p>
* This is the main test method (the rest is infrastructure).
*/
static void testUpdateConfiguration() {
String configFile = null;
try {
// manager initialized with default configuration.
LogManager manager = LogManager.getLogManager();
// Test default configuration. It should not have
// any value for "com.foo.level" and "com.foo.handlers"
assertEquals(null, manager.getProperty("com.foo.level"),
"com.foo.level in default configuration");
assertEquals(null, manager.getProperty("com.foo.handlers"),
"com.foo.handlers in default configuration");
// Create a logging configuration file that contains
// com.foo.level=FINEST
// and set "java.util.logging.config.file" to this file.
Properties props = new Properties();
props.setProperty("com.foo.level", "FINEST");
configFile = storePropertyToFile("config1", props);
setSystemProperty("java.util.logging.config.file", configFile);
// Update configuration with configFile
// then test that the new configuration has
// com.foo.level=FINEST
// and nothing for com.foo.handlers
manager.updateConfiguration(null);
assertEquals("FINEST", manager.getProperty("com.foo.level"),
"com.foo.level in " + configFile);
assertEquals(null, manager.getProperty("com.foo.handlers"),
"com.foo.handlers in " + configFile);
// clear ("java.util.logging.config.file" system property,
// and call updateConfiguration again.
// check that the new configuration no longer has
// any value for com.foo.level, and still no value
// for com.foo.handlers
setSystemProperty("java.util.logging.config.file", null);
manager.updateConfiguration(null);
assertEquals(null, manager.getProperty("com.foo.level"),
"com.foo.level in default configuration");
assertEquals(null, manager.getProperty("com.foo.handlers"),
"com.foo.handlers in default configuration");
// creates the com.foo logger, check it has
// the default config: no level, and no handlers
final Logger logger = Logger.getLogger("com.foo");
assertEquals(null, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertDeepEquals(new Handler[0], logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
// set "java.util.logging.config.file" to configFile and
// call updateConfiguration.
// check that the configuration has
// com.foo.level=FINEST
// and nothing for com.foo.handlers
// check that the logger has now a FINEST level and still
// no handlers
setSystemProperty("java.util.logging.config.file", configFile);
manager.updateConfiguration(null);
assertEquals("FINEST", manager.getProperty("com.foo.level"),
"com.foo.level in " + configFile);
assertEquals(Level.FINEST, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertDeepEquals(new Handler[0], logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
assertEquals(null, manager.getProperty("com.foo.handlers"),
"com.foo.handlers in " + configFile);
// Calls updateConfiguration with a lambda whose effect should
// be to set the FINER level on the "com.foo" logger.
// Check that the new configuration has
// com.foo.level=FINER
// and nothing for com.foo.handlers
// check that the logger has now a FINER level and still
// no handlers
manager.updateConfiguration(
(k) -> ("com.foo.level".equals(k) ? (o, n) -> "FINER" : (o, n) -> n));
assertEquals("FINER", manager.getProperty("com.foo.level"),
"com.foo.level set to FINER by updateConfiguration");
assertEquals(Level.FINER, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertDeepEquals(new Handler[0], logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
assertEquals(null, manager.getProperty("com.foo.handlers"),
"com.foo.handlers in " + configFile);
// Calls updateConfiguration with a lambda whose effect is a noop.
// This should not change the configuration, so
// check that the new configuration still has
// com.foo.level=FINER
// and nothing for com.foo.handlers
// check that the logger still has FINER level and still
// no handlers
manager.updateConfiguration(
(k) -> ((o, n) -> o));
assertEquals("FINER", manager.getProperty("com.foo.level"),
"com.foo.level preserved by updateConfiguration");
assertEquals(Level.FINER, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertDeepEquals(new Handler[0], logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
assertEquals(null, manager.getProperty("com.foo.handlers"),
"com.foo.handlers in " + configFile);
// Calls updateConfiguration with a lambda whose effect is to
// take all values from the new configuration.
// This should update the configuration to what is in configFile, so
// check that the new configuration has
// com.foo.level=FINEST
// and nothing for com.foo.handlers
// check that the logger now has FINEST level and still
// no handlers
manager.updateConfiguration(
(k) -> ((o, n) -> n));
assertEquals("FINEST", manager.getProperty("com.foo.level"),
"com.foo.level updated by updateConfiguration");
assertEquals(Level.FINEST, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertDeepEquals(new Handler[0], logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
assertEquals(null, manager.getProperty("com.foo.handlers"),
"com.foo.handlers in " + configFile);
// now set a handler on the com.foo logger.
MyHandler h = new MyHandler();
logger.addHandler(h);
assertDeepEquals(new Handler[] {h}, logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
// Calls updateConfiguration with a lambda whose effect should
// be to set the FINER level on the "com.foo" logger, and
// take the value from configFile for everything else.
// Check that the new configuration has
// com.foo.level=FINER
// and nothing for com.foo.handlers
// check that the logger has now a FINER level, but that its
// handlers are still present and have not been reset
// since neither the old nor new configuration defined them.
manager.updateConfiguration(
(k) -> ("com.foo.level".equals(k) ? (o, n) -> "FINER" : (o, n) -> n));
assertEquals("FINER", manager.getProperty("com.foo.level"),
"com.foo.level set to FINER by updateConfiguration");
assertEquals(Level.FINER, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertDeepEquals(new Handler[] {h}, logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
assertEquals(null, manager.getProperty("com.foo.handlers"),
"com.foo.handlers in " + configFile);
// now add some configuration for com.foo.handlers in the
// configuration file.
props.setProperty("com.foo.handlers", MyHandler.class.getName());
storePropertyToFile("config1", props);
// we didn't call updateConfiguration, so just changing the
// content of the file should have had no effect yet.
assertEquals("FINER", manager.getProperty("com.foo.level"),
"com.foo.level set to FINER by updateConfiguration");
assertEquals(Level.FINER, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertEquals(null,
manager.getProperty("com.foo.handlers"),
"manager.getProperty(\"com.foo.handlers\")");
assertDeepEquals(new Handler[] {h}, logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
// Calls updateConfiguration with a lambda whose effect is a noop.
// This should not change the current configuration, so
// check that the new configuration still has
// com.foo.level=FINER
// and nothing for com.foo.handlers
// check that the logger still has FINER level and still
// has its handlers and that they haven't been reset.
manager.updateConfiguration((k) -> ((o, n) -> o));
assertEquals("FINER", manager.getProperty("com.foo.level"),
"com.foo.level set to FINER by updateConfiguration");
assertEquals(Level.FINER, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertEquals(null,
manager.getProperty("com.foo.handlers"),
"manager.getProperty(\"com.foo.handlers\")");
assertDeepEquals(new Handler[] {h}, logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
// Calls updateConfiguration with a lambda whose effect is to
// take all values from the new configuration.
// This should update the configuration to what is in configFile, so
// check that the new configuration has
// com.foo.level=FINEST
// com.foo.handlers=SimpleUpdateConfigurationTest$MyHandler
// check that the logger now has FINEST level
// and a new handler instance, since the old config
// had no handlers for com.foo and the new config has one.
manager.updateConfiguration((k) -> ((o, n) -> n));
assertEquals("FINEST", manager.getProperty("com.foo.level"),
"com.foo.level updated by updateConfiguration");
assertEquals(Level.FINEST, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertEquals(MyHandler.class.getName(),
manager.getProperty("com.foo.handlers"),
"manager.getProperty(\"com.foo.handlers\")");
Handler[] loggerHandlers = logger.getHandlers().clone();
assertEquals(1, loggerHandlers.length,
"Logger.getLogger(\"com.foo\").getHandlers().length");
assertEquals(MyHandler.class, loggerHandlers[0].getClass(),
"Logger.getLogger(\"com.foo\").getHandlers()[0].getClass()");
assertEquals(h.count + 1, ((MyHandler)logger.getHandlers()[0]).count,
"Logger.getLogger(\"com.foo\").getHandlers()[0].count");
// Calls updateConfiguration with a lambda whose effect is a noop.
// This should not change the current configuration, so
// check that the new configuration still has
// com.foo.level=FINEST
// com.foo.handlers=SimpleUpdateConfigurationTest$MyHandler
// check that the logger still has FINEST level and still
// has its handlers and that they haven't been reset.
manager.updateConfiguration((k) -> ((o, n) -> o));
assertDeepEquals(loggerHandlers, logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
assertEquals("FINEST", manager.getProperty("com.foo.level"),
"com.foo.level updated by updateConfiguration");
assertEquals(Level.FINEST, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertEquals(MyHandler.class.getName(),
manager.getProperty("com.foo.handlers"),
"manager.getProperty(\"com.foo.handlers\")");
// Calls updateConfiguration with a lambda whose effect is to
// take all values from the new configuration.
// Because the content of the configFile hasn't changed, then
// it should also be a noop.
// check that the new configuration still has
// com.foo.level=FINEST
// com.foo.handlers=SimpleUpdateConfigurationTest$MyHandler
// check that the logger still has FINEST level and still
// has its handlers and that they haven't been reset.
manager.updateConfiguration((k) -> ((o, n) -> n));
assertDeepEquals(loggerHandlers, logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
assertEquals("FINEST", manager.getProperty("com.foo.level"),
"com.foo.level updated by updateConfiguration");
assertEquals(Level.FINEST, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertEquals(MyHandler.class.getName(),
manager.getProperty("com.foo.handlers"),
"manager.getProperty(\"com.foo.handlers\")");
// Calls updateConfiguration with a null lambda, whose effect is to
// take all values from the new configuration.
// Because the content of the configFile hasn't changed, then
// it should also be a noop.
// check that the new configuration still has
// com.foo.level=FINEST
// com.foo.handlers=SimpleUpdateConfigurationTest$MyHandler
// check that the logger still has FINEST level and still
// has its handlers and that they haven't been reset.
manager.updateConfiguration((k) -> ((o, n) -> n));
assertDeepEquals(loggerHandlers, logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
assertEquals("FINEST", manager.getProperty("com.foo.level"),
"com.foo.level updated by updateConfiguration");
assertEquals(Level.FINEST, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertEquals(MyHandler.class.getName(),
manager.getProperty("com.foo.handlers"),
"manager.getProperty(\"com.foo.handlers\")");
// no remove com.foo.handlers=SimpleUpdateConfigurationTest$MyHandler
// from the configuration file.
props.remove("com.foo.handlers");
storePropertyToFile("config1", props);
// Calls updateConfiguration with a lambda whose effect is a noop.
// This should not change the current configuration, so
// check that the new configuration still has
// com.foo.level=FINEST
// com.foo.handlers=SimpleUpdateConfigurationTest$MyHandler
// check that the logger still has FINEST level and still
// has its handlers and that they haven't been reset.
manager.updateConfiguration((k) -> ((o, n) -> o));
assertDeepEquals(loggerHandlers, logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
assertEquals("FINEST", manager.getProperty("com.foo.level"),
"com.foo.level updated by updateConfiguration");
assertEquals(Level.FINEST, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertEquals(MyHandler.class.getName(),
manager.getProperty("com.foo.handlers"),
"manager.getProperty(\"com.foo.handlers\")");
// Calls updateConfiguration with a lambda whose effect is to
// take all values from the new configuration.
// This should update the configuration to what is in configFile, so
// check that the new configuration has
// com.foo.level=FINEST
// and nothing for com.foo.handlers
// check that the logger still has FINEST level
// and no handlers, since the old config
// had an handler for com.foo and the new config doesn't.
manager.updateConfiguration((k) -> ((o, n) -> n));
assertDeepEquals(new Handler[0], logger.getHandlers(),
"Logger.getLogger(\"com.foo\").getHandlers()");
assertEquals("FINEST", manager.getProperty("com.foo.level"),
"com.foo.level updated by updateConfiguration");
assertEquals(Level.FINEST, logger.getLevel(),
"Logger.getLogger(\"com.foo\").getLevel()");
assertEquals(null,
manager.getProperty("com.foo.handlers"),
"manager.getProperty(\"com.foo.handlers\")");
} catch (RuntimeException | Error r) {
throw r;
} catch (Exception x) {
throw new RuntimeException(x);
} finally {
if (configFile != null) {
// cleanup
final String file = configFile;
Configure.doPrivileged(() -> {
try {
Files.delete(Paths.get(file));
} catch (RuntimeException | Error r) {
throw r;
} catch (Exception x) {
throw new RuntimeException(x);
}
});
}
}
}
public static void main(String[] args) throws Exception {
execute(SimpleUpdateConfigurationTest::testUpdateConfiguration);
}
static class Configure {
static void updateConfigurationWith(Properties propertyFile,
Function<String,BiFunction<String,String,String>> remapper) {
try {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
propertyFile.store(bytes, propertyFile.getProperty("test.name"));
ByteArrayInputStream bais = new ByteArrayInputStream(bytes.toByteArray());
LogManager.getLogManager().updateConfiguration(bais, remapper);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
static void doPrivileged(Runnable run) {
run.run();
}
static <T> T callPrivileged(Callable<T> call) throws Exception {
return call.call();
}
}
static final class TestAssertException extends RuntimeException {
TestAssertException(String msg) {
super(msg);
}
}
private static void assertEquals(long expected, long received, String msg) {
if (expected != received) {
throw new TestAssertException("Unexpected result for " + msg
+ ".\n\texpected: " + expected
+ "\n\tactual: " + received);
} else {
System.out.println("Got expected " + msg + ": " + received);
}
}
private static void assertEquals(String expected, String received, String msg) {
if (!Objects.equals(expected, received)) {
throw new TestAssertException("Unexpected result for " + msg
+ ".\n\texpected: " + expected
+ "\n\tactual: " + received);
} else {
System.out.println("Got expected " + msg + ": " + received);
}
}
private static void assertEquals(Object expected, Object received, String msg) {
if (!Objects.equals(expected, received)) {
throw new TestAssertException("Unexpected result for " + msg
+ ".\n\texpected: " + expected
+ "\n\tactual: " + received);
} else {
System.out.println("Got expected " + msg + ": " + received);
}
}
public static String deepToString(Object o) {
if (o == null) {
return "null";
} else if (o.getClass().isArray()) {
String s;
if (o instanceof Object[])
s = Arrays.deepToString((Object[]) o);
else if (o instanceof byte[])
s = Arrays.toString((byte[]) o);
else if (o instanceof short[])
s = Arrays.toString((short[]) o);
else if (o instanceof int[])
s = Arrays.toString((int[]) o);
else if (o instanceof long[])
s = Arrays.toString((long[]) o);
else if (o instanceof char[])
s = Arrays.toString((char[]) o);
else if (o instanceof float[])
s = Arrays.toString((float[]) o);
else if (o instanceof double[])
s = Arrays.toString((double[]) o);
else if (o instanceof boolean[])
s = Arrays.toString((boolean[]) o);
else
s = o.toString();
return s;
} else {
return o.toString();
}
}
private static void assertDeepEquals(Object expected, Object received, String msg) {
if (!Objects.deepEquals(expected, received)) {
throw new TestAssertException("Unexpected result for " + msg
+ ".\n\texpected: " + deepToString(expected)
+ "\n\tactual: " + deepToString(received));
} else {
System.out.println("Got expected " + msg + ": " + deepToString(received));
}
}
}

View file

@ -0,0 +1,472 @@
/*
* 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.logging.FileHandler;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @test
* @bug 8033661 8189291
* @summary tests LogManager.updateConfiguration(bin)
* @modules java.logging/java.util.logging:open
* @run main/othervm UpdateConfigurationTest
* @author danielfuchs
*/
public class UpdateConfigurationTest {
// We will test the handling of abstract logger nodes with file handlers
public static void run(Properties propertyFile, boolean last) throws Exception {
test(propertyFile.getProperty("test.name"), propertyFile, last);
}
private static final String PREFIX =
"FileHandler-" + UUID.randomUUID() + ".log";
private static final String userDir = System.getProperty("user.dir", ".");
private static final boolean userDirWritable = Files.isWritable(Paths.get(userDir));
static enum ConfigMode { APPEND, REPLACE, DEFAULT;
boolean append() { return this == APPEND; }
Function<String, BiFunction<String,String,String>> remapper() {
switch(this) {
case APPEND:
return (k) -> ((o,n) -> (n == null ? o : n));
case REPLACE:
return (k) -> ((o,n) -> n);
}
return null;
}
}
private static final List<Properties> properties;
static {
// The test will be run with each of the configurations below.
// The 'child' logger is forgotten after each test
Properties props1 = new Properties();
props1.setProperty("test.name", "props1");
props1.setProperty("test.config.mode", ConfigMode.REPLACE.name());
props1.setProperty(FileHandler.class.getName() + ".pattern", PREFIX);
props1.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE));
props1.setProperty(FileHandler.class.getName() + ".level", "ALL");
props1.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter");
props1.setProperty("com.foo.handlers", FileHandler.class.getName());
props1.setProperty("com.bar.level", "FINEST");
Properties props2 = new Properties();
props2.setProperty("test.name", "props2");
props2.setProperty("test.config.mode", ConfigMode.DEFAULT.name());
props2.setProperty(FileHandler.class.getName() + ".pattern", PREFIX);
props2.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE));
props2.setProperty(FileHandler.class.getName() + ".level", "ALL");
props2.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter");
props2.setProperty("com.foo.handlers", FileHandler.class.getName());
props2.setProperty("com.foo.handlers.ensureCloseOnReset", "true");
props2.setProperty("com.level", "FINE");
Properties props3 = new Properties();
props3.setProperty("test.name", "props3");
props3.setProperty("test.config.mode", ConfigMode.APPEND.name());
props3.setProperty(FileHandler.class.getName() + ".pattern", PREFIX);
props3.setProperty(FileHandler.class.getName() + ".limit", String.valueOf(Integer.MAX_VALUE));
props3.setProperty(FileHandler.class.getName() + ".level", "ALL");
props3.setProperty(FileHandler.class.getName() + ".formatter", "java.util.logging.SimpleFormatter");
props3.setProperty("com.foo.handlers", ""); // specify "" to override the value in the previous conf
props3.setProperty("com.foo.handlers.ensureCloseOnReset", "false");
props3.setProperty("com.bar.level", "FINER");
properties = Collections.unmodifiableList(Arrays.asList(
props1, props2, props3, props1));
}
static Properties previous;
static Properties current;
static final Field propsField;
static {
LogManager manager = LogManager.getLogManager();
try {
propsField = LogManager.class.getDeclaredField("props");
propsField.setAccessible(true);
previous = current = (Properties) propsField.get(manager);
} catch (NoSuchFieldException | IllegalAccessException ex) {
throw new ExceptionInInitializerError(ex);
}
}
static Properties getProperties() {
try {
return (Properties) propsField.get(LogManager.getLogManager());
} catch (IllegalAccessException x) {
throw new RuntimeException(x);
}
}
static String trim(String value) {
return value == null ? null : value.trim();
}
/**
* Tests one of the configuration defined above.
* <p>
* This is the main test method (the rest is infrastructure).
* <p>
* Creates a child of the com.foo logger (com.foo.child), resets
* the configuration, and verifies that com.foo has no handler.
* Then reapplies the configuration and verifies that the handler
* for com.foo has been reestablished, depending on whether
* java.util.logging.LogManager.reconfigureHandlers is present and
* true.
* <p>
* Finally releases the logger com.foo.child, so that com.foo can
* be garbage collected, and the next configuration can be
* tested.
*/
static void test(ConfigMode mode, String name, Properties props, boolean last)
throws Exception {
// Then create a child of the com.foo logger.
Logger fooChild = Logger.getLogger("com.foo.child");
fooChild.info("hello world");
Logger barChild = Logger.getLogger("com.bar.child");
barChild.info("hello world");
ReferenceQueue<Logger> queue = new ReferenceQueue();
WeakReference<Logger> fooRef = new WeakReference<>(Logger.getLogger("com.foo"), queue);
if (fooRef.get() != fooChild.getParent()) {
throw new RuntimeException("Unexpected parent logger: "
+ fooChild.getParent() +"\n\texpected: " + fooRef.get());
}
WeakReference<Logger> barRef = new WeakReference<>(Logger.getLogger("com.bar"), queue);
if (barRef.get() != barChild.getParent()) {
throw new RuntimeException("Unexpected parent logger: "
+ barChild.getParent() +"\n\texpected: " + barRef.get());
}
Reference<? extends Logger> ref2;
int max = 10;
barChild = null;
System.gc();
while ((ref2 = queue.poll()) == null) {
System.gc();
Thread.sleep(100);
if (--max == 0) break;
}
Throwable failed = null;
try {
if (ref2 != null) {
String refName = ref2 == fooRef ? "fooRef" : ref2 == barRef ? "barRef" : "unknown";
if (ref2 != barRef) {
throw new RuntimeException("Unexpected logger reference cleared: " + refName);
} else {
System.out.println("Reference " + refName + " cleared as expected");
}
} else if (ref2 == null) {
throw new RuntimeException("Expected 'barRef' to be cleared");
}
// Now lets try to check that ref2 has expected handlers, and
// attempt to configure again.
String p = current.getProperty("com.foo.handlers", "").trim();
assertEquals(p.isEmpty() ? 0 : 1, fooChild.getParent().getHandlers().length,
"["+name+"] fooChild.getParent().getHandlers().length");
Configure.doPrivileged(() -> Configure.updateConfigurationWith(props, mode.remapper()));
String p2 = previous.getProperty("com.foo.handlers", "").trim();
assertEquals(p, p2, "["+name+"] com.foo.handlers");
String n = trim(props.getProperty("com.foo.handlers", null));
boolean hasHandlers = mode.append()
? (n == null ? !p.isEmpty() : !n.isEmpty())
: n != null && !n.isEmpty();
assertEquals( hasHandlers ? 1 : 0,
fooChild.getParent().getHandlers().length,
"["+name+"] fooChild.getParent().getHandlers().length"
+ "[p=\""+p+"\", n=" + (n==null?null:"\""+n+"\"") + "]");
checkProperties(mode, previous, current, props);
} catch (Throwable t) {
failed = t;
} finally {
if (last || failed != null) {
final Throwable suppressed = failed;
Configure.doPrivileged(LogManager.getLogManager()::reset);
Configure.doPrivileged(() -> {
try {
StringBuilder builder = new StringBuilder();
Files.list(Paths.get(userDir))
.filter((f) -> f.toString().contains(PREFIX))
.filter((f) -> f.toString().endsWith(".lck"))
.forEach((f) -> {
builder.append(f.toString()).append('\n');
});
if (!builder.toString().isEmpty()) {
throw new RuntimeException("Lock files not cleaned:\n"
+ builder.toString());
}
} catch(RuntimeException | Error x) {
if (suppressed != null) x.addSuppressed(suppressed);
throw x;
} catch(Exception x) {
if (suppressed != null) x.addSuppressed(suppressed);
throw new RuntimeException(x);
}
});
if (suppressed == null) {
// Now we need to forget the child, so that loggers are released,
// and so that we can run the test with the next configuration...
// No need to do that if failed!=null however, as the first
// ref might not have been cleared yet and failing here would
// hide the original failure.
fooChild = null;
System.out.println("Setting fooChild to: " + fooChild);
while ((ref2 = queue.poll()) == null) {
System.gc();
Thread.sleep(1000);
}
if (ref2 != fooRef) {
throw new RuntimeException("Unexpected reference: "
+ ref2 +"\n\texpected: " + fooRef);
}
if (ref2.get() != null) {
throw new RuntimeException("Referent not cleared: " + ref2.get());
}
System.out.println("Got fooRef after reset(), fooChild is " + fooChild);
}
}
}
if (failed != null) {
// should rarely happen...
throw new RuntimeException(failed);
}
}
private static void checkProperties(ConfigMode mode,
Properties previous, Properties current, Properties props) {
Set<String> set = new HashSet<>();
// Check that all property names from 'props' are in current.
set.addAll(props.stringPropertyNames());
set.removeAll(current.keySet());
if (!set.isEmpty()) {
throw new RuntimeException("Missing properties in current: " + set);
}
set.clear();
set.addAll(current.stringPropertyNames());
set.removeAll(previous.keySet());
set.removeAll(props.keySet());
if (!set.isEmpty()) {
throw new RuntimeException("Superfluous properties in current: " + set);
}
set.clear();
Stream<String> allnames =
Stream.concat(
Stream.concat(previous.stringPropertyNames().stream(),
props.stringPropertyNames().stream()),
current.stringPropertyNames().stream())
.collect(Collectors.toCollection(TreeSet::new))
.stream();
if (mode.append()) {
// Check that all previous property names are in current.
set.addAll(previous.stringPropertyNames());
set.removeAll(current.keySet());
if (!set.isEmpty()) {
throw new RuntimeException("Missing properties in current: " + set
+ "\n\tprevious: " + previous
+ "\n\tcurrent: " + current
+ "\n\tprops: " + props);
}
allnames.forEach((k) -> {
String p = previous.getProperty(k, "").trim();
String n = current.getProperty(k, "").trim();
if (props.containsKey(k)) {
assertEquals(props.getProperty(k), n, k);
} else {
assertEquals(p, n, k);
}
});
} else {
// Check that only properties from 'props' are in current.
set.addAll(current.stringPropertyNames());
set.removeAll(props.keySet());
if (!set.isEmpty()) {
throw new RuntimeException("Superfluous properties in current: " + set);
}
allnames.forEach((k) -> {
String p = previous.getProperty(k, "");
String n = current.getProperty(k, "");
if (props.containsKey(k)) {
assertEquals(props.getProperty(k), n, k);
} else {
assertEquals("", n, k);
}
});
}
}
public static void main(String... args) throws Exception {
try {
for (int i=0; i<properties.size();i++) {
Properties propertyFile = properties.get(i);
run(propertyFile, i == properties.size() - 1);
}
} finally {
if (userDirWritable) {
Configure.doPrivileged(() -> {
// cleanup - delete files that have been created
try {
Files.list(Paths.get(userDir))
.filter((f) -> f.toString().contains(PREFIX))
.forEach((f) -> {
try {
System.out.println("deleting " + f);
Files.delete(f);
} catch(Throwable t) {
System.err.println("Failed to delete " + f + ": " + t);
}
});
} catch(Throwable t) {
System.err.println("Cleanup failed to list files: " + t);
t.printStackTrace();
}
});
}
}
}
static class Configure {
static void updateConfigurationWith(Properties propertyFile,
Function<String,BiFunction<String,String,String>> remapper) {
try {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
propertyFile.store(bytes, propertyFile.getProperty("test.name"));
ByteArrayInputStream bais = new ByteArrayInputStream(bytes.toByteArray());
LogManager.getLogManager().updateConfiguration(bais, remapper);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
static void doPrivileged(Runnable run) {
Properties before = getProperties();
try {
run.run();
} finally {
Properties after = getProperties();
if (before != after) {
previous = before;
current = after;
}
}
}
static <T> T callPrivileged(Callable<T> call) throws Exception {
Properties before = getProperties();
try {
return call.call();
} finally {
Properties after = getProperties();
if (before != after) {
previous = before;
current = after;
}
}
}
}
@FunctionalInterface
public static interface FileHandlerSupplier {
public FileHandler test() throws Exception;
}
static final class TestAssertException extends RuntimeException {
TestAssertException(String msg) {
super(msg);
}
}
private static void assertEquals(long expected, long received, String msg) {
if (expected != received) {
throw new TestAssertException("Unexpected result for " + msg
+ ".\n\texpected: " + expected
+ "\n\tactual: " + received);
} else {
System.out.println("Got expected " + msg + ": " + received);
}
}
private static void assertEquals(String expected, String received, String msg) {
if (!Objects.equals(expected, received)) {
throw new TestAssertException("Unexpected result for " + msg
+ ".\n\texpected: " + expected
+ "\n\tactual: " + received);
} else {
System.out.println("Got expected " + msg + ": " + received);
}
}
public static void test(String name, Properties props, boolean last) throws Exception {
ConfigMode configMode = ConfigMode.valueOf(props.getProperty("test.config.mode"));
System.out.println("\nTesting: " + name + " mode=" + configMode);
if (!userDirWritable) {
throw new RuntimeException("Not writable: "+userDir);
}
switch(configMode) {
case REPLACE:
case APPEND:
case DEFAULT:
test(configMode, name, props, last); break;
default:
throw new RuntimeException("Unknwown mode: " + configMode);
}
}
}

View file

@ -0,0 +1,82 @@
/*
* 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.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
/**
* @test
* @bug 8152515
* @summary Checks that LinkageError are ignored when closing handlers
* during Shutdown.
* @build LinkageErrorTest
* @run main/othervm LinkageErrorTest
*/
public class LinkageErrorTest {
public static class TestHandler extends Handler {
private volatile boolean closed;
public TestHandler() {
INSTANCES.add(this);
}
@Override
public void publish(LogRecord record) {
}
@Override
public void flush() {
}
@Override
public void close() throws SecurityException {
closed = true;
try {
System.out.println(INSTANCES);
} catch (Throwable t) {
// ignore
}
throw new LinkageError();
}
@Override
public String toString() {
return super.toString() + "{closed=" + closed + '}';
}
private static final CopyOnWriteArrayList<Handler> INSTANCES
= new CopyOnWriteArrayList<>();
}
private static final Logger LOGGER = Logger.getLogger("test");
private static final Logger GLOBAL = Logger.getGlobal();
public static void main(String[] args) {
LOGGER.addHandler(new TestHandler());
LOGGER.addHandler(new TestHandler());
GLOBAL.addHandler(new TestHandler());
}
}

View file

@ -0,0 +1,103 @@
/*
* Copyright (c) 2013, 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.logging.Level;
import java.util.logging.Logger;
/**
* @test
* @bug 8026499
* @summary checks that Logger.getLogger("").setLevel() is working correctly.
* @modules java.base/sun.util.logging
* java.logging
* @build TestRootLoggerLevel
* @run main/othervm TestRootLoggerLevel
* @author danielfuchs
*/
public class TestRootLoggerLevel {
public static enum Loggers {
ROOT("", "root"),
GLOBAL(Logger.GLOBAL_LOGGER_NAME);
private final String name;
private final String displayName;
Loggers(String name) {
this(name, name);
}
Loggers(String name, String displayName) {
this.name = name;
this.displayName = displayName;
}
}
public static void main(String[] args) throws Exception {
for (Loggers log : Loggers.values()) {
System.out.println(log.displayName + ": "
+ Logger.getLogger(log.name)
+ ": " + Logger.getLogger(log.name).getLevel());
}
// Before the fix, setting the root logger level here had only
// a transient effect...
for (Loggers log : Loggers.values()) {
System.out.println("Logger.getLogger(\""
+ log.name
+ "\").setLevel(Level.FINEST);");
Logger.getLogger(log.name).setLevel(Level.FINEST);
System.out.println(log.displayName + ": "
+ Logger.getLogger(log.name)
+ ": " + Logger.getLogger(log.name).getLevel());
// At this point - the root logger should have a level which is
// FINEST - just check it here for sanity.
if (Logger.getLogger(log.name).getLevel() != Level.FINEST) {
throw new RuntimeException("Logger " + log.displayName
+ " level should be FINEST: "
+ Logger.getLogger(log.name).getLevel());
}
}
// Initializing some PlatformLogger caused the root logger
// level to be reset to INFO.
//
final Object platformLogger =
sun.util.logging.PlatformLogger.getLogger("java.foo");
System.out.println("Got platform logger: " + platformLogger);
for (Loggers log : Loggers.values()) {
// Check that the root logger still has the FINEST value assigned
// to it earlier.
System.out.println(log.displayName + ": "
+ Logger.getLogger(log.name)
+ ": " + Logger.getLogger(log.name).getLevel());
if (Logger.getLogger(log.name).getLevel() != Level.FINEST) {
throw new RuntimeException("Logger " + log.displayName
+ " level should be FINEST: "
+ Logger.getLogger(log.name).getLevel());
}
}
}
}

View file

@ -0,0 +1,272 @@
/*
* 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.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Phaser;
import java.util.concurrent.Semaphore;
import java.util.logging.Handler;
import java.util.logging.LogManager;
import java.util.logging.Logger;
/**
* @test
* @bug 7113878
* @summary This is not a test that will check that 7113878 is fixed, but
* rather a test that will invoke the modified code & try to verify
* that fixing 7113878 has not introduced some big regression.
* This test should pass, whether 7113878 is there or not.
* @run main/othervm TestLoggerNames
* @author danielfuchs
*/
public class TestLoggerNames {
static final class TestLogger extends java.util.logging.Logger {
final Semaphore sem = new Semaphore(0);
final Semaphore wait = new Semaphore(0);
public TestLogger(String name, String resourceBundleName) {
super(name, resourceBundleName);
}
@Override
public Handler[] getHandlers() {
boolean found = false;
try {
System.out.println("Entering "+getName()+" getHandlers()");
for (StackTraceElement ste : Thread.currentThread().getStackTrace()) {
if (LogManager.class.getName().equals(ste.getClassName())
&& "reset".equals(ste.getMethodName())) {
found = true;
System.out.println(getName()+" getHandlers() called by " + ste);
}
}
sem.release();
try {
System.out.println("TestLogger: Acquiring wait for "+getName());
wait.acquire();
try {
System.out.println("TestLogger: Acquired wait for "+getName());
return super.getHandlers();
} finally {
System.out.println("TestLogger: Releasing wait for "+getName());
wait.release();
}
} finally {
System.out.println("Unblocking "+getName());
sem.acquire();
System.out.println("Unblocked "+getName());
if (found) {
System.out.println("Reset will proceed...");
}
}
} catch (InterruptedException x) {
throw new IllegalStateException(x);
}
}
}
static volatile boolean stop;
static volatile Throwable resetFailed;
static volatile Throwable checkLoggerNamesFailed;
static volatile Phaser phaser = new Phaser(2);
static void checkLoggerNames(List<Logger> loggers) {
Enumeration<String> names = LogManager.getLogManager().getLoggerNames();
if (names instanceof Iterator) {
for (Iterator<?> it = Iterator.class.cast(names); it.hasNext(); ) {
try {
it.remove();
throw new RuntimeException("Iterator supports remove!");
} catch (UnsupportedOperationException x) {
System.out.println("OK: Iterator doesn't support remove.");
}
}
// We're not supposed to come here, but if we do then we
// need to rewind names...
names = LogManager.getLogManager().getLoggerNames();
}
List<String> loggerNames = Collections.list(names);
if (!loggerNames.contains("")) {
throw new RuntimeException("\"\"" +
" not found in " + loggerNames);
}
if (!loggerNames.contains("global")) {
throw new RuntimeException("global" +
" not found in " + loggerNames);
}
for (Logger l : loggers) {
if (!loggerNames.contains(l.getName())) {
throw new RuntimeException(l.getName() +
" not found in " + loggerNames);
}
}
System.out.println("Got all expected logger names");
}
public static void main(String[] args) throws InterruptedException {
TestLogger test = new TestLogger("com.foo.bar.zzz", null);
LogManager.getLogManager().addLogger(test);
try {
Logger.getLogger(null);
throw new RuntimeException("Logger.getLogger(null) didn't throw expected NPE");
} catch (NullPointerException x) {
System.out.println("Got expected NullPointerException for Logger.getLogger(null)");
}
List<Logger> loggers = new CopyOnWriteArrayList<>();
loggers.add(Logger.getLogger("one.two.addMeAChild"));
loggers.add(Logger.getLogger("aaa.bbb.replaceMe"));
loggers.add(Logger.getLogger("bbb.aaa.addMeAChild"));
if (test != Logger.getLogger("com.foo.bar.zzz")) {
throw new AssertionError("wrong logger returned");
}
loggers.add(Logger.getLogger("ddd.aaa.addMeAChild"));
checkLoggerNames(loggers);
Thread loggerNamesThread = new Thread(() -> {
try {
while (!stop) {
// Make a defensive copy of the list of loggers that we pass
// to checkLoggerNames - in order to make sure that
// we won't see new loggers that might appear after
// checkLoggerNames has called LogManager.getLoggerNames().
// ('loggers' is a live list and the main thread adds and
// and removes loggers from it concurrently to this thread).
checkLoggerNames(new ArrayList<>(loggers));
Thread.sleep(10);
if (!stop) {
phaser.arriveAndAwaitAdvance();
}
}
} catch (Throwable t) {
t.printStackTrace(System.err);
checkLoggerNamesFailed = t;
// makes sure the main thread isn't going to wait
// forever waiting for this dead thread to arrive at the
// phaser.
phaser.arrive();
}
}, "loggerNames");
Thread resetThread = new Thread(() -> {
try {
System.out.println("Calling reset...");
LogManager.getLogManager().reset();
System.out.println("Reset done...");
System.out.println("Reset again...");
LogManager.getLogManager().reset();
System.out.println("Reset done...");
} catch(Throwable t) {
resetFailed = t;
System.err.println("Unexpected exception or error in reset Thread");
t.printStackTrace(System.err);
}
}, "reset");
resetThread.setDaemon(true);
resetThread.start();
System.out.println("Waiting for reset to get handlers");
test.sem.acquire();
try {
loggerNamesThread.start();
System.out.println("Reset has called getHandlers on " + test.getName());
int i = 0;
for (Enumeration<String> e = LogManager.getLogManager().getLoggerNames();
e.hasMoreElements();) {
String name = e.nextElement();
if (name.isEmpty()) continue;
if (name.endsWith(".addMeAChild")) {
Logger l = Logger.getLogger(name+".child");
loggers.add(l);
System.out.println("*** Added " + l.getName());
i++;
} else if (name.endsWith("replaceMe")) {
Logger l = Logger.getLogger(name);
loggers.remove(l);
l = Logger.getLogger(name.replace("replaceMe", "meReplaced"));
loggers.add(l);
// Give some chance to the loggerNames thread to arrive at the
// phaser first. We don't really care if the logger is actually
// replaced this turn - or the next.
Thread.sleep(100);
System.gc();
if (LogManager.getLogManager().getLogger(name) == null) {
// The original logger may not have been gc'ed yet, since
// it could still be referenced by the list being processed
// by the loggerNamesThread, if that thread hasn't arrived
// to the phaser yet...
System.out.println("*** "+ name
+ " successfully replaced with " + l.getName());
}
i++;
} else {
System.out.println("Nothing to do for logger: " + name);
}
if (checkLoggerNamesFailed != null) {
// The checkLoggerNames thread failed. No need to
// continue.
break;
}
// Waits for the checkLoggerNamesThread to arrive
phaser.arriveAndAwaitAdvance();
if (i >= 3 && i++ == 3) {
System.out.println("Loggers are now: " +
Collections.list(LogManager.getLogManager().getLoggerNames()));
test.wait.release();
test.sem.release();
System.out.println("Joining " + resetThread);
resetThread.join();
}
}
} catch (RuntimeException | InterruptedException | Error x) {
test.wait.release();
test.sem.release();
throw x;
} finally {
stop = true;
phaser.arriveAndDeregister();
loggerNamesThread.join();
loggers.clear();
}
if (resetFailed != null || checkLoggerNamesFailed != null) {
RuntimeException r = new RuntimeException("Some of the concurrent threads failed");
if (resetFailed != null) r.addSuppressed(resetFailed);
if (checkLoggerNamesFailed != null) r.addSuppressed(checkLoggerNamesFailed);
throw r;
}
}
}

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2013, 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.logging.*;
/*
* @test
* @bug 8010727
* @summary LogManager.addLogger should succeed to add a logger named ""
* if LogManager.getLogger("") returns null.
*
* @run main LogManagerInstanceTest
*/
public class LogManagerInstanceTest {
public static void main(String[] argv) {
LogManager mgr = LogManager.getLogManager();
if (getRootLogger(mgr) == null) {
throw new RuntimeException("Root logger not exist");
}
SecondLogManager mgr2 = new SecondLogManager();
Logger root = getRootLogger(mgr2);
if (mgr2.base != root) {
throw new RuntimeException(mgr2.base + " is not the root logger");
}
}
private static Logger getRootLogger(LogManager mgr) {
Logger l = mgr.getLogger("");
if (l != null && !l.getName().isEmpty()) {
throw new RuntimeException(l.getName() + " is not an invalid root logger");
}
return l;
}
static class SecondLogManager extends LogManager {
final Logger base;
private SecondLogManager() {
Logger root = getLogger("");
if (root == null) {
root = new BaseLogger("", null);
if (!super.addLogger(root))
throw new RuntimeException("Fail to addLogger " + root);
} else {
throw new RuntimeException("Root logger already exists");
}
this.base = root;
}
}
static class BaseLogger extends Logger {
BaseLogger(String name, String rbname) {
super(name, rbname);
}
}
}

View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 2020, 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 8245302
* @summary test the relationship between
* thread id long and int methods
* @build LogRecordThreadIdTest
* @run testng/othervm LogRecordThreadIdTest
*/
import java.util.logging.Level;
import java.util.logging.LogRecord;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotEquals;
public class LogRecordThreadIdTest {
LogRecord record, record1, record2;
@BeforeTest
public void setUp() throws Exception {
record = new LogRecord(Level.INFO, "record");
record1 = new LogRecord(Level.INFO, "record1");
record2 = new LogRecord(Level.INFO, "record2");
}
/**
* Tests threadID setter methods for consistency
* with longThreadID
*/
@Test
public void testSetThreadId() {
record.setThreadID(Integer.MAX_VALUE - 20);
record1.setThreadID(Integer.MAX_VALUE - 1);
assertEquals(record.getLongThreadID(), Integer.MAX_VALUE - 20L);
assertEquals(record.getThreadID(), Integer.MAX_VALUE - 20);
assertEquals(record1.getThreadID(), Integer.MAX_VALUE - 1);
assertEquals(record1.getLongThreadID(), Integer.MAX_VALUE - 1);
}
/**
* Tests longThreadID methods for consistency
* with threadID
*/
@Test
public void testSetLongThreadId() {
record.setLongThreadID(Integer.MAX_VALUE - 20L);
record1.setLongThreadID(Integer.MAX_VALUE + 10L);
record2.setLongThreadID(Integer.MAX_VALUE);
assertEquals(record.getThreadID(), Integer.MAX_VALUE - 20);
assertEquals(record.getLongThreadID(), Integer.MAX_VALUE - 20L);
assertNotEquals(record1.getThreadID(), Integer.MAX_VALUE + 10L);
assertEquals(record1.getLongThreadID(), Integer.MAX_VALUE + 10L);
assertEquals(record2.getThreadID(), Integer.MAX_VALUE);
assertEquals(record2.getLongThreadID(), Integer.MAX_VALUE);
}
}

View file

@ -0,0 +1,167 @@
/*
* Copyright (c) 2020, 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.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
/**
* @test
* @bug 8239013
* @summary This test verifies that the Logger cache does
* not keep a strong reference on dynamically loaded resource
* bundles
* @build BundleTest MyBundle LoggingApp
* @run main/othervm -Xmx64M -Djava.util.logging.config.file=logging.properties BundleTest
*/
public class BundleTest {
// My handler is used to get at the published LogRecords
public static class MyHandler extends Handler {
final List<LogRecord> records = new CopyOnWriteArrayList<>();
@Override
public void publish(LogRecord record) {
records.add(record);
}
@Override
public void flush() { }
@Override
public void close() throws SecurityException { }
}
public static void main(String[] args) throws Exception {
// copy classes and resource files
List<URL> classes = setUp();
// create an URL class loader that contains a copy of
// LoggingApp and MyBundle classes
URLClassLoader loader = new URLClassLoader(classes.toArray(new URL[0]), null);
Class<?> appClass = Class.forName(LoggingApp.class.getName(), true, loader);
Class<?> bundleClass = Class.forName(MyBundle.class.getName(), true, loader);
if (bundleClass.getClassLoader() != loader) {
throw new AssertionError("bundleClass not loaded by URL loader");
}
// Invoke LoggingApp.logger() which will create a logger
// that uses a "MyBundle" bundle, and then log a message
// to force the logger to load up an instance of the
// MyBundle class in its catalog cache.
Logger logger;
ReferenceQueue<Object> queue = new ReferenceQueue<>();
Reference<Object> loaderRef = new PhantomReference<>(loader, queue);
MyHandler handler = new MyHandler();
Thread currentThread = Thread.currentThread();
ClassLoader context = currentThread.getContextClassLoader();
currentThread.setContextClassLoader(loader);
try {
logger = (Logger) appClass.getMethod("logger").invoke(null);
logger.addHandler(handler);
logger.fine("foo");
ResourceBundle rb = handler.records.get(0).getResourceBundle();
// verify that the class of the resource bundle is the
// class loaded earlier by 'loader'
if (rb.getClass() != bundleClass) {
throw new AssertionError("unexpected loader for: " + rb.getClass());
}
// At this point the logger has a reference to an instance
// of the MyBundle class loaded by 'loader' in its catalog cache.
// This is demonstrated by the presence of that bundle in the
// LogRecord.
} finally {
currentThread.setContextClassLoader(context);
}
// cleanup all things that might reference 'loader'
appClass = bundleClass = null;
loader = null;
handler.records.clear();
// now try to trigger a full GC to force the cleanup
// of soft caches. If the logger has a strong reference
// to MyBundle, this will eventually cause an
// OutOfMemoryError, and the test will fail.
Reference<?> ref;
System.gc();
List<byte[]> memory = new ArrayList<>();
boolean stop = false;
System.out.println("Waiting for URL loader to be GC'ed");
long timeout = 100;
while ((ref = queue.remove(timeout)) == null) {
if (stop) break;
try {
// eat memory to trigger cleaning of SoftReference
memory.add(new byte[1024*1024]);
System.out.printf("Total memory added: %s Mb%n", memory.size());
} catch (OutOfMemoryError oome) {
stop = true;
memory = null;
timeout = 1000; // give more time for the last GC
}
System.gc();
}
memory = null;
if (stop) {
System.out.println("no more memory...");
}
// Verify that loader was GC'ed
if (ref != loaderRef) {
throw new AssertionError("Loader was not GC'ed");
}
System.out.println("Loader was GC'ed");
Reference.reachabilityFence(logger);
}
static String file(Class<?> type) {
return type.getSimpleName() + ".class";
}
public static List<URL> setUp() throws IOException {
String classes = System.getProperty("test.classes", "build");
String cwd = System.getProperty("user.dir", ".");
String sources = System.getProperty("test.src", "src");
for (var type : List.of(LoggingApp.class, MyBundle.class)) {
var from = Path.of(classes, file(type));
var to = Path.of(cwd, file(type));
Files.copy(from, to, StandardCopyOption.REPLACE_EXISTING);
}
Files.copy(Path.of(sources, "logging.properties"),
Path.of(cwd, "logging.properties"),
StandardCopyOption.REPLACE_EXISTING);
return List.of(Path.of(cwd).toUri().toURL());
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2020, 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.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;
public class LoggingApp {
private static final Lock lock = new ReentrantLock();
private static volatile Logger logger;
public static Logger logger() {
Logger logger = LoggingApp.logger;
if (logger != null) return logger;
lock.lock();
try {
if ((logger = LoggingApp.logger) != null) return logger;
LoggingApp.logger = logger =
Logger.getLogger("com.example", "MyBundle");
} finally {
lock.unlock();
}
return logger;
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2020, 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.ResourceBundle;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class MyBundle extends ResourceBundle {
Map<String, String> mapping = new ConcurrentHashMap<>();
/**
* Gets an object for the given key from this resource bundle.
* Returns null if this resource bundle does not contain an
* object for the given key.
*
* @param key the key for the desired object
* @throws NullPointerException if {@code key} is {@code null}
* @return the object for the given key, or null
*/
protected Object handleGetObject(String key) {
return mapping.computeIfAbsent(key,
(k) -> k + "-" + System.identityHashCode(this.getClass().getClassLoader()));
}
/**
* Returns an enumeration of the keys.
*
* @return an {@code Enumeration} of the keys contained in
* this {@code ResourceBundle} and its parent bundles.
*/
public Enumeration<String> getKeys() {
return Collections.enumeration(mapping.keySet());
}
}

View file

@ -0,0 +1,9 @@
handlers= java.util.logging.ConsoleHandler
.level= INFO
# Limit the message that are printed on the console to INFO and above.
java.util.logging.ConsoleHandler.level = ALL
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
com.example.level = ALL
com.example.handlers = java.util.logging.ConsoleHandler

View file

@ -0,0 +1,172 @@
/*
* 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.
*/
import java.io.ByteArrayOutputStream;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import java.util.logging.StreamHandler;
/**
* @test
* @bug 8028788
* @summary tests that the message format string is correctly constructed
* by Logger.entering
* @run main/othervm LoggerEnteringWithParams
* @author daniel fuchs
*/
public class LoggerEnteringWithParams {
static final Object[] data = {
"one", "two", "three", "four", "five", "six", "seven", "eight"
};
static final class TestHandler extends Handler {
final List<LogRecord> events = new CopyOnWriteArrayList<>();
final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
final StreamHandler wrapped = new StreamHandler(bytes, new SimpleFormatter());
@Override
public void publish(LogRecord record) {
events.add(record);
wrapped.publish(record);
wrapped.flush();
}
@Override
public void flush() {
wrapped.flush();
}
@Override
public void close() throws SecurityException {
wrapped.close();
}
@Override
public synchronized void setLevel(Level newLevel) throws SecurityException {
super.setLevel(newLevel);
wrapped.setLevel(newLevel);
}
public void reset() {
bytes.reset();
events.clear();
}
}
public static void main(String[] args) {
Logger logger = Logger.getLogger("some.logger");
TestHandler handler = new TestHandler();
logger.setUseParentHandlers(false);
handler.setLevel(Level.ALL);
logger.setLevel(Level.FINEST);
logger.addHandler(handler);
// Auto-detect the line termination used by SimpleFormatter - (CR vs CRLF)
logger.entering("test", "test");
final String test = handler.bytes.toString();
System.out.println(test);
final String lineEnd = test.substring(test.indexOf("ENTRY")+"ENTRY".length());
System.out.print("Line termination is " + lineEnd.length() + " char(s) long:");
for (int i=0; i<lineEnd.length(); i++) {
System.out.print(" code="+(int)lineEnd.charAt(i));
}
System.out.println();
handler.reset();
for (int i=0 ; i<=data.length; i++) {
final StringBuilder b = new StringBuilder("ENTRY");
final StringBuilder f = new StringBuilder("ENTRY");
final Object[] params = new Object[i];
for (int k=0; k<i; k++) {
params[k] = data[k];
b.append(' ').append(String.valueOf(params[k]));
f.append(" {").append(String.valueOf(k)).append('}');
}
final String expected = b.toString();
final String format = f.toString();
final String className = "class"+i;
final String methodName = "method"+i;
logger.entering(className, methodName, params);
final String logged = handler.bytes.toString();
System.out.println("got:\n" + logged);
if (!logged.contains(className)) {
throw new RuntimeException("Marker for " + className
+ " not found."
+ "\n\tgot: <<\n" + logged + "\t >>");
}
if (!logged.contains(methodName)) {
throw new RuntimeException("Marker for " + methodName
+ " not found."
+ "\n\tgot: <<\n" + logged + "\t >>");
}
if (!logged.contains(expected)) {
throw new RuntimeException("Marker for parameters[size="
+ i + "] not found"
+ "\n\tgot: <<\n" + logged + "\t >>");
}
if (!logged.contains(expected+lineEnd)) {
throw new RuntimeException("Marker for parameters[size="
+ i + "] has extra characters"
+ "\n\tgot: <<\n" + logged + "\t >>");
}
LogRecord record = handler.events.remove(0);
if (!handler.events.isEmpty()) {
throw new RuntimeException("Handler has more log records: "
+ handler.events.toString());
}
if (!className.equals(record.getSourceClassName())) {
throw new RuntimeException("Unexpected class name in LogRecord."
+ "\n\texpected: " + className
+ "\n\tgot: " + record.getSourceClassName());
}
if (!methodName.equals(record.getSourceMethodName())) {
throw new RuntimeException("Unexpected method name in LogRecord."
+ "\n\texpected: " + methodName
+ "\n\tgot: " + record.getSourceMethodName());
}
if (!format.equals(record.getMessage())) {
throw new RuntimeException("Unexpected message format in LogRecord."
+ "\n\texpected: " + format
+ "\n\tgot: " + record.getMessage());
}
if (!Arrays.deepEquals(params, record.getParameters())) {
throw new RuntimeException("Unexpected parameter array in LogRecord."
+ "\n\texpected: " + Arrays.toString(params)
+ "\n\tgot: " + Arrays.toString(record.getParameters()));
}
handler.reset();
}
}
}

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2013, 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.List;
import java.util.logging.Logger;
/**
* @test
* @bug 7184195
* @summary checks that java.util.logging.Logger.getGlobal().info() logs without configuration
* @build TestGetGlobal testgetglobal.HandlerImpl testgetglobal.LogManagerImpl1 testgetglobal.LogManagerImpl2 testgetglobal.LogManagerImpl3 testgetglobal.BadLogManagerImpl testgetglobal.DummyLogManagerImpl
* @run main/othervm TestGetGlobal
* @run main/othervm -Djava.util.logging.manager=testgetglobal.LogManagerImpl1 TestGetGlobal
* @run main/othervm -Djava.util.logging.manager=testgetglobal.LogManagerImpl2 TestGetGlobal
* @run main/othervm -Djava.util.logging.manager=testgetglobal.LogManagerImpl3 TestGetGlobal
* @run main/othervm -Djava.util.logging.manager=testgetglobal.BadLogManagerImpl TestGetGlobal
* @run main/othervm -Djava.util.logging.manager=testgetglobal.DummyLogManagerImpl TestGetGlobal
* @author danielfuchs
*/
public class TestGetGlobal {
static final String[] messages = {
"1. This message should not appear on the console.",
"2. This message should appear on the console.",
"3. This message should now appear on the console too."
};
static {
System.setProperty("java.util.logging.config.file",
System.getProperty("test.src", ".") + java.io.File.separator + "logging.properties");
}
public static void main(String... args) {
final String manager = System.getProperty("java.util.logging.manager", null);
final String description = "TestGetGlobal"
+ (manager == null ? "" : "-Djava.util.logging.manager=" + manager);
Logger.global.info(messages[0]); // at this point LogManager is not
// initialized yet, so this message should not appear.
Logger.getGlobal().info(messages[1]); // calling getGlobal() will
// initialize the LogManager - and thus this message should appear.
Logger.global.info(messages[2]); // Now that the LogManager is
// initialized, this message should appear too.
final List<String> expected = Arrays.asList(Arrays.copyOfRange(messages, 1, messages.length));
if (!testgetglobal.HandlerImpl.received.equals(expected)) {
System.err.println("Test case failed: " + description);
throw new Error("Unexpected message list: "+testgetglobal.HandlerImpl.received+" vs "+ expected
+ "\n\t"+description);
}
}
}

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2013, 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.List;
import java.util.logging.Logger;
/**
* @test
* @bug 7184195
* @summary checks that java.util.logging.Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).info() logs without configuration
* @build TestGetGlobalByName testgetglobal.HandlerImpl testgetglobal.LogManagerImpl1 testgetglobal.LogManagerImpl2 testgetglobal.LogManagerImpl3 testgetglobal.BadLogManagerImpl testgetglobal.DummyLogManagerImpl
* @run main/othervm TestGetGlobalByName
* @run main/othervm -Djava.util.logging.manager=testgetglobal.LogManagerImpl1 TestGetGlobalByName
* @run main/othervm -Djava.util.logging.manager=testgetglobal.LogManagerImpl2 TestGetGlobalByName
* @run main/othervm -Djava.util.logging.manager=testgetglobal.LogManagerImpl3 TestGetGlobalByName
* @run main/othervm -Djava.util.logging.manager=testgetglobal.BadLogManagerImpl TestGetGlobalByName
* @run main/othervm -Djava.util.logging.manager=testgetglobal.DummyLogManagerImpl TestGetGlobalByName
* @author danielfuchs
*/
public class TestGetGlobalByName {
static final String[] messages = {
"1. This message should not appear on the console.",
"2. This message should appear on the console.",
"3. This message should now appear on the console too."
};
static {
System.setProperty("java.util.logging.config.file",
System.getProperty("test.src", ".") + java.io.File.separator + "logging.properties");
}
public static void main(String... args) {
Logger.global.info(messages[0]); // at this point LogManager is not
// initialized yet, so this message should not appear.
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).info(messages[1]); // calling getLogger() will
// initialize the LogManager - and thus this message should appear.
Logger.global.info(messages[2]); // Now that the LogManager is
// initialized, this message should appear too.
final List<String> expected = Arrays.asList(Arrays.copyOfRange(messages, 1, messages.length));
if (!testgetglobal.HandlerImpl.received.equals(expected)) {
throw new Error("Unexpected message list: "+testgetglobal.HandlerImpl.received+" vs "+ expected);
}
}
}

View file

@ -0,0 +1,181 @@
/*
* Copyright (c) 2013, 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.List;
import java.util.logging.Logger;
/**
* @test
* @bug 7184195 8021003
* @summary Test that the global logger can log with no configuration when accessed from multiple threads.
* @build TestGetGlobalConcurrent testgetglobal.HandlerImpl testgetglobal.LogManagerImpl1 testgetglobal.LogManagerImpl2 testgetglobal.LogManagerImpl3 testgetglobal.BadLogManagerImpl testgetglobal.DummyLogManagerImpl
* @run main/othervm TestGetGlobalConcurrent
* @run main/othervm -Djava.util.logging.manager=testgetglobal.LogManagerImpl1 TestGetGlobalConcurrent
* @run main/othervm -Djava.util.logging.manager=testgetglobal.LogManagerImpl2 TestGetGlobalConcurrent
* @run main/othervm -Djava.util.logging.manager=testgetglobal.LogManagerImpl3 TestGetGlobalConcurrent
* @run main/othervm -Djava.util.logging.manager=testgetglobal.BadLogManagerImpl TestGetGlobalConcurrent
* @run main/othervm -Djava.util.logging.manager=testgetglobal.DummyLogManagerImpl TestGetGlobalConcurrent
* @author danielfuchs
*/
public class TestGetGlobalConcurrent {
static final String[] messages = {
"1. This message should not appear on the console.",
"2. This message should appear on the console.",
"3. This message should now appear on the console too.",
"4. This message should appear on the console.",
"5. This message should now appear on the console too.",
"6. This message should appear on the console.",
"7. This message should now appear on the console too.",
"8. This message should appear on the console.",
"9. This message should now appear on the console too."
};
static {
System.setProperty("java.util.logging.config.file",
System.getProperty("test.src", ".") + java.io.File.separator + "logging.properties");
}
public static void test1() {
final int nb = 1;
final int i = 2*nb + 1;
Logger.getGlobal().info(messages[i]); // calling getGlobal() will
// initialize the LogManager - and thus this message should appear.
Logger.global.info(messages[i+1]); // Now that the LogManager is
// initialized, this message should appear too.
final List<String> expected = Arrays.asList(Arrays.copyOfRange(messages, i, i+2));
if (!testgetglobal.HandlerImpl.received.containsAll(expected)) {
fail(new Error("Unexpected message list: "+testgetglobal.HandlerImpl.received+" vs "+ expected));
}
}
public static void test2() {
final int nb = 2;
final int i = 2*nb + 1;
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).info(messages[i]); // calling getGlobal() will
// initialize the LogManager - and thus this message should appear.
Logger.global.info(messages[i+1]); // Now that the LogManager is
// initialized, this message should appear too.
final List<String> expected = Arrays.asList(Arrays.copyOfRange(messages, i, i+2));
if (!testgetglobal.HandlerImpl.received.containsAll(expected)) {
fail(new Error("Unexpected message list: "+testgetglobal.HandlerImpl.received+" vs "+ expected));
}
}
public static void test3() {
final int nb = 3;
final int i = 2*nb + 1;
java.util.logging.LogManager.getLogManager();
Logger.getGlobal().info(messages[i]); // calling getGlobal() will
// initialize the LogManager - and thus this message should appear.
Logger.global.info(messages[i+1]); // Now that the LogManager is
// initialized, this message should appear too.
final List<String> expected = Arrays.asList(Arrays.copyOfRange(messages, i, i+2));
if (!testgetglobal.HandlerImpl.received.containsAll(expected)) {
fail(new Error("Unexpected message list: "+testgetglobal.HandlerImpl.received+" vs "+ expected));
}
}
public static void test4() {
log = new MyLogger("foo.bar");
java.util.logging.LogManager.getLogManager().addLogger(log);
}
private static volatile Throwable failed = null;
private static volatile Logger log = null;
public static class MyLogger extends Logger {
public MyLogger(String name) {
super(name, null);
}
}
public static void fail(Throwable failure) {
failure.printStackTrace();
if (failed == null) failed = failure;
}
public static class WaitAndRun implements Runnable {
private final Runnable run;
public WaitAndRun(Runnable run) {
this.run = run;
}
public void run() {
try {
Thread.sleep(10);
run.run();
} catch (Exception | Error x) {
fail(x);
}
}
}
static final class Run1 implements Runnable {
public void run() { test1(); }
}
static final class Run2 implements Runnable {
public void run() { test2(); }
}
static final class Run3 implements Runnable {
public void run() { test3(); }
}
static final class Run4 implements Runnable {
public void run() { test4(); }
}
static String description = "Unknown";
public static void main(String... args) throws Exception {
final String manager = System.getProperty("java.util.logging.manager", null);
description = "TestGetGlobalConcurrent"
+ (manager == null ? "" : "-Djava.util.logging.manager=" + manager);
final Thread t1 = new Thread(new WaitAndRun(new Run1()), "test1");
final Thread t2 = new Thread(new WaitAndRun(new Run2()), "test2");
final Thread t3 = new Thread(new WaitAndRun(new Run3()), "test3");
final Thread t4 = new Thread(new WaitAndRun(new Run4()), "test4");
t1.setDaemon(true); t2.setDaemon(true); t3.setDaemon(true); t4.setDaemon(true);
t1.start(); t2.start(); t3.start(); t4.start();
Thread.sleep(10);
Logger.getGlobal().info(messages[1]); // calling getGlobal() will
// initialize the LogManager - and thus this message should appear.
Logger.global.info(messages[2]); // Now that the LogManager is
// initialized, this message should appear too.
final List<String> expected = Arrays.asList(Arrays.copyOfRange(messages, 1, 3));
if (!testgetglobal.HandlerImpl.received.containsAll(expected)) {
fail(new Error("Unexpected message list: "+testgetglobal.HandlerImpl.received+" vs "+ expected));
}
t1.join(); t2.join(); t3.join(); t4.join();
if (failed != null) {
throw new Error("Test failed: "+description, failed);
}
System.out.println("Test passed");
}
}

View file

@ -0,0 +1,2 @@
.level=INFO
handlers=testgetglobal.HandlerImpl

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2013, 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 testgetglobal;
import java.util.logging.LogManager;
import java.util.logging.Logger;
/**
* This class is used to verify that calling Logger.getLogger(Logger.GLOBAL_LOGGER_NAME)
* in the constructor of a LogManager subclass installed as default
* LogManager does not cause issues beyond throwing the expected NPE.
* In that case the default LogManager class will simply be used.
* @author danielfuchs
*/
public class BadLogManagerImpl extends LogManager {
final Logger globalLogger;
public BadLogManagerImpl() {
// The call below should generate an NPE, which will be
// catched in LogManager initializer.
globalLogger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
System.err.println("Global is: " + globalLogger);
throw new Error("Should not have reached here");
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2013, 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 testgetglobal;
import java.util.logging.LogManager;
import java.util.logging.Logger;
/**
* A dummy LogManager subclass that does nothing beyond extending LogManager.
* @author danielfuchs
*/
public class DummyLogManagerImpl extends LogManager {
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2013, 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 testgetglobal;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.List;
import java.util.logging.ConsoleHandler;
import java.util.logging.LogRecord;
/**
*
* @author danielfuchs
*/
public class HandlerImpl extends ConsoleHandler {
public static final List<String> received = new CopyOnWriteArrayList<>();
public HandlerImpl() {
}
@Override
public void publish(LogRecord record) {
received.add(record.getMessage());
super.publish(record);
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2013, 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 testgetglobal;
import java.util.logging.LogManager;
import java.util.logging.Logger;
/**
* This class is used to verify that calling Logger.getGlobal() in the static
* initializer of a LogManager subclass installed as default LogManager
* does not cause issues.
* @author danielfuchs
*/
public class LogManagerImpl1 extends LogManager {
static final Logger global;
static {
global = Logger.getGlobal();
System.err.println("Global is: " + global);
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2013, 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 testgetglobal;
import java.util.logging.LogManager;
import java.util.logging.Logger;
/**
* This class is used to verify that calling Logger.getGlobal() in the constructor
* initializer of a LogManager subclass installed as default LogManager
* does not cause issues.
* @author danielfuchs
*/
public class LogManagerImpl2 extends LogManager {
final Logger globalLogger;
public LogManagerImpl2() {
globalLogger = Logger.getGlobal();
System.err.println("Global is: " + globalLogger);
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2013, 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 testgetglobal;
import java.util.logging.LogManager;
import java.util.logging.Logger;
/**
* This class is used to verify that calling Logger.getLogger(Logger.GLOBAL_LOGGER_NAME)
* in the static initializer of a LogManager subclass installed as default
* LogManager does not cause issues beyond throwing the expected NPE.
* @author danielfuchs
*/
public class LogManagerImpl3 extends LogManager {
static final Logger global;
static {
Logger g = null;
try {
g = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
throw new Error("Should not have reached here");
} catch (Exception x) {
// This is to be expected: Logger.getLogger(Logger.GLOBAL_LOGGER_NAME)
// will call LogManager.getLogManager() which will return null, since
// we haven't manage to do new LogManagerImpl3() yet.
//
System.err.println("Got expected exception - you cannot call"
+ " Logger.getLogger(Logger.GLOBAL_LOGGER_NAME)"
+ " in LogManager subclass static initializer: " + x);
x.printStackTrace();
}
if (g == null) {
g = Logger.getGlobal();
}
global = g;
System.err.println("Global is: " + global);
}
}

View file

@ -0,0 +1,388 @@
/*
* 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.lang.reflect.Method;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Objects;
import java.util.Queue;
import java.util.ResourceBundle;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
/**
* @test
* @bug 8152389
* @summary Verify the correct behavior of LogRecord.inferCaller() in particular
* when a message is directly logged through the root logger.
* @run main/othervm TestInferCaller
* @author danielfuchs
*/
public class TestInferCaller {
static final class LogEvent {
public final String className;
public final String methodName;
public final LogRecord record;
public LogEvent(String className, String methodName, LogRecord record) {
this.className = className;
this.methodName = methodName;
this.record = record;
}
}
static final class TestHandler extends Handler {
public static final Queue<LogEvent> PUBLISHED = new LinkedList<LogEvent>();
public TestHandler() {
initLevel(Level.ALL);
}
@Override
public void close() throws SecurityException { }
@Override
public void publish(LogRecord record) {
LogEvent event = new LogEvent(record.getSourceClassName(),
record.getSourceMethodName(),
record);
PUBLISHED.add(event);
}
@Override
public void flush() {}
private void initLevel(Level newLevel) {
super.setLevel(newLevel);
}
}
public void test1(Logger logger) {
System.out.println("test1: " + loggerName(logger));
AtomicInteger count = new AtomicInteger();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
logger.setLevel(Level.ALL);
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
logger.severe("message " + count.incrementAndGet());
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
LogEvent event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), "test1", "message " + count.get());
logger.warning("message " + count.incrementAndGet());
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), "test1", "message " + count.get());
logger.info("message " + count.incrementAndGet());
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), "test1", "message " + count.get());
logger.config("message " + count.incrementAndGet());
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), "test1", "message " + count.get());
logger.fine("message " + count.incrementAndGet());
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), "test1", "message " + count.get());
logger.finer("message " + count.incrementAndGet());
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), "test1", "message " + count.get());
logger.finest("message " + count.incrementAndGet());
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), "test1", "message " + count.get());
}
void test2(Logger logger) {
AtomicInteger count = new AtomicInteger();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
logger.setLevel(Level.ALL);
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
for (Level l : Arrays.asList(Level.SEVERE, Level.WARNING, Level.INFO,
Level.CONFIG, Level.FINE, Level.FINER, Level.FINEST)) {
System.out.println("test2: " + loggerName(logger) + " " + l);
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
logger.log(l, "message " + count.incrementAndGet());
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
LogEvent event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), "test2", "message " + count.get());
logger.log(l, "message " + count.incrementAndGet(), "param");
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), "test2", "message " + count.get());
logger.log(l, "message " + count.incrementAndGet(), new Object[] {"foo", "bar"});
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), "test2", "message " + count.get());
logger.log(l, "message " + count.incrementAndGet(), new RuntimeException());
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), "test2", "message " + count.get());
// JDK 8 & 9 only (uses lambda)
logger.log(l, () -> "message " + count.incrementAndGet());
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), "test2", "message " + count.get());
// JDK 8 & 9 only (uses lambda)
logger.log(l, new RuntimeException(), () -> "message " + count.incrementAndGet());
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), "test2", "message " + count.get());
// JDK 9 only: new API
logger.logrb(l, (ResourceBundle)null, "message " + count.incrementAndGet(), (Object[]) null);
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), "test2", "message " + count.get());
// JDK 9 only: new API
logger.logrb(l, (ResourceBundle)null, "message " + count.incrementAndGet(), new RuntimeException());
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), "test2", "message " + count.get());
}
}
void test3(Logger logger) {
System.out.println("test3: " + loggerName(logger));
AtomicInteger count = new AtomicInteger();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
logger.setLevel(Level.ALL);
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
testReflection(logger, count, "severe", "testReflection");
testReflection(logger, count, "warning", "testReflection");
testReflection(logger, count, "info", "testReflection");
testReflection(logger, count, "config", "testReflection");
testReflection(logger, count, "fine", "testReflection");
testReflection(logger, count, "finer", "testReflection");
testReflection(logger, count, "finest", "testReflection");
}
void testReflection(Logger logger, AtomicInteger count, String logm, String method) {
try {
Method m = Logger.class.getMethod(logm, String.class);
m.invoke(logger, "message " + count.incrementAndGet());
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
LogEvent event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), method, "message " + count.get());
Method m2 = Method.class.getMethod("invoke", Object.class, new Object[0].getClass());
m2.invoke(m, logger, new Object[] { "message " + count.incrementAndGet() });
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), method, "message " + count.get());
m2.invoke(m2, m, new Object[] {logger, new Object[] { "message " + count.incrementAndGet() }});
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), method, "message " + count.get());
m2.invoke(m2, m2, new Object[] { m, new Object[] {logger, new Object[] { "message " + count.incrementAndGet() }}});
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), method, "message " + count.get());
} catch (Error | RuntimeException x ) {
throw x;
} catch (Exception x) {
throw new RuntimeException(x);
}
}
// JDK 8 & 9 only (uses lambda)
public void test4(Logger logger) {
System.out.println("test4: " + loggerName(logger));
AtomicInteger count = new AtomicInteger();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
logger.setLevel(Level.ALL);
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
logger.severe(() -> "message " + count.incrementAndGet());
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
LogEvent event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), "test4", "message " + count.get());
logger.warning(() -> "message " + count.incrementAndGet());
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), "test4", "message " + count.get());
logger.info(() -> "message " + count.incrementAndGet());
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), "test4", "message " + count.get());
logger.config(() -> "message " + count.incrementAndGet());
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), "test4", "message " + count.get());
logger.fine(() -> "message " + count.incrementAndGet());
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), "test4", "message " + count.get());
logger.finer(() -> "message " + count.incrementAndGet());
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), "test4", "message " + count.get());
logger.finest(() -> "message " + count.incrementAndGet());
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), "test4", "message " + count.get());
}
// JDK 8 & 9 only (uses lambda)
void test5(Logger logger) {
System.out.println("test5: " + loggerName(logger));
AtomicInteger count = new AtomicInteger();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
logger.setLevel(Level.ALL);
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
testLambda(count, logger::severe, "testLambda");
testLambda(count, logger::warning, "testLambda");
testLambda(count, logger::info, "testLambda");
testLambda(count, logger::config, "testLambda");
testLambda(count, logger::fine, "testLambda");
testLambda(count, logger::finer, "testLambda");
testLambda(count, logger::finest, "testLambda");
}
// JDK 8 & 9 only (uses lambda)
void testLambda(AtomicInteger count, Consumer<String> logm, String method) {
logm.accept("message " + count.incrementAndGet());
assertEquals(1, TestHandler.PUBLISHED.size(), "No event in queue: ");
LogEvent event = TestHandler.PUBLISHED.remove();
assertEquals(0, TestHandler.PUBLISHED.size(), "Queue should be empty: ");
checkEvent(event, this.getClass().getName(), method, "message " + count.get());
}
private static String loggerName(Logger logger) {
String name = logger.getName();
if (name == null) return "<anonymous>";
if (name.isEmpty()) return "<RootLogger>";
return "\"" + name + "\"";
}
public void test(Logger logger) {
test1(logger);
test2(logger);
test3(logger);
// JDK 8 & 9 only (uses lambda)
test4(logger);
test5(logger);
}
public static void main(String[] args) {
TestInferCaller test = new TestInferCaller();
Logger root = Logger.getLogger("");
for (Handler h : root.getHandlers()) {
h.setLevel(Level.OFF);
}
root.addHandler(new TestHandler());
for (Logger logger : Arrays.asList(root, Logger.getGlobal(),
Logger.getAnonymousLogger(), Logger.getLogger("foo.bar"))) {
System.out.println("Testing with: " + loggerName(logger) + " " + logger.getClass());
test.test(logger);
}
}
private static void assertEquals(int expected, int actual, String what) {
if (expected != actual) {
throw new RuntimeException(what
+ "\n\texpected: " + expected
+ "\n\tactual: " + actual);
}
}
private static void assertEquals(String expected, String actual, String what) {
if (!Objects.equals(expected, actual)) {
throw new RuntimeException(what
+ "\n\texpected: " + expected
+ "\n\tactual: " + actual);
}
}
private void checkEvent(LogEvent event, String className, String methodName, String message) {
assertEquals(className, event.className, "Bad class name: ");
assertEquals(className, event.record.getSourceClassName(), "Bad source class name: ");
assertEquals(methodName, event.methodName, "Bad method name: ");
assertEquals(methodName, event.record.getSourceMethodName(), "Bad source method name: ");
assertEquals(message, event.record.getMessage(), "Bad message: ");
}
}

View file

@ -0,0 +1,92 @@
/*
* Copyright (c) 2013, 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.ArrayList;
import java.util.List;
import java.util.logging.Logger;
/**
* @test
* @bug 8005899
* @build TestLogger testlogger.MyResource
* @run main/othervm TestLogger
**/
public class TestLogger {
public static final String RESOURCE_BUNDLE = "testlogger.MyResource";
public static final String ORG_LOGGER = "org";
public static final String FOO_LOGGER = ORG_LOGGER + ".foo.Foo";
public static final String BAR_LOGGER = ORG_LOGGER + ".bar.Bar";
public static final String GEE_LOGGER = ORG_LOGGER + ".gee.Gee";
public static final String GEE_GEE_LOGGER = GEE_LOGGER+".Gee";
public static void main(String[] args) {
newLogger(FOO_LOGGER, RESOURCE_BUNDLE);
newLogger(FOO_LOGGER);
newLogger(BAR_LOGGER);
newLogger(BAR_LOGGER, RESOURCE_BUNDLE);
newLogger(GEE_LOGGER, null);
newLogger(GEE_LOGGER, RESOURCE_BUNDLE);
newLogger(ORG_LOGGER);
newLogger(GEE_GEE_LOGGER);
for (String log : new String[] { FOO_LOGGER, BAR_LOGGER, GEE_LOGGER }) {
if (!RESOURCE_BUNDLE.equals(Logger.getLogger(log).getResourceBundleName())) {
throw new RuntimeException("Shouldn't allow to reset the resource bundle for " + log);
}
try {
Logger logger = Logger.getLogger(log, null);
if (!RESOURCE_BUNDLE.equals(logger.getResourceBundleName())) {
throw new RuntimeException("Shouldn't allow to reset the resource bundle for " + log);
}
throw new RuntimeException("Expected IllegalArgumentException not thrown for " + log);
} catch (IllegalArgumentException e) {
System.out.println("Got expected exception for " + log +": " + e);
}
}
for (String log : new String[] { ORG_LOGGER, GEE_GEE_LOGGER }) {
if (Logger.getLogger(log).getResourceBundleName() != null) {
throw new RuntimeException("Resource bundle is not null for log: "
+ Logger.getLogger(log).getResourceBundleName());
}
try {
Logger logger = Logger.getLogger(log, null);
if (logger.getResourceBundleName() != null) {
throw new RuntimeException("Resource bundle is not null for log: "
+ logger.getResourceBundleName());
}
System.out.println("Success calling Logger.getLogger(\""+log+"\", null)");
} catch (IllegalArgumentException e) {
throw new RuntimeException("Unexpected exception for " + log +": " + e, e);
}
}
}
private static List<Logger> strongRefs = new ArrayList<>();
private static void newLogger(String name) {
strongRefs.add(Logger.getLogger(name));
}
private static void newLogger(String name, String resourceBundleName) {
strongRefs.add(Logger.getLogger(name, resourceBundleName));
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2013, 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 testlogger;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Properties;
import java.util.ResourceBundle;
/**
* A dummy resource bundle for testing purposes.
* @author danielfuchs
*/
public class MyResource extends ResourceBundle {
Map<String, Object> bundle = new HashMap<>();
@Override
protected Object handleGetObject(String key) {
bundle.put(key,"Localized: " + key);
return bundle.get(key);
}
@Override
public Enumeration<String> getKeys() {
final Hashtable<String, Object> h = new Hashtable<>(bundle);
return h.keys();
}
}

View file

@ -0,0 +1,512 @@
/*
* Copyright (c) 2013, 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.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
/**
* @test
* @bug 8024525
* @summary checks that isLoggable() can be overridden to control logging.
* @author danielfuchs
* @run main/othervm TestIsLoggable
*/
public class TestIsLoggable {
// This logger can be configured to override its default level
// for a particular set of thread ids
public static final class ThreadLogger extends Logger {
final Map<Long, Level> threadMap =
Collections.synchronizedMap(new HashMap<Long, Level>());
public ThreadLogger(String name) {
super(name, null);
}
@Override
public boolean isLoggable(Level level) {
final Level threadLevel = threadMap.get(Thread.currentThread().getId());
if (threadLevel == null) return super.isLoggable(level);
final int levelValue = threadLevel.intValue();
final int offValue = Level.OFF.intValue();
if (level.intValue() < levelValue || levelValue == offValue) {
return false;
}
return true;
}
}
public static final class TestHandler extends Handler {
final List<String> messages = new CopyOnWriteArrayList<>();
@Override
public void publish(LogRecord record) {
messages.add(record.getMessage());
}
@Override
public void flush() {
}
@Override
public void close() throws SecurityException {
messages.clear();
}
}
// Sorted list of standard levels
static final List<Level> LEVELS = Collections.unmodifiableList(
java.util.Arrays.asList(new Level[] {
Level.SEVERE, Level.WARNING, Level.INFO, Level.CONFIG,
Level.FINE, Level.FINER, Level.FINEST
}));
// Test cases:
// LEV_ test logger.severe(msg) .. logger.finest(msg)
// LOG_ logger.log(Level.SEVERE, msg) ... logger.log(Level.FINEST, msg)
// LOG1_ logger.log(Level.SEVERE, msg, param1) ...
// LOG2_ logger.log(Level.SEVERE, msg, params[]) ...
// LOG3_ logger.log(Level.SEVERE, msg, throwable) ...
// LOGP_ logger.logp(Level.SEVERE, class, method, msg) ...
// LOGP1_ logger.logp(Level.SEVERE, class, method, msg, param1) ...
// LOGP2_ logger.logp(Level.SEVERE, class, method, msg, params[]) ...
// LOGP3_ logger.logp(Level.SEVERE, class, method, msg, throwable) ...
public static enum LogTest {
LEV_SEVERE, LEV_WARNING, LEV_INFO, LEV_CONFIG, LEV_FINE, LEV_FINER, LEV_FINEST,
LOG_SEVERE, LOG_WARNING, LOG_INFO, LOG_CONFIG, LOG_FINE, LOG_FINER, LOG_FINEST,
LOG1_SEVERE, LOG1_WARNING, LOG1_INFO, LOG1_CONFIG, LOG1_FINE, LOG1_FINER, LOG1_FINEST,
LOG2_SEVERE, LOG2_WARNING, LOG2_INFO, LOG2_CONFIG, LOG2_FINE, LOG2_FINER, LOG2_FINEST,
LOG3_SEVERE, LOG3_WARNING, LOG3_INFO, LOG3_CONFIG, LOG3_FINE, LOG3_FINER, LOG3_FINEST,
LOGP_SEVERE, LOGP_WARNING, LOGP_INFO, LOGP_CONFIG, LOGP_FINE, LOGP_FINER, LOGP_FINEST,
LOGP1_SEVERE, LOGP1_WARNING, LOGP1_INFO, LOGP1_CONFIG, LOGP1_FINE, LOGP1_FINER, LOGP1_FINEST,
LOGP2_SEVERE, LOGP2_WARNING, LOGP2_INFO, LOGP2_CONFIG, LOGP2_FINE, LOGP2_FINER, LOGP2_FINEST,
LOGP3_SEVERE, LOGP3_WARNING, LOGP3_INFO, LOGP3_CONFIG, LOGP3_FINE, LOGP3_FINER, LOGP3_FINEST;
// call the method Logger.severe() ... Logger.finest() corresponding
// to the given level 'l' (severe() for SEVERE etc...)
public void loglevel(Level l, Logger logger, String message) {
LogTest test = LogTest.valueOf("LEV_"+l.getName());
switch(test) {
case LEV_SEVERE:
logger.severe(message);
break;
case LEV_WARNING:
logger.warning(message);
break;
case LEV_INFO:
logger.info(message);
break;
case LEV_CONFIG:
logger.config(message);
break;
case LEV_FINE:
logger.fine(message);
break;
case LEV_FINER:
logger.finer(message);
break;
case LEV_FINEST:
logger.finest(message);
break;
}
}
// The threshold at which the logger is expected to start logging.
// trick: we derive the threshold level from the testcase name...
public Level threshold() {
for (Level l : LEVELS ) {
if (this.toString().endsWith(l.getName())) {
return l;
}
}
return Level.OFF;
}
// Levels for which the logger is expected to log something.
public List<Level> loggable() {
return LEVELS.subList(0, LEVELS.indexOf(threshold())+1);
}
// Levels which will be blocked because they are weaker than the
// threshold()
public List<Level> weaker() {
return LEVELS.subList(LEVELS.indexOf(threshold())+1, LEVELS.size());
}
// Log a message at this testcase threshold, using this testcase method.
public void log(Logger logger, String message) {
log(threshold(), logger, message);
}
// Log a message at the given level, using this testcase method.
public void log(Level level, Logger logger, String message) {
if (this.toString().startsWith("LOG_")) {
logger.log(level, message);
} else if (this.toString().startsWith("LOG1_")) {
logger.log(level, message, "dummy param");
} else if (this.toString().startsWith("LOG2_")) {
logger.log(level, message, new Object[] {"dummy", "param"});
} else if (this.toString().startsWith("LOG3_")) {
logger.log(level, message, new Exception("dummy exception"));
} else if (this.toString().startsWith("LOGP_")) {
logger.logp(level, "TestCase", "log", message);
} else if (this.toString().startsWith("LOGP1_")) {
logger.logp(level, "TestCase", "log", message, "dummy param");
} else if (this.toString().startsWith("LOGP2_")) {
logger.logp(level, "TestCase", "log", message,
new Object[] {"dummy", "param"});
} else if (this.toString().startsWith("LOGP3_")) {
logger.logp(level, "TestCase", "log", message,
new Exception("dummy exception"));
} else if (this.toString().startsWith("LEV_")) {
loglevel(level, logger, message);
}
}
// String description of the logging method called.
public String method() {
if (this.toString().startsWith("LOG_")) {
return "Logger.log(Level." + threshold().getName() +", msg): ";
} else if (this.toString().startsWith("LOG1_")) {
return "Logger.log(Level." + threshold().getName() +", msg, param1): ";
} else if (this.toString().startsWith("LOG2_")) {
return "Logger.log(Level." + threshold().getName() +", msg, params[]): ";
} else if (this.toString().startsWith("LOG3_")) {
return "Logger.log(Level." + threshold().getName() +", msg, throwable): ";
} else if (this.toString().startsWith("LEV_")) {
return "Logger."+threshold().getName().toLowerCase(Locale.ROOT)+"(): ";
} else if (this.toString().startsWith("LOGP_")) {
return "Logger.logp(Level." + threshold().getName() +", msg): ";
} else if (this.toString().startsWith("LOGP1_")) {
return "Logger.logp(Level." + threshold().getName() +", msg, param1): ";
} else if (this.toString().startsWith("LOGP2_")) {
return "Logger.logp(Level." + threshold().getName() +", msg, params[]): ";
} else if (this.toString().startsWith("LOGP3_")) {
return "Logger.logp(Level." + threshold().getName() +", msg, throwable): ";
}
throw new RuntimeException("Unknown test case: "+this);
}
}
// The purpose of this test is to verify that the various log methods in
// Logger now call Logger.isLoggable().
// To do that - we're going to use a subclass of Logger, ThreadLogger, which
// only overrides isLoggable() - and compare the level it is given to a level
// it finds in a map indexed with the current thread id.
// We will register a TestHandler with our ThreadLogger which will store
// the messages in a messages map. This will allow us to verify whether the
// logging method we're testing has or hasn't logged.
//
// The TestCase enum above allows us to test a combination of every possible
// log method with every possible level inside a loop - with the
// exception of exiting/entering/throwing that we will be testing
// outside of that loop.
//
public static void main(String... args) {
LogManager manager = LogManager.getLogManager();
ThreadLogger logger = new ThreadLogger("foo.bar");
//manager.addLogger(logger);
TestHandler handler = new TestHandler();
logger.addHandler(handler);
//By default, logger's level is Level.INFO
final List<Level> loggable = LEVELS.subList(0, LEVELS.indexOf(Level.INFO)+1);
// Check our test implementation of logger.isLoggable();
//
// Since we haven't put anything in the threadMap, isLoggable() should
// return true for all levels stronger or equals to Level.INFO.
// here we're just checking that our implementation of
// ThreadLogger.isLoggable() returns what we want - we're just testing
// the test code...
for (Level level : LEVELS) {
if (logger.isLoggable(level) != loggable.contains(level)) {
throw new RuntimeException(level +
": unexpected result for isLoggable(): expected " +
(loggable.contains(level)));
}
}
// Test that entering/exiting/throwing call isLoggable()
// Here we test the default behavior: this call shouldn't log anything
// because by default the logger level is Level.INFO and these
// methods log at Level.FINER.
// So by default - these methods don't log anything. We check it here.
logger.entering("blah", "blah");
logger.entering("blah", "blah", "blah");
logger.entering("blah", "blah", new Object[] {"blah"});
if (!handler.messages.isEmpty()) {
throw new RuntimeException("Expected empty, got "+handler.messages);
}
logger.exiting("blah", "blah");
logger.exiting("blah", "blah", "blah");
logger.exiting("blah", "blah", new Object[] {"blah"});
if (!handler.messages.isEmpty()) {
throw new RuntimeException("Expected empty, got "+handler.messages);
}
logger.throwing("blah", "blah", new Exception("blah"));
if (!handler.messages.isEmpty()) {
throw new RuntimeException("Expected empty, got "+handler.messages);
}
// Now we're going to put each level in turn in the threadMap.
// This means that isLoggable(Level.FINER) should now return true if the
// level in the map is not one of the level in the 'stronger' list below
// (here stronger=stronger than FINER)
final List<Level> stronger = LEVELS.subList(0, LEVELS.indexOf(Level.FINER));
for (Level l : LEVELS) {
logger.threadMap.put(Thread.currentThread().getId(), l);
// Check that our implementation of isLoggable(level) now returns true
// if 'level' is stronger or equals to 'l' - here we're just checking
// that our implementation of ThreadLogger.isLoggable() returns what
// we want - we're just testing the test code...
final List<Level> loggableLevels = LEVELS.subList(0, LEVELS.indexOf(l)+1);
for (Level level : LEVELS) {
if (logger.isLoggable(level) != loggableLevels.contains(level)) {
throw new RuntimeException(level +
": unexpected result for isLoggable(): expected " +
(loggableLevels.contains(level)));
}
}
// These methods should now start to log when the level we put in
// the map is weaker or equals to Level.FINER.
// This validates that these methods now call ThreadLogger.isLoggable()
// since the default level for our logger is still Level.INFO.
// If the methods didn't call ThreadLogger.isLoggable() they wouldn't
// log anything, whatever we put in the threadMap...
logger.entering("blah", "blah");
logger.entering("blah", "blah", "blah");
logger.entering("blah", "blah", new Object[] {"blah"});
if (stronger.contains(l)) {
if (!handler.messages.isEmpty()) {
throw new RuntimeException(l +
": Expected empty, got " + handler.messages);
}
} else {
if (handler.messages.size() != 3) {
throw new RuntimeException(l +
": Expected size 3, got " + handler.messages);
}
}
logger.exiting("blah", "blah");
logger.exiting("blah", "blah", "blah");
logger.exiting("blah", "blah", new Object[] {"blah"});
if (stronger.contains(l)) {
if (!handler.messages.isEmpty()) {
throw new RuntimeException(l +
": Expected empty, got " + handler.messages);
}
} else {
if (handler.messages.size() != 6) {
throw new RuntimeException(l +
": Expected size 6, got " + handler.messages);
}
}
logger.throwing("blah", "blah", new Exception("blah"));
if (stronger.contains(l)) {
if (!handler.messages.isEmpty()) {
throw new RuntimeException(l +
": Expected empty, got " + handler.messages);
}
} else {
if (handler.messages.size() != 7) {
throw new RuntimeException(l +
": Expected size 7, got " + handler.messages);
}
}
if (!stronger.contains(l)) {
System.out.println(l + ": Logger.entering/exiting/throwing: " +
handler.messages);
}
handler.messages.clear();
}
// Cleanup so that we can start the next test with a clean plate...
handler.messages.clear();
logger.threadMap.clear();
// Test that each logging method calls isLoggable()
//
for (LogTest testCase : LogTest.values()) {
// Each test case is a combination of:
// 1. A level to put in the threadMap.
// 2. A log method to call
final String method = testCase.method();
// check our implementation of logger.isLoggable();
// by default the logger level is Level.INFO, so our implementation
// of isLoggable() should return true for all levels stronger or
// equal to INFO and false for the others.
// We check that here.
for (Level level : LEVELS) {
if (logger.isLoggable(level) != loggable.contains(level)) {
throw new RuntimeException(level +
": unexpected result for isLoggable(): expected " +
(loggable.contains(level)));
}
}
// Check that by default the log method will not log for level
// weaker than Level.INFO.
for (Level l : LEVELS.subList(LEVELS.indexOf(Level.INFO) + 1, LEVELS.size())) {
final String test = method + l + ": ";
testCase.log(l, logger, "blah");
if (!handler.messages.isEmpty()) {
throw new RuntimeException(test +
"Expected empty, got " + handler.messages);
}
}
// Let's put Level.OFF in the threadMap. Nothing should be logged,
// whichever level is used...
logger.threadMap.put(Thread.currentThread().getId(), Level.OFF);
// Check that isLoggable() now always return false.
for (Level level : LEVELS) {
if (logger.isLoggable(level)) {
throw new RuntimeException(level +
": unexpected result for isLoggable(): expected " +
false);
}
}
// Check that the log method of the test case won't log, whatever
// level we pass to it. This validates that level method calls
// isLoggable() - because otherwise it would log for levels stronger
// or equal to INFO.
for (Level l : LEVELS) {
final String test = "[threadMap=OFF] " + method + l + ": ";
testCase.log(l, logger, "blah");
if (!handler.messages.isEmpty()) {
throw new RuntimeException(test +
"Expected empty, got " + handler.messages);
}
}
System.out.println("[threadMap=OFF] " + method + "logged " + handler.messages);
// Now put the testcase's level in the threadMap.
logger.threadMap.put(Thread.currentThread().getId(), testCase.threshold());
// The levels for which logging should happen are those that are stronger
// or equals to the testcase's thresholds.
final List<Level> loggableLevels =
LEVELS.subList(0, LEVELS.indexOf(testCase.threshold())+1);
// Check that our implementation of isLoggable() is taking into account
// what we put in the map.
for (Level level : LEVELS) {
if (logger.isLoggable(level) != loggableLevels.contains(level)) {
throw new RuntimeException(level +
": unexpected result for isLoggable(): expected " +
(loggableLevels.contains(level)));
}
}
// Now check that the log method is indeed calling our implementation
// of isLoggable(). We do this by first verifying that it won't log
// for levels weaker than what we put in the map.
//
for (Level l : testCase.weaker()) {
final String test = method + l + ": ";
testCase.log(l, logger, "blah");
if (!handler.messages.isEmpty()) {
throw new RuntimeException(test +
"Expected empty, got " + handler.messages);
}
}
// Then we check that it will log for the testcase threshold.
final String test2 = method + testCase.threshold() + ": ";
testCase.log(logger, testCase.threshold() + " blah");
if (handler.messages.isEmpty()) {
throw new RuntimeException(test2 +
"Expected 1 message, but list is empty");
}
if (!handler.messages.contains(testCase.threshold() + " blah")) {
throw new RuntimeException(test2 + " blah not found: "
+ handler.messages);
}
handler.messages.clear();
// Now we check that it logs for all 'loggable' level (and doesn't
// log for the others).
for (Level l : LEVELS) {
final String test = method + l + ": ";
testCase.log(l, logger, l + ": blah");
if (testCase.loggable().contains(l)) {
if (!handler.messages.contains(l + ": blah")) {
throw new RuntimeException(test + "blah not found: " +
handler.messages);
}
} else {
if (handler.messages.contains(l + ": blah")) {
throw new RuntimeException(test + "blah found: " +
handler.messages);
}
}
}
if (handler.messages.size() != testCase.loggable().size()) {
throw new RuntimeException(method +
" Sizes don't match: expected " +
testCase.loggable().size() + " got " +
handler.messages);
}
// Some visual feedback on what happened.
System.out.println(method + "logged " + handler.messages);
// Cleanup for next step.
// Since we're iterating over all possible levels we can be
// sure that we haven't missed anything.
// For instance - it could be argued that logger.severe() will
// always log. But since we have 1 case where we put Level.OFF in
// the map and we have verified that severe() didn't log in that
// case, but that it logged in any other case, then we know
// beyond doubt that it called our implementation of isLoggable().
logger.threadMap.clear();
handler.messages.clear();
}
}
}

View file

@ -0,0 +1,387 @@
/*
* Copyright (c) 2013, 2022, 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.ref.Reference;
import java.util.Arrays;
import java.util.Locale;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import resources.ListBundle;
/**
* @test
* @bug 8013839
* @summary tests Logger.logrb(..., ResourceBundle);
* @build TestLogrbResourceBundle resources.ListBundle resources.ListBundle_fr
* @run main TestLogrbResourceBundle
* @author danielfuchs
*/
public class TestLogrbResourceBundle {
static final String LIST_BUNDLE_NAME = "resources.ListBundle";
static final String PROPERTY_BUNDLE_NAME = "resources.PropertyBundle";
/**
* A dummy handler class that we can use to check the bundle/bundle name
* that was present in the last LogRecord instance published.
*/
static final class TestHandler extends Handler {
ResourceBundle lastBundle = null;
String lastBundleName = null;
Object[] lastParams = null;
Throwable lastThrown = null;
String lastMessage = null;
@Override
public void publish(LogRecord record) {
lastBundle = record.getResourceBundle();
lastBundleName = record.getResourceBundleName();
lastParams = record.getParameters();
lastThrown = record.getThrown();
lastMessage = record.getMessage();
}
@Override
public void flush() {
}
@Override
public void close() throws SecurityException {
}
}
/**
* We're going to do the same test with each of the different new logrb
* forms.
* <ul>
* <li> LOGRB_NO_ARGS: calling logrb with no message argument.
* <li> LOGRB_SINGLE_ARG: calling logrb with a single message argument.
* <li> LOGRB_ARG_ARRAY: calling logrb with an array of message arguments.
* <li> LOGRB_VARARGS: calling logrb with a variable list of message arguments.
* <li> LOGRB_THROWABLE: calling logrb with an exception.
* </ul>
*/
private static enum TestCase {
LOGRB_NO_ARGS, LOGRB_SINGLE_ARG, LOGRB_ARG_ARRAY, LOGRB_VARARGS, LOGRB_THROWABLE;
public void logrb(Logger logger, ResourceBundle bundle) {
switch(this) {
case LOGRB_NO_ARGS:
logger.logrb(Level.CONFIG,
TestLogrbResourceBundle.class.getName(),
"main", bundle, "dummy");
break;
case LOGRB_SINGLE_ARG:
logger.logrb(Level.CONFIG,
TestLogrbResourceBundle.class.getName(),
"main", bundle, "dummy", "bar");
break;
case LOGRB_ARG_ARRAY:
logger.logrb(Level.CONFIG,
TestLogrbResourceBundle.class.getName(),
"main", bundle, "dummy",
new Object[] { "bar", "baz"} );
break;
case LOGRB_VARARGS:
logger.logrb(Level.CONFIG,
TestLogrbResourceBundle.class.getName(),
"main", bundle, "dummy",
"bar", "baz" );
break;
case LOGRB_THROWABLE:
logger.logrb(Level.CONFIG,
TestLogrbResourceBundle.class.getName(),
"main", bundle, "dummy",
new Exception("dummy exception") );
break;
default:
}
}
/**
* Checks that the last published logged record had the expected data.
* @param handler the TestHandler through which the record was published.
*/
public void checkLogged(TestHandler handler) {
checkLogged(handler.lastMessage, handler.lastParams, handler.lastThrown);
}
private void checkLogged(String message, Object[] parameters, Throwable thrown) {
switch(this) {
case LOGRB_NO_ARGS:
if ("dummy".equals(message) && thrown == null
&& (parameters == null || parameters.length == 0)) {
return; // OK: all was as expected.
}
break;
case LOGRB_SINGLE_ARG:
if ("dummy".equals(message) && thrown == null
&& parameters != null
&& parameters.length == 1
&& "bar".equals(parameters[0])) {
return; // OK: all was as expected.
}
break;
case LOGRB_VARARGS:
case LOGRB_ARG_ARRAY:
if ("dummy".equals(message) && thrown == null
&& parameters != null
&& parameters.length > 1
&& Arrays.deepEquals(new Object[] { "bar", "baz"},
parameters)) {
return; // OK: all was as expected.
}
break;
case LOGRB_THROWABLE:
if ("dummy".equals(message) && thrown != null
&& thrown.getClass() == Exception.class
&& "dummy exception".equals(thrown.getMessage())) {
return; // OK: all was as expected.
}
break;
default:
}
// We had some unexpected stuff: throw exception.
throw new RuntimeException(this + ": "
+ "Unexpected content in last published log record: "
+ "\n\tmessage=\"" + message + "\""
+ "\n\tparameters=" + Arrays.toString(parameters)
+ "\n\tthrown=" + thrown);
}
}
static String getBaseName(ResourceBundle bundle) {
return bundle == null ? null : bundle.getBaseBundleName();
}
public static void main(String... args) throws Exception {
Locale defaultLocale = Locale.getDefault();
final ResourceBundle bundle = ResourceBundle.getBundle(LIST_BUNDLE_NAME);
final ResourceBundle bundle_fr =
ResourceBundle.getBundle(LIST_BUNDLE_NAME, Locale.FRENCH);
final ResourceBundle propertyBundle = ResourceBundle.getBundle(PROPERTY_BUNDLE_NAME);
final ResourceBundle propertyBundle_fr =
ResourceBundle.getBundle(PROPERTY_BUNDLE_NAME, Locale.FRENCH);
Logger foobar = Logger.getLogger("foo.bar");
final TestHandler handler = new TestHandler();
foobar.addHandler(handler);
foobar.setLevel(Level.CONFIG);
final ResourceBundle anonBundle = new ListBundle();
try {
// First we're going to call logrb on a logger that
// has no bundle set...
// For each possible logrb form...
for (TestCase test : TestCase.values()) {
// For various resource bundles
for (ResourceBundle b : new ResourceBundle[] {
anonBundle, bundle, bundle_fr, propertyBundle,
anonBundle, null, propertyBundle_fr,
}) {
// Prints the resource bundle base name (can be null,
// we don't enforce non-null names in logrb.
final String baseName = getBaseName(b);
System.out.println("Testing " + test + " with " + baseName);
// log in the 'foobar' logger using bundle 'b'
test.logrb(foobar, b);
// check that the correct bundle was set in the published
// LogRecord
if (handler.lastBundle != b) {
throw new RuntimeException("Unexpected bundle: "
+ handler.lastBundle);
}
// check that the correct bundle name was set in the published
// LogRecord
if (!Objects.equals(handler.lastBundleName, baseName)) {
throw new RuntimeException("Unexpected bundle name: "
+ handler.lastBundleName);
}
// check that calling foobar.logrb() had no side effect on
// the bundle used by foobar. foobar should still have no
// bundle set.
if (foobar.getResourceBundle() != null) {
throw new RuntimeException("Unexpected bundle: "
+ foobar.getResourceBundle());
}
if (foobar.getResourceBundleName() != null) {
throw new RuntimeException("Unexpected bundle: "
+ foobar.getResourceBundleName());
}
// Test that the last published log record had all the
// data that this test case had logged (message, parameters,
// thrown...
test.checkLogged(handler);
}
}
// No we're going to set a resource bundle on the foobar logger
// and do it all again...
// For the same bundle in two different locales
for (ResourceBundle propBundle : new ResourceBundle[] {
propertyBundle, propertyBundle_fr,
}) {
// set the bundle on foobar...
foobar.setResourceBundle(propBundle);
// check the bundle was correctly set...
if (!propBundle.equals(foobar.getResourceBundle())) {
throw new RuntimeException("Unexpected bundle: "
+ foobar.getResourceBundle());
}
if (!Objects.equals(getBaseName(propBundle), foobar.getResourceBundleName())) {
throw new RuntimeException("Unexpected bundle name: "
+ foobar.getResourceBundleName());
}
System.out.println("Configuring " + foobar.getName() + " with "
+ propBundle);
// for each possible logrb form...
for (TestCase test : TestCase.values()) {
// for various resource bundles
for (ResourceBundle b : new ResourceBundle[] {
anonBundle, bundle, null, bundle_fr, propertyBundle,
anonBundle, propertyBundle_fr,
}) {
final String baseName = getBaseName(b);
System.out.println("Testing " + test + " with " + baseName);
// call foobar.logrb
test.logrb(foobar, b);
// check which resource bundle was used (should be
// the one passed to logrb)
if (handler.lastBundle != b) {
throw new RuntimeException("Unexpected bundle: "
+ handler.lastBundle);
}
if (!Objects.equals(handler.lastBundleName, baseName)) {
throw new RuntimeException("Unexpected bundle name: "
+ handler.lastBundleName);
}
// Verify there was no side effect on the bundle that
// had been previously set on the logger...
if (foobar.getResourceBundle() != propBundle) {
throw new RuntimeException("Unexpected bundle: "
+ foobar.getResourceBundle());
}
if (!Objects.equals(getBaseName(propBundle),
foobar.getResourceBundleName())) {
throw new RuntimeException("Unexpected bundle name: "
+ foobar.getResourceBundleName());
}
// Checked that the published LogRecord had the
// expected content logged by this test case.
test.checkLogged(handler);
}
}
}
// Now we're going to the same thing, but with a logger which
// has an inherited resource bundle.
Logger foobaz = Logger.getLogger("foo.bar.baz");
// keep "foo.bar" logger alive until its child has
// been created. A child logger has a strong
// reference to its first parent.
Reference.reachabilityFence(foobar);
// check that foobaz has no bundle set locally.
if (foobaz.getResourceBundle() != null) {
throw new RuntimeException("Unexpected bundle: "
+ foobaz.getResourceBundle());
}
if (foobaz.getResourceBundleName() != null) {
throw new RuntimeException("Unexpected bundle: "
+ foobaz.getResourceBundle());
}
// The current locale should have no effect on logrb.
Locale.setDefault(Locale.GERMAN); // shouldn't change anything...
// for each possible logrb form
for (TestCase test : TestCase.values()) {
// for various resource bundle
for (ResourceBundle b : new ResourceBundle[] {
anonBundle, bundle, bundle_fr, propertyBundle, null,
anonBundle, propertyBundle_fr,
}) {
final String baseName = getBaseName(b);
System.out.println("Testing " + test + " with "
+ foobaz.getName() + " and "
+ baseName);
// call foobaz.logrb with the bundle
test.logrb(foobaz, b);
// check that the bundle passed to logrb was used.
if (handler.lastBundle != b) {
throw new RuntimeException("Unexpected bundle: "
+ handler.lastBundle);
}
if (!Objects.equals(handler.lastBundleName, baseName)) {
throw new RuntimeException("Unexpected bundle name: "
+ handler.lastBundleName);
}
// check that there was no effect on the bundle set
// on foobaz: it should still be null.
if (foobaz.getResourceBundle() != null) {
throw new RuntimeException("Unexpected bundle: "
+ foobaz.getResourceBundle());
}
if (foobaz.getResourceBundleName() != null) {
throw new RuntimeException("Unexpected bundle: "
+ foobaz.getResourceBundleName());
}
// check that the last published log record had all the
// data that was logged by this testcase.
test.checkLogged(handler);
}
}
} finally {
Locale.setDefault(defaultLocale);
}
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2013, 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 resources;
import java.util.ListResourceBundle;
/**
*
* @author danielfuchs
*/
public class ListBundle extends ListResourceBundle {
@Override
protected Object[][] getContents() {
return new Object[][] {
{ "dummy", "foo" },
};
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2013, 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 resources;
import java.util.ListResourceBundle;
/**
*
* @author danielfuchs
*/
public class ListBundle_fr extends ListResourceBundle {
@Override
protected Object[][] getContents() {
return new Object[][] {
{ "dummy", "toto" },
};
}
}

View file

@ -0,0 +1,23 @@
#
# Copyright (c) 2013, 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.
#
dummy=foo

View file

@ -0,0 +1,24 @@
#
# Copyright (c) 2013, 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.
#
dummy=toto

View file

@ -0,0 +1,368 @@
/*
* Copyright (c) 2013, 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.Locale;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import resources.ListBundle;
/**
* @test
* @bug 8013839 8189291
* @summary tests Logger.setResourceBundle;
* @build TestSetResourceBundle resources.ListBundle resources.ListBundle_fr
* @run main/othervm TestSetResourceBundle
* @author danielfuchs
*/
public class TestSetResourceBundle {
static final String LIST_BUNDLE_NAME = "resources.ListBundle";
static final String PROPERTY_BUNDLE_NAME = "resources.PropertyBundle";
/**
* A dummy handler class that we can use to check the bundle/bundle name
* that was present in the last LogRecord instance published.
*/
static final class TestHandler extends Handler {
volatile ResourceBundle lastBundle = null;
volatile String lastBundleName = null;
@Override
public void publish(LogRecord record) {
lastBundle = record.getResourceBundle();
lastBundleName = record.getResourceBundleName();
}
@Override
public void flush() {
}
@Override
public void close() throws SecurityException {
}
}
public static void main(String... args) throws Exception {
Locale defaultLocale = Locale.getDefault();
try {
test("foo.bar");
} finally {
Locale.setDefault(defaultLocale);
}
}
static String getBaseName(ResourceBundle bundle) {
return bundle == null ? null : bundle.getBaseBundleName();
}
/**
* Main test runner.
* @param loggerName The logger to use.
* @throws Exception if the test fails.
*/
public static void test(String loggerName) throws Exception {
System.out.println("Starting test for " + loggerName);
final ResourceBundle bundle = ResourceBundle.getBundle(LIST_BUNDLE_NAME);
Logger foobar = Logger.getLogger(loggerName);
// Checks that IAE is thrown if the bundle has a null base name.
try {
foobar.setResourceBundle(new ListBundle());
throw new RuntimeException("Expected exception not raised!");
} catch (IllegalArgumentException x) {
System.out.println("Got expected exception: " + x);
}
// Verify that resource bundle was not set.
if (foobar.getResourceBundle() != null) {
throw new RuntimeException("Unexpected bundle: "
+ foobar.getResourceBundle());
}
if (foobar.getResourceBundleName() != null) {
throw new RuntimeException("Unexpected bundle: "
+ foobar.getResourceBundleName());
}
// Set acceptable resource bundle on logger.
foobar.setResourceBundle(bundle);
// check that the bundle has been set correctly
if (bundle != foobar.getResourceBundle()) {
throw new RuntimeException("Unexpected bundle: "
+ foobar.getResourceBundle());
}
if (!Objects.equals(getBaseName(bundle), foobar.getResourceBundleName())) {
throw new RuntimeException("Unexpected bundle name: "
+ foobar.getResourceBundleName());
}
// Check that we can replace the bundle with a bundle of the same name.
final ResourceBundle bundle_fr =
ResourceBundle.getBundle(LIST_BUNDLE_NAME, Locale.FRENCH);
foobar.setResourceBundle(bundle_fr);
if (bundle_fr != foobar.getResourceBundle()) {
throw new RuntimeException("Unexpected bundle: "
+ foobar.getResourceBundle());
}
if (!Objects.equals(getBaseName(bundle_fr), foobar.getResourceBundleName())) {
throw new RuntimeException("Unexpected bundle name: "
+ foobar.getResourceBundleName());
}
// Create a child logger
final Logger foobaz = Logger.getLogger(loggerName + ".baz");
if (foobar != foobaz.getParent()) {
throw new RuntimeException("Unexpected parent: " +
foobaz.getParent() + " != " + foobar);
}
// Check that the child logger does not have a bundle set locally
if (foobaz.getResourceBundle() != null) {
throw new RuntimeException("Unexpected bundle: "
+ foobaz.getResourceBundle());
}
if (foobaz.getResourceBundleName() != null) {
throw new RuntimeException("Unexpected bundle: "
+ foobaz.getResourceBundleName());
}
// Add a handler on the child logger.
final TestHandler handler = new TestHandler();
foobaz.addHandler(handler);
// log a message on the child logger
foobaz.severe("dummy");
// checks that the message has been logged with the bundle
// inherited from the parent logger
if (!LIST_BUNDLE_NAME.equals(handler.lastBundleName)) {
debugLogger(foobaz, foobar, handler);
throw new RuntimeException("Unexpected bundle name: "
+ handler.lastBundleName);
}
if (!bundle_fr.equals(handler.lastBundle)) {
debugLogger(foobaz, foobar, handler);
throw new RuntimeException("Unexpected bundle: "
+ handler.lastBundle);
}
// Check that we can get set a bundle on the child logger
// using Logger.getLogger.
final Logger foobaz2 = Logger.getLogger(loggerName + ".baz", PROPERTY_BUNDLE_NAME);
if (foobaz2 != foobaz) {
throw new RuntimeException("Unexpected logger: " + foobaz2 + " != " + foobaz);
}
if (foobar != foobaz.getParent()) {
throw new RuntimeException("Unexpected parent: " +
foobaz.getParent() + " != " + foobar);
}
// check that the child logger has the correct bundle.
// it should no longer inherit it from its parent.
if (!PROPERTY_BUNDLE_NAME.equals(foobaz2.getResourceBundleName())) {
throw new RuntimeException("Unexpected bundle name: "
+ foobaz2.getResourceBundleName());
}
if (!PROPERTY_BUNDLE_NAME.equals(foobaz2.getResourceBundle().getBaseBundleName())) {
throw new RuntimeException("Unexpected bundle name: "
+ foobaz2.getResourceBundle().getBaseBundleName());
}
boolean found = false;
for (Handler h : foobaz2.getHandlers()) {
if (h == handler) {
found = true;
break;
}
}
if (!found) {
throw new RuntimeException("Expected handler not found in: " +
foobaz2.getName() + "(" + foobaz2.getClass().getName()+")" );
}
// log a message on the child logger
foobaz2.severe("dummy");
// check that the last published log record has the appropriate
// bundle.
if (!PROPERTY_BUNDLE_NAME.equals(handler.lastBundleName)) {
debugLogger(foobaz2, foobar, handler);
throw new RuntimeException("Unexpected bundle name: "
+ handler.lastBundleName);
}
if (foobaz2.getResourceBundle() != handler.lastBundle) {
debugLogger(foobaz2, foobar, handler);
throw new RuntimeException("Unexpected bundle: "
+ handler.lastBundle);
}
// try to set a bundle that has a different name, and checks that
// it fails in IAE.
try {
foobaz2.setResourceBundle(bundle_fr);
throw new RuntimeException("Expected exception not raised!");
} catch (IllegalArgumentException x) {
System.out.println("Got expected exception: " + x);
}
// Test with a subclass of logger which overrides
// getResourceBundle() and getResourceBundleName()
Logger customLogger = new Logger(foobar.getName()+".bie", null) {
@Override
public ResourceBundle getResourceBundle() {
return bundle_fr;
}
@Override
public String getResourceBundleName() {
return PROPERTY_BUNDLE_NAME;
}
};
final TestHandler handler2 = new TestHandler();
customLogger.addHandler(handler2);
customLogger.setLevel(Level.FINE);
LogManager.getLogManager().addLogger(customLogger);
Logger l = Logger.getLogger(customLogger.getName());
if (l != customLogger) {
throw new RuntimeException("Wrong logger: " + l);
}
// log on the custom logger.
customLogger.fine("dummy");
// check that the log record had the correct bundle.
if (! PROPERTY_BUNDLE_NAME.equals(handler2.lastBundleName)) {
debugLogger(customLogger, foobar, handler2);
throw new RuntimeException("Unexpected bundle name: "
+ handler2.lastBundleName);
}
if (! PROPERTY_BUNDLE_NAME.equals(customLogger.getResourceBundleName())) {
debugLogger(customLogger, foobar, handler2);
throw new RuntimeException("Unexpected bundle name: "
+ customLogger.getResourceBundleName());
}
if (bundle_fr != handler2.lastBundle) {
throw new RuntimeException("Unexpected bundle: "
+ handler2.lastBundle);
}
if (bundle_fr != customLogger.getResourceBundle()) {
throw new RuntimeException("Unexpected bundle: "
+ customLogger.getResourceBundle());
}
// Do the same thing again with a child of the custom logger.
Logger biebar = Logger.getLogger(customLogger.getName() + ".bar");
biebar.fine("dummy");
// because getResourceBundleName() is called on parent logger
// we will have handler2.lastBundleName = PROPERTY_BUNDLE_NAME
if (!PROPERTY_BUNDLE_NAME.equals(handler2.lastBundleName)) {
debugLogger(biebar, customLogger, handler2);
throw new RuntimeException("Unexpected bundle name: "
+ handler2.lastBundleName);
}
// because getResourceBundle() is not called on parent logger
// we will have getBaseName(handler2.lastBundle) = PROPERTY_BUNDLE_NAME
// and not handler2.lastBundle = bundle_fr
if (handler2.lastBundle == null) {
debugLogger(biebar, customLogger, handler2);
throw new RuntimeException("Unexpected bundle: "
+ handler2.lastBundle);
}
if (!PROPERTY_BUNDLE_NAME.equals(getBaseName(handler2.lastBundle))) {
debugLogger(biebar, customLogger, handler2);
throw new RuntimeException("Unexpected bundle name: "
+ getBaseName(handler2.lastBundle));
}
// Just make sure that these loggers won't be eagerly GCed...
if (foobar == null || !loggerName.equals(foobar.getName())) {
throw new RuntimeException("foobar is null "
+ "- or doesn't have the expected name: " + foobar);
}
if (foobaz == null || !foobaz.getName().startsWith(loggerName)) {
throw new RuntimeException("foobaz is null "
+ "- or doesn't have the expected name: " + foobaz);
}
if (foobaz2 == null || !foobaz2.getName().startsWith(loggerName)) {
throw new RuntimeException("foobaz2 is null "
+ "- or doesn't have the expected name: " + foobaz2);
}
if (!customLogger.getName().startsWith(loggerName)) {
throw new RuntimeException("customLogger "
+ "doesn't have the expected name: " + customLogger);
}
if (!biebar.getName().startsWith(loggerName)) {
throw new RuntimeException("biebar "
+ "doesn't have the expected name: " + biebar.getName());
}
System.out.println("Test passed for " + loggerName);
}
static void debugLogger(Logger logger, Logger expectedParent, TestHandler handler) {
final String logName = logger.getName();
final String prefix = " " + logName;
System.err.println("Logger " + logName
+ " logged with bundle name " + handler.lastBundleName
+ " (" + handler.lastBundle + ")");
System.err.println(prefix + ".getResourceBundleName() is "
+ logger.getResourceBundleName());
System.err.println(prefix + ".getResourceBundle() is "
+ logger.getResourceBundle());
final Logger parent = logger.getParent();
final String pname = parent == null ? null : parent.getName();
final String pclass = parent == null ? ""
: ("(" + parent.getClass().getName() + ")");
final String presn = parent == null ? null
: parent.getResourceBundleName();
final ResourceBundle pres = parent == null ? null
: parent.getResourceBundle();
System.err.println(prefix + ".getParent() is "
+ pname + (pname == null ? ""
: (" " + pclass + ": " + parent)));
System.err.println(" expected parent is :" + expectedParent);
System.err.println(prefix + ".parent.getResourceBundleName() is "
+ presn);
System.err.println(prefix + ".parent.getResourceBundle() is "
+ pres);
System.err.println(" expected parent getResourceBundleName() is "
+ expectedParent.getResourceBundleName());
System.err.println(" expected parent.getResourceBundle() is "
+ expectedParent.getResourceBundle());
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2013, 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 resources;
import java.util.ListResourceBundle;
/**
*
* @author danielfuchs
*/
public class ListBundle extends ListResourceBundle {
@Override
protected Object[][] getContents() {
return new Object[][] {
{ "dummy", "foo" },
};
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2013, 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 resources;
import java.util.ListResourceBundle;
/**
*
* @author danielfuchs
*/
public class ListBundle_fr extends ListResourceBundle {
@Override
protected Object[][] getContents() {
return new Object[][] {
{ "dummy", "toto" },
};
}
}

View file

@ -0,0 +1,23 @@
#
# Copyright (c) 2013, 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.
#
dummy=foo

View file

@ -0,0 +1,24 @@
#
# Copyright (c) 2013, 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.
#
dummy=toto

View file

@ -0,0 +1,203 @@
/*
* Copyright (c) 2011, 2013, 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 7045594 8002070
* @summary ResourceBundle setting race in Logger.getLogger(name, rbName)
* @author Daniel D. Daugherty
* @build RacingThreadsTest LoggerResourceBundleRace
* @run main/othervm LoggerResourceBundleRace
*
* (In samevm mode, the bundle classes don't end up in the classpath.)
*/
import java.util.ListResourceBundle;
import java.util.MissingResourceException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
public class LoggerResourceBundleRace extends RacingThreadsTest {
private static final int N_LOOPS = 500000; // # of race loops
private static final int N_SECS = 15; // # of secs to run test
// # of parallel threads; must match number of MyResources inner classes
private static final int N_THREADS = 3;
private static final String LOGGER_PREFIX = "myLogger-";
private static final String RESOURCE_PREFIX
= "LoggerResourceBundleRace$MyResources";
// these counters are AtomicInteger since any worker thread can increment
private static final AtomicInteger iaeCnt = new AtomicInteger();
private static final AtomicInteger worksCnt = new AtomicInteger();
Logger dummy; // dummy Logger
LoggerResourceBundleRace(String name, int n_threads, int n_loops,
int n_secs) {
super(name, n_threads, n_loops, n_secs);
}
// Main test driver
//
public static void main(String[] args) {
LoggerResourceBundleRace test
= new LoggerResourceBundleRace("LoggerResourceBundleRace",
N_THREADS, N_LOOPS, N_SECS);
test.setVerbose(
Boolean.getBoolean("LoggerResourceBundleRace.verbose"));
DriverThread driver = new DriverThread(test);
MyWorkerThread[] workers = new MyWorkerThread[N_THREADS];
for (int i = 0; i < workers.length; i++) {
workers[i] = new MyWorkerThread(i, test);
}
test.runTest(driver, workers);
}
public void oneTimeDriverInit(DriverThread dt) {
super.oneTimeDriverInit(dt);
dummy = null;
}
public void perRaceDriverInit(DriverThread dt) {
super.perRaceDriverInit(dt);
// - allocate a new dummy Logger without a ResourceBundle;
// this gives the racing threads less to do
// - reset the counters
dummy = Logger.getLogger(LOGGER_PREFIX + getLoopCnt());
iaeCnt.set(0);
worksCnt.set(0);
}
public void executeRace(WorkerThread wt) {
super.executeRace(wt);
Logger myLogger = null;
try {
MyWorkerThread mwt = (MyWorkerThread) wt; // short hand
// Here is the race:
// - the target Logger object has already been created by
// the DriverThread without a ResourceBundle name
// - in parallel, each WorkerThread calls Logger.getLogger()
// with a different ResourceBundle name
// - Logger.getLogger() should only successfully set the
// ResourceBundle name for one WorkerThread; all other
// WorkerThread calls to Logger.getLogger() should throw
// IllegalArgumentException
myLogger = Logger.getLogger(LOGGER_PREFIX + getLoopCnt(),
mwt.rbName);
if (myLogger.getResourceBundleName().equals(mwt.rbName)) {
// no exception and the ResourceBundle names match
worksCnt.incrementAndGet(); // ignore return
} else {
System.err.println(wt.getName()
+ ": ERROR: expected ResourceBundleName '"
+ mwt.rbName + "' does not match actual '"
+ myLogger.getResourceBundleName() + "'");
incAndGetFailCnt(); // ignore return
}
} catch (IllegalArgumentException iae) {
iaeCnt.incrementAndGet(); // ignore return
} catch (MissingResourceException mre) {
// This exception happens when N_THREADS above does not
// match the number of MyResources inner classes below.
// We exit since this is a coding error.
unexpectedException(wt, mre);
System.exit(2);
}
}
public void checkRaceResults(DriverThread dt) {
super.checkRaceResults(dt);
if (worksCnt.get() != 1) {
System.err.println(dt.getName() + ": ERROR: worksCnt should be 1"
+ ": loopCnt=" + getLoopCnt() + ", worksCnt=" + worksCnt.get());
incAndGetFailCnt(); // ignore return
} else if (iaeCnt.get() != N_THREADS - 1) {
System.err.println(dt.getName() + ": ERROR: iaeCnt should be "
+ (N_THREADS - 1) + ": loopCnt=" + getLoopCnt()
+ ", iaeCnt=" + iaeCnt.get());
incAndGetFailCnt(); // ignore return
}
}
public void oneTimeDriverEpilog(DriverThread dt) {
super.oneTimeDriverEpilog(dt);
// Use the dummy Logger after the testing loop to make sure that
// dummy doesn't get optimized away in the testing loop.
dummy.info("This is a test message.");
}
// N_THREADS above must match number of MyResources inner classes
//
public static class MyResources0 extends ListResourceBundle {
static final Object[][] contents = {
{"sample1", "translation #1 for sample1"},
{"sample2", "translation #1 for sample2"},
};
public Object[][] getContents() {
return contents;
}
}
public static class MyResources1 extends ListResourceBundle {
static final Object[][] contents = {
{"sample1", "translation #2 for sample1"},
{"sample2", "translation #2 for sample2"},
};
public Object[][] getContents() {
return contents;
}
}
public static class MyResources2 extends ListResourceBundle {
static final Object[][] contents = {
{"sample1", "translation #3 for sample1"},
{"sample2", "translation #3 for sample2"},
};
public Object[][] getContents() {
return contents;
}
}
// WorkerThread with a thread specific ResourceBundle name
//
public static class MyWorkerThread extends WorkerThread {
public final String rbName; // ResourceBundle name
MyWorkerThread(int workerNum, RacingThreadsTest test) {
super(workerNum, test);
rbName = RESOURCE_PREFIX + workerNum;
}
}
}

View file

@ -0,0 +1,100 @@
/*
* Copyright 2009 Google, Inc. 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 6830220 6278014
* @summary Test Logger subclasses
*/
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.LogRecord;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
public class LoggerSubclass {
void test(String[] args) {
final String name = "myLogger";
final String message = "myMessage";
final AtomicInteger getHandlerCount = new AtomicInteger(0);
final AtomicLong lastSequenceNumber = new AtomicLong(-1L);
final AtomicInteger lastThreadID = new AtomicInteger(-1);
final Logger logger = new Logger(name, null) {
public Handler[] getHandlers() {
getHandlerCount.getAndIncrement();
return super.getHandlers();
}};
equal(logger.getName(), name);
equal(logger.getResourceBundle(), null);
equal(logger.getFilter(), null);
equal(logger.getLevel(), null);
check(logger.isLoggable(Level.WARNING));
logger.addHandler(new Handler() {
public void close() {}
public void flush() {}
public void publish(LogRecord l) {
equal(l.getLoggerName(), name);
equal(l.getMessage(), message);
equal(l.getResourceBundle(), null);
equal(l.getSourceClassName(), "LoggerSubclass");
equal(l.getSourceMethodName(), "test");
equal(l.getThrown(), null);
equal(l.getLevel(), Level.WARNING);
if (lastSequenceNumber.get() != -1) {
equal(lastSequenceNumber.get() + 1,
l.getSequenceNumber());
equal(lastThreadID.get(),
l.getThreadID());
equal((int) Thread.currentThread().getId(),
l.getThreadID());
}
lastSequenceNumber.set(l.getSequenceNumber());
lastThreadID.set(l.getThreadID());
}});
for (int i = 1; i < 4; i++) {
logger.warning(message); // Should invoke getHandlers()
equal(i, getHandlerCount.get());
}
}
//--------------------- Infrastructure ---------------------------
volatile int passed = 0, failed = 0;
void pass() {passed++;}
void fail() {failed++; Thread.dumpStack();}
void fail(String msg) {System.err.println(msg); fail();}
void unexpected(Throwable t) {failed++; t.printStackTrace();}
void check(boolean cond) {if (cond) pass(); else fail();}
void equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) pass();
else fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
try {new LoggerSubclass().instanceMain(args);}
catch (Throwable e) {throw e.getCause() == null ? e : e.getCause();}}
public void instanceMain(String[] args) throws Throwable {
try {test(args);} catch (Throwable t) {unexpected(t);}
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View file

@ -0,0 +1,264 @@
/*
* Copyright (c) 2012, 2013, 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 8005263
* @run testng LoggerSupplierAPIsTest
*/
import java.util.logging.Logger;
import java.util.logging.Level;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.util.function.Supplier;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
@Test(groups="unit")
public class LoggerSupplierAPIsTest {
static class CountingSupplier implements Supplier<String> {
AtomicInteger sno = new AtomicInteger();
@Override
public String get() {
return "Log message " + sno.getAndIncrement();
}
public int getCount() { return sno.get(); }
public void reset() { sno.set(0); }
}
static class CountingHandler extends Handler {
AtomicInteger count = new AtomicInteger();
ArrayList<LogRecord> ar = new ArrayList<>();
@Override
public void close() { reset(); }
@Override
public void flush() { reset(); }
@Override
public void publish(LogRecord lr) {
// Can do some more assertion here?
// System.out.println(lr.getMessage());
count.incrementAndGet();
}
public int getCount() {
return count.get();
}
public List<LogRecord> getLogs() {
return Collections.unmodifiableList(ar);
}
public void reset() {
count.set(0);
ar.clear();
}
}
static class HelperEx extends Exception {
final Level level;
HelperEx(Level l) { level = l; }
Level getLevel() { return level; }
}
static class SystemInfo {
public static String Java() {
return "Java: " + System.getProperty("java.version") +
" installed at " + System.getProperty("java.home");
}
public static String OS() {
return "OS: " + System.getProperty("os.name") +
" " + System.getProperty("os.version") +
" " + System.getProperty("os.arch");
}
}
static final CountingSupplier supplier = new CountingSupplier();
static final CountingHandler handler = new CountingHandler();
static final Logger logger;
static final Level[] levels = { Level.ALL, Level.OFF, Level.SEVERE,
Level.WARNING, Level.INFO, Level.CONFIG,
Level.FINE, Level.FINER, Level.FINEST };
static final int[] invokes = { 7, 0, 1, 2, 3, 4, 5, 6, 7 };
static final int[] log_count = { 10, 0, 1, 2, 3, 4, 6, 8, 10 };
static {
logger = Logger.getLogger("LoggerSupplierApisTest");
logger.setUseParentHandlers(false);
logger.addHandler(handler);
}
public void setup() {
supplier.reset();
handler.reset();
}
private void testLog() {
logger.log(Level.SEVERE, supplier);
logger.log(Level.WARNING, supplier);
logger.log(Level.INFO, supplier);
logger.log(Level.CONFIG, supplier);
logger.log(Level.FINE, supplier);
logger.log(Level.FINER, supplier);
logger.log(Level.FINEST, supplier);
// Lambda expression
logger.log(Level.FINE, () ->
"Timestamp: " + System.currentTimeMillis() +
", user home directory: " + System.getProperty("user.home"));
// Method reference
logger.log(Level.FINER, SystemInfo::Java);
logger.log(Level.FINEST, SystemInfo::OS);
}
private void testLogThrown() {
logger.log(Level.SEVERE, new HelperEx(Level.SEVERE), supplier);
logger.log(Level.WARNING, new HelperEx(Level.WARNING), supplier);
logger.log(Level.INFO, new HelperEx(Level.INFO), supplier);
logger.log(Level.CONFIG, new HelperEx(Level.CONFIG), supplier);
logger.log(Level.FINE, new HelperEx(Level.FINE), supplier);
logger.log(Level.FINER, new HelperEx(Level.FINER), supplier);
logger.log(Level.FINEST, new HelperEx(Level.FINEST), supplier);
// Lambda expression
logger.log(Level.FINE, new HelperEx(Level.FINE), () ->
"Timestamp: " + System.currentTimeMillis() +
", user home directory: " + System.getProperty("user.home"));
// Method reference
logger.log(Level.FINER, new HelperEx(Level.FINER), SystemInfo::Java);
logger.log(Level.FINEST, new HelperEx(Level.FINEST), SystemInfo::OS);
}
private void testLogp() {
final String cls = getClass().getName();
final String mtd = "testLogp";
logger.logp(Level.SEVERE, cls, mtd, supplier);
logger.logp(Level.WARNING, cls, mtd, supplier);
logger.logp(Level.INFO, cls, mtd, supplier);
logger.logp(Level.CONFIG, cls, mtd, supplier);
logger.logp(Level.FINE, cls, mtd, supplier);
logger.logp(Level.FINER, cls, mtd, supplier);
logger.logp(Level.FINEST, cls, mtd, supplier);
// Lambda expression
logger.logp(Level.FINE, cls, mtd, () ->
"Timestamp: " + System.currentTimeMillis() +
", user home directory: " + System.getProperty("user.home"));
// Method reference
logger.logp(Level.FINER, cls, mtd, SystemInfo::Java);
logger.logp(Level.FINEST, cls, mtd, SystemInfo::OS);
}
private void testLogpThrown() {
final String cls = getClass().getName();
final String mtd = "testLogpThrown";
logger.logp(Level.SEVERE, cls, mtd, new HelperEx(Level.SEVERE), supplier);
logger.logp(Level.WARNING, cls, mtd, new HelperEx(Level.WARNING), supplier);
logger.logp(Level.INFO, cls, mtd, new HelperEx(Level.INFO), supplier);
logger.logp(Level.CONFIG, cls, mtd, new HelperEx(Level.CONFIG), supplier);
logger.logp(Level.FINE, cls, mtd, new HelperEx(Level.FINE), supplier);
logger.logp(Level.FINER, cls, mtd, new HelperEx(Level.FINER), supplier);
logger.logp(Level.FINEST, cls, mtd, new HelperEx(Level.FINEST), supplier);
// Lambda expression
logger.logp(Level.FINE, cls, mtd, new HelperEx(Level.FINE), () ->
"Timestamp: " + System.currentTimeMillis() +
", user home directory: " + System.getProperty("user.home"));
// Method reference
logger.logp(Level.FINER, cls, mtd, new HelperEx(Level.FINER), SystemInfo::Java);
logger.logp(Level.FINEST, cls, mtd, new HelperEx(Level.FINEST), SystemInfo::OS);
}
private void testLevelConvenientMethods() {
logger.severe(supplier);
logger.warning(supplier);
logger.info(supplier);
logger.config(supplier);
logger.fine(supplier);
logger.finer(supplier);
logger.finest(supplier);
// Lambda expression
logger.fine(() ->
"Timestamp: " + System.currentTimeMillis() +
", user home directory: " + System.getProperty("user.home"));
// Method reference
logger.finer(SystemInfo::Java);
logger.finest(SystemInfo::OS);
}
private void validate(int index, boolean thrown, String methodName) {
assertEquals(supplier.getCount(), invokes[index]);
assertEquals(handler.getCount(), log_count[index]);
// Verify associated Throwable is right
if (thrown) {
for (LogRecord r: handler.getLogs()) {
assertTrue(r.getThrown() instanceof HelperEx, "Validate Thrown");
HelperEx e = (HelperEx) r.getThrown();
assertEquals(r.getLevel(), e.getLevel(), "Validate Thrown Log Level");
}
}
if (methodName != null) {
for (LogRecord r: handler.getLogs()) {
assertEquals(r.getSourceClassName(), getClass().getName());
assertEquals(r.getSourceMethodName(), methodName);
}
}
}
public void verifyLogLevel() {
for (int i = 0; i < levels.length; i++) {
logger.setLevel(levels[i]);
setup();
testLog();
validate(i, false, null);
setup();
testLogThrown();
validate(i, true, null);
setup();
testLogp();
validate(i, false, "testLogp");
setup();
testLogpThrown();
validate(i, true, "testLogpThrown");
setup();
testLevelConvenientMethods();
validate(i, false, null);
}
}
}

View file

@ -0,0 +1,101 @@
/*
* Copyright (c) 2006, 2020, 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 4994705
*
* @summary deadlock in LogManager
* @author Serguei Spitsyn / SAP
*
* @library /test/lib
* @build LoggingDeadlock
* @run main/timeout=15 LoggingDeadlock
* @key randomness
*/
/*
*
* There can be a deadlock between two class initializations.
* It happens if the LogManager.<clinit> and the Logger.<clinit>
* are invoked concurrently on two different threads.
* There is a cyclic dependence between the two static initializers:
* 1. LogManager.<clinit> instantiate the class RootLogger which
* is a subclass of the Logger class.
* It requires the Logger class initialization to complete.
* 2. Logger.<clinit> initializes the field "global", so it
* it makes a call: Logger.getLogger("global").
* Subsequently the LogManager static method getLogManager()
* is called which requires the LogManager class
* initialization to complete.
* This cyclic dependence causes a deadlock, so two class
* initializations are waiting for each other.
* This is a regression test for this bug.
*/
import java.util.Random;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import jdk.test.lib.RandomFactory;
public class LoggingDeadlock {
private static int preventLoopElision;
private static final Random random = RandomFactory.getRandom();
public static void randomDelay() {
int runs = random.nextInt(1000000);
int c = 0;
for (int i = 0; i < runs; ++i) {
c = c + i;
}
preventLoopElision = c;
}
public static void main(String[] args) throws InterruptedException{
Thread t1 = new Thread(new Runnable() {
public void run() {
randomDelay();
// Trigger Logger.<clinit>
Logger.getAnonymousLogger();
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
randomDelay();
// Trigger LogManager.<clinit>
LogManager.getLogManager();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("\nTest passed");
}
}

View file

@ -0,0 +1,335 @@
/*
* Copyright (c) 2006, 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 6467152 6716076 6829503 8132550
* @summary deadlock occurs in LogManager initialization and JVM termination
* @key randomness
* @library /test/lib
* @author Serguei Spitsyn / Hitachi / Martin Buchholz
*
* @build LoggingDeadlock2
* @run main LoggingDeadlock2
*/
/*
*
* There is a clear deadlock between LogManager.<clinit> and
* Cleaner.run() methods.
* T1 thread:
* The LogManager.<clinit> creates LogManager.manager object,
* sets shutdown hook with the Cleaner class and then waits
* to lock the LogManager.manager monitor.
* T2 thread:
* It is started by the System.exit() as shutdown hook thread.
* It locks the LogManager.manager monitor and then calls the
* static methods of the LogManager class (in this particular
* case it is a trick of the inner classes implementation).
* It is waits when the LogManager.<clinit> is completed.
*
* This is a regression test for this bug.
*/
import java.util.List;
import java.util.Random;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.LogManager;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.concurrent.TimeUnit;
import jdk.test.lib.process.ProcessTools;
public class LoggingDeadlock2 {
// ask child process to dumpstack after 60secs
public static final long DUMP_STACK_FREQUENCY_MS = 60000;
// A marker that allows to validate the subprocess output.
public static final String MARKER = "$";
public static void realMain(String arg[]) throws Throwable {
try {
System.out.println(javaChildArgs);
// Build process (with VM flags)
ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(
javaChildArgs);
ProcessResults r = run(pb.start());
equal(r.exitValue(), 99);
// output of subprocess should end with "$"
final String out = r.out();
final String trailingOutput = out.indexOf(MARKER) > -1
? out.substring(out.indexOf(MARKER)+MARKER.length())
: out;
equal(trailingOutput, "");
equal(r.err(), "");
equal(out.startsWith("JavaChild started"), true);
equal(out.endsWith("$"), true);
} catch (Throwable t) { unexpected(t); }
}
public static class JavaChild {
public static void main(String args[]) throws Throwable {
System.out.println("JavaChild started");
final CyclicBarrier startingGate = new CyclicBarrier(2);
final Throwable[] thrown = new Throwable[1];
// Some random variation, to help tickle races.
final Random rnd = new Random();
final long seed = rnd.nextLong();
rnd.setSeed(seed);
System.out.println("seed=" + seed);
final boolean dojoin = rnd.nextBoolean();
final int JITTER = 1024;
final int iters1 = rnd.nextInt(JITTER);
final int iters2 = JITTER - iters1;
final AtomicInteger counter = new AtomicInteger(0);
System.out.println("dojoin=" + dojoin);
System.out.println("iters1=" + iters1);
System.out.println("iters2=" + iters2);
Thread exiter = new Thread() {
public void run() {
try {
startingGate.await();
for (int i = 0; i < iters1; i++)
counter.getAndIncrement();
System.exit(99);
} catch (Throwable t) {
t.printStackTrace();
System.exit(86);
}
}};
exiter.start();
System.out.println("exiter started");
// signal end of verbose output
System.out.print(MARKER);
System.out.flush();
startingGate.await();
for (int i = 0; i < iters2; i++)
counter.getAndIncrement();
// This may or may not result in a first call to
// Runtime.addShutdownHook after shutdown has already
// commenced.
LogManager.getLogManager();
if (dojoin) {
exiter.join();
if (thrown[0] != null)
throw new Error(thrown[0]);
check(counter.get() == JITTER);
}
}
}
//----------------------------------------------------------------
// The rest of this test is copied from ProcessBuilder/Basic.java
//----------------------------------------------------------------
private static final String jstackExe =
System.getProperty("java.home") +
File.separator + "bin" + File.separator + "jstack";
private static final String classpath =
System.getProperty("java.class.path");
private static final List<String> javaChildArgs = List.of(
"-classpath", classpath, "LoggingDeadlock2$JavaChild");
private static class ProcessResults {
private final String out;
private final String err;
private final int exitValue;
private final Throwable throwable;
public ProcessResults(String out,
String err,
int exitValue,
Throwable throwable) {
this.out = out;
this.err = err;
this.exitValue = exitValue;
this.throwable = throwable;
}
public String out() { return out; }
public String err() { return err; }
public int exitValue() { return exitValue; }
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("<STDOUT>\n" + out() + "</STDOUT>\n")
.append("<STDERR>\n" + err() + "</STDERR>\n")
.append("exitValue = " + exitValue + "\n");
if (throwable != null)
sb.append(throwable.getStackTrace());
return sb.toString();
}
}
private static class StreamAccumulator extends Thread {
private final InputStream is;
private final StringBuilder sb = new StringBuilder();
private Throwable throwable = null;
public String result () throws Throwable {
if (throwable != null)
throw throwable;
return sb.toString();
}
StreamAccumulator (InputStream is) {
this.is = is;
}
public void run() {
try {
Reader r = new InputStreamReader(is);
int n;
while ((n = r.read()) > 0) {
sb.append((char)n);
// prints everything immediately to System.out so that we can
// see the traces even in the event of a test timeout
System.out.write((char)n);
System.out.flush();
}
} catch (Throwable t) {
throwable = t;
} finally {
try { is.close(); }
catch (Throwable t) { throwable = t; }
}
}
}
/**
* If the child process deadlocks, then the parent may fail in timeout.
* In that case, we won't have any interesting traces, unless we manage
* to get a thread dump from the child.
* It is unsure whether obtaining a thread dump from a deadlocked child
* will work - but maybe we could see something if the timeout is a false
* positive (the child has not deadlocked but hasn't managed to fully start
* yet, for instance).
* The idea here is to periodically try to obtain a thread dump from the
* child, every 60sec - which should be always less than the jtreg timeout.
*/
private static class TimeoutThread extends Thread {
final long ms;
final Process process;
TimeoutThread(long ms, Process p) {
super("TimeoutThread");
setDaemon(true);
this.ms = ms;
this.process = p;
}
@Override
public void run() {
long start = System.nanoTime();
try {
while (true) {
sleep(ms);
System.err.println("Timeout reached: " + ms);
if (process.isAlive()) {
long pid = process.pid();
ProcessBuilder jstack = new ProcessBuilder(jstackExe, String.valueOf(pid));
System.err.println("Dumping subprocess stack: " + pid);
Process p = jstack.inheritIO().start();
p.waitFor(ms, TimeUnit.MILLISECONDS);
} else {
System.err.println("Process is not alive!");
break;
}
}
} catch (InterruptedException ex) {
System.err.println("Interrupted: " + ex);
} catch (IOException io) {
System.err.println("Failed to get stack from subprocess");
io.printStackTrace();
}
}
}
private static ProcessResults run(Process p) {
Throwable throwable = null;
int exitValue = -1;
String out = "";
String err = "";
StreamAccumulator outAccumulator =
new StreamAccumulator(p.getInputStream());
StreamAccumulator errAccumulator =
new StreamAccumulator(p.getErrorStream());
try {
System.out.println("Waiting for child process to exit");
outAccumulator.start();
errAccumulator.start();
// ask subprocess to dump stack every 60 secs.
new TimeoutThread(DUMP_STACK_FREQUENCY_MS, p).start();
exitValue = p.waitFor();
System.out.println("\nChild exited with status: " + exitValue);
outAccumulator.join();
errAccumulator.join();
out = outAccumulator.result();
err = errAccumulator.result().replaceAll(".* VM warning:.* deprecated.*\\R", "");
} catch (Throwable t) {
throwable = t;
}
return new ProcessResults(out, err, exitValue, throwable);
}
//--------------------- Infrastructure ---------------------------
static volatile int passed = 0, failed = 0;
static void pass() {passed++;}
static void fail() {failed++; Thread.dumpStack();}
static void fail(String msg) {System.out.println(msg); fail();}
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
static void check(boolean cond) {if (cond) pass(); else fail();}
static void check(boolean cond, String m) {if (cond) pass(); else fail(m);}
static void equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) pass();
else fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
try {realMain(args);} catch (Throwable t) {unexpected(t);}
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View file

@ -0,0 +1,81 @@
/*
* Copyright (c) 2011, 2012, 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 6487638 7041595
* @summary Calling LogManager.addLogger() and Logger.getLogger() cause deadlock
* @author Serguei Spitsyn
* @build LoggingDeadlock3
* @run main/timeout=80 LoggingDeadlock3
*/
import java.io.*;
import java.util.logging.LogManager;
import java.util.logging.Logger;
public class LoggingDeadlock3 {
static final int ITER_CNT = 50000;
static final String MSG_PASSED = "LoggingDeadlock3: passed";
static final LogManager logMgr = LogManager.getLogManager();
static final PrintStream out = System.out;
public static void main(String args[]) throws Exception {
String tstSrc = System.getProperty("test.src");
File fname = new File(tstSrc, "LoggingDeadlock3.props");
String prop = fname.getCanonicalPath();
System.setProperty("java.util.logging.config.file", prop);
logMgr.readConfiguration();
Thread t1 = new Thread(new AddLogger());
Thread t2 = new Thread(new GetLogger());
t1.start(); t2.start();
t1.join(); t2.join();
out.println("\n" + MSG_PASSED);
}
public static class MyLogger extends Logger {
protected MyLogger(String name) { super(name, null); }
}
public static class GetLogger implements Runnable {
public void run() {
for (int cnt = 0; cnt < ITER_CNT * 8; cnt++) {
Logger.getLogger("com.sun.Hello"+cnt/10);
if (cnt % 1000 == 0) out.print("1");
if (cnt % 10000 == 0) out.println();
}
}
}
public static class AddLogger implements Runnable {
public void run() {
for (int cnt = 0; cnt < ITER_CNT; cnt++) {
Logger addLogger = new MyLogger("com.sun.Hello"+cnt);
logMgr.addLogger(addLogger);
if (cnt % 100 == 0) out.print("2");
if (cnt % 1000 == 0) out.println();
}
}
}
}

View file

@ -0,0 +1,3 @@
com.sun.LEVEL=FINE
com.sun.level=FINE
com.sun.Hello.level=INFO

View file

@ -0,0 +1,107 @@
/*
* Copyright (c) 2011, 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 6977677 8004928
* @summary Deadlock between LogManager.<clinit> and Logger.getLogger()
* @author Daniel D. Daugherty
* @modules java.base/sun.util.logging
* java.logging
* @compile -XDignore.symbol.file LoggingDeadlock4.java
* @run main/othervm/timeout=15 LoggingDeadlock4
*/
import java.util.concurrent.CountDownLatch;
import java.util.logging.LogManager;
import java.util.logging.Logger;
public class LoggingDeadlock4 {
private static CountDownLatch barrier = new CountDownLatch(1);
private static CountDownLatch lmIsRunning = new CountDownLatch(1);
private static CountDownLatch logIsRunning = new CountDownLatch(1);
// Create a sun.util.logging.PlatformLogger$JavaLogger object
// that has to be redirected when the LogManager class
// is initialized. This can cause a deadlock between
// LogManager.<clinit> and Logger.getLogger().
private static final sun.util.logging.PlatformLogger log =
sun.util.logging.PlatformLogger.getLogger("java.util.logging");
public static void main(String[] args) {
System.out.println("main: LoggingDeadlock4 is starting.");
Thread lmThread = new Thread("LogManagerThread") {
public void run() {
// let main know LogManagerThread is running
lmIsRunning.countDown();
System.out.println(Thread.currentThread().getName()
+ ": is running.");
try {
barrier.await(); // wait for race to start
} catch (InterruptedException e) {
}
LogManager.getLogManager();
}
};
lmThread.start();
Thread logThread = new Thread("LoggerThread") {
public void run() {
// let main know LoggerThread is running
logIsRunning.countDown();
System.out.println(Thread.currentThread().getName()
+ ": is running.");
try {
barrier.await(); // wait for race to start
} catch (InterruptedException e) {
}
Logger.getLogger("foo logger");
}
};
logThread.start();
try {
// wait for LogManagerThread and LoggerThread to get going
lmIsRunning.await();
logIsRunning.await();
} catch (InterruptedException e) {
}
barrier.countDown(); // start the race
try {
lmThread.join();
logThread.join();
} catch (InterruptedException ie) {
}
System.out.println("main: LoggingDeadlock4 is done.");
}
}

View file

@ -0,0 +1,215 @@
/*
* 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.
*/
/*
* @test
* @bug 8349206
* @summary j.u.l.Handler classes create deadlock risk via synchronized publish() method.
* @library /test/lib
* @modules java.base/sun.util.logging
* java.logging
* @run main/othervm LoggingDeadlock5
*/
import jdk.test.lib.Utils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.concurrent.Semaphore;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.StreamHandler;
/**
* This test verifies that logging Handler implementations no longer suffer
* from the deadlock risk outlined in JDK-8349206.
*
* <p>This test should reliably cause, and detect, deadlocks in a timely
* manner if the problem occurs, and SHOULD NOT time out.
*/
public class LoggingDeadlock5 {
private static final String UTF_8 = StandardCharsets.UTF_8.name();
// Formatter which calls toString() on all arguments.
private static final Formatter TEST_FORMATTER = new Formatter() {
@Override
public String format(LogRecord record) {
// All we care about is that our formatter will invoke toString() on user arguments.
// This can be called without arguments in one of the threads.
Object[] parameters = record.getParameters();
if (parameters != null) {
for (Object p : parameters) {
var unused = p.toString();
}
}
return "<formatted string>";
}
};
// A handler which *should* cause a deadlock by synchronizing publish().
private static final Handler SELF_TEST_HANDLER = new Handler() {
@Override
public synchronized void publish(LogRecord record) {
TEST_FORMATTER.formatMessage(record);
}
@Override
public void flush() {
}
@Override
public void close() {
}
};
public static void main(String[] args) throws InterruptedException, IOException {
// Self test that deadlocks are correctly caught (and don't deadlock the test itself).
new DeadLocker(SELF_TEST_HANDLER).checkDeadlock(true);
// In theory, we should test SocketHandler here as well because, while
// it is just a subclass, it could be adding locking around the call to
// super.publish(). However, it is problematic in a test environment
// since it needs to open sockets. It is not being tested for now.
assertNoDeadlock(new StreamHandler(new ByteArrayOutputStream(0), TEST_FORMATTER));
// Single log file in current directory, no rotation. JTreg will delete
// temporary files after test completion.
assertNoDeadlock(new FileHandler("temp_log_file"));
// Any other problematic handler classes can be easily added here if
// they are simple enough to be constructed in a test environment.
}
static void assertNoDeadlock(Handler handler) throws InterruptedException, UnsupportedEncodingException {
try {
handler.setEncoding(UTF_8);
new DeadLocker(handler).checkDeadlock(false);
} finally {
// Handlers may have open resources, so must be closed.
handler.close();
}
}
private static class DeadLocker {
// Since this is used to self-test for an expected deadlock, it will
// delay the test by at least this duration (so being overly large is
// a potential issue). The deadlock is set up so it should occur almost
// immediately (if it occurs), but tests are run under very high loads
// in higher tiers, so it's necessary to be a bit pessimistic here.
private final static Duration JOIN_WAIT =
Duration.ofMillis(Utils.adjustTimeout(2000));
private final Semaphore readyToDeadlock = new Semaphore(0);
private final Semaphore userLockIsHeld = new Semaphore(0);
private final Logger logger = Logger.getAnonymousLogger();
private final Handler handlerUnderTest;
private final Object userLock = new Object();
private boolean deadlockEncountered = true;
DeadLocker(Handler handlerUnderTest) {
this.handlerUnderTest = handlerUnderTest;
this.logger.setUseParentHandlers(false);
this.logger.addHandler(handlerUnderTest);
this.logger.setLevel(Level.INFO);
this.handlerUnderTest.setLevel(Level.INFO);
}
void checkDeadlock(boolean expectDeadlock) throws InterruptedException {
// Note: Even though the message format string isn't used in the
// test formatter, it must be a valid log format string (Logger
// detects if there are no "{n}" placeholders and skips calling
// the formatter otherwise).
Thread t1 = runAsDaemon(() -> logger.log(Level.INFO, "Hello {0}", new Argument()));
readyToDeadlock.acquireUninterruptibly();
// First thread is blocked until userLockIsHeld is released in t2.
Thread t2 = runAsDaemon(this::locksThenLogs);
// If deadlock occurs, the join() calls both return false.
int threadsBlocked = 0;
if (!t1.join(JOIN_WAIT)) {
threadsBlocked += 1;
}
if (!t2.join(JOIN_WAIT)) {
threadsBlocked += 1;
}
// These indicate test problems, not a failure of the code under test.
errorIf(threadsBlocked == 1, "Inconsistent number of blocked threads.");
errorIf(deadlockEncountered != (threadsBlocked == 2),
"Deadlock reporting should coincide with number of blocked threads.");
// This is the actual test assertion.
if (expectDeadlock != deadlockEncountered) {
String issue = expectDeadlock ? "Expected deadlock but none occurred" : "Unexpected deadlock";
throw new AssertionError(errorMsg(issue));
}
}
void locksThenLogs() {
synchronized (userLock) {
userLockIsHeld.release();
logger.log(Level.INFO, "This will cause a deadlock if the Handler locks!");
}
}
void calledFromToString() {
this.readyToDeadlock.release();
this.userLockIsHeld.acquireUninterruptibly();
synchronized (userLock) {
this.deadlockEncountered = false;
}
}
class Argument {
@Override
public String toString() {
calledFromToString();
return "<to string>";
}
}
String errorMsg(String msg) {
return String.format("Handler deadlock test [%s]: %s", handlerUnderTest.getClass().getName(), msg);
}
void errorIf(boolean condition, String msg) {
if (condition) {
throw new RuntimeException(errorMsg("TEST ERROR - " + msg));
}
}
}
static Thread runAsDaemon(Runnable job) {
Thread thread = new Thread(job);
thread.setDaemon(true);
thread.start();
return thread;
}
}

View file

@ -0,0 +1,253 @@
/*
* Copyright (c) 2003, 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 5007165
*
* @summary Basic Test for LoggingMXBean via MBeanServer
* @author Ron Mann
* @modules java.logging
* java.management
* @build LoggingMXBeanTest
* @run main LoggingMXBeanTest
*/
import javax.management.*;
import java.util.logging.*;
public class LoggingMXBeanTest
{
LoggingMXBean mBean;
ObjectName objectName = null;
static String LOGGER_NAME_1 = "com.sun.management.Logger1";
static String LOGGER_NAME_2 = "com.sun.management.Logger2";
static Logger logger1;
static Logger logger2;
public LoggingMXBeanTest() throws Exception {
/*
* Create the MBeanServeri, register the LoggingMXBean
*/
System.out.println( "***************************************************" );
System.out.println( "********** LoggingMXBean Unit Test **********" );
System.out.println( "***************************************************" );
System.out.println( "" );
System.out.println( "*******************************" );
System.out.println( "*********** Phase 1 ***********" );
System.out.println( "*******************************" );
System.out.println( " Creating MBeanServer " );
System.out.print( " Register LoggingMXBean: " );
MBeanServer mbs = MBeanServerFactory.createMBeanServer();
String[] list = new String[0];
try {
objectName = new ObjectName(LogManager.LOGGING_MXBEAN_NAME);
mBean = LogManager.getLoggingMXBean();
mbs.registerMBean( mBean, objectName );
}
catch ( Exception e ) {
System.out.println( "FAILED" );
throw e;
}
System.out.println( "PASSED" );
System.out.println("");
/*
* Access our MBean to get the current list of Loggers
*/
System.out.println( "*******************************" );
System.out.println( "*********** Phase 2 ***********" );
System.out.println( "*******************************" );
System.out.println( " Test Logger Name retrieval (getLoggerNames) " );
// check that Level object are returned properly
try {
list = (String[]) mbs.getAttribute( objectName, "LoggerNames" );
}
catch ( Exception e ) {
System.out.println(" : FAILED" );
throw e;
}
/*
* Dump the list of Loggers already present, if any
*/
Object[] params = new Object[1];
String[] signature = new String[1];
Level l;
if ( list == null ) {
System.out.println(" : PASSED. No Standard Loggers Present" );
System.out.println("");
}
else {
System.out.println(" : PASSED. There are " + list.length + " Loggers Present" );
System.out.println("");
System.out.println( "*******************************" );
System.out.println( "*********** Phase 2B **********" );
System.out.println( "*******************************" );
System.out.println( " Examine Existing Loggers" );
for ( int i = 0; i < list.length; i++ ) {
try {
params[0] = list[i];
signature[0] = "java.lang.String";
String levelName = (String) mbs.invoke( objectName, "getLoggerLevel", params, signature );
System.out.println(" : Logger #" + i + " = " + list[i] );
System.out.println(" : Level = " + levelName );
}
catch ( Exception e ) {
System.out.println(" : FAILED" );
throw e;
}
}
System.out.println(" : PASSED" );
}
/*
* Create two new loggers to the list of Loggers already present
*/
System.out.println("");
System.out.println( "*******************************" );
System.out.println( "*********** Phase 3 ***********" );
System.out.println( "*******************************" );
System.out.println( " Create and test new Loggers" );
logger1 = Logger.getLogger( LOGGER_NAME_1 );
logger2 = Logger.getLogger( LOGGER_NAME_2 );
// check that Level object are returned properly
try {
list = (String[]) mbs.getAttribute( objectName, "LoggerNames" );
}
catch ( Exception e ) {
System.out.println(" : FAILED" );
throw e;
}
/*
* Check for the existence of our new Loggers
*/
boolean log1 = false, log2 = false;
if ( list == null || list.length < 2 ) {
System.out.println(" : FAILED. Could not Detect the presense of the new Loggers" );
throw new RuntimeException(
"Could not Detect the presense of the new Loggers");
}
else {
for ( int i = 0; i < list.length; i++ ) {
if ( list[i].equals( LOGGER_NAME_1 ) ) {
log1 = true;
System.out.println( " : Found new Logger : " + list[i] );
}
if ( list[i].equals( LOGGER_NAME_2 ) ) {
log2 = true;
System.out.println( " : Found new Logger : " + list[i] );
}
}
if ( log1 && log2 )
System.out.println( " : PASSED." );
else {
System.out.println( " : FAILED. Could not Detect the new Loggers." );
throw new RuntimeException(
"Could not Detect the presense of the new Loggers");
}
}
/*
* Set a new Logging levels and check that it succeeded
*/
System.out.println("");
System.out.println( "*******************************" );
System.out.println( "*********** Phase 4 ***********" );
System.out.println( "*******************************" );
System.out.println( " Set and Check the Logger Level" );
log1 = false;
log2 = false;
try {
// Set the level of logger1 to ALL
params = new Object[2];
signature = new String[2];
params[0] = LOGGER_NAME_1;
params[1] = Level.ALL.getName();
signature[0] = "java.lang.String";
signature[1] = "java.lang.String";
mbs.invoke( objectName, "setLoggerLevel", params, signature );
// Set the level of logger2 to FINER
params[0] = LOGGER_NAME_2;
params[1] = Level.FINER.getName();
mbs.invoke( objectName, "setLoggerLevel", params, signature );
// Okay read back the Level from Logger1. Should be ALL
params = new Object[1];
signature = new String[1];
params[0] = LOGGER_NAME_1;
signature[0] = "java.lang.String";
String levelName = (String) mbs.invoke( objectName, "getLoggerLevel", params, signature );
l = Level.parse(levelName);
System.out.print(" Logger1: " );
if ( l.equals( Level.ALL ) ) {
System.out.println("Level Set to ALL: PASSED" );
log1 = true;
}
else {
System.out.println("Level Set to ALL: FAILED" );
throw new RuntimeException(
"Level Set to ALL but returned " + l.toString());
}
// Okay read back the Level from Logger2. Should be FINER
params = new Object[1];
signature = new String[1];
params[0] = LOGGER_NAME_2;
signature[0] = "java.lang.String";
levelName = (String) mbs.invoke( objectName, "getLoggerLevel", params, signature );
l = Level.parse(levelName);
System.out.print(" Logger2: " );
if ( l.equals( Level.FINER ) ) {
System.out.println("Level Set to FINER: PASSED" );
log2 = true;
}
else {
System.out.println("Level Set to FINER: FAILED" );
throw new RuntimeException(
"Level Set to FINER but returned " + l.toString());
}
}
catch ( Exception e ) {
throw e;
}
System.out.println( "" );
System.out.println( "***************************************************" );
System.out.println( "***************** All Tests Passed ****************" );
System.out.println( "***************************************************" );
}
public static void main(String[] argv) throws Exception {
new LoggingMXBeanTest();
}
}

View file

@ -0,0 +1,191 @@
/*
* Copyright (c) 2004, 2012, 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 5007165
* @summary Basic Test for LoggingMXBean (direct access to MXBean)
* @author Mandy Chung
*
* @build LoggingMXBeanTest2
* @run main LoggingMXBeanTest2
*/
import java.util.logging.*;
import java.util.List;
import java.util.ListIterator;
public class LoggingMXBeanTest2
{
static LoggingMXBean mbean = LogManager.getLoggingMXBean();
static String LOGGER_NAME_1 = "com.sun.management.Logger";
static String LOGGER_NAME_2 = "com.sun.management.Logger.Logger2";
static String UNKNOWN_LOGGER_NAME = "com.sun.management.Unknown";
static Logger logger1;
static Logger logger2;
public LoggingMXBeanTest2() throws Exception {
logger1 = Logger.getLogger( LOGGER_NAME_1 );
logger1.setLevel(Level.FINE);
logger2 = Logger.getLogger( LOGGER_NAME_2 );
logger2.setLevel(null);
/*
* Check for the existence of our new Loggers
*/
System.out.println("Test Logger Name retrieval (getLoggerNames)");
boolean log1 = false, log2 = false;
List<String> loggers = mbean.getLoggerNames();
if (loggers == null || loggers.size() < 2) {
throw new RuntimeException(
"Could not Detect the presense of the new Loggers");
}
for (ListIterator<String> iter = loggers.listIterator(); iter.hasNext(); ) {
String logger = iter.next();
if (logger.equals(LOGGER_NAME_1)) {
log1 = true;
System.out.println(" : Found new Logger : " + logger);
}
if (logger.equals(LOGGER_NAME_2)) {
log2 = true;
System.out.println(" : Found new Logger : " + logger);
}
}
if ( log1 && log2 )
System.out.println(" : PASSED." );
else {
System.out.println(" : FAILED. Could not Detect the new Loggers." );
throw new RuntimeException(
"Could not Detect the presense of the new Loggers");
}
System.out.println("Test getLoggerLevel");
String l1 = mbean.getLoggerLevel(LOGGER_NAME_1);
System.out.println(" : Level for Logger " + LOGGER_NAME_1 + " : " + l1);
if (!l1.equals(Level.FINE.getName())) {
throw new RuntimeException(
"Expected level for " + LOGGER_NAME_1 + " = " +
Level.FINE.getName() + " but got " + l1);
}
String l2 = mbean.getLoggerLevel(LOGGER_NAME_2);
System.out.println(" : Level for Logger " + LOGGER_NAME_2 + " : " + l2);
if (!l2.equals("")) {
throw new RuntimeException(
"Expected level for " + LOGGER_NAME_2 + " = \"\"" +
" but got " + l2);
}
String l3 = mbean.getLoggerLevel(UNKNOWN_LOGGER_NAME);
System.out.println(" : Level for unknown logger : " + l3);
if (l3 != null) {
throw new RuntimeException(
"Expected level for " + UNKNOWN_LOGGER_NAME + " = null" +
" but got " + l3);
}
System.out.println("Test setLoggerLevel");
mbean.setLoggerLevel(LOGGER_NAME_1, "INFO");
System.out.println(" : Set Level for Logger " + LOGGER_NAME_1 + " to: INFO");
Level l = logger1.getLevel();
if (l != Level.INFO) {
throw new RuntimeException(
"Expected level for " + LOGGER_NAME_1 + " = " +
Level.INFO + " but got " + l);
}
mbean.setLoggerLevel(LOGGER_NAME_2, "SEVERE");
System.out.println(" : Set Level for Logger " + LOGGER_NAME_2 + " to: SERVER");
l = logger2.getLevel();
if (l != Level.SEVERE) {
throw new RuntimeException(
"Expected level for " + LOGGER_NAME_2 + " = " +
Level.SEVERE+ " but got " + l);
}
mbean.setLoggerLevel(LOGGER_NAME_1, null);
System.out.println(" : Set Level for Logger " + LOGGER_NAME_1 + " to: null");
l = logger1.getLevel();
if (l != null) {
throw new RuntimeException(
"Expected level for " + LOGGER_NAME_1 + " = null " +
" but got " + l);
}
boolean iaeCaught = false;
System.out.println(" : Set Level for unknown Logger to: FINE");
try {
mbean.setLoggerLevel(UNKNOWN_LOGGER_NAME, "FINE");
} catch (IllegalArgumentException e) {
// expected
iaeCaught = true;
System.out.println(" : IllegalArgumentException caught as expected");
}
if (!iaeCaught) {
throw new RuntimeException(
"Expected IllegalArgumentException for setting level for " +
UNKNOWN_LOGGER_NAME + " not thrown");
}
iaeCaught = false;
System.out.println(" : Set Level for Logger " + LOGGER_NAME_1 + " to: DUMMY");
try {
mbean.setLoggerLevel(LOGGER_NAME_1, "DUMMY");
} catch (IllegalArgumentException e) {
// expected
iaeCaught = true;
System.out.println(" : IllegalArgumentException caught as expected");
}
if (!iaeCaught) {
throw new RuntimeException(
"Expected IllegalArgumentException for invalid level.");
}
System.out.println("Test getParentLoggerName");
String p1 = mbean.getParentLoggerName(LOGGER_NAME_2);
System.out.println(" : Parent Logger for " + LOGGER_NAME_2 + " : " + p1);
if (!p1.equals(LOGGER_NAME_1)) {
throw new RuntimeException(
"Expected parent for " + LOGGER_NAME_2 + " = " +
LOGGER_NAME_1 + " but got " + p1);
}
String p2 = mbean.getParentLoggerName("");
System.out.println(" : Parent Logger for \"\" : " + p2);
if (!p2.equals("")) {
throw new RuntimeException(
"Expected parent for root logger \"\" = \"\"" +
" but got " + p2);
}
String p3 = mbean.getParentLoggerName(UNKNOWN_LOGGER_NAME);
System.out.println(" : Parent Logger for unknown logger : " + p3);
if (p3 != null) {
throw new RuntimeException(
"Expected level for " + UNKNOWN_LOGGER_NAME + " = null" +
" but got " + p3);
}
}
public static void main(String[] argv) throws Exception {
new LoggingMXBeanTest2();
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2005, 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 4948142
@summary test NIO changes don't generate unchecked exceptions
@run main LoggingNIOChange
*/
import java.util.logging.*;
public class LoggingNIOChange {
public static void main(String args[]) throws Exception {
ConsoleHandler console = new ConsoleHandler();
XMLFormatter f = new XMLFormatter();
try {
console.setEncoding("junk");
f.getHead(console);
console.setEncoding(null);
f.getHead(console);
}catch (java.io.UnsupportedEncodingException e) {
}
}
}

View file

@ -0,0 +1,156 @@
/*
* Copyright (c) 2012, 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 7159567
* @summary Test of configuring a MemoryHandler sub-class handler target via logging.properties
* @run main/othervm MemoryHandlerTest
*/
import java.io.File;
import java.io.IOException;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.MemoryHandler;
public class MemoryHandlerTest {
static final String CFG_FILE_PROP = "java.util.logging.config.file";
static final String LM_PROP_FNAME = "MemoryHandlerTest.props";
static Logger logger;
public static void main(String... args) throws IOException {
// load logging.propertes for the test
String tstSrc = System.getProperty("test.src", ".");
File fname = new File(tstSrc, LM_PROP_FNAME);
String prop = fname.getCanonicalPath();
System.setProperty(CFG_FILE_PROP, prop);
LogManager.getLogManager();
// create a logger
logger = Logger.getLogger(MemoryHandlerTest.class.getName());
// don't have parent handlers get log messages
logger.setUseParentHandlers(false);
//
// Test 1,2: create a CustomMemoryHandler which in the config has
// specified a target of CustomTargetHandler. (1) Make sure that it
// is created and (2) that the target handler is loaded.
//
CustomMemoryHandler cmh = new CustomMemoryHandler();
try {
logger.addHandler(cmh);
} catch (RuntimeException rte) {
throw new RuntimeException(
"Test Failed: did not load java.util.logging.ConsoleHandler as expected",
rte);
}
// if we get here and our config has been processed properly, then we
// should have loaded our target handler
if (CustomTargetHandler.numLoaded !=1) {
throw new RuntimeException(
"Test failed: did not load CustomTargetHandler as expected");
}
//
// Test 3: try to add a handler with no target. This should fail with
// an exception
CustomMemoryHandlerNoTarget cmhnt = null;
try {
cmhnt = new CustomMemoryHandlerNoTarget();
} catch (RuntimeException re) {
// expected -- no target specified
System.out.println("Info: " + re.getMessage() + " as expected.");
}
if (cmhnt != null) {
throw new RuntimeException(
"Test Failed: erroneously loaded CustomMemoryHandlerNoTarget");
}
// Test 4: log a message and check that the target handler is actually used
logger.log(Level.WARNING, "Unused");
if (CustomTargetHandler.numPublished != 1) {
throw new RuntimeException("Test failed: CustomTargetHandler was not used");
}
// Test 5: make sure that SimpleTargetHandler hasn't been erroneously called
if (SimpleTargetHandler.numPublished != 0) {
throw new RuntimeException("Test failed: SimpleTargetHandler has been used");
}
// Test 6: try using SimpleTargetHanlder via standard MemoryHandler
// (which has target set to SimpleTargetHandler)
MemoryHandler mh = new MemoryHandler();
mh.publish(new LogRecord(Level.INFO, "Unused msg to MemoryHandler"));
// see if it made it to the SimpleTargetHandler
if (SimpleTargetHandler.numPublished != 1) {
throw new RuntimeException("Test failed: SimpleTargetHandler was not used");
}
}
public static class CustomMemoryHandler extends MemoryHandler {
}
public static class CustomMemoryHandlerNoTarget extends MemoryHandler {
}
public static class CustomTargetHandler extends Handler {
public static int numPublished;
public static int numLoaded;
public CustomTargetHandler() {
numLoaded++;
}
@Override
public void publish(LogRecord unused) {
numPublished++;
}
@Override
public void flush() {
}
@Override
public void close() throws SecurityException {
}
}
public static class SimpleTargetHandler extends Handler {
public static int numPublished;
@Override
public void publish(LogRecord unused) {
numPublished++;
}
@Override
public void flush() {
}
@Override
public void close() throws SecurityException {
}
}
}

View file

@ -0,0 +1,9 @@
.level= INFO
MemoryHandlerTest$CustomMemoryHandler.push=WARNING
MemoryHandlerTest$CustomMemoryHandlerNoTarget.push=WARNING
MemoryHandlerTest$CustomMemoryHandler.target=MemoryHandlerTest$CustomTargetHandler
handlers=java.util.logging.ConsoleHandler,MemoryHandlerTest$CustomMemoryHandler,MemoryHandlerTest$CustomMemoryHandlerNoTarget
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.MemoryHandler.target=MemoryHandlerTest$SimpleTargetHandler
java.util.logging.MemoryHandler.push = INFO

View file

@ -0,0 +1,182 @@
/*
* Copyright (c) 2012, 2026, 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 6498300 8377457 8379548
*
* @summary regression: parent loggers are not properly registered
* @author ss45998
*
* @build ParentLoggersTest
* @run main/othervm ParentLoggersTest
*/
/*
* There are several original tests which were failed when
* this regression was introduced. This is an extra test
* to ensure that the parent loggers with the defined
* .level property are implicitly registered.
*/
import java.util.*;
import java.io.*;
import java.util.logging.*;
public class ParentLoggersTest {
static final LogManager logMgr = LogManager.getLogManager();
static final PrintStream out = System.out;
static final boolean PASSED = true;
static final boolean FAILED = false;
static final String MSG_PASSED = "ParentLoggersTest: passed";
static final String MSG_FAILED = "ParentLoggersTest: failed";
/* Properties */
static final String TST_SRC_PROP = "test.src";
static final String CFG_FILE_PROP = "java.util.logging.config.file";
static final String LM_PROP_FNAME = "ParentLoggersTest.props";
/* Logger names */
static final String PARENT_NAME_1 = "myParentLogger";
static final String PARENT_NAME_2 = "abc.xyz.foo";
static final String LOGGER_NAME_1 = PARENT_NAME_1 + ".myLogger";
static final String LOGGER_NAME_2 = PARENT_NAME_2 + ".myBar.myLogger";
static final List<String> initialLoggerNames = new ArrayList<>();
static final List<Logger> createdLoggers = new ArrayList<>();
public static void main(String args[]) throws Exception {
// cache the initial set of loggers before this test begins
// to add any loggers
Enumeration<String> e = logMgr.getLoggerNames();
List<String> defaultLoggers = getDefaultLoggerNames();
while (e.hasMoreElements()) {
String logger = e.nextElement();
if (!defaultLoggers.contains(logger)) {
initialLoggerNames.add(logger);
}
}
System.out.println("# default loggers are: " + defaultLoggers);
System.out.println("# initial loggers are: " + initialLoggerNames);
String tstSrc = System.getProperty(TST_SRC_PROP);
File fname = new File(tstSrc, LM_PROP_FNAME);
String prop = fname.getCanonicalPath();
System.setProperty(CFG_FILE_PROP, prop);
logMgr.readConfiguration();
System.out.println();
if (checkLoggers() == PASSED) {
System.out.println(MSG_PASSED);
} else {
System.out.println(MSG_FAILED);
throw new Exception(MSG_FAILED);
}
}
public static List<String> getDefaultLoggerNames() {
List<String> expectedLoggerNames = new ArrayList<>();
// LogManager always creates two loggers:
expectedLoggerNames.add(""); // root logger: ""
expectedLoggerNames.add("global"); // global logger: "global"
return expectedLoggerNames;
}
/* Check: getLoggerNames() must return correct names
* for registered loggers and their parents.
* Returns boolean values: PASSED or FAILED
*/
public static boolean checkLoggers() {
String failMsg = "# checkLoggers: getLoggerNames() returned unexpected loggers";
List<String> expectedLoggerNames = new ArrayList<>(getDefaultLoggerNames());
// Create the logger LOGGER_NAME_1
createdLoggers.add(Logger.getLogger(LOGGER_NAME_1));
expectedLoggerNames.add(PARENT_NAME_1);
expectedLoggerNames.add(LOGGER_NAME_1);
// Create the logger LOGGER_NAME_2
createdLoggers.add(Logger.getLogger(LOGGER_NAME_2));
expectedLoggerNames.add(PARENT_NAME_2);
expectedLoggerNames.add(LOGGER_NAME_2);
Enumeration<String> returnedLoggersEnum = logMgr.getLoggerNames();
List<String> returnedLoggerNames = new ArrayList<>(0);
while (returnedLoggersEnum.hasMoreElements()) {
String logger = returnedLoggersEnum.nextElement();
if (!initialLoggerNames.contains(logger)) {
// filter out the loggers that have been added before this test runs
returnedLoggerNames.add(logger);
}
}
System.out.println("# Created loggers are: " +
createdLoggers.stream().map(Logger::getName).toList());
System.out.println("# Expected loggers are: " + expectedLoggerNames);
System.out.println("# Returned loggers are: " + returnedLoggerNames);
return checkNames(expectedLoggerNames, returnedLoggerNames, failMsg);
}
// Returns boolean values: PASSED or FAILED
private static boolean checkNames(List<String> expNames,
List<String> retNames,
String failMsg) {
boolean status = PASSED;
if (expNames.size() != retNames.size()) {
status = FAILED;
} else if (!new HashSet<>(expNames).equals(new HashSet<>(retNames))) {
status = FAILED;
}
if (!status) {
printFailMsg(expNames, retNames, failMsg);
}
return status;
}
private static void printFailMsg(List<String> expNames,
List<String> retNames,
String failMsg) {
out.println();
out.println(failMsg);
if (expNames.isEmpty()) {
out.println("# there are NO expected logger names");
} else {
out.println("# expected logger names (" + expNames.size() + "):");
for (int i = 0; i < expNames.size(); i++) {
out.println(" expNames[" + i + "] = " + expNames.get(i));
}
}
if (retNames.isEmpty()) {
out.println("# there are NO returned logger names");
} else {
out.println("# returned logger names (" + retNames.size() + "):");
for (int i = 0; i < retNames.size(); i++) {
out.println(" retNames[" + i + "] = " + retNames.get(i));
}
}
}
}

View file

@ -0,0 +1,62 @@
############################################################
# Test Logging Configuration File
#
# This file is a clone of the Default Logging Configuration File
#
# It defines two test specific properties (at the bottom):
# - myParentLogger.level = INFO
# - abc.xyz.foo.level = SEVERE
############################################################
############################################################
# Global properties
############################################################
# "handlers" specifies a comma separated list of log Handler
# classes. These handlers will be installed during VM startup.
# Note that these classes must be on the system classpath.
# By default we only configure a ConsoleHandler, which will only
# show messages at the INFO and above levels.
handlers= java.util.logging.ConsoleHandler
# To also add the FileHandler, use the following line instead.
#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
# Default global logging level.
# This specifies which kinds of events are logged across
# all loggers. For any given facility this global level
# can be overriden by a facility specific level
# Note that the ConsoleHandler also has a separate level
# setting to limit messages printed to the console.
.level= INFO
############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################
# default file output is in user's home directory.
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
# Limit the message that are printed on the console to INFO and above.
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
############################################################
# Facility specific properties.
# Provides extra control for each logger.
############################################################
# For example, set the com.xyz.foo logger to only log SEVERE
# messages:
com.xyz.foo.level = SEVERE
############################################################
# The test specific properties.
############################################################
myParentLogger.level = INFO
abc.xyz.foo.level = SEVERE

View file

@ -0,0 +1,687 @@
/*
* Copyright (c) 2011, 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.concurrent.atomic.AtomicInteger;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* RacingThreadsTest is a support class for creating a test
* where multiple threads are needed to exercise a code path.
* The RacingThreadsTest class is typically used as follows:
* <ul>
* <li>
* Extend RacingThreadsTest class in order to provide the test
* specific variables and/or code, e.g., <br>
* public class MyRacingThreadsTest extends RacingThreadsTest
* <li>
* Use
* "new MyRacingThreadsTest(name, n_threads, n_loops, n_secs)"
* to create your test with the specified name and the specified
* number of WorkerThreads that execute the test code in parallel
* up to n_loops iterations or n_secs seconds.
* <li>
* Use
* "new DriverThread(test)"
* to create the test DriverThread that manages all the
* WorkerThreads. The DriverThread class can be extended to
* provide test specific code and/or variables. However, that
* is typically done in your test's subclass.
* <li>
* Use
* "new WorkerThread(workerNum, test)"
* to create WorkerThread-workerNum that executes the test code.
* The WorkerThread class can be extended to provide test thread
* specific code and/or variables.
* <li>
* Use
* "RacingThreadsTest.runTest(driver, workers)"
* to run the test. If the test fails, then a RuntimeException
* is thrown.
* </ul>
*
* The RacingThreadsTest class provides many methods that can be
* overridden in order to provide test specific semantics at each
* identified test execution point. At a minimum, your test's
* subclass needs to override the
* "void executeRace(WorkerThread)"
* method in order to exercise your race condition and it needs to
* override the
* "void checkRaceResults(DriverThread)"
* method in order to check the results of the race. Your
* checkRaceResults() method should call the
* "int incAndGetFailCnt()"
* method when it detects a failure. It can also call the
* "void unexpectedException(Thread, Exception)"
* method if it detects an unexpected exception; this will cause
* an error message to be output and the failure count to be
* incremented. When the RacingThreadsTest.runTest() method is
* done running the races, if there is a non-zero failure count,
* then a RuntimeException will be thrown.
* <p>
* The RacingThreadsTest class uses three internal barriers to
* coordinate actions between the DriverThread and the WorkerThreads.
* These barriers should not be managed or used by your test's
* subclass and are only mentioned here to provide clarity about
* interactions between the DriverThread and the WorkerThreads.
* The following transaction diagram shows when the different
* RacingThreadsTest methods are called relative to the different
* barriers:
*
* <pre>
* DriverThread WorkerThread-0 WorkerThread-N-1
* --------------------- --------------------- ---------------------
* run(workers)
* oneTimeDriverInit()
* &lt;start WorkerThreads&gt; run() run()
* &lt;top of race loop&gt; : :
* perRaceDriverInit() oneTimeWorkerInit() oneTimeWorkerInit()
* : &lt;top of race loop&gt; &lt;top of race loop&gt;
* : perRaceWorkerInit() perRaceWorkerInit()
* startBarrier startBarrier startBarrier
* : executeRace() executeRace()
* finishBarrier finishBarrier finishBarrier
* checkRaceResults() : :
* resetBarrier resetBarrier resetBarrier
* perRaceDriverEpilog() perRaceWorkerEpilog() perRaceWorkerEpilog()
* &lt;repeat race or done&gt; &lt;repeat race or done&gt; &lt;repeat race or done&gt;
* : oneTimeWorkerEpilog() oneTimeWorkerEpilog()
* &lt;join WorkerThreads&gt; &lt;WorkerThread ends&gt; &lt;WorkerThread ends&gt;
* oneTimeDriverEpilog()
* &lt;DriverThread ends&gt;
* </pre>
*
* Just to be clear about the parallel parts of this infrastructure:
* <ul>
* <li>
* After the DriverThread starts the WorkerThreads, the DriverThread
* and the WorkerThreads are running in parallel until the startBarrier
* is reached.
* <li>
* After the WorkerThreads leave the startBarrier, they are running
* the code in executeRace() in parallel which is the whole point
* of this class.
* <li>
* The DriverThread heads straight to the finishBarrier and waits for
* the WorkerThreads to get there.
* <li>
* After the DriverThread leaves the finishBarrier, it checks the
* results of the race.
* <li>
* The WorkerThreads head straight to the resetBarrier and wait for
* the DriverThread to get there.
* <li>
* If this is not the last race, then after the DriverThread and
* WorkerThreads leave the resetBarrier, the DriverThread and the
* WorkerThreads are running in parallel until the startBarrier
* is reached.
* <li>
* If this is the last race, then after the DriverThread and
* WorkerThreads leave the resetBarrier, the DriverThread and the
* WorkerThreads are running in parallel as each WorkerThread ends.
* <li>
* The DriverThread waits for the WorkerThreads to end and
* then it ends
* </ul>
*
* Once the DriverThread has ended, the RacingThreadsTest.runTest()
* method checks the failure count. If there were no failures, then
* a "Test PASSed" message is printed. Otherwise, the failure count
* is printed, a "Test FAILed" message is printed and a RuntimeException
* is thrown.
*/
public class RacingThreadsTest {
/**
* name of the test
*/
public final String TEST_NAME;
/**
* maximum number of test iterations (race loops)
*/
public final int N_LOOPS;
/**
* the maximum number of seconds to execute the test loop
*/
public final int N_SECS;
/**
* number of WorkerThreads
*/
public final int N_THREADS;
/**
* Creates a test with the specified name and the specified number
* of WorkerThreads that execute the test code in parallel up to
* n_loops iterations or n_secs seconds. The RacingThreadsTest
* class is extended in order to provide the test specific variables
* and/or code.
* @param name the name of the test
* @param n_threads the number of WorkerThreads
* @param n_loops the maximum number of test iterations
* @param n_secs the maximum number of seconds to execute the test loop
*/
RacingThreadsTest(String name, int n_threads, int n_loops, int n_secs) {
TEST_NAME = name;
N_THREADS = n_threads;
N_LOOPS = n_loops;
N_SECS = n_secs;
finishBarrier = new CyclicBarrier(N_THREADS + 1);
resetBarrier = new CyclicBarrier(N_THREADS + 1);
startBarrier = new CyclicBarrier(N_THREADS + 1);
}
/**
* Entry point for exercising the RacingThreadsTest class.
*/
public static void main(String[] args) {
// a dummy test:
// - 2 threads
// - 3 loops
// - 2 seconds
// - standard DriverThread
// - standard WorkerThread
RacingThreadsTest test = new RacingThreadsTest("dummy", 2, 3, 2);
DriverThread driver = new DriverThread(test);
WorkerThread[] workers = new WorkerThread[2];
for (int i = 0; i < workers.length; i++) {
workers[i] = new WorkerThread(i, test);
}
test.runTest(driver, workers);
}
private static volatile boolean done = false; // test done flag
// # of fails; AtomicInteger since any WorkerThread can increment
private static final AtomicInteger failCnt = new AtomicInteger();
// # of loops; volatile is OK since only DriverThread increments
// but using AtomicInteger for consistency
private static final AtomicInteger loopCnt = new AtomicInteger();
private static boolean verbose
= Boolean.getBoolean("RacingThreadsTest.verbose");
// barriers for starting, finishing and resetting the race
private final CyclicBarrier finishBarrier;
private final CyclicBarrier resetBarrier;
private final CyclicBarrier startBarrier;
/**
* Get current done flag value.
* @return the current done flag value
*/
public boolean getDone() {
return done;
}
/**
* Set done flag to specified value.
* @param v the new done flag value
*/
public void setDone(boolean v) {
done = v;
}
/**
* Get current failure counter value.
* @return the current failure count
*/
public int getFailCnt() {
return failCnt.get();
}
/**
* Increment and get current failure counter value.
* @return the current failure count after incrementing
*/
public int incAndGetFailCnt() {
return failCnt.incrementAndGet();
}
/**
* Get current loop counter value.
* @return the current loop count
*/
public int getLoopCnt() {
return loopCnt.get();
}
/**
* Increment and get current loop counter value.
* @return the current loop count after incrementing
*/
public int incAndGetLoopCnt() {
return loopCnt.incrementAndGet();
}
/**
* Get current verbose flag value.
* @return the current verbose flag value
*/
public boolean getVerbose() {
return verbose;
}
/**
* Set verbose flag to specified value.
* @param v the new verbose flag value
*/
public void setVerbose(boolean v) {
verbose = v;
}
/**
* Run the test with the specified DriverThread and the
* specified WorkerThreads.
* @param driver the DriverThread for running the test
* @param workers the WorkerThreads for executing the race
* @exception RuntimeException the test has failed
*/
public void runTest(DriverThread driver, WorkerThread[] workers) {
driver.run(workers);
try {
driver.join();
} catch (InterruptedException ie) {
unexpectedException(Thread.currentThread(), ie);
// fall through to test failed below
}
if (failCnt.get() == 0) {
System.out.println(TEST_NAME + ": Test PASSed.");
} else {
System.out.println(TEST_NAME + ": failCnt=" + failCnt.get());
System.out.println(TEST_NAME + ": Test FAILed.");
throw new RuntimeException("Test Failed");
}
}
/**
* Helper method for reporting an unexpected Exception and
* calling incAndGetFailCnt();
* @param t the Thread that caught the exception
* @param e the Exception that was caught
*/
public void unexpectedException(Thread t, Exception e) {
System.err.println(t.getName() + ": ERROR: unexpected exception: " + e);
incAndGetFailCnt(); // ignore return
}
// The following methods are typically overridden by the subclass
// of RacingThreadsTest to provide test specific semantics at each
// identified test execution point:
/**
* Initialize 1-time items for the DriverThread.
* Called by the DriverThread before WorkerThreads are started.
* @param dt the DriverThread
*/
public void oneTimeDriverInit(DriverThread dt) {
if (verbose)
System.out.println(dt.getName() + ": oneTimeDriverInit() called");
}
/**
* Initialize 1-time items for a WorkerThread. Called by a
* WorkerThread after oneTimeDriverInit() and before the
* WorkerThread checks in with startBarrier. May execute in
* parallel with perRaceDriverInit() or with another
* WorkerThread's oneTimeWorkerInit() call or another
* WorkerThread's perRaceWorkerInit() call.
* @param wt the WorkerThread
*/
public void oneTimeWorkerInit(WorkerThread wt) {
if (verbose)
System.out.println(wt.getName() + ": oneTimeWorkerInit() called");
}
/**
* Initialize per-race items for the DriverThread. Called by the
* DriverThread before it checks in with startBarrier. May execute
* in parallel with oneTimeWorkerInit() and perRaceWorkerInit()
* calls. After any race except for the last race, this method may
* execute in parallel with perRaceWorkerEpilog().
* @param dt the DriverThread
*/
public void perRaceDriverInit(DriverThread dt) {
if (verbose)
System.out.println(dt.getName() + ": perRaceDriverInit() called");
}
/**
* Initialize per-race items for a WorkerThread. Called by each
* WorkerThread before it checks in with startBarrier. On the first
* call, this method may execute in parallel with another
* WorkerThread's oneTimeWorkerInit() call. On any call, this method
* may execute in parallel with perRaceDriverInit() or another
* WorkerThread's perRaceWorkerInit() call. After any race except
* for the last race, this method may execute in parallel with
* perRaceDriverEpilog() or another WorkerThread's
* perRaceWorkerEpilog() call.
* @param wt the WorkerThread
*/
public void perRaceWorkerInit(WorkerThread wt) {
if (verbose)
System.out.println(wt.getName() + ": perRaceWorkerInit() called");
}
/**
* Execute the race in a WorkerThread. Called by each WorkerThread
* after it has been released from startBarrier.
* @param wt the WorkerThread
*/
public void executeRace(WorkerThread wt) {
if (verbose)
System.out.println(wt.getName() + ": executeRace() called");
}
/**
* Check race results in the DriverThread. Called by the DriverThread
* after it has been released from finishBarrier and before the
* DriverThread checks in with resetBarrier.
* @param dt the DriverThread
*/
public void checkRaceResults(DriverThread dt) {
if (verbose)
System.out.println(dt.getName() + ": checkRaceResults() called");
}
/**
* Handle end-of-race items for the DriverThread. Called by the
* DriverThread after it has been released from resetBarrier and
* before the DriverThread checks in again with startBarrier. Can
* execute in parallel with perRaceWorkerEpilog(). If this is not
* the last race, can execute in parallel with perRaceWorkerInit().
* If this is the last race, can execute in parallel with
* oneTimeWorkerEpilog().
* @param dt the DriverThread
*/
public void perRaceDriverEpilog(DriverThread dt) {
if (verbose)
System.out.println(dt.getName() + ": perRaceDriverEpilog() called");
}
/**
* Handle end-of-race items for a WorkerThread. Called by each
* WorkerThread after it has been released from resetBarrier and
* before the WorkerThread checks in again with startBarrier.
* Can execute in parallel with perRaceDriverEpilog() or another
* WorkerThread's perRaceWorkerEpilog() call. If this is not the
* last race, can execute in parallel with perRaceDriverInit(),
* or another WorkerThread's perRaceWorkerInit() call. If this
* is the last race, can execute in parallel with another
* WorkerThread's oneTimeWorkerEpilog() call.
* @param wt the WorkerThread
*/
public void perRaceWorkerEpilog(WorkerThread wt) {
if (verbose)
System.out.println(wt.getName() + ": perRaceWorkerEpilog() called");
}
/**
* Handle end-of-test items for a WorkerThread. Called by each
* WorkerThread after it has detected that all races are done and
* before oneTimeDriverEpilog() is called. Can execute in parallel
* with perRaceDriverEpilog(), with another WorkerThread's
* perRaceWorkerEpilog() call or with another WorkerThread's
* oneTimeWorkerEpilog() call.
* @param wt the WorkerThread
*/
public void oneTimeWorkerEpilog(WorkerThread wt) {
if (verbose)
System.out.println(wt.getName() + ": oneTimeWorkerEpilog() called");
}
/**
* Handle end-of-test items for the DriverThread. Called by the
* DriverThread after all the WorkerThreads have called
* oneTimeWorkerEpilog().
* @param dt the DriverThread
*/
public void oneTimeDriverEpilog(DriverThread dt) {
if (verbose)
System.out.println(dt.getName() + ": oneTimeDriverEpilog() called");
}
/**
* DriverThread for executing the test.
*/
public static class DriverThread extends Thread {
private final RacingThreadsTest test;
/**
* Create the test DriverThread that manages all the WorkerThreads.
* The DriverThread class can be extended to provide test specific
* variables and/or code. However, that is typically done in the
* subclass of RacingThreadsTest.
* @parameter test the RacingThreadsTest being run
*/
DriverThread(RacingThreadsTest test) {
super("DriverThread");
this.test = test;
}
private void run(WorkerThread[] workers) {
System.out.println(getName() + ": is starting.");
System.out.println(getName() + ": # WorkerThreads: " + test.N_THREADS);
System.out.println(getName() + ": max # loops: " + test.N_LOOPS);
System.out.println(getName() + ": max # secs: " + test.N_SECS);
// initialize 1-time items for the DriverThread
test.oneTimeDriverInit(this);
// start all the threads
for (int i = 0; i < workers.length; i++) {
workers[i].start();
}
// All WorkerThreads call oneTimeWorkerInit() and
// perRaceWorkerInit() on the way to startBarrier.
long endTime = System.currentTimeMillis() + test.N_SECS * 1000;
for (; !test.getDone() && test.getLoopCnt() < test.N_LOOPS;
test.incAndGetLoopCnt()) {
if (test.getVerbose() && (test.N_LOOPS < 10 ||
(test.getLoopCnt() % (test.N_LOOPS / 10)) == 0)) {
System.out.println(getName() + ": race loop #"
+ test.getLoopCnt());
}
// initialize per-race items for the DriverThread
test.perRaceDriverInit(this);
try {
// we've setup the race so start it when all
// WorkerThreads get to the startBarrier
test.startBarrier.await();
} catch (BrokenBarrierException bbe) {
test.unexpectedException(this, bbe);
return;
} catch (InterruptedException ie) {
test.unexpectedException(this, ie);
return;
}
// All WorkerThreads are racing via executeRace()
// at this point
// wait for all threads to finish the race
try {
test.finishBarrier.await();
} catch (BrokenBarrierException bbe) {
test.unexpectedException(this, bbe);
return;
} catch (InterruptedException ie) {
test.unexpectedException(this, ie);
return;
}
// All WorkerThreads are heading to resetBarrier at this
// point so we can check the race results before we reset
// for another race (or bail because we are done).
test.checkRaceResults(this);
if (test.getLoopCnt() + 1 >= test.N_LOOPS ||
System.currentTimeMillis() >= endTime) {
// This is the last loop or we're out of time.
// Let test threads know we are done before we release
// them from resetBarrier
test.setDone(true);
}
// release the WorkerThreads from resetBarrier
try {
test.resetBarrier.await();
} catch (BrokenBarrierException bbe) {
test.unexpectedException(this, bbe);
return;
} catch (InterruptedException ie) {
test.unexpectedException(this, ie);
return;
}
// All WorkerThreads call perRaceWorkerEpilog(). If
// this is not the last loop, then all WorkerThreads
// will also call perRaceWorkerInit() on the way to
// startBarrier. If this is the last loop, then all
// WorkerThreads will call oneTimeWorkerEpilog() on
// their way to ending.
// handle end-of-race items for the DriverThread
test.perRaceDriverEpilog(this);
}
System.out.println(getName() + ": completed " + test.getLoopCnt()
+ " race loops.");
if (test.getLoopCnt() < test.N_LOOPS) {
System.out.println(getName() + ": race stopped @ " + test.N_SECS
+ " seconds.");
}
for (int i = 0; i < workers.length; i++) {
try {
workers[i].join();
} catch (InterruptedException ie) {
test.unexpectedException(this, ie);
return;
}
}
// handle end-of-test items for the DriverThread
test.oneTimeDriverEpilog(this);
System.out.println(getName() + ": is done.");
}
}
/**
* WorkerThread for executing the race.
*/
public static class WorkerThread extends Thread {
private final RacingThreadsTest test;
private final int workerNum;
/**
* Creates WorkerThread-N that executes the test code. The
* WorkerThread class can be extended to provide test thread
* specific variables and/or code.
* @param workerNum the number for the new WorkerThread
* @parameter test the RacingThreadsTest being run
*/
WorkerThread(int workerNum, RacingThreadsTest test) {
super("WorkerThread-" + workerNum);
this.test = test;
this.workerNum = workerNum;
}
/**
* get the WorkerThread's number
* @return the WorkerThread's number
*/
public int getWorkerNum() {
return workerNum;
}
/**
* Run the race in a WorkerThread.
*/
public void run() {
System.out.println(getName() + ": is running.");
// initialize 1-time items for the WorkerThread
test.oneTimeWorkerInit(this);
while (!test.getDone()) {
// initialize per-race items for the WorkerThread
test.perRaceWorkerInit(this);
try {
test.startBarrier.await(); // wait for race to start
} catch (BrokenBarrierException bbe) {
test.unexpectedException(this, bbe);
return;
} catch (InterruptedException ie) {
test.unexpectedException(this, ie);
return;
}
// execute the race for the WorkerThread
test.executeRace(this);
try {
test.finishBarrier.await(); // this thread is done
} catch (BrokenBarrierException bbe) {
test.unexpectedException(this, bbe);
return;
} catch (InterruptedException ie) {
test.unexpectedException(this, ie);
return;
}
try {
test.resetBarrier.await(); // wait for race to reset
} catch (BrokenBarrierException bbe) {
test.unexpectedException(this, bbe);
return;
} catch (InterruptedException ie) {
test.unexpectedException(this, ie);
return;
}
// handle end-of-race items for the WorkerThread
test.perRaceWorkerEpilog(this);
}
// handle end-of-test items for the WorkerThread
test.oneTimeWorkerEpilog(this);
System.out.println(getName() + ": is ending.");
}
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2012, 2013, 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 8004931
* @summary Invoke getDeclaredMethods on LogManager to ensure that
* all types referenced in the method signatures is present.
*/
import java.util.logging.LogManager;
import java.lang.reflect.Method;
public class Reflect {
static void printMethods(Class<?> c) {
System.out.println(c);
for (Method m: c.getDeclaredMethods()) {
System.out.println(" " + m);
}
}
public static void main(String[] args) {
printMethods(java.util.logging.LogManager.class);
printMethods(java.util.logging.LogManager.getLogManager().getClass());
}
}

View file

@ -0,0 +1,143 @@
/*
* Copyright (c) 2013, 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.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
/**
* @test
* @bug 8030850
* @summary Tests that setting .level=FINEST for the root logger in logging
* configuration file does work.
* @modules java.base/jdk.internal.access
* java.logging
* @run main/othervm RootLevelInConfigFile
*
* @author danielfuchs
*/
public class RootLevelInConfigFile {
public static final String CONFIG_FILE_KEY = "java.util.logging.config.file";
public static void main(String[] args) throws IOException {
System.setProperty(CONFIG_FILE_KEY,
new File(System.getProperty("test.src", "."),
"rootlogger.properties").getAbsolutePath());
System.out.println(CONFIG_FILE_KEY + "="
+ System.getProperty(CONFIG_FILE_KEY));
if (! new File(System.getProperty(CONFIG_FILE_KEY)).canRead()) {
throw new RuntimeException("can't read config file: "
+ System.getProperty(CONFIG_FILE_KEY));
}
final String configFile = System.getProperty(CONFIG_FILE_KEY);
test("no security");
}
public static void test(String conf) throws IOException {
System.out.println("Testing with " + conf);
testLoggableLevels();
LogManager.getLogManager().readConfiguration();
testLoggableLevels();
}
private static void testLoggableLevels() {
Logger foobar = Logger.getLogger("foo.bar");
if (!foobar.isLoggable(Level.FINEST)) {
throw new RuntimeException("Expected FINEST to be loggable in "
+ foobar.getName());
}
if (!foobar.getParent().isLoggable(Level.FINEST)) {
throw new RuntimeException("Expected FINEST to be loggable in "
+ foobar.getName());
}
Logger global = Logger.getGlobal();
if (!global.isLoggable(Level.FINEST)) {
throw new RuntimeException("Expected FINEST to be loggable in "
+ global.getName());
}
if (!global.getParent().isLoggable(Level.FINEST)) {
throw new RuntimeException("Expected FINEST to be loggable in "
+ global.getName());
}
Logger root = Logger.getLogger("");
if (!global.isLoggable(Level.FINEST)) {
throw new RuntimeException("Expected FINEST to be loggable in "
+ root.getName());
}
if (!global.getParent().isLoggable(Level.FINEST)) {
throw new RuntimeException("Expected FINEST to be loggable in "
+ root.getName());
}
root.setLevel(Level.FINER);
if (foobar.isLoggable(Level.FINEST)) {
throw new RuntimeException("Didn't expect FINEST to be loggable in "
+ foobar.getName());
}
if (foobar.getParent().isLoggable(Level.FINEST)) {
throw new RuntimeException("Didn't expect FINEST to be loggable in "
+ foobar.getName());
}
if (global.isLoggable(Level.FINEST)) {
throw new RuntimeException("Didn't expect FINEST to be loggable in "
+ global.getName());
}
if (global.getParent().isLoggable(Level.FINEST)) {
throw new RuntimeException("Didn't expect FINEST to be loggable in "
+ global.getName());
}
if (!foobar.isLoggable(Level.FINER)) {
throw new RuntimeException("Expected FINER to be loggable in "
+ foobar.getName());
}
if (!foobar.getParent().isLoggable(Level.FINER)) {
throw new RuntimeException("Expected FINER to be loggable in "
+ foobar.getName());
}
if (!global.isLoggable(Level.FINER)) {
throw new RuntimeException("Expected FINER to be loggable in "
+ global.getName());
}
if (!global.getParent().isLoggable(Level.FINER)) {
throw new RuntimeException("Expected FINER to be loggable in "
+ global.getName());
}
}
}

View file

@ -0,0 +1 @@
.level=FINEST

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