1 package org.kuali.common.aws.s3; 2 3 import static com.google.common.base.Stopwatch.createStarted; 4 import static com.google.common.collect.Lists.newArrayList; 5 import static java.lang.String.format; 6 import static org.kuali.common.core.collect.Lists.shuffledCopy; 7 import static org.kuali.common.util.base.Exceptions.illegalState; 8 import static org.kuali.common.util.log.Loggers.newLogger; 9 10 import java.io.File; 11 import java.util.List; 12 import java.util.concurrent.Callable; 13 14 import org.kuali.common.aws.s3.model.ObjectSummary; 15 import org.kuali.common.core.base.TimedInterval; 16 import org.kuali.common.core.build.ValidatingBuilder; 17 import org.kuali.common.core.validate.annotation.IdiotProofImmutable; 18 import org.kuali.common.util.inform.PercentCompleteInformer; 19 import org.slf4j.Logger; 20 21 import com.amazonaws.services.s3.model.AmazonS3Exception; 22 import com.google.common.base.Function; 23 import com.google.common.base.Stopwatch; 24 import com.google.common.collect.ImmutableList; 25 26 @IdiotProofImmutable 27 public final class CopyObjectToFileCallable implements Callable<List<TimedInterval>> { 28 29 private static final Logger logger = newLogger(); 30 31 private final S3Service s3; 32 private final ImmutableList<ObjectSummary> objects; 33 private final Function<String, String> keyToPath; 34 private final String bucket; 35 private final PercentCompleteInformer informer; 36 37 @Override 38 public List<TimedInterval> call() { 39 List<TimedInterval> timings = newArrayList(); 40 List<ObjectSummary> shuffled = shuffledCopy(objects); 41 for (ObjectSummary object : shuffled) { 42 Stopwatch sw = createStarted(); 43 try { 44 String path = keyToPath.apply(object.getKey()); 45 File file = new File(path); 46 logger.debug(format("creating -> %s", file)); 47 s3.copyObjectToFile(bucket, object.getKey(), file); 48 } catch (AmazonS3Exception e) { 49 throw illegalState(e, "key=%s", object.getKey()); 50 } 51 TimedInterval timing = TimedInterval.build(sw); 52 timings.add(timing); 53 informer.incrementProgress(); 54 } 55 return timings; 56 } 57 58 private CopyObjectToFileCallable(Builder builder) { 59 this.s3 = builder.s3; 60 this.objects = ImmutableList.copyOf(builder.objects); 61 this.bucket = builder.bucket; 62 this.informer = builder.informer; 63 this.keyToPath = builder.keyToPath; 64 } 65 66 public static Builder builder() { 67 return new Builder(); 68 } 69 70 public static class Builder extends ValidatingBuilder<CopyObjectToFileCallable> { 71 72 private S3Service s3; 73 private List<ObjectSummary> objects; 74 private String bucket; 75 private PercentCompleteInformer informer; 76 private Function<String, String> keyToPath; 77 78 public Builder withObjects(List<ObjectSummary> objects) { 79 this.objects = objects; 80 return this; 81 } 82 83 public Builder withKeyToPath(Function<String, String> keyToPath) { 84 this.keyToPath = keyToPath; 85 return this; 86 } 87 88 public Builder withS3(S3Service s3) { 89 this.s3 = s3; 90 return this; 91 } 92 93 public Builder withBucket(String bucket) { 94 this.bucket = bucket; 95 return this; 96 } 97 98 public Builder withInformer(PercentCompleteInformer informer) { 99 this.informer = informer; 100 return this; 101 } 102 103 @Override 104 public CopyObjectToFileCallable build() { 105 return validate(new CopyObjectToFileCallable(this)); 106 } 107 } 108 109 public S3Service getS3() { 110 return s3; 111 } 112 113 public String getBucket() { 114 return bucket; 115 } 116 117 public PercentCompleteInformer getInformer() { 118 return informer; 119 } 120 121 public List<ObjectSummary> getObjects() { 122 return objects; 123 } 124 125 public Function<String, String> getKeyToPath() { 126 return keyToPath; 127 } 128 129 }