You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下Apache Tomcat 8实现浏览器关闭时通过CGI执行Shell清理脚本

Got it, let's tackle this problem head-on—since you can't modify the target HTML (it's constantly changing) but need to trigger that cleanup shell script via CGI when a browser window closes, here are three practical, workable approaches tailored to your Tomcat 8 setup:


1. Detect Connection Closures with a Tomcat Servlet Filter

When a browser window closes, the underlying HTTP connection is usually terminated (though some browsers keep connections alive briefly for pooling). You can use a Tomcat Filter to listen for these connection events and trigger your cleanup script directly.

Steps to Implement:

  • Create a Java Filter class and deploy it to your Tomcat webapp (the same one hosting your CGI scripts):
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@WebFilter("/*") // Match all requests, or narrow to your target page path
public class ConnectionCloseDetector implements Filter {

    @Override
    public void init(FilterConfig config) throws ServletException {}

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpReq = (HttpServletRequest) req;
        AsyncContext asyncCtx = httpReq.startAsync();
        
        // Add a listener to catch connection completion/closure
        asyncCtx.addListener(new AsyncListener() {
            @Override
            public void onComplete(AsyncEvent event) throws IOException {
                // Trigger cleanup when the connection finishes (includes browser close)
                runCleanupScript();
            }

            @Override
            public void onTimeout(AsyncEvent event) throws IOException {}
            @Override
            public void onError(AsyncEvent event) throws IOException {}
            @Override
            public void onStartAsync(AsyncEvent event) throws IOException {}
        });
        
        chain.doFilter(req, res);
    }

    private void runCleanupScript() {
        try {
            // Replace with your actual CGI cleanup script path
            Process process = Runtime.getRuntime().exec("/var/lib/tomcat8/webapps/ROOT/cgi-bin/cleanup.sh");
            process.waitFor();
        } catch (Exception e) {
            // Log errors instead of printing to console in production
            e.printStackTrace();
        }
    }

    @Override
    public void destroy() {}
}
  • Caveats: Some browsers may hold connections open in a pool, so there might be a small delay before the trigger fires. Add logic to track user sessions (via HttpSession) to avoid running the script multiple times for the same user.

2. Use a Reverse Proxy (e.g., Nginx) to Detect Client Disconnects

If your Tomcat sits behind a reverse proxy like Nginx, you can leverage Nginx's ability to detect when a client drops the connection, then trigger your CGI script directly.

Example Nginx Config Snippet:

location /your-target-page {
    proxy_pass http://localhost:8080/your-target-page;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_read_timeout 30s;

    # Trigger cleanup when the client disconnects
    post_action @run-cleanup;
}

location @run-cleanup {
    internal; # Only accessible internally, not via direct requests
    # Forward to your Tomcat CGI endpoint
    proxy_pass http://localhost:8080/cgi-bin/cleanup.sh;
}
  • Notes: Ensure your Nginx version supports post_action (most modern versions do). Also, make sure Nginx has permissions to forward requests to Tomcat, and Tomcat's CGI handler is configured correctly.

3. WebSocket Connection Closure (Most Reliable)

WebSockets maintain a persistent connection, and the server gets an immediate onclose event when the browser window closes. Since you can't modify the target HTML, use a Filter to inject a tiny WebSocket connection script into the page's response automatically.

Step 1: Deploy a WebSocket Endpoint

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/ws-cleanup")
public class CleanupWebSocket {

    @OnOpen
    public void onOpen(Session session) {
        // Optional: Track sessions to avoid duplicate cleanup runs
    }

    @OnClose
    public void onClose(Session session) {
        // Runs immediately when the browser closes the WebSocket
        runCleanupScript();
    }

    private void runCleanupScript() {
        try {
            Process process = Runtime.getRuntime().exec("/var/lib/tomcat8/webapps/ROOT/cgi-bin/cleanup.sh");
            process.waitFor();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Step 2: Inject WebSocket Code via Filter

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebFilter("/your-target-page") // Target only your dynamic page
public class WebSocketInjectorFilter implements Filter {

    @Override
    public void init(FilterConfig config) throws ServletException {}

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse httpRes = (HttpServletResponse) res;
        PrintWriter out = httpRes.getWriter();
        
        // Let Tomcat serve the original page first
        chain.doFilter(req, res);
        
        // Inject WebSocket connection script at the end of the page
        out.println("<script>");
        out.println("const ws = new WebSocket(`ws://${window.location.host}/ws-cleanup`);");
        out.println("ws.onclose = () => {}; // No client-side action needed");
        out.println("</script>");
        out.flush();
    }

    @Override
    public void destroy() {}
}
  • Why this works: The Filter modifies the page's response on the fly, so you don't need to touch the original HTML. WebSocket closure events are far more reliable than HTTP connection checks for detecting browser window closes.

General Tips

  • Avoid duplicate runs: Use user session IDs (from HttpSession or WebSocket Session) to mark when cleanup has already been executed for a user.
  • Permissions: Ensure the Tomcat system user (e.g., tomcat) has execute permissions on your cleanup script: chmod +x /path/to/cleanup.sh.
  • Testing: Test with multiple browsers—some handle connection closures differently (e.g., Chrome vs. Firefox).

内容的提问来源于stack exchange,提问作者Sanjay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:19:58