1 /* 2 * Copyright (c) 2013, 2016, 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.*; 25 import java.lang.management.ManagementFactory; 26 import java.lang.management.PlatformLoggingMXBean; 27 import java.lang.ref.Reference; 28 import java.lang.ref.ReferenceQueue; 29 import java.lang.ref.WeakReference; 30 import java.lang.reflect.InvocationTargetException; 31 import java.lang.reflect.Method; 32 import java.net.MalformedURLException; 33 import java.net.URL; 34 import java.net.URLClassLoader; 35 import java.util.*; 36 import java.util.logging.*; 37 38 /* 39 * @test 40 * @bug 8026027 6543126 8187073 41 * @modules java.management 42 * @summary Test Level.parse to look up custom levels by name and its 43 * localized name, as well as severity. 44 * 45 * @run main/othervm CustomLevel 46 */ 47 48 public class CustomLevel extends Level { 49 public CustomLevel(String name, int value, String resourceBundleName) { 50 super(name, value, resourceBundleName); 51 } 52 53 private static final List<Level> levels = new ArrayList<>(); 54 private static final String RB_NAME = "myresource"; 55 private static final String OTHERRB_NAME = "myresource2"; 56 57 private static class CustomLevelReference extends WeakReference<Level> { 58 final String name; 59 final int value; 60 final String resourceBundleName; 61 public CustomLevelReference(Level level, ReferenceQueue<Level> queue) { 62 super(level, queue); 63 name = level.getName(); 64 value = level.intValue(); 65 resourceBundleName = level.getResourceBundleName(); 66 } 67 68 @Override 69 public String toString() { 70 return "CustomLevelReference(\"" + name + "\", " + value + ", \"" 71 + resourceBundleName + "\")"; 72 } 73 74 } 75 76 public static void main(String[] args) throws Exception { 77 setupCustomLevels(); 78 setUpCustomLevelsOtherLoader(); 79 PlatformLoggingMXBean mxbean = ManagementFactory.getPlatformMXBean(PlatformLoggingMXBean.class); 80 Logger logger = Logger.getLogger("foo.bar"); 81 82 // Level.parse will return the custom Level instance 83 for (Level level : levels) { 84 final ResourceBundle rb = getResourceBundle(level); 85 String name = level.getName(); 86 Level l = Level.parse(name); 87 if (!name.equals("WARNING") && !name.equals("INFO") 88 && !name.equals("SEVERE")) { 89 // custom level whose name doesn't conflict with any standard one 90 checkCustomLevel(l, level); 91 } else if (l != Level.WARNING && l != Level.INFO && l != Level.SEVERE 92 || !name.equals(l.getName())) { 93 throw new RuntimeException("Unexpected level " + formatLevel(l)); 94 } 95 System.out.println("Level.parse found expected level: " 96 + formatLevel(l)); 97 String localizedName = rb.getString(level.getName()); 98 l = Level.parse(localizedName); 99 if (l != level) { 100 throw new RuntimeException("Unexpected level " + l + " " 101 + l.getClass() + " for " + localizedName 102 + " in " + rb.getBaseBundleName()); 103 } 104 l = Level.parse(String.valueOf(level.intValue())); 105 System.out.println("Level.parse(" + level.intValue() + ") returns " + l); 106 if (l != level) { 107 if (l == null || l.intValue() != level.intValue()) { 108 throw new RuntimeException("Unexpected level " + l 109 + (l == null ? "" : (" " + l.getClass())) 110 + " for " + level.intValue()); 111 } 112 } 113 mxbean.setLoggerLevel(logger.getName(), String.valueOf(level.intValue())); 114 Level l2 = logger.getLevel(); 115 if (l2 != level) { 116 if (l2 == null || l2.intValue() != level.intValue()) { 117 throw new RuntimeException("Unexpected level " + l2 118 + (l2 == null ? "" : (" " + l2.getClass())) 119 + " for " + level.intValue()); 120 } 121 } 122 } 123 124 125 final long otherLevelCount = levels.stream() 126 .filter(CustomLevel::isCustomLoader) 127 .count(); 128 129 // Now verify that custom level instances are correctly 130 // garbage collected when no longer referenced 131 ReferenceQueue<Level> queue = new ReferenceQueue<>(); 132 List<CustomLevelReference> refs = new ArrayList<>(); 133 List<CustomLevelReference> customRefs = new ArrayList<>(); 134 int otherLevels = 0; 135 while (!levels.isEmpty()) { 136 Level l = levels.stream().findAny().get(); 137 boolean isCustomLoader = isCustomLoader(l); 138 if (isCustomLoader) otherLevels++; 139 140 CustomLevelReference ref = new CustomLevelReference(l, queue); 141 if (isCustomLoader) { 142 customRefs.add(ref); 143 } else { 144 refs.add(ref); 145 } 146 147 // remove strong references to l 148 levels.remove(l); 149 l = null; 150 151 // Run gc and wait for garbage collection 152 if (otherLevels == otherLevelCount) { 153 if (customRefs.size() != otherLevelCount) { 154 throw new RuntimeException("Test bug: customRefs.size() != " 155 + otherLevelCount); 156 } 157 waitForGC(customRefs, queue); 158 } 159 } 160 if (otherLevelCount != otherLevels || otherLevelCount == 0) { 161 throw new RuntimeException("Test bug: " 162 + "no or wrong count of levels loaded from custom loader"); 163 } 164 if (!customRefs.isEmpty()) { 165 throw new RuntimeException( 166 "Test bug: customRefs.size() should be empty!"); 167 } 168 while (!refs.isEmpty()) { 169 final Reference<?> ref = refs.remove(0); 170 if (ref.get() == null) { 171 throw new RuntimeException("Unexpected garbage collection for " 172 + ref); 173 } 174 } 175 } 176 177 private static void waitForGC(List<CustomLevelReference> customRefs, 178 ReferenceQueue<Level> queue) 179 throws InterruptedException 180 { 181 while (!customRefs.isEmpty()) { 182 Reference<? extends Level> ref2; 183 do { 184 System.gc(); 185 Thread.sleep(100); 186 } while ((ref2 = queue.poll()) == null); 187 188 // Check garbage collected reference 189 if (!customRefs.contains(ref2)) { 190 throw new RuntimeException("Unexpected reference: " + ref2); 191 } 192 CustomLevelReference ref = customRefs.remove(customRefs.indexOf(ref2)); 193 System.out.println(ref2 + " garbage collected"); 194 final String name = ref.name; 195 Level l; 196 try { 197 l = Level.parse(name); 198 if (!name.equals("SEVERE") 199 && !name.equals("INFO") 200 || !name.equals(l.getName())) { 201 throw new RuntimeException("Unexpected level " 202 + formatLevel(l)); 203 } else { 204 if (l == Level.WARNING || l == Level.INFO 205 || l == Level.SEVERE) { 206 System.out.println("Level.parse found expected level: " 207 + formatLevel(l)); 208 } else { 209 throw new RuntimeException("Unexpected level " 210 + formatLevel(l)); 211 } 212 } 213 } catch (IllegalArgumentException iae) { 214 if (!name.equals("WARNING") 215 && !name.equals("INFO") 216 && !name.equals("SEVERE")) { 217 System.out.println("Level.parse fired expected exception: " 218 + iae); 219 } else { 220 throw iae; 221 } 222 } 223 } 224 } 225 226 private static boolean isCustomLoader(Level level) { 227 final ClassLoader cl = level.getClass().getClassLoader(); 228 return cl != null 229 && cl != ClassLoader.getPlatformClassLoader() 230 && cl != ClassLoader.getSystemClassLoader(); 231 } 232 233 static ResourceBundle getResourceBundle(Level level) { 234 return isCustomLoader(level) 235 ? ResourceBundle.getBundle(OTHERRB_NAME, Locale.getDefault(), 236 level.getClass().getClassLoader()) 237 : ResourceBundle.getBundle(RB_NAME); 238 } 239 240 private static void setupCustomLevels() throws IOException { 241 levels.add(new CustomLevel("EMERGENCY", 1090, RB_NAME)); 242 levels.add(new CustomLevel("ALERT", 1060, RB_NAME)); 243 levels.add(new CustomLevel("CRITICAL", 1030, RB_NAME)); 244 levels.add(new CustomLevel("WARNING", 1010, RB_NAME)); 245 levels.add(new CustomLevel("INFO", 1000, RB_NAME)); 246 } 247 248 static void setUpCustomLevelsOtherLoader() 249 throws MalformedURLException, 250 ClassNotFoundException, NoSuchMethodException, 251 IllegalAccessException, InvocationTargetException 252 { 253 final String classes = System.getProperty("test.classes", 254 "build/classes"); 255 final String sources = System.getProperty("test.src", 256 "src"); 257 final URL curl = new File(classes).toURI().toURL(); 258 final URL surl = new File(sources).toURI().toURL(); 259 URLClassLoader loader = new URLClassLoader(new URL[] {curl, surl}, 260 ClassLoader.getPlatformClassLoader()); 261 Class<?> customLevelClass = Class.forName("CustomLevel", false, loader); 262 Method m = customLevelClass.getMethod("setUpCustomLevelsOtherLoader", 263 List.class); 264 m.invoke(null, levels); 265 } 266 267 public static void setUpCustomLevelsOtherLoader(List<Level> levels) { 268 levels.add(new CustomLevel("OTHEREMERGENCY", 1091, OTHERRB_NAME)); 269 levels.add(new CustomLevel("OTHERALERT", 1061, OTHERRB_NAME)); 270 levels.add(new CustomLevel("OTHERCRITICAL", 1031, OTHERRB_NAME)); 271 levels.add(new CustomLevel("SEVERE", 1011, OTHERRB_NAME)); 272 levels.add(new CustomLevel("INFO", 1000, OTHERRB_NAME)); 273 } 274 275 static void checkCustomLevel(Level level, Level expected) { 276 // Level value must be the same 277 if (!level.equals(expected)) { 278 throw new RuntimeException(formatLevel(level) + " != " 279 + formatLevel(expected)); 280 } 281 282 if (!level.getName().equals(expected.getName())) { 283 throw new RuntimeException(formatLevel(level) + " != " 284 + formatLevel(expected)); 285 } 286 287 // Level.parse is expected to return the custom Level 288 if (level != expected) { 289 throw new RuntimeException(formatLevel(level) + " != " 290 + formatLevel(expected)); 291 } 292 293 final ResourceBundle rb = getResourceBundle(level); 294 String name = rb.getString(level.getName()); 295 if (!level.getLocalizedName().equals(name)) { 296 // must have the same localized name 297 throw new RuntimeException(level.getLocalizedName() + " != " + name); 298 } 299 } 300 301 static String formatLevel(Level l) { 302 return l + ":" + l.intValue() + ":" + l.getClass().getName(); 303 } 304 }