java - 如何在 JAR 文件中的 Jetty 服务器中启用 JSP?
问题描述
tl;dr:我怎样才能为这个项目启用 JSP 支持(你也可以下载为 zip 文件)?
我正在尝试使用 Jetty 创建一个简单的“hello world”Web 应用程序,我对目前所拥有的非常满意。重要的文件是:
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.happycoding</groupId>
<artifactId>app-engine-hello-world</artifactId>
<version>1</version>
<properties>
<!-- App Engine currently supports Java 11 -->
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jetty.version>9.4.31.v20200723</jetty.version>
<!-- Project-specific properties -->
<exec.mainClass>io.happycoding.ServerMain</exec.mainClass>
<googleCloudProjectId>YOUR_PROJECT_ID_HERE</googleCloudProjectId>
</properties>
<dependencies>
<!-- Java Servlets API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<!-- Jetty -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-annotations</artifactId>
<version>${jetty.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Copy static resources like html files into the output jar file. -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.7</version>
<executions>
<execution>
<id>copy-web-resources</id>
<phase>compile</phase>
<goals><goal>copy-resources</goal></goals>
<configuration>
<outputDirectory>
${project.build.directory}/classes/META-INF/resources
</outputDirectory>
<resources>
<resource><directory>./src/main/webapp</directory></resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<!-- Package everything into a single executable jar file. -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>shade</goal></goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>${exec.mainClass}</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
<!-- App Engine plugin for deploying to the live site. -->
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>appengine-maven-plugin</artifactId>
<version>2.2.0</version>
<configuration>
<projectId>${googleCloudProjectId}</projectId>
<version>1</version>
</configuration>
</plugin>
</plugins>
</build>
</project>
ServerMain.java
package io.happycoding;
import java.net.URL;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.webapp.WebInfConfiguration;
/**
* Starts up the server, including a DefaultServlet that handles static files,
* and any servlet classes annotated with the @WebServlet annotation.
*/
public class ServerMain {
public static void main(String[] args) throws Exception {
// Create a server that listens on port 8080.
Server server = new Server(8080);
WebAppContext webAppContext = new WebAppContext();
server.setHandler(webAppContext);
// Load static content from inside the jar file.
URL webAppDir =
ServerMain.class.getClassLoader().getResource("META-INF/resources");
webAppContext.setResourceBase(webAppDir.toURI().toString());
// Enable annotations so the server sees classes annotated with @WebServlet.
webAppContext.setConfigurations(new Configuration[]{
new AnnotationConfiguration(),
new WebInfConfiguration(),
});
// Look for annotations in the classes directory (dev server) and in the
// jar file (live server)
webAppContext.setAttribute(
"org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",
".*/target/classes/|.*\\.jar");
// Handle static resources, e.g. html files.
webAppContext.addServlet(DefaultServlet.class, "/");
// Start the server!
server.start();
System.out.println("Server started!");
// Keep the main thread alive while the server is running.
server.join();
}
}
索引.jsp
<%@ page import="java.util.Date" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Google Cloud Hello World</title>
</head>
<body>
<h1>Google Cloud Hello World</h1>
<p>This is a sample HTML file. Click <a href="/hello">here</a> to see content served from a servlet.</p>
<p>Learn more at <a href="https://happycoding.io">HappyCoding.io</a>.</p>
<p>The current time is: <%= new Date().toString() %></p>
</body>
</html>
服务器启动并完美呈现 HTML。Servlet 也可以工作。但是当我尝试像上面那样使用 JSP 时,我看到 JSP 代码呈现在 HTML 中,而不是被解析为 Java。
我试过谷歌搜索,但我发现的每个教程都是通过创建一个单独的 WAR 文件来工作的。我的服务器是从 JAR 文件启动的,我试图使代码尽可能简单,所以我尽量避免使用单独的 WAR 文件。
我可以对我的pom.xml
文件和我的ServerMain.java
文件进行一些小的更改以启用 JSP 吗?
解决方案
要在嵌入式 Jetty 服务器中启用 JSP 支持,您需要做两件事,修改您的项目pom.xml
文件以包含必要的依赖项,并在您的类中配置JettyJspServlet
相关的 Jetty 内容。ServerMain
首先,在您的项目中包含以下依赖项pom.xml
。他们将为 JSP 和 JSTL 提供支持:
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>apache-jsp</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>apache-jstl</artifactId>
<version>${jetty.version}</version>
<type>pom</type>
</dependency>
然后,使用这个ServerMain
类的修改版本:
package io.happycoding;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import org.apache.tomcat.util.scan.StandardJarScanner;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.apache.jsp.JettyJasperInitializer;
import org.eclipse.jetty.jsp.JettyJspServlet;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.webapp.WebInfConfiguration;
/**
* Starts up the server, including a DefaultServlet that handles static files,
* and any servlet classes annotated with the @WebServlet annotation.
*/
public class ServerMain {
public static void main(String[] args) throws Exception {
// Create a server that listens on port 8080.
Server server = new Server(8080);
WebAppContext webAppContext = new WebAppContext();
server.setHandler(webAppContext);
// Load static content from inside the jar file.
URL webAppDir =
ServerMain.class.getClassLoader().getResource("META-INF/resources");
webAppContext.setResourceBase(webAppDir.toURI().toString());
// Enable annotations so the server sees classes annotated with @WebServlet.
webAppContext.setConfigurations(new Configuration[]{
new AnnotationConfiguration(),
new WebInfConfiguration(),
});
// Look for annotations in the classes directory (dev server) and in the
// jar file (live server)
webAppContext.setAttribute(
"org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",
".*/target/classes/|.*\\.jar");
// Handle static resources, e.g. html files.
webAppContext.addServlet(DefaultServlet.class, "/");
// Configure JSP support.
enableEmbeddedJspSupport(webAppContext);
// Start the server!
server.start();
System.out.println("Server started!");
// Keep the main thread alive while the server is running.
server.join();
}
/**
* Setup JSP Support for ServletContextHandlers.
* <p>
* NOTE: This is not required or appropriate if using a WebAppContext.
* </p>
*
* @param servletContextHandler the ServletContextHandler to configure
* @throws IOException if unable to configure
*/
private static void enableEmbeddedJspSupport(ServletContextHandler servletContextHandler) throws IOException
{
// Establish Scratch directory for the servlet context (used by JSP compilation)
File tempDir = new File(System.getProperty("java.io.tmpdir"));
File scratchDir = new File(tempDir.toString(), "embedded-jetty-jsp");
if (!scratchDir.exists())
{
if (!scratchDir.mkdirs())
{
throw new IOException("Unable to create scratch directory: " + scratchDir);
}
}
servletContextHandler.setAttribute("javax.servlet.context.tempdir", scratchDir);
// Set Classloader of Context to be sane (needed for JSTL)
// JSP requires a non-System classloader, this simply wraps the
// embedded System classloader in a way that makes it suitable
// for JSP to use
ClassLoader jspClassLoader = new URLClassLoader(new URL[0], ServerMain.class.getClassLoader());
servletContextHandler.setClassLoader(jspClassLoader);
// Manually call JettyJasperInitializer on context startup
servletContextHandler.addBean(new JspStarter(servletContextHandler));
// Create / Register JSP Servlet (must be named "jsp" per spec)
ServletHolder holderJsp = new ServletHolder("jsp", JettyJspServlet.class);
holderJsp.setInitOrder(0);
holderJsp.setInitParameter("logVerbosityLevel", "DEBUG");
holderJsp.setInitParameter("fork", "false");
holderJsp.setInitParameter("xpoweredBy", "false");
holderJsp.setInitParameter("compilerTargetVM", "1.8");
holderJsp.setInitParameter("compilerSourceVM", "1.8");
holderJsp.setInitParameter("keepgenerated", "true");
servletContextHandler.addServlet(holderJsp, "*.jsp");
}
/**
* JspStarter for embedded ServletContextHandlers
*
* This is added as a bean that is a jetty LifeCycle on the ServletContextHandler.
* This bean's doStart method will be called as the ServletContextHandler starts,
* and will call the ServletContainerInitializer for the jsp engine.
*
*/
public static class JspStarter extends AbstractLifeCycle implements ServletContextHandler.ServletContainerInitializerCaller
{
JettyJasperInitializer sci;
ServletContextHandler context;
public JspStarter (ServletContextHandler context)
{
this.sci = new JettyJasperInitializer();
this.context = context;
this.context.setAttribute("org.apache.tomcat.JarScanner", new StandardJarScanner());
}
@Override
protected void doStart() throws Exception
{
ClassLoader old = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(context.getClassLoader());
try
{
sci.onStartup(null, context.getServletContext());
super.doStart();
}
finally
{
Thread.currentThread().setContextClassLoader(old);
}
}
}
}
如您所见,与前面代码的主要区别在于enableEmbeddedJspSupport
在配置嵌入式 Jetty 服务器时包含对新方法的方法调用main
。
该enableEmbeddedJspSupport
方法和伴随类是您可以在此 Github 存储库中的类JspStarter
中找到的代码的改编版本。Main
推荐阅读
- sql - 在 SQL Server 中将负 Varchar、Null 转换为 Decimal
- javascript - 将 js 前端连接到 fastapi 应用程序 - 身份验证问题
- python-3.x - 从控制台文本输出中删除颜色代码
- python - 截断字符串并替换为“X”Python Pandas DataFrame
- python - 类型错误:预期字节,找到 pyarrow.lib.StringArray
- android - Android OrientationEventListener SensorManager.SENSOR_DELAY_NORMAL 必要性
- reactjs - React native {something} 不是函数
- javascript - 在 Heroku 上部署节点应用程序时出现问题 - 构建成功,但返回错误:找不到模块“请求”
- javascript - 用 Jest 如何正确地模拟/测试构造函数
- prolog - Prolog - 值范围列表,不包括范围