parent
bc4603925e
commit
8cae031441
|
|
@ -38,10 +38,9 @@ import org.springframework.core.io.ClassPathResource;
|
|||
|
||||
/**
|
||||
* Application that can be used to establish a link to remotely running Spring Boot code.
|
||||
* Allows remote debugging and remote updates (if enabled). This class should be launched
|
||||
* from within your IDE and should have the same classpath configuration as the locally
|
||||
* developed application. The remote URL of the application should be provided as a
|
||||
* non-option argument.
|
||||
* Allows remote updates (if enabled). This class should be launched from within your IDE
|
||||
* and should have the same classpath configuration as the locally developed application.
|
||||
* The remote URL of the application should be provided as a non-option argument.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.3.0
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import org.apache.commons.logging.Log;
|
|||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
|
|
@ -45,10 +44,6 @@ import org.springframework.boot.devtools.restart.server.DefaultSourceFolderUrlFi
|
|||
import org.springframework.boot.devtools.restart.server.HttpRestartServer;
|
||||
import org.springframework.boot.devtools.restart.server.HttpRestartServerHandler;
|
||||
import org.springframework.boot.devtools.restart.server.SourceFolderUrlFilter;
|
||||
import org.springframework.boot.devtools.tunnel.server.HttpTunnelServer;
|
||||
import org.springframework.boot.devtools.tunnel.server.HttpTunnelServerHandler;
|
||||
import org.springframework.boot.devtools.tunnel.server.RemoteDebugPortProvider;
|
||||
import org.springframework.boot.devtools.tunnel.server.SocketTargetServerConnection;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
|
@ -148,39 +143,6 @@ public class RemoteDevToolsAutoConfiguration {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration for remote debug HTTP tunneling.
|
||||
*/
|
||||
@ConditionalOnProperty(prefix = "spring.devtools.remote.debug", name = "enabled", matchIfMissing = true)
|
||||
static class RemoteDebugTunnelConfiguration {
|
||||
|
||||
@Autowired
|
||||
private DevToolsProperties properties;
|
||||
|
||||
@Autowired
|
||||
private ServerProperties serverProperties;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "remoteDebugHandlerMapper")
|
||||
public UrlHandlerMapper remoteDebugHandlerMapper(
|
||||
@Qualifier("remoteDebugHttpTunnelServer") HttpTunnelServer server) {
|
||||
String url = (this.serverProperties.getServlet().getContextPath() == null ? ""
|
||||
: this.serverProperties.getServlet().getContextPath())
|
||||
+ this.properties.getRemote().getContextPath() + "/debug";
|
||||
logger.warn("Listening for remote debug traffic on " + url);
|
||||
Handler handler = new HttpTunnelServerHandler(server);
|
||||
return new UrlHandlerMapper(url, handler);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "remoteDebugHttpTunnelServer")
|
||||
public HttpTunnelServer remoteDebugHttpTunnelServer() {
|
||||
return new HttpTunnelServer(
|
||||
new SocketTargetServerConnection(new RemoteDebugPortProvider()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
|
||||
@ConditionalOnBean(ObjectPostProcessor.class)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
* Copyright 2012-2017 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.
|
||||
|
|
@ -48,8 +48,6 @@ public class RemoteDevToolsProperties {
|
|||
|
||||
private Restart restart = new Restart();
|
||||
|
||||
private Debug debug = new Debug();
|
||||
|
||||
private Proxy proxy = new Proxy();
|
||||
|
||||
public String getContextPath() {
|
||||
|
|
@ -80,10 +78,6 @@ public class RemoteDevToolsProperties {
|
|||
return this.restart;
|
||||
}
|
||||
|
||||
public Debug getDebug() {
|
||||
return this.debug;
|
||||
}
|
||||
|
||||
public Proxy getProxy() {
|
||||
return this.proxy;
|
||||
}
|
||||
|
|
@ -105,38 +99,6 @@ public class RemoteDevToolsProperties {
|
|||
|
||||
}
|
||||
|
||||
public static class Debug {
|
||||
|
||||
public static final Integer DEFAULT_LOCAL_PORT = 8000;
|
||||
|
||||
/**
|
||||
* Enable remote debug support.
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* Local remote debug server port.
|
||||
*/
|
||||
private int localPort = DEFAULT_LOCAL_PORT;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public int getLocalPort() {
|
||||
return this.localPort;
|
||||
}
|
||||
|
||||
public void setLocalPort(int localPort) {
|
||||
this.localPort = localPort;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Proxy {
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.devtools.remote.client;
|
||||
|
||||
import javax.net.ServerSocketFactory;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.boot.devtools.autoconfigure.RemoteDevToolsProperties;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
|
||||
/**
|
||||
* Condition used to check that the actual local port is available.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
class LocalDebugPortAvailableCondition extends SpringBootCondition {
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
ConditionMessage.Builder message = ConditionMessage
|
||||
.forCondition("Local Debug Port Condition");
|
||||
Integer port = context.getEnvironment().getProperty(
|
||||
"spring.devtools.remote.debug.local-port", Integer.class,
|
||||
RemoteDevToolsProperties.Debug.DEFAULT_LOCAL_PORT);
|
||||
if (isPortAvailable(port)) {
|
||||
return ConditionOutcome.match(message.foundExactly("local debug port"));
|
||||
}
|
||||
return ConditionOutcome.noMatch(message.didNotFind("local debug port").atAll());
|
||||
}
|
||||
|
||||
private boolean isPortAvailable(int port) {
|
||||
try {
|
||||
ServerSocketFactory.getDefault().createServerSocket(port).close();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* 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.devtools.remote.client;
|
||||
|
||||
import java.nio.channels.SocketChannel;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.devtools.tunnel.client.TunnelClientListener;
|
||||
|
||||
/**
|
||||
* {@link TunnelClientListener} to log open/close events.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class LoggingTunnelClientListener implements TunnelClientListener {
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(LoggingTunnelClientListener.class);
|
||||
|
||||
@Override
|
||||
public void onOpen(SocketChannel socket) {
|
||||
logger.info("Remote debug connection opened");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(SocketChannel socket) {
|
||||
logger.info("Remote debug connection closed");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
* Copyright 2012-2017 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.
|
||||
|
|
@ -25,14 +25,12 @@ import java.util.concurrent.ExecutorService;
|
|||
import java.util.concurrent.Executors;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.servlet.Filter;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
|
|
@ -52,11 +50,7 @@ import org.springframework.boot.devtools.livereload.LiveReloadServer;
|
|||
import org.springframework.boot.devtools.restart.DefaultRestartInitializer;
|
||||
import org.springframework.boot.devtools.restart.RestartScope;
|
||||
import org.springframework.boot.devtools.restart.Restarter;
|
||||
import org.springframework.boot.devtools.tunnel.client.HttpTunnelConnection;
|
||||
import org.springframework.boot.devtools.tunnel.client.TunnelClient;
|
||||
import org.springframework.boot.devtools.tunnel.client.TunnelConnection;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||
|
|
@ -120,9 +114,8 @@ public class RemoteClientConfiguration {
|
|||
@PostConstruct
|
||||
private void logWarnings() {
|
||||
RemoteDevToolsProperties remoteProperties = this.properties.getRemote();
|
||||
if (!remoteProperties.getDebug().isEnabled()
|
||||
&& !remoteProperties.getRestart().isEnabled()) {
|
||||
logger.warn("Remote restart and debug are both disabled.");
|
||||
if (!remoteProperties.getRestart().isEnabled()) {
|
||||
logger.warn("Remote restart is disabled.");
|
||||
}
|
||||
if (!this.remoteUrl.startsWith("https://")) {
|
||||
logger.warn("The connection to " + this.remoteUrl
|
||||
|
|
@ -239,32 +232,4 @@ public class RemoteClientConfiguration {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Client configuration for remote debug HTTP tunneling.
|
||||
*/
|
||||
@ConditionalOnProperty(prefix = "spring.devtools.remote.debug", name = "enabled", matchIfMissing = true)
|
||||
@ConditionalOnClass(Filter.class)
|
||||
@Conditional(LocalDebugPortAvailableCondition.class)
|
||||
static class RemoteDebugTunnelClientConfiguration {
|
||||
|
||||
@Autowired
|
||||
private DevToolsProperties properties;
|
||||
|
||||
@Value("${remoteUrl}")
|
||||
private String remoteUrl;
|
||||
|
||||
@Bean
|
||||
public TunnelClient remoteDebugTunnelClient(
|
||||
ClientHttpRequestFactory requestFactory) {
|
||||
RemoteDevToolsProperties remoteProperties = this.properties.getRemote();
|
||||
String url = this.remoteUrl + remoteProperties.getContextPath() + "/debug";
|
||||
TunnelConnection connection = new HttpTunnelConnection(url, requestFactory);
|
||||
int localPort = remoteProperties.getDebug().getLocalPort();
|
||||
TunnelClient client = new TunnelClient(localPort, connection);
|
||||
client.addListener(new LoggingTunnelClientListener());
|
||||
return client;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
* Copyright 2012-2017 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.
|
||||
|
|
@ -196,12 +196,6 @@ public class HttpTunnelConnection implements TunnelConnection {
|
|||
close();
|
||||
return;
|
||||
}
|
||||
if (response.getStatusCode() == HttpStatus.SERVICE_UNAVAILABLE) {
|
||||
logger.warn("Remote application responded with service unavailable. Did "
|
||||
+ "you forget to start it with remote debugging enabled?");
|
||||
close();
|
||||
return;
|
||||
}
|
||||
if (response.getStatusCode() == HttpStatus.OK) {
|
||||
HttpTunnelPayload payload = HttpTunnelPayload.get(response);
|
||||
if (payload != null) {
|
||||
|
|
|
|||
|
|
@ -158,9 +158,6 @@ public class HttpTunnelServer {
|
|||
catch (ConnectException ex) {
|
||||
httpConnection.respond(HttpStatus.GONE);
|
||||
}
|
||||
catch (RemoteDebugNotRunningException ex) {
|
||||
httpConnection.respond(HttpStatus.SERVICE_UNAVAILABLE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
* 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.devtools.tunnel.server;
|
||||
|
||||
/**
|
||||
* Exception thrown to indicate that remote debug is not running.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class RemoteDebugNotRunningException extends RuntimeException {
|
||||
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
* 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.devtools.tunnel.server;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.lang.UsesUnsafeJava;
|
||||
|
||||
/**
|
||||
* {@link PortProvider} that provides the port being used by the Java remote debugging.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class RemoteDebugPortProvider implements PortProvider {
|
||||
|
||||
private static final String JDWP_ADDRESS_PROPERTY = "sun.jdwp.listenerAddress";
|
||||
|
||||
private static final Log logger = LogFactory.getLog(RemoteDebugPortProvider.class);
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
if (!isRemoteDebugRunning()) {
|
||||
throw new RemoteDebugNotRunningException();
|
||||
}
|
||||
return getRemoteDebugPort();
|
||||
}
|
||||
|
||||
public static boolean isRemoteDebugRunning() {
|
||||
return getRemoteDebugPort() != -1;
|
||||
}
|
||||
|
||||
@UsesUnsafeJava
|
||||
@SuppressWarnings("restriction")
|
||||
private static int getRemoteDebugPort() {
|
||||
String property = sun.misc.VMSupport.getAgentProperties()
|
||||
.getProperty(JDWP_ADDRESS_PROPERTY);
|
||||
try {
|
||||
if (property != null && property.contains(":")) {
|
||||
return Integer.valueOf(property.split(":")[1]);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.trace(
|
||||
"Unable to get JDWP port from property value '" + property + "'");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -31,8 +31,6 @@ import org.springframework.boot.devtools.restart.MockRestarter;
|
|||
import org.springframework.boot.devtools.restart.server.HttpRestartServer;
|
||||
import org.springframework.boot.devtools.restart.server.SourceFolderUrlFilter;
|
||||
import org.springframework.boot.devtools.tunnel.server.HttpTunnelServer;
|
||||
import org.springframework.boot.devtools.tunnel.server.RemoteDebugPortProvider;
|
||||
import org.springframework.boot.devtools.tunnel.server.SocketTargetServerConnection;
|
||||
import org.springframework.boot.devtools.tunnel.server.TargetServerConnection;
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
|
@ -154,46 +152,6 @@ public class RemoteDevToolsAutoConfigurationTests {
|
|||
this.context.getBean("remoteRestartHandlerMapper");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeTunnelWithDefaultSetup() throws Exception {
|
||||
loadContext("spring.devtools.remote.secret:supersecret");
|
||||
DispatcherFilter filter = this.context.getBean(DispatcherFilter.class);
|
||||
this.request.setRequestURI(DEFAULT_CONTEXT_PATH + "/debug");
|
||||
this.request.addHeader(DEFAULT_SECRET_HEADER_NAME, "supersecret");
|
||||
filter.doFilter(this.request, this.response, this.chain);
|
||||
assertTunnelInvoked(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeTunnelWithCustomServerContextPath() throws Exception {
|
||||
loadContext("spring.devtools.remote.secret:supersecret",
|
||||
"server.servlet.context-path:/test");
|
||||
DispatcherFilter filter = this.context.getBean(DispatcherFilter.class);
|
||||
this.request.setRequestURI("/test" + DEFAULT_CONTEXT_PATH + "/debug");
|
||||
this.request.addHeader(DEFAULT_SECRET_HEADER_NAME, "supersecret");
|
||||
filter.doFilter(this.request, this.response, this.chain);
|
||||
assertTunnelInvoked(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeTunnelWithCustomHeaderName() throws Exception {
|
||||
loadContext("spring.devtools.remote.secret:supersecret",
|
||||
"spring.devtools.remote.secretHeaderName:customheader");
|
||||
DispatcherFilter filter = this.context.getBean(DispatcherFilter.class);
|
||||
this.request.setRequestURI(DEFAULT_CONTEXT_PATH + "/debug");
|
||||
this.request.addHeader("customheader", "supersecret");
|
||||
filter.doFilter(this.request, this.response, this.chain);
|
||||
assertTunnelInvoked(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disableRemoteDebug() throws Exception {
|
||||
loadContext("spring.devtools.remote.secret:supersecret",
|
||||
"spring.devtools.remote.debug.enabled:false");
|
||||
this.thrown.expect(NoSuchBeanDefinitionException.class);
|
||||
this.context.getBean("remoteDebugHandlerMapper");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void devToolsHealthReturns200() throws Exception {
|
||||
loadContext("spring.devtools.remote.secret:supersecret");
|
||||
|
|
@ -239,12 +197,6 @@ public class RemoteDevToolsAutoConfigurationTests {
|
|||
@Import(RemoteDevToolsAutoConfiguration.class)
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public HttpTunnelServer remoteDebugHttpTunnelServer() {
|
||||
return new MockHttpTunnelServer(
|
||||
new SocketTargetServerConnection(new RemoteDebugPortProvider()));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HttpRestartServer remoteRestartHttpRestartServer() {
|
||||
SourceFolderUrlFilter sourceFolderUrlFilter = mock(
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.devtools.remote.client;
|
||||
|
||||
import java.net.ServerSocket;
|
||||
|
||||
import javax.net.ServerSocketFactory;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link LocalDebugPortAvailableCondition}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class LocalDebugPortAvailableConditionTests {
|
||||
|
||||
private LocalDebugPortAvailableCondition condition = new LocalDebugPortAvailableCondition();
|
||||
|
||||
@Test
|
||||
public void portAvailable() throws Exception {
|
||||
ConditionOutcome outcome = getOutcome(0);
|
||||
assertThat(outcome.isMatch()).isTrue();
|
||||
assertThat(outcome.getMessage())
|
||||
.isEqualTo("Local Debug Port Condition found local debug port");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void portInUse() throws Exception {
|
||||
ServerSocket serverSocket = ServerSocketFactory.getDefault()
|
||||
.createServerSocket(0);
|
||||
ConditionOutcome outcome = getOutcome(serverSocket.getLocalPort());
|
||||
serverSocket.close();
|
||||
assertThat(outcome.isMatch()).isFalse();
|
||||
assertThat(outcome.getMessage())
|
||||
.isEqualTo("Local Debug Port Condition did not find local debug port");
|
||||
}
|
||||
|
||||
private ConditionOutcome getOutcome(int port) {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
TestPropertyValues.of("spring.devtools.remote.debug.local-port:" + port)
|
||||
.applyTo(environment);
|
||||
ConditionContext context = mock(ConditionContext.class);
|
||||
given(context.getEnvironment()).willReturn(environment);
|
||||
ConditionOutcome outcome = this.condition.getMatchOutcome(context, null);
|
||||
return outcome;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -38,7 +38,6 @@ import org.springframework.boot.devtools.remote.server.Dispatcher;
|
|||
import org.springframework.boot.devtools.remote.server.DispatcherFilter;
|
||||
import org.springframework.boot.devtools.restart.MockRestarter;
|
||||
import org.springframework.boot.devtools.restart.RestartScopeInitializer;
|
||||
import org.springframework.boot.devtools.tunnel.client.TunnelClient;
|
||||
import org.springframework.boot.test.rule.OutputCapture;
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
||||
|
|
@ -86,11 +85,10 @@ public class RemoteClientConfigurationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void warnIfDebugAndRestartDisabled() throws Exception {
|
||||
configure("spring.devtools.remote.debug.enabled:false",
|
||||
"spring.devtools.remote.restart.enabled:false");
|
||||
public void warnIfRestartDisabled() throws Exception {
|
||||
configure("spring.devtools.remote.restart.enabled:false");
|
||||
assertThat(this.output.toString())
|
||||
.contains("Remote restart and debug are both disabled");
|
||||
.contains("Remote restart is disabled");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -140,13 +138,6 @@ public class RemoteClientConfigurationTests {
|
|||
this.context.getBean(ClassPathFileSystemWatcher.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void remoteDebugDisabled() throws Exception {
|
||||
configure("spring.devtools.remote.debug.enabled:false");
|
||||
this.thrown.expect(NoSuchBeanDefinitionException.class);
|
||||
this.context.getBean(TunnelClient.class);
|
||||
}
|
||||
|
||||
private void configure(String... pairs) {
|
||||
configure("http://localhost", true, pairs);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -146,15 +146,6 @@ public class HttpTunnelConnectionTests {
|
|||
assertThat(this.requestFactory.getExecutedRequests().size()).isGreaterThan(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serviceUnavailableResponseLogsWarningAndClosesTunnel() throws Exception {
|
||||
this.requestFactory.willRespond(HttpStatus.SERVICE_UNAVAILABLE);
|
||||
TunnelChannel tunnel = openTunnel(true);
|
||||
assertThat(tunnel.isOpen()).isFalse();
|
||||
this.outputCapture.expect(containsString(
|
||||
"Did you forget to start it with remote debugging enabled?"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void connectFailureLogsWarning() throws Exception {
|
||||
this.requestFactory.willRespond(new ConnectException());
|
||||
|
|
|
|||
|
|
@ -1265,8 +1265,6 @@ content into your application; rather pick only the properties that you need.
|
|||
|
||||
# REMOTE DEVTOOLS ({sc-spring-boot-devtools}/autoconfigure/RemoteDevToolsProperties.{sc-ext}[RemoteDevToolsProperties])
|
||||
spring.devtools.remote.context-path=/.~~spring-boot!~ # Context path used to handle the remote connection.
|
||||
spring.devtools.remote.debug.enabled=true # Enable remote debug support.
|
||||
spring.devtools.remote.debug.local-port=8000 # Local remote debug server port.
|
||||
spring.devtools.remote.proxy.host= # The host of the proxy to use to connect to the remote application.
|
||||
spring.devtools.remote.proxy.port= # The port of the proxy to use to connect to the remote application.
|
||||
spring.devtools.remote.restart.enabled=true # Enable remote restart.
|
||||
|
|
|
|||
|
|
@ -1047,49 +1047,6 @@ before starting the remote client, it won't be pushed to the remote server.
|
|||
|
||||
|
||||
|
||||
[[using-boot-devtools-remote-debugtunnel]]
|
||||
==== Remote debug tunnel
|
||||
Java remote debugging is useful when diagnosing issues on a remote application.
|
||||
Unfortunately, it's not always possible to enable remote debugging when your application
|
||||
is deployed outside of your data center. Remote debugging can also be tricky to setup if
|
||||
you are using a container based technology such as Docker.
|
||||
|
||||
To help work around these limitations, devtools supports tunneling of remote debug traffic
|
||||
over HTTP. The remote client provides a local server on port `8000` that you can attach
|
||||
a remote debugger to. Once a connection is established, debug traffic is sent over HTTP
|
||||
to the remote application. You can use the `spring.devtools.remote.debug.local-port`
|
||||
property if you want to use a different port.
|
||||
|
||||
You'll need to ensure that your remote application is started with remote debugging
|
||||
enabled. Often this can be achieved by configuring `JAVA_OPTS`. For example, with
|
||||
Cloud Foundry you can add the following to your `manifest.yml`:
|
||||
|
||||
[source,yaml,indent=0]
|
||||
----
|
||||
---
|
||||
env:
|
||||
JAVA_OPTS: "-Xdebug -Xrunjdwp:server=y,transport=dt_socket,suspend=n"
|
||||
----
|
||||
|
||||
TIP: Notice that you don't need to pass an `address=NNNN` option to `-Xrunjdwp`. If
|
||||
omitted Java will simply pick a random free port.
|
||||
|
||||
NOTE: Debugging a remote service over the Internet can be slow and you might need to
|
||||
increase timeouts in your IDE. For example, in Eclipse you can select `Java` -> `Debug`
|
||||
from `Preferences...` and change the `Debugger timeout (ms)` to a more suitable value
|
||||
(`60000` works well in most situations).
|
||||
|
||||
WARNING: When using the remote debug tunnel with IntelliJ IDEA, all breakpoints must be
|
||||
configured to suspend the thread rather than the VM. By default, breakpoints in IntelliJ
|
||||
IDEA suspend the entire VM rather than only suspending the thread that hit the
|
||||
breakpoint. This has the unwanted side-effect of suspending the thread that manages the
|
||||
remote debug tunnel, causing your debugging session to freeze. When using the remote
|
||||
debug tunnel with IntelliJ IDEA, all breakpoints should be configured to suspend the
|
||||
thread rather than the VM. Please see
|
||||
https://youtrack.jetbrains.com/issue/IDEA-165769[IDEA-165769] for further details.
|
||||
|
||||
|
||||
|
||||
[[using-boot-packaging-for-production]]
|
||||
== Packaging your application for production
|
||||
Executable jars can be used for production deployment. As they are self-contained, they
|
||||
|
|
|
|||
Loading…
Reference in New Issue