# Nullpointer im Debugger, jedoch nicht beim normalen Starten



## Gucky (1. Mai 2015)

Hallo liebe Community,

ich programmiere schon länger an einem Programm zum Serialisieren von Kopier- und Verschiebevorgängen.
Nun habe ich etwas verändert, dann wollte ich debuggen und bekomme an einer ganz anderen Stelle einen Nullpointer.
Dieser Fehler tritt jedoch nur im Debugger auf und wenn ich im Konstruktor von HauptframeSeropiep einen Brakpoint setze, funktioniert auch alles normal. Auch wenn ich vor dem Instanziieren des neuen HauptframeSeropiep Objektes eine Sekunde warte läuft alles. Wenn dieser Breakpoint jedoch die ganze VM anhält läuft es nicht. Es reicht sogar schon aus das Ganze zum Laufen zu bringen, wenn ich das Instanziieren vom neuen Objekt vor das Ändern des LookAndFeels tue.

Ich habe gegooglet und dabei kam heraus, dass es sich um einen Bug in eclipse handeln kann. Nun läuft mein eclipse aber schon seit drei Jahren stabil ohne einen einzigen Absturz (unterbrochen durch rechnerbedingtes Neuinstallieren), weshalb ich auch noch nicht auf modernere Versionen gewechselt habe. Ich benutze eclipse Galileo.

Die Exception lautet:

```
Exception in thread "main" java.lang.NullPointerException
	at javax.swing.MultiUIDefaults.getUIError(Unknown Source)
	at javax.swing.UIDefaults.getUI(Unknown Source)
	at javax.swing.UIManager.getUI(Unknown Source)
	at javax.swing.JPanel.updateUI(Unknown Source)
	at javax.swing.JPanel.<init>(Unknown Source)
	at javax.swing.JPanel.<init>(Unknown Source)
	at javax.swing.JPanel.<init>(Unknown Source)
	at javax.swing.JRootPane.createGlassPane(Unknown Source)
	at javax.swing.JRootPane.<init>(Unknown Source)
	at javax.swing.JFrame.createRootPane(Unknown Source)
	at javax.swing.JFrame.frameInit(Unknown Source)
	at javax.swing.JFrame.<init>(Unknown Source)
	at anzeige.HauptframeSeropiep.<init>(HauptframeSeropiep.java:26)
	at anwendung.Steuer.initHauptframe(Steuer.java:54)
	at anwendung.Steuer.<init>(Steuer.java:35)
	at anwendung.Steuer.main(Steuer.java:75)
```

Ich habe die beteiligten Klassen gekürzt, um nicht so einen riesigen Wust aus Code zu posten.
Steuer:

```
//zahlreiche Importe

public class Steuer implements ErrorListener{
	private static final String MAYORERRORMESSAGE = "A mayor problem occurred. Please try again later or contat the developers.";
	private EventMulticaster em;
	private HauptframeSeropiep hf;
	private Datenhandler dh;
	private JDialog errDial;
	private JTextArea errText;
	
	public Steuer(){
		this.em = new EventMulticaster();
		this.dh = new Datenhandler(em);
		initHauptframe();
		initEventMulticaster();
	}
	
	private void initEventMulticaster(){
		em.addPathChangedEventListener(hf.getDataList());
		em.addPathChangedEventListener(hf.getDataTree());
		em.addDragnDropEventListener(dh);
		em.addErrorListener(this);
	}

	private void initHauptframe(){
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				try {
					  UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
					} catch( Exception e ) { e.printStackTrace(); }
			}
		});
		hf = new HauptframeSeropiep(em);
		hf.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		hf.addWindowListener(new WindowAdapter(){
			@Override
			public void windowClosing(WindowEvent e){
				if (dh.isRunning()){
					if (JOptionPane.showConfirmDialog(hf, RessourceManager.getText("closemessage")) == JOptionPane.OK_OPTION){
						System.exit(0);
					}
				} else {
					System.exit(0);
				}
			}
		});
		hf.setVisible(true);
	}
	
	public static void main(String[] args){
		new Steuer();
	}

	public void errorOccured(ErrorEvent e) {
		//kümmert sich um passierte Fehler
	}
	
	private void zeigeErrDial(String str){
		//zeigt einen Dialog mit einer Fehlermeldung
	}
}
```

und HauptframeSeropiep:

```
//zahlreiche Importe

public class HauptframeSeropiep extends JFrame {

	private static final long serialVersionUID = 1L;
	private JPanel jContentPane = null;
	private PathTextField pathTextField;
	private DateiList list;
	private DateiBaum baum;
	private EventMulticaster em;

	public HauptframeSeropiep(EventMulticaster em) {
		super(); //hier wir der Nullpointer geworfen
		this.em = em;
		initialize();
	}

	public DateiList getDataList() {
		return list;
	}
	
	public DateiBaum getDataTree(){
		return baum;
	}

	private void initialize() {
		this.setSize(547, 472);
		this.setContentPane(getJContentPane());
		this.setTitle("Seropiep");
		this.setLocationRelativeTo(null);
		this.setIconImage(RessourceManager.getImage("icon"));
		em.addPathChangedEventListener(pathTextField);
	}

	private JPanel getJContentPane() {
		if (jContentPane == null) {
			jContentPane = new JPanel();
			
			pathTextField = new PathTextField();
			pathTextField.setColumns(10);
			
			JButton btnRück = new JButton("<");
			
			JButton btnVor = new JButton(">");
			
			JScrollPane scrollPane = new JScrollPane();
			
			JScrollPane scrollPane_1 = new JScrollPane();
			
			Processlabel processlabel = new Processlabel();
			em.addProcessListener(processlabel);

			//erstellen des Layouts
			
			list = new DateiList(em);
			scrollPane_1.setViewportView(list);
			
			baum = new DateiBaum(em);
			scrollPane.setViewportView(baum);
			jContentPane.setLayout(gl_jContentPane);
		}
		return jContentPane;
	}
}
```

PS: Ich habe eine Zeit lang auf einem Gemisch aus Deutsch und Englisch programmiert. Nach und nach wollte ich das Programm übersetzen, also bitte nicht darüber beschweren. Falls ihr noch andere Tipps für mich habt, immer her damit.


----------



## Major_Sauce (1. Mai 2015)

Schönen Abend, 

Den Fehler selbst verstehe ich nicht, bzw ich habe mich nicht wirklich damit befasst.
Verbesserungsvorschläge hätte ich jedoch, wenn auch nur sehr kleine.

1.

```
private static final String MAYORERRORMESSAGE = "A mayor problem occurred. Please try again later or contat the developers.";
```

wenn du das "MAYORERRORMESSAGE" mit Unterstrichen trennst, dann wird das ganze ein wenig leserlicher --> "MAYOR_ERROR_MESSAGE"

2. Ich sehe dass die "HauptframeSeropiep" Klasse das vom JFrame erbt. Dabei frage ich mich wieso du dies machst. 
Extends benutzt man normalerweise nur, wenn man das Parent wirklich erweitert, ich denke ein normales JFrame hätte gereicht (Es sei denn im nicht gezeigten Code passiert da noch was). Dies könnte dann außerdem das Problem lösen, da sich das super(), bei dem die NullPointer geworfen wird, wohl auf das JFrame bezieht wenn ich das richtig gesehen habe.

mfg Major


----------



## Gucky (1. Mai 2015)

Moin,

das mit MAYOR_ERROR_MESSAGE habe ich geändert. Das Andere hat leider nichts gebracht. Ich habe es geändert, der Fehler kommt aber immer noch aber ich werde es ändern. Würdest du eine Factory vorschlagen oder einfach eine Handlerklasse für das Hauptframe?

LG
Gucky


----------



## Thallius (2. Mai 2015)

Lass den super() Aufruf einfach weg. Der ist unnötig.

Gruß

Claus


----------



## Gucky (2. Mai 2015)

super() wegzulassen bringt leider auch nichts. Der Nullpointer kommt trotzdem.

LG
Gucky

EDIT: Ich habe jetzt einmal das Verändern des LookAndFeels weggelassen. Dann funktioniert es plötzlich wieder.

EDIT2: Auch dieses abenteuerliche Konstrukt funktioniert:

```
try{
	hf = new HauptframeSeropiep(em);
} catch (NullPointerException e){
	synchronized(this){
		try {
			this.wait(1000);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
	}
	e.printStackTrace();
	System.out.println (e.getMessage());
	hf = new HauptframeSeropiep(em);
}
```


----------



## Major_Sauce (2. Mai 2015)

Eine Factory ist hier glaube ich ein Overkill, eine Handlerklasse sollte genügen.

mfg Major


----------



## Gucky (3. Mai 2015)

Das mit der Handlerklasse werde ich einbauen. Das ist ja nicht allzu kompliziert.
Leider löst sich mein Eingangsproblem dadurch nicht. Der Nullpointer fliegt immer noch.


----------



## Major_Sauce (3. Mai 2015)

Wie särs wenn du einfach überprüfst ob das ein Eclipse-Bug ist oder nicht ?
Exportier das ding mal und dann lass es ausführen. 

mfg Major


----------



## Dompteur (3. Mai 2015)

Dein Problem sieht nach einer Race-Condition aus. 
Du hast da anscheinend zumindest 2 Threads, die auf die gleichen Daten zugreifen. Wenn du den Debugger verwendest, dann scheint es, dass der eine Thread die Daten richtig initialisieren kann. D.h.: Der Thread konnte seine Arbeit erledigen und ist fertig. Der andere Thread greift also auf konsistente Daten zu.
Das selbe hast du ja auch selbst beobachten, weil du schreibst ja: "Auch wenn ich vor dem Instanziieren des neuen HauptframeSeropiep Objektes eine Sekunde warte läuft alles."

Ohne Debugger laufen beiden Threads in den kritischen Bereich, in dem die Daten noch nicht vollständig initialisiert sind. Daher stürzt einer ab.

Das ist zwar noch keine Lösung, aber vielleicht hilft es dir, die Richtung in der du weitersuchen kannst, zu sehen.


----------



## Gucky (3. Mai 2015)

Mit dem Debugger funktioniert es grade nicht aber danke für den Ansatz. Ich werde mir das mal angucken und dann melde ich mich wieder.


----------



## Gucky (3. Mai 2015)

@Dompteur
Moin,
leider funktioniert auch eine Synchronisation nicht. Wenn ich aber alles vom Swingthread machen lasse, funktioniert alles, was für die von dir erwähnte Race-Condition spricht. Es erscheint mir aber unschön, alles vom Swingthread machen zu lassen. Gibt es da noch andere Möglichkeiten?
Aber schon mal vielen Dank für diesen Ansatz.


```
SwingUtilities.invokeLater(new Runnable() {
	public void run() {
		try {
			synchronized(this){
				UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
			}
		} catch( Exception e ) { e.printStackTrace(); }
	}
});
synchronized(this){
	hf = new HauptframeSeropiep(em);
	hf.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
	hf.addWindowListener(new WindowAdapter(){
		@Override
		public void windowClosing(WindowEvent e){
			if (dh.isRunning()){
				if (JOptionPane.showConfirmDialog(hf, RessourceManager.getText("closemessage")) == JOptionPane.OK_OPTION){
					System.exit(0);
				}
			} else {
				System.exit(0);
			}
		}
	});
	hf.setVisible(true);
}
```

@Major
Es funktioniert sogar, wenn ich in eclipse ganz normal auf starten klicke. Es funktioniert nur dann nicht, wenn ich debuggen klicke.


----------



## Dompteur (3. Mai 2015)

Ich bin kein Swing-Experte. Aber ich glaube, dass ich irgendwann einmal aufgeschnappt habe, dass Swing nicht thread-safe ist. Man muss also alle Swing-Aufrufe im gleichen Thread machen.

Vielleicht findest du etwas, wenn du da gezielt danach suchst.


----------



## Gucky (3. Mai 2015)

Ich habe gesucht und bin fündig geworden. Anscheinend versucht mein Mainframe auf das LookAndFeel zuzugreifen, während der UIManager dieses ändert.
Das die Synchronisation oben nicht geholfen hat liegt daran, dass sich *this* im invokeLater auf das neue Runnable bezogen hat und deshalb auf zwei unterschiedliche Objekte synchronisiert wurde und deshalb auch zwei Mute verwendet worden sind. Das kann ja nicht klappen.
Jetzt lege ich den Mainthread schlafen, solange bis der SwingThread es geschafft hat das LookAndFeel zu ändern. Dann erst darf der Mainthread weiter machen und kann das Fenster anzeigen.

Vielen Dank euch allen. Jetzt läuft es wie geschmiert.

Der Thread kann geschlossen werden.


----------

