31 import java.security.*; |
31 import java.security.*; |
32 import java.lang.ref.ReferenceQueue; |
32 import java.lang.ref.ReferenceQueue; |
33 import java.lang.ref.WeakReference; |
33 import java.lang.ref.WeakReference; |
34 import java.util.concurrent.ConcurrentHashMap; |
34 import java.util.concurrent.ConcurrentHashMap; |
35 import java.util.concurrent.CopyOnWriteArrayList; |
35 import java.util.concurrent.CopyOnWriteArrayList; |
|
36 import java.util.concurrent.locks.ReentrantLock; |
36 import sun.misc.JavaAWTAccess; |
37 import sun.misc.JavaAWTAccess; |
37 import sun.misc.ManagedLocalsThread; |
38 import sun.misc.ManagedLocalsThread; |
38 import sun.misc.SharedSecrets; |
39 import sun.misc.SharedSecrets; |
39 |
40 |
40 /** |
41 /** |
178 // Have we done the primordial reading of the configuration file? |
179 // Have we done the primordial reading of the configuration file? |
179 // (Must be done after a suitable amount of java.lang.System |
180 // (Must be done after a suitable amount of java.lang.System |
180 // initialization has been done) |
181 // initialization has been done) |
181 private volatile boolean readPrimordialConfiguration; |
182 private volatile boolean readPrimordialConfiguration; |
182 // Have we initialized global (root) handlers yet? |
183 // Have we initialized global (root) handlers yet? |
183 // This gets set to false in readConfiguration |
184 // This gets set to STATE_UNINITIALIZED in readConfiguration |
184 private boolean initializedGlobalHandlers = true; |
185 private static final int |
185 // True if JVM death is imminent and the exit hook has been called. |
186 STATE_INITIALIZED = 0, // initial state |
186 private boolean deathImminent; |
187 STATE_INITIALIZING = 1, |
|
188 STATE_READING_CONFIG = 2, |
|
189 STATE_UNINITIALIZED = 3, |
|
190 STATE_SHUTDOWN = 4; // terminal state |
|
191 private volatile int globalHandlersState; // = STATE_INITIALIZED; |
|
192 // A concurrency lock for reset(), readConfiguration() and Cleaner. |
|
193 private final ReentrantLock configurationLock = new ReentrantLock(); |
187 |
194 |
188 // This list contains the loggers for which some handlers have been |
195 // This list contains the loggers for which some handlers have been |
189 // explicitly configured in the configuration file. |
196 // explicitly configured in the configuration file. |
190 // It prevents these loggers from being arbitrarily garbage collected. |
197 // It prevents these loggers from being arbitrarily garbage collected. |
191 private static final class CloseOnReset { |
198 private static final class CloseOnReset { |
262 public void run() { |
269 public void run() { |
263 // This is to ensure the LogManager.<clinit> is completed |
270 // This is to ensure the LogManager.<clinit> is completed |
264 // before synchronized block. Otherwise deadlocks are possible. |
271 // before synchronized block. Otherwise deadlocks are possible. |
265 LogManager mgr = manager; |
272 LogManager mgr = manager; |
266 |
273 |
267 // If the global handlers haven't been initialized yet, we |
274 // set globalHandlersState to STATE_SHUTDOWN atomically so that |
268 // don't want to initialize them just so we can close them! |
275 // no attempts are made to (re)initialize the handlers or (re)read |
269 synchronized (LogManager.this) { |
276 // the configuration again. This is terminal state. |
270 // Note that death is imminent. |
277 configurationLock.lock(); |
271 deathImminent = true; |
278 globalHandlersState = STATE_SHUTDOWN; |
272 initializedGlobalHandlers = true; |
279 configurationLock.unlock(); |
273 } |
|
274 |
280 |
275 // Do a reset to close all active handlers. |
281 // Do a reset to close all active handlers. |
276 reset(); |
282 reset(); |
277 } |
283 } |
278 } |
284 } |
1312 * the caller does not have LoggingPermission("control"). |
1318 * the caller does not have LoggingPermission("control"). |
1313 */ |
1319 */ |
1314 |
1320 |
1315 public void reset() throws SecurityException { |
1321 public void reset() throws SecurityException { |
1316 checkPermission(); |
1322 checkPermission(); |
|
1323 |
1317 List<CloseOnReset> persistent; |
1324 List<CloseOnReset> persistent; |
1318 synchronized (this) { |
1325 |
|
1326 // We don't want reset() and readConfiguration() |
|
1327 // to run in parallel |
|
1328 configurationLock.lock(); |
|
1329 try { |
|
1330 // install new empty properties |
1319 props = new Properties(); |
1331 props = new Properties(); |
1320 // make sure we keep the loggers persistent until reset is done. |
1332 // make sure we keep the loggers persistent until reset is done. |
1321 // Those are the loggers for which we previously created a |
1333 // Those are the loggers for which we previously created a |
1322 // handler from the configuration, and we need to prevent them |
1334 // handler from the configuration, and we need to prevent them |
1323 // from being gc'ed until those handlers are closed. |
1335 // from being gc'ed until those handlers are closed. |
1324 persistent = new ArrayList<>(closeOnResetLoggers); |
1336 persistent = new ArrayList<>(closeOnResetLoggers); |
1325 closeOnResetLoggers.clear(); |
1337 closeOnResetLoggers.clear(); |
1326 // Since we are doing a reset we no longer want to initialize |
1338 |
1327 // the global handlers, if they haven't been initialized yet. |
1339 // if reset has been called from shutdown-hook (Cleaner), |
1328 initializedGlobalHandlers = true; |
1340 // or if reset has been called from readConfiguration() which |
1329 } |
1341 // already holds the lock and will change the state itself, |
1330 for (LoggerContext cx : contexts()) { |
1342 // then do not change state here... |
1331 Enumeration<String> enum_ = cx.getLoggerNames(); |
1343 if (globalHandlersState != STATE_SHUTDOWN && |
1332 while (enum_.hasMoreElements()) { |
1344 globalHandlersState != STATE_READING_CONFIG) { |
1333 String name = enum_.nextElement(); |
1345 // ...else user called reset()... |
1334 Logger logger = cx.findLogger(name); |
1346 // Since we are doing a reset we no longer want to initialize |
1335 if (logger != null) { |
1347 // the global handlers, if they haven't been initialized yet. |
1336 resetLogger(logger); |
1348 globalHandlersState = STATE_INITIALIZED; |
1337 } |
1349 } |
1338 } |
1350 |
1339 } |
1351 for (LoggerContext cx : contexts()) { |
1340 persistent.clear(); |
1352 resetLoggerContext(cx); |
1341 } |
1353 } |
1342 |
1354 |
1343 // Private method to reset an individual target logger. |
1355 persistent.clear(); |
1344 private void resetLogger(Logger logger) { |
1356 } finally { |
1345 // Close all the Logger's handlers. |
1357 configurationLock.unlock(); |
|
1358 } |
|
1359 } |
|
1360 |
|
1361 private void resetLoggerContext(LoggerContext cx) { |
|
1362 Enumeration<String> enum_ = cx.getLoggerNames(); |
|
1363 while (enum_.hasMoreElements()) { |
|
1364 String name = enum_.nextElement(); |
|
1365 Logger logger = cx.findLogger(name); |
|
1366 if (logger != null) { |
|
1367 resetLogger(logger); |
|
1368 } |
|
1369 } |
|
1370 } |
|
1371 |
|
1372 private void closeHandlers(Logger logger) { |
1346 Handler[] targets = logger.getHandlers(); |
1373 Handler[] targets = logger.getHandlers(); |
1347 for (Handler h : targets) { |
1374 for (Handler h : targets) { |
1348 logger.removeHandler(h); |
1375 logger.removeHandler(h); |
1349 try { |
1376 try { |
1350 h.close(); |
1377 h.close(); |
1351 } catch (Exception ex) { |
1378 } catch (Exception ex) { |
1352 // Problems closing a handler? Keep going... |
1379 // Problems closing a handler? Keep going... |
1353 } |
1380 } |
1354 } |
1381 } |
|
1382 } |
|
1383 |
|
1384 // Private method to reset an individual target logger. |
|
1385 private void resetLogger(Logger logger) { |
|
1386 // Close all the Logger handlers. |
|
1387 closeHandlers(logger); |
|
1388 |
|
1389 // Reset Logger level |
1355 String name = logger.getName(); |
1390 String name = logger.getName(); |
1356 if (name != null && name.equals("")) { |
1391 if (name != null && name.equals("")) { |
1357 // This is the root logger. |
1392 // This is the root logger. |
1358 logger.setLevel(defaultLevel); |
1393 logger.setLevel(defaultLevel); |
1359 } else { |
1394 } else { |
1406 * the caller does not have LoggingPermission("control"). |
1441 * the caller does not have LoggingPermission("control"). |
1407 * @exception IOException if there are problems reading from the stream. |
1442 * @exception IOException if there are problems reading from the stream. |
1408 */ |
1443 */ |
1409 public void readConfiguration(InputStream ins) throws IOException, SecurityException { |
1444 public void readConfiguration(InputStream ins) throws IOException, SecurityException { |
1410 checkPermission(); |
1445 checkPermission(); |
1411 reset(); |
1446 |
1412 |
1447 // We don't want reset() and readConfiguration() to run |
1413 // Load the properties |
1448 // in parallel. |
|
1449 configurationLock.lock(); |
1414 try { |
1450 try { |
1415 props.load(ins); |
1451 if (globalHandlersState == STATE_SHUTDOWN) { |
1416 } catch (IllegalArgumentException x) { |
1452 // already in terminal state: don't even bother |
1417 // props.load may throw an IllegalArgumentException if the stream |
1453 // to read the configuration |
1418 // contains malformed Unicode escape sequences. |
1454 return; |
1419 // We wrap that in an IOException as readConfiguration is |
1455 } |
1420 // specified to throw IOException if there are problems reading |
1456 |
1421 // from the stream. |
1457 // change state to STATE_READING_CONFIG to signal reset() to not change it |
1422 // Note: new IOException(x.getMessage(), x) allow us to get a more |
1458 globalHandlersState = STATE_READING_CONFIG; |
1423 // concise error message than new IOException(x); |
|
1424 throw new IOException(x.getMessage(), x); |
|
1425 } |
|
1426 |
|
1427 // Instantiate new configuration objects. |
|
1428 String names[] = parseClassNames("config"); |
|
1429 |
|
1430 for (String word : names) { |
|
1431 try { |
1459 try { |
1432 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word); |
1460 // reset configuration which leaves globalHandlersState at STATE_READING_CONFIG |
1433 clz.newInstance(); |
1461 // so that while reading configuration, any ongoing logging requests block and |
1434 } catch (Exception ex) { |
1462 // wait for the outcome (see the end of this try statement) |
1435 System.err.println("Can't load config class \"" + word + "\""); |
1463 reset(); |
1436 System.err.println("" + ex); |
1464 |
1437 // ex.printStackTrace(); |
1465 try { |
1438 } |
1466 // Load the properties |
1439 } |
1467 props.load(ins); |
1440 |
1468 } catch (IllegalArgumentException x) { |
1441 // Set levels on any pre-existing loggers, based on the new properties. |
1469 // props.load may throw an IllegalArgumentException if the stream |
1442 setLevelsOnExistingLoggers(); |
1470 // contains malformed Unicode escape sequences. |
1443 |
1471 // We wrap that in an IOException as readConfiguration is |
1444 try { |
1472 // specified to throw IOException if there are problems reading |
1445 invokeConfigurationListeners(); |
1473 // from the stream. |
|
1474 // Note: new IOException(x.getMessage(), x) allow us to get a more |
|
1475 // concise error message than new IOException(x); |
|
1476 throw new IOException(x.getMessage(), x); |
|
1477 } |
|
1478 |
|
1479 // Instantiate new configuration objects. |
|
1480 String names[] = parseClassNames("config"); |
|
1481 |
|
1482 for (String word : names) { |
|
1483 try { |
|
1484 Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word); |
|
1485 clz.newInstance(); |
|
1486 } catch (Exception ex) { |
|
1487 System.err.println("Can't load config class \"" + word + "\""); |
|
1488 System.err.println("" + ex); |
|
1489 // ex.printStackTrace(); |
|
1490 } |
|
1491 } |
|
1492 |
|
1493 // Set levels on any pre-existing loggers, based on the new properties. |
|
1494 setLevelsOnExistingLoggers(); |
|
1495 |
|
1496 // Note that we need to reinitialize global handles when |
|
1497 // they are first referenced. |
|
1498 globalHandlersState = STATE_UNINITIALIZED; |
|
1499 } catch (Throwable t) { |
|
1500 // If there were any trouble, then set state to STATE_INITIALIZED |
|
1501 // so that no global handlers reinitialization is performed on not fully |
|
1502 // initialized configuration. |
|
1503 globalHandlersState = STATE_INITIALIZED; |
|
1504 // re-throw |
|
1505 throw t; |
|
1506 } |
1446 } finally { |
1507 } finally { |
1447 // Note that we need to reinitialize global handles when |
1508 configurationLock.unlock(); |
1448 // they are first referenced. |
1509 } |
1449 synchronized (this) { |
1510 |
1450 initializedGlobalHandlers = false; |
1511 // should be called out of lock to avoid dead-lock situations |
1451 } |
1512 // when user code is involved |
1452 } |
1513 invokeConfigurationListeners(); |
1453 } |
1514 } |
1454 |
1515 |
1455 /** |
1516 /** |
1456 * Get the value of a logging property. |
1517 * Get the value of a logging property. |
1457 * The method returns null if the property is not found. |
1518 * The method returns null if the property is not found. |
1574 } |
1635 } |
1575 |
1636 |
1576 // Private method to load the global handlers. |
1637 // Private method to load the global handlers. |
1577 // We do the real work lazily, when the global handlers |
1638 // We do the real work lazily, when the global handlers |
1578 // are first used. |
1639 // are first used. |
1579 private synchronized void initializeGlobalHandlers() { |
1640 private void initializeGlobalHandlers() { |
1580 if (initializedGlobalHandlers) { |
1641 int state = globalHandlersState; |
|
1642 if (state == STATE_INITIALIZED || |
|
1643 state == STATE_SHUTDOWN) { |
|
1644 // Nothing to do: return. |
1581 return; |
1645 return; |
1582 } |
1646 } |
1583 |
1647 |
1584 initializedGlobalHandlers = true; |
1648 // If we have not initialized global handlers yet (or need to |
1585 |
1649 // reinitialize them), lets do it now (this case is indicated by |
1586 if (deathImminent) { |
1650 // globalHandlersState == STATE_UNINITIALIZED). |
1587 // Aaargh... |
1651 // If we are in the process of initializing global handlers we |
1588 // The VM is shutting down and our exit hook has been called. |
1652 // also need to lock & wait (this case is indicated by |
1589 // Avoid allocating global handlers. |
1653 // globalHandlersState == STATE_INITIALIZING). |
1590 return; |
1654 // If we are in the process of reading configuration we also need to |
1591 } |
1655 // wait to see what the outcome will be (this case |
1592 loadLoggerHandlers(rootLogger, null, "handlers"); |
1656 // is indicated by globalHandlersState == STATE_READING_CONFIG) |
|
1657 // So in either case we need to wait for the lock. |
|
1658 configurationLock.lock(); |
|
1659 try { |
|
1660 if (globalHandlersState != STATE_UNINITIALIZED) { |
|
1661 return; // recursive call or nothing to do |
|
1662 } |
|
1663 // set globalHandlersState to STATE_INITIALIZING first to avoid |
|
1664 // getting an infinite recursion when loadLoggerHandlers(...) |
|
1665 // is going to call addHandler(...) |
|
1666 globalHandlersState = STATE_INITIALIZING; |
|
1667 try { |
|
1668 loadLoggerHandlers(rootLogger, null, "handlers"); |
|
1669 } finally { |
|
1670 globalHandlersState = STATE_INITIALIZED; |
|
1671 } |
|
1672 } finally { |
|
1673 configurationLock.unlock(); |
|
1674 } |
1593 } |
1675 } |
1594 |
1676 |
1595 static final Permission controlPermission = new LoggingPermission("control", null); |
1677 static final Permission controlPermission = new LoggingPermission("control", null); |
1596 |
1678 |
1597 void checkPermission() { |
1679 void checkPermission() { |