Skip to content

Commit b6a690f

Browse files
committed
Support rendering GSON templates via controllers. Fixes #9593
1 parent dfcd573 commit b6a690f

File tree

9 files changed

+114
-33
lines changed

9 files changed

+114
-33
lines changed

grails-plugin-controllers/src/main/groovy/grails/artefact/controller/support/ResponseRenderer.groovy

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,18 @@ import grails.web.http.HttpHeaders
3030
import grails.web.mime.MimeType
3131
import grails.web.mime.MimeUtility
3232
import groovy.json.StreamingJsonBuilder
33-
import groovy.text.Template
3433
import groovy.transform.CompileStatic
3534
import groovy.util.slurpersupport.GPathResult
3635
import groovy.xml.StreamingMarkupBuilder
3736
import org.codehaus.groovy.grails.web.metaclass.ControllerDynamicMethods
3837
import org.grails.gsp.GroovyPageTemplate
39-
import org.grails.gsp.ResourceAwareTemplateEngine
40-
import org.grails.io.support.GrailsResourceUtils
4138
import org.grails.io.support.SpringIOUtils
4239
import org.grails.web.converters.Converter
4340
import org.grails.web.json.JSONElement
4441
import org.grails.web.servlet.mvc.ActionResultTransformer
4542
import org.grails.web.servlet.mvc.GrailsWebRequest
4643
import org.grails.web.servlet.mvc.exceptions.ControllerExecutionException
44+
import org.grails.web.servlet.view.CompositeViewResolver
4745
import org.grails.web.servlet.view.GroovyPageView
4846
import org.grails.web.sitemesh.GrailsLayoutDecoratorMapper
4947
import org.grails.web.sitemesh.GrailsLayoutView
@@ -300,46 +298,32 @@ trait ResponseRenderer extends WebAttributes {
300298
modelObject = argMap[ARGUMENT_MODEL]
301299
}
302300
String templateName = argMap[ARGUMENT_TEMPLATE].toString()
303-
String contextPath = getContextPath(webRequest, argMap)
304-
305301
String var
306302
if (argMap.containsKey(ARGUMENT_VAR)) {
307303
var = String.valueOf( argMap[ARGUMENT_VAR] )
308304
}
309305

310306
// get the template uri
311-
String templateUri = applicationAttributes.getTemplateURI((GroovyObject)this, templateName)
307+
String templateUri = applicationAttributes.getTemplateURI((GroovyObject)this, templateName, false)
312308

313-
// retrieve gsp engine
314-
ResourceAwareTemplateEngine engine = applicationAttributes.getPagesTemplateEngine()
309+
// retrieve view resolver
310+
def applicationContext = applicationAttributes.getApplicationContext()
311+
def viewResolver = applicationContext.getBean(CompositeViewResolver.BEAN_NAME, CompositeViewResolver)
315312
try {
316-
Template t = engine.createTemplateForUri([
317-
GrailsResourceUtils.appendPiecesForUri(contextPath, templateUri),
318-
GrailsResourceUtils.appendPiecesForUri(contextPath, "/grails-app/views/", templateUri)] as String[])
319-
320-
if (t == null) {
321-
throw new ControllerExecutionException("Unable to load template for uri [$templateUri]. Template not found.")
322-
}
323313

324-
if (t instanceof GroovyPageTemplate) {
325-
((GroovyPageTemplate)t).allowSettingContentType = true
314+
View view = viewResolver.resolveView(templateUri, webRequest.locale)
315+
if(view instanceof GroovyPageView) {
316+
((GroovyPageTemplate)((GroovyPageView)view).template).allowSettingContentType = true
326317
}
327-
328-
def gspView = new GroovyPageView()
329-
gspView.template = t
330-
331-
try {
332-
gspView.afterPropertiesSet()
333-
} catch (Exception e) {
334-
throw new RuntimeException("Problem initializing view", e)
318+
if (view == null) {
319+
throw new ControllerExecutionException("Unable to load template for uri [$templateUri]. Template not found.")
335320
}
336321

337-
View view = gspView
338322
boolean renderWithLayout = (explicitSiteMeshLayout || webRequest.getCurrentRequest().getAttribute(GrailsLayoutDecoratorMapper.LAYOUT_ATTRIBUTE))
339323
if(renderWithLayout && groovyPageLayoutFinder) {
340324
applySiteMeshLayout webRequest.currentRequest, false, explicitSiteMeshLayout
341325
try {
342-
view = new GrailsLayoutView(groovyPageLayoutFinder, gspView)
326+
view = new GrailsLayoutView(groovyPageLayoutFinder, view)
343327
} catch (NoSuchBeanDefinitionException e) {
344328
// ignore
345329
}

grails-plugin-controllers/src/main/groovy/org/grails/plugins/web/controllers/ControllersGrailsPlugin.groovy

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import org.grails.web.mapping.mvc.UrlMappingsInfoHandlerAdapter
3131
import org.grails.web.servlet.mvc.GrailsDispatcherServlet
3232
import org.grails.web.servlet.mvc.GrailsWebRequestFilter
3333
import org.grails.web.servlet.mvc.TokenResponseActionResultTransformer
34+
import org.grails.web.servlet.view.CompositeViewResolver
3435
import org.springframework.beans.factory.support.AbstractBeanDefinition
3536
import org.springframework.boot.context.embedded.FilterRegistrationBean
3637
import org.springframework.boot.context.embedded.ServletRegistrationBean
@@ -123,6 +124,9 @@ class ControllersGrailsPlugin extends Plugin {
123124
}
124125

125126
multipartResolver(StandardServletMultipartResolver)
127+
128+
"${CompositeViewResolver.BEAN_NAME}"(CompositeViewResolver)
129+
126130
multipartConfigElement(MultipartConfigElement, uploadTmpDir, maxFileSize, maxRequestSize, fileSizeThreashold)
127131

128132
def handlerInterceptors = springConfig.containsBean("localeChangeInterceptor") ? [ref("localeChangeInterceptor")] : []

grails-plugin-gsp/src/main/groovy/org/grails/plugins/web/GroovyPagesGrailsPlugin.groovy

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,6 @@ class GroovyPagesGrailsPlugin extends Plugin {
263263
grailsLayoutViewResolverPostProcessor(GrailsLayoutViewResolverPostProcessor)
264264
}
265265

266-
final pluginManager = manager
267266
// Now go through tag libraries and configure them in Spring too. With AOP proxies and so on
268267
for (taglib in application.tagLibClasses) {
269268

grails-plugin-testing/src/main/groovy/grails/test/runtime/ControllerTestPlugin.groovy

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ import grails.web.CamelCaseUrlConverter
2626
import grails.web.HyphenatedUrlConverter
2727
import groovy.transform.CompileStatic
2828
import groovy.transform.TypeCheckingMode
29+
import org.grails.web.servlet.view.CompositeViewResolver
30+
import org.grails.web.servlet.view.GroovyPageViewResolver
31+
import org.grails.web.util.GrailsApplicationAttributes
2932

3033
import javax.servlet.ServletContext
3134

@@ -92,11 +95,14 @@ class ControllerTestPlugin implements TestPlugin {
9295
grailsApplication.addArtefact(UrlMappingsArtefactHandler.TYPE, classLoader.loadClass(urlMappingsClass))
9396
}
9497

98+
localeResolver(SessionLocaleResolver)
9599
multipartResolver(StandardServletMultipartResolver)
96100
grailsUrlMappingsHolder(UrlMappingsHolderFactoryBean) {
97101
grailsApplication = grailsApplication
98102
}
99103

104+
"${CompositeViewResolver.BEAN_NAME}"(CompositeViewResolver)
105+
100106
if(ClassUtils.isPresent("org.grails.plugins.web.GroovyPagesGrailsPlugin", classLoader)) {
101107
def lazyBean = { bean ->
102108
bean.lazyInit = true
@@ -120,6 +126,13 @@ class ControllerTestPlugin implements TestPlugin {
120126
groovyPagesTemplateEngine = ref("groovyPagesTemplateEngine")
121127
}
122128

129+
// Configure a Spring MVC view resolver
130+
jspViewResolver(GroovyPageViewResolver) { bean ->
131+
prefix = GrailsApplicationAttributes.PATH_TO_VIEWS
132+
suffix = GroovyPageViewResolver.GSP_SUFFIX
133+
templateEngine = groovyPagesTemplateEngine
134+
groovyPageLocator = groovyPageLocator
135+
}
123136
}
124137
filteringCodecsByContentTypeSettings(FilteringCodecsByContentTypeSettings, grailsApplication)
125138

grails-web-common/src/main/groovy/grails/web/pages/GroovyPagesUriService.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ public interface GroovyPagesUriService {
4646

4747
void clear();
4848

49+
String getTemplateURI(String controllerName, String templateName, boolean includeExtension);
50+
4951
String getAbsoluteTemplateURI(String templateName);
5052

5153
String getAbsoluteViewURI(String viewName);
@@ -60,4 +62,6 @@ public interface GroovyPagesUriService {
6062
* @return The view URI
6163
*/
6264
String getViewURI(String controllerName, String viewName);
65+
66+
String getTemplateURI(GroovyObject controller, String templateName, boolean includeExtension);
6367
}

grails-web-common/src/main/groovy/org/grails/web/pages/GroovyPagesUriSupport.java

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ public String getTemplateURI(GroovyObject controller, String templateName) {
5050
return getTemplateURI(getLogicalControllerName(controller),templateName);
5151
}
5252

53+
@Override
54+
public String getTemplateURI(GroovyObject controller, String templateName, boolean includeExtension) {
55+
return getTemplateURI(getLogicalControllerName(controller),templateName, includeExtension);
56+
}
57+
5358
public void clear() {
5459
// do nothing
5560
}
@@ -98,6 +103,16 @@ public String getLogicalControllerName(GroovyObject controller) {
98103
* @return The template URI
99104
*/
100105
public String getTemplateURI(String controllerName, String templateName) {
106+
return getTemplateURI(controllerName, templateName, true);
107+
}
108+
/**
109+
* Obtains the URI to a template using the controller name and template name
110+
* @param controllerName The controller name
111+
* @param templateName The template name
112+
* @return The template URI
113+
*/
114+
@Override
115+
public String getTemplateURI(String controllerName, String templateName, boolean includeExtension) {
101116
if (templateName.startsWith(SLASH_STR)) {
102117
return getAbsoluteTemplateURI(templateName);
103118
}
@@ -112,15 +127,22 @@ public String getTemplateURI(String controllerName, String templateName) {
112127
}
113128
if(controllerName != null) {
114129
buf.append(SLASH)
115-
.append(controllerName);
130+
.append(controllerName);
116131
}
117132
buf.append(SLASH)
118-
.append(pathToTemplate)
119-
.append(UNDERSCORE)
120-
.append(templateName);
121-
return buf.append(EXTENSION).toString();
133+
.append(pathToTemplate)
134+
.append(UNDERSCORE)
135+
.append(templateName);
136+
137+
if(includeExtension) {
138+
return buf.append(EXTENSION).toString();
139+
}
140+
else {
141+
return buf.toString();
142+
}
122143
}
123144

145+
124146
/**
125147
* Used to resolve template names that are not relative to a controller.
126148
*

grails-web-common/src/main/groovy/org/grails/web/servlet/DefaultGrailsApplicationAttributes.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,11 @@ public String getTemplateURI(GroovyObject controller, String templateName) {
242242
return getGroovyPagesUriService().getTemplateURI(controller, templateName);
243243
}
244244

245+
@Override
246+
public String getTemplateURI(GroovyObject controller, String templateName, boolean includeExtension) {
247+
return getGroovyPagesUriService().getTemplateURI(controller, templateName, includeExtension);
248+
}
249+
245250
public GroovyPagesUriService getGroovyPagesUriService() {
246251
if (groovyPagesUriService == null) {
247252
groovyPagesUriService = fetchBeanFromAppCtx(GroovyPagesUriService.BEAN_ID);
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package org.grails.web.servlet.view
2+
3+
import groovy.transform.CompileStatic
4+
import org.springframework.beans.factory.annotation.Autowired
5+
import org.springframework.web.servlet.View
6+
import org.springframework.web.servlet.ViewResolver
7+
8+
/**
9+
* Performs the job of iterating across registered view resolvers and returning the first matching view similar to the
10+
* hard coded behavior in DispatcherServlet
11+
*
12+
* @author Graeme Rocher
13+
* @since 3.1.1
14+
*/
15+
@CompileStatic
16+
class CompositeViewResolver {
17+
18+
public static final String BEAN_NAME = "compositeViewResolver"
19+
20+
@Autowired(required = false)
21+
List<ViewResolver> viewResolvers = []
22+
23+
View resolveView(String viewName, Locale locale) {
24+
for(resolver in viewResolvers) {
25+
26+
def view = resolver.resolveViewName(viewName, locale)
27+
if(view != null) {
28+
return view
29+
}
30+
}
31+
return null
32+
}
33+
}

grails-web-common/src/main/groovy/org/grails/web/util/GrailsApplicationAttributes.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,25 @@ public interface GrailsApplicationAttributes extends ApplicationAttributes, org.
9999
*/
100100
String getApplicationUri(ServletRequest request);
101101

102+
/**
103+
* Resolve the URI for a template
104+
*
105+
* @param controller The controller
106+
* @param templateName The name of the template
107+
* @return The template name
108+
*/
102109
String getTemplateURI(GroovyObject controller, String templateName);
103110

111+
/**
112+
* Resolve the URI for a template
113+
*
114+
* @param controller The controller
115+
* @param templateName The name of the template
116+
* @param includeExtension Whether to include the GSP etension
117+
* @return The template name
118+
*/
119+
String getTemplateURI(GroovyObject controller, String templateName, boolean includeExtension);
120+
104121
String getNoSuffixViewURI(GroovyObject controller, String viewName);
105122

106123
/**

0 commit comments

Comments
 (0)