Jetty 可以在嵌入式模式下运行。这意味着无需构建 WAR 文件并将其部署在独立的 Jetty 服务器中。Jetty 是一个软件组件,可以像任何其他 POJO(Plain Old Java Object)一样实例化和使用。
简单的处理程序
AHandler
是处理传入请求的组件。请求在handle
方法中处理。处理程序被传递 servlet API 请求和响应对象,但它们不是 servlet。
$ mkdir simplehandler
$ cd simplehandler/
$ mkdir -p src/com/zetcode
$ mkdir build
$ touch src/com/zetcode/SimpleHandlerEx.java
我们创建一个新的项目目录及其结构。
SimpleHandlerEx.java
package com.zetcode;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
public class SimpleHandlerEx extends AbstractHandler {
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException {
response.setContentType("text/plain;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
baseRequest.setHandled(true);
response.getWriter().println("Hello there");
}
public static void main(String[] args) throws Exception {
Server server = new Server(8080);
server.setHandler(new SimpleHandlerEx());
server.start();
server.join();
}
}
启动服务器并为传入请求设置处理程序。
response.setContentType("text/plain;charset=utf-8");
由于我们只输出简单的文本,我们设置 text/plain 媒体类型。Internet 媒体类型是 Internet 上使用的标准标识符,用于指示文件包含的数据类型。
response.getWriter().println("Hello there");
作为响应,我们发送一条简单的消息。该getWriter
方法返回一个PrintWriter
向客户端发送字符文本的对象。
server.join();
该join
方法使服务器线程加入当前线程。在服务器准备好之前它一直处于阻塞状态。该方法还等到服务器完全停止。
<?xml version="1.0" encoding="UTF-8"?>
<project name="SimpleHandlerEx" default="compile">
<property name="name" value="simplehandler"/>
<property environment="env"/>
<property name="src.dir" value="src"/>
<property name="build.dir" value="build"/>
<property name="jetty.lib.dir" location="${env.JETTY_HOME}/lib"/>
<path id="compile.classpath">
<fileset dir="${jetty.lib.dir}">
<include name="**/*.jar"/>
</fileset>
</path>
<path id="run.classpath">
<pathelement path="${build.dir}"/>
<fileset dir="${jetty.lib.dir}">
<include name="**/*.jar"/>
</fileset>
</path>
<target name="init">
<mkdir dir="${build.dir}"/>
</target>
<target name="compile" depends="init">
<javac srcdir="${src.dir}" destdir="${build.dir}"
includeantruntime="false">
<classpath refid="compile.classpath"/>
</javac>
<echo>Compilation completed</echo>
</target>
<target name="run" depends="compile">
<echo>Running the program</echo>
<java classname="com.zetcode.SimpleHandlerEx"
classpathref="run.classpath"/>
</target>
<target name="clean" depends="init">
<delete dir="${build.dir}"/>
<echo>Cleaning completed</echo>
</target>
</project>
构建文件包括一个运行任务,它执行编译的应用程序。
<property name="jetty.lib.dir" location="${env.JETTY_HOME}/lib"/>
要编译和运行我们的示例,我们需要一些 JAR。它们位于lib/
Jetty 主页的子目录中。
$ ant run
$ curl localhost:8080
Hello there
当我们向 curl
应用程序发送一个简单的 GET 请求时,我们会收到此消息。
定义上下文根
Web 应用程序的上下文根决定了哪些 URL 将被委派给应用程序。JettyContextHandler
可以定义应用程序的上下文。
ContextEx.java
package com.zetcode;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.ContextHandler;
class MyHandler extends AbstractHandler {
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException {
response.setContentType("text/plain;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
baseRequest.setHandled(true);
response.getWriter().println("Hello there");
}
}
public class ContextEx {
public static void main(String[] args) throws Exception {
Server server = new Server(8080);
ContextHandler con = new ContextHandler();
con.setContextPath("/path");
con.setHandler(new MyHandler());
server.setHandler(con);
server.start();
server.join();
}
}
我们应用程序的处理程序将为名为 的上下文根调用/path
。
ContextHandler context = new ContextHandler();
context.setContextPath("/path");
实例化AContextHandler
并设置路径。
context.setHandler(new MyHandler());
处理程序是为上下文处理程序对象设置的。
server.setHandler(context);
我们使用该方法将上下文处理程序对象设置为服务器setHandler
。
$ curl localhost:8080/path/
Hello there
我们将请求发送到定义的上下文根。对于不匹配的上下文,Jetty 返回404
错误消息。
简单servlet
在以下示例中,将在向嵌入式 Jetty 发送带有特定 URL 的请求时启动 Java servlet。
$ mkdir simpleservlet
$ cd simpleservlet/
$ mkdir -p src/com/zetcode/
$ touch src/com/zetcode/SimpleServlet.java
$ touch src/com/zetcode/SimpleApp.java
$ touch src/web/WEB-INF/web.xml
$ touch build.xml
我们创建一个项目目录及其结构。我们使用 Ant 构建项目并使用web.xml
部署描述符来配置我们的 Web 应用程序。
$ tree
.
├── build.xml
└── src
├── com
│ └── zetcode
│ ├── SimpleApp.java
│ └── SimpleServlet.java
└── web
└── WEB-INF
└── web.xml
5 directories, 4 files
此时这些是项目目录的内容。
package com.zetcode;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;
public class SimpleApp {
public static void main(String[] args) throws Exception {
String webdir = "src/web/";
Server server = new Server(8080);
WebAppContext wcon = new WebAppContext();
wcon.setContextPath("/simserv");
wcon.setDescriptor(webdir + "/WEB-INF/web.xml");
wcon.setResourceBase(webdir);
wcon.setParentLoaderPriority(true);
server.setHandler(wcon);
server.start();
server.join();
}
}
为了在嵌入式模式下配置我们的 Web 应用程序,我们使用了 WebAppContext
该类。此类是 ContextHandler
协调 Web 应用程序处理程序的构造和配置的扩展。
wcon.setContextPath("/simserv");
使用该setContextPath
方法定义上下文路径。
wcon.setDescriptor(webdir + "/WEB-INF/web.xml");
部署描述符是使用该setDescriptor
方法设置的。
wcon.setResourceBase(webdir);
我们为我们的应用程序设置了文档根目录。它是一个目录(或目录或 URL 的集合),其中包含上下文的静态资源。这些可以是图像、HTML 文件或 JSP 文件。
package com.zetcode;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.*;
public class SimpleServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/plain");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println("Simple servlet");
}
}
SimpleServlet
将以纯文本消息响应 。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>SimpleServlet</servlet-name>
<servlet-class>com.zetcode.SimpleServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SimpleServlet</servlet-name>
<url-pattern>simple.do</url-pattern>
</servlet-mapping>
</web-app>
在web.xml
文件中,执行com.zetcode.SimpleServlet
被映射到simple.do
字符串。这必须在请求 URL 的末尾指定。
build.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="SimpleServlet" default="compile">
<property name="name" value="simple"/>
<property environment="env"/>
<property name="src.dir" value="src"/>
<property name="web.dir" value="${src.dir}/web"/>
<property name="build.dir" location="${web.dir}/WEB-INF/classes"/>
<property name="jetty.lib.dir" location="${env.JETTY_HOME}/lib"/>
<path id="compile.classpath">
<fileset dir="${jetty.lib.dir}">
<include name="**/*.jar"/>
</fileset>
</path>
<path id="run.classpath">
<pathelement path="${build.dir}" />
<fileset dir="${jetty.lib.dir}">
<include name="**/*.jar"/>
</fileset>
</path>
<target name="init">
<mkdir dir="${build.dir}"/>
</target>
<target name="compile" depends="init">
<javac srcdir="${src.dir}" destdir="${build.dir}"
includeantruntime="false">
<classpath refid="compile.classpath"/>
</javac>
<echo>Compilation completed</echo>
</target>
<target name="clean" depends="init">
<delete dir="${build.dir}"/>
<echo>Cleaning completed</echo>
</target>
<target name="run" depends="compile">
<echo>Running the program</echo>
<java classname="com.zetcode.SimpleApp"
classpathref="run.classpath"/>
</target>
</project>
这是随附的 Ant 构建文件。
$ ant run
$ curl localhost:8080/simserv/simple.do
Simple servlet
我们构建并运行应用程序。发送 GET 消息以 localhost:8080/simserv/simple.do
返回“Simple servlet”纯消息。
@WebServlet 注释
@WebServlet
注释用于声明一个 servlet 。注释由 servlet 容器在部署时处理。声明的 servlet 在指定的 URL 模式下可用。
$ tree
.
├── build.xml
└── src
├── com
│ └── zetcode
│ ├── AnnotatedAppEx.java
│ └── MyServlet.java
└── web
└── WEB-INF
5 directories, 3 files
这些是我们项目目录的内容。在此示例中,我们不包含该web.xml
文件,因为对于 Servlet 3.1 API,包含此文件是可选的。
package com.zetcode;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.plus.webapp.EnvConfiguration;
import org.eclipse.jetty.plus.webapp.PlusConfiguration;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.FragmentConfiguration;
import org.eclipse.jetty.webapp.MetaInfConfiguration;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.webapp.WebInfConfiguration;
import org.eclipse.jetty.webapp.WebXmlConfiguration;
public class AnnotatedAppEx {
public static void main(String[] args) throws Exception {
String webdir = "src/web/";
Server server = new Server(8080);
WebAppContext wcon = new WebAppContext();
wcon.setResourceBase(webdir);
wcon.setContextPath("/annotated");
wcon.setConfigurations(new Configuration[] {
new AnnotationConfiguration(), new WebXmlConfiguration(),
new WebInfConfiguration(), new PlusConfiguration(),
new MetaInfConfiguration(), new FragmentConfiguration(),
new EnvConfiguration() });
wcon.setParentLoaderPriority(true);
server.setHandler(wcon);
server.start();
server.join();
}
}
此类启动启用了注释的嵌入式 Jetty 服务器。
wcon.setConfigurations(new Configuration[] {
new AnnotationConfiguration(), new WebXmlConfiguration(),
new WebInfConfiguration(), new PlusConfiguration(),
new MetaInfConfiguration(), new FragmentConfiguration(),
new EnvConfiguration() });
要启用注解,我们需要设置这些配置类。这提供了对多种功能的支持。
package com.zetcode;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
@WebServlet(urlPatterns = { "/aserv" })
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/plain");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println("MyServlet called");
}
}
在MyServlet.java
文件中,我们提供@WebServlet
注释。servlet 在/aserv
url 上可用。
build.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="AnnotatedServlet" default="compile">
<property name="name" value="annotated"/>
<property environment="env"/>
<property name="src.dir" value="src"/>
<property name="web.dir" value="${src.dir}/web"/>
<property name="build.dir" location="${web.dir}/WEB-INF/classes"/>
<property name="jetty.lib.dir" location="${env.JETTY_HOME}/lib"/>
<path id="compile.classpath">
<fileset dir="${jetty.lib.dir}">
<include name="**/*.jar"/>
</fileset>
</path>
<path id="run.classpath">
<pathelement path="${build.dir}"/>
<fileset dir="${jetty.lib.dir}">
<include name="**/*.jar"/>
</fileset>
</path>
<target name="init">
<mkdir dir="${build.dir}"/>
</target>
<target name="compile" depends="init">
<javac srcdir="${src.dir}" destdir="${build.dir}"
includeantruntime="false">
<classpath refid="compile.classpath"/>
</javac>
<echo>Compilation completed</echo>
</target>
<target name="clean" depends="init">
<delete dir="${build.dir}"/>
<echo>Cleaning completed</echo>
</target>
<target name="run" depends="compile">
<echo>Running the program</echo>
<java classname="com.zetcode.AnnotatedAppEx"
classpathref="run.classpath"/>
</target>
</project>
这是我们项目的 Ant 构建文件。
$ ant run
$ curl localhost:8080/annotated/aserv
MyServlet called
已MyServlet
成功启动。
启用 JavaServer 页面
为了在嵌入式 Jetty 模式下启用 JavaServer Pages,我们需要创建一个临时 servlet 目录,设置一个非系统类加载器,添加一个jsp
servlet,并添加一个default
servlet。
在以下示例中,我们创建一个简单的 JavaServer Page 并构建一个 WAR 存档。此存档将设置为以嵌入式模式启动的 Jetty。
$ pwd
/home/janbodnar/prog/jetty/jspexample
$ tree
.
├── build.xml
└── src
├── com
│ └── zetcode
│ └── JSPExample.java
└── web
├── index.jsp
└── WEB-INF
5 directories, 3 files
这是项目结构。
index.jsp
<!DOCTYPE html>
<html>
<body>
<p>
Today's date: <%= (new java.util.Date()).toLocaleString() %>
</p>
</body>
</html>
我们的 JSP 将输出当前的本地化日期。
package com.zetcode;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import org.apache.jasper.servlet.JspServlet;
import org.apache.tomcat.InstanceManager;
import org.apache.tomcat.SimpleInstanceManager;
import org.eclipse.jetty.annotations.ServletContainerInitializersStarter;
import org.eclipse.jetty.apache.jsp.JettyJasperInitializer;
import org.eclipse.jetty.plus.annotation.ContainerInitializer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.webapp.WebAppContext;
public class JSPExample {
private void startServer() throws Exception {
String jetty_base = "/home/janbodnar/prog/jetty/my-base";
File tmpdir = new File(System.getProperty("java.io.tmpdir"));
File scdir = new File(tmpdir.toString(), "embedded-jetty-jsp");
if (!scdir.exists()) {
if (!scdir.mkdirs()) {
throw new IOException("Unable to create scratch directory: " + scdir);
}
}
Server server = new Server(8080);
WebAppContext wcon = new WebAppContext();
wcon.setParentLoaderPriority(true);
wcon.setContextPath("/");
wcon.setAttribute("javax.servlet.wcon.tempdir", scdir);
wcon.setAttribute(InstanceManager.class.getName(),
new SimpleInstanceManager());
server.setHandler(wcon);
JettyJasperInitializer sci = new JettyJasperInitializer();
ServletContainerInitializersStarter sciStarter =
new ServletContainerInitializersStarter(wcon);
ContainerInitializer initializer = new ContainerInitializer(sci, null);
List<ContainerInitializer> initializers = new ArrayList<>();
initializers.add(initializer);
wcon.setAttribute("org.eclipse.jetty.containerInitializers", initializers);
wcon.addBean(sciStarter, true);
ClassLoader jspClassLoader = new URLClassLoader(new URL[0],
this.getClass().getClassLoader());
wcon.setClassLoader(jspClassLoader);
ServletHolder holderJsp = new ServletHolder("jsp", JspServlet.class);
holderJsp.setInitOrder(0);
holderJsp.setInitParameter("fork","false");
holderJsp.setInitParameter("keepgenerated", "true");
wcon.addServlet(holderJsp, "*.jsp");
ServletHolder holderDefault = new ServletHolder("default",
DefaultServlet.class);
holderDefault.setInitParameter("dirAllowed", "true");
wcon.addServlet(holderDefault, "/");
wcon.setWar(jetty_base + "/webapps/jspexample.war");
server.setHandler(wcon);
server.start();
server.join();
}
public static void main(String[] args) throws Exception {
JSPExample ex = new JSPExample();
ex.startServer();
}
}
我们启用 JSP 支持并设置一个名为jspexample.war
Web 应用程序上下文的 WAR 文件。
File tmpdir = new File(System.getProperty("java.io.tmpdir"));
File scdir = new File(tmpdir.toString(), "embedded-jetty-jsp");
if (!scdir.exists()) {
if (!scdir.mkdirs()) {
throw new IOException("Unable to create scratch directory: " + scdir);
}
}
我们为 servlet 上下文创建一个临时目录。它在 JSP 编译期间使用。
System.setProperty("org.apache.jasper.compiler.disablejsr199","false");
此行强制使用标准 javac。(Eclipse 也使用了一种。)
JettyJasperInitializer sci = new JettyJasperInitializer();
ServletContainerInitializersStarter sciStarter =
new ServletContainerInitializersStarter(wcon);
ContainerInitializer initializer = new ContainerInitializer(sci, null);
List<ContainerInitializer> initializers = new ArrayList<>();
initializers.add(initializer);
wcon.setAttribute("org.eclipse.jetty.containerInitializers", initializers);
wcon.addBean(sciStarter, true);
这是JSP 初始化代码。
ClassLoader jspClassLoader = new URLClassLoader(new URL[0],
this.getClass().getClassLoader());
wcon.setClassLoader(jspClassLoader);
JSP 需要一个非系统类加载器,这只是简单地包装了嵌入式系统类加载器,使其适合 JSP 使用。
ServletHolder holderJsp = new ServletHolder("jsp", JspServlet.class);
holderJsp.setInitOrder(0);
holderJsp.setInitParameter("fork","false");
holderJsp.setInitParameter("keepgenerated", "true");
wcon.addServlet(holderJsp, "*.jsp");
我们添加一个JspServlet
. 它的名字必须是“jsp”。
ServletHolder holderDefault = new ServletHolder("default",
DefaultServlet.class);
holderDefault.setInitParameter("dirAllowed", "true");
wcon.addServlet(holderDefault, "/");
我们添加一个DefaultServlet
. 它的名称必须是“默认”。
wcon.setWar(jetty_base + "/webapps/jspexample.war");
WAR 文件设置为 Web 应用程序上下文。
<?xml version="1.0" encoding="UTF-8"?>
<project name="JSPExample" default="compile">
<property name="name" value="jspexample"/>
<property environment="env"/>
<property name="src.dir" value="src"/>
<property name="web.dir" value="${src.dir}/web"/>
<property name="build.dir" location="${web.dir}/WEB-INF/classes"/>
<property name="dist.dir" location="dist"/>
<property name="jetty.lib.dir" location="${env.JETTY_HOME}/lib"/>
<property name="jetty.base" location="${env.JETTY_BASE}"/>
<property name="deploy.path" location="${jetty.base}/webapps"/>
<path id="compile.classpath">
<fileset dir="${jetty.lib.dir}">
<include name="**/*.jar"/>
</fileset>
</path>
<path id="run.classpath">
<pathelement path="${build.dir}"/>
<fileset dir="${jetty.lib.dir}">
<include name="**/*.jar"/>
</fileset>
</path>
<target name="init">
<mkdir dir="${build.dir}"/>
<mkdir dir="${dist.dir}"/>
</target>
<target name="compile" depends="init">
<javac srcdir="${src.dir}" destdir="${build.dir}"
includeantruntime="false">
<classpath refid="compile.classpath"/>
</javac>
<echo>Compilation completed</echo>
</target>
<target name="archive" depends="compile">
<war destfile="${dist.dir}/${name}.war" needxmlfile="false">
<fileset dir="${web.dir}"/>
</war>
<echo>Archive created</echo>
</target>
<target name="clean" depends="init">
<delete dir="${build.dir}"/>
<delete dir="${dist.dir}"/>
<echo>Cleaning completed</echo>
</target>
<target name="deploy" depends="archive">
<copy file="${dist.dir}/${name}.war" todir="${deploy.path}"/>
<echo>Archive deployed</echo>
</target>
<target name="run" depends="deploy">
<echo>Running the program</echo>
<java classname="com.zetcode.JSPExample"
classpathref="run.classpath"/>
</target>
</project>
这是我们项目的 Ant 构建。所有必要的 jars 都可以在该JETTY_HOME
目录的 lib 子目录中找到。
发出ant run
会导致以下安全异常:java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "getClassLoader")。
我们需要在java.policy
文件中添加一个配置选项。
$ vi $JAVA_HOME/jre/lib/security/java.policy
我们编辑java.policy
文件。
permission java.lang.RuntimePermission "getClassLoader", "read";
我们添加了这个权限。
$ ant run
$ curl localhost:8080/index.jsp
<!DOCTYPE html>
<html>
<body>
<p>
Today's date: Sep 16, 2014 1:54:05 PM
</p>
</body>
</html>
JSP 返回当前日期。
在 Jetty 教程的这一部分中,我们在嵌入式模式下使用了 Jetty。我们已经定义了一个简单的处理程序和 servlet,使用了一个@WebServlet
注解,并启用了 JSP 支持。