< prev index next >

src/java.logging/share/classes/java/util/logging/Level.java

Print this page

        

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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.  Oracle designates this

@@ -22,16 +22,20 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
 
 package java.util.logging;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
 import java.lang.reflect.Module;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Optional;
 import java.util.ResourceBundle;
 
 /**
  * The Level class defines a set of standard logging levels that
  * can be used to control logging output.  The logging Level objects

@@ -406,18 +410,21 @@
     private static final long serialVersionUID = -8176160795706313070L;
 
     // Serialization magic to prevent "doppelgangers".
     // This is a performance optimization.
     private Object readResolve() {
-        KnownLevel o = KnownLevel.matches(this);
-        if (o != null) {
-            return o.levelObject;
-        }
+        Level level;
+        KnownLevel ref;
+        do {
+            ref = KnownLevel.matches(this);
+            level = KnownLevel.levelObject(ref);
+            if (level != null) return level;
+        } while (ref != null);
 
         // Woops.  Whoever sent us this object knows
         // about a new log level.  Add it to our list.
-        Level level = new Level(this.name, this.value, this.resourceBundleName);
+        level = new Level(this.name, this.value, this.resourceBundleName);
         return level;
     }
 
     /**
      * Parse a level name string into a Level.

@@ -448,42 +455,47 @@
      */
     public static synchronized Level parse(String name) throws IllegalArgumentException {
         // Check that name is not null.
         name.length();
 
-        KnownLevel level;
+        KnownLevel ref;
+        Level level;
 
         // Look for a known Level with the given non-localized name.
-        level = KnownLevel.findByName(name);
-        if (level != null) {
-            return level.levelObject;
-        }
+        do {
+            ref = KnownLevel.findByName(name);
+            level = KnownLevel.levelObject(ref);
+            if (level != null) return level;
+        } while (ref != null);
 
         // Now, check if the given name is an integer.  If so,
         // first look for a Level with the given value and then
         // if necessary create one.
         try {
             int x = Integer.parseInt(name);
-            level = KnownLevel.findByValue(x);
-            if (level == null) {
-                // add new Level
+            do {
+                ref = KnownLevel.findByValue(x);
+                level = KnownLevel.levelObject(ref);
+                if (level != null) return level;
+            } while (ref != null);
+            // add new Level.
                 Level levelObject = new Level(name, x);
-                level = KnownLevel.findByValue(x);
-            }
-            return level.levelObject;
+            ref = KnownLevel.findByValue(x);
+            return KnownLevel.levelObject(ref);
         } catch (NumberFormatException ex) {
             // Not an integer.
             // Drop through.
         }
 
         // Finally, look for a known level with the given localized name,
         // in the current default locale.
         // This is relatively expensive, but not excessively so.
-        level = KnownLevel.findByLocalizedLevelName(name);
-        if (level != null) {
-            return level.levelObject;
-        }
+        do {
+            ref = KnownLevel.findByLocalizedLevelName(name);
+            level = KnownLevel.levelObject(ref);
+            if (level != null) return level;
+        } while (ref != null);
 
         // OK, we've tried everything and failed
         throw new IllegalArgumentException("Bad level \"" + name + "\"");
     }
 

@@ -528,26 +540,47 @@
     //
     // Implementation Notes:
     // If Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods
     // were final, the following KnownLevel implementation can be removed.
     // Future API change should take this into consideration.
-    static final class KnownLevel {
+    static final class KnownLevel extends WeakReference<Level> {
         private static Map<String, List<KnownLevel>> nameToLevels = new HashMap<>();
         private static Map<Integer, List<KnownLevel>> intToLevels = new HashMap<>();
-        final Level levelObject;     // instance of Level class or Level subclass
+        private static final ReferenceQueue<Level> QUEUE = new ReferenceQueue<>();
+
         final Level mirroredLevel;   // mirror of the custom Level
         KnownLevel(Level l) {
-            this.levelObject = l;
+            super(l, QUEUE);
             if (l.getClass() == Level.class) {
                 this.mirroredLevel = l;
             } else {
                 // this mirrored level object is hidden
                 this.mirroredLevel = new Level(l.name, l.value, l.resourceBundleName, false);
             }
         }
 
+        private void remove() {
+            Optional.ofNullable(nameToLevels.get(mirroredLevel.name)).ifPresent((x) -> x.remove(this));
+            Optional.ofNullable(intToLevels.get(mirroredLevel.value)).ifPresent((x) -> x.remove(this));
+        }
+
+        static synchronized void purge(KnownLevel ref) {
+            ref.remove();
+        }
+
+        // Remove all stale KnownLevel instances
+        static synchronized void purge() {
+            Reference<? extends Level> ref;
+            while ((ref = QUEUE.poll()) != null) {
+                if (ref instanceof KnownLevel) {
+                    ((KnownLevel)ref).remove();
+                }
+            }
+        }
+
         static synchronized void add(Level l) {
+            purge();
             // the mirroredLevel object is always added to the list
             // before the custom Level instance
             KnownLevel o = new KnownLevel(l);
             List<KnownLevel> list = nameToLevels.get(l.name);
             if (list == null) {

@@ -564,56 +597,73 @@
             list.add(o);
         }
 
         // Returns a KnownLevel with the given non-localized name.
         static synchronized KnownLevel findByName(String name) {
+            purge();
             List<KnownLevel> list = nameToLevels.get(name);
             if (list != null) {
-                return list.get(0);
+                return list.stream().findFirst().orElse(null);
             }
             return null;
         }
 
         // Returns a KnownLevel with the given value.
         static synchronized KnownLevel findByValue(int value) {
+            purge();
             List<KnownLevel> list = intToLevels.get(value);
             if (list != null) {
-                return list.get(0);
+                return list.stream().findFirst().orElse(null);
             }
             return null;
         }
 
         // Returns a KnownLevel with the given localized name matching
         // by calling the Level.getLocalizedLevelName() method (i.e. found
         // from the resourceBundle associated with the Level object).
         // This method does not call Level.getLocalizedName() that may
         // be overridden in a subclass implementation
         static synchronized KnownLevel findByLocalizedLevelName(String name) {
+            purge();
             for (List<KnownLevel> levels : nameToLevels.values()) {
                 for (KnownLevel l : levels) {
-                    String lname = l.levelObject.getLocalizedLevelName();
+                    Level levelObject = l.get();
+                    if (levelObject != null) {
+                        String lname = levelObject.getLocalizedLevelName();
                     if (name.equals(lname)) {
                         return l;
                     }
                 }
             }
+            }
             return null;
         }
 
         static synchronized KnownLevel matches(Level l) {
+            purge();
             List<KnownLevel> list = nameToLevels.get(l.name);
             if (list != null) {
-                for (KnownLevel level : list) {
-                    Level other = level.mirroredLevel;
+                for (KnownLevel ref : list) {
+                    Level other = ref.mirroredLevel;
                     if (l.value == other.value &&
                            (l.resourceBundleName == other.resourceBundleName ||
                                (l.resourceBundleName != null &&
                                 l.resourceBundleName.equals(other.resourceBundleName)))) {
-                        return level;
+                        return ref;
                     }
                 }
             }
             return null;
         }
+
+        static Level levelObject(KnownLevel ref) {
+            if (ref == null) return null;
+            final Level level = ref.get();
+            if (level == null) {
+                purge(ref);
+            }
+            return level;
+        }
+
     }
 
 }
< prev index next >