001package org.kuali.common.devops.jenkins.upgrade;
002
003import static com.amazonaws.services.s3.Headers.CONTENT_TYPE;
004import static com.google.common.base.Stopwatch.createStarted;
005import static com.google.common.collect.Lists.newArrayList;
006import static com.google.common.collect.Maps.newHashMap;
007import static java.lang.String.format;
008import static org.kuali.common.core.collect.Lists.shuffledCopy;
009import static org.kuali.common.devops.archive.sweep.Functions.hostnameToKey;
010import static org.kuali.common.devops.jenkins.upgrade.Jenkins.jenkinsContentType;
011import static org.kuali.common.util.Encodings.UTF8;
012import static org.kuali.common.util.log.Loggers.newLogger;
013
014import java.io.File;
015import java.util.List;
016import java.util.Map;
017import java.util.concurrent.Callable;
018
019import org.kuali.common.aws.s3.S3Service;
020import org.kuali.common.aws.s3.model.ObjectMetadata;
021import org.kuali.common.aws.s3.model.PutFileRequest;
022import org.kuali.common.core.base.TimedInterval;
023import org.kuali.common.core.build.ValidatingBuilder;
024import org.kuali.common.core.io.BasicFile;
025import org.kuali.common.core.validate.annotation.IdiotProofImmutable;
026import org.kuali.common.util.inform.PercentCompleteInformer;
027import org.slf4j.Logger;
028
029import com.google.common.base.Function;
030import com.google.common.base.Optional;
031import com.google.common.base.Stopwatch;
032import com.google.common.collect.ImmutableList;
033
034@IdiotProofImmutable
035public final class BasicFileUploadCallable implements Callable<List<TimedInterval>> {
036
037        private static final Logger logger = newLogger();
038
039        private final S3Service s3;
040        private final ImmutableList<BasicFile> files;
041        private final String hostname;
042        private final String bucket;
043        private final PercentCompleteInformer informer;
044        private final Function<BasicFile, Double> weigher;
045        // TODO Pass this in instead of hard coding it here
046        private final Function<BasicFile, Optional<String>> contentType = jenkinsContentType();
047
048        @Override
049        public List<TimedInterval> call() {
050                String prefix = hostnameToKey().apply(hostname);
051                List<TimedInterval> timings = newArrayList();
052                List<BasicFile> shuffled = shuffledCopy(files);
053                for (BasicFile file : shuffled) {
054                        Stopwatch sw = createStarted();
055                        String s3Key = prefix + file.getPath();
056                        try {
057                                PutFileRequest request = getPutFileRequest(file, s3Key);
058                                s3.putFile(request);
059                        } catch (Exception e) {
060                                logger.info(format("error -> %s", e));
061                        }
062                        TimedInterval timing = TimedInterval.build(sw);
063                        timings.add(timing);
064                        long progress = weigher.apply(file).longValue();
065                        informer.incrementProgress(progress);
066                }
067                return timings;
068        }
069
070        private PutFileRequest getPutFileRequest(BasicFile file, String s3Key) {
071                PutFileRequest.Builder builder = PutFileRequest.builder().withBucket(bucket).withEncoding(UTF8).withFile(new File(file.getPath())).withKey(s3Key);
072                Optional<String> contentType = this.contentType.apply(file);
073                if (contentType.isPresent()) {
074                        Map<String, Object> raw = newHashMap();
075                        raw.put(CONTENT_TYPE, contentType.get());
076                        ObjectMetadata meta = ObjectMetadata.builder().withRawMetadata(raw).build();
077                        builder.withMetadata(meta);
078                }
079                return builder.build();
080        }
081
082        private BasicFileUploadCallable(Builder builder) {
083                this.s3 = builder.s3;
084                this.files = ImmutableList.copyOf(builder.files);
085                this.hostname = builder.hostname;
086                this.bucket = builder.bucket;
087                this.informer = builder.informer;
088                this.weigher = builder.weigher;
089        }
090
091        public static Builder builder() {
092                return new Builder();
093        }
094
095        public static class Builder extends ValidatingBuilder<BasicFileUploadCallable> {
096
097                private S3Service s3;
098                private List<BasicFile> files;
099                private String hostname;
100                private String bucket;
101                private PercentCompleteInformer informer;
102                private Function<BasicFile, Double> weigher;
103
104                public Builder withWeigher(Function<BasicFile, Double> weigher) {
105                        this.weigher = weigher;
106                        return this;
107                }
108
109                public Builder withS3(S3Service s3) {
110                        this.s3 = s3;
111                        return this;
112                }
113
114                public Builder withFiles(List<BasicFile> files) {
115                        this.files = files;
116                        return this;
117                }
118
119                public Builder withHostname(String hostname) {
120                        this.hostname = hostname;
121                        return this;
122                }
123
124                public Builder withBucket(String bucket) {
125                        this.bucket = bucket;
126                        return this;
127                }
128
129                public Builder withInformer(PercentCompleteInformer informer) {
130                        this.informer = informer;
131                        return this;
132                }
133
134                @Override
135                public BasicFileUploadCallable build() {
136                        return validate(new BasicFileUploadCallable(this));
137                }
138        }
139
140        public S3Service getS3() {
141                return s3;
142        }
143
144        public List<BasicFile> getFiles() {
145                return files;
146        }
147
148        public String getHostname() {
149                return hostname;
150        }
151
152        public String getBucket() {
153                return bucket;
154        }
155
156        public PercentCompleteInformer getInformer() {
157                return informer;
158        }
159
160        public Function<BasicFile, Double> getWeigher() {
161                return weigher;
162        }
163
164}