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.ref.Reference;
  26 import java.lang.ref.ReferenceQueue;
  27 import java.lang.ref.WeakReference;
  28 import java.util.*;
  29 import java.util.logging.*;
  30 
  31 /*
  32  * @test
  33  * @bug 8026027 6543126
  34  * @summary Test Level.parse to look up custom levels by name and its
  35  *          localized name
  36  *
  37  * @run main/othervm CustomLevel
  38  */
  39 
  40 public class CustomLevel extends Level {
  41     public CustomLevel(String name, int value, String resourceBundleName) {
  42         super(name, value, resourceBundleName);
  43     }
  44 
  45     private static final List<Level> levels = new ArrayList<>();
  46     private static final String RB_NAME = "myresource";
  47 
  48     private static class CustomLevelReference extends WeakReference<Level> {
  49         final String name;
  50         final int value;
  51         final String resourceBundleName;
  52         public CustomLevelReference(Level level, ReferenceQueue<Level> queue) {
  53             super(level, queue);
  54             name = level.getName();
  55             value = level.intValue();
  56             resourceBundleName = level.getResourceBundleName();
  57         }
  58 
  59         @Override
  60         public String toString() {
  61             return "CustomLevelReference(\"" + name + "\", " + value + ", \""
  62                     + resourceBundleName + "\")";
  63         }
  64     }
  65 
  66     public static void main(String[] args) throws Exception {
  67         setupCustomLevels();
  68 
  69         // Level.parse will return the custom Level instance
  70         ResourceBundle rb = ResourceBundle.getBundle(RB_NAME);
  71         for (Level level : levels) {
  72             String name = level.getName();
  73             Level l = Level.parse(name);
  74             if (!name.equals("WARNING") && !name.equals("INFO")) {
  75                 // custom level whose name doesn't conflict with any standard one
  76                 checkCustomLevel(l, level);
  77             } else if (l != Level.WARNING && l != Level.INFO
  78                     || !name.equals(l.getName())) {
  79                 throw new RuntimeException("Unexpected level " + formatLevel(l));
  80             }
  81             System.out.println("Level.parse found expected level: "
  82                             + formatLevel(l));
  83             String localizedName = rb.getString(level.getName());
  84             l = Level.parse(localizedName);
  85             if (l != level) {
  86                 throw new RuntimeException("Unexpected level " + l + " " + l.getClass());
  87             }
  88         }
  89 
  90         // Now verify that custom level instances are correctly
  91         // garbage collected when no longer referenced
  92         ReferenceQueue<Level> queue = new ReferenceQueue<>();
  93         while (!levels.isEmpty()) {
  94             Level l = levels.stream().findAny().get();
  95             CustomLevelReference ref = new CustomLevelReference(l, queue);
  96 
  97             // remove strong references to l
  98             levels.remove(l);
  99             l = null;
 100 
 101             // Run gc and wait for garbage collection
 102             Reference<? extends Level> ref2;
 103             do {
 104                 System.gc();
 105                 Thread.sleep(100);
 106             } while ((ref2 = queue.poll()) == null);
 107 
 108             // Check garbage collected reference
 109             if (ref2 != ref) {
 110                 throw new RuntimeException("Unexpected reference: " + ref2);
 111             }
 112             System.out.println(ref2 + " garbage collected");
 113             final String name = ref.name;
 114             try {
 115                 l = Level.parse(name);
 116                 if (!name.equals("WARNING") && !name.equals("INFO")
 117                         || !name.equals(l.getName())) {
 118                     throw new RuntimeException("Unexpected level "
 119                             + formatLevel(l));
 120                 } else {
 121                     if (l == Level.WARNING || l == Level.INFO) {
 122                         System.out.println("Level.parse found expected level: "
 123                                 + formatLevel(l));
 124                     } else {
 125                         throw new RuntimeException("Unexpected level "
 126                             + formatLevel(l));
 127                     }
 128                 }
 129             } catch (IllegalArgumentException iae) {
 130                 if (!name.equals("WARNING") && !name.equals("INFO")) {
 131                     System.out.println("Level.parse fired expected exception: "
 132                             + iae);
 133                 } else {
 134                     throw iae;
 135                 }
 136             }
 137         }
 138     }
 139 
 140     private static void setupCustomLevels() throws IOException {
 141         levels.add(new CustomLevel("EMERGENCY", 1090, RB_NAME));
 142         levels.add(new CustomLevel("ALERT", 1060, RB_NAME));
 143         levels.add(new CustomLevel("CRITICAL", 1030, RB_NAME));
 144         levels.add(new CustomLevel("WARNING", 1010, RB_NAME));
 145         levels.add(new CustomLevel("INFO", 1000, RB_NAME));
 146     }
 147     static void checkCustomLevel(Level level, Level expected) {
 148         // Level value must be the same
 149         if (!level.equals(expected)) {
 150             throw new RuntimeException(formatLevel(level) + " != " + formatLevel(expected));
 151         }
 152 
 153         if (!level.getName().equals(expected.getName())) {
 154             throw new RuntimeException(formatLevel(level) + " != " + formatLevel(expected));
 155         }
 156 
 157         // Level.parse is expected to return the custom Level
 158         if (level != expected) {
 159             throw new RuntimeException(formatLevel(level) + " != " + formatLevel(expected));
 160         }
 161 
 162         ResourceBundle rb = ResourceBundle.getBundle(RB_NAME);
 163         String name = rb.getString(level.getName());
 164         if (!level.getLocalizedName().equals(name)) {
 165             // must have the same localized name
 166             throw new RuntimeException(level.getLocalizedName() + " != " + name);
 167         }
 168     }
 169 
 170     static String formatLevel(Level l) {
 171         return l + ":" + l.intValue() + ":" + l.getClass().getName();
 172     }
 173 }