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.context.expression;
18 
19 import java.io.Serializable;
20 import java.security.AccessControlException;
21 import java.security.Permission;
22 import java.util.Properties;
23 
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import static org.junit.Assert.*;
27 import org.junit.Test;
28 
29 import org.springframework.beans.TestBean;
30 import org.springframework.beans.factory.ObjectFactory;
31 import org.springframework.beans.factory.annotation.Autowired;
32 import org.springframework.beans.factory.annotation.Qualifier;
33 import org.springframework.beans.factory.annotation.Value;
34 import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
35 import org.springframework.beans.factory.config.Scope;
36 import org.springframework.beans.factory.config.TypedStringValue;
37 import org.springframework.beans.factory.support.AutowireCandidateQualifier;
38 import org.springframework.beans.factory.support.DefaultListableBeanFactory;
39 import org.springframework.beans.factory.support.GenericBeanDefinition;
40 import org.springframework.beans.factory.support.RootBeanDefinition;
41 import org.springframework.context.annotation.AnnotationConfigUtils;
42 import org.springframework.context.support.GenericApplicationContext;
43 import org.springframework.util.SerializationTestUtils;
44 import org.springframework.util.StopWatch;
45 
46 /**
47  * @author Juergen Hoeller
48  * @since 3.0
49  */
50 public class ApplicationContextExpressionTests {
51 
52 	private static final Log factoryLog = LogFactory.getLog(DefaultListableBeanFactory.class);
53 
54 	@Test
genericApplicationContext()55 	public void genericApplicationContext() throws Exception {
56 		GenericApplicationContext ac = new GenericApplicationContext();
57 		AnnotationConfigUtils.registerAnnotationConfigProcessors(ac);
58 
59 		ac.getBeanFactory().registerScope("myScope", new Scope() {
60 			public Object get(String name, ObjectFactory objectFactory) {
61 				return objectFactory.getObject();
62 			}
63 			public Object remove(String name) {
64 				return null;
65 			}
66 			public void registerDestructionCallback(String name, Runnable callback) {
67 			}
68 			public Object resolveContextualObject(String key) {
69 				if (key.equals("mySpecialAttr")) {
70 					return "42";
71 				}
72 				else {
73 					return null;
74 				}
75 			}
76 			public String getConversationId() {
77 				return null;
78 			}
79 		});
80 
81 		PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
82 		Properties placeholders = new Properties();
83 		placeholders.setProperty("code", "123");
84 		ppc.setProperties(placeholders);
85 		ac.addBeanFactoryPostProcessor(ppc);
86 
87 		GenericBeanDefinition bd0 = new GenericBeanDefinition();
88 		bd0.setBeanClass(TestBean.class);
89 		bd0.getPropertyValues().add("name", "myName");
90 		bd0.addQualifier(new AutowireCandidateQualifier(Qualifier.class, "original"));
91 		ac.registerBeanDefinition("tb0", bd0);
92 
93 		GenericBeanDefinition bd1 = new GenericBeanDefinition();
94 		bd1.setBeanClass(TestBean.class);
95 		bd1.setScope("myScope");
96 		bd1.getConstructorArgumentValues().addGenericArgumentValue("XXX#{tb0.name}YYY#{mySpecialAttr}ZZZ");
97 		bd1.getConstructorArgumentValues().addGenericArgumentValue("#{mySpecialAttr}");
98 		ac.registerBeanDefinition("tb1", bd1);
99 
100 		GenericBeanDefinition bd2 = new GenericBeanDefinition();
101 		bd2.setBeanClass(TestBean.class);
102 		bd2.setScope("myScope");
103 		bd2.getPropertyValues().add("name", "{ XXX#{tb0.name}YYY#{mySpecialAttr}ZZZ }");
104 		bd2.getPropertyValues().add("age", "#{mySpecialAttr}");
105 		bd2.getPropertyValues().add("country", "${code} #{systemProperties.country}");
106 		ac.registerBeanDefinition("tb2", bd2);
107 
108 		GenericBeanDefinition bd3 = new GenericBeanDefinition();
109 		bd3.setBeanClass(ValueTestBean.class);
110 		bd3.setScope("myScope");
111 		ac.registerBeanDefinition("tb3", bd3);
112 
113 		GenericBeanDefinition bd4 = new GenericBeanDefinition();
114 		bd4.setBeanClass(ConstructorValueTestBean.class);
115 		bd4.setScope("myScope");
116 		ac.registerBeanDefinition("tb4", bd4);
117 
118 		GenericBeanDefinition bd5 = new GenericBeanDefinition();
119 		bd5.setBeanClass(MethodValueTestBean.class);
120 		bd5.setScope("myScope");
121 		ac.registerBeanDefinition("tb5", bd5);
122 
123 		GenericBeanDefinition bd6 = new GenericBeanDefinition();
124 		bd6.setBeanClass(PropertyValueTestBean.class);
125 		bd6.setScope("myScope");
126 		ac.registerBeanDefinition("tb6", bd6);
127 
128 		System.getProperties().put("country", "UK");
129 		try {
130 			ac.refresh();
131 
132 			TestBean tb0 = ac.getBean("tb0", TestBean.class);
133 
134 			TestBean tb1 = ac.getBean("tb1", TestBean.class);
135 			assertEquals("XXXmyNameYYY42ZZZ", tb1.getName());
136 			assertEquals(42, tb1.getAge());
137 
138 			TestBean tb2 = ac.getBean("tb2", TestBean.class);
139 			assertEquals("{ XXXmyNameYYY42ZZZ }", tb2.getName());
140 			assertEquals(42, tb2.getAge());
141 			assertEquals("123 UK", tb2.getCountry());
142 
143 			ValueTestBean tb3 = ac.getBean("tb3", ValueTestBean.class);
144 			assertEquals("XXXmyNameYYY42ZZZ", tb3.name);
145 			assertEquals(42, tb3.age);
146 			assertEquals("123 UK", tb3.country);
147 			assertEquals("123 UK", tb3.countryFactory.getObject());
148 			System.getProperties().put("country", "US");
149 			assertEquals("123 UK", tb3.country);
150 			assertEquals("123 US", tb3.countryFactory.getObject());
151 			System.getProperties().put("country", "UK");
152 			assertEquals("123 UK", tb3.country);
153 			assertEquals("123 UK", tb3.countryFactory.getObject());
154 			assertSame(tb0, tb3.tb);
155 
156 			tb3 = (ValueTestBean) SerializationTestUtils.serializeAndDeserialize(tb3);
157 			assertEquals("123 UK", tb3.countryFactory.getObject());
158 
159 			ConstructorValueTestBean tb4 = ac.getBean("tb4", ConstructorValueTestBean.class);
160 			assertEquals("XXXmyNameYYY42ZZZ", tb4.name);
161 			assertEquals(42, tb4.age);
162 			assertEquals("123 UK", tb4.country);
163 			assertSame(tb0, tb4.tb);
164 
165 			MethodValueTestBean tb5 = ac.getBean("tb5", MethodValueTestBean.class);
166 			assertEquals("XXXmyNameYYY42ZZZ", tb5.name);
167 			assertEquals(42, tb5.age);
168 			assertEquals("123 UK", tb5.country);
169 			assertSame(tb0, tb5.tb);
170 
171 			PropertyValueTestBean tb6 = ac.getBean("tb6", PropertyValueTestBean.class);
172 			assertEquals("XXXmyNameYYY42ZZZ", tb6.name);
173 			assertEquals(42, tb6.age);
174 			assertEquals("123 UK", tb6.country);
175 			assertSame(tb0, tb6.tb);
176 		}
177 		finally {
178 			System.getProperties().remove("country");
179 		}
180 	}
181 
182 	@Test
prototypeCreationReevaluatesExpressions()183 	public void prototypeCreationReevaluatesExpressions() {
184 		GenericApplicationContext ac = new GenericApplicationContext();
185 		AnnotationConfigUtils.registerAnnotationConfigProcessors(ac);
186 		RootBeanDefinition rbd = new RootBeanDefinition(PrototypeTestBean.class);
187 		rbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
188 		rbd.getPropertyValues().add("country", "#{systemProperties.country}");
189 		rbd.getPropertyValues().add("country2", new TypedStringValue("#{systemProperties.country}"));
190 		ac.registerBeanDefinition("test", rbd);
191 		ac.refresh();
192 
193 		try {
194 			System.getProperties().put("name", "juergen1");
195 			System.getProperties().put("country", "UK1");
196 			PrototypeTestBean tb = (PrototypeTestBean) ac.getBean("test");
197 			assertEquals("juergen1", tb.getName());
198 			assertEquals("UK1", tb.getCountry());
199 			assertEquals("UK1", tb.getCountry2());
200 
201 			System.getProperties().put("name", "juergen2");
202 			System.getProperties().put("country", "UK2");
203 			tb = (PrototypeTestBean) ac.getBean("test");
204 			assertEquals("juergen2", tb.getName());
205 			assertEquals("UK2", tb.getCountry());
206 			assertEquals("UK2", tb.getCountry2());
207 		}
208 		finally {
209 			System.getProperties().remove("name");
210 			System.getProperties().remove("country");
211 		}
212 	}
213 
214 	@Test
prototypeCreationIsFastEnough()215 	public void prototypeCreationIsFastEnough() {
216 		if (factoryLog.isTraceEnabled() || factoryLog.isDebugEnabled()) {
217 			// Skip this test: Trace logging blows the time limit.
218 			return;
219 		}
220 		GenericApplicationContext ac = new GenericApplicationContext();
221 		RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class);
222 		rbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
223 		rbd.getConstructorArgumentValues().addGenericArgumentValue("#{systemProperties.name}");
224 		rbd.getPropertyValues().add("country", "#{systemProperties.country}");
225 		ac.registerBeanDefinition("test", rbd);
226 		ac.refresh();
227 		StopWatch sw = new StopWatch();
228 		sw.start("prototype");
229 		System.getProperties().put("name", "juergen");
230 		System.getProperties().put("country", "UK");
231 		try {
232 			for (int i = 0; i < 100000; i++) {
233 				TestBean tb = (TestBean) ac.getBean("test");
234 				assertEquals("juergen", tb.getName());
235 				assertEquals("UK", tb.getCountry());
236 			}
237 			sw.stop();
238 		}
239 		finally {
240 			System.getProperties().remove("country");
241 			System.getProperties().remove("name");
242 		}
243 		assertTrue("Prototype creation took too long: " + sw.getTotalTimeMillis(), sw.getTotalTimeMillis() < 6000);
244 	}
245 
246 	@Test
systemPropertiesSecurityManager()247 	public void systemPropertiesSecurityManager() {
248 		GenericApplicationContext ac = new GenericApplicationContext();
249 		AnnotationConfigUtils.registerAnnotationConfigProcessors(ac);
250 
251 		GenericBeanDefinition bd = new GenericBeanDefinition();
252 		bd.setBeanClass(TestBean.class);
253 		bd.getPropertyValues().add("country", "#{systemProperties.country}");
254 		ac.registerBeanDefinition("tb", bd);
255 
256 		SecurityManager oldSecurityManager = System.getSecurityManager();
257 		try {
258 			System.setProperty("country", "NL");
259 
260 			SecurityManager securityManager = new SecurityManager() {
261 				@Override
262 				public void checkPropertiesAccess() {
263 					throw new AccessControlException("Not Allowed");
264 				}
265 				@Override
266 				public void checkPermission(Permission perm) {
267 					// allow everything else
268 				}
269 			};
270 			System.setSecurityManager(securityManager);
271 			ac.refresh();
272 
273 			TestBean tb = ac.getBean("tb", TestBean.class);
274 			assertEquals("NL", tb.getCountry());
275 
276 		}
277 		finally {
278 			System.setSecurityManager(oldSecurityManager);
279 			System.getProperties().remove("country");
280 		}
281 	}
282 
283 	@Test
stringConcatenationWithDebugLogging()284 	public void stringConcatenationWithDebugLogging() {
285 		GenericApplicationContext ac = new GenericApplicationContext();
286 		AnnotationConfigUtils.registerAnnotationConfigProcessors(ac);
287 
288 		GenericBeanDefinition bd = new GenericBeanDefinition();
289 		bd.setBeanClass(String.class);
290 		bd.getConstructorArgumentValues().addGenericArgumentValue("test-#{ T(java.lang.System).currentTimeMillis() }");
291 		ac.registerBeanDefinition("str", bd);
292 		ac.refresh();
293 
294 		String str = ac.getBean("str", String.class);
295 		assertTrue(str.startsWith("test-"));
296 	}
297 
298 
299 	public static class ValueTestBean implements Serializable {
300 
301 		@Autowired @Value("XXX#{tb0.name}YYY#{mySpecialAttr}ZZZ")
302 		public String name;
303 
304 		@Autowired @Value("#{mySpecialAttr}")
305 		public int age;
306 
307 		@Value("${code} #{systemProperties.country}")
308 		public String country;
309 
310 		@Value("${code} #{systemProperties.country}")
311 		public ObjectFactory<String> countryFactory;
312 
313 		@Autowired @Qualifier("original")
314 		public transient TestBean tb;
315 	}
316 
317 
318 	public static class ConstructorValueTestBean {
319 
320 		public String name;
321 
322 		public int age;
323 
324 		public String country;
325 
326 		public TestBean tb;
327 
328 		@Autowired
ConstructorValueTestBean( @alueR) String name, @Value(R) int age, @Qualifier(R) TestBean tb, @Value(R) String country)329 		public ConstructorValueTestBean(
330 				@Value("XXX#{tb0.name}YYY#{mySpecialAttr}ZZZ") String name,
331 				@Value("#{mySpecialAttr}") int age,
332 				@Qualifier("original") TestBean tb,
333 				@Value("${code} #{systemProperties.country}") String country) {
334 			this.name = name;
335 			this.age = age;
336 			this.country = country;
337 			this.tb = tb;
338 		}
339 	}
340 
341 
342 	public static class MethodValueTestBean {
343 
344 		public String name;
345 
346 		public int age;
347 
348 		public String country;
349 
350 		public TestBean tb;
351 
352 		@Autowired
configure( @ualifierR) TestBean tb, @Value(R) String name, @Value(R) int age, @Value(R) String country)353 		public void configure(
354 				@Qualifier("original") TestBean tb,
355 				@Value("XXX#{tb0.name}YYY#{mySpecialAttr}ZZZ") String name,
356 				@Value("#{mySpecialAttr}") int age,
357 				@Value("${code} #{systemProperties.country}") String country) {
358 			this.name = name;
359 			this.age = age;
360 			this.country = country;
361 			this.tb = tb;
362 		}
363 	}
364 
365 
366 	public static class PropertyValueTestBean {
367 
368 		public String name;
369 
370 		public int age;
371 
372 		public String country;
373 
374 		public TestBean tb;
375 
376 		@Value("XXX#{tb0.name}YYY#{mySpecialAttr}ZZZ")
setName(String name)377 		public void setName(String name) {
378 			this.name = name;
379 		}
380 
381 		@Value("#{mySpecialAttr}")
setAge(int age)382 		public void setAge(int age) {
383 			this.age = age;
384 		}
385 
386 		@Value("${code} #{systemProperties.country}")
setCountry(String country)387 		public void setCountry(String country) {
388 			this.country = country;
389 		}
390 
391 		@Autowired @Qualifier("original")
setTb(TestBean tb)392 		public void setTb(TestBean tb) {
393 			this.tb = tb;
394 		}
395 	}
396 
397 
398 	public static class PrototypeTestBean {
399 
400 		public String name;
401 
402 		public String country;
403 
404 		public String country2;
405 
406 		@Value("#{systemProperties.name}")
setName(String name)407 		public void setName(String name) {
408 			this.name = name;
409 		}
410 
getName()411 		public String getName() {
412 			return name;
413 		}
414 
setCountry(String country)415 		public void setCountry(String country) {
416 			this.country = country;
417 		}
418 
getCountry()419 		public String getCountry() {
420 			return country;
421 		}
422 
setCountry2(String country2)423 		public void setCountry2(String country2) {
424 			this.country2 = country2;
425 		}
426 
getCountry2()427 		public String getCountry2() {
428 			return country2;
429 		}
430 	}
431 
432 }
433