# IllegalStateException - Problem mit GUI und Observer pattern



## mas (14. Dez 2007)

Hallo,

Dies ist ein Repost eines Beitrags aus der Kategorie AWT, Swing, usw. Passt wohl besser hier rein. Moderator: den anderen Post könnte man löschen. (www.java-forum.org/de/topic60816_illegalstateexception-problem-gui-observer-pattern.html)

Ich habe versucht, ein Beobachtungsmuster zu implementieren. Dabei habe ich eine Klasse, die die Daten beherbergt, wenn diese geändert werden soll das GUI benachrichtigt werden und die Daten da eingetragen (z.T. an versch. Orten).

Mit meiner Implementation bekomme ich eine Ausnahme, die ich zwar zu deuten weiss, aber noch keine Möglichkeit gefunden habe, sie zu umgehen. Hier die Ausnahme:


```
Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Attempt to mutate in notification
	at javax.swing.text.AbstractDocument.writeLock(Unknown Source)
	at javax.swing.text.AbstractDocument.replace(Unknown Source)
	at javax.swing.text.JTextComponent.setText(Unknown Source)
	at ch.arocom.arodimnt.create.gui.bottomPanels.ProjectBottomTab.setDeviceText(ProjectBottomTab.java:163)
	at ch.arocom.arodimnt.create.gui.bottomPanels.ProjectBottomWeatherStationTab.update(ProjectBottomWeatherStationTab.java:67)
	at java.util.Observable.notifyObservers(Unknown Source)
	at java.util.Observable.notifyObservers(Unknown Source)
	at ch.arocom.arodimnt.create.logic.saveableObjects.WeatherStation.setPort(WeatherStation.java:39)
	at ch.arocom.arodimnt.create.gui.bottomPanels.ProjectBottomWeatherStationTab.performDeviceUpdate(ProjectBottomWeatherStationTab.java:38)
	at ch.arocom.arodimnt.create.gui.bottomPanels.ProjectBottomTab$1.removeUpdate(ProjectBottomTab.java:83)
	at javax.swing.text.AbstractDocument.fireRemoveUpdate(Unknown Source)
	at javax.swing.text.AbstractDocument.handleRemove(Unknown Source)
	at javax.swing.text.AbstractDocument.remove(Unknown Source)
	at javax.swing.text.AbstractDocument.replace(Unknown Source)
	at javax.swing.text.JTextComponent.setText(Unknown Source)
	at ch.arocom.arodimnt.create.gui.bottomPanels.ProjectBottomTab.setDeviceText(ProjectBottomTab.java:163)
	at ch.arocom.arodimnt.create.gui.bottomPanels.ProjectBottomWeatherStationTab.update(ProjectBottomWeatherStationTab.java:67)
	at java.util.Observable.notifyObservers(Unknown Source)
	at java.util.Observable.notifyObservers(Unknown Source)
	at ch.arocom.arodimnt.create.logic.saveableObjects.WeatherStation.setPresent(WeatherStation.java:59)
	at ch.arocom.arodimnt.create.gui.bottomPanels.ProjectBottomWeatherStationTab.itemStateChanged(ProjectBottomWeatherStationTab.java:25)
	at javax.swing.AbstractButton.fireItemStateChanged(Unknown Source)
	at javax.swing.AbstractButton$Handler.itemStateChanged(Unknown Source)
	at javax.swing.DefaultButtonModel.fireItemStateChanged(Unknown Source)
	at javax.swing.JToggleButton$ToggleButtonModel.setSelected(Unknown Source)
	at javax.swing.JToggleButton$ToggleButtonModel.setPressed(Unknown Source)
	at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
	at java.awt.Component.processMouseEvent(Unknown Source)
	at javax.swing.JComponent.processMouseEvent(Unknown Source)
	at java.awt.Component.processEvent(Unknown Source)
	at java.awt.Container.processEvent(Unknown Source)
	at java.awt.Component.dispatchEventImpl(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
	at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
	at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Window.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.awt.EventDispatchThread.pumpOneEventForHierarchy(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.run(Unknown Source)
```

Das ganze endet in:


```
Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Attempt to mutate in notification
```

Dies bedeutet (soviel hab ich beim googeln herausgefunden), dass der Aufruf von setText() ausgeführt werden soll, obwohl bereits ein setText läuft. Wie ich das aber verhindern kann, hab ich noch nicht gefunden.

Folgend die wichtigen Klassen, auf das wesentliche gekürzt.

Klasse WeatherStation Datenklasse (Subject), die das GUI informiert.

