Java tutorial
/** * 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); } } } } } }