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
ouear
... 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?