/**
* Copyright (C) 2011 Brian Ferris <bdferris@onebusaway.org>
*
* 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 org.onebusaway.container.spring;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.PropertyOverrideConfigurer;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
/**
* A link {@link BeanPostProcessor} that allows one to set and override bean
* properties in a flexible ways before the bean is initialized. This is similar
* to the functionality of {@link PropertyOverrideConfigurer}, except that it is
* a Spring bean post processor as opposed to a factory bean, which determines
* execution order in the application context construction lifecycle and allows
* one to use bean references for overriding bean properties as opposed to just
* string values.
*
* A simple example:
*
* <pre class="code"><bean class="org.onebusaway.container.spring.OverridePostProcessor">
* <property name="map">
* <map>
* <entry key="beanName.beanProperty" value ="someOtherBeanName" />
* </map>
* </property>
* </bean></pre>
*
* @author bdferris
*/
public class OverridePostProcessor implements BeanPostProcessor,
PriorityOrdered, BeanFactoryAware {
private Map<String, List<PropertyEntry>> _propertyEntriesByBeanName = new HashMap<String, List<PropertyEntry>>();
private int _order = Ordered.LOWEST_PRECEDENCE - 2;
private BeanFactory _beanFactory;
public void setMap(Map<String, String> map) {
processMap(map);
}
public void setOrder(int order) {
_order = order;
}
@Override
public int getOrder() {
return _order;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
_beanFactory = beanFactory;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
List<PropertyEntry> entries = _propertyEntriesByBeanName.get(beanName);
if (entries != null) {
for (PropertyEntry entry : entries) {
BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(bean);
Object value = _beanFactory.getBean(entry.value);
if( value == null)
throw new IllegalStateException("could not find bean with name: " + entry.value);
wrapper.setPropertyValue(entry.propertyName, value);
}
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
private void processMap(Map<String, String> map) {
for (Map.Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
int index = key.indexOf('.');
if (index == -1)
throw new IllegalArgumentException(
"expected map key of form \"beanName.propertyName\" for " + key);
String beanName = key.substring(0, index);
String propertyName = key.substring(index + 1);
List<PropertyEntry> entries = _propertyEntriesByBeanName.get(beanName);
if (entries == null) {
entries = new ArrayList<PropertyEntry>();
_propertyEntriesByBeanName.put(beanName, entries);
}
entries.add(new PropertyEntry(propertyName, entry.getValue()));
}
}
private static class PropertyEntry {
public PropertyEntry(String propertyName, String value) {
this.propertyName = propertyName;
this.value = value;
}
private String propertyName;
private String value;
}
}