# Komisches Problem mit ShutdownHook + SystemTray



## hdi (16. Nov 2010)

Hey,

folgendes KSKB:


```
import java.awt.AWTException;
import java.awt.SystemTray;
import java.awt.TrayIcon;

import javax.swing.ImageIcon;

public class Demo {

	static final TrayIcon trayIcon = new TrayIcon(new ImageIcon(new byte[] {}).getImage());

	public static void main(String[] args) {

		try {
			SystemTray.getSystemTray().add(trayIcon);
		} catch (AWTException e) {
			e.printStackTrace();
		}

		Runtime.getRuntime().addShutdownHook(new Thread() {
			public void run() {
				System.out.println("removing tray...");
				SystemTray.getSystemTray().remove(trayIcon);
				System.out.println("tray removed!");
			}
		});

		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		// SystemTray.getSystemTray().remove(trayIcon);
		// System.out.println("tray removed in main-method");

		System.exit(0);
	}
}
```

Es geht offensichtlich darum, dass ich nen Shutdown Hook haben möchte der das TrayIcon aus dem Tray löscht wenn die App beendet wird. Es fehlt jetzt hier ein echtes Icon, bei mir ist es ein richtiges Bild, aber ihr seht im Tray halt dann einen Platzhalter.

Was beim Start dieser Demo passiert ist nun leider, dass er in der run-Methode des Shutdown Hooks bei der remove() Zeile hängen bleibt, d.h. das Programm beendet sich nicht und bleibt hängen. Komisch aber: Wenn ich diese Zeile aus dem Shutdown Hook auskommentiere, und die unten auskommentierten Zeilen aktiviere, dann funktioniert alles.
Also kann es ja irgendwie nicht an der remove()-Methode liegen.

Wieso funzt das in der main, aber nich in der run des Shutdown Hooks?

Thx

edit: ich dachte mir vllt kümmert sich der SystemTray selber darum und das Icon war schon removed oder sonst was, aber das ist nicht der Fall, denn wenn man die App einfach killt dann bleibt das Icon im Tray angezeigt (bis man mit der Maus drüber fährt dann verschwindet es, aber so ist das denke ich nicht gedacht)


----------



## Aldimann (16. Nov 2010)

Schonmal hier geschaut?
Klick mich

Da sind manche formulierungen drin wo ich erst zwei bis drei mal lesen und checken würde ob das wirklich das tut was ich denke


----------



## hdi (16. Nov 2010)

Ja hab's mir durchgelesen. Aber soweit ich das verstehe sollte mein Code kein Problem sein. Hab den Code auch mal in nen synchronized Block über das trayIcon gelegt, weil sie sagen vonwegen nicht thread-safe usw. Aber das bringt's auch nich


----------



## FArt (16. Nov 2010)

hdi hat gesagt.:


> Ja hab's mir durchgelesen. Aber soweit ich das verstehe sollte mein Code kein Problem sein. Hab den Code auch mal in nen synchronized Block über das trayIcon gelegt, weil sie sagen vonwegen nicht thread-safe usw. Aber das bringt's auch nich



Das ist nicht sinnvoll. Der Abschnitt soll dir sagen, dass die Shutdown-Hooks parallel ablaufen, teilweise mit noch aktiven Threads der Applikation.

Warum willst du das überhaupt so machen? Du solltest die Ressourcen über eine finally-Methode in deiner Hauptverarbeitung bereinigen. Die kommt immer dran und zwar in einem bekannten Context.

Wenn etwas hängt solltest du einen Thread-Dump ziehen, da sieht man wer wo hängt... evtl. ist das ein Deadlock, denn der Systemtray an sich ist synchronisiert...


----------



## ice-breaker (16. Nov 2010)

Ehrlich gesagt stand ich vor dem gleichen Problem auch schon, sowohl mit Windows als auch mit Macs.
Das TrayIcon verschwindet nicht, wenn die JVM gekillt wird, da kannst du dich drehen und wenden wie du willst, es geht einfach nicht, ich habe nach 2 Tagen aufgegeben. Bei Windows würde ich es auf eine Unzulänglichkeit des OS schieben, denn da sehe ich auch von Programmen in C noch das TrayIcon wenn es sich gekillt hat, bei Macs weiß ich es nicht.


