Pure JS (2): 热部署 (利用 JDK 7 NIO 监控文件变化)

[align=center][size=large][b]Pure JS (2): 热部署 (利用 JDK 7 NIO 监控文件变化)[/b][/size][/align]

  接着上一篇文章([url]http://xxing22657-yahoo-com-cn.iteye.com/blog/1052485[/url])的话题,我们来谈谈服务器端 JS 的热部署问题。

  由于 JavaScript 是动态语言,动态编译并执行脚本并不困难。所以关键是监控文件变化。
  这里推荐使用 JDK 7 NIO 中新增的 Watch Service API。Watch Service API 将尽可能使用操作系统的文件 API ,当操作系统不支持时,则使用轮询。简而言之,就是 JNI + 轮询,自己实现轮询倒不难,但要实现跨平台的 JNI 调用就很啰嗦了。

  JDK 7 还没有正式发布,可以先到这里下一个 Early Access 版 :
  [url]http://jdk7.java.net/[/url]
  安装完后可以通过 eclipse 的 Window -> Preference -> Java -> Intalled JREs 添加刚安装的 JRE,并勾选为默认的 JRE 环境。

  关于 Watch Service API 的使用说明可以参考这里:
  [url]http://download.oracle.com/javase/tutorial/essential/io/notification.html[/url]

[align=center][size=medium][b]前期准备[/b][/size][/align]
  首先,我们在 src 目录下创建 purejs package ,将之前编写的 JSServer,JSEngine 和 JSServlet 都移动到这个 package 下。因为后面将会用到 import static 的写法,没有 package 名称的话会报错。
  然后,我们需要在 JSEngine 类中增加执行单个文件的方法( excuteFile(File file) ),并在 excuteFiles(String dir) 方法中引用它。

  现在的 JSEngine 应该是这样的:

public class JSEngine {
// Declaration of engine, compliable and invocable ...

static {
// Initialize of engine, compliable and invocable ...
}

public static void excuteFiles(String dir) throws ScriptException,
IOException {
for (File file : new File(dir).listFiles()) {
excuteFile(file);
}
}

public static void excuteFile(File file) throws ScriptException,
IOException {
FileReader reader = new FileReader(file);
compliable.compile(reader).eval();
reader.close();
}

// Defination of invoke ...
}

[align=center][size=medium][b]增加文件监控类 FileChangeMonitor[/b][/size][/align]
  实现思路是:
  1. 创建一个新的线程。
  2. 在线程中创建 Watch Service 的实例 watcher 。
  3. 在 watcher 上注册文件的创建和修改事件。
  4. 在循环中使用 watcher.take() 等待事件的发生。
  5. 遍历捕捉到的所有事件,获取文件名,通知 Lisener 文件的更改。

  具体实现如下:

package purejs;

import static java.nio.file.StandardWatchEventKinds.*;

import java.io.*;
import java.nio.file.*;

public class FileChangeMonitor {
public interface FileChangeListener {
public void fileChanged(File file);
}

public static void monit(final String dir,
final FileChangeListener listener) {
new Thread() {
public void run() {
Path path = new File(dir).toPath();
WatchService watcher;
try {
watcher = FileSystems.getDefault().newWatchService();
path.register(watcher, ENTRY_CREATE, ENTRY_MODIFY);
} catch (IOException e) {
e.printStackTrace();
return;
}

while (true) {
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException _) {
return;
}

processKey(path, key, listener);
}
};
}.start();
}

private static void processKey(Path path, WatchKey key,
FileChangeListener listener) {
for (WatchEvent<?> event : key.pollEvents()) {
if (event.kind() == OVERFLOW) {
continue;
}

Path fileName = (Path) event.context();
File file = path.resolve(fileName).toFile();
listener.fileChanged(file);
}

key.reset();
}
}

[align=center][size=medium][b]在 JSServer 中引入 FileChangeMonitor[/b][/size][/align]
  将 main 函数的执行分成两个步骤:
  1. 加载脚本。这里又分为两个步骤:
    【执行脚本】 直接调用 JSEngine 执行“ scripts ”目录下的脚本。
    【设置监控】 调用 FileChangeMonitor.monit(...),文件变更时再次执行脚本。
  2. 启动 Server,与之前相同。

public class JSServer {
public static void main(String[] args) throws Exception {
loadScripts();
startServer();
}

private static void loadScripts() throws ScriptException, IOException {
JSEngine.excuteFiles("scripts");
FileChangeMonitor.monit("scripts", new FileChangeListener() {
public void fileChanged(File file) {
try {
JSEngine.excuteFile(file);
} catch (ScriptException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}

// Defination of startServer() and configContext(...)
}

[align=center][size=medium][b]运行并测试[/b][/size][/align]
  运行程序,输入[url]http://localhost:8080[/url],可以看到浏览器输出:

Hello,Pei Xiaoxing!

  修改 api.js ,比如改为:

api = {
sayHello: function(params) {
return 'Hello, again!';
}
};

  保存修改,再次访问[url]http://localhost:8080[/url],可以看到输出变化了:

  Hello, again!


[align=center][size=medium][b]删除文件?[/b][/size][/align]
  我们没有对文件删除的行为进行检查,但仍可以“曲线救国”。
  比如我们不想要“ api ”这个对象了,可以把包含如下内容的文件放入 “ scripts ” :

if (typeof api !== 'undefined') {
delete api;
}

  因为我们监控了文件的创建,所以这个脚本将很快被执行。这样一来,“ api ”对象就被删除了。
  再次访问 [url]http://localhost:8080[/url] ,控制台将输出如下错误:

ReferenceError: "api" is not defined.

  然后就可以把这个文件和 api.js 一起删除了。

[color=red]最新版的 Java 7 中,java.nio.file.StandardWatchEventKind 已改为 java.nio.file.StandardWatchEventKinds,文章已更新。

下载附件后,请将 FileChangeMonitor.java 中的 StandardWatchEventKind 改为 StandardWatchEventKinds[/color]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值