Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting a timer in a JavaEE friendly way that works on JavaSE too (for use in a JDBC driver)

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:

  • As a container module or built-in component that's launched at container startup;
  • As a standalone deployment object that can be undeployed and redeployed at runtime; or
  • Embedded inside their application 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....

I can't just use 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.

The JSR 236 timer abstraction layer is defunct

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.

EJB timers are unsuitable

There are EJB timers, but they're unsuitable for low-level stuff like a JDBC driver implementation because they're:

  • persistent across container or machine restart
  • high overhead
  • may be implemented using a database for timer persistence
  • oriented toward business time not machine time
  • unavailable in plain servlet containers
  • unavailable in "web profile" EE app servers

So EJB timers can be struck entirely off the list.

I can't roll my own timer thread

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.

WorkManager doesn't do timers

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.

The Java Connector Architecture (JCA)

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.

So I'm stuck

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?

like image 639
Craig Ringer Avatar asked Dec 15 '11 03:12

Craig Ringer


People also ask

What is an EJB timer?

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.

What is timer service in Java?

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.

How do you initialize a timer in Java?

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();

Does Java have a timer class?

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.


1 Answers

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().

Related:

  • 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
like image 73
BalusC Avatar answered Sep 24 '22 01:09

BalusC