uk.org.ponder.rsac.support.BeanDefUtil.java Source code

Java tutorial

Introduction

Here is the source code for uk.org.ponder.rsac.support.BeanDefUtil.java

Source

/*
 * Created on 04-Feb-2006
 */

// Some code in this file is subject to the ASF licence, terms follow:
/*
 * Copyright 2002-2004 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
// Under term 4c) of the licence, attribution details taken from the original
// source (AbstractBeanFactory.java) are as follows:
// * @author Rod Johnson
// * @author Juergen Hoeller
// * @since 15 April 2001
package uk.org.ponder.rsac.support;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.ManagedList;

import uk.org.ponder.conversion.StringArrayParser;
import uk.org.ponder.reflect.ClassGetter;
import uk.org.ponder.reflect.ReflectUtils;
import uk.org.ponder.rsac.BeanDefConverter;
import uk.org.ponder.saxalizer.AccessMethod;
import uk.org.ponder.saxalizer.support.MethodAnalyser;
import uk.org.ponder.saxalizer.support.SAXAccessMethod;
import uk.org.ponder.stringutil.StringList;
import uk.org.ponder.util.Logger;

public class BeanDefUtil {

    // NB an IMPORTANT CHANGE from the ABF version is change of references to
    // AbstractBeanFactory into ConfigurableListableBeanFactory. This considerably
    // weakens the type generality of our system but is unavoidable since
    // CLBF is the LOWEST level at which a getBeanDefinition() method is visible
    // to us, since we are no longer members of ABF. Since in practice we plan
    // to implement all RSAC using GenericApplicationContexts, this is not a
    // problem, and anyone who wants to inherit bean definitions FROM application
    // scope INTO request scope is i) deranged and ii) gets everything they
    // deserve.
    static AbstractBeanDefinition getMergedBeanDefinition(ConfigurableListableBeanFactory factory, String beanName,
            boolean includingAncestors) throws BeansException {
        try {
            return getMergedBeanDefinition(factory, beanName, factory.getBeanDefinition(beanName));
        } catch (NoSuchBeanDefinitionException ex) {
            if (includingAncestors && factory.getParentBeanFactory() instanceof ConfigurableListableBeanFactory) {
                return getMergedBeanDefinition((ConfigurableListableBeanFactory) factory.getParentBeanFactory(),
                        beanName, true);
            } else {
                throw ex;
            }
        }
    }

    // Need resistance for incompatibility in signature of BeanDefinition which 
    // has appeared in Spring 2.1 - this now features the getParentName method which
    // used only to appear in ChildBeanDefinition. There is now also "GenericBeanDefinition"
    // which is inherited off the common parent AbstractBeanDefinition.
    static String getParentDefinitionName(BeanDefinition bd) {
        try {
            if (ReflectUtils.hasMethod(bd, "getParentName")) {
                Method method = bd.getClass().getMethod("getParentName", SAXAccessMethod.emptyclazz);
                return (String) method.invoke(bd, SAXAccessMethod.emptyobj);
            }
        } catch (Exception e) {
        }
        return null;
    }

    static AbstractBeanDefinition getMergedBeanDefinition(ConfigurableListableBeanFactory factory, String beanName,
            BeanDefinition bd) throws BeansException {

        if (!(bd instanceof AbstractBeanDefinition)) {
            throw new IllegalArgumentException("Bean definition " + beanName + " of " + bd.getClass()
                    + " which is not descended from AbstractBeanDefinition can not be recognised");
        }
        AbstractBeanDefinition abd = (AbstractBeanDefinition) bd;
        String parentName = getParentDefinitionName(abd);
        if (parentName == null) {
            return abd;
        } else {
            AbstractBeanDefinition pbd = null;
            if (!beanName.equals(parentName)) {
                pbd = getMergedBeanDefinition(factory, parentName, true);
            } else {
                if (factory.getParentBeanFactory() instanceof ConfigurableListableBeanFactory) {
                    ConfigurableListableBeanFactory parentFactory = (ConfigurableListableBeanFactory) factory
                            .getParentBeanFactory();
                    pbd = getMergedBeanDefinition(parentFactory, parentName, true);
                } else {
                    throw new NoSuchBeanDefinitionException(parentName,
                            "Parent name '" + parentName + "' is equal to bean name '" + beanName
                                    + "' - cannot be resolved without an AbstractBeanFactory parent");
                }
            }

            // deep copy with overridden values
            pbd.overrideFrom(abd);
            return pbd;
        }

    }

    static RSACBeanInfo convertBeanDef(BeanDefinition origdef, String beanname,
            ConfigurableListableBeanFactory factory, MethodAnalyser abdAnalyser, BeanDefConverter converter) {
        RSACBeanInfo rbi = new RSACBeanInfo();
        AbstractBeanDefinition def = getMergedBeanDefinition(factory, beanname, origdef);
        MutablePropertyValues pvs = def.getPropertyValues();
        PropertyValue[] values = pvs.getPropertyValues();
        for (int j = 0; j < values.length; ++j) {
            PropertyValue thispv = values[j];
            Object beannames = BeanDefUtil.propertyValueToBeanName(thispv.getValue(), converter);
            boolean skip = false;
            // skip recording the dependency if it was unresolvable (some
            // unrecognised
            // type) or was a single-valued type referring to a static dependency.
            // NB - we now record ALL dependencies - bean-copying strategy
            // discontinued.
            if (beannames == null
            // || beannames instanceof String
            // && !blankcontext.containsBean((String) beannames)
            ) {
                skip = true;
            }
            if (!skip) {
                rbi.recordDependency(thispv.getName(), beannames);
            }
        }
        // NB - illegal cast here is unavoidable.
        // Bit of a problem here with Spring flow - apparently the bean class
        // will NOT be set for a "factory-method" bean UNTIL it has been
        // instantiated
        // via the logic in AbstractAutowireCapableBeanFactory l.376:
        // protected BeanWrapper instantiateUsingFactoryMethod(
        AbstractBeanDefinition abd = def;
        rbi.factorybean = abd.getFactoryBeanName();
        rbi.factorymethod = abd.getFactoryMethodName();
        rbi.initmethod = abd.getInitMethodName();
        rbi.destroymethod = abd.getDestroyMethodName();
        rbi.islazyinit = abd.isLazyInit();
        rbi.dependson = abd.getDependsOn();
        rbi.issingleton = abd.isSingleton();
        rbi.isabstract = abd.isAbstract();
        rbi.aliases = factory.containsBeanDefinition(beanname) ? factory.getAliases(beanname)
                : StringArrayParser.EMPTY_STRINGL;
        if (abd.hasConstructorArgumentValues()) {
            ConstructorArgumentValues cav = abd.getConstructorArgumentValues();
            boolean hasgeneric = !cav.getGenericArgumentValues().isEmpty();
            boolean hasindexed = !cav.getIndexedArgumentValues().isEmpty();
            if (hasgeneric && hasindexed) {
                throw new UnsupportedOperationException("RSAC Bean " + beanname
                        + " has both indexed and generic constructor arguments, which is not supported");
            }
            if (hasgeneric) {
                List cvalues = cav.getGenericArgumentValues();
                rbi.constructorargvals = new ConstructorArgumentValues.ValueHolder[cvalues.size()];
                for (int i = 0; i < cvalues.size(); ++i) {
                    rbi.constructorargvals[i] = (ConstructorArgumentValues.ValueHolder) cvalues.get(i);
                }
            } else if (hasindexed) {
                Map cvalues = cav.getIndexedArgumentValues();
                rbi.constructorargvals = new ConstructorArgumentValues.ValueHolder[cvalues.size()];
                for (int i = 0; i < cvalues.size(); ++i) {
                    rbi.constructorargvals[i] = (ConstructorArgumentValues.ValueHolder) cvalues.get(new Integer(i));
                }
            }
        }
        if (rbi.factorymethod == null) {
            // Core Spring change at 2.0M5 - ALL bean classes are now irrevocably
            // lazy!!
            // Package org.springframework.beans
            // introduced lazy loading (and lazy validation) of bean classes in
            // standard bean factories and bean definition readers
            AccessMethod bcnaccess = abdAnalyser.getAccessMethod("beanClassName");
            if (bcnaccess != null) {
                String bcn = (String) bcnaccess.getChildObject(abd);
                if (bcn != null) {
                    rbi.beanclass = ClassGetter.forName(bcn);
                    if (rbi.beanclass == null) {
                        throw new IllegalArgumentException("Class name " + bcn + " for bean definition with name "
                                + beanname + " cannot be resolved");
                    }
                }
            } else {
                // all right then BE like that! We'll work out the class later.
                // NB - beandef.getBeanClass() was eliminated around 1.2, we must
                // use the downcast even earlier now.
                rbi.beanclass = abd.getBeanClass();
            }
        }
        return rbi;
    }

    // magic evil code from AbstractBeanFactory l.443 - this is the main reason
    // I abandoned Spring Forms and the like, and it will return to plague us.
    // Just take a look at the constructor for BeanWrapperImpl - one of these
    // is created for EVERY BEAN IN A FACTORY!

    // protected BeanWrapper createBeanWrapper(Object beanInstance) {
    // return (beanInstance != null ? new BeanWrapperImpl(beanInstance) : new
    // BeanWrapperImpl());
    // }

    // this method is really
    // resolveValueIfNecessary **LITE**, we assume all other resolution
    // is done by the parent factory and are ONLY interested in propertyvalues
    // that refer to other beans IN THIS CONTAINER.
    // org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
    // l.900:
    // protected Object resolveValueIfNecessary(
    // String beanName, RootBeanDefinition mergedBeanDefinition, String argName,
    // Object value)
    // throws BeansException {
    // Since actual values are the rarer case, make THEM the composite ones.

    // returns either a String or StringList of bean names, or a ValueHolder
    public static Object propertyValueToBeanName(Object value, BeanDefConverter converter) {
        Object beanspec = null;
        if (value instanceof BeanDefinitionHolder) {
            // Resolve BeanDefinitionHolder: contains BeanDefinition with name and
            // aliases.
            BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value;
            String beanname = bdHolder.getBeanName();
            converter.convertBeanDef(bdHolder.getBeanDefinition(), beanname, true);
            beanspec = beanname;
        } else if (value instanceof BeanDefinition) {
            throw new IllegalArgumentException("No idea what to do with bean definition!");
        } else if (value instanceof RuntimeBeanReference) {
            RuntimeBeanReference ref = (RuntimeBeanReference) value;
            beanspec = ref.getBeanName();
        } else if (value instanceof ManagedList) {
            List valuelist = (List) value;
            StringList togo = new StringList();
            for (int i = 0; i < valuelist.size(); ++i) {
                String thisbeanname = (String) propertyValueToBeanName(valuelist.get(i), converter);
                togo.add(thisbeanname);
            }
            beanspec = togo;
        } else if (value instanceof String) {
            beanspec = new ValueHolder((String) value);
        } else if (value instanceof TypedStringValue) {
            beanspec = new ValueHolder(((TypedStringValue) value).getValue());
        } else {
            Logger.log.warn(
                    "RSACBeanLocator Got value " + value + " of unknown type " + value.getClass() + ": ignoring");
        }
        return beanspec;
    }

}