/*
 * @(#)TimedURLConnection.java
 *
 * Copyright (c) 2001-2002 JScape
 * 1147 S. 53rd Pl., Mesa, Arizona, 85206, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * JScape. ("Confidential Information").  You shall not disclose such
 * Confidential Information and shall use it only in accordance with
 * the terms of the license agreement you entered into with JScape.
 */

package com.jscape.inet.util;


import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.net.URLConnection;


/**
 * Implements methods for opening an InputStream or OutputStream on a URLConnection within a desired timeout. *
 * <p/>
 * Example Usage:
 * <p/>
 * <pre>
 * int timeout = 5000; // 5 second timeout
 * try
 * {
 *   URL url = new URL("http://www.myserver.com");
 *   URLConnection conn = url.openConnection();
 *   InputStream in = TimedURLConnection.getInputStream(conn,timeout);
 * }
 * catch(Exception ioe)
 * {
 *   // handle exception
 * }
 * </pre>
 */
public class TimedURLConnection {

    private static final int POLL_DELAY = 100;

    /**
     * Gets OutputStream from a URLConnection within a specified timeout.
     *
     * @param conn  the URLConnection
     * @param delay timeout in milliseconds
     *
     * @throws java.io.IOException timeout is reached or other I/O error occurs
     */
    public static OutputStream getOutputStream(URLConnection conn, int delay)
            throws IOException {
        OutputStreamThread st = new OutputStreamThread(conn);

        st.start();
        int timer = 0;
        OutputStream outStream = null;

        for ( ;; ) {
            // Check to see if a connection is established
            if (st.isConnected()) {
                outStream = st.getOutputStream();
                break;
            } else {
                // Check to see if an error occurred
                if (st.isError()) {
                    throw (st.getException());
                }
                try {
                    Thread.sleep(POLL_DELAY);
                } catch (InterruptedException ie) {
                    // todo: ignore?
                }
                timer += POLL_DELAY;

                if (delay > 0 && timer > delay) {
                    st = null;
                    throw new InterruptedIOException("Timeout while trying to connect to URL " + conn.getURL());
                }
            }
        }
        st = null;
        return outStream;
    }

    /**
     * Gets InputStream from a <code>URLConnection</code> within a specified timeout.
     *
     * @param conn  the <code>URLConnection</code>
     * @param delay timeout in milliseconds
     *
     * @throws java.io.IOException timeout is reached or other I/O error occurs
     */
    public static InputStream getInputStream(URLConnection conn, int delay)
            throws InterruptedIOException, IOException {
        InputStreamThread st = new InputStreamThread(conn);

        st.start();
        int timer = 0;
        InputStream inStream = null;

        for ( ;; ) {
            // Check to see if a connection is established
            if (st.isConnected()) {
                inStream = st.getInputStream();
                break;
            } else {
                // Check to see if an error occurred
                if (st.isError()) {
                    throw (st.getException());
                }
                try {
                    Thread.sleep(POLL_DELAY);
                } catch (InterruptedException ie) {
                    // todo: ignore?
                }
                timer += POLL_DELAY;

                if (delay > 0 && timer > delay) {
                    st = null;
                    throw new InterruptedIOException("Timeout while trying to connect to URL " + conn.getURL());
                }
            }
        }

        st = null;
        return inStream;
    }

    private TimedURLConnection() {
    }

    // thread responsible for acquiring InputStream
    static class InputStreamThread
            extends Thread {

        private volatile InputStream mInStream = null;
        private IOException mException = null;
        private URLConnection mConn = null;

        InputStreamThread(URLConnection conn) {
            mConn = conn;
        }

        // thread checks for connection
        public void run() {
            InputStream inStream = null;

            try {
                inStream = mConn.getInputStream();
            } catch (IOException ioe) {
                mException = ioe;
                return;
            }
            mInStream = inStream;
        }

        /**
         * Checks if client is connected to server.
         *
         * @return connection status
         */
        public boolean isConnected() {
            return mInStream != null;
        }

        // check for error
        public boolean isError() {
            return mException != null;
        }

        // get InputStream
        public InputStream getInputStream() {
            return mInStream;
        }

        // get Exception
        public IOException getException() {
            return mException;
        }
    }


    // thread responsible for acquiring OutputStream
    static class OutputStreamThread
            extends Thread {

        private volatile OutputStream mOutStream = null;
        private IOException mException = null;
        private URLConnection mConn = null;

        OutputStreamThread(URLConnection conn) {
            mConn = conn;
        }

        // thread checks for connection
        public void run() {
            OutputStream outStream = null;

            try {
                outStream = mConn.getOutputStream();
            } catch (IOException ioe) {
                mException = ioe;
                return;
            }
            mOutStream = outStream;
        }

        /**
         * Checks if client is connected to server.
         *
         * @return connection status
         */
        public boolean isConnected() {
            return mOutStream != null;
        }

        // check for error
        public boolean isError() {
            return mException != null;
        }

        // get output stream
        public OutputStream getOutputStream() {
            return mOutStream;
        }

        // get exception
        public IOException getException() {
            return mException;
        }
    }
}

