Sporadische ClassCastException in Swing-Anwendung

Eines meiner Swing-Programme wirft gelegentlich beim Start eine (die Funktion nicht weiter störende) Ausnahme, die ich natürlich gern beseitigen würde. Alle dort auftauchenden Klassen gehören zu Java selbst und die Fehlermeldung taucht nur sporadisch auf, weswegen ich nicht systematisch Teile abklemmen und nach ihr suchen kann. Beim Debuggen tritt sie bislang leider gar nicht auf. Vielleicht ist es ein Timing-Problem.

Falls hier jemand eine gute Idee hat, wonach ich vielleicht schauen oder was ich probieren könnte, wäre ich froh.

Die Fehlermeldung lautet:

Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException
    at java.desktop/javax.swing.LayoutComparator.compare(LayoutComparator.java:76)
    at java.desktop/javax.swing.LayoutComparator.compare(LayoutComparator.java:42)
    at java.desktop/javax.swing.SortingFocusTraversalPolicy.mergeSort(SortingFocusTraversalPolicy.java:652)
    at java.desktop/javax.swing.SortingFocusTraversalPolicy.mergeSort(SortingFocusTraversalPolicy.java:666)
    at java.desktop/javax.swing.SortingFocusTraversalPolicy.mergeSort(SortingFocusTraversalPolicy.java:667)
    at java.desktop/javax.swing.SortingFocusTraversalPolicy.mergeSort(SortingFocusTraversalPolicy.java:666)
    at java.desktop/javax.swing.SortingFocusTraversalPolicy.mergeSort(SortingFocusTraversalPolicy.java:666)
    at java.desktop/javax.swing.SortingFocusTraversalPolicy.mergeSort(SortingFocusTraversalPolicy.java:666)
    at java.desktop/javax.swing.SortingFocusTraversalPolicy.legacySort(SortingFocusTraversalPolicy.java:163)
    at java.desktop/javax.swing.SortingFocusTraversalPolicy.enumerateAndSortCycle(SortingFocusTraversalPolicy.java:152)
    at java.desktop/javax.swing.SortingFocusTraversalPolicy.getFocusTraversalCycle(SortingFocusTraversalPolicy.java:125)
    at java.desktop/javax.swing.SortingFocusTraversalPolicy.getFirstComponent(SortingFocusTraversalPolicy.java:478)
    at java.desktop/javax.swing.LayoutFocusTraversalPolicy.getFirstComponent(LayoutFocusTraversalPolicy.java:167)
    at java.desktop/javax.swing.SortingFocusTraversalPolicy.getDefaultComponent(SortingFocusTraversalPolicy.java:571)
    at java.desktop/java.awt.FocusTraversalPolicy.getInitialComponent(FocusTraversalPolicy.java:169)
    at java.desktop/java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:481)
    at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:4871)
    at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2321)
    at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2769)
    at java.desktop/java.awt.Component.dispatchEvent(Component.java:4822)
    at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:772)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:95)
    at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:745)
    at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:743)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
    at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742)
    at java.desktop/java.awt.SequencedEvent.dispatch(SequencedEvent.java:204)
    at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:770)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:95)
    at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:745)
    at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:743)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
    at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742)
    at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
    at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
    at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

Allenfalls ein Problem mit mehreren Threads?

Dein Programm startet im „main“-Thread, Swing läuft hingegen im „UI“-Thread. Wenn das Timing schlecht ist, änderst du irgendwelche Daten gerade während Swing sie bearbeitet.

Probier mal alle Interaktion mit Swing in den UI-Thread zu verlagern: siehe die Methoden Swingutilities.invokeLater/invokeAndWait.

Beim Start verändere ich nichts in anderen Threads - und eigentlich ist alles in invokeLater() gekapselt, was aus eigenen Threads heraus passiert.

„Eigentlich“ :slight_smile:

Wenn man den Java Source reinschaut, sieht man das:

            if (a == null) {
                // 'a' is not part of a Window hierarchy. Can't cope.
                throw new ClassCastException();
            }

Das deutet auf ein Thread-Problem hin, und zwar das du ein Objekt aus einem Layout entfernst und das nicht im EDT. Damit hängt es nicht mehr unterhalb eines Window Objekts und deswegen geht der Teil, der anscheinend gerade das Window neu validiert oder ähnliches auf die Bretter.

Zumindest solltest du dir mal die Stellen ansehen, wo du Objekte entfernst. Besseren Tipp hab ich gerade nicht.

1 Like

Ja, eigentlich ist eigentlich kein Wort :smiley:

Ganz oben angefangen (wie schon angedeutet wurde) : Wenn du sowas hast wie

public static void main(String args[]) 
{
    JFrame f = new JFrame();
    f.setVisible(true);
}

kann das schon „falsch“ sein. („Ganz oben“, weil du meintest, dass es schon beim Start passiert). Ansonsten … wäre Code natürlich hilfreich, aber wenn das was „großes“ (oder internes/proprietäres) ist, hilft wohl nur, genauer zu sagen, wo irgendwas mit Threads gemacht wird. (Die „single thread rule“ einzuhalten kann etwas diffizil sein, aber ohne weitere Infos könnte man da jetzt nur im Nebel stochern…)

Ich hab auch nur „eigentlich“ geschrieben, weil ich ja irgendwo definitiv einen Fehler gemacht haben muss. :slight_smile:

Ja das ganze „gehört“ meiner Firma und ist auch ziemlich groß. Ich werde Montag mal schauen, ob ich nicht doch mit Auskommentieren und mehrfachem Start herausbekommen kann, wo der Fehler ist. Und ob vielleicht wirklich nur ganz am Anfang ein invokeLater() fehlt und das erst bei einer gewissen Komplexität der Oberfläche zum Problem wird.

Entfernt wird beim Start des Programms nichts. Wobei es umschaltbare Gui-Bereiche gibt, da werde ich nochmal genau drauf schauen, ob dort vielleicht doch am Anfang irgendwas entfernt wird, auch wenn ich das gerade eigentlich nicht glaube.

Das ganze unter der Prämisse, dass ich Montag auch Zeit dafür habe.

(Wenn ihr jemanden für ein Code-Review braucht: Ich bin Freiberufler und „Swing-Experte“…)

Noch eine Frage aus Neugier: Verwendet ihr irgendein Fancy Look+Feel? (Das Default-L&F ist (leider?) recht robust gegen Single-Thread-Rule-Verstöße, aber einige andere L&Fs machen manchmal trickreiche Sachen, wo etwas, was ~„meistens“ funktioniert dann plötzlich und unerklärlich abkachelt…

1 Like

Ja, damit die Tools nicht ganz so schrecklich aussehen, verwende ich das Nimbus Look & Feel.
Ich hab nun wirklich ganz ganz außen, bevor meine Logik und Oberfläche überhaupt erst ansetzen und die Parameter übergeben bekommen, ein SwingUtilities.invokeLater(() -> blablaInEdt()); eingebaut. Es ist wohl noch nicht als verlässlich anzusehen, aber zumindest ist die Fehlermeldung bislang nicht wieder aufgetaucht.

Ich würde wetten … :wink: Falls vorher irgendeine GUI-Component im main-Thread erstellt oder verändert (oder sogar sichtbar gemacht) wurde, war das eben falsch. Und falls dass die einzige Leiche war, die da im Keller liegt*, wäre das Problem behoben.

* Das ist unwahrscheinlich, weil es viel subtilere Thread-Regel-Verstöße gibt, als den, aber ~„wenn’s funktioniert, ist’s ja gut…“ :wink:

Das möchte ich natürlich nicht ausschließen, dass noch weitere Fehler drin sind. Man kann bei einem Programm ab einer gewissen Größe eigentlich fast sicher davon ausgehen, dass Bugs enthalten sind.

Aber zur Threadbehandlung starte ich alles was nicht innerhalb von wenigen Millisekunden eine Antwort generiert in einem eigenen Thread und zeige die Ergebnisse dann nach einem invokeLater() wieder in der Gui an. Sei es beim Laden, beim Speichern, bei anderen Operationen. Einfach damit die Gui weiterhin reagiert und nicht solange einfriert.

Ja, das ist der Standard, und „richtig“ in diesem Sinne (wenn da dann noch z.B. ein Progress-Dialog angezeigt werden soll, kann’s wieder fummelig werden - deswegen hatte ich da mal GitHub - javagl/SwingTasks: Utility classes for task execution in Swing gebastelt). Aber die Singe-Thread-Rule kann auch sehr subtil verletzt werden. Wenn man sowas hat wie ein TableModel, und irgendwo tableModel.setValueAt(r,c,"Hello") aufruft, dann ist das OK … auuuußer wenn dieses TableModel gerade in einer JTable angezeigt wird. (Nur als Beispiel).

Wie auch immer: Wenn das ursprüngliche Problem jetzt behoben ist, ist das ja OK.