一、写在前面的
fis3-jello 是基于 fis3 针对 jsp/velocity 模板的前端工程解决方案。github 地址为:https://github.com/fex-team/fis3-jello。
注意:fis3 对 Node 版本要求 0.8.x,0.10.x, 0.12.x,4.x,6.x,不在此列表中的版本不予支持。
本文主要介绍如何在 fis3-jello 的基础上来添加自己的需求。
二、fis3-server-jello
fis3-jello 具有很多特性,大多特性都是通过内嵌的 JavaEE 服务器实现的,例如模板继承、模板数据绑定。fis3-jello 在 release 时,会使用 fis3 提供的配置/接口将项目中相应的目录复制到内嵌服务器的目录中。我们在 release 后,可以通过命令 fis3 server open 打开服务器资源目录看到。
1. fis3 如何开发服务器插件
fis3 中的服务器是通过独立的 npm 包来完成的,命名规范为 fis3-server-xxxx。而 fis3-jello 中的服务器对应的 npm 包为 fis3-server-jello。
fis3-jello 打开内嵌服务器的命令为 fis3 server start --type jello,而该命令的执行模块为 fis3-command-server,该模块接收到 --type jello 参数后,通过 fis.require() 方法找到对应的服务器模块,之后调用 fis3-server-jello 模块中的 start 方法。
2. 结构
fis3-server-jello 的 github 地址为:https://github.com/fex-team/fis3-server-jello。
文件目录如下:
│ .gitignore
│ .npmignore
│ fis.keystore
│ index.js
│ LICENSE
│ package.json
│ README.md
│
└─vendor
framework.tar
server.jar
server.js
3. 分析
该插件的大致运行流程为,先检查计算机中是否配置 java 环境,代码如下:
function checkJavaEnable (opt, callback) { var javaVersion = false process.stdout.write('checking java support: ') var java = spawn('java', ['-version']) java.on('exit', function () { if (!javaVersion) { process.stdout.write('java not support!') } callback(javaVersion. opt) }) }
接着解压 framework.tar 至系统临时目录下,最后构建 java 命令来执行 server.jar。
var markerFile = path.join(opt.root, 'WEB-INF', 'web.xml') if (!fis.util.exists(markerFile)) { extract(path.join(__dirname, 'framework.tar'), opt.root, done) } else { setTimeout(done, 200) }
我反编译了 server.jar,看到主要就是使用 apache commons 将 tomcat embed 包装成一个命令工具,反编译得到的代码如下。
package com.baidu.fis.server.launch; import java.io.File; import java.io.PrintStream; import org.apache.catalina.Server; import org.apache.catalina.connector.Connector; import org.apache.catalina.startup.Tomcat; import org.apache.commons.cli.BasicParser; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Options; public class Main { public static int PORT = 8080; public static String BASE_DIR = null; public static String WEBAPP_DIR = "./web"; public static String WEBAPP_PATH = ""; public static Boolean HTTPS = Boolean.valueOf(false); public static void main(String[] args) throws Exception { System.setProperty("java.awt.headless", "true"); Options options = new Options(); options.addOption("h", "help", false, "output usage information"); options.addOption("p", "port", true, "server listen port"); options.addOption("base", true, "tomcat base dir"); options.addOption("root", true, "document root"); options.addOption("webapp", true, "webapp path"); options.addOption("type", false, "useless just ignore it."); options.addOption("https", false, "https"); HelpFormatter help = new HelpFormatter(); BasicParser parser = new BasicParser(); CommandLine cmd = parser.parse(options, args); if (cmd.hasOption("help")) { help.printHelp("assemble", options); return; } if (cmd.hasOption("port")) { PORT = Integer.valueOf(cmd.getOptionValue("port")).intValue(); } if (cmd.hasOption("base")) { BASE_DIR = cmd.getOptionValue("base"); } else if (BASE_DIR == null) { BASE_DIR = System.getProperty("java.io.tmpdir"); } if (cmd.hasOption("root")) { WEBAPP_DIR = cmd.getOptionValue("root"); } if (cmd.hasOption("webapp")) { WEBAPP_PATH = cmd.getOptionValue("webapp"); if ((!WEBAPP_PATH.isEmpty()) && (!WEBAPP_PATH.startsWith("/"))) { WEBAPP_PATH = "/" + WEBAPP_PATH; } } if (cmd.hasOption("https")) { HTTPS = Boolean.valueOf(true); } startServer(); } protected static void startServer() { Tomcat tomcat = new Tomcat(); try { String base = new File(BASE_DIR).getCanonicalPath(); String webapp = new File(WEBAPP_DIR).getCanonicalPath(); tomcat.setBaseDir(base); Connector defaultConnector = tomcat.getConnector(); defaultConnector.setPort(PORT); if (HTTPS.booleanValue()) { System.out.println("Use HTTPS"); System.out.println(System.getProperty("user.dir") + "/../fis.keystore"); defaultConnector.setScheme("https"); defaultConnector.setSecure(true); defaultConnector.setAttribute("keystoreFile", System.getProperty("user.dir") + "/../fis.keystore"); defaultConnector.setAttribute("keystorePass", "123456"); defaultConnector.setAttribute("clientAuth", "false"); defaultConnector.setAttribute("sslProtocol", "TLS"); defaultConnector.setAttribute("SSLEnabled", Boolean.valueOf(true)); } tomcat.addWebapp(WEBAPP_PATH, webapp); tomcat.start(); tomcat.getServer().await(); } catch (Exception e) { e.printStackTrace(); } } }
最后来看一下 framework.tar 文件,在执行 server.jar 之前,程序会解压 framework.tar 中的文件到指定的系统临时目录中。我们可以解压 framework.tar 文件,看到文件的目录结构如下:
├─META-INF └─WEB-INF │ fis.tld │ tools.xml │ velocity.properties │ web.xml │ ├─lib │ antlr-2.7.2.jar │ aopalliance-1.0.jar │ commons-beanutils-1.7.0.jar │ commons-chain-1.1.jar │ commons-collections-3.2.1.jar │ commons-digester-1.8.jar │ commons-fileupload-1.3.1.jar │ commons-io-2.4.jar │ commons-lang-2.4.jar │ commons-logging-1.1.jar │ commons-validator-1.3.1.jar │ dom4j-1.1.jar │ fastjson-1.1.41.jar │ fis-velocity-tools.jar │ jstl-1.2.jar │ oro-2.0.8.jar │ slf4j-api-1.7.7.jar │ spring-aop-3.2.10.RELEASE.jar │ spring-beans-3.2.10.RELEASE.jar │ spring-context-3.2.10.RELEASE.jar │ spring-core-3.2.10.RELEASE.jar │ spring-expression-3.2.10.RELEASE.jar │ spring-web-3.2.10.RELEASE.jar │ sslext-1.2-0.jar │ standard-1.1.2.jar │ struts-core-1.3.8.jar │ struts-taglib-1.3.8.jar │ struts-tiles-1.3.8.jar │ velocity-1.7.jar │ velocity-tools-2.0.jar │ └─views index.vm
这里最值得注意的就是 /WEB-INF/lib/fis-velocity-tools.jar,该 jar 包就是用来 fis3-jello 服务器中最主要的程序,其开源的工程git地址为:https://github.com/fex-team/fis-velocity-tools。我们可以通过修改该项目,来定制自己的特性。
三、fis-velocity-tools
1. 开发准备
整个项目使用 maven 工具进行管理,为了使其在 tomcat 容器中运行以方便开发,我在 pom.xml 中添加了一个插件,如下:
<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>tomcat-maven-plugin</artifactId> <version>1.1</version> <configuration> <path>/wp</path> <port>8080</port> <uriEncoding>UTF-8</uriEncoding> <url>http://localhost:8080/</url> <server>tomcat6</server> </configuration> </plugin>
2. web.xml
这个项目就是在开发一个 web 项目。