/* * Software Name : ATK * * Copyright (C) 2007 - 2012 France Télécom * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ------------------------------------------------------------------ * File Name : Downloader.java * * Created : 26/05/2009 * Author(s) : Yvain Leyral */ package com.orange.atk.atkUI.corecli.utils; import java.io.BufferedInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.net.Authenticator; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.PasswordAuthentication; import java.net.Socket; import java.net.URL; import org.apache.log4j.Logger; import com.orange.atk.atkUI.corecli.Alert; /** * A class to handle an HTTP download session, in an independant thread. The * class implements the TimerMonitor interface, to allow for abortion of the * session upon timeout. The Timers used just have to call the timeout() method * to make the session end. There's a timer to guard the connection phase * itself, then a second one to guard the data transfer. */ public class Downloader extends Thread implements TimerMonitor { private DownloadMonitor monitor; private String url; private File target; private int httpMaxConTime, httpMaxDwnTime; private Timer connectionTimer; private Timer downloadTimer; /** * A login to access to the specified URL. */ private String login; /** * A password to access to the specified URL. */ private String password; /** * if the connection requires an authentification of the device. */ private String userAgent; /** stop condition for this thread */ private boolean shouldStop = false; /** * Error message */ private String errorMessage; /** * Construct a Downloader to download the file located at the given URL, and * save it to the target file passed. The max times allowed for timers are * other parameters. The last parameter is the DownloadMonitor to whom * report the evolution of the session). * * @param url * The URL to open * @param target * The target file where to save the downloaded file * @param httpMaxConTime * The maximum number of seconds allowed before the connection * timeout * @param httpMaxDwnTime * The maximum number of seconds allowed before the transfer * timeout * @param monitor * The download monitor to use to report changes of the session * status */ Downloader(String url, File target, int httpMaxConTime, int httpMaxDwnTime, DownloadMonitor monitor) { this.url = url; this.target = target; this.httpMaxConTime = httpMaxConTime; this.httpMaxDwnTime = httpMaxDwnTime; this.monitor = monitor; connectionTimer = new Timer(httpMaxConTime, this); downloadTimer = new Timer(httpMaxDwnTime, this); } /** * Terminates the download session. Dumps the passed message (will be * "(stopped)" if null is passed). Also, changes the status of the * DownloadMonitor provided at creation, as per the one passed. Finally, the * thread is stopped. * * @param message * The message to dump * @param status * The status to report to the associated DownloadMonitor. Valid * values are the ones defined in the DownloadMonitor class. * @throws Alert */ public void terminate(String message, int status) { if (message == null) { message = "(stopped)"; } errorMessage = "Download: " + message + "(" + url + ")"; monitor.setDownloadStatus(status); stopAsap(); } /** * Method of the TimerMonitor interface. A Timer that expires calls that * method, passing a reference to itself. Depending on its identity, the * corresponding message and status are passed to terminate(). * * @param t * The Timer object that calls that method, so the Downloader can * identify which timer expired */ public void timeout(Timer t) { if (t == connectionTimer) { terminate("Connection attempt aborted, timeout (" + httpMaxConTime + " s).", DownloadMonitor.DLOAD_TIMEOUT_C); } else if (t == downloadTimer) { terminate("File transfer aborted, timeout (" + httpMaxDwnTime + " s).", DownloadMonitor.DLOAD_TIMEOUT_T); } else { String msg = "An unknown timer has expired !"; Logger.getLogger(this.getClass()).error(msg); stopAsap(); } } /** Stop this thread as soon as possible. */ public synchronized void stopAsap() { shouldStop = true; // also stop asap timers connectionTimer.stopAsap(); downloadTimer.stopAsap(); } /** * Main entry point of the thread, when started. */ public void run() { if (isAuthenticate()) { Authenticator.setDefault(new MyAuthenticator()); } PrintStream outstream = null; HttpURLConnection huc = null; OutputStream out = null; Socket socket = null; InputStream in = null; try { if (target == null) { Logger.getLogger(this.getClass()).warn("target null"); } outstream = new PrintStream(new FileOutputStream(target)); URL urlObj = new URL(url); if ((userAgent != null) && (userAgent.trim().length() > 0)) { if (userAgent.indexOf('\n') > 0) { Logger.getLogger(this.getClass()).warn( "INVALID user_agent: '" + userAgent + "' : contains a newline character --> No user agent used"); } else { System.getProperties().put("http.agent", userAgent); } } huc = (HttpURLConnection) urlObj.openConnection(); connectionTimer.start(); huc.connect(); if (!shouldStop) { // no timeout occurs during connection connectionTimer.stopAsap(); int code = huc.getResponseCode(); if (code != 200) { huc.disconnect(); if (code == 404 || code == 401 || code == 502) { terminate("Connection failed. Server returned HTTP code " + code + ".", DownloadMonitor.DLOAD_FAILED_NO_NEW_ATTEMPT); } else { terminate("Connection failed. Server returned HTTP code " + code + ".", DownloadMonitor.DLOAD_FAILED); } } else { // Get an input stream for reading in = huc.getInputStream(); } } if (in != null) { BufferedInputStream bufIn = new BufferedInputStream(in); // Read bytes until end of stream, and write them to local file downloadTimer.start(); int data = bufIn.read(); while ((!shouldStop) && (data != -1)) { outstream.write(data); data = bufIn.read(); } bufIn.close(); if (!shouldStop) { // no timeout occurs during download downloadTimer.stopAsap(); terminate("Completed.", DownloadMonitor.DLOAD_SUCCESS); } } if (huc != null) { huc.disconnect(); } } catch (MalformedURLException e) { terminate("Malformed URL", DownloadMonitor.DLOAD_FAILED); } catch (FileNotFoundException e) { terminate("Can't create file:" + target.getAbsolutePath(), DownloadMonitor.DLOAD_FAILED); } catch (IOException e) { terminate("I/O Error - " + e, DownloadMonitor.DLOAD_FAILED); } catch (SecurityException e) { terminate("Can't create file (security issue):" + target.getAbsolutePath(), DownloadMonitor.DLOAD_FAILED); } finally { // do it what ever exception occurs or not System.getProperties().remove("http.agent"); try { if (out != null) out.close(); if (socket != null) socket.close(); if (in != null) in.close(); if (outstream != null) outstream.close(); } catch (IOException e) { terminate("I/O Error - " + e, DownloadMonitor.DLOAD_FAILED); } } } /** * This class allows making a connection with an authentication (login and * password). * * @author apenault * */ public class MyAuthenticator extends Authenticator { // This method is called when a password-protected URL is accessed protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(login, password.toCharArray()); } } /** * To know if the connection must be authenticated by a login and a password * * @return true if the connection to the given URL requires a login and a * password */ public boolean isAuthenticate() { return (login != null && password != null); } /** * Sets the value of User-Agent to use for the HTTP connection. * * @param userAgent */ public void setUserAgent(String userAgent) { this.userAgent = userAgent; } public void setLogin(String login) { this.login = login; } public void setPassword(String password) { this.password = password; } public String getErrorMessage() { return errorMessage; } }