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.internal;
016:
017: import static org.apache.tapestry.ioc.internal.IOCMessages.buildMethodConflict;
018: import static org.easymock.EasyMock.and;
019: import static org.easymock.EasyMock.contains;
020:
021: import java.lang.reflect.Method;
022: import java.util.Set;
023:
024: import org.apache.commons.logging.Log;
025: import org.apache.tapestry.ioc.AutobuildModule;
026: import org.apache.tapestry.ioc.IOCConstants;
027: import org.apache.tapestry.ioc.ObjectCreator;
028: import org.apache.tapestry.ioc.ServiceBuilderResources;
029: import org.apache.tapestry.ioc.StringHolder;
030: import org.apache.tapestry.ioc.def.ContributionDef;
031: import org.apache.tapestry.ioc.def.DecoratorDef;
032: import org.apache.tapestry.ioc.def.ModuleDef;
033: import org.apache.tapestry.ioc.def.ServiceDef;
034: import org.apache.tapestry.ioc.internal.services.ClassFactoryImpl;
035: import org.apache.tapestry.ioc.internal.util.InternalUtils;
036: import org.apache.tapestry.ioc.services.ClassFactory;
037: import org.apache.tapestry.ioc.test.IOCTestCase;
038: import org.testng.annotations.AfterClass;
039: import org.testng.annotations.BeforeClass;
040: import org.testng.annotations.Test;
041:
042: public class DefaultModuleDefImplTest extends IOCTestCase {
043: private ClassFactory _classFactory;
044:
045: @BeforeClass
046: public void setup() {
047: _classFactory = new ClassFactoryImpl();
048: }
049:
050: @AfterClass
051: public void cleanup() {
052: _classFactory = null;
053: }
054:
055: @Test
056: public void simple_module() throws Exception {
057: String className = SimpleModule.class.getName();
058:
059: Log log = mockLog();
060:
061: replay();
062:
063: // BigDecimal is arbitrary, any class would do.
064:
065: ModuleDef md = new DefaultModuleDefImpl(SimpleModule.class,
066: log, _classFactory);
067:
068: assertEquals(md.toString(), "ModuleDef[" + className
069: + " Barney, Fred, Wilma]");
070:
071: Set<String> ids = md.getServiceIds();
072:
073: assertEquals(ids.size(), 3);
074: assertTrue(ids.contains("Fred"));
075: assertTrue(ids.contains("Barney"));
076: assertTrue(ids.contains("Wilma"));
077:
078: ServiceDef sd = md.getServiceDef("Fred");
079:
080: assertEquals(sd.getServiceId(), "Fred");
081:
082: assertEquals(sd.getServiceInterface(), FieService.class);
083:
084: assertTrue(sd.toString().contains(className + ".buildFred()"));
085: assertEquals(sd.getServiceScope(), IOCConstants.DEFAULT_SCOPE);
086: assertEquals(sd.isEagerLoad(), false);
087:
088: sd = md.getServiceDef("Wilma");
089: assertEquals(sd.isEagerLoad(), true);
090:
091: // Now the decorator method.
092:
093: Set<DecoratorDef> defs = md.getDecoratorDefs();
094:
095: assertEquals(defs.size(), 1);
096:
097: DecoratorDef dd = defs.iterator().next();
098:
099: assertEquals(dd.getDecoratorId(), "Logging");
100: assertTrue(dd.toString().contains(
101: className + ".decorateLogging(Class, Object)"));
102:
103: verify();
104: }
105:
106: @Test
107: public void default_service_id_from_return_type() {
108: Log log = mockLog();
109:
110: replay();
111:
112: ModuleDef def = new DefaultModuleDefImpl(
113: DefaultServiceIdModule.class, log, null);
114:
115: assertEquals(def.getServiceIds().size(), 1);
116:
117: ServiceDef sd = def.getServiceDef("FieService");
118:
119: assertEquals(sd.getServiceId(), "FieService");
120:
121: verify();
122: }
123:
124: /** Two different methods both claim to build the same service. */
125: @Test
126: public void service_id_conflict() throws Exception {
127: Method conflictMethod = ServiceIdConflictMethodModule.class
128: .getMethod("buildFred");
129: String conflictMethodString = InternalUtils.asString(
130: conflictMethod, _classFactory);
131:
132: String expectedMethod = InternalUtils.asString(
133: ServiceIdConflictMethodModule.class.getMethod(
134: "buildFred", Object.class), _classFactory);
135:
136: Log log = mockLog();
137:
138: log.warn(buildMethodConflict(conflictMethodString,
139: expectedMethod), null);
140:
141: replay();
142:
143: // BigDecimal is arbitrary, any class would do.
144:
145: ModuleDef md = new DefaultModuleDefImpl(
146: ServiceIdConflictMethodModule.class, log, _classFactory);
147:
148: Set<String> ids = md.getServiceIds();
149:
150: assertEquals(ids.size(), 1);
151: assertTrue(ids.contains("Fred"));
152:
153: ServiceDef sd = md.getServiceDef("Fred");
154:
155: assertEquals(sd.getServiceId(), "Fred");
156:
157: assertEquals(sd.getServiceInterface(), FieService.class);
158:
159: // The methods are considered in ascending order, by name, then descending order, by
160: // parameter count. So the grinder will latch onto the method that takes a parameter,
161: // and consider the other method (with no parameters) the conflict.
162:
163: assertEquals(sd.toString(), expectedMethod.toString());
164:
165: verify();
166: }
167:
168: @Test
169: public void builder_method_returns_void() throws Exception {
170: Method m = VoidBuilderMethodModule.class.getMethod("buildNull");
171:
172: Log log = mockLog();
173:
174: log.warn(IOCMessages.buildMethodWrongReturnType(m), null);
175:
176: replay();
177:
178: ModuleDef md = new DefaultModuleDefImpl(
179: VoidBuilderMethodModule.class, log, null);
180:
181: assertTrue(md.getServiceIds().isEmpty());
182:
183: verify();
184: }
185:
186: @Test
187: public void builder_method_returns_array() throws Exception {
188: Method m = BuilderMethodModule.class
189: .getMethod("buildStringArray");
190:
191: Log log = mockLog();
192:
193: log.warn(IOCMessages.buildMethodWrongReturnType(m), null);
194:
195: replay();
196:
197: ModuleDef md = new DefaultModuleDefImpl(
198: BuilderMethodModule.class, log, null);
199:
200: assertTrue(md.getServiceIds().isEmpty());
201:
202: verify();
203: }
204:
205: @Test
206: public void decorator_method_returns_void() throws Exception {
207: invalidDecoratorMethod(VoidDecoratorMethodModule.class,
208: "decorateVoid");
209: }
210:
211: private void invalidDecoratorMethod(Class moduleClass,
212: String methodName) throws NoSuchMethodException {
213: Method m = moduleClass.getMethod(methodName, Object.class);
214:
215: Log log = mockLog();
216:
217: log.warn(IOCMessages.decoratorMethodWrongReturnType(m), null);
218:
219: replay();
220:
221: ModuleDef md = new DefaultModuleDefImpl(moduleClass, log, null);
222:
223: assertTrue(md.getDecoratorDefs().isEmpty());
224:
225: verify();
226: }
227:
228: @Test
229: public void decorator_method_returns_primitive() throws Exception {
230: invalidDecoratorMethod(PrimitiveDecoratorMethodModule.class,
231: "decoratePrimitive");
232: }
233:
234: @Test
235: public void decorator_method_returns_array() throws Exception {
236: invalidDecoratorMethod(ArrayDecoratorMethodModule.class,
237: "decorateArray");
238: }
239:
240: @Test
241: public void decorator_method_does_not_include_delegate_parameter()
242: throws Exception {
243: Class moduleClass = NoDelegateDecoratorMethodModule.class;
244: Method m = moduleClass.getMethod("decorateNoDelegate");
245:
246: Log log = mockLog();
247:
248: log.warn(IOCMessages.decoratorMethodNeedsDelegateParameter(m),
249: null);
250:
251: replay();
252:
253: ModuleDef md = new DefaultModuleDefImpl(moduleClass, log, null);
254:
255: assertTrue(md.getDecoratorDefs().isEmpty());
256:
257: verify();
258: }
259:
260: @Test
261: public void contribution_without_annotation() {
262: attemptConfigurationMethod(SimpleModule.class, "Barney",
263: "contributeBarney(Configuration)");
264: }
265:
266: @Test
267: public void ordered_contribution_method() {
268: attemptConfigurationMethod(OrderedConfigurationModule.class,
269: "Ordered", "contributeOrdered(OrderedConfiguration)");
270: }
271:
272: @Test
273: public void mapped_contribution_method() {
274: attemptConfigurationMethod(MappedConfigurationModule.class,
275: "Mapped", "contributeMapped(MappedConfiguration)");
276: }
277:
278: private void attemptConfigurationMethod(Class moduleClass,
279: String expectedServiceId, String expectedMethodSignature) {
280: Log log = mockLog();
281:
282: replay();
283:
284: ModuleDef md = new DefaultModuleDefImpl(moduleClass, log,
285: _classFactory);
286:
287: Set<ContributionDef> defs = md.getContributionDefs();
288:
289: assertEquals(defs.size(), 1);
290:
291: ContributionDef cd = defs.iterator().next();
292:
293: // The target service id is derived from the method name
294:
295: assertEquals(cd.getServiceId(), expectedServiceId);
296:
297: // Can't be exact, because the source file & line number are probably attached (and those
298: // can change)
299:
300: assertTrue(cd.toString().contains(
301: moduleClass.getName() + "." + expectedMethodSignature));
302:
303: verify();
304: }
305:
306: @Test
307: public void contribution_with_too_many_parameters()
308: throws Exception {
309: Class moduleClass = TooManyContributionParametersModule.class;
310: Method m = findMethod(moduleClass, "contributeTooMany");
311:
312: Log log = mockLog();
313: log.warn(IOCMessages.tooManyContributionParameters(m));
314:
315: replay();
316:
317: ModuleDef md = new DefaultModuleDefImpl(moduleClass, log, null);
318:
319: assertTrue(md.getContributionDefs().isEmpty());
320:
321: verify();
322: }
323:
324: @Test
325: public void contribution_with_no_contribution_parameter()
326: throws Exception {
327: Class moduleClass = NoUsableContributionParameterModule.class;
328: Method m = findMethod(moduleClass, "contributeNoParameter");
329:
330: Log log = mockLog();
331: log.warn(IOCMessages.noContributionParameter(m));
332:
333: replay();
334:
335: ModuleDef md = new DefaultModuleDefImpl(moduleClass, log, null);
336:
337: assertTrue(md.getContributionDefs().isEmpty());
338:
339: verify();
340: }
341:
342: @Test
343: public void simple_binder_method() {
344: Log log = mockLog();
345:
346: replay();
347:
348: ModuleDef md = new DefaultModuleDefImpl(AutobuildModule.class,
349: log, _classFactory);
350:
351: ServiceDef sd = md.getServiceDef("StringHolder");
352:
353: assertEquals(sd.getServiceInterface(), StringHolder.class);
354: assertEquals(sd.getServiceId(), "StringHolder");
355: assertEquals(sd.getServiceScope(), IOCConstants.DEFAULT_SCOPE);
356: assertFalse(sd.isEagerLoad());
357:
358: verify();
359: }
360:
361: @Test
362: public void bind_service_with_all_options() {
363: Log log = mockLog();
364:
365: replay();
366:
367: ModuleDef md = new DefaultModuleDefImpl(
368: ComplexAutobuildModule.class, log, _classFactory);
369:
370: ServiceDef sd = md.getServiceDef("SH");
371:
372: assertEquals(sd.getServiceInterface(), StringHolder.class);
373: assertEquals(sd.getServiceId(), "SH");
374: assertEquals(sd.getServiceScope(), "magic");
375: assertTrue(sd.isEagerLoad());
376:
377: verify();
378: }
379:
380: @Test
381: public void attempt_to_bind_a_service_with_no_public_constructor() {
382: Log log = mockLog();
383:
384: replay();
385:
386: try {
387: new DefaultModuleDefImpl(
388: UninstantiableAutobuildServiceModule.class, log,
389: _classFactory);
390: unreachable();
391: } catch (RuntimeException ex) {
392: assertEquals(
393: ex.getMessage(),
394: "Class org.apache.tapestry.ioc.internal.RunnableServiceImpl (implementation of service \'Runnable\') does not contain any public constructors.");
395: }
396:
397: verify();
398: }
399:
400: @Test
401: public void instance_method_bind_is_ignored() {
402: Log log = mockLog();
403:
404: log.error(and(contains(NonStaticBindMethodModule.class
405: .getName()), contains("but is an instance method")));
406:
407: replay();
408:
409: ModuleDef md = new DefaultModuleDefImpl(
410: NonStaticBindMethodModule.class, log, _classFactory);
411:
412: // Prove that the bind method was not invoke
413:
414: assertTrue(md.getServiceIds().isEmpty());
415:
416: verify();
417: }
418:
419: @Test
420: public void multiple_constructors_on_autobuild_service_implementation() {
421: Log log = mockLog();
422: ServiceBuilderResources resources = mockServiceBuilderResources();
423:
424: train_isDebugEnabled(log, true);
425:
426: // The point is, we're choosing the constructor with the largest number of parameters.
427:
428: log
429: .debug(contains("Invoking constructor org.apache.tapestry.ioc.internal.MultipleConstructorsAutobuildService(StringHolder)"));
430:
431: train_getServiceId(resources, "StringHolder");
432: train_getServiceLog(resources, log);
433: train_getServiceInterface(resources, StringHolder.class);
434: train_getService(resources, "ToUpperCaseStringHolder",
435: StringHolder.class, new ToUpperCaseStringHolder());
436:
437: replay();
438:
439: ModuleDef def = new DefaultModuleDefImpl(
440: MutlipleAutobuildServiceConstructorsModule.class, log,
441: _classFactory);
442:
443: ServiceDef sd = def.getServiceDef("StringHolder");
444:
445: assertNotNull(sd);
446:
447: ObjectCreator oc = sd.createServiceCreator(resources);
448:
449: StringHolder holder = (StringHolder) oc.createObject();
450:
451: holder.setValue("foo");
452: assertEquals(holder.getValue(), "FOO");
453:
454: verify();
455: }
456:
457: @Test
458: public void exception_from_inside_bind_method() {
459: Log log = mockLog();
460:
461: replay();
462:
463: try {
464: new DefaultModuleDefImpl(ExceptionInBindMethod.class, log,
465: _classFactory);
466: unreachable();
467: } catch (RuntimeException ex) {
468: assertTrue(ex
469: .getMessage()
470: .matches(
471: "Error invoking service binder method org.apache.tapestry.ioc.internal.ExceptionInBindMethod.bind\\(ServiceBinder\\) "
472: + "\\(at ExceptionInBindMethod.java:\\d+\\): Really, how often is this going to happen\\?"));
473: }
474:
475: verify();
476: }
477:
478: @Test
479: public void autoload_service_is_eager_load_via_annotation() {
480: Log log = mockLog();
481:
482: replay();
483:
484: ModuleDef md = new DefaultModuleDefImpl(
485: EagerLoadViaAnnotationModule.class, log, _classFactory);
486:
487: ServiceDef sd = md.getServiceDef("Runnable");
488:
489: assertTrue(sd.isEagerLoad());
490:
491: verify();
492: }
493: }
|