diff --git a/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/RemoteSpringApplication.java b/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/RemoteSpringApplication.java new file mode 100644 index 00000000000..98b54a1c345 --- /dev/null +++ b/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/RemoteSpringApplication.java @@ -0,0 +1,76 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.developertools; + +import org.springframework.boot.Banner; +import org.springframework.boot.ResourceBanner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.developertools.remote.client.RemoteClientConfiguration; +import org.springframework.boot.developertools.restart.RestartInitializer; +import org.springframework.boot.developertools.restart.Restarter; +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. + * + * @author Phillip Webb + * @since 1.3.0 + * @see RemoteClientConfiguration + */ +public class RemoteSpringApplication { + + private void run(String[] args) { + Restarter.initialize(args, RestartInitializer.NONE); + SpringApplication application = new SpringApplication( + RemoteClientConfiguration.class); + application.setWebEnvironment(false); + application.setBanner(getBanner()); + application.addListeners(new RemoteUrlPropertyExtractor()); + application.run(args); + waitIndefinitely(); + } + + private Banner getBanner() { + ClassPathResource banner = new ClassPathResource("remote-banner.txt", + RemoteSpringApplication.class); + return new ResourceBanner(banner); + } + + private void waitIndefinitely() { + while (true) { + try { + Thread.sleep(1000); + } + catch (InterruptedException ex) { + } + } + } + + /** + * Run the {@link RemoteSpringApplication}. + * @param args the program arguments (including the remote URL as a non-option + * argument) + */ + public static void main(String[] args) { + new RemoteSpringApplication().run(args); + } + +} diff --git a/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/RemoteUrlPropertyExtractor.java b/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/RemoteUrlPropertyExtractor.java new file mode 100644 index 00000000000..0741b445322 --- /dev/null +++ b/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/RemoteUrlPropertyExtractor.java @@ -0,0 +1,61 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.developertools; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Collections; +import java.util.Map; + +import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.core.env.CommandLinePropertySource; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * {@link ApplicationListener} to extract the remote URL for the + * {@link RemoteSpringApplication} to use. + * + * @author Phillip Webb + */ +class RemoteUrlPropertyExtractor implements + ApplicationListener { + + private static final String NON_OPTION_ARGS = CommandLinePropertySource.DEFAULT_NON_OPTION_ARGS_PROPERTY_NAME; + + @Override + public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { + ConfigurableEnvironment environment = event.getEnvironment(); + String url = environment.getProperty(NON_OPTION_ARGS); + Assert.state(StringUtils.hasLength(url), "No remote URL specified"); + Assert.state(url.indexOf(",") == -1, "Multiple URLs specified"); + try { + new URI(url); + } + catch (URISyntaxException ex) { + throw new IllegalStateException("Malformed URL '" + url + "'"); + } + Map source = Collections.singletonMap("remoteUrl", (Object) url); + PropertySource propertySource = new MapPropertySource("remoteUrl", source); + environment.getPropertySources().addLast(propertySource); + } + +} diff --git a/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/autoconfigure/DeveloperToolsProperties.java b/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/autoconfigure/DeveloperToolsProperties.java index 6afbc3a1400..6e2dd83415a 100644 --- a/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/autoconfigure/DeveloperToolsProperties.java +++ b/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/autoconfigure/DeveloperToolsProperties.java @@ -33,6 +33,8 @@ public class DeveloperToolsProperties { private Livereload livereload = new Livereload(); + private RemoteDeveloperToolsProperties remote = new RemoteDeveloperToolsProperties(); + public Restart getRestart() { return this.restart; } @@ -41,6 +43,10 @@ public class DeveloperToolsProperties { return this.livereload; } + public RemoteDeveloperToolsProperties getRemote() { + return this.remote; + } + /** * Restart properties */ diff --git a/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/autoconfigure/RemoteDeveloperToolsAutoConfiguration.java b/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/autoconfigure/RemoteDeveloperToolsAutoConfiguration.java new file mode 100644 index 00000000000..1c157c58fbc --- /dev/null +++ b/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/autoconfigure/RemoteDeveloperToolsAutoConfiguration.java @@ -0,0 +1,70 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.developertools.autoconfigure; + +import java.util.Collection; + +import javax.servlet.Filter; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +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; +import org.springframework.boot.developertools.remote.server.AccessManager; +import org.springframework.boot.developertools.remote.server.Dispatcher; +import org.springframework.boot.developertools.remote.server.DispatcherFilter; +import org.springframework.boot.developertools.remote.server.Handler; +import org.springframework.boot.developertools.remote.server.HandlerMapper; +import org.springframework.boot.developertools.remote.server.HttpStatusHandler; +import org.springframework.boot.developertools.remote.server.UrlHandlerMapper; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.server.ServerHttpRequest; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for remote development support. + * + * @author Phillip Webb + * @author Rob Winch + * @since 1.3.0 + */ +@Configuration +@ConditionalOnProperty(prefix = "spring.developertools.remote", name = "enabled") +@ConditionalOnClass({ Filter.class, ServerHttpRequest.class }) +@EnableConfigurationProperties(DeveloperToolsProperties.class) +public class RemoteDeveloperToolsAutoConfiguration { + + @Autowired + private DeveloperToolsProperties properties; + + @Bean + public HandlerMapper remoteDeveloperToolsHealthCheckHandlerMapper() { + Handler handler = new HttpStatusHandler(); + return new UrlHandlerMapper(this.properties.getRemote().getContextPath(), handler); + } + + @Bean + @ConditionalOnMissingBean + public DispatcherFilter remoteDeveloperToolsDispatcherFilter( + Collection mappers) { + Dispatcher dispatcher = new Dispatcher(AccessManager.PERMIT_ALL, mappers); + return new DispatcherFilter(dispatcher); + } + +} diff --git a/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/autoconfigure/RemoteDeveloperToolsProperties.java b/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/autoconfigure/RemoteDeveloperToolsProperties.java new file mode 100644 index 00000000000..3a2eab4aafa --- /dev/null +++ b/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/autoconfigure/RemoteDeveloperToolsProperties.java @@ -0,0 +1,45 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.developertools.autoconfigure; + + +/** + * Configuration properties for remote Spring Boot applications. + * + * @author Phillip Webb + * @author Rob Winch + * @since 1.3.0 + * @see DeveloperToolsProperties + */ +public class RemoteDeveloperToolsProperties { + + public static final String DEFAULT_CONTEXT_PATH = "/.~~spring-boot!~"; + + /** + * Context path used to handle the remote connection. + */ + private String contextPath = DEFAULT_CONTEXT_PATH; + + public String getContextPath() { + return this.contextPath; + } + + public void setContextPath(String contextPath) { + this.contextPath = contextPath; + } + +} diff --git a/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/package-info.java b/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/package-info.java new file mode 100644 index 00000000000..9c0f4c95ff4 --- /dev/null +++ b/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/package-info.java @@ -0,0 +1,21 @@ +/* + * 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. + */ + +/** + * Spring Boot developer tools. + */ +package org.springframework.boot.developertools; + diff --git a/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/remote/client/RemoteClientConfiguration.java b/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/remote/client/RemoteClientConfiguration.java new file mode 100644 index 00000000000..ff8c90d71e0 --- /dev/null +++ b/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/remote/client/RemoteClientConfiguration.java @@ -0,0 +1,56 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.developertools.remote.client; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.developertools.autoconfigure.DeveloperToolsProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; + +/** + * Configuration used to connect to remote Spring Boot applications. + * + * @author Phillip Webb + * @since 1.3.0 + * @see org.springframework.boot.developertools.RemoteSpringApplication + */ +@Configuration +@EnableConfigurationProperties(DeveloperToolsProperties.class) +public class RemoteClientConfiguration { + + @Autowired + private DeveloperToolsProperties properties; + + @Value("${remoteUrl}") + private String remoteUrl; + + @Bean + public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { + return new PropertySourcesPlaceholderConfigurer(); + } + + @Bean + public ClientHttpRequestFactory clientHttpRequestFactory() { + return new SimpleClientHttpRequestFactory(); + } + +} diff --git a/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/remote/client/package-info.java b/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/remote/client/package-info.java new file mode 100644 index 00000000000..d602593b8c3 --- /dev/null +++ b/spring-boot-developer-tools/src/main/java/org/springframework/boot/developertools/remote/client/package-info.java @@ -0,0 +1,21 @@ +/* + * 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. + */ + +/** + * Client support for a remotely running Spring Boot application. + */ +package org.springframework.boot.developertools.remote.client; + diff --git a/spring-boot-developer-tools/src/main/resources/META-INF/spring.factories b/spring-boot-developer-tools/src/main/resources/META-INF/spring.factories index 88d7a6e0f16..69e53238a2f 100644 --- a/spring-boot-developer-tools/src/main/resources/META-INF/spring.factories +++ b/spring-boot-developer-tools/src/main/resources/META-INF/spring.factories @@ -8,4 +8,5 @@ org.springframework.boot.developertools.restart.RestartApplicationListener # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -org.springframework.boot.developertools.autoconfigure.LocalDeveloperToolsAutoConfiguration +org.springframework.boot.developertools.autoconfigure.LocalDeveloperToolsAutoConfiguration,\ +org.springframework.boot.developertools.autoconfigure.RemoteDeveloperToolsAutoConfiguration diff --git a/spring-boot-developer-tools/src/main/resources/org/springframework/boot/developertools/remote-banner.txt b/spring-boot-developer-tools/src/main/resources/org/springframework/boot/developertools/remote-banner.txt new file mode 100644 index 00000000000..687037696fb --- /dev/null +++ b/spring-boot-developer-tools/src/main/resources/org/springframework/boot/developertools/remote-banner.txt @@ -0,0 +1,7 @@ + . ____ _ __ _ _ + /\\ / ___'_ __ _ _(_)_ __ __ _ ___ _ \ \ \ \ +( ( )\___ | '_ | '_| | '_ \/ _` | | _ \___ _ __ ___| |_ ___ \ \ \ \ + \\/ ___)| |_)| | | | | || (_| []::::::[] / -_) ' \/ _ \ _/ -_) ) ) ) ) + ' |____| .__|_| |_|_| |_\__, | |_|_\___|_|_|_\___/\__\___|/ / / / + =========|_|==============|___/===================================/_/_/_/ + :: Spring Boot Remote :: ${spring-boot.formatted-version} diff --git a/spring-boot-developer-tools/src/test/java/org/springframework/boot/developertools/RemoteUrlPropertyExtractorTests.java b/spring-boot-developer-tools/src/test/java/org/springframework/boot/developertools/RemoteUrlPropertyExtractorTests.java new file mode 100644 index 00000000000..b572b0767fa --- /dev/null +++ b/spring-boot-developer-tools/src/test/java/org/springframework/boot/developertools/RemoteUrlPropertyExtractorTests.java @@ -0,0 +1,81 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.developertools; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.developertools.RemoteUrlPropertyExtractor; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Configuration; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; + +/** + * Tests for {@link RemoteUrlPropertyExtractor}. + * + * @author Phillip Webb + */ +public class RemoteUrlPropertyExtractorTests { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void missingUrl() throws Exception { + this.thrown.expect(IllegalStateException.class); + this.thrown.expectMessage("No remote URL specified"); + doTest(); + } + + @Test + public void malformedUrl() throws Exception { + this.thrown.expect(IllegalStateException.class); + this.thrown.expectMessage("Malformed URL '::://wibble'"); + doTest("::://wibble"); + + } + + @Test + public void multipleUrls() throws Exception { + this.thrown.expect(IllegalStateException.class); + this.thrown.expectMessage("Multiple URLs specified"); + doTest("http://localhost:8080", "http://localhost:9090"); + } + + @Test + public void validUrl() throws Exception { + ApplicationContext context = doTest("http://localhost:8080"); + assertThat(context.getEnvironment().getProperty("remoteUrl"), + equalTo("http://localhost:8080")); + } + + private ApplicationContext doTest(String... args) { + SpringApplication application = new SpringApplication(Config.class); + application.setWebEnvironment(false); + application.addListeners(new RemoteUrlPropertyExtractor()); + return application.run(args); + } + + @Configuration + static class Config { + + } + +} diff --git a/spring-boot-developer-tools/src/test/java/org/springframework/boot/developertools/autoconfigure/RemoteDeveloperToolsAutoConfigurationTests.java b/spring-boot-developer-tools/src/test/java/org/springframework/boot/developertools/autoconfigure/RemoteDeveloperToolsAutoConfigurationTests.java new file mode 100644 index 00000000000..f626f131fd2 --- /dev/null +++ b/spring-boot-developer-tools/src/test/java/org/springframework/boot/developertools/autoconfigure/RemoteDeveloperToolsAutoConfigurationTests.java @@ -0,0 +1,102 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.developertools.autoconfigure; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration; +import org.springframework.boot.developertools.remote.server.DispatcherFilter; +import org.springframework.boot.developertools.restart.MockRestarter; +import org.springframework.boot.test.EnvironmentTestUtils; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.mock.web.MockFilterChain; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.mock.web.MockServletContext; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; + +/** + * Tests for {@link RemoteDeveloperToolsAutoConfiguration}. + * + * @author Rob Winch + * @author Phillip Webb + */ +public class RemoteDeveloperToolsAutoConfigurationTests { + + private static final String DEFAULT_CONTEXT_PATH = RemoteDeveloperToolsProperties.DEFAULT_CONTEXT_PATH; + + @Rule + public MockRestarter mockRestarter = new MockRestarter(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private AnnotationConfigWebApplicationContext context; + + private MockHttpServletRequest request; + + private MockHttpServletResponse response; + + private MockFilterChain chain; + + @Before + public void setup() { + this.request = new MockHttpServletRequest(); + this.response = new MockHttpServletResponse(); + this.chain = new MockFilterChain(); + } + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void developerToolsHealthReturns200() throws Exception { + loadContext("spring.developertools.remote.enabled:true"); + DispatcherFilter filter = this.context.getBean(DispatcherFilter.class); + this.request.setRequestURI(DEFAULT_CONTEXT_PATH); + this.response.setStatus(500); + filter.doFilter(this.request, this.response, this.chain); + assertThat(this.response.getStatus(), equalTo(200)); + } + + private void loadContext(String... properties) { + this.context = new AnnotationConfigWebApplicationContext(); + this.context.setServletContext(new MockServletContext()); + this.context.register(Config.class, ServerPropertiesAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + EnvironmentTestUtils.addEnvironment(this.context, properties); + this.context.refresh(); + } + + @Configuration + @Import(RemoteDeveloperToolsAutoConfiguration.class) + static class Config { + + } +} diff --git a/spring-boot-developer-tools/src/test/java/org/springframework/boot/developertools/remote/client/RemoteClientConfigurationTests.java b/spring-boot-developer-tools/src/test/java/org/springframework/boot/developertools/remote/client/RemoteClientConfigurationTests.java new file mode 100644 index 00000000000..ecd6a4362fa --- /dev/null +++ b/spring-boot-developer-tools/src/test/java/org/springframework/boot/developertools/remote/client/RemoteClientConfigurationTests.java @@ -0,0 +1,126 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.developertools.remote.client; + +import java.io.IOException; + +import org.junit.After; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext; +import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; +import org.springframework.boot.developertools.classpath.ClassPathFileSystemWatcher; +import org.springframework.boot.developertools.remote.server.Dispatcher; +import org.springframework.boot.developertools.remote.server.DispatcherFilter; +import org.springframework.boot.developertools.restart.MockRestarter; +import org.springframework.boot.developertools.restart.RestartScopeInitializer; +import org.springframework.boot.test.EnvironmentTestUtils; +import org.springframework.boot.test.OutputCapture; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.util.SocketUtils; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link RemoteClientConfiguration}. + * + * @author Phillip Webb + */ +public class RemoteClientConfigurationTests { + + @Rule + public MockRestarter restarter = new MockRestarter(); + + @Rule + public OutputCapture output = new OutputCapture(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private AnnotationConfigEmbeddedWebApplicationContext context; + + private static int remotePort = SocketUtils.findAvailableTcpPort(); + + @After + public void cleanup() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void doesntWarnIfUsingHttps() throws Exception { + configureWithRemoteUrl("https://localhost"); + assertThat(this.output.toString(), not(containsString("is insecure"))); + } + + @Test + public void remoteRestartDisabled() throws Exception { + configure("spring.developertools.remote.restart.enabled:false"); + this.thrown.expect(NoSuchBeanDefinitionException.class); + this.context.getBean(ClassPathFileSystemWatcher.class); + } + + private void configure(String... pairs) { + configureWithRemoteUrl("http://localhost", pairs); + } + + private void configureWithRemoteUrl(String remoteUrl, String... pairs) { + this.context = new AnnotationConfigEmbeddedWebApplicationContext(); + new RestartScopeInitializer().initialize(this.context); + this.context.register(Config.class, RemoteClientConfiguration.class); + String remoteUrlProperty = "remoteUrl:" + remoteUrl + ":" + + RemoteClientConfigurationTests.remotePort; + EnvironmentTestUtils.addEnvironment(this.context, remoteUrlProperty); + EnvironmentTestUtils.addEnvironment(this.context, pairs); + this.context.refresh(); + } + + @Configuration + static class Config { + + @Bean + public TomcatEmbeddedServletContainerFactory tomcat() { + return new TomcatEmbeddedServletContainerFactory(remotePort); + } + + @Bean + public DispatcherFilter dispatcherFilter() throws IOException { + return new DispatcherFilter(dispatcher()); + } + + public Dispatcher dispatcher() throws IOException { + Dispatcher dispatcher = mock(Dispatcher.class); + ServerHttpRequest anyRequest = (ServerHttpRequest) any(); + ServerHttpResponse anyResponse = (ServerHttpResponse) any(); + given(dispatcher.handle(anyRequest, anyResponse)).willReturn(true); + return dispatcher; + } + + } + +}