I'm interested in doing some work on the PostgreSQL JDBC driver to help with an implementation of Statement.setQueryTimeout(...)
, one of the more problematic spec conformance holes in the driver. To do this, I need a portable way to get a timer or set an alarm/callback that works in all Java EE app servers, servlet containers, and in Java SE environments.
It seems that's not as simple as it should be, and I'm stuck enough that I'm throwing myself upon your mercy for a hint. How the hell do I do a simple timer callback that works in Java SE, Java EE, and servlet containers?
If necessary I can possibly endure doing separate -ee and -se releases, but it's extremely undesirable. Releases for each container are completely impractical, though auto-selected per-container adapters might be acceptable if strongly undersirable.
The PgJDBC driver must run in crusty old app servers under ancient versions of the JVM, but I don't care in the slightest if statement timeouts are only available in the JDBC4 version of the driver for modern containers and JVMs. There's already a conditional compilation infrastructure in place to allow releases of JDBC3/JDK 1.4 and JDBC4/JDK 1.5 drivers, so code that only works under 1.5 or even 1.6 isn't a problem.
(edit): An added complication is that the JDBC driver may be deployed by users:
war
or ear
... and we need to support all those scenarios, preferably without requiring custom application configuration! If we can't support all those scenarios it needs to at least work without statement timeout support and to fail gracefully where statement timeouts can't be supported.
Ah, write once, run anwwhere....
java.util.Timer
or java.util.concurrent
:I see broad statements that the use of java.util.Timer
or the Java SE concurrency utilities (JSR-166) in the java.util.concurrent
package is discouraged in Java EE, but rarely any detail. The JSR 236 proposal says that:
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.
A bit more reading suggests that calls from unmanaged threads won't get container services, so all sorts of things throughout the app may break in exciting and unexpected ways. Given that a timer invocation may result in an exception being thrown by PgJDBC and propagating into user application code this is important.
(edit): The JDBC driver its self does not require any container services so I don't care if they work within its timer thread(s) so long as those threads never run any user code. The issue is reliably ensuring that they don't.
JSR 236 is defunct, and I don't see any replacement that satisfies the same requirements for portable timers.
I can't find any reference to a cross-container portable way to obtain a container-pooled timer either. If I could grab a timer from JNDI on containers and fall back to direct instantiation where getting one from JNDI failed that'd be OK... but I can't even find a way to do that.
There are EJB timers, but they're unsuitable for low-level stuff like a JDBC driver implementation because they're:
So EJB timers can be struck entirely off the list.
The same issues that prevent the use of java.util.Timer
and friends prevent me from launching my own timer thread and managing my own timers. That's a non-starter.
The Java EE spec says:
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.
and the 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.
There's a javax.resource.spi.work.WorkManager, but (a) it's intended for use on the service provider side not the application side, and (b) it's not really designed for timers. A timer can probably be hacked in using a Work item that sleeps with a timeout, but that's ugly at best and probably going to be quite inefficient.
It doesn't look like it'll work on Java SE, either.
As referred to in the Java EE tutorial, the Connector Architecture might be a viable option for EE containers. However, again, servlet containers like Tomcat or Jetty may not support it.
I'm also concerned about the performance implications of going down this route.
How do I do this simple task?
Do I need to write a new ThreadPoolExecutor
that gets threads from the container via JNDI, then use that as the base for a new ScheduledThreadPoolExecutor
? If so, is there even a portable way to get threads from a container, or do I need per-container JNDI lookup and adapter code?
Am I missing something stupid and blindingly obvious?
How do other libraries that need asynchronous work, timers or callbacks handle portability between Java EE and Java SE?
The EJB timer service provides a reliable and transactional notification service for timed events. Timer notifications may be scheduled to occur at a specific time, after a specific elapsed duration, or at specific recurring intervals. You can define callback methods on your EJB to receive these time-based events.
The timer service of the enterprise bean container enables you to schedule timed notifications for all types of enterprise beans except for stateful session beans. You can schedule a timed notification to occur according to a calendar schedule, at a specific time, after a duration of time, or at timed intervals.
You need to construct your timer with delay and your custom action listener. Then you use it like that: int delay = 1; //you set your delay here Timer timer = new Timer(delay, new TimerAction()); timer. start();
A Java. util. Timer is a utility class used to schedule a task to be executed after a specific amount of time. Tasks can be scheduled for one-time execution or for repeated execution periodically.
Timer
is indeed an extremely bad idea in a Java EE environment. For example, if it throws an exception, it's completely killed and you'd basically need to restart the whole server to get it to run again. But ScheduledExecutorService
should do it, if used wisely and with extreme care.
You'd need to create it at application wide level, not inside some EJB or any other container managed class (as the Java EE spec is trying to tell you). Even more, most Java EE reference implementations also use application wide executors under the covers to speed up loading. Think of for example Glassfish and Mojarra.
As to the application wide startup/shutdown hook which works in both Java EE and Java SE, a JDBC4 compatible driver get loaded automatically by ServiceLoader
mechanism by /META-INF/services/java.sql.Driver
file, also in WARs. It get unloaded only on JVM shutdown. For Java EE you could make use of /META-INF/services/javax.servlet.ServletContainerInitializer
to add a ServletContextListener
programmatically which explicitly unregisters the driver and shutdowns its thread pool on contextDestroyed()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With