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