001    
002    /*
003     * Copyright (C) 2011 Archie L. Cobbs. All rights reserved.
004     *
005     * $Id: AbstractSpringSchemaUpdate.java 240 2012-01-19 19:42:54Z archie.cobbs $
006     */
007    
008    package org.dellroad.stuff.schema;
009    
010    import java.util.HashSet;
011    
012    import org.springframework.beans.factory.BeanFactory;
013    import org.springframework.beans.factory.BeanFactoryAware;
014    import org.springframework.beans.factory.BeanNameAware;
015    import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
016    import org.springframework.beans.factory.InitializingBean;
017    import org.springframework.beans.factory.NoSuchBeanDefinitionException;
018    import org.springframework.beans.factory.config.ConfigurableBeanFactory;
019    
020    /**
021     * Support superclass for {@link SchemaUpdate}s declared in Spring {@link BeanFactory}s that infer their
022     * names and required predecessors from their Spring bean attributes.
023     *
024     * <p>
025     * Instances infer their {@linkplain #getName name} and {@linkplain #getRequiredPredecessors required predecessors} from
026     * their Spring bean name (specified by the <code>id</code> XML attribute) and Spring dependencies (specified by
027     * the <code>depends-on</code> XML attribute), respectively.
028     *
029     * <p>
030     * Note: the use of <code>depends-on</code> is an abuse of Spring's dependency notation for convenience. Normally
031     * <code>depends-on</code> refers to bean intialization ordering, whereas this class uses it to refer to schema update ordering.
032     * Schema updates are not normally expected to have any initialization ordering requirements, so this abuse shouldn't matter.
033     * If they do, this class should not be used.
034     *
035     * <p>
036     * The containing {@link BeanFactory} must be a {@link ConfigurableBeanFactory} (normally this is always the case).
037     *
038     * @param <T> database transaction type
039     */
040    public abstract class AbstractSpringSchemaUpdate<T> extends AbstractSchemaUpdate<T>
041      implements BeanNameAware, BeanFactoryAware, InitializingBean {
042    
043        protected String beanName;
044        protected BeanFactory beanFactory;
045    
046        @Override
047        public void setBeanName(String beanName) {
048            this.beanName = beanName;
049        }
050    
051        @Override
052        public void setBeanFactory(BeanFactory beanFactory) {
053            this.beanFactory = beanFactory;
054        }
055    
056        /**
057         * Configures the update name and required predecessors based on the Spring bean's name and
058         * {@link BeanFactory} dependencies.
059         */
060        @Override
061        public void afterPropertiesSet() throws Exception {
062            if (this.beanFactory == null)
063                throw new IllegalArgumentException("no BeanFactory configured");
064            if (this.beanName == null)
065                throw new IllegalArgumentException("no beanName configured");
066            this.setName(this.beanName);
067            this.setRequiredPredecessorsFromDependencies();
068        }
069    
070        /**
071         * Infer required predecessors from Spring dependencies.
072         *
073         * @throws IllegalArgumentException if this instance's {@code beanFactory} is not yet configured,
074         *  or not a {@link ConfigurableBeanFactory}
075         */
076        @SuppressWarnings("unchecked")
077        protected void setRequiredPredecessorsFromDependencies() {
078    
079            // Check factory type
080            if (!(this.beanFactory instanceof ConfigurableBeanFactory))
081                throw new IllegalArgumentException("BeanFactory is not a ConfigurableBeanFactory: " + this.beanFactory);
082            ConfigurableBeanFactory configurableBeanFactory = (ConfigurableBeanFactory)this.beanFactory;
083    
084            // Find required predecessors defined as Spring dependencies
085            String[] predecessorNames = configurableBeanFactory.getDependenciesForBean(this.beanName);
086            HashSet<SchemaUpdate<T>> predecessors = new HashSet<SchemaUpdate<T>>(predecessorNames.length);
087            for (String predecessorName : predecessorNames) {
088                try {
089                    predecessors.add((SchemaUpdate<T>)configurableBeanFactory.getBean(predecessorName, SchemaUpdate.class));
090                } catch (NoSuchBeanDefinitionException e) {
091                    continue;
092                } catch (BeanNotOfRequiredTypeException e) {
093                    continue;
094                }
095            }
096            this.setRequiredPredecessors(predecessors);
097        }
098    }
099