----------



## hdi (16. Nov 2010)

@icebreaker ne also das verschwindet schon wenn man remove() macht. Führ mal mein Bsp aus mit dem remove() in der main-Methode, das klappt schon. Das Problem hier liegt am Hook.



> Warum willst du das überhaupt so machen? Du solltest die Ressourcen über eine finally-Methode in deiner Hauptverarbeitung bereinigen. Die kommt immer dran und zwar in einem bekannten Context.


Ehrlich gesagt weiß ich nicht was du mit dieser finally-Methode meinst. Der User kann das Programm jederzeit killen zB im TaskManager, dann sollte auch das TrayIcon verschwinden. Aber ich weiß nich wann und ob das passiert, ich dachte genau für so einen Fall gibt es ShutdownHooks. Die kommen doch auch immer dran.



> Wenn etwas hängt solltest du einen Thread-Dump ziehen


KAnnst du mir bitte erklären wie ich sowas mache, das sagt mir nichts


----------



## FArt (16. Nov 2010)

hdi hat gesagt.:


> Ehrlich gesagt weiß ich nicht was du mit dieser finally-Methode meinst. Der User kann das Programm jederzeit killen zB im TaskManager, dann sollte auch das TrayIcon verschwinden. Aber ich weiß nich wann und ob das passiert, ich dachte genau für so einen Fall gibt es ShutdownHooks. Die kommen doch auch immer dran.
> 
> 
> KAnnst du mir bitte erklären wie ich sowas mache, das sagt mir nichts



Wenn der Prozess beendet wird, dann laufen die Shutdownhooks. Wenn er gekillt wird, hilft nichts mehr.
Bei mir läuft dein kleines Beispiel (MAC). Threaddump: entweder im Debugger laufen lassen oder thread dump java - Google-Suche


----------



## ice-breaker (16. Nov 2010)

hdi hat gesagt.:


> @icebreaker ne also das verschwindet schon wenn man remove() macht. Führ mal mein Bsp aus mit dem remove() in der main-Methode, das klappt schon. Das Problem hier liegt am Hook.



natürlich funktioniert ein manuelles remove, aber sobald das remove aus dem Shutdown-Hook kommt funktioniert es einfach nicht, warum auch immer.


----------



## noobadix (16. Nov 2010)

Das TrayIcon verschwindet bei mir immer von selbst, wenn das Program beendet wird, auch wenn ich es via TaskManager kille. Allerdings braucht es manchmal seine Zeit und verschwindet erst, wenn ich die Leiste auf und zu mache. Würde es wie ice-breaker dem OS zuschieben.

Komisches Problem, komische Lösung:
Setzte ausschließlich den remove-Befehl in einen try block und die Konsolenausgaben daneben, dann klappt es oO


----------



## hdi (13. Sep 2011)

...ich muss diesen Thread wieder ausgraben. Stehe nämlich wieder vor dem selben Problem, (siehe Ursprungs-Post) und noch immer keine Lösung. Was hier als letzte Antwort vonwegen try-Block gesagt wurde funktioniert bei mir auch nicht. 

Ich hatte damals einfach drauf gepfiffen und den Tray an jeder Stelle im Code wo die App sich beenden könnte entfernt. Aber das kann's ja nicht sein, die ShutdownHooks sind ja denke ich gerade dazu da um solch redundanten Code zu verhindern und an einer globalen Stelle bzw. kontextunabhängig reagieren zu können. 

Vllt weiß inzwischen einer mehr darüber?! Wenn wir da keine Lösung finden sollte man das als Bug posten. Aber ich hoffe wir (d.h. IHR ) kriegen das doch noch hin!

Danke


----------



## FArt (14. Sep 2011)

Hast du dir schon mal einen Threaddump dieser Situation angeschaut?

Zitat aus der Doku Runtime (Java 2 Platform SE 5.0) :


> Shutdown hooks run at a delicate time in the life cycle of a virtual machine and should therefore be coded defensively. They should, in particular, be written to be thread-safe and to avoid deadlocks insofar as possible. They should also not rely blindly upon services that may have registered their own shutdown hooks and therefore may themselves in the process of shutting down.



Ich habe mir jetzt die nativen Callstacks nicht angeschaut, schätze aber das ist ein Synchronisationsproblem.



> Ich hatte damals einfach drauf gepfiffen und den Tray an jeder Stelle im Code wo die App sich beenden könnte entfernt. Aber das kann's ja nicht sein, die ShutdownHooks sind ja denke ich gerade dazu da um solch redundanten Code zu verhindern und an einer globalen Stelle bzw. kontextunabhängig reagieren zu können.



Das ist Käse. Das ist eindeutig ein Designfehler der Applikation. Es sollte eine Stelle geben, die die Finalisierung der Applikation durchführt und die von allen möglichen Stellen dann angesprungen wird. Dann hast du den Code nicht redundant, benötigst aber keinen Shutdown-Hook.


----------



## hdi (14. Sep 2011)

Wie zieh ich mir einen Threaddump?



> Es sollte eine Stelle geben, die die Finalisierung der Applikation durchführt und die von allen möglichen Stellen dann angesprungen wird. Dann hast du den Code nicht redundant, benötigst aber keinen Shutdown-Hook.


Ja es geht natürlich ohne Hook. Aber da frage ich mich doch wofür es diese Möglichkeit gibt.. Es geht hier um das Entfernen eines Tray-Icons. Das muss sich mit nix in meiner App irgendwie synchronisieren und hängt auch von nix anderem ab, deswegen dachte ich der Einfachheit halber pack ich's in einen Hook und gut ist.


----------



## TheRealSpikee (15. Sep 2011)

ohne das ich mir jetzt alles durchgelesen habe frage an TO

WARUM zur hölle willst du ein TrayIcon explizit bei beenden deiner app entfernen ? so bald deine app komplett terminiert ist und die VM beendet ist wird es automatisch entfernt ...

daher versteh ich den grund überhaupt nicht warum du selbst das TrayIcon removen willst ... schon garnicht beim ENDE der app ...


----------



## hdi (15. Sep 2011)

> so bald deine app komplett terminiert ist und die VM beendet ist wird es automatisch entfernt ...


Leider nein, das ist es ja. Zumindest nicht unter Win7 x64, und unter Vista x86 war es das selbe: Das Icon bleibt im Tray solange bis man mit der Maus drüberfährt, dann erst verschwindet es. Ich weiß nicht ob das am OS liegt oder an Java, aber wenn man es zur Laufzeit per remove entfernt dann wird der Tray auch aktualisiert und das Icon ist weg. Es ist halt etwas unschön und verwirrend, wenn das Icon nach wie vor sichtbar ist obwohl die App nicht läuft. Deswegen will ich's explizit per remove() entfernen.


----------



## TheRealSpikee (15. Sep 2011)

sorry ... aber dann wird entweder deine App abnormal terminiert so das der finalizer der das TrayIcon entfernt nicht mehr ausgeführt wird ... oder deine Systeme haben ganz schön einen kleben ...
Ich selbst besitze Systeme mit unterschiedlichen Windows varianten ... von 2000 bis Seven Ulti x64 ... unter ALLEN systemen funktioniert es problemlos ...
das von dir beschriebene probleme kann ich nur erzwingen wenn ich meine app über den taskmanager gewaltsam abnormal terminiere ... bei regulärem System.exit(int) jedoch wird es anstandslos entfernt

mal n anderer test : was passiert denn wenn du das entfernen des TrayIcons KOMPLETT weglässt ... also weder im main-thread noch in nem shutdown-hook ? bleibt dein TrayIcon immer noch kleben ? wenn ja dann hat entweder dein Java oder deine systeme einen gewaltigen schaden ...
es kann aber auch sein das du etwas in deinem code machst was halt eine abnormale terminierung zur folge hat ... was die warscheinlichste erklärung wäre ... das es allerdings auf zwei verschiedenen systemen unabhängig von ein ander auftritt lässt eher auf deinen code als auf java selbst schließen


----------



## Spacerat (15. Sep 2011)

1. Hab' das ungute Gefühl, als hätte TheRealSpikee recht. Das TreeIcon verschwindet von selbst.

2. Ersteres könnte demzufolge etwas mit der Threadsicherheit zu tun haben, welche anscheinend nicht gegeben ist und es deswegen halt an dieser Stelle zum Deadlock kommt.

