/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.amazonaws.devicefarm;
import com.amazonaws.services.devicefarm.AWSDeviceFarmClient;
import com.amazonaws.services.devicefarm.model.*;
import com.google.common.collect.Lists;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.FileEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.gradle.api.logging.Logger;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* Artifact uploader for AWS Device Farm
*/
public class DeviceFarmUploader {
private final AWSDeviceFarmClient api;
private final Logger logger;
private final ExecutorService uploadExecutor;
public DeviceFarmUploader(final AWSDeviceFarmClient api, final Logger logger) {
this.api = api;
this.logger = logger;
this.uploadExecutor = Executors.newCachedThreadPool();
}
/**
* Upload a single file, waits for upload to complete.
* @param file the file
* @param project the project
* @param uploadType the upload type
* @return upload object
*/
public Upload upload(final File file, final Project project,
final UploadType uploadType) {
if(!(file.exists() && file.canRead())) {
throw new DeviceFarmException(String.format("File %s does not exist or is not readable", file));
}
final CreateUploadRequest appUploadRequest = new CreateUploadRequest()
.withName(file.getName())
.withProjectArn(project.getArn())
.withContentType("application/octet-stream")
.withType(uploadType.toString());
final Upload upload = api.createUpload(appUploadRequest).getUpload();
final CloseableHttpClient httpClient = HttpClients.createDefault();
final HttpPut httpPut = new HttpPut(upload.getUrl());
httpPut.setHeader("Content-Type", upload.getContentType());
final FileEntity entity = new FileEntity(file);
httpPut.setEntity(entity);
writeToLog(String.format("Uploading %s to S3", file.getName()));
final HttpResponse response;
try {
response = httpClient.execute(httpPut);
} catch (IOException e) {
throw new DeviceFarmException(String.format("Error uploading artifact %s", file), e);
}
if (response.getStatusLine().getStatusCode() != 200) {
throw new DeviceFarmException(String.format("Upload returned non-200 responses: %s", response.getStatusLine().getStatusCode()));
}
waitForUpload(file, upload);
return upload;
}
public Collection<Upload> batchUpload(final List<File> artifacts, final Project project, final UploadType uploadType) {
List<Future<Upload>> futures = Lists.newArrayList();
// Upload each artifact and create a future for it.
for (final File file : artifacts) {
futures.add(uploadExecutor.submit(
new Callable<Upload>() {
@Override
public Upload call() throws Exception {
return upload(file, project, uploadType);
}
}
));
}
List<Upload> uploads = Lists.newArrayList();
// Check future results and append the upload results to a list.
for (Future<Upload> f : futures) {
try {
uploads.add(f.get());
} catch (Exception e) {
throw new DeviceFarmException(e);
}
}
return uploads;
}
private void waitForUpload(final File file, final Upload upload) {
while (true) {
GetUploadRequest describeUploadRequest = new GetUploadRequest()
.withArn(upload.getArn());
GetUploadResult describeUploadResult = api.getUpload(describeUploadRequest);
String status = describeUploadResult.getUpload().getStatus();
if ("SUCCEEDED".equalsIgnoreCase(status)) {
break;
}
else if ("FAILED".equalsIgnoreCase(status)) {
throw new DeviceFarmException(String.format("Upload %s failed!", upload.getName()));
}
else {
try {
writeToLog(String.format("Waiting for upload %s to be ready (current status: %s)", file.getName(), status));
Thread.sleep(5000);
}
catch (InterruptedException e) {
break;
}
}
}
}
private void writeToLog(final String msg) {
logger.info(msg);
}
}