/** * Copyright Copyright 2010-15 Simon Andrews * * This file is part of BamQC. * * BamQC 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 3 of the License, or * (at your option) any later version. * * BamQC 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 BamQC; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Changelog: * - Piero Dalle Pezze: Imported from SeqMonk and adjusted for BamQC * - Simon Andrews: Class creation. */ package uk.ac.babraham.BamQC.Network; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import java.util.Enumeration; import java.util.Vector; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import uk.ac.babraham.BamQC.BamQCException; import uk.ac.babraham.BamQC.DataTypes.ProgressListener; import uk.ac.babraham.BamQC.Preferences.BamQCPreferences; /** * The GenomeDownloader actually performs the network interaction required * to download a new genome from the main genome database and install it * in the local genome cache. * @author Simon Andrews */ public class GenomeDownloader implements Runnable { private Vector<ProgressListener> listeners = new Vector<ProgressListener>(); private BamQCPreferences prefs = BamQCPreferences.getInstance(); private String species; private String assembly; private boolean allowCaching; private int size; /** * Download genome. The values for this should be obtained from the genome * index file or the header of an existing BamQC file. The size is used * merely to provide better feedback during the download of the data and isn't * expected to be set correctly from a BamQC file where the compressed size * isn't recorded. * * @param species The latin name of the species * @param assembly The official assembly name * @param size The size of the download in bytes * @param allowCaching sets the cache headers to say if a cached copy is OK */ public void downloadGenome (String species, String assembly, int size, boolean allowCaching) { this.species = species; this.assembly = assembly; this.allowCaching = allowCaching; this.size = size; Thread t = new Thread(this); t.start(); // if we are running in command mode, we do not want to leave the process until it has finished. // Otherwise we could start the analysis when it is still downloading.. if(System.getProperty("java.awt.headless") != null) { try { t.join(); } catch (Exception ex) { ProgressListener [] en = listeners.toArray(new ProgressListener[0]); for (int i=en.length-1;i>=0;i--) { en[i].progressExceptionReceived(ex); } } } } /** * Adds a progress listener. * * @param pl The progress listener to add */ public void addProgressListener (ProgressListener pl) { if (pl != null && ! listeners.contains(pl)) listeners.add(pl); } /** * Removes a progress listener. * * @param pl The progress listener to remove */ public void removeProgressListener (ProgressListener pl) { if (pl != null && listeners.contains(pl)) listeners.remove(pl); } /* (non-Javadoc) * @see java.lang.Runnable#run() */ @Override public void run() { // First we need to download the file from the repository try { // System.out.println("Downloading "+prefs.getGenomeDownloadLocation()+species+"/"+assembly+".zip"); URL url = new URL((new String(prefs.getGenomeDownloadLocation()+species+"/"+assembly+".zip")).replaceAll(" ","%20")); URLConnection connection = url.openConnection(); connection.setUseCaches(allowCaching); InputStream is = connection.getInputStream(); DataInputStream d = new DataInputStream(new BufferedInputStream(is)); // System.out.println("Output file is "+prefs.getGenomeBase()+"/"+assembly+".zip"); File f = new File(prefs.getGenomeBase()+"/"+assembly+".zip"); DataOutputStream o; try { o = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(f))); } catch (FileNotFoundException fnfe) { throw new BamQCException("Could't write into your genomes directory. Please check your file preferences."); } byte [] b = new byte [1024]; int totalBytes = 0; int i; while ((i=d.read(b))>0){ // System.out.println("Read "+totalBytes+" bytes"); o.write(b,0,i); totalBytes += i; Enumeration<ProgressListener> en = listeners.elements(); // while (en.hasMoreElements()) { // en.nextElement().progressUpdated("Downloaded "+totalBytes/1048576+"Mb",totalBytes,size); // } if(totalBytes % 1000 == 0) { while (en.hasMoreElements()) { en.nextElement().progressUpdated("Downloaded "+totalBytes/1000+"KB",totalBytes,size); } } } d.close(); o.close(); File outFile = new File(prefs.getGenomeBase()+"/"+species+"/"+assembly); outFile.mkdirs(); // Now we can uncompress the downloaded file to create the genome ZipFile zipFile = new ZipFile(f); Enumeration<? extends ZipEntry> e = zipFile.entries(); int count = 0; while (e.hasMoreElements()) { count++; Enumeration<ProgressListener> en = listeners.elements(); while (en.hasMoreElements()) { en.nextElement().progressUpdated("Unzipped "+count+" files",count,zipFile.size()); } ZipEntry ze = e.nextElement(); if (ze.isDirectory()) continue; // System.out.println("Found entry "+ze.getName()+" with size "+ze.getSize()); BufferedOutputStream out; try { out = new BufferedOutputStream(new FileOutputStream(prefs.getGenomeBase()+"/"+ze.getName())); } catch (FileNotFoundException fnfe) { throw new BamQCException("Couldn't write into the genomes directory. Please check your file permissions or change your genomes folder."); } BufferedInputStream in = new BufferedInputStream(zipFile.getInputStream(ze)); while ((i=in.read(b))>0) { out.write(b,0,i); } in.close(); out.close(); } // Get rid of the zip file so we don't waste disk space. zipFile.close(); f.delete(); // Finally we need to clear out any cache files which exist from a previous // installation of this genome so the new one will be cached the next time // it's loaded. File cacheDir = new File(outFile.getAbsoluteFile()+"/cache"); if (cacheDir.exists()) { File [] cacheFiles = cacheDir.listFiles(); for (int cf=0;cf<cacheFiles.length;cf++) { if (cacheFiles[cf].isFile() && cacheFiles[cf].getName().indexOf("cache")>=0) { if (!cacheFiles[cf].delete()) { System.out.println("Failed to delete cache file "+cacheFiles[cf].getAbsolutePath()); } } } } } catch (Exception ex) { ProgressListener [] en = listeners.toArray(new ProgressListener[0]); for (int i=en.length-1;i>=0;i--) { en[i].progressExceptionReceived(ex); } return; } // Tell everyone we're finished /* * Something odd happens here on my linux system. If I notify the listeners * in the usual order then I'm told that there are two listeners, but the loop * through these listeners (either via Enumeration or array) only notifies one * (BamQCApplication) and the progress dialog is never told. * * why telling the application first should stop further processing. * * On my windows system I don't get this problem. * */ ProgressListener [] en = listeners.toArray(new ProgressListener[0]); for (int i=en.length-1;i>=0;i--) { en[i].progressComplete("Genome downloaded!", null); } } }