< prev index next >

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

Print this page




  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 
  27 package java.util.logging;
  28 
  29 import java.io.*;
  30 import java.util.*;
  31 import java.security.*;
  32 import java.lang.ref.ReferenceQueue;
  33 import java.lang.ref.WeakReference;
  34 import java.util.concurrent.ConcurrentHashMap;

  35 import java.util.concurrent.CopyOnWriteArrayList;
  36 import java.util.concurrent.locks.ReentrantLock;






  37 import sun.misc.JavaAWTAccess;
  38 import sun.misc.ManagedLocalsThread;
  39 import sun.misc.SharedSecrets;
  40 
  41 /**
  42  * There is a single global LogManager object that is used to
  43  * maintain a set of shared state about Loggers and log services.
  44  * <p>
  45  * This LogManager object:
  46  * <ul>
  47  * <li> Manages a hierarchical namespace of Logger objects.  All
  48  *      named Loggers are stored in this namespace.
  49  * <li> Manages a set of logging control properties.  These are
  50  *      simple key-value pairs that can be used by Handlers and
  51  *      other logging objects to configure themselves.
  52  * </ul>
  53  * <p>
  54  * The global LogManager object can be retrieved using LogManager.getLogManager().
  55  * The LogManager object is created during class initialization and
  56  * cannot subsequently be changed.


 771                     return false;
 772                 }
 773             }
 774 
 775             // We're adding a new logger.
 776             // Note that we are creating a weak reference here.
 777             final LogManager owner = getOwner();
 778             logger.setLogManager(owner);
 779             ref = owner.new LoggerWeakRef(logger);
 780 
 781             // Apply any initial level defined for the new logger, unless
 782             // the logger's level is already initialized
 783             Level level = owner.getLevelProperty(name + ".level", null);
 784             if (level != null && !logger.isLevelInitialized()) {
 785                 doSetLevel(logger, level);
 786             }
 787 
 788             // instantiation of the handler is done in the LogManager.addLogger
 789             // implementation as a handler class may be only visible to LogManager
 790             // subclass for the custom log manager case
 791             processParentHandlers(logger, name);
 792 
 793             // Find the new node and its parent.
 794             LogNode node = getNode(name);
 795             node.loggerRef = ref;
 796             Logger parent = null;
 797             LogNode nodep = node.parent;
 798             while (nodep != null) {
 799                 LoggerWeakRef nodeRef = nodep.loggerRef;
 800                 if (nodeRef != null) {
 801                     parent = nodeRef.get();
 802                     if (parent != null) {
 803                         break;
 804                     }
 805                 }
 806                 nodep = nodep.parent;
 807             }
 808 
 809             if (parent != null) {
 810                 doSetParent(logger, parent);
 811             }


 819             // soon as it is published in namedLoggers (findLogger takes
 820             // benefit of the ConcurrentHashMap implementation of namedLoggers
 821             // to avoid synchronizing on retrieval when that is possible).
 822             namedLoggers.put(name, ref);
 823             return true;
 824         }
 825 
 826         void removeLoggerRef(String name, LoggerWeakRef ref) {
 827             namedLoggers.remove(name, ref);
 828         }
 829 
 830         synchronized Enumeration<String> getLoggerNames() {
 831             // ensure that this context is properly initialized before
 832             // returning logger names.
 833             ensureInitialized();
 834             return Collections.enumeration(namedLoggers.keySet());
 835         }
 836 
 837         // If logger.getUseParentHandlers() returns 'true' and any of the logger's
 838         // parents have levels or handlers defined, make sure they are instantiated.
 839         private void processParentHandlers(final Logger logger, final String name) {

 840             final LogManager owner = getOwner();
 841             AccessController.doPrivileged(new PrivilegedAction<Void>() {
 842                 @Override
 843                 public Void run() {
 844                     if (logger != owner.rootLogger) {
 845                         boolean useParent = owner.getBooleanProperty(name + ".useParentHandlers", true);
 846                         if (!useParent) {
 847                             logger.setUseParentHandlers(false);
 848                         }
 849                     }
 850                     return null;
 851                 }
 852             });
 853 
 854             int ix = 1;
 855             for (;;) {
 856                 int ix2 = name.indexOf('.', ix);
 857                 if (ix2 < 0) {
 858                     break;
 859                 }
 860                 String pname = name.substring(0, ix2);
 861                 if (owner.getProperty(pname + ".level") != null ||
 862                     owner.getProperty(pname + ".handlers") != null) {
 863                     // This pname has a level/handlers definition.
 864                     // Make sure it exists.
 865                     demandLogger(pname, null, null);


 866                 }
 867                 ix = ix2+1;
 868             }
 869         }
 870 
 871         // Gets a node in our tree of logger nodes.
 872         // If necessary, create it.
 873         LogNode getNode(String name) {
 874             if (name == null || name.equals("")) {
 875                 return root;
 876             }
 877             LogNode node = root;
 878             while (name.length() > 0) {
 879                 int ix = name.indexOf('.');
 880                 String head;
 881                 if (ix > 0) {
 882                     head = name.substring(0, ix);
 883                     name = name.substring(ix + 1);
 884                 } else {
 885                     head = name;


 925                         // the other Logger is not holding a strong reference to
 926                         // the other Logger, then it is possible for the other
 927                         // Logger to be GC'ed after we saw it in addLogger() and
 928                         // before we can refetch it. If it has been GC'ed then
 929                         // we'll just loop around and try again.
 930                         result = findLogger(name);
 931                     }
 932                 } while (result == null);
 933             }
 934             return result;
 935         }
 936     }
 937 
 938     // Add new per logger handlers.
 939     // We need to raise privilege here. All our decisions will
 940     // be made based on the logging configuration, which can
 941     // only be modified by trusted code.
 942     private void loadLoggerHandlers(final Logger logger, final String name,
 943                                     final String handlersPropertyName)
 944     {
 945         AccessController.doPrivileged(new PrivilegedAction<Object>() {
 946             @Override
 947             public Object run() {
 948                 String names[] = parseClassNames(handlersPropertyName);
 949                 final boolean ensureCloseOnReset = names.length > 0
 950                     && getBooleanProperty(handlersPropertyName + ".ensureCloseOnReset",true);



 951 






 952                 int count = 0;













 953                 for (String type : names) {
 954                     try {
 955                         Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(type);
 956                         Handler hdl = (Handler) clz.newInstance();
 957                         // Check if there is a property defining the
 958                         // this handler's level.
 959                         String levs = getProperty(type + ".level");
 960                         if (levs != null) {
 961                             Level l = Level.findLevel(levs);
 962                             if (l != null) {
 963                                 hdl.setLevel(l);
 964                             } else {
 965                                 // Probably a bad level. Drop through.
 966                                 System.err.println("Can't set level for " + type);
 967                             }
 968                         }
 969                         // Add this Handler to the logger
 970                         logger.addHandler(hdl);
 971                         if (++count == 1 && ensureCloseOnReset) {
 972                             // add this logger to the closeOnResetLoggers list.
 973                             closeOnResetLoggers.addIfAbsent(CloseOnReset.create(logger));
 974                         }
 975                     } catch (Exception ex) {
 976                         System.err.println("Can't load log handler \"" + type + "\"");
 977                         System.err.println("" + ex);
 978                         ex.printStackTrace();
 979                     }
 980                 }
 981 
 982                 return null;
 983             }
 984         });
 985     }
 986 
 987 
 988     // loggerRefQueue holds LoggerWeakRef objects for Logger objects
 989     // that have been GC'ed.
 990     private final ReferenceQueue<Logger> loggerRefQueue
 991         = new ReferenceQueue<>();
 992 
 993     // Package-level inner class.
 994     // Helper class for managing WeakReferences to Logger objects.
 995     //
 996     // LogManager.namedLoggers
 997     //     - has weak references to all named Loggers
 998     //     - namedLoggers keeps the LoggerWeakRef objects for the named
 999     //       Loggers around until we can deal with the book keeping for
1000     //       the named Logger that is being GC'ed.
1001     // LogManager.LogNode.loggerRef
1002     //     - has a weak reference to a named Logger
1003     //     - the LogNode will also keep the LoggerWeakRef objects for
1004     //       the named Loggers around; currently LogNodes never go away.


1237      *
1238      * @return  enumeration of logger name strings
1239      */
1240     public Enumeration<String> getLoggerNames() {
1241         return getUserContext().getLoggerNames();
1242     }
1243 
1244     /**
1245      * Reinitialize the logging properties and reread the logging configuration.
1246      * <p>
1247      * The same rules are used for locating the configuration properties
1248      * as are used at startup.  So normally the logging properties will
1249      * be re-read from the same file that was used at startup.
1250      * <P>
1251      * Any log level definitions in the new configuration file will be
1252      * applied using Logger.setLevel(), if the target Logger exists.
1253      * <p>
1254      * Any {@linkplain #addConfigurationListener registered configuration
1255      * listener} will be invoked after the properties are read.
1256      *












1257      * @exception  SecurityException  if a security manager exists and if
1258      *             the caller does not have LoggingPermission("control").
1259      * @exception  IOException if there are IO problems reading the configuration.
1260      */
1261     public void readConfiguration() throws IOException, SecurityException {
1262         checkPermission();
1263 
1264         // if a configuration class is specified, load it and use it.
1265         String cname = System.getProperty("java.util.logging.config.class");
1266         if (cname != null) {
1267             try {
1268                 // Instantiate the named class.  It is its constructor's
1269                 // responsibility to initialize the logging configuration, by
1270                 // calling readConfiguration(InputStream) with a suitable stream.
1271                 try {
1272                     Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname);
1273                     clz.newInstance();
1274                     return;
1275                 } catch (ClassNotFoundException ex) {
1276                     Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
1277                     clz.newInstance();
1278                     return;
1279                 }
1280             } catch (Exception ex) {
1281                 System.err.println("Logging configuration class \"" + cname + "\" failed");
1282                 System.err.println("" + ex);
1283                 // keep going and useful config file.
1284             }
1285         }
1286 








1287         String fname = System.getProperty("java.util.logging.config.file");
1288         if (fname == null) {
1289             fname = System.getProperty("java.home");
1290             if (fname == null) {
1291                 throw new Error("Can't find java.home ??");
1292             }
1293             File f = new File(fname, "conf");
1294             f = new File(f, "logging.properties");
1295             fname = f.getCanonicalPath();
1296         }
1297         try (final InputStream in = new FileInputStream(fname)) {
1298             final BufferedInputStream bin = new BufferedInputStream(in);
1299             readConfiguration(bin);
1300         }

1301     }
1302 
1303     /**
1304      * Reset the logging configuration.
1305      * <p>
1306      * For all named loggers, the reset operation removes and closes
1307      * all Handlers and (except for the root logger) sets the level
1308      * to null.  The root logger's level is set to Level.INFO.
1309      *
1310      * @exception  SecurityException  if a security manager exists and if
1311      *             the caller does not have LoggingPermission("control").
1312      */
1313 
1314     public void reset() throws SecurityException {
1315         checkPermission();
1316 
1317         List<CloseOnReset> persistent;
1318 
1319         // We don't want reset() and readConfiguration()
1320         // to run in parallel


1412             String word = hands.substring(ix, end);
1413             ix = end+1;
1414             word = word.trim();
1415             if (word.length() == 0) {
1416                 continue;
1417             }
1418             result.add(word);
1419         }
1420         return result.toArray(new String[result.size()]);
1421     }
1422 
1423     /**
1424      * Reinitialize the logging properties and reread the logging configuration
1425      * from the given stream, which should be in java.util.Properties format.
1426      * Any {@linkplain #addConfigurationListener registered configuration
1427      * listener} will be invoked after the properties are read.
1428      * <p>
1429      * Any log level definitions in the new configuration file will be
1430      * applied using Logger.setLevel(), if the target Logger exists.
1431      *











1432      * @param ins       stream to read properties from
1433      * @exception  SecurityException  if a security manager exists and if
1434      *             the caller does not have LoggingPermission("control").
1435      * @exception  IOException if there are problems reading from the stream.
1436      */
1437     public void readConfiguration(InputStream ins) throws IOException, SecurityException {
1438         checkPermission();
1439 
1440         // We don't want reset() and readConfiguration() to run
1441         // in parallel.
1442         configurationLock.lock();
1443         try {
1444             if (globalHandlersState == STATE_SHUTDOWN) {
1445                 // already in terminal state: don't even bother
1446                 // to read the configuration
1447                 return;
1448             }
1449 
1450             // change state to STATE_READING_CONFIG to signal reset() to not change it
1451             globalHandlersState = STATE_READING_CONFIG;


1489                 // Note that we need to reinitialize global handles when
1490                 // they are first referenced.
1491                 globalHandlersState = STATE_UNINITIALIZED;
1492             } catch (Throwable t) {
1493                 // If there were any trouble, then set state to STATE_INITIALIZED
1494                 // so that no global handlers reinitialization is performed on not fully
1495                 // initialized configuration.
1496                 globalHandlersState = STATE_INITIALIZED;
1497                 // re-throw
1498                 throw t;
1499             }
1500         } finally {
1501             configurationLock.unlock();
1502         }
1503 
1504         // should be called out of lock to avoid dead-lock situations
1505         // when user code is involved
1506         invokeConfigurationListeners();
1507     }
1508 






























































































































































































































































































































































































































































































































































