/* * 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.
*/
/** * @test * @bug 8029281 8028763 * @summary Attempts to detect synchronization issues with getResourceBundle() * and getResourceBundleName(). It might also detect issues in the way * that the logger tree is cleaned up after a logger has been garbage * collected. This test helped find the root cause of 8029092, so if * this test fails one might also expect failures in * java/util/logging/Logger/logrb/TestLogrbResourceBundle.java and * java/util/logging/Logger/setResourceBundle/TestSetResourceBundle.java. * Note that this is a best effort test. Running it in a loop to * reproduce intermittent issues can be a good idea. * @modules java.logging * java.management * @run main/othervm -Djava.security.manager=allow TestLoggerBundleSync * @author danielfuchs
*/ publicclass TestLoggerBundleSync {
/** * This test will run both with and without a security manager. * * The test starts a number of threads that will attempt to concurrently * set resource bundles on Logger, and verifies the consistency of the * obtained results. * * This is a best effort test. * * @param args the command line arguments
*/ publicstaticvoid main(String[] args) throws Exception {
try { // test without security
System.out.println("No security");
test();
/** * Starts all threads, wait 15secs, then stops all threads. * @throws Exception if a deadlock was detected or an error occurred.
*/ publicstaticvoid test() throws Exception {
goOn = true;
thrown = null; long sGetRBCount = getRBcount.get(); long sSetRBCount = setRBcount.get(); long sSetRBNameCount = setRBNameCount.get(); long sCheckCount = checkCount.get(); long sNextLong = nextLong.get(); long sIgnoreLogCount = ignoreLogCount.get();
List<Thread> threads = new ArrayList<>(); for (Class<? extends ResourceBundle> type : classes) {
threads.add(new SetRB(type));
threads.add(new SetRBName(type));
} for (int i =0 ; i < READERS ; i++) {
threads.add(new GetRB());
}
threads.add(new DeadlockDetector());
threads.add(0, new Stopper(TIME)); for (Thread t : threads) {
t.start();
} for (Thread t : threads) { try {
t.join();
} catch (Exception x) {
fail(x);
}
} if (thrown != null) { throw thrown;
}
System.out.println("Passed: " + (nextLong.longValue() - sNextLong)
+ " unique loggers created");
System.out.println("\t " +(getRBcount.get() - sGetRBCount)
+ " loggers tested by " + READERS + " Thread(s),");
System.out.println("\t " + (setRBcount.get() - sSetRBCount)
+ " resource bundles set by " + classes.size() + " Thread(s),");
System.out.println("\t " + (setRBNameCount.get() - sSetRBNameCount)
+ " resource bundle names set by " + classes.size() + " Thread(s),");
System.out.println("\t " + (ignoreLogCount.get() - sIgnoreLogCount)
+ " log messages emitted by other GetRB threads were ignored"
+ " to ensure MT test consistency,");
System.out.println("\t ThreadMXBean.findDeadlockedThreads called "
+ (checkCount.get() -sCheckCount) + " times by 1 Thread.");
}
staticfinalclass GetRB extendsThread { finalclass MyHandler extends Handler { volatile ResourceBundle rb; volatile String rbName; volatileint count = 0;
@Override publicsynchronizedvoid publish(LogRecord record) {
Object[] params = record.getParameters(); // Each GetRB thread has its own handler, but since they // log into the same logger, each handler may receive // messages emitted by other threads. // This means that GetRB#2.handler may receive a message // emitted by GetRB#1 at a time where the resource bundle // was still null. // To avoid falling into this trap, the GetRB thread passes // 'this' as argument to the messages it logs - which does // allow us here to ignore messages that where not emitted // by our own GetRB.this thread... if (params.length == 1) { if (params[0] == GetRB.this) { // The message was emitted by our thread.
count++;
rb = record.getResourceBundle();
rbName = record.getResourceBundleName();
} else { // The message was emitted by another thread: just // ignore it, as it may have been emitted at a time // where the resource bundle was still null, and // processing it may overwrite the 'rb' and 'rbName' // recorded from the message emitted by our own thread. if (VERBOSE) {
System.out.println("Ignoring message logged by " + params[0]);
}
ignoreLogCount.incrementAndGet();
}
} else {
ignoreLogCount.incrementAndGet();
System.err.println("Unexpected message received");
}
}
void reset() {
rbName = null;
rb = null;
}
@Override publicvoid flush() {
}
@Override publicvoid close() throws SecurityException {
}
}; final MyHandler handler = new MyHandler();
@Override publicvoid run() { try {
handler.setLevel(Level.FINEST); while (goOn) {
Logger l;
Logger foo = Logger.getLogger("foo");
Logger bar = Logger.getLogger("foo.bar"); for (long i=0; i < nextLong.longValue() + 100 ; i++) { if (!goOn) break;
l = Logger.getLogger("foo.bar.l"+i); final ResourceBundle b = l.getResourceBundle(); final String name = l.getResourceBundleName(); if (b != null) { if (!name.equals(b.getBaseBundleName())) { thrownew RuntimeException("Unexpected bundle name: "
+b.getBaseBundleName());
}
}
Logger ll = Logger.getLogger(l.getName()+".bie.bye");
ResourceBundle hrb;
String hrbName; if (handler.getLevel() != Level.FINEST) { thrownew RuntimeException("Handler level is not finest: "
+ handler.getLevel());
} finalint countBefore = handler.count;
handler.reset();
ll.setLevel(Level.FINEST);
ll.addHandler(handler);
ll.log(Level.FINE, "dummy {0}", this);
ll.removeHandler(handler); finalint countAfter = handler.count; if (countBefore == countAfter) { thrownew RuntimeException("Handler not called for "
+ ll.getName() + "("+ countAfter +")");
}
hrb = handler.rb;
hrbName = handler.rbName; if (name != null) { // if name is not null, then it implies that it // won't change, since setResourceBundle() cannot // replace a non null name. // Since we never set the resource bundle on 'll', // then ll must inherit its resource bundle [name] // from l - and therefor we should find it in // handler.rb/handler.rbName if (!name.equals(hrbName)) { thrownew RuntimeException("Unexpected bundle name: "
+hrbName);
} // here we know that hrbName is not null so hrb // should not be null either. if (!name.equals(hrb.getBaseBundleName())) { thrownew RuntimeException("Unexpected bundle name: "
+hrb.getBaseBundleName());
}
}
// Make sure to refer to 'l' explicitly in order to // prevent eager garbage collecting before the end of // the test (JDK-8030192) if (!ll.getName().startsWith(l.getName())) { thrownew RuntimeException("Logger " + ll.getName()
+ "does not start with expected prefix "
+ l.getName());
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.