/** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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. */ package org.apache.airavata.gfac.bes.utils; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Map; import org.unigrids.services.atomic.types.GridFileType; import org.unigrids.services.atomic.types.ProtocolType; import de.fzj.unicore.uas.client.FileTransferClient; import de.fzj.unicore.uas.client.StorageClient; import de.fzj.unicore.uas.client.UFTPConstants; import de.fzj.unicore.uas.client.UFTPFileTransferClient; import de.fzj.unicore.uas.fts.FiletransferOptions.IMonitorable; import de.fzj.unicore.uas.fts.FiletransferOptions.SupportsPartialRead; /** * helper that exports remote files from a UNICORE Storage * to the local client machine.<br/> * Simple wildcards ("*" and "?") and download of * directories are supported. * * TODO this should be refactored so the single-file download logic * is separated from the wildcard/directory/provided outputStream logic * * @author schuller */ public class FileDownloader extends FileTransferBase{ private boolean showProgress=true; private boolean forceFileOnly=false; private OutputStream targetStream=null; public FileDownloader(String from, String to, Mode mode){ this(from,to,mode,true); } public FileDownloader(String from, String to, Mode mode, boolean failOnError){ this.to=to; this.from=from; this.mode=mode; this.failOnError=failOnError; } public void perform(StorageClient sms)throws Exception{ boolean isWildcard=hasWildCards(from); boolean isDirectory=false; GridFileType gridSource=null; if(isWildcard){ performWildCardExport(sms); } else { //check if source is a directory gridSource=sms.listProperties(from); isDirectory=gridSource.getIsDirectory(); if(isDirectory){ if(forceFileOnly){ throw new IOException("Source is a directory"); } performDirectoryExport(gridSource, new File(to), sms); } else{ download(gridSource,new File(to),sms); } } } protected void performDirectoryExport(GridFileType directory, File targetDirectory, StorageClient sms)throws Exception{ if(!targetDirectory.exists()|| !targetDirectory.canWrite()){ throw new IOException("Target directory <"+to+"> does not exist or is not writable!"); } if(!targetDirectory.isDirectory()){ throw new IOException("Target <"+to+"> is not a directory!"); } GridFileType[]gridFiles=sms.listDirectory(directory.getPath()); for(GridFileType file: gridFiles){ if(file.getIsDirectory()){ if(!recurse) { System.out.println("Skipping directory "+file.getPath()); continue; } else{ File newTargetDirectory=new File(targetDirectory,getName(file.getPath())); boolean success=newTargetDirectory.mkdirs(); if(!success)throw new IOException("Can create directory: "+newTargetDirectory.getAbsolutePath()); performDirectoryExport(file, newTargetDirectory, sms); continue; } } download(file, new File(targetDirectory,getName(file.getPath())), sms); } } protected void performWildCardExport(StorageClient sms)throws Exception{ String dir=getDir(from); if(dir==null)dir="/"; GridFileType[] files=sms.find(dir, false, from, false, null, null); File targetDir=targetStream==null?new File(to):null; if(targetStream==null){ if(!targetDir.isDirectory())throw new IOException("Target is not a directory."); } for(GridFileType f: files){ download(f, targetDir, sms); } } private String getDir(String path){ return new File(path).getParent(); } private String getName(String path){ return new File(path).getName(); } /** * download a single regular file * * @param source - grid file descriptor * @param localFile - local file or directory to write to * @param sms * @throws Exception */ private void download(GridFileType source, File localFile, StorageClient sms)throws Exception{ if(source==null || source.getIsDirectory()){ throw new IllegalStateException("Source="+source); } OutputStream os=targetStream!=null?targetStream:null; FileTransferClient ftc=null; try{ String path=source.getPath(); if(targetStream==null){ if(localFile.isDirectory()){ localFile=new File(localFile,getName(source.getPath())); } if(mode.equals(Mode.nooverwrite) && localFile.exists()){ System.out.println("File exists and creation mode was set to 'nooverwrite'."); return; } System.out.println("Downloading remote file '"+sms.getUrl()+"#/"+path+"' -> "+localFile.getAbsolutePath()); os=new FileOutputStream(localFile.getAbsolutePath(), mode.equals(Mode.append)); } chosenProtocol=sms.findSupportedProtocol(preferredProtocols.toArray(new ProtocolType.Enum[preferredProtocols.size()])); Map<String,String>extraParameters=makeExtraParameters(chosenProtocol); ftc=sms.getExport(path,extraParameters,chosenProtocol); configure(ftc, extraParameters); System.out.println("DEB:File transfer URL : "+ftc.getUrl()); // ProgressBar p=null; if(ftc instanceof IMonitorable && showProgress){ long size=ftc.getSourceFileSize(); if(isRange()){ size=getRangeSize(); } // p=new ProgressBar(localFile.getName(),size,msg); // ((IMonitorable) ftc).setProgressListener(p); } long startTime=System.currentTimeMillis(); if(isRange()){ if(!(ftc instanceof SupportsPartialRead)){ throw new Exception("Byte range is defined but protocol does not allow " + "partial read! Please choose a different protocol!"); } System.out.println("Byte range: "+startByte+" - "+(getRangeSize()>0?endByte:"")); SupportsPartialRead pReader=(SupportsPartialRead)ftc; pReader.readPartial(startByte, endByte-startByte+1, os); } else{ ftc.readAllData(os); } // if(p!=null){ // p.finish(); // } if(timing){ long duration=System.currentTimeMillis()-startTime; double rate=(double)localFile.length()/(double)duration; System.out.println("Rate: " +rate+ " kB/sec."); } if(targetStream==null)copyProperties(source, localFile); } finally{ try{ if(targetStream==null && os!=null){ os.close(); } }catch(Exception ignored){} if(ftc!=null){ try{ ftc.destroy(); }catch(Exception e1){ // System.out.println("Could not destroy the filetransfer client",e1); } } } } /** * if possible, copy the remote executable flag to the local file * @throws Exception */ private void copyProperties(GridFileType source, File localFile)throws Exception{ try{ localFile.setExecutable(source.getPermissions().getExecutable()); } catch(Exception ex){ //TODO: logging // ("Can't set 'executable' flag for "+localFile.getName(), ex); } } private void configure(FileTransferClient ftc, Map<String,String>params){ if(ftc instanceof UFTPFileTransferClient){ UFTPFileTransferClient u=(UFTPFileTransferClient)ftc; String secret=params.get(UFTPConstants.PARAM_SECRET); u.setSecret(secret); } } public void setShowProgress(boolean showProgress) { this.showProgress = showProgress; } public void setForceFileOnly(boolean forceFileOnly) { this.forceFileOnly = forceFileOnly; } public void setTargetStream(OutputStream targetStream) { this.targetStream = targetStream; } }