如何关闭在 WildFly 上正确定义为 DataSource 的嵌入式 Derby

How to shutdown embedded Derby which is defined as a DataSource properly on WildFly

当我在 WildFly 上正常定义 Derby DataSource 时,db.lck 文件未被删除,这表明每次关闭 WildFly 时数据库都没有正确关闭。因为嵌入式 Derby 需要一个特殊的关闭过程,该过程正在获取与 JDBC URL 的新连接,该连接以“;shutdown=true”字符串结尾。

所以,我需要类似关闭挂钩的东西来执行关闭过程。我发现老JBoss有办法实现:

https://developer.jboss.org/wiki/SetUpADerbyDatasource http://grepcode.com/file/repository.jboss.org/nexus/content/repositories/releases/org.jboss.jbossas/jboss-as-varia/6.0.0.Final/org/jboss/jdbc/DerbyDatabase.java

但我不知道如何在最近的 WildFly 上应用它,因为它的数据源定义中似乎不再允许 "mbean" 和 "depends" 元素,而且我找不到它相当于最近的 WildFly。

我觉得"connection-listener-class"数据源定义的变量很重要,不失为一种实现方式。我还没有尝试过,但它看起来有点复杂,我不确定它是否按预期工作。

那么,有没有办法定义一个 shutdown-hook 来使用最近的 WildFly 执行 Derby 的关闭过程?

编辑:

我发布了将 Apache Derby 安装到 WildFly 的说明,其中包括我的解决方案。 http://www.nailedtothex.org/roller/kyle/entry/installing-apache-derby-to-wildfly

connection-listener-class 如我所料。我注册了一个侦听器 class 然后 Derby 在 WildFly 每次关闭时正确地开始关闭。

但我应该记住一件事 - test-connection-in-pool 不会触发连接监听器-class。如果我只是启动 WildFly,然后执行 test-connection-in-pool 到 Derby 的数据源,然后关闭 WildFly,则永远不会调用侦听器并且 db.lck 将保持未删除状态。所以,这可能不是一个理想的解决方案。更直接的 shutdown-hook 之类的东西会更好。

我所做的如下:

  1. 将包含 ConnectionListener 实现和 module.xml 的 jar derby.jar 放入 $WILDFLY_HOME/modules/org/apache/derby/main
  2. 注册JDBC Derby 驱动程序(以下命令用于jboss-cli)
    /subsystem=datasources/jdbc-driver=derby:add(driver-name=derby, driver-module-name=org.apache.derby, driver-class-name=org.apache.derby.jdbc.EmbeddedDriver)
  3. 注册数据源
    data-source add --name=DerbyDS --driver-name=derby --connection-url=jdbc:derby:/Users/kyle/tmp/derbytest --jndi-name=java:jboss/jdbc/DerbyDS --user-name=sa --password=sa
  4. 配置连接监听器-class
    /subsystem=datasources/data-source=DerbyDS:write-attribute(name=connection-listener-class, value=org.nailedtothex.derby.DerbyShutdownConnectionListener)
  5. 配置连接监听器-属性
    /subsystem=datasources/data-source=DerbyDS:write-attribute(name=connection-listener-property,value={"url"=>"jdbc:derby:/Users/kyle/tmp/derbytest"}

module.xml

<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.0" name="org.apache.derby">
    <resources>
        <resource-root path="derby.jar"/>
        <resource-root path="derby-shutdown-hook-1.0-SNAPSHOT.jar"/>
    </resources>
    <dependencies>
        <module name="javax.api"/>
        <module name="javax.transaction.api"/>
        <module name="org.jboss.ironjacamar.jdbcadapters"/>
    </dependencies>
</module>

DerbyShutdownConnectionListener.java

Please refer GitHub for whole project contains pom.xml

import org.jboss.jca.adapters.jdbc.spi.listener.ConnectionListener;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;

public class DerbyShutdownConnectionListener implements ConnectionListener {

    private static final Logger log = Logger.getLogger(DerbyShutdownConnectionListener.class.getName());
    private static final String DEFAULT_URL = "jdbc:derby:";
    private static final String SHUTDOWN_SUFFIX = ";shutdown=true";

    private String url;
    private String urlForShutdown;

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    @Override
    synchronized public void initialize(final ClassLoader classLoader) throws SQLException {
        urlForShutdown = createUrlForShutdown();
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                log.log(Level.INFO, "Shutdown derby. URL to use: {0}", urlForShutdown);
                shutdown(urlForShutdown);
            }
        });
        log.log(Level.INFO, "Derby shutdown hook added. URL to use: {0}", urlForShutdown);
    }

    private String createUrlForShutdown() {
        return (url == null ? DEFAULT_URL : url) + SHUTDOWN_SUFFIX;
    }

    private void shutdown(String url) {
        Connection cn = null;
        try {
            cn = DriverManager.getConnection(url);
        } catch (SQLException e) {
            if ("08006".equals(e.getSQLState()) || "XJ015".equals(e.getSQLState())) {
                log.log(Level.INFO, "Derby shutdown succeeded. SQLState={0}", e.getSQLState());
                return;
            }
            log.log(Level.SEVERE, "Derby shutdown failed", e);
        } finally {
            if (cn != null) {
                try {
                    cn.close();
                } catch (Exception e) {
                }
            }
        }
    }

    @Override
    public void activated(Connection connection) throws SQLException {
    }

    @Override
    public void passivated(Connection connection) throws SQLException {
    }
}

我找到了更好的 MBean 解决方案。它只是在每次 WildFly 关闭时执行关闭程序。

只需克隆 this repository 并构建一个 jar,将其放入 $WILDFLY_HOME/standalone/deployments。项目中有各种文件,所以将所有文件都粘贴到这里很烦人。

对 org.jboss.ironjacamar.jdbcadapters、connection-listener-class 和 connection-listener-属性 的依赖是不必要的,所以现在 $WILDFLY_HOME/modules/org/apache/derby/main/module.xml 可以简化如下:

module.xml

<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.0" name="org.apache.derby">
    <resources>
        <resource-root path="derby.jar"/>
    </resources>
    <dependencies>
        <module name="javax.api"/>
        <module name="javax.transaction.api"/>
    </dependencies>
</module>