Skip to content

Commit

Permalink
Merge pull request #13845 from codeconsole/7.0.x-url-mappings-autocon…
Browse files Browse the repository at this point in the history
…figuration

AutoConfiguration for Url Mappings and Controllers Plugins
  • Loading branch information
codeconsole authored Nov 17, 2024
2 parents b8c89b4 + 040d422 commit 10293ad
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 71 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package org.grails.plugins.web.controllers;

import grails.config.Settings;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.Filter;
import org.grails.spring.config.http.GrailsFilters;
import org.grails.web.filters.HiddenHttpMethodFilter;
import org.grails.web.servlet.mvc.GrailsWebRequestFilter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.web.filter.CharacterEncodingFilter;

import java.util.EnumSet;

@AutoConfiguration(before = { HttpEncodingAutoConfiguration.class, WebMvcAutoConfiguration.class })
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class ControllersAutoConfiguration {

@Value("${" + Settings.FILTER_ENCODING + ":utf-8}")
private String filtersEncoding;

@Value("${" + Settings.FILTER_FORCE_ENCODING + ":false}")
private boolean filtersForceEncoding;

@Bean
@ConditionalOnMissingBean(CharacterEncodingFilter.class)
public CharacterEncodingFilter characterEncodingFilter() {
FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
OrderedCharacterEncodingFilter characterEncodingFilter = new OrderedCharacterEncodingFilter();
characterEncodingFilter.setEncoding(filtersEncoding);
characterEncodingFilter.setForceEncoding(filtersForceEncoding);
characterEncodingFilter.setOrder(GrailsFilters.CHARACTER_ENCODING_FILTER.getOrder());
return characterEncodingFilter;
}

@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
public FilterRegistrationBean<Filter> hiddenHttpMethodFilter() {
FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new HiddenHttpMethodFilter());
registrationBean.addUrlPatterns(Settings.DEFAULT_WEB_SERVLET_PATH);
registrationBean.setOrder(GrailsFilters.HIDDEN_HTTP_METHOD_FILTER.getOrder());
return registrationBean;
}

@Bean
@ConditionalOnMissingBean(GrailsWebRequestFilter.class)
public FilterRegistrationBean<Filter> grailsWebRequestFilter() {
FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new GrailsWebRequestFilter());
registrationBean.setDispatcherTypes(EnumSet.of(
DispatcherType.FORWARD,
DispatcherType.INCLUDE,
DispatcherType.REQUEST)
);
registrationBean.addUrlPatterns(Settings.DEFAULT_WEB_SERVLET_PATH);
registrationBean.setOrder(GrailsFilters.GRAILS_WEB_REQUEST_FILTER.getOrder());
return registrationBean;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,19 @@ import groovy.transform.CompileStatic
import groovy.util.logging.Slf4j
import org.grails.core.artefact.ControllerArtefactHandler
import org.grails.plugins.web.servlet.context.BootStrapClassRunner
import org.grails.spring.config.http.GrailsFilters
import org.grails.web.errors.GrailsExceptionResolver
import org.grails.web.filters.HiddenHttpMethodFilter
import org.grails.web.servlet.mvc.GrailsDispatcherServlet
import org.grails.web.servlet.mvc.GrailsWebRequestFilter
import org.grails.web.servlet.mvc.TokenResponseActionResultTransformer
import org.grails.web.servlet.view.CompositeViewResolver
import org.springframework.beans.factory.support.AbstractBeanDefinition
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean
import org.springframework.boot.web.servlet.FilterRegistrationBean
import org.springframework.context.ApplicationContext
import org.springframework.util.ClassUtils
import org.springframework.web.filter.CharacterEncodingFilter
import org.springframework.web.multipart.support.StandardServletMultipartResolver
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
import jakarta.servlet.DispatcherType
import jakarta.servlet.MultipartConfigElement

