Commit b1dd9288 authored by Andy Wilkinson's avatar Andy Wilkinson

Retry class file upload to remote application that fails to connect

Closes gh-6339
parent 68fb5789
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 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.
......@@ -19,6 +19,7 @@ package org.springframework.boot.devtools.remote.client;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.ConnectException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
......@@ -51,6 +52,7 @@ import org.springframework.util.FileCopyUtils;
* Listens and pushes any classpath updates to a remote endpoint.
*
* @author Phillip Webb
* @author Andy Wilkinson
* @since 1.3.0
*/
public class ClassPathChangeUploader
......@@ -91,23 +93,45 @@ public class ClassPathChangeUploader
public void onApplicationEvent(ClassPathChangedEvent event) {
try {
ClassLoaderFiles classLoaderFiles = getClassLoaderFiles(event);
ClientHttpRequest request = this.requestFactory.createRequest(this.uri,
HttpMethod.POST);
byte[] bytes = serialize(classLoaderFiles);
HttpHeaders headers = request.getHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentLength(bytes.length);
FileCopyUtils.copy(bytes, request.getBody());
logUpload(classLoaderFiles);
ClientHttpResponse response = request.execute();
Assert.state(response.getStatusCode() == HttpStatus.OK, "Unexpected "
+ response.getStatusCode() + " response uploading class files");
performUpload(classLoaderFiles, bytes);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
private void performUpload(ClassLoaderFiles classLoaderFiles, byte[] bytes)
throws IOException {
try {
while (true) {
try {
ClientHttpRequest request = this.requestFactory
.createRequest(this.uri, HttpMethod.POST);
HttpHeaders headers = request.getHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentLength(bytes.length);
FileCopyUtils.copy(bytes, request.getBody());
ClientHttpResponse response = request.execute();
Assert.state(response.getStatusCode() == HttpStatus.OK,
"Unexpected " + response.getStatusCode()
+ " response uploading class files");
logUpload(classLoaderFiles);
return;
}
catch (ConnectException ex) {
logger.warn("Failed to connect when uploading to " + this.uri
+ ". Upload will be retried in 2 seconds");
Thread.sleep(2000);
}
}
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
throw new IllegalStateException(ex);
}
}
private void logUpload(ClassLoaderFiles classLoaderFiles) {
int size = classLoaderFiles.size();
logger.info(
......
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 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.
......@@ -20,6 +20,7 @@ import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.ConnectException;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
......@@ -45,12 +46,14 @@ import org.springframework.mock.http.client.MockClientHttpRequest;
import org.springframework.util.FileCopyUtils;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
/**
* Tests for {@link ClassPathChangeUploader}.
*
* @author Phillip Webb
* @author Andy Wilkinson
*/
public class ClassPathChangeUploaderTests {
......@@ -102,19 +105,28 @@ public class ClassPathChangeUploaderTests {
@Test
public void sendsClassLoaderFiles() throws Exception {
File sourceFolder = this.temp.newFolder();
Set<ChangedFile> files = new LinkedHashSet<ChangedFile>();
File file1 = createFile(sourceFolder, "File1");
File file2 = createFile(sourceFolder, "File2");
File file3 = createFile(sourceFolder, "File3");
files.add(new ChangedFile(sourceFolder, file1, Type.ADD));
files.add(new ChangedFile(sourceFolder, file2, Type.MODIFY));
files.add(new ChangedFile(sourceFolder, file3, Type.DELETE));
Set<ChangedFiles> changeSet = new LinkedHashSet<ChangedFiles>();
changeSet.add(new ChangedFiles(sourceFolder, files));
ClassPathChangedEvent event = new ClassPathChangedEvent(this, changeSet, false);
ClassPathChangedEvent event = createClassPathChangedEvent(sourceFolder);
this.requestFactory.willRespond(HttpStatus.OK);
this.uploader.onApplicationEvent(event);
assertThat(this.requestFactory.getExecutedRequests().size(), is(1));
MockClientHttpRequest request = this.requestFactory.getExecutedRequests().get(0);
verifyUploadRequest(sourceFolder, request);
}
@Test
public void retriesOnConnectException() throws Exception {
File sourceFolder = this.temp.newFolder();
ClassPathChangedEvent event = createClassPathChangedEvent(sourceFolder);
this.requestFactory.willRespond(new ConnectException());
this.requestFactory.willRespond(HttpStatus.OK);
this.uploader.onApplicationEvent(event);
assertThat(this.requestFactory.getExecutedRequests().size(), is(2));
verifyUploadRequest(sourceFolder,
this.requestFactory.getExecutedRequests().get(1));
}
private void verifyUploadRequest(File sourceFolder, MockClientHttpRequest request)
throws IOException, ClassNotFoundException {
ClassLoaderFiles classLoaderFiles = deserialize(request.getBodyAsBytes());
Collection<SourceFolder> sourceFolders = classLoaderFiles.getSourceFolders();
assertThat(sourceFolders.size(), equalTo(1));
......@@ -133,6 +145,21 @@ public class ClassPathChangeUploaderTests {
assertThat(file.getKind(), equalTo(kind));
}
private ClassPathChangedEvent createClassPathChangedEvent(File sourceFolder)
throws IOException {
Set<ChangedFile> files = new LinkedHashSet<ChangedFile>();
File file1 = createFile(sourceFolder, "File1");
File file2 = createFile(sourceFolder, "File2");
File file3 = createFile(sourceFolder, "File3");
files.add(new ChangedFile(sourceFolder, file1, Type.ADD));
files.add(new ChangedFile(sourceFolder, file2, Type.MODIFY));
files.add(new ChangedFile(sourceFolder, file3, Type.DELETE));
Set<ChangedFiles> changeSet = new LinkedHashSet<ChangedFiles>();
changeSet.add(new ChangedFiles(sourceFolder, files));
ClassPathChangedEvent event = new ClassPathChangedEvent(this, changeSet, false);
return event;
}
private File createFile(File sourceFolder, String name) throws IOException {
File file = new File(sourceFolder, name);
FileCopyUtils.copy(name.getBytes(), file);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment