Commit 8cae0314 authored by Stephane Nicoll's avatar Stephane Nicoll

Remove Devtools remote debugging support

Closes gh-9489
parent bc460392
...@@ -38,10 +38,9 @@ import org.springframework.core.io.ClassPathResource; ...@@ -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. * 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 * Allows remote updates (if enabled). This class should be launched from within your IDE
* from within your IDE and should have the same classpath configuration as the locally * and should have the same classpath configuration as the locally developed application.
* developed application. The remote URL of the application should be provided as a * The remote URL of the application should be provided as a non-option argument.
* non-option argument.
* *
* @author Phillip Webb * @author Phillip Webb
* @since 1.3.0 * @since 1.3.0
......
...@@ -24,7 +24,6 @@ import org.apache.commons.logging.Log; ...@@ -24,7 +24,6 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired; 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.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
...@@ -45,10 +44,6 @@ import org.springframework.boot.devtools.restart.server.DefaultSourceFolderUrlFi ...@@ -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.HttpRestartServer;
import org.springframework.boot.devtools.restart.server.HttpRestartServerHandler; import org.springframework.boot.devtools.restart.server.HttpRestartServerHandler;
import org.springframework.boot.devtools.restart.server.SourceFolderUrlFilter; 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.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
...@@ -148,39 +143,6 @@ public class RemoteDevToolsAutoConfiguration { ...@@ -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 @Configuration
@ConditionalOnClass(WebSecurityConfigurerAdapter.class) @ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@ConditionalOnBean(ObjectPostProcessor.class) @ConditionalOnBean(ObjectPostProcessor.class)
......
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -48,8 +48,6 @@ public class RemoteDevToolsProperties { ...@@ -48,8 +48,6 @@ public class RemoteDevToolsProperties {
private Restart restart = new Restart(); private Restart restart = new Restart();
private Debug debug = new Debug();
private Proxy proxy = new Proxy(); private Proxy proxy = new Proxy();
public String getContextPath() { public String getContextPath() {
...@@ -80,10 +78,6 @@ public class RemoteDevToolsProperties { ...@@ -80,10 +78,6 @@ public class RemoteDevToolsProperties {
return this.restart; return this.restart;
} }
public Debug getDebug() {
return this.debug;
}
public Proxy getProxy() { public Proxy getProxy() {
return this.proxy; return this.proxy;
} }
...@@ -105,38 +99,6 @@ public class RemoteDevToolsProperties { ...@@ -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 { public static class Proxy {
/** /**
......
/*
* 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;
}
}
}
/*
* 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");
}
}
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -25,14 +25,12 @@ import java.util.concurrent.ExecutorService; ...@@ -25,14 +25,12 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.servlet.Filter;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; 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.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
...@@ -52,11 +50,7 @@ import org.springframework.boot.devtools.livereload.LiveReloadServer; ...@@ -52,11 +50,7 @@ import org.springframework.boot.devtools.livereload.LiveReloadServer;
import org.springframework.boot.devtools.restart.DefaultRestartInitializer; import org.springframework.boot.devtools.restart.DefaultRestartInitializer;
import org.springframework.boot.devtools.restart.RestartScope; import org.springframework.boot.devtools.restart.RestartScope;
import org.springframework.boot.devtools.restart.Restarter; 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.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
...@@ -120,9 +114,8 @@ public class RemoteClientConfiguration { ...@@ -120,9 +114,8 @@ public class RemoteClientConfiguration {
@PostConstruct @PostConstruct
private void logWarnings() { private void logWarnings() {
RemoteDevToolsProperties remoteProperties = this.properties.getRemote(); RemoteDevToolsProperties remoteProperties = this.properties.getRemote();
if (!remoteProperties.getDebug().isEnabled() if (!remoteProperties.getRestart().isEnabled()) {
&& !remoteProperties.getRestart().isEnabled()) { logger.warn("Remote restart is disabled.");
logger.warn("Remote restart and debug are both disabled.");
} }
if (!this.remoteUrl.startsWith("https://")) { if (!this.remoteUrl.startsWith("https://")) {
logger.warn("The connection to " + this.remoteUrl logger.warn("The connection to " + this.remoteUrl
...@@ -239,32 +232,4 @@ public class RemoteClientConfiguration { ...@@ -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;
}
}
} }
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -196,12 +196,6 @@ public class HttpTunnelConnection implements TunnelConnection { ...@@ -196,12 +196,6 @@ public class HttpTunnelConnection implements TunnelConnection {
close(); close();
return; 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) { if (response.getStatusCode() == HttpStatus.OK) {
HttpTunnelPayload payload = HttpTunnelPayload.get(response); HttpTunnelPayload payload = HttpTunnelPayload.get(response);
if (payload != null) { if (payload != null) {
......
...@@ -158,9 +158,6 @@ public class HttpTunnelServer { ...@@ -158,9 +158,6 @@ public class HttpTunnelServer {
catch (ConnectException ex) { catch (ConnectException ex) {
httpConnection.respond(HttpStatus.GONE); httpConnection.respond(HttpStatus.GONE);
} }
catch (RemoteDebugNotRunningException ex) {
httpConnection.respond(HttpStatus.SERVICE_UNAVAILABLE);
}
} }
/** /**
......
/*
* 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 {
}
/*
* 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; ...@@ -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.HttpRestartServer;
import org.springframework.boot.devtools.restart.server.SourceFolderUrlFilter; import org.springframework.boot.devtools.restart.server.SourceFolderUrlFilter;
import org.springframework.boot.devtools.tunnel.server.HttpTunnelServer; 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.devtools.tunnel.server.TargetServerConnection;
import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
...@@ -154,46 +152,6 @@ public class RemoteDevToolsAutoConfigurationTests { ...@@ -154,46 +152,6 @@ public class RemoteDevToolsAutoConfigurationTests {
this.context.getBean("remoteRestartHandlerMapper"); 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 @Test
public void devToolsHealthReturns200() throws Exception { public void devToolsHealthReturns200() throws Exception {
loadContext("spring.devtools.remote.secret:supersecret"); loadContext("spring.devtools.remote.secret:supersecret");
...@@ -239,12 +197,6 @@ public class RemoteDevToolsAutoConfigurationTests { ...@@ -239,12 +197,6 @@ public class RemoteDevToolsAutoConfigurationTests {
@Import(RemoteDevToolsAutoConfiguration.class) @Import(RemoteDevToolsAutoConfiguration.class)
static class Config { static class Config {
@Bean
public HttpTunnelServer remoteDebugHttpTunnelServer() {
return new MockHttpTunnelServer(
new SocketTargetServerConnection(new RemoteDebugPortProvider()));
}
@Bean @Bean
public HttpRestartServer remoteRestartHttpRestartServer() { public HttpRestartServer remoteRestartHttpRestartServer() {
SourceFolderUrlFilter sourceFolderUrlFilter = mock( SourceFolderUrlFilter sourceFolderUrlFilter = mock(
......
/*
* 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; ...@@ -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.remote.server.DispatcherFilter;
import org.springframework.boot.devtools.restart.MockRestarter; import org.springframework.boot.devtools.restart.MockRestarter;
import org.springframework.boot.devtools.restart.RestartScopeInitializer; 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.rule.OutputCapture;
import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
...@@ -86,11 +85,10 @@ public class RemoteClientConfigurationTests { ...@@ -86,11 +85,10 @@ public class RemoteClientConfigurationTests {
} }
@Test @Test
public void warnIfDebugAndRestartDisabled() throws Exception { public void warnIfRestartDisabled() throws Exception {
configure("spring.devtools.remote.debug.enabled:false", configure("spring.devtools.remote.restart.enabled:false");
"spring.devtools.remote.restart.enabled:false");
assertThat(this.output.toString()) assertThat(this.output.toString())
.contains("Remote restart and debug are both disabled"); .contains("Remote restart is disabled");
} }
@Test @Test
...@@ -140,13 +138,6 @@ public class RemoteClientConfigurationTests { ...@@ -140,13 +138,6 @@ public class RemoteClientConfigurationTests {
this.context.getBean(ClassPathFileSystemWatcher.class); 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) { private void configure(String... pairs) {
configure("http://localhost", true, pairs); configure("http://localhost", true, pairs);
} }
......
...@@ -146,15 +146,6 @@ public class HttpTunnelConnectionTests { ...@@ -146,15 +146,6 @@ public class HttpTunnelConnectionTests {
assertThat(this.requestFactory.getExecutedRequests().size()).isGreaterThan(10); 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 @Test
public void connectFailureLogsWarning() throws Exception { public void connectFailureLogsWarning() throws Exception {
this.requestFactory.willRespond(new ConnectException()); this.requestFactory.willRespond(new ConnectException());
......
...@@ -1265,8 +1265,6 @@ content into your application; rather pick only the properties that you need. ...@@ -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]) # 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.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.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.proxy.port= # The port of the proxy to use to connect to the remote application.
spring.devtools.remote.restart.enabled=true # Enable remote restart. 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. ...@@ -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]] [[using-boot-packaging-for-production]]
== Packaging your application for production == Packaging your application for production
Executable jars can be used for production deployment. As they are self-contained, they Executable jars can be used for production deployment. As they are self-contained, they
......
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