Archiv

Artikel getaggt mit ‘Java’

SWT: GUI bei langen Aktionen nicht einfrieren lassen

26. November 2011 2 Kommentare

Länger dauernde Aktionen sollten nie im GUI-Thread laufen. Dies gilt auch für SWT, der GUI-Bibliothek von IBM/Eclipse für Java. Allerdings gibt es bei SWT einige Punkte zu beachten, ohne die ein Umbau schnell sehr mühsam werden kann.

 

Ausgangslage

Bei den meisten Erklärungen zu SWT wird im SelectionListener eines Knopfes alle Logik eingebaut, die beim Klick darauf ausgeführt werden soll. So lange die daraus resultierenden Aktionen schnell verarbeitet werden können ist dagegen auch nichts einzuwenden.

Dauert es aber länger friert einem sehr schnell das GUI ein. Je nach PC variieren die Auswirkungen von einem flackern der Anzeige bis zu kompletten Blockieren der Anwendung. Der Code wird in so einem Fall wohl meist so aussehen:

workButtonSlow.addSelectionListener(new SelectionAdapter() {
	public void widgetSelected(SelectionEvent arg0) {
		progress.setSelection(0);
		workButtonSlow.setEnabled(false);
		
		// simuliert lange dauernde Aktion
		for (int i = 0; i < 100; i++) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				System.out.println(e.getMessage());
			}
			progress.setSelection(progress.getSelection() + 1);
		}

		workButtonSlow.setText("Thread beendet");
		workButtonSlow.setEnabled(true);
	}
});

 

Lösung: Threads und asyncExec

Nach einigen Anläufen bin ich bei Threads und einer Synchronisierung des GUI über asyncExec gelandet. Die lange dauernden Aktionen werden in einen eigenen Thread ausgelagert und im SelectionListener nur noch gestartet. Die Verarbeitung erfolgt so losgelöst vom GUI-Thread und behindert das Neuzeichnen der Oberfläche nicht – dies genügt damit die Anwendung viel reaktiver erscheint.

Da man nun während der Ausführung der Aktion weiterarbeiten kann, steht man unter Umständen vor neuen Problemen. Falls die gleiche Aktion nicht noch einmal parallel dazu gestartet werden darf, muss man dies nun explizit verhindern. Je nach Anwendung genügt es den entsprechenden Knopf beim Start des Arbeitsthreads zu deaktivieren und erst beim beenden wieder zu aktivieren.

Eine Implementierung mit einer eigenen Thread-Klasse kann so aussehen:

class LongRunningOperation extends Thread {
		private Display display;
		private ProgressBar progressBar;
		private Button workButton;

		/**
		 * Alles übergeben was aus diesem Thread erreichbar sein soll
		 */
		public LongRunningOperation(Display display, ProgressBar progressBar,
				Button workButton) {
			this.display = display;
			this.progressBar = progressBar;
			this.workButton = workButton;
		}

		/**
		 * Länger laufende Methode um eine Verarbeitung zu simulieren
		 */
		public void run() {
			for (int i = 0; i < 100; i++) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					System.out.println(e.getMessage());
				}
				progressBar.setSelection(progressBar.getSelection() + 1);
				// ProgressBar kann nur via asyncExec aktualisiert werden!
				display.asyncExec(new Runnable() {
					public void run() {
						if (progressBar.isDisposed())
							return;

						progressBar.setSelection(progressBar.getSelection() + 1);
					}
				});
			}

			// Gleiches gilt für alle GUI-Elemente
			display.asyncExec(new Runnable() {
				public void run() {
					if (workButton.isDisposed())
						return;
					workButton.setText("Thread beendet");
					workButton.setEnabled(true);
				}
			});
		}
	}

Der Knopf über den die zeitintensive Aktion gestartet wird ist wie alle anderen GUI-Elemente in SWT aber nicht direkt aus einem anderen Thread heraus veränderbar. Damit der Ausführungskontext stimmt müssen alle Veränderungen dieser Elemente als Runnable der Methode asyncExec übergeben werden. Wichtig ist das man das Display-Objekt nutzt mit dem man die Shell der Anwendung initialisiert hat.

Versucht man asyncExec zu umgehen wird SWT mit dieser Exception antworten:

Exception in thread “Thread-0″ org.eclipse.swt.SWTException: Invalid thread access

