001/*
002 * Copyright 2002-2016 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;
023import javax.servlet.http.HttpServletRequest;
024import javax.servlet.http.HttpServletResponse;
025
026import groovy.text.Template;
027import groovy.text.markup.MarkupTemplateEngine;
028
029import org.springframework.beans.BeansException;
030import org.springframework.beans.factory.BeanFactoryUtils;
031import org.springframework.beans.factory.NoSuchBeanDefinitionException;
032import org.springframework.context.ApplicationContext;
033import org.springframework.context.ApplicationContextException;
034import org.springframework.web.servlet.view.AbstractTemplateView;
035import org.springframework.web.util.NestedServletException;
036
037/**
038 * An {@link AbstractTemplateView} subclass based on Groovy XML/XHTML markup templates.
039 *
040 * <p>Spring's Groovy Markup Template support requires Groovy 2.3.1 and higher.
041 *
042 * @author Brian Clozel
043 * @author Rossen Stoyanchev
044 * @since 4.1
045 * @see GroovyMarkupViewResolver
046 * @see GroovyMarkupConfigurer
047 * @see <a href="http://groovy-lang.org/templating.html#_the_markuptemplateengine">
048 * Groovy Markup Template engine documentation</a>
049 */
050public class GroovyMarkupView extends AbstractTemplateView {
051
052        private MarkupTemplateEngine engine;
053
054
055        /**
056         * Set the MarkupTemplateEngine to use in this view.
057         * <p>If not set, the engine is auto-detected by looking up a single
058         * {@link GroovyMarkupConfig} bean in the web application context and using
059         * it to obtain the configured {@code MarkupTemplateEngine} instance.
060         * @see GroovyMarkupConfig
061         */
062        public void setTemplateEngine(MarkupTemplateEngine engine) {
063                this.engine = engine;
064        }
065
066        /**
067         * Invoked at startup.
068         * If no {@link #setTemplateEngine(MarkupTemplateEngine) templateEngine} has
069         * been manually set, this method looks up a {@link GroovyMarkupConfig} bean
070         * by type and uses it to obtain the Groovy Markup template engine.
071         * @see GroovyMarkupConfig
072         * @see #setTemplateEngine(groovy.text.markup.MarkupTemplateEngine)
073         */
074        @Override
075        protected void initApplicationContext(ApplicationContext context) {
076                super.initApplicationContext();
077                if (this.engine == null) {
078                        setTemplateEngine(autodetectMarkupTemplateEngine());
079                }
080        }
081
082        /**
083         * Autodetect a MarkupTemplateEngine via the ApplicationContext.
084         * Called if a MarkupTemplateEngine has not been manually configured.
085         */
086        protected MarkupTemplateEngine autodetectMarkupTemplateEngine() throws BeansException {
087                try {
088                        return BeanFactoryUtils.beanOfTypeIncludingAncestors(getApplicationContext(),
089                                        GroovyMarkupConfig.class, true, false).getTemplateEngine();
090                }
091                catch (NoSuchBeanDefinitionException ex) {
092                        throw new ApplicationContextException("Expected a single GroovyMarkupConfig bean in the current " +
093                                        "Servlet web application context or the parent root context: GroovyMarkupConfigurer is " +
094                                        "the usual implementation. This bean may have any name.", ex);
095                }
096        }
097
098
099        @Override
100        public boolean checkResource(Locale locale) throws Exception {
101                try {
102                        this.engine.resolveTemplate(getUrl());
103                }
104                catch (IOException ex) {
105                        return false;
106                }
107                return true;
108        }
109
110        @Override
111        protected void renderMergedTemplateModel(Map<String, Object> model,
112                        HttpServletRequest request, HttpServletResponse response) throws Exception {
113
114                Template template = getTemplate(getUrl());
115                template.make(model).writeTo(new BufferedWriter(response.getWriter()));
116        }
117
118        /**
119         * Return a template compiled by the configured Groovy Markup template engine
120         * for the given view URL.
121         */
122        protected Template getTemplate(String viewUrl) throws Exception {
123                try {
124                        return this.engine.createTemplateByPath(viewUrl);
125                }
126                catch (ClassNotFoundException ex) {
127                        Throwable cause = (ex.getCause() != null ? ex.getCause() : ex);
128                        throw new NestedServletException(
129                                        "Could not find class while rendering Groovy Markup view with name '" +
130                                        getUrl() + "': " + ex.getMessage() + "'", cause);
131                }
132        }
133
134}