库关闭例程在"普通"Java应用程序和Web应用程序中运行良好

Mar*_*eel 14 java servlets jdbc jna shutdown-hook

我维护一个JDBC驱动程序,它也具有通过本机库(通过JNA访问)提供的嵌入式数据库服务器模式.由于卸载其依赖项的顺序,作为卸载本机库本身的一部分完成的关闭在Windows上遇到问题.为了避免访问冲突或其他问题,我需要在卸载此库之前显式关闭嵌入式引擎.

鉴于其使用的性质,很难确定调用shutdown的适当时机,而我现在看到的普通Java应用程序的唯一正确方法是使用实现关闭逻辑Runtime.getRuntime().addShutdownHook的子类来注册一个关闭钩子Thread.

这对于一个普通的Java应用程序工作正常,但对于包括我的库作为应用程序(在部分Web应用程序WEB-INF/libWAR的),这将导致取消部署内存泄漏的关闭钩子将保持强引用我关机实现和Web应用程序的类加载器.

解决这个问题的合适和适当的方法是什么?我现在正在研究的选项是:

  • java.sql.DriverAction.deregister()做清理工作.

    不适合作为驱动程序将不会在正常的应用程序出口上注销.

  • 使用java.sql.DriverAction.deregister()以除去关闭挂钩并执行关机逻辑本身.

    DriverAction鉴于驱动程序仍然支持Java 7,使用有点问题,并且这个类是在JDBC 4.2(Java 8)中引入的.从技术上讲,这并不总是正确使用操作(JDBC驱动程序也可以在现有连接保持有效和正在使用时取消注册),并且可能在未注册javax.sql.DataSourceJDBC java.sql.Driver实现时使用(通过a )驱动程序.

  • 包括使用驱动程序javax.servlet.ServletContextListener注释的实现@WebListener,该实现将删除关闭挂钩并执行关闭逻辑本身.

    如果将驱动程序作为一个整体而不是特定的Web应用程序部署到服务器,则此选项会出现复杂情况(尽管可以解决这些复杂问题).

Java中是否有关闭机制我忽略了它可能适合我的需求?

Tom*_*ski 2

我试图弄清楚这一点,因为这看起来很有趣。我在这里发布我的发现,尽管我觉得我可能仍然误解了某些东西,或者做了一些过于牵强的简化。实际上,也有可能我完全误解了你的情况,这个答案毫无用处(如果是这样,我深表歉意)。

我在这里收集的内容基于两个概念:

  • 应用程序服务器全局状态(我使用System.props,但它可能不是最好的选择 - 也许一些临时文件会做得更好)
  • 容器特定的全局状态(这意味着容器特定的加载的所有类ClassLoader

我提出了一种EmbeddedEngineHandler.loadEmbeddedEngineIfNeeded方法,称为:

  • 在您的驾驶员注册期间
  • 在你的javax.sql.DataSource实现静态初始化程序中(如果这整个DataSource相关的事情是这样工作的 - 我对此知之甚少)

Runtime.removeShutdownHook如果我没猜错的话,你就根本不需要打电话了。

我在这里不确定的主要问题是 - 如果驱动程序是全局部署的,它是否会在初始化任何 servlet 之前注册?如果不是,那么我就错了,这行不通。但也许检查一下ClassLoader会有EmbeddedEngineHandler帮助呢?


这是EmbeddedEngineHandler

final class EmbeddedEngineHandler {

    private static final String PREFIX = ""; // some ID for your library here
    private static final String IS_SERVLET_CONTEXT = PREFIX + "-is-servlet-context";
    private static final String GLOBAL_ENGINE_LOADED = PREFIX + "-global-engine-loaded";

    private static final String TRUE = "true";

    private static volatile boolean localEngineLoaded = false;

    // LOADING
    static void loadEmbeddedEngineIfNeeded() {
        if (isServletContext()) {
            // handles only engine per container case
            loadEmbeddedEngineInLocalContextIfNeeded();
        } else {
            // handles both normal Java application & global driver cases
            loadEmbeddedEngineInGlobalContextIfNeeded();
        }

    }

    private static void loadEmbeddedEngineInLocalContextIfNeeded() {
        if (!isGlobalEngineLoaded() && !isLocalEngineLoaded()) { // will not load if we have a global driver
            loadEmbeddedEngine();
            markLocalEngineAsLoaded();
        }
    }

    private static void loadEmbeddedEngineInGlobalContextIfNeeded() {
        if (!isGlobalEngineLoaded()) {
            loadEmbeddedEngine();
            markGlobalEngineAsLoaded();
            Runtime.getRuntime().addShutdownHook(new Thread(EmbeddedEngineHandler::unloadEmbeddedEngine));
        }
    }

    private static void loadEmbeddedEngine() {
    }

    static void unloadEmbeddedEngine() {
    }

    // SERVLET CONTEXT (state shared between containers)
    private static boolean isServletContext() {
        return TRUE.equals(System.getProperty(IS_SERVLET_CONTEXT));
    }

    static void markAsServletContext() {
        System.setProperty(IS_SERVLET_CONTEXT, TRUE);
    }

    // GLOBAL ENGINE (state shared between containers)
    private static boolean isGlobalEngineLoaded() {
        return TRUE.equals(System.getProperty(GLOBAL_ENGINE_LOADED));
    }

    private static void markGlobalEngineAsLoaded() {
        System.setProperty(GLOBAL_ENGINE_LOADED, TRUE);
    }

    // LOCAL ENGINE (container-specific state)
    static boolean isLocalEngineLoaded() {
        return localEngineLoaded;
    }

    private static void markLocalEngineAsLoaded() {
        localEngineLoaded = true;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是ServletContextListener

@WebListener
final class YourServletContextListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        EmbeddedEngineHandler.markAsServletContext();
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        if (EmbeddedEngineHandler.isLocalEngineLoaded()) {
            EmbeddedEngineHandler.unloadEmbeddedEngine();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)