<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Elmars Notizen</title>
	<atom:link href="http://www.elmar-baumann.de/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.elmar-baumann.de/blog</link>
	<description>Software, Programmieren, Sonstiges</description>
	<lastBuildDate>Mon, 16 Jan 2012 18:25:43 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Bildschirmtaugliche Texte</title>
		<link>http://www.elmar-baumann.de/blog/2012/01/15/bildschirmtaugliche-texte/</link>
		<comments>http://www.elmar-baumann.de/blog/2012/01/15/bildschirmtaugliche-texte/#comments</comments>
		<pubDate>Sun, 15 Jan 2012 19:43:43 +0000</pubDate>
		<dc:creator>Elmar</dc:creator>
				<category><![CDATA[E-Books]]></category>
		<category><![CDATA[Publishing]]></category>
		<category><![CDATA[Websites]]></category>

		<guid isPermaLink="false">http://www.elmar-baumann.de/blog/?p=1382</guid>
		<description><![CDATA[Hier beschreibe ich, wie Texte formatiert sein sollten, damit ich diese gut lesen kann auf Bildschirmen von Smartphones, Tablet-Computern, Netbooks, Notebooks und Desktop-Computern. Grundsätzliche Voraussetzungen Die grundsätzlichen Voraussetzungen sind: passende Schriftart und Zeilenabstand der Bildschirmhardware und Umgebung angepasste Schriftgröße Zeilen nicht länger als 60 bis 70 Zeichen bequemer Fortschritt (&#8220;Blättern&#8221;), kein horizontales Scrollen Passende Schriftart [...]]]></description>
			<content:encoded><![CDATA[<p>Hier beschreibe ich, wie Texte formatiert sein sollten, damit ich diese gut lesen kann auf Bildschirmen von Smartphones, Tablet-Computern, Netbooks, Notebooks und Desktop-Computern.<br />
<span id="more-1382"></span></p>
<h2>Grundsätzliche Voraussetzungen</h2>
<p>Die grundsätzlichen Voraussetzungen sind:</p>
<ul>
<li>passende Schriftart und Zeilenabstand</li>
<li>der Bildschirmhardware und Umgebung angepasste Schriftgröße</li>
<li>Zeilen nicht länger als 60 bis 70 Zeichen</li>
<li>bequemer Fortschritt (&#8220;Blättern&#8221;), <em>kein</em> horizontales Scrollen</li>
</ul>
<h2>Passende Schriftart und Zeilenabstand</h2>
<p>Die <strong><a title="Wikipedia" href="http://de.wikipedia.org/wiki/Schriftart">Schriftart</a></strong> (der <strong>Font</strong>) sollte optimiert sein für die Bildschirmdarstellung. Ich konnte bislang <a title="Wikipedia" href="http://de.wikipedia.org/wiki/Serife">serifenlose</a> Schriften besser lesen als jene mit Serifen aufgrund der relativ geringen Bildschirmauflösung (Pixel pro Millimeter): Die Serifen benötigen extra Pixel, nur bei größerer Auflösung/Darstellung kann ich die Serifenschrift gleich gut lesen wie eine serifenlose, klein wirkt sie auf mich unscharf, &#8220;schlierig&#8221;. Am besten ist, mir die Wahl zu lassen, mit welcher Schriftart Texte dargestellt werden: Ich teile der Darstellungssoftware die mir passende mit und diese wird fortan benutzt.</p>
<p>Der Abstand zwischen den Zeilen sollte größer sein als vorgegeben durch die Schriftart aufgrund der geringen Auflösung. Stehen die Zeilen eng zusammen, ist der Text einer Zeile weniger klar voneinander getrennt von darüber oder darunter stehendem. Schön wäre, ich könnte den Zeilenabstand passend für den benutzten Bildschirm einstellen, so lange das unüblich ist, ist das 1,25 &#8211; 1,5-fache der Vorgabe meist in Ordnung.</p>
<h2>Der Bildschirmhardware und Umgebung angepasste Schriftgröße</h2>
<p>Die Hardware limitiert:</p>
<ul>
<li><strong>Auflösung</strong>: Die Auflösung sind die Pixel pro Millimeter. Mit den Pixel werden die Buchstaben/Zeichen dargestellt, je weniger Pixel pro Längeneinheit zur Verfügung stehen, desto gröber, ungenauer, schlampiger erscheinen die Buchstaben/Zeichen.</li>
<li><strong>Kontrast/Schärfe</strong>: Der Kontrast ist der Helligkeitsunterschied zwischen Buchstaben und Hintergrund. Der höchste Kontrast ist jener zwischen Schwarz – kein Pixel leuchtet – und Weiß – alle drei Farben eines Pixel leuchten in voller Stärke. Gute Monitore haben einen hohen Maximalkontrast, dieser kostet Energie und Geräte, die ihre Energie aus Akkus beziehen, liefern ihren Bildschirmen meist weniger Energie, sollen die Akkus lange halten pro Ladezyklus. Die Schärfe hängt eng zusammen mit dem Kontrast, ein niedriger Kontrast wird weniger scharf empfunden als ein hoher. Es gibt weitere Faktoren, die die Schärfe bestimmen und die ich hier nicht behandle, beispielsweise die Oberfläche des Bildschirms (Material, spiegelnd, matt), der Font (Schriftart) und die Qualität des Font-Rendering.</li>
</ul>
<p>Die Umgebung bestimmt, wie hell es außerhalb des Bildschirms ist und ob und wie stark sich auf dem Bildschirm etwas spiegelt. Hinzu kommt, aus welcher Entfernung ich auf den Bildschirm schaue, ob ich meine Lesebrille benutze und in welcher Tagesform ich mich befinde. Da all diese Faktoren nicht programmatisch berücksichtigt werden können (zumindest in der Gegenwart), ist vorherzusehen: Ich <strong>vergrößere</strong> kleine Schrift, bis ich den Text ohne Anstrengung lesen kann.</p>
<h2>Zeilen nicht länger als 60 bis 70 Zeichen</h2>
<p>Sind Zeilen sehr lang, ist es schwierig, die Folgezeile schnell zu finden. Nach meinen Recherchen sind um 60 bis 70 Zeichen die Obergrenze. Kürzer dürfen Zeilen sein, wo die Untergrenze liegt, weiß ich nicht, ich könnte mir die Hälfte des Maximalwerts vorstellen, das heißt etwa 30 Zeichen. Zu kurze Zeilen sind auch nicht ergonomisch: Ich bin gezwungen, die Augen ständig zu neuen Zeilenanfängen zu bewegen und dieser Weg ist länger als jener zum nächsten Wort. Außerdem habe ich häufiger zu &#8220;blättern&#8221;.</p>
<h2>Bequemer Fortschritt (&#8220;Blättern&#8221;), <em>kein</em> horizontales Scrollen</h2>
<p>Bequem ist für mich, nur eine Taste zu drücken, damit der Text fortschreitet, nach unten rollt. Diese Taste will ich möglichst selten drücken, deshalb sollte der Fortschritt möglichst groß sein, das heißt, die erste Zeile nach danach ist jene, die der letzten zuvor folgt. Am liebsten ist mir die Leertaste, zur Not geht auch die Bild-nach-unten-Taste (Page Down).</p>
<h2>Konsequenzen</h2>
<h3>Der Text ist einspaltig, <em>nicht</em> mehrspaltig</h3>
<p>Mehrspaltiger Text ist sehr unergonomisch zu lesen, falls er nicht vollständig auf dem Bildschirm dargestellt wird: Ich bin gezwungen, pro Seite ein- bis mehrmals nach oben zu blättern, dann wieder nach unten, dann wieder nach oben, je Spalte wiederholend, in schlimmen Fällen habe ich auch noch zusätzlich horizontal zu scrollen, da Spalten weiter rechts andernfalls nicht sichtbar wären. Kurz: Statt dass ich den Text fließend lesen kann, bin ich beschäftigt mit der Navigation durch den Text und habe unfreiwillige Lesepausen, die ich nicht zum Überdenken des Texts nutze, sondern zum Steuern des Computers.</p>
<h3>Die Zeilenlänge ist als maximale Zeichenanzahl definiert</h3>
<p>Eine Angabe von maximal 60 (70) Zeichen sorgt dafür, dass ich schnell Folgezeilen finde und nicht zu häufig zu diesen springen muss. Das Maximum ist erforderlich: Falls der Bildschirm keine 60 oder 70 Zeichen darstellen kann – bei kleinen Bildschirmen oder großer Schrift – sollte früher umgebrochen werden, andernfalls müsste ich horizontal scrollen.</p>
<h3><em>Keine</em> PDF-Dateien</h3>
<p>PDF-Dateien sind für ein bestimmtes Ausgabemedium <em>vor</em>formatiert. Alleine für den Ausdruck auf Papier ist dies zufriedenstellend möglich, die Unwägbarkeiten der Bildschirmausgabe habe ich oben angerissen. Ein PDF-Dokument für beispielsweise einen Tablet-Computer ist besser als eines für den Papierausdruck, jedoch unterstellt dies implizit heutige Hardware und ist vielleicht schon morgen überholt. Auch für den Publizierer ist es aufwändig, fortlaufend zahlreiche PDF-Dateien zu erstellen: Eine für den Papierausdruck, eine für riesige Desktop-Bildschirme, eine für Tablet-Computer, eine für Smartphones usw. Und der Benutzer hat die &#8220;Qual der Wahl&#8221;; kann/darf er nicht alle Formate auswählen und wechselt seine Hardware, hat er eine ungeeignete PDF-Datei.</p>
<p>Diesen Text schreibe ich, weil mich die PDF-Dateien schon immer ärgerten: Auf meinen 27 Zoll-Desktop-Bildschirm mit 2.560 × 1.440 Pixel Auflösung habe ich in der Regel keine Probleme, egal mit welchen PDF-Dateien, auf einem Notebook sieht das anders aus, über Netbooks und Smartphones kann ich nichts aussagen, aber ich wage die Prognose, dass es hier noch problematischer ist.</p>
<p>Ich halte folgende für die Hauptursachen, dass PDF Dateien &#8220;grassieren&#8221;, die hauptsächlich oder ausschließlich am Bildschirm gelesen werden (bei PDF-Dateien kann der Ausdruck gesperrt werden):</p>
<ul>
<li>Die Ersteller kommen aus dem Druckbereich (Print-Sektor) und erstellen PDF-Seiten wie Buchseiten ohne sich intensiv damit beschäftigt zu haben, wie Texte auf Bildschirmen gut zu lesen sind, wie viele unterschiedliche Bildschirme es gibt, sie wissen nicht: Es gibt nur wenig gesicherte Erkenntnisse über das Ausgabemedium im Gegensatz zum Druckbereich, bei dem Papier und Druckmaschinen von vorneherein bekannt sind und wo der Ausdruck vorher über Proofs sichergestellt/optimiert werden kann</li>
<li>Die Seite soll unbedingt im Gesamtbild gut aussehen, überhaupt herrscht noch der ungeeignete Seitenbegriff vor: Computertexte sind eher altägyptische Papyrusrollen und keine zwischen zwei Buchdeckeln gebundene Seiten; Buchseiten sind optimiert für das schnelle Auffinden von Textstellen, Computer können beliebig Sprungmarken setzen und den Text komplett durchsuchen, sodass (nummerierte) Seiten überflüssig sind</li>
<li>Die Seite auf allen Computern gleich aussehen, das im Voraus festgelegte Aussehen darf/soll nicht variieren</li>
<li>Es wird so erledigt, weil es &#8220;Gang und Gäbe&#8221; ist, jeder geht so vor</li>
</ul>
<h3>Lösung: Adaptive Layouts benutzen</h3>
<p>Ich kann nur über Hypertext schreiben: Hier lässt sich über die Cascading Style Sheets (CSS) die Zeilenbreite definieren basierend auf der Buchstabengröße (<strong>em</strong>) und eine Maximalbreite so vorgeben, dass horizontales Scrollen nicht nötig ist (<strong>max-width</strong>). Auch können Bilder mit CSS so skaliert werden, dass sie vollständig dargestellt werden und nicht rechts oder unten abgeschnitten.</p>
<p>Ich gehe davon aus, für E-Book-Reader ist ein ähnliches Format definiert.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.elmar-baumann.de/blog/2012/01/15/bildschirmtaugliche-texte/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Fehlermeldungen</title>
		<link>http://www.elmar-baumann.de/blog/2012/01/09/fehlermeldungen/</link>
		<comments>http://www.elmar-baumann.de/blog/2012/01/09/fehlermeldungen/#comments</comments>
		<pubDate>Mon, 09 Jan 2012 21:15:03 +0000</pubDate>
		<dc:creator>Elmar</dc:creator>
				<category><![CDATA[Programme]]></category>
		<category><![CDATA[Programmierung]]></category>
		<category><![CDATA[Software-Entwicklung]]></category>

		<guid isPermaLink="false">http://www.elmar-baumann.de/blog/?p=1285</guid>
		<description><![CDATA[In diesem Artikel fasse ich zusammen, wie Fehlermeldungen aussehen, wie sie aussehen sollten und wie nicht. Außerdem reiße ich an, wie Programmierer mit Fehlern umgehen können. Der Artikel bezieht sich hauptsächlich auf Programme, die mit dem Benutzer interagieren. Er gilt aber auch für Programme, die ohne Aufsicht (im Hintergrund) ablaufen: Diese können in der Regel [...]]]></description>
			<content:encoded><![CDATA[<p>In diesem Artikel fasse ich zusammen, wie Fehlermeldungen aussehen, wie sie aussehen sollten und wie <em>nicht</em>. Außerdem reiße ich an, wie Programmierer mit Fehlern umgehen können. </p>
<p>Der Artikel bezieht sich hauptsächlich auf Programme, die mit dem Benutzer interagieren. Er gilt aber auch für Programme, die ohne Aufsicht (im Hintergrund) ablaufen: Diese können in der Regel zwar keine Benutzereingaben anfordern, die eine Fehlerbehebung anbieten, jedoch haben sie Fehler in eine Logdatei (Protokolldatei, <strong>Windows-Ereignisanzeige</strong>) zu schreiben <strong>in der gleichen Qualität</strong>.<br />
<span id="more-1285"></span></p>
<h2>Qualitätsabstufungen: Wie melden Computerprogramme Fehler?</h2>
<p>Die Qualität der Fehlermeldungen steigt mit jedem Aufzählungspunkt, den letzten Punkt sollten wir von Programmen einfordern, den vorletzten hat ein Programm <em>mindestens</em> einzuhalten.</p>
<ul>
<li><strong>Keine Fehlermeldung bei Fehlern</strong>: Leider gibt es noch Programme, die Fehler <em>nicht</em> melden und ich habe unter Umständen nachzuschauen, ob das Programm die Arbeit richtig erledigte. Reagierte das Programm nicht wie beabsichtigt und ich habe Glück, schreibt es eine gute Fehlermeldung in eine <strong>Logdatei</strong>. Habe ich Pech, kann ich nur durch Intuition, Versuch und Irrtum das Programm zum Funktionieren überreden.</li>
<li><strong>&quot;Es ist ein unbekannter Fehler aufgetreten&quot;</strong>: Hier weiß ich, etwas lief schief, aber nicht <em>was</em>. Vor zwei Tagen wollte ich Daten auf eine Blu-ray-Disc brennen und das Brennprogramm zeigte genau diesen Text an. Ursache war, dass im gleichen Verzeichnis der gleiche Dateiname einmal mit großem Anfangsbuchstaben stand und einmal mit kleinem (<code>text.txt</code> und <code>Text.txt</code>), was anscheinend die benutzte <a href="http://de.wikipedia.org/wiki/Universal_Disk_Format" title="Wikipedia">UDF</a>-Version nicht erlaubt (prinzipiell ist dies möglich) bzw. das Brennprogramm nicht akzeptiert. Diese Ursache musste ich selbst herausfinden und das war nicht einfach, für ein Computerprogramm hingegen ist dies kein größeres Problem, beispielsweise kann es intern alle Dateinamen eines Verzeichnisses in Kleinbuchstaben umwandeln und zählen wie häufig jeder klein geschriebene vorkommt.</li>
<li><strong>Falsche Fehlermeldung</strong>: Die Fehlermeldung hat <em>nichts</em> zu tun mit der Ursache. Durch (Web-) Recherche habe ich herauszufinden, unter welchen Umständen diese Meldung bislang auftrat und alle Lösungsvorschläge der Reihe nach so lange durchzuführen, bis das Programm richtig läuft.</li>
<li><strong>Fehlernummern</strong>: Statt einer Beschreibung des Fehlers erscheint eine Meldung in der Art: &quot;Es ist ein Fehler aufgetreten. Fehler-Code 0x47110815B2312&quot;. Durch (Web-) Recherche führt der Fehlercode zum tatsächlichen Fehler oder auch nicht. Die misslungenen automatischen Updates von Windows 7 lieferten mir bislang nur solche Fehlercodes, deren Erklärung ich dann auf dem Microsoft Website anschauen musste. Dort waren für den gleichen Code in der Regel mehrere mögliche Ursachen aufgelistet und ich durfte die Lösungsvorschläge je Ursache der Reihe nach durchprobieren. Das kostete sehr viel Zeit und führte selten zum Erfolg.</li>
<li><strong>Richtige Fehlermeldung</strong>: Nur in diesem Fall kann ich den Fehler ohne viel Stress beheben. Voraussetzung ist eine gute Formulierung, beispielsweise: &quot;Die Datei <code>abc.txt</code> kann nicht in das Verzeichnis <code>xyz</code> geschrieben werden, da Sie keine Schreibrechte im Verzeichnis <code>xyz</code> besitzen&quot;.</li>
<li><strong>Richtige Fehlermeldung inklusive Lösungsvorschlag</strong>: Um beim letzten Beispiel zu bleiben, könnte ergänzend erklärt werden, <em>wie</em> ich Schreibrechte erhalte.  Eigentlich sollte eine Schaltfläche &quot;Schreibrechte erlangen&quot; auf der Fehlermeldung existieren <strong>mit guter Usability</strong>, <em>keine</em> kruden Dialoge, bei denen ich mehrfach auf weitere Schaltflächen zu klicken habe, die erneut Dialoge öffnen, wie beispielsweise bei der Rechtevergabe über den Windows-Explorer.</li>
</ul>
<p><strong>Fazit:</strong> Kann das Programm den Fehler nicht beheben, hat es zumindest eine präzise Beschreibung der Ursache zu liefern, damit ich ohne Zeitverschwendung weiß, ob ich etwas tun kann und was ich tun sollte. Kann es den Fehler beheben, sollte die Fehlermeldung eine Schaltfläche enthalten, die dies durchführt ohne dass ich hierbei unnötig viel zu unternehmen habe (gute Usability/Gebrauchstauglichkeit), im besten Fall ist das ein (einziger) Mausklick oder Eintippen eines Buchstabens auf der Kommandozeile.</p>
<p>Wir schreiben das Jahr 2012 und noch immer halte ich diesen Artikel für nötig aufgrund täglichen Herumärgerns mit schlechten Fehlermeldungen. Weshalb sollen viele Benutzer enorme Zeit aufwenden und Fehlerursachen häufig erraten anstelle dass sich wenige Programmierer etwas mehr Zeit nehmen beim Programmieren?</p>
<h2>Programmiertechnik grob angerissen</h2>
<p>Ein Programmierer sollte es als &#8220;Ehrensache&#8221; betrachten, gute Fehlermeldungen zu liefern. Darüber freuen sich nicht nur die Benutzer, auch der Programmierer kann so Bugs schneller beheben.</p>
<p>Oft entstehen Fehler in &#8220;unteren Schichten&#8221;, in Methoden (Funktionen, Prozeduren, Subroutinen) die durch Methoden aufgerufen werden,  die durch Methoden aufgerufen werden usw. Falls die Programmiersprache Ausnahmen (<strong>Exceptions</strong>) werfen kann wie beispielsweise C++ und Java, sollte eine Methode davon bei Bedarf Gebrauch machen.</p>
<p>Ein Fehler, über den ich mich häufig an dieser Stelle ärgerte, ist, der Exception nicht alle verfügbaren Daten zu geben, die helfen können, den Fehler möglichst genau zu identifizieren und zu beheben. Das sind neben dem Wortlaut des Fehlers (Fehlernachricht, <strong>Error Message</strong>) unter anderem die Methodenparameter und die präzise Systemzeit (Millisekunden oder genauer). Ich warf in einem C++-Programm eine Exception, falls beim Öffnen einer Datei das Dateiformat ungültig war, vergaß jedoch den Dateinamen in die Meldung zu schreiben. Da die Datei nicht durch Benutzerauswahl geöffnet wurde, hatte ich bei Fehlern immer viel Arbeit, den Benutzern zu erklären, wo er sie finden kann.</p>
<p>Kann eine Methode eine Exception nicht beheben, reicht sie diese weiter oder wirft eine Exception mit dem Exception-Objekt als Ursache und mit zusätzlichen dienlichen Informationen. Das geht so weiter bis hin in die oberste Schicht, die den Fehler protokolliert und die Fehlermeldung anzeigt.</p>
<p>Die Meldung richtet sich nach der Aufgabe der obersten Schicht, sie lautet beispielsweise &quot;Die Datei <code>Blafasel.txt</code> konnte nicht geöffnet werden!&quot;. In einem Detailabschnitt stehen dann alle Meldungen vorheriger Exceptions wie beispielsweise &quot;Die Datei <code>Blafasel.txt</code> existiert nicht&quot; oder &quot;Im Verzeichnis <code>xyz</code> fehlen Leserechte&quot; oder &quot;Die Datei <code>Blafasel.xml</code> hat ein ungültiges Dateiformat: Zeile 25 hat ein schließendes Tag &lt;/blubb&gt; ohne dass vorher ein öffnendes Tag &lt;blubb&gt; existiert&quot;.</p>
<p>Falls es möglich ist, sollte in der Logdatei der gesamte Callstack stehen, so weiß der Programmierer, von wo aus es im Quellcode zum Fehler ging wo genau er auftrat und kann Bugs manchmal sekundenschnell beheben.</p>
<p>Ich integriere in Fehlermeldungen eine Schaltfläche zum Schicken einer E-Mail, das Benutzen eines Bugtracking-Systems führt garantiert zu <em>viel</em> weniger Feedback, da es wissenden Programmbenutzern eine Menge Arbeit aufbürdet und die unwissenden ausschließt.</p>
<p>Hat eines meiner Programme Bugs, stehe <em>ich</em> in der &#8220;Schuld&#8221;, diese zu beheben und ich finde es unschön, falls der Benutzer neben dem negativen Erlebnis auch noch viel Arbeit hat, Fehler zu melden. In der Regel werden Benutzer diese Arbeit unterlassen, sofern sie für das Programm nicht Geld bezahlten und statt dessen ein anderes benutzen, falls es Alternativen gibt.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.elmar-baumann.de/blog/2012/01/09/fehlermeldungen/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Buchempfehlungen</title>
		<link>http://www.elmar-baumann.de/blog/2011/12/19/buchempfehlungen/</link>
		<comments>http://www.elmar-baumann.de/blog/2011/12/19/buchempfehlungen/#comments</comments>
		<pubDate>Mon, 19 Dec 2011 19:44:09 +0000</pubDate>
		<dc:creator>Elmar</dc:creator>
				<category><![CDATA[Bücher]]></category>

		<guid isPermaLink="false">http://www.elmar-baumann.de/blog/?p=1229</guid>
		<description><![CDATA[Richard Dawkins: Die Schöpfungslüge &#8211; Warum Darwin recht hat, Ullstein, Berlin 2010, ISBN 978-3-550-08765-3 Michael de Ridder: Wie wollen wir sterben? Ein ärztliches Plädoyer für eine neue Sterbekultur in Zeiten der Hochleistungsmedizin,Deutsche Verlags-Anstalt, München 2010, ISBN 978-3-421-04419-8 John Powell: Was Sie schon immer über Musik wissen wollten &#8211; Alles über Harmonien, Rhythmus und das Geheimnis [...]]]></description>
			<content:encoded><![CDATA[<ul>
<li><em>Richard Dawkins</em>: <strong>Die Schöpfungslüge</strong> &#8211; Warum Darwin recht hat, Ullstein, Berlin 2010, ISBN 978-3-550-08765-3</li>
<li><em>Michael de Ridder</em>: <strong>Wie wollen wir sterben?</strong> Ein ärztliches Plädoyer für eine neue Sterbekultur in Zeiten der Hochleistungsmedizin,Deutsche Verlags-Anstalt, München 2010, ISBN 978-3-421-04419-8</li>
<li><em>John Powell</em>:<strong> Was Sie schon immer über Musik wissen wollten</strong> &#8211; Alles über Harmonien, Rhythmus und das Geheimnis einer guten Melodie, Rogner &amp; Bernhard, Berlin 2010, ISBN 978-3-8077-1065-5</li>
<ul>
<p>Siehe auch: &quot;<a href="http://www.elmar-baumann.de/blog/2011/12/19/uber-meine-buchempfehlungen/">Über meine Buchempfehlungen</a>&quot;.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.elmar-baumann.de/blog/2011/12/19/buchempfehlungen/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Über meine Buchempfehlungen</title>
		<link>http://www.elmar-baumann.de/blog/2011/12/19/uber-meine-buchempfehlungen/</link>
		<comments>http://www.elmar-baumann.de/blog/2011/12/19/uber-meine-buchempfehlungen/#comments</comments>
		<pubDate>Mon, 19 Dec 2011 19:42:08 +0000</pubDate>
		<dc:creator>Elmar</dc:creator>
				<category><![CDATA[Bücher]]></category>

		<guid isPermaLink="false">http://www.elmar-baumann.de/blog/?p=1231</guid>
		<description><![CDATA[Empfehle ich Bücher, verzichte ich in der Regel auf Inhaltsbeschreibungen. Diese können Sie auf den Webseiten der Verlage oder Anbieter lesen. Für eine Rezension fehlt mir vermutlich die Zeit. Daher liste ich nur die Autoren, Titel, Verlage, Jahr der Ausgaben und ISBN auf.]]></description>
			<content:encoded><![CDATA[<p>Empfehle ich Bücher, verzichte ich in der Regel auf Inhaltsbeschreibungen. Diese können Sie auf den Webseiten der Verlage oder Anbieter lesen. Für eine Rezension fehlt mir vermutlich die Zeit. Daher liste ich nur die Autoren, Titel, Verlage, Jahr der Ausgaben und ISBN auf.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.elmar-baumann.de/blog/2011/12/19/uber-meine-buchempfehlungen/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ist in ferner Zukunft zu lesen, was ich heute im Web veröffentliche?</title>
		<link>http://www.elmar-baumann.de/blog/2011/11/10/ist-in-ferner-zukunft-zu-lesen-was-ich-heute-im-web-veroeffentliche/</link>
		<comments>http://www.elmar-baumann.de/blog/2011/11/10/ist-in-ferner-zukunft-zu-lesen-was-ich-heute-im-web-veroeffentliche/#comments</comments>
		<pubDate>Thu, 10 Nov 2011 20:30:20 +0000</pubDate>
		<dc:creator>Elmar</dc:creator>
				<category><![CDATA[Websites]]></category>
		<category><![CDATA[Webworking]]></category>

		<guid isPermaLink="false">http://www.elmar-baumann.de/blog/?p=1188</guid>
		<description><![CDATA[Schreibe ich im Folgenden von Daten, verstehe ich darunter Texte, Bilder, Filme – prinzipiell alles, das sich digitalisieren lässt. Kopien Kopien meiner Daten können auf verschiedenen Computern existieren, beispielsweise kann jeder meine Texte auf die Festplatte seines Rechners speichern, Suchmaschinen und Wayback-Maschinen erledigen dies fortlaufend. Im Gegensatz zum eigenen Site, der alles enthält, das ich [...]]]></description>
			<content:encoded><![CDATA[<p>Schreibe ich im Folgenden von <strong>Daten</strong>, verstehe ich darunter Texte, Bilder, Filme – prinzipiell alles, das sich digitalisieren lässt.<br />
<span id="more-1188"></span></p>
<h2>Kopien</h2>
<p>Kopien meiner Daten können auf verschiedenen Computern existieren, beispielsweise kann jeder meine Texte auf die Festplatte seines Rechners speichern, Suchmaschinen und <a href="http://de.wikipedia.org/wiki/Wayback_Machine">Wayback-Maschinen</a> erledigen dies fortlaufend. Im Gegensatz zum eigenen Site, der alles enthält, das ich veröffentliche, können die Daten dort unvollständig sein, Wayback-Maschinen speichern beispielsweise keine Bilder. Vermutlich werden Ausdrucke meiner Texte auf Papier am längsten bestehen.</p>
<h2>Speicherung der Daten</h2>
<p>Speichere ich meine Daten auf einem Server eines <a href="http://de.wikipedia.org/wiki/Internet_Service_Provider">Internet Service Providers</a>, sind sie so lange zu erreichen, wie ich den Provider bezahlen kann oder will, vorausgesetzt, er ist ausreichend kompetent bezüglich der Internetanbindung.</p>
<p>Prinzipiell sollte ich damit rechnen, dass Daten, die ich auf Rechnern von Providern speichere, die ich nicht dafür mit Geld bezahle, von heute auf morgen verschwunden sein können. Facebook, das ich <em>nicht</em> nutze, könnte entscheiden: Dieser Benutzer klickt zu wenig auf Werbung oder zu wenige schauen an, was er schreibt, deshalb wird von ihm nichts mehr veröffentlicht. Es könnte, wie jeder andere Anbieter, der das Geld zum Betrieb seiner Webserver aus anderen Quellen bezieht, aus Geldmangel sein Angebot einschränken, zukünftig Geld dafür verlangen oder es einstellen.</p>
<h2>Sicherung (Backup)</h2>
<p>Was ich nicht auf meine privaten physikalischen Datenträger gespeichert habe, entzieht sich meiner Kontrolle. Ist eine <a href="http://de.wikipedia.org/wiki/Cloud_Computing">Cloud</a>, deren Hardware mir nicht zugänglich ist, die ich nicht besitze, der einzige Speicherort meiner digitalen Daten, sollte ich damit rechnen, dass diese unwiederbringlich verloren gehen können, wie das beispielsweise bei Amazon <a href="http://www.heise.de/newsticker/meldung/Wolkenbruch-bei-Amazon-Datenverlust-in-der-Cloud-1234444.html">geschah</a>.</p>
<p>Meine statischen HTML-Seiten schreibe ich ohnehin auf meinen lokalen Computer, was in Datenbanken auf Webservern gespeichert wird, wie dieser Blog-Artikel, lasse ich <strong>automatisiert</strong> auf meine Rechner übertragen, das tägliche Backup sichert die übertragenen Daten zusätzlich.</p>
<p>Die <strong>automatisierte</strong> Sicherung von Datenbankinhalten auf lokale Rechner und von dort auf Backupmedien dürfte weniger technikaffinen vermutlich zu kompliziert sein.</p>
<p>Ein Backup, das sich automatisiert auf eigene lokale Datenspeicher übertragen lässt, wird vermutlich eher selten von Anbietern wie Facebook zu erwarten sein. Falls doch, stelle ich mir die Frage: Ist das Backup in einem Format, das anderswo – nicht bei diesem Anbieter – zugänglich wiederhergestellt werden kann? Dazu kämen eigentlich nur (X-) HTML infrage sowie XML inklusive relativer Verlinkung, halbwegs akzeptabel wäre ein Format, das sich so konvertieren lässt, dass es verbreitete <a href="http://de.wikipedia.org/wiki/Content-Management-System">Content Management</a>-Systeme wie <a href="http://typo3.org/">TYPO3</a> oder <a href="http://wordpress.org/">WordPress</a> nutzen können.</p>
<h2>Wiederherstellung (Restore)</h2>
<p>Habe ich meine Daten gesichert, bereitet die Wiederherstellung von HTML-Dateien wenig Probleme. Komplizierter ist das Wiederherstellen dynamischer Inhalte von Content Management-Systemen oder <a href="http://de.wikipedia.org/wiki/Blog">Blogs</a> und <a href="http://de.wikipedia.org/wiki/Wiki">Wikis</a>, wobei sich darüber streiten lässt, ob Blogs und Wikis nicht Content Management-Systeme sind, wohin ich tendiere, jemand der Spezialist auf diesem Gebiet ist, sieht das vielleicht anders. Die Datenbank einschließlich Inhalt sollte sich mit wenig Aufwand wiederherstellen lassen.</p>
<p>Was ist jedoch, falls das Programm, das diese Inhalte an den Webserver liefert – TYPO3, WordPress, <a href="http://www.mediawiki.org/wiki/MediaWiki/de?uselang=de">MediaWiki</a>, &#8230;– ein anderes Datenbankformat erwartet – zusätzliche/andere Tabellen/Spalten, falls es eine relationale Datenbank nutzt? Ich glaube nicht, dass eine WordPress-Version in 10 Jahren die heutige Datenbank dieses Blogs benutzen kann. Die Konvertierung in ein neues Format funktioniert bei vermutlich allen Content Management-Systemen nur über ein paar Versionen hinweg. Anders ausgedrückt: Ich sollte fortlaufend Updates durchführen, das hat WordPress von den mir bekannten Programmen am besten gelöst.</p>
<h2>Programme, die dynamisch Inhalte an den Webserver liefern</h2>
<p>Ich könnte auf die Idee kommen, stets die gleiche Programmversion zu benutzen, damit ich die Datenbank nicht anpassen muss. Abgesehen davon, dass dies wenig zu empfehlen ist, da praktisch jedes Programm Sicherheitslücken hat, die in den neuen Versionen behoben werden, kann folgendes geschehen: Das Programm läuft nicht mehr, zeigt nichts mehr an. Beispiel: Auf dem Webserver wird eine neuere PHP-Version installiert – damit laufen viele Programme wie WordPress, TYPO3 oder das MediaWiki. Je nach Programm oder Programmfunktion wird nun mit der neuen PHP-Version nichts mehr angezeigt oder nur Teile des Inhalts. Auch könnte die Schnittstelle zur Datenbank sich ändern, damit käme das alte Programm nicht mehr zurecht.</p>
<p>Einer der Gründe, weshalb Programme, die ich heute schreibe, morgen nicht mehr funktionieren könnten, ist die stetige Weiterentwicklung von Programmiersprachen, Programmbibliotheken und Betriebssystemen – den Wirten der Programme: Meine Programme rufen Systemfunktionen auf, die morgen vielleicht nicht mehr existieren und wie alle umfangreicheren Programme nutzen sie Bibliotheken, Module, DLLs, Klassen etc.. Diese könnten zukünftig nicht mehr zur Verfügung stehen, nicht mehr alle Funktionen anbieten oder sich anders verhalten.</p>
<p>Deshalb sind statische HTML-Seiten – HTML-Dateien – am langzeitstabilsten, aber sie sind auch mühseliger zu erstellen als ein Blogartikel.</p>
<h2>Anzeigeprogramme</h2>
<p>Unter einem Anzeigeprogramm verstehe ich in diesem Zusammenhang beispielsweise den <a href="http://de.wikipedia.org/wiki/Webbrowser">Webbrowser</a>, in dem Sie vermutlich diesen Artikel lesen. Es gibt andere Möglichkeiten, den Text zu lesen, diese dürften eher selten in die Praxis umgesetzt werden, beispielsweise Herunterladen mit <a href="http://www.gnu.org/software/wget/">wget</a> und Öffnen der heruntergeladenen HTML-Datei mit <a href="http://www.libreoffice.org/">LibreOffice</a>, &#8230; Das Anzeigeprogramm kommuniziert auf bestimmte Weise mit einem <a href="http://de.wikipedia.org/wiki/Webserver">Webserver</a>, der die Daten auf bestimmte Weise ausliefert.</p>
<p>Der genaue Ablauf wird sich nicht so schnell ändern, aber falls das geschieht, falls das Internet beispielsweise ein anderes <a href="http://de.wikipedia.org/wiki/Netzwerkprotokoll">Protokoll</a> als <a href="http://de.wikipedia.org/wiki/TCP/IP">TCP/IP</a> benutzt und die Kommunikation zwischen den verbundenen Computern anders gelöst ist, dürfte gelten: Statische HTML-Seiten werden auch dann einfach auszuliefern sein; ohne Hinzutun werden dynamische Inhalte wie dieser Blog-Artikel wohl nicht mehr abzurufen sein.</p>
<p>Weitere Probleme bereiten <a href="http://de.wikipedia.org/wiki/Propriet%C3%A4r">proprietäre</a> Formate wie beispielsweise <a href="http://de.wikipedia.org/wiki/Adobe_Flash">Flash</a> anstelle <a href="http://de.wikipedia.org/wiki/HTML">HTML</a>: Diese können eher nicht mehr in Gebrauch sein, als man denkt, beispielsweise <a href="http://www.heise.de/newsticker/meldung/Adobe-stellt-mobiles-Flash-Plugin-ein-1375503.html">will Adobe Flash nicht mehr weiterentwickeln</a>, das <em>mobile</em> Plugin ist nur exemplarisch, es ist eher wahrscheinlich, dass es komplett nicht mehr weiterentwickelt wird.</p>
<p>Kurz: Was ich nicht als Text im (X-) HTML-Format veröffentliche, ist weniger zukunftssicher, Bilder gängiger Formate wie <a href="http://de.wikipedia.org/wiki/Portable_Network_Graphics">PNG</a> oder <a href="http://de.wikipedia.org/wiki/JPEG">JPEG</a> sollten auch in fernerer Zukunft von Computerprogrammen dargestellt werden können.</p>
<h2>Zusammenfassung</h2>
<p>Will ich mich lange im World Wide Web präsentieren, erreiche ich das am besten so:</p>
<ul>
<li>Ich veröffentliche die Inhalte bei Internet Service Providern, die ich dafür bezahle und die mich den Webserver ausreichend steuern lassen. Nur so habe ich Kontrolle darüber, was genau wie lange unter einem <a href="http://de.wikipedia.org/wiki/Uniform_Resource_Identifier">URI</a> abgerufen werden kann; ich setze voraus, ich bin der Inhaber der Domain des URIs.</li>
<li>Alle Inhalte sind auch lokal bei mir gespeichert, sodass ich diese bei Providerwechsel ohne größeren Aufwand übertragen kann.</li>
<li>Ich weiß: Statische HTML-Seiten – HTML-Dateien – sind am langzeitstabilsten</li>
<li>Dynamische Seiten:
<ul>
<li>Die dynamischen (Datenbank-) Inhalte lasse ich <strong>automatisiert</strong> lokal sichern</li>
<li>Ich aktualisiere permanent die den Inhalt generierenden Programme – Content Management-Systeme, Blogsoftware etc. – inklusive deren Umgebung – Module, Bibliotheken etc.</li>
<li>Mit dem Update der Programme aktualisiere ich das Format der Inhalte – die Datenbank, falls erforderlich, damit die neuen Programmversionen den Inhalt interpretieren können</li>
</ul>
</li>
</ul>
<h2>Wer will meine Texte zukünftig lesen?</h2>
<p>Ich habe nur die Oberfläche angekratzt, Datenpersistenz ist nicht trivial, Computer und Programme sind komplex. Trotzdem bin ich nicht beunruhigt: Wer will morgen lesen, was ich heute schreibe? Es ist nicht einfach, die permanent wachsende Datenmenge zu filtern und auszuwerten, da bleibt kaum Zeit, in Altem zu stöbern. Vielleicht präsentiert eine Suchmaschine älteres erst so nachrangig, dass es ohnehin niemand mehr findet.</p>
<p>Die Frage, welche meiner Texte morgen zu lesen sein sollen, ist mir zu philosophisch. Ich gehe nur kurz auf technische Themen ein, unter die ich die überwiegende Mehrzahl meiner Texte einordne.</p>
<p>Texte über Technik werden wohl nur so lange interessant sein, wie die Technik gebräuchlich ist. Die Eigenheiten eines fotografischen Films dürften die wenigsten interessieren, da praktisch jeder mit Digitalkameras fotografiert, wie ich im <a href="http://www.elmar-baumann.de/fotografie/lexikon/raw-konverter.html">RAW-Konverter</a> der Version 1 etwas bewerkstellige, ist für mich uninteressant, wenn ich die Version 3 benutze und dort anders vorzugehen ist.</p>
<p>Ich sollte mich eher fragen: Welche Texte lösche ich oder kennzeichne diese als veraltet, sodass sie weniger stören.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.elmar-baumann.de/blog/2011/11/10/ist-in-ferner-zukunft-zu-lesen-was-ich-heute-im-web-veroeffentliche/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Das NetBeans-Lookup für Nicht-RCP-Projekte benutzen</title>
		<link>http://www.elmar-baumann.de/blog/2011/09/26/das-netbeans-lookup-fur-nicht-rcp-projekte-benutzen/</link>
		<comments>http://www.elmar-baumann.de/blog/2011/09/26/das-netbeans-lookup-fur-nicht-rcp-projekte-benutzen/#comments</comments>
		<pubDate>Mon, 26 Sep 2011 17:35:53 +0000</pubDate>
		<dc:creator>Elmar</dc:creator>
				<category><![CDATA[Programmierung]]></category>
		<category><![CDATA[Software-Entwicklung]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Netbeans]]></category>

		<guid isPermaLink="false">http://www.elmar-baumann.de/blog/?p=1128</guid>
		<description><![CDATA[Im Artikel Java-Aktionen richtig benutzen überlegte ich, wie Java-Actions ihre Daten erhalten und sich aktivieren oder deaktivieren abhängig davon, ob es relevante Daten gibt. Ich wollte nicht Swing-Components nach Daten befragen, die Actions dort als Listener registrieren und auf Statusänderungen des GUI reagieren. Als Lösung schlug ich Lookups vor &#8211; Container mit Elementen, die Beobachter [...]]]></description>
			<content:encoded><![CDATA[<p>Im Artikel <a href="http://www.elmar-baumann.de/blog/2010/09/18/java-aktionen-richtig-benutzen/" title="Artikel">Java-Aktionen richtig benutzen</a> überlegte ich, wie Java-<code>Action</code>s ihre Daten erhalten und sich aktivieren oder deaktivieren abhängig davon, ob es relevante Daten gibt. Ich wollte <em>nicht</em> Swing-<code>Component</code>s nach Daten befragen, die Actions dort als Listener registrieren und auf Statusänderungen des GUI reagieren. Als Lösung schlug ich <strong>Lookups</strong> vor &ndash; Container mit Elementen, die Beobachter benachrichtigen, falls sich ihr Inhalt ändert. </p>
<p>Ein solches Lookup benutzen <a href="http://netbeans.org/features/platform/">NetBeans Platform</a>-Anwendungen, es kann in <em>Nicht</em>-Platform-Anwendungen eingesetzt werden. Die Projekte integrieren dazu das JAR <code>org-openide-util-lookup.jar</code>, es ist unterhalb des NetBeans-Installationsverzeichnisses im Verzeichnis <code>platform/lib</code>.<br />
<span id="more-1128"></span></p>
<h2>Zugrunde liegende Idee</h2>
<ul>
<li>Ein Anwendungsmodul hat einen oder mehrere Lookups</li>
<li>Wird im GUI etwas ausgewählt oder abgewählt, gelangen relevante Daten, die das GUI-Element repräsentiert, in eines der Lookups</li>
<li>Die Actions beobachten das Lookup: Ist es leer, sind sie deaktiviert, enthält es Elemente, sind sie aktiviert. In <code>actionPerformed()</code> benutzen Actions die Elemente des Lookups.</li>
</ul>
<p>Das NetBeans-Lookup benachrichtigt Beobachter nur, falls Daten eine bestimmten Typs im Lookup sind: Eine Action, die Personen in eine Datenbank speichert, will in der Regel nicht wissen, ob und wieviele Bücher im Lookup sind.</p>
<h2>Beispiels-Implementierung</h2>
<p>Ich habe ein Beispiels-Projekt implementiert und in ein <a href="/blog/wp-content/uploads/LookupTest.zip">ZIP-Archiv</a> gepackt. Nach dem Entpacken kann es mit NetBeans geöffnet werden. Das Kontextemenü des <strong>Projects</strong>-Window bietet <strong>Run</strong> oder <strong>Debug</strong> an (auf die Klasse <code>Main</code> mit der rechten Maustaste klicken).</p>
<p><em>(Nur) Der Übersichtlichkeit wegen verzichtete ich bei den Beispielen auf Parameterüberprüfung, in &#8220;Produktions-Code&#8221; sollten die Parameter überprüft werden, beispielsweise eine NullPointerException geworfen, falls null überreicht wird anstelle einer erwarteten Objektreferenz.</em></p>
<h3>LookupAction</h3>
<p>Die <code>LookupAction</code> habe ich kopiert aus dem Projekt <a href="http://jphototagger.org">JPhotoTagger</a>. <code>LookupAction</code>s beobachten einen Lookup. Bei Aufruf von <code>actionPerformed()</code> ruft die <code>LookupAction</code> bei den spezialisierten Actions die abstrakte Methode <code>actionPerformed(Collection&lt;? extends T&gt; lookupContent)</code> auf, mit <code>isEnabled(Collection&lt;? extends T&gt; lookupContent)</code> können sie entscheiden, ob sie aktiviert sein sollen, in der Regel werden sie <code>true</code> liefern, falls der Lookup-Content Elemente enthält.</p>
<pre>
public abstract class LookupAction&lt;T&gt; extends AbstractAction implements LookupListener {

    private final Class&lt;? extends T&gt; lookupResultClass;
    private final Lookup lookup;
    private Lookup.Result&lt;? extends T&gt; lookupResult;

    protected LookupAction(Class&lt;? extends T&gt; lookupResultClass, Lookup lookup) {
        this.lookupResultClass = lookupResultClass;
        this.lookup = lookup;
        setLookupResult();
    }

    protected abstract boolean isEnabled(Collection&lt;? extends T&gt; lookupContent);

    protected abstract void actionPerformed(Collection&lt;? extends T&gt; lookupContent);

    private void setLookupResult() {
        if (lookupResult == null) {
            lookupResult = lookup.lookupResult(lookupResultClass);
            lookupResult.addLookupListener(this);
            resultChanged(null);
        }
    }

    @Override
    public void resultChanged(LookupEvent evt) {
        setEnabledInDispatchThread();
    }

    private void setEnabledInDispatchThread() {
        final boolean isEnabled = isEnabled(lookupResult.allInstances());

        if (EventQueue.isDispatchThread()) {
            setEnabled(isEnabled);
        } else {
            EventQueue.invokeLater(new Runnable() {

                @Override
                public void run() {
                    setEnabled(isEnabled);
                }
            });
        }
    }

    protected Collection&lt;? extends T&gt; getLookupContent() {
        return (lookupResult == null)
                ? Collections.&lt;T&gt;emptyList()
                : lookupResult.allInstances();
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        setLookupResult();
        actionPerformed(lookupResult.allInstances());
    }
}
</pre>
<h3>Eigenes Lookup</h3>
<p>Die Klasse <code>ModifiableLookup</code> liefert ein Lookup und bietet Methoden an, dessen Inhalt zu modifizieren.</p>
<pre>
public final class ModifiableLookup implements Lookup.Provider {

    private final InstanceContent content = new InstanceContent();
    private final Lookup lookup = new AbstractLookup(content);

    @Override
    public Lookup getLookup() {
        return lookup;
    }

    public void add(Object content) {
        this.content.add(content);
    }

    public void remove(Object content) {
        this.content.remove(content);
    }

    public void set(Collection&lt;?&gt; content) {
        this.content.set(content, null);
    }
}
</pre>
<h3>Beispiel: Ausgewählte Listenelemente in das Lookup einfügen</h3>
<p>Will ich ausgewählte Items einer <code>JList</code> in das Lookup einfügen, kann ich folgende Klasse benutzen: </p>
<pre>
public final class LookupListSelectionListener implements ListSelectionListener {

    private final ModifiableLookup lookup;

    public LookupListSelectionListener(ModifiableLookup lookup) {
        this.lookup = lookup;
    }

    @Override
    public void valueChanged(ListSelectionEvent e) {
        if (!e.getValueIsAdjusting()) {
            JList list = (JList) e.getSource();
            List&lt;Object&gt; selectedValues = Arrays.asList(list.getSelectedValues());
            lookup.set(selectedValues);
        }
    }
}
</pre>
<h3>Beispielsklassen, die in das Lookup sollen</h3>
<pre>
public final class Human {

    private final String name;

    public Human(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return name;
    }
}
</pre>
<pre>
public class Dog {

    private final String name;

    public Dog(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return name + " (Wuff)";
    }
}
</pre>
<pre>
public final class CreatureListModel extends DefaultListModel {

    private static final long serialVersionUID = 1L;

    public CreatureListModel() {
        addElement(new Human("Anton"));
        addElement(new Human("Berta"));
        addElement(new Human("Cäsar"));
        addElement(new Human("Dora"));
        addElement(new Human("Emil"));
        addElement(new Dog("Fiffi"));
        addElement(new Dog("Waldi"));
    }
}
</pre>
<h3>Kommunikation über ein zentrales Lookup</h3>
<p>Ein Anwendungsmodul kann Lookups beispielsweise so bereitstellen:</p>
<pre>
public final class Lookups {

    public static final Lookups INSTANCE = new Lookups();
    private final ModifiableLookup humansLookup = new ModifiableLookup();

    public ModifiableLookup getHumansLookup() {
        return humansLookup;
    }

    private Lookups() {
    }
}
</pre>
<h3>Erweiterte LookupAction</h3>
<p>Die erweiterte <code>LookupAction</code> interessiert sich (nur) für Instanzen von <code>Human</code></p>
<pre>
public final class ShowHumansInLookupAction extends LookupAction&lt;Human&gt; {

    private static final long serialVersionUID = 1L;

    public ShowHumansInLookupAction() {
        super(Human.class, Lookups.INSTANCE.getHumansLookup().getLookup());
        putValue(Action.NAME, "Zeige ausgewählte Menschen");
    }

    @Override
    protected boolean isEnabled(Collection&lt;? extends Human&gt; humans) {
        return !humans.isEmpty();
    }

    @Override
    protected void actionPerformed(final Collection&lt;? extends Human&gt; humans) {
        JOptionPane.showMessageDialog(null, humans);
    }
}
</pre>
<h3>In einem GUI benutzen</h3>
<p>Die entscheidenden Zeilen in einem GUI, die Action ist einem Button zugeordnet, Model und ListSelectionListener der Liste:</p>
<pre>
    list.setModel(new CreatureListModel());
    list.addListSelectionListener(new LookupListSelectionListener(Lookups.INSTANCE.getHumansLookup()));
    button.setAction(new ShowHumansInLookupAction());
</pre>
<h3>Worauf achten beim Ausführen der Beispielsanwendung</h3>
<ul>
<li>Ist nicht ausgewählt, setzt sich die Action automatisch auf deaktiviert, der Button mit der Action ist deaktiviert (&#8220;ausgegraut&#8221;)</li>
<li>Werden ein oder mehrere Menschen ausgewählt, ist die Action aktiviert, der Button reagiert auf Klicks (Betätigung)</li>
<li>Sind nur Hunde ausgewählt, ist der Button deaktiviert</li>
<li>Die Action erhält nur Menschen: Sind Hunde und Menschen ausgewählt, zeigt bei Klick auf den Button die Action den Lookup-Content an &ndash; es sind nur die erwarteten Objekte darin</li>
</ul>
<h2>Vorteile</h2>
<ul>
<li>Die Action ist <em>nicht</em> an ein GUI gebunden (sie benötigt nur ein Lookup). Sie ist nicht anzupassen, falls später beispielsweise ein <code>JTree</code> die Geschöpfe anzeigt (getrennt nach Mensch und Tier). Es könnte auch ein Dialog zum Ändern der Daten eines Menschen benutzt werden: Der &#8220;Mensch im Dialog&#8221; gelangt in das Lookup, das die Action benutzt.</li>
<li>Die Action <strong>fragt nicht nach den benötigten Daten</strong>, sie erhält sie</li>
<li>Die Action aktiviert und deaktiviert sich automatisch</li>
<li>Das Lookup könnte über Modulgrenzen hinweg benutzt werden, beispielsweise über das Java-<strong>Service Provider Interface</strong> (SPI) oder ein globales Lookup, wie es die NetBeans-Platform anbietet. Die Actions anderer Module sind nur mit dem passenden Lookup zu verknüpfen, sie müssen nicht wissen, woher die Daten kommen (wie sie an diese gelangen).</li>
<li>Actions sind nur ein Beispiel, es können beliebige Klassen <code>LookupListener</code> sein</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.elmar-baumann.de/blog/2011/09/26/das-netbeans-lookup-fur-nicht-rcp-projekte-benutzen/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Java: Laufzeitausnahmen anzeigen &#8211; Variante</title>
		<link>http://www.elmar-baumann.de/blog/2011/09/21/java-laufzeitausnahmen-anzeigen-variante/</link>
		<comments>http://www.elmar-baumann.de/blog/2011/09/21/java-laufzeitausnahmen-anzeigen-variante/#comments</comments>
		<pubDate>Wed, 21 Sep 2011 19:47:04 +0000</pubDate>
		<dc:creator>Elmar</dc:creator>
				<category><![CDATA[Programmierung]]></category>
		<category><![CDATA[Software-Entwicklung]]></category>
		<category><![CDATA[Exceptions]]></category>
		<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.elmar-baumann.de/blog/?p=1098</guid>
		<description><![CDATA[Im Artikel &#8220;Java: Laufzeitausnahmen anzeigen&#8221; steht, wie ich im AWT-EventDispatchThread nicht gefangene Ausnahmen mit einem Dialog anzeige. Inzwischen verfolge ich eine weniger &#8220;aufdringliche&#8221; Variante, die nicht von der Arbeit ablenkt oder das Schließen des Programmfensters verhindert, falls im EventDispatchThread laufend neue Ausnahmen geworfen und nicht gefangen werden: Die Ersatz-EventQueue benutzt das Logging-System und loggt die [...]]]></description>
			<content:encoded><![CDATA[<p>Im Artikel &#8220;<a href="/blog/2010/04/02/java-laufzeitausnahmen-anzeigen/" title="">Java: Laufzeitausnahmen anzeigen</a>&#8221; steht, wie ich im <strong>AWT-EventDispatchThread</strong> nicht gefangene Ausnahmen mit einem Dialog anzeige. Inzwischen verfolge ich eine weniger &#8220;aufdringliche&#8221; Variante, die nicht von der Arbeit ablenkt oder das Schließen des Programmfensters verhindert, falls im <code>EventDispatchThread</code> laufend neue Ausnahmen geworfen und nicht gefangen werden:<br />
<span id="more-1098"></span></p>
<ul>
<li>Die Ersatz-<code>EventQueue</code> benutzt das Logging-System und loggt die Ausnahme mit dem Level <code>SEVERE</code></li>
<li>Ein <code>Handler</code> &ndash; ein erweiterter <code>java.util.logging.Handler</code> &ndash; reagiert auf bestimmte Loglevel, zeigt beispielsweise einen &#8220;Fehlerindikator&#8221; an, bei Klick darauf erhält der Benutzer nähere Informationen über den Fehler</li>
</ul>
<h2>Modifizierte EventQueue</h2>
<pre>
public final class AppEventQueue extends java.awt.EventQueue {

    @Override
    protected void dispatchEvent(AWTEvent event) {
        try {
            super.dispatchEvent(event);
        } catch (Throwable t) {
            Logger.getLogger(AppEventQueue.class.getName()).log(Level.SEVERE, null, t);
        }
    }
}
</pre>
<h2>Handler</h2>
<pre>
public final class ErrorLogHandler extends Handler {

    private static final int MIN_LOG_LEVEL_VALUE = Level.WARNING.intValue();

    public ErrorLogHandler() {
        Logger.getLogger("").addHandler(this);
    }

    @Override
    public void publish(LogRecord record) {
        int recordLevelValue = record.getLevel().intValue();
        boolean isError = recordLevelValue &gt;= MIN_LOG_LEVEL_VALUE;

        if (isError) {
            // Hier den Fehler visualisieren
        }
    }

    @Override
    public void flush() {
        // ignorieren
    }

    @Override
    public void close() throws SecurityException {
        // ignorieren
    }
}
</pre>
<p>Der Handler fügt sich dem <strong>root Logger</strong> hinzu, der <strong>root Logger</strong> ruft die Handler-Methode <code>publish()</code> auf. Jeden Loglevel ab <code>WARNING</code> betrachtet der Handler als Fehler (<code>isError</code>). Bei Fehlern kann er beispielsweise über die <code>setIcon()</code>-Methode eines Labels ein Fehler-Icon setzen und bei Klick auf das Label die Fehlerinformationen anzeigen, beispielsweise anhand Fehler-<code>LogRecord</code>s, die er temporär speichert.</p>
<h2>EventQueue ersetzen</h2>
<p>Die <code>EventQueue</code> ersetze ich &#8220;rechtzeitig&#8221; durch folgende Codezeile (im Projekt <a href="http://jphototagger.org/">JPhotoTagger</a> unmittelbar nach Erzeugen des Programmfenster-Frames):</p>
<pre>
Toolkit.getDefaultToolkit().getSystemEventQueue().push(new AppEventQueue());
</pre>
<p>Die folgenden Abbildungen zeigen die Auswirkungen: In <a href="http://netbeans.org">NetBeans</a> setzte ich einen Haltepunkt in die Zeile 127 der Klasse <code>AppInit</code>, Methode <code>setJptEventQueue()</code>, das ist die Zeile mit dem <code>push()</code>-Afuruf. Gelb unterlegt ist der <strong>Thread AWT-EventQueue-0</strong>. Nach Aufruf ist zu sehen, dass nun <strong>Thread AWT-EventQueue-1</strong> läuft, die Namen lassen sich nicht ändern, es wird nur die Nummer hochgezählt.</p>
<p><a href="http://www.elmar-baumann.de/blog/wp-content/uploads/jpt-eventqueue-01.png"><img src="http://www.elmar-baumann.de/blog/wp-content/uploads/jpt-eventqueue-01.png" alt="" width="312" height="224" class="alignnone size-full wp-image-1112" /></a><br />JPhotoTagger: AWT-EventQueue ersetzen, Debugger-Ansicht vor Ersetzen</p>
<p><a href="http://www.elmar-baumann.de/blog/wp-content/uploads/jpt-eventqueue-02.png"><img src="http://www.elmar-baumann.de/blog/wp-content/uploads/jpt-eventqueue-02.png" alt="" width="312" height="133" class="alignnone size-full wp-image-1113" /></a><br />JPhotoTagger: AWT-EventQueue ersetzen, Debugger-Ansicht nach Ersetzen.</p>
<p><a href="http://www.elmar-baumann.de/blog/wp-content/uploads/jpt-error-handler.png"><img src="http://www.elmar-baumann.de/blog/wp-content/uploads/jpt-error-handler.png" alt="" width="318" height="68" class="alignnone size-full wp-image-1118" /></a><br />Fehleranzeige durch einen Log-Handler in JPhotoTagger am unteren Programmfensterrand.</p>
<h2>Außerhalb der AWT-EventQueue</h2>
<p>In anderen Threads kann ich nicht gefangene Ausnahmen loggen mit <code>java.lang.Thread#setDefaultUncaughtExceptionHandler()</code> &ndash; der Handler wird <em>nicht</em> aufgerufen im <strong>AWT-Event Dispatch Thread</strong>. Beispiel:</p>
<pre>
public final class UncaughtExceptionLogger implements Thread.UncaughtExceptionHandler {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
         Logger.getLogger(UncaughtExceptionLogger.class.getName()).log(Level.SEVERE, null, e);
    }
}
</pre>
<p>Benutzung:</p>
<pre>
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionLogger());
</pre>
<h2>Zusammenfassung (mit Erweiterung um FileHandler)</h2>
<ul>
<li>Nicht gefangene Ausnahmen sollen dem Benutzer unaufdringlich angezeigt werden und in einer Logdatei stehen</li>
<li>Ein erweiterter <code>java.util.logging.Handler</code> <strong>zeigt dem Benutzer Probleme an</strong> ab dem Loglevel <code>WARNING</code>, ein <code>java.util.logging.FileHandler</code> <strong>schreibt die LogRecords in eine Logdatei</strong>. Diese Handler füge ich dem <strong>root Logger</strong> hinzu.</li>
<li>Je ein Logger <strong>loggt ungefangene Ausnahmen</strong> mit dem Level <code>SEVERE</code>
<ul>
<li>im <strong>AWT-EventDispatchThread</strong> in einer eigenen <code>EventQueue</code>, die die &#8220;Standard&#8221;-<code>EventQueue</code> ersetzt</li>
<li>in (allen) <strong>anderen Threads</strong> in einem <code>java.lang.Thread.UncaughtExceptionHandler</code>, den ich setze mit <code>Thread#setDefaultUncaughtExceptionHandler()</code></li>
</ul>
</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.elmar-baumann.de/blog/2011/09/21/java-laufzeitausnahmen-anzeigen-variante/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Quellcode auf Webseiten</title>
		<link>http://www.elmar-baumann.de/blog/2011/09/20/quellcode-auf-webseiten/</link>
		<comments>http://www.elmar-baumann.de/blog/2011/09/20/quellcode-auf-webseiten/#comments</comments>
		<pubDate>Tue, 20 Sep 2011 19:46:10 +0000</pubDate>
		<dc:creator>Elmar</dc:creator>
				<category><![CDATA[Websites]]></category>
		<category><![CDATA[Webworking]]></category>
		<category><![CDATA[Layout]]></category>

		<guid isPermaLink="false">http://www.elmar-baumann.de/blog/?p=1075</guid>
		<description><![CDATA[Die Breite für Texte auf einer Webseite sollte beschränkt sein auf etwa 60 bis 70 Zeichen. Längere Zeilen sind schwieriger zu lesen: Der Beginn von Folgezeilen ist schwieriger zu finden. Quellcode von Computerprogrammen hat oft mehr Zeichen pro Zeile. In der Regel wird er eingebettet in &#60;pre&#62;-Blöcke, so bleibt seine Formatierung erhalten. Sollen Fließtext und [...]]]></description>
			<content:encoded><![CDATA[<p>Die Breite für Texte auf einer Webseite sollte beschränkt sein auf etwa 60 bis 70 Zeichen. Längere Zeilen sind schwieriger zu lesen: Der Beginn von Folgezeilen ist schwieriger zu finden. Quellcode von Computerprogrammen hat oft mehr Zeichen pro Zeile. In der Regel wird er eingebettet in <code>&lt;pre&gt;</code>-Blöcke, so bleibt seine Formatierung erhalten. Sollen Fließtext und Quellcode auf Webseiten gemischt werden und die Darstellungsfläche des Quellcodes soll nicht breiter werden als jene des Texts, geschieht das nach meinen Erfahrungen meistens auf folgende ungünstige Weisen:<br />
<span id="more-1075"></span></p>
<ul>
<li>Der Code ist in einer Art &#8220;Unterfenster&#8221;, verschiebe ich eine horizontale Scrollbar (Scroll-Leiste), sehe ich das Ende längerer Zeilen</li>
<li>Der Code wird rechts abgeschnitten</li>
</ul>
<p>Die horizontale Scrollbar hat mindestens folgende &#8220;gravierende&#8221; Nachteile:</p>
<ul>
<li>Ich kann nicht den gesamten Code auf einmal sehen, schiebe ich die Scrollbar nach rechts, ist der Code links abgeschnitten</li>
<li>Ich kann nicht wie gewohnt navigieren, bin gezwungen mit der Maus extra eine Scrollbar anzusteuern, will ich anschließend den Text lesen, muss ich wieder anders vorgehen. Ich bin es gewohnt, nach dem Laden einer Webseite diese &#8220;bequem&#8221; mit der Leertaste so lange nach unten zu blättern, wie sie mich interessiert, das geht so nicht, ich muss ständig zur Maus greifen und diese hin- und herfahren.</li>
</ul>
<p>Manchmal ist außer der horizontalen Scrollbar zusätzlich eine vertikale vorhanden, ich kann dann den Quellcode auch nach unten nicht vollständig überblicken.</p>
<p>Der Code wird rechts abgeschnitten, falls dem Ersteller des Cascading Stylesheets (CSS) Ästhetik wichtiger ist als der Inhalt (<code>overflow: hidden;</code>). Das ist noch weniger akzeptabel wie die Scrollbars.</p>
<p>Ich will nicht gezwungen sein, Text zu markieren, in die Zwischenablage zu kopieren, einen Texteditor zu öffnen und den Code aus der Zwischenablage in den Texteditor einzufügen: So kann ich durch <code>overflow: hidden</code> versteckten Text hervorholen oder den Inhalt von &#8220;Unterfenstern&#8221; in eine größere Darstellungsfläche kopieren.</p>
<p>Folgende für mich praktikable Lösungen verhindern das:</p>
<ul>
<li>Der Code steht <em>nicht</em> in <code>&lt;pre&gt;</code>-Blöcken, Zeilenumbrüche werden mit <code>&lt;br /&gt;</code> erzielt, Einrückungen mit <code>&amp;nbsp;</code> (im normalen Textfluss würden andernfalls mehrere aufeinanderfolgende Leerzeichen auf eines reduziert)</li>
<li>Das Seitenlayout hat nur zwei Spalten: Eine für die &#8220;Navigation&#8221; und eine für den Text. Die Textspalte ist rechts und versteckt <em>nicht</em> überlaufenden Text. Der Code steht in <code>&lt;pre&gt;</code>-Blöcken.</li>
</ul>
<p>Letzere Lösung &ndash; 2 Spalten, überlaufender Text wird nicht versteckt, Code in <code>&lt;pre&gt;</code>-Blöcken &ndash; halte ich für die bessere: Bei der ersten Lösung werden Zeilen dort umgebrochen, wo der Programmierer Quelltext nicht umbräche, da er unübersichtlich würde. Die &#8220;Navigationsspalte&#8221; muss dazu links sein, andernfalls könnte der Quelltext in &#8220;Navigationstext&#8221; ragen, dann wären beide Texte schwierig zu entziffern.</p>
<p>Aus diesem Grund habe ich die &#8220;Navigationsspalte&#8221; nach links verschoben (es war nur eine float-Anweisung im CSS zu ändern von <code>left</code> auf <code>right</code>).</p>
]]></content:encoded>
			<wfw:commentRss>http://www.elmar-baumann.de/blog/2011/09/20/quellcode-auf-webseiten/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Modulare Java-Programmierung via Services</title>
		<link>http://www.elmar-baumann.de/blog/2011/07/25/modulare-java-programmierung-via-services/</link>
		<comments>http://www.elmar-baumann.de/blog/2011/07/25/modulare-java-programmierung-via-services/#comments</comments>
		<pubDate>Mon, 25 Jul 2011 17:15:54 +0000</pubDate>
		<dc:creator>Elmar</dc:creator>
				<category><![CDATA[Software-Entwicklung]]></category>
		<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.elmar-baumann.de/blog/?p=1031</guid>
		<description><![CDATA[Eine umfangreichere Anwendung ist unübersichtlich, schwierig zu &#8220;pflegen&#8221; und erweitern, falls ich sie in einem einzigen JAR unterbringe. Teile ich sie auf in mehrere Projekte und jedes Projekt ergibt ein JAR, ist dies besser. Die Abhängigkeiten der Projekte untereinander sollten minimal sein (eine Klasse des Projekts X sollte nicht nach einer Klasse des Projekts Y [...]]]></description>
			<content:encoded><![CDATA[<p>Eine umfangreichere Anwendung ist unübersichtlich, schwierig zu &#8220;pflegen&#8221; und erweitern, falls ich sie in einem einzigen <a href="http://download.oracle.com/javase/1.5.0/docs/guide/jar/jar.html">JAR</a> unterbringe. Teile ich sie auf in mehrere Projekte und jedes Projekt ergibt ein JAR, ist dies besser. Die Abhängigkeiten der Projekte untereinander sollten minimal sein (eine Klasse des Projekts X sollte nicht nach einer Klasse des Projekts Y verlangen).<br />
<span id="more-1031"></span><br />
Bevor ich eine größere Anwendung programmiere, sollte ich mich einarbeiten in ein Framework, das zu diesem Zweck entwickelt wurde, beispielsweise der <a href="http://netbeans.org/kb/trails/platform.html">NetBeans Platform</a> und dieses benutzen. Die Einarbeitung kostet Zeit, die Benutzung spart diese jedoch auf lange Sicht und schont die Nerven.</p>
<p>Falls ich mich nicht einarbeiten will, ist der <a href="http://download.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html">ServiceLoader</a> nach meinen aktuellen Kenntnissen am geeignetsten, dieser lässt sich auch in einem Framework benutzen.</p>
<p>Dazu definiere ich in einem oder mehreren Projekten <strong>Interfaces</strong> (<strong>Services</strong>). Diese implementiere ich in <em>anderen</em> Projekten.</p>
<p>Beispielsweise könnte ich entscheiden, das Programmfenster hat drei Bereiche, in die andere Module (Projekte) &#8220;Fenster&#8221; einfügen können. Die Interface-Definition sähe z.B. so aus:</p>
<pre>
public <span style="color:#0000ff;font-weight:bold">interface WindowSystem</span> {
    void dockIntoSelectView(Component component);
    void dockIntoEditView(Component component);
    void dockIntoPropertiesView(Component component);
}
</pre>
<p>In irgend einem Projekt implementiere ich dies und publiziere diese Implementierung gemäß der JavaDoc des <code>ServiceLoader</code> im <code>META-INF/services</code>-Verzeichnis:</p>
<pre>
public final class ApplicationFrame <span style="color:#0000ff;font-weight:bold">implements WindowSystem</span> {

    <span style="color:#0000ff;font-weight:bold">@Override</span>
    public void dockIntoSelectView(Component component) {
        tabbedSelectViewPane.add(component);
    }

    <span style="color:#0000ff;font-weight:bold">@Override</span>
    public void dockIntoEditView(Component component) {
        tabbedPaneEditView.add(component);
    }

    <span style="color:#0000ff;font-weight:bold">@Override</span>
    public void dockIntoPropertiesView(Component component) {
        tabbedPanePropertiesView.add(component);
    }
}
</pre>
<p>Will ich in einem anderen Projekt der Select-View des Anwendungsfensters etwas hinzufügen, benutze ich den <code>ServiceLoader</code>. Ich vereinfache mir die Arbeit mit der Klasse <code>ServiceLookup</code> eines Projekts, das alle anderen Projekte nutzen dürfen und das keine Abhängigkeit zu einem anderen Projekt hat, siehe Implementierung weiter unten:</p>
<pre>
public final class ImageFileBrowser <span style="color:#0000ff;font-weight:bold">implements Module</span> {

    private JTree fileSystemTree;

    <span style="color:#0000ff;font-weight:bold">@Override</span>
    public void initModule() {
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                createFileSystemTree();
                addFileSystemTree();
            }
        );
    }

    private void createFileSystemTree() {
        FileFilter directoryFilter = FileSystemUtil.getDirectoriesOnlyFileFilter();
        TreeModel fileSystemTreeModel = FileSystemUtil.createFileSystemTreeModel(directoryFilter);

        fileSystemTree = new JTree(fileSystemTreeModel);
        fileSystemTree.setCellRenderer(new FileSystemTreeCellRenderer());
        fileSystemTree.addTreeSelectionListener(new ImageDisplayTreeSelectionListener());
    }

    private void addFileSystemTree() {
        <span style="color:#0000ff;font-weight:bold">WindowSystem windowSystem = ServiceLookup.lookup(WindowSystem.class);</span>

        <span style="color:#0000ff;font-weight:bold">windowSystem.dockIntoSelectView(fileSystemTree);</span>
    }
}
</pre>
<p>Klasse <code>ServiceLookup</code> eines &#8220;Utility&#8221;-Projekts, das keine Abhängigkeiten zu anderen Projekten hat und das alle anderen Projekte benutzen dürfen:</p>
<pre>
public final class ServiceLookup {

    public static &lt;T&gt; T lookup(Class&lt;T&gt; serviceClass) {
        if (serviceClass == null) {
            throw new NullPointerException("serviceClass == null");
        }

        ServiceLoader&lt;T&gt; serviceLoader = ServiceLoader.load(serviceClass);
        Iterator&lt;T&gt; serviceImplementations = serviceLoader.iterator();

        return serviceImplementations.hasNext()
                ? serviceImplementations.next()
                : null;
    }

    public static &lt;T&gt; Collection&lt;? extends T&gt; lookupAll(Class&lt;T&gt; serviceClass) {
        if (serviceClass == null) {
            throw new NullPointerException("serviceClass == null");
        }

        Collection&lt;T&gt; serviceImplementations = new ArrayList&lt;T&gt;();
        ServiceLoader&lt;T&gt; serviceLoader = ServiceLoader.load(serviceClass);

        for (T service : serviceLoader) {
            serviceImplementations.add(service);
        }

        return serviceImplementations;
    }

    private ServiceLookup() {}
}
</pre>
<p>Prinzip einer modularen Anwendung mit Hilfe des <code>ServiceLoader</code>:</p>
<ul>
<li>Die Anwendung besteht aus unterschiedlichen Projekten, jedes hat eine bestimmte Aufgabe (&#8220;Zuständigkeit&#8221;)</li>
<li>Einige Projekte deklarieren verschiedene <strong>Services</strong> als <strong>Interface</strong></li>
<li>Andere Projekte implementieren diese Services und veröffentlichen die Implementierungen im <code>META-INF/services</code>-Verzeichnis</li>
<li>Projekte benutzen die Services und fügen so der Anwendung weitere Fähigkeiten hinzu</li>
<li>Ein &#8220;Sammelprojekt&#8221; enthält die JARs aller Teilprojekte und den &#8220;Startcode&#8221;, der eine von allen Teilprojekten zu implementierende &#8220;init&#8221;-Methode aufruft (im Beispiel oben die Methode <code>initModule()</code> des Services <code>Module</code>, &#8220;Abholen&#8221; der Implementierungen mit <code>ServiceLookup.lookupAll()</code>)</li>
</ul>
<p>Will ich die Anwendung erweitern, eröffne ich ein neues Projekt, benutze die Services und füge das Projekt-JAR dem Sammelprojekt hinzu. Ich brauche nicht die bisherigen Projekte anzupassen.</p>
<p>Das Beispiel oben könnte ich erweitern: Ein neues Projekt implementiert analog zu <code>ImageFileBrowser</code> über die <code>Module</code>-Schnittstelle beispielsweise eine Klasse <code>Mp3FileBrowser</code>, ein weiteres <code>VideoFileBrowser</code>. Diese Projekte müssen nichts voneinander wissen ebensowenig wie das Fenstersystem wissen muss, welche Fenster mit welcher Funktion ihm hinzugefügt werden.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.elmar-baumann.de/blog/2011/07/25/modulare-java-programmierung-via-services/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ärgernisse mit Windows-Installationsprogrammen</title>
		<link>http://www.elmar-baumann.de/blog/2011/04/29/argernisse-mit-windows-installationsprogrammen/</link>
		<comments>http://www.elmar-baumann.de/blog/2011/04/29/argernisse-mit-windows-installationsprogrammen/#comments</comments>
		<pubDate>Fri, 29 Apr 2011 21:23:43 +0000</pubDate>
		<dc:creator>Elmar</dc:creator>
				<category><![CDATA[Betriebssysteme]]></category>
		<category><![CDATA[Programme]]></category>

		<guid isPermaLink="false">http://www.elmar-baumann.de/blog/?p=995</guid>
		<description><![CDATA[Start einer Liste. Im Idealfall ärgert ein Installationsprogramm nicht, in schlimmen Fällen treffen mehre Punkte zu. Unter &#8220;obligatorisch&#8221; verstehe ich, das Installationsprogramm führt etwas aus, ohne nachzufragen oder zu informieren. Installation zusätzlicher Programme, beispielsweise einer weiteren Anwendung oder einer Web-Browser-Toolbar, obwohl dies verneint wurde oder obligatorisch (obligatorisch: Oft ist das der Fall bei einer &#8220;Standardinstallation&#8221;) [...]]]></description>
			<content:encoded><![CDATA[<p>Start einer Liste. Im Idealfall ärgert ein Installationsprogramm nicht, in schlimmen Fällen treffen mehre Punkte zu. Unter &#8220;obligatorisch&#8221; verstehe ich, das Installationsprogramm führt etwas aus, ohne nachzufragen oder zu informieren.<br />
<span id="more-995"></span></p>
<ul>
<li>Installation zusätzlicher Programme, beispielsweise einer weiteren Anwendung oder einer Web-Browser-Toolbar, obwohl dies verneint wurde oder obligatorisch (obligatorisch: Oft ist das der Fall bei einer &#8220;Standardinstallation&#8221;)</li>
<li>Obligatorisches Hinzufügen von Verknüpfungen in den System Tray</li>
<li>Obligatorisches Hinzufügen von Verknüpfungen in die Schnellstartleiste</li>
<li>Obligatorisches Hinzufügen von Verknüpfungen auf den Desktop</li>
<li>Bei älteren System als Vista: Keine Nachfrage, wo im Programme-Ordner des Startmenüs die Verknüpfungen platziert werden sollen, statt dessen obligatorisches Einfügen eines Programme-Ordners oder einer Verknüpfung in den Wurzelordner, sodass der Programme-Ordner des Startmenüs ohne &#8220;eigene Pflege&#8221; bald so viele Einträge enthält, dass das Finden eines Programms etliche Zeit beansprucht, da dort keine Suche existiert</li>
<li>Obligatorisches Hinzufügen zu einer der etwa 50 Autostart-Varianten</li>
<li>Obligatorisches Hinzufügen eines Dienstes, der automatisch gestartet wird und der dauerhaft Arbeitsspeicher und Rechenleistung verbraucht</li>
<li>Obligatorisches &#8220;in Besitz nehmen&#8221; von Dateitypen und im Programm kann dies nicht deaktiviert werden</li>
<li>Unsinnige Benennung der in Besitz genommenen Dateitypen, beispielsweise heißen alle Bildtypen &#8220;Xyz-Programm-Datei&#8221;; im Explorer kann dann nicht mehr so nach Dateityp sortiert werden, dass JPEG-Dateien getrennt sind von TIFF-Dateien</li>
<li>Bei jeder Installation wird ein neuer Autoplay-Handler eingerichtet und bisherige Autoplay-Handler, die nicht mehr funktionieren, werden nicht gelöscht; wird ein Speichermedium eingelegt, ist der Autoplay-Dialog gefüllt mit fehlerhaften Vorschlägen</li>
<li>Obligatorisches extensives Auffüllen des Explorer-&#8221;Neu&#8221;-Kontextmenüs, werden diese Einträge mühevoll via Regedit entfernt, sind sie nach Start des Programms wieder da und im Programm kann dieses Verhalten nicht abgestellt werden</li>
<li>Eine automatische Aktualisierung wird als Dienst installiert, der nicht über die erforderlichen Rechte verfügt und fortan bei jedem Windows-Login Fehlermeldungen anzeigt (&#8220;Sie verfügen nicht über die benötigten Rechte &#8230;&#8221;, keine Schaltfläche, die funktioniert wie &#8220;runas /user:Administrator&#8221;)</li>
<li>Nicht funktionierendes Deinstallationsprogramm</li>
<li>Deinstallation hinterlässt viel &#8220;Müll&#8221; wie nicht funktionierende Autoplay-Handler oder Dienste-Starts</li>
<li>Deinstallation löscht nicht die (große) MSI-Datei mit den Installationsdateien</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.elmar-baumann.de/blog/2011/04/29/argernisse-mit-windows-installationsprogrammen/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

