// BlogBridge -- RSS feed reader, manager, and web based service
// Copyright (C) 2002-2006 by R. Pito Salas
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software Foundation;
// either version 2 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with this program;
// if not, write to the Free Software Foundation, Inc., 59 Temple Place,
// Suite 330, Boston, MA 02111-1307 USA
//
// Contact: R. Pito Salas
// mailto:pitosalas@users.sourceforge.net
// More information: about BlogBridge
// http://www.blogbridge.com
// http://sourceforge.net/projects/blogbridge
//
// $Id: Downloader.java,v 1.12 2007/05/02 10:27:06 spyromus Exp $
//
package com.salas.bb.utils.net;
import com.jgoodies.uif.application.Application;
import com.salas.bb.networking.manager.NetManager;
import com.salas.bb.utils.i18n.Strings;
import javax.swing.*;
import java.io.*;
import java.net.URL;
import java.text.MessageFormat;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Downloader of updates.
*/
public class Downloader
{
private static final Logger LOG = Logger.getLogger(Downloader.class.getName());
private static final String THREAD_NAME = "Download File";
private static File lastSaveDir;
private boolean canceled;
private IStreamProgressListener listener;
/**
* Creates downloader with given progress listener object.
*
* @param aListener listener.
*/
public Downloader(IStreamProgressListener aListener)
{
listener = aListener;
}
/**
* Downloads file from location to target directory.
*
* @param aLocation location to take file from.
* @param aTargetDir target directory.
*
* @return downloaded file.
*
* @throws InterruptedException in case if downloading was interrupted.
* @throws NullPointerException when no location or target directory specified.
* @throws IOException if any I/O exception happens.
*/
public File download(URL aLocation, File aTargetDir)
throws InterruptedException, IOException
{
return download(aLocation, aTargetDir, null);
}
/**
* Downloads file from location to target directory.
*
* @param aLocation location to take file from.
* @param aTargetDir target directory.
* @param aTargetFileName name of target file or <code>NULL</code>.
*
* @return downloaded file.
*
* @throws InterruptedException in case if downloading was interrupted.
* @throws NullPointerException when no location or target directory specified.
* @throws IOException if any I/O exception happens.
*/
public File download(URL aLocation, File aTargetDir, String aTargetFileName)
throws InterruptedException, IOException
{
if (aLocation == null || aTargetDir == null)
throw new NullPointerException(Strings.error("unspecified.net.location.and.target.directory"));
canceled = false;
// Extract or create name for the target file
String targetFilename = aTargetFileName;
if (targetFilename == null) targetFilename = getFilename(aLocation);
if (targetFilename == null) targetFilename = createUniqueName(aLocation);
// Create file handles for temporary part file and target file
File targetFile = new File(aTargetDir, targetFilename);
File partFile = new File(aTargetDir, targetFilename + ".part");
// If file doesn't exist continue with downloading
if (!targetFile.exists())
{
// Download, copy and remove part file
downloadToPartFile(aLocation, partFile);
partFile.renameTo(targetFile);
}
return targetFile;
}
/**
* Reads contents of URL to the part file. If part file is already present then
* we evaluate the position to resume from.
*
* @param aLocation location to get resource from.
* @param aPartFile part file to put data at.
*
* @throws IOException if I/O error happens.
* @throws InterruptedException in case if downloading was interrupted.
*/
private void downloadToPartFile(URL aLocation, File aPartFile)
throws IOException, InterruptedException
{
// Determine the position to continue from
long position = 0;
if (aPartFile.exists()) position = aPartFile.length();
OutputStream writer = null;
InputStream reader = null;
URLInputStream is;
try
{
is = new URLInputStream(aLocation, (int)position, 0);
NetManager.register(NetManager.TYPE_DOWNLOADS, aLocation.toString(), null, is);
if (listener != null) is.addListener(listener);
reader = new BufferedInputStream(is);
writer = new BufferedOutputStream(new FileOutputStream(aPartFile, true));
int ch = -1;
while (!isCanceled() && (ch = reader.read()) != -1)
{
writer.write(ch);
}
if (ch != -1) throw new InterruptedException(Strings.error("net.downloading.canceled"));
} finally
{
if (writer != null)
{
writer.flush();
writer.close();
}
if (reader != null)
{
reader.close();
}
}
}
/**
* Cancels downloading.
*/
public void cancel()
{
canceled = true;
}
/**
* Returns <code>TRUE</code> only if downloading has been canceled.
*
* @return <code>TRUE</code> only if downloading has been canceled.
*/
private boolean isCanceled()
{
return canceled;
}
/**
* Returns the file name part of the target location for local use (saving of
* downloaded resource).
*
* @param aLocation location.
*
* @return file name or <code>NULL</code> if name not specified or location is <code>NULL</code>.
*/
public static String getFilename(URL aLocation)
{
String path = aLocation == null ? null : aLocation.getPath();
if (path != null)
{
int lastPathSeparator = path.lastIndexOf('/');
if (lastPathSeparator != -1)
{
path = path.substring(lastPathSeparator + 1);
}
if (path.trim().length() == 0) path = null;
}
return path;
}
/**
* Creates unique name for file from its location URL.
*
* @param aLocation location URL.
*
* @return unique name.
*/
static String createUniqueName(URL aLocation)
{
return Long.toHexString(aLocation.toString().hashCode()).toUpperCase();
}
/**
* Asks user to point the location for the resource.
*
* @param link URL of the image.
* @param reportCompletion <code>TRUE</code> to report "Download Complete."
*/
public static void saveResource(final URL link, final boolean reportCompletion)
{
// Get initial file directory
String name = getFilename(link);
if (name == null) name = "untitled";
File dir = lastSaveDir == null ? new File(System.getProperty("user.home")) : lastSaveDir;
File file = new File(dir, name);
// Select the target directory
JFileChooser fc = new JFileChooser();
fc.setSelectedFile(file);
fc.setMultiSelectionEnabled(false);
int result = fc.showSaveDialog(Application.getDefaultParentFrame());
if (result == JFileChooser.APPROVE_OPTION)
{
// Saving
final File directory = fc.getSelectedFile().getParentFile();
final String filename = fc.getSelectedFile().getName();
lastSaveDir = directory;
final Downloader downloader = new Downloader(null);
Thread thread = new Thread(THREAD_NAME)
{
public void run()
{
try
{
downloader.download(link, directory, filename);
if (reportCompletion)
{
JOptionPane.showMessageDialog(
Application.getDefaultParentFrame(),
MessageFormat.format(Strings.message("net.download.complete"), filename),
Strings.message("net.download.dialog.title"),
JOptionPane.INFORMATION_MESSAGE
);
}
} catch (InterruptedException e)
{
LOG.log(Level.SEVERE, Strings.error("interrupted"), e);
} catch (IOException e)
{
JOptionPane.showMessageDialog(Application.getDefaultParentFrame(),
MessageFormat.format(Strings.message("net.download.failed"), e.getMessage()),
Strings.message("net.download.dialog.title"),
JOptionPane.ERROR_MESSAGE);
}
}
};
thread.start();
}
}
}