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. |  * 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; | ||||||
| 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 | ||||||
| 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 { | ||||||
| 
 | 
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** |  | ||||||
| 	 * 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) | ||||||
|  |  | ||||||
|  | @ -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"); |  * 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 { | ||||||
| 
 | 
 | ||||||
| 	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 { | ||||||
| 		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 { | ||||||
| 
 | 
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	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 { | ||||||
| 
 | 
 | ||||||
| 		/** | 		/** | ||||||
|  |  | ||||||
|  | @ -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"); |  * 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; | ||||||
| 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; | ||||||
| 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 { | ||||||
| 	@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 { | ||||||
| 
 | 
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** |  | ||||||
| 	 * 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"); |  * 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 { | ||||||
| 				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 { | ||||||
| 		catch (ConnectException ex) { | 		catch (ConnectException ex) { | ||||||
| 			httpConnection.respond(HttpStatus.GONE); | 			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.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 { | ||||||
| 		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 { | ||||||
| 	@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( | ||||||
|  |  | ||||||
|  | @ -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.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 { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@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 { | ||||||
| 		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 { | ||||||
| 		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. | ||||||
| 
 | 
 | ||||||
| 	# 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. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| [[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 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue