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);
    }

}

Reply via email to