2

环境:JBoss AS 7.1.1.Final。

我有一个使用从 JBoss AS JNDI 获取的数据源的 WAR 应用程序。当我关闭服务器(控制台中的 Ctrl+C)时,应用程序收到关闭命令并开始销毁其 Spring 上下文。但是,我使用调度程序来执行一些数据库操作。当应用程序关闭时,我希望完成当前队列中的任务(但不接受新任务 - 标准 JDK Executor.shutdown() 行为)。当我在不停止服务器的情况下取消部署应用程序时,这可以正常工作。但是,当我停止整个服务器时,连接管理器会在应用程序取消部署之前关闭,这会导致

14:31:51,604 INFO  [org.jboss.as.logging] JBAS011503: Restored bootstrap log handlers
14:31:51,617 INFO  [org.apache.coyote.http11.Http11Protocol] Stopping Coyote HTTP/1.1 on http-127.0.0.1-127.0.0.1-18080
14:31:51,638 INFO  [org.hornetq.ra.HornetQResourceAdapter] HornetQ resource adapter stopped
14:31:51,653 INFO  [org.apache.catalina.core.ContainerBase.[jboss.web].[default-host].[/MY-APP]] Closing Spring root WebApplicationContext
14:31:51,656 INFO  [org.springframework.web.context.support.XmlWebApplicationContext] Closing Root WebApplicationContext: startup date [Tue Jul 09 14:30:56 CST 2013]; root of context hierarchy
14:31:51,659 INFO  [org.springframework.beans.factory.support.DefaultListableBeanFactory] Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@2c591927: defining beans [<snipped>]; root of factory hierarchy
14:31:51,662 INFO  [org.hornetq.core.server.impl.HornetQServerImpl] HornetQ Server version 2.2.13.Final (HQ_2_2_13_FINAL_AS7, 122) [5f713ff6-5f86-11e2-a25d-1f3857764d50] stopped
14:31:51,673 INFO  [MY-APP.Shutdown] Initializing shutdown. Already running tasks will be finished, new tasks will not be executed.
14:31:53,626 ERROR [org.springframework.scheduling.support.TaskUtils$LoggingErrorHandler] Unexpected error occurred in scheduled task.: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is java.sql.SQLException: javax.resource.ResourceException: IJ000451: The connection manager is shutdown: java:/my/DS1
    at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:80) [spring-jdbc-3.2.3.RELEASE.jar:3.2.3.RELEASE]
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:575) [spring-jdbc-3.2.3.RELEASE.jar:3.2.3.RELEASE]
    at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:818) [spring-jdbc-3.2.3.RELEASE.jar:3.2.3.RELEASE]
    at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:874) [spring-jdbc-3.2.3.RELEASE.jar:3.2.3.RELEASE]
    at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:882) [spring-jdbc-3.2.3.RELEASE.jar:3.2.3.RELEASE]
    at com.my.app.scanner.db.SyncEntryRepository.deleteById(SyncEntryRepository.java:26) [classes:]
    at com.my.app.rules.orphanentries.OrphanedEntriesProcessor.process(OrphanedEntriesProcessor.java:22) [classes:]
    at com.my.app.routing.Router$RoutingWorker.performRouting(Router.java:49) [classes:]
    at com.my.app.routing.Router$RoutingWorker.route(Router.java:32) [classes:]
    at com.my.app.routing.Router.route(Router.java:18) [classes:]
    at com.my.app.transformation.Transformation.perform(Transformation.java:21) [classes:]
    at com.my.app.MyApp.run(MyApp.java:18) [classes:]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [rt.jar:1.6.0_45]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) [rt.jar:1.6.0_45]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) [rt.jar:1.6.0_45]
    at java.lang.reflect.Method.invoke(Method.java:597) [rt.jar:1.6.0_45]
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:64) [spring-context-3.2.3.RELEASE.jar:3.2.3.RELEASE]
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:53) [spring-context-3.2.3.RELEASE.jar:3.2.3.RELEASE]
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:439) [rt.jar:1.6.0_45]
    at java.util.concurrent.FutureTask$Sync.innerRunAndReset(FutureTask.java:317) [rt.jar:1.6.0_45]
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:150) [rt.jar:1.6.0_45]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$101(ScheduledThreadPoolExecutor.java:98) [rt.jar:1.6.0_45]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.runPeriodic(ScheduledThreadPoolExecutor.java:180) [rt.jar:1.6.0_45]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:204) [rt.jar:1.6.0_45]
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895) [rt.jar:1.6.0_45]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918) [rt.jar:1.6.0_45]
    at java.lang.Thread.run(Thread.java:662) [rt.jar:1.6.0_45]
Caused by: java.sql.SQLException: javax.resource.ResourceException: IJ000451: The connection manager is shutdown: java:/my/DS1
    at org.jboss.jca.adapters.jdbc.WrapperDataSource.getConnection(WrapperDataSource.java:137)
    at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:111) [spring-jdbc-3.2.3.RELEASE.jar:3.2.3.RELEASE]
    at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:77) [spring-jdbc-3.2.3.RELEASE.jar:3.2.3.RELEASE]
    ... 26 more
Caused by: javax.resource.ResourceException: IJ000451: The connection manager is shutdown: java:/my/DS1
    at org.jboss.jca.core.connectionmanager.AbstractConnectionManager.getManagedConnection(AbstractConnectionManager.java:321)
    at org.jboss.jca.core.connectionmanager.tx.TxConnectionManagerImpl.getManagedConnection(TxConnectionManagerImpl.java:368)
    at org.jboss.jca.core.connectionmanager.AbstractConnectionManager.allocateConnection(AbstractConnectionManager.java:464)
    at org.jboss.jca.adapters.jdbc.WrapperDataSource.getConnection(WrapperDataSource.java:129)
    ... 28 more

14:31:53,640 INFO  [MY-APP.Shutdown] Shutdown complete
14:31:53,651 INFO  [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean] Closing JPA EntityManagerFactory for persistence unit 'default'
14:31:53,656 INFO  [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean] Closing JPA EntityManagerFactory for persistence unit 'default'
14:31:53,837 INFO  [org.jboss.as.server.deployment] JBAS015877: Stopped deployment MY-APP.war in 2259ms
14:31:56,158 INFO  [com.arjuna.ats.jbossatx] ARJUNA032018: Destroying TransactionManagerService
14:31:56,158 INFO  [com.arjuna.ats.jbossatx] ARJUNA032014: Stopping transaction recovery manager
14:31:56,160 INFO  [org.jboss.as] JBAS015950: JBoss AS 7.1.1.Final "Brontes" stopped in 4567ms

在我的standalone.xml中我有

<subsystem xmlns="urn:jboss:domain:datasources:1.0">
    <datasources>
        <datasource jndi-name="java:/my/DS1" pool-name="My1" enabled="true" use-java-context="true">
            <connection-url>jdbc:oracle:thin:@10.172.1.1:1521:ABCD</connection-url>
            <driver>oracle</driver>
            <pool>
                <min-pool-size>1</min-pool-size>
                <max-pool-size>10</max-pool-size>
                <prefill>true</prefill>
                <use-strict-min>false</use-strict-min>
            </pool>
            <security>
                <user-name>xxx</user-name>
                <password>xxx</password>
            </security>
            <timeout>
                <idle-timeout-minutes>5</idle-timeout-minutes>
            </timeout>
            <statement>
                <prepared-statement-cache-size>500</prepared-statement-cache-size>
                <share-prepared-statements>true</share-prepared-statements>
            </statement>
        </datasource>
            <drivers>
            <driver name="oracle" module="com.oracle"/>
        </drivers>
    </datasources>
</subsystem>

然后,我在代码中查找数据源:

DataSource ds = new InitialContext().lookup("java:/my/DS1");
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

必须以编程方式查找数据源,而不是在 spring.xml 中硬编码,因为可以有多个数据源,并且它们的 JNDI 名称在应用程序启动时扫描的外部属性文件中配置。

然后,将 jdbcTemplate 传递给使用 Spring 调度调用的 TableScanner:

public TableScanner(JdbTemplate jdbcTemplate) {
    this.jdbcTemplate = jdbcTemplate;
}

@Scheduled(fixedDelay = 5000)
public void run() {
    // query a table using jdbcTemplate and process the retrieved records, deleting them at the end of processing
}

在 run() 方法调用期间发生服务器停止时,应处理并删除所有检索到的记录。当应用程序尝试删除它们时,连接管理器已经关闭,因此出现异常。

有没有办法让连接管理器(或我可能需要的任何其他 JBoss AS 子系统)等到应用程序停止?

4

4 回答 4

2

我遇到了同样的问题,现在我找到了https://issues.jboss.org/browse/WFLY-944中描述的原因。为了避免这种情况,那里提出的解决方案是将 jndi 声明为 Web 应用程序中的资源,以便在应用程序终止之前它不会与 jboss 服务器解除绑定。

要实现这一点,请将以下部分添加到您的 web.xml 中,该部分设置对您的 jboss jndi 的引用,名称为“jdbc/myDS”。

<resource-ref>
<res-ref-name>jdbc/myDS</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
<lookup-name>java:/my/DS1</lookup-name>
</resource-ref>

然后,与其从指向实际 jndi 的代码部分查找“java:/my/DS1”,不如查找“java:comp/env/jdbc/myDS”,它将引用您放入的资源web.xml 实际上指向实际的 jndi(我们之前在 web.xml 中添加的部分中指定为“lookup-name”xml 属性):

数据源 ds = (DataSource) ctx.lookup("java:comp/env/jdbc/myDS");

于 2015-01-09T14:47:35.030 回答
1

我不知道 Spring Scheduling 提供了哪些功能,但是如果您使用标准 API 中的执行程序,则解决方案是ServletContextListener向您的 Web 应用程序添加一个,在取消部署或停止您的应用程序时由容器调用. 在该contextDestroyed(ServletContextEvent sce)方法中,您将调用类似的executor.shutdown(); executor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS)内容,这将停止执行程序并等待所有待处理的任务完成。在调用awaitTermination.

恕我直言,更好的解决方案可能是避免在 Java EE 应用程序中使用 Spring,至少当 Java EE 应用程序服务器也提供完全相同的功能时,例如在这种情况下。

于 2013-07-09T08:55:30.010 回答
0

您是否考虑过使用 EJB 调度 bean?这样 jboss 将产生工作线程。

于 2013-07-09T08:53:47.027 回答
0

我发现了这个 JBoss AS 问题,它反映了我的问题。看来,如果数据源没有通过@Resource 以静态方式绑定,那么服务器不知道数据源仍在使用中。我选择让 Spring 自己管理数据源,这可能不是 Java EE 方式,但可以完成工作并且适合我的情况。

于 2013-07-11T03:14:19.430 回答