extensions/net.sf.basedb.genepattern/trunk/src/net/sf/basedb/genepattern/file/FileTransferGateway.java

Code
Comments
Other
Rev Date Author Line
1107 03 Jun 09 nicklas 1 package net.sf.basedb.genepattern.file;
1107 03 Jun 09 nicklas 2
1109 04 Jun 09 nicklas 3 import java.io.BufferedInputStream;
1129 16 Jun 09 nicklas 4 import java.io.ByteArrayOutputStream;
1107 03 Jun 09 nicklas 5 import java.io.File;
1107 03 Jun 09 nicklas 6 import java.io.FileFilter;
1109 04 Jun 09 nicklas 7 import java.io.FileInputStream;
1107 03 Jun 09 nicklas 8 import java.io.IOException;
1107 03 Jun 09 nicklas 9 import java.io.InputStream;
1107 03 Jun 09 nicklas 10 import java.io.OutputStream;
1129 16 Jun 09 nicklas 11 import java.util.ArrayList;
1107 03 Jun 09 nicklas 12 import java.util.Date;
1107 03 Jun 09 nicklas 13 import java.util.HashSet;
1129 16 Jun 09 nicklas 14 import java.util.List;
1107 03 Jun 09 nicklas 15 import java.util.Set;
1107 03 Jun 09 nicklas 16
1107 03 Jun 09 nicklas 17 import net.sf.basedb.core.Application;
1107 03 Jun 09 nicklas 18 import net.sf.basedb.core.ConfigurationException;
1107 03 Jun 09 nicklas 19 import net.sf.basedb.core.ItemAlreadyExistsException;
1129 16 Jun 09 nicklas 20 import net.sf.basedb.core.ProgressReporter;
1129 16 Jun 09 nicklas 21 import net.sf.basedb.core.signal.ThreadSignalHandler;
1116 10 Jun 09 nicklas 22 import net.sf.basedb.genepattern.wrapper.JobResult;
1107 03 Jun 09 nicklas 23 import net.sf.basedb.util.FileUtil;
1107 03 Jun 09 nicklas 24 import net.sf.basedb.util.StaticCache;
1107 03 Jun 09 nicklas 25 import net.sf.basedb.util.formatter.DateFormatter;
1107 03 Jun 09 nicklas 26
1107 03 Jun 09 nicklas 27 import org.genepattern.webservice.Parameter;
1107 03 Jun 09 nicklas 28
1107 03 Jun 09 nicklas 29 /**
1109 04 Jun 09 nicklas 30   Handles file transfers to and from a GenePattern server. One getaway instance can
1107 03 Jun 09 nicklas 31   handle file for one GenePattern job. The files that are needed for the
1107 03 Jun 09 nicklas 32   job should be added to the gateway with {@link #addFile(String, FileProxy)}
1107 03 Jun 09 nicklas 33   which returns a {@link Parameter} object that can be used when invoking
1129 16 Jun 09 nicklas 34   GenePattern. After all files has been added to the gateway, {@link #prepareForUpload()}
1129 16 Jun 09 nicklas 35   must be called. This method makes a copy of all added files to a temporary
1129 16 Jun 09 nicklas 36   location that is accessible by the GenePattern server via the BASE web server
1129 16 Jun 09 nicklas 37   (even if the file transfer is initialized from a job agent). 
1109 04 Jun 09 nicklas 38   <p>
1109 04 Jun 09 nicklas 39   Once the job has finished the result files can be downloaded with
1109 04 Jun 09 nicklas 40   {@link #downloadResultFile(JobResult, String, OutputStream)} or
1109 04 Jun 09 nicklas 41   {@link #downloadResultFile(JobResult, String)}.
1109 04 Jun 09 nicklas 42   <p>
1109 04 Jun 09 nicklas 43   After the gateway has been used it is recommended to call {@link #cleanUp()}
1109 04 Jun 09 nicklas 44   to make sure that any temporary files are deleted.
1107 03 Jun 09 nicklas 45     
1107 03 Jun 09 nicklas 46   @author nicklas
1107 03 Jun 09 nicklas 47   @since 1.0
1107 03 Jun 09 nicklas 48 */
1107 03 Jun 09 nicklas 49 public class FileTransferGateway 
1107 03 Jun 09 nicklas 50 {
1107 03 Jun 09 nicklas 51
1107 03 Jun 09 nicklas 52   /**
1107 03 Jun 09 nicklas 53     The root cache key where files that are about to be transfered
1107 03 Jun 09 nicklas 54     to GenePattern are stored.
1107 03 Jun 09 nicklas 55   */
1107 03 Jun 09 nicklas 56   public static final String cacheRoot = "genepattern.temp.files";
1107 03 Jun 09 nicklas 57   
1107 03 Jun 09 nicklas 58   private final String id;
1127 15 Jun 09 nicklas 59   private final String downloadServletUrl;
1109 04 Jun 09 nicklas 60   private final Set<String> uploadFiles;
1109 04 Jun 09 nicklas 61   private final Set<File> downloadFiles;
1129 16 Jun 09 nicklas 62   private final List<FileProxy> proxies;
1109 04 Jun 09 nicklas 63   private final File workDir;
1107 03 Jun 09 nicklas 64   private final Set<String> directories;
1107 03 Jun 09 nicklas 65   private final String subDir;
1107 03 Jun 09 nicklas 66   
1107 03 Jun 09 nicklas 67   /**
1107 03 Jun 09 nicklas 68     Create a new gateway.
1127 15 Jun 09 nicklas 69     @param downloadServletUrl The URL to the download servlet that is listening
1127 15 Jun 09 nicklas 70       for requests from the GenePattern server to download file.
1107 03 Jun 09 nicklas 71   */
1127 15 Jun 09 nicklas 72   public FileTransferGateway(String downloadServletUrl)
1107 03 Jun 09 nicklas 73   {
1127 15 Jun 09 nicklas 74     this.downloadServletUrl = downloadServletUrl;
1107 03 Jun 09 nicklas 75     this.id = "gp-" + System.identityHashCode(this);
1107 03 Jun 09 nicklas 76     this.subDir = new DateFormatter("yyyy-MM-dd").format(new Date());
1109 04 Jun 09 nicklas 77     this.uploadFiles = new HashSet<String>();
1107 03 Jun 09 nicklas 78     this.directories = new HashSet<String>();
1109 04 Jun 09 nicklas 79     this.downloadFiles = new HashSet<File>();
1129 16 Jun 09 nicklas 80     this.proxies = new ArrayList<FileProxy>();
1109 04 Jun 09 nicklas 81     this.workDir = new File(new File(System.getProperty("java.io.tmpdir")), getId());
1107 03 Jun 09 nicklas 82     directories.add(cacheRoot);
1107 03 Jun 09 nicklas 83     directories.add(subDir);
1107 03 Jun 09 nicklas 84     directories.add(id);
1107 03 Jun 09 nicklas 85   }
1107 03 Jun 09 nicklas 86   
1107 03 Jun 09 nicklas 87   /**
1107 03 Jun 09 nicklas 88     The unique ID of this gateway.
1107 03 Jun 09 nicklas 89   */
1107 03 Jun 09 nicklas 90   public String getId()
1107 03 Jun 09 nicklas 91   {
1107 03 Jun 09 nicklas 92     return id;
1107 03 Jun 09 nicklas 93   }
1107 03 Jun 09 nicklas 94   
1107 03 Jun 09 nicklas 95   /**
1107 03 Jun 09 nicklas 96     Add a file to this gateway. The filename as returned by
1107 03 Jun 09 nicklas 97     {@link FileProxy#getFileName()} must be unique among the
1107 03 Jun 09 nicklas 98     files already added to this gateway. The file will be
1107 03 Jun 09 nicklas 99     copied to a temporary location as a result of calling
1107 03 Jun 09 nicklas 100     this method. If the {@link FileProxy#getInputStream()} method
1107 03 Jun 09 nicklas 101     return a non-null object that stream is used to make the copy.
1107 03 Jun 09 nicklas 102     If null is returned, {@link FileProxy#writeTo(OutputStream)}
1107 03 Jun 09 nicklas 103     is called to let the proxy do the copy instead.
1107 03 Jun 09 nicklas 104     <p>
1107 03 Jun 09 nicklas 105     Note! The gateway rely on the static cache ({@link Application#getStaticCache()})
1107 03 Jun 09 nicklas 106     for temporary file storage so this cache must have been enabled.
1107 03 Jun 09 nicklas 107     <p>
1107 03 Jun 09 nicklas 108     Note! When the job has been completed on the GenePattern server the
1107 03 Jun 09 nicklas 109     gateway should be cleaned up by calling {@link #cleanUp()}.
1107 03 Jun 09 nicklas 110     
1107 03 Jun 09 nicklas 111     @param gpParameterName The name of the GenePattern parameter
1107 03 Jun 09 nicklas 112     @param file A file proxy
1107 03 Jun 09 nicklas 113     @return A GenePattern parameter object
1107 03 Jun 09 nicklas 114     @throws ItemAlreadyExistsException If another file with the same name
1107 03 Jun 09 nicklas 115       has been added
1107 03 Jun 09 nicklas 116     @throws ConfigurationException If the static cache is not enabled
1107 03 Jun 09 nicklas 117   */
1107 03 Jun 09 nicklas 118   public Parameter addFile(String gpParameterName, FileProxy file)
1107 03 Jun 09 nicklas 119   {
1107 03 Jun 09 nicklas 120     String filename = file.getFileName();
1109 04 Jun 09 nicklas 121     if (uploadFiles.contains(filename))
1107 03 Jun 09 nicklas 122     {
1107 03 Jun 09 nicklas 123       throw new ItemAlreadyExistsException("File already exists: " + filename);
1107 03 Jun 09 nicklas 124     }
1129 16 Jun 09 nicklas 125     uploadFiles.add(filename);
1129 16 Jun 09 nicklas 126     proxies.add(file);
1129 16 Jun 09 nicklas 127     String downloadPath = subDir + "/" + id + "/" + filename;
1129 16 Jun 09 nicklas 128     return new Parameter(gpParameterName, downloadServletUrl + "/" + downloadPath);
1129 16 Jun 09 nicklas 129   }
1129 16 Jun 09 nicklas 130   
1129 16 Jun 09 nicklas 131   /**
1129 16 Jun 09 nicklas 132     Prepare all added files for upload. The preparation includes
1129 16 Jun 09 nicklas 133     copying the files to a temporary location so this method may
1129 16 Jun 09 nicklas 134     take some time if the files are large.
1129 16 Jun 09 nicklas 135     @param progress An optional progress reporter
1129 16 Jun 09 nicklas 136     @return The number of files copied
1129 16 Jun 09 nicklas 137   */
1129 16 Jun 09 nicklas 138   public int prepareUpload(ProgressReporter progress)
1129 16 Jun 09 nicklas 139   {
1107 03 Jun 09 nicklas 140     StaticCache cache = Application.getStaticCache();
1107 03 Jun 09 nicklas 141     if (cache.isDisabled())
1107 03 Jun 09 nicklas 142     {
1107 03 Jun 09 nicklas 143       throw new ConfigurationException("The static cache is disabled. " +
1107 03 Jun 09 nicklas 144         "Please set 'cache.static.disabled = false' in 'base.config'.");
1107 03 Jun 09 nicklas 145     }
1107 03 Jun 09 nicklas 146     
1129 16 Jun 09 nicklas 147     int numFiles = proxies.size();
1129 16 Jun 09 nicklas 148     int currentFile = 0;
1129 16 Jun 09 nicklas 149     for (FileProxy file : proxies)
1107 03 Jun 09 nicklas 150     {
1129 16 Jun 09 nicklas 151       ThreadSignalHandler.checkInterrupted();
1129 16 Jun 09 nicklas 152       String filename = file.getFileName();
1129 16 Jun 09 nicklas 153       String downloadPath = subDir + "/" + id + "/" + filename;
1129 16 Jun 09 nicklas 154       String cacheKey = cacheRoot + "/" + downloadPath;
1129 16 Jun 09 nicklas 155       OutputStream toCache = null;
1129 16 Jun 09 nicklas 156       InputStream fromProxy = null;
1129 16 Jun 09 nicklas 157       if (progress != null)
1107 03 Jun 09 nicklas 158       {
1129 16 Jun 09 nicklas 159         progress.display(100 * currentFile / numFiles, "Copying '" + filename + "'");
1107 03 Jun 09 nicklas 160       }
1129 16 Jun 09 nicklas 161       try
1107 03 Jun 09 nicklas 162       {
1129 16 Jun 09 nicklas 163         fromProxy = file.getInputStream();
1129 16 Jun 09 nicklas 164         if (fromProxy != null)
1129 16 Jun 09 nicklas 165         {
1129 16 Jun 09 nicklas 166           cache.write(cacheKey, fromProxy, 1000);
1129 16 Jun 09 nicklas 167         }
1129 16 Jun 09 nicklas 168         else
1129 16 Jun 09 nicklas 169         {
1129 16 Jun 09 nicklas 170           toCache = cache.write(cacheKey, 1000);
1129 16 Jun 09 nicklas 171           file.writeTo(toCache);
1129 16 Jun 09 nicklas 172         }
1107 03 Jun 09 nicklas 173       }
1129 16 Jun 09 nicklas 174       catch (IOException ex)
1129 16 Jun 09 nicklas 175       {
1129 16 Jun 09 nicklas 176         throw new RuntimeException(ex);
1129 16 Jun 09 nicklas 177       }
1129 16 Jun 09 nicklas 178       finally
1129 16 Jun 09 nicklas 179       {
1129 16 Jun 09 nicklas 180         FileUtil.close(toCache);
1129 16 Jun 09 nicklas 181         FileUtil.close(fromProxy);
1129 16 Jun 09 nicklas 182       }
1107 03 Jun 09 nicklas 183     }
1129 16 Jun 09 nicklas 184     proxies.clear();
1129 16 Jun 09 nicklas 185     return numFiles;
1107 03 Jun 09 nicklas 186   }
1107 03 Jun 09 nicklas 187   
1107 03 Jun 09 nicklas 188   /**
1109 04 Jun 09 nicklas 189     Download a GenePattern result file and make it available as a InputStream.
1109 04 Jun 09 nicklas 190     Downloaded file are cached and subseqent calls to this method requesting
1109 04 Jun 09 nicklas 191     the same filename will use the cached file.
1109 04 Jun 09 nicklas 192     
1109 04 Jun 09 nicklas 193     @param result The job result information
1109 04 Jun 09 nicklas 194     @param filename A filename that is part of the job result
1109 04 Jun 09 nicklas 195     @return An input stream that reads from the downloaded file or null if the
1109 04 Jun 09 nicklas 196       file doesn't exists as part of the job result
1109 04 Jun 09 nicklas 197     @throws IOException
1107 03 Jun 09 nicklas 198   */
1109 04 Jun 09 nicklas 199   public InputStream downloadResultFile(JobResult result, String filename)
1109 04 Jun 09 nicklas 200     throws IOException
1109 04 Jun 09 nicklas 201   {
1109 04 Jun 09 nicklas 202     File workDir = getWorkingDirectory();
1109 04 Jun 09 nicklas 203     File tmpFile = new File(workDir, filename);
1109 04 Jun 09 nicklas 204     if (!tmpFile.exists())
1109 04 Jun 09 nicklas 205     {
1116 10 Jun 09 nicklas 206       tmpFile = result.downloadFile(filename, workDir.getAbsolutePath());
1109 04 Jun 09 nicklas 207       if (tmpFile != null) downloadFiles.add(tmpFile);
1109 04 Jun 09 nicklas 208     }
1109 04 Jun 09 nicklas 209     InputStream in = null;
1109 04 Jun 09 nicklas 210     if (tmpFile != null)
1109 04 Jun 09 nicklas 211     {
1109 04 Jun 09 nicklas 212       in = new BufferedInputStream(new FileInputStream(tmpFile));
1109 04 Jun 09 nicklas 213     }
1109 04 Jun 09 nicklas 214     return in;
1109 04 Jun 09 nicklas 215   }
1109 04 Jun 09 nicklas 216   
1109 04 Jun 09 nicklas 217   /**
1109 04 Jun 09 nicklas 218     Download a GenePattern result file and copy it to the given ouput stream.
1109 04 Jun 09 nicklas 219     Downloaded file are cached and subseqent calls to this method requesting
1109 04 Jun 09 nicklas 220     the same filename will use the cached file.
1109 04 Jun 09 nicklas 221     
1109 04 Jun 09 nicklas 222     @param result The job result information
1109 04 Jun 09 nicklas 223     @param filename A filename that is part of the job result
1109 04 Jun 09 nicklas 224     @param to The output stream to write to
1109 04 Jun 09 nicklas 225     @throws IOException
1109 04 Jun 09 nicklas 226   */
1109 04 Jun 09 nicklas 227   public long downloadResultFile(JobResult result, String filename, OutputStream to)
1109 04 Jun 09 nicklas 228     throws IOException
1109 04 Jun 09 nicklas 229   {
1109 04 Jun 09 nicklas 230     return FileUtil.copy(downloadResultFile(result, filename), to);
1109 04 Jun 09 nicklas 231   }
1109 04 Jun 09 nicklas 232   
1129 16 Jun 09 nicklas 233   /**
1129 16 Jun 09 nicklas 234     Download a GenePattern result file and read it in as a string.
1129 16 Jun 09 nicklas 235     @param result The job result information
1129 16 Jun 09 nicklas 236     @param filename The name of a (text) file that is part of the job result
1129 16 Jun 09 nicklas 237     @param charset The character set used in the file
1129 16 Jun 09 nicklas 238     @return The file contents as a string
1129 16 Jun 09 nicklas 239     @throws IOException
1129 16 Jun 09 nicklas 240   */
1129 16 Jun 09 nicklas 241   public String downloadAsString(JobResult result, String filename, String charset)
1129 16 Jun 09 nicklas 242     throws IOException
1129 16 Jun 09 nicklas 243   {
1129 16 Jun 09 nicklas 244     ByteArrayOutputStream to = new ByteArrayOutputStream();
1129 16 Jun 09 nicklas 245     downloadResultFile(result, filename, to);
1129 16 Jun 09 nicklas 246     return to.toString(charset);
1129 16 Jun 09 nicklas 247   }
1129 16 Jun 09 nicklas 248   
1109 04 Jun 09 nicklas 249   private File getWorkingDirectory()
1109 04 Jun 09 nicklas 250   {
1109 04 Jun 09 nicklas 251     workDir.mkdirs();
1109 04 Jun 09 nicklas 252     return workDir;
1109 04 Jun 09 nicklas 253   }
1109 04 Jun 09 nicklas 254   
1109 04 Jun 09 nicklas 255   /**
1109 04 Jun 09 nicklas 256     Clean up and remove all files that has been added or downloaded 
1109 04 Jun 09 nicklas 257     to this gateway.
1109 04 Jun 09 nicklas 258   */
1107 03 Jun 09 nicklas 259   public void cleanUp()
1107 03 Jun 09 nicklas 260   {
1109 04 Jun 09 nicklas 261     if (uploadFiles.size() > 0)
1107 03 Jun 09 nicklas 262     {
1107 03 Jun 09 nicklas 263       StaticCache cache = Application.getStaticCache();
1107 03 Jun 09 nicklas 264       cache.cleanUp(
1107 03 Jun 09 nicklas 265         new FileFilter()
1107 03 Jun 09 nicklas 266         {
1107 03 Jun 09 nicklas 267           @Override
1107 03 Jun 09 nicklas 268           public boolean accept(File file) 
1107 03 Jun 09 nicklas 269           {
1107 03 Jun 09 nicklas 270             String name = file.getName();
1109 04 Jun 09 nicklas 271             boolean accept = file.isDirectory() ? directories.contains(name) :
1109 04 Jun 09 nicklas 272               id.equals(file.getParentFile().getName()) && uploadFiles.contains(name);
1107 03 Jun 09 nicklas 273             return accept;
1107 03 Jun 09 nicklas 274           }
1107 03 Jun 09 nicklas 275         }
1107 03 Jun 09 nicklas 276       );
1109 04 Jun 09 nicklas 277       uploadFiles.clear();
1107 03 Jun 09 nicklas 278     }
1109 04 Jun 09 nicklas 279     if (downloadFiles.size() > 0)
1109 04 Jun 09 nicklas 280     {
1109 04 Jun 09 nicklas 281       for (File f : downloadFiles)
1109 04 Jun 09 nicklas 282       {
1109 04 Jun 09 nicklas 283         f.delete();
1109 04 Jun 09 nicklas 284       }
1109 04 Jun 09 nicklas 285       workDir.delete();
1109 04 Jun 09 nicklas 286       downloadFiles.clear();
1109 04 Jun 09 nicklas 287     }
1107 03 Jun 09 nicklas 288   }
1107 03 Jun 09 nicklas 289
1107 03 Jun 09 nicklas 290   /**
1107 03 Jun 09 nicklas 291     To make sure that we always cleanup files.
1107 03 Jun 09 nicklas 292   */
1107 03 Jun 09 nicklas 293   @Override
1107 03 Jun 09 nicklas 294   protected void finalize() 
1107 03 Jun 09 nicklas 295     throws Throwable
1107 03 Jun 09 nicklas 296   {
1107 03 Jun 09 nicklas 297     cleanUp();
1107 03 Jun 09 nicklas 298     super.finalize();
1107 03 Jun 09 nicklas 299   }
1107 03 Jun 09 nicklas 300   
1107 03 Jun 09 nicklas 301 }