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

Pure JS (1): 在 jetty 上运行 JavaScript

阅读更多
Pure JS (1): 在 jetty 上运行 JavaScript


  所谓 Pure JS ,就是客户端和服务器端程序都用 JavaScript 编写。

  实现思路是:
  1. 客户端发起 Ajax 请求,请求的数据格式为 JSON ,方法为 POST
  2. 服务器端用 Jetty 接收请求
  3. 用 Java 6 ScriptEngine 执行 JavaScript 脚本
  4. 结果以 JSON 格式返回

  另外,在系统启动时需要对所有脚本进行初始加载。

  接下来就按照这个思路逐步进行。

客户端 Ajax 请求

  在 Eclipse 中建立 Java 工程,新建 webapp 目录和 webapp/js 目录。
  这里需要用到 jQuery 1.6 和一个 $.toJSON 插件 (json.js),可以在附件中找到。

  webapp 下的 index.html 的代码如下:
<script src='js/jquery.js'></script>
<script src='js/json.js'></script>
<script src='js/proxy.js'></script>
<script src='js/app.js'></script>
  实际上只是依次引用了多个js文件。

  proxy.js 的作用是利用 jQuery 发起 Ajax 请求。
  实现如下:
function proxy(request, success, failure) {
	request = $.toJSON(request);

	$.post('api', { request : request }, function(result){
		if (result.success) {
			success && success(result.data);
		} else if (failure) {
			failure(result.error);
		} else {
			alert('Operation Failed!');
		}
	}, 'json');
}

  参数解释如下:

  【requset】 请求对象,包含两个属性:
    【action】 需要服务器端执行的动作,如 'sayHello' 等
    【params】 请求参数,是一个 JavaScript Object, 如 { name: 'Pei Xiaoxing' } ,也可以是更复杂的对象

  【success】 执行成功是需要执行的函数 (服务器端返回的 result.success 为 false )
  【failure】 执行失败时需要执行的函数 (服务器端返回的 result.success 为 true )

  首先将 requset 转为字符串,
  然后调用 jQuery 的 post() 函数,url 指定为 'api' ,这是服务器端处理 AJAX 请求的 Servlet 的路径;
  最后根据返回的 result.success 分别执行回调函数 success  和 failure。


  app.js 的实现如下:
$(function(){
	var request = {
		action: 'sayHello',
		params: { name: 'Pei Xiaoxing' }
	};

	proxy(request, function(data) {
		$('body').html(data);
	});
});

  构造一个requset对象作为 proxy 的第一个参数,调用刚刚实现的 proxy 函数;
  服务器端返回成功信息时,就将数据填充到 body 。

服务器端 Jetty 接收请求

  利用 Jetty 接收服务请求,分为两类:
  1. 资源请求,如 html , js 之类的文件,映射到路径 “/”。
  2. API请求,如客户端提交的 “ sayHello ” 动作,映射到路径 “/api”。

  同时,在启动 Server 前,先执行所有服务器端脚本,以获得所有动作的定义。

  JSServer 实现如下:
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;

public class JSServer {
	public static void main(String[] args) throws Exception {
		JSEngine.excuteFiles("scripts");
		startServer();
	}

	private static void startServer() throws Exception, InterruptedException {
		Server server = new Server(8080);
		configContext(server);
		server.start();
		server.join();
	}

	private static void configContext(Server server) {
		ServletContextHandler context = new ServletContextHandler(
				ServletContextHandler.SESSIONS);
		context.setContextPath("/");
		context.setResourceBase("webapp");
		server.setHandler(context);

		context.addServlet(new ServletHolder(new DefaultServlet()), "/");
		context.addServlet(new ServletHolder(new JSServlet()), "/api");
	}
}

  main 函数中做了两件事:
  1. 执行 “scripts” 文件夹下的所有 js 脚本。
  2. 启动 jetty server

  startServer() 函数新建一个监听 8080 端口的 Server ,配置 Context 并启动。
  configContext() 函数设置请求路径为 "/",以及资源路径为“webapp”,并添加两个 Servlet 的映射。

JSEngine : Java 6 ScriptEngine 封装

  JSEngine 对 Java 6 ScriptEngine 进行简单封装,这纯粹是个人习惯了,
  因为使用 ScriptEngine 的时候总是要进行各种强制转换,干脆封装在一个类中了。

  实现如下:
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