Sind alle Zugriffe entsprechend umgeformt, kann man im SelectionListener des Knopfes die Thread-Klasse starten:

		workButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent arg0) {
				progress.setSelection(0);
				workButton.setEnabled(false);
				new LongRunningOperation(s_display, progress, workButton)
						.start();
				workButton.setText("SelectionListener beendet!");
			}
		});

 

Fazit

Mit dieser Umbauarbeit kann man auch lange laufende Aktionen ausführen ohne dass einem das GUI einfriert oder an Reaktionsfähigkeit einbüsst. Dieser Ansatz ist ein wenig aufwändig, erfüllt aber seinen Zweck. (Das ganze Beispiel ist auf Github verfügbar)

Falls es einfachere Wege gibt würde ich mich über einen Kommentar freuen.

Kategorien:Eclipse, Java Schlagworte: ,

Java 7 – was lange dauerte ist endlich da

Man hätte schon fast meinen können Java 7 würde nicht mehr erscheinen. Java 6 erschien Mitte Dezember 2006 und erlaubte uns damals noch einige letzte Tests bevor unsere Diplomarbeit AtaraxiS abgegeben werden musste. Diese Woche war es nun so weit: nach 4 Jahren und fast 7 Monaten hat Oracle das JDK 7 freigegeben.

 
Wenig Neues…
Über die vielen Jahre wechselte Java nicht nur von Sun zu Oracle, auch die Feature-Liste schmolz wie Schnee im Sommer. Von den 70 Verbesserungen die Project Coin vorgeschlagen hat sind so nur 6 in Java 7 angekommen. Und Coin war nur eines von vielen Themen.

 
…aber einige praktische Verbesserungen
Von den wenigen Verbesserungen gibt es aber 2 die einem sofort ins Auge stechen:

  • Strings in Switch-Statements
  • Mehrere Exceptions mit einem Catch-Block fangen

Dazu hat Heise einige Beispiele zusammen getragen. Auch wenn das nichts Revolutionäres ist, so kann man damit doch einige recht mühsame Umgehungslösungen vermeiden.

 
Dateisystem: Da war doch was?
Bisher war die Arbeit mit dem Dateisystem in Java jeweils mühsam. Man musste für jede Kleinigkeit (wie copy) selber seine Methoden schreiben oder auf Bibliotheken wie Apache Commons IO ausweichen. Java 7 bringt nun auch dafür etliche Verbesserungen. Neue Klassen und Interfaces wie Path, Files oder WatchService geben dem Entwickler viele neue Möglichkeiten. Wie bei den Strings und switch kann man den nötigen Code für die gleiche Funktionalität massiv reduzieren.

 
Ausblick Java 8
All die Features die nicht in Java 7 drin sind sollen es in Java 8 schaffen. Geplant ist Java 8 für Ende 2012. Schaut man aber wie gut der Plan für Java 7 stimmte dürfte es niemanden erstaunen wenn es da noch etliche Verschiebungen geben wird.

 
Fazit
Java 7 bietet etliche Detailverbesserungen, alle grossen Neuerungen dürften aber frühestens mit Java 8 kommen. Bis dahin werden wohl andere Sprachen die auf der JRE aufsetzen (wie zum Beispiel Groovy) als Trendsetter dienen müssen.

Kategorien:Java Schlagworte:

Buch-Rezension zu „Waltzing with Bears“

27. Juni 2011 1 Kommentar

Waltzing with Bears: Managing Risk on Software Projects“ von Tom DeMarco und Timothy Lister erschien 2003 bei Dorset House. Risikomanagement ist etwas was nicht erst in den letzten Jahren aktuell wurde. DeMarco und Lister haben ihre Erfahrungen in dieses knapp 200 Seiten umfassenden Buch einfliessen lassen. Wie meist bei DeMarco ist das Buch trotz der vielen Theorie sehr angenehm zu lesen.

 
 
 
 
Die einzelnen Teile des Buches gehen diesen Fragen rund ums Risikomanagement nach:

  • Warum?
  • Wann nicht?
  • Wie?
  • Wie viel?
  • Wird’s gemacht?

