blob: 81ab44eb0910c3ad80d46099aa2c6f484d487a19 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.cocoon.spring.configurator.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
/**
* This is a map implementation collecting all beans of a specific type (class)
* from a spring bean factory. The beans are available through their configured
* bean id.
*
* The map has a lazy implementation: the beans are not searched on
* instantiation but the first time the map is accessed. This avoids any startup
* ordering problems.
*
* By default the map searches in the bean factory it is used in and in all the
* parent bean factories (if there are any). This behaviour can be changed by
* calling the {@link #setCheckParent(boolean)} method.
*
* @version $Id$
* @since 1.0.1
*/
public class BeanMap implements Map<Object, Object>, BeanFactoryAware {
/** The real map. */
protected final Map<Object, Object> beanMap = new HashMap<Object, Object>();
/** Is the map initialized? */
protected boolean initialized = false;
/** The bean factory. */
protected ListableBeanFactory beanFactory;
/** The class for all beans in this map. */
protected Class<?> beanClass;
/** Do we strip the prefix from the bean name? */
protected boolean stripPrefix = true;
/** Do we check the parent factories? */
protected boolean checkParent = true;
/** Do we check for properties? */
protected List<String> hasProperties = new ArrayList<String>();
/** Which property should we use to key the map? */
protected String keyProperty;
/**
* Get all the beans from the bean factory and put them into a map using their id.
*
* @param beanNames The bean names to load.
*/
protected void load(Set<String> beanNames) {
for (String beanName : beanNames) {
Object key = this.stripPrefix(beanName);
if (this.hasProperties.size() > 0) {
final Object bean = this.beanFactory.getBean(beanName);
final BeanWrapperImpl wrapper = new BeanWrapperImpl(bean);
boolean isOk = true;
for (String propName : this.hasProperties) {
if (!wrapper.isReadableProperty(propName)) {
isOk = false;
}
}
if (isOk) {
if (this.keyProperty != null && this.keyProperty.length() > 0
&& wrapper.isReadableProperty(this.keyProperty)) {
key = wrapper.getPropertyValue(this.keyProperty);
}
this.beanMap.put(key, bean);
}
} else {
final Object bean = this.beanFactory.getBean(beanName);
final BeanWrapperImpl wrapper = new BeanWrapperImpl(bean);
if (this.keyProperty != null && this.keyProperty.length() > 0
&& wrapper.isReadableProperty(this.keyProperty)) {
key = wrapper.getPropertyValue(this.keyProperty);
}
this.beanMap.put(key, bean);
}
}
}
protected Object stripPrefix(final String beanName) {
final String prefix1 = this.beanClass.getName() + '.';
final String prefix2 = this.beanClass.getName() + '/';
Object key = beanName;
if (this.stripPrefix && (beanName.startsWith(prefix1) || beanName.startsWith(prefix2))) {
key = beanName.substring(prefix1.length());
}
return key;
}
/**
* Check if the bean is already initialized. If not, the bean's are searched
* in the bean factory.
*/
protected void checkInit() {
if (!this.initialized) {
synchronized (this) {
if (!this.initialized) {
// although it is unlikely, but if this bean is used outside
// spring
// it will just contain an empty map
if (this.beanFactory != null) {
final Set<String> beanNames = new HashSet<String>();
this.getBeanNames(this.beanFactory, beanNames);
this.load(beanNames);
}
this.initialized = true;
}
}
}
}
/**
* Get all bean names for the given type.
*
* @param factory
* The bean factory.
* @param beanNames
* The set containing the resulting bean names.
*/
protected void getBeanNames(ListableBeanFactory factory, Set<String> beanNames) {
// check parent first
if (this.checkParent) {
if (factory instanceof HierarchicalBeanFactory) {
if (((HierarchicalBeanFactory) factory).getParentBeanFactory() != null) {
this.getBeanNames((ListableBeanFactory) ((HierarchicalBeanFactory) factory).getParentBeanFactory(),
beanNames);
}
}
}
// get all bean names for our class
final String[] names = this.lookupBeans(factory);
for (int i = 0; i < names.length; i++) {
beanNames.add(names[i]);
}
}
protected String[] lookupBeans(ListableBeanFactory factory) {
return factory.getBeanNamesForType(this.beanClass);
}
/**
* @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory)
*/
public void setBeanFactory(BeanFactory factory) throws BeansException {
if (!(factory instanceof ListableBeanFactory)) {
throw new BeanDefinitionStoreException("BeanFactory must be listable.");
}
this.beanFactory = (ListableBeanFactory) factory;
}
public void setStripPrefix(boolean stripPrefix) {
this.stripPrefix = stripPrefix;
}
public void setCheckParent(boolean checkParent) {
this.checkParent = checkParent;
}
public void setHasProperties(String pHasProperties) {
final StringTokenizer tokenizer = new StringTokenizer(pHasProperties, " \t\n\r\f,");
final List<String> propNames = new ArrayList<String>();
while (tokenizer.hasMoreTokens()) {
propNames.add(tokenizer.nextToken());
}
this.hasProperties = propNames;
}
public void setKeyProperty(String pKeyProperty) {
this.keyProperty = pKeyProperty;
}
public void setType(Class<?> typeClass) {
this.beanClass = typeClass;
}
/**
* @see java.util.Map#clear()
*/
public void clear() {
// no need to call checkInit as we clear the map!
this.initialized = true;
this.beanMap.clear();
}
/**
* @see java.util.Map#containsKey(java.lang.Object)
*/
public boolean containsKey(Object key) {
this.checkInit();
return this.beanMap.containsKey(key);
}
/**
* @see java.util.Map#containsValue(java.lang.Object)
*/
public boolean containsValue(Object value) {
this.checkInit();
return this.beanMap.containsValue(value);
}
/**
* @see java.util.Map#entrySet()
*/
public Set<Entry<Object, Object>> entrySet() {
this.checkInit();
return this.beanMap.entrySet();
}
/**
* @see java.util.Map#get(java.lang.Object)
*/
public Object get(Object key) {
this.checkInit();
return this.beanMap.get(key);
}
/**
* @see java.util.Map#isEmpty()
*/
public boolean isEmpty() {
this.checkInit();
return this.beanMap.isEmpty();
}
/**
* @see java.util.Map#keySet()
*/
public Set<Object> keySet() {
this.checkInit();
return this.beanMap.keySet();
}
/**
* @see java.util.Map#put(java.lang.Object, java.lang.Object)
*/
public Object put(Object key, Object value) {
this.checkInit();
return this.beanMap.put(key, value);
}
/**
* @see java.util.Map#putAll(java.util.Map)
*/
public void putAll(Map<?, ?> t) {
this.checkInit();
this.beanMap.putAll(t);
}
/**
* @see java.util.Map#remove(java.lang.Object)
*/
public Object remove(Object key) {
this.checkInit();
return this.beanMap.remove(key);
}
/**
* @see java.util.Map#size()
*/
public int size() {
this.checkInit();
return this.beanMap.size();
}
/**
* @see java.util.Map#values()
*/
public Collection<Object> values() {
this.checkInit();
return this.beanMap.values();
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
this.checkInit();
return this.beanMap.equals(obj);
}
/**
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
this.checkInit();
return this.beanMap.hashCode();
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
this.checkInit();
return this.beanMap.toString();
}
}