Trigger livereload on remote updates
Add livereload support to RemoteClientConfiguration which is triggered whenever updates are pushed to the remote application. Closes gh-3086pull/3077/merge
parent
05ea2d77ef
commit
c27b63b354
@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-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 org.springframework.boot.developertools.remote.client;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.springframework.boot.developertools.autoconfigure.OptionalLiveReloadServer;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.client.ClientHttpRequest;
|
||||||
|
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||||
|
import org.springframework.http.client.ClientHttpResponse;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Runnable} that waits to triggers live reload until the remote server has
|
||||||
|
* restarted.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
*/
|
||||||
|
class DelayedLiveReloadTrigger implements Runnable {
|
||||||
|
|
||||||
|
private static final long SHUTDOWN_TIME = 1000;
|
||||||
|
|
||||||
|
private static final long SLEEP_TIME = 500;
|
||||||
|
|
||||||
|
private static final long TIMEOUT = 30000;
|
||||||
|
|
||||||
|
private static final Log logger = LogFactory.getLog(DelayedLiveReloadTrigger.class);
|
||||||
|
|
||||||
|
private final OptionalLiveReloadServer liveReloadServer;
|
||||||
|
|
||||||
|
private final ClientHttpRequestFactory requestFactory;
|
||||||
|
|
||||||
|
private final URI uri;
|
||||||
|
|
||||||
|
private long shutdownTime = SHUTDOWN_TIME;
|
||||||
|
|
||||||
|
private long sleepTime = SLEEP_TIME;
|
||||||
|
|
||||||
|
private long timeout = TIMEOUT;
|
||||||
|
|
||||||
|
public DelayedLiveReloadTrigger(OptionalLiveReloadServer liveReloadServer,
|
||||||
|
ClientHttpRequestFactory requestFactory, String url) {
|
||||||
|
Assert.notNull(liveReloadServer, "LiveReloadServer must not be null");
|
||||||
|
Assert.notNull(requestFactory, "RequestFactory must not be null");
|
||||||
|
Assert.hasLength(url, "URL must not be empty");
|
||||||
|
this.liveReloadServer = liveReloadServer;
|
||||||
|
this.requestFactory = requestFactory;
|
||||||
|
try {
|
||||||
|
this.uri = new URI(url);
|
||||||
|
}
|
||||||
|
catch (URISyntaxException ex) {
|
||||||
|
throw new IllegalArgumentException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setTimings(long shutdown, long sleep, long timeout) {
|
||||||
|
this.shutdownTime = shutdown;
|
||||||
|
this.sleepTime = sleep;
|
||||||
|
this.timeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
Thread.sleep(this.shutdownTime);
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
while (!isUp()) {
|
||||||
|
long runTime = System.currentTimeMillis() - start;
|
||||||
|
if (runTime > this.timeout) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Thread.sleep(this.sleepTime);
|
||||||
|
}
|
||||||
|
logger.info("Remote server has changed, triggering LiveReload");
|
||||||
|
this.liveReloadServer.triggerReload();
|
||||||
|
}
|
||||||
|
catch (InterruptedException ex) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isUp() {
|
||||||
|
try {
|
||||||
|
ClientHttpRequest request = createRequest();
|
||||||
|
ClientHttpResponse response = request.execute();
|
||||||
|
return response.getStatusCode() == HttpStatus.OK;
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClientHttpRequest createRequest() throws IOException {
|
||||||
|
return this.requestFactory.createRequest(this.uri, HttpMethod.GET);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-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 org.springframework.boot.developertools.remote.client;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.ExpectedException;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.springframework.boot.developertools.autoconfigure.OptionalLiveReloadServer;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.client.ClientHttpRequest;
|
||||||
|
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||||
|
import org.springframework.http.client.ClientHttpResponse;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.greaterThan;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link DelayedLiveReloadTrigger}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
*/
|
||||||
|
public class DelayedLiveReloadTriggerTests {
|
||||||
|
|
||||||
|
private static final String URL = "http://localhost:8080";
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public ExpectedException thrown = ExpectedException.none();
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private OptionalLiveReloadServer liveReloadServer;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ClientHttpRequestFactory requestFactory;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ClientHttpRequest errorRequest;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ClientHttpRequest okRequest;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ClientHttpResponse errorResponse;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ClientHttpResponse okResponse;
|
||||||
|
|
||||||
|
private DelayedLiveReloadTrigger trigger;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() throws IOException {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
given(this.errorRequest.execute()).willReturn(this.errorResponse);
|
||||||
|
given(this.okRequest.execute()).willReturn(this.okResponse);
|
||||||
|
given(this.errorResponse.getStatusCode()).willReturn(
|
||||||
|
HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
given(this.okResponse.getStatusCode()).willReturn(HttpStatus.OK);
|
||||||
|
this.trigger = new DelayedLiveReloadTrigger(this.liveReloadServer,
|
||||||
|
this.requestFactory, URL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void liveReloadServerMustNotBeNull() throws Exception {
|
||||||
|
this.thrown.expect(IllegalArgumentException.class);
|
||||||
|
this.thrown.expectMessage("LiveReloadServer must not be null");
|
||||||
|
new DelayedLiveReloadTrigger(null, this.requestFactory, URL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestFactoryMustNotBeNull() throws Exception {
|
||||||
|
this.thrown.expect(IllegalArgumentException.class);
|
||||||
|
this.thrown.expectMessage("RequestFactory must not be null");
|
||||||
|
new DelayedLiveReloadTrigger(this.liveReloadServer, null, URL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void urlMostNotBeNull() throws Exception {
|
||||||
|
this.thrown.expect(IllegalArgumentException.class);
|
||||||
|
this.thrown.expectMessage("URL must not be empty");
|
||||||
|
new DelayedLiveReloadTrigger(this.liveReloadServer, this.requestFactory, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void urlMustNotBeEmpty() throws Exception {
|
||||||
|
this.thrown.expect(IllegalArgumentException.class);
|
||||||
|
this.thrown.expectMessage("URL must not be empty");
|
||||||
|
new DelayedLiveReloadTrigger(this.liveReloadServer, this.requestFactory, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void triggerReloadOnStatus() throws Exception {
|
||||||
|
given(this.requestFactory.createRequest(new URI(URL), HttpMethod.GET)).willThrow(
|
||||||
|
new IOException()).willReturn(this.errorRequest, this.okRequest);
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
this.trigger.setTimings(10, 200, 30000);
|
||||||
|
this.trigger.run();
|
||||||
|
assertThat(System.currentTimeMillis() - startTime, greaterThan(300L));
|
||||||
|
verify(this.liveReloadServer).triggerReload();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void timeout() throws Exception {
|
||||||
|
given(this.requestFactory.createRequest(new URI(URL), HttpMethod.GET)).willThrow(
|
||||||
|
new IOException());
|
||||||
|
this.trigger.setTimings(10, 0, 10);
|
||||||
|
this.trigger.run();
|
||||||
|
verify(this.liveReloadServer, never()).triggerReload();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue