< 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.
  57  * <p>
  58  * At startup the LogManager class is located using the
  59  * java.util.logging.manager system property.








  60  * <p>
  61  * The LogManager defines two optional system properties that allow control over
  62  * the initial configuration:

  63  * <ul>
  64  * <li>"java.util.logging.config.class"
  65  * <li>"java.util.logging.config.file"
  66  * </ul>
  67  * These two properties may be specified on the command line to the "java"
  68  * command, or as system property definitions passed to JNI_CreateJavaVM.
  69  * <p>
  70  * If the "java.util.logging.config.class" property is set, then the
  71  * property value is treated as a class name.  The given class will be
  72  * loaded, an object will be instantiated, and that object's constructor
  73  * is responsible for reading in the initial configuration.  (That object
  74  * may use other system properties to control its configuration.)  The
  75  * alternate configuration class can use {@code readConfiguration(InputStream)}
  76  * to define properties in the LogManager.
  77  * <p>
  78  * If "java.util.logging.config.class" property is <b>not</b> set,
  79  * then the "java.util.logging.config.file" system property can be used
  80  * to specify a properties file (in java.util.Properties format). The
  81  * initial logging configuration will be read from this file.
  82  * <p>
  83  * If neither of these properties is defined then the LogManager uses its
  84  * default configuration. The default configuration is typically loaded from the
  85  * properties file "{@code conf/logging.properties}" in the Java installation
  86  * directory.
  87  * <p>
  88  * The properties for loggers and Handlers will have names starting
  89  * with the dot-separated name for the handler or logger.
  90  * <p>


  91  * The global logging properties may include:
  92  * <ul>
  93  * <li>A property "handlers".  This defines a whitespace or comma separated
  94  * list of class names for handler classes to load and register as
  95  * handlers on the root Logger (the Logger named "").  Each class
  96  * name must be for a Handler class which has a default constructor.
  97  * Note that these Handlers may be created lazily, when they are
  98  * first used.
  99  *
 100  * <li>A property "&lt;logger&gt;.handlers". This defines a whitespace or
 101  * comma separated list of class names for handlers classes to
 102  * load and register as handlers to the specified logger. Each class
 103  * name must be for a Handler class which has a default constructor.
 104  * Note that these Handlers may be created lazily, when they are
 105  * first used.
 106  *
 107  * <li>A property "&lt;logger&gt;.handlers.ensureCloseOnReset". This defines a
 108  * a boolean value. If "&lt;logger&gt;.handlers" is not defined or is empty,
 109  * this property is ignored. Otherwise it defaults to {@code true}. When the
 110  * value is {@code true}, the handlers associated with the logger are guaranteed


 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.


1225      * Get an enumeration of known logger names.
1226      * <p>
1227      * Note:  Loggers may be added dynamically as new classes are loaded.
1228      * This method only reports on the loggers that are currently registered.
1229      * It is also important to note that this method only returns the name
1230      * of a Logger, not a strong reference to the Logger itself.
1231      * The returned String does nothing to prevent the Logger from being
1232      * garbage collected. In particular, if the returned name is passed
1233      * to {@code LogManager.getLogger()}, then the caller must check the
1234      * return value from {@code LogManager.getLogger()} for null to properly
1235      * handle the case where the Logger has been garbage collected in the
1236      * time since its name was returned by this method.
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
1321         configurationLock.lock();
1322         try {
1323             // install new empty properties
1324             props = new Properties();
1325             // make sure we keep the loggers persistent until reset is done.
1326             // Those are the loggers for which we previously created a
1327             // handler from the configuration, and we need to prevent them
1328             // from being gc'ed until those handlers are closed.


1404                 if (Character.isWhitespace(hands.charAt(end))) {
1405                     break;
1406                 }
1407                 if (hands.charAt(end) == ',') {
1408                     break;
1409                 }
1410                 end++;
1411             }
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


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.Function;
  40 import java.util.function.Predicate;
  41 import java.util.function.Supplier;
  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.
  64  * <p>
  65  * At startup the LogManager class is located using the
  66  * java.util.logging.manager system property.
  67  *
  68  * <h3>LogManager Configuration</h3>
  69  *
  70  * A LogManager initializes the logging configuration via
  71  * the {@link #readConfiguration()} method during LogManager initialization.
  72  * By default, LogManager default configuration is used.
  73  * The logging configuration read by LogManager must be in the
  74  * {@linkplain Properties properties file} format.
  75  * <p>
  76  * The LogManager defines two optional system properties that allow control over
  77  * the initial configuration, as specified in the {@link #readConfiguration()}
  78  * method:
  79  * <ul>
  80  * <li>"java.util.logging.config.class"
  81  * <li>"java.util.logging.config.file"
  82  * </ul>




















  83  * <p>
  84  * These two system properties may be specified on the command line to the "java"
  85  * command, or as system property definitions passed to JNI_CreateJavaVM.
  86  * <p>
  87  * The {@linkplain Properties properties} for loggers and Handlers will have
  88  * names starting with the dot-separated name for the handler or logger.<br>
  89  * The global logging properties may include:
  90  * <ul>
  91  * <li>A property "handlers".  This defines a whitespace or comma separated
  92  * list of class names for handler classes to load and register as
  93  * handlers on the root Logger (the Logger named "").  Each class
  94  * name must be for a Handler class which has a default constructor.
  95  * Note that these Handlers may be created lazily, when they are
  96  * first used.
  97  *
  98  * <li>A property "&lt;logger&gt;.handlers". This defines a whitespace or
  99  * comma separated list of class names for handlers classes to
 100  * load and register as handlers to the specified logger. Each class
 101  * name must be for a Handler class which has a default constructor.
 102  * Note that these Handlers may be created lazily, when they are
 103  * first used.
 104  *
 105  * <li>A property "&lt;logger&gt;.handlers.ensureCloseOnReset". This defines a
 106  * a boolean value. If "&lt;logger&gt;.handlers" is not defined or is empty,
 107  * this property is ignored. Otherwise it defaults to {@code true}. When the
 108  * value is {@code true}, the handlers associated with the logger are guaranteed


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


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


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




 994             } catch (Exception ex) {
 995                 System.err.println("Can't load log handler \"" + type + "\"");
 996                 System.err.println("" + ex);
 997                 ex.printStackTrace();
 998             }
 999         }
1000 
1001         return handlers;


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


1242      * Get an enumeration of known logger names.
1243      * <p>
1244      * Note:  Loggers may be added dynamically as new classes are loaded.
1245      * This method only reports on the loggers that are currently registered.
1246      * It is also important to note that this method only returns the name
1247      * of a Logger, not a strong reference to the Logger itself.
1248      * The returned String does nothing to prevent the Logger from being
1249      * garbage collected. In particular, if the returned name is passed
1250      * to {@code LogManager.getLogger()}, then the caller must check the
1251      * return value from {@code LogManager.getLogger()} for null to properly
1252      * handle the case where the Logger has been garbage collected in the
1253      * time since its name was returned by this method.
1254      *
1255      * @return  enumeration of logger name strings
1256      */
1257     public Enumeration<String> getLoggerNames() {
1258         return getUserContext().getLoggerNames();
1259     }
1260 
1261     /**
1262      * Reads and initializes the logging configuration.
1263      * <p>
1264      * If the "java.util.logging.config.class" system property is set, then the
1265      * property value is treated as a class name.  The given class will be
1266      * loaded, an object will be instantiated, and that object's constructor
1267      * is responsible for reading in the initial configuration.  (That object
1268      * may use other system properties to control its configuration.)  The
1269      * alternate configuration class can use {@code readConfiguration(InputStream)}
1270      * to define properties in the LogManager.
1271      * <p>
1272      * If "java.util.logging.config.class" system property is <b>not</b> set,
1273      * then this method will read the initial configuration from a properties
1274      * file and calls the {@link #readConfiguration(InputStream)} method to initialize
1275      * the configuration. The "java.util.logging.config.file" system property can be used
1276      * to specify the properties file that will be read as the initial configuration;
1277      * if not set, then the LogManager default configuration is used.
1278      * The default configuration is typically loaded from the
1279      * properties file "{@code conf/logging.properties}" in the Java installation
1280      * directory.
1281      *
1282      * <p>
1283      * Any {@linkplain #addConfigurationListener registered configuration
1284      * listener} will be invoked after the properties are read.
1285      *
1286      * @apiNote This {@code readConfiguration} method should only be used for
1287      * initializing the configuration during LogManager initialization or
1288      * used with the "java.util.logging.config.class" property.
1289      * When this method is called after loggers have been created, and
1290      * the "java.util.logging.config.class" system property is not set, all
1291      * existing loggers will be {@linkplain #reset() reset}. Then any
1292      * existing loggers that have a level property specified in the new
1293      * configuration stream will be {@linkplain
1294      * Logger#setLevel(java.util.logging.Level) set} to the specified log level.
1295      * <p>
1296      * To properly update the logging configuration, use the
1297      * {@link #updateConfiguration(java.util.function.Function)} or
1298      * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)}
1299      * methods instead.
1300      *
1301      * @exception   SecurityException  if a security manager exists and if
1302      *              the caller does not have LoggingPermission("control").
1303      * @exception   IOException if there are IO problems reading the configuration.
1304      */
1305     public void readConfiguration() throws IOException, SecurityException {
1306         checkPermission();
1307 
1308         // if a configuration class is specified, load it and use it.
1309         String cname = System.getProperty("java.util.logging.config.class");
1310         if (cname != null) {
1311             try {
1312                 // Instantiate the named class.  It is its constructor's
1313                 // responsibility to initialize the logging configuration, by
1314                 // calling readConfiguration(InputStream) with a suitable stream.
1315                 try {
1316                     Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname);
1317                     clz.newInstance();
1318                     return;
1319                 } catch (ClassNotFoundException ex) {
1320                     Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
1321                     clz.newInstance();
1322                     return;
1323                 }
1324             } catch (Exception ex) {
1325                 System.err.println("Logging configuration class \"" + cname + "\" failed");
1326                 System.err.println("" + ex);
1327                 // keep going and useful config file.
1328             }
1329         }
1330 
1331         String fname = getConfigurationFileName();
1332         try (final InputStream in = new FileInputStream(fname)) {
1333             final BufferedInputStream bin = new BufferedInputStream(in);
1334             readConfiguration(bin);
1335         }
1336     }
1337 
1338     String getConfigurationFileName() throws IOException {
1339         String fname = System.getProperty("java.util.logging.config.file");
1340         if (fname == null) {
1341             fname = System.getProperty("java.home");
1342             if (fname == null) {
1343                 throw new Error("Can't find java.home ??");
1344             }
1345             fname = Paths.get(fname, "conf", "logging.properties")
1346                     .toAbsolutePath().normalize().toString();





1347         }
1348         return fname;
1349     }
1350 
1351     /**
1352      * Reset the logging configuration.
1353      * <p>
1354      * For all named loggers, the reset operation removes and closes
1355      * all Handlers and (except for the root logger) sets the level
1356      * to null. The root logger's level is set to Level.INFO, and
1357      * its handlers will be recreated lazily when they are first used.
1358      *
1359      * @exception  SecurityException  if a security manager exists and if
1360      *             the caller does not have LoggingPermission("control").
1361      */
1362 
1363     public void reset() throws SecurityException {
1364         checkPermission();
1365 
1366         List<CloseOnReset> persistent;
1367 
1368         // We don't want reset() and readConfiguration()
1369         // to run in parallel
1370         configurationLock.lock();
1371         try {
1372             // install new empty properties
1373             props = new Properties();
1374             // make sure we keep the loggers persistent until reset is done.
1375             // Those are the loggers for which we previously created a
1376             // handler from the configuration, and we need to prevent them
1377             // from being gc'ed until those handlers are closed.


1453                 if (Character.isWhitespace(hands.charAt(end))) {
1454                     break;
1455                 }
1456                 if (hands.charAt(end) == ',') {
1457                     break;
1458                 }
1459                 end++;
1460             }
1461             String word = hands.substring(ix, end);
1462             ix = end+1;
1463             word = word.trim();
1464             if (word.length() == 0) {
1465                 continue;
1466             }
1467             result.add(word);
1468         }
1469         return result.toArray(new String[result.size()]);
1470     }
1471 
1472     /**
1473      * Reads and initializes the logging configuration from the given input stream,
1474      * which should be in {@linkplain java.util.Properties properties file} format.
1475      * <p>
1476      * Any {@linkplain #addConfigurationListener registered configuration
1477      * listener} will be invoked after the properties are read.
1478      * <p>
1479      * @apiNote This {@code readConfiguration} method should only be used for
1480      * initializing the configuration during LogManager initialization or
1481      * used with the "java.util.logging.config.class" property.
1482      * When this method is called after loggers have been created, all
1483      * existing loggers will be {@linkplain #reset() reset}. Then any
1484      * existing loggers that have a level property specified in the new
1485      * configuration stream will be {@linkplain 
1486      * Logger#setLevel(java.util.logging.Level) set} to the specified log level.
1487      * <p>
1488      * To properly update the logging configuration, use the
1489      * {@link #updateConfiguration(java.util.function.Function)} or
1490      * {@link #updateConfiguration(java.io.InputStream, java.util.function.Function)}
1491      * methods instead.
1492      *
1493      * @param ins  stream to read properties from
1494      * @exception  SecurityException  if a security manager exists and if
1495      *             the caller does not have LoggingPermission("control").
1496      * @exception  IOException if there are problems reading from the stream.
1497      */
1498     public void readConfiguration(InputStream ins) throws IOException, SecurityException {
1499         checkPermission();
1500 
1501         // We don't want reset() and readConfiguration() to run
1502         // in parallel.
1503         configurationLock.lock();
1504         try {
1505             if (globalHandlersState == STATE_SHUTDOWN) {
1506                 // already in terminal state: don't even bother
1507                 // to read the configuration
1508                 return;
1509             }
1510 
1511             // change state to STATE_READING_CONFIG to signal reset() to not change it


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


< prev index next >