< 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 >