package com.nutiteq.net;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;
import com.nutiteq.log.Log;
import com.nutiteq.utils.IOUtils;
/**
* Default stream opener used inside library. Handles cleanup for resources
* opened by it.
*
* Status codes 200 (OK) and 304 (not modified) are handled the same way - with
* data read.
*
* This implementation tries to follow up to 3 redirects (HTTP status codes 301,
* 302, 307). If it is not successful, then an error notification will be sent
* to stream waiter.
*/
public class DefaultDownloadStreamOpener implements DownloadStreamOpener {
/**
* Default timeout for network activity.
*/
public static final int DEFAULT_TIMEOUT = 20 * 1000;
private static final String HTTP_REDIRECT_LOCATION_HEADER = "Location";
private final String urlExtension;
private final Hashtable properties = new Hashtable();
private static final int MAX_FOLLOWED_REDIRECTS = 3;
private final long timeout;
/**
* @param urlExtension
* Optional extension for http URLs, for instance ";deviceside=true".
* Used on some Blackberry devices.
*/
public DefaultDownloadStreamOpener(final String urlExtension) {
this(urlExtension, DEFAULT_TIMEOUT);
}
public DefaultDownloadStreamOpener() {
this("", DEFAULT_TIMEOUT);
}
public DefaultDownloadStreamOpener(final long timeout) {
this("", timeout);
}
public DefaultDownloadStreamOpener(final String urlExtension, final long timeout) {
this.urlExtension = urlExtension;
this.timeout = timeout;
}
/**
* Add request properties, that will be added added to every request (for
* example User-Agent).
*
* @param propertyName
* request property name
* @param propertyValue
* request property value
*/
public void addRequestProperty(final String propertyName, final String propertyValue) {
properties.put(propertyName, propertyValue);
}
public void openInputStream(final DownloadStreamWaiter streamWaiter, final String url) {
openInputStream(streamWaiter, url, 0, null);
}
public void openInputStream(final DownloadStreamWaiter streamWaiter,
final DataPostingDownloadable postingDownloadable) {
openInputStream(streamWaiter, postingDownloadable.getUrl(), 0, postingDownloadable);
}
private void openInputStream(final DownloadStreamWaiter streamWaiter, final String url,
final int redirects, final DataPostingDownloadable downloadable) {
final String downloadableUrl = url + urlExtension;
final DataInputStream dis = null;
HttpConnection connection = null;
InputStream is = null;
String redirectUrl = null;
try {
final long startTime = System.currentTimeMillis();
Log.info("Downloading " + downloadableUrl);
connection = (HttpConnection) Connector.open(downloadableUrl, Connector.READ_WRITE, true);
connection.setRequestMethod(downloadable == null ? HttpConnection.GET : downloadable.getRequestMethod());
connection.setRequestProperty("Cache-Control", "No-Transform");
if (properties.size() > 0) {
final Enumeration keysEnum = properties.keys();
while (keysEnum.hasMoreElements()) {
final String key = (String) keysEnum.nextElement();
final String value = (String) properties.get(key);
connection.setRequestProperty(key, value);
}
}
if (downloadable != null) {
final byte[] dataBytes = downloadable.getPostContent().getBytes("iso-8859-1");
connection.setRequestProperty("Content-Length", Integer.toString(dataBytes.length));
connection.setRequestProperty("content-type", downloadable.getContentType());
final OutputStream dos = connection.openOutputStream();
dos.write(dataBytes);
}
is = connection.openInputStream();
final int responseCode = connection.getResponseCode();
Log.debug("Connection opened in " + (System.currentTimeMillis() - startTime));
if (responseCode == HttpConnection.HTTP_OK
|| responseCode == HttpConnection.HTTP_NOT_MODIFIED) {
final long processStart = System.currentTimeMillis();
streamWaiter.streamOpened(is);
Log.debug("Response read in " + (System.currentTimeMillis() - processStart));
} else if (responseCode == HttpConnection.HTTP_TEMP_REDIRECT
|| responseCode == HttpConnection.HTTP_MOVED_PERM
|| responseCode == HttpConnection.HTTP_MOVED_TEMP) {
redirectUrl = connection.getHeaderField(HTTP_REDIRECT_LOCATION_HEADER);
Log.debug("Redirect to " + redirectUrl);
} else {
streamWaiter.error(RESPONCE_NOT_OK, "");
}
} catch (final IOException e) {
Log.error("Downloader: " + e.getMessage());
streamWaiter.error(NETWORK_ERROR, e.getMessage());
} catch (final SecurityException e) {
Log.error("Downloader security: " + e.getMessage());
streamWaiter.error(SECURITY_EXCEPTION, e.getMessage());
} finally {
IOUtils.closeStream(dis);
IOUtils.closeStream(is);
IOUtils.closeConnection(connection);
}
if (redirects == MAX_FOLLOWED_REDIRECTS && redirectUrl != null) {
streamWaiter.error(TOO_MANY_REDIRECTS, "Too manu redirects created!");
return;
}
if (redirectUrl != null) {
openInputStream(streamWaiter, redirectUrl, redirects + 1, downloadable);
}
}
public long getTimeout() {
return timeout;
}
}