/**
Expand Down Expand Up @@ -71,8 +65,6 @@ class ControllersGrailsPlugin extends Plugin {
long maxFileSize = config.getProperty(Settings.CONTROLLERS_UPLOAD_MAX_FILE_SIZE, Long, 128000L)
long maxRequestSize = config.getProperty(Settings.CONTROLLERS_UPLOAD_MAX_REQUEST_SIZE, Long, 128000L)
int fileSizeThreashold = config.getProperty(Settings.CONTROLLERS_UPLOAD_FILE_SIZE_THRESHOLD, Integer, 0)
String filtersEncoding = config.getProperty(Settings.FILTER_ENCODING, 'utf-8')
boolean filtersForceEncoding = config.getProperty(Settings.FILTER_FORCE_ENCODING, Boolean, false)
boolean isTomcat = ClassUtils.isPresent("org.apache.catalina.startup.Tomcat", application.classLoader)
String grailsServletPath = config.getProperty(Settings.WEB_SERVLET_PATH, isTomcat ? Settings.DEFAULT_TOMCAT_SERVLET_PATH : Settings.DEFAULT_WEB_SERVLET_PATH)
int resourcesCachePeriod = config.getProperty(Settings.RESOURCES_CACHE_PERIOD, Integer, 0)
Expand All @@ -85,40 +77,10 @@ class ControllersGrailsPlugin extends Plugin {

tokenResponseActionResultTransformer(TokenResponseActionResultTransformer)

def catchAllMapping = [Settings.DEFAULT_WEB_SERVLET_PATH]

characterEncodingFilter(FilterRegistrationBean) {
filter = bean(CharacterEncodingFilter) {
encoding = filtersEncoding
forceEncoding = filtersForceEncoding
}
urlPatterns = catchAllMapping
order = GrailsFilters.CHARACTER_ENCODING_FILTER.order
}

hiddenHttpMethodFilter(FilterRegistrationBean) {
filter = bean(HiddenHttpMethodFilter)
urlPatterns = catchAllMapping
order = GrailsFilters.HIDDEN_HTTP_METHOD_FILTER.order
}

grailsWebRequestFilter(FilterRegistrationBean) {
filter = bean(GrailsWebRequestFilter)
urlPatterns = catchAllMapping
order = GrailsFilters.GRAILS_WEB_REQUEST_FILTER.order
dispatcherTypes = EnumSet.of(
DispatcherType.FORWARD,
DispatcherType.INCLUDE,
DispatcherType.REQUEST
)
}

exceptionHandler(GrailsExceptionResolver) {
exceptionMappings = ['java.lang.Exception': '/error']
}

multipartResolver(StandardServletMultipartResolver)

"${CompositeViewResolver.BEAN_NAME}"(CompositeViewResolver)

multipartConfigElement(MultipartConfigElement, uploadTmpDir, maxFileSize, maxRequestSize, fileSizeThreashold)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.grails.plugins.web.controllers.ControllersAutoConfiguration
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package org.grails.plugins.web.mapping;

import grails.config.Settings;
import grails.util.Environment;
import grails.web.CamelCaseUrlConverter;
import grails.web.HyphenatedUrlConverter;
import grails.web.UrlConverter;
import grails.web.mapping.LinkGenerator;
import grails.web.mapping.UrlMappings;
import grails.web.mapping.cors.GrailsCorsConfiguration;
import grails.web.mapping.cors.GrailsCorsFilter;
import org.grails.web.mapping.CachingLinkGenerator;
import org.grails.web.mapping.DefaultLinkGenerator;
import org.grails.web.mapping.mvc.UrlMappingsInfoHandlerAdapter;
import org.grails.web.mapping.servlet.UrlMappingsErrorPageCustomizer;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.*;

@AutoConfiguration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@EnableConfigurationProperties({ GrailsCorsConfiguration.class })
public class UrlMappingsAutoConfiguration {
@Value("${" + Settings.WEB_LINK_GENERATOR_USE_CACHE + ":#{null}}")
private Boolean cacheUrls;

@Value("${" + Settings.SERVER_URL + ":#{null}}")
private String serverURL;

@Bean(UrlConverter.BEAN_NAME)
@ConditionalOnMissingBean(name = UrlConverter.BEAN_NAME)
@ConditionalOnProperty(name = Settings.WEB_URL_CONVERTER, havingValue = "camelCase", matchIfMissing = true)
public UrlConverter camelCaseUrlConverter() {
return new CamelCaseUrlConverter();
}

@Bean(UrlConverter.BEAN_NAME)
@ConditionalOnMissingBean(name = UrlConverter.BEAN_NAME)
@ConditionalOnProperty(name = Settings.WEB_URL_CONVERTER, havingValue = "hyphenated")
public UrlConverter hyphenatedUrlConverter() {
return new HyphenatedUrlConverter();
}

@Bean
public LinkGenerator grailsLinkGenerator() {
if (cacheUrls == null) {
cacheUrls = !Environment.isDevelopmentMode() && !Environment.getCurrent().isReloadEnabled();
}
return cacheUrls? new CachingLinkGenerator(serverURL) : new DefaultLinkGenerator(serverURL);
}

@Bean
@ConditionalOnProperty(name = Settings.SETTING_CORS_FILTER, havingValue = "true", matchIfMissing = true)
public GrailsCorsFilter grailsCorsFilter(GrailsCorsConfiguration grailsCorsConfiguration) {
return new GrailsCorsFilter(grailsCorsConfiguration);
}

@Bean
public UrlMappingsErrorPageCustomizer urlMappingsErrorPageCustomizer(ObjectProvider<UrlMappings> urlMappingsProvider) {
UrlMappingsErrorPageCustomizer errorPageCustomizer = new UrlMappingsErrorPageCustomizer();
errorPageCustomizer.setUrlMappings(urlMappingsProvider.getIfAvailable());
return errorPageCustomizer;
}

@Bean
public UrlMappingsInfoHandlerAdapter urlMappingsInfoHandlerAdapter() {
return new UrlMappingsInfoHandlerAdapter();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,16 @@ import grails.config.Settings
import grails.plugins.Plugin
import grails.util.Environment
import grails.util.GrailsUtil
import grails.web.CamelCaseUrlConverter
import grails.web.HyphenatedUrlConverter
import grails.web.mapping.cors.GrailsCorsFilter
import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
import org.grails.core.artefact.UrlMappingsArtefactHandler
import grails.web.mapping.cors.GrailsCorsConfiguration
import org.grails.spring.beans.factory.HotSwappableTargetSourceFactoryBean
import org.grails.web.mapping.CachingLinkGenerator
import org.grails.web.mapping.DefaultLinkGenerator
import grails.web.mapping.LinkGenerator
import grails.web.mapping.UrlMappings
import grails.web.mapping.UrlMappingsHolder
import org.grails.web.mapping.UrlMappingsHolderFactoryBean
import org.grails.web.mapping.mvc.UrlMappingsHandlerMapping
import org.grails.web.mapping.mvc.UrlMappingsInfoHandlerAdapter
import org.grails.web.mapping.servlet.UrlMappingsErrorPageCustomizer
import org.springframework.aop.framework.ProxyFactoryBean
import org.springframework.aop.target.HotSwappableTargetSource
import org.springframework.context.ApplicationContext
Expand All @@ -61,31 +54,15 @@ class UrlMappingsGrailsPlugin extends Plugin {
}

def config = application.config
String serverURL = config.getProperty(Settings.SERVER_URL) ?: null
String urlConverterType = config.getProperty(Settings.WEB_URL_CONVERTER)
boolean isReloadEnabled = Environment.isDevelopmentMode() || Environment.current.isReloadEnabled()
boolean cacheUrls = config.getProperty(Settings.WEB_LINK_GENERATOR_USE_CACHE, Boolean, !isReloadEnabled)

"${grails.web.UrlConverter.BEAN_NAME}"('hyphenated' == urlConverterType ? HyphenatedUrlConverter : CamelCaseUrlConverter)

boolean corsFilterEnabled = config.getProperty(Settings.SETTING_CORS_FILTER, Boolean, true)

grailsCorsConfiguration(GrailsCorsConfiguration)

urlMappingsHandlerMapping(UrlMappingsHandlerMapping, ref("grailsUrlMappingsHolder")) {
if (!corsFilterEnabled) {
grailsCorsConfiguration = ref("grailsCorsConfiguration")
}
}

if (corsFilterEnabled) {
grailsCorsFilter(GrailsCorsFilter, ref("grailsCorsConfiguration"))
}

urlMappingsInfoHandlerAdapter(UrlMappingsInfoHandlerAdapter)
urlMappingsErrorPageCustomizer(UrlMappingsErrorPageCustomizer)
grailsLinkGenerator(cacheUrls ? CachingLinkGenerator : DefaultLinkGenerator, serverURL)

if (isReloadEnabled) {
urlMappingsTargetSource(HotSwappableTargetSourceFactoryBean) {
it.lazyInit = true
Expand All @@ -98,7 +75,7 @@ class UrlMappingsGrailsPlugin extends Plugin {
targetSource = urlMappingsTargetSource
proxyInterfaces = [UrlMappings]
}
} else {
} else {
grailsUrlMappingsHolder(UrlMappingsHolderFactoryBean) { bean ->
bean.lazyInit = true
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.grails.plugins.web.mapping.UrlMappingsAutoConfiguration
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ package grails.boot
import grails.artefact.Artefact
import grails.boot.config.GrailsAutoConfiguration
import grails.web.Controller
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.EnableWebMvc
import spock.lang.Specification

/**
Expand All @@ -32,8 +31,7 @@ class EmbeddedContainerWithGrailsSpec extends Specification {
new URL("http://localhost:${context.webServer.port}/foos").text == 'all foos'
}

@Configuration
@EnableWebMvc
@SpringBootApplication
static class Application extends GrailsAutoConfiguration {
@Bean
ConfigurableServletWebServerFactory webServerFactory() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ package grails.boot

import grails.boot.config.GrailsAutoConfiguration
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory
import org.springframework.context.ConfigurableApplicationContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.EnableWebMvc
import spock.lang.Specification

/**
Expand All @@ -34,8 +32,7 @@ class GrailsSpringApplicationSpec extends Specification{
}


@Configuration
@EnableWebMvc
@SpringBootApplication
static class Application extends GrailsAutoConfiguration {
@Bean
ConfigurableServletWebServerFactory webServerFactory() {
Expand Down

0 comments on commit 10293ad

Please sign in to comment.