1 /*
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.
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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */

25
26 package java.util.logging;
27
28 import java.lang.ref.Reference;
29 import java.lang.ref.ReferenceQueue;
30 import java.lang.ref.WeakReference;
31 import java.security.AccessController;
32 import java.security.PrivilegedAction;
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.Locale;
38 import java.util.Map;
39 import java.util.Optional;
40 import java.util.ResourceBundle;
41 import java.util.function.Function;
42 import jdk.internal.loader.ClassLoaderValue;
43 import jdk.internal.misc.JavaUtilResourceBundleAccess;
44 import jdk.internal.misc.SharedSecrets;
45
46 /**
47  * The Level class defines a set of standard logging levels that
48  * can be used to control logging output.  The logging Level objects
49  * are ordered and are specified by ordered integers.  Enabling logging
50  * at a given level also enables logging at all higher levels.
51  * <p>
52  * Clients should normally use the predefined Level constants such
53  * as Level.SEVERE.
54  * <p>
55  * The levels in descending order are:
56  * <ul>
57  * <li>SEVERE (highest value)
58  * <li>WARNING
59  * <li>INFO
60  * <li>CONFIG
61  * <li>FINE
62  * <li>FINER
63  * <li>FINEST  (lowest value)
64  * </ul>
65  * In addition there is a level OFF that can be used to turn
66  * off logging, and a level ALL that can be used to enable
67  * logging of all messages.
68  * <p>
69  * It is possible for third parties to define additional logging
70  * levels by subclassing Level.  In such cases subclasses should
71  * take care to chose unique integer level values and to ensure that
72  * they maintain the Object uniqueness property across serialization
73  * by defining a suitable readResolve method.
74  *
75  * @since 1.4
76  */

