1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * See LICENSE.txt included in this distribution for the specific 9 * language governing permissions and limitations under the License. 10 * 11 * When distributing Covered Code, include this CDDL HEADER in each 12 * file and include the License file at LICENSE.txt. 13 * If applicable, add the following below this CDDL HEADER, with the 14 * fields enclosed by brackets "[]" replaced with your own identifying 15 * information: Portions Copyright [yyyy] [name of copyright owner] 16 * 17 * CDDL HEADER END 18 */ 19 20 /* 21 * Copyright (c) 2006, 2021, Oracle and/or its affiliates. All rights reserved. 22 */ 23 package org.opengrok.indexer.configuration; 24 25 import org.opengrok.indexer.logger.LoggerFactory; 26 27 import java.io.File; 28 import java.io.IOException; 29 import java.nio.file.ClosedWatchServiceException; 30 import java.nio.file.FileSystems; 31 import java.nio.file.FileVisitResult; 32 import java.nio.file.Files; 33 import java.nio.file.Path; 34 import java.nio.file.Paths; 35 import java.nio.file.SimpleFileVisitor; 36 import java.nio.file.WatchEvent; 37 import java.nio.file.WatchKey; 38 import java.nio.file.WatchService; 39 import java.util.logging.Level; 40 import java.util.logging.Logger; 41 42 import static java.nio.file.FileVisitResult.CONTINUE; 43 import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; 44 import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE; 45 import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; 46 47 public class WatchDogService { 48 private static final Logger LOGGER = LoggerFactory.getLogger(WatchDogService.class); 49 50 private Thread watchDogThread; 51 private WatchService watchDogWatcher; 52 public static final int THREAD_SLEEP_TIME = 2000; 53 WatchDogService()54 WatchDogService() { 55 56 } 57 58 /** 59 * Starts a watch dog service for a directory. It automatically reloads the 60 * AuthorizationFramework if there was a change in <b>real-time</b>. 61 * Suitable for plugin development. 62 * 63 * You can control start of this service by a configuration parameter 64 * {@link Configuration#authorizationWatchdogEnabled} 65 * 66 * @param directory root directory for plugins 67 */ start(File directory)68 public void start(File directory) { 69 stop(); 70 71 if (directory == null || !directory.isDirectory() || !directory.canRead()) { 72 LOGGER.log(Level.INFO, "Watch dog cannot be started - invalid directory: {0}", directory); 73 return; 74 } 75 LOGGER.log(Level.INFO, "Starting watchdog in: {0}", directory); 76 watchDogThread = new Thread(() -> { 77 try { 78 watchDogWatcher = FileSystems.getDefault().newWatchService(); 79 Path dir = Paths.get(directory.getAbsolutePath()); 80 81 Files.walkFileTree(dir, new SimpleFileVisitor<>() { 82 @Override 83 public FileVisitResult postVisitDirectory(Path d, IOException exc) throws IOException { 84 // attach monitor 85 LOGGER.log(Level.FINEST, "Watchdog registering {0}", d); 86 d.register(watchDogWatcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY); 87 return CONTINUE; 88 } 89 }); 90 91 LOGGER.log(Level.INFO, "Watch dog started {0}", directory); 92 while (!Thread.currentThread().isInterrupted()) { 93 final WatchKey key; 94 try { 95 key = watchDogWatcher.take(); 96 } catch (ClosedWatchServiceException x) { 97 break; 98 } 99 boolean reload = false; 100 for (WatchEvent<?> event : key.pollEvents()) { 101 final WatchEvent.Kind<?> kind = event.kind(); 102 103 if (kind == ENTRY_CREATE || kind == ENTRY_DELETE || kind == ENTRY_MODIFY) { 104 reload = true; 105 } 106 } 107 if (reload) { 108 Thread.sleep(THREAD_SLEEP_TIME); // experimental wait if file is being written right now 109 RuntimeEnvironment.getInstance().getAuthorizationFramework().reload(); 110 } 111 if (!key.reset()) { 112 break; 113 } 114 } 115 } catch (InterruptedException | IOException ex) { 116 LOGGER.log(Level.FINEST, "Watchdog finishing (exiting)", ex); 117 Thread.currentThread().interrupt(); 118 } 119 LOGGER.log(Level.FINER, "Watchdog finishing (exiting)"); 120 }, "watchDogService"); 121 watchDogThread.start(); 122 } 123 124 /** 125 * Stops the watch dog service. 126 */ stop()127 public void stop() { 128 if (watchDogWatcher != null) { 129 try { 130 watchDogWatcher.close(); 131 } catch (IOException ex) { 132 LOGGER.log(Level.WARNING, "Cannot close WatchDogService: ", ex); 133 } 134 } 135 if (watchDogThread != null) { 136 watchDogThread.interrupt(); 137 try { 138 watchDogThread.join(); 139 } catch (InterruptedException ex) { 140 LOGGER.log(Level.WARNING, "Cannot join WatchDogService thread: ", ex); 141 } 142 } 143 LOGGER.log(Level.INFO, "Watchdog stopped"); 144 } 145 } 146