Die Verwendung von Spring JMS in unserer Anwendung, die auf WebSphere laufen muss, erwies sich als eine gewisse Herausforderung. Und da ich beim Googeln zwar viele Informationen, aber nur einen kleinen, leicht zu übersehenden Text gefunden habe, habe ich beschlossen, diesen Blog zu schreiben.
Teil I
Die Notwendigkeit, Spring JMS im Gegensatz zu 'normalen' MessageDrivenBeans zu verwenden, ergab sich aus der folgenden Anforderung:
Wenn wir Nachrichten aus der Warteschlange erhalten, müssen wir diese Nachrichten über eine Socket-Verbindung an ein anderes System senden.
Das bedeutet, dass wir nicht mit der Verarbeitung der Nachrichten aus der Warteschlange beginnen können, solange die Socket-Verbindung nicht verfügbar ist. Diese Socket-Verbindung wird beim Start der Anwendung in einer Spring Bean erstellt, so dass nicht garantiert werden kann, dass die Verbindung verfügbar ist. Außerdem wird die Anwendung innerhalb eines WebSphere-Clusters eingesetzt, so dass einer der beiden Server im Cluster nie eine Verbindung zum Socket erhält.
Wir brauchen also eine Art programmatische Kontrolle über den Start des Nachrichtenhörers.
Glücklicherweise ist dies in Spring mit den Start- (und Stop-) Methoden des AbstractJmsListeningContainer oder des GenericMessageEndpointManager möglich. Da wir bereits eine Implementierung mit MessageDrivenbeans und ActivationSpecifications zur Verteilung der Nachrichten hatten, entschieden wir uns für den JmsMessageEndpointManager, da wir unsere in WebSphere konfigurierten ActivationSpecifications wiederverwenden konnten (wir dachten, wir könnten das tun.....)
Die Wiederverwendung der konfigurierten ActivationSpecification war nicht möglich (siehe dieses Thema im springsource-Forum ). Daher haben wir die ActivationSpecification in den spring config-Dateien zusammen mit dem Message Endpoint Handler, dem Resource Adapter und dem Workmanager konfiguriert, wie im Artikel using Spring JMS on WebSphere beschrieben.
Es wurden keine Meldungen aufgefangen und keine Fehlermeldungen gefunden, die uns in die richtige Richtung geführt hätten. Ohne weitere Informationen wurde es also zu einer Art Versuch-und-Irrtum-Übung (was ich hasse und wegen des langen /Versuch-zu-fixieren/Einführung/Test-Zyklus noch mehr hasse).
Nach einigen Versuchen beschloss ich, mir die Quellen, die ich zur Entwicklung der Konfiguration verwendet hatte, genauer anzusehen. Nachdem ich sie mehr als einmal durchgelesen hatte, blieb mein Blick an folgendem Text hängen, der in dem Artikel using Spring JMS on WebSphere versteckt war:
...Beachten Sie, dass keine anderen Spring JMS MessageListenerContainer-Typen unterstützt werden...
Hat das bedeutet.....? Ja, das war es. Bei der Entscheidung, den Message Endpoint Handler zu verwenden, hatte ich den falschen Typ verwendet. Nachdem ich also einige Minuten lang die Konfiguration umgeschrieben hatte, um den DefaultMessageListenerContainer zu verwenden (zusammen mit einem Messageselector, um die Aktivierungsspezifikation zu ersetzen), die autoStartup-Eigenschaft auf false gesetzt und sie in meine Connection Manager Bean injiziert hatte, um den Listener programmatisch zu starten, nachdem die Verbindung hergestellt wurde, gab es einen weiteren langen /Versuch, den Zyklus zu beheben/einzuführen/zu testen...........
Teil II
und dann hat es fast funktioniert. Eine ClassCastException in der onMessage-Methode, die besagt, dass die Klasse A nicht in die Klasse A gecastet werden konnte. Warum ist es nicht möglich, etwas in sich selbst zu casten. Ok, ich gebe zu, dass es etwas seltsam erscheint, dies zu tun, und ich habe es auch nicht absichtlich getan. Die onMessage-Methode sollte eine serialisierte Nachricht als Eingabeparameter erhalten, also dachte ich, ich müsste sie casten. Aber es stellte sich heraus, dass es bereits eine Klasse A ist. Also habe ich die Signatur der Methode so geändert, dass sie eine Klasse A als Eingabeparameter annimmt.
Auf diese Weise sind wir zwar die ClassCastException losgeworden, aber jetzt haben wir einen NoSuchMethodError (ich bin mir nicht ganz sicher, was ich bevorzuge). Dies wird wie ein ClassLoader-Problem aussehen und wenn es wie ein ClassLoader-Problem aussieht und sich wie ein ClassLoader-Problem verhält, ist es ein ClassLoader-Problem!
Aber wie kann das sein? Ich habe eine Ear-Datei mit einer War-Datei, die alle Spring Beans mit dem contextLoaderListener lädt und sonst nichts. Nun ja, fast nichts anderes. Auf demselben Server wird ein anderes Ear eingesetzt, das ebenfalls die Klasse A verwendet. Daher befindet sich die Klasse A im Kernmodul (das nicht ohne Grund Kernmodul heißt!). Könnte es daran liegen?
Ja, das kann es sein! Der Websphere-Server kann beschließen, einen ApplicationClassloader für mehrere Anwendungen zu verwenden. Daher müssen wir auf Serverebene die Classloading-Policy auf Single setzen, um die ClassCastException oder den NoSuchMethodError zu verhindern.
Ich hoffe, dies hilft allen, die diese Fallstricke vermeiden wollen.
Verfasst von
Kris Geusebroek
Unsere Ideen
Weitere Blogs
Contact


