tomcat - Apache Tomcat:如何在 [重新] 部署期间防止 404 状态
问题描述
当我更改部署描述符(例如apache-tomcat/conf/Catalina/localhost/myApp.xml
)或将myApp.war
文件替换为我的 Web 应用程序的新版本时,它会停止并使用更新的配置myApp.xml
或来自 的新应用程序版本再次启动myApp.war
。
进入上下文路径的请求/myApp
将获得404
状态错误页面,直到应用程序完全加载并启用以服务请求。
我想对此进行自定义并显示一个503 temporary unavailable
错误页面。某处是否有配置指令?
到目前为止,我想到的解决方法包括:
- 根据需要编辑
apache-tomcat/conf/web.xml
和替换org.apache.catalina.servlets.DefaultServlet
自定义 Servlet 实现。 - 或者根据需要提供一个小型的自定义 Web 应用程序来
apache-tomcat/webapps/ROOT/
响应。
这些有点hacky恕我直言,我希望有人知道更轻量级的解决方案。我也知道部署多个版本的相同上下文路径(myApp##v001.xml
, myApp##v002.xml
, ...)的可能性,但是让多个应用程序实例并行运行还不是一种选择。
解决方案
我最终编写了一个小 servlet,它被部署为ROOT.war
并检查给定请求是否存在具有匹配上下文路径的不可用(=停止)部署。如果是,则返回 503 状态,否则返回 404。
为了访问外部部署tomcat-catalina
,除了 servlet api 之外,该应用还依赖于:
<dependencies>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
<version>${tomcatVersion}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>${tomcatVersion}</version>
<scope>provided</scope>
</dependency>
</dependencies>
它必须通过以下方式获得特权context.xml
:
<Context privileged="true"/>
这是映射到的 servlet <url-pattern>/</url-pattern>
:
package my.pkg;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.Container;
import org.apache.catalina.ContainerServlet;
import org.apache.catalina.Context;
import org.apache.catalina.Host;
import org.apache.catalina.Wrapper;
public class DefaultErrorPageServlet extends HttpServlet implements ContainerServlet {
private static final long serialVersionUID = 1L;
private Host host;
private Wrapper wrapper;
@Override
protected void doGet(final HttpServletRequest req, final HttpServletResponse resp)
throws ServletException, IOException {
if (matchesForeignUnavailableContextPath(req)) {
// 503 if there is an unavailabe deployment matching the request
sendUnavailable(resp);
} else {
// 404 if there is no unavailabe deployment matching the request
sendNotFound(resp);
}
}
@Override
protected void doPost(final HttpServletRequest req, final HttpServletResponse resp)
throws ServletException, IOException {
// Redirect to make the client GET the requested URL
resp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
final String requestUrl = req.getRequestURL().toString();
resp.addHeader("Location", requestUrl);
}
private boolean matchesForeignUnavailableContextPath(HttpServletRequest req) {
if (null != host) {
Container[] children = host.findChildren();
for (Container container : children) {
String contextName = container.getName();
Context context = (Context) host.findChild(contextName);
if (null != context && !contextName.isEmpty()) {
String contextPath = context.getPath();
boolean started = context.getState().isAvailable();
String requestUri = req.getRequestURI();
if (!started && requestUri.startsWith(contextPath)) {
return true;
}
}
}
}
return false;
}
private void sendNotFound(HttpServletResponse resp) throws IOException {
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
resp.getWriter().append(
// HTML document to show on 404 not found
);
}
private void sendUnavailable(HttpServletResponse resp) throws IOException {
resp.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
resp.getWriter().append(
// HTML document to show on 503 unavailable
);
}
@Override
public Wrapper getWrapper() {
return wrapper;
}
@Override
public void setWrapper(Wrapper wrapper) {
// see also org.apache.catalina.manager.ManagerServlet.setWrapper(Wrapper)
if (null == wrapper) {
return;
}
this.wrapper = wrapper;
final Context context = (Context) wrapper.getParent();
this.host = (Host) context.getParent();
}
}
推荐阅读
- dataframe - 我怎样才能找到每个/少数特征的一些阈值,以决定记录是属于类 True 还是 False。XGboost 分类器
- python - 在包含 X 和 Y 的两个 np 数组中查找 x,y 坐标
- r - 绕过 ifelse() 限制,将 156 个唯一值插入到大规模数据帧
- typescript - 如何从道具 Typescript 中解构函数?
- c - Mq_receive 看似崩溃的程序没有任何错误
- r - 具有多个(平行)列的 Pivot_wider
- javascript - Chart.js 饼图更新后不呈现
- sql - 执行命令从表中选择一个值
- android - 为了链接到 Google 的 Firebase,Build.Gradle 出错
- excel - 如何跳转到 Excel 中不匹配的记录?