7 Stimmen

Einen Timer in einer JavaEE-freundlichen Weise erhalten, die auch unter JavaSE funktioniert (zur Verwendung in einem JDBC-Treiber)

Ich bin daran interessiert, etwas Arbeit am PostgreSQL JDBC-Treiber zu leisten, um bei einer Implementierung von Statement.setQueryTimeout(...) eine der problematischeren Lücken in der Spezifikationskonformität des Treibers. Dazu benötige ich einen portablen Weg, um einen Timer zu erhalten oder einen Alarm/Rückruf zu setzen, der in allen Java EE-App-Servern, Servlet-Containern und in Java SE-Umgebungen funktioniert.

Es scheint, dass das nicht so einfach ist, wie es sein sollte, und ich stecke so fest, dass ich mich für einen Tipp auf Ihre Gnade stürze. Wie zum Teufel mache ich einen einfachen Timer-Callback, der in Java SE, Java EE und Servlet-Containern funktioniert?

Wenn es nötig ist, kann ich es aushalten, getrennte -ee- und -se-Freigaben zu machen, aber das ist äußerst unerwünscht. Releases für jeden Container sind völlig unpraktisch, obwohl automatisch ausgewählte pro-Container-Adapter akzeptabel sein könnte, wenn auch stark unerwünscht.

Der PgJDBC-Treiber muss in verkrusteten alten App-Servern unter alten Versionen der JVM laufen, aber es ist mir völlig egal, ob Statement-Timeouts nur in der JDBC4-Version des Treibers für moderne Container und JVMs verfügbar sind. Es gibt bereits eine Infrastruktur für die bedingte Kompilierung, die die Freigabe von JDBC3/JDK 1.4 und JDBC4/JDK 1.5 Treibern ermöglicht, so dass Code, der nur unter 1.5 oder sogar 1.6 funktioniert, kein Problem darstellt.

(Bearbeiten): Eine zusätzliche Komplikation ist, dass der JDBC-Treiber von Benutzern eingesetzt werden kann:

  • Als Containermodul oder integrierte Komponente, die beim Start des Containers gestartet wird;
  • als eigenständiges Bereitstellungsobjekt, das zurückgenommen und zur Laufzeit erneut bereitgestellt werden kann; oder
  • Eingebettet in ihre Anwendung war ou ear

... und wir müssen all diese Szenarien unterstützen, vorzugsweise ohne dass eine benutzerdefinierte Anwendungskonfiguration erforderlich ist! Wenn wir nicht alle diese Szenarien unterstützen können, muss es zumindest funktionieren ohne Anweisungszeitüberschreitungen zu unterstützen und in den Fällen, in denen Anweisungszeitüberschreitungen nicht unterstützt werden können, einen ordnungsgemäßen Abbruch zu gewährleisten.

Ah, einmal schreiben, überall laufen....

Ich kann nicht einfach java.util.Timer ou java.util.concurrent :

Ich sehe allgemeine Aussagen, dass die Verwendung von java.util.Timer oder die Java SE Gleichzeitigkeitshilfsprogramme (JSR-166) in der java.util.concurrent Paket wird in Java EE entmutigt, aber selten ausführlich. Der Vorschlag JSR 236 sagt das:

java.util.Timer, java.lang.Thread und die Java SE Concurrency Utilities (JSR-166) im Paket java.util.concurrency (sic) sollten niemals in verwalteten Umgebungen verwendet werden, da sie Threads außerhalb des Geltungsbereichs des Containers erstellen.

Wenn man etwas mehr liest, stellt man fest, dass Aufrufe von nicht verwalteten Threads keine Containerdienste erhalten, so dass alle möglichen Dinge in der gesamten Anwendung auf spannende und unerwartete Weise kaputt gehen können. Angesichts der Tatsache, dass ein Timer-Aufruf zu einer Ausnahme führen kann, die von PgJDBC ausgelöst wird und sich in den Code der Benutzeranwendung ausbreitet, ist dies wichtig.

(Bearbeiten): Der JDBC-Treiber selbst benötigt keine Containerdienste daher ist es mir egal, ob sie innerhalb des/der Timer-Threads arbeiten, solange diese Threads keinen Anwendercode ausführen. Das Problem ist, zuverlässig sicherzustellen, dass sie es nicht tun.

Die Zeitgeber-Abstraktionsschicht JSR 236 ist nicht mehr verfügbar.

JSR 236 ist nicht mehr aktuell, und ich sehe keinen Ersatz, der die gleichen Anforderungen an tragbare Zeitgeber erfüllt.

Ich kann auch keinen Hinweis auf einen containerübergreifenden portablen Weg finden, um einen container-gepoolten Timer zu erhalten. Wenn ich einen Timer von JNDI auf Containern abrufen könnte und auf die direkte Instanziierung zurückgreifen könnte, wenn das Abrufen eines von JNDI fehlgeschlagen ist, wäre das OK... aber ich kann nicht einmal einen Weg finden, das zu tun.

EJB-Zeitgeber sind ungeeignet

Es gibt EJB-Timer, aber sie sind für Low-Level-Sachen wie eine JDBC-Treiber-Implementierung ungeeignet, weil sie:

  • beständig über den Neustart von Containern oder Maschinen hinweg
  • hoher Overhead
  • kann umgesetzt werden Verwendung einer Datenbank für Timer-Persistenz
  • Orientierung an der Geschäftszeit und nicht an der Maschinenzeit
  • in einfachen Servlet-Containern nicht verfügbar
  • nicht verfügbar im "Webprofil" EE-App-Server

EJB-Timer können also komplett von der Liste gestrichen werden.

Ich kann meinen eigenen Timer-Thread nicht drehen

Die gleichen Probleme, die den Einsatz von java.util.Timer und Freunde hindern mich daran, meinen eigenen Timer-Thread zu starten und meine eigenen Timer zu verwalten. Das ist ein No-Go.

In der Java EE-Spezifikation heißt es:

Die Enterprise Bean darf nicht versuchen, Threads zu verwalten. Die Enterprise Bean darf nicht versuchen, einen Thread zu starten, zu stoppen, anzuhalten oder fortzusetzen, oder die Priorität oder den Namen eines Threads zu ändern. Die Enterprise Bean darf nicht versuchen, Thread-Gruppen zu verwalten.

und die EE-Tutorial sagt :

Ressourcenadapter, die Threads unsachgemäß verwenden, können die gesamte Anwendungsserverumgebung gefährden. So kann ein Ressourcenadapter beispielsweise zu viele Threads erstellen oder erstellte Threads nicht ordnungsgemäß freigeben. Eine schlechte Thread-Behandlung verhindert das Herunterfahren des Anwendungsservers und beeinträchtigt die Leistung des Anwendungsservers, da das Erstellen und Auflösen von Threads kostspielige Operationen sind.

WorkManager beherrscht keine Zeitschaltuhren

Es gibt eine javax.resource.spi.work.WorkManager aber (a) ist es für den Einsatz auf der Seite des Dienstanbieters und nicht auf der Seite der Anwendung gedacht, und (b) ist es nicht wirklich für Zeitgeber konzipiert. Ein Timer kann wahrscheinlich in ein Work Item gehackt werden, das mit einer Zeitüberschreitung schläft, aber das ist bestenfalls hässlich und wird wahrscheinlich ziemlich ineffizient sein.

Es sieht auch nicht so aus, als würde es mit Java SE funktionieren.

Die Java Connector Architektur (JCA)

Wie bereits erwähnt im Java EE-Tutorial könnte die Connector-Architektur eine praktikable Option für EE-Container sein. Aber auch hier gilt, dass Servlet-Container wie Tomcat oder Jetty sie möglicherweise nicht unterstützen.

Ich bin auch besorgt über die Auswirkungen auf die Leistung, wenn ich diesen Weg gehe.

Ich stecke also fest

Wie kann ich diese einfache Aufgabe erledigen?

Muss ich eine neue ThreadPoolExecutor die Threads über JNDI vom Container abruft, und verwenden Sie diese dann als Basis für eine neue ScheduledThreadPoolExecutor ? Wenn ja, gibt es sogar einen portablen Weg, um Threads von einem Container zu erhalten, oder muss ich pro Container JNDI Lookup und Adapter-Code?

Übersehe ich etwas Dummes und Offensichtliches?

Wie handhaben andere Bibliotheken, die asynchrone Arbeit, Zeitgeber oder Rückrufe benötigen, die Portabilität zwischen Java EE und Java SE?

3voto

BalusC Punkte 1034465

Timer ist in der Tat eine äußerst schlechte Idee in einer Java EE-Umgebung. Wenn zum Beispiel eine Ausnahme ausgelöst wird, ist die Anwendung komplett zerstört und man muss den gesamten Server neu starten, um sie wieder zum Laufen zu bringen. Aber ScheduledExecutorService sollten es tun, wenn sie klug und mit äußerster Sorgfalt eingesetzt werden.

Sie müssen sie auf anwendungsweiter Ebene erstellen, nicht innerhalb einer EJB oder einer anderen vom Container verwalteten Klasse (wie es die Java EE-Spezifikation versucht, Ihnen zu sagen). Mehr noch, die meisten Java EE-Referenzimplementierungen verwenden auch anwendungsweite Executors, um das Laden zu beschleunigen. Denken Sie zum Beispiel an Glassfish und Mojarra.

Was den anwendungsweiten Startup/Shutdown-Hook betrifft, der sowohl in Java EE als auch in Java SE funktioniert, wird ein JDBC4-kompatibler Treiber automatisch von ServiceLoader Mechanismus von /META-INF/services/java.sql.Driver Datei, auch in WARs. Sie wird nur beim Herunterfahren der JVM entladen. Für Java EE können Sie die folgende Datei verwenden /META-INF/services/javax.servlet.ServletContainerInitializer zum Hinzufügen einer ServletContextListener programmgesteuert, wodurch der Treiber explizit abgemeldet und sein Thread-Pool geschlossen wird, wenn contextDestroyed() .

Verwandt:

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X