diff --git a/pom.xml b/pom.xml index 8978c433730c5452acf48f31f49386aab4c9b208..616931ed9e1b4457b98ca0a4c554d3d8cc2fbad1 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ https://jfinal.com jfinal undertow - + UTF-8 diff --git a/src/main/java/com/jfinal/server/undertow/hotswap/HotSwapWatcher.java b/src/main/java/com/jfinal/server/undertow/hotswap/HotSwapWatcher.java index 5043b7de9fb25f7191162455aac992e32c6602b1..5eeece8db8c8794298675b7bd17363a1110f2a23 100644 --- a/src/main/java/com/jfinal/server/undertow/hotswap/HotSwapWatcher.java +++ b/src/main/java/com/jfinal/server/undertow/hotswap/HotSwapWatcher.java @@ -1,12 +1,12 @@ /** * Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com). - *

+ * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

+ * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -41,159 +41,159 @@ import com.jfinal.server.undertow.UndertowServer; * 监听 class path 下 .class 文件变动,触发 UndertowServer.restart() */ public class HotSwapWatcher extends Thread { - - protected UndertowServer undertowServer; - - // protected int watchingInterval = 1000; // 1900 与 2000 相对灵敏 - protected int watchingInterval = 500; - - protected List watchingPaths; - private WatchKey watchKey; - protected volatile boolean running = true; - - public HotSwapWatcher(UndertowServer undertowServer) { - setName("HotSwapWatcher"); - // 避免在调用 deploymentManager.stop()、undertow.stop() 后退出 JVM - setDaemon(false); - setPriority(Thread.MAX_PRIORITY); - - this.undertowServer = undertowServer; - this.watchingPaths = buildWatchingPaths(); - } - - protected List buildWatchingPaths() { - Set watchingDirSet = new HashSet<>(); - String[] classPathArray = System.getProperty("java.class.path").split(File.pathSeparator); - for (String classPath : classPathArray) { - buildDirs(new File(classPath.trim()), watchingDirSet); - } - - List dirList = new ArrayList(watchingDirSet); - Collections.sort(dirList); - - List pathList = new ArrayList(dirList.size()); - for (String dir : dirList) { - pathList.add(Paths.get(dir)); - } - - return pathList; - } - - private void buildDirs(File file, Set watchingDirSet) { - if (file.isDirectory()) { - watchingDirSet.add(file.getPath()); - - File[] fileList = file.listFiles(); - for (File f : fileList) { - buildDirs(f, watchingDirSet); - } - } - } - - public void run() { - try { - doRun(); - } catch (Throwable e) { - throw new RuntimeException(e); - } - } - - protected void doRun() throws IOException { - ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); - WatchService watcher = FileSystems.getDefault().newWatchService(); - addShutdownHook(watcher); - - for (Path path : watchingPaths) { - path.register( - watcher, - // StandardWatchEventKinds.ENTRY_DELETE, - StandardWatchEventKinds.ENTRY_MODIFY, - StandardWatchEventKinds.ENTRY_CREATE - ); - } + + protected UndertowServer undertowServer; + + // protected int watchingInterval = 1000; // 1900 与 2000 相对灵敏 + protected int watchingInterval = 500; + + protected List watchingPaths; + private WatchKey watchKey; + protected volatile boolean running = true; + + public HotSwapWatcher(UndertowServer undertowServer) { + setName("HotSwapWatcher"); + // 避免在调用 deploymentManager.stop()、undertow.stop() 后退出 JVM + setDaemon(false); + setPriority(Thread.MAX_PRIORITY); + + this.undertowServer = undertowServer; + this.watchingPaths = buildWatchingPaths(); + } + + protected List buildWatchingPaths() { + Set watchingDirSet = new HashSet<>(); + String[] classPathArray = System.getProperty("java.class.path").split(File.pathSeparator); + for (String classPath : classPathArray) { + buildDirs(new File(classPath.trim()), watchingDirSet); + } + + List dirList = new ArrayList(watchingDirSet); + Collections.sort(dirList); + + List pathList = new ArrayList(dirList.size()); + for (String dir : dirList) { + pathList.add(Paths.get(dir)); + } + + return pathList; + } + + private void buildDirs(File file, Set watchingDirSet) { + if (file.isDirectory()) { + watchingDirSet.add(file.getPath()); + + File[] fileList = file.listFiles(); + for (File f : fileList) { + buildDirs(f, watchingDirSet); + } + } + } + + public void run() { + try { + doRun(); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + protected void doRun() throws IOException { + ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); + WatchService watcher = FileSystems.getDefault().newWatchService(); + addShutdownHook(watcher); + + for (Path path : watchingPaths) { + path.register( + watcher, + // StandardWatchEventKinds.ENTRY_DELETE, + StandardWatchEventKinds.ENTRY_MODIFY, + StandardWatchEventKinds.ENTRY_CREATE + ); + } + + executorService.scheduleAtFixedRate(() -> { + watch(watcher); + }, 0, 500, TimeUnit.MILLISECONDS); // while (running) { // watch(watcher); // } - executorService.scheduleAtFixedRate(() -> { - watch(watcher); - }, 0, 500, TimeUnit.MILLISECONDS); - } - - private void watch(WatchService watcher) { - try { - // watchKey = watcher.poll(watchingInterval, TimeUnit.MILLISECONDS); // watcher.take(); 阻塞等待 - // 比较两种方式的灵敏性,或许 take() 方法更好,起码资源占用少,测试 windows 机器上的响应 - watchKey = watcher.take(); - - if (watchKey == null) { - // System.out.println(System.currentTimeMillis() / 1000); - //continue ; - return; - } - } catch (Throwable e) { // 控制台 ctrl + c 退出 JVM 时也将抛出异常 - running = false; - if (e instanceof InterruptedException) { // 另一线程调用 hotSwapWatcher.interrupt() 抛此异常 - // while 循环没有通过 while (running && !Thread.currentThread().isInterrupted()) 控制流程,下一行代码可以不需要 - Thread.currentThread().interrupt(); // Restore the interrupted status - } - return; - } - - List> watchEvents = watchKey.pollEvents(); - for (WatchEvent event : watchEvents) { - String fileName = event.context().toString(); - if (fileName.endsWith(".class")) { - if (undertowServer.isStarted()) { - undertowServer.restart(); - resetWatchKey(); - - while ((watchKey = watcher.poll()) != null) { - // System.out.println("---> poll() "); - watchKey.pollEvents(); - resetWatchKey(); - } - - break; - } - } - } - - resetWatchKey(); - } - - private void resetWatchKey() { - if (watchKey != null) { - watchKey.reset(); - watchKey = null; - } - } - - /** - * 添加关闭钩子在 JVM 退出时关闭 WatchService - * - * 注意:addShutdownHook 方式添加的回调在 kill -9 pid 强制退出 JVM 时不会被调用 - * kill 不带参数 -9 时才回调 - */ - protected void addShutdownHook(WatchService watcher) { - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - try { - watcher.close(); - } catch (Throwable e) { - UndertowKit.doNothing(e); - } - })); - } - - public void exit() { - running = false; - try { - this.interrupt(); - } catch (Throwable e) { - UndertowKit.doNothing(e); - } - } - + } + + private void watch(WatchService watcher) { + try { + // watchKey = watcher.poll(watchingInterval, TimeUnit.MILLISECONDS); // watcher.take(); 阻塞等待 + // 比较两种方式的灵敏性,或许 take() 方法更好,起码资源占用少,测试 windows 机器上的响应 + watchKey = watcher.take(); + + if (watchKey == null) { + // System.out.println(System.currentTimeMillis() / 1000); + //continue ; + return; + } + } catch (Throwable e) { // 控制台 ctrl + c 退出 JVM 时也将抛出异常 + running = false; + if (e instanceof InterruptedException) { // 另一线程调用 hotSwapWatcher.interrupt() 抛此异常 + // while 循环没有通过 while (running && !Thread.currentThread().isInterrupted()) 控制流程,下一行代码可以不需要 + Thread.currentThread().interrupt(); // Restore the interrupted status + } + return; + } + List> watchEvents = watchKey.pollEvents(); + for (WatchEvent event : watchEvents) { + String fileName = event.context().toString(); + if (fileName.endsWith(".class")) { + if (undertowServer.isStarted()) { + undertowServer.restart(); + resetWatchKey(); + + while ((watchKey = watcher.poll()) != null) { + // System.out.println("---> poll() "); + watchKey.pollEvents(); + resetWatchKey(); + } + + break; + } + } + } + + resetWatchKey(); + } + + private void resetWatchKey() { + if (watchKey != null) { + watchKey.reset(); + watchKey = null; + } + } + + /** + * 添加关闭钩子在 JVM 退出时关闭 WatchService + * + * 注意:addShutdownHook 方式添加的回调在 kill -9 pid 强制退出 JVM 时不会被调用 + * kill 不带参数 -9 时才回调 + */ + protected void addShutdownHook(WatchService watcher) { + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try { + watcher.close(); + } catch (Throwable e) { + UndertowKit.doNothing(e); + } + })); + } + + public void exit() { + running = false; + try { + this.interrupt(); + } catch (Throwable e) { + UndertowKit.doNothing(e); + } + } + // public static void main(String[] args) throws InterruptedException { // HotSwapWatcher watcher = new HotSwapWatcher(null); // watcher.start();