diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/velocity/VelocityAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/velocity/VelocityAutoConfiguration.java index cf2b628995c..7c08177f841 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/velocity/VelocityAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/velocity/VelocityAutoConfiguration.java @@ -35,6 +35,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat import org.springframework.boot.autoconfigure.template.TemplateLocation; import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.web.servlet.view.velocity.EmbeddedVelocityViewResolver; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -128,7 +129,7 @@ public class VelocityAutoConfiguration { @ConditionalOnMissingBean(name = "velocityViewResolver") @ConditionalOnProperty(name = "spring.velocity.enabled", matchIfMissing = true) public VelocityViewResolver velocityViewResolver() { - VelocityViewResolver resolver = new VelocityViewResolver(); + EmbeddedVelocityViewResolver resolver = new EmbeddedVelocityViewResolver(); this.properties.applyToViewResolver(resolver); return resolver; } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/velocity/VelocityAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/velocity/VelocityAutoConfigurationTests.java index c4682beb05d..2779101f86e 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/velocity/VelocityAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/velocity/VelocityAutoConfigurationTests.java @@ -30,6 +30,7 @@ import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.BeanCreationException; import org.springframework.boot.test.EnvironmentTestUtils; +import org.springframework.boot.web.servlet.view.velocity.EmbeddedVelocityViewResolver; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; @@ -42,6 +43,7 @@ import org.springframework.web.servlet.view.velocity.VelocityViewResolver; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.assertThat; @@ -136,7 +138,7 @@ public class VelocityAutoConfigurationTests { } @Test - public void customFreeMarkerSettings() { + public void customVelocitySettings() { registerAndRefreshContext("spring.velocity.properties.directive.parse.max.depth:10"); assertThat(this.context.getBean(VelocityConfigurer.class).getVelocityEngine() .getProperty("directive.parse.max.depth"), equalTo((Object) "10")); @@ -174,6 +176,13 @@ public class VelocityAutoConfigurationTests { } } + @Test + public void usesEmbeddedVelocityViewResolver() { + registerAndRefreshContext("spring.velocity.toolbox:/toolbox.xml"); + VelocityViewResolver resolver = this.context.getBean(VelocityViewResolver.class); + assertThat(resolver, instanceOf(EmbeddedVelocityViewResolver.class)); + } + private void registerAndRefreshContext(String... env) { EnvironmentTestUtils.addEnvironment(this.context, env); this.context.register(VelocityAutoConfiguration.class); diff --git a/spring-boot/pom.xml b/spring-boot/pom.xml index 5b3c9fc1b33..caa9c3963cf 100644 --- a/spring-boot/pom.xml +++ b/spring-boot/pom.xml @@ -114,6 +114,16 @@ tomcat-embed-logging-juli true + + org.apache.velocity + velocity + true + + + org.apache.velocity + velocity-tools + true + org.codehaus.btm btm @@ -184,6 +194,11 @@ spring-web true + + org.springframework + spring-webmvc + true + org.yaml snakeyaml @@ -202,13 +217,13 @@ test - org.springframework - spring-webmvc + org.slf4j + jcl-over-slf4j test - org.slf4j - jcl-over-slf4j + org.springframework + spring-context-support test diff --git a/spring-boot/src/main/java/org/springframework/boot/web/servlet/view/velocity/EmbeddedVelocityToolboxView.java b/spring-boot/src/main/java/org/springframework/boot/web/servlet/view/velocity/EmbeddedVelocityToolboxView.java new file mode 100644 index 00000000000..a32ac85cae4 --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/web/servlet/view/velocity/EmbeddedVelocityToolboxView.java @@ -0,0 +1,111 @@ +/* + * 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.web.servlet.view.velocity; + +import java.io.InputStream; +import java.util.Map; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.context.Context; +import org.springframework.aop.framework.ProxyFactory; +import org.springframework.core.io.ClassPathResource; +import org.springframework.web.servlet.view.velocity.VelocityToolboxView; + +/** + * Extended version of {@link VelocityToolboxView} that can load toolbox locations from + * the classpath as well as the servlet context. This is useful when run an embedded web + * server. + * + * @author Phillip Webb + * @since 1.2.5 + */ +@SuppressWarnings("deprecation") +public class EmbeddedVelocityToolboxView extends VelocityToolboxView { + + @Override + protected Context createVelocityContext(Map model, + HttpServletRequest request, HttpServletResponse response) throws Exception { + org.apache.velocity.tools.view.context.ChainedContext context = new org.apache.velocity.tools.view.context.ChainedContext( + new VelocityContext(model), getVelocityEngine(), request, response, + getServletContext()); + if (getToolboxConfigLocation() != null) { + setContextToolbox(context); + } + return context; + } + + @SuppressWarnings("unchecked") + private void setContextToolbox( + org.apache.velocity.tools.view.context.ChainedContext context) { + org.apache.velocity.tools.view.ToolboxManager toolboxManager = org.apache.velocity.tools.view.servlet.ServletToolboxManager + .getInstance(getToolboxConfigFileAwareServletContext(), + getToolboxConfigLocation()); + Map toolboxContext = toolboxManager.getToolbox(context); + context.setToolbox(toolboxContext); + } + + private ServletContext getToolboxConfigFileAwareServletContext() { + ProxyFactory factory = new ProxyFactory(); + factory.setTarget(getServletContext()); + factory.addAdvice(new GetResourceMethodInterceptor(getToolboxConfigLocation())); + return (ServletContext) factory.getProxy(); + } + + /** + * {@link MethodInterceptor} to allow the calls to getResourceAsStream() to resolve + * the toolboxFile from the classpath. + */ + private static class GetResourceMethodInterceptor implements MethodInterceptor { + + private final String toolboxFile; + + public GetResourceMethodInterceptor(String toolboxFile) { + if (toolboxFile != null && !toolboxFile.startsWith("/")) { + toolboxFile = "/" + toolboxFile; + } + this.toolboxFile = toolboxFile; + } + + @Override + public Object invoke(MethodInvocation invocation) throws Throwable { + if (invocation.getMethod().getName().equals("getResourceAsStream") + && invocation.getArguments()[0].equals(this.toolboxFile)) { + InputStream inputStream = (InputStream) invocation.proceed(); + if (inputStream == null) { + try { + inputStream = new ClassPathResource(this.toolboxFile, Thread + .currentThread().getContextClassLoader()) + .getInputStream(); + } + catch (Exception ex) { + // Ignore + } + } + return inputStream; + } + return invocation.proceed(); + } + + } + +} diff --git a/spring-boot/src/main/java/org/springframework/boot/web/servlet/view/velocity/EmbeddedVelocityViewResolver.java b/spring-boot/src/main/java/org/springframework/boot/web/servlet/view/velocity/EmbeddedVelocityViewResolver.java new file mode 100644 index 00000000000..0d7132a9725 --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/web/servlet/view/velocity/EmbeddedVelocityViewResolver.java @@ -0,0 +1,52 @@ +/* + * 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.web.servlet.view.velocity; + +import org.springframework.web.servlet.view.velocity.VelocityView; +import org.springframework.web.servlet.view.velocity.VelocityViewResolver; + +/** + * Extended version of {@link VelocityViewResolver} that uses + * {@link EmbeddedVelocityToolboxView} when the {@link #setToolboxConfigLocation(String) + * toolboxConfigLocation} is set. + * + * @author Phillip Webb + * @since 1.2.5 + */ +public class EmbeddedVelocityViewResolver extends VelocityViewResolver { + + private String toolboxConfigLocation; + + @Override + protected void initApplicationContext() { + if (this.toolboxConfigLocation != null) { + if (VelocityView.class.equals(getViewClass())) { + this.logger.info("Using EmbeddedVelocityToolboxView instead of " + + "default VelocityView due to specified toolboxConfigLocation"); + setViewClass(EmbeddedVelocityToolboxView.class); + } + } + super.initApplicationContext(); + } + + @Override + public void setToolboxConfigLocation(String toolboxConfigLocation) { + super.setToolboxConfigLocation(toolboxConfigLocation); + this.toolboxConfigLocation = toolboxConfigLocation; + } + +} diff --git a/spring-boot/src/main/java/org/springframework/boot/web/servlet/view/velocity/package-info.java b/spring-boot/src/main/java/org/springframework/boot/web/servlet/view/velocity/package-info.java new file mode 100644 index 00000000000..b1722c40ed0 --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/web/servlet/view/velocity/package-info.java @@ -0,0 +1,19 @@ +/* + * 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. + */ +/** + * @author pwebb + */ +package org.springframework.boot.web.servlet.view.velocity; \ No newline at end of file diff --git a/spring-boot/src/test/java/org/springframework/boot/web/servlet/view/velocity/EmbeddedVelocityToolboxViewTests.java b/spring-boot/src/test/java/org/springframework/boot/web/servlet/view/velocity/EmbeddedVelocityToolboxViewTests.java new file mode 100644 index 00000000000..73ff4948f81 --- /dev/null +++ b/spring-boot/src/test/java/org/springframework/boot/web/servlet/view/velocity/EmbeddedVelocityToolboxViewTests.java @@ -0,0 +1,95 @@ +/* + * 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.web.servlet.view.velocity; + +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.struts.mock.MockHttpServletRequest; +import org.apache.struts.mock.MockHttpServletResponse; +import org.apache.struts.mock.MockServletContext; +import org.apache.velocity.tools.ToolContext; +import org.junit.Test; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; +import org.springframework.web.servlet.view.velocity.VelocityConfigurer; + +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; + +/** + * Tests for {@link EmbeddedVelocityToolboxView}. + * + * @author Phillip Webb + */ +public class EmbeddedVelocityToolboxViewTests { + + private static final String PATH = EmbeddedVelocityToolboxViewTests.class + .getPackage().getName().replace(".", "/"); + + @Test + public void loadsContextFromClassPath() throws Exception { + ToolContext context = getToolContext(PATH + "/toolbox.xml"); + assertThat(context.getToolbox().keySet(), contains("math")); + } + + @Test + public void loadsWithoutConfig() throws Exception { + ToolContext context = getToolContext(null); + assertThat(context, notNullValue()); + } + + private ToolContext getToolContext(String toolboxConfigLocation) throws Exception { + AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); + context.setServletContext(new MockServletContext()); + context.register(Config.class); + context.refresh(); + EmbeddedVelocityToolboxView view = context + .getBean(EmbeddedVelocityToolboxView.class); + view.setToolboxConfigLocation(toolboxConfigLocation); + Map model = new LinkedHashMap(); + HttpServletRequest request = new MockHttpServletRequest(); + HttpServletResponse response = new MockHttpServletResponse(); + ToolContext toolContext = (ToolContext) view.createVelocityContext(model, + request, response); + context.close(); + return toolContext; + } + + @Configuration + static class Config { + + @Bean + public EmbeddedVelocityToolboxView view() { + EmbeddedVelocityToolboxView view = new EmbeddedVelocityToolboxView(); + view.setUrl("http://example.com"); + return view; + } + + @Bean + public VelocityConfigurer velocityConfigurer() { + return new VelocityConfigurer(); + } + + } + +} diff --git a/spring-boot/src/test/java/org/springframework/boot/web/servlet/view/velocity/EmbeddedVelocityViewResolverTests.java b/spring-boot/src/test/java/org/springframework/boot/web/servlet/view/velocity/EmbeddedVelocityViewResolverTests.java new file mode 100644 index 00000000000..3c0ed944545 --- /dev/null +++ b/spring-boot/src/test/java/org/springframework/boot/web/servlet/view/velocity/EmbeddedVelocityViewResolverTests.java @@ -0,0 +1,96 @@ +/* + * 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.web.servlet.view.velocity; + +import org.apache.struts.mock.MockServletContext; +import org.junit.Test; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; +import org.springframework.web.servlet.view.velocity.VelocityConfigurer; +import org.springframework.web.servlet.view.velocity.VelocityView; + +import static org.junit.Assert.assertEquals; + +/** + * Tests for {@link EmbeddedVelocityViewResolver}. + * + * @author Phillip Webb + */ +public class EmbeddedVelocityViewResolverTests { + + @Test + public void standardViewWithoutToolboxConfig() throws Exception { + ApplicationContext context = loadContext(WithoutToolboxConfig.class); + EmbeddedVelocityViewResolver resolver = context + .getBean(EmbeddedVelocityViewResolver.class); + Object viewClass = ReflectionTestUtils.getField(resolver, "viewClass"); + assertEquals(VelocityView.class, viewClass); + } + + @Test + public void embeddedViewWithToolboxConfig() throws Exception { + ApplicationContext context = loadContext(WithToolboxConfig.class); + EmbeddedVelocityViewResolver resolver = context + .getBean(EmbeddedVelocityViewResolver.class); + Object viewClass = ReflectionTestUtils.getField(resolver, "viewClass"); + assertEquals(EmbeddedVelocityToolboxView.class, viewClass); + } + + private ApplicationContext loadContext(Class config) { + AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); + context.setServletContext(new MockServletContext()); + context.register(config); + context.refresh(); + return context; + } + + @Configuration + static class WithoutToolboxConfig { + + @Bean + public EmbeddedVelocityViewResolver resolver() { + return new EmbeddedVelocityViewResolver(); + } + + @Bean + public VelocityConfigurer velocityConfigurer() { + return new VelocityConfigurer(); + } + + } + + @Configuration + static class WithToolboxConfig { + + @Bean + public EmbeddedVelocityViewResolver resolver() { + EmbeddedVelocityViewResolver resolver = new EmbeddedVelocityViewResolver(); + resolver.setToolboxConfigLocation("/toolbox.xml"); + return resolver; + } + + @Bean + public VelocityConfigurer velocityConfigurer() { + return new VelocityConfigurer(); + } + + } + +} diff --git a/spring-boot/src/test/resources/org/springframework/boot/web/servlet/view/velocity/toolbox.xml b/spring-boot/src/test/resources/org/springframework/boot/web/servlet/view/velocity/toolbox.xml new file mode 100644 index 00000000000..6be5e9f4d89 --- /dev/null +++ b/spring-boot/src/test/resources/org/springframework/boot/web/servlet/view/velocity/toolbox.xml @@ -0,0 +1,8 @@ + + + + math + application + org.apache.velocity.tools.generic.MathTool + +