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 }