com.urbanmania.spring.beans.factory.config.annotations.PropertyAnnotationAndPlaceholderConfigurer.java Source code

Java tutorial

Introduction

Here is the source code for com.urbanmania.spring.beans.factory.config.annotations.PropertyAnnotationAndPlaceholderConfigurer.java

Source

/**
 * Copyright 2010 Ricardo Gladwell
 *
 * 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.
 */

package com.urbanmania.spring.beans.factory.config.annotations;

import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
 * @author Ricardo Gladwell <ricardo.gladwell@gmail.com>
 * @see com.urbanmania.spring.beans.factory.config.annotations.PropertyAnnotationConfigurer
 */
public class PropertyAnnotationAndPlaceholderConfigurer extends PropertyPlaceholderConfigurer
        implements PropertyListener, ApplicationContextAware, BeanFactoryAware {

    private static final Logger log = Logger.getLogger(PropertyAnnotationAndPlaceholderConfigurer.class.getName());

    PropertyLoader[] propertyLoaders;
    String basePackage;
    ApplicationContext applicationContext;
    ConfigurableBeanFactory beanFactory;
    Map<String, List<UpdateDescriptor>> updatableProperties = new Hashtable<String, List<UpdateDescriptor>>();

    public void setPropertyLoaders(PropertyLoader[] propertyLoaders) {
        this.propertyLoaders = propertyLoaders;
    }

    public void setBasePackage(String basePackage) {
        this.basePackage = basePackage;
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        super.setBeanFactory(beanFactory);
        this.beanFactory = (ConfigurableBeanFactory) beanFactory;
    }

    @Override
    protected void loadProperties(Properties props) throws IOException {
        super.loadProperties(props);

        if (propertyLoaders != null) {
            for (PropertyLoader propertyLoader : propertyLoaders) {
                log.info("Loading propertyLoader=[" + propertyLoader + "]");
                Properties loaded = propertyLoader.loadProperties();
                props.putAll(loaded);
                propertyLoader.registerPropertyListener(this);
            }
        }
    }

    @Override
    protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties properties)
            throws BeansException {
        super.processProperties(beanFactory, properties);

        for (String name : beanFactory.getBeanDefinitionNames()) {
            MutablePropertyValues mpv = beanFactory.getBeanDefinition(name).getPropertyValues();

            Class<?> clazz;
            try {
                clazz = Class.forName(beanFactory.getBeanDefinition(name).getBeanClassName());
            } catch (ClassNotFoundException e) {
                throw new BeanConfigurationException("error retrieving bean class", e);
            }
            if (FactoryBean.class.isAssignableFrom(clazz)) {
                processAnnotatedProperties(properties, name, mpv, clazz);
            }

            clazz = beanFactory.getType(name);
            processAnnotatedProperties(properties, name, mpv, clazz);
        }
    }

    private void processAnnotatedProperties(Properties properties, String name, MutablePropertyValues mpv,
            Class<?> clazz) {
        // TODO support proxies
        if (clazz != null && clazz.getPackage() != null) {
            if (basePackage != null && !clazz.getPackage().getName().startsWith(basePackage)) {
                return;
            }

            log.info("Configuring properties for bean=" + name + "[" + clazz + "]");

            for (PropertyDescriptor property : BeanUtils.getPropertyDescriptors(clazz)) {
                if (log.isLoggable(Level.FINE))
                    log.fine("examining property=[" + clazz.getName() + "." + property.getName() + "]");
                Method setter = property.getWriteMethod();
                Method getter = property.getReadMethod();
                Property annotation = null;
                if (setter != null && setter.isAnnotationPresent(Property.class)) {
                    annotation = setter.getAnnotation(Property.class);
                } else if (setter != null && getter != null && getter.isAnnotationPresent(Property.class)) {
                    annotation = getter.getAnnotation(Property.class);
                } else if (setter == null && getter != null && getter.isAnnotationPresent(Property.class)) {
                    throwBeanConfigurationException(clazz, property.getName());
                }
                if (annotation != null) {
                    setProperty(properties, name, mpv, clazz, property, annotation);
                }
            }

            for (Field field : clazz.getDeclaredFields()) {
                if (log.isLoggable(Level.FINE))
                    log.fine("examining field=[" + clazz.getName() + "." + field.getName() + "]");
                if (field.isAnnotationPresent(Property.class)) {
                    Property annotation = field.getAnnotation(Property.class);
                    PropertyDescriptor property = BeanUtils.getPropertyDescriptor(clazz, field.getName());

                    if (property == null || property.getWriteMethod() == null) {
                        throwBeanConfigurationException(clazz, field.getName());
                    }

                    setProperty(properties, name, mpv, clazz, property, annotation);
                }
            }
        }
    }

    private void throwBeanConfigurationException(Class<?> clazz, String name) {
        throw new BeanConfigurationException(
                "setter for property=[" + clazz.getName() + "." + name + "] not available.");
    }

    private void setProperty(Properties properties, String name, MutablePropertyValues mpv, Class<?> clazz,
            PropertyDescriptor property, Property annotation) {
        String value = resolvePlaceholder(annotation.key(), properties, SYSTEM_PROPERTIES_MODE_FALLBACK);

        if (value == null) {
            value = annotation.value();
        }

        value = parseStringValue(value, properties, new HashSet<String>());

        log.info("setting property=[" + clazz.getName() + "." + property.getName() + "] value=[" + annotation.key()
                + "=" + value + "]");

        mpv.addPropertyValue(property.getName(), value);

        if (annotation.update()) {
            registerBeanPropertyForUpdate(clazz, annotation, property);
        }
    }

    public void registerBeanPropertyForUpdate(Class<?> beanClass, Property annotation,
            PropertyDescriptor property) {
        log.info("watching updates for property=[" + property.getPropertyType().getName() + "." + property.getName()
                + "] key=[" + annotation.key() + "]");
        List<UpdateDescriptor> properties = updatableProperties.get(annotation.key());

        if (properties == null) {
            properties = new ArrayList<UpdateDescriptor>();
        }

        UpdateDescriptor descriptor = new UpdateDescriptor();
        descriptor.setPropertyDescriptor(property);
        descriptor.setBeanClass(beanClass);
        properties.add(descriptor);
        updatableProperties.put(annotation.key(), properties);
    }

    public void propertyChanged(PropertyEvent event) {
        if (updatableProperties.get(event.getKey()) != null) {
            for (UpdateDescriptor update : updatableProperties.get(event.getKey())) {
                for (Object bean : applicationContext.getBeansOfType(update.getBeanClass()).values()) {
                    log.info("updating property=[" + bean.getClass().getName() + "."
                            + update.getPropertyDescriptor().getName() + "] value=[" + event.getValue() + "]");
                    try {
                        update.getPropertyDescriptor().getWriteMethod().invoke(bean,
                                this.beanFactory.getTypeConverter().convertIfNecessary(event.getValue(),
                                        update.getPropertyDescriptor().getPropertyType()));
                    } catch (IllegalArgumentException e) {
                        throw new BeanConfigurationException("Error updating " + bean.getClass().getName() + "."
                                + update.getPropertyDescriptor().getName() + "] value=[" + event.getValue() + "]",
                                e);
                    } catch (IllegalAccessException e) {
                        throw new BeanConfigurationException("Error updating " + bean.getClass().getName() + "."
                                + update.getPropertyDescriptor().getName() + "] value=[" + event.getValue() + "]",
                                e);
                    } catch (InvocationTargetException e) {
                        throw new BeanConfigurationException("Error updating " + bean.getClass().getName() + "."
                                + update.getPropertyDescriptor().getName() + "] value=[" + event.getValue() + "]",
                                e);
                    }
                }
            }
        }
    }

}