undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
283
test/jdk/java/util/logging/CheckLockLocationTest.java
Normal file
283
test/jdk/java/util/logging/CheckLockLocationTest.java
Normal 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 );
|
||||
}
|
||||
}
|
||||
374
test/jdk/java/util/logging/CheckZombieLockTest.java
Normal file
374
test/jdk/java/util/logging/CheckZombieLockTest.java
Normal 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 );
|
||||
}
|
||||
}
|
||||
188
test/jdk/java/util/logging/ClassLoaderLeakTest.java
Normal file
188
test/jdk/java/util/logging/ClassLoaderLeakTest.java
Normal 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();
|
||||
}
|
||||
}
|
||||
207
test/jdk/java/util/logging/CustomLogManager.java
Normal file
207
test/jdk/java/util/logging/CustomLogManager.java
Normal 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 {
|
||||
}
|
||||
}
|
||||
65
test/jdk/java/util/logging/CustomLogManagerTest.java
Normal file
65
test/jdk/java/util/logging/CustomLogManagerTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
107
test/jdk/java/util/logging/DefaultConfigTest.java
Normal file
107
test/jdk/java/util/logging/DefaultConfigTest.java
Normal 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 ",
|
||||
".")));
|
||||
}
|
||||
}
|
||||
217
test/jdk/java/util/logging/DrainFindDeadlockTest.java
Normal file
217
test/jdk/java/util/logging/DrainFindDeadlockTest.java
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
55
test/jdk/java/util/logging/FileHandlerAccessTest.java
Normal file
55
test/jdk/java/util/logging/FileHandlerAccessTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
351
test/jdk/java/util/logging/FileHandlerLongLimit.java
Normal file
351
test/jdk/java/util/logging/FileHandlerLongLimit.java
Normal 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);
|
||||
}
|
||||
}
|
||||
115
test/jdk/java/util/logging/FileHandlerMaxLocksTest.java
Normal file
115
test/jdk/java/util/logging/FileHandlerMaxLocksTest.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
168
test/jdk/java/util/logging/FileHandlerPath.java
Normal file
168
test/jdk/java/util/logging/FileHandlerPath.java
Normal 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);
|
||||
}
|
||||
}
|
||||
194
test/jdk/java/util/logging/FileHandlerPatternExceptions.java
Normal file
194
test/jdk/java/util/logging/FileHandlerPatternExceptions.java
Normal 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);
|
||||
}
|
||||
}
|
||||
232
test/jdk/java/util/logging/FileHandlerPatternGeneration.java
Normal file
232
test/jdk/java/util/logging/FileHandlerPatternGeneration.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
59
test/jdk/java/util/logging/GetGlobalTest.java
Normal file
59
test/jdk/java/util/logging/GetGlobalTest.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
105
test/jdk/java/util/logging/Handler/StreamHandlerLockingTest.java
Normal file
105
test/jdk/java/util/logging/Handler/StreamHandlerLockingTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
# mandatory Default properties...
|
||||
|
||||
.level= INFO
|
||||
handlers=HandlersConfigTest$ConfiguredHandler
|
||||
|
||||
java.util.logging.MemoryHandler.target=HandlersConfigTest$ConfiguredHandler
|
||||
323
test/jdk/java/util/logging/HandlersConfigTest.java
Normal file
323
test/jdk/java/util/logging/HandlersConfigTest.java
Normal 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 { }
|
||||
}
|
||||
}
|
||||
|
|
@ -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.");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
96
test/jdk/java/util/logging/IsLoggableHandlerTest.java
Normal file
96
test/jdk/java/util/logging/IsLoggableHandlerTest.java
Normal 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 { }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
305
test/jdk/java/util/logging/Level/CustomLevel.java
Normal file
305
test/jdk/java/util/logging/Level/CustomLevel.java
Normal 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();
|
||||
}
|
||||
}
|
||||
5
test/jdk/java/util/logging/Level/myresource.properties
Normal file
5
test/jdk/java/util/logging/Level/myresource.properties
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
EMERGENCY=localized.emergency
|
||||
ALERT=localized.alert
|
||||
CRITICAL=localized.critical
|
||||
WARNING=localized.warning
|
||||
INFO=localized.info
|
||||
5
test/jdk/java/util/logging/Level/myresource2.properties
Normal file
5
test/jdk/java/util/logging/Level/myresource2.properties
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
OTHEREMERGENCY=localized.otheremergency
|
||||
OTHERALERT=localized.otheralert
|
||||
OTHERCRITICAL=localized.othercritical
|
||||
SEVERE=localized.severe
|
||||
INFO=localized.info.2
|
||||
108
test/jdk/java/util/logging/LocalizedLevelName.java
Normal file
108
test/jdk/java/util/logging/LocalizedLevelName.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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 + ')';
|
||||
}
|
||||
}
|
||||
|
|
@ -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 + ')';
|
||||
}
|
||||
}
|
||||
|
|
@ -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 + ')';
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
82
test/jdk/java/util/logging/LogManager/LinkageErrorTest.java
Normal file
82
test/jdk/java/util/logging/LogManager/LinkageErrorTest.java
Normal 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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
272
test/jdk/java/util/logging/LogManager/TestLoggerNames.java
Normal file
272
test/jdk/java/util/logging/LogManager/TestLoggerNames.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
76
test/jdk/java/util/logging/LogManagerInstanceTest.java
Normal file
76
test/jdk/java/util/logging/LogManagerInstanceTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
86
test/jdk/java/util/logging/LogRecordThreadIdTest.java
Normal file
86
test/jdk/java/util/logging/LogRecordThreadIdTest.java
Normal 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);
|
||||
|
||||
}
|
||||
}
|
||||
167
test/jdk/java/util/logging/Logger/bundleLeak/BundleTest.java
Normal file
167
test/jdk/java/util/logging/Logger/bundleLeak/BundleTest.java
Normal 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());
|
||||
}
|
||||
}
|
||||
44
test/jdk/java/util/logging/Logger/bundleLeak/LoggingApp.java
Normal file
44
test/jdk/java/util/logging/Logger/bundleLeak/LoggingApp.java
Normal 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;
|
||||
}
|
||||
}
|
||||
55
test/jdk/java/util/logging/Logger/bundleLeak/MyBundle.java
Normal file
55
test/jdk/java/util/logging/Logger/bundleLeak/MyBundle.java
Normal 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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
.level=INFO
|
||||
handlers=testgetglobal.HandlerImpl
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
388
test/jdk/java/util/logging/Logger/getLogger/TestInferCaller.java
Normal file
388
test/jdk/java/util/logging/Logger/getLogger/TestInferCaller.java
Normal 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: ");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
92
test/jdk/java/util/logging/Logger/getLogger/TestLogger.java
Normal file
92
test/jdk/java/util/logging/Logger/getLogger/TestLogger.java
Normal 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));
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
512
test/jdk/java/util/logging/Logger/isLoggable/TestIsLoggable.java
Normal file
512
test/jdk/java/util/logging/Logger/isLoggable/TestIsLoggable.java
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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" },
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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" },
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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" },
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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" },
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
203
test/jdk/java/util/logging/LoggerResourceBundleRace.java
Normal file
203
test/jdk/java/util/logging/LoggerResourceBundleRace.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
100
test/jdk/java/util/logging/LoggerSubclass.java
Normal file
100
test/jdk/java/util/logging/LoggerSubclass.java
Normal 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");}
|
||||
}
|
||||
264
test/jdk/java/util/logging/LoggerSupplierAPIsTest.java
Normal file
264
test/jdk/java/util/logging/LoggerSupplierAPIsTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
101
test/jdk/java/util/logging/LoggingDeadlock.java
Normal file
101
test/jdk/java/util/logging/LoggingDeadlock.java
Normal 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");
|
||||
}
|
||||
}
|
||||
335
test/jdk/java/util/logging/LoggingDeadlock2.java
Normal file
335
test/jdk/java/util/logging/LoggingDeadlock2.java
Normal 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");}
|
||||
}
|
||||
81
test/jdk/java/util/logging/LoggingDeadlock3.java
Normal file
81
test/jdk/java/util/logging/LoggingDeadlock3.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
test/jdk/java/util/logging/LoggingDeadlock3.props
Normal file
3
test/jdk/java/util/logging/LoggingDeadlock3.props
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
com.sun.LEVEL=FINE
|
||||
com.sun.level=FINE
|
||||
com.sun.Hello.level=INFO
|
||||
107
test/jdk/java/util/logging/LoggingDeadlock4.java
Normal file
107
test/jdk/java/util/logging/LoggingDeadlock4.java
Normal 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.");
|
||||
}
|
||||
}
|
||||
215
test/jdk/java/util/logging/LoggingDeadlock5.java
Normal file
215
test/jdk/java/util/logging/LoggingDeadlock5.java
Normal 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;
|
||||
}
|
||||
}
|
||||
253
test/jdk/java/util/logging/LoggingMXBeanTest.java
Normal file
253
test/jdk/java/util/logging/LoggingMXBeanTest.java
Normal 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();
|
||||
}
|
||||
}
|
||||
191
test/jdk/java/util/logging/LoggingMXBeanTest2.java
Normal file
191
test/jdk/java/util/logging/LoggingMXBeanTest2.java
Normal 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();
|
||||
}
|
||||
}
|
||||
44
test/jdk/java/util/logging/LoggingNIOChange.java
Normal file
44
test/jdk/java/util/logging/LoggingNIOChange.java
Normal 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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
156
test/jdk/java/util/logging/MemoryHandlerTest.java
Normal file
156
test/jdk/java/util/logging/MemoryHandlerTest.java
Normal 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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
9
test/jdk/java/util/logging/MemoryHandlerTest.props
Normal file
9
test/jdk/java/util/logging/MemoryHandlerTest.props
Normal 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
|
||||
182
test/jdk/java/util/logging/ParentLoggersTest.java
Normal file
182
test/jdk/java/util/logging/ParentLoggersTest.java
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
62
test/jdk/java/util/logging/ParentLoggersTest.props
Normal file
62
test/jdk/java/util/logging/ParentLoggersTest.props
Normal 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
|
||||
687
test/jdk/java/util/logging/RacingThreadsTest.java
Normal file
687
test/jdk/java/util/logging/RacingThreadsTest.java
Normal 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()
|
||||
* <start WorkerThreads> run() run()
|
||||
* <top of race loop> : :
|
||||
* perRaceDriverInit() oneTimeWorkerInit() oneTimeWorkerInit()
|
||||
* : <top of race loop> <top of race loop>
|
||||
* : perRaceWorkerInit() perRaceWorkerInit()
|
||||
* startBarrier startBarrier startBarrier
|
||||
* : executeRace() executeRace()
|
||||
* finishBarrier finishBarrier finishBarrier
|
||||
* checkRaceResults() : :
|
||||
* resetBarrier resetBarrier resetBarrier
|
||||
* perRaceDriverEpilog() perRaceWorkerEpilog() perRaceWorkerEpilog()
|
||||
* <repeat race or done> <repeat race or done> <repeat race or done>
|
||||
* : oneTimeWorkerEpilog() oneTimeWorkerEpilog()
|
||||
* <join WorkerThreads> <WorkerThread ends> <WorkerThread ends>
|
||||
* oneTimeDriverEpilog()
|
||||
* <DriverThread ends>
|
||||
* </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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
44
test/jdk/java/util/logging/Reflect.java
Normal file
44
test/jdk/java/util/logging/Reflect.java
Normal 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());
|
||||
}
|
||||
}
|
||||
143
test/jdk/java/util/logging/RootLogger/RootLevelInConfigFile.java
Normal file
143
test/jdk/java/util/logging/RootLogger/RootLevelInConfigFile.java
Normal 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());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
.level=FINEST
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue