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