# View aus anderen Thread aktualisieren



## stefan. (16. Jun 2009)

Hallo,

ich habe eine View mit einer Table

View:


```
public class MessageView extends ViewPart {
private TableViewer viewer;
private MyList myList;

	public void createPartControl(Composite parent) {
		this.parent=parent;

		myList=new MyList();
		StaticClass.setMyList(myList);
		
		Composite tableComposite = new Composite(parent, SWT.NONE);
		TableColumnLayout tableColumnLayout = new TableColumnLayout();
		tableComposite.setLayout(tableColumnLayout);
		viewer = new TableViewer(tableComposite, SWT.MULTI|SWT.H_SCROLL|		SWT.V_SCROLL|SWT.FULL_SELECTION);

		// Spalte erzeugen
		TableViewerColumn viewermessageColumn = new TableViewerColumn(viewer, SWT.NONE);
		viewermessageColumn.getColumn().setText("Message");
		tableColumnLayout.setColumnData(viewermessageColumn.getColumn(), new ColumnWeightData(88,600,true));

		viewer.setContentProvider(new MessageContent...);
		
		viewer.setInput(myList);	
	}
}
```

MyList – Liste welche die Nachrichten enthält

```
public class MyList {
	private Vector<Message> messagelist = new Vector<Message>();
	private Set<IMessageListViewer> changeListeners = new HashSet<IMessageListViewer>();
	
	public void addMessage(Message message){
		messagelist.add(message);
		Iterator<IMessageListViewer> iterator = changeListeners.iterator();
		while (iterator.hasNext())
			((IMessageListViewer) iterator.next()).addMessage(message);
	}
	public void removeChangeListener(IMessageListViewer viewer) {
		changeListeners.remove(viewer);
	}

	public void addChangeListener(IMessageListViewer viewer) {
		changeListeners.add(viewer);
	}
```

zugehöriges List Interface: IMessageListViewer

```
public interface IMessageListViewer {
	
	public void addMessage(FixMessage message);
}
```
Nun habe ich einen 2. Thread welcher empfangene Nachrichten in der View darstellen soll:

Klasse welche Nachrichten empfängt:


```
public class MessageProvider implements IMessage {
	@Override
	public void newMessage(Message message) {
		//Message in Liste adden

		final Message a=message;
		getDisplay().asyncExec(new Runnable() {
			@Override
			public void run() {
				StaticClass.getMyList.addMessage(a);
	
			}
		}
	}
}
```

Da ich sonst eine Exception bekomme, weil der Viewer in einem anderen Thread läuft, muss ich die Liste über die asyncExec() Methode aktualisieren. 

Die Liste bekomme ich (in der _newMessage()_) über eine statische Variable, nun habe ich da aber 2 Probleme: 
1. werden die empfangenen Nachrichten-Objekte (mir unerklärlicherweise) beim empfangen neuer Nachrichten teilweise vom Inhalt verändert und 2. ist die Lösung alles andere als schön/elegant. 

*Deswegen nun die Frage*, wie kann ich es besser machen , d.h. Wie komme ich aus dem 2. Thread in der Methode newMessage() an die Liste um neue Nachrichten anzufügen?

Danke für jeden Tipp!

PS: Es handelt sich um eine RCP Anwendung.


----------



## dzim (17. Jun 2009)

Ähm... Ich bin auf dem Gebiet der Threads nicht so firm, aber ich denke, was du mal versuchen könntest, wäre der UIJob.
Ich hab mit dem immer hantiert, wenn ich irgendwo was auf einer GUI anpassen musste (sonst kam immer eine IllegalThreadAccessException oder so).

Versuchs einfach mal, schaden kann's jedenfalls nicht!


----------



## stefan. (17. Jun 2009)

Hallo dzim,

das mit den UIJob klingt interessant, werde ich mir auch nochmal näher anschauen aber auf dem ersten Blick scheint das die gleiche Wirkung wie getDisplay().asyncExec(new Runnable() {} zu haben !? Denn ohne asyncExec() bekomme ich die IllegalThread.. Exception ...

Ich suche zunächst nach einer Möglichkeit die Liste global? in der RCP Anwendung ablegen zu können um dann von jedem Plugin aus darauf zuzugreifen. Evtl gibt es ja dafür einen ExtensionPoint? Bzw. wie wird das sonst gehandhabt ?


----------



## dzim (17. Jun 2009)

k.A. Wildcard ist hier der crack! ,-)

