import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.management.ManagementFactory;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import static java.nio.file.StandardWatchEventKinds.*;
public class WatchDog implements Launcher {
private static Logger LOGGER = LoggerFactory.getLogger(WatchDog.class);
public static final String ENV_KEY_TARGET = "WATCH_DOG_TARGET";
private final Launcher launcher;
public WatchDog(Launcher launcher) {
this.launcher = launcher;
}
@Override
public void launch(String[] args) {
if (!startWatchDog()) {
launcher.launch(args);
}
}
private static boolean startWatchDog() {
String target = System.getenv(ENV_KEY_TARGET);
if (null == target) {
LOGGER.debug("Do not have target for watch dog, launch directly");
return false;
}
LOGGER.debug("Watch dog is watching " + target + " ...");
watch(target);
return true;
}
private static void watch(String target) {
try {
int thisProcessId = Integer.valueOf(ManagementFactory.getRuntimeMXBean().getName().split("@")[0]);
String thisProcessCommand = getProcessCommand(thisProcessId);
Process subProcess = launchWithoutWatchDog(thisProcessCommand);
final WatchService watchService = FileSystems.getDefault().newWatchService();
try {
Path targetPath = Paths.get(target);
Files.walkFileTree(targetPath, new SimpleFileVisitor<Path>(){
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
dir.register(watchService, ENTRY_MODIFY, ENTRY_DELETE, ENTRY_CREATE);
return super.preVisitDirectory(dir, attrs);
}
});
while (true) {
WatchKey key = watchService.take();
key.pollEvents();
key.reset();
while (null != (key = watchService.poll())) {
key.pollEvents();
key.reset();
}
subProcess.destroy();
subProcess = launchWithoutWatchDog(thisProcessCommand);
}
} finally {
watchService.close();
}
} catch (Throwable e) {
throw new RuntimeException("Failed to watch " + target, e);
}
}
private static String getProcessCommand(int processId) {
String command = "pargs -l " + processId;
try {
Process process = Runtime.getRuntime().exec(command);
process.waitFor();
return readAll(process.getInputStream());
} catch (Throwable e) {
throw new RuntimeException("Failed to execute " + command, e);
}
}
private static String readAll(InputStream inputStream) throws IOException {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder result = new StringBuilder();
String line;
while (null != (line = reader.readLine())) {
result.append(line);
}
return result.toString();
} finally {
inputStream.close();
}
}
private static Process launchWithoutWatchDog(String command) {
try {
LOGGER.info("Launch: " + command);
ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.command(command.replace("'", "").split(" "));
processBuilder.environment().remove(ENV_KEY_TARGET);
processBuilder.inheritIO();
return processBuilder.start();
} catch (Throwable e) {
throw new RuntimeException("Failed to launch without watch dog: " + command, e);
}
}
}
public interface Launcher {
public void launch(String[] args);
}
五項關鍵技術:
- 代碼改動的時候自動更新class,這個部分交由IDE來完成。
- 獲取當前Java進程的啟動命令行
- 獲取當前進程的PID,由JMX的api完成
- 由PID獲得命令行參數,這個部分由solaris的pargs完成,其他操作系統也有類似命令
- 探測文件夾內容的改動,由Java7的WatchServer完成
- 在Java進程內,啟動另外一個Java進程。由Java7的ProcessBuilder完成。注意inheritIO這個太方便了。
- 同一個main函數,兩種執行狀態(監控和非監控)。由傳入不同的環境變量完成。如果沒有WATCH_DOG_TARGET那么就執行真正的main,如果有則把當前進程作為監控進程。
浙公網安備 33010602011771號