This is an automated email from the ASF dual-hosted git repository.

rgoers pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 7a9a466a756a8bd77bc4bc29ee9517c4c098f470
Author: Ralph Goers <[email protected]>
AuthorDate: Wed Jan 30 17:02:50 2019 -0700

    LOG4J2-913 - Add support for dynamic reconfiguration
---
 .../log4j/core/config/AbstractConfiguration.java   | 48 ++++++++++++++--------
 .../logging/log4j/core/config/HttpWatcher.java     |  2 +-
 .../logging/log4j/core/util/WatchEventService.java | 28 +++++++++++++
 .../logging/log4j/core/util/WatchManager.java      | 43 +++++++++++++++++++
 .../apache/logging/log4j/core/util/Watcher.java    |  1 +
 .../log4j-spring-cloud-config-client/pom.xml       |  8 ++++
 .../cloud/config/client/Log4j2EventListener.java   | 45 ++++++++++++++++++++
 .../cloud/config/client/WatchEventManager.java     | 48 ++++++++++++++++++++++
 ...pache.logging.log4j.core.util.WatchEventService |  1 +
 .../src/main/resources/META-INF/spring.factories   |  1 +
 .../src/main/resources/log4j2.component.properties |  0
 log4j-spring-cloud-config/pom.xml                  | 10 +++++
 12 files changed, 216 insertions(+), 19 deletions(-)

diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
index de0d6eb..4a93e02 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
@@ -245,27 +245,39 @@ public abstract class AbstractConfiguration extends 
AbstractFilterable implement
 
     protected void initializeWatchers(Reconfigurable reconfigurable, 
ConfigurationSource configSource,
         int monitorIntervalSeconds) {
-        if ((configSource.getFile() != null || configSource.getURL() != null) 
&& monitorIntervalSeconds > 0) {
-            watchManager.setIntervalSeconds(monitorIntervalSeconds);
-            if (configSource.getFile() != null) {
-                final Source cfgSource = new Source(configSource);
-                final long lastModifeid = 
configSource.getFile().lastModified();
-                final ConfigurationFileWatcher watcher = new 
ConfigurationFileWatcher(this, reconfigurable,
-                    listeners, lastModifeid);
-                watchManager.watch(cfgSource, watcher);
-            } else if (configSource.getURL() != null) {
-                if (configSource.getLastModified() > 0) {
-                    final Source cfgSource = new Source(configSource);
-                    final Watcher watcher = 
WatcherFactory.getInstance(pluginPackages)
-                        .newWatcher(cfgSource, this, reconfigurable, 
listeners, configSource.getLastModified());
-                    watchManager.watch(cfgSource, watcher);
-                } else {
-                    LOGGER.info("{} does not support dynamic reconfiguration", 
configSource.getURI());
-                }
-            }
+        if (configSource.getFile() != null || configSource.getURL() != null) {
+               if (monitorIntervalSeconds > 0) {
+                               
watchManager.setIntervalSeconds(monitorIntervalSeconds);
+                               if (configSource.getFile() != null) {
+                                       final Source cfgSource = new 
Source(configSource);
+                                       final long lastModifeid = 
configSource.getFile().lastModified();
+                                       final ConfigurationFileWatcher watcher 
= new ConfigurationFileWatcher(this, reconfigurable,
+                                               listeners, lastModifeid);
+                                       watchManager.watch(cfgSource, watcher);
+                               } else {
+                                       if (configSource.getURL() != null) {
+                                               monitorSource(reconfigurable, 
configSource);
+                                       }
+                               }
+                       } else if (watchManager.hasEventListeners() && 
configSource.getURL() != null && monitorIntervalSeconds >= 0) {
+                               monitorSource(reconfigurable, configSource);
+                       }
         }
     }
 
+    private void monitorSource(Reconfigurable reconfigurable, 
ConfigurationSource configSource) {
+               if (configSource.getLastModified() > 0) {
+                       final Source cfgSource = new Source(configSource);
+                       final Watcher watcher = 
WatcherFactory.getInstance(pluginPackages)
+                               .newWatcher(cfgSource, this, reconfigurable, 
listeners, configSource.getLastModified());
+                       if (watcher != null) {
+                               watchManager.watch(cfgSource, watcher);
+                       }
+               } else {
+                       LOGGER.info("{} does not support dynamic 
reconfiguration", configSource.getURI());
+               }
+       }
+
        /**
      * Start the configuration.
      */
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/HttpWatcher.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/HttpWatcher.java
index a2d1220..6072168 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/HttpWatcher.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/HttpWatcher.java
@@ -30,7 +30,7 @@ import org.apache.logging.log4j.util.PropertiesUtil;
 /**
  *
  */
-@Plugin(name = "http", category = Watcher.CATEGORY, printObject = true)
+@Plugin(name = "http", category = Watcher.CATEGORY, elementType = 
Watcher.ELEMENT_TYPE, printObject = true)
 @PluginAliases("https")
 public class HttpWatcher extends AbstractWatcher {
 
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchEventService.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchEventService.java
new file mode 100644
index 0000000..31021c8
--- /dev/null
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchEventService.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.util;
+
+/**
+ *
+ */
+public interface WatchEventService {
+
+       void subscribe(WatchManager manager);
+
+       void unsubscribe(WatchManager manager);
+
+}
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java
index 37e0a44..3e37152 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java
@@ -17,9 +17,13 @@
 package org.apache.logging.log4j.core.util;
 
 import java.io.File;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ScheduledFuture;
@@ -30,6 +34,7 @@ import org.apache.logging.log4j.core.AbstractLifeCycle;
 import org.apache.logging.log4j.core.config.ConfigurationFileWatcher;
 import org.apache.logging.log4j.core.config.ConfigurationScheduler;
 import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.LoaderUtil;
 
 /**
  * Manages {@link FileWatcher}s.
@@ -44,11 +49,22 @@ public class WatchManager extends AbstractLifeCycle {
     private int intervalSeconds = 0;
     private ScheduledFuture<?> future;
     private final ConfigurationScheduler scheduler;
+    private final List<WatchEventService> eventServiceList;
+    private final UUID id = UuidUtil.getTimeBasedUuid();
 
     public WatchManager(final ConfigurationScheduler scheduler) {
         this.scheduler = scheduler;
+        eventServiceList = getEventServices();
     }
 
+    public UUID getId() {
+       return this.id;
+       }
+
+       public boolean hasEventListeners() {
+       return eventServiceList.size() > 0;
+       }
+
     /**
      * Resets all file monitors to their current last modified time. If this 
manager does not watch any file, nothing
      * happens.
@@ -141,15 +157,22 @@ public class WatchManager extends AbstractLifeCycle {
     @Override
     public void start() {
         super.start();
+
         if (intervalSeconds > 0) {
             future = scheduler.scheduleWithFixedDelay(new WatchRunnable(), 
intervalSeconds, intervalSeconds,
                     TimeUnit.SECONDS);
         }
+        for (WatchEventService service : eventServiceList) {
+               service.subscribe(this);
+               }
     }
 
     @Override
     public boolean stop(final long timeout, final TimeUnit timeUnit) {
         setStopping();
+               for (WatchEventService service : eventServiceList) {
+                       service.unsubscribe(this);
+               }
         final boolean stopped = stop(future);
         setStopped();
         return stopped;
@@ -179,6 +202,10 @@ public class WatchManager extends AbstractLifeCycle {
         watchers.remove(source);
     }
 
+       public void checkFiles() {
+               new WatchRunnable().run();
+       }
+
     /**
      * Watches the given file.
      *
@@ -248,6 +275,22 @@ public class WatchManager extends AbstractLifeCycle {
         return new Date(millis).toString();
     }
 
+       private List<WatchEventService> getEventServices() {
+       List<WatchEventService> list = new ArrayList<>();
+               for (final ClassLoader classLoader : 
LoaderUtil.getClassLoaders()) {
+                       try {
+                               final ServiceLoader<WatchEventService > 
serviceLoader =
+                                       
ServiceLoader.load(WatchEventService.class, classLoader);
+                               for (final WatchEventService service : 
serviceLoader) {
+                                       list.add(service);
+                               }
+                       } catch (final Throwable ex) {
+                               LOGGER.debug("Unable to retrieve 
WatchEventService from ClassLoader {}", classLoader, ex);
+                       }
+               }
+               return list;
+       }
+
     private final class WatchRunnable implements Runnable {
 
         // Use a hard class reference here in case a refactoring changes the 
class name.
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Watcher.java 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Watcher.java
index 7ae9cd0..c2be48d 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Watcher.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Watcher.java
@@ -30,6 +30,7 @@ import org.apache.logging.log4j.core.config.Reconfigurable;
 public interface Watcher {
 
     public static final String CATEGORY = "Watcher";
+    public static final String ELEMENT_TYPE = "watcher";
 
     /**
      * Returns the list of listeners for this configuration.
diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/pom.xml 
b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/pom.xml
index 8799078..c0a31f7 100644
--- a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/pom.xml
+++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/pom.xml
@@ -48,6 +48,14 @@
       <artifactId>spring-boot</artifactId>
     </dependency>
     <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-context</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-context-support</artifactId>
+    </dependency>
+    <dependency>
       <groupId>org.hamcrest</groupId>
       <artifactId>hamcrest-all</artifactId>
       <scope>test</scope>
diff --git 
a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/Log4j2EventListener.java
 
b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/Log4j2EventListener.java
new file mode 100644
index 0000000..29cdc92
--- /dev/null
+++ 
b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/Log4j2EventListener.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.spring.cloud.config.client;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
+import org.springframework.cloud.context.refresh.ContextRefresher;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Component;
+
+/**
+ * Listen for events indicating the remote configuration has changed.
+ */
+@Component
+@ConditionalOnClass(ContextRefresher.class)
+@ConditionalOnBean(ContextRefresher.class)
+@ConditionalOnProperty(value = "spring.cloud.config.watch.enabled")
+public class Log4j2EventListener {
+
+       private static Logger LOGGER = 
LogManager.getLogger(Log4j2EventListener.class);
+
+       @EventListener(EnvironmentChangeEvent.class)
+       public void handleEnvironmentChangeEvent(EnvironmentChangeEvent event) {
+               LOGGER.debug("Environment change event received");
+               WatchEventManager.publishEvent();
+       }
+}
diff --git 
a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/WatchEventManager.java
 
b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/WatchEventManager.java
new file mode 100644
index 0000000..a49d5f2
--- /dev/null
+++ 
b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/WatchEventManager.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.spring.cloud.config.client;
+
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.logging.log4j.core.util.WatchEventService;
+import org.apache.logging.log4j.core.util.WatchManager;
+
+/**
+ *
+ */
+public class WatchEventManager implements WatchEventService {
+       private static final ConcurrentMap<UUID, WatchManager> watchManagers = 
new ConcurrentHashMap<>();
+
+       public static void publishEvent() {
+               for (WatchManager manager : watchManagers.values()) {
+                       manager.checkFiles();
+               }
+       }
+
+       @Override
+       public void subscribe(WatchManager manager) {
+               watchManagers.put(manager.getId(), manager);
+
+       }
+
+       @Override
+       public void unsubscribe(WatchManager manager) {
+               watchManagers.remove(manager.getId());
+       }
+}
diff --git 
a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/resources/META-INF/services/org.apache.logging.log4j.core.util.WatchEventService
 
b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/resources/META-INF/services/org.apache.logging.log4j.core.util.WatchEventService
new file mode 100644
index 0000000..701bfd3
--- /dev/null
+++ 
b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/resources/META-INF/services/org.apache.logging.log4j.core.util.WatchEventService
@@ -0,0 +1 @@
+org.apache.logging.log4j.spring.cloud.config.client.WatchEventManager
\ No newline at end of file
diff --git 
a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/resources/META-INF/spring.factories
 
b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..95a972f
--- /dev/null
+++ 
b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/resources/META-INF/spring.factories
@@ -0,0 +1 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.apache.logging.log4j.spring.cloud.config.client.Log4j2EventListener
\ No newline at end of file
diff --git 
a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/resources/log4j2.component.properties
 
b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/resources/log4j2.component.properties
new file mode 100644
index 0000000..e69de29
diff --git a/log4j-spring-cloud-config/pom.xml 
b/log4j-spring-cloud-config/pom.xml
index a736e86..37282c9 100644
--- a/log4j-spring-cloud-config/pom.xml
+++ b/log4j-spring-cloud-config/pom.xml
@@ -53,6 +53,16 @@
       </dependency>
       <dependency>
         <groupId>org.springframework</groupId>
+        <artifactId>spring-context</artifactId>
+        <version>${spring.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.springframework</groupId>
+        <artifactId>spring-context-support</artifactId>
+        <version>${spring.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.springframework</groupId>
         <artifactId>spring-web</artifactId>
         <version>${spring.version}</version>
       </dependency>

Reply via email to