Jetty有一个口号:“不要将你的应用部署到Jetty中,而是将Jetty部署到你的应用中”。Jetty提供了另一种开发和部署web应用的思维,使用jetty的嵌入式模式就是将一个http模块放入到你的应用中,而不需要将你的应用放入到http服务器中。
将Jetty嵌入到应用中通常需要下面的步骤:
创建一个server
添加/配置Connectors
添加/配置Handlers
给Handlers添加/配置Servlets/Webapps
启动server
join(让主线程阻塞,防止退出)
创建一个Server
下面的代码可以创建一个最简单的Jetty服务器:
1
2
3
4
5
6
7
8
9
public class SimplestServer
{
public static void main ( String [] args ) throws Exception
{
Server server = new Server ( 8080 );
server . start ();
server . join ();
}
}
它会启动一个Http服务器并且监听8080端口。但是由于没有配置任何handlers,随意对于任意的请求都会返回404错误。
配置handlers
为了让Jetty能够正常输出响应给客户端,需要给Jetty配置一个handler,这个handler可以:
检测和修改http request
生成http response
调用另外一个handler(HandlerWrapper)
继续调用一个或多个handler(HandlerCollection)
下面这个例子展示了一个可以输出hello world的handler:
1
2
3
4
5
6
7
8
9
10
11
public class HelloHandler extends AbstractHandler
{
public void handle ( String target , Request baseRequest , 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>Hello World</h1>" );
}
}
使用这个handler来配置server:
1
2
3
4
5
6
7
8
public static void main ( String [] args ) throws Exception
{
Server server = new Server ( 8080 );
server . setHandler ( new HelloHandler ());
server . start ();
server . join ();
}
这就是配置一个server的全部过程,虽然复杂的请求处理需要多个handler相互配合,更加复杂的handler会在后面做介绍。
配置Connectors
可以给jetty server配置一个或多个connector,甚至可以对每一个connector做详细的配置,比如监听端口、缓存大小、超时等等。
下面的代码展示了在前面这个例子的基础上给server做connector的配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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 = new SslSelectChannelConnector ();
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 ();
}
}
理解handler collections, wrappers和scopes
复杂的请求处理尝尝需要多个handler协同处理,这些handler通过以下方式组织在一起:
HandlerCollection:可以将多个handler组装起来,并且按顺序挨个调用他们。这常常用来将统计handler、日志handler和真正的生成response的handler组装在一起。
HandlerList:将多个handler组装起来,并且按顺序调用,直到其中一个handler抛出异常,或者reponse被提交(commit),或者handler.isHandled()返回true。它常常用来组合多个条件性的(conditionally)的handler。
HandlerWrapper:通常是一个handler的基类。提供了一种装饰模式的handler,可以实现面向切面的handler处理机制。
ConetextHandlerCollection:被jetty server内部使用,一般不会用到。实际上是一个HandlerCollection,里面组装了多个ContextHandler,并且对于每个request请求,会根据request的URI(contextPath)并根据最长前缀的匹配模式选择一个ContextHandler来执行。
配置一个文件服务器
下面的代码使用HandlerList联合ResourceHandler和DefaultHandler:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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 , new DefaultHandler () });
server . setHandler ( handlers );
server . start ();
server . join ();
}
}
resourcehandler首先根据request检查本地文件系统上匹配的文件,如果没有找到匹配的文件,则交给defaulthandler继续处理,它会直接返回404错误。
使用xml来配置文件服务器
Jetty XML配置可以将java代码使用xml来配置,前面的文件服务器可以用下面的xml类进行配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?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>
<New class= "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" >
<Array type= "org.eclipse.jetty.server.Handler" >
<Item>
<New class= "org.eclipse.jetty.server.handler.ResourceHandler" >
<Set name= "directoriesListed" > true</Set>
<Set name= "welcomeFiles" >
<Array type= "String" ><Item> index.html</Item></Array>
</Set>
<Set name= "resourceBase" > .</Set>
</New>
</Item>
<Item>
<New class= "org.eclipse.jetty.server.handler.DefaultHandler" >
</New>
</Item>
</Array>
</Set>
</New>
</Set>
</Configure>
可以使用下面的代码来让这个xml文件运行起来:
1
2
3
4
5
6
7
8
9
10
11
public class FileServerXml
{
public static void main ( String [] args ) throws Exception
{
Resource fileserver_xml = Resource . newSystemResource ( "fileserver.xml" );
XmlConfiguration configuration = new XmlConfiguration ( fileserver_xml . getInputStream ());
Server server = ( Server ) configuration . configure ();
server . start ();
server . join ();
}
}
配置contexts
ContextHandler实际上是一个HandlerWrapper,它仅仅负责那些与它的contextPath相匹配的request请求。
匹配这个context的request请求的path属性被同时更新,比如contextPath,pathInfo(同一个requst uri在不同的context下面,path属性被截取的不一样)。下面的参数都可以有选择性的设置给context:
context的thread classloader
一些属性(attributes)
一些init参数
resource base
一些虚拟主机的名字
下面就是一个设置context的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 ();
}
}
创建servlets
下面是一个servlet:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class HelloServlet extends HttpServlet
{
private String greeting = "Hello World" ;
public HelloServlet (){}
public HelloServlet ( String greeting )
{
this . greeting = greeting ;
}
protected void doGet ( HttpServletRequest request , HttpServletResponse response ) 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是一个特殊的ContextHandler,它可以支持标准的servlet,下面是一个使用ServletContextHandler的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class OneServletContext
{
public static void main ( String [] args ) throws Exception
{
Server server = new Server ( 8080 );
ServletContextHandler context = new ServletContextHandler ( ServletContextHandler . SESSIONS );
context . setContextPath ( "/" );
server . setHandler ( context );
context . addServlet ( new ServletHolder ( new HelloServlet ()), "/*" );
context . addServlet ( new ServletHolder ( new HelloServlet ( "Buongiorno Mondo" )), "/it/*" );
context . addServlet ( new ServletHolder ( new HelloServlet ( "Bonjour le Monde" )), "/fr/*" );
server . start ();
server . join ();
}
}
配置WebAppContext
WebAppContext是一个ServletContextHandler的变种,准确的说WebAppContext继承于ServletContextHandler,它用标准的webapp文件组织布局和web.xml来配置servlets、filters和其他特性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 ();
}
}
如果webapp没有打包成war文件,那么可以指定webapp的根目录:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 ();
}
}
配置ContextHandlerCollection
ContextHandlerCollection可以使用request的uri来根据最长前缀的模式匹配对应的context,下面的例子就是同时安装两个context:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class ManyContexts
{
public static void main ( String [] args ) throws Exception
{
Server server = new Server ( 8080 );
ServletContextHandler context0 = new ServletContextHandler ( ServletContextHandler . SESSIONS );
context0 . setContextPath ( "/ctx0" );
context0 . addServlet ( new ServletHolder ( new HelloServlet ()), "/*" );
context0 . addServlet ( new ServletHolder ( new HelloServlet ( "Buongiorno Mondo" )), "/it/*" );
context0 . addServlet ( new ServletHolder ( new HelloServlet ( "Bonjour le Monde" )), "/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 ();
}
}
ContextHandlerCollection中可以添加多个ContextHandler,这个例子里就存放了一个ServletContextHandler和一个WebAppContext。