All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.web.reactive.result.view.freemarker.FreeMarkerView Maven / Gradle / Ivy

/*
 * Copyright 2002-2017 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.reactive.result.view.freemarker;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;

import freemarker.core.ParseException;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapperBuilder;
import freemarker.template.ObjectWrapper;
import freemarker.template.SimpleHash;
import freemarker.template.Template;
import freemarker.template.Version;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.MimeType;
import org.springframework.web.reactive.result.view.AbstractUrlBasedView;
import org.springframework.web.server.ServerWebExchange;

/**
 * A {@code View} implementation that uses the FreeMarker template engine.
 *
 * 

Depends on a single {@link FreeMarkerConfig} object such as * {@link FreeMarkerConfigurer} being accessible in the application context. * Alternatively set the FreeMarker configuration can be set directly on this * class via {@link #setConfiguration}. * *

The {@link #setUrl(String) url} property is the location of the FreeMarker * template relative to the FreeMarkerConfigurer's * {@link FreeMarkerConfigurer#setTemplateLoaderPath templateLoaderPath}. * *

Note: Spring's FreeMarker support requires FreeMarker 2.3 or higher. * * @author Rossen Stoyanchev * @since 5.0 */ public class FreeMarkerView extends AbstractUrlBasedView { @Nullable private Configuration configuration; @Nullable private String encoding; /** * Set the FreeMarker Configuration to be used by this view. *

Typically this property is not set directly. Instead a single * {@link FreeMarkerConfig} is expected in the Spring application context * which is used to obtain the FreeMarker configuration. */ public void setConfiguration(@Nullable Configuration configuration) { this.configuration = configuration; } /** * Return the FreeMarker configuration used by this view. */ @Nullable protected Configuration getConfiguration() { return this.configuration; } /** * Obtain the FreeMarker configuration for actual use. * @return the FreeMarker configuration (never {@code null}) * @throws IllegalStateException in case of no Configuration object set * @since 5.0 */ protected Configuration obtainConfiguration() { Configuration configuration = getConfiguration(); Assert.state(configuration != null, "No Configuration set"); return configuration; } /** * Set the encoding of the FreeMarker template file. *

By default {@link FreeMarkerConfigurer} sets the default encoding in * the FreeMarker configuration to "UTF-8". It's recommended to specify the * encoding in the FreeMarker Configuration rather than per template if all * your templates share a common encoding. */ public void setEncoding(@Nullable String encoding) { this.encoding = encoding; } /** * Return the encoding for the FreeMarker template. */ @Nullable protected String getEncoding() { return this.encoding; } @Override public void afterPropertiesSet() throws Exception { super.afterPropertiesSet(); if (getConfiguration() == null) { FreeMarkerConfig config = autodetectConfiguration(); setConfiguration(config.getConfiguration()); } } /** * Autodetect a {@link FreeMarkerConfig} object via the ApplicationContext. * @return the Configuration instance to use for FreeMarkerViews * @throws BeansException if no Configuration instance could be found * @see #setConfiguration */ protected FreeMarkerConfig autodetectConfiguration() throws BeansException { try { return BeanFactoryUtils.beanOfTypeIncludingAncestors( obtainApplicationContext(), FreeMarkerConfig.class, true, false); } catch (NoSuchBeanDefinitionException ex) { throw new ApplicationContextException( "Must define a single FreeMarkerConfig bean in this web application context " + "(may be inherited): FreeMarkerConfigurer is the usual implementation. " + "This bean may be given any name.", ex); } } /** * Check that the FreeMarker template used for this view exists and is valid. *

Can be overridden to customize the behavior, for example in case of * multiple templates to be rendered into a single view. */ @Override public boolean checkResourceExists(Locale locale) throws Exception { try { // Check that we can get the template, even if we might subsequently get it again. getTemplate(locale); return true; } catch (FileNotFoundException ex) { // Allow for ViewResolver chaining... return false; } catch (ParseException ex) { throw new ApplicationContextException( "Failed to parse FreeMarker template for URL [" + getUrl() + "]", ex); } catch (IOException ex) { throw new ApplicationContextException( "Could not load FreeMarker template for URL [" + getUrl() + "]", ex); } } @Override protected Mono renderInternal(Map renderAttributes, @Nullable MediaType contentType, ServerWebExchange exchange) { // Expose all standard FreeMarker hash models. SimpleHash freeMarkerModel = getTemplateModel(renderAttributes, exchange); if (logger.isDebugEnabled()) { logger.debug(exchange.getLogPrefix() + "Rendering [" + getUrl() + "]"); } Locale locale = LocaleContextHolder.getLocale(exchange.getLocaleContext()); DataBuffer dataBuffer = exchange.getResponse().bufferFactory().allocateBuffer(); try { Charset charset = getCharset(contentType); Writer writer = new OutputStreamWriter(dataBuffer.asOutputStream(), charset); getTemplate(locale).process(freeMarkerModel, writer); } catch (IOException ex) { DataBufferUtils.release(dataBuffer); String message = "Could not load FreeMarker template for URL [" + getUrl() + "]"; return Mono.error(new IllegalStateException(message, ex)); } catch (Throwable ex) { DataBufferUtils.release(dataBuffer); return Mono.error(ex); } return exchange.getResponse().writeWith(Flux.just(dataBuffer)); } private Charset getCharset(@Nullable MediaType mediaType) { return Optional.ofNullable(mediaType).map(MimeType::getCharset).orElse(getDefaultCharset()); } /** * Build a FreeMarker template model for the given model Map. *

The default implementation builds a {@link SimpleHash}. * @param model the model to use for rendering * @param exchange current exchange * @return the FreeMarker template model, as a {@link SimpleHash} or subclass thereof */ protected SimpleHash getTemplateModel(Map model, ServerWebExchange exchange) { SimpleHash fmModel = new SimpleHash(getObjectWrapper()); fmModel.putAll(model); return fmModel; } /** * Return the configured FreeMarker {@link ObjectWrapper}, or the * {@link ObjectWrapper#DEFAULT_WRAPPER default wrapper} if none specified. * @see freemarker.template.Configuration#getObjectWrapper() */ protected ObjectWrapper getObjectWrapper() { ObjectWrapper ow = obtainConfiguration().getObjectWrapper(); Version version = Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS; return (ow != null ? ow : new DefaultObjectWrapperBuilder(version).build()); } /** * Retrieve the FreeMarker template for the given locale, * to be rendering by this view. *

By default, the template specified by the "url" bean property * will be retrieved. * @param locale the current locale * @return the FreeMarker template to render */ protected Template getTemplate(Locale locale) throws IOException { return (getEncoding() != null ? obtainConfiguration().getTemplate(getUrl(), locale, getEncoding()) : obtainConfiguration().getTemplate(getUrl(), locale)); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy