diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mobile/DeviceDelegatingViewResolverAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mobile/DeviceDelegatingViewResolverAutoConfiguration.java index 9e88580395b..a5bf1bdf9c1 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mobile/DeviceDelegatingViewResolverAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mobile/DeviceDelegatingViewResolverAutoConfiguration.java @@ -28,7 +28,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat import org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration; import org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration; import org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration; -import org.springframework.boot.autoconfigure.mustache.web.MustacheViewResolver; +import org.springframework.boot.autoconfigure.mustache.servlet.MustacheViewResolver; import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration; import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/MustacheAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/MustacheAutoConfiguration.java index 51a2547bf6f..e84c8f4c276 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/MustacheAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/MustacheAutoConfiguration.java @@ -30,7 +30,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; -import org.springframework.boot.autoconfigure.mustache.web.MustacheViewResolver; +import org.springframework.boot.autoconfigure.mustache.servlet.MustacheViewResolver; import org.springframework.boot.autoconfigure.template.TemplateLocation; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.ApplicationContext; @@ -43,6 +43,7 @@ import org.springframework.core.env.Environment; * {@link EnableAutoConfiguration Auto-configuration} for Mustache. * * @author Dave Syer + * @author Brian Clozel * @since 1.2.2 */ @Configuration @@ -123,4 +124,34 @@ public class MustacheAutoConfiguration { } + @Configuration + @ConditionalOnWebApplication(type = Type.REACTIVE) + protected static class MustacheReactiveWebConfiguration { + + private final MustacheProperties mustache; + + protected MustacheReactiveWebConfiguration(MustacheProperties mustache) { + this.mustache = mustache; + } + + @Bean + @ConditionalOnMissingBean(org.springframework.boot.autoconfigure + .mustache.reactive.MustacheViewResolver.class) + public org.springframework.boot.autoconfigure + .mustache.reactive.MustacheViewResolver mustacheViewResolver(Compiler mustacheCompiler) { + org.springframework.boot.autoconfigure + .mustache.reactive.MustacheViewResolver resolver + = new org.springframework.boot.autoconfigure + .mustache.reactive.MustacheViewResolver(mustacheCompiler); + resolver.setPrefix(this.mustache.getPrefix()); + resolver.setSuffix(this.mustache.getSuffix()); + resolver.setViewNames(this.mustache.getViewNames()); + resolver.setRequestContextAttribute(this.mustache.getRequestContextAttribute()); + resolver.setCharset(this.mustache.getCharsetName()); + resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10); + return resolver; + } + + } + } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/reactive/MustacheView.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/reactive/MustacheView.java new file mode 100644 index 00000000000..d40f7fb5ddc --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/reactive/MustacheView.java @@ -0,0 +1,118 @@ +/* + * 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.autoconfigure.mustache.reactive; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.nio.charset.Charset; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; + +import com.samskivert.mustache.Mustache; +import com.samskivert.mustache.Template; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import org.springframework.core.io.Resource; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.result.view.AbstractUrlBasedView; +import org.springframework.web.reactive.result.view.View; +import org.springframework.web.server.ServerWebExchange; + +/** + * Spring WebFlux {@link View} using the Mustache template engine. + * + * @author Brian Clozel + * @since 2.0.0 + */ +public class MustacheView extends AbstractUrlBasedView { + + private Mustache.Compiler compiler; + + private String charset; + + /** + * Set the JMustache compiler to be used by this view. + *

Typically this property is not set directly. Instead a single + * {@link Mustache.Compiler} is expected in the Spring application context + * which is used to compile Mustache templates. + * @param compiler the Mustache compiler + */ + public void setCompiler(Mustache.Compiler compiler) { + this.compiler = compiler; + } + + /** + * Set the charset used for reading Mustache template files. + * @param charset the charset to use for reading template files + */ + public void setCharset(String charset) { + this.charset = charset; + } + + @Override + public boolean checkResourceExists(Locale locale) throws Exception { + return resolveResource() != null; + } + + private Resource resolveResource() { + Resource resource = getApplicationContext().getResource(getUrl()); + if (resource == null || !resource.exists()) { + return null; + } + return resource; + } + + @Override + protected Mono renderInternal(Map model, + MediaType contentType, ServerWebExchange exchange) { + Resource resource = resolveResource(); + if (resource == null) { + return Mono.error(new IllegalStateException("Could not find Mustache template with URL [" + + getUrl() + "]")); + } + DataBuffer dataBuffer = exchange.getResponse().bufferFactory().allocateBuffer(); + try (Reader reader = getReader(resource)) { + Template template = this.compiler.compile(reader); + Charset charset = getCharset(contentType).orElse(getDefaultCharset()); + try (Writer writer = new OutputStreamWriter(dataBuffer.asOutputStream(), charset)) { + template.execute(model, writer); + writer.flush(); + } + } + catch (Throwable exc) { + return Mono.error(exc); + } + return exchange.getResponse().writeWith(Flux.just(dataBuffer)); + } + + private Reader getReader(Resource resource) throws IOException { + if (this.charset != null) { + return new InputStreamReader(resource.getInputStream(), this.charset); + } + return new InputStreamReader(resource.getInputStream()); + } + + private Optional getCharset(MediaType mediaType) { + return (mediaType != null ? Optional.ofNullable(mediaType.getCharset()) : Optional.empty()); + } +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/reactive/MustacheViewResolver.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/reactive/MustacheViewResolver.java new file mode 100644 index 00000000000..2a60f8881b5 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/reactive/MustacheViewResolver.java @@ -0,0 +1,77 @@ +/* + * 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.autoconfigure.mustache.reactive; + +import com.samskivert.mustache.Mustache; + +import org.springframework.web.reactive.result.view.AbstractUrlBasedView; +import org.springframework.web.reactive.result.view.UrlBasedViewResolver; +import org.springframework.web.reactive.result.view.ViewResolver; + +/** + * Spring WebFlux {@link ViewResolver} for Mustache. + * + * @author Brian Clozel + * @since 2.0.0 + */ +public class MustacheViewResolver extends UrlBasedViewResolver { + + private final Mustache.Compiler compiler; + + private String charset; + + /** + * Create a {@code MustacheViewResolver} backed by a default + * instance of a {@link Mustache.Compiler}. + */ + public MustacheViewResolver() { + this.compiler = Mustache.compiler(); + setViewClass(requiredViewClass()); + } + + /** + * Create a {@code MustacheViewResolver} backed by a custom + * instance of a {@link Mustache.Compiler}. + * @param compiler the Mustache compiler used to compile templates + */ + public MustacheViewResolver(Mustache.Compiler compiler) { + this.compiler = compiler; + setViewClass(requiredViewClass()); + } + + /** + * Set the charset. + * @param charset the charset + */ + public void setCharset(String charset) { + this.charset = charset; + } + + @Override + protected Class requiredViewClass() { + return MustacheView.class; + } + + @Override + protected AbstractUrlBasedView createUrlBasedView(String viewName) { + MustacheView view = (MustacheView) super.createUrlBasedView(viewName); + view.setCompiler(this.compiler); + view.setCharset(this.charset); + return view; + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/reactive/package-info.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/reactive/package-info.java new file mode 100644 index 00000000000..e89db28df66 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/reactive/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** + * Auto-configuration for Mustache with Spring WebFlux. + */ +package org.springframework.boot.autoconfigure.mustache.reactive; diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/web/MustacheView.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/servlet/MustacheView.java similarity index 93% rename from spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/web/MustacheView.java rename to spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/servlet/MustacheView.java index c368941e6a8..f877b2bbb62 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/web/MustacheView.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/servlet/MustacheView.java @@ -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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.mustache.web; +package org.springframework.boot.autoconfigure.mustache.servlet; import java.util.Map; diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/web/MustacheViewResolver.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/servlet/MustacheViewResolver.java similarity index 96% rename from spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/web/MustacheViewResolver.java rename to spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/servlet/MustacheViewResolver.java index 6cc66e9b124..d513c655547 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/web/MustacheViewResolver.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/servlet/MustacheViewResolver.java @@ -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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.mustache.web; +package org.springframework.boot.autoconfigure.mustache.servlet; import java.io.IOException; import java.io.InputStreamReader; diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/web/package-info.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/servlet/package-info.java similarity index 83% rename from spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/web/package-info.java rename to spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/servlet/package-info.java index dafe21fc771..3d137e0afd2 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/web/package-info.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/servlet/package-info.java @@ -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. @@ -17,4 +17,4 @@ /** * Auto-configuration for Mustache with Spring MVC. */ -package org.springframework.boot.autoconfigure.mustache.web; +package org.springframework.boot.autoconfigure.mustache.servlet; diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mobile/DeviceDelegatingViewResolverAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mobile/DeviceDelegatingViewResolverAutoConfigurationTests.java index fe8c45ada1f..e6e4cb971e6 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mobile/DeviceDelegatingViewResolverAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mobile/DeviceDelegatingViewResolverAutoConfigurationTests.java @@ -34,7 +34,7 @@ import org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfigura import org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration; import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration; -import org.springframework.boot.autoconfigure.mustache.web.MustacheViewResolver; +import org.springframework.boot.autoconfigure.mustache.servlet.MustacheViewResolver; import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration; import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; import org.springframework.boot.test.util.EnvironmentTestUtils; diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/MustacheAutoConfigurationIntegrationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/MustacheAutoConfigurationIntegrationTests.java deleted file mode 100644 index 92531a58cf2..00000000000 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/MustacheAutoConfigurationIntegrationTests.java +++ /dev/null @@ -1,122 +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.autoconfigure.mustache; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.Date; -import java.util.Map; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; -import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration; -import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.stereotype.Controller; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.web.bind.annotation.RequestMapping; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Integration tests for {@link MustacheAutoConfiguration}. - * - * @author Dave Syer - */ -@RunWith(SpringRunner.class) -@DirtiesContext -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { - "spring.mustache.prefix:classpath:/mustache-templates/" }) -public class MustacheAutoConfigurationIntegrationTests { - - @Autowired - private ServletWebServerApplicationContext context; - - private int port; - - @Before - public void init() { - this.port = this.context.getWebServer().getPort(); - } - - @Test - public void testHomePage() throws Exception { - String body = new TestRestTemplate().getForObject("http://localhost:" + this.port, - String.class); - assertThat(body.contains("Hello World")).isTrue(); - } - - @Test - public void testPartialPage() throws Exception { - String body = new TestRestTemplate() - .getForObject("http://localhost:" + this.port + "/partial", String.class); - assertThat(body.contains("Hello World")).isTrue(); - } - - @Configuration - @MinimalWebConfiguration - @Controller - public static class Application { - - @RequestMapping("/") - public String home(Map model) { - model.put("time", new Date()); - model.put("message", "Hello World"); - model.put("title", "Hello App"); - return "home"; - } - - @RequestMapping("/partial") - public String layout(Map model) { - model.put("time", new Date()); - model.put("message", "Hello World"); - model.put("title", "Hello App"); - return "partial"; - } - - public static void main(String[] args) { - SpringApplication.run(Application.class, args); - } - - } - - @Target(ElementType.TYPE) - @Retention(RetentionPolicy.RUNTIME) - @Documented - @Import({ MustacheAutoConfiguration.class, - ServletWebServerFactoryAutoConfiguration.class, - DispatcherServletAutoConfiguration.class, - PropertyPlaceholderAutoConfiguration.class }) - protected @interface MinimalWebConfiguration { - - } - -} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/MustacheAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/MustacheAutoConfigurationTests.java new file mode 100644 index 00000000000..1e71ed027fd --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/MustacheAutoConfigurationTests.java @@ -0,0 +1,121 @@ +/* + * 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.autoconfigure.mustache; + +import com.samskivert.mustache.Mustache; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.mustache.servlet.MustacheViewResolver; +import org.springframework.boot.test.util.EnvironmentTestUtils; +import org.springframework.boot.web.reactive.context.GenericReactiveWebApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link MustacheAutoConfiguration}. + * + * @author Brian Clozel + */ +public class MustacheAutoConfigurationTests { + + private AnnotationConfigWebApplicationContext webContext; + + private GenericReactiveWebApplicationContext reactiveWebContext; + + @Test + public void registerBeansForServletApp() { + loadWithServlet(null); + assertThat(this.webContext.getBeansOfType(Mustache.Compiler.class)).hasSize(1); + assertThat(this.webContext.getBeansOfType(MustacheResourceTemplateLoader.class)).hasSize(1); + assertThat(this.webContext.getBeansOfType(MustacheViewResolver.class)).hasSize(1); + } + + @Test + public void registerCompilerForServletApp() { + loadWithServlet(CustomCompilerConfiguration.class); + assertThat(this.webContext.getBeansOfType(MustacheResourceTemplateLoader.class)).hasSize(1); + assertThat(this.webContext.getBeansOfType(MustacheViewResolver.class)).hasSize(1); + assertThat(this.webContext.getBeansOfType(Mustache.Compiler.class)).hasSize(1); + assertThat(this.webContext.getBean(Mustache.Compiler.class).standardsMode).isTrue(); + } + + @Test + public void registerBeansForReactiveApp() { + loadWithReactive(null); + assertThat(this.reactiveWebContext.getBeansOfType(Mustache.Compiler.class)).hasSize(1); + assertThat(this.reactiveWebContext.getBeansOfType(MustacheResourceTemplateLoader.class)).hasSize(1); + assertThat(this.reactiveWebContext.getBeansOfType(MustacheViewResolver.class)).isEmpty(); + assertThat(this.reactiveWebContext + .getBeansOfType(org.springframework.boot.autoconfigure.mustache.reactive.MustacheViewResolver.class) + ).hasSize(1); + } + + + @Test + public void registerCompilerForReactiveApp() { + loadWithReactive(CustomCompilerConfiguration.class); + assertThat(this.reactiveWebContext.getBeansOfType(Mustache.Compiler.class)).hasSize(1); + assertThat(this.reactiveWebContext.getBeansOfType(MustacheResourceTemplateLoader.class)).hasSize(1); + assertThat(this.reactiveWebContext.getBeansOfType(MustacheViewResolver.class)).isEmpty(); + assertThat(this.reactiveWebContext + .getBeansOfType(org.springframework.boot.autoconfigure.mustache.reactive.MustacheViewResolver.class) + ).hasSize(1); + assertThat(this.reactiveWebContext.getBean(Mustache.Compiler.class).standardsMode).isTrue(); + } + + private void loadWithServlet(Class config) { + this.webContext = new AnnotationConfigWebApplicationContext(); + EnvironmentTestUtils.addEnvironment(this.webContext, + "spring.mustache.prefix=classpath:/mustache-templates/"); + if (config != null) { + this.webContext.register(config); + } + this.webContext.register(BaseConfiguration.class); + this.webContext.refresh(); + } + + private void loadWithReactive(Class config) { + this.reactiveWebContext = new GenericReactiveWebApplicationContext(); + EnvironmentTestUtils.addEnvironment(this.reactiveWebContext, + "spring.mustache.prefix=classpath:/mustache-templates/"); + if (config != null) { + this.reactiveWebContext.register(config); + } + this.reactiveWebContext.register(BaseConfiguration.class); + this.reactiveWebContext.refresh(); + } + + @Configuration + @Import({MustacheAutoConfiguration.class}) + protected static class BaseConfiguration { + + } + + @Configuration + protected static class CustomCompilerConfiguration { + + @Bean + public Mustache.Compiler compiler(Mustache.TemplateLoader mustacheTemplateLoader) { + return Mustache.compiler().standardsMode(true).withLoader(mustacheTemplateLoader); + } + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/reactive/MustacheViewResolverTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/reactive/MustacheViewResolverTests.java new file mode 100644 index 00000000000..3f84133b1b6 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/reactive/MustacheViewResolverTests.java @@ -0,0 +1,54 @@ +/* + * 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.autoconfigure.mustache.reactive; + +import org.junit.Before; +import org.junit.Test; + +import org.springframework.context.support.GenericApplicationContext; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link org.springframework.boot.autoconfigure.mustache.reactive.MustacheViewResolver}. + * + * @author Brian Clozel + */ +public class MustacheViewResolverTests { + + private MustacheViewResolver resolver = new MustacheViewResolver(); + + @Before + public void init() { + GenericApplicationContext applicationContext = new GenericApplicationContext(); + applicationContext.refresh(); + this.resolver.setApplicationContext(applicationContext); + this.resolver.setPrefix("classpath:/mustache-templates/"); + this.resolver.setSuffix(".html"); + } + + @Test + public void resolveNonExistent() throws Exception { + assertThat(this.resolver.resolveViewName("bar", null).block()).isNull(); + } + + @Test + public void resolveExisting() throws Exception { + assertThat(this.resolver.resolveViewName("foo", null).block()).isNotNull(); + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/reactive/MustacheViewTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/reactive/MustacheViewTests.java new file mode 100644 index 00000000000..ebb05c81534 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/reactive/MustacheViewTests.java @@ -0,0 +1,61 @@ +/* + * 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.autoconfigure.mustache.reactive; + +import java.nio.charset.StandardCharsets; +import java.util.Collections; + +import com.samskivert.mustache.Mustache; +import org.junit.Before; +import org.junit.Test; + +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.http.MediaType; +import org.springframework.mock.http.server.reactive.MockServerHttpRequest; +import org.springframework.mock.http.server.reactive.MockServerWebExchange; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link MustacheView}. + * + * @author Brian Clozel + */ +public class MustacheViewTests { + + private GenericApplicationContext context = new GenericApplicationContext(); + + private MockServerWebExchange exchange; + + @Before + public void init() { + this.context.refresh(); + } + + @Test + public void viewResolvesHandlebars() throws Exception { + this.exchange = MockServerHttpRequest.get("/test").toExchange(); + MustacheView view = new MustacheView(); + view.setCompiler(Mustache.compiler()); + view.setUrl("classpath:/mustache-templates/foo.html"); + view.setCharset(StandardCharsets.UTF_8.displayName()); + view.setApplicationContext(this.context); + view.render(Collections.singletonMap("World", "Spring"), MediaType.TEXT_HTML, this.exchange).block(); + assertThat(this.exchange.getResponse().getBodyAsString().block()).isEqualTo("Hello Spring"); + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/reactive/MustacheWebIntegrationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/reactive/MustacheWebIntegrationTests.java new file mode 100644 index 00000000000..34f2bddec72 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/reactive/MustacheWebIntegrationTests.java @@ -0,0 +1,119 @@ +/* + * 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.autoconfigure.mustache.reactive; + +import java.util.Date; + +import com.samskivert.mustache.Mustache; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration; +import org.springframework.boot.autoconfigure.mustache.MustacheResourceTemplateLoader; +import org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration; +import org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerAutoConfiguration; +import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.stereotype.Controller; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration Tests for {@link MustacheAutoConfiguration}, {@link MustacheViewResolver} + * and {@link MustacheView}. + * + * @author Brian Clozel + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, + properties = "spring.main.web-application-type=reactive") +public class MustacheWebIntegrationTests { + + @Autowired + private WebTestClient client; + + @Test + public void testHomePage() throws Exception { + String result = (String) this.client.get().uri("/").exchange() + .expectStatus().isOk() + .expectBody(String.class).returnResult().getResponseBody(); + assertThat(result).contains("Hello App").contains("Hello World"); + } + + @Test + public void testPartialPage() throws Exception { + String result = (String) this.client.get().uri("/partial").exchange() + .expectStatus().isOk() + .expectBody(String.class).returnResult().getResponseBody(); + assertThat(result).contains("Hello App").contains("Hello World"); + } + + @Configuration + @Import({ReactiveWebServerAutoConfiguration.class, + WebFluxAutoConfiguration.class, + HttpHandlerAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class}) + @Controller + public static class Application { + + @RequestMapping("/") + public String home(Model model) { + model.addAttribute("time", new Date()); + model.addAttribute("message", "Hello World"); + model.addAttribute("title", "Hello App"); + return "home"; + } + + @RequestMapping("/partial") + public String layout(Model model) { + model.addAttribute("time", new Date()); + model.addAttribute("message", "Hello World"); + model.addAttribute("title", "Hello App"); + return "partial"; + } + + @Bean + public MustacheViewResolver viewResolver() { + Mustache.Compiler compiler = Mustache.compiler().withLoader( + new MustacheResourceTemplateLoader("classpath:/mustache-templates/", ".html")); + MustacheViewResolver resolver = new MustacheViewResolver(compiler); + resolver.setPrefix("classpath:/mustache-templates/"); + resolver.setSuffix(".html"); + return resolver; + } + + public static void main(String[] args) { + SpringApplication application = new SpringApplication(Application.class); + application.setWebApplicationType(WebApplicationType.REACTIVE); + application.run(args); + } + + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/web/MustacheViewResolverTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/servlet/MustacheViewResolverTests.java similarity index 98% rename from spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/web/MustacheViewResolverTests.java rename to spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/servlet/MustacheViewResolverTests.java index dff2651272f..b15931873f7 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/web/MustacheViewResolverTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/servlet/MustacheViewResolverTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.mustache.web; +package org.springframework.boot.autoconfigure.mustache.servlet; import java.io.ByteArrayInputStream; import java.io.InputStream; diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/web/MustacheViewTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/servlet/MustacheViewTests.java similarity index 97% rename from spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/web/MustacheViewTests.java rename to spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/servlet/MustacheViewTests.java index 02db8318cff..c1e1d7f4b57 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/web/MustacheViewTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/servlet/MustacheViewTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.mustache.web; +package org.springframework.boot.autoconfigure.mustache.servlet; import java.util.Collections; diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/web/MustacheWebIntegrationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/servlet/MustacheWebIntegrationTests.java similarity index 98% rename from spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/web/MustacheWebIntegrationTests.java rename to spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/servlet/MustacheWebIntegrationTests.java index d9912f1cb90..cf8a5d28b95 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/web/MustacheWebIntegrationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/servlet/MustacheWebIntegrationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.mustache.web; +package org.springframework.boot.autoconfigure.mustache.servlet; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; diff --git a/spring-boot-samples/spring-boot-sample-web-mustache/pom.xml b/spring-boot-samples/spring-boot-sample-web-mustache/pom.xml index 3a3c37618f8..1fff694c84a 100644 --- a/spring-boot-samples/spring-boot-sample-web-mustache/pom.xml +++ b/spring-boot-samples/spring-boot-sample-web-mustache/pom.xml @@ -21,6 +21,10 @@ + + org.springframework.boot + spring-boot-starter-web + org.springframework.boot spring-boot-starter-mustache diff --git a/spring-boot-starters/spring-boot-starter-mustache/pom.xml b/spring-boot-starters/spring-boot-starter-mustache/pom.xml index 387965dc16c..04e46985447 100644 --- a/spring-boot-starters/spring-boot-starter-mustache/pom.xml +++ b/spring-boot-starters/spring-boot-starter-mustache/pom.xml @@ -8,7 +8,7 @@ spring-boot-starter-mustache Spring Boot Mustache Starter - Starter for building MVC web applications using Mustache views + Starter for building web applications using Mustache views http://projects.spring.io/spring-boot/ Pivotal Software, Inc. @@ -22,10 +22,6 @@ org.springframework.boot spring-boot-starter - - org.springframework.boot - spring-boot-starter-web - com.samskivert jmustache