Im ersten Teil wird auf anschauliche Art erklärt was Risiken sind und wieso man vor ihnen nicht weglaufen kann. Projekte die ganz ohne Risiko sind haben meist auch nur sehr wenig finanzielle Anreize, wodurch man früher oder später hinter die risikoreichen Projekte muss.

 
Wann auf Risikomanagement verzichten?
Teil 2 geht dann auf die Situationen ein, wo man besser auf ein Risikomanagement verzichtet. Dies mag einen erstaunen, doch gibt es solche Situationen wirklich. Es macht schlicht keinen Sinn in einer Firma die nur Ja-Sager will von Dingen wie Risiken zu sprechen. Bei Risiken spricht man von Möglichkeiten und Unsicherheiten. Ein Firmenmotto wie

It’s okay to be wrong, but not okay to be uncertain.

steht diesem Vorhaben im Weg. Eine andere Situation ist wenn Glück integraler Bestandteil des Projektplans ist. Dabei ist nicht gemeint dass man mit ein wenig Glück vielleicht ein wenig früher fertig wird. Sondern das man bei allen Schritten viel Glück braucht damit man überhaupt auch nur im Entferntesten diesen Plan umsetzen kann.

 
Wie managt man Risiken?
Gut die Hälfte des Buches widmet sich der Frage wie man Risiken managen kann. Als erstes Beispiel dient das Risiko des verpassten Liefertermins. Mittels Terminkurven wird die Wahrscheinlichkeit des Lieferzeitpunktes untersucht. Die eine Grenze bildet der frühestmögliche Zeitpunkt (N) unter optimalsten Bedingungen, die andere ist der Termin bei dem man sicher fertig sein wird (und damit viel weiter in der Zukunft liegt als das Projekt dauern soll).

Der Punkt N wird hier als Nano-Prozent Datum definiert. Die Chancen zu diesem Zeitpunkt zu liefern sind grösser als 0, aber immer noch verschwindend gering. Leider wird dieser Zeitpunkt aber oft als Liefertermin benutzt, was zu all den bekannten „Verzögerungen“ führt.

Wie geht man nun mit diesem Risiko um? Die Autoren schlagen vor Anzeichen für ein Eintreten zu sammeln und zu überwachen. Davon erhofft man sich bei ersten Anzeichen noch reagieren zu können. Allerdings gilt es bei grösseren Risiken Vorkehrungen zu treffen. Genau wie bei einer Versicherung muss man sich vor dem Eintreffen des Schadensereignisses darum kümmern. Dabei muss man wieder abwägen wie viel einem eine Vorkehrung für etwas das nur vielleicht eintreffen wird wert ist. Es wäre kein Buch von DeMarco wenn es dazu nicht auch zahlreiche Formeln und betriebswirtschaftliche Grundlagen gäbe.

Neben der Terminüberschreitung gibt es auch zu anderen häufigen Risiken bei Software Projekten (wie Explosion der Anforderungen oder Personalfluktuation) Beispiele die nach dem gleichen Muster angegangen werden.

 
Risiken minimieren
DeMarco und Lister haben auch etliche Ansätze zum Reduzieren der Risiken. Für die Lieferverzögerung ist ihr Ansatz die Software in mehreren Teilen zu liefern. Mit doch recht vielen Worten wird ein vorgehen erklärt das anhand des Nutzens für den Kunden Teile abgrenzt und zu Lieferpaketen gruppiert. Aus heutiger Sicht ist einem so ein Vorgehen aus Scrum oder XP bekannt und bräuchte weniger Erklärungsbedarf.

 
Kosten, Nutzen und Risikobereitschaft
Wie auch bei „Software by Numbers“ sollten für etliche Berechnungen sowohl die Kosten wie auch der Nutzen vorliegen. DeMarco und Lister sind diesbezüglich aber ein wenig realistischer:

Cost = $6,235,812.55
Benefit = „We gotta have it.“

Ein wenig mehr Erklärung wie man dann aber doch zu genügend guten Zahlen kommt hätte in dem Buch noch Platz finden sollen.

Ein weiteres schönes Zitat zur Risikobereitschaft zeigt eines der grossen Probleme bei Software Projekten:

Take lots of risks when the benefit is negligible. How else can we possible get costs down enough to justify this loser project?

Dies steht im Wiederspruch zu dem was man eigentlich erwarten würde: Viel Risiko wenn es viel zu gewinnen gibt, wenig Risiko wenn der mögliche Nutzen minimal ist. All die Death-March Projekte zeigen aber das DeMarco und Lister auch hier recht haben. Leider.

 
Fazit
Ich finde das Buch ist ein guter Einstieg ins Thema. Es deckt die 20% des Themas Risikomanagement ab die rund 80% des Nutzens beinhalten. Damit ist aber auch klar dass man nach dem Lesen kein Experte für Risikomanagement sein kann.

Ein wenig mehr Wissen über Risiken im Entwicklerteam würde noch so manchem Projekt gut tun. Dazu kann man dieses Buch gut nutzen.

 
Zum Buch
Waltzing with Bears: Managing Risk on Software Projects” von Tom DeMarco und Timothy Lister, 2003 Dorset House

Täglich reflektieren – nicht nur etwas für CCD

31. Januar 2011 1 Kommentar

Täglich über seine Arbeit zu reflektieren ist eine der Praktiken, die bei Clean Code Developer (CCD) im roten Grad gefordert werden. Der rote Grad ist der Beginn einer Reise, die einem als Softwareentwickler zu einem (inneren) Wertesystem führen soll. Dies geschieht durch eine bewusste Auseinandersetzung mit seinem Metier und dem verinnerlichen grundlegender Praktiken und Prinzipien.

Im roten Grad gibt es neben der Reflexion noch zahlreiche andere Praktiken und Prinzipien. Ich finde diese Praktik aber besonders wichtig und schliesse mich der Begründung von CCD an:

Keine Verbesserung, kein Fortschritt, kein Lernen ohne Reflexion. Aber nur, wenn Reflexion auch eingeplant wird, findet sie unter dem Druck des Tagesgeschäftes auch statt.

Wer nicht über seine Arbeit reflektiert hat keine Chance diese zu verbessern. Man findet sicher weitere Möglichkeiten seine Arbeit anders zu machen. Doch lernt man dabei auch etwas? Natürlich kann es spannend sein seine Arbeit jeden Tag ein wenig anders zu gestalten. Doch ist das wirklich lernen? Braucht es zum anders machen nicht auch die Erkenntnis, welche der vielen Möglichkeiten einem helfen?

Werde ich wirklich ein besserer Programmierer wenn ich LINQ an jeder Stelle einsetze – unabhängig davon ob es da Sinn macht? Wird mein Code wirklich leserlicher, wenn ich var in allen Bereichen verdamme? Gewinne ich wirklich etwas beim Einsatz optionaler Parameter für Parameter die ich in jedem Fall angeben muss? Oder folge ich damit nicht einfach einem Hype?

Um eine Antwort darauf zu finden, muss ich nachdenken – und über meine getane Arbeit reflektieren.

 
Reflektieren – aber wie?
Um zu überprüfen ob meine erledigte Arbeit mit meinen Zielen übereinstimmt brauche ich als erstes Ziele. So banal dies tönt, so wichtig ist es. Wenn ich täglich reflektieren will, muss mein Ziel idealerweise innerhalb des gleichen Zeitrahmens erreichbar sein. Ist mein Arbeitspaket grösser, muss ich meine Arbeit in Stücke teilen, die ich innerhalb eines Tages erledigen kann.

Habe ich die Ziele, kann ich überprüfen ob ich diese Ziele erreicht habe. (Konnte ich das GUI wie gefordert erstellen? Habe ich allen doppelten Code entfernt?) Diese Ziele müssen aber nicht nur technischer Natur sein. Ich kann dabei auch darüber nachdenken, wie ich diese Ziele erreicht habe. Was hat mir bei meinem Arbeitsablauf gefallen? Was hat mich Zeit gekostet? Wo sehe ich Möglichkeiten meine Effizienz zu steigern oder mich zu verbessern?

Mir hilft es wenn ich neben den Zielen auch 3 oder 4 Punkte zum Tag im Allgemeinen aufschreibe. Dies kann von technischen Kniffen und der Ursache hinter kryptischen Fehlermeldungen bis zu ganz allgemein gehaltenen Aussagen reichen. (wie: „Nach jedem erledigten Teil ein commit.“)
Wenn sich Punkte wiederholen, weiss ich dass ich dem nachgehen muss.

 
Und wann?
CCD schlägt vor am Abend jedes Tages zu reflektieren. Ein Arbeitstag ist eine gute und greifbare Einheit, die für ein längerfristig ausgelegtes Vorhaben genau genug ist.

Ich überprüfe am Ende jedes Arbeitstages ob auch wirklich alle Bugfixes vom branch in den trunk gemerged wurden und ob meine Issues alle nachgeführt sind. Da ich dabei eh über meine an diesem Tag erledigte Arbeit nachdenken muss, nutzte ich diese Gelegenheit zur Reflexion. Der Mehraufwand für CCD ist dadurch vernachlässigbar.

Aber auch ohne CCD und ohne Issues die gepflegt werden müssen ist eine tägliche Reflexion machbar. Es braucht gar nicht so viel Zeit wie man vermutet. Mit ein wenig Übung geht dies in kürzester Zeit in weniger als 10 Minuten.

Ein weiterer positiver Nebeneffekt ist der dadurch gemachte saubere Abschluss des Arbeitstages. Wenn alles abgeschlossen ist gibt es auch keinen Grund mehr im Feierabend darüber nachzudenken. Der Feierabend dient dann wie vorgesehen der Erholung – und nur der.

 
Fazit
Die tägliche Reflexion scheint so banal und vernachlässigbar. Sie ist aber der Garant für eine stetige Verbesserung und dadurch unverzichtbar. Mit wenig Aufwand kann man die Reflexion in seinen Arbeitsalltag aufnehmen. Nur wer weiss wo er steht kann sich sinnvolle nächste Schritte überlegen. Und für beides hilft das tägliche reflektieren.

Kategorien:.Net, Software Entwicklung Schlagworte: , , , ,

Java: String von der Kommandozeile lesen (mit java.util.Scanner)

Warum ist das Einlesen eines Strings von der Kommandozeile in Java so kompliziert?

war eine Frage die mir heute gestellt wurde. Fragt man Google nach einer Lösung um einen String einzulesen wird meist so etwas unter den ersten Treffern landen:

BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String input = in.readLine();

Mit Hilfe von BufferedReader und InputStreamReader kann man nach dem gleichen Prinzip sowohl einen String aus einer Datei, von der Kommandozeile oder jedem beliebigen Stream lesen. Wenn man es häufig genug anwendet, braucht man da gar nicht mehr gros zu überlegen.

Dies ist allerdings doch recht viel Code, wenn man bedenkt das man in C# mit dieser Zeile auskommt:

string input = Console.ReadLine();

 
Scanner als Alternative
Seit Java 5 gibt es die Klasse java.util.Scanner, die man auch zum einlesen von Werten aus der Kommandozeile nutzen kann:

Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine();

Es sind zwar auch 2 Zeilen Code, doch braucht man keine InputStreams und *Reader zu kombinieren. Für Strings ist der Vorteil wohl zu wenig gross, sonst würde nicht in so vielen Schulungsunterlagen die Variante mit dem BufferedReader erklärt.

 
Und Zahlen?
Der Scanner ermöglicht die Prüfung ob der nächste Wert vom gewünschten Datentyp ist. So genügt es den Scanner zu fragen und man spart sich den Aufwand den Wert selber zu überprüfen:

	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);

		if (scanner.hasNextDouble()) {
			System.out.println("Ihre Zahl: " + scanner.nextDouble());
		} else {
			System.out.println("Leider kein Double gefunden.");
		}
	}

Ich finde dies deutlich angenehmer als mit dem BufferedReader-Ansatz und einer eigenen Validierungsmethode:

	public static void main(String[] args) throws IOException {
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		String input = in.readLine();
		
		if (isDouble(input)) {
			double x = Double.valueOf(input).doubleValue();
			System.out.println("Ihre Zahl: " + x);
		} else {
			System.out.println("Leider kein Double gefunden.");
		}
	}

	public static boolean isDouble(String string) {
		try {
			Double.valueOf(string);
			return true;
		} catch (NumberFormatException e) {
			return false;
		}
	}

Vergisst man vor der Konvertierung zu prüfen ob es auch wirklich ein gültiger Wert ist, läuft man sehr schnell in diese Fehlermeldung:

Exception in thread "main" java.lang.NumberFormatException: For input string: "h"
at sun.misc.FloatingDecimal.readJavaFormatString(Unknown Source)
at java.lang.Double.valueOf(Unknown Source)
at ch.jgraber.blog.DemoTraditionalReadLine.main(DemoTraditionalReadLine.java:18)

 
Fazit
Java bietet mit java.util.Scanner eine einfache Möglichkeit um Strings oder Zahlen von der Kommandozeile zu lesen. Mir scheint als ob diese einfache Möglichkeit bisher zu wenig Beachtung findet. Suche ich nur falsch oder ist dies wirklich so?

Kategorien:Java Schlagworte:
Follow

Bekomme jeden neuen Artikel in deinen Posteingang.

Join 142 other followers