12ffbb0cfSVladimir Kotal /* 22ffbb0cfSVladimir Kotal * CDDL HEADER START 32ffbb0cfSVladimir Kotal * 42ffbb0cfSVladimir Kotal * The contents of this file are subject to the terms of the 52ffbb0cfSVladimir Kotal * Common Development and Distribution License (the "License"). 62ffbb0cfSVladimir Kotal * You may not use this file except in compliance with the License. 72ffbb0cfSVladimir Kotal * 82ffbb0cfSVladimir Kotal * See LICENSE.txt included in this distribution for the specific 92ffbb0cfSVladimir Kotal * language governing permissions and limitations under the License. 102ffbb0cfSVladimir Kotal * 112ffbb0cfSVladimir Kotal * When distributing Covered Code, include this CDDL HEADER in each 122ffbb0cfSVladimir Kotal * file and include the License file at LICENSE.txt. 132ffbb0cfSVladimir Kotal * If applicable, add the following below this CDDL HEADER, with the 142ffbb0cfSVladimir Kotal * fields enclosed by brackets "[]" replaced with your own identifying 152ffbb0cfSVladimir Kotal * information: Portions Copyright [yyyy] [name of copyright owner] 162ffbb0cfSVladimir Kotal * 172ffbb0cfSVladimir Kotal * CDDL HEADER END 182ffbb0cfSVladimir Kotal */ 192ffbb0cfSVladimir Kotal 202ffbb0cfSVladimir Kotal /* 21*c6f0939bSAdam Hornacek * Copyright (c) 2006, 2021, Oracle and/or its affiliates. All rights reserved. 222ffbb0cfSVladimir Kotal */ 232ffbb0cfSVladimir Kotal package org.opengrok.indexer.configuration; 242ffbb0cfSVladimir Kotal 252ffbb0cfSVladimir Kotal import org.opengrok.indexer.logger.LoggerFactory; 262ffbb0cfSVladimir Kotal 272ffbb0cfSVladimir Kotal import java.io.File; 282ffbb0cfSVladimir Kotal import java.io.IOException; 292ffbb0cfSVladimir Kotal import java.nio.file.ClosedWatchServiceException; 302ffbb0cfSVladimir Kotal import java.nio.file.FileSystems; 312ffbb0cfSVladimir Kotal import java.nio.file.FileVisitResult; 322ffbb0cfSVladimir Kotal import java.nio.file.Files; 332ffbb0cfSVladimir Kotal import java.nio.file.Path; 342ffbb0cfSVladimir Kotal import java.nio.file.Paths; 352ffbb0cfSVladimir Kotal import java.nio.file.SimpleFileVisitor; 362ffbb0cfSVladimir Kotal import java.nio.file.WatchEvent; 372ffbb0cfSVladimir Kotal import java.nio.file.WatchKey; 382ffbb0cfSVladimir Kotal import java.nio.file.WatchService; 392ffbb0cfSVladimir Kotal import java.util.logging.Level; 402ffbb0cfSVladimir Kotal import java.util.logging.Logger; 412ffbb0cfSVladimir Kotal 422ffbb0cfSVladimir Kotal import static java.nio.file.FileVisitResult.CONTINUE; 432ffbb0cfSVladimir Kotal import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; 442ffbb0cfSVladimir Kotal import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE; 452ffbb0cfSVladimir Kotal import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; 462ffbb0cfSVladimir Kotal 472ffbb0cfSVladimir Kotal public class WatchDogService { 482ffbb0cfSVladimir Kotal private static final Logger LOGGER = LoggerFactory.getLogger(WatchDogService.class); 492ffbb0cfSVladimir Kotal 502ffbb0cfSVladimir Kotal private Thread watchDogThread; 512ffbb0cfSVladimir Kotal private WatchService watchDogWatcher; 522ffbb0cfSVladimir Kotal public static final int THREAD_SLEEP_TIME = 2000; 532ffbb0cfSVladimir Kotal WatchDogService()542ffbb0cfSVladimir Kotal WatchDogService() { 552ffbb0cfSVladimir Kotal 562ffbb0cfSVladimir Kotal } 572ffbb0cfSVladimir Kotal 582ffbb0cfSVladimir Kotal /** 592ffbb0cfSVladimir Kotal * Starts a watch dog service for a directory. It automatically reloads the 602ffbb0cfSVladimir Kotal * AuthorizationFramework if there was a change in <b>real-time</b>. 612ffbb0cfSVladimir Kotal * Suitable for plugin development. 622ffbb0cfSVladimir Kotal * 632ffbb0cfSVladimir Kotal * You can control start of this service by a configuration parameter 642ffbb0cfSVladimir Kotal * {@link Configuration#authorizationWatchdogEnabled} 652ffbb0cfSVladimir Kotal * 662ffbb0cfSVladimir Kotal * @param directory root directory for plugins 672ffbb0cfSVladimir Kotal */ start(File directory)682ffbb0cfSVladimir Kotal public void start(File directory) { 692ffbb0cfSVladimir Kotal stop(); 702ffbb0cfSVladimir Kotal 712ffbb0cfSVladimir Kotal if (directory == null || !directory.isDirectory() || !directory.canRead()) { 722ffbb0cfSVladimir Kotal LOGGER.log(Level.INFO, "Watch dog cannot be started - invalid directory: {0}", directory); 732ffbb0cfSVladimir Kotal return; 742ffbb0cfSVladimir Kotal } 752ffbb0cfSVladimir Kotal LOGGER.log(Level.INFO, "Starting watchdog in: {0}", directory); 762ffbb0cfSVladimir Kotal watchDogThread = new Thread(() -> { 772ffbb0cfSVladimir Kotal try { 782ffbb0cfSVladimir Kotal watchDogWatcher = FileSystems.getDefault().newWatchService(); 792ffbb0cfSVladimir Kotal Path dir = Paths.get(directory.getAbsolutePath()); 802ffbb0cfSVladimir Kotal 81*c6f0939bSAdam Hornacek Files.walkFileTree(dir, new SimpleFileVisitor<>() { 822ffbb0cfSVladimir Kotal @Override 832ffbb0cfSVladimir Kotal public FileVisitResult postVisitDirectory(Path d, IOException exc) throws IOException { 842ffbb0cfSVladimir Kotal // attach monitor 852ffbb0cfSVladimir Kotal LOGGER.log(Level.FINEST, "Watchdog registering {0}", d); 862ffbb0cfSVladimir Kotal d.register(watchDogWatcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY); 872ffbb0cfSVladimir Kotal return CONTINUE; 882ffbb0cfSVladimir Kotal } 892ffbb0cfSVladimir Kotal }); 902ffbb0cfSVladimir Kotal 912ffbb0cfSVladimir Kotal LOGGER.log(Level.INFO, "Watch dog started {0}", directory); 922ffbb0cfSVladimir Kotal while (!Thread.currentThread().isInterrupted()) { 932ffbb0cfSVladimir Kotal final WatchKey key; 942ffbb0cfSVladimir Kotal try { 952ffbb0cfSVladimir Kotal key = watchDogWatcher.take(); 962ffbb0cfSVladimir Kotal } catch (ClosedWatchServiceException x) { 972ffbb0cfSVladimir Kotal break; 982ffbb0cfSVladimir Kotal } 992ffbb0cfSVladimir Kotal boolean reload = false; 1002ffbb0cfSVladimir Kotal for (WatchEvent<?> event : key.pollEvents()) { 1012ffbb0cfSVladimir Kotal final WatchEvent.Kind<?> kind = event.kind(); 1022ffbb0cfSVladimir Kotal 1032ffbb0cfSVladimir Kotal if (kind == ENTRY_CREATE || kind == ENTRY_DELETE || kind == ENTRY_MODIFY) { 1042ffbb0cfSVladimir Kotal reload = true; 1052ffbb0cfSVladimir Kotal } 1062ffbb0cfSVladimir Kotal } 1072ffbb0cfSVladimir Kotal if (reload) { 1082ffbb0cfSVladimir Kotal Thread.sleep(THREAD_SLEEP_TIME); // experimental wait if file is being written right now 1092ffbb0cfSVladimir Kotal RuntimeEnvironment.getInstance().getAuthorizationFramework().reload(); 1102ffbb0cfSVladimir Kotal } 1112ffbb0cfSVladimir Kotal if (!key.reset()) { 1122ffbb0cfSVladimir Kotal break; 1132ffbb0cfSVladimir Kotal } 1142ffbb0cfSVladimir Kotal } 1152ffbb0cfSVladimir Kotal } catch (InterruptedException | IOException ex) { 1162ffbb0cfSVladimir Kotal LOGGER.log(Level.FINEST, "Watchdog finishing (exiting)", ex); 1172ffbb0cfSVladimir Kotal Thread.currentThread().interrupt(); 1182ffbb0cfSVladimir Kotal } 1192ffbb0cfSVladimir Kotal LOGGER.log(Level.FINER, "Watchdog finishing (exiting)"); 1202ffbb0cfSVladimir Kotal }, "watchDogService"); 1212ffbb0cfSVladimir Kotal watchDogThread.start(); 1222ffbb0cfSVladimir Kotal } 1232ffbb0cfSVladimir Kotal 1242ffbb0cfSVladimir Kotal /** 1252ffbb0cfSVladimir Kotal * Stops the watch dog service. 1262ffbb0cfSVladimir Kotal */ stop()1272ffbb0cfSVladimir Kotal public void stop() { 1282ffbb0cfSVladimir Kotal if (watchDogWatcher != null) { 1292ffbb0cfSVladimir Kotal try { 1302ffbb0cfSVladimir Kotal watchDogWatcher.close(); 1312ffbb0cfSVladimir Kotal } catch (IOException ex) { 1322ffbb0cfSVladimir Kotal LOGGER.log(Level.WARNING, "Cannot close WatchDogService: ", ex); 1332ffbb0cfSVladimir Kotal } 1342ffbb0cfSVladimir Kotal } 1352ffbb0cfSVladimir Kotal if (watchDogThread != null) { 1362ffbb0cfSVladimir Kotal watchDogThread.interrupt(); 1372ffbb0cfSVladimir Kotal try { 1382ffbb0cfSVladimir Kotal watchDogThread.join(); 1392ffbb0cfSVladimir Kotal } catch (InterruptedException ex) { 1402ffbb0cfSVladimir Kotal LOGGER.log(Level.WARNING, "Cannot join WatchDogService thread: ", ex); 1412ffbb0cfSVladimir Kotal } 1422ffbb0cfSVladimir Kotal } 1435389f557SVladimir Kotal LOGGER.log(Level.INFO, "Watchdog stopped"); 1442ffbb0cfSVladimir Kotal } 1452ffbb0cfSVladimir Kotal } 146