1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import java.io.ByteArrayOutputStream; 25 import java.io.IOException; 26 import java.io.OutputStream; 27 import java.io.PrintStream; 28 import java.lang.System.Logger; 29 import java.lang.System.Logger.Level; 30 import java.lang.System.LoggerFinder; 31 import java.lang.reflect.Module; 32 import java.util.Enumeration; 33 import java.util.Locale; 34 import java.util.Objects; 35 import java.util.ResourceBundle; 36 import java.util.concurrent.ConcurrentHashMap; 37 // Can't use testng because testng requires java.logging 38 //import org.testng.annotations.Test; 39 40 /** 41 * @test 42 * @bug 8177835 43 * @summary Checks that the DefaultLoggerFinder and LoggingProviderImpl 44 * implementations of the System.LoggerFinder conform to the 45 * LoggerFinder specification, in particular with respect to 46 * throwing NullPointerException. The test uses --limit-module 47 * to force the selection of one or the other. 48 * @author danielfuchs 49 * @build LoggerFinderAPI 50 * @run main/othervm --limit-modules java.base,java.logging 51 * -Djava.util.logging.SimpleFormatter.format=LOG-%4$s:-[%2$s]-%5$s%6$s%n 52 * LoggerFinderAPI 53 * @run main/othervm -Djdk.system.logger.format=LOG-%4$s:-[%2$s]-%5$s%6$s%n 54 * --limit-modules java.base 55 * LoggerFinderAPI 56 */ 57 public class LoggerFinderAPI { 58 59 // Simplified log format string. No white space for main/othervm line 60 static final String TEST_FORMAT = "LOG-%4$s:-[%2$s]-%5$s%6$s%n"; 61 static final String JDK_FORMAT_PROP_KEY = "jdk.system.logger.format"; 62 static final String JUL_FORMAT_PROP_KEY = 63 "java.util.logging.SimpleFormatter.format"; 64 65 static class RecordStream extends OutputStream { 66 static final Object LOCK = new Object[0]; 67 final PrintStream out; 68 final PrintStream err; 69 final ByteArrayOutputStream bos = new ByteArrayOutputStream(); 70 boolean record; 71 RecordStream(PrintStream out, PrintStream err) { 72 this.out = out; 73 this.err = err; 74 } 75 76 @Override 77 public void write(int i) throws IOException { 78 if (record) { 79 bos.write(i); 80 out.write(i); 81 } else { 82 err.write(i); 83 } 84 } 85 86 void startRecording() { 87 out.flush(); 88 err.flush(); 89 bos.reset(); 90 record = true; 91 } 92 byte[] stopRecording() { 93 out.flush(); 94 err.flush(); 95 record = false; 96 return bos.toByteArray(); 97 } 98 } 99 100 static final PrintStream ERR = System.err; 101 static final PrintStream OUT = System.out; 102 static final RecordStream LOG_STREAM = new RecordStream(OUT, ERR); 103 static { 104 Locale.setDefault(Locale.US); 105 PrintStream perr = new PrintStream(LOG_STREAM); 106 System.setErr(perr); 107 } 108 109 public static class MyResourceBundle extends ResourceBundle { 110 final ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>(); 111 @Override 112 protected Object handleGetObject(String string) { 113 return map.computeIfAbsent(string, s -> "[localized] " + s); 114 } 115 116 @Override 117 public Enumeration<String> getKeys() { 118 return map.keys(); 119 } 120 121 } 122 123 public static void main(String[] args) { 124 // Set on the command line, to ensure that the test will fail if 125 // the 'wrong' provider gets selected. 126 // System.setProperty(JDK_FORMAT_PROP_KEY, TEST_FORMAT); 127 // System.setProperty(JUL_FORMAT_PROP_KEY, TEST_FORMAT); 128 LoggerFinder finder = System.LoggerFinder.getLoggerFinder(); 129 System.out.println("LoggerFinder is " + finder.getClass().getName()); 130 131 LoggerFinderAPI apiTest = new LoggerFinderAPI(); 132 for (Object[] params : getLoggerDataProvider()) { 133 apiTest.testGetLogger((String)params[0], 134 (String)params[1], 135 (Module)params[2], 136 Throwable.class.getClass().cast(params[3])); 137 } 138 for (Object[] params : getLocalizedLoggerDataProvider()) { 139 apiTest.testGetLocalizedLogger((String)params[0], 140 (String)params[1], 141 (ResourceBundle)params[2], 142 (Module)params[3], 143 Throwable.class.getClass().cast(params[4])); 144 } 145 } 146 147 //Can't use testng because testng requires java.logging 148 //@Test(dataProvider = "testGetLoggerDataProvider") 149 void testGetLogger(String desc, String name, Module mod, Class<? extends Throwable> thrown) { 150 try { 151 LoggerFinder finder = System.LoggerFinder.getLoggerFinder(); 152 Logger logger = finder.getLogger(name, mod); 153 if (thrown != null) { 154 throw new AssertionError("Exception " + thrown.getName() 155 + " not thrown for " 156 + "LoggerFinder.getLogger" 157 + " with " + desc); 158 } 159 // Make sure we don't fail if tests are run in parallel 160 synchronized(RecordStream.LOCK) { 161 LOG_STREAM.startRecording(); 162 try { 163 logger.log(Level.INFO, "{0} with {1}: PASSED", 164 "LoggerFinder.getLogger", 165 desc); 166 } finally { 167 byte[] logged = LOG_STREAM.stopRecording(); 168 check(logged, "testGetLogger", desc, null, 169 "LoggerFinder.getLogger"); 170 } 171 } 172 } catch (Throwable x) { 173 if (thrown != null && thrown.isInstance(x)) { 174 System.out.printf("Got expected exception for %s with %s: %s\n", 175 "LoggerFinder.getLogger", desc, String.valueOf(x)); 176 } else throw x; 177 } 178 } 179 180 //Can't use testng because testng requires java.logging 181 //@Test(dataProvider = "getLocalizedLoggerDataProvider") 182 void testGetLocalizedLogger(String desc, String name, ResourceBundle bundle, 183 Module mod, Class<? extends Throwable> thrown) { 184 try { 185 LoggerFinder finder = System.LoggerFinder.getLoggerFinder(); 186 Logger logger = finder.getLocalizedLogger(name, bundle, mod); 187 if (thrown != null) { 188 throw new AssertionError("Exception " + thrown.getName() 189 + " not thrown for " 190 + "LoggerFinder.getLocalizedLogger" 191 + " with " + desc); 192 } 193 // Make sure we don't fail if tests are run in parallel 194 synchronized(RecordStream.LOCK) { 195 LOG_STREAM.startRecording(); 196 try { 197 logger.log(Level.INFO, "{0} with {1}: PASSED", 198 "LoggerFinder.getLocalizedLogger", 199 desc); 200 } finally { 201 byte[] logged = LOG_STREAM.stopRecording(); 202 check(logged, "testGetLocalizedLogger", desc, bundle, 203 "LoggerFinder.getLocalizedLogger"); 204 } 205 } 206 } catch (Throwable x) { 207 if (thrown != null && thrown.isInstance(x)) { 208 System.out.printf("Got expected exception for %s with %s: %s\n", 209 "LoggerFinder.getLocalizedLogger", desc, String.valueOf(x)); 210 } else throw x; 211 } 212 } 213 214 private void check(byte[] logged, String test, String desc, 215 ResourceBundle bundle, String meth) { 216 String msg = new String(logged); 217 String expected = String.format(TEST_FORMAT, null, 218 "LoggerFinderAPI " + test, null, Level.INFO.name(), 219 (bundle==null?"":"[localized] ") + meth + " with " + desc + ": PASSED", 220 ""); 221 if (!Objects.equals(msg, expected)) { 222 throw new AssertionError("Expected log message not found: " 223 + "\n\texpected: " + expected 224 + "\n\tretrieved: " + msg); 225 } 226 } 227 228 229 static final Module MODULE = LoggerFinderAPI.class.getModule(); 230 static final ResourceBundle BUNDLE = new MyResourceBundle(); 231 static final Object[][] GET_LOGGER = { 232 {"null name", null, MODULE , NullPointerException.class}, 233 {"null module", "foo", null, NullPointerException.class}, 234 {"null name and module", null, null, NullPointerException.class}, 235 {"non null name and module", "foo", MODULE, null}, 236 }; 237 static final Object[][] GET_LOCALIZED_LOGGER = { 238 {"null name", null, BUNDLE, MODULE , NullPointerException.class}, 239 {"null module", "foo", BUNDLE, null, NullPointerException.class}, 240 {"null name and module", null, BUNDLE, null, NullPointerException.class}, 241 {"non null name and module", "foo", BUNDLE, MODULE, null}, 242 {"null name and bundle", null, null, MODULE , NullPointerException.class}, 243 {"null module and bundle", "foo", null, null, NullPointerException.class}, 244 {"null name and module and bundle", null, null, null, NullPointerException.class}, 245 {"non null name and module, null bundle", "foo", null, MODULE, null}, 246 }; 247 public static Object[][] getLoggerDataProvider() { 248 return GET_LOGGER; 249 } 250 public static Object[][] getLocalizedLoggerDataProvider() { 251 return GET_LOCALIZED_LOGGER; 252 } 253 }