(Ein) Vorteil eines digitalen Musikarchivs (MP3) auf beschreibbaren Datenspeichern

Freitag, 4. Juni 2010

Einer der wichtigen Vorteile eines digitalen Musikarchivs auf beschreibbaren Datenspeichern ist: Ich kann es meinem Geschmack anpassen durch (bequemes) Löschen von Stücken, die mir definitiv nicht (mehr) gefallen.

Sitze ich am Computer und höre ein Musikstück, das mir nicht gefällt und von dem ich weiß, es wird mir auch zukünftig nicht gefallen, lösche ich es während der Player es spielt. Für Amarok benutze ich dazu das Skript Delete Current Playing File.

Java: Laufzeitausnahmen anzeigen

Freitag, 2. April 2010

Im vorherigen Artikel schrieb ich, meine public und protected-Methoden werfen Laufzeitausnahmen bei Programmierfehlern. Damit der Benutzer diese sieht und den Support benachrichtigen kann, gehe ich so vor:

  • Ersetzen der EventQueue durch eine, die bei nicht gefangenen Ausnahmen einen Dialog anzeigt
  • Dieser Dialog bietet dem Benutzer an, den Fehler per E-Mail zu übermitteln

Eigene Queue:

/**
 * JPhotoTagger's event queue.
 * <p>
 * Catches throwables and displays a dialog with information about the cause.
 *
 * @author Elmar Baumann
 */
public final class AppEventQueue extends java.awt.EventQueue {
    @Override
    protected void dispatchEvent(AWTEvent event) {
        try {
            super.dispatchEvent(event);
        } catch (Throwable t) {
            AppLogger.logSevere(AppEventQueue.class, t);
            getDialog(t).setVisible(true);
        }
    }

    private LongMessageDialog getDialog(Throwable t) {
        LongMessageDialog dlg =
            new LongMessageDialog(GUI.INSTANCE.getAppFrame(), true,
                                  UserSettings.INSTANCE.getSettings(), null);

        dlg.setTitle(JptBundle.INSTANCE.getString("AppEventQueue.Error.Title"));
        dlg.setErrorIcon();
        dlg.setMail(AppInfo.MAIL_TO_ADDRESS_BUGS, AppInfo.MAIL_SUBJECT_BUGS);
        dlg.setShortMessage(
            JptBundle.INSTANCE.getString("AppEventQueue.Error.Message"));
        dlg.setLongMessage(createMessage(t));

        return dlg;
    }

    private String createMessage(Throwable t) {
        String                message = AppLogger.getMessage(t);
        ByteArrayOutputStream baos    = new ByteArrayOutputStream();
        PrintStream           ps      = new PrintStream(baos);

        t.printStackTrace(ps);

        return message + "\n" + baos.toString();
    }
}

Screenshot des Dialogs (Fehler von gestern):

JPhotoTagger: Dialog bei nicht gefangenen Ausnahmen

Referenzen: Catching all Runtime Exceptions in Swing und Questions About Exception Handling.

Java: NullPointerException zum Erkennen von Programmierfehlern

Freitag, 2. April 2010

Werden einer public oder protected-Methode null-Parameter überreicht, die per Vorbedingung nicht null sein dürfen, werde ich zukünftig prinzipiell am Anfang der Methode eine NullPointerException werfen. Verzichte ich darauf, kann mein Programm nicht richtig arbeiten, ohne dass ich dies bemerke.

Ein aktuelles Beispiel aus meinem Projekt JPhotoTagger: Es können benutzerdefinierte Dateifilter angelegt werden. Der Datenbank-Primärschlüssel ist der Name des Filters. Dieser wird in ein JTextField eingegeben, und bevor der Filter in die Datenbank gespeichert wird, wird überprüft, ob der eingegebene Name bereits in der Datenbank existiert:

public boolean exists(String name) { ... }

Vermutlich sorgte die Autocomplete-Funktion des Editors dafür, dass nicht die Methode getText() des Textfelds aufgerufen wurde, sondern getName(). Diese liefert null (da ich das Textfeld nicht benannt habe). Das Ergebnis: exists() liefert immer false, denn das Tabellen-Namensfeld kann nicht auf NULL gesetzt werden.

Was den Fehler vorerst verbirgt: Beim Einfügen in die Datenbank wird die richtige Methode des Textfelds aufgerufen, es sieht alles gut aus, der Filter kann benutzt werden und auch modifiziert. Das Problem tritt erst später auf (oder nie), falls versucht wird, den gleichen Namen erneut per SQL-INSERT in die Datenbank zu schreiben. Hätte ich keine Einschränkung für den Namen gesetzt, gäbe es mehrere gleichnamige Filter in der Datenbank. Das Umstellen des Codes auf

public boolean exists(String name) {
    if (name == null) {
        throw new NullPointerException("name == null");
    }
    ....
}

zeigte den Fehler sofort.

assert ist kein Ersatz, dieses wird nur beim Entwickler ausgeführt, nicht beim Benutzer. Es kann auch nicht false geliefert werden, falls der Parameter null ist.

Zusammengefasst:

  • Explizit in den Javadoc-Kommentar der Methode schreiben, falls ein Methodenparameter null sein darf
  • Alle anderen Parameter dürfen per Vertrag - als Vorbedingung - nicht null sein, dies ist nicht zu dokumentieren
  • Die Vorbedingungen werden sichergestellt, bevor der Funktionscode ausgeführt wird. Ist ein Methodenparameter vertragswidrig null, werden in public und protected-Methoden NullPointerExceptions geworfen. Das gleiche gilt für ungültige Parameterwerte (IllegalArgumentException), falls die Methode in einem bestimmten Objekt-Zustand nicht aufgerufen werden darf (IllegalStateException) etc.
  • Der nachfolgende Code kann (nun) von gültigen Werten ausgehen (braucht diese nicht zu überprüfen), sein Vertrag ist, zu liefern, was die Methode verspricht
  • assert benutze ich als Absicherung bei private-Methoden, in denen eigentlich alles wie gewünscht verlaufen sollte, wo Fehlschläge jedoch nicht ausgeschlossen sind
  • Zukünftig sollte ich automatisierte Tests obligatorisch einsetzen

Noch kürzer: Für jede Methode Vor-, Nachbedingungen und Invarianten ausarbeiten und diese sicherstellen.

Die Nachbedingungen sind auch einen Artikel wert, ich warte auf ein Aha-Erlebnis wie hier.

Wie ich die Ausnahmen dem Benutzer präsentiere, steht im folgenden Artikel.

Ausnahmen:

  • Überschriebene Methoden und implementierte Interfaces, die von Objekten einer anderen Bibliothek aufgerufen werden, beispielsweise vom JDK. Falls diese im Gegensatz zur Dokumentation null überreichen oder ungültige Parameter, liegt ein Defekt in der Bibliothek vor, der von deren Programmieren zu beheben ist.
  • Parameter, die nicht benutzt werden, können einen beliebigen Wert haben, er interessiert nicht.

Ärgernisse mit openSUSE 11.2/KDE 4.3

Montag, 16. November 2009

Hier setze ich den vorherigen Artikel fort; nunmehr installierte ich openSUSE 11.2, das KDE 4.3 installiert.

Erfreulich ist: Die Netzwerkkarte meines Boards wird jetzt stets erkannt.

Der Gesamteindruck ist weniger positiv, denn es gibt Ärgernisse, die nicht sein müssten. Manchmal existieren Workarounds, diese zu finden und anzuwenden, erfordert unnötige Arbeit:

  • Systemeinstellungen > Persönliche Informationen > Pfade: Pfad für heruntergeladene Dateien. Als Download-Pfad war das Home-Verszeichnis eingestellt. Nach Ändern dieses ungünstigen Pfads wird gefragt, ob alle heruntergeladenen Dateien ins neue Download-Verzeichnis verschoben werden sollen. Wird hier “Verschieben” ausgewählt, wird das gesamte Home-Verzeichnis ins neue Download-Verzeichnis verschoben. Es wird nicht geprüft, ob das Home-Verzeichnis - die Default-Einstellung - die Quelle ist, was kein Problem wäre, gäbe es eine Liste heruntergeladener Dateien und (nur) diese würden verschoben!
  • Es wurde eine Thunderbird-Beta-Version installiert: 3.0.b4, mit der folgende für mich wichtige Add-Ons nicht mehr funktionieren, es gibt keine aktuellen: Enigmail und Lightning. Mit anderen Worten: Das openSUSE-Team beschloss, der Anwender müsse mit Thunderbird keine E-Mail mehr verschlüsseln können, keine verschlüsselt abgeschickte mehr lesen und keine Termine mehr verwalten, bis (irgendwann) die Add-Ons angepasst und freigegeben wurden an die neue Version. Workaround: Für Enigmail gibt es ein funktionierendes Nightly Build, für Lightning nicht, da es an vergangene Termine pausenlos erinnert. Auch Xpunge kann ich nicht mehr benutzen. Nachtrag: Nachdem auch das Versenden von Nachrichten in Newsgroups nicht mehr funktionierte, deinstallierte ich Thunderbird 3 Beta und installierte das auf mozilla.org erhältliche tar-Archiv der Version 2, das wie gewohnt funktioniert.
  • K3b wollte wieder keine 8 GB-DVD brennen, da es ein neueres genisoimage forderte, es wurde ein älteres installiert und im Online-Repository gibt es kein neueres. Workaround: Neueste Version herunterladen.
  • K3b brennt auf DVD-R keine Multisessions, obwohl sich manuell eine starten lässt, die DVD kann anschließend entsorgt werden, was nicht billig ist, da ich nur gute DVDs benutze. Im automatischen Modus wird ebenfalls keine Multisession-DVD erzeugt. Multisessions sind auch mit DVD-R möglich, nicht nur mit DVD+R.
  • K3b: Der Mauscursor ist dauerhaft eine rotierende “Sanduhr”
  • Amarok und Podcasts sind sehr fehlerhaft implementiert, beinahe unbrauchbar (Konfiguration: Verzeichnis je Podcast selbst definiert, Podcasts sollen heruntergeladen werden): 1. Podcasts werden “irgendwie”  sortiert, die neuesten sind nicht ganz oben in der Liste, manchmal ganz unten, manchmal in der Mitte. Wie sortiert wird, kann nicht eingestellt werden, wobei eine zeitlich absteigende Sortierung als Default ok wäre. 2. Wird die Anzahl beschränkt und die Liste hat so viele Einträge wie in der Beschränkung eingestellt, werden neue Podcasts nicht angezeigt, vermutlich aufgrund der fehlerhaften Sortierung. 3. Das Symbol eines Podcasts ändert sich nicht, falls ein neuer Podcasts dazugekommen ist. 4. Obwohl eingestellt, werden Podcasts nicht heruntergeladen. Nur so wird garantiert die Dauer angezeigt und ein Podcast fortgesetzt an der gleichen Stelle nach Betätigen der Pause-Taste. Leider wird Amarok 1.x nicht mehr über die Paketverwaltung angeboten, die Version 2.x also zwangsweise installiert.
  • KMail: Das Verzeichnis, in dem Mails gespeichert werden kann nicht ausgewählt werden (es ist vorgeschrieben). Workaround: Symlink ~/.kde4/share/apps/kmail/mail erzeugen auf das Mail-Verzeichnis.
  • KMail: KMail einrichten > Sicherheit > Nachrichten erstellen: Wird Versendete Nachrichten verschlüsselt speichern abgewählt, werden trotzdem mit GnuPG verschlüsselte Nachrichten verschlüsselt gespeichert. Es gibt keinen Workaround, die kmailrc-Option store-displayed-messages-unencrypted=true, Abschnitt [Reader] ist immer noch ohne Funktion.
  • Es ist nicht möglich/ersichtlich, die Anzeigedauer der Geräteüberwachung zu definieren: Sie sollte in den Einstellungen für Geräteüberwachung sein, was nicht der Fall ist. Das Popup verdeckt Anwendungsfenster nach Einlegen einer CD/DVD, eines USB-Sticks etc., muss erst selektiert und deselektiert werden, um zu verschwinden. Die Geräteüberwachung ist in einigen Fällen auch nützlich, deswegen ist Entfernen kein zufrieden stellender Workaround. Vielmehr sollte das Popup deaktiviert werden können bzw. z.B. nur 1 Sekunde angezeigt werden.
  • In den Systemeinstellungen können nun Geräte-Aktionen definiert werden. Trotz sinnvoller Einstellung ist es mir nicht gelungen, eine zu definieren, die beim Einlegen einer Film-DVD angeboten wird in der Geräteüberwachung.
  • Dolphin: Der Inhalt eines Verzeichnisses wird nicht automatisch aktualisiert
  • Der externe USB-DVD-Brenner schließt die Schublade von alleine sofort nach dem Ausfahren, manchmal bereits nach Öffnen um einen halben Zentimeter. Das ist definitiv ein KDE- oder Linux-Problem, da unter Windows dieses Verhalten nicht auftritt und nach Abziehen des USB-Kabels der Brenner wie erwünscht reagiert: Die Schublade kann vollständig geöffnet, eine CD/DVD eingelegt werden und sie wird erst geschlossen, nachdem ich das veranlasse.
  • Der Schreibmodus auf externe, dynamisch eingebundene Medien ist immer noch nicht über Yast2 einstellbar oder schwer aufzufinden: Das Kopieren großer Dateien auf einen USB-Sticks ist so extrem langsam. Das Konfigurieren des hald (kein permanentes Synchronisieren sondern Nutzen eines ausreichend großen Schreibcaches) ist zu fehleranfällig (und umständlich).

Gegenüber dem letzten Posting funktionieren in KDE jetzt:

  • Optionen für das Herunterfahren werden angezeigt
  • Alt+F2 (krunner) merkt sich eingegebene Befehle nach Beenden von KDE

Es wäre gut, openSUSE nähme das open nicht wörtlich im Sinne von alles oder nichts: Nach jeder Installation können weder Lieder aus der MP3-Sammlung gehört werden noch DVDs angeschaut. Für einige Linux-Neulinge dürfte das bereits ein Grund sein, Linux wieder zu entfernen. Auch sollten proprietäre Grafikkarten- und sonstige Treiber (Drucker, Scanner, …), z.B. von Nvidia oder Hewlett Packard, gleich installiert werden und nicht die leistungsschwächeren Open Source-Treiber. Der Anwender soll nicht “bestraft” werden, weil die Hersteller die Quellen nicht veröffentlichen. Es könnte eine Option angeboten werden: “Ja, ich will MP3-Dateien hören und DVDs sehen, auch wenn der Quelltext für die erforderliche Software nicht unter GPL veröffentlicht wurde”. Ich bin sicher, die meisten Benutzer würden sich dafür entscheiden.

Ärgernisse mit OpenSUSE 11.1/KDE 4.1

Samstag, 7. November 2009

System

  • openSUSE 11.1 (i586); neu installiert auf leere Festplattenpartition (so keine Seiteneffekte durch bestehende Installation)
  • KDE 4.1.3 Release 4.10.4
  • Board: Gigabyte GA-P55-UD3
  • CPU: Intel Core i5 750 2,67 GHz (Quad Core)
  • RAM 8 GB
  • Festplatte Hitachi 1 TB
  • Grafik: GeForce GTX 260/PCI/SSE2

OpenSUSE

Die Verbindung zum DSL-Router ist unzuverlässig. Wird der Rechner eingeschaltet und sofort Linux gebootet, funktioniert die Verbindung meistens, manchmal aber erst nach einem Reboot. Wurde vorher Windows 7 gebootet, funktioniert die Verbindung nie, der Rechner ist vom Stromnetz zu trennen vor dem Booten von Linux.

Unter Windows funktioniert die Verbindung immer. Ein Austausch des Routers (anderer Hersteller) löst auch nicht das Problem. Somit ist ein Hardwaredefekt unwahrscheinlich.

Deaktivieren von ACPI, Neuladen des Netzwerkkartenmoduls (r8169), Neustart des Netzwerks, … helfen nicht.

Hardware:  Onboard Realtek RTL8111/8168B PCI Express Gigabit Ethernet controller.

Lösung: Unbekannt. Relevante Auszüge aus /var/log/messages:

ifup:     eth0      device: Realtek Semiconductor Co., Ltd. RTL8111/8
168B PCI Express Gigabit Ethernet controller (rev 03)
kernel: r8169: eth0: link down
kernel: ADDRCONF(NETDEV_UP): eth0: link is not ready
...
dhcpcd[3007]: eth0: timed out

KDE 4.1

  • Manchmal war der Desktop schwarz nach dem Login, das Starten von Anwendungen war nicht möglich, da keine Kontrolleiste existierte und Alt+F2 nicht funktionierte (Lösung: Verzicht auf alle Effekte, Editieren außerhalb von KDE der kwinrc nachdem Desktop schwarz war)
  • Installierte Programme werden meistens nicht in’s K-Menü eingetragen (Lösung: Manuell über Menü-Editor eintragen)
  • Im Autostart deaktiviertes Programm startet trotzdem (Lösung: Löschen der Verknüpfung zum Programm aus Autostart-Ordner)
  • Programmstart über Alt+F2 vergisst nach Neustart bisherige Aufrufe
  • Abmelden über Desktop-Kontextmenü oder Kontrolleiste kennt nur eine Option: Entweder Herunterfahren oder Neustart oder Abmelden oder … obwohl eingestellt ist, dass Optionen angeboten werden sollen
  • Insgesamt weniger Funktionalität, z.B. können keine Programme definiert werden, die beim Einlegen einer DVD angeboten werden (Anschauen mit Xine, Kopieren, …), in der Kontrolleiste lässt sich nicht festlegen, dass Fenster des gleichen Programms zusammengefasst werden (es wird mehr Platz wird belegt) etc.

KDE 4-Programme

  • Amarok: Nahm in die Sammlung nur das erste Verzeichnis unterhalb der Wurzel auf und dessen Unterverzeichnisse (zurück zu Amarok 1.4)
  • K3b: DVDs werden nicht auf Double Layer-Rohlinge kopiert wegen einer angeblich zu niedrigen/nicht vorhandenen Version von growisofs (zurück zu K3b 1.0.5)
  • In KMail kann kein Verzeichnis eingestellt werden, in das die E-Mails gespeichert werden (Thunderbird wegen gemeinsamer Nutzung unter Windows, auch wenn Enigmail versandte E-Mails nur verschlüsselt speichern kann, ansonsten altes KMail benutzen)

Sollte KDE 4.1 ein deutlicher Fortschritt sein gegenüber KDE 4.0, bin ich froh, nie KDE 4.0 benutzt zu haben. Ich werde bei Verfügbarkeit (Release) OpenSUSE 11.2 installieren, das hoffentlich keine Probleme hat mit der Onboard-Netzwerkkarte. Ein Prae-Release (Milestone 7) von OpenSUSE 11.2 konnte ich nicht benutzen, da dies die Tastatur nicht erkannte (2 weitere Tastaturen anderer Marke getestet, 1 ebenfalls USB, die andere PS/2), ebenfalls nicht die USB-Maus. Legacy-Support im BIOS war (natürlich) aktiviert.