1509     /**
1510      * Get the value of a logging property.
1511      * The method returns null if the property is not found.
1512      * @param name      property name
1513      * @return          property value
1514      */
1515     public String getProperty(String name) {
1516         return props.getProperty(name);
1517     }
1518 
1519     // Package private method to get a String property.
1520     // If the property is not defined we return the given
1521     // default value.
1522     String getStringProperty(String name, String defaultValue) {
1523         String val = getProperty(name);
1524         if (val == null) {
1525             return defaultValue;
1526         }
1527         return val.trim();
1528     }




  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 
  27 package java.util.logging;
  28 
  29 import java.io.*;
  30 import java.util.*;
  31 import java.security.*;
  32 import java.lang.ref.ReferenceQueue;
  33 import java.lang.ref.WeakReference;
  34 import java.util.concurrent.ConcurrentHashMap;
  35 import java.nio.file.Paths;
  36 import java.util.concurrent.CopyOnWriteArrayList;
  37 import java.util.concurrent.locks.ReentrantLock;
  38 import java.util.function.BiFunction;
  39 import java.util.function.BiPredicate;
  40 import java.util.function.Function;
  41 import java.util.function.Predicate;
  42 import java.util.stream.Collectors;
  43 import java.util.stream.Stream;
  44 import sun.misc.JavaAWTAccess;
  45 import sun.misc.ManagedLocalsThread;
  46 import sun.misc.SharedSecrets;
  47 
  48 /**
  49  * There is a single global LogManager object that is used to
  50  * maintain a set of shared state about Loggers and log services.
  51  * <p>
  52  * This LogManager object:
  53  * <ul>
  54  * <li> Manages a hierarchical namespace of Logger objects.  All
  55  *      named Loggers are stored in this namespace.
  56  * <li> Manages a set of logging control properties.  These are
  57  *      simple key-value pairs that can be used by Handlers and
  58  *      other logging objects to configure themselves.
  59  * </ul>
  60  * <p>
  61  * The global LogManager object can be retrieved using LogManager.getLogManager().
  62  * The LogManager object is created during class initialization and
  63  * cannot subsequently be changed.


 778                     return false;
 779                 }
 780             }
 781 
 782             // We're adding a new logger.
 783             // Note that we are creating a weak reference here.
 784             final LogManager owner = getOwner();
 785             logger.setLogManager(owner);
 786             ref = owner.new LoggerWeakRef(logger);
 787 
 788             // Apply any initial level defined for the new logger, unless
 789             // the logger's level is already initialized
 790             Level level = owner.getLevelProperty(name + ".level", null);
 791             if (level != null && !logger.isLevelInitialized()) {
 792                 doSetLevel(logger, level);
 793             }
 794 
 795             // instantiation of the handler is done in the LogManager.addLogger
 796             // implementation as a handler class may be only visible to LogManager
 797             // subclass for the custom log manager case
 798             processParentHandlers(logger, name, VisitedLoggers.of(null));
 799 
 800             // Find the new node and its parent.
 801             LogNode node = getNode(name);
 802             node.loggerRef = ref;
 803             Logger parent = null;
 804             LogNode nodep = node.parent;
 805             while (nodep != null) {
 806                 LoggerWeakRef nodeRef = nodep.loggerRef;
 807                 if (nodeRef != null) {
 808                     parent = nodeRef.get();
 809                     if (parent != null) {
 810                         break;
 811                     }
 812                 }
 813                 nodep = nodep.parent;
 814             }
 815 
 816             if (parent != null) {
 817                 doSetParent(logger, parent);
 818             }


 826             // soon as it is published in namedLoggers (findLogger takes
 827             // benefit of the ConcurrentHashMap implementation of namedLoggers
 828             // to avoid synchronizing on retrieval when that is possible).
 829             namedLoggers.put(name, ref);
 830             return true;
 831         }
 832 
 833         void removeLoggerRef(String name, LoggerWeakRef ref) {
 834             namedLoggers.remove(name, ref);
 835         }
 836 
 837         synchronized Enumeration<String> getLoggerNames() {
 838             // ensure that this context is properly initialized before
 839             // returning logger names.
 840             ensureInitialized();
 841             return Collections.enumeration(namedLoggers.keySet());
 842         }
 843 
 844         // If logger.getUseParentHandlers() returns 'true' and any of the logger's
 845         // parents have levels or handlers defined, make sure they are instantiated.
 846         private void processParentHandlers(final Logger logger, final String name,
 847                 BiPredicate<Logger,String> visited) {
 848             final LogManager owner = getOwner();
 849             AccessController.doPrivileged(new PrivilegedAction<Void>() {
 850                 @Override
 851                 public Void run() {
 852                     if (logger != owner.rootLogger) {
 853                         boolean useParent = owner.getBooleanProperty(name + ".useParentHandlers", true);
 854                         if (!useParent) {
 855                             logger.setUseParentHandlers(false);
 856                         }
 857                     }
 858                     return null;
 859                 }
 860             });
 861 
 862             int ix = 1;
 863             for (;;) {
 864                 int ix2 = name.indexOf('.', ix);
 865                 if (ix2 < 0) {
 866                     break;
 867                 }
 868                 String pname = name.substring(0, ix2);
 869                 if (owner.getProperty(pname + ".level") != null ||
 870                     owner.getProperty(pname + ".handlers") != null) {
 871                     // This pname has a level/handlers definition.
 872                     // Make sure it exists.
 873                     if (visited.test(demandLogger(pname, null, null), pname)) {
 874                         break;
 875                     }
 876                 }
 877                 ix = ix2+1;
 878             }
 879         }
 880 
 881         // Gets a node in our tree of logger nodes.
 882         // If necessary, create it.
 883         LogNode getNode(String name) {
 884             if (name == null || name.equals("")) {
 885                 return root;
 886             }
 887             LogNode node = root;
 888             while (name.length() > 0) {
 889                 int ix = name.indexOf('.');
 890                 String head;
 891                 if (ix > 0) {
 892                     head = name.substring(0, ix);
 893                     name = name.substring(ix + 1);
 894                 } else {
 895                     head = name;


 935                         // the other Logger is not holding a strong reference to
 936                         // the other Logger, then it is possible for the other
 937                         // Logger to be GC'ed after we saw it in addLogger() and
 938                         // before we can refetch it. If it has been GC'ed then
 939                         // we'll just loop around and try again.
 940                         result = findLogger(name);
 941                     }
 942                 } while (result == null);
 943             }
 944             return result;
 945         }
 946     }
 947 
 948     // Add new per logger handlers.
 949     // We need to raise privilege here. All our decisions will
 950     // be made based on the logging configuration, which can
 951     // only be modified by trusted code.
 952     private void loadLoggerHandlers(final Logger logger, final String name,
 953                                     final String handlersPropertyName)
 954     {
 955         AccessController.doPrivileged(new PrivilegedAction<Void>() {
 956             @Override
 957             public Void run() {
 958                 setLoggerHandlers(logger, name, handlersPropertyName,
 959                     createLoggerHandlers(name, handlersPropertyName));
 960                 return null;
 961             }
 962         });
 963     }
 964 
 965     private void setLoggerHandlers(final Logger logger, final String name,
 966                                    final String handlersPropertyName,
 967                                    List<Handler> handlers)
 968     {
 969         final boolean ensureCloseOnReset = ! handlers.isEmpty()
 970                     && getBooleanProperty(handlersPropertyName + ".ensureCloseOnReset",true);
 971         int count = 0;
 972         for (Handler hdl : handlers) {
 973             logger.addHandler(hdl);
 974             if (++count == 1 && ensureCloseOnReset) {
 975                 // add this logger to the closeOnResetLoggers list.
 976                 closeOnResetLoggers.addIfAbsent(CloseOnReset.create(logger));
 977             }
 978         }
 979     }
 980 
 981     private List<Handler> createLoggerHandlers(final String name, final String handlersPropertyName)
 982     {
 983         String names[] = parseClassNames(handlersPropertyName);
 984         List<Handler> handlers = new ArrayList<>(names.length);
 985         for (String type : names) {
 986             try {
 987                 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(type);
 988                 Handler hdl = (Handler) clz.newInstance();
 989                 // Check if there is a property defining the
 990                 // this handler's level.
 991                 String levs = getProperty(type + ".level");
 992                 if (levs != null) {
 993                     Level l = Level.findLevel(levs);
 994                     if (l != null) {
 995                         hdl.setLevel(l);
 996                     } else {
 997                         // Probably a bad level. Drop through.
 998                         System.err.println("Can't set level for " + type);
 999                     }
1000                 }
1001                 // Add this Handler to the logger
1002                 handlers.add(hdl);




1003             } catch (Exception ex) {
1004                 System.err.println("Can't load log handler \"" + type + "\"");
1005                 System.err.println("" + ex);
1006                 ex.printStackTrace();
1007             }
1008         }
1009 
1010         return handlers;


1011     }
1012 
1013 
1014     // loggerRefQueue holds LoggerWeakRef objects for Logger objects
1015     // that have been GC'ed.
1016     private final ReferenceQueue<Logger> loggerRefQueue
1017         = new ReferenceQueue<>();
1018 
1019     // Package-level inner class.
1020     // Helper class for managing WeakReferences to Logger objects.
1021     //
1022     // LogManager.namedLoggers
1023     //     - has weak references to all named Loggers
1024     //     - namedLoggers keeps the LoggerWeakRef objects for the named
1025     //       Loggers around until we can deal with the book keeping for
1026     //       the named Logger that is being GC'ed.
1027     // LogManager.LogNode.loggerRef
1028     //     - has a weak reference to a named Logger
1029     //     - the LogNode will also keep the LoggerWeakRef objects for
1030     //       the named Loggers around; currently LogNodes never go away.


1263      *
1264      * @return  enumeration of logger name strings
1265      */
1266     public Enumeration<String> getLoggerNames() {
1267         return getUserContext().getLoggerNames();
1268     }
1269 
1270     /**
1271      * Reinitialize the logging properties and reread the logging configuration.
1272      * <p>
1273      * The same rules are used for locating the configuration properties
1274      * as are used at startup.  So normally the logging properties will
1275      * be re-read from the same file that was used at startup.
1276      * <P>
1277      * Any log level definitions in the new configuration file will be
1278      * applied using Logger.setLevel(), if the target Logger exists.
1279      * <p>
1280      * Any {@linkplain #addConfigurationListener registered configuration
1281      * listener} will be invoked after the properties are read.
1282      *
1283      * @apiNote {@code readConfiguration} is principally useful for subclasses
1284      *    of LogManager which may override this method to plug in their
1285      *    own primordial custom configuration.
1286      *    <p>
1287      *    Calling this method directly from the application code after the
1288      *    LogManager has been initialized is discouraged.
1289      *
1290      *    Applications that wish to update the LogManager
1291      *    configuration after the LogManager has been initialized should
1292      *    call {@link #updateConfiguration(java.util.function.Function)}
1293      *    instead.
1294      *
1295      * @exception  SecurityException  if a security manager exists and if
1296      *             the caller does not have LoggingPermission("control").
1297      * @exception  IOException if there are IO problems reading the configuration.
1298      */
1299     public void readConfiguration() throws IOException, SecurityException {
1300         checkPermission();
1301 
1302         // if a configuration class is specified, load it and use it.
1303         String cname = System.getProperty("java.util.logging.config.class");
1304         if (cname != null) {
1305             try {
1306                 // Instantiate the named class.  It is its constructor's
1307                 // responsibility to initialize the logging configuration, by
1308                 // calling readConfiguration(InputStream) with a suitable stream.
1309                 try {
1310                     Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname);
1311                     clz.newInstance();
1312                     return;
1313                 } catch (ClassNotFoundException ex) {
1314                     Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
1315                     clz.newInstance();
1316                     return;
1317                 }
1318             } catch (Exception ex) {
1319                 System.err.println("Logging configuration class \"" + cname + "\" failed");
1320                 System.err.println("" + ex);
1321                 // keep going and useful config file.
1322             }
1323         }
1324 
1325         String fname = getConfigurationFileName();
1326         try (final InputStream in = new FileInputStream(fname)) {
1327             final BufferedInputStream bin = new BufferedInputStream(in);
1328             readConfiguration(bin);
1329         }
1330     }
1331 
1332     String getConfigurationFileName() throws IOException {
1333         String fname = System.getProperty("java.util.logging.config.file");
1334         if (fname == null) {
1335             fname = System.getProperty("java.home");
1336             if (fname == null) {
1337                 throw new Error("Can't find java.home ??");
1338             }
1339             fname = Paths.get(fname, "conf", "logging.properties")
1340                     .toAbsolutePath().normalize().toString();





1341         }
1342         return fname;
1343     }
1344 
1345     /**
1346      * Reset the logging configuration.
1347      * <p>
1348      * For all named loggers, the reset operation removes and closes
1349      * all Handlers and (except for the root logger) sets the level
1350      * to null.  The root logger's level is set to Level.INFO.
1351      *
1352      * @exception  SecurityException  if a security manager exists and if
1353      *             the caller does not have LoggingPermission("control").
1354      */
1355 
1356     public void reset() throws SecurityException {
1357         checkPermission();
1358 
1359         List<CloseOnReset> persistent;
1360 
1361         // We don't want reset() and readConfiguration()
1362         // to run in parallel


1454             String word = hands.substring(ix, end);
1455             ix = end+1;
1456             word = word.trim();
1457             if (word.length() == 0) {
1458                 continue;
1459             }
1460             result.add(word);
1461         }
1462         return result.toArray(new String[result.size()]);
1463     }
1464 
1465     /**
1466      * Reinitialize the logging properties and reread the logging configuration
1467      * from the given stream, which should be in java.util.Properties format.
1468      * Any {@linkplain #addConfigurationListener registered configuration
1469      * listener} will be invoked after the properties are read.
1470      * <p>
1471      * Any log level definitions in the new configuration file will be
1472      * applied using Logger.setLevel(), if the target Logger exists.
1473      *
1474      * @apiNote {@code readConfiguration} is principally useful for subclasses
1475      *    of LogManager which may override this method to plug in their
1476      *    own primordial custom configuration.
1477      *    <p>
1478      *    Calling this method directly from the application code after the
1479      *    LogManager has been initialized is discouraged.
1480      *    Applications that wish to update the LogManager configuration after
1481      *    the LogManager has been initialized should call
1482      *    {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)}
1483      *    instead.
1484      *
1485      * @param ins       stream to read properties from
1486      * @exception  SecurityException  if a security manager exists and if
1487      *             the caller does not have LoggingPermission("control").
1488      * @exception  IOException if there are problems reading from the stream.
1489      */
1490     public void readConfiguration(InputStream ins) throws IOException, SecurityException {
1491         checkPermission();
1492 
1493         // We don't want reset() and readConfiguration() to run
1494         // in parallel.
1495         configurationLock.lock();
1496         try {
1497             if (globalHandlersState == STATE_SHUTDOWN) {
1498                 // already in terminal state: don't even bother
1499                 // to read the configuration
1500                 return;
1501             }
1502 
1503             // change state to STATE_READING_CONFIG to signal reset() to not change it
1504             globalHandlersState = STATE_READING_CONFIG;


1542                 // Note that we need to reinitialize global handles when
1543                 // they are first referenced.
1544                 globalHandlersState = STATE_UNINITIALIZED;
1545             } catch (Throwable t) {
1546                 // If there were any trouble, then set state to STATE_INITIALIZED
1547                 // so that no global handlers reinitialization is performed on not fully
1548                 // initialized configuration.
1549                 globalHandlersState = STATE_INITIALIZED;
1550                 // re-throw
1551                 throw t;
1552             }
1553         } finally {
1554             configurationLock.unlock();
1555         }
1556 
1557         // should be called out of lock to avoid dead-lock situations
1558         // when user code is involved
1559         invokeConfigurationListeners();
1560     }
1561 
1562     // This enum enumerate the configuration properties that will be
1563     // re-established on existing loggers when the configuration is updated
1564     // with LogManager.updateConfiguration().
1565     //
1566     // Note that this works properly only for the global LogManager - as
1567     // Handler and its subclasses get their configuration from
1568     // LogManager.getLogManager().
1569     //
1570     static enum ConfigurationProperties implements Predicate<String> {
1571         LEVEL(".level"), HANDLERS(".handlers"), USEPARENT(".useParentHandlers");
1572         final String suffix;
1573         final int length;
1574         private ConfigurationProperties(String suffix) {
1575             this.suffix = Objects.requireNonNull(suffix);
1576             length = suffix.length();
1577         }
1578         @Override
1579         public boolean test(String key) {
1580             if (this == HANDLERS && suffix.substring(1).equals(key)) return true;
1581             if (this == HANDLERS && suffix.equals(key)) return false;
1582             return key.endsWith(suffix);
1583         }
1584         String key(String loggerName) {
1585             if (this == HANDLERS && (loggerName == null || loggerName.isEmpty())) {
1586                 return suffix.substring(1);
1587             }
1588             return loggerName + suffix;
1589         }
1590         String loggerName(String key) {
1591             assert key.equals(suffix.substring(1)) && this == HANDLERS || key.endsWith(suffix);
1592             if (this == HANDLERS && suffix.substring(1).equals(key)) return "";
1593             return key.substring(0, key.length() - length);
1594         }
1595 
1596         /**
1597          * If the property is one that should be reestablished by
1598          * updateConfiguration, returns the name of the logger for which the
1599          * property is configured. Otherwise, returns null.
1600          * @param property a property key in 'props'
1601          * @return the name of the logger on which the property is to be set,
1602          *         if the property is one that we should reestablish.
1603          *         {@code null} otherwise.
1604          */
1605         static String getLoggerName(String property) {
1606             for (ConfigurationProperties p : ConfigurationProperties.ALL) {
1607                 if (p.test(property)) {
1608                     return p.loggerName(property);
1609                 }
1610             }
1611             return null; // Not a property that should be reestablished.
1612         }
1613 
1614         /**
1615          * If the property is one that should be reestablished by
1616          * updateConfiguration, returns the corresponding
1617          * ConfigurationProperties object. Otherwise, returns null.
1618          * @param property a property key in 'props'
1619          * @return the corresponding ConfigurationProperties object,
1620          *         if the property is one that we should reestablish.
1621          *         {@code null} otherwise.
1622          */
1623         static ConfigurationProperties of(String property) {
1624             for (ConfigurationProperties p : ConfigurationProperties.ALL) {
1625                 if (p.test(property)) {
1626                     return p;
1627                 }
1628             }
1629             return null; // Not a property that should be reestablished.
1630         }
1631 
1632         /**
1633          * Returns true if the given property is one that should be reestablished.
1634          * Used to filter property name streams.
1635          * @param property a property key from the configuration.
1636          * @return true if this property is of interest for updateConfiguration.
1637          */
1638         static boolean isOf(String property) {
1639             return of(property) != null;
1640         }
1641 
1642         /**
1643          * Returns true if the new property value is different from the old.
1644          * @param k a property key in the configuration
1645          * @param previous the old configuration
1646          * @param next the new configuration
1647          * @return true if the property is changing value between the two
1648          *         configurations.
1649          */
1650         static boolean isChanging(String k, Properties previous, Properties next) {
1651             final String p = trim(previous.getProperty(k, null));
1652             final String n = trim(next.getProperty(k, null));
1653             return ! Objects.equals(p,n);
1654         }
1655 
1656         /**
1657          * Applies the remapping function for the given key to the next
1658          * configuration.
1659          * If the remapping function is null then this method does nothing.
1660          * Otherwise, it calls the remapping function to compute the value
1661          * that should be associated with {@code key} in the resulting
1662          * configuration, and applies it to {@code next}.
1663          * If the remapping function returns {@code null} the key is removed
1664          * from {@code next}.
1665          *
1666          * @param k a property key in the configuration
1667          * @param previous the old configuration
1668          * @param next the new configuration (modified by this function)
1669          * @param remappingFunction the remapping function.
1670          */
1671         static void merge(String k, Properties previous, Properties next,
1672                           BiFunction<String, String, String> remappingFunction) {
1673             if (remappingFunction != null) {
1674                 String p = trim(previous.getProperty(k, null));
1675                 String n = trim(next.getProperty(k, null));
1676                 String mapped = trim(remappingFunction.apply(p,n));
1677                 if (!Objects.equals(n, mapped)) {
1678                     if (mapped == null) {
1679                         next.remove(k);
1680                     } else {
1681                         next.setProperty(k, mapped);
1682                     }
1683                 }
1684             }
1685         }
1686 
1687         private static final EnumSet<ConfigurationProperties> ALL =
1688                 EnumSet.allOf(ConfigurationProperties.class);
1689     }
1690 
1691     // trim the value if not null.
1692     private static String trim(String value) {
1693         return value == null ? null : value.trim();
1694     }
1695 
1696     /**
1697      * An object that keep track of loggers we have already visited.
1698      * Used when updating configuration, to avoid processing the same logger
1699      * twice.
1700      */
1701     static final class VisitedLoggers implements BiPredicate<Logger, String> {
1702         final Map<Logger,String> visited;
1703         private VisitedLoggers(Map<Logger,String> visited) {
1704             this.visited = visited;
1705         }
1706         @Override
1707         public boolean test(Logger logger, String name) {
1708             return visited != null && visited.put(logger, name) != null;
1709         }
1710         public void clear() {
1711             if (visited != null) visited.clear();
1712         }
1713 
1714         public static VisitedLoggers of(Map<Logger, String> visited) {
1715             assert visited == null || visited instanceof IdentityHashMap;
1716             return visited == null ? NEVER : new VisitedLoggers(visited);
1717         }
1718 
1719         // An object that considers that no logger has ever been visited.
1720         // This is used when processParentHandlers is called from
1721         // LoggerContext.addLocalLogger
1722         static final VisitedLoggers NEVER = new VisitedLoggers(null);
1723     }
1724 
1725 
1726     /**
1727      * Type of the modification for a given property. One of ADDED, CHANGED,
1728      * or REMOVED.
1729      */
1730     static enum ModType {
1731         ADDED,   // property had no value in the old conf, but has one in the new.
1732         CHANGED, // property has a different value in the old conf and the new conf.
1733         REMOVED; // property has no value in the new conf, but had one in the old.
1734         static ModType of(String previous, String next) {
1735             if (previous == null && next != null) {
1736                 return ADDED;
1737             }
1738             if (next == null && previous != null) {
1739                 return REMOVED;
1740             }
1741             if (!Objects.equals(trim(previous), trim(next))) {
1742                 return CHANGED;
1743             }
1744             return null;
1745         }
1746     }
1747 
1748     /**
1749      * Updates an existing configuration.
1750      * <p>
1751      * @implSpec
1752      * This is equivalent to calling:
1753      * <pre>
1754      *   try (final InputStream in = new FileInputStream(&lt;logging.properties&gt;)) {
1755      *       final BufferedInputStream bin = new BufferedInputStream(in);
1756      *       updateConfiguration(bin, remapper);
1757      *   }
1758      * </pre>
1759      * where {@code <logging.properties>} is the logging configuration file path.
1760      *
1761      * @param remapper Used to control how the old configuration and
1762      *   candidate configuration will be mapped into the new configuration.
1763      *   See {@link
1764      *   #updateConfiguration(java.io.InputStream, java.util.function.Function)}
1765      *   for more details.
1766      *
1767      * @exception  SecurityException  if a security manager exists and if
1768      *             the caller does not have LoggingPermission("control"), or
1769      *             does not have the permissions required to set up the
1770      *             configuration (e.g. open file specified for FileHandlers
1771      *             etc...)
1772      *
1773      * @exception  IOException if there are problems reading from the
1774      *             logging configuration file.
1775      */
1776     public void updateConfiguration(Function<String, BiFunction<String,String,String>> remapper)
1777             throws IOException {
1778         checkPermission();
1779         String fname = getConfigurationFileName();
1780         try (final InputStream in = new FileInputStream(fname)) {
1781             final BufferedInputStream bin = new BufferedInputStream(in);
1782             updateConfiguration(bin, remapper);
1783         }
1784     }
1785 
1786     /**
1787      * Update the logging configuration.  The given {@code remapper} function
1788      * is invoked for each configuration key in order to produce a resulting
1789      * logging configuration to to be applied. The result of applying that new
1790      * configuration is detailed in the table below.
1791      * The registered {@linkplain #addConfigurationListener configuration
1792      * listener} will be invoked after the configuration is successfully updated.
1793      * <br>
1794      * <table>
1795      *   <caption>Updating configuration properties</caption>
1796      * <tr>
1797      * <th>Property</th>
1798      * <th>Resulting Behavior</th>
1799      * </tr>
1800      * <tr>
1801      * <td valign="top">{@code <logger>.level}</td>
1802      * <td>
1803      * <ul>
1804      *   <li>If the new configuration defines a level for a logger and
1805      *       if the new level is different than the level specified in the
1806      *       the old configuration, or not specified in
1807      *       the old configuration, then the level for that logger will be
1808      *       updated, and the change propagated to the existing logger children,
1809      *       if necessary.
1810      *   </li>
1811      *   <li>If the old configuration defined a level for a logger, and the new
1812      *       configuration doesn't, then the logger level remains unchanged.
1813      *       To completely replace a configuration - the caller should therefore
1814      *       call {@link #reset() reset} to empty the current configuration, before
1815      *       calling {@code updateConfiguration}.
1816      *   </li>
1817      * </ul>
1818      * </td>
1819      * <tr>
1820      * <td valign="top">{@code <logger>.useParentHandlers}</td>
1821      * <td>
1822      * <ul>
1823      *   <li>If either the new or the old value for the useParentHandlers property
1824      *       is not null, then if the logger exists or if children for
1825      *       that logger exist, that logger will be updated to the new value.
1826      *       The value of the useParentHandlers property is the value specified
1827      *       in the configuration; if not specified, the default is true.
1828      *   </li>
1829      * </ul>
1830      * </td>
1831      * </tr>
1832      * <tr>
1833      * <td valign="top">{@code <logger>.handlers}</td>
1834      * <td>
1835      * <ul>
1836      *   <li>If the new configuration defines a list of handlers for a logger,
1837      *       and if the new list is different than the list specified in the old
1838      *       configuration for that logger (that could be empty), if the logger
1839      *       exists or whose children exist, the handlers associated with that logger
1840      *       are closed and removed and the new handlers will be created per the new
1841      *       configuration and added to that logger.
1842      *   </li>
1843      *   <li>If the old configuration defined some handlers for a logger, and
1844      *       the new configuration doesn't, if that logger exists,
1845      *       its handlers will be removed and closed.
1846      *   </li>
1847      *   <li>Changing the list of handlers on an existing logger will cause all
1848      *       its previous handlers to be removed and closed, regardless of whether
1849      *       they had been created from the configuration or programmatically.
1850      *       The old handlers will be replaced by new handlers, if any.
1851      *   </li>
1852      * </ul>
1853      * </td>
1854      * </tr>
1855      * <tr>
1856      * <td valign="top">{@code <handler-name>.*}</td>
1857      * <td>
1858      * <ul>
1859      *   <li>Properties configured/changed on handler classes will only affect
1860      *       newly created handlers. If a node is configured with the same list
1861      *       of handlers in the old and the new configuration, then these handlers
1862      *       will remain unchanged.
1863      *   </li>
1864      * </ul>
1865      * </td>
1866      * </tr>
1867      * </table>
1868      * <p>
1869      * To completely reestablish a configuration, an application can first call
1870      * {@link #reset() reset} to fully remove the old configuration, followed by
1871      * {@code updateConfiguration} to establish the new configuration.
1872      *
1873      * @param ins      stream to read properties from
1874      * @param remapper a functional interface that takes a configuration
1875      *            key and returns a remapping function for values associated
1876      *            with that key. The remapping function takes the old value
1877      *            and the candidate new value as parameters and produces the
1878      *            actual value to be applied. If the {@code remapper} is null,
1879      *            or if the remapping function it returns is null, then the
1880      *            candidate value will be the new value.
1881      *            A {@code null} value passed as parameter to the remapping
1882      *            function indicates that no value was present in the
1883      *            corresponding configuration.
1884      *            <p>
1885      *            Examples of {@code remapper} are:
1886      *            <ul><li>{@code (k) -> ((o, n) -> n)}: always take the new value.
1887      *                    equivalent to passing null for {@code remapper}</li>
1888      *                <li>{@code (k) -> ((o, n) -> n == null ? o : n)}: keep the
1889      *                    old value if the candidate configuration has no value
1890      *                    for that key, otherwise take the new value.
1891      *                    This is equivalent to adding the new
1892      *                    configuration to the old, letting new values override
1893      *                    old values, as what would be obtained with
1894      *                    {@code result.putAll(old); result.putAll(candidate)}.
1895      *                </li>
1896      *                <li>{@code (k) -> ((o, n) -> o == null ? n : o)}: only
1897      *                    take the new value if the old configuration didn't
1898      *                    have a value for that key.
1899      *                    This is equivalent to appending the new
1900      *                    configuration to the old, without letting new values
1901      *                    override old values, as what would be obtained with
1902      *                    {@code result.putAll(candidate); result.putAll(old)}.
1903      *                </li>
1904      *                <li>
1905      * <pre>{@code (k) -> k.endsWith(".handlers")}
1906      *      {@code     ? ((o, n) -> (o == null ? n : o))}
1907      *      {@code     : ((o, n) -> n)}</pre> do not let the new configuration
1908      *                 override existing per logger handlers. Only root handlers
1909      *                 can be overridden.</li>
1910      *            </ul>
1911      * @throws  SecurityException if a security manager exists and if
1912      *          the caller does not have LoggingPermission("control"), or
1913      *          does not have the permissions required to set up the
1914      *          configuration (e.g. open files specified for FileHandlers)
1915      *
1916      * @exception  IOException if there are problems reading from the
1917      *             logging configuration file.
1918      */
1919     public void updateConfiguration(InputStream ins,
1920             Function<String, BiFunction<String,String,String>> remapper)
1921             throws IOException {
1922         checkPermission();
1923         ensureLogManagerInitialized();
1924         drainLoggerRefQueueBounded();
1925 
1926         final Properties previous;
1927         final Set<String> updatePropertyNames;
1928         Properties next = new Properties();
1929         next.load(ins);
1930         List<LoggerContext> cxs = Collections.emptyList();
1931         final VisitedLoggers visited = VisitedLoggers.of(new IdentityHashMap<>());
1932 
1933         if (globalHandlersState == STATE_SHUTDOWN) return;
1934         
1935         // exclusive lock: readConfiguration/reset/updateConfiguration can't
1936         //           run concurrently.
1937         // configurationLock.writeLock().lock();
1938         configurationLock.lock();
1939         try {
1940             if (globalHandlersState == STATE_SHUTDOWN) return;
1941             previous = props;
1942 
1943             // Builds a TreeSet of all (old and new) property names.
1944             updatePropertyNames =
1945                     Stream.concat(previous.stringPropertyNames().stream(),
1946                                   next.stringPropertyNames().stream())
1947                         .collect(Collectors.toCollection(TreeSet::new));
1948 
1949             if (remapper != null) {
1950                 // remapper will potentially modify the content of
1951                 // 'next', so we need to call it before affecting props=next.
1952                 // give a chance to the remapper to control all
1953                 // properties - not just those we will reset.
1954                 updatePropertyNames.stream()
1955                         .forEachOrdered(k -> ConfigurationProperties
1956                                 .merge(k, previous, next, remapper.apply(k)));
1957             }
1958 
1959             props = next;
1960 
1961             // allKeys will contain all keys:
1962             //    - which correspond to a configuration property we are interested in
1963             //      (first filter)
1964             //    - whose value is changing (new, removed, different) in the configuration
1965             //      (second filter)
1966             final Stream<String> allKeys = updatePropertyNames.stream()
1967                     .filter(ConfigurationProperties::isOf)
1968                     .filter(k -> ConfigurationProperties.isChanging(k, previous, next));
1969 
1970             // Group configuration properties by logger name
1971             // We use a TreeMap so that parent loggers will be visited before
1972             // child loggers.
1973             final Map<String, TreeSet<String>> loggerConfigs =
1974                     allKeys.collect(
1975                             Collectors.groupingBy(
1976                                     ConfigurationProperties::getLoggerName,
1977                                     TreeMap::new,
1978                                     Collectors.toCollection(TreeSet::new)));
1979 
1980             if (!loggerConfigs.isEmpty()) {
1981                 cxs = contexts();
1982             }
1983             final List<Logger> tmp = cxs.isEmpty()
1984                     ? Collections.emptyList() : new ArrayList<>(cxs.size());
1985             for (Map.Entry<String, TreeSet<String>> e : loggerConfigs.entrySet()) {
1986                 // This can be a logger name, or something else...
1987                 // The only thing we know is that we found a property
1988                 //    we are interested in.
1989                 // For instance, if we found x.y.z.level, then x.y.z could be
1990                 // a logger, but it could also be a handler class...
1991                 // Anyway...
1992                 final String name = e.getKey();
1993                 final Set<String> properties = e.getValue();
1994                 tmp.clear();
1995                 for (LoggerContext cx : cxs) {
1996                     Logger l = cx.findLogger(name);
1997                     if (l == null) continue;
1998                     if (!visited.test(l, name)) {
1999                         tmp.add(l);
2000                     }
2001                 }
2002                 if (tmp.isEmpty()) continue;
2003                 for (String pk : properties) {
2004                     ConfigurationProperties cp = ConfigurationProperties.of(pk);
2005                     String p = previous.getProperty(pk, null);
2006                     String n = next.getProperty(pk, null);
2007 
2008                     // Determines the type of modification.
2009                     ModType mod = ModType.of(p, n);
2010 
2011                     // mod == null means that the two values are equals, there
2012                     // is nothing to do. Usually, this should not happen as such
2013                     // properties should have been filtered above.
2014                     // It could happen however if the properties had
2015                     // trailing/leading whitespaces.
2016                     if (mod == null) continue;
2017 
2018                     switch (cp) {
2019                         case LEVEL:
2020                             if (mod == ModType.REMOVED) continue;
2021                             Level level = Level.findLevel(trim(n));
2022                             if (level == null) {
2023                                 if (name.isEmpty()) {
2024                                     rootLogger.setLevel(level);
2025                                 }
2026                                 for (Logger l : tmp) {
2027                                     if (!name.isEmpty() || l != rootLogger) {
2028                                         l.setLevel(level);
2029                                     }
2030                                 }
2031                             }
2032                             break;
2033                         case USEPARENT:
2034                             if (!name.isEmpty()) {
2035                                 boolean useParent = getBooleanProperty(pk, true);
2036                                 if (n != null || p != null) {
2037                                     // reset the flag only if the previous value
2038                                     // or the new value are not null.
2039                                    for (Logger l : tmp) {
2040                                         l.setUseParentHandlers(useParent);
2041                                     }
2042                                 }
2043                             }
2044                             break;
2045                         case HANDLERS:
2046                             List<Handler> hdls = null;
2047                             if (name.isEmpty()) {
2048                                 // special handling for the root logger.
2049                                 globalHandlersState = STATE_READING_CONFIG;
2050                                 try {
2051                                     closeHandlers(rootLogger);
2052                                     globalHandlersState = STATE_UNINITIALIZED;
2053                                 } catch (Throwable t) {
2054                                     globalHandlersState = STATE_INITIALIZED;
2055                                     throw t;
2056                                 }
2057                             }
2058                             for (Logger l : tmp) {
2059                                 if (l == rootLogger) continue;
2060                                 closeHandlers(l);
2061                                 if (mod == ModType.REMOVED) {
2062                                     closeOnResetLoggers.removeIf(c -> c.logger == l);
2063                                     continue;
2064                                 }
2065                                 if (hdls == null) {
2066                                     hdls = name.isEmpty()
2067                                             ? Arrays.asList(rootLogger.getHandlers())
2068                                             : createLoggerHandlers(name, pk);
2069                                 }
2070                                 setLoggerHandlers(l, name, pk, hdls);
2071                             }
2072                             break;
2073                         default: break;
2074                     }
2075                 }
2076             }
2077         } finally {
2078             configurationLock.unlock();
2079             visited.clear();
2080         }
2081 
2082         // Now ensure that if an existing logger has acquired a new parent
2083         // in the configuration, this new parent will be created - if needed,
2084         // and added to the context of the existing child.
2085         //
2086         drainLoggerRefQueueBounded();
2087         for (LoggerContext cx : cxs) {
2088             for (Enumeration<String> names = cx.getLoggerNames() ; names.hasMoreElements();) {
2089                 String name = names.nextElement();
2090                 if (name.isEmpty()) continue;  // don't need to process parents on root.
2091                 Logger l = cx.findLogger(name);
2092                 if (l != null && !visited.test(l, name)) {
2093                     // should pass visited here to cut the processing when
2094                     // reaching a logger already visited.
2095                     cx.processParentHandlers(l, name, visited);
2096                 }
2097             }
2098         }
2099 
2100         // We changed the configuration: invoke configuration listeners
2101         invokeConfigurationListeners();
2102     }
2103 
2104     /**
2105      * Get the value of a logging property.
2106      * The method returns null if the property is not found.
2107      * @param name      property name
2108      * @return          property value
2109      */
2110     public String getProperty(String name) {
2111         return props.getProperty(name);
2112     }
2113 
2114     // Package private method to get a String property.
2115     // If the property is not defined we return the given
2116     // default value.
2117     String getStringProperty(String name, String defaultValue) {
2118         String val = getProperty(name);
2119         if (val == null) {
2120             return defaultValue;
2121         }
2122         return val.trim();
2123     }


< prev index next >