
org.jboss.spring.support.SpringInjectionSupport Maven / Gradle / Ivy
The newest version!
/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.spring.support;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.jboss.annotation.spring.Spring;
import org.jboss.logging.Logger;
import org.jboss.spring.util.Version;
import org.jboss.spring.util.VersionProvider;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
/**
* Injects objects from bean factory located in JNDI at jndiName gained
* from @Spring annotation's field jndiName.
* It is applied to setter methods and fields annotated with @Spring annotation.
*
* @author Ales Justin
* @author Marius Bogoevici
* @see MethodComparator Excludes overridden @Spring annotated methods
* Class type check is performed before actual setting.
*/
public abstract class SpringInjectionSupport {
protected Logger log = Logger.getLogger(getClass());
private final Comparator METHOD_COMPARATOR = new MethodComparator();
protected Object inject(Object target) throws Exception {
log.debug("Invoking Spring injection: " + target.getClass().getName());
Method[] methods = getAllMethods(target);
for (Method m : methods) {
Spring spring = m.getAnnotation(Spring.class);
if (spring != null) {
if (isSetterMethod(m)) {
injectToMethod(target, m, spring);
} else {
log.warn("Spring annotation only allowed on setter methods.");
}
}
}
Field[] fields = getAllFields(target);
for (Field f : fields) {
Spring spring = f.getAnnotation(Spring.class);
if (spring != null) {
injectToField(target, f, spring);
}
}
return target;
}
protected Method[] getAllMethods(Object bean) {
Class> beanClass = bean.getClass();
Set methods = new TreeSet(METHOD_COMPARATOR);
while (beanClass != Object.class) {
methods.addAll(Arrays.asList(beanClass.getDeclaredMethods()));
beanClass = beanClass.getSuperclass();
}
return methods.toArray(new Method[methods.size()]);
}
protected Field[] getAllFields(Object bean) {
Class> beanClass = bean.getClass();
List fields = new ArrayList();
while (beanClass != Object.class) {
fields.addAll(Arrays.asList(beanClass.getDeclaredFields()));
beanClass = beanClass.getSuperclass();
}
return fields.toArray(new Field[fields.size()]);
}
private boolean isSetterMethod(Method m) {
return m.getName().startsWith("set") && m.getParameterTypes().length == 1;
}
/**
* Get jndi name for bean factory.
* Simple check for null or empty string is applied.
* You can override this in subclasses for any extra
* jndi name handling.
*
* @param jndiName the current jndi name
* @return jndiName parameter
*/
protected String getJndiName(String jndiName) {
if (jndiName == null || jndiName.length() == 0) {
throw new IllegalArgumentException("Empty BeanFactory jndi name.");
}
// On JBoss AS 7, custom bindings can be created only under java:/jboss
// so this is what the deployer does
// Append the prefix if the path is relative - should allow deployments that
// worked in JBoss AS 5/6 to be portable in JBoss AS 7
if (VersionProvider.VERSION.compareTo(Version.AS_7) >= 0 && !jndiName.startsWith("java:")) {
jndiName = "java:jboss/" + jndiName;
}
return jndiName;
}
private Object getObjectFromBeanFactory(Spring spring, String defaultBeanName, Class> beanType) throws Exception {
String jndiName = getJndiName(spring.jndiName());
BeanFactory beanFactory = lookup(jndiName, BeanFactory.class);
String beanName = spring.bean();
if (beanName != null && beanName.length() > 0) {
return beanFactory.getBean(beanName, beanType);
} else {
// by type injection
if (beanFactory instanceof ListableBeanFactory) {
ListableBeanFactory lbf = (ListableBeanFactory) beanFactory;
Map, ?> beans = lbf.getBeansOfType(beanType);
if (beans.size() > 1) {
Object bean = beans.get(defaultBeanName);
if (bean == null) {
throw new IllegalArgumentException("More than one bean of type: " + beanType);
}
return bean;
} else if (beans.size() == 1) {
return beans.values().iterator().next();
} else {
throw new IllegalArgumentException("No such bean by type: " + beanType);
}
} else {
// bean factory is not listable - use default bean name
return beanFactory.getBean(defaultBeanName, beanType);
}
}
}
/**
* @param jndiName the JNDI location of the Spring context relative to
* 'java:' on JBoss AS5 and JBoss AS6 and 'java:jboss' on JBoss AS7
* @param expectedClass the expected type of the retrieved object
* @return
*/
private T lookup(String jndiName, Class expectedClass) {
Object instance;
try {
InitialContext initialContext = new InitialContext();
instance = initialContext.lookup(jndiName);
if (!(expectedClass.isAssignableFrom(instance.getClass()))) {
throw new IllegalArgumentException("Cannot retrieve an " +
expectedClass.getName() + " from " + jndiName + " - a " +
instance.getClass().getName() + " found instead");
}
} catch (NamingException e) {
throw new IllegalStateException(e);
}
return expectedClass.cast(instance);
}
private void injectToMethod(Object target, Method method, Spring spring) throws Exception {
String defaultBeanName = getDefaultBeanName(method);
Object bean = getObjectFromBeanFactory(spring, defaultBeanName, method.getParameterTypes()[0]);
logInjection(spring, bean, target, method);
method.setAccessible(true);
method.invoke(target, bean);
}
protected String getDefaultBeanName(Method method) {
StringBuffer buffer = new StringBuffer();
buffer.append(method.getName().substring(3, 3).toLowerCase());
buffer.append(method.getName().substring(4));
return buffer.toString();
}
private void injectToField(Object target, Field field, Spring spring) throws Exception {
String defaultBeanName = getDefaultBeanName(field);
Object bean = getObjectFromBeanFactory(spring, defaultBeanName, field.getType());
logInjection(spring, bean, target, field);
field.setAccessible(true);
field.set(target, bean);
}
protected String getDefaultBeanName(Field field) {
return field.getName();
}
private void logInjection(Spring spring, Object bean, Object target, Member m) {
log.debug("Injecting bean '" + spring.bean() + "' of class type " +
bean.getClass().getName() + " into " + target + " via " + m);
}
/**
* Equals on overridden methods.
* Any other solution?
*/
private class MethodComparator implements Comparator {
public int compare(Method m1, Method m2) {
String name1 = m1.getName();
String name2 = m2.getName();
if (name1.equals(name2)) {
Class> returnType1 = m1.getReturnType();
Class> returnType2 = m2.getReturnType();
Class>[] params1 = m1.getParameterTypes();
Class>[] params2 = m1.getParameterTypes();
if (params1.length == params2.length) {
if (returnType1.equals(returnType2)) {
int i;
int length = params1.length;
for (i = 0; i < length; i++) {
if (!params1[i].equals(params2[i])) {
break;
}
}
//not equal
if (i < length) {
return params1[i].getName().compareTo(params2[i].getName());
} else {
//overridden method
if (m1.getAnnotation(Spring.class) != null) {
log.warn("Found overridden @Spring annotated method: " + m1);
}
return 0;
}
} else {
return returnType1.getName().compareTo(returnType2.getName());
}
} else {
return params1.length - params2.length;
}
} else {
return name1.compareTo(name2);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy