package org.xmlsh.aws; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.LinkedList; import java.util.List; import org.xmlsh.aws.util.AWSS3Command; import org.xmlsh.aws.util.S3Path; import org.xmlsh.core.CoreException; import org.xmlsh.core.Options; import org.xmlsh.core.UnexpectedException; import org.xmlsh.core.XValue; import org.xmlsh.core.io.FileOutputPort; import org.xmlsh.core.io.OutputPort; import org.xmlsh.util.Util; import com.amazonaws.AmazonClientException; import com.amazonaws.AmazonServiceException; import com.amazonaws.services.s3.model.GetObjectRequest; import com.amazonaws.services.s3.model.ListObjectsRequest; import com.amazonaws.services.s3.model.ObjectListing; import com.amazonaws.services.s3.model.ObjectMetadata; import com.amazonaws.services.s3.model.S3Object; import com.amazonaws.services.s3.model.S3ObjectSummary; import com.amazonaws.services.s3.transfer.Download; public class s3get extends AWSS3Command { private boolean bRecurse = false; private boolean bVerbose = false; private List<Download> mDownloads = new LinkedList<Download>(); private String mDelim = S3Path.kDEF_DELIM; private boolean bForce = false; /** * @param args * @throws IOException */ @Override public int run(List<XValue> args) throws Exception { Options opts = getOptions("f=force,meta:,r=recurse,v=verbose,delim:"); parseOptions(opts, args); setSerializeOpts(this.getSerializeOpts(opts)); OutputPort metaPort = null; if(opts.hasOpt("meta")) metaPort = mShell.getEnv() .getOutputPort(opts.getOptStringRequired("meta"), false); args = opts.getRemainingArgs(); bRecurse = opts.hasOpt("recurse"); bVerbose = opts.hasOpt("verbose"); mDelim = opts.getOptString("delim", S3Path.kDEF_DELIM); bForce = opts.hasOpt("force"); try { getS3Client(opts); } catch (UnexpectedException e) { usage(e.getLocalizedMessage()); return 1; } S3Path src; OutputPort dest; int ret = 0; switch(args.size()){ case 0: src = getS3Path(); return 1; case 1: { src = getS3Path(args.get(0)); dest = getStdout(); ret += get(src, dest, metaPort); break; } default: List<XValue> srcs = args; XValue xds = srcs.remove(args.size() - 1); for(XValue s : srcs) { src = getS3Path(s); String prefix = null; if(bRecurse) prefix = src.getKey(); ret += get(src, xds, metaPort, prefix); } } waitForDownloads(); shutdownTransferManager(); return ret; } private void waitForDownloads() throws AmazonServiceException, AmazonClientException, InterruptedException { while(!mDownloads.isEmpty()) { Download d = mDownloads.remove(0); d.waitForCompletion(); } } private int get(S3Path src, XValue dest, OutputPort metaPort, String prefix) throws CoreException, IOException, AmazonServiceException, AmazonClientException, InterruptedException { if(src.isDirectory()) { int ret = 0; if(!bRecurse && !bForce) { mShell.printErr("Skipping directory: " + src.getKey()); return 0; } ListObjectsRequest request = getListRequest(src, null); traceCall("listObjects"); ObjectListing list = getAWSClient().listObjects(request); do { List<S3ObjectSummary> objs = list.getObjectSummaries(); for(S3ObjectSummary obj : objs) { S3Path s = getS3Path(obj.getBucketName(), obj.getKey()); if(s.isDirectory()) continue; ret += get(s, dest, metaPort, prefix); } waitForDownloads(); if(list.isTruncated()) { // String marker = list.getNextMarker(); list = getAWSClient().listNextBatchOfObjects(list); } else break; } while(true); return ret; } else return get(src, getOutput(src, dest, prefix), metaPort); } private OutputPort getOutput(S3Path src, XValue dest, String prefix) throws CoreException, IOException { String fname = dest.toString(); String key = src.getKey(); if(!Util.isBlank(prefix)) { if(!key.startsWith(prefix) && !bForce) { mShell.printErr("Skipping key - does not start with prefix: " + key); return null; } key = key.substring(prefix.length()); while(key.startsWith(mDelim)) key = key.substring(1); } File out = getFile(fname); if(out.isDirectory() || fname.endsWith(mDelim)) out = new File(out, key); File parent = out.getParentFile(); if(parent != null && !parent.exists()) parent.mkdirs(); return new FileOutputPort(out, false, false); } private int get(S3Path src, OutputPort dest, OutputPort metaPort) { if(bVerbose) mShell.printErr("Getting " + src.toString()); try { GetObjectRequest request = new GetObjectRequest(src.getBucket(), src.getKey()); ObjectMetadata meta = null; if(dest.isFile()) { traceCall("getObject"); Download download = getTransferManager().download(request, dest.getFile()); mDownloads.add(download); } else { traceCall("getObject"); S3Object obj = getAWSClient().getObject(request); meta = obj.getObjectMetadata(); InputStream is = obj.getObjectContent(); OutputStream os = dest.asOutputStream(getSerializeOpts()); Util.copyStream(is, os); os.close(); is.close(); } if(metaPort != null) { mWriter = metaPort.asXMLStreamWriter(getSerializeOpts()); startDocument(); writeMeta(meta); endDocument(); mWriter.close(); } } catch (Exception e) { mShell.printErr("Exception getting: " + src, e); return 1; } return 0; } @Override public void usage() { super.usage("Usage: s3get source [target]"); } }