Jetty 9嵌入式開發
當前Jetty網址上推薦使用的穩定版本:Jetty9.0。
介紹
直接連結:
Jetty有一個標語,“不要部署你的應用在Jetty上,部署Jetty在你的應用中”。這意味著可選擇使用Jetty捆綁你的應用作為一個標準WAR進行部署。Jetty設計成一個軟體元件,可以例項化並且使用在Java程式中,例如:如何POJO。但是另外一種方法,以嵌入式模式執行Jetty,這意味著把HTTP模組放入到你的應用中,而不是把你的應用放入到HTTP服務中。
本教程引導你逐步從最簡單的Jetty服務例項到使用標準部署描述執行多個Web應用。大部分示例的原始碼是標準Jetty專案的一部分。
在學習該教程之前,完成一個HelloWorld教程是值得的。該教程可以在“嵌入式Jetty網路研討會記錄”中找到。
Jetty版本:本教程的程式碼來自於Jetty7,但是也應該在Jetty 8中可以使用。對於最新的穩定版本,參考最新發行版的連結程式碼。可能與本教程中給出的程式碼例子有稍微的不同。
概述
為了嵌入Jetty服務,通常執行下面的步驟:
1)建立一個服務
2)新增和配置聯結器
3)新增和配置處理器
4)新增和配置Servlet、Webapp到處理器
5)啟動服務
6)等待(join服務防止主執行緒退出)
建立一個服務
下面的程式碼來自於SimplestServer.jar,例項化和執行一個最簡單的Jetty服務
public class SimplestServer
{
public static void main(String[] args) throws Exception
{
Server server = new Server(8080);
server.start();
server.join();
}
}
在埠8080上執行一個HTTP服務。這不是非常有用的服務,因為它不處理,對於每個請求只是返回一個404錯誤。
編寫處理器
為了生成請求的響應,Jetty要求在服務上設定一個Handler。一個處理器可能:
1) 檢查和修改HTTP請求
2)生成完整的HTTP詳情
3)呼叫其他的處理器(參見HandlerWrapper)
4)選擇一個或者多個處理器呼叫(參考HandlerCollection)
Hello world處理器:
下面程式碼基於HelloHandler.java,顯示一個簡單的Helloworld處理器。
public classHelloHandler extends AbstractHandler
{
public void handle(String target,RequestbaseRequest,HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
baseRequest.setHandled(true);
response.getWriter().println("<h1>HelloWorld</h1>");
}
}
傳遞給handle方法的引數包括:
1) target - 請求的目標,它可以是一個URI或者命名分發器的名稱
2) baseRequest – Jetty可變請求物件,它總是解包的
3) request - 不可變的請求物件,它可能已經被封裝
4) response - 響應,它可能已經被封裝
處理器設定請求的狀態,內容型別,在使用writer生成請求體之前標註請求已經處理(後續就不再處理)。
下面的程式碼來自於OneHandler.java,顯示Jetty服務如何使用該處理器。
public staticvoid main(String[] args) throws Exception
{
Server server = new Server(8080);
server.setHandler(new HelloHandler());
server.start();
server.join();
}
現在你知道基於Jetty編寫一個HTTP服務所需要了解的所有東西。然而,複雜的請求處理通常使用多處理器構建。我們在後面章節中看看它是如何組合處理器的。你能檢視Jetty的org.eclipse.jetty.server.handler包中有那些可用處理器。
配置連線
為了配置服務使用的HTTP連線。你能設定服務上的一個或者多個聯結器。你能詳細配置每個連結,例如:介面、埠、緩衝大小,超時時間等。
下面的程式碼基於ManyConnectors.java,顯示如何為HelloWorld示例設定和配置聯結器。
public class ManyConnectors
{
public static void main(String[] args) throws Exception
{
Server server = new Server();
SelectChannelConnector connector0 = new SelectChannelConnector();
connector0.setPort(8080);
connector0.setMaxIdleTime(30000);
connector0.setRequestHeaderSize(8192);
SelectChannelConnector connector1 = new SelectChannelConnector();
connector1.setHost("127.0.0.1");
connector1.setPort(8888);
connector1.setThreadPool(new QueuedThreadPool(20));
connector1.setName("admin");
SslSelectChannelConnector ssl_connector = newSslSelectChannelConnector();
String jetty_home =
System.getProperty("jetty.home","../jetty-distribution/target/distribution");
System.setProperty("jetty.home",jetty_home);
ssl_connector.setPort(8443);
SslContextFactory cf = ssl_connector.getSslContextFactory();
cf.setKeyStore(jetty_home + "/etc/keystore");
cf.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
cf.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
server.setConnectors(new Connector[]{ connector0, connector1,ssl_connector });
server.setHandler(new HelloHandler());
server.start();
server.join();
}
}
理解處理器集合、封裝和範圍
複雜請求處理通常構建多個處理器,能夠以各種方式組合:
1) 處理器集合:保留其他處理器集合,依次呼叫每個處理器。對於合併統計和日誌處理器(生成請求的處理器)是有用的。
2) 處理器列表:一個處理器集合,依次呼叫處理器直到丟擲異常、委派響應或request.isHandled返回true。它能作為合併處理器使用,有條件的處理一個請求。
3) 處理器封裝:一個處理器基類可以用於菊花鏈處理程式,以面向方面程式設計風格組合在一起。例如,一個標準的Web應用通過上下文、會話、安全和Servlet處理器的鏈式實現。
4) 內容處理器集合:使用最長的請求URI字首(contextPath)來選擇指定的ContextHandler來處理請求。
參見如何編寫Jetty處理器。
配置檔案服務
下面程式碼來自於FileServer.java,使用HandleList組合ResourceHandler和DefaultHandler。
public class FileServer
{
public static void main(String[] args) throws Exception
{
Server server = new Server();
SelectChannelConnector connector = new SelectChannelConnector();
connector.setPort(8080);
server.addConnector(connector);
ResourceHandler resource_handler = new ResourceHandler();
resource_handler.setDirectoriesListed(true);
resource_handler.setWelcomeFiles(new String[]{ "index.html"});
resource_handler.setResourceBase(".");
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[] { resource_handler, newDefaultHandler() });
server.setHandler(handlers);
server.start();
server.join();
}
}
資源處理器首先傳遞給請求,檢視在本地目錄下是否有滿足的檔案。如果沒有找到一個檔案,請求傳遞給預設的處理器生成一個404錯誤(或favicon.ico)。
使用XML配置的檔案伺服器
現在是時候讓你記住JettyXML配置格式能夠渲染簡單的Java程式碼到XML配置。所以上面的檔案伺服器例子可以使用Jetty XML來編寫,如下。
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC"-//Jetty//Configure//EN""http://www.eclipse.org/jetty/configure.dtd">
<Configure id="FileServer"class="org.eclipse.jetty.server.Server">
<Call name="addConnector">
<Arg>
<Newclass="org.eclipse.jetty.server.nio.SelectChannelConnector">
<Set name="port">8080</Set>
</New>
</Arg>
</Call>
<Set name="handler">
<New class="org.eclipse.jetty.server.handler.HandlerList">
<Set name="handlers">
<Arraytype="org.eclipse.jetty.server.Handler">
<Item>
<Newclass="org.eclipse.jetty.server.handler.ResourceHandler">
<Setname="directoriesListed">true</Set>
<Setname="welcomeFiles">
<Arraytype="String"><Item>index.html</Item></Array>
</Set>
<Setname="resourceBase">.</Set>
</New>
</Item>
<Item>
<Newclass="org.eclipse.jetty.server.handler.DefaultHandler">
</New>
</Item>
</Array>
</Set>
</New>
</Set>
</Configure>
你可以從FileServerXml.java中執行這個XML檔案。
public class FileServerXml
{
public static void main(String[] args) throws Exception
{
Resource fileserver_xml =Resource.newSystemResource("fileserver.xml");
XmlConfiguration configuration = newXmlConfiguration(fileserver_xml.getInputStream());
Server server = (Server)configuration.configure();
server.start();
server.join();
}
}
使用Spring配置檔案伺服器
你也能使用Spring框架聚合Jetty服務,上面的檔案伺服器示例可以使用Spring配置重寫,如下:
<beans>
<bean id="Server"class="org.eclipse.jetty.server.Server" init-method="start"destroy-method="stop">
<property name="connectors">
<list>
<bean id="Connector"class="org.eclipse.jetty.server.nio.SelectChannelConnector">
<property name="port" value="8080"/>
</bean>
</list>
</property>
<property name="handler">
<bean id="handlers"class="org.eclipse.jetty.server.handler.HandlerList">
<property name="handlers">
<list>
<beanclass="org.eclipse.jetty.server.handler.ResourceHandler">
<propertyname="directoriesListed" value="true"/>
<propertyname="welcomeFiles">
<list>
<value>index.html</value>
</list>
</property>
<propertyname="resourceBase" value="."/>
</bean>
<beanclass="org.eclipse.jetty.server.handler.DefaultHandler"/>
</list>
</property>
</bean>
</property>
</bean>
</beans>
設定上下文
ContextHandler是是HandlerWrapper,只負責請求符合配置上下路徑的URL字首。
符合上下文路徑(有它們路徑方法)的請求將進行相應的更新,以下可選上下文特徵被正確的應用。
* 一個執行緒內容類載入器
* 屬性的集合
* 初始化引數集合
* 基礎資源(又名文件根目錄)
* 虛擬主機名集合
沒有匹配的請求不進行處理。
下面的程式碼基於OneContext.java,設定上下文路徑和hello處理器的類載入器。
public class OneContext
{
public static void main(String[] args) throws Exception
{
Server server = new Server(8080);
ContextHandler context = new ContextHandler();
context.setContextPath("/hello");
context.setResourceBase(".");
context.setClassLoader(Thread.currentThread().getContextClassLoader());
server.setHandler(context);
context.setHandler(new HelloHandler());
server.start();
server.join();
}
}
建立Servlet
Servlet是提供應用邏輯和處理HTTP請求的標準方法。Servlet就像約束標準方法的處理器對映指定URI給指定的Servlet。下面的程式碼基於HelloServlet.java。
public class HelloServlet extendsHttpServlet
{
private String greeting="Hello World";
public HelloServlet(){}
public HelloServlet(String greeting)
{
this.greeting=greeting;
}
protected void doGet(HttpServletRequest request, HttpServletResponseresponse) throws ServletException, IOException
{
response.setContentType("text/html");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println("<h1>"+greeting+"</h1>");
response.getWriter().println("session=" +request.getSession(true).getId());
}
}
設定ServletContext
ServletContextHandler是一個支援標準Servlet的專用ContextHandler。下面的程式碼來自於OneServletContext,顯示三個HelloworldServlet例項註冊到一個ServletContextHandler上。
public class OneServletContext
{
public static void main(String[] args) throws Exception
{
Server server = new Server(8080);
ServletContextHandler context = newServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
server.setHandler(context);
context.addServlet(new ServletHolder(newHelloServlet()),"/*");
context.addServlet(new ServletHolder(new HelloServlet("BuongiornoMondo")),"/it/*");
context.addServlet(new ServletHolder(new HelloServlet("Bonjour leMonde")),"/fr/*");
server.start();
server.join();
}
}
設定Web應用上下文
一個Web應用上下文是ServletContextHandler的變種,使用標準的佈局和web.xml配置Servlet、過濾器和其它特徵。
public class OneWebApp
{
public static void main(String[] args) throws Exception
{
String jetty_home =System.getProperty("jetty.home","..");
Server server = new Server(8080);
WebAppContext webapp = new WebAppContext();
webapp.setContextPath("/");
webapp.setWar(jetty_home+"/webapps/test.war");
server.setHandler(webapp);
server.start();
server.join();
}
}
如果不熟期間,你沒有組裝應用程式為WAR檔案,你可以從源元件上執行它,如下:
public class OneWebAppUnassembled
{
public static void main(String[] args) throws Exception
{
Server server = new Server(8080);
WebAppContext context = new WebAppContext();
context.setDescriptor(webapp+"/WEB-INF/web.xml");
context.setResourceBase("../test-jetty-webapp/src/main/webapp");
context.setContextPath("/");
context.setParentLoaderPriority(true);
server.setHandler(context);
server.start();
server.join();
}
}
配置上下文處理器集合
上下文處理器集合使用最長請求URI字首選擇指定的上下文。下面的示例在單個Jetty服務上組合前面兩個示例。
public class ManyContexts
{
public static void main(String[] args) throws Exception
{
Server server = new Server(8080);
ServletContextHandler context0 = newServletContextHandler(ServletContextHandler.SESSIONS);
context0.setContextPath("/ctx0");
context0.addServlet(new ServletHolder(newHelloServlet()),"/*");
context0.addServlet(new ServletHolder(new HelloServlet("BuongiornoMondo")),"/it/*");
context0.addServlet(new ServletHolder(new HelloServlet("Bonjour leMonde")),"/fr/*");
WebAppContext webapp = new WebAppContext();
webapp.setContextPath("/ctx1");
webapp.setWar(jetty_home+"/webapps/test.war");
ContextHandlerCollection contexts = new ContextHandlerCollection();
contexts.setHandlers(new Handler[] { context0, webapp });
server.setHandler(contexts);
server.start();
server.join();
}
}
嵌入式JSP
嵌入式JSP支援可能有點令人困惑,如果你檢視Jetty發行版lib/jsp目錄下的JAR包。這是因為我們為Eclipse封裝了JSP軟體集,標記為OSGI軟體集,不能直接從Maven Central中直接下載。在Maven中心中有可用的依賴,因為它們有OSGI軟體集自己實際原始碼。OSGI軟體集簡化Maven中心artifact,解壓成多個擴充套件的bundle。
你能使用的示例如下:
INFO]org.eclipse.jetty:jetty-jsp-2.1:jar:7.2.2-SNAPSHOT
[INFO] +-org.eclipse.jetty:jetty-util:jar:7.2.2-SNAPSHOT:provided
[INFO] +-org.mortbay.jetty:jsp-2.1-glassfish:jar:2.1.v20100127:provided
[INFO] | +- org.eclipse.jdt.core.compiler:ecj:jar:3.5.1:provided
[INFO] | +- org.mortbay.jetty:jsp-api-2.1-glassfish:jar:2.1.v20100127:provided
[INFO] | \- ant:ant:jar:1.6.5:provided
[INFO] \-javax.servlet:servlet-api:jar:2.5:provided
你應該能夠依賴於一個嵌入式聚合,我們提供獲取這些依賴,沒有任何問題。
http://repo2.maven.org/maven2/org/eclipse/jetty/aggregate