| // Copyright 2006, 2007, 2008, 2009, 2010 The Apache Software Foundation |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package org.apache.tapestry5.ioc; |
| |
| import java.io.File; |
| import java.lang.reflect.Method; |
| import java.net.URL; |
| import java.net.URLClassLoader; |
| import java.sql.PreparedStatement; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.tapestry5.ioc.internal.AdviseByMarkerModule; |
| import org.apache.tapestry5.ioc.internal.AlphabetModule; |
| import org.apache.tapestry5.ioc.internal.AlphabetModule2; |
| import org.apache.tapestry5.ioc.internal.DecorateByMarkerModule; |
| import org.apache.tapestry5.ioc.internal.ExceptionInConstructorModule; |
| import org.apache.tapestry5.ioc.internal.ExtraPublicConstructorsModule; |
| import org.apache.tapestry5.ioc.internal.IOCInternalTestCase; |
| import org.apache.tapestry5.ioc.internal.PrivateConstructorModule; |
| import org.apache.tapestry5.ioc.internal.UpcaseService; |
| import org.apache.tapestry5.ioc.internal.services.SimpleAnnotation; |
| import org.apache.tapestry5.ioc.internal.services.StartupModule2; |
| import org.apache.tapestry5.ioc.services.Builtin; |
| import org.apache.tapestry5.ioc.services.ServiceActivity; |
| import org.apache.tapestry5.ioc.services.ServiceActivityScoreboard; |
| import org.apache.tapestry5.ioc.services.Status; |
| import org.apache.tapestry5.ioc.services.SymbolSource; |
| import org.apache.tapestry5.ioc.services.TypeCoercer; |
| import org.apache.tapestry5.ioc.util.NonmatchingMappedConfigurationOverrideModule; |
| import org.easymock.EasyMock; |
| import org.testng.Assert; |
| import org.testng.annotations.Test; |
| |
| /** |
| * A few tests that are easiest (or even just possible) by building a Registry and trying out a few |
| * things. |
| */ |
| public class IntegrationTest extends IOCInternalTestCase |
| { |
| public static int countingGreeterInstantiationCount; |
| |
| private Registry buildRegistry() |
| { |
| return buildRegistry(FredModule.class, BarneyModule.class); |
| } |
| |
| @Test |
| public void duplicate_service_names_are_failure() |
| { |
| try |
| { |
| buildRegistry(FredModule.class, DuplicateFredModule.class); |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| assertTrue(ex.getMessage().startsWith("Service id 'Fred' has already been defined by")); |
| |
| // Can't check the entire message, because we can't guarantee what order the modules |
| // will be processed in. |
| } |
| } |
| |
| @Test |
| public void static_builder_method_does_not_instantiate_builder() |
| { |
| StaticModule.setInstantiated(false); |
| StaticModule.setFredRan(false); |
| |
| Registry r = buildRegistry(StaticModule.class); |
| |
| Runnable fred = r.getService("Fred", Runnable.class); |
| |
| fred.run(); |
| |
| assertFalse(StaticModule.isInstantiated()); |
| assertTrue(StaticModule.getFredRan()); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void static_decorator_method_does_not_instantiate_builder() |
| { |
| StaticModule.setInstantiated(false); |
| StaticModule.setDecoratorRan(false); |
| |
| Registry r = buildRegistry(StaticModule.class); |
| |
| Runnable fred = r.getService("Barney", Runnable.class); |
| |
| fred.run(); |
| |
| assertFalse(StaticModule.isInstantiated()); |
| assertTrue(StaticModule.getDecoratorRan()); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void static_contributor_method_does_not_instantiate_builder() |
| { |
| StaticModule.setInstantiated(false); |
| |
| Registry r = buildRegistry(StaticModule.class); |
| |
| NameListHolder holder = r.getService("Names", NameListHolder.class); |
| |
| List<String> names = holder.getNames(); |
| |
| assertEquals(names, Arrays.asList("Fred")); |
| |
| assertFalse(StaticModule.isInstantiated()); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void shutdown_deactivates_proxies() |
| { |
| Registry r = buildRegistry(); |
| |
| Runnable service = r.getService("Fred", Runnable.class); |
| |
| service.run(); |
| |
| r.shutdown(); |
| |
| try |
| { |
| service.run(); |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| assertMessageContains(ex, |
| "Proxy for service Fred is no longer active because the IOC Registry has been shut down."); |
| } |
| |
| // Show that toString() still works, even for a shutdown proxy. |
| |
| assertEquals(service.toString(), "<Proxy for Fred(java.lang.Runnable)>"); |
| } |
| |
| /** |
| * Along the way, we also test a few other things, such as decorator matching and automatic |
| * dependency resolution. |
| */ |
| @Test |
| public void public_service_decorator_order() |
| { |
| Registry r = buildRegistry(); |
| |
| Runnable service = r.getService("Fred", Runnable.class); |
| |
| // Force creation |
| |
| service.run(); |
| |
| List<String> names = r.getService(DecoratorList.class).getNames(); |
| |
| // Note that the order of invocation appears backwards, since we build back-to-front |
| |
| assertEquals(names, Arrays.asList("gamma", "beta", "alpha")); |
| } |
| |
| @Test |
| public void public_service_unordered_configuration() |
| { |
| Registry r = buildRegistry(); |
| |
| NameListHolder service = r.getService("UnorderedNames", NameListHolder.class); |
| |
| List<String> names = service.getNames(); |
| |
| assertEquals(names, Arrays.asList("Beta", "Gamma", "UnorderedNames")); |
| } |
| |
| /** |
| * We don't have to do as many public/private etc. tests for the other types of configuration, |
| * because the code |
| * paths are so similar. |
| */ |
| |
| @Test |
| public void service_ordered_configuration() |
| { |
| Registry r = buildRegistry(); |
| |
| NameListHolder service = r.getService("OrderedNames", NameListHolder.class); |
| |
| List<String> names = service.getNames(); |
| |
| assertEquals(names, Arrays.asList("BARNEY", "FRED")); |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Test |
| public void service_mapped_configuration() |
| { |
| Registry r = buildRegistry(); |
| |
| Sizer sizer = r.getService("Sizer", Sizer.class); |
| |
| assertEquals(sizer.size(null), 0); |
| |
| // Have to be exact on type here. |
| |
| List list = new ArrayList(); |
| list.add(1); |
| list.add(2); |
| list.add(3); |
| |
| assertEquals(sizer.size(list), 3); |
| |
| Map map = new HashMap(); |
| map.put("fred", "flinstone"); |
| map.put("barney", "rubble"); |
| |
| assertEquals(sizer.size(map), 2); |
| |
| // Random objects are size 1 |
| |
| assertEquals(sizer.size(this), 1); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void unknown_scope() |
| { |
| Registry r = buildRegistry(UnknownScopeModule.class); |
| |
| try |
| { |
| Runnable runnable = r.getService("UnknownScope", Runnable.class); |
| |
| runnable.run(); |
| |
| unreachable(); |
| } |
| catch (Exception ex) |
| { |
| assertMessageContains(ex, "Error building service proxy for service 'UnknownScope'", |
| "Unknown service scope 'magic'"); |
| } |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void scope_mismatch() |
| { |
| Registry r = buildRegistry(ScopeMismatchModule.class); |
| |
| try |
| { |
| r.getService(StringBuilder.class); |
| unreachable(); |
| } |
| catch (Exception ex) |
| { |
| assertMessageContains(ex, |
| "Error building service proxy for service 'ScopeRequiresAProxyAndNoInterfaceIsProvided'", |
| "Service scope 'perthread' requires a proxy"); |
| } |
| |
| r.shutdown(); |
| |
| } |
| |
| @Test |
| public void simple_perthread() throws Exception |
| { |
| final Registry r = buildRegistry(PerThreadModule.class); |
| |
| final StringHolder holder = r.getService(StringHolder.class); |
| |
| // Something about some of the other tests causes this one to fail |
| // unless we start with cleanupThread(), there must be a loose ThreadLocal |
| // hanging around causing problems. |
| |
| r.cleanupThread(); |
| |
| holder.setValue("fred"); |
| assertEquals(holder.getValue(), "fred", holder.toString()); |
| |
| Runnable runnable = new Runnable() |
| { |
| public void run() |
| { |
| Assert.assertNull(holder.getValue()); |
| |
| holder.setValue("barney"); |
| assertEquals(holder.getValue(), "barney"); |
| |
| r.cleanupThread(); |
| } |
| }; |
| |
| Thread t = new Thread(runnable); |
| |
| t.start(); |
| t.join(); |
| |
| assertEquals(holder.getValue(), "fred"); |
| |
| r.cleanupThread(); |
| |
| r.shutdown(); |
| } |
| |
| /** |
| * This test fails at times and I'm not sure why. It's some kind of interaction with other tests |
| * but hard to figure |
| * out. Damn ThreadLocals! |
| */ |
| @Test |
| public void registry_thread_cleanup() |
| { |
| Registry r = buildRegistry(PerThreadModule.class); |
| |
| r.cleanupThread(); |
| |
| StringHolder holder = r.getService(StringHolder.class); |
| |
| assertNull(holder.getValue()); |
| |
| holder.setValue("fred"); |
| assertEquals(holder.getValue(), "fred"); |
| |
| r.cleanupThread(); |
| |
| assertNull(holder.getValue()); |
| } |
| |
| @Test |
| public void recursive_module_construction_is_caught() |
| { |
| Registry r = buildRegistry(RecursiveConstructorModule.class); |
| |
| try |
| { |
| Runnable runnable = r.getService("Runnable", Runnable.class); |
| |
| // We can get the proxy, but invoking a method causes |
| // the module to be instantiated ... but that also invokes a method on |
| // the proxy. |
| |
| runnable.run(); |
| |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| assertTrue(ex.getMessage().contains("has failed due to recursion")); |
| } |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void eager_service_loading() |
| { |
| Registry r = buildRegistry(EagerLoadModule.class); |
| |
| assertFalse(EagerLoadModule._eagerLoadDidHappen, "EagerLoadModule is not in correct initial state."); |
| |
| r.performRegistryStartup(); |
| |
| assertTrue(EagerLoadModule._eagerLoadDidHappen); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void access_to_services_ignores_case() |
| { |
| Registry r = buildRegistry(FredModule.class); |
| |
| Runnable fred = r.getService("Fred", Runnable.class); |
| |
| assertSame(r.getService("FRED", Runnable.class), fred); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void simple_autobuild() |
| { |
| Registry r = buildRegistry(AutobuildModule.class); |
| |
| StringHolder sh = r.getService(StringHolder.class); |
| |
| sh.setValue("Foo"); |
| |
| assertEquals(sh.getValue(), "Foo"); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void proxy_annotations() throws Exception |
| { |
| Registry r = buildRegistry(AutobuildModule.class); |
| |
| StringHolder sh = r.getService(StringHolder.class); |
| |
| SimpleAnnotation annotation = sh.getClass().getAnnotation(SimpleAnnotation.class); |
| assertNotNull(annotation); |
| assertEquals(annotation.value(), "StringHolderImpl"); |
| |
| Method method = sh.getClass().getMethod("getValue"); |
| |
| annotation = method.getAnnotation(SimpleAnnotation.class); |
| assertNotNull(annotation); |
| assertEquals(annotation.value(), "StringHolderImpl#getValue()"); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void exception_in_autobuild_service_constructor() |
| { |
| Registry r = buildRegistry(ExceptionInConstructorModule.class); |
| |
| Pingable pingable = r.getService(Pingable.class); |
| |
| try |
| { |
| pingable.ping(); |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| assertMessageContains(ex, "Error invoking constructor", "ExceptionInConstructorServiceImpl()", |
| "for service 'Pingable'", "Yes, we have no tomatoes."); |
| } |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void non_proxied_service() |
| { |
| Registry r = buildRegistry(NonProxiedServiceModule.class); |
| |
| // Note: obtained via the (or an) interface implemented by |
| // the service implementation. |
| |
| StringHolder holder = r.getService(StringHolder.class); |
| |
| assertTrue(holder instanceof StringHolderImpl); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void convention_over_configuration_service() |
| { |
| Registry r = buildRegistry(ConventionModule.class); |
| |
| StringHolder holder = r.getService(StringHolder.class); |
| |
| holder.setValue("Bar"); |
| |
| assertEquals(holder.getValue(), "Bar"); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void convention_over_configuration_service_impl_not_found() |
| { |
| try |
| { |
| buildRegistry(ConventionModuleImplementationNotFound.class); |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| assertMessageContains(ex, "No service implements the interface " + StringTransformer.class.getName() |
| + ". Please provide"); |
| } |
| } |
| |
| @Test |
| public void convention_over_configuration_service_wrong_impl_found() |
| { |
| try |
| { |
| buildRegistry(ConventionFailureModule.class); |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| assertMessageContains(ex, "No service implements the interface " + Pingable.class.getName()); |
| } |
| } |
| |
| @Test |
| public void service_builder_method_uses_autobuild() |
| { |
| Registry r = buildRegistry(ServiceBuilderAutobuilderModule.class); |
| |
| StringHolder holder = r.getService(StringHolder.class); |
| |
| // Check that it works. |
| |
| holder.setValue("Foo"); |
| |
| assertEquals(holder.getValue(), "Foo"); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void autobuild_via_registry() |
| { |
| Registry r = buildRegistry(); |
| |
| StringHolder holder = r.autobuild(StringHolderImpl.class); |
| |
| assertSame(holder.getClass(), StringHolderImpl.class); |
| |
| // Check that it works. |
| |
| holder.setValue("Foo"); |
| |
| assertEquals(holder.getValue(), "Foo"); |
| |
| r.shutdown(); |
| } |
| |
| /** TAP5-967 */ |
| @Test |
| public void autobuild_with_description_via_registry() |
| { |
| Registry r = buildRegistry(); |
| |
| StringHolder holder = r.autobuild("Building StringHolderImpl", StringHolderImpl.class); |
| |
| assertSame(holder.getClass(), StringHolderImpl.class); |
| |
| // Check that it works. |
| |
| holder.setValue("Bar"); |
| |
| assertEquals(holder.getValue(), "Bar"); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void service_builder_method_uses_autobuild_with_failure() |
| { |
| Registry r = buildRegistry(ServiceBuilderAutobuilderModule.class); |
| |
| // We can get the proxy. |
| |
| Pingable pingable = r.getService(Pingable.class); |
| |
| try |
| { |
| // But it fails at realization |
| |
| pingable.ping(); |
| |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| assertMessageContains(ex, |
| "Class org.apache.tapestry5.ioc.UnbuildablePingable does not contain a public constructor needed to autobuild."); |
| |
| // Like to check that the message includes the source location |
| |
| assertTrue(ex.getMessage().matches(".*\\(at ServiceBuilderAutobuilderModule.java:\\d+\\).*")); |
| } |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void autobuild_via_registry_no_constructor() |
| { |
| Registry r = buildRegistry(); |
| |
| try |
| { |
| r.autobuild(UnbuildablePingable.class); |
| |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| assertMessageContains(ex, |
| "Class org.apache.tapestry5.ioc.UnbuildablePingable does not contain a public constructor needed to autobuild."); |
| } |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void autobuild_via_registry_constructor_exception() |
| { |
| Registry r = buildRegistry(); |
| |
| try |
| { |
| r.autobuild(FailInConstructorRunnable.class); |
| |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| assertMessageContains(ex, |
| "Error invoking constructor org.apache.tapestry5.ioc.FailInConstructorRunnable()", |
| "Failure in Runnable constructor."); |
| |
| // Like to check that the message includes the source location |
| |
| assertTrue(ex.getMessage().matches(".*\\(at FailInConstructorRunnable.java:\\d+\\).*")); |
| } |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void get_service_by_unknown_id() |
| { |
| Registry r = buildRegistry(); |
| |
| try |
| { |
| r.getService("PeekABoo", Runnable.class); |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| assertMessageContains(ex, "Service id \'PeekABoo\' is not defined by any module."); |
| } |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void request_service_by_type_with_no_matches() |
| { |
| |
| Registry r = buildRegistry(); |
| |
| try |
| { |
| r.getService(PreparedStatement.class); |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| assertEquals(ex.getMessage(), "No service implements the interface java.sql.PreparedStatement."); |
| } |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void request_service_by_type_with_multiple_matches() |
| { |
| Registry r = buildRegistry(DuplicateServiceTypeModule.class); |
| |
| try |
| { |
| r.getService(Pingable.class); |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| assertEquals( |
| ex.getMessage(), |
| "Service interface org.apache.tapestry5.ioc.Pingable is matched by 2 services: Barney, Fred. Automatic dependency resolution requires that exactly one service implement the interface."); |
| } |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void service_build_method_return_type_not_interface() |
| { |
| Registry r = buildRegistry(ConcreteServiceBuilderModule.class); |
| |
| StringHolder holder = r.getService(StringHolder.class); |
| |
| // No interface means no proxy. |
| |
| assertTrue(holder instanceof StringHolderImpl); |
| |
| // But the implementation is cached |
| |
| assertSame(r.getService(StringHolder.class), holder); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void symbol_in_inject_annotation_is_expanded() |
| { |
| Registry r = buildRegistry(GreeterModule.class); |
| |
| Greeter g = r.getService("Greeter", Greeter.class); |
| |
| assertEquals(g.getGreeting(), "Hello"); |
| assertEquals(g.toString(), "<Proxy for Greeter(org.apache.tapestry5.ioc.Greeter)>"); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void symbol_in_registry_call_for_service_is_expanded() |
| { |
| Registry r = buildRegistry(GreeterModule.class); |
| |
| Greeter g = r.getService("${greeter}", Greeter.class); |
| |
| assertEquals(g.getGreeting(), "Hello"); |
| assertEquals(g.toString(), "<Proxy for HelloGreeter(org.apache.tapestry5.ioc.Greeter)>"); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void injection_by_marker_with_single_match() |
| { |
| Registry r = buildRegistry(GreeterModule.class); |
| |
| Greeter g = r.getService("InjectedBlueGreeter", Greeter.class); |
| |
| assertEquals(g.getGreeting(), "Blue"); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void injection_by_marker_with_multiple_matches() |
| { |
| Registry r = buildRegistry(GreeterModule.class); |
| |
| Greeter g = r.getService("InjectedRedGreeter", Greeter.class); |
| |
| try |
| { |
| g.getGreeting(); |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| assertMessageContains( |
| ex, |
| "Error invoking service builder method", |
| "Unable to locate a single service assignable to type org.apache.tapestry5.ioc.Greeter with marker annotation(s) org.apache.tapestry5.ioc.RedMarker", |
| "org.apache.tapestry5.ioc.GreeterModule.buildRedGreeter1()", |
| "org.apache.tapestry5.ioc.GreeterModule.buildRedGreeter2()"); |
| } |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void injection_by_marker_with_zero_matches() |
| { |
| Registry r = buildRegistry(GreeterModule.class); |
| |
| Greeter g = r.getService("InjectedYellowGreeter", Greeter.class); |
| |
| try |
| { |
| g.getGreeting(); |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| assertMessageContains( |
| ex, |
| "Error invoking service builder method", |
| " Unable to locate any service assignable to type org.apache.tapestry5.ioc.Greeter with marker annotation(s) org.apache.tapestry5.ioc.YellowMarker."); |
| } |
| |
| r.shutdown(); |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Test |
| public void builtin_services_available_via_marker_annotation() |
| { |
| Registry r = buildRegistry(); |
| |
| TypeCoercer tc1 = r.getService("TypeCoercer", TypeCoercer.class); |
| |
| Builtin annotation = newMock(Builtin.class); |
| |
| AnnotationProvider ap = mockAnnotationProvider(); |
| |
| train_getAnnotation(ap, Builtin.class, annotation); |
| |
| // On the build server, the order of keys inside the RegistryImpl's _markerToServiceDef |
| // is different, and so it *may* query ofr a number of other annotation |
| // besides Builtin. |
| |
| expect(ap.getAnnotation(EasyMock.isA(Class.class))).andStubReturn(null); |
| |
| replay(); |
| |
| TypeCoercer tc2 = r.getObject(TypeCoercer.class, ap); |
| |
| assertSame(tc1, tc2); |
| |
| verify(); |
| |
| r.shutdown(); |
| } |
| |
| /** |
| * A cursory test for {@link ServiceActivityScoreboard}, just to see if any data has been |
| * collected. |
| */ |
| @Test |
| public void service_activity_scoreboard() |
| { |
| Registry r = buildRegistry(GreeterModule.class); |
| |
| ServiceActivityScoreboard scoreboard = r.getService(ServiceActivityScoreboard.class); |
| |
| // Force the state of a few services. |
| |
| TypeCoercer tc = r.getService("TypeCoercer", TypeCoercer.class); |
| |
| tc.coerce("123", Integer.class); |
| |
| r.getService("BlueGreeter", Greeter.class); |
| |
| // Now get the activity list and poke around. |
| |
| List<ServiceActivity> serviceActivity = scoreboard.getServiceActivity(); |
| |
| assertTrue(serviceActivity.size() > 0); |
| |
| for (ServiceActivity a : serviceActivity) |
| { |
| String serviceId = a.getServiceId(); |
| |
| if (serviceId.equals("ClassFactory")) |
| assertEquals(a.getStatus(), Status.BUILTIN); |
| |
| if (serviceId.equals("RedGreeter1")) |
| assertEquals(a.getStatus(), Status.DEFINED); |
| |
| if (serviceId.equals("TypeCoercer")) |
| assertEquals(a.getStatus(), Status.REAL); |
| |
| if (serviceId.equals("BlueGreeter")) |
| assertEquals(a.getStatus(), Status.VIRTUAL); |
| } |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void proxy_autobuild_object() |
| { |
| Registry r = buildRegistry(); |
| |
| IntegrationTest.countingGreeterInstantiationCount = 0; |
| |
| Greeter g = r.proxy(Greeter.class, CountingGreeterImpl.class); |
| |
| assertEquals(IntegrationTest.countingGreeterInstantiationCount, 0); |
| |
| assertNotNull(g.toString()); |
| |
| assertEquals(IntegrationTest.countingGreeterInstantiationCount, 0); |
| |
| // Show that the class is not instantiated until a method is invoked, and that its |
| // only instantiated once. |
| |
| for (int i = 0; i < 5; i++) |
| { |
| assertEquals(g.getGreeting(), "Hello"); |
| assertEquals(IntegrationTest.countingGreeterInstantiationCount, 1); |
| } |
| |
| r.shutdown(); |
| } |
| |
| /** |
| * TAPESTRY-2117 |
| */ |
| @Test |
| public void circular_module_references_are_ignored() |
| { |
| Registry r = buildRegistry(HelterModule.class); |
| |
| Runnable helter = r.getService("Helter", Runnable.class); |
| Runnable skelter = r.getService("Skelter", Runnable.class); |
| |
| assertNotSame(helter, skelter); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void bind_to_service_builder() |
| { |
| Registry r = buildRegistry(ServiceBuilderModule.class); |
| |
| Greeter g = r.getService("Greeter", Greeter.class); |
| |
| assertEquals(g.getGreeting(), "Greetings from service Greeter."); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void bind_to_service_binder_that_throws_exception() |
| { |
| Registry r = buildRegistry(ServiceBuilderModule.class); |
| |
| Greeter g = r.getService("BrokenGreeter", Greeter.class); |
| |
| try |
| { |
| g.getGreeting(); |
| unreachable(); |
| } |
| catch (Exception ex) |
| { |
| assertEquals(ex.getMessage(), |
| "Exception constructing service 'BrokenGreeter': Failure inside ServiceBuilder callback."); |
| } |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void invalid_class_in_manifest() throws Exception |
| { |
| File fakejar = new File("src/test/fakejar"); |
| |
| assertTrue(fakejar.exists() && fakejar.isDirectory(), "src/test/fakejar must be an existing directory"); |
| |
| URL url = fakejar.toURL(); |
| |
| URLClassLoader loader = new URLClassLoader(new URL[] |
| { url }, Thread.currentThread().getContextClassLoader()); |
| |
| RegistryBuilder builder = new RegistryBuilder(loader); |
| |
| try |
| { |
| IOCUtilities.addDefaultModules(builder); |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| assertMessageContains(ex, "Exception loading module(s) from manifest", |
| "Failure loading Tapestry IoC module class does.not.exist.Module"); |
| } |
| } |
| |
| @Test |
| public void local_annotation() |
| { |
| Registry r = buildRegistry(GreeterModule.class, LocalModule.class); |
| |
| StringHolder g = r.getService("LocalGreeterHolder", StringHolder.class); |
| |
| // Comes from the @Local DrawlGreeter, even though there are many other Greeter services |
| // available. |
| |
| assertEquals(g.getValue(), "Hello, y'all!"); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void decorator_receive_delegate_by_specific_type() |
| { |
| Registry r = buildRegistry(GreeterModule.class, SpecificDecoratorModule.class); |
| |
| Greeter g = r.getService("HelloGreeter", Greeter.class); |
| |
| assertEquals(g.getGreeting(), "HELLO"); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void cyclic_dependency_in_MOP() throws Exception |
| { |
| Registry r = buildRegistry(CyclicMOPModule.class); |
| |
| Runnable trigger = r.getService("Trigger", Runnable.class); |
| |
| try |
| { |
| trigger.run(); |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| assertMessageContains(ex, "Construction of service 'TypeCoercer' has failed due to recursion"); |
| } |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void no_public_constructor_on_module_builder_class() |
| { |
| Registry r = buildRegistry(PrivateConstructorModule.class); |
| |
| try |
| { |
| r.getService("Trigger", Runnable.class).run(); |
| |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| assertMessageContains(ex, |
| "Module class org.apache.tapestry5.ioc.internal.PrivateConstructorModule does not contain any public constructors."); |
| } |
| } |
| |
| @Test |
| public void too_many_public_constructors_on_module_builder_class() |
| { |
| Registry r = buildRegistry(ExtraPublicConstructorsModule.class); |
| |
| UpcaseService s = r.getService(UpcaseService.class); |
| |
| assertEquals(s.upcase("Hello, ${fred}"), "HELLO, FLINTSTONE"); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void unordered_configuration_contribute_by_class() |
| { |
| Registry r = buildRegistry(ContributeByClassModule.class); |
| |
| StringTransformer st = r.getService("MasterStringTransformer", StringTransformer.class); |
| |
| assertEquals(st.transform("Tapestry"), "TAPESTRY"); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void ordered_configuration_contribute_by_class() |
| { |
| Registry r = buildRegistry(ContributeByClassModule.class); |
| |
| StringTransformer st = r.getService("StringTransformerChain", StringTransformer.class); |
| |
| assertEquals(st.transform("Tapestry"), "TAPESTRY"); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void mapped_configuration_contribute_by_class() |
| { |
| Registry r = buildRegistry(ContributeByClassModule.class); |
| |
| StringTransformer st = r.getService("MappedStringTransformer", StringTransformer.class); |
| |
| assertEquals(st.transform("Tapestry"), "TAPESTRY"); |
| |
| r.shutdown(); |
| } |
| |
| /** |
| * TAP5-139 |
| */ |
| @Test |
| public void autobuild_injection() |
| { |
| Registry r = buildRegistry(AutobuildInjectionModule.class); |
| |
| StringTransformer st = r.getService(StringTransformer.class); |
| |
| assertEquals(st.transform("Hello, ${fred}"), "Hello, flintstone"); |
| |
| r.shutdown(); |
| } |
| |
| /** |
| * TAP5-292 |
| */ |
| @Test |
| public void field_resource_injection() |
| { |
| Registry r = buildRegistry(FieldResourceInjectionModule.class); |
| |
| FieldResourceService s = r.getService(FieldResourceService.class); |
| |
| assertEquals(s.getServiceId(), "FieldResourceService"); |
| assertListsEquals(s.getLabels(), "Barney", "Betty", "Fred", "Wilma"); |
| |
| r.shutdown(); |
| } |
| |
| /** |
| * TAP5-292 |
| */ |
| @Test |
| public void failed_field_resource_injection() |
| { |
| Registry r = buildRegistry(FieldResourceInjectionModule.class); |
| |
| StringTransformer s = r.getService(StringTransformer.class); |
| |
| try |
| { |
| s.transform("hello"); |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| assertMessageContains(ex, |
| "Unable to determine resource value to inject into field 'unknownRunnable' (of type java.lang.Runnable)."); |
| } |
| } |
| |
| /** |
| * TAP5-291 |
| */ |
| @Test |
| public void post_injection_method_invoked() |
| { |
| Registry r = buildRegistry(PostInjectionMethodModule.class); |
| |
| Greeter g = r.getService(Greeter.class); |
| |
| assertEquals(g.getGreeting(), "Greetings from ServiceIdGreeter."); |
| } |
| |
| /** |
| * TAP5-429 |
| */ |
| @Test |
| public void contribute_to_unknown_service() |
| { |
| try |
| { |
| buildRegistry(InvalidContributeDefModule.class); |
| unreachable(); |
| } |
| catch (IllegalArgumentException ex) |
| { |
| assertMessageContains( |
| ex, |
| "Contribution org.apache.tapestry5.ioc.InvalidContributeDefModule.contributeDoesNotExist(Configuration)", |
| "is for service 'DoesNotExist', which does not exist."); |
| } |
| } |
| |
| /** |
| * TAP5-436 |
| */ |
| @Test |
| public void extra_methods_on_module_class_are_errors() |
| { |
| try |
| { |
| buildRegistry(ExtraMethodsModule.class); |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| assertMessageContains(ex, |
| "Module class org.apache.tapestry5.ioc.ExtraMethodsModule contains unrecognized public methods: ", |
| "thisMethodIsInvalid()", "soIsThisMethod()."); |
| } |
| } |
| |
| /** |
| * TAP5-430 |
| */ |
| @Test |
| public void service_builder_method_marked_for_no_decoration() |
| { |
| Registry r = buildRegistry(PreventDecorationModule.class); |
| |
| StringTransformer st = r.getService(StringTransformer.class); |
| |
| assertEquals(st.transform("tapestry"), "TAPESTRY"); |
| |
| r.shutdown(); |
| } |
| |
| /** |
| * TAP5-430 |
| */ |
| @Test |
| public void bind_service_marked_for_no_decoration_explicitly() |
| { |
| Registry r = buildRegistry(PreventDecorationModule.class); |
| |
| Greeter g = r.getService(Greeter.class); |
| |
| assertEquals(g.getGreeting(), "Greetings from ServiceIdGreeter."); |
| |
| r.shutdown(); |
| } |
| |
| /** |
| * TAP5-430 |
| */ |
| @Test |
| public void bind_service_with_prevent_service_decoration_annotations_on_implementation_class() |
| { |
| Registry r = buildRegistry(PreventDecorationModule.class); |
| |
| Rocket rocket = r.getService(Rocket.class); |
| |
| assertEquals(rocket.getCountdown(), "3, 2, 1, Launch!"); |
| |
| r.shutdown(); |
| } |
| |
| /** |
| * TAP5-437 |
| */ |
| @Test |
| public void successful_ordered_configuration_override() |
| { |
| Registry r = buildRegistry(FredModule.class, BarneyModule.class, ConfigurationOverrideModule.class); |
| |
| NameListHolder service = r.getService("OrderedNames", NameListHolder.class); |
| |
| List<String> names = service.getNames(); |
| |
| assertEquals(names, Arrays.asList("BARNEY", "WILMA", "Mr. Flintstone")); |
| } |
| |
| /** |
| * TAP5-437 |
| */ |
| @Test |
| public void failed_ordered_configuration_override() |
| { |
| Registry r = buildRegistry(FredModule.class, BarneyModule.class, FailedConfigurationOverrideModule.class); |
| |
| NameListHolder service = r.getService("OrderedNames", NameListHolder.class); |
| |
| try |
| { |
| service.getNames(); |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| assertMessageContains( |
| ex, |
| "Failure processing override from org.apache.tapestry5.ioc.FailedConfigurationOverrideModule.contributeOrderedNames(OrderedConfiguration)", |
| "Override for object 'wilma' is invalid as it does not match an existing object."); |
| } |
| } |
| |
| /** |
| * TAP5-437 |
| */ |
| @Test |
| public void duplicate_ordered_configuration_override() |
| { |
| Registry r = buildRegistry(FredModule.class, BarneyModule.class, ConfigurationOverrideModule.class, |
| DuplicateConfigurationOverrideModule.class); |
| |
| NameListHolder service = r.getService("OrderedNames", NameListHolder.class); |
| |
| try |
| { |
| service.getNames(); |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| // Can't get too specific since we don't know which module will get processed first |
| assertMessageContains(ex, "Error invoking service contribution method ", |
| "Contribution 'fred' has already been overridden"); |
| } |
| } |
| |
| /** |
| * TAP5-437 |
| */ |
| @Test |
| public void mapped_configuration_override() |
| { |
| Registry r = buildRegistry(FredModule.class, BarneyModule.class, ConfigurationOverrideModule.class); |
| |
| StringLookup sl = r.getService(StringLookup.class); |
| |
| // Due to override wilma to null: |
| |
| assertListsEquals(sl.keys(), "barney", "betty", "fred"); |
| |
| assertEquals(sl.lookup("fred"), "Mr. Flintstone"); |
| } |
| |
| /** |
| * TAP5-437 |
| */ |
| @Test |
| public void nonmatching_mapped_configuration_override() |
| { |
| Registry r = buildRegistry(FredModule.class, BarneyModule.class, |
| NonmatchingMappedConfigurationOverrideModule.class); |
| |
| StringLookup sl = r.getService(StringLookup.class); |
| |
| try |
| { |
| sl.keys(); |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| assertMessageContains( |
| ex, |
| "Exception constructing service 'StringLookup'", |
| "Error invoking service builder method org.apache.tapestry5.ioc.FredModule.buildStringLookup(Map) ", |
| "Override for key alley cat (at org.apache.tapestry5.ioc.util.NonmatchingMappedConfigurationOverrideModule.contributeStringLookup(MappedConfiguration)", |
| "does not match an existing key."); |
| } |
| } |
| |
| /** |
| * TAP-437 |
| */ |
| @Test |
| public void duplicate_override_for_mapped_configuration() |
| { |
| Registry r = buildRegistry(FredModule.class, BarneyModule.class, ConfigurationOverrideModule.class, |
| DuplicateConfigurationOverrideModule.class); |
| |
| StringLookup sl = r.getService(StringLookup.class); |
| |
| try |
| { |
| sl.keys(); |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| assertMessageContains(ex, "Error invoking service contribution method", |
| "Contribution key fred has already been overridden"); |
| } |
| } |
| |
| /** |
| * TAP5-316 |
| */ |
| @Test |
| public void service_override() |
| { |
| Registry r = buildRegistry(GreeterServiceOverrideModule.class); |
| |
| Greeter g = r.getObject(Greeter.class, null); |
| |
| assertEquals(g.getGreeting(), "Override Greeting"); |
| } |
| |
| /** |
| * TAP5-60 |
| */ |
| @Test |
| public void non_void_advisor_method_is_error() |
| { |
| try |
| { |
| buildRegistry(NonVoidAdvisorMethodModule.class); |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| assertMessageContains( |
| ex, |
| "Advise method org.apache.tapestry5.ioc.NonVoidAdvisorMethodModule.adviseFoo(MethodAdviceReceiver)", |
| "does not return void."); |
| } |
| } |
| |
| /** |
| * TAP5-60 |
| */ |
| @Test |
| public void advisor_methods_must_take_a_method_advisor_parameter() |
| { |
| try |
| { |
| buildRegistry(AdviceMethodMissingAdvisorParameterModule.class); |
| unreachable(); |
| } |
| catch (RuntimeException ex) |
| { |
| assertMessageContains(ex, |
| "Advise method org.apache.tapestry5.ioc.AdviceMethodMissingAdvisorParameterModule.adviseBar()", |
| "must take a parameter of type org.apache.tapestry5.ioc.MethodAdviceReceiver."); |
| } |
| } |
| |
| /** |
| * TAP5-60 |
| */ |
| @Test |
| public void advise_services() |
| { |
| Registry r = buildRegistry(AdviceDemoModule.class); |
| |
| Greeter g = r.getService(Greeter.class); |
| |
| assertEquals(g.getGreeting(), "ADVICE IS EASY!"); |
| } |
| |
| @Test |
| public void contribute_by_annotation() |
| { |
| Registry r = buildRegistry(AlphabetModule.class, AlphabetModule2.class); |
| |
| NameListHolder greek = r.getService("Greek", NameListHolder.class); |
| |
| assertListsEquals(greek.getNames(), "Alpha", "Beta", "Gamma", "Delta"); |
| |
| NameListHolder anotherGreek = r.getService("AnotherGreek", NameListHolder.class); |
| |
| assertListsEquals(anotherGreek.getNames(), "Alpha", "Beta", "Gamma", "Delta", "Epsilon"); |
| |
| NameListHolder hebrew = r.getService("Hebrew", NameListHolder.class); |
| |
| assertListsEquals(hebrew.getNames(), "Alef", "Bet", "Gimel", "Dalet", "He", "Vav"); |
| |
| NameListHolder2 holder = r.getService("ServiceWithEmptyConfiguration", NameListHolder2.class); |
| |
| assertEquals(holder.getNames(), Arrays.asList()); |
| |
| r.shutdown(); |
| |
| } |
| |
| @Test |
| public void contribute_by_annotation_to_nonexistent_service() |
| { |
| try |
| { |
| buildRegistry(InvalidContributeDefModule2.class); |
| unreachable(); |
| } |
| catch (Exception e) |
| { |
| assertMessageContains( |
| e, |
| "Contribution org.apache.tapestry5.ioc.InvalidContributeDefModule2.provideConfiguration(OrderedConfiguration)", |
| "is for service 'interface org.apache.tapestry5.ioc.NameListHolder'", |
| "qualified with marker annotations [", "interface org.apache.tapestry5.ioc.BlueMarker", |
| "interface org.apache.tapestry5.ioc.RedMarker", "], which does not exist."); |
| } |
| } |
| |
| @Test |
| public void contribute_by_annotation_wrong_marker() |
| { |
| try |
| { |
| buildRegistry(InvalidContributeDefModule3.class); |
| unreachable(); |
| } |
| catch (Exception e) |
| { |
| assertMessageContains( |
| e, |
| "Contribution org.apache.tapestry5.ioc.InvalidContributeDefModule3.provideConfiguration(OrderedConfiguration)", |
| "is for service 'interface org.apache.tapestry5.ioc.NameListHolder'", |
| "qualified with marker annotations [interface org.apache.tapestry5.ioc.BlueMarker], which does not exist."); |
| } |
| } |
| |
| @Test |
| public void service_resources_available_when_invoking_contribute_method() |
| { |
| Registry r = buildRegistry(InjectionCheckModule.class); |
| |
| InjectionCheck service = r.getService(InjectionCheck.class); |
| |
| assertSame(service.getLogger(), service.getValue("logger")); |
| |
| IndirectResources il = (IndirectResources) service.getValue("indirect-resources"); |
| |
| assertSame(service.getLogger(), il.getLogger()); |
| assertSame(service.getLogger(), il.getResources().getLogger()); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void advise_by_annotation() |
| { |
| Registry r = buildRegistry(GreeterModule2.class, AdviseByMarkerModule.class); |
| |
| Greeter green = r.getService("GreenGreeter", Greeter.class); |
| |
| assertEquals(green.getGreeting(), "gamma[beta[alpha[Green]]]"); |
| |
| r.shutdown(); |
| |
| } |
| |
| @Test |
| public void advise_by_locale_annotation() |
| { |
| Registry r = buildRegistry(GreeterModule2.class, AdviseByMarkerModule.class); |
| |
| Greeter red = r.getService("RedGreeter", Greeter.class); |
| |
| assertEquals(red.getGreeting(), "delta[Red]"); |
| |
| r.shutdown(); |
| |
| } |
| |
| @Test |
| public void decorate_by_annotation() |
| { |
| Registry r = buildRegistry(GreeterModule2.class, DecorateByMarkerModule.class); |
| |
| Greeter green = r.getService("GreenGreeter", Greeter.class); |
| |
| assertEquals(green.getGreeting(), "Decorated by foo[Decorated by baz[Decorated by bar[Green]]]"); |
| |
| r.shutdown(); |
| |
| } |
| |
| @Test |
| public void decorate_by_locale_annotation() |
| { |
| Registry r = buildRegistry(GreeterModule2.class, DecorateByMarkerModule.class); |
| |
| Greeter red = r.getService("RedGreeter", Greeter.class); |
| |
| assertEquals(red.getGreeting(), "Decorated by barney[Red]"); |
| |
| r.shutdown(); |
| |
| } |
| |
| @Test |
| public void startup_inside_module() |
| { |
| Registry r = buildRegistry(StartupModule2.class); |
| |
| assertFalse(StartupModule2.staticStartupInvoked); |
| |
| assertFalse(StartupModule2.instanceStartupInvoked); |
| |
| r.performRegistryStartup(); |
| |
| assertTrue(StartupModule2.staticStartupInvoked); |
| |
| assertTrue(StartupModule2.instanceStartupInvoked); |
| |
| r.shutdown(); |
| } |
| |
| @Test |
| public void case_ignored_in_service_id_of_contribute_method() |
| { |
| Registry r = buildRegistry(CaseInsensitiveContributeMethodModule.class); |
| |
| SymbolSource symbolSource = r.getService(SymbolSource.class); |
| |
| assertEquals(symbolSource.valueForSymbol("it"), "works"); |
| |
| r.shutdown(); |
| } |
| } |