import javax.script.Compilable;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class JSEngine {
	private static ScriptEngine engine;
	private static Compilable compliable;
	private static Invocable invocable;

	static {
		ScriptEngineManager manager = new ScriptEngineManager();
		engine = manager.getEngineByName("javascript");
		compliable = (Compilable) engine;
		invocable = (Invocable) engine;
	}

	public static void excuteFiles(String dir) throws ScriptException,
			IOException {
		for (File file : new File(dir).listFiles()) {
			FileReader reader = new FileReader(file);
			compliable.compile(reader).eval();
			reader.close();
		}
	}

	public static String invoke(String func, Object... args)
			throws NoSuchMethodException, ScriptException {
		return (String) invocable.invokeFunction(func, args);
	}
}

  excuteFiles 的作用是编译并执行所指定的目录下的所有 js 脚本,invoke 的作用是调用特定函数。

JSServlet :处理 Ajax 请求

  JSServlet 用于接收 Ajax ,并执行相应的 js 脚本,实现如下:
import java.io.IOException;

import javax.script.ScriptException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class JSServlet extends HttpServlet {
	private static final long serialVersionUID = -7419703636280795226L;
	private static final String ERROR
		= "{\"error\": \"Server internal error.\", \"success\": flase}";

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res)
			throws ServletException, IOException {
		String result;
		try {
			result = JSEngine.invoke("run", req);
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
			result = ERROR;
		} catch (ScriptException e) {
			e.printStackTrace();
			result = ERROR;
		}

		res.getWriter().write(result);
	}
}

  service() 函数将 HttpServletRequest req 直接作为参数传入,调用 js 脚本 run() 函数,
  取得返回结果,并写入到 response 中。
  出错则返回 “ Server internal error. ”。

  run() 函数是 js 脚本的入口,完成路径映射,JSON 解析和序列化等工作。

编写服务器端脚本

  服务器端脚本有两个:
  1. api.js 包含所请求的动作的逻辑;
  2. run.js 脚本的执行入口,接收 req 参数,并执行指定脚本。

  api.js 的实现如下:
api = {
	sayHello: function(params) {	
		return 'Hello, ' + params.name + '!';
	}
};

  返回类似 “ Hello, xxx! ” 的信息


  run.js 的实现如下:
function run(req) {
	var ret;
	try {		
		var request = JSON.parse(req.getParameter('request'));
		var action = request.action;
		var params = request.params;

		ret = { data : api[action](params), success : true }
	} catch (e) {
		println(e);
		ret = { error : e.toString(), success : false }
	}

	return JSON.stringify(ret);
}

  首先将 JSON 字符串请求转换为 JavaScript 对象,并提取 action 和 params 。
  然后调用 “api” 对象的 “ action ” 方法, 参数为“ params ”,并获得返回数据。返回数据也可以是复杂对象。
  最后将返回结果序列化为字符串;出错则将出错信息输出并返回给客户端。

  其中 JSON.parse ,JSON.stringify , println 都是内置函数。(JDK 6 中 JSON 对象还不是内置的,请查看下面红字部分。)

运行并查看结果

  选择 JSServer 类 ,右键选择 “ Run as Java Application ” , 打开浏览器,输入 “ http://localhost:8080 ” 并回车,可以看到浏览器输出结果如下:
Hello, Pei Xiaoxing!

加入Controller?

  如果 api 的形式是 api.controller.action ,那么可以将 app.js 中的 request 对象改为:
var request = {
	controller: 'hello'
	action: 'say',
	params: { name: 'Pei Xiaoxing' }
};

  并修改 run.js 中参数提取的方式:
var controller = requset.controller;
var action = request.action;
var params = request.params;

ret = { data : api[controller][action](params), success : true }

  api 对象的定义可以分开写,比如 api.hello.js 定义一个 controller :
var api = api || {};

api.hello = {
    say: function(){ ... }
}

  api.anotherHello.js 定义另一个 controller :
var api = api || {};

api.anotherHello = {
    anotherSay: function(){ ... }
}


