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}