diff --git a/build.gradle b/build.gradle index 1af7e61e76..57d43d9d66 100644 --- a/build.gradle +++ b/build.gradle @@ -768,6 +768,7 @@ project("spring-webmvc") { optional("org.apache.velocity:velocity:1.7") optional("velocity-tools:velocity-tools-view:1.4") optional("org.freemarker:freemarker:2.3.20") + optional("org.codehaus.groovy:groovy-all:${groovyVersion}") optional("com.lowagie:itext:2.1.7") optional("net.sf.jasperreports:jasperreports:$jasperReportsVersion") { exclude group: "com.fasterxml.jackson.core", module: "jackson-annotations" diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/groovy/GroovyMarkupConfig.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/groovy/GroovyMarkupConfig.java new file mode 100644 index 0000000000..596dfaf801 --- /dev/null +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/groovy/GroovyMarkupConfig.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002-2014 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.web.servlet.view.groovy; + +import groovy.text.markup.MarkupTemplateEngine; + +/** + * Interface to be implemented by objects that configure and manage a + * Groovy MarkupTemplateEngine for automatic lookup in a web environment. + * Detected and used by GroovyMarkupView. + * + * @author Brian Clozel + * @see GroovyMarkupConfigurer + * @since 4.1 + */ +public interface GroovyMarkupConfig { + + /** + * Return the Groovy MarkupTemplateEngine for the current web application context. + * May be unique to one servlet, or shared in the root context. + * @return the Groovy Template engine + */ + public MarkupTemplateEngine getTemplateEngine(); +} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/groovy/GroovyMarkupConfigurer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/groovy/GroovyMarkupConfigurer.java new file mode 100644 index 0000000000..c7daf047af --- /dev/null +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/groovy/GroovyMarkupConfigurer.java @@ -0,0 +1,234 @@ +/* + * Copyright 2002-2014 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.web.servlet.view.groovy; + +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import groovy.text.markup.MarkupTemplateEngine; +import groovy.text.markup.TemplateConfiguration; +import groovy.text.markup.TemplateResolver; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.core.io.Resource; +import org.springframework.util.StringUtils; + +/** + * An extension of Groovy's {@link groovy.text.markup.TemplateConfiguration} and + * an implementation of Spring MVC's {@link GroovyMarkupConfig} for creating + * a {@code MarkupTemplateEngine} for use in a web application. The most basic + * way to configure this class is to set the "resourceLoaderPath". For example: + * + *
+ *
+ * // Add the following to an @Configuration class
+ *
+ * @Bean
+ * public GroovyMarkupConfig groovyMarkupConfigurer() {
+ *
+ * GroovyMarkupConfigurer configurer = new GroovyMarkupConfigurer();
+ * configurer.setResourceLoaderPath("classpath:/WEB-INF/groovymarkup/");
+ * return configurer;
+ * }
+ *
+ *
+ * By default this bean will create a {@link MarkupTemplateEngine} with:
+ * This bean must be included in the application context of any application + * using the Spring MVC {@link GroovyMarkupView} for rendering. It exists purely + * for the purpose of configuring Groovy's Markup templates. It is not meant to be + * referenced by application components directly. It implements GroovyMarkupConfig + * to be found by GroovyMarkupView without depending on a bean name. Each + * DispatcherServlet can define its own GroovyMarkupConfigurer if desired. + * + *
Note that resource caching is enabled by default in {@link MarkupTemplateEngine}. + * Use the {@link #setCacheTemplates(boolean)} to configure that as necessary. + + *
Spring's Groovy Markup template support requires Groovy 2.3.1 or higher. + * + * @author Brian Clozel + * @author Rossen Stoyanchev + * @since 4.1 + * + * @see GroovyMarkupView + * @see + * Groovy Markup Template engine documentation + */ +public class GroovyMarkupConfigurer extends TemplateConfiguration + implements GroovyMarkupConfig, ApplicationContextAware, InitializingBean { + + private String resourceLoaderPath = "classpath:"; + + private MarkupTemplateEngine templateEngine; + + private ApplicationContext applicationContext; + + + /** + * Set the Groovy Markup Template resource loader path(s) via a Spring resource + * location. Accepts multiple locations as a comma-separated list of paths. + * Standard URLs like "file:" and "classpath:" and pseudo URLs are supported + * as understood by Spring's {@link org.springframework.core.io.ResourceLoader}. + * Relative paths are allowed when running in an ApplicationContext. + * + */ + public void setResourceLoaderPath(String resourceLoaderPath) { + this.resourceLoaderPath = resourceLoaderPath; + } + + public String getResourceLoaderPath() { + return this.resourceLoaderPath; + } + + /** + * Set a pre-configured MarkupTemplateEngine to use for the Groovy Markup + * Template web configuration. + *
Note that this engine instance has to be manually configured, since all
+ * other bean properties of this configurer will be ignored.
+ */
+ public void setTemplateEngine(MarkupTemplateEngine templateEngine) {
+ this.templateEngine = templateEngine;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public MarkupTemplateEngine getTemplateEngine() {
+ return templateEngine;
+ }
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ this.applicationContext = applicationContext;
+ }
+
+ protected ApplicationContext getApplicationContext() {
+ return this.applicationContext;
+ }
+
+ /**
+ * This method should not be used, since the considered Locale for resolving
+ * templates is the Locale for the current HTTP request.
+ */
+ @Override
+ public void setLocale(Locale locale) {
+ super.setLocale(locale);
+ }
+
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ if (this.templateEngine == null) {
+ this.templateEngine = createTemplateEngine();
+ }
+ }
+
+ protected MarkupTemplateEngine createTemplateEngine() throws IOException {
+ if (this.templateEngine == null) {
+ ClassLoader templateClassLoader = createTemplateClassLoader();
+ this.templateEngine = new MarkupTemplateEngine(templateClassLoader, this, createTemplateResolver());
+ }
+ return this.templateEngine;
+ }
+
+ /**
+ * Create a parent classloader for Groovy to use as parent classloader when
+ * loading and compiling templates.
+ */
+ protected ClassLoader createTemplateClassLoader() throws IOException {
+ String[] paths = StringUtils.commaDelimitedListToStringArray(getResourceLoaderPath());
+ List The default implementation returns a resolver that uses the Locale
+ * associated with the current request to resolve the relevant template file.
+ * Effectively the locale configured at the engine level is ignored.
+ *
+ * @see LocaleContextHolder
+ * @see #setLocale(java.util.Locale)
+ */
+ protected TemplateResolver createTemplateResolver() {
+ return new MarkupTemplateResolver();
+ }
+
+
+ /**
+ * A custom {@link TemplateResolver template resolver} that resolves
+ * templates using the locale found with LocaleContextHolder.
+ *
+ * @author Cedric Champeau
+ * @author Brian Clozel
+ * @see LocaleContextHolder
+ */
+ private static class MarkupTemplateResolver implements TemplateResolver {
+
+ private ClassLoader classLoader;
+
+ @Override
+ public void configure(ClassLoader templateClassLoader, TemplateConfiguration configuration) {
+ this.classLoader = templateClassLoader;
+ }
+
+ @Override
+ public URL resolveTemplate(final String templatePath) throws IOException {
+ MarkupTemplateEngine.TemplateResource resource = MarkupTemplateEngine.TemplateResource.parse(templatePath);
+ Locale locale = LocaleContextHolder.getLocale();
+ URL url = this.classLoader.getResource(resource.withLocale(locale.toString().replace("-", "_")).toString());
+ if (url == null) {
+ url = this.classLoader.getResource(resource.withLocale(locale.getLanguage()).toString());
+ }
+ if (url == null) {
+ url = this.classLoader.getResource(resource.withLocale(null).toString());
+ }
+ if (url == null) {
+ throw new IOException("Unable to load template:" + templatePath);
+ }
+ return url;
+ }
+ }
+}
diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/groovy/GroovyMarkupView.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/groovy/GroovyMarkupView.java
new file mode 100644
index 0000000000..05859b99cf
--- /dev/null
+++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/groovy/GroovyMarkupView.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2002-2014 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.web.servlet.view.groovy;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import groovy.text.Template;
+import groovy.text.markup.MarkupTemplateEngine;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactoryUtils;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextException;
+import org.springframework.web.servlet.view.AbstractTemplateView;
+import org.springframework.web.util.NestedServletException;
+
+/**
+ * An {@link org.springframework.web.servlet.view.AbstractTemplateView AbstractTemplateView}
+ * based on Groovy XML/XHTML markup templates.
+ *
+ * Spring's Groovy Markup Template support requires Groovy 2.3.1 and higher.
+ *
+ * @author Brian Clozel
+ * @author Rossen Stoyanchev
+ * @since 4.1
+ *
+ * @see GroovyMarkupViewResolver
+ * @see GroovyMarkupConfigurer
+ * @see
+ * Groovy Markup Template engine documentation
+ */
+public class GroovyMarkupView extends AbstractTemplateView {
+
+ private MarkupTemplateEngine engine;
+
+
+ /**
+ * Set the MarkupTemplateEngine to use in this view.
+ *
+ * If not set, the engine is auto-detected by looking up up a single
+ * {@link GroovyMarkupConfig} bean in the web application context and using
+ * it to obtain the configured {@code MarkupTemplateEngine} instance.
+ *
+ * @see GroovyMarkupConfig
+ */
+ public void setTemplateEngine(MarkupTemplateEngine engine) {
+ this.engine = engine;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean checkResource(Locale locale) throws Exception {
+ try {
+ this.engine.resolveTemplate(getUrl());
+ }
+ catch (IOException exception) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Invoked at startup.
+ * If no {@link #setTemplateEngine(MarkupTemplateEngine) templateEngine} has
+ * been manually set, this method looks up a {@link GroovyMarkupConfig} bean
+ * by type and uses it to obtain the Groovy Markup template engine.
+ *
+ * @see GroovyMarkupConfig
+ * @see #setTemplateEngine(groovy.text.markup.MarkupTemplateEngine)
+ */
+ @Override
+ protected void initApplicationContext(ApplicationContext context) {
+ super.initApplicationContext();
+ if (this.engine == null) {
+ setTemplateEngine(autodetectMarkupTemplateEngine());
+ }
+ }
+
+ /**
+ * Auto-detect a MarkupTemplateEngine via the ApplicationContext.
+ * Called if a MarkupTemplateEngine has not been manually configured.
+ */
+ protected MarkupTemplateEngine autodetectMarkupTemplateEngine() throws BeansException {
+ try {
+ return BeanFactoryUtils.beanOfTypeIncludingAncestors(getApplicationContext(),
+ GroovyMarkupConfig.class, true, false).getTemplateEngine();
+ }
+ catch (NoSuchBeanDefinitionException ex) {
+ throw new ApplicationContextException(
+ "Expected a single GroovyMarkupConfig bean in the current Servlet web application context " +
+ "or the parent root context: GroovyMarkupConfigurer is the usual implementation. " +
+ "This bean may have any name.", ex);
+ }
+ }
+
+ @Override
+ protected void renderMergedTemplateModel(Map The view class for all views created by this resolver can be specified
+ * via the {@link #setViewClass(Class)} property.
+ *
+ * Note: When chaining ViewResolvers this resolver will check for the
+ * existence of the specified template resources and only return a non-null
+ * View object if a template is actually found.
+ *
+ * @author Brian Clozel
+ * @see GroovyMarkupConfigurer
+ * @since 4.1
+ */
+public class GroovyMarkupViewResolver extends AbstractTemplateViewResolver {
+
+
+ public GroovyMarkupViewResolver() {
+ this.setViewClass(requiredViewClass());
+ }
+
+
+ @Override
+ protected Class> requiredViewClass() {
+ return GroovyMarkupView.class;
+ }
+
+ /**
+ * This resolver supports i18n, so cache keys should contain the locale.
+ */
+ @Override
+ protected Object getCacheKey(String viewName, Locale locale) {
+ return viewName + "_" + locale;
+ }
+
+}
\ No newline at end of file
diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/groovy/package-info.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/groovy/package-info.java
new file mode 100644
index 0000000000..5323cc0ce3
--- /dev/null
+++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/groovy/package-info.java
@@ -0,0 +1,9 @@
+
+/**
+ * Support classes for the integration of
+ *
+ * Groovy Templates as Spring web view technology.
+ * Contains a View implementation for Groovy templates.
+ */
+package org.springframework.web.servlet.view.groovy;
+
diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/groovy/GroovyMarkupConfigurerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/groovy/GroovyMarkupConfigurerTests.java
new file mode 100644
index 0000000000..081d437d01
--- /dev/null
+++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/groovy/GroovyMarkupConfigurerTests.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2002-2014 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.web.servlet.view.groovy;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Arrays;
+import java.util.Locale;
+
+import groovy.text.TemplateEngine;
+import groovy.text.markup.MarkupTemplateEngine;
+import groovy.text.markup.TemplateConfiguration;
+import groovy.text.markup.TemplateResolver;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.springframework.context.i18n.LocaleContextHolder;
+import org.springframework.context.support.StaticApplicationContext;
+
+/**
+ * Unit tests for
+ * {@link org.springframework.web.servlet.view.groovy.GroovyMarkupConfigurer}.
+ *
+ * @author Brian Clozel
+ */
+public class GroovyMarkupConfigurerTests {
+
+ private static final String RESOURCE_LOADER_PATH = "classpath:org/springframework/web/servlet/view/groovy/";
+
+ private StaticApplicationContext applicationContext;
+
+ private static final String TEMPLATE_PREFIX = "org/springframework/web/servlet/view/groovy/";
+
+ private GroovyMarkupConfigurer configurer;
+
+ private TemplateResolver resolver;
+
+ @Before
+ public void setup() throws Exception {
+ this.applicationContext = new StaticApplicationContext();
+ this.configurer = new GroovyMarkupConfigurer();
+ this.configurer.setResourceLoaderPath(RESOURCE_LOADER_PATH);
+ this.resolver = this.configurer.createTemplateResolver();
+ this.resolver.configure(this.getClass().getClassLoader(), null);
+ }
+
+ @Test
+ public void defaultTemplateEngine() throws Exception {
+ this.configurer.setApplicationContext(this.applicationContext);
+ this.configurer.afterPropertiesSet();
+
+ TemplateEngine engine = this.configurer.getTemplateEngine();
+ assertNotNull(engine);
+ assertEquals(MarkupTemplateEngine.class, engine.getClass());
+
+ MarkupTemplateEngine markupEngine = (MarkupTemplateEngine) engine;
+ TemplateConfiguration configuration = markupEngine.getTemplateConfiguration();
+ assertNotNull(configuration);
+ assertEquals(GroovyMarkupConfigurer.class, configuration.getClass());
+ }
+
+ @Test
+ public void customTemplateEngine() throws Exception {
+ this.configurer.setApplicationContext(this.applicationContext);
+ this.configurer.setTemplateEngine(new TestTemplateEngine());
+ this.configurer.afterPropertiesSet();
+
+ TemplateEngine engine = this.configurer.getTemplateEngine();
+ assertNotNull(engine);
+ assertEquals(TestTemplateEngine.class, engine.getClass());
+ }
+
+ @Test
+ public void customTemplateConfiguration() throws Exception {
+ this.configurer.setApplicationContext(this.applicationContext);
+ this.configurer.setCacheTemplates(false);
+ this.configurer.afterPropertiesSet();
+
+ TemplateEngine engine = this.configurer.getTemplateEngine();
+ assertNotNull(engine);
+ assertEquals(MarkupTemplateEngine.class, engine.getClass());
+
+ MarkupTemplateEngine markupEngine = (MarkupTemplateEngine) engine;
+ TemplateConfiguration configuration = markupEngine.getTemplateConfiguration();
+ assertNotNull(configuration);
+ assertFalse(configuration.isCacheTemplates());
+ }
+
+ @Test
+ public void parentLoader() throws Exception {
+
+ this.configurer.setApplicationContext(this.applicationContext);
+
+ ClassLoader classLoader = this.configurer.createTemplateClassLoader();
+ assertNotNull(classLoader);
+ URLClassLoader urlClassLoader = (URLClassLoader) classLoader;
+ assertThat(Arrays.asList(urlClassLoader.getURLs()), Matchers.hasSize(1));
+ assertThat(Arrays.asList(urlClassLoader.getURLs()).get(0).toString(),
+ Matchers.endsWith("org/springframework/web/servlet/view/groovy/"));
+
+ this.configurer.setResourceLoaderPath(RESOURCE_LOADER_PATH + ",classpath:org/springframework/web/servlet/view/");
+ classLoader = this.configurer.createTemplateClassLoader();
+ assertNotNull(classLoader);
+ urlClassLoader = (URLClassLoader) classLoader;
+ assertThat(Arrays.asList(urlClassLoader.getURLs()), Matchers.hasSize(2));
+ assertThat(Arrays.asList(urlClassLoader.getURLs()).get(0).toString(),
+ Matchers.endsWith("org/springframework/web/servlet/view/groovy/"));
+ assertThat(Arrays.asList(urlClassLoader.getURLs()).get(1).toString(),
+ Matchers.endsWith("org/springframework/web/servlet/view/"));
+ }
+
+ private class TestTemplateEngine extends MarkupTemplateEngine {
+
+ public TestTemplateEngine() {
+ super(new TemplateConfiguration());
+ }
+ }
+
+ @Test
+ public void resolveSampleTemplate() throws Exception {
+ URL url = this.resolver.resolveTemplate(TEMPLATE_PREFIX + "test.tpl");
+ Assert.assertNotNull(url);
+ }
+
+ @Test
+ public void resolveI18nFullLocale() throws Exception {
+ LocaleContextHolder.setLocale(Locale.GERMANY);
+ URL url = this.resolver.resolveTemplate(TEMPLATE_PREFIX + "i18n.tpl");
+ Assert.assertNotNull(url);
+ Assert.assertThat(url.getPath(), Matchers.containsString("i18n_de_DE.tpl"));
+ }
+
+ @Test
+ public void resolveI18nPartialLocale() throws Exception {
+ LocaleContextHolder.setLocale(Locale.FRANCE);
+ URL url = this.resolver.resolveTemplate(TEMPLATE_PREFIX + "i18n.tpl");
+ Assert.assertNotNull(url);
+ Assert.assertThat(url.getPath(), Matchers.containsString("i18n_fr.tpl"));
+ }
+
+ @Test
+ public void resolveI18nDefaultLocale() throws Exception {
+ LocaleContextHolder.setLocale(Locale.US);
+ URL url = this.resolver.resolveTemplate(TEMPLATE_PREFIX + "i18n.tpl");
+ Assert.assertNotNull(url);
+ Assert.assertThat(url.getPath(), Matchers.containsString("i18n.tpl"));
+ }
+
+ @Test(expected = IOException.class)
+ public void failMissingTemplate() throws Exception {
+ LocaleContextHolder.setLocale(Locale.US);
+ this.resolver.resolveTemplate(TEMPLATE_PREFIX + "missing.tpl");
+ Assert.fail();
+ }
+}
diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/groovy/GroovyMarkupViewResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/groovy/GroovyMarkupViewResolverTests.java
new file mode 100644
index 0000000000..2190e3689d
--- /dev/null
+++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/groovy/GroovyMarkupViewResolverTests.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2002-2014 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.web.servlet.view.groovy;
+
+import java.util.Locale;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.springframework.beans.DirectFieldAccessor;
+
+/**
+ * Unit tests for
+ * {@link org.springframework.web.servlet.view.groovy.GroovyMarkupViewResolver}.
+ *
+ * @author Brian Clozel
+ */
+public class GroovyMarkupViewResolverTests {
+
+ @Test
+ public void viewClass() throws Exception {
+ GroovyMarkupViewResolver resolver = new GroovyMarkupViewResolver();
+ Assert.assertEquals(GroovyMarkupView.class, resolver.requiredViewClass());
+ DirectFieldAccessor viewAccessor = new DirectFieldAccessor(resolver);
+ Class viewClass = (Class) viewAccessor.getPropertyValue("viewClass");
+ Assert.assertEquals(GroovyMarkupView.class, viewClass);
+ }
+
+ @Test
+ public void cacheKey() throws Exception {
+ GroovyMarkupViewResolver resolver = new GroovyMarkupViewResolver();
+ String cacheKey = (String) resolver.getCacheKey("test", Locale.US);
+ Assert.assertNotNull(cacheKey);
+ Assert.assertEquals("test_en_US", cacheKey);
+ }
+
+}
diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/groovy/GroovyMarkupViewTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/groovy/GroovyMarkupViewTests.java
new file mode 100644
index 0000000000..dafeae4a67
--- /dev/null
+++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/groovy/GroovyMarkupViewTests.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2002-2014 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.web.servlet.view.groovy;
+
+import static org.junit.Assert.*;
+import static org.mockito.BDDMockito.*;
+import static org.mockito.Mockito.mock;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+
+import groovy.text.Template;
+import groovy.text.TemplateEngine;
+import groovy.text.markup.MarkupTemplateEngine;
+import groovy.text.markup.TemplateConfiguration;
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.springframework.beans.DirectFieldAccessor;
+import org.springframework.context.ApplicationContextException;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.i18n.LocaleContextHolder;
+import org.springframework.mock.web.test.MockHttpServletRequest;
+import org.springframework.mock.web.test.MockHttpServletResponse;
+import org.springframework.mock.web.test.MockServletContext;
+import org.springframework.web.context.WebApplicationContext;
+
+/**
+ * @author Brian Clozel
+ */
+public class GroovyMarkupViewTests {
+
+ private static final String RESOURCE_LOADER_PATH = "classpath*:org/springframework/web/servlet/view/groovy/";
+
+ private WebApplicationContext webAppContext;
+
+ private ServletContext servletContext;
+
+ @Before
+ public void setup() {
+ this.webAppContext = mock(WebApplicationContext.class);
+ this.servletContext = new MockServletContext();
+ this.servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.webAppContext);
+ }
+
+ @Test
+ public void missingGroovyMarkupConfig() throws Exception {
+ GroovyMarkupView view = new GroovyMarkupView();
+ given(this.webAppContext.getBeansOfType(GroovyMarkupConfig.class, true, false))
+ .willReturn(new HashMap Bonjour Spring Include German Hallo Spring Include Default ¡hola Spring This is the bodyHello Spring
"));
+ }
+
+ @Test
+ public void renderI18nTemplate() throws Exception {
+ Map