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

标签 postgresql jakarta-ee jdbc timer threadpool

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

似乎这并不像它应该的那么简单,而且我已经被困住了,以至于我将自己投向了您的怜悯以寻求提示。 我到底怎么做一个在 Java SE、Java EE 和 servlet 容器中工作的简单计时器回调?

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

PgJDBC 驱动程序必须在旧版 JVM 下的硬 shell 旧应用服务器中运行,但我丝毫不关心语句超时是否仅在用于现代容器和 JVM 的驱动程序的 JDBC4 版本中可用。已经有条件编译基础设施允许发布 JDBC3/JDK 1.4 和 JDBC4/JDK 1.5 驱动程序,因此仅在 1.5 甚至 1.6 下运行的代码不是问题。

(编辑):增加的复杂性是 JDBC 驱动程序可能由用户部署:

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

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

    啊,写一次,到处跑……

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

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

    java.util.Timer, java.lang.Thread and the Java SE concurrency utilities (JSR-166) in the java.util.concurrency (sic) package should never be used within managed environments, as it creates threads outside the purview of the container.


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

    (编辑):JDBC 驱动程序本身不需要任何容器服务,所以我不在乎它们是否在其计时器线程中工作,只要这些线程从不运行任何用户代码。问题是可靠地确保他们没有。

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

    JSR 236 已不复存在,我看不到任何替代品可以满足可移植计时器的相同要求。

    我也找不到任何对跨容器可移植方法的引用来获取容器池计时器。如果我可以从容器上的 JNDI 获取一个计时器,然后退回到直接实例化,而从 JNDI 获取计时器失败,那就可以了……但我什至找不到办法做到这一点。

    EJB 定时器不合适

    有 EJB 计时器,但它们不适合像 JDBC 驱动程序实现这样的低级东西,因为它们是:
  • 跨容器或机器重启的持久性
  • 高开销
  • 可以使用数据库实现定时器持久化
  • 面向业务时间而非机器时间
  • 在普通 servlet 容器中不可用
  • 在“网络配置文件”EE 应用服务器中不可用

  • 因此 EJB 计时器可以完全从列表中删除。

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

    阻止使用 java.util.Timer 的相同问题和 friend 阻止我启动自己的计时器线程和管理自己的计时器。那是无从下手。

    Java EE 规范说:

    The enterprise bean must not attempt to manage threads. The enterprise bean must not attempt to start, stop, suspend, or resume a thread, or to change a thread’s priority or name. The enterprise bean must not attempt to manage thread groups.


    EE tutorial says :

    Resource adapters that improperly use threads can jeopardize the entire application server environment. For example, a resource adapter might create too many threads or might not properly release threads it has created. Poor thread handling inhibits application server shutdown and impacts the application server’s performance because creating and destroying threads are expensive operations.


    WorkManager 不做计时器

    有一个 javax.resource.spi.work.WorkManager ,但是 (a) 它旨在用于服务提供者端而不是应用程序端,并且 (b) 它并不是真正为计时器设计的。计时器可能会在使用超时 sleep 的工作项时被黑客入侵,但这充其量是丑陋的,并且可能会非常低效。

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

    Java 连接器架构 (JCA)

    如提及in the Java EE tutorial ,连接器架构可能是 EE 容器的可行选择。然而,再次强调,像 Tomcat 或 Jetty 这样的 servlet 容器可能不支持它。

    我还担心沿着这条路线走下去对性能的影响。

    所以我被困住了

    我如何完成这个简单的任务?

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

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

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

    最佳答案

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

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

    至于适用于 Java EE 和 Java SE 的应用程序范围的启动/关闭 Hook ,JDBC4 兼容驱动程序由 ServiceLoader 自动加载。机制由 /META-INF/services/java.sql.Driver文件,也在 WARs 中。它仅在 JVM 关闭时卸载。对于 Java EE,您可以使用 /META-INF/services/javax.servlet.ServletContainerInitializer添加 ServletContextListener以编程方式显式注销驱动程序并在 contextDestroyed() 上关闭其线程池.

    有关的:

  • Spawning threads in a JSF managed bean for scheduled tasks using a timer
  • Is it safe to start a new thread in a JSF managed bean?
  • To prevent a memory leak, the JDBC Driver has been forcibly unregistered
  • 关于postgresql - 以适用于 JavaSE 的 JavaEE 友好方式获取计时器(用于 JDBC 驱动程序),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8514725/

    相关文章:

    PHP:将 MySQL 转换为 Postgresql

    sql - Postgres问题

    postgresql - 如何从 postgres sql 中的 inet 值中提取八位字节?

    java - Maven 站点命令失败

    java - 未检测到 Tomcat JDBC 连接器

    python - 在同一层次结构中的不同类之间使用 Django ORM 语法中的 UNION

    jakarta-ee - 在应用程序运行时编辑 WEB-INF 中的资源

    java - 为什么我在调用 ResultSet.refreshRow() 时看到 NotUpdatable?

    java - Connection.close() 不会关闭连接,它会使其永远 hibernate ?

    java - Spring Security openId 支持和用户解除认证