```
public class WeatherStation extends Observable implements Serializable{

	private int device, port, system;
	private boolean isPresent;
	
	public WeatherStation(int device, int port, int system){
		this.device = device;
		this.port = port;
		this.system = system;
		isPresent = true;
	}
	
	public WeatherStation(){
		isPresent = false;
	}

	public int getDevice() {
		return device;
	}

	public void setDevice(int device) {
		this.device = device;
		setChanged();
		notifyObservers();
	}

	public void setPort(int port) {
		this.port = port;
		setChanged();
		notifyObservers();
	}

	public void setSystem(int system) {
		this.system = system;
		setChanged();
		notifyObservers();
	}

	public void setPresent(boolean isPresent) {
		this.isPresent = isPresent;
		setChanged();
		notifyObservers();
	}
	
}
```

GUI-Klasse ProjectBottomWeatherStationTab beihaltet die observierten Textfelder.

```
public class ProjectBottomWeatherStationTab extends ProjectBottomTab{

	WeatherStation weatherStation;
	
	public ProjectBottomWeatherStationTab(){
		super();
		weatherStation = new WeatherStation();
		
		setTitleText("Wetterstation");
		setPortText("2");
		setCheckboxText("Dieses Projekt enthält eine Wetterstation.");
	}
	
	@Override
	public void itemStateChanged(ItemEvent e) {
		if(checkbox.isSelected()){
			System.out.println("Set weather station present");
			weatherStation.setPresent(true);
		}
		else{
			System.out.println("UN-Set weather station present");
			weatherStation.setPresent(false);
		}
	}
	
	@Override
	public void performDeviceUpdate(){
		try{
			weatherStation.setDevice( Integer.valueOf(  device.getText() ));
		} catch (NumberFormatException e){
			weatherStation.setPort( 0 );
		}
	}
	
	@Override
	public void performPortUpdate(){
		try{
			weatherStation.setPort( Integer.valueOf(  device.getText() ));
		} catch (NumberFormatException e){
			weatherStation.setPort( 0 );
		}
	}
	
	@Override
	public void performSystemUpdate(){
		try{
			weatherStation.setSystem( Integer.valueOf(  device.getText() ));
		} catch (NumberFormatException e){
			weatherStation.setPort( 0 );
		}
	}
	
	@Override
	public void update(Observable o, Object arg) {
		WeatherStation ws = (WeatherStation)o;
		
		device.setEnabled(ws.isPresent());
		port.setEnabled(ws.isPresent());
		
		setDeviceText( String.valueOf(ws.getDevice()) );
		setPortText( String.valueOf(ws.getPort()) );
		setSystemText( String.valueOf(ws.getSystem() ));
	}
}
```

GUI-Klasse ProjectBottomPanel. Hier wird der Observer hinzugefügt.

```
public class ProjectBottomPanel extends JPanel{

	private MainWindow parent;
	
	private WeatherStation weatherStation;
	
	public ProjectBottomPanel(MainWindow parent){
	    this.parent = parent;

	    weatherStation = new WeatherStation(5001,2,1);
	    
	    // WEATHER PANEL
	    JPanel weatherContainer = new JPanel();
	    weatherContainer.setLayout(new FlowLayout(FlowLayout.LEFT));
	    ProjectBottomWeatherStationTab wst = new ProjectBottomWeatherStationTab();
	    wst.weatherStation = weatherStation;
	    weatherStation.addObserver((Observer)wst);
	    weatherContainer.add(wst);
	    
	    // ADD TO TABBED PANE
	    JTabbedPane jtp = new JTabbedPane();
	    jtp.add("Wetterstation",weatherContainer);
	    this.add(jtp, BorderLayout.CENTER);
	}
}
```

Was mache ich falsch? Hat jemand eine Idee? Ich probiere schon eine ganze Zeit, krieg aber nix gescheites raus.

Herzlichen Dank
  mas


----------



## EOB (18. Dez 2007)

warum schreibst du dir keinen observer selber? ich finde, dass der eingebaute observer eher ungeeignet ist, weil java.util.observable eine klasse ist. 
im head first design patterns ist das recht gut beschrieben, eben mit nutzung von nem interface anstatt einer klasse.

grüße
eob


----------



## mas (18. Dez 2007)

Na ja, das hab ich jetzt mal gemacht, der Effekt ist derselbe. Ich habe offensichtlich Probleme mit dem DocumentListener... Für meine Zwecke tuts nun auch der FocusListener, hätte das Problem aber trotzdem gerne erkannt...

Gruss
  mas


----------



## EOB (18. Dez 2007)

kanntest du das schon?

http://www.scs.carleton.ca/~lanthier/teaching/COMP1406/Notes/COMP1406_3/1406Notes3.html

grüße
eob


----------



## mas (18. Dez 2007)

Nein kannte ich nicht, danke für den Link.

Schöne Grafik und gut erklärt. Dankeschön.

Gruss
mas


----------

