xref: /OpenGrok/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/WatchDogService.java (revision c6f0939b1c668e9f8e1e276424439c3106b3a029)
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