On 4/23/06, Igor Vaynberg <[EMAIL PROTECTED]> wrote: > why dont you at least attach the code to an email message in this list. that > way it will be archived and if somone searches down the road they can use > it.
Hereyago. Attached SpringSetterInjector.java and the accompanying unit test. -Lasse-
package wicket.spring; import java.lang.reflect.Method; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.springframework.beans.factory.BeanFactory; import wicket.Component; import wicket.application.IComponentInstantiationListener; public class SpringSetterInjector implements IComponentInstantiationListener { private BeanFactory springContext; /** * @param springContext * The Spring <tt>BeanFactory</tt> (typically an * <tt>ApplicationContext</tt>) to use for obtaining * dependencies. */ public SpringSetterInjector(BeanFactory springContext) { this.springContext = springContext; } /** * Implements the <tt>IComponentInstantiationListener</tt> interface. */ public void onInstantiation(Component component) { Collection setters = getSetters(component); for (Iterator i = setters.iterator(); i.hasNext();) { injectIfMatchingBeanFound(component, (Method) i.next()); } } private void injectIfMatchingBeanFound(Component component, Method method) { String beanName = getBeanNameFor(method); if (springContext.containsBean(beanName)) { Object dependency = springContext.getBean(beanName); try { if (!method.isAccessible()) { method.setAccessible(true); } method.invoke(component, new Object[] { dependency }); } catch (Throwable e) { while (e.getCause() != null) { e = e.getCause(); } e.printStackTrace(); throw new RuntimeException(e); } } } private Collection getSetters(Component component) { return getSetters(component.getClass()).values(); } private Map getSetters(Class c) { Map setters = getSettersDeclaredIn(c); if (c.getSuperclass() != null) { Map parentSetters = getSetters(c.getSuperclass()); addIfKeyDoesNotExistYet(setters, parentSetters); } return setters; } private void addIfKeyDoesNotExistYet(Map to, Map from) { for (Iterator i = from.keySet().iterator(); i.hasNext();) { String setterName = (String) i.next(); if (!to.containsKey(setterName)) { to.put(setterName, from.get(setterName)); } } } private Map getSettersDeclaredIn(Class c) { Map setters = new HashMap(); Method[] methods = c.getDeclaredMethods(); for (int i = 0; i < methods.length; i++) { Method setter = methods[i]; if (isSetter(setter)) { setters.put(setter.getName(), setter); } } return setters; } private boolean isSetter(Method method) { if (!method.getName().startsWith("set")) { return false; } if (method.getName().length() < 4) { return false; } if (method.getParameterTypes().length != 1) { return false; } if (method.getReturnType() != void.class) { return false; } return true; } private String getBeanNameFor(Method method) { String methodName = method.getName(); String beanName = methodName.substring(3, 4).toLowerCase(); if (methodName.length() > 4) { beanName = beanName + methodName.substring(4); } return beanName; } }
package wicket.spring; import junit.framework.TestCase; import org.easymock.MockControl; import org.easymock.classextension.MockClassControl; import org.springframework.beans.BeansException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import wicket.Component; import wicket.markup.MarkupStream; import wicket.spring.test.ApplicationContextMock; public class TestSpringSetterInjector extends TestCase { private ApplicationContextMock ctx; @Override protected void setUp() throws Exception { ctx = new MockApplicationContext(); ctx.putBean("dependency", new Dependency("dependency")); ctx.putBean("a", new Dependency("a")); ctx.putBean("b", new Dependency("b")); ctx.putBean("privateDependency", new Dependency("private")); ctx.putBean("packagePrivateDependency", new Dependency("package")); ctx.putBean("protectedDependency", new Dependency("protected")); ctx.putBean("inheritedDependency", new Dependency("inherited")); } /** * Represents a dependency which a component needs. */ private class Dependency { private String name; public Dependency(String name) { this.name = name; } @Override public String toString() { return "Dependency(\"" + name + "\")"; } @Override public int hashCode() { return name.hashCode(); } @Override public boolean equals(Object o) { if (!o.getClass().equals(getClass())) { return false; } return ((Dependency) o).name.equals(this.name); } } private abstract class SampleBaseComponent extends Component { public SampleBaseComponent(String name) { super(name); } public void setInheritedDependency(Dependency impl) { } } /** * Represents a component in need of dependency injection. */ private class SampleComponent extends SampleBaseComponent { public SampleComponent(String name) { super(name); } public void onRender(MarkupStream stream) { } // ----- VALID SETTERS ----- // tests that a regular dependency is injected correctly public void setDependency(Dependency impl) { } // tests that one-letter setter names are handled correctly public void setA(Dependency impl) { } // tests that private setters are injected private void setPrivateDependency(Dependency impl) { } // tests that package private setters are injected void setPackagePrivateDependency(Dependency impl) { } // tests that protected setters are injected protected void setProtectedDependency(Dependency impl) { } // ----- NOT VALID SETTERS ----- // tests that only valid setter names are considered public void set(Dependency impl) { } // tests that only setters with matching beans are injected public void setNoSuchBean(Dependency impl) { } // tests that only void methods are injected public boolean setB(Dependency impl) { return false; } // tests that methods taking more than one argument are not injected public void setA(Dependency impl, String thisIsNotSetter) { } // tests that methods that don't take arguments are not injected public void setA() { } } /** * Had to extend ApplicationContextMock because it doesn't implement * getBean(String) for some reason. */ private class MockApplicationContext extends ApplicationContextMock { @Override public Object getBean(String name) throws BeansException { return getBean(name, Object.class); } @Override public boolean containsBean(String name) { try { return getBean(name) != null; } catch (NoSuchBeanDefinitionException e) { return false; } } } /** * The test method that verifies all injections expected for a * SampleComponent. It uses EasyMock for 1) being able to instantiate the * SampleComponent without an Application/Session/etc and 2) for easily * verifying that only the expected method calls occurred. */ public void testComponentGetsInjectedWithDependency() throws Exception { MockControl control = MockClassControl .createControl(SampleComponent.class); SampleComponent comp = (SampleComponent) control.getMock(); comp.setDependency(bean("dependency")); comp.setA(bean("a")); comp.setPackagePrivateDependency(bean("packagePrivateDependency")); comp.setProtectedDependency(bean("protectedDependency")); comp.setPrivateDependency(bean("privateDependency")); comp.setInheritedDependency(bean("inheritedDependency")); control.replay(); new SpringSetterInjector(ctx).onInstantiation(comp); control.verify(); } private Dependency bean(String name) { return (Dependency) ctx.getBean(name); } }