REST 风格 URL ? Session, Cookie ?

  对于 REST 风格 URL,可以在 run 函数中通过 req.getRequestURI() 获取请求路径,然后直接对路径进行解析。
  同样,也可以通过 req.getSession() 和 req.getCookies() 获取 session 和 cookie 。



  以上就是我们的第一个 Server 端 JS 程序了。

  当然,只是运行这种简单的脚本没有多少意义,但 ScriptEngine 的优势是可以直接导入 Java API。
  因此 Java 中能做的,在我们的服务器端也都能做。


  关于 Server 端 JavaScript 程序的后续研究的列表如下:

  热部署 (已完成)
  http://xxing22657-yahoo-com-cn.iteye.com/blog/1054496

  文件上传与下载(已完成)
  http://xxing22657-yahoo-com-cn.iteye.com/blog/1068055
  http://xxing22657-yahoo-com-cn.iteye.com/blog/1070383

  利用 MongoDB 进行数据存储(已完成)
  http://xxing22657-yahoo-com-cn.iteye.com/blog/1071205
  http://xxing22657-yahoo-com-cn.iteye.com/blog/1076016
  http://xxing22657-yahoo-com-cn.iteye.com/blog/1097596

  仿 Jquery Template 的服务器端模板引擎(已完成)
  http://xxing22657-yahoo-com-cn.iteye.com/blog/1112920
  http://xxing22657-yahoo-com-cn.iteye.com/blog/1113665
  http://xxing22657-yahoo-com-cn.iteye.com/blog/1114521

  完善框架功能(已完成 1/6)
  http://xxing22657-yahoo-com-cn.iteye.com/blog/1121314

  其他模板引擎 (FreeMarker,Velocity,仿 JSP)
  数据库操作(SQLite Driver, MySQL JDBC)
  ORM(ibatis 等)
  其他NoSQL数据库(CouchDB等)
  分布式缓存 (Memcached 等)

  有朋友指出运行时会出现 "JSON" is not defined 的错误,这是因为 JDK 6 ScriptEngine 中确实没有定义 JSON 对象。因为我自己运行时使用了 JRE 7 预览版,所以没有发现这个问题。

  请解压附件中的 json.rar ,并将解压后得到的 json.js 放到“ scripts ”目录下。
20
16
分享到:
评论
7 楼 xuhang1128 2011-06-08  
xuhang1128 写道
裴小星 写道
yangwen13 写道
运行会报异常:
ReferenceError: "JSON" is not defined.
run.js需要在哪里引入?


谢谢指出。
增加了一个附件,请查看。

 

6 楼 裴小星 2011-05-22  
key232323 写道
有更轻巧高效的 Java Web Server ?

这个play! framework里有servlet形式,也有就是基于mina的一个非web container的http server,

ZHH2009(douyu的作者)最近好像也在搞这个,打算把netty nio弄出来,我很期待他的进展。

我的mvc中的逻辑处理单元也屏蔽了servlet api,但相当粗糙,你肯定会失望的很。我给你个链接,如果感兴趣请看下。(只看org.dy.web.core就行)

http://wx.cisee.net/Increx/index.php/Index/download


好,我研究研究。谢谢。
5 楼 key232323 2011-05-22  
有更轻巧高效的 Java Web Server ?

这个play! framework里有servlet形式,也有就是基于mina的一个非web container的http server,

ZHH2009(douyu的作者)最近好像也在搞这个,打算把netty nio弄出来,我很期待他的进展。

我的mvc中的逻辑处理单元也屏蔽了servlet api,但相当粗糙,你肯定会失望的很。我给你个链接,如果感兴趣请看下。(只看org.dy.web.core就行)

http://wx.cisee.net/Increx/index.php/Index/download
4 楼 裴小星 2011-05-22  
to key232323:

我觉得没什么关系,语言偏好不同。

估计将来的 Java ,会跟现在的 C/C++ 差不多,成为基础性的语言;
而开发应用的语言将会是继承 Java 优点又更加灵活方便的语言,就像 Java 对 C++ 的继承一样。
Groovy 、 JavaScript 、 Scala 等都是候选。
不过 Oracle 收购了 Sun ,Java 的发展多少有点令人担忧。


我看到你的Blog提到 Java 小型应用的性能问题。其实我们都做到这个地步了,完全可以不用servlet container,但是不知道有没有更轻巧高效的 Java Web Server ?

你那个 groovy mvc 在哪里?能给个链接吗?
3 楼 key232323 2011-05-22  
哎——我用groovy搞了一个mvc,跟你这个思路一摸一样,而且

    热部署
  文件上传与下载
  利用 JDBC 连接 MySQL
  模板引擎 (FreeMarker,Velocity)
  ORM(ibatis 等)
  NoSQL数据库(MongoDB,CouchDB)

几乎都考虑了。。悲剧
2 楼 裴小星 2011-05-21  
yangwen13 写道
运行会报异常:
ReferenceError: "JSON" is not defined.
run.js需要在哪里引入?


谢谢指出。
增加了一个附件,请查看。
1 楼 yangwen13 2011-05-21  
运行会报异常:
ReferenceError: "JSON" is not defined.
run.js需要在哪里引入?

相关推荐

    jetty-http-9.4.11.v20180605-API文档-中英对照版.zip

    赠送jar包:jetty-http-9.4.11.v20180605.jar; 赠送原API文档:jetty-http-9.4.11.v20180605-javadoc.jar; 赠送源代码:jetty-http-9.4.11.v20180605-sources.jar; 赠送Maven依赖信息文件:jetty-...

    jetty-sslengine-6.1.26-API文档-中文版.zip

    赠送jar包:jetty-sslengine-6.1.26.jar; 赠送原API文档:jetty-sslengine-6.1.26-javadoc.jar; 赠送源代码:jetty-sslengine-6.1.26-sources.jar; 赠送Maven依赖信息文件:jetty-sslengine-6.1.26.pom; 包含...

    jetty-io-9.4.43.v20210629-API文档-中英对照版.zip

    赠送jar包:jetty-io-9.4.43.v20210629.jar; 赠送原API文档:jetty-io-9.4.43.v20210629-javadoc.jar; 赠送源代码:jetty-io-9.4.43.v20210629-sources.jar; 赠送Maven依赖信息文件:jetty-io-9.4.43.v20210629....

    jetty-security-9.3.19.v20170502-API文档-中文版.zip

    赠送jar包:jetty-security-9.3.19.v20170502.jar; 赠送原API文档:jetty-security-9.3.19.v20170502-javadoc.jar; 赠送源代码:jetty-security-9.3.19.v20170502-sources.jar; 赠送Maven依赖信息文件:jetty-...

    jetty-client-9.4.11.v20180605-API文档-中文版.zip

    赠送jar包:jetty-client-9.4.11.v20180605.jar; 赠送原API文档:jetty-client-9.4.11.v20180605-javadoc.jar; 赠送源代码:jetty-client-9.4.11.v20180605-sources.jar; 赠送Maven依赖信息文件:jetty-client-...

    jetty-maven-springMVC-mybitas-eclipse集成demo

    jetty-maven-springMVC-mybitas-eclipse集成demo 数据库配置:/jetty-maven/src/main/resources/applicationContext.xml 数据库导入到test数据库:/jetty-maven/src/main/resources/hms_user.sql 配置好maven环境,...

    jetty-6.1.26-API文档-中英对照版.zip

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

    jetty-6.1.26-API文档-中文版.zip

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

    jetty-http-9.4.43.v20210629-API文档-中英对照版.zip

    赠送jar包:jetty-http-9.4.43.v20210629.jar; 赠送原API文档:jetty-http-9.4.43.v20210629-javadoc.jar; 赠送源代码:jetty-http-9.4.43.v20210629-sources.jar; 赠送Maven依赖信息文件:jetty-...

    jetty-util-9.4.43.v20210629-API文档-中文版.zip

    赠送jar包:jetty-util-9.4.43.v20210629.jar; 赠送原API文档:jetty-util-9.4.43.v20210629-javadoc.jar; 赠送源代码:jetty-util-9.4.43.v20210629-sources.jar; 赠送Maven依赖信息文件:jetty-util-9.4.43.v...

    grails-jetty-example:使用Jetty而不是Tomcat的示例Grails 4 Web应用程序

    Grails Jetty示例应用程序这是一个使用Jetty而不是Tomcat的示例Grails Web应用程序。 为了运行该应用程序,请按照以下说明进行操作: 打开命令行,然后导航到项目目录/文件夹。 使用./gradlew bootWar任务构建WAR。 ...

    jetty-server-8.1.8-API文档-中文版.zip

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

    jetty-continuation-8.1.8.v20121106-API文档-中文版.zip

    赠送jar包:jetty-continuation-8.1.8.v20121106.jar; 赠送原API文档:jetty-continuation-8.1.8.v20121106-javadoc.jar; 赠送源代码:jetty-continuation-8.1.8.v20121106-sources.jar; 赠送Maven依赖信息文件:...

    jetty-server-9.4.8.v20171121-API文档-中文版.zip

    赠送jar包:jetty-server-9.4.8.v20171121.jar; 赠送原API文档:jetty-server-9.4.8.v20171121-javadoc.jar; 赠送源代码:jetty-server-9.4.8.v20171121-sources.jar; 赠送Maven依赖信息文件:jetty-server-9.4.8...

    jetty-server-8.1.8-API文档-中英对照版.zip

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

    jetty-util-6.1.26-API文档-中文版.zip

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

    jetty-webapp-9.3.19.v20170502-API文档-中文版.zip

    赠送jar包:jetty-webapp-9.3.19.v20170502.jar; 赠送原API文档:jetty-webapp-9.3.19.v20170502-javadoc.jar; 赠送源代码:jetty-webapp-9.3.19.v20170502-sources.jar; 赠送Maven依赖信息文件:jetty-webapp-...

    jetty-io-9.4.43.v20210629-API文档-中文版.zip

    赠送jar包:jetty-io-9.4.43.v20210629.jar; 赠送原API文档:jetty-io-9.4.43.v20210629-javadoc.jar; 赠送源代码:jetty-io-9.4.43.v20210629-sources.jar; 赠送Maven依赖信息文件:jetty-io-9.4.43.v20210629....

Global site tag (gtag.js) - Google Analytics