Ich würde es aber vielleicht wirklich über einen eigenen ExtensionPint machen.
Dann müssen sich andere Plugins aber über den Extension Point als Listener registrieren. Das wär vielleicht das einfachste.

Aber da ich dein Problem noch nicht 100%ig verstanden habe, bin ich da vielleicht gerade noch nicht die große Hilfe.

Fakt ist: Wenn du PlugIn übergreifend arbeiten willst, wirst du um Extension Points nicht herum kommen, aus eigener Erfahrung weiß ich, das das zwar ein bisschen tricky ist, aber alles im Rahmen des Machbaren.


----------



## stefan. (17. Jun 2009)

dzim hat gesagt.:


> Aber da ich dein Problem noch nicht 100%ig verstanden habe, bin ich da vielleicht gerade noch nicht die große Hilfe.



Was ist dir denn unklar? Kann es ja nochmal versuchen näher zu Beschreiben:

Ich benutze in einer View die JFace Komponente TableViewer. Diese soll Nachrichten welche über einen extra Thread empfangen werden darstellen. Bei neuen Nachrichten wird die newMessage() Methode aufgerufen, also muss ich von dieser Methode aus den TableViewer aktualiseren (Nachricht hinzufügen). 
Dem TableViewer kann ich über setInput z.B. eine Liste mit Objekten übergeben die dargestellt werden. Wenn ich diese Liste aber in der View selber erzeuge, habe ich keine Referenz darauf. Somit benötige ich irgend eine Möglichkeit die Referenz der Liste global abzulegen um dann in der newMessage() auf die Referenz zuzugreifen und liste.addMessage() auszuführen.


----------



## Wildcard (17. Jun 2009)

Beziehe dich immer auf das gleiche Modell (die gleiche Instanz). Kommt eine neue Message dann fügst du sie dem Parent hinzu, informierst die Listener das sich die Struktur des Parents geändert hat. Einer dieser Listener refreshed dann den Tree mit asyncExec.
Schau dir vielleicht mal EMF an. Damit bekommst du ein sehr mächtiges Datenmodell mit kompletem Notification Support und auf Wunsch auch Databinding das dir das ganze gehampel abnimmt.


----------



## stefan. (17. Jun 2009)

Hallo Wildcard, danke für deinen Tip!

Meinst du nach dem Observable-Prinzip, ich habe einen Beobachter, der bei einer neuen Nachricht informiert wird und dann addMessage macht ? Wobei ich den Beobachter ja auch irgendwie anmelden müsste und wieder Referenzen bräuchte .. und was meinst du mit Parent ? So richtig blick ich leider noch nicht durch 

Hast du evtl ein Beispiel/Tutorial/Einführung in EMF ?


----------



## Wildcard (17. Jun 2009)

Ahh, nicht Parent, habe mich verlesen, du hast einen TableViewer, keinen TreeViewer.
Trotzdem hast du wohl irgendeine Form der Datenstruktur die GUI unabhängig ist. Die wird verändert und benachrichtigt die Observer.


----------



## stefan. (18. Jun 2009)

Selbst wenn meine Datenstruktur zusätzlich als Observer arbeitet, weiss ich nicht wie ich mich am Observable registrieren soll, da mir dessen Referenz fehlt.







Mein Problem ist:

Da ich die Viewer instanz nicht selbst erzeuge sondern RCP das für mich tut, weiss ich nicht wie ich auf diese zugreifen kann. Wenn ich die Instanz selbst erzeugen würde, könnte ich diese beispielsweise über get() bis zur gewünschten Stelle durchreichen. Also muss es doch in RCP eine Möglichkeit geben (zumindest innerhalb eines Plugins) Methoden anderer Klassen aufzurufen?


----------



## Wildcard (18. Jun 2009)

Du kannst dir natürlich problemlos eine Referenz auf die View abolen (ist ein ganz normales Objekt), aber das Datenmodel sollte normalerweise eher ausserhalb der View verwaltet werden, die View  ist dann nur ein 'View'  auf die Daten anstatt sie selbst zu erzeugen.
Angenommen du hättest so eine Art MessageProvider der alle Messages vorhält und an dem sich Observer registrieren können.
Deine View würde sich auf diesem Provider als Observer anmelden und informiert werden wenn es etwas neues gibt und der Thread wird vom Provider selbst gestartet um neue Nachrichten zu empfangen.


