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:
committed by
Rossen Stoyanchev
parent
6b6b008c1f
commit
0ecfa8e404
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user