`
裴小星
  • 浏览: 260759 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
8ccf5db2-0d60-335f-a337-3c30d2feabdb
Java NIO翻译
浏览量:27552
F3e939f0-dc16-3d6e-8c0b-3315c810fb91
PureJS开发过程详解
浏览量:71780
07a6d496-dc19-3c71-92cf-92edb5203cef
MongoDB Java ...
浏览量:61921
社区版块
存档分类
最新评论

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

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


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

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

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

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

前期准备

  首先,我们在 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 ...
}

增加文件监控类 FileChangeMonitor

  实现思路是:
  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();
	}
}

在 JSServer 中引入 FileChangeMonitor

  将 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(...)
}

运行并测试

  运行程序,输入http://localhost:8080,可以看到浏览器输出:
Hello,Pei Xiaoxing!

  修改 api.js ,比如改为:
api = {
	sayHello: function(params) {	
		return 'Hello, again!';
	}
};

  保存修改,再次访问http://localhost:8080,可以看到输出变化了:
  Hello, again!


删除文件?

  我们没有对文件删除的行为进行检查,但仍可以“曲线救国”。
  比如我们不想要“ api ”这个对象了,可以把包含如下内容的文件放入 “ scripts ” :
if (typeof api !== 'undefined') {
	delete api;
}

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

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

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

下载附件后,请将 FileChangeMonitor.java 中的 StandardWatchEventKind 改为 StandardWatchEventKinds

16
12
分享到:
评论
6 楼 sp42 2014-08-18  
非常感谢!热部署帮助很大!
5 楼 裴小星 2012-02-23  
washheart163.com 写道
可以看看node.js

node.js 虽然自己的库比较全,但 Rhnio 有 Java 现有的开源资源支撑,都有各自的优势。
相对而言比较熟悉 Java,因此 Rhino 更适合我。
4 楼 washheart163.com 2012-01-11  
可以看看node.js
3 楼 裴小星 2011-05-25  
下最新版看了下,果然改了。
2 楼 裴小星 2011-05-24  
是吗?可能版本还是不同吧。
我的版本还是 java.nio.file.StandardWatchEventKind
1 楼 lizhen1643 2011-05-24  
java.nio.file.StandardWatchEventKind此类貌似已经变成
java.nio.file.StandardWatchEventKinds了

相关推荐

    JDK_7_NIO2

    JDK1.7新特性,NIO2异步学习笔记。

    基于jdk7文件监控定制

    jdk7 原生的监控包含很多编程细节, 实际工作不得不处理这些复杂的细节,这里进行轻量封装 提取出fileCreated,fileModified,fileDeleted,fileRenamed等更好用的接口

    shell脚本:一键部署jdk(带详细注释).sh

    shell脚本:一键部署jdk(带详细注释)

    centos安装jdk1.8时出现没有/lib/ld-linux.so.2:这个文件的原因分析

    -bash: /usr/local/jdk/jdk1.8.0_181/bin/java: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory 安装完后 java -version 查看版本出现: 原因是:没有那个文件或目录,找了很久发现需要...

    JAVA NIO 按行读取大文件,支持 GB级别

    本类,是专门为了处理大文件,按行读取开发的类。 采用读文件的缓存 fbb 1024*5 行缓存 bb 256 字节 设计思想: 每次通过nio读取字节到 fbb中 然后对fbb自己中的内容进行行判断即 10 回车 13 行号 0 文件...

    FileMonitor 基于jdk7文件监控WatchService

    屏蔽jdk7文件监控细节,提取fileCreated,fileModified,fileDeleted,fileRenamed等方法,API更简洁、好用

    使用jdk7的nio2操作文件拷贝和剪切示例

    使用jdk7的NIO2进行文件或文件夹的拷贝移动操作。可以自动创建路径,差异化更新文件,简单的出错重连机制

    centos7安装部署jdk7

    centos7安装部署jdk7,全网最全面的linux环境下安装jdk的安装步骤,手把手教你一步一步安装jdk,各种细节一应俱全

    bcprov-jdk15on-1.59-API文档-中文版.zip

    赠送jar包:bcprov-jdk15on-1.59.jar; 赠送原API文档:bcprov-jdk15on-1.59-javadoc.jar; 赠送源代码:bcprov-jdk15on-1.59-sources.jar; 赠送Maven依赖信息文件:bcprov-jdk15on-1.59.pom; 包含翻译后的API文档...

    mapstruct-jdk8-1.2.0.Final-API文档-中文版.zip

    赠送jar包:mapstruct-jdk8-1.2.0.Final.jar; 赠送原API文档:mapstruct-jdk8-1.2.0.Final-javadoc.jar; 赠送源代码:mapstruct-jdk8-1.2.0.Final-sources.jar; 赠送Maven依赖信息文件:mapstruct-jdk8-1.2.0....

    bcprov-jdk15on-1.56.jar中文文档.zip

    (2)有时,一套Java组件会有多个jar,所以在下载前,请仔细阅读本篇描述,以确保这就是你需要的文件; # Maven依赖: ``` &lt;groupId&gt;org.bouncycastle &lt;artifactId&gt;bcprov-jdk15on *** ``` # Gradle依赖: `...

    解决-BASH: /HOME/JAVA/JDK1.8.0_221/BIN/JAVA: 权限不够问题

    1)进入存放jdk文件的文件夹路径 我这里是 usr/local/jdk/ 2)输入命令 chmod 777 jdk1.8.0_221/bin/java修改权限 3)再次输入 java -version 成功 总结 以上所述是小编给大家介绍的解决-BASH: /HOME/JAVA/JDK1.8.0...

    bcpkix-jdk15on-1.68-API文档-中英对照版.zip

    赠送jar包:bcpkix-jdk15on-1.68.jar; 赠送原API文档:bcpkix-jdk15on-1.68-javadoc.jar; 赠送源代码:bcpkix-jdk15on-1.68-sources.jar; 赠送Maven依赖信息文件:bcpkix-jdk15on-1.68.pom; 包含翻译后的API文档...

    bcpkix-jdk15on-1.58-API文档-中文版.zip

    赠送jar包:bcpkix-jdk15on-1.58.jar; 赠送原API文档:bcpkix-jdk15on-1.58-javadoc.jar; 赠送源代码:bcpkix-jdk15on-1.58-sources.jar; 赠送Maven依赖信息文件:bcpkix-jdk15on-1.58.pom; 包含翻译后的API文档...

    bcpkix-jdk15on-1.59-API文档-中文版.zip

    赠送jar包:bcpkix-jdk15on-1.59.jar; 赠送原API文档:bcpkix-jdk15on-1.59-javadoc.jar; 赠送源代码:bcpkix-jdk15on-1.59-sources.jar; 赠送Maven依赖信息文件:bcpkix-jdk15on-1.59.pom; 包含翻译后的API文档...

    JDK7新特性(完整篇)

    1.4 JDK7新特性&lt;四&gt; NIO2.0 文件系统 . . . 1.5 JDK7新特性&lt;五&gt; fork/join 框架 . . . . . 1.6 JDK7新特性&lt;六&gt; 监听文件系统的更改 1.7 JDK7新特性&lt;七&gt; 遍历文件树 . . . . . . . 1.8 JDK7新特性&lt;八&gt;异步io/AIO ...

    jdk7 安装文件 windows 系统

    jdk7 安装文件 系统

    mapstruct-jdk8-1.2.0.Final-API文档-中英对照版.zip

    赠送jar包:mapstruct-jdk8-1.2.0.Final.jar; 赠送原API文档:mapstruct-jdk8-1.2.0.Final-javadoc.jar; 赠送源代码:mapstruct-jdk8-1.2.0.Final-sources.jar; 赠送Maven依赖信息文件:mapstruct-jdk8-1.2.0....

    利用JDK7的NIO2.0进行I/O读写和监视

    NULL 博文链接:https://sharong.iteye.com/blog/1569883

Global site tag (gtag.js) - Google Analytics