----------



## stefan. (18. Jun 2009)

Wildcard hat gesagt.:


> Angenommen du hättest so eine Art MessageProvider der alle Messages vorhält und an dem sich Observer registrieren können.
> Deine View würde sich auf diesem Provider als Observer anmelden und informiert werden wenn es etwas neues gibt und der Thread wird vom Provider selbst gestartet um neue Nachrichten zu empfangen.



Du meinst die View initiiert den MessageProvider und meldet sich zugleich bei diesem als Observer an. Der MessageProvider startet einen extra Thread welcher auf Nachrichten wartet? Das wäre zum einem weniger schön, da man dann von der View abhängig ist und zum anderen geht das in meinem Fall nicht, da der Thread von einem anderen Plugin initiiert wird.






Plugin.core stellt einen ExtensionPoint zur Verfügung mit einem Java-Attribut (Plugin.view erweitert diesen). Beim Aufbau der Verbindung erzeugt das core Plugin mittels 
	
	
	
	





```
configurationElement.createExecutableExtension("class");
```
 eine neue Instanz der Klasse welche Nachrichten empfängt. 
Gibt es bei RCP keine Möglichkeit Objekte/Datenstrukturen global abzulegen ?

Und rein Interesse halber, wie kann ich mir ein View-Objekt holen, müsste ja irgendwie über die Workbench gehen ?


----------



## Wildcard (19. Jun 2009)

Nein, ich meine deine Message Logik sollte völlig unabhängig von der Oberfläche sein und notfalls auch headless laufen können. Der Provider könnte zum Beispiel einfach ein OSGi Service sein den die View dann verwendet.


View finden geht so:
IWorkbenchPage (Eclipse Platform API Specification)


----------



## stefan. (1. Jul 2009)

Hallo Wildcard,

zunächst ersteinmal danke für den Tipp mit OSGi! Für die Messages habe ich nun den EventAdmin Service genommen, welcher genau auf die Aufgabe passt ;-)

Eine allgemeine Frage habe ich aber dennnoch:

Die Verbindungs-Logik (Aufbau, senden einer Nachricht etc..) habe ich in ein separates Plugin ausgelagert. Dieses Plugin exportiert einige Packages und in der (RCP) Oberfläche lege ich dann per klick auf Verbinden ein Objekt der Verbindungs-Logik an und rufe die start() Methode davon auf. Nun ist dies alles andere als 'unabhängig von der GUI', deswegen die Frage: Wie kann ich das besser Konzipieren? Wäre ein weiterer OSGi Service dafür geeignet, wovon sich die GUI eine Referenz holt?


----------



## Wildcard (1. Jul 2009)

Die 'Verbindungslogik' exportiert einen OSGi Service (YourConnectionService) der dann zB eine login(Credentials) Methode hat.
OSGi Declarative Services bieten sich hervorragend dafür an.


----------



## stefan. (2. Jul 2009)

@Wildcard

habe es nun genauso gemacht wie du Vorgeschlagen hast und funktioniert auch sehr gut :toll:

Für den Verbindungsaufbau (und Abbau) benutze ich ein Command welches mit (je) einen Button verknüpft ist. Das command definiert einen defaultHandler womit ich in der execute() Methode die Verbindung aufbauen kann.

execute() im DefaultHandler startConnection:

```
public Object execute(ExecutionEvent event) throws ExecutionException {
	//... Service Referenz holen, start Connection() ...
}
```

Aber wie kann ich die Buttons connect/disconnect dynamsich enablen bzw. disablen? (Also connect nach erfolgreichem Verbindungsaufbau disablen, disconnect Button enablen). 
Denn die isEnabled() Methode im Handler aktualisiert sich nur bei Fokus-Änderungen ?


----------



## Wildcard (2. Jul 2009)

Entweder deklarativ:
Eclipse Tips: Commands Part 2: Selection and Enablement of IHandlers
oder ein Property Change Event feuern, wenn ich das gerade richtig im Kopf habe.


----------

