Add Spring View support for Groovy Markup Templates

This commit adds support for Groovy Markup templates.
Spring's support requires Groovy 2.3.1+.

To use it, simply create a GroovyMarkupConfigurer and a
GroovyMarkupViewResolver beans in the web application context.

Issue: SPR-11789
This commit is contained in:
Brian Clozel
2014-06-03 15:50:50 +02:00
committed by Rossen Stoyanchev
parent 6b6b008c1f
commit 0ecfa8e404
18 changed files with 956 additions and 0 deletions

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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<String, GroovyMarkupConfig>());
view.setUrl("sampleView");
try {
view.setApplicationContext(this.webAppContext);
fail();
}
catch (ApplicationContextException ex) {
assertTrue(ex.getMessage().contains("GroovyMarkupConfig"));
}
}
@Test
public void customTemplateEngine() throws Exception {
GroovyMarkupView view = new GroovyMarkupView();
view.setTemplateEngine(new TestTemplateEngine());
view.setApplicationContext(this.webAppContext);
DirectFieldAccessor accessor = new DirectFieldAccessor(view);
TemplateEngine engine = (TemplateEngine) accessor.getPropertyValue("engine");
assertNotNull(engine);
assertEquals(TestTemplateEngine.class, engine.getClass());
}
@Test
public void detectTemplateEngine() throws Exception {
GroovyMarkupView view = new GroovyMarkupView();
view.setTemplateEngine(new TestTemplateEngine());
view.setApplicationContext(this.webAppContext);
DirectFieldAccessor accessor = new DirectFieldAccessor(view);
TemplateEngine engine = (TemplateEngine) accessor.getPropertyValue("engine");
assertNotNull(engine);
assertEquals(TestTemplateEngine.class, engine.getClass());
}
@Test
public void checkResource() throws Exception {
GroovyMarkupView view = createViewWithUrl("test.tpl");
assertTrue(view.checkResource(Locale.US));
}
@Test
public void checkMissingResource() throws Exception {
GroovyMarkupView view = createViewWithUrl("missing.tpl");
assertFalse(view.checkResource(Locale.US));
}
@Test
public void checkI18nResource() throws Exception {
GroovyMarkupView view = createViewWithUrl("i18n.tpl");
assertTrue(view.checkResource(Locale.FRENCH));
}
@Test
public void checkI18nResourceMissingLocale() throws Exception {
GroovyMarkupView view = createViewWithUrl("i18n.tpl");
assertTrue(view.checkResource(Locale.CHINESE));
}
@Test
public void renderMarkupTemplate() throws Exception {
Map<String, Object> model = new HashMap<>();
model.put("name", "Spring");
MockHttpServletResponse response = renderViewWithModel("test.tpl", model, Locale.US);
assertThat(response.getContentAsString(), Matchers.containsString("<h1>Hello Spring</h1>"));
}
@Test
public void renderI18nTemplate() throws Exception {
Map<String, Object> model = new HashMap<>();
model.put("name", "Spring");
MockHttpServletResponse response = renderViewWithModel("i18n.tpl", model, Locale.FRANCE);
assertEquals("<p>Bonjour Spring</p>", response.getContentAsString());
response = renderViewWithModel("i18n.tpl", model, Locale.GERMANY);
assertEquals("<p>Include German</p><p>Hallo Spring</p>", response.getContentAsString());
response = renderViewWithModel("i18n.tpl", model, new Locale("es"));
assertEquals("<p>Include Default</p><p>¡hola Spring</p>", response.getContentAsString());
}
@Test
public void renderLayoutTemplate() throws Exception {
Map<String, Object> model = new HashMap<>();
MockHttpServletResponse response = renderViewWithModel("content.tpl", model, Locale.US);
assertEquals("<html><head><title>Layout example</title></head><body><p>This is the body</p></body></html>",
response.getContentAsString());
}
private MockHttpServletResponse renderViewWithModel(String viewUrl, Map<String, Object> model, Locale locale) throws Exception {
GroovyMarkupView view = createViewWithUrl(viewUrl);
MockHttpServletResponse response = new MockHttpServletResponse();
MockHttpServletRequest request = new MockHttpServletRequest();
request.setPreferredLocales(Arrays.asList(locale));
LocaleContextHolder.setLocale(locale);
view.renderMergedTemplateModel(model, request, response);
return response;
}
private GroovyMarkupView createViewWithUrl(String viewUrl) throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(GroovyMarkupConfiguration.class);
ctx.refresh();
GroovyMarkupView view = new GroovyMarkupView();
view.setApplicationContext(ctx);
view.setUrl(viewUrl);
view.afterPropertiesSet();
return view;
}
public class TestTemplateEngine extends MarkupTemplateEngine {
public TestTemplateEngine() {
super(new TemplateConfiguration());
}
@Override
public Template createTemplate(Reader reader) throws CompilationFailedException, ClassNotFoundException, IOException {
return null;
}
}
@Configuration
static class GroovyMarkupConfiguration {
@Bean
public GroovyMarkupConfig groovyMarkupConfigurer() {
GroovyMarkupConfigurer configurer = new GroovyMarkupConfigurer();
configurer.setResourceLoaderPath(RESOURCE_LOADER_PATH);
return configurer;
}
}
}