Micro-Service Architecture/Executable War
Overview
看ThoughtWorks的技术雷达,里面提到了Micro-Service和Embedded Jetty。大家越来越喜欢用一个嵌入式Jetty加一个嵌入式的H2,不用依赖任何Container,就可以启动一个应用了。
这种嵌入式Embedded有两种风格:
一种是提供一个Zip包,里面有一个bin目录放着jsw(Java Service Wrapper),一个lib目录放着Jetty的jar,一个webapp目录放着真正的应用,一个conf目录放着jetty和jsw的配置文件。用户解开zip包后进入bin目录就可以简单的start/stop应用。像ActiveMQ, Solr, Nexus, Sonar等都是这个风格。
另一种更简洁,把jetty的jar包解开成class放入war包里,再放一个Main class进去,用户用java -jar xxxx.war 就可以启动应用,像Jenkins,Selenium的Server等都是这种风格。
在SpringSide的showcae项目里,演示了后一种风格,同样原理也可以自行制作第一种风格的包。
打包
在showcase里,使用如下,就会在target目录生成一个showcase.war和一个showcase-xxx.standalone.war.
mvn package -Pstandalone
观察一下showcase的pom.xml,依赖的Jetty包定义成provided,既可以用来编译Main.java,又不会打包到WEB-INF/lib中. 然后定义了一个runtime级别的jstl.1.2.jar,因为Jetty用的那个Jasper的编译器只懂得从WEB-INF/lib下的jar里的META-INF中找tld,而不能从war的/META-INF中找。
然后是pom.xml里的standalone profile,可以看到是使用assembly插件实现showcase-4.xx.standalone.war的打包。
再观察assembly-standalone.xml:
首先把org.eclipse.jetty: 及其依赖的包,全部解开class来, 然后打包war包里(注意要exclude掉META-INF里的内容,否则那些*.RDA之类的文件会报jar包签名错之类的错)。
然后把原来war包里的内容同样copy到war包里。
最后从classes中将一个Main.class也copy到war包中。再回到pom.xml里,assembly插件还会修改war包里的META-INF/Manifest.mf,设置main class为刚才那个Main class。
最后再看一下Main.class的实现:
System.setProperty("org.apache.jasper.compiler.disablejsr199", "true");
设置环境变量可确定使用Eclipse的JDT,而不是JDK的compiler来编译JSP。Jetty7.5后默认使用JDK Compiler,但我的机器上老是报NoJDK,郁闷死。
ProtectionDomain protectionDomain = Main.class.getProtectionDomain();
URL location = protectionDomain.getCodeSource().getLocation();
String warFile = location.toExternalForm();
神奇的语句,可以通过Main.class找到war包的路径。
String currentDir = new File(location.getPath()).getParent();
File workDir = new File(currentDir, "work");
context.setTempDirectory(workDir);
设置work dir,war包将解压到该目录,jsp编译后的文件也将放入其中。
运行
用java -jar xxx.war -Dport=8082 可以简单的在8082端口(默认为8080)运行应用,如果要设置其他JVM参数
java-Xms2048m -Xmx2048m -XX:MaxPermSize=128m -jar xxx.war -Dport=8082
Tomcat
Tomcat的Maven Plugin自带打运行包的功能耶。使用如下命令可以在target目录生成一个showcase-xxx-standalone-tomcat.jar,不过暂时有H2 Driver找不到的问题
mvn package -Pstandalone-tomcat