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