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