Resource Caching and Timeout Handling

Kahuna caches resources on the client side to minimize network traffic.
UrlResource
# server : UrlResourceServer
+ getInputStream(Resource) : InputStream
+ getLiveInputStream() : InputStream
UrlResourceServer
# cacheManager : ResourceCacheManager
# timeout : long
+ UrlResourceServer(ResourceCacheManager)
+ getInputStream(Resource) : InputStream
+ getLiveInputStream(URL) : InputStream
+ increaseTimeout()
ResourceCacheManager
+ getInputStream(Resource) : InputStream
+ isCached(Resource) : boolean
- getCachedFile(Resource) : File
- cache(Resource)
+ moreTimeRequested(TimeoutException, Component) : boolean
TimeoutException
- timeoutOwner : TimeoutOwner
+ TimeoutException(String, long, TimeoutOwner)
+ increaseTimeout()
public class UrlResource extends ResourceBasics {

    public InputStream getInputStream() throws IOException {
        return server.getInputStream(this);
    }
}
public class UrlResourceServer extends ResourceServerBasics {

    public InputStream getInputStream(Resource resource) throws IOException {
        return cacheManager.getInputStream(resource);
    }
}
public class ResourceCacheManager {

    public InputStream getInputStream(Resource resource) throws IOException {
        return new FileInputStream(getCachedFile(resource));
    }

    File getCachedFile(Resource resource) throws IOException {
        if (!isCached(resource)) {
            boolean keepTrying = true;
            while (keepTrying) {
                try {
                    String address = resource.toAddress();
                    cache(resource);
                    keepTrying = false;
                } catch (TimeoutException e) {
                    keepTrying = moreTimeRequested(e);
                    if (!keepTrying) {
                        throw e;
                    }
                }
            }
        }
        return new File(getCachedFilePath(resource));
    }

    private void cache(Resource resource) throws IOException {
        InputStream  in;
        OutputStream out;

        in  = new BufferedInputStream(resource.getLiveInputStream());
        out = new BufferedOutputStream(
                new FileOutputStream(getCachedFilePath(resource))));
        int b;
        while ((b = in.read()) != -1) {
            out.write(b);
        }
        in.close();
        out.close();
    }

}
public class UrlResource extends ResourceBasics {

    public InputStream getLiveInputStream() throws IOException {
        return ((UrlResourceServer)server).getLiveInputStream(url);
    }
}
    public static boolean moreTimeRequested(TimeoutException exception) {
        . . .
        int result = JOptionPane.showConfirmDialog(. . .);
        boolean wantsMoreTime = (result == JOptionPane.YES_OPTION);
        if (wantsMoreTime && incCheck.isSelected()) {
            exception.increaseTimeout();
        }
        return wantsMoreTime;
    }
public class UrlResourceServer extends ResourceServerBasics 
                        implements TimeoutOwner {

    InputStream getLiveInputStream(URL url) throws IOException {
        StreamerThread streamer = new StreamerThread(url);
        streamer.start();
        try {
            streamer.join(timeout);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (streamer.hasStream()) {
            return streamer.getStream();
        }
        throw streamer.getException();
    }

    private class StreamerThread extends Thread {
        URL url;
        InputStream stream = null;
        IOException exception = null;
    
        public StreamerThread(URL url) {
            this.url = url;
        }
        public void run() {
            try {
                stream = url.openConnection().getInputStream();
            } catch (IOException e) {
                exception = e;
            }
        }
        public boolean hasStream() {
            return (stream != null);
        }
        public InputStream getStream() {
            return stream;
        }
        public IOException getException() {
            if (exception == null) {
                exception = new TimeoutException(url.toString(), 
                        timeout, UrlResourceServer.this);
            }
            return exception;
        }
    }
}