77
78 public class Level implements java.io.Serializable {
79     private static final String defaultBundle =
80         "sun.util.logging.resources.logging";
81
82     // Calling SharedSecrets.getJavaUtilResourceBundleAccess()
83     // forces the initialization of ResourceBundle.class, which
84     // can be too early if the VM has not finished booting yet.
85     private static final class RbAccess {
86         static final JavaUtilResourceBundleAccess RB_ACCESS =
87             SharedSecrets.getJavaUtilResourceBundleAccess();
88     }
89
90     /**
91      * @serial  The non-localized name of the level.
92      */

93     private final String name;
94
95     /**
96      * @serial  The integer value of the level.
97      */

98     private final int value;
99
100     /**
101      * @serial The resource bundle name to be used in localizing the level name.
102      */

103     private final String resourceBundleName;
104
105     // localized level name
106     private transient String localizedLevelName;
107     private transient Locale cachedLocale;
108
109     /**
110      * OFF is a special level that can be used to turn off logging.
111      * This level is initialized to <CODE>Integer.MAX_VALUE</CODE>.
112      */

113     public static final Level OFF = new Level("OFF",Integer.MAX_VALUE, defaultBundle);
114
115     /**
116      * SEVERE is a message level indicating a serious failure.
117      * <p>
118      * In general SEVERE messages should describe events that are
119      * of considerable importance and which will prevent normal
120      * program execution.   They should be reasonably intelligible
121      * to end users and to system administrators.
122      * This level is initialized to <CODE>1000</CODE>.
123      */

124     public static final Level SEVERE = new Level("SEVERE",1000, defaultBundle);
125
126     /**
127      * WARNING is a message level indicating a potential problem.
128      * <p>
129      * In general WARNING messages should describe events that will
130      * be of interest to end users or system managers, or which
131      * indicate potential problems.
132      * This level is initialized to <CODE>900</CODE>.
133      */

134     public static final Level WARNING = new Level("WARNING", 900, defaultBundle);
135
136     /**
137      * INFO is a message level for informational messages.
138      * <p>
139      * Typically INFO messages will be written to the console
140      * or its equivalent.  So the INFO level should only be
141      * used for reasonably significant messages that will
142      * make sense to end users and system administrators.
143      * This level is initialized to <CODE>800</CODE>.
144      */

145     public static final Level INFO = new Level("INFO", 800, defaultBundle);
146
147     /**
148      * CONFIG is a message level for static configuration messages.
149      * <p>
150      * CONFIG messages are intended to provide a variety of static
151      * configuration information, to assist in debugging problems
152      * that may be associated with particular configurations.
153      * For example, CONFIG message might include the CPU type,
154      * the graphics depth, the GUI look-and-feel, etc.
155      * This level is initialized to <CODE>700</CODE>.
156      */

157     public static final Level CONFIG = new Level("CONFIG", 700, defaultBundle);
158
159     /**
160      * FINE is a message level providing tracing information.
161      * <p>
162      * All of FINE, FINER, and FINEST are intended for relatively
163      * detailed tracing.  The exact meaning of the three levels will
164      * vary between subsystems, but in general, FINEST should be used
165      * for the most voluminous detailed output, FINER for somewhat
166      * less detailed output, and FINE for the  lowest volume (and
167      * most important) messages.
168      * <p>
169      * In general the FINE level should be used for information
170      * that will be broadly interesting to developers who do not have
171      * a specialized interest in the specific subsystem.
172      * <p>
173      * FINE messages might include things like minor (recoverable)
174      * failures.  Issues indicating potential performance problems
175      * are also worth logging as FINE.
176      * This level is initialized to <CODE>500</CODE>.
177      */

178     public static final Level FINE = new Level("FINE", 500, defaultBundle);
179
180     /**
181      * FINER indicates a fairly detailed tracing message.
182      * By default logging calls for entering, returning, or throwing
183      * an exception are traced at this level.
184      * This level is initialized to <CODE>400</CODE>.
185      */

186     public static final Level FINER = new Level("FINER", 400, defaultBundle);
187
188     /**
189      * FINEST indicates a highly detailed tracing message.
190      * This level is initialized to <CODE>300</CODE>.
191      */

192     public static final Level FINEST = new Level("FINEST", 300, defaultBundle);
193
194     /**
195      * ALL indicates that all messages should be logged.
196      * This level is initialized to <CODE>Integer.MIN_VALUE</CODE>.
197      */

198     public static final Level ALL = new Level("ALL", Integer.MIN_VALUE, defaultBundle);
199
200     private static final Level[] standardLevels = {
201         OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, ALL
202     };
203
204     /**
205      * Create a named Level with a given integer value.
206      * <p>
207      * Note that this constructor is "protected" to allow subclassing.
208      * In general clients of logging should use one of the constant Level
209      * objects such as SEVERE or FINEST.  However, if clients need to
210      * add new logging levels, they may subclass Level and define new
211      * constants.
212      * @param name  the name of the Level, for example "SEVERE".
213      * @param value an integer value for the level.
214      * @throws NullPointerException if the name is null
215      */

216     protected Level(String name, int value) {
217         this(name, value, null);
218     }
219
220     /**
221      * Create a named Level with a given integer value and a
222      * given localization resource name.
223      *
224      * @param name  the name of the Level, for example "SEVERE".
225      * @param value an integer value for the level.
226      * @param resourceBundleName name of a resource bundle to use in
227      *    localizing the given name. If the resourceBundleName is null
228      *    or an empty string, it is ignored.
229      * @throws NullPointerException if the name is null
230      */

231     protected Level(String name, int value, String resourceBundleName) {
232         this(name, value, resourceBundleName, true);
233     }
234
235     // private constructor to specify whether this instance should be added
236     // to the KnownLevel list from which Level.parse method does its look up
237     private Level(String name, int value, String resourceBundleName, boolean visible) {
238         if (name == null) {
239             throw new NullPointerException();
240         }
241         this.name = name;
242         this.value = value;
243         this.resourceBundleName = resourceBundleName;
244         this.localizedLevelName = resourceBundleName == null ? name : null;
245         this.cachedLocale = null;
246         if (visible) {
247             KnownLevel.add(this);
248         }
249     }
250
251     /**
252      * Return the level's localization resource bundle name, or
253      * null if no localization bundle is defined.
254      *
255      * @return localization resource bundle name
256      */

257     public String getResourceBundleName() {
258         return resourceBundleName;
259     }
260
261     /**
262      * Return the non-localized string name of the Level.
263      *
264      * @return non-localized name
265      */

266     public String getName() {
267         return name;
268     }
269
270     /**
271      * Return the localized string name of the Level, for
272      * the current default locale.
273      * <p>
274      * If no localization information is available, the
275      * non-localized name is returned.
276      *
277      * @return localized name
278      */

279     public String getLocalizedName() {
280         return getLocalizedLevelName();
281     }
282
283     // package-private getLevelName() is used by the implementation
284     // instead of getName() to avoid calling the subclass's version
285     final String getLevelName() {
286         return this.name;
287     }
288
289     private String computeLocalizedLevelName(Locale newLocale) {
290         // Resource bundle should be loaded from the defining module
291         // or its defining class loader, if it's unnamed module,
292         // of this Level instance that can be a custom Level subclass;
293         Module module = this.getClass().getModule();
294         ResourceBundle rb = RbAccess.RB_ACCESS.getBundle(resourceBundleName,
295                 newLocale, module);
296
297         final String localizedName = rb.getString(name);
298         final boolean isDefaultBundle = defaultBundle.equals(resourceBundleName);
299         if (!isDefaultBundle) return localizedName;
300
301         // This is a trick to determine whether the name has been translated
302         // or not. If it has not been translated, we need to use Locale.ROOT
303         // when calling toUpperCase().
304         final Locale rbLocale = rb.getLocale();
305         final Locale locale =
306                 Locale.ROOT.equals(rbLocale)
307                 || name.equals(localizedName.toUpperCase(Locale.ROOT))
308                 ? Locale.ROOT : rbLocale;
309
310         // ALL CAPS in a resource bundle's message indicates no translation
311         // needed per Oracle translation guideline.  To workaround this
312         // in Oracle JDK implementation, convert the localized level name
313         // to uppercase for compatibility reason.
314         return Locale.ROOT.equals(locale) ? name : localizedName.toUpperCase(locale);
315     }
316
317     // Avoid looking up the localizedLevelName twice if we already
318     // have it.
319     final String getCachedLocalizedLevelName() {
320
321         if (localizedLevelName != null) {
322             if (cachedLocale != null) {
323                 if (cachedLocale.equals(Locale.getDefault())) {
324                     // OK: our cached value was looked up with the same
325                     //     locale. We can use it.
326                     return localizedLevelName;
327                 }
328             }
329         }
330
331         if (resourceBundleName == null) {
332             // No resource bundle: just use the name.
333             return name;
334         }
335
336         // We need to compute the localized name.
337         // Either because it's the first time, or because our cached
338         // value is for a different locale. Just return null.
339         return null;
340     }
341
342     final synchronized String getLocalizedLevelName() {
343
344         // See if we have a cached localized name
345         final String cachedLocalizedName = getCachedLocalizedLevelName();
346         if (cachedLocalizedName != null) {
347             return cachedLocalizedName;
348         }
349
350         // No cached localized name or cache invalid.
351         // Need to compute the localized name.
352         final Locale newLocale = Locale.getDefault();
353         try {
354             localizedLevelName = computeLocalizedLevelName(newLocale);
355         } catch (Exception ex) {
356             localizedLevelName = name;
357         }
358         cachedLocale = newLocale;
359         return localizedLevelName;
360     }
361
362     // Returns a mirrored Level object that matches the given name as
363     // specified in the Level.parse method.  Returns null if not found.
364     //
365     // It returns the same Level object as the one returned by Level.parse
366     // method if the given name is a non-localized name or integer.
367     //
368     // If the name is a localized name, findLevel and parse method may
369     // return a different level value if there is a custom Level subclass
370     // that overrides Level.getLocalizedName() to return a different string
371     // than what's returned by the default implementation.
372     //
373     static Level findLevel(String name) {
374         if (name == null) {
375             throw new NullPointerException();
376         }
377
378         Optional<Level> level;
379
380         // Look for a known Level with the given non-localized name.
381         level = KnownLevel.findByName(name, KnownLevel::mirrored);
382         if (level.isPresent()) {
383             return level.get();
384         }
385
386         // Now, check if the given name is an integer.  If so,
387         // first look for a Level with the given value and then
388         // if necessary create one.
389         try {
390             int x = Integer.parseInt(name);
391             level = KnownLevel.findByValue(x, KnownLevel::mirrored);
392             if (level.isPresent()) {
393                 return level.get();
394             }
395             // add new Level
396             Level levelObject = new Level(name, x);
397             // There's no need to use a reachability fence here because
398             // KnownLevel keeps a strong reference on the level when
399             // level.getClass() == Level.class.
400             return KnownLevel.findByValue(x, KnownLevel::mirrored).get();
401         } catch (NumberFormatException ex) {
402             // Not an integer.
403             // Drop through.
404         }
405
406         level = KnownLevel.findByLocalizedLevelName(name,
407                 KnownLevel::mirrored);
408         if (level.isPresent()) {
409             return level.get();
410         }
411
412         return null;
413     }
414
415     /**
416      * Returns a string representation of this Level.
417      *
418      * @return the non-localized name of the Level, for example "INFO".
419      */

420     @Override
421     public final String toString() {
422         return name;
423     }
424
425     /**
426      * Get the integer value for this level.  This integer value
427      * can be used for efficient ordering comparisons between
428      * Level objects.
429      * @return the integer value for this level.
430      */

431     public final int intValue() {
432         return value;
433     }
434
435     private static final long serialVersionUID = -8176160795706313070L;
436
437     // Serialization magic to prevent "doppelgangers".
438     // This is a performance optimization.
439     private Object readResolve() {
440         Optional<Level> level = KnownLevel.matches(this);
441         if (level.isPresent()) {
442             return level.get();
443         }
444         // Woops.  Whoever sent us this object knows
445         // about a new log level.  Add it to our list.
446         return new Level(this.name, this.value, this.resourceBundleName);
447     }
448
449     /**
450      * Parse a level name string into a Level.
451      * <p>
452      * The argument string may consist of either a level name
453      * or an integer value.
454      * <p>
455      * For example:
456      * <ul>
457      * <li>     "SEVERE"
458      * <li>     "1000"
459      * </ul>
460      *
461      * @param  name   string to be parsed
462      * @throws NullPointerException if the name is null
463      * @throws IllegalArgumentException if the value is not valid.
464      * Valid values are integers between <CODE>Integer.MIN_VALUE</CODE>
465      * and <CODE>Integer.MAX_VALUE</CODE>, and all known level names.
466      * Known names are the levels defined by this class (e.g., <CODE>FINE</CODE>,
467      * <CODE>FINER</CODE>, <CODE>FINEST</CODE>), or created by this class with
468      * appropriate package access, or new levels defined or created
469      * by subclasses.
470      *
471      * @return The parsed value. Passing an integer that corresponds to a known name
472      * (e.g., 700) will return the associated name (e.g., <CODE>CONFIG</CODE>).
473      * Passing an integer that does not (e.g., 1) will return a new level name
474      * initialized to that value.
475      */

476     public static synchronized Level parse(String name) throws IllegalArgumentException {
477         // Check that name is not null.
478         name.length();
479
480         Optional<Level> level;
481
482         // Look for a known Level with the given non-localized name.
483         level = KnownLevel.findByName(name, KnownLevel::referent);
484         if (level.isPresent()) {
485             return level.get();
486         }
487
488         // Now, check if the given name is an integer.  If so,
489         // first look for a Level with the given value and then
490         // if necessary create one.
491         try {
492             int x = Integer.parseInt(name);
493             level = KnownLevel.findByValue(x, KnownLevel::referent);
494             if (level.isPresent()) {
495                 return level.get();
496             }
497             // add new Level.
498             Level levelObject = new Level(name, x);
499             // There's no need to use a reachability fence here because
500             // KnownLevel keeps a strong reference on the level when
501             // level.getClass() == Level.class.
502             return KnownLevel.findByValue(x, KnownLevel::referent).get();
503         } catch (NumberFormatException ex) {
504             // Not an integer.
505             // Drop through.
506         }
507
508         // Finally, look for a known level with the given localized name,
509         // in the current default locale.
510         // This is relatively expensive, but not excessively so.
511         level = KnownLevel.findByLocalizedLevelName(name, KnownLevel::referent);
512         if (level .isPresent()) {
513             return level.get();
514         }
515
516         // OK, we've tried everything and failed
517         throw new IllegalArgumentException("Bad level \"" + name + "\"");
518     }
519
520     /**
521      * Compare two objects for value equality.
522      * @return true if and only if the two objects have the same level value.
523      */

524     @Override
525     public boolean equals(Object ox) {
526         try {
527             Level lx = (Level)ox;
528             return (lx.value == this.value);
529         } catch (Exception ex) {
530             return false;
531         }
532     }
533
534     /**
535      * Generate a hashcode.
536      * @return a hashcode based on the level value
537      */

538     @Override
539     public int hashCode() {
540         return this.value;
541     }
542
543     // KnownLevel class maintains the global list of all known levels.
544     // The API allows multiple custom Level instances of the same name/value
545     // be created. This class provides convenient methods to find a level
546     // by a given name, by a given value, or by a given localized name.
547     //
548     // KnownLevel wraps the following Level objects:
549     // 1. levelObject:   standard Level object or custom Level object
550     // 2. mirroredLevel: Level object representing the level specified in the
551     //                   logging configuration.
552     //
553     // Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods
554     // are non-final but the name and resource bundle name are parameters to
555     // the Level constructor.  Use the mirroredLevel object instead of the
556     // levelObject to prevent the logging framework to execute foreign code
557     // implemented by untrusted Level subclass.
558     //
559     // Implementation Notes:
560     // If Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods
561     // were final, the following KnownLevel implementation can be removed.
562     // Future API change should take this into consideration.
563     static final class KnownLevel extends WeakReference<Level> {
564         private static Map<String, List<KnownLevel>> nameToLevels = new HashMap<>();
565         private static Map<Integer, List<KnownLevel>> intToLevels = new HashMap<>();
566         private static final ReferenceQueue<Level> QUEUE = new ReferenceQueue<>();
567
568         // CUSTOM_LEVEL_CLV is used to register custom level instances with
569         // their defining class loader, so that they are garbage collected
570         // if and only if their class loader is no longer strongly
571         // referenced.
572         private static final ClassLoaderValue<List<Level>> CUSTOM_LEVEL_CLV =
573                     new ClassLoaderValue<>();
574
575         final Level mirroredLevel;   // mirror of the custom Level
576         KnownLevel(Level l) {
577             super(l, QUEUE);
578             if (l.getClass() == Level.class) {
579                 this.mirroredLevel = l;
580             } else {
581                 // this mirrored level object is hidden
582                 this.mirroredLevel = new Level(l.name, l.value,
583                         l.resourceBundleName, false);
584             }
585         }
586
587         Optional<Level> mirrored() {
588             return Optional.of(mirroredLevel);
589         }
590
591         Optional<Level> referent() {
592             return Optional.ofNullable(get());
593         }
594
595         private void remove() {
596             Optional.ofNullable(nameToLevels.get(mirroredLevel.name))
597                     .ifPresent((x) -> x.remove(this));
598             Optional.ofNullable(intToLevels.get(mirroredLevel.value))
599                     .ifPresent((x) -> x.remove(this));
600         }
601
602         // Remove all stale KnownLevel instances
603         static synchronized void purge() {
604             Reference<? extends Level> ref;
605             while ((ref = QUEUE.poll()) != null) {
606                 if (ref instanceof KnownLevel) {
607                     ((KnownLevel)ref).remove();
608                 }
609             }
610         }
611
612         private static void registerWithClassLoader(Level customLevel) {
613             PrivilegedAction<ClassLoader> pa =
614                   () -> customLevel.getClass().getClassLoader();
615             PrivilegedAction<String> pn =  customLevel.getClass()::getName;
616             final String name = AccessController.doPrivileged(pn);
617             final ClassLoader cl = AccessController.doPrivileged(pa);
618             CUSTOM_LEVEL_CLV.computeIfAbsent(cl, (c, v) -> new ArrayList<>())
619                 .add(customLevel);
620         }
621
622         static synchronized void add(Level l) {
623             purge();
624             // the mirroredLevel object is always added to the list
625             // before the custom Level instance
626             KnownLevel o = new KnownLevel(l);
627             List<KnownLevel> list = nameToLevels.get(l.name);
628             if (list == null) {
629                 list = new ArrayList<>();
630                 nameToLevels.put(l.name, list);
631             }
632             list.add(o);
633
634             list = intToLevels.get(l.value);
635             if (list == null) {
636                 list = new ArrayList<>();
637                 intToLevels.put(l.value, list);
638             }
639             list.add(o);
640
641             // keep the custom level reachable from its class loader
642             // This will ensure that custom level values are not GC'ed
643             // until there class loader is GC'ed.
644             if (o.mirroredLevel != l) {
645                 registerWithClassLoader(l);
646             }
647
648         }
649
650         // Returns a KnownLevel with the given non-localized name.
651         static synchronized Optional<Level> findByName(String name,
652                 Function<KnownLevel, Optional<Level>> selector) {
653             purge();
654             return nameToLevels.getOrDefault(name, Collections.emptyList())
655                         .stream()
656                         .map(selector)
657                         .flatMap(Optional::stream)
658                         .findFirst();
659         }
660
661         // Returns a KnownLevel with the given value.
662         static synchronized Optional<Level> findByValue(int value,
663                 Function<KnownLevel, Optional<Level>> selector) {
664             purge();
665             return intToLevels.getOrDefault(value, Collections.emptyList())
666                         .stream()
667                         .map(selector)
668                         .flatMap(Optional::stream)
669                         .findFirst();
670         }
671
672         // Returns a KnownLevel with the given localized name matching
673         // by calling the Level.getLocalizedLevelName() method (i.e. found
674         // from the resourceBundle associated with the Level object).
675         // This method does not call Level.getLocalizedName() that may
676         // be overridden in a subclass implementation
677         static synchronized Optional<Level> findByLocalizedLevelName(String name,
678                 Function<KnownLevel, Optional<Level>> selector) {
679             purge();
680             return nameToLevels.values().stream()
681                          .flatMap(List::stream)
682                          .map(selector)
683                          .flatMap(Optional::stream)
684                          .filter(l -> name.equals(l.getLocalizedLevelName()))
685                          .findFirst();
686         }
687
688         static synchronized Optional<Level> matches(Level l) {
689             purge();
690             List<KnownLevel> list = nameToLevels.get(l.name);
691             if (list != null) {
692                 for (KnownLevel ref : list) {
693                     Level levelObject = ref.get();
694                     if (levelObject == nullcontinue;
695                     Level other = ref.mirroredLevel;
696                     Class<? extends Level> type = levelObject.getClass();
697                     if (l.value == other.value &&
698                            (l.resourceBundleName == other.resourceBundleName ||
699                                (l.resourceBundleName != null &&
700                                 l.resourceBundleName.equals(other.resourceBundleName)))) {
701                         if (type == l.getClass()) {
702                             return Optional.of(levelObject);
703                         }
704                     }
705                 }
706             }
707             return Optional.empty();
708         }
709     }
710
711 }
712