3. Ungetestete Lösung: In der Main könnte man ja mal einen weiteren Thread starten, welcher seinerseits Das TrayIcon erstellt, es dem SysTray hinzufügt und dann bis zur Ausführung des SD-Hooks wartet. Im SD-Hook weckt man diesen Thread wieder auf, welcher dann nur noch das bis dato immer noch synchronisierte TrayIcon wieder entfernt und sich dann seinerseits selbst beendet. Faktisch dürfte also kein anderer Thread während der gesammten Laufzeit auf das Icon zugreifen können, ohne in einem DL zu enden. Das sollte dann auch rein theoretisch für jene Threads gelten, die augenblicklich für jenen DL in deiner Version verantwortlich sind.


----------



## hdi (15. Sep 2011)

Ok Leute, irgendwo im Verlauf des Threads wusste ich wohl selber nicht mehr was ich genau will  Ich hab mir das auch grad selbst wieder klarmachen müssen: Es geht nur um den Fall dass die App gekillt wird, nicht ordnungsgemäß terminiert! Bei letzterem verschwindet das TrayIcon sofort, da habt ihr Recht. (Ohne irgendeinen Hook und auch ohne remove())

Ein ShutdownHook hilft ja in so einem Fall auch nichts mehr, wie schon früher in diesem Thread klargestellt wurde. Ich war dann aber darüber verwundert dass es dieses Problem mit dem DL gibt, und hab das eigentliche Problem aus den Augen verloren.

Ursprünglich dachte ich halt die ShutdownHooks werden bei der (auch erzwungenen) Terminierung der VM ausgeführt, und deswegen wollte ich das halt machen. Ich denke da kann man also nichts dran machen.. Das OS updated das System Tray halt nicht automatisch sobald ein Prozess abgeschossen wird.

Lassen wir's gut sein. Wer meine Applikation über den Task Manager killt ist dann hatl selber Schuld, dann soll er halt mit dem Tray-Überbleibsel leben  Sorry für die Verwirrung, und danke für die Hilfe.


----------



## TheRealSpikee (15. Sep 2011)

Wie gesagt : habe mir ja nicht alles durchgelesen und daher auch nicht die Zeile wo du erwähnt hast das es darum geht das man die VM gewaltsam terminiert. Hätte ich das gelesen hätte ich nicht rätzelraten machen müssen sondern hätte dir in Bezug darauf eine klare Antwort gegeben : DAS GEHT NICHT !

Es ist aber nicht das Problem von Java oder gar irgendwelchen DeadLocks ... sondern generell ein Problem von Windows selbst. Seit mit Win95A die TrayBar eingeführt wurde gibt es den Bug das Anwendungen welche dort Icons platzieren wollen dies dem System mitteilen müssen. Klingt ja auch irgendwie logisch. Der eigentlich Bug ist jetzt aber das auch das Entfernen gemeldet werden muss ... und zwar von Anfang an seit es die TrayBar gibt.
Ich weis nicht wie es unter anderen Systemen ist ... ich kann euch nur sagen wie es unter Windows ist ...
Die TrayBar hat 3 Listener
1) Add-Listener : nimmt Meldungen über neue TrayIcons an , verarbeitet diese und fügt letztendlich das neue TrayIcon hinzu
2) Change-Listener : nimmt Meldungen für Veränderungen UND LÖSCHUNG von TrayIcons an , verarbeitet diese und stellt endweder den neuen Zustand dar oder löscht das TrayIcon
3) Action-Listener : Listener für alles andere was mit diesem TrayIcon geschieht ... wird dierekt an die Anwendung delegiert ... darunter fallen auch sämtlich Mouse-Events wie Klicken für Menü oder Hover mit ToolTip ...

Was also fehlt wäre ein 4ter Listener welcher eine Art keep-alive-poll macht ...
Theoretisch müsste er kontinuierlich die Anwendungen der TrayIcons fragen : bist du noch da und reagierst du auch noch auf das TrayIcon ? Und die Anwendung müsste dann sowas antworten wie : ja , bin noch da , TrayIcon wird noch verwendet , auf Events wird reagiert ...
Genau dieser 4te Listener fehlt aber ... was dazu führt das das beschriebene Problem nicht nur bei Java ... sondern bei ALLEN TrayIcons auftritt wenn man die zugehörige Anwendung gewaltsam über den TaskManager killt.
Warum aber verschwindet dann ein TrayIcon wenn man mit der Maus drüber fährt ? Nun ... das liegt daran das dieses MouseEvent an die Anwendung delegiert wird ... und dabei bemerkt Windows dann auch endlich das diese Pipe nicht mehr existiert ... folglich wird angenommen das auf Events des TrayIcons nicht mehr reagiert werden kann und es damit also eigentlich nicht mehr besteht. Ob die Anwendung selbst noch läuft oder nicht ist dabei egal ... wichtig ist die Pipe über die die Events übertragen werden. Da durch das nun dieser Fehler aufgetreten ist wird das TrayIcon entfernt weil scheinbar keine Kontrolle mehr vorhanden ist ...

Es gab seltene Fälle , sowohl Java als auch NICHT-Java , in denen bei einer gefreezten Anwendung *wodurch wohl ? I/O-Ops im EDT* das OS anahm das die Pipe zusammengebrochen wäre und wärend die Anwendung noch lief , aber blockiert war , das TrayIcon entfernt wurde ...
Das natürlich nach dem "wieder auftauen" der Anwendung sich diese mit einem FATAL ERROR bedankte weil nun ihrerseits diese Pipe zu war ist leider die Kehrseite zu diesem Bug ...


Ich hoffe das war halbwegs verständlich ...


----------



## hdi (15. Sep 2011)

Vielen Dank für diese Background-Infos! 

