1 /*
2  * Copyright 2002-2010 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package org.springframework.web.servlet.view.velocity;
18 
19 import static org.easymock.EasyMock.createMock;
20 import static org.easymock.EasyMock.expectLastCall;
21 import static org.easymock.EasyMock.replay;
22 import static org.easymock.EasyMock.verify;
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.assertNotNull;
25 import static org.junit.Assert.assertTrue;
26 import static org.junit.Assert.fail;
27 
28 import java.io.IOException;
29 import java.util.HashMap;
30 import java.util.Locale;
31 import java.util.Map;
32 
33 import javax.servlet.http.HttpServletRequest;
34 import javax.servlet.http.HttpServletResponse;
35 
36 import org.apache.velocity.Template;
37 import org.apache.velocity.app.VelocityEngine;
38 import org.apache.velocity.context.Context;
39 import org.apache.velocity.exception.MethodInvocationException;
40 import org.apache.velocity.exception.ParseErrorException;
41 import org.apache.velocity.tools.generic.DateTool;
42 import org.apache.velocity.tools.generic.MathTool;
43 import org.apache.velocity.tools.generic.NumberTool;
44 import org.junit.Test;
45 import org.springframework.context.ApplicationContextException;
46 import org.springframework.mock.web.MockHttpServletRequest;
47 import org.springframework.mock.web.MockHttpServletResponse;
48 import org.springframework.mock.web.MockServletContext;
49 import org.springframework.web.context.WebApplicationContext;
50 import org.springframework.web.servlet.DispatcherServlet;
51 import org.springframework.web.servlet.View;
52 import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
53 import org.springframework.web.servlet.support.RequestDataValueProcessor;
54 import org.springframework.web.servlet.view.AbstractView;
55 
56 /**
57  * @author Rod Johnson
58  * @author Juergen Hoeller
59  * @author Dave Syer
60  */
61 public class VelocityViewTests {
62 
63 	@Test
testNoVelocityConfig()64 	public void testNoVelocityConfig() throws Exception {
65 		VelocityView vv = new VelocityView();
66 		WebApplicationContext wac = createMock(WebApplicationContext.class);
67 		wac.getBeansOfType(VelocityConfig.class, true, false);
68 		expectLastCall().andReturn(new HashMap<String, Object>());
69 		wac.getParentBeanFactory();
70 		expectLastCall().andReturn(null);
71 		replay(wac);
72 
73 		vv.setUrl("anythingButNull");
74 		try {
75 			vv.setApplicationContext(wac);
76 			fail();
77 		}
78 		catch (ApplicationContextException ex) {
79 			// Check there's a helpful error message
80 			assertTrue(ex.getMessage().contains("VelocityConfig"));
81 		}
82 
83 		verify(wac);
84 	}
85 
86 	@Test
testNoTemplateName()87 	public void testNoTemplateName() throws Exception {
88 		VelocityView vv = new VelocityView();
89 		try {
90 			vv.afterPropertiesSet();
91 			fail("Should have thrown IllegalArgumentException");
92 		}
93 		catch (IllegalArgumentException ex) {
94 			// Check there's a helpful error message
95 			assertTrue(ex.getMessage().indexOf("url") != -1);
96 		}
97 	}
98 
99 	@Test
testMergeTemplateSucceeds()100 	public void testMergeTemplateSucceeds() throws Exception {
101 		testValidTemplateName(null);
102 	}
103 
104 	@Test
testMergeTemplateFailureWithIOException()105 	public void testMergeTemplateFailureWithIOException() throws Exception {
106 		testValidTemplateName(new IOException());
107 	}
108 
109 	@Test
testMergeTemplateFailureWithParseErrorException()110 	public void testMergeTemplateFailureWithParseErrorException() throws Exception {
111 		testValidTemplateName(new ParseErrorException(""));
112 	}
113 
114 	@Test
testMergeTemplateFailureWithUnspecifiedException()115 	public void testMergeTemplateFailureWithUnspecifiedException() throws Exception {
116 		testValidTemplateName(new Exception(""));
117 	}
118 
119 	@Test
testMergeTemplateFailureWithMethodInvocationException()120 	public void testMergeTemplateFailureWithMethodInvocationException() throws Exception {
121 		testValidTemplateName(new MethodInvocationException("Bad template", null, "none", "foo.vm", 1, 100));
122 	}
123 
124 	/**
125 	 * @param mergeTemplateFailureException may be null in which case mergeTemplate override will succeed.
126 	 * If it's non null it will be checked
127 	 */
testValidTemplateName(final Exception mergeTemplateFailureException)128 	private void testValidTemplateName(final Exception mergeTemplateFailureException) throws Exception {
129 		Map<String, Object> model = new HashMap<String, Object>();
130 		model.put("foo", "bar");
131 
132 		final String templateName = "test.vm";
133 
134 		WebApplicationContext wac = createMock(WebApplicationContext.class);
135 		MockServletContext sc = new MockServletContext();
136 		sc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);
137 
138 		final Template expectedTemplate = new Template();
139 		VelocityConfig vc = new VelocityConfig() {
140 			public VelocityEngine getVelocityEngine() {
141 				return new TestVelocityEngine(templateName, expectedTemplate);
142 			}
143 		};
144 		wac.getBeansOfType(VelocityConfig.class, true, false);
145 		Map<String, Object> configurers = new HashMap<String, Object>();
146 		configurers.put("velocityConfigurer", vc);
147 		expectLastCall().andReturn(configurers);
148 		wac.getParentBeanFactory();
149 		expectLastCall().andReturn(null);
150 		wac.getServletContext();
151 		expectLastCall().andReturn(sc).times(3);
152 		wac.getBean("requestDataValueProcessor", RequestDataValueProcessor.class);
153 		expectLastCall().andReturn(null);
154 		replay(wac);
155 
156 		HttpServletRequest request = new MockHttpServletRequest();
157 		final HttpServletResponse expectedResponse = new MockHttpServletResponse();
158 
159 		VelocityView vv = new VelocityView() {
160 			protected void mergeTemplate(Template template, Context context, HttpServletResponse response) throws Exception {
161 				assertTrue(template == expectedTemplate);
162 				assertTrue(context.getKeys().length >= 1);
163 				assertTrue(context.get("foo").equals("bar"));
164 				assertTrue(response == expectedResponse);
165 				if (mergeTemplateFailureException != null) {
166 					throw mergeTemplateFailureException;
167 				}
168 			}
169 		};
170 		vv.setUrl(templateName);
171 		vv.setApplicationContext(wac);
172 
173 		try {
174 			vv.render(model, request, expectedResponse);
175 			if (mergeTemplateFailureException != null) {
176 				fail();
177 			}
178 		}
179 		catch (Exception ex) {
180 			assertNotNull(mergeTemplateFailureException);
181 			assertEquals(ex, mergeTemplateFailureException);
182 		}
183 
184 		verify(wac);
185 	}
186 
187 	@Test
testKeepExistingContentType()188 	public void testKeepExistingContentType() throws Exception {
189 		final String templateName = "test.vm";
190 
191 		WebApplicationContext wac = createMock(WebApplicationContext.class);
192 		MockServletContext sc = new MockServletContext();
193 		sc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);
194 
195 		final Template expectedTemplate = new Template();
196 		VelocityConfig vc = new VelocityConfig() {
197 			public VelocityEngine getVelocityEngine() {
198 				return new TestVelocityEngine(templateName, expectedTemplate);
199 			}
200 		};
201 		wac.getBeansOfType(VelocityConfig.class, true, false);
202 		Map<String, Object> configurers = new HashMap<String, Object>();
203 		configurers.put("velocityConfigurer", vc);
204 		expectLastCall().andReturn(configurers);
205 		wac.getParentBeanFactory();
206 		expectLastCall().andReturn(null);
207 		wac.getServletContext();
208 		expectLastCall().andReturn(sc).times(3);
209 		wac.getBean("requestDataValueProcessor", RequestDataValueProcessor.class);
210 		expectLastCall().andReturn(null);
211 		replay(wac);
212 
213 		HttpServletRequest request = new MockHttpServletRequest();
214 		final HttpServletResponse expectedResponse = new MockHttpServletResponse();
215 		expectedResponse.setContentType("myContentType");
216 
217 		VelocityView vv = new VelocityView() {
218 			protected void mergeTemplate(Template template, Context context, HttpServletResponse response) {
219 				assertTrue(template == expectedTemplate);
220 				assertTrue(response == expectedResponse);
221 			}
222 			protected void exposeHelpers(Map<String, Object> model, HttpServletRequest request) throws Exception {
223 				model.put("myHelper", "myValue");
224 			}
225 		};
226 
227 		vv.setUrl(templateName);
228 		vv.setApplicationContext(wac);
229 		vv.render(new HashMap<String, Object>(), request, expectedResponse);
230 
231 		verify(wac);
232 		assertEquals("myContentType", expectedResponse.getContentType());
233 	}
234 
235 	@Test
testExposeHelpers()236 	public void testExposeHelpers() throws Exception {
237 		final String templateName = "test.vm";
238 
239 		WebApplicationContext wac = createMock(WebApplicationContext.class);
240 		wac.getParentBeanFactory();
241 		expectLastCall().andReturn(null);
242 		wac.getServletContext();
243 		expectLastCall().andReturn(new MockServletContext());
244 
245 		final Template expectedTemplate = new Template();
246 		VelocityConfig vc = new VelocityConfig() {
247 			public VelocityEngine getVelocityEngine() {
248 				return new TestVelocityEngine(templateName, expectedTemplate);
249 			}
250 		};
251 		wac.getBeansOfType(VelocityConfig.class, true, false);
252 		Map<String, Object> configurers = new HashMap<String, Object>();
253 		configurers.put("velocityConfigurer", vc);
254 		expectLastCall().andReturn(configurers);
255 		replay(wac);
256 
257 
258 		// let it ask for locale
259 		HttpServletRequest req = createMock(HttpServletRequest.class);
260 		req.getAttribute(View.PATH_VARIABLES);
261 		expectLastCall().andReturn(null);
262 		req.getAttribute(DispatcherServlet.LOCALE_RESOLVER_ATTRIBUTE);
263 		expectLastCall().andReturn(new AcceptHeaderLocaleResolver());
264 		req.getLocale();
265 		expectLastCall().andReturn(Locale.CANADA);
266 		replay(req);
267 
268 		final HttpServletResponse expectedResponse = new MockHttpServletResponse();
269 
270 		VelocityView vv = new VelocityView() {
271 			protected void mergeTemplate(Template template, Context context, HttpServletResponse response) throws Exception {
272 				assertTrue(template == expectedTemplate);
273 				assertTrue(response == expectedResponse);
274 
275 				assertEquals("myValue", context.get("myHelper"));
276 				assertTrue(context.get("math") instanceof MathTool);
277 
278 				assertTrue(context.get("dateTool") instanceof DateTool);
279 				DateTool dateTool = (DateTool) context.get("dateTool");
280 				assertTrue(dateTool.getLocale().equals(Locale.CANADA));
281 
282 				assertTrue(context.get("numberTool") instanceof NumberTool);
283 				NumberTool numberTool = (NumberTool) context.get("numberTool");
284 				assertTrue(numberTool.getLocale().equals(Locale.CANADA));
285 			}
286 
287 			protected void exposeHelpers(Map<String, Object> model, HttpServletRequest request) throws Exception {
288 				model.put("myHelper", "myValue");
289 			}
290 		};
291 
292 		vv.setUrl(templateName);
293 		vv.setApplicationContext(wac);
294 		Map<String, Class> toolAttributes = new HashMap<String, Class>();
295 		toolAttributes.put("math", MathTool.class);
296 		vv.setToolAttributes(toolAttributes);
297 		vv.setDateToolAttribute("dateTool");
298 		vv.setNumberToolAttribute("numberTool");
299 		vv.setExposeSpringMacroHelpers(false);
300 
301 		vv.render(new HashMap<String, Object>(), req, expectedResponse);
302 
303 		verify(wac);
304 		verify(req);
305 		assertEquals(AbstractView.DEFAULT_CONTENT_TYPE, expectedResponse.getContentType());
306 	}
307 
308 }
309