以JavaEE友好的方式获取一个适用于JavaSE的计时器(用于JDBC驱动程序)

Cra*_*ger 7 postgresql jdbc timer java-ee threadpool

我有兴趣在PostgreSQL JDBC驱动程序上做一些工作来帮助实现驱动程序中Statement.setQueryTimeout(...)一个更有问题的规范一致性漏洞.为此,我需要一种可移植的方法来获取计时器或设置在所有Java EE应用服务器,servlet容器和Java SE环境中都能运行的警报/回调.

它似乎并不像它应该的那样简单,而且我已经足够坚持了,我会把自己倚靠在你的怜悯之上.我该怎么做一个在Java SE,Java EE和servlet容器中工作的简单定时器回调?

如果有必要,我可以忍受单独的-ee和-se版本,但这是非常不受欢迎的.每个容器的发布完全不切实际,但如果强烈不合适,则可以接受自动选择的每个容器适配器.

PgJDBC驱动程序必须在古老版本的JVM下运行在硬件旧的应用程序服务器中,但是如果语句超时仅在现代容器和JVM的驱动程序的JDBC4版本中可用,我并不在意.已经有一个条件编译基础设施允许发布JDBC3/JDK 1.4和JDBC4/JDK 1.5驱动程序,因此只能在1.5或甚至1.6下运行的代码不是问题.

(编辑):一个额外的复杂性是用户可以部署JDBC驱动程序:

  • 作为在容器启动时启动的容器模块或内置组件;
  • 作为可以在运行时取消部署和重新部署的独立部署对象; 要么
  • 嵌入他们的应用程序warear

...我们需要支持所有这些场景,最好不需要自定义应用程序配置!如果我们不能支持所有这些场景,它至少需要在没有语句超时支持的情况下工作,并且在不支持语句超时的情况下优雅地失败.

啊,写一次,跑啊......

我不能只使用java.util.Timerjava.util.concurrent:

我看到广泛的声明,在Java EE中不鼓励使用java.util.TimerJava SE并发实用程序(JSR-166)java.util.concurrent,但很少有任何细节.JSR 236提案说:

java.util.Timer,java.lang.Thread和java.util.concurrency(sic)包中的Java SE并发实用程序(JSR-166)永远不应该在托管环境中使用,因为它创建了在受管环境之外的线程.容器.

更多的阅读表明来自非托管线程的调用不会获得容器服务,因此整个应用程序中的各种事情可能会以令人兴奋和意想不到的方式中断.假定定时器调用可能导致PgJDBC抛出异常并传播到用户应用程序代码中,这很重要.

(编辑):它自己的JDBC驱动程序不需要任何容器服务,因此我不关心它们是否在其计时器线程内工作,只要这些线程永远不会运行任何用户代码.问题是可靠地确保他们不这样做.

JSR 236计时器抽象层已不存在

JSR 236已经不存在了,我没有看到任何满足便携式计时器相同要求的替代品.

我找不到任何对跨容器可移植方式的引用来获得容器池计时器.如果我可以从容器上的JNDI中获取一个计时器并回退到直接实例化,那么从JNDI获取一个计时器失败就可以了......但我甚至找不到办法做到这一点.

EJB计时器不合适

有EJB计时器,但它们不适合像JDBC驱动程序实现这样的低级别的东西,因为它们是:

  • 持续跨容器或机器重启
  • 高开销
  • 可以使用用于计时器持久性的数据库来实现
  • 面向商业时间而非机器时间
  • 在普通的servlet容器中不可用
  • 在"web profile"EE应用服务器中不可用

因此,EJB计时器可以完全脱离列表.

我不能滚动自己的计时器线程

阻止使用java.util.Timer和朋友的相同问题阻止我启动我自己的计时器线程和管理我自己的计时器.这不是首发.

Java EE规范说:

企业bean不得尝试管理线程.企业bean不得尝试启动,停止,暂停或恢复线程,或更改线程的优先级或名称.企业bean不得尝试管理线程组.

EE教程说:

不正确地使用线程的资源适配器可能危及整个应用程序服务器环境.例如,资源适配器可能会创建太多线程,或者可能无法正确释放它创建的线程.糟糕的线程处理会阻止应用程序服务器关闭并影响应用程序服务器的性能,因为创建和销毁线程是一项昂贵的操作.

WorkManager不做定时器

有一个javax.resource.spi.work.WorkManager,但是(a)它的目的是在服务提供者端而不是应用程序端使用,(b)它不是真正为计时器设计的.一个计时器可能会被使用工作项进行黑客入侵,该工作项会在超时时间内休眠,但这样做很难看,并且可能效率很低.

看起来它也不适用于Java SE.

Java连接器体系结构(JCA)

正如Java EE教程中提到的,Connector Architecture可能是EE容器的可行选项.但是,像Tomcat或Jetty这样的servlet容器可能不支持它.

我也担心沿着这条路走下去的性能影响.

所以我被卡住了

我该怎么做这个简单的任务?

我是否需要编写一个新的ThreadPoolExecutor,通过JNDI从容器中获取线程,然后使用它作为新的基础ScheduledThreadPoolExecutor?如果是这样,是否有一种可移植的方式从容器中获取线程,或者我是否需要每容器JNDI查找和适配器代码?

我错过了一些愚蠢而且非常明显的东西吗?

其他需要异步工作,定时器或回调的库如何处理Java EE和Java SE之间的可移植性?

Bal*_*usC 3

Timer在 Java EE 环境中确实是一个非常糟糕的主意。例如,如果它抛出异常,它就会被完全终止,您基本上需要重新启动整个服务器才能使其再次运行。但ScheduledExecutorService如果使用得当并且极其小心,就应该这样做。

您需要在应用程序级别创建它,而不是在某些 EJB 或任何其他容器托管类中创建它(正如 Java EE 规范试图告诉您的那样)。更重要的是,大多数 Java EE 参考实现还在幕后使用应用程序范围的执行器来加速加载。例如 Glassfish 和 Mojarra。

至于在 Java EE 和 Java SE 中工作的应用程序范围的启动/关闭挂钩,JDBC4 兼容的驱动程序会通过文件ServiceLoader机制自动加载/META-INF/services/java.sql.Driver,也在 WAR 中。它仅在 JVM 关闭时被卸载。对于 Java EE,您可以使用以编程方式/META-INF/services/javax.servlet.ServletContainerInitializer添加一个ServletContextListener显式取消注册驱动程序并关闭其线程池的方法contextDestroyed()

有关的: