Blog

Schwarzer Bildschirm nur mit Cursor nach Upgrade auf OpenSuse Tumbleweed und SDDM

Nach dem Upgrade auf OpenSuse Tumbleweed und auf den neuen Desktop-Manager SDDM konnte ich, nachdem sich nach einigen Minuten Inaktivität, der Bildschirm ausgeschaltet hatte, das System nicht mehr entsperren. Symptom: Schwarzer Bildschirm nur mir (beweglichem) Cursor. Mit Ctrl+Alt+F1 (...F6) konnte man zwar in die Console umschalten, von dort half aber auch nur noch

systemctl restart sddm

Das ist aber natürlich unbefriedigend...
Der Hinweis den Benutzer sddm in die Gruppe video aufzunehmen brachte keine Verbesserung.

Lösung: Folgendes Verzeichnis löschen (oder zunächst mal sicherheitshalber nur umbenennen):

~/.cache

Nebenwirkungen: bei mir keine.

Möglicherweise bestand das Problem bereits seit einiger Zeit unter 13.2 mit kdm. Damals aber nur sporadisch nach dem Aufwecken aus dem Suspend. Symptom ebenfalls schwarzer Bildschirm mit "lebendigem" Cursor. Dafür konnte ich dann aber nichtmal mehr in ein TTY wechseln - es blieb also nur das harte Ausschalten.

Jetzt muss sich noch zeigen, ob das auch auf meinem Arbeitsrechner Abhilfe schafft. Das System dort ist ein zum "Kubuntu" mutiertes Ubuntu, nachdem mir Unity zu sehr auf den Zeiger gegangen ist (ständig blieben Schatten irgendwelcher Popup-Fenster als Geister zurück, Problem mit dem Scaling auf 4K-Screen, und und und). Symptome dort: Nachdem der Bildschirm schlafen gegangen ist muss man plasmashell neu starten, da sonst alle Desktop Icons neu sortiert sind und er das aktuelle Hintergrundbild "vergessen" hat. Außerdem vergisst leider auch SDDM (oder wer auch immer) den Skalierungsfaktor fü den 4K-Bildschirm nach jedem Neustart (Trotzdem gefällt mir der SDDM deutlich besser als Unity oder Gnome)

killall plasmashell; plasmashell
Zum Feedback-Formular...

OpenSuse 12.1 (KDE Plasma-Desktop) und NVidia-Grafikkarte auf Laptop "Optimus-Technologie"

  1. Da Optimus (Umschaltung zwischen dem einfachen On-Board Grafik-Chip und der NVidia-Karte) wohl noch nicht wirklich gut unterstützt wird (das Linux-Äquivalent heißt dann passenderweise " Bumblebee "), musste ich zuerst im Bios fest auf die NVidia-Karte umschalten (sonst hatte ich bei grafisch komplexeren bzw. 3D-Anwendungen seltsame Effekte, wie z.B. bunte hüpfende Pixel)
  2. Danach arbeitet der mitgelieferte Nouveau-Treiber (offener Treiber für NVidia-Grafikkarten) eigentlich ganz zufriedenstellend, aber bei bestimmten Anwendungen gab es doch noch Probleme. Also habe ich in YAST unter Software →Software Repositories die (zur OpenSuse-Version passende) NVidia-Seite eingetragen, also bei mir:
    http://download.nvidia.com/opensuse/12.1
    Und dann über Install/Remove Software und den Suchbegriff "nvidia" den passenden Treiber angeklickt. Nun werden weitere Komponenten (die mit diesem Filter ebenfalls sichtbar sind) als abhängig mitselektiert. Hier muss man aber aufpassen! Bei mir wurden teilweise die falschen Komponenten ausgewählt. Eine der Komponenten endet auf den Kernel-Flavour (bei mir "default"). Wenn hier (nur) das falsche "Flavour" selektiert wird (z.B. "...desktop" oder "...pae") gibt es einen Haufen Probleme (X-Windows startet nicht mehr, Lösung s.u.). Die Kernel-Version (samt "Flavour") kann man sich übrigens in einer Shell mit "uname -a" anzeigen lassen. Die Kernel-Flavour-abhängigen Pakete kann man aber alle parallel installieren- sinnvoll ist das aber nur wenn man auch die entsprechenden Kernel-Flavours (pae, desktop, default) dazu installiert hat.
  3. Nach einem Neustart lief dann tatsächlich der NVidia-Treiber (über My Computer, bzw. Konqueror mit der Url " sysinfo:/" erkennbar), nur leider waren alle Schriften (Fenstertitel, etc.) viel zu groß (noch häufiger kommen aber wohl viel zu kleine Schriften vor). Um das zu beheben geht man in Configure Desktop (unter Favoriten) und dort zu Application Appearance und dort wiederum zu Fonts. Dort aktiviert man DPI für Schriften erzwingen (bei mir sieht die Einstellung "96 DPI" gut aus). Danach nochmaliger Neustart bzw. zumindest ab- und wieder anmelden (reicht).
  4. Das letzte Problem: Wenn der Rechner in den Ruhezustand (Sleep-Modus), oder auch nur in den Screensaver ging funktionierte danach die Helligkeitsregelung nicht mehr (weder über die Funktionstasten, noch über den Schieberegeler, der erscheint wenn man in der Task-Leiste auf Battery-Monitor geht). Seit ich in /etc/X11/xorg.conf.d/50-device.conf Folgendes eingetragen habe geht auch das:
    Section "Device"
      Identifier "Device0"
      Driver "nvidia"
      VendorName "NVIDIA Corporation"
      Option "RegistryDwords" "EnableBrightnessControl=1"
    EndSection

    Achtung: Will man wieder auf nouveau umsteigen (z.B. weil es fü ein Rolling-Release wie OpenSuse Tumbleweed ziemlich umständlich ist den NVidia-Treiber zu installieren) muss das unbedingt entfernt werden, sonst startet die grafische Oberfläche nicht mehr!

    In dieser Datei war vorher übrigens nur eine auskommentierte Template-Device-Section eingetragen. Ob das ein Fehler bei der Installation ist, oder immer so ist, weiß ich leider nicht. Wichtig ist dabei in jedem Fall das Attribut "EnableBrightnessControl=1".

Wenn, wie bei mir das Kind in den Brunnen gefallen ist und die falschen Komponenten installiert wurden und X-Windows daraufhin nicht mehr startet
(ich kann hier aber nur den Fall "Mein Kernel ist im default-Flavour aber nur die desktop-Komponente wurde installiert" aus eigener Erfahrung beschreiben):

  1. Mit etwas Verzögerung (in der offenbar erfolglos versucht wird X zu starten) kommt ein Text-Modus Login-Prompt.
  2. Dort als root einloggen und YAST starten.
  3. Alle NVidia-Komponenten deinstallieren (ziemlich fummelig im Text-Modus), dabei aufpassen nicht den Nouveau-Treiber zu deinstallieren, den man auch mit dem Filter "nvidia" findet.
  4. Die Datei /etc/X11/xorg.config, die fälschlicherweise angelegt wurde löschen, oder sicherheitshalber besser nur umbenennen oder verschieben (zumindest müsste wohl der NVidia-Treiber darin durch "nv" für den Nouveau-Treiber ersetzt werden, was ich aber nicht ausprobiert habe).
  5. Mit dem Befehl "reboot" den Rechner neu starten (X sollte nun wieder laufen) und die richtigen Treiber-Komponenten installieren (wie oben beschrieben).

Bei mir klappte übrigens beim ersten Mal alles (die richtigen Komponenten wurden automatisch ausgewählt und installiert). Ich habe aber den NVidia-Treiber nochmal deinstalliert (auch wieder via YAST), weil ich dachte doch mit dem Nouveau-Treiber hinzukommen. Erst als ich den NVidia-Treiber erneut auf demselben Weg installieren wollte hatte ich o.g. Probleme...

Zum Feedback-Formular...

Grafische Oberfläche (X) startet nicht mehr nach Update von OpenSuse (hier 12.1 auf 12.3) mit NVidia-Treiber

Nach dem Update von OpenSuse, in dem Fall von 12.1 auf 12.3 startet die grafische Oberfläche nicht mehr. Zumindest wenn man im "Failsafe-Modus" startet sollte man sich im Text-Modus anmelden können (mit Alt+F1 bzw. Alt+Ctrl+F1 müsste man in den Text-Modus kommen). Symptome:
Aus dem Text-Modus kann man nun YaST starten. Es ist wieder etwas fummelig, aber man muss versuchen die richtigen NVidia-Pakete zu installieren und die falschen zu deinstallieren. Ggf. muss man erst noch das richtige Repository angeben. Für OpenSuse 12.3 also:

Bei mir wurden die falschen Pakete ausgewählt. Auch ich selbst habe, trotz vorhergehender Konsultation der NVidia-Seite, erstmal noch die "falschen" Pakete gewählt - jedenfalls funktionierten diese nicht. Auch das Installieren mittels einer von der NVidia-Seite direkt heruntergeladenen Run-Datei hat nicht funktioniert bzw. keine Besserung gebracht. Erst als ich die gfxG02-Pakete statt der gfxG03-Pakete gewählt habe funktioniert alles wieder. Die Kernel-Flavour-abhängigen Pakete kann man übrigens alle installieren, wenn man verschiedene Kernel-Flavours (pae, desktop, default) parallel installiert hat.

Zum Feedback-Formular...

Internet-Explorer Tabs (Reiter, Registerkarten) verschwunden (IE8 unter XP)

Dafür kann es wohl mehrere Ursachen geben. Bei mir war es unter XP mit dem IE8 Folgendes: Ich hatte das Fenster zum Öffnen des Windows-Themes geändert, das Theme dort geändert (von Classic auf XP) und die Operation dann aber abgebrochen. Danach waren die Tabs im IE verschwunden (den Zusammenhang habe ich aber nur zufällig entdeckt).

Lösung: Das Fenster erneut öffnen und aktuelles Theme wählen und bestätigen. IE nochmal neu starten.
Zum Feedback-Formular...

Die Google Bilder-Suche zeigt nur noch die erste Seite an (Seamonkey)

...das kann daran liegen, dass man eingestellt hat, dass nur noch Bilder vom Herkunftsserver geladen und angezeigt werden. Die Einstellung ist beim (englischen) Seamonkey unter "Privacy & Security" → "Images" zu finden und heißt "Only load images that come from the originating server". Das sollte in "Load all images" (zurück-)geändert werden. Wenn es das nicht ist scheint es teilweise was zu bringen die "Privaten Daten" zu löschen, allerdings brachte das in meinem Fall natürlich nichts...
Zum Feedback-Formular...

Prozess "plugin-container" (Firefox / Seamonkey) erzeugt extrem hohe CPU-Auslastung

Das in einschlägigen Foren oft vorgeschlagene Deaktivieren des Prozesses (als URL "about:config" eingeben und dann nach "dom.ipc.plugins" filtern) ist keine gute Idee. Bei mir stürzte danach dauernd der Browser ab, und erzeugte zuvor selbst eine reichlich hohe Prozessorlast. Nein, schuld ist der Flash-Player, denn um dessen Abstürze abzufangen existiert der "plugin-container" eigentlich. Wer auf Flash-verseuchte Seiten (damit aber auch die meisten Filmchen) verzichten kann, der kann den Flash-Player komplett deinstallieren. Danach wird der Prozess "plugin-container" übrigens garnicht mehr gestartet. Alternativ kann man mal eine andere (ältere/neuere) Flash-Player-Version installieren oder die aktuelle einmal de- und erneut installieren (z.B. unter Linux über den jeweiligen Paketmanager). Bei mir hat das geholfen...

Nachtrag: inzwischen dreht dafür unter Linux (OpenSuse 12.1) öfter mal der XOrg-Prozess völlig durch (nahe 100% CPU-Auslastung). Wenn ich den Browser schließe und wieder öffne beruhigt sich das wieder. Eine bessere Lösung habe ich leider noch nicht gefunden...

Zum Feedback-Formular...

JAR via Batch-Datei aufrufen - oder wie vermeide ich den Fehler "unable to access jarfile"

Folgendes Problem: Wenn man eine JAR (myJar.jar) aus einer Batch-Datei (myBatch.bat) aufrufen will, die z.B. so aussieht:

myBatch.bat:
java -jar myJar.jar


Wobei ich hier davon ausgehe, dass myBatch.bat und myJar.jar im selben Verzeichnis liegen. Wenn man die Batch-Datei nun diese über das Cmd-Fenster aufruft, während man sich in einem anderen als dem Verzeichnis der Batch- und Jar-Datei befinden, bekommt man in etwa folgende Fehlermeldung:

unable to access jarfile myJar.jar

Das Problem lässt sich leicht beheben (auch wenn auf einigen Seiten das Gegenteil behauptet wird), indem man die myBatch.bat etwas umschreibt:

myBatch.bat:
java -jar
%~0dp0/myJar.jar

%dp0 ergibt dabei den Pfad der Batch-Datei. Sinnvoll/nötig ist ein Aufruf via Kommandozeile insbesondere dann, wenn man beim Aufruf noch Parameter übergeben will, wie z.B. hier

myBatch.bat:
java -jar
%~0dp0/myJar.jar %1 %2

Zum Feedback-Formular...

Mehrere ausführbahre Klassen (also solche mit einer main-Methode) in eine JAR packen und ausführen

Wo ich gerade bei JARs bin. Man kann quasi mehrere Programme in eine Jar packen. Die lassen sich dann folgendermaßen aufrufen:

java -cp myJar.jar packageXY.subpackageAB.myMainClass1
java -cp myJar.jar packageXY.subpackageAB.myMainClass2
...

Wobei man dann bei der Erstellung der Jar (z.B. mit FatJar) keine Main-Class angibt. Wenn die Programme dieselben Basis-Klassen und Libraries benutzen (wie z.B. in meinem "E3D" Paket) lässt sich damit enorm Speicherplatz sparen...

Zum Feedback-Formular...

Adobe PDF (Distiller) dreht Seiten (und ich will das nicht)

Aufgrund der teilweise Verwendung gedrehten Textes wurden bei mir (alle) Seiten beim Ausdruck über Adobe-PDF gedreht, was aber in diesem Fall grober Unfug war, da der Text an dem sich offenbar orientiert wurde, ein um 90 gedrehter Text am Seitenrand war. Lösen lässt sich das Problem so:
  1. Den Distiller selbst öffnen (z.B. im Eingabefeld des Windows-7-Start-Menüs "distiller" eintippern, ansonsten versteckt sich der Distiller z.B. hier: "C:\Program Files (x86)\Adobe\Acrobat 10.0\Acrobat\acrodist.exe"
  2. Im Menü "Voreinstellungen" des Distillers "Adobe PDF-Einstellungen bearbeiten..." auswählen
  3. Dort dann "Seiten automatisch drehen" auf "aus" stellen
  4. Mit "ok" bestätigen (wenn man noch die Standard-Einstellungen hatte wird man gefragt werden unter welchem Namen man die geänderten Einstellungen abspeichern möchte - so heißt dann auch das Job-Settings-Profil)
  5. Beim Drucken über Adobe-PDF dann erst noch auf "Eigenschaften" klicken
  6. Im Eigenschaftsfenster unter dem Reiter "Adobe PDF-Einstellungen" dann für "Standardeinstellungen" das eben angelegte Job-Settings-Profil wählen
  7. Eigenschaftsfenster wieder schließen und Drucken - jetzt sollte es nicht mehr gedreht werden...
Zum Feedback-Formular...

Start einer Eclipse-Application (RCP) aus Eclipse heraus kommt es zu folgendem Fehler: "Cannot load 32-bit SWT libraries on 64-bit JVM"

Ich musste aus bestimmten Gründen ein 32-Bit Eclipse (4.3 - Kepler) auf einem 64-Bit Rechner (Windows-7) installieren. Dazu sind diverse Java-Versionen (JREs/JDKs in den Versionen 1.6, 1.7, jeweils in 32- und 64-Bit) auf dem Rechner installiert. Nun habe ich ein Tutorial zur RCP-Entwicklung (Applikation, die auf dem Eclipse-Framework aufbaut) angefangen (recht gut übrigens, soweit wie ich bisher gekommen bin :-)):
http://www.vogella.com/articles/EclipseRCP/article.html
Erstes kleineres Problem (aber das nur am Rande): ich musste die "offizielle" Update-Site für die E4-Erweiterungen benutzen (wie die Seite zu erreichen ist, ist im o.g. Tutorial beschrieben), sonst bekam ich Fehler beim Erstellen der Beispiel-Appikation über den Wizard, dass irgendein Editor nicht geöffnet werden könne.

Etwas länger beschäftigt hat mich dann das oben genannte 32-/64-Bit-Problem. Ich konnte in den Projekt-Properties JREs/JDKs einstellen wie ich wollte, es kam beim Start der Applikation immer der hier nochmal erwähnte Fehler:
"Cannot load 32-bit SWT libraries on 64-bit JVM"
Die Lösung:
Man öffnet die .product-Datei. Dort geht man auf den Tab "Launching" (die Tabs sind hier unten aufgelistet). Dort stellt man das Execution-Environment ein - in meinem Fall JavaSE-1.7. Nur verbarg sich dahinter eine 64-Bit-Java-Version. Also musste ich zusätzlich noch über den Button "Environments" neben dem Auswahlfeld eine 32-Bit-Version als Default für JavaSE-1.7 wählen.

Was theoretisch auch geht:
man wählt in den Run-Configurations bzw. wahlweise auch den Debug-Configurations (oben über das Run- bzw. Debug-Menü zu erreichen) im Tab "Main" unter "Runtime JRE" eine 32-Bit-Java-Version aus.
Großes Aber:
Das funktioniert nur solange man das Programm über das Run-Menü startet! Denn jedesmal wenn man den beschriebenen und im Tutorial stark angeratenen Weg über die .product-Datei geht, wird das wieder überschrieben :-( Daher ist der obere Weg also deutlich vorzuziehen...

Zum Feedback-Formular...

Eclipse-Plugin-Projekt zieht falsches Plugin unter Plugin-Dependencies an

Ich habe zwei Plugin-Projekte erstellt in denen mehrere identische Packages und Klassen enthalten sind. Allerdings hat das eine Plugin einen erweiterten Umfang - d.h. zusätzliche Packages und Klassen. Das ältere Plugin wollte ich für ein anderes Projekt unverändert behalten, in einem zweiten Projekt jedoch auf die neuere, erweiterte Version umstellen. Leider weigerte sich Eclipse beharrlich, das alte Plugin-Projekt zu "vergessen" - es blieb unter "Plugin-Dependencies" im Package-Explorer sichtbar, obwohl es weder im Manifest, noch in der .product-Datei aufgeführt war. Ergebnis war, dass sich die Applikation (RCP) nicht starten ließ, weil Klassen nicht gefunden wurden.
Ursache, war dass die hier über die Manifest-Datei über die Dependencies die Packages referenziert wurden. Diese Packages kommen aber in beiden Plugin-Projekten vor.

Lösung 1 (korrekt):
Man sorgt dafür, dass die beiden Plugin-Versionen auch tatsächlich unterschiedliche Versionen haben (über deren Manifest-Dateien) und schränkt dann in der Manifest des nutzenden Plugins die Versionen an den angegebenen Packages im Manifest entsprechend ein (rechte Maustaste auf das Package im Dependencies-Tab des Manifest-Editors → Properties).

Lösung 2 (auch gut):
Man löscht die Package-Abhängigkeiten und fügt stattdessen Abhängigkeiten zu dem Plugin (jeweils dam in der richtigen Version ein), wobei diese dann unterschiedliche IDs haben müssen (→ Manifest). Alternativ kann man auch bei den Plugin-Abhängigkeiten die Versionen einschränken (s.o.)

Lösung 3 (Notlösung):
Hat man keinen Einfluss auf Id und Version des zu benutzenden Plugins funktioniert zur Not auch das alte Plugin-Projekt kurz zu schließen, ggf. ein "Clean" durchzuführen, die Applikation einmal zu starten, um zu sehen ob alles funktioniert. Danach kann das alte Plugin-Projekt wieder geöffnet werden. Eclipse sollte nun die Zuordnung "gefressen" haben und beibehalten, solange man das eingebundene Plugin-Projekt nicht mehr ändert. Muss man es ändern, taucht evtl. Plötzlich wieder das falsche Plugin-Projekt in den Dependencies auf (wenn man es ändern kann geht aber auch Lösung 1 oder 2).

Lösung 4 (immer möglich):
Man benutzt zwei verschiedene Workspaces.

Zum Feedback-Formular...

In Java über alle Dateien in ineinander verschachtelten Zip-Archiven (nested zip archives) iterieren

Die erste Frage, die sich mir beim Öffnen verschachtelter ZIP-Archive ("Zip in Zip", "nested Zip") stellte, lautet: Wie komme ich eigentlich an die ZipEntries einer inneren Zip-Datei innerhalb einer anderen Zip-Datei - und das über beliebig viele Verschachtelungsebenen?
Die Antwort ist eigentlich ganz einfach: An einem solchen ZipEntry aus einem ZipInputStream zis1 angekommen, wird einfach ein weiterer ZipInputStream zis2 initialisiert: ZipInputStream zis2 = new ZipInputStream(zis1). Dies wird in der folgenden rekursiven Methode verwendet:

01/** 02 * Recursively iterate over all entries in a zip archive and nested 03 * zip archives. 04 * Notice: There is no real directory structure; every entry has the 05 * full path in its name; directory names end with "/" 06 */ 07void iterate (InputStream in) 08{ 09 ZipInputStream zip = new ZipInputStream(in); 10 ZipEntry entry; 11 while ((entry = zip.getNextEntry()) != null) { 12 if (entry.isDirectory()) { 13 // just skip that - we do not need to go into directories 14 // because we get all entries in all directories using 15 // 'ZipInputStream.getNextEntry()' 16 } else if (isZip(entry)) { // e.g. by entry.getName().endsWidth(".zip") 17 // recursively creates another ZipInputStream on 18 // top of this ZipInputStream for the current entry 19 iterate(zip); 20 } 21 } 22}


Hier wird eine Methode boolean isZip(ZipEntry) angenommen, die z.B. aufgrund des Namens (String ZipEntry.getName()) erkennt, ob es sich um ein eingebettetes Zip-Archiv handelt. Die zweite Frage lautet: Wie stelle ich fest, ob eine Datei ein Zip-Archiv oder irgendein anderes Datei-Format ist, ohne es anhand des Names (z.B. "*zip") zu bestimmen? Hierfür kann testweise ein neuer ZipInputStream angelegt und versucht werden das erste ZipEntry daraus zu lesen. Gelingt das, handelt es sich um ein Zip-Archiv, sonst ist es ein anderes Dateiformat. Folgende Methode liefert null falls es kein Zip-Archiv ist und sonst das (erste) ZipEntry.

01/** 02 * Wrapper for {@link ZipInputStream#getNextEntry()}, 03 * returning 'null' in case of {@link ZipException}s. 04 * @throws IOException other than {@link ZipException} 05 */ 06ZipEntry getNextZipEntry (ZipInputStream zipStream) throws IOException 07{ 08 try { 09 return (zipStream.getNextEntry()); 10 } catch (ZipException exception) { 11 return (null); 12 } 13}


Diese Methode sollte nur verwendet werden, um das jeweils erste ZipEntry aus einem ZipInputStream zu holen. Die dritte Frage die daraufhin aber noch zu klären ist: Wie verarbeite ich die Datei, wenn es kein Zip-Archiv ist, obwohl ZipInputStream.getNextEntry() bereits Bytes aus dem InputStream lesen musste, um feststellen zu können, dass es doch kein Zip-Archiv ist?
Hierfür schalten wir jeweils eine erweiterte Abwandlung der Java-Standardklasse PushbackInputStream vor. Diese Klasse sieht so aus:

01/** 02 * A {@link PushbackInputStream} which is buffering 03 * readout byte chunks, which could be pushed back 04 * using method {@link #pushback()}. 05 */ 06class BufferingPushbackInputStream extends java.io.PushbackInputStream 07{ 08 /** Buffering? */ 09 private boolean buffering = true; 10 11 /** Buffer (list of read byte-arrays) */ 12 private final List<byte[]> buffer; 13 14 /** 15 * Constructor 16 * @param in {@link InputStream} 17 */ 18 public BufferingPushbackInputStream (InputStream in) { 19 this(in, 1024, 1); 20 } 21 22 /** 23 * Constructor 24 * @param in {@link InputStream} 25 * @param byteBufferSize buffer size of the {@link PushbackInputStream}, 26 * must be big enough to hold all bytes which 27 * should be pushed back 28 * @param readBufferSize max. number of calls to one of the read-methods 29 * before {@link #pushback()} is called 30 */ 31 public BufferingPushbackInputStream (InputStream in, 32 int byteBufferSize, 33 int readBufferSize) 34 { 35 super(in, byteBufferSize); 36 buffer = new ArrayList<byte[]>(readBufferSize); 37 } 38 39 @Override 40 public int read () throws IOException { // (not used) 41 int b = super.read(); 42 if (b != -1 && buffering) { 43 buffer.add(new byte[]{(byte)b}); 44 } 45 return (b); 46 } 47 48 @Override 49 public int read (byte[] b) throws IOException { // (not used) 50 return (read(b, 0, b.length)); 51 } 52 53 @Override 54 public int read (byte[] b, int off, int len) throws IOException { // (used!) 55 int s = super.read(b, off, len); 56 if (s > 0 && buffering) { 57 buffer.add(Arrays.copyOfRange(b, off, off + s)); 58 } 59 return (s); 60 } 61 62 @Override 63 public synchronized void close () throws IOException { <== Trick (s.u.) 64 // we never want this stream to be closed directly, 65 // because this class always is a wrapper around an 66 // inner stream, which is closed elsewhere anyway 67 // this fixes some problems with methods which do 68 // automatic closing (e.g. 'SAXBuilder.build()') 69 } 70 71 /** Switch on/off buffering */ 72 public void setBuffering (boolean buffering) { 73 this.buffering = buffering; 74 } 75 76 /** Pushback all buffered data */ 77 public void pushback () throws IOException { 78 for (byte[] b : buffer) unread(b); 79 buffer.clear(); 80 } 81}


Diese Klasse erlaubt es uns, die zum Testen, ob es sich um ein Zip-Archiv handelt, ausgelesenen Bytes zu speichern und ggf. zurückzuschreiben falls es sich um eine "normale" Datei handelt. Da es sich hier um eine Wrapper-Klasse für einen anderen Stream handelt, unterbinden wir das Schließen des Streams durch Überschreiben der Methode close(). Das Schließen würde nämlich alle übergeordneten Streams ebenfalls schließen und die Iteration mit einer Exception abbrechen. Genau das passiert z.B. bei Verwendung der Methode SAXBuilder.build(...) zum Parsen einer XML-Datei.
Unter Verwendung der neuen Klasse BufferingPushbackInputStream können wir die oben gezeigte Methode iterate(InputStream) folgendermaßen abwandeln, so dass Zip-Archive nun automatisch erkannt werden:

01/** 02 * Iterate over a {@link InputStream} checking it to 03 * be a ZIP archive first and handle it accordingly. 04 */ 05void iterate (InputStream inStream) throws IOException 06{ 07 // check is it a zip archive or a normal file? 08 BufferingPushbackInputStream pushbackStream = new BufferingPushbackInputStream(inStream); 09 ZipInputStream zipStream = new ZipInputStream(pushbackStream); 10 ZipEntry entry = getNextZipEntry(zipStream); 11 if (entry != null) { // zip? 12 pushbackStream.setBuffering(false); 13 do { // for all zip entries do... 14 if (entry.isDirectory()) { 15 // skip directories (see above) 16 } else { 17 // recursively iterate over sub-elements 18 iterate(zipStream); 19 } 20 } while ((entry = zipStream.getNextEntry()) != null); 21 } else { // normal file (not a zip) 22 pushbackStream.pushback(); // undo zipStream.getNextEntry() 23 // TODO do something with the file given by the 'pushbackStream' 24 // (dependent on its type: read it, parse it with a XML-parser, ...) 25 } 26}


Initial aufgerufen wird diese Methode z.B. so:

01File file = new File("Archiv.zip"); 02InputStream stream = new FileInputStream(file); 03try { 04 iterateStream(stream, file.getName()); 05} finally { 06 stream.close(); <== Haupt-Stream schließen! 07}


Im Gegensatz zu den "Sub-Streams" ist hier natürlich wichtig sicherzustellen, dass der FileInputStream auch wieder geschlossen wird.

Das Ganze ließe sich z.B. noch derart erweitern, dass jeweils entschieden werden kann, ob ein eingebettetes Zip-Archiv durchsucht werden soll oder nicht. Etwas komplizierter ist das Auslassen von "Verzeichnissen" in einem Zip, da die gezippten Dateien im Prinzip in einer flachen Hierarchie abgelegt sind und lediglich immer den kompletten Pfad im Namen haben, anhand dessen dann die Verzeichniszugehörigkeit erkannt werden muss. Dazu vielleicht ein anderes Mal mehr... stay tuned :-)

Zum Feedback-Formular...

Seamonkey Debugger (Venkman) hängt wenn "Bei Exceptions stoppen" eingestellt wurde

Problem:
Der Javscript-Debugger hängt sich und den Seamonkey-Browser komplett auf, nachdem dort unter Debug → Auslöser bestätigen "Bei Exceptions stoppen" gewählt wurde. Damit kann dann leider auch die Einstellung nicht mehr auf dem normalen Weg, über das Menü, rückgängig gemacht werden...

Lösung:
Config-Seite aufrufen: about:config
und dort die folgende Eigenschaft suchen und ändern:
extensions.venkman.lastThrowMode "break" / "trace" → "ignore"

Update
Der Venkman-Editor wird zwar nach wie vor mit dem Seamonkey ausgeliefert, funktioniert aber nicht mehr, da die von ihm benutzte API deaktiviert wurde. Es gibt mehrere Debugger die stattdessen installiert und benutzt werden können. Ich nutze Firebug (der allerdings das Problem hat, dass das Menü-Item dauernd verschwindet; starten via F12 funktioniert aber unabhängig davon (s.a. https://bugzilla.mozilla.org/show_bug.cgi?id=1132983). Firebug hat auch den großen Vorteil nur eine Seite zu debuggen (während Venkman bei zig geöffneten Tabs völlig versagte).

Zum Feedback-Formular...

Eclipse-Projekt von Subversive auf Subclise umstellen (SVN)

Problem:
Irgendwie bekam ich die Projekte mit Subversive nicht korrekt eingebunden. Auschecken ging, aber danach konnte ich keine Synchronisation, kein Update, etc. durchführen. Fehlermeldung war immer diese:

407 Proxy Authentication Required

Offenbar hat es irgendwas mit dem Proxy zu tun hinter dem ich sitze. Dieser ist jedoch unter den Eclipse-Preferences (Menü Window → Preferences → Suche nach Proxy) korrekt eingestellt ("Active Provider" steht auf "Manual", Die Zeile fü "HTTP" ist korrekt angegeben).

"Kleine" Lösung:
Bevor man den Weg der Umstellung von Subversive auf Subclipse versucht, kann man mal dort in den Preferences die Zeile "SOCKS" selektieren und rechts auf den Button "Clear" klicken. Offenbar hat das bei ähnlich gelagerten Problemen teilweise geholfen - bei mir aber nicht...

"Große" Lösung:
Bei mir funktionierte dann alles wie gewünscht. Falls Subclipse noch weniger funktioniert kann man entsprechend wieder von Subclipse zu Subversive umziehen...

Zum Feedback-Formular...

Neue Generator Functions in ECMAScript 6 (Javascript) zur sequenziellen Abarbeitung asynchroner (Callback-basierter) Tasks

Man findet im Netz so einiges, wo externe Libraries verwendet werden, die dann "Promises" oder ähnliche Konstrukte zur Verfügung stellen. Aber eher wenig, wo mal gezeigt wird, wie man mit Generator Functions direkt die Aufrufe asynchroner Funktionen vereinfachen und der "Callback-Hell" entfliehen kann. Problematisch ist das mit dem "normalen" Ansatz der Callbacks insbesondere dann, wenn man verschachtelte asynchron laufende Funktionen hat: Nachteil:
hier muss die Callback-Funktion die man A übergibt dann B aufrufen usw., was schnell unübersichtlich wird (u.a. wegen der tiefen Verschachtelung, wenn man alles "inline" schreibt) und das Fehlerhandling und auch das Sammeln von Ergebnissen kompliziert macht (Fehler/Ergebnisse müssen durch alle Aufrufe gefangen/gesammelt und nach oben durchgereicht werden).

Nachfolgend ein kleines Beispiel, wie man Callback-basierte asynchrone Funktionen über eine Generator Function serialisieren kann (läuft zumindest im FF und Seamonkey und wahrscheinlich auch in Chrome). Der interessante "Trick" besteht eigentlich nur darin, dass die Callbackfunktion resumer() die Methode iterator.next() aufruft (Z.31), womit sichergestellt wird, dass die einzelnen Tasks nacheinander abgearbeitet werden...

Vorteil:
Man hat alles, was man in diversen Callbacks hätte machen müssen, gebündelt in der Generator Function (Aufruf der Tasks, Fehlerhandling, Ergebnisse sammeln) und es sieht dort nach "normalem" iterativem Programmieren aus.

01/** 02 * Process all given tasks (async. functions) in a serialized form, 03 * i.e. task[i] will not be started before task[i-1] has been finished. 04 * @param callback a callback function with two parameters: 05 * <ul><li>results of all tasks (array with the same length as 'tasks')</li> 06 * <li>errors of all tasks (array with the same length as 'tasks')</li> 07 * </ul> 08 * @param tasks array of (async.) functions with the following parameters each: 09 * <ul><li>a callback function with two parameter: result and error</li> 10 * <li>array of results of all previous tasks</li> 11 * <li>array of errors for all previous tasks</li> 12 * </ul> 13 */ 14function serialize (callback, tasks) 15{ 16 // TODO replace most 'var' by 'let' (not yet supported in Seamonkey!?) 17 18 // generator calling several async processes sequentially 19 // (starting the next only after the previous one has finished) 20 var iterator = undefined; // iterator (created by 'generator()' below) 21 var generator = function*(tasks) { // generator function 22 var results = []; // array of results 23 var errors = []; 24 25 // callback and error handler for all tasks resuming at next 'yield' 26 // either by calling next (sending the result) or by throwing an error 27 var resumer = function(result, error) { 28 if (error) { 29 iterator.throw(error); // throw error 30 } else { 31 iterator.next(result); // resume with next task 32 } 33 }; 34 35 // do the work: call tasks making sure previous task has 36 // finished before calling the next one 37 for (var i = 0; i < tasks.length; i++) { 38 var task = tasks[i]; 39 task(resumer, results, errors); 40 try { 41 42 // evaluation stops here for every call to 'iterator.next()' 43 // 'yield' also receives errors thrown by 'iterator.throw()' 44 var result = yield (null); 45 46 results.push(result); 47 errors.push(undefined); 48 49 } catch (error) { 50 51 // error handling (done here at a central point for all tasks) 52 results.push(undefined); 53 errors.push(error); 54 } 55 } 56 57 // finish 58 callback(results, errors); 59 return; 60 }; 61 62 // create iterator for the given 'tasks' 63 iterator = generator(tasks); 64 65 // start (evaluate 'iter' until first 'yield' - i.e. call first task in 'tasks') 66 iterator.next(); 67} 68 69/** Just a little async. task */ 70function async (callback, n) { 71 if (n == 23) { 72 document.body.innerHTML += " ERROR " + n + " is bad | "; 73 callback(undefined, new Error(n + " is bad!")); 74 } else if (n <= 0) { 75 document.body.innerHTML += " STOP | "; 76 callback("done", undefined); 77 } else { 78 console.log("N=" + n); 79 document.body.innerHTML += " " + n; 80 setTimeout(function(){ async(callback, n-1); }, 100); 81 } 82} 83 84/** 85 * Example showing how to sequentially call several async processes 86 * by using new ECMAScript6 generator functions. 87 * 88 * To see the outputs made by 'cosole.log()' in seamonkey open URL 89 * "about:config" and set browser.dom.window.console.enabled = true. 90 * Then open extra->web development->error console 91 */ 92function initGeneratorCallbackTest () 93{ 94 console.clear(); // XXX doesn't work (Seamonkey)!? 95 96 // create wrapper functions for 'async' to set parameter 'n' 97 var tasks = [function(callback, results, errors){ async(callback, 10); }, 98 function(callback, results, errors){ async(callback, 25); }, 99 function(callback, results, errors){ async(callback, 20); }, 100 function(callback, results, errors){ async(callback, 30); }]; 101 102 serialize(function(results,errors){ 103 console.log("FINISHED"); 104 document.body.innerHTML += " FINISHED - RESULTS: [" + results + "], ERRORS: [" + errors + "]"; 105 }, tasks); 106}


Anmerkungen dazu:
Und hier eine dazu passende Mini-Html-Datei zum testen (obigen Sourcecode in Datei "GeneratorCallbackTest.js" kopieren und in dasselbe Verzeichnis legen, dann diese Html-Seite im Browser öffnen) :

  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de" dir="ltr">
    <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
      <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
      <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
      <title>Javscript Generator Callback Test</title>
      <script type="text/javascript" src="GeneratorCallbackTest.js"></script>
    </head>
    <body onload="initGeneratorCallbackTest();">
    </body>
  </html>
  


Und hier das Ganze zum direkt ausprobieren: GeneratorCallbackTest.html

Nachtrag:
Für den hier gezeigten Fall funktioniert die nachfolgend gezeigte Variante von serialize() (die ohne Generator-Functions auskommt) genauso und ist dabei auch noch kürzer. Bei einem weniger generischen Ansatz würde der Ansatz mit den Generator-Functions eher einen Vorteil bringen.

01function serialize (callback, tasks) 02{ 03 var i = 0; 04 var results = []; 05 var errors = []; 06 var resumer = function(res,err){ 07 if (err) { 08 results.push(undefined); 09 errors.push(err); 10 } else { 11 results.push(res); 12 errors.push(undefined); 13 } 14 if (++i < tasks.length) { 15 tasks[i](resumer); 16 } else { 17 callback(results, errors); 18 } 19 } 20 tasks[0](resumer); 21}

Zum Feedback-Formular...

Seamonkey findet keine Plugins mehr (u.a. Flashplayer)

Symptome:
Diagnose:
Zunächst mal prüfen ob es am Profil liegt. Dazu im Menü Tools → "Switch Config..." auswählen. Dort auf ein anderes Profil wechseln (ggf. vorher ein neues anlegen, was auch aus diesem Dialog heraus möglich ist).

Lösung:
Wenn es am Profil liegt, besteht die Standardmethode darin alle Dateien "extensions.*" zu löschen (oder zu verschieben). Das hat bei mir allerdings nichts gebracht. Bei mir lag es an der Datei prefs.js, die ebenfalls im Profil-Verzeichnis liegt. Was an dieser Datei genau defekt ist habe ich nicht herausgefunden, da ich eine Sicherungskopie hatte, von der ich die prefs.js herüberkopieren konnte. Ohne Sicherungskopie, bliebe evtl. nur mit einer neuen prefs.js bzw. sogar einem ganz neuen Profil anzufangen, wobei man z.B. das Mail-Verzeichnis übernehmen kann.
Das Profil befindet sich übrigens normalerweise hier:

Zum Feedback-Formular...

Kein Netzwerk nach dem Wecken aus dem Ruhe- bzw. Energiesparzustand (OpenSuse 13.2)

Schon lange verliert mein Laptop sporadisch das Netzwerk nachdem ich ihn aus dem Ruhe- bzw. Energiesparmodus wecke. Seit dem Update auf OpenSuse 13.2 passiert das allerdings "zuverlässig" jedesmal. Wenn WLAN vorher deaktiviert war (Hardwareschalter) lässt sich zumindest das WLAN noch einmal aktivieren. Nach dem nächsten "Nickerchen" ist dann aber auch das weg. Ich habe lange gesucht und diverse Hinweise gefunden, nur leider bei mir alle nicht funktionieren (entweder weil sie keinen Effekt haben, oder weil die entspr. Kommandos auf meinem System nicht verfügbar sind.

Randbedingungen:
Bei mir läuft der NetworkManager (prüfbar z.B. mit "systemctl status NetworkManager" - auch als Nicht-root, also ohne "sudo"). System: OpenSuse 13.2 (KDE).

Lösung:
Neustart des NetworkManagers mit:
sudo systemctl restart NetworkManager

Um das nicht jedesmal von Hand machen zu müssen, kann man den Neustart des NetworkManagers nach dem Wecken in ein systemd-Skript auslagern (die Datei als root neu anlegen):
/usr/lib/systemd/system/network-manager-resume.service
(in /etc/systemd/system/ ginge auch, /usr/lib/systemd/system/ sollte aber bevorzugt verwendet werden)

Dort folgenden Inhalt eintragen:
[Unit]
Description=Restart NetworkManager after suspend
After=suspend.target

[Service]
Type=oneshot
ExecStart=systemctl restart NetworkManager

[Install]
WantedBy=suspend.target

Und dann den neuen Service scharfschalten:
sudo systemctl enable network-manager-resume.service

Danach (spätestens aber nach dem nächsten Reboot) sollte das dann funktionieren...
Zum Feedback-Formular...

WLANs nicht (de-)aktivierbar (Not authorized to control Networking)

In OpenSuse 13.2 (KDE) lässt sich über das Toolbar-Icon kein WLAN-Netzwerk aktivieren/deaktivieren. Die Fehlermeldung lautet:
Not authorized to control Networking

Lösung:
Was bei half war die /etc/NetworkManager/NetworkManager.conf zu editieren.
Vorher:
					[main]
					plugins=ifcfg-suse,keyfile
				

Nachher:
					[main]
					plugins=ifcfg-suse,keyfile
					
					[ifcfg-suse]
					managed=true
                                        
				

(Hinweis: In einigen anderen Lösungsbeschreibungen im Internet ist "ifcfg-suse" durch "ifupdown" ersetzt, da dort das Debian-Pendant verwendet wird)
Nach dem nächsten Reboot funktionierte das dann...

Den Benutzer weiteren Gruppen wie "network" oder "netdev" hinzuzufügen brachte nichts (schon weil es diese Gruppen bei mir nicht gibt). Der vollständigkeit halber aber auch noch das Kommando dafür:
sudo usermod -G netdev -a
Zum Feedback-Formular...