org.jboss.spring.support.SpringInjectionSupport.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.spring.support.SpringInjectionSupport.java

Source

/*
 * 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 <a href="mailto:ales.justin@genera-lynx.com">Ales Justin</a>
 * @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> 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<Method> methods = new TreeSet<Method>(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<Field> fields = new ArrayList<Field>();
        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> T lookup(String jndiName, Class<T> 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<Method> {

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