001: // Copyright 2006, 2007 The Apache Software Foundation
002: //
003: // Licensed under the Apache License, Version 2.0 (the "License");
004: // you may not use this file except in compliance with the License.
005: // You may obtain a copy of the License at
006: //
007: // http://www.apache.org/licenses/LICENSE-2.0
008: //
009: // Unless required by applicable law or agreed to in writing, software
010: // distributed under the License is distributed on an "AS IS" BASIS,
011: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: // See the License for the specific language governing permissions and
013: // limitations under the License.
014:
015: package org.apache.tapestry.ioc;
016:
017: import java.sql.PreparedStatement;
018: import java.util.ArrayList;
019: import java.util.Arrays;
020: import java.util.HashMap;
021: import java.util.List;
022: import java.util.Map;
023:
024: import org.apache.tapestry.ioc.internal.ExceptionInConstructorModule;
025: import org.apache.tapestry.ioc.internal.IOCInternalTestCase;
026: import org.testng.Assert;
027: import org.testng.annotations.Test;
028:
029: /**
030: * A few tests that are easiest (or even just possible) by building a Registry and trying out a few
031: * things.
032: */
033: public class IntegrationTest extends IOCInternalTestCase {
034: private Registry buildRegistry() {
035: return buildRegistry(FredModule.class, BarneyModule.class);
036: }
037:
038: @Test
039: public void duplicate_service_names_are_failure() {
040: try {
041: buildRegistry(FredModule.class, DuplicateFredModule.class);
042: unreachable();
043: } catch (RuntimeException ex) {
044: assertTrue(ex.getMessage().startsWith(
045: "Service id 'Fred' has already been defined by"));
046:
047: // Can't check the entire message, because we can't guarantee what order the modules
048: // will be processed in.
049: }
050: }
051:
052: @Test
053: public void static_builder_method_does_not_instantiate_builder() {
054: StaticModule.setInstantiated(false);
055: StaticModule.setFredRan(false);
056:
057: Registry r = buildRegistry(StaticModule.class);
058:
059: Runnable fred = r.getService("Fred", Runnable.class);
060:
061: fred.run();
062:
063: assertFalse(StaticModule.isInstantiated());
064: assertTrue(StaticModule.getFredRan());
065: }
066:
067: @Test
068: public void static_decorator_method_does_not_instantiate_builder() {
069: StaticModule.setInstantiated(false);
070: StaticModule.setDecoratorRan(false);
071:
072: Registry r = buildRegistry(StaticModule.class);
073:
074: Runnable fred = r.getService("Barney", Runnable.class);
075:
076: fred.run();
077:
078: assertFalse(StaticModule.isInstantiated());
079: assertTrue(StaticModule.getDecoratorRan());
080: }
081:
082: @Test
083: public void static_contributor_method_does_not_instantiate_builder() {
084: StaticModule.setInstantiated(false);
085:
086: Registry r = buildRegistry(StaticModule.class);
087:
088: NameListHolder holder = r.getService("Names",
089: NameListHolder.class);
090:
091: List<String> names = holder.getNames();
092:
093: assertEquals(names, Arrays.asList("Fred"));
094:
095: assertFalse(StaticModule.isInstantiated());
096: }
097:
098: @Test
099: public void shutdown_deactivates_proxies() {
100: Registry r = buildRegistry();
101:
102: Runnable service = r.getService("Fred", Runnable.class);
103:
104: service.run();
105:
106: r.shutdown();
107:
108: try {
109: service.run();
110: unreachable();
111: } catch (IllegalStateException ex) {
112: assertEquals(
113: ex.getMessage(),
114: "Proxy for service Fred is no longer active because the IOC Registry has been shut down.");
115: }
116:
117: // Show that toString() still works, even for a shutdown proxy.
118:
119: assertEquals(service.toString(),
120: "<Proxy for Fred(java.lang.Runnable)>");
121: }
122:
123: /**
124: * Along the way, we also test a few other things, such as decorator matching and automatic
125: * dependency resolution.
126: */
127: @Test
128: public void public_service_decorator_order() {
129: Registry r = buildRegistry();
130:
131: Runnable service = r.getService("Fred", Runnable.class);
132:
133: // Force creation
134:
135: service.run();
136:
137: List<String> names = r.getService(DecoratorList.class)
138: .getNames();
139:
140: // Note that the order of invocation appears backwards, since we build back-to-front
141:
142: assertEquals(names, Arrays.asList("gamma", "beta", "alpha"));
143: }
144:
145: @Test
146: public void public_service_unordered_configuration() {
147: Registry r = buildRegistry();
148:
149: NameListHolder service = r.getService("UnorderedNames",
150: NameListHolder.class);
151:
152: List<String> names = service.getNames();
153:
154: assertEquals(names, Arrays.asList("Beta", "Gamma",
155: "UnorderedNames"));
156: }
157:
158: /**
159: * We don't have to do as many public/private etc. tests for the other types of configuration,
160: * because the code paths are so similar.
161: */
162:
163: @Test
164: public void service_ordered_configuration() {
165: Registry r = buildRegistry();
166:
167: NameListHolder service = r.getService("OrderedNames",
168: NameListHolder.class);
169:
170: List<String> names = service.getNames();
171:
172: assertEquals(names, Arrays.asList("BARNEY", "FRED"));
173:
174: }
175:
176: @SuppressWarnings("unchecked")
177: @Test
178: public void service_mapped_configuration() {
179: Registry r = buildRegistry();
180:
181: Sizer sizer = r.getService("Sizer", Sizer.class);
182:
183: assertEquals(sizer.size(null), 0);
184:
185: // Have to be exact on type here.
186:
187: List list = new ArrayList();
188: list.add(1);
189: list.add(2);
190: list.add(3);
191:
192: assertEquals(sizer.size(list), 3);
193:
194: Map map = new HashMap();
195: map.put("fred", "flinstone");
196: map.put("barney", "rubble");
197:
198: assertEquals(sizer.size(map), 2);
199:
200: // Random objects are size 1
201:
202: assertEquals(sizer.size(this ), 1);
203: }
204:
205: @Test
206: public void unknown_scope() {
207: Registry r = buildRegistry(UnknownScopeModule.class);
208:
209: try {
210: Runnable runnable = r.getService("UnknownScope",
211: Runnable.class);
212:
213: runnable.run();
214:
215: unreachable();
216: } catch (Exception ex) {
217: assertMessageContains(ex,
218: "Exception constructing service 'UnknownScope'",
219: "Unknown service scope 'magic'");
220: }
221: }
222:
223: @Test
224: public void simple_perthread() throws Exception {
225: final Registry r = buildRegistry(PerThreadModule.class);
226:
227: final StringHolder holder = r.getService(StringHolder.class);
228:
229: // Something about some of the other tests causes this one to fail
230: // unless we start with cleanupThread(), there must be a loose ThreadLocal
231: // hanging around causing problems.
232:
233: r.cleanupThread();
234:
235: holder.setValue("fred");
236: assertEquals(holder.getValue(), "fred", holder.toString());
237:
238: Runnable runnable = new Runnable() {
239: public void run() {
240: Assert.assertNull(holder.getValue());
241:
242: holder.setValue("barney");
243: assertEquals(holder.getValue(), "barney");
244:
245: r.cleanupThread();
246: }
247: };
248:
249: Thread t = new Thread(runnable);
250:
251: t.start();
252: t.join();
253:
254: assertEquals(holder.getValue(), "fred");
255:
256: r.cleanupThread();
257: }
258:
259: /**
260: * This test fails at times and I'm not sure why. It's some kind of interaction with other tests
261: * but hard to figure out. Damn ThreadLocals!
262: */
263: @Test
264: public void registry_thread_cleanup() {
265: Registry r = buildRegistry(PerThreadModule.class);
266:
267: r.cleanupThread();
268:
269: StringHolder holder = r.getService(StringHolder.class);
270:
271: assertNull(holder.getValue());
272:
273: holder.setValue("fred");
274: assertEquals(holder.getValue(), "fred");
275:
276: r.cleanupThread();
277:
278: assertNull(holder.getValue());
279: }
280:
281: @Test
282: public void recursive_module_construction_is_caught() {
283: Registry r = buildRegistry(RecursiveConstructorModule.class);
284:
285: try {
286: Runnable runnable = r
287: .getService("Runnable", Runnable.class);
288:
289: // We can get the proxy, but invoking a method causes
290: // the module to be instantiated ... but that also invokes a method on
291: // the proxy.
292:
293: runnable.run();
294:
295: unreachable();
296: } catch (RuntimeException ex) {
297: assertTrue(ex.getMessage().contains(
298: "has failed due to recursion"));
299: }
300: }
301:
302: @Test
303: public void eager_service_loading() {
304: Registry r = buildRegistry(EagerLoadModule.class);
305:
306: assertFalse(EagerLoadModule._eagerLoadDidHappen,
307: "EagerLoadModule is not in correct initial state.");
308:
309: r.eagerLoadServices();
310:
311: assertTrue(EagerLoadModule._eagerLoadDidHappen);
312: }
313:
314: @Test
315: public void access_to_services_ignores_case() {
316: Registry r = buildRegistry(FredModule.class);
317:
318: Runnable fred = r.getService("Fred", Runnable.class);
319:
320: assertSame(r.getService("FRED", Runnable.class), fred);
321: }
322:
323: @Test
324: public void simple_autobuild() {
325: Registry r = buildRegistry(AutobuildModule.class);
326:
327: StringHolder sh = r.getService(StringHolder.class);
328:
329: sh.setValue("Foo");
330:
331: assertEquals(sh.getValue(), "Foo");
332: }
333:
334: @Test
335: public void exception_in_autobuild_service_constructor() {
336: Registry r = buildRegistry(ExceptionInConstructorModule.class);
337:
338: Runnable runnable = r.getService(Runnable.class);
339:
340: try {
341: runnable.run();
342: unreachable();
343: } catch (RuntimeException ex) {
344: assertMessageContains(
345: ex,
346: "Error invoking constructor",
347: "ExceptionInConstructorServiceImpl() (at ExceptionInConstructorServiceImpl.java",
348: "for service 'Runnable'",
349: "Yes, we have no tomatoes.");
350: }
351: }
352:
353: @Test
354: public void non_proxied_service() {
355: Registry r = buildRegistry(NonProxiedServiceModule.class);
356:
357: // Note: obtained via the (or an) interface implemented by
358: // the service implementation.
359:
360: StringHolder holder = r.getService(StringHolder.class);
361:
362: assertTrue(holder instanceof StringHolderImpl);
363: }
364:
365: @Test
366: public void service_builder_method_uses_autobuild() {
367: Registry r = buildRegistry(ServiceBuilderAutobuilderModule.class);
368:
369: StringHolder holder = r.getService(StringHolder.class);
370:
371: // Check that it works.
372:
373: holder.setValue("Foo");
374:
375: assertEquals(holder.getValue(), "Foo");
376: }
377:
378: @Test
379: public void autobuild_via_registry() {
380: Registry r = buildRegistry();
381:
382: StringHolder holder = r.autobuild(StringHolderImpl.class);
383:
384: assertSame(holder.getClass(), StringHolderImpl.class);
385:
386: // Check that it works.
387:
388: holder.setValue("Foo");
389:
390: assertEquals(holder.getValue(), "Foo");
391: }
392:
393: @Test
394: public void service_builder_method_uses_autobuild_with_failure() {
395: Registry r = buildRegistry(ServiceBuilderAutobuilderModule.class);
396:
397: // We can get the proxy.
398:
399: Runnable runnable = r.getService(Runnable.class);
400:
401: try {
402: // But it fails at realization
403:
404: runnable.run();
405:
406: unreachable();
407: } catch (RuntimeException ex) {
408: assertMessageContains(
409: ex,
410: "Class org.apache.tapestry.ioc.UnbuildableRunnable does not contain a public constructor needed to autobuild.");
411:
412: // Like to check that the message includes the source location
413:
414: assertTrue(ex
415: .getMessage()
416: .matches(
417: ".*\\(at ServiceBuilderAutobuilderModule.java:\\d+\\).*"));
418: }
419: }
420:
421: @Test
422: public void autobuild_via_registry_no_constructor() {
423: Registry r = buildRegistry();
424:
425: try {
426: r.autobuild(UnbuildableRunnable.class);
427:
428: unreachable();
429: } catch (RuntimeException ex) {
430: assertMessageContains(
431: ex,
432: "Class org.apache.tapestry.ioc.UnbuildableRunnable does not contain a public constructor needed to autobuild.");
433: }
434: }
435:
436: @Test
437: public void autobuild_via_registry_constructor_exception() {
438: Registry r = buildRegistry();
439:
440: try {
441: r.autobuild(FailInConstructorRunnable.class);
442:
443: unreachable();
444: } catch (RuntimeException ex) {
445: assertMessageContains(
446: ex,
447: "Error invoking constructor org.apache.tapestry.ioc.FailInConstructorRunnable()",
448: "Failure in Runnable constructor.");
449:
450: // Like to check that the message includes the source location
451:
452: assertTrue(ex.getMessage().matches(
453: ".*\\(at FailInConstructorRunnable.java:\\d+\\).*"));
454: }
455: }
456:
457: @Test
458: public void get_service_by_unknown_id() {
459: Registry r = buildRegistry();
460:
461: try {
462: r.getService("PeekABoo", Runnable.class);
463: unreachable();
464: } catch (RuntimeException ex) {
465: assertMessageContains(ex,
466: "Service id \'PeekABoo\' is not defined by any module.");
467: }
468: }
469:
470: @Test
471: public void request_service_by_type_with_no_matches() {
472:
473: Registry r = buildRegistry();
474:
475: try {
476: r.getService(PreparedStatement.class);
477: unreachable();
478: } catch (RuntimeException ex) {
479: assertEquals(ex.getMessage(),
480: "No service implements the interface java.sql.PreparedStatement.");
481: }
482: }
483:
484: @Test
485: public void request_service_by_type_with_multiple_matches() {
486: Registry r = buildRegistry(DuplicateServiceTypeModule.class);
487:
488: try {
489: r.getService(Runnable.class);
490: unreachable();
491: } catch (RuntimeException ex) {
492: assertEquals(
493: ex.getMessage(),
494: "Service interface java.lang.Runnable is matched by 2 services: Barney, Fred. Automatic dependency resolution requires that exactly one service implement the interface.");
495: }
496: }
497:
498: @Test
499: public void service_build_method_return_type_not_interface() {
500: Registry r = buildRegistry(ConcreteServiceBuilderModule.class);
501:
502: StringHolder holder = r.getService(StringHolder.class);
503:
504: // No interface means no proxy.
505:
506: assertTrue(holder instanceof StringHolderImpl);
507:
508: // But the implementation is cached
509:
510: assertSame(r.getService(StringHolder.class), holder);
511: }
512:
513: @Test
514: public void symbol_in_inject_annotation_is_expanded() {
515: Registry r = buildRegistry(GreeterModule.class);
516:
517: Greeter g = r.getService("Greeter", Greeter.class);
518:
519: assertEquals(g.getGreeting(), "Hello");
520: assertEquals(g.toString(),
521: "<Proxy for Greeter(org.apache.tapestry.ioc.Greeter)>");
522: }
523:
524: @Test
525: public void symbol_in_registry_call_for_service_is_expanded() {
526: Registry r = buildRegistry(GreeterModule.class);
527:
528: Greeter g = r.getService("${greeter}", Greeter.class);
529:
530: assertEquals(g.getGreeting(), "Hello");
531: assertEquals(g.toString(),
532: "<Proxy for HelloGreeter(org.apache.tapestry.ioc.Greeter)>");
533: }
534:
535: }
|