Jetty是一個用 Java 實現、開源、基於標準的,而且具備豐富功能的 Http 服務器和 Web 容器。Jetty中應用最普遍的一項功能就是能夠做爲嵌入式Web容器。javascript
本文將着重介紹如何配置使用Jetty的嵌入式Web容器功能,關於Jetty的基本配置和功能請參考http://www.ibm.com/developerworks/cn/web/wa-lo-jetty/css 1、開發階段一、使用maven啓動Jetty咱們修改了源碼的時候eclipse會自動編譯,Jetty Maven Plugin插件發現編譯文件有變化後會自動更新到jetty容器中,很是方便咱們進行開發。html 首先定義Jetty的版本屬性java <properties> <jetty.version>8.1.9.v20130131</jetty.version> </properties> 而後引入Jetty依賴linux
<!-- jetty --> <dependency> <groupId>org.eclipse.jetty.aggregate</groupId> <artifactId>jetty-webapp</artifactId> <version>${jetty.version}</version> <scope>test</scope></dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-jsp</artifactId> <version>${jetty.version}</version> <scope>test</scope> </dependency>
配置Jetty Maven Plugin插件,示例以下git
<plugin> <groupId>org.mortbay.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>${jetty.version}</version> <configuration> <systemProperties> <systemProperty> <name>spring.profiles.active</name> <value>development</value> </systemProperty> </systemProperties> <useTestClasspath>true</useTestClasspath> <webAppConfig><contextPath>/${project.artifactId}</contextPath> </webAppConfig> </configuration></plugin>
該配置運行jetty並指定spring的profile爲development,同時設定web應用的上下文地址與應用自己的artifactId一致。github 執行以下命令啓動Jetty,便可經過http://localhost:8080/${project.artifactId}訪問Web應用。web mvn jetty:run
Jetty Maven Plugin插件支持多個maven goals,最經常使用的就是run,下面的參數支持大部分的goalsspring (1)配置Jetty容器(支持全部goals)apache
好比org.mortbay.jetty.NCSARequestLog就是一個NCSA格式((美國)國家超級計算技術應用中心 (NCSA) 公用格式,是經常使用的標準日誌格式)的實現。 (2)配置Web應用程序(不支持run-forked、stop兩個goals)
run goals將會啓動Jetty並運行應用程序,不須要應用程序編譯成war包。另外run還支持webapp節點的其它屬性:
Jetty Maven Plugin插件支持的其它goals簡介以下(詳見http://wiki.eclipse.org/Jetty/Feature/Jetty_Maven_Plugin):
二、在java中啓動JettySpringSide4中封裝了Jetty的操做提供了工具類JettyFactory ,讓咱們能夠很簡單的啓動Jetty容器,JettyFactory代碼以下:
/** * Copyright (c) 2005-2012 springside.org.cn * * Licensed under the Apache License, Version 2.0 (the "License"); */ package org.springside.modules.test.jetty; importjava.util.List; import org.apache.commons.lang3.StringUtils; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.nio.SelectChannelConnector; import org.eclipse.jetty.webapp.WebAppClassLoader; import org.eclipse.jetty.webapp.WebAppContext; import com.google.common.collect.Lists; /** * 建立Jetty Server的工廠類. * * @author calvin */ public class JettyFactory { private static final String DEFAULT_WEBAPP_PATH = "src/main/webapp"; privatestatic final String WINDOWS_WEBDEFAULT_PATH = "jetty/webdefault-windows.xml"; /** * 建立用於開發運行調試的Jetty Server, 以src/main/webapp爲Web應用目錄. */ public staticServer createServerInSource(int port, String contextPath) { Server server = new Server(); // 設置在JVM退出時關閉Jetty的鉤子。 server.setStopAtShutdown(true); SelectChannelConnector connector = new SelectChannelConnector(); connector.setPort(port); //解決Windows下重複啓動Jetty竟然不報告端口衝突的問題. connector.setReuseAddress(false); server.setConnectors(new Connector[] { connector }); WebAppContext webContext =new WebAppContext(DEFAULT_WEBAPP_PATH, contextPath); // 修改webdefault.xml,解決Windows下Jetty Lock住靜態文件的問題. webContext.setDefaultsDescriptor(WINDOWS_WEBDEFAULT_PATH); server.setHandler(webContext); return server; } /** * 設置除jstl-*.jar外其餘含tld文件的jar包的名稱. * jar名稱不須要版本號,如sitemesh, shiro-web */ public static void setTldJarNames(Server server, String... jarNames) { WebAppContext context = (WebAppContext) server.getHandler(); List<String> jarNameExprssions = Lists.newArrayList(".*/jstl-[^/]*\\.jar$", ".*/.*taglibs[^/]*\\.jar$"); for (String jarName : jarNames) { jarNameExprssions.add(".*/" + jarName + "-[^/]*\\.jar$"); } context.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", StringUtils.join(jarNameExprssions, '|')); } /** * 快速從新啓動application,重載target/classes與target/test-classes. */ public static void reloadContext(Server server) throws Exception { WebAppContext context = (WebAppContext) server.getHandler(); System.out.println("[INFO] Application reloading"); context.stop(); WebAppClassLoader classLoader = new WebAppClassLoader(context); classLoader.addClassPath("target/classes"); classLoader.addClassPath("target/test-classes"); context.setClassLoader(classLoader); context.start(); System.out.println("[INFO] Application reloaded"); } }
JettyFactory包含三個方法
Jetty的jsp處理引擎來自於Glassfish,要求JSF標籤必須位於Jetty容器的classpath中,不能位於Web應用的classpath中,而Jetty的WebAppClassLoader優先使用父classloader加載類,致使tld文件都被加載到父classloader中,在Jetty的classpath中根本掃描不到,因此會出現找不到tld文件的狀況。setTldJarNames方法能夠設置將包含tld的jar包加載到Jetty的classpath中。
調用JettyFactory在Jetty中運行調試Maven Web應用的示例代碼以下:
package org.springside.examples.quickstart; import org.eclipse.jetty.server.Server;import org.springside.modules.test.jetty.JettyFactory; import org.springside.modules.test.spring.Profiles; /** * 使用Jetty運行調試Web應用, 在Console輸入回車快速從新加載應用. * * @author calvin */ public class QuickStartServer { public static final int PORT = 8080; public static final String CONTEXT = "/quickstart"; public static final String[] TLD_JAR_NAMES = new String[] { "sitemesh", "spring-webmvc", "shiro-web", "springside-core" }; public static void main(String[] args) throws Exception { // 設定Spring的profile Profiles.setProfileAsSystemProperty(Profiles.DEVELOPMENT); // 啓動Jetty Server server = JettyFactory.createServerInSource(PORT, CONTEXT); JettyFactory.setTldJarNames(server, TLD_JAR_NAMES); try { server.start(); System.out.println("[INFO] Server running at http://localhost:" + PORT + CONTEXT); System.out.println("[HINT] Hit Enter to reload the application quickly"); // 等待用戶輸入回車重載應用. while (true) { char c = (char) System.in.read(); if (c == '\n') { JettyFactory.reloadContext(server); } } } catch (Exception e) { e.printStackTrace(); System.exit(-1); } } }
上段代碼還提供了經過捕獲在console中輸入的回車自動從新載入上下文,並從新載入Class文件,提升了響應速度。 在執行main方法過程當中若是發生以下錯誤: class "javax.servlet.HttpConstraintElement"'s signer information does not match signer information of other classes in the same package 經過執行以下命令檢查依賴 mvn dependency:tree -Dverbose|grep servlet 檢查結果如圖 發現是由於Jetty8版本的包的依賴包org.eclipse.jetty.orbit.javax.servlet3.0.jar提供了javax.servlet.HttpConstraintElement類,而javax.servlet.servlet-api.jar的依賴包javax.servlet.javax.servlet-api-3.0.1.jar也提供了javax.servlet.HttpConstraintElement類,二者發生了衝突。可使用7.6.14.v20131031版本的Jetty解決此問題。 2、測試階段在功能測試或集成測試階段,但願在測試開始時自動運行Jetty加載項目進行測試,測試完成時中止Jetty容器。Jetty Maven Plugin插件能夠幫助咱們完成這種自動化工做。配置示例以下:
<plugin> <groupId>org.mortbay.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <configuration> <scanIntervalSeconds>10</scanIntervalSeconds> <stopKey>foo</stopKey> <stopPort>9999</stopPort> </configuration> <executions> <execution> <id>start-jetty</id> <phase>pre-integration-test</phase> <goals> <goal>start</goal> </goals><configuration> <scanIntervalSeconds>0</scanIntervalSeconds> <daemon>true</daemon></configuration> </execution> <execution> <id>stop-jetty</id> <phase>post-integration-test</phase> <goals> <goal>stop</goal> </goals> </execution> </executions> </plugin>
在上述配置中,經過execution來自定義運行階段:
使用<daemon>true</daemon>配置選項來預防Jetty無限期運行,迫使它只在執行Maven時才運行。 3、運行階段爲了可以建立能夠直接運行的war包,須要把jetty jar包解開,將其中的class直接編譯到war包中,並須要在war中提供一個能夠建立並運行Jetty的Main方法。本文提供兩種實現方法: 方法一SpringSide4中提供了一種實現方法,稍加修改優化後步驟以下: 一、使用maven-assembly-plugin從新打包maven-assembly-plugin插件能將應用程序打包成指定格式的分發包,更重要的是可以自定義包含/排除指定的目錄或文件。 爲方便操做,單獨創建一個Maven Profile用於打包,配置以下:
<profile> <id>standalone</id> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>single</goal> </goals> <configuration> <descriptors> <descriptor>assembly-standalone.xml</descriptor> </descriptors> <archive><manifest> <mainClass>org.springside.examples.showcase.Main</mainClass> </manifest></archive> </configuration> </execution> </executions> </plugin> </plugins> </build></profile>
上述配置中,經過execution配置打包操做在package階段開始,引入assembly-standalone.xml文件定義打包的規則,配置archive修改war包中的META-INF/Manifest.mf,替換main class爲org.springside.examples.showcase.Main。 assembly-standalone.xml中的配置以下:
<?xml version="1.0" encoding="UTF-8"?> <assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd"> <id>standalone</id> <formats> <format>war</format> </formats> <includeBaseDirectory>false</includeBaseDirectory> <dependencySets> <dependencySet> <outputDirectory>/</outputDirectory> <includes> <include>org.eclipse.jetty*:*</include> </includes> <scope>provided</scope><unpack>true</unpack> <unpackOptions> <excludes> <exclude>*</exclude> <exclude>META-INF/*</exclude> <exclude>about_files/*</exclude> </excludes> </unpackOptions> </dependencySet> </dependencySets> <fileSets> <fileSet> <directory>${project.basedir}/target/${project.build.finalName}</directory> <outputDirectory>/</outputDirectory> <excludes> <exclude>META-INF/**/*</exclude> </excludes> </fileSet> <fileSet> <directory>${project.basedir}/target/classes</directory> <includes> <include>**/*/Main.class</include> </includes> <outputDirectory>/</outputDirectory> </fileSet> </fileSets></assembly>
assembly-standalone.xml涉及到幾個關鍵點:
二、使用代碼建立Jetty容器
package org.springside.examples.quickstart; import java.io.File; import java.net.URL; import java.security.ProtectionDomain; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.webapp.WebAppContext; /** * Main Class for standalone running. * * @author calvin */ public class Main { public static void main(String[] args)throws Exception { String contextPath = "/"; int port = Integer.getInteger("port", 8080); Server server = createServer(contextPath, port); try { server.start(); server.join(); } catch (Exception e) { e.printStackTrace(); System.exit(100); } } privatestatic Server createServer(String contextPath, int port) { // use Eclipse JDT compiler System.setProperty("org.apache.jasper.compiler.disablejsr199", "true"); Server server = new Server(port); server.setStopAtShutdown(true); ProtectionDomain protectionDomain = Main.class.getProtectionDomain(); URL location = protectionDomain.getCodeSource().getLocation(); String warFile = location.toExternalForm(); WebAppContext context = new WebAppContext(warFile, contextPath); context.setServer(server); // 設置work dir,war包將解壓到該目錄,jsp編譯後的文件也將放入其中。 String currentDir = new File(location.getPath()).getParent(); File workDir = new File(currentDir, "work"); context.setTempDirectory(workDir); server.setHandler(context); return server; } }
createServer方法負責建立Jetty服務,獲取war包路徑,建立context及工做目錄 main方法負責調用createServer方法建立Jetty服務,設置上下文路徑及啓動端口,並啓動Jetty服務,另外若是war包所在的路徑包含中文,則獲取路徑的代碼應修改成: ProtectionDomain protectionDomain = Main.class.getProtectionDomain(); URL location = protectionDomain.getCodeSource().getLocation(); location = java.net.URLDecoder.decode(location , "utf-8");
三、注意事項經過以上配置,已經能夠在Web應用程序內嵌入Jetty容器了,但還須要注意如下幾點
<dependency> <groupId>org.mortbay.jetty</groupId> <artifactId>jsp-2.1-glassfish</artifactId> <version>2.1.v20100127</version> </dependency>
四、運行執行以下命令將Web應用打包成war包,在${project.basedir}/target目錄下將會生成嵌入Jetty容器的war包。 mvn package -Pstandalone
經過以下命令運行war包。 Java -Xms2048m -Xmx2048m -XX:MaxPermSize=128m -jar xxx.war
方法二方法一中主要是使用了maven-assembly-plugin進行自定義打包,除此以外還可使用maven-war-plugin、maven-antrun-plugin、maven-dependency-plugin、maven-compiler-plugin共同實現建立可執行的war包 Maven POM配置示例以下:
<profile> <id>standalone</id> <build> <finalName>quickstart</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.3</version> <configuration> <archive> <manifest> <mainClass>org.springside.examples.quickstart.Main</mainClass> </manifest> </archive> <warName>${project.artifactId}-standalone</warName> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-antrun-plugin</artifactId> <version>1.7</version> <executions> <execution> <id>main-class-placement</id> <phase>prepare-package</phase> <configuration> <target> <move todir="${project.build.directory}/${project.artifactId}/"> <fileset dir="${project.build.directory}/classes/"> <include name="**/*/Main.class" /> </fileset> </move> </target> </configuration> <goals> <goal>run</goal> </goals> </execution> </executions> </plugin> <plugin><groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>2.5.1</version> <executions> <execution> <id>jetty-classpath</id><phase>prepare-package</phase> <goals> <goal>unpack-dependencies</goal> </goals> <configuration> <includeGroupIds>org.eclipse.jetty, org.eclipse.jetty.orbit, javax.servlet</includeGroupIds> <includeScope>provided</includeScope> <!-- remove some files in order to decrease size --> <excludes>*, about_files/*, META-INF/*</excludes> <!--<excludeArtifactIds>jsp-api,jstl</excludeArtifactIds> --> <outputDirectory> ${project.build.directory}/${project.artifactId} </outputDirectory> </configuration> </execution> </executions> </plugin> <!-- to support compilation in linux --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.5.1</version> <configuration> <target>1.6</target> <source>1.6</source> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </profile>
注意事項、org.springside.examples.showcase.Main類實現及運行方法同方法一。 |