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:
parent
6b6b008c1f
commit
0ecfa8e404
|
@ -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"
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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:
|
||||
*
|
||||
* <pre class="code">
|
||||
*
|
||||
* // Add the following to an @Configuration class
|
||||
*
|
||||
* @Bean
|
||||
* public GroovyMarkupConfig groovyMarkupConfigurer() {
|
||||
*
|
||||
* GroovyMarkupConfigurer configurer = new GroovyMarkupConfigurer();
|
||||
* configurer.setResourceLoaderPath("classpath:/WEB-INF/groovymarkup/");
|
||||
* return configurer;
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* By default this bean will create a {@link MarkupTemplateEngine} with:
|
||||
* <ul>
|
||||
* <li>a parent ClassLoader for loading Groovy templates with their references
|
||||
* <li>the default configuration in the base class {@link TemplateConfiguration}
|
||||
* <li>a {@link groovy.text.markup.TemplateResolver} for resolving template files
|
||||
* </ul>
|
||||
*
|
||||
* You can provide the {@link MarkupTemplateEngine} instance directly to this bean
|
||||
* in which case all other properties will not be effectively ignored.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>Note that resource caching is enabled by default in {@link MarkupTemplateEngine}.
|
||||
* Use the {@link #setCacheTemplates(boolean)} to configure that as necessary.
|
||||
|
||||
* <p>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 <a href="http://beta.groovy-lang.org/docs/groovy-2.3.2/html/documentation/markup-template-engine.html">
|
||||
* Groovy Markup Template engine documentation</a>
|
||||
*/
|
||||
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.
|
||||
* <p>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<URL> urls = new ArrayList<URL>();
|
||||
for (String path : paths) {
|
||||
Resource[] resources = getApplicationContext().getResources(path);
|
||||
if (resources.length > 0) {
|
||||
for (Resource resource : resources) {
|
||||
if (resource.exists()) {
|
||||
urls.add(resource.getURL());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ClassLoader classLoader = getApplicationContext().getClassLoader();
|
||||
return (urls.size() > 0 ? new URLClassLoader(urls.toArray(new URL[urls.size()]), classLoader) : classLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link TemplateResolver} to be used by the
|
||||
* {@link MarkupTemplateEngine} to resolve template files.
|
||||
*
|
||||
* <p>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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
*
|
||||
* <p>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 <a href="http://beta.groovy-lang.org/docs/groovy-2.3.2/html/documentation/markup-template-engine.html">
|
||||
* Groovy Markup Template engine documentation</a>
|
||||
*/
|
||||
public class GroovyMarkupView extends AbstractTemplateView {
|
||||
|
||||
private MarkupTemplateEngine engine;
|
||||
|
||||
|
||||
/**
|
||||
* Set the MarkupTemplateEngine to use in this view.
|
||||
*
|
||||
* <p>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<String, Object> model,
|
||||
HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
Template template = getTemplate(getUrl());
|
||||
template.make(model).writeTo(new BufferedWriter(response.getWriter()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a template compiled by the configured Groovy Markup template engine
|
||||
* for the given view URL.
|
||||
*/
|
||||
protected Template getTemplate(String viewUrl) throws Exception {
|
||||
try {
|
||||
return this.engine.createTemplateByPath(viewUrl);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
Throwable cause = ex.getCause() != null ? ex.getCause() : ex;
|
||||
throw new NestedServletException(
|
||||
"Could not find class while rendering Groovy Markup view with name '" +
|
||||
getUrl() + "': " + ex.getMessage() + "'", cause);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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.springframework.web.servlet.view.AbstractTemplateViewResolver;
|
||||
|
||||
/**
|
||||
* Convenience subclass of
|
||||
* {@link org.springframework.web.servlet.view.AbstractTemplateViewResolver}
|
||||
* that supports {@link GroovyMarkupView} (i.e. Groovy XML/XHTML markup templates)
|
||||
* and custom subclasses of it.
|
||||
*
|
||||
* <p>The view class for all views created by this resolver can be specified
|
||||
* via the {@link #setViewClass(Class)} property.
|
||||
*
|
||||
* <p><b>Note:</b> 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
/**
|
||||
* Support classes for the integration of
|
||||
* <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0/html/documentation/markup-template-engine.html">
|
||||
* Groovy Templates</a> as Spring web view technology.
|
||||
* Contains a View implementation for Groovy templates.
|
||||
*/
|
||||
package org.springframework.web.servlet.view.groovy;
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
layout 'layout-main.tpl',
|
||||
title: 'Layout example',
|
||||
bodyContents: contents { p('This is the body') }
|
|
@ -0,0 +1 @@
|
|||
p('Hello Spring')
|
|
@ -0,0 +1,2 @@
|
|||
include template: 'includes/include.tpl'
|
||||
p('Hallo Spring')
|
|
@ -0,0 +1,2 @@
|
|||
include template: 'includes/include.tpl'
|
||||
p('¡hola Spring')
|
|
@ -0,0 +1 @@
|
|||
p('Bonjour Spring')
|
|
@ -0,0 +1 @@
|
|||
p('Include Default')
|
|
@ -0,0 +1 @@
|
|||
p('Include German')
|
|
@ -0,0 +1,8 @@
|
|||
html {
|
||||
head {
|
||||
title(title)
|
||||
}
|
||||
body {
|
||||
bodyContents()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
yieldUnescaped '<!DOCTYPE html>'
|
||||
html {
|
||||
head {
|
||||
meta('http-equiv':'"Content-Type", content="text/html; charset=utf-8"')
|
||||
title('Spring Framework Groovy Template Support')
|
||||
}
|
||||
body {
|
||||
div(class:'test') {
|
||||
h1("Hello $name")
|
||||
p("Groovy Templates")
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue