SpringWebThymeleafContextConverter.java

1
package fr.sii.ogham.spring.template.thymeleaf;
2
3
import java.lang.reflect.Field;
4
import java.util.HashMap;
5
import java.util.Map;
6
7
8
import org.springframework.context.ApplicationContext;
9
import org.springframework.expression.EvaluationContext;
10
import org.springframework.web.context.request.RequestContextHolder;
11
import org.springframework.web.servlet.View;
12
import org.springframework.web.servlet.support.RequestContext;
13
import org.springframework.web.servlet.view.AbstractTemplateView;
14
import org.thymeleaf.context.IContext;
15
16
import fr.sii.ogham.core.exception.template.ContextException;
17
import fr.sii.ogham.spring.util.compat.HttpServletRequestWrapper;
18
import fr.sii.ogham.spring.util.compat.HttpServletResponseWrapper;
19
import fr.sii.ogham.spring.util.compat.ServletContextWrapper;
20
import fr.sii.ogham.template.thymeleaf.common.ThymeleafContextConverter;
21
22
/**
23
 * Specific context converter for Spring that registers static variables and
24
 * {@link EvaluationContext} for SpEL expressions.
25
 *
26
 * The aim is to provide the same support as if user was using Spring in web
27
 * context (access to Spring beans from templates, be able to use static
28
 * variables, ...).
29
 *
30
 * @author Aurélien Baudet
31
 *
32
 */
33
public class SpringWebThymeleafContextConverter implements ThymeleafContextConverter {
34
	private final ThymeleafContextConverter delegate;
35
	private final String springRequestContextVariableName;
36
	private final ApplicationContext applicationContext;
37
	private final WebContextProvider webContextProvider;
38
	private final ThymeleafRequestContextWrapper thymeleafRequestContextWrapper;
39
	private final ThymeleafWebContextProvider thymeleafWebContextProvider;
40
	private final ContextMerger contextMerger;
41
	private final ServletRequestContextProvider requestContextProvider;
42
43
	public SpringWebThymeleafContextConverter(ThymeleafContextConverter delegate, String springRequestContextVariableName, ApplicationContext applicationContext, WebContextProvider webContextProvider,
44
											  ThymeleafRequestContextWrapper thymeleafRequestContextWrapper, ThymeleafWebContextProvider thymeleafWebContextProvider, ContextMerger contextMerger,
45
											  ServletRequestContextProvider requestContextProvider) {
46
		super();
47
		this.delegate = delegate;
48
		this.springRequestContextVariableName = springRequestContextVariableName;
49
		this.applicationContext = applicationContext;
50
		this.webContextProvider = webContextProvider;
51
		this.thymeleafRequestContextWrapper = thymeleafRequestContextWrapper;
52
		this.thymeleafWebContextProvider = thymeleafWebContextProvider;
53
		this.contextMerger = contextMerger;
54
		this.requestContextProvider = requestContextProvider;
55
	}
56
57
	/*
58
	 * If this is not null, we are using Spring 3.1+ and there is the
59
	 * possibility to automatically add @PathVariable's to models. This will be
60
	 * computed at class initialization time.
61
	 */
62
	private static final String pathVariablesSelector;
63
64
	static {
65
66
		/*
67
		 * Compute whether we can obtain @PathVariable's from the request and
68
		 * add them automatically to the model (Spring 3.1+)
69
		 */
70
71
		String pathVariablesSelectorValue = null;
72
		try {
73
			// We are looking for the value of the View.PATH_VARIABLES constant,
74
			// which is a String
75
			final Field pathVariablesField = View.class.getDeclaredField("PATH_VARIABLES");
76
			pathVariablesSelectorValue = (String) pathVariablesField.get(null);
77
		} catch (final NoSuchFieldException | IllegalAccessException ignored) {
78
			pathVariablesSelectorValue = null;
79
		}
80
		pathVariablesSelector = pathVariablesSelectorValue;
81
	}
82
83
	@Override
84
	public IContext convert(fr.sii.ogham.core.template.context.Context context) throws ContextException {
85
		IContext base = delegate.convert(context);
86
87
		// the web context may be lost due to @Async method call
88 1 1. convert : negated conditional → RUN_ERROR
		if (isAsyncCall()) {
89 1 1. convert : replaced return value with null for fr/sii/ogham/spring/template/thymeleaf/SpringWebThymeleafContextConverter::convert → RUN_ERROR
			return base;
90
		}
91
92
		// partially borrowed from org.thymeleaf.spring5.view.ThymeleafView
93
		final Map<String, Object> springModel = new HashMap<>(30);
94
95
		HttpServletRequestWrapper request = webContextProvider.getRequest(context);
96
		HttpServletResponseWrapper response = webContextProvider.getResponse(context);
97
		ServletContextWrapper servletContext = webContextProvider.getServletContext(context);
98
99 1 1. convert : negated conditional → RUN_ERROR
		if (pathVariablesSelector != null) {
100
			@SuppressWarnings("unchecked")
101
			final Map<String, Object> pathVars = (Map<String, Object>) request.getAttribute(pathVariablesSelector);
102 1 1. convert : negated conditional → RUN_ERROR
			if (pathVars != null) {
103 1 1. convert : removed call to java/util/Map::putAll → NO_COVERAGE
				springModel.putAll(pathVars);
104
			}
105
		}
106
107
		final RequestContext requestContext = requestContextProvider.getRequestContext(request, response, servletContext, springModel);
108
109
		// For compatibility with ThymeleafView
110 1 1. convert : removed call to fr/sii/ogham/spring/template/thymeleaf/SpringWebThymeleafContextConverter::addRequestContextAsVariable → RUN_ERROR
		addRequestContextAsVariable(springModel, springRequestContextVariableName, requestContext);
111
		// For compatibility with AbstractTemplateView
112 1 1. convert : removed call to fr/sii/ogham/spring/template/thymeleaf/SpringWebThymeleafContextConverter::addRequestContextAsVariable → RUN_ERROR
		addRequestContextAsVariable(springModel, AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE, requestContext);
113
114 1 1. convert : removed call to fr/sii/ogham/spring/template/thymeleaf/ThymeleafRequestContextWrapper::wrapAndRegister → RUN_ERROR
		thymeleafRequestContextWrapper.wrapAndRegister(requestContext, request, response, servletContext, springModel);
115
116 1 1. convert : replaced return value with null for fr/sii/ogham/spring/template/thymeleaf/SpringWebThymeleafContextConverter::convert → RUN_ERROR
		return contextMerger.merge(thymeleafWebContextProvider.getWebContext(context, base, request, response, servletContext, applicationContext, springModel), base);
117
	}
118
119
	private boolean isAsyncCall() {
120
		try {
121
			RequestContextHolder.currentRequestAttributes();
122 1 1. isAsyncCall : replaced boolean return with true for fr/sii/ogham/spring/template/thymeleaf/SpringWebThymeleafContextConverter::isAsyncCall → RUN_ERROR
			return false;
123
		} catch(IllegalStateException e) {
124 1 1. isAsyncCall : replaced boolean return with false for fr/sii/ogham/spring/template/thymeleaf/SpringWebThymeleafContextConverter::isAsyncCall → RUN_ERROR
			return true;
125
		}
126
	}
127
128
	protected static void addRequestContextAsVariable(final Map<String, Object> model, final String variableName, final RequestContext requestContext) throws ContextException {
129
130 1 1. addRequestContextAsVariable : negated conditional → RUN_ERROR
		if (model.containsKey(variableName)) {
131
			throw new ContextException(new RuntimeException("Cannot expose request context in model attribute '" + variableName + "' because of an existing model object of the same name"));
132
		}
133
		model.put(variableName, requestContext);
134
135
	}
136
137
}

Mutations

88

1.1
Location : convert
Killed by :
negated conditional → RUN_ERROR

89

1.1
Location : convert
Killed by :
replaced return value with null for fr/sii/ogham/spring/template/thymeleaf/SpringWebThymeleafContextConverter::convert → RUN_ERROR

99

1.1
Location : convert
Killed by :
negated conditional → RUN_ERROR

102

1.1
Location : convert
Killed by :
negated conditional → RUN_ERROR

103

1.1
Location : convert
Killed by :
removed call to java/util/Map::putAll → NO_COVERAGE

110

1.1
Location : convert
Killed by :
removed call to fr/sii/ogham/spring/template/thymeleaf/SpringWebThymeleafContextConverter::addRequestContextAsVariable → RUN_ERROR

112

1.1
Location : convert
Killed by :
removed call to fr/sii/ogham/spring/template/thymeleaf/SpringWebThymeleafContextConverter::addRequestContextAsVariable → RUN_ERROR

114

1.1
Location : convert
Killed by :
removed call to fr/sii/ogham/spring/template/thymeleaf/ThymeleafRequestContextWrapper::wrapAndRegister → RUN_ERROR

116

1.1
Location : convert
Killed by :
replaced return value with null for fr/sii/ogham/spring/template/thymeleaf/SpringWebThymeleafContextConverter::convert → RUN_ERROR

122

1.1
Location : isAsyncCall
Killed by :
replaced boolean return with true for fr/sii/ogham/spring/template/thymeleaf/SpringWebThymeleafContextConverter::isAsyncCall → RUN_ERROR

124

1.1
Location : isAsyncCall
Killed by :
replaced boolean return with false for fr/sii/ogham/spring/template/thymeleaf/SpringWebThymeleafContextConverter::isAsyncCall → RUN_ERROR

130

1.1
Location : addRequestContextAsVariable
Killed by :
negated conditional → RUN_ERROR

Active mutators

Tests examined


Report generated by PIT 1.13.1