001/* 002 * Copyright 2002-2017 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.servlet.view.groovy; 018 019import java.io.BufferedWriter; 020import java.io.IOException; 021import java.util.Locale; 022import java.util.Map; 023 024import javax.servlet.http.HttpServletRequest; 025import javax.servlet.http.HttpServletResponse; 026 027import groovy.text.Template; 028import groovy.text.markup.MarkupTemplateEngine; 029 030import org.springframework.beans.BeansException; 031import org.springframework.beans.factory.BeanFactoryUtils; 032import org.springframework.beans.factory.NoSuchBeanDefinitionException; 033import org.springframework.context.ApplicationContext; 034import org.springframework.context.ApplicationContextException; 035import org.springframework.lang.Nullable; 036import org.springframework.util.Assert; 037import org.springframework.web.servlet.view.AbstractTemplateView; 038import org.springframework.web.util.NestedServletException; 039 040/** 041 * An {@link AbstractTemplateView} subclass based on Groovy XML/XHTML markup templates. 042 * 043 * <p>Spring's Groovy Markup Template support requires Groovy 2.3.1 and higher. 044 * 045 * @author Brian Clozel 046 * @author Rossen Stoyanchev 047 * @since 4.1 048 * @see GroovyMarkupViewResolver 049 * @see GroovyMarkupConfigurer 050 * @see <a href="http://groovy-lang.org/templating.html#_the_markuptemplateengine"> 051 * Groovy Markup Template engine documentation</a> 052 */ 053public class GroovyMarkupView extends AbstractTemplateView { 054 055 @Nullable 056 private MarkupTemplateEngine engine; 057 058 059 /** 060 * Set the MarkupTemplateEngine to use in this view. 061 * <p>If not set, the engine is auto-detected by looking up a single 062 * {@link GroovyMarkupConfig} bean in the web application context and using 063 * it to obtain the configured {@code MarkupTemplateEngine} instance. 064 * @see GroovyMarkupConfig 065 */ 066 public void setTemplateEngine(MarkupTemplateEngine engine) { 067 this.engine = engine; 068 } 069 070 /** 071 * Invoked at startup. 072 * If no {@link #setTemplateEngine(MarkupTemplateEngine) templateEngine} has 073 * been manually set, this method looks up a {@link GroovyMarkupConfig} bean 074 * by type and uses it to obtain the Groovy Markup template engine. 075 * @see GroovyMarkupConfig 076 * @see #setTemplateEngine(groovy.text.markup.MarkupTemplateEngine) 077 */ 078 @Override 079 protected void initApplicationContext(ApplicationContext context) { 080 super.initApplicationContext(); 081 if (this.engine == null) { 082 setTemplateEngine(autodetectMarkupTemplateEngine()); 083 } 084 } 085 086 /** 087 * Autodetect a MarkupTemplateEngine via the ApplicationContext. 088 * Called if a MarkupTemplateEngine has not been manually configured. 089 */ 090 protected MarkupTemplateEngine autodetectMarkupTemplateEngine() throws BeansException { 091 try { 092 return BeanFactoryUtils.beanOfTypeIncludingAncestors(obtainApplicationContext(), 093 GroovyMarkupConfig.class, true, false).getTemplateEngine(); 094 } 095 catch (NoSuchBeanDefinitionException ex) { 096 throw new ApplicationContextException("Expected a single GroovyMarkupConfig bean in the current " + 097 "Servlet web application context or the parent root context: GroovyMarkupConfigurer is " + 098 "the usual implementation. This bean may have any name.", ex); 099 } 100 } 101 102 103 @Override 104 public boolean checkResource(Locale locale) throws Exception { 105 Assert.state(this.engine != null, "No MarkupTemplateEngine set"); 106 try { 107 this.engine.resolveTemplate(getUrl()); 108 } 109 catch (IOException ex) { 110 return false; 111 } 112 return true; 113 } 114 115 @Override 116 protected void renderMergedTemplateModel(Map<String, Object> model, 117 HttpServletRequest request, HttpServletResponse response) throws Exception { 118 119 String url = getUrl(); 120 Assert.state(url != null, "'url' not set"); 121 122 Template template = getTemplate(url); 123 template.make(model).writeTo(new BufferedWriter(response.getWriter())); 124 } 125 126 /** 127 * Return a template compiled by the configured Groovy Markup template engine 128 * for the given view URL. 129 */ 130 protected Template getTemplate(String viewUrl) throws Exception { 131 Assert.state(this.engine != null, "No MarkupTemplateEngine set"); 132 try { 133 return this.engine.createTemplateByPath(viewUrl); 134 } 135 catch (ClassNotFoundException ex) { 136 Throwable cause = (ex.getCause() != null ? ex.getCause() : ex); 137 throw new NestedServletException( 138 "Could not find class while rendering Groovy Markup view with name '" + 139 getUrl() + "': " + ex.getMessage() + "'", cause); 140 } 141 } 142 143}