jdk/src/java.logging/share/classes/java/util/logging/Level.java
changeset 40944 dba53de83476
parent 36511 9d0388c6b336
child 42338 a60f280f803c
equal deleted inserted replaced
40943:46cd6d4f353f 40944:dba53de83476
     1 /*
     1 /*
     2  * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
     2  * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     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
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     7  * published by the Free Software Foundation.  Oracle designates this
    22  * or visit www.oracle.com if you need additional information or have any
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    23  * questions.
    24  */
    24  */
    25 
    25 
    26 package java.util.logging;
    26 package java.util.logging;
       
    27 import java.lang.ref.Reference;
       
    28 import java.lang.ref.ReferenceQueue;
       
    29 import java.lang.ref.WeakReference;
    27 import java.lang.reflect.Module;
    30 import java.lang.reflect.Module;
       
    31 import java.security.AccessController;
       
    32 import java.security.PrivilegedAction;
    28 import java.util.ArrayList;
    33 import java.util.ArrayList;
       
    34 import java.util.Collections;
    29 import java.util.HashMap;
    35 import java.util.HashMap;
    30 import java.util.List;
    36 import java.util.List;
    31 import java.util.Locale;
    37 import java.util.Locale;
    32 import java.util.Map;
    38 import java.util.Map;
       
    39 import java.util.Optional;
    33 import java.util.ResourceBundle;
    40 import java.util.ResourceBundle;
       
    41 import java.util.function.Function;
       
    42 import jdk.internal.loader.ClassLoaderValue;
    34 
    43 
    35 /**
    44 /**
    36  * The Level class defines a set of standard logging levels that
    45  * The Level class defines a set of standard logging levels that
    37  * can be used to control logging output.  The logging Level objects
    46  * can be used to control logging output.  The logging Level objects
    38  * are ordered and are specified by ordered integers.  Enabling logging
    47  * are ordered and are specified by ordered integers.  Enabling logging
   175      * ALL indicates that all messages should be logged.
   184      * ALL indicates that all messages should be logged.
   176      * This level is initialized to <CODE>Integer.MIN_VALUE</CODE>.
   185      * This level is initialized to <CODE>Integer.MIN_VALUE</CODE>.
   177      */
   186      */
   178     public static final Level ALL = new Level("ALL", Integer.MIN_VALUE, defaultBundle);
   187     public static final Level ALL = new Level("ALL", Integer.MIN_VALUE, defaultBundle);
   179 
   188 
       
   189     private static final Level[] standardLevels = {
       
   190         OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, ALL
       
   191     };
       
   192 
   180     /**
   193     /**
   181      * Create a named Level with a given integer value.
   194      * Create a named Level with a given integer value.
   182      * <p>
   195      * <p>
   183      * Note that this constructor is "protected" to allow subclassing.
   196      * Note that this constructor is "protected" to allow subclassing.
   184      * In general clients of logging should use one of the constant Level
   197      * In general clients of logging should use one of the constant Level
   265     private String computeLocalizedLevelName(Locale newLocale) {
   278     private String computeLocalizedLevelName(Locale newLocale) {
   266         // Resource bundle should be loaded from the defining module
   279         // Resource bundle should be loaded from the defining module
   267         // or its defining class loader, if it's unnamed module,
   280         // or its defining class loader, if it's unnamed module,
   268         // of this Level instance that can be a custom Level subclass;
   281         // of this Level instance that can be a custom Level subclass;
   269         Module module = this.getClass().getModule();
   282         Module module = this.getClass().getModule();
   270         ResourceBundle rb = ResourceBundle.getBundle(resourceBundleName, newLocale, module);
   283         ResourceBundle rb = ResourceBundle.getBundle(resourceBundleName,
       
   284                 newLocale, module);
   271 
   285 
   272         final String localizedName = rb.getString(name);
   286         final String localizedName = rb.getString(name);
   273         final boolean isDefaultBundle = defaultBundle.equals(resourceBundleName);
   287         final boolean isDefaultBundle = defaultBundle.equals(resourceBundleName);
   274         if (!isDefaultBundle) return localizedName;
   288         if (!isDefaultBundle) return localizedName;
   275 
   289 
   348     static Level findLevel(String name) {
   362     static Level findLevel(String name) {
   349         if (name == null) {
   363         if (name == null) {
   350             throw new NullPointerException();
   364             throw new NullPointerException();
   351         }
   365         }
   352 
   366 
   353         KnownLevel level;
   367         Optional<Level> level;
   354 
   368 
   355         // Look for a known Level with the given non-localized name.
   369         // Look for a known Level with the given non-localized name.
   356         level = KnownLevel.findByName(name);
   370         level = KnownLevel.findByName(name, KnownLevel::mirrored);
   357         if (level != null) {
   371         if (level.isPresent()) {
   358             return level.mirroredLevel;
   372             return level.get();
   359         }
   373         }
   360 
   374 
   361         // Now, check if the given name is an integer.  If so,
   375         // Now, check if the given name is an integer.  If so,
   362         // first look for a Level with the given value and then
   376         // first look for a Level with the given value and then
   363         // if necessary create one.
   377         // if necessary create one.
   364         try {
   378         try {
   365             int x = Integer.parseInt(name);
   379             int x = Integer.parseInt(name);
   366             level = KnownLevel.findByValue(x);
   380             level = KnownLevel.findByValue(x, KnownLevel::mirrored);
   367             if (level == null) {
   381             if (!level.isPresent()) {
   368                 // add new Level
   382                 // add new Level
   369                 Level levelObject = new Level(name, x);
   383                 Level levelObject = new Level(name, x);
   370                 level = KnownLevel.findByValue(x);
   384                 // There's no need to use a reachability fence here because
   371             }
   385                 // KnownLevel keeps a strong reference on the level when
   372             return level.mirroredLevel;
   386                 // level.getClass() == Level.class.
       
   387                 return KnownLevel.findByValue(x, KnownLevel::mirrored).get();
       
   388             }
   373         } catch (NumberFormatException ex) {
   389         } catch (NumberFormatException ex) {
   374             // Not an integer.
   390             // Not an integer.
   375             // Drop through.
   391             // Drop through.
   376         }
   392         }
   377 
   393 
   378         level = KnownLevel.findByLocalizedLevelName(name);
   394         level = KnownLevel.findByLocalizedLevelName(name,
   379         if (level != null) {
   395                 KnownLevel::mirrored);
   380             return level.mirroredLevel;
   396         if (level.isPresent()) {
       
   397             return level.get();
   381         }
   398         }
   382 
   399 
   383         return null;
   400         return null;
   384     }
   401     }
   385 
   402 
   406     private static final long serialVersionUID = -8176160795706313070L;
   423     private static final long serialVersionUID = -8176160795706313070L;
   407 
   424 
   408     // Serialization magic to prevent "doppelgangers".
   425     // Serialization magic to prevent "doppelgangers".
   409     // This is a performance optimization.
   426     // This is a performance optimization.
   410     private Object readResolve() {
   427     private Object readResolve() {
   411         KnownLevel o = KnownLevel.matches(this);
   428         Optional<Level> level = KnownLevel.matches(this);
   412         if (o != null) {
   429         if (level.isPresent()) {
   413             return o.levelObject;
   430             return level.get();
   414         }
   431         }
   415 
       
   416         // Woops.  Whoever sent us this object knows
   432         // Woops.  Whoever sent us this object knows
   417         // about a new log level.  Add it to our list.
   433         // about a new log level.  Add it to our list.
   418         Level level = new Level(this.name, this.value, this.resourceBundleName);
   434         return new Level(this.name, this.value, this.resourceBundleName);
   419         return level;
       
   420     }
   435     }
   421 
   436 
   422     /**
   437     /**
   423      * Parse a level name string into a Level.
   438      * Parse a level name string into a Level.
   424      * <p>
   439      * <p>
   448      */
   463      */
   449     public static synchronized Level parse(String name) throws IllegalArgumentException {
   464     public static synchronized Level parse(String name) throws IllegalArgumentException {
   450         // Check that name is not null.
   465         // Check that name is not null.
   451         name.length();
   466         name.length();
   452 
   467 
   453         KnownLevel level;
   468         Optional<Level> level;
   454 
   469 
   455         // Look for a known Level with the given non-localized name.
   470         // Look for a known Level with the given non-localized name.
   456         level = KnownLevel.findByName(name);
   471         level = KnownLevel.findByName(name, KnownLevel::referent);
   457         if (level != null) {
   472         if (level.isPresent()) {
   458             return level.levelObject;
   473             return level.get();
   459         }
   474         }
   460 
   475 
   461         // Now, check if the given name is an integer.  If so,
   476         // Now, check if the given name is an integer.  If so,
   462         // first look for a Level with the given value and then
   477         // first look for a Level with the given value and then
   463         // if necessary create one.
   478         // if necessary create one.
   464         try {
   479         try {
   465             int x = Integer.parseInt(name);
   480             int x = Integer.parseInt(name);
   466             level = KnownLevel.findByValue(x);
   481             level = KnownLevel.findByValue(x, KnownLevel::referent);
   467             if (level == null) {
   482             if (level.isPresent()) {
   468                 // add new Level
   483                 return level.get();
   469                 Level levelObject = new Level(name, x);
   484             }
   470                 level = KnownLevel.findByValue(x);
   485             // add new Level.
   471             }
   486             Level levelObject = new Level(name, x);
   472             return level.levelObject;
   487             // There's no need to use a reachability fence here because
       
   488             // KnownLevel keeps a strong reference on the level when
       
   489             // level.getClass() == Level.class.
       
   490             return KnownLevel.findByValue(x, KnownLevel::referent).get();
   473         } catch (NumberFormatException ex) {
   491         } catch (NumberFormatException ex) {
   474             // Not an integer.
   492             // Not an integer.
   475             // Drop through.
   493             // Drop through.
   476         }
   494         }
   477 
   495 
   478         // Finally, look for a known level with the given localized name,
   496         // Finally, look for a known level with the given localized name,
   479         // in the current default locale.
   497         // in the current default locale.
   480         // This is relatively expensive, but not excessively so.
   498         // This is relatively expensive, but not excessively so.
   481         level = KnownLevel.findByLocalizedLevelName(name);
   499         level = KnownLevel.findByLocalizedLevelName(name, KnownLevel::referent);
   482         if (level != null) {
   500         if (level .isPresent()) {
   483             return level.levelObject;
   501             return level.get();
   484         }
   502         }
   485 
   503 
   486         // OK, we've tried everything and failed
   504         // OK, we've tried everything and failed
   487         throw new IllegalArgumentException("Bad level \"" + name + "\"");
   505         throw new IllegalArgumentException("Bad level \"" + name + "\"");
   488     }
   506     }
   528     //
   546     //
   529     // Implementation Notes:
   547     // Implementation Notes:
   530     // If Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods
   548     // If Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods
   531     // were final, the following KnownLevel implementation can be removed.
   549     // were final, the following KnownLevel implementation can be removed.
   532     // Future API change should take this into consideration.
   550     // Future API change should take this into consideration.
   533     static final class KnownLevel {
   551     static final class KnownLevel extends WeakReference<Level> {
   534         private static Map<String, List<KnownLevel>> nameToLevels = new HashMap<>();
   552         private static Map<String, List<KnownLevel>> nameToLevels = new HashMap<>();
   535         private static Map<Integer, List<KnownLevel>> intToLevels = new HashMap<>();
   553         private static Map<Integer, List<KnownLevel>> intToLevels = new HashMap<>();
   536         final Level levelObject;     // instance of Level class or Level subclass
   554         private static final ReferenceQueue<Level> QUEUE = new ReferenceQueue<>();
       
   555 
       
   556         // CUSTOM_LEVEL_CLV is used to register custom level instances with
       
   557         // their defining class loader, so that they are garbage collected
       
   558         // if and only if their class loader is no longer strongly
       
   559         // referenced.
       
   560         private static final ClassLoaderValue<List<Level>> CUSTOM_LEVEL_CLV =
       
   561                     new ClassLoaderValue<>();
       
   562 
   537         final Level mirroredLevel;   // mirror of the custom Level
   563         final Level mirroredLevel;   // mirror of the custom Level
   538         KnownLevel(Level l) {
   564         KnownLevel(Level l) {
   539             this.levelObject = l;
   565             super(l, QUEUE);
   540             if (l.getClass() == Level.class) {
   566             if (l.getClass() == Level.class) {
   541                 this.mirroredLevel = l;
   567                 this.mirroredLevel = l;
   542             } else {
   568             } else {
   543                 // this mirrored level object is hidden
   569                 // this mirrored level object is hidden
   544                 this.mirroredLevel = new Level(l.name, l.value, l.resourceBundleName, false);
   570                 this.mirroredLevel = new Level(l.name, l.value,
   545             }
   571                         l.resourceBundleName, false);
       
   572             }
       
   573         }
       
   574 
       
   575         Optional<Level> mirrored() {
       
   576             return Optional.of(mirroredLevel);
       
   577         }
       
   578 
       
   579         Optional<Level> referent() {
       
   580             return Optional.ofNullable(get());
       
   581         }
       
   582 
       
   583         private void remove() {
       
   584             Optional.ofNullable(nameToLevels.get(mirroredLevel.name))
       
   585                     .ifPresent((x) -> x.remove(this));
       
   586             Optional.ofNullable(intToLevels.get(mirroredLevel.value))
       
   587                     .ifPresent((x) -> x.remove(this));
       
   588         }
       
   589 
       
   590         // Remove all stale KnownLevel instances
       
   591         static synchronized void purge() {
       
   592             Reference<? extends Level> ref;
       
   593             while ((ref = QUEUE.poll()) != null) {
       
   594                 if (ref instanceof KnownLevel) {
       
   595                     ((KnownLevel)ref).remove();
       
   596                 }
       
   597             }
       
   598         }
       
   599 
       
   600         private static void registerWithClassLoader(Level customLevel) {
       
   601             PrivilegedAction<ClassLoader> pa =
       
   602                   () -> customLevel.getClass().getClassLoader();
       
   603             PrivilegedAction<String> pn =  customLevel.getClass()::getName;
       
   604             final String name = AccessController.doPrivileged(pn);
       
   605             final ClassLoader cl = AccessController.doPrivileged(pa);
       
   606             CUSTOM_LEVEL_CLV.computeIfAbsent(cl, (c, v) -> new ArrayList<>())
       
   607                 .add(customLevel);
   546         }
   608         }
   547 
   609 
   548         static synchronized void add(Level l) {
   610         static synchronized void add(Level l) {
       
   611             purge();
   549             // the mirroredLevel object is always added to the list
   612             // the mirroredLevel object is always added to the list
   550             // before the custom Level instance
   613             // before the custom Level instance
   551             KnownLevel o = new KnownLevel(l);
   614             KnownLevel o = new KnownLevel(l);
   552             List<KnownLevel> list = nameToLevels.get(l.name);
   615             List<KnownLevel> list = nameToLevels.get(l.name);
   553             if (list == null) {
   616             if (list == null) {
   560             if (list == null) {
   623             if (list == null) {
   561                 list = new ArrayList<>();
   624                 list = new ArrayList<>();
   562                 intToLevels.put(l.value, list);
   625                 intToLevels.put(l.value, list);
   563             }
   626             }
   564             list.add(o);
   627             list.add(o);
       
   628 
       
   629             // keep the custom level reachable from its class loader
       
   630             // This will ensure that custom level values are not GC'ed
       
   631             // until there class loader is GC'ed.
       
   632             if (o.mirroredLevel != l) {
       
   633                 registerWithClassLoader(l);
       
   634             }
       
   635 
   565         }
   636         }
   566 
   637 
   567         // Returns a KnownLevel with the given non-localized name.
   638         // Returns a KnownLevel with the given non-localized name.
   568         static synchronized KnownLevel findByName(String name) {
   639         static synchronized Optional<Level> findByName(String name,
   569             List<KnownLevel> list = nameToLevels.get(name);
   640                 Function<KnownLevel, Optional<Level>> selector) {
   570             if (list != null) {
   641             purge();
   571                 return list.get(0);
   642             return nameToLevels.getOrDefault(name, Collections.emptyList())
   572             }
   643                         .stream()
   573             return null;
   644                         .map(selector)
       
   645                         .flatMap(Optional::stream)
       
   646                         .findFirst();
   574         }
   647         }
   575 
   648 
   576         // Returns a KnownLevel with the given value.
   649         // Returns a KnownLevel with the given value.
   577         static synchronized KnownLevel findByValue(int value) {
   650         static synchronized Optional<Level> findByValue(int value,
   578             List<KnownLevel> list = intToLevels.get(value);
   651                 Function<KnownLevel, Optional<Level>> selector) {
   579             if (list != null) {
   652             purge();
   580                 return list.get(0);
   653             return intToLevels.getOrDefault(value, Collections.emptyList())
   581             }
   654                         .stream()
   582             return null;
   655                         .map(selector)
       
   656                         .flatMap(Optional::stream)
       
   657                         .findFirst();
   583         }
   658         }
   584 
   659 
   585         // Returns a KnownLevel with the given localized name matching
   660         // Returns a KnownLevel with the given localized name matching
   586         // by calling the Level.getLocalizedLevelName() method (i.e. found
   661         // by calling the Level.getLocalizedLevelName() method (i.e. found
   587         // from the resourceBundle associated with the Level object).
   662         // from the resourceBundle associated with the Level object).
   588         // This method does not call Level.getLocalizedName() that may
   663         // This method does not call Level.getLocalizedName() that may
   589         // be overridden in a subclass implementation
   664         // be overridden in a subclass implementation
   590         static synchronized KnownLevel findByLocalizedLevelName(String name) {
   665         static synchronized Optional<Level> findByLocalizedLevelName(String name,
   591             for (List<KnownLevel> levels : nameToLevels.values()) {
   666                 Function<KnownLevel, Optional<Level>> selector) {
   592                 for (KnownLevel l : levels) {
   667             purge();
   593                     String lname = l.levelObject.getLocalizedLevelName();
   668             return nameToLevels.values().stream()
   594                     if (name.equals(lname)) {
   669                          .flatMap(List::stream)
   595                         return l;
   670                          .map(selector)
   596                     }
   671                          .flatMap(Optional::stream)
   597                 }
   672                          .filter(l -> name.equals(l.getLocalizedLevelName()))
   598             }
   673                          .findFirst();
   599             return null;
   674         }
   600         }
   675 
   601 
   676         static synchronized Optional<Level> matches(Level l) {
   602         static synchronized KnownLevel matches(Level l) {
   677             purge();
   603             List<KnownLevel> list = nameToLevels.get(l.name);
   678             List<KnownLevel> list = nameToLevels.get(l.name);
   604             if (list != null) {
   679             if (list != null) {
   605                 for (KnownLevel level : list) {
   680                 for (KnownLevel ref : list) {
   606                     Level other = level.mirroredLevel;
   681                     Level levelObject = ref.get();
       
   682                     if (levelObject == null) continue;
       
   683                     Level other = ref.mirroredLevel;
   607                     if (l.value == other.value &&
   684                     if (l.value == other.value &&
   608                            (l.resourceBundleName == other.resourceBundleName ||
   685                            (l.resourceBundleName == other.resourceBundleName ||
   609                                (l.resourceBundleName != null &&
   686                                (l.resourceBundleName != null &&
   610                                 l.resourceBundleName.equals(other.resourceBundleName)))) {
   687                                 l.resourceBundleName.equals(other.resourceBundleName)))) {
   611                         return level;
   688                         return Optional.of(levelObject);
   612                     }
   689                     }
   613                 }
   690                 }
   614             }
   691             }
   615             return null;
   692             return Optional.empty();
   616         }
   693         }
   617     }
   694     }
   618 
   695 
   619 }
   696 }