Den letzten Absatz hättest du aber ruhig für dich behalten können. Was fällt dir eigentlich ein mir sowas zu erzählen? Gerade wenn du weißt, dass ich grad eine Tray-gestützte App schreibe... Jetzt kann ich nicht mehr gut schlafen ;( Vielen Dank.. Du Sau


----------



## TheRealSpikee (15. Sep 2011)

Tja .. was Windows angeht bin ich schon fast von anfang an dabei ...

Was dich angeht : n großen Java-Video-Kurs machen ... darin vielleicht auch TrayIcons verwenden *weis ich nicht , noch nicht angeguckt* ... dafür Geld wollen ... und dann sowas fundamentales nicht wissen ... du enttäuschst mich gerade. Ich glaube ich habe gerade meiner Liste "die 50€ sind zu viel" noch einen Punkt hinzugefügt ...

Und im Schlaflosigkeit verbreiten bin ich Profi ... ich selbst schlaf ja grad mal 4h innerhalb von 24h ...


----------



## hdi (15. Sep 2011)

Wir reden hier von irgendeinem (durch den Programmierer nicht vermeidbaren) Windows-Bug in Bezug auf das Tray (der wie du selbst sagst nichts mit Java zu tun hat), sowie einem nicht näher untersuchten Verhalten bei Verwendung der Methode SystemTray#remove(TrayIcon) innerhalb eines als ShutdownHook registrierten Threads. 

Ja stimmt, das ist mega fundamental, das gehört ja in jedem Buch auf die erste Seite. Und wie man sieht wusste jeder hier genau über diese Verhaltensweisen Bescheid, nur ich nicht...

Wirst du jetzt gleichermaßen arrogant wie frech, weil du mir etwas über den Tray erzählen konntest das ich nicht wusste? Gratulation! Das berechtigt dich allerdings nicht hier irgendwelche Unterstellungen zu machen. Übrigens - *wie immer* - lustig zu sehen dass du als Gast postet. Es ist amüsant wie die Ultra-Java-Experten hier allesamt keinen Account haben. Angst, man könnte dir mal eine falsche Antwort zuweisen? 

...Aber bitte verstehe diese Antwort nicht als Aufforderung zur Diskussion. Ich wollte dich nur kurz auf den Boden zurückbringen bevor du in der Sonne verbrennst. :bahnhof:


----------



## TheRealSpikee (15. Sep 2011)

Chill mal ...
Generell hast du ja diene Marktlücke gefunden ... und offenbar gibt es genug die bereit sind das auch zu zahlen ... naja ... wer denkt das es ein Wunder-Kurs wäre ... egal.

Was das REG angeht : ich reg mich hier nich weil mir das Niveau einfach zu tief ist ...
Ich mein : guck dir alleine mal im Anfänger-Forum an auf was für Phantasien da einige kommen ... da könnt ich immer nur immer wieder sagen : wenn man keine Ahnung hat einfach mal die Fresse halten ...

Ich mein ... die besten sind eh immer : kann jetzt seit 2 Tagen 3 Worte Java und will damit jetzt n hochkomplexes Spiel entwickeln .. am besten so in n paar Tagen ...
Nee .. da könnt ich kotzen ...

Auch find ichs lustig wie es hier ne Rubrik JavaScript gibt ...
Ich würde jeden JS-Thread einfach kommentarlos löschen ... denn wer nicht mal den Unterschied beider Sprachen am unterschiedlichen Namen erkennt ist mir auch keine Antwort wert.


----------



## hdi (15. Sep 2011)

> Chill mal ...
> Generell hast du ja diene Marktlücke gefunden ... und offenbar gibt es genug die bereit sind das auch zu zahlen ... naja ... wer denkt das es ein Wunder-Kurs wäre ... egal.



Woher hast du denn den Begriff "Wunder-Kurs"? Muss wohl einer meiner User gesagt haben, denn ich war es nicht. Ich bin allerdings schon davon überzeugt dass es - abgesehen von mehreren Seminaren direkt bei Sun, welche viele tausend Euro kosten - in der Tat der beste Weg ist Java zu lernen - und im P/L-Verhälntnis zudem noch der mit Abstand günstigste. Wenn du das nicht glaubst (wissen kannst du es ja nicht, da du wie du sagtest den Kurs nicht gesehen hast), dann ist das völlig O.K.



> Was das REG angeht : ich reg mich hier nich weil mir das Niveau einfach zu tief ist ...


Warum bist du dann _überhaupt _hier unterwegs? 



> Ich mein ... die besten sind eh immer : kann jetzt seit 2 Tagen 3 Worte Java und will damit jetzt n hochkomplexes Spiel entwickeln .. am besten so in n paar Tagen ...
> Nee .. da könnt ich kotzen ...


_Du _könntest kotzen weil ein _Anfänger _seine Fähigkeiten nicht einschätzen kann? Wieso denn das. Klingt so als hättest du selber sehr viel Mühe gehabt, dir Java ordentlich beizubringen, und dein erstes komplexes Programm erst nach 5 Jahren hinbekommen. Vielleicht hättest du meinen Kurs sehen sollen 



> Auch find ichs lustig wie es hier ne Rubrik JavaScript gibt ...


Eben weil viele Anfänger Java und JS verwechseln. Wenn es die Rubrik nicht gäbe, würden alle JS-Fragen im regulären Forum landen. 

Ich hab nen Vorschlag: Zieh ein eigenes Forum auf, wo das Niveau dann höher ist. Am besten du schiebst auch noch gleich einen Videokurs hinterher, der den Leuten Java _richtig _beibringt, z.B. mit einem Video über den Windows Tray. 

Tust so, als wärst du der Java-Gott schlechthin und bist scheinbar nur hier unterwegs um zu meckern und andere User auch noch auf persönlicher Ebene anzufahren (Ja, ich mach das jetzt auch, du hast angefangen). Ich meine, schau dir mal meine Antwort auf deine Erklärung zum Windows Tray an, und dann deine Antwort auf meine... Und dann frag ich mich doch, wer hier chillen muss. Eine deratige Antwort auf meinen Post strotzt doch nur so von Verbitterung oder irgendwelchen anderen Komplexen deinerseits.

...jetzt hast du mich doch dazu gebracht zu diskutieren. Troll 1:0. Mist ;(


----------



## Illuvatar (15. Sep 2011)

hdi hat gesagt.:


> ...jetzt hast du mich doch dazu gebracht zu diskutieren. Troll 1:0. Mist ;(



Ja, Troll oder nicht, das war vielleicht nicht die beste Idee.
TheRealSpikee hat sich mit seinen Äußerungen doch schon selbst genug disqualifiziert. Zwar nicht in fachlicher Hinsicht - dafür umso mehr in jeder anderen...

Wie ich das verstehe ist das eigentliche Thema erledigt? Dann kann ich ja getrost zumachen.


----------

