InputStream Time out

Hallo,

ich möchte (oft) ein Bild/Image einer Website laden. Leider “friert” der InputStream dabei manchmal ein. Das möchte ich umgehen/Workaround.

InputStream bietet leider keine Lese-Methode mit Time out an, deshalb bin ich einen Umweg über Executor und Futur gegangen:

    public void run() {
        bild = new Bild();
        InputStream is = null;
        try {
            is = new URL(URL).openStream();
            final InputStream is2 = is;
            Future<Integer> futur = SERVICE.submit(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    return is2.read();
                }
            });
            for (int b = futur.get(500, TimeUnit.MILLISECONDS); b != -1; b = futur.get(500, TimeUnit.MILLISECONDS)) {
                if (!bild.fuegeByteHinzu((byte) b)) {
                    break;
                }
            }
        } catch (Exception exc) {
            System.out.println(exc);
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException ioe) {
                    System.out.println(ioe);
                }
            }
        }
        this.setChanged();
        this.notifyObservers();
    }```

Problem:

- futur.get( gibt immer nur 0 (oder 1) zurück
- die Anwendung läuft, wenn die main-Methode durch ist, ewig

Was mache ich falsch?

Grüße

for (int b = futur.get(500, TimeUnit.MILLISECONDS); b != -1; b = futur.get(500, TimeUnit.MILLISECONDS)) {
ist ja eine schlimme Zeile, nicht derartigen Code wiederholen, und sei es so kleines innerhalb einer Zeile,
da muss es doch andere Lösungen geben

und

while ((b = futur.get(500, TimeUnit.MILLISECONDS)) != -1) {```
sind zwar mehr Zeilen, sieht doch aber viel besser aus,
bietet sich auch sonst überall an, etwa beim normalen Lesen aus Stream

----------

bei Timeout gibt es Exception? läuft der call() dann weiter, wer kriegt wann den Wert?
welches ist das erste zu lesende byte?
ruhig im call alles loggen, erstmal dort den gelesenen Wert ausgeben, dann kannst du beurteilen, ob der richtigte weitergeleitet wird,
vielleicht auch Zeitmessung fürs Lesen

das Future ist sicherlich nur die Ausgabe zu EINER Callable-Ausführung, immer wieder das zu fragen bringt nichts neues,
du musst bestimmt jeweils das Callable neu absenden (kann vielleicht dasselbe mehrfach sein oder zur Sichtheit unterschiedliche Objekt) 
und neue Futures abfragen

insbesondere dann, wenn du nicht mehr das alte Future weiter verwendest, frage ich mich was im Timeout-Fall passiert,
read() am Stream abgebrochen oder später fertig und gelesener Wert geht ins Nirvana?

-----------

vielleicht ist ein normaler Thread doch besser: die Thread-Schleife liest unbeirrt soviel möglich ist, schreibt die Bytes in eine Liste,
ein zweiter schaut nur in die Liste, wenn nix da dann wait+notify (falls bekannt), 
wenn nötig die Thread-Schleife pausieren lassen falls mal fertig mit einem Lesen und genug Daten im Cache usw.

aber all das zu bauen willst du vielleicht vermeiden ;)


-------

ExecutorService muss man wohl beenden
http://www.coderanch.com/t/596968/threads/java/Threads-executors-hanging-main-method

Ich habe zwar selbst nicht viel Ahnung was Futures bzw. FutureTasks angeht. Aber Du hast das Konzept nicht verstanden.
Wenn ein FutureTask via Executer ausgeführt, dann wird einmalig die call() des Callable ausgeführt und der Rückgabewert im FutureTask “gespeichert”. Auch ein mehrfacher Aufruf von get() liefert immer den selben Wert.
In Deinem Code wird das erste Byte des Streams gelesen. Sofern das erfolgreich war, kann get() nie mehr -1 liefern.
Entweder Du liest in der call() den gesamten Stream aus und legst ein TimeOut dafür fest.
Oder Du erzeugst in einer Schleife solange FutureTasks und führst diese aus bis der Stream komplett ausgelesen ist oder einer der FutureTasks eine TimeoutException geworfen hat.
Aber ob es dann nicht besser ist das mit Threads zu bauen…

Hää? Was habe ich nicht verstanden? Auf stack overflow wird das genau so angegeben, und jene Antwort war die beste. Kopfschüttela facepalm

poste doch den Link, falls es dort auch eine Schleife ist, kann man vielleicht den Unterschied erklären,

du gibst nur ein Callable ab, das wird maximal einmal ausgeführt, nur 1x read(),

