forked from belaban/JGroups
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6e12fbb
commit f5d5c0e
Showing
6 changed files
with
384 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
package org.jgroups.util; | ||
|
||
import java.io.FileNotFoundException; | ||
import java.io.IOException; | ||
import java.nio.file.Files; | ||
import java.nio.file.NoSuchFileException; | ||
import java.nio.file.Path; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
import java.util.function.Consumer; | ||
|
||
import org.jgroups.logging.Log; | ||
import org.jgroups.logging.LogFactory; | ||
|
||
public class FileWatcher implements Runnable, AutoCloseable { | ||
|
||
static final Log LOG = LogFactory.getLog(FileWatcher.class); | ||
|
||
public static final int SLEEP = 2_000; | ||
private final Thread thread; | ||
private final ConcurrentHashMap<Path, Watched> watched; | ||
private boolean running = true; | ||
|
||
public FileWatcher() { | ||
watched = new ConcurrentHashMap<>(); | ||
thread = new Thread(this, "FileWatcher"); | ||
Runtime.getRuntime().addShutdownHook(new Thread(this::stop)); | ||
thread.start(); | ||
} | ||
|
||
public void unwatch(Path path) { | ||
watched.remove(path); | ||
LOG.debug("Unwatched %s", path); | ||
} | ||
|
||
public void watch(Path path, Consumer<Path> callback) { | ||
watched.compute(path, (k, w) -> { | ||
if (w == null) { | ||
w = new Watched(); | ||
try { | ||
w.lastModified = Files.getLastModifiedTime(path).toMillis(); | ||
} catch (FileNotFoundException | NoSuchFileException e) { | ||
w.lastModified = -1; | ||
LOG.debug("File not found %s", path); | ||
} catch (IOException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
w.watchers.add(callback); | ||
return w; | ||
}); | ||
LOG.debug("Watching %s", path); | ||
} | ||
|
||
@Override | ||
public void run() { | ||
while (running) { | ||
try { | ||
Thread.sleep(SLEEP); | ||
} catch (InterruptedException e) { | ||
return; | ||
} | ||
if (!running) { | ||
return; | ||
} | ||
for (Map.Entry<Path, Watched> e : watched.entrySet()) { | ||
Watched w = e.getValue(); | ||
try { | ||
long lastModified = Files.getLastModifiedTime(e.getKey()).toMillis(); | ||
if (w.lastModified < lastModified) { | ||
w.lastModified = lastModified; | ||
for (Consumer<Path> c : w.watchers) { | ||
c.accept(e.getKey()); | ||
} | ||
} | ||
} catch (FileNotFoundException | NoSuchFileException ex) { | ||
w.lastModified = -1; | ||
} catch (IOException ex) { | ||
throw new RuntimeException(ex); | ||
} | ||
} | ||
} | ||
} | ||
|
||
public void stop() { | ||
running = false; | ||
try { | ||
thread.join(); | ||
} catch (InterruptedException e) { | ||
// Ignore | ||
} | ||
} | ||
|
||
@Override | ||
public void close() { | ||
stop(); | ||
} | ||
|
||
static class Watched { | ||
long lastModified; | ||
List<Consumer<Path>> watchers = new ArrayList<>(2); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package org.jgroups.util; | ||
|
||
import java.io.Closeable; | ||
import java.io.IOException; | ||
import java.net.Socket; | ||
import java.nio.file.Path; | ||
import java.security.Principal; | ||
import java.security.PrivateKey; | ||
import java.security.cert.X509Certificate; | ||
import java.time.Instant; | ||
import java.util.Objects; | ||
import java.util.concurrent.atomic.AtomicReference; | ||
import java.util.function.Function; | ||
|
||
import javax.net.ssl.SSLEngine; | ||
import javax.net.ssl.X509ExtendedKeyManager; | ||
|
||
import org.jgroups.logging.Log; | ||
import org.jgroups.logging.LogFactory; | ||
|
||
/** | ||
* A {@link X509ExtendedKeyManager} which uses a @{@link FileWatcher} to check for changes. | ||
*/ | ||
public final class ReloadingX509KeyManager extends X509ExtendedKeyManager implements Closeable { | ||
|
||
static final Log LOG = LogFactory.getLog(ReloadingX509KeyManager.class); | ||
|
||
private final AtomicReference<X509ExtendedKeyManager> manager; | ||
private final Path path; | ||
private final Function<Path, X509ExtendedKeyManager> action; | ||
private final FileWatcher watcher; | ||
private Instant lastLoaded; | ||
|
||
public ReloadingX509KeyManager(FileWatcher watcher, Path path, Function<Path, X509ExtendedKeyManager> action) { | ||
Objects.requireNonNull(watcher, "watcher must be non-null"); | ||
Objects.requireNonNull(path, "path must be non-null"); | ||
Objects.requireNonNull(action, "action must be non-null"); | ||
|
||
this.manager = new AtomicReference<>(); | ||
this.watcher = watcher; | ||
this.path = path; | ||
this.action = action; | ||
reload(this.path); | ||
watcher.watch(path, this::reload); | ||
} | ||
|
||
private void reload(Path path) { | ||
manager.set(action.apply(path)); | ||
lastLoaded = Instant.now(); | ||
LOG.debug("Loaded '%s'", path); | ||
} | ||
|
||
@Override | ||
public String[] getClientAliases(String keyType, Principal[] issuers) { | ||
return manager.get().getClientAliases(keyType, issuers); | ||
} | ||
|
||
@Override | ||
public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) { | ||
return manager.get().chooseClientAlias(keyType, issuers, socket); | ||
} | ||
|
||
@Override | ||
public String[] getServerAliases(String keyType, Principal[] issuers) { | ||
return manager.get().getServerAliases(keyType, issuers); | ||
} | ||
|
||
@Override | ||
public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { | ||
return manager.get().chooseServerAlias(keyType, issuers, socket); | ||
} | ||
|
||
@Override | ||
public X509Certificate[] getCertificateChain(String alias) { | ||
return manager.get().getCertificateChain(alias); | ||
} | ||
|
||
@Override | ||
public PrivateKey getPrivateKey(String alias) { | ||
return manager.get().getPrivateKey(alias); | ||
} | ||
|
||
@Override | ||
public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine) { | ||
return manager.get().chooseEngineClientAlias(keyType, issuers, engine); | ||
} | ||
|
||
@Override | ||
public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) { | ||
return manager.get().chooseEngineServerAlias(keyType, issuers, engine); | ||
} | ||
|
||
public Instant lastLoaded() { | ||
return lastLoaded; | ||
} | ||
|
||
@Override | ||
public void close() throws IOException { | ||
watcher.unwatch(path); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package org.jgroups.util; | ||
|
||
import java.io.Closeable; | ||
import java.io.IOException; | ||
import java.net.Socket; | ||
import java.nio.file.Path; | ||
import java.security.cert.CertificateException; | ||
import java.security.cert.X509Certificate; | ||
import java.time.Instant; | ||
import java.util.Objects; | ||
import java.util.concurrent.atomic.AtomicReference; | ||
import java.util.function.Function; | ||
|
||
import javax.net.ssl.SSLEngine; | ||
import javax.net.ssl.X509ExtendedTrustManager; | ||
|
||
import org.jgroups.logging.Log; | ||
import org.jgroups.logging.LogFactory; | ||
|
||
/** | ||
* A {@link X509ExtendedTrustManager} which uses a @{@link FileWatcher} to check for changes. | ||
*/ | ||
public class ReloadingX509TrustManager extends X509ExtendedTrustManager implements Closeable { | ||
|
||
static final Log LOG = LogFactory.getLog(ReloadingX509TrustManager.class); | ||
|
||
private final AtomicReference<X509ExtendedTrustManager> manager; | ||
private final Path path; | ||
private final Function<Path, X509ExtendedTrustManager> action; | ||
private final FileWatcher watcher; | ||
private Instant lastLoaded; | ||
|
||
public ReloadingX509TrustManager(FileWatcher watcher, Path path, Function<Path, X509ExtendedTrustManager> action) { | ||
Objects.requireNonNull(watcher, "watcher must be non-null"); | ||
Objects.requireNonNull(path, "path must be non-null"); | ||
Objects.requireNonNull(action, "action must be non-null"); | ||
this.manager = new AtomicReference<>(); | ||
this.path = path; | ||
this.action = action; | ||
this.watcher = watcher; | ||
reload(this.path); | ||
watcher.watch(this.path, this::reload); | ||
} | ||
|
||
private void reload(Path path) { | ||
manager.set(action.apply(path)); | ||
lastLoaded = Instant.now(); | ||
LOG.debug("Loaded '%s'", path); | ||
} | ||
|
||
@Override | ||
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { | ||
manager.get().checkClientTrusted(chain, authType); | ||
} | ||
|
||
@Override | ||
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { | ||
manager.get().checkServerTrusted(chain, authType); | ||
} | ||
|
||
@Override | ||
public X509Certificate[] getAcceptedIssuers() { | ||
return manager.get().getAcceptedIssuers(); | ||
} | ||
|
||
@Override | ||
public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { | ||
manager.get().checkClientTrusted(chain, authType, socket); | ||
} | ||
|
||
@Override | ||
public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { | ||
manager.get().checkServerTrusted(chain, authType, socket); | ||
} | ||
|
||
@Override | ||
public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { | ||
manager.get().checkClientTrusted(chain, authType, engine); | ||
} | ||
|
||
@Override | ||
public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { | ||
manager.get().checkServerTrusted(chain, authType, engine); | ||
} | ||
|
||
public Instant lastLoaded() { | ||
return lastLoaded; | ||
} | ||
|
||
@Override | ||
public void close() throws IOException { | ||
watcher.unwatch(path); | ||
} | ||
} |
Oops, something went wrong.