Java 7 实现代码的即改即用

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);
}


五项关键技术:

  1. 代码改动的时候自动更新class,这个部分交由IDE来完成。
  2. 获取当前Java进程的启动命令行
    1. 获取当前进程的PID,由JMX的api完成
    2. 由PID获得命令行参数,这个部分由solaris的pargs完成,其他操作系统也有类似命令
  3. 探测文件夹内容的改动,由Java7的WatchServer完成
  4. 在Java进程内,启动另外一个Java进程。由Java7的ProcessBuilder完成。注意inheritIO这个太方便了。
  5. 同一个main函数,两种执行状态(监控和非监控)。由传入不同的环境变量完成。如果没有WATCH_DOG_TARGET那么就执行真正的main,如果有则把当前进程作为监控进程。

你可能感兴趣的:(Java 7 实现代码的即改即用)