du musst in der Schleife immer neue Callable absende, bei Timeout evtl. beim aktuellen bleiben, es so lange befragen bis es etwas liefert

OK, ich habe es mal geändert in:

    public void run() {
        try {
            bild = SERVICE.submit(new Callable<Bild>() {
                @Override
                public Bild call() throws Exception {
                    Bild bild = new Bild();
                    InputStream is = null;
                    try {
                        is = new URL(URL).openStream();
                        for (int b = is.read(); b != -1; b = is.read()) {
                            if (!bild.fuegeByteHinzu((byte) b)) {
                                break;
                            }
                        }
                    } catch (IOException ioe) {
                        System.out.println(ioe);
                    } finally {
                        if (is != null) {
                            try {
                                is.close();
                            } catch (IOException ioe) {
                                System.out.println(ioe);
                            }
                        }
                    }
                    return bild;
                }
            }).get(2000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException ie) {
            System.out.println(ie);
        } catch (ExecutionException ee) {
            System.out.println(ee);
        } catch (TimeoutException te) {
            System.out.println(te);
        }
        this.setChanged();
        this.notifyObservers();
    }```

```private static final ExecutorService SERVICE = Executors.newSingleThreadExecutor(); // Klassenkonstante```

Damit bekomme ich:

java.util.concurrent.TimeoutException und
Exception in thread "main" java.lang.NullPointerException (bild == null) und
die Anwendung läuft ewig

2 Sekunden sollten doch für Bild-Aufrufen/Laden reichen? Zweimal funktionierte es anscheinend auch. Könnt ihr mir sagen, was ich falsch gemacht habe?

Hier ist der Link: http://stackoverflow.com/questions/804951/is-it-possible-to-read-from-a-inputstream-with-a-timeout

Rufst du denn am Schluss shutdown auf?
Afaik. kann man in Java einen Thread ja nicht richtig “abschießen”, deshalb läuft dieser auch nach dem Timeout ggf. noch weiter. Das führt dann letztendlich dazu, dass am “Ende” deines Programmes der Thread noch läuft und die Anwendung sich deswegen nicht schließt. Ein shutdown auf dem ExecutorService sollte das Problem allerdings beheben.

Nein, das habe ich jetzt vergessen,- weil ich den Service auch in einer anderen Klasse schließen möchte, müsste dieser public sein… das ist dann auch wieder schmuddelig.
Die Null"pointer"-Ausnahme lag tatsächlich daran, dass der Timeout von 2 Sek. für ca. 300kByte zu klein/kurz war.
Irgendwas muss ich bei SOV falsch verstanden haben: Dort wird für JEDES Byte (das der IS liefern kann) executor.submit( aufgerufen und ein Future erstellt/instanziiert. -.-
Schönen Abend!

Ok, das Thema war von mir.

Dort wird für JEDES Byte (das der IS liefern kann) executor.submit( aufgerufen und ein Future erstellt

das stimmt zwar, aber man muss unterscheiden, ob time out für ein Byte oder für „ALLE“ Bytes.

Nächstes Problem (1): Ich erstelle einen öffentlichen Service, der heruntergefahren werden soll. Bzw. wenn ich in Klasse A (main-Methode) eine öffentliche Variable habe, muss ich in Klasse B (Verwendung) darauf zugreifen. Umgekehrt muss ich eine öffentliche Variable in A herunterfahren. Gibt es nicht so etwas wie einen Dekonstruktor in/der Klasse B?

Und wäre es schlimm, JEDES Mal einen neuen Service (Executors.newSingleThreadExecutor();) zu erstellen und nach dem Aufrufen eines Bildes wieder herunterzufahren? (Performance, Speicher, Platz)

Andere Frage (2): bEnennt ihr alle Variablen und Methoden immer in Englisch?

Noch ein Problem (3): Wenn ich die Zuweisung der Variablen innerhalb des try- mache:

            bild = SERVICE.submit(new Callable<Bild>() {
                @Override
                public Bild call() throws Exception {
                    Bild bild = new Bild();```

dann kann bei einer Interrupted(/TimeoutException)Exception auch null bzw. gar nix zurückgegeben werden. :(

Anhang (4): Ich sitze nun schon mehrere Wochen an diesem Prog und bekomme es immer noch nicht hin. :/

[QUOTE=CyborgBeta]
das stimmt zwar, aber man muss unterscheiden, ob time out für ein Byte oder für “ALLE” Bytes.[/QUOTE]
ja, das ist aber keine neue Erkenntnis.
Alternativ könnte man auch versuchen eine feste Anzahl an Bytes zu lesen. Ein timeout hierfür läßt sich leicht festlegen.

zu 1) kannst du das mal konkreter und strukturiert darstellen. Was soll “Variable … herunterfahren” sein?
zu 2) ja, die Java API ist auf Englisch und jeder der im IT Umfeld zu tun hat versteht i.d.R Englisch
zu 3) ja, aber wo ist das Problem

Aber nur, weil heute Sonntag ist/du es bist:
Service muss (ordnungsgemäß) heruntergefahren werden, wenn keine Tasks mehr hinzugefügt werden, damit die Anwendung net ewig läuft. Ich kann deswegen eine public-static-final-Klassen konstant variable in A oder B erstellen(/deklarieren/definieren/initialisieren). In A: unschöner Zugriff in B darauf (A hat eigentlich mit dem Service nix zu tun). In B: Das Herunterfahren der Variable in A. :frowning:

*** Edit ***

Funkt, aber ich sage nicht, dass das vernünftig ist:

 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pkg05derloader;

import java.io.*;
import java.net.URL;
import java.util.Observable;
import java.util.concurrent.*;

/**
 * @author CB
 */
public class BildLaden extends Observable implements Runnable {

    private final String URL;
    private final Bild BILD = new Bild();

    public BildLaden(String url) {
        if (url == null) {
            throw new NullPointerException("url");
        }
        this.URL = url;
    }

    @Override
    public void run() {
        ExecutorService service = Executors.newSingleThreadExecutor();
        try {
            service.submit(new Callable<Boolean>() {
                @Override
                public Boolean call() throws Exception {
                    InputStream is = null;
                    try {
                        is = new URL(URL).openStream();
                        for (int b = is.read(); b != -1; b = is.read()) {
                            if (!BILD.fuegeByteHinzu((byte) b)) {
                                break;
                            }
                        }
                    } catch (IOException ioe) {
                        System.out.println(ioe);
                        return false;
                    } finally {
                        if (is != null) {
                            try {
                                is.close();
                            } catch (IOException ioe) {
                                System.out.println(ioe);
                                return false;
                            }
                        }
                    }
                    return true;
                }
            }).get(10 * 1000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException ie) {
            System.out.println(ie);
        } catch (ExecutionException ee) {
            System.out.println(ee);
        } catch (TimeoutException te) {
            System.out.println(te);
        }
        service.shutdown();
        this.setChanged();
        this.notifyObservers();
    }

    public Bild getBild() {
        return BILD;
    }
}```

class BildLaden ist ja eigentlich schon ein Thread/Runnable und service.submit('s Rückgabe wird nicht weiterverwendet.

Wie würdet ihr das machen? Das break; in Zeile 39 zu return false;? Im Bytecode wird (momentan) die gesamte for-Schleife zu einer Zeile.

Das sieht alles wie sehr dahingemogelt aus.

Wäre es nicht einfacher entsprechende Timeouts für die URLConnection zu setzen?

URLConnection connection = new URL(URL).openConnection();
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
InputStream is = connection.getInputStream();

Ich hab’s mal geändert (das ist mir vorher nicht aufgefallen, dass es diese Möglichkeit mindestens seit 1.5 schon gibt):

 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pkg05derloader;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Observable;

/**
 * @author CB
 */
public class BildLaden extends Observable implements Runnable {

    private final String URL;
    private final Bild BILD = new Bild();

    public BildLaden(String url) {
        if (url == null) {
            throw new NullPointerException("url == null");
        }
        this.URL = url;
    }

    @Override
    public void run() {
        URLConnection urlc = null;
        InputStream is = null;
        try {
            urlc = new URL(URL).openConnection();
            urlc.setConnectTimeout(5000);
            urlc.setReadTimeout(5000);
            is = urlc.getInputStream();
            for (int b = is.read(); b != -1; b = is.read()) {
                if (!BILD.fuegeByteHinzu((byte) b)) {
                    break;
                }
            }
        } catch (Exception exc) {
            exc.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException ioe) {
                    ioe.printStackTrace();
                }
            }
        }
        this.setChanged();
        this.notifyObservers();
    }

    public Bild getBild() {
        return BILD;
    }
}```

Mich würde interessieren, [U]welchen Timeout-Wert ich jetzt setzen soll[/U]. Das Bild/Image ist etwa [U]300kb groß[/U], der [U]Ping beträgt 50 bis 100ms[/U] (von einem Server aus sogar weniger als 10ms).

Außerdem interessiert mich, welches von beiden jetzt [U]mehr Ressourcen benötigt[/U] (Speicherplatz).

Hat noch jmd 'ne vernünftige Idee?