Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implements an event service and demonstrates its use for sending/receiving SSL key/trust store changes #3378

Open
wants to merge 1 commit into
base: 2.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@
import org.apache.logging.log4j.core.config.DefaultConfiguration;
import org.apache.logging.log4j.core.config.NullConfiguration;
import org.apache.logging.log4j.core.config.Reconfigurable;
import org.apache.logging.log4j.core.event.Event;
import org.apache.logging.log4j.core.event.Event.EventType;
import org.apache.logging.log4j.core.event.EventFilter;
import org.apache.logging.log4j.core.event.EventListener;
import org.apache.logging.log4j.core.event.EventService;
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
import org.apache.logging.log4j.core.jmx.Server;
import org.apache.logging.log4j.core.util.Cancellable;
Expand Down Expand Up @@ -73,7 +78,8 @@ public class LoggerContext extends AbstractLifeCycle
AutoCloseable,
Terminable,
ConfigurationListener,
LoggerContextShutdownEnabled {
LoggerContextShutdownEnabled,
EventListener {

/**
* Property name of the property change event fired if the configuration is changed.
Expand Down Expand Up @@ -308,6 +314,7 @@ public void start() {
configLock.unlock();
}
}
EventService.subscribe(this, new EventFilter(EventType.CONFIGURATION_CHANGED));
LOGGER.debug("LoggerContext[name={}, {}] started OK.", getName(), this);
}

Expand All @@ -331,6 +338,7 @@ public void start(final Configuration config) {
}
}
setConfiguration(config);
EventService.subscribe(this, new EventFilter(EventType.CONFIGURATION_CHANGED));
LOGGER.info("{}[name={}] started with configuration {}.", getClass().getSimpleName(), getName(), config);
}

Expand Down Expand Up @@ -863,4 +871,12 @@ private Logger newInstance(final String name, final MessageFactory messageFactor
protected Logger newInstance(LoggerContext context, String name, MessageFactory messageFactory) {
return new Logger(context, name, messageFactory);
}

@Override
public void onEvent(Event event) {
LOGGER.debug("Event received: " + event.getEventType());
if (configuration instanceof Reconfigurable) {
onChange((Reconfigurable) configuration);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
* Provides Asynchronous Logger classes and interfaces for low-latency logging.
*/
@Export
@Version("2.24.1")
@Version("2.25.0")
package org.apache.logging.log4j.core.async;

import org.osgi.annotation.bundle.Export;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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.event;

/**
* Represents an event
*/
public class Event {
public enum EventType {
CONFIGURATION_CHANGED
};

private EventType eventType;

public Event(final EventType eventType) {
this.eventType = eventType;
}

public EventType getEventType() {
return eventType;
}

public void setEventType(final EventType eventType) {
this.eventType = eventType;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* 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.event;

import org.apache.logging.log4j.core.event.Event.EventType;

/**
* A filter that can be used in subscribing for events
*/
public class EventFilter {

private EventType eventType;

public EventFilter(final EventType eventType) {
this.eventType = eventType;
}

public boolean matches(final Event event) {
return event.getEventType().equals(eventType);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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.event;

/**
* Interface to be implemented by classes interested in receiving events
*/
public interface EventListener {

/**
* Handle an event
* @param event the event to handle
*/
void onEvent(Event event);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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.event;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* A service for handling events and subscriptions to events
*/
public class EventService {

private static Map<EventListener, EventFilter> eventListeners = new ConcurrentHashMap<>();

/**
* Subscribe to receive events
* @param eventListener the listener the events should be sent to
* @param eventFilter a filter to control the events to be sent to the listener
*/
public static void subscribe(final EventListener eventListener, final EventFilter eventFilter) {
eventListeners.put(eventListener, eventFilter);
}

/**
* Publish the given event to subscribed listeners
*
* @param event the event to publish
*/
public static void publish(Event event) {
System.out.println("Publishing event " + event);

eventListeners.entrySet().stream()
.filter(listener -> listener.getValue().matches(event))
.forEach(listener -> {
System.out.println("To:" + listener.getKey());
listener.getKey().onEvent(event);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* 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.
*/
/**
* Classes and interfaces supporting configuration of Log4j 2 with JSON.
*/
@Export
@Version("2.20.1")
package org.apache.logging.log4j.core.event;

import org.osgi.annotation.bundle.Export;
import org.osgi.annotation.versioning.Version;
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,29 @@
*/
package org.apache.logging.log4j.core.net.ssl;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executors;
import org.apache.logging.log4j.core.config.ConfigurationSource;
import org.apache.logging.log4j.core.event.Event;
import org.apache.logging.log4j.core.event.Event.EventType;
import org.apache.logging.log4j.core.event.EventService;
import org.apache.logging.log4j.core.util.NetUtils;

/**
Expand Down Expand Up @@ -63,6 +76,20 @@ public AbstractKeyStoreConfiguration(final String location, final String passwor
this(location, new MemoryPasswordProvider(password == null ? null : password.toCharArray()), keyStoreType);
}

private static void watchKeyAndTrustStoreFiles(final String keyStoreLocation, final String trustStoreLocation) {
Set<String> filePathsToWatch = new HashSet<>();
if (keyStoreLocation != null) {
filePathsToWatch.add(keyStoreLocation);
}
if (trustStoreLocation != null) {
filePathsToWatch.add(trustStoreLocation);
}
}

private void watchKeyStoreForChanges(final String location) {
Executors.newSingleThreadExecutor().execute(new WatchRunnable(location));
}

@Override
protected KeyStore load() throws StoreConfigurationException {
final String loadLocation = this.getLocation();
Expand All @@ -81,6 +108,9 @@ protected KeyStore load() throws StoreConfigurationException {
try (final InputStream fin = openInputStream(loadLocation)) {
ks.load(fin, password);
LOGGER.debug("KeyStore successfully loaded from location {}", loadLocation);

watchKeyStoreForChanges(loadLocation);

return ks;
}
} catch (final CertificateException e) {
Expand Down Expand Up @@ -152,4 +182,39 @@ public boolean equals(final Object obj) {
public String getKeyStoreType() {
return keyStoreType;
}

private static final class WatchRunnable implements Runnable {

private String fileToWatch;

public WatchRunnable(String fileToWatch) {
this.fileToWatch = fileToWatch;
}

@Override
public void run() {
WatchService watchService;
try {
watchService = FileSystems.getDefault().newWatchService();

File file = new File(fileToWatch);
Path directory = file.getParentFile().toPath();
LOGGER.info("Watching {} for changes", fileToWatch);
directory.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);

WatchKey key;
while ((key = watchService.take()) != null) {
for (WatchEvent<?> event : key.pollEvents()) {
if (file.getName().equals(event.context().toString())) {
LOGGER.info("Change detected in configuration file {}", event.context());
EventService.publish(new Event(EventType.CONFIGURATION_CHANGED));
}
}
key.reset();
}
} catch (IOException | InterruptedException exception) {
LOGGER.error("Error watching a configuration related file for SocketAppender", exception);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
* Implementation of Log4j 2.
*/
@Export
@Version("2.24.2")
@Version("2.25.0")
package org.apache.logging.log4j.core;

import org.osgi.annotation.bundle.Export;
Expand Down
Loading