001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.beans.factory.support;
018:
019: import java.util.ArrayList;
020: import java.util.Iterator;
021: import java.util.LinkedHashMap;
022: import java.util.LinkedHashSet;
023: import java.util.List;
024: import java.util.Map;
025: import java.util.Properties;
026: import java.util.Set;
027:
028: import org.springframework.beans.BeanWrapper;
029: import org.springframework.beans.BeansException;
030: import org.springframework.beans.TypeConverter;
031: import org.springframework.beans.factory.BeanCreationException;
032: import org.springframework.beans.factory.BeanDefinitionStoreException;
033: import org.springframework.beans.factory.BeanFactoryUtils;
034: import org.springframework.beans.factory.FactoryBean;
035: import org.springframework.beans.factory.config.BeanDefinition;
036: import org.springframework.beans.factory.config.BeanDefinitionHolder;
037: import org.springframework.beans.factory.config.RuntimeBeanNameReference;
038: import org.springframework.beans.factory.config.RuntimeBeanReference;
039: import org.springframework.beans.factory.config.TypedStringValue;
040:
041: /**
042: * Helper class for use in bean factory implementations,
043: * resolving values contained in bean definition objects
044: * into the actual values applied to the target bean instance.
045: *
046: * <p>Operates on an {@link AbstractBeanFactory} and a plain
047: * {@link org.springframework.beans.factory.config.BeanDefinition} object.
048: * Used by {@link AbstractAutowireCapableBeanFactory}.
049: *
050: * @author Juergen Hoeller
051: * @since 1.2
052: * @see AbstractAutowireCapableBeanFactory
053: */
054: class BeanDefinitionValueResolver {
055:
056: private final AbstractBeanFactory beanFactory;
057:
058: private final String beanName;
059:
060: private final BeanDefinition beanDefinition;
061:
062: private final TypeConverter typeConverter;
063:
064: /**
065: * Create a BeanDefinitionValueResolver for the given BeanFactory and BeanDefinition.
066: * @param beanFactory the BeanFactory to resolve against
067: * @param beanName the name of the bean that we work on
068: * @param beanDefinition the BeanDefinition of the bean that we work on
069: * @param typeConverter the TypeConverter to use for resolving TypedStringValues
070: */
071: public BeanDefinitionValueResolver(AbstractBeanFactory beanFactory,
072: String beanName, BeanDefinition beanDefinition,
073: TypeConverter typeConverter) {
074:
075: this .beanFactory = beanFactory;
076: this .beanName = beanName;
077: this .beanDefinition = beanDefinition;
078: this .typeConverter = typeConverter;
079: }
080:
081: /**
082: * Given a PropertyValue, return a value, resolving any references to other
083: * beans in the factory if necessary. The value could be:
084: * <li>A BeanDefinition, which leads to the creation of a corresponding
085: * new bean instance. Singleton flags and names of such "inner beans"
086: * are always ignored: Inner beans are anonymous prototypes.
087: * <li>A RuntimeBeanReference, which must be resolved.
088: * <li>A ManagedList. This is a special collection that may contain
089: * RuntimeBeanReferences or Collections that will need to be resolved.
090: * <li>A ManagedSet. May also contain RuntimeBeanReferences or
091: * Collections that will need to be resolved.
092: * <li>A ManagedMap. In this case the value may be a RuntimeBeanReference
093: * or Collection that will need to be resolved.
094: * <li>An ordinary object or <code>null</code>, in which case it's left alone.
095: * @param argName the name of the argument that the value is defined for
096: * @param value the value object to resolve
097: * @return the resolved object
098: */
099: public Object resolveValueIfNecessary(Object argName, Object value) {
100: // We must check each value to see whether it requires a runtime reference
101: // to another bean to be resolved.
102: if (value instanceof RuntimeBeanReference) {
103: RuntimeBeanReference ref = (RuntimeBeanReference) value;
104: return resolveReference(argName, ref);
105: } else if (value instanceof RuntimeBeanNameReference) {
106: String ref = ((RuntimeBeanNameReference) value)
107: .getBeanName();
108: if (!this .beanFactory.containsBean(ref)) {
109: throw new BeanDefinitionStoreException(
110: "Invalid bean name '" + ref
111: + "' in bean reference for " + argName);
112: }
113: return ref;
114: } else if (value instanceof BeanDefinitionHolder) {
115: // Resolve BeanDefinitionHolder: contains BeanDefinition with name and aliases.
116: BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value;
117: return resolveInnerBean(argName, bdHolder.getBeanName(),
118: bdHolder.getBeanDefinition());
119: } else if (value instanceof BeanDefinition) {
120: // Resolve plain BeanDefinition, without contained name: use dummy name.
121: BeanDefinition bd = (BeanDefinition) value;
122: return resolveInnerBean(argName, "(inner bean)", bd);
123: } else if (value instanceof ManagedList) {
124: // May need to resolve contained runtime references.
125: return resolveManagedList(argName, (List) value);
126: } else if (value instanceof ManagedSet) {
127: // May need to resolve contained runtime references.
128: return resolveManagedSet(argName, (Set) value);
129: } else if (value instanceof ManagedMap) {
130: // May need to resolve contained runtime references.
131: return resolveManagedMap(argName, (Map) value);
132: } else if (value instanceof ManagedProperties) {
133: Properties original = (Properties) value;
134: Properties copy = new Properties();
135: for (Iterator it = original.entrySet().iterator(); it
136: .hasNext();) {
137: Map.Entry propEntry = (Map.Entry) it.next();
138: Object propKey = propEntry.getKey();
139: Object propValue = propEntry.getValue();
140: if (propKey instanceof TypedStringValue) {
141: propKey = ((TypedStringValue) propKey).getValue();
142: }
143: if (propValue instanceof TypedStringValue) {
144: propValue = ((TypedStringValue) propValue)
145: .getValue();
146: }
147: copy.put(propKey, propValue);
148: }
149: return copy;
150: } else if (value instanceof TypedStringValue) {
151: // Convert value to target type here.
152: TypedStringValue typedStringValue = (TypedStringValue) value;
153: try {
154: Class resolvedTargetType = resolveTargetType(typedStringValue);
155: if (resolvedTargetType != null) {
156: return this .typeConverter.convertIfNecessary(
157: typedStringValue.getValue(),
158: resolvedTargetType);
159: } else {
160: // No target type specified - no conversion necessary...
161: return typedStringValue.getValue();
162: }
163: } catch (Throwable ex) {
164: // Improve the message by showing the context.
165: throw new BeanCreationException(this .beanDefinition
166: .getResourceDescription(), this .beanName,
167: "Error converting typed String value for "
168: + argName, ex);
169: }
170: } else {
171: // No need to resolve value...
172: return value;
173: }
174: }
175:
176: /**
177: * Resolve the target type in the given TypedStringValue.
178: * @param value the TypedStringValue to resolve
179: * @return the resolved target type (or <code>null</code> if none specified)
180: * @throws ClassNotFoundException if the specified type cannot be resolved
181: * @see TypedStringValue#resolveTargetType
182: */
183: protected Class resolveTargetType(TypedStringValue value)
184: throws ClassNotFoundException {
185: if (value.hasTargetType()) {
186: return value.getTargetType();
187: }
188: return value.resolveTargetType(this .beanFactory
189: .getBeanClassLoader());
190: }
191:
192: /**
193: * Resolve an inner bean definition.
194: * @param argName the name of the argument that the inner bean is defined for
195: * @param innerBeanName the name of the inner bean
196: * @param innerBd the bean definition for the inner bean
197: * @return the resolved inner bean instance
198: */
199: private Object resolveInnerBean(Object argName,
200: String innerBeanName, BeanDefinition innerBd) {
201: RootBeanDefinition mbd = null;
202: try {
203: mbd = this .beanFactory.getMergedBeanDefinition(
204: innerBeanName, innerBd, this .beanDefinition);
205: // Check given bean name whether it is unique. If not already unique,
206: // add counter - increasing the counter until the name is unique.
207: String actualInnerBeanName = innerBeanName;
208: if (mbd.isSingleton()) {
209: actualInnerBeanName = adaptInnerBeanName(innerBeanName);
210: }
211: // Guarantee initialization of beans that the inner bean depends on.
212: String[] dependsOn = mbd.getDependsOn();
213: if (dependsOn != null) {
214: for (int i = 0; i < dependsOn.length; i++) {
215: String dependsOnBean = dependsOn[i];
216: this .beanFactory.getBean(dependsOnBean);
217: this .beanFactory.registerDependentBean(
218: dependsOnBean, actualInnerBeanName);
219: }
220: }
221: Object innerBean = this .beanFactory.createBean(
222: actualInnerBeanName, mbd, null);
223: this .beanFactory.registerDependentBean(actualInnerBeanName,
224: this .beanName);
225: if (innerBean instanceof FactoryBean) {
226: return this .beanFactory.getObjectFromFactoryBean(
227: (FactoryBean) innerBean, actualInnerBeanName,
228: mbd);
229: } else {
230: return innerBean;
231: }
232: } catch (BeansException ex) {
233: throw new BeanCreationException(
234: this .beanDefinition.getResourceDescription(),
235: this .beanName,
236: "Cannot create inner bean '"
237: + innerBeanName
238: + "' "
239: + (mbd != null
240: && mbd.getBeanClassName() != null ? "of type ["
241: + mbd.getBeanClassName() + "] "
242: : "") + "while setting " + argName,
243: ex);
244: }
245: }
246:
247: /**
248: * Checks the given bean name whether it is unique. If not already unique,
249: * a counter is added, increasing the counter until the name is unique.
250: * @param innerBeanName the original name for the inner bean
251: * @return the adapted name for the inner bean
252: */
253: private String adaptInnerBeanName(String innerBeanName) {
254: String actualInnerBeanName = innerBeanName;
255: int counter = 0;
256: while (this .beanFactory.isBeanNameInUse(actualInnerBeanName)) {
257: counter++;
258: actualInnerBeanName = innerBeanName
259: + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR
260: + counter;
261: }
262: return actualInnerBeanName;
263: }
264:
265: /**
266: * Resolve a reference to another bean in the factory.
267: */
268: private Object resolveReference(Object argName,
269: RuntimeBeanReference ref) {
270: try {
271: if (ref.isToParent()) {
272: if (this .beanFactory.getParentBeanFactory() == null) {
273: throw new BeanCreationException(
274: this .beanDefinition
275: .getResourceDescription(),
276: this .beanName,
277: "Can't resolve reference to bean '"
278: + ref.getBeanName()
279: + "' in parent factory: no parent factory available");
280: }
281: return this .beanFactory.getParentBeanFactory().getBean(
282: ref.getBeanName());
283: } else {
284: Object bean = this .beanFactory.getBean(ref
285: .getBeanName());
286: this .beanFactory.registerDependentBean(ref
287: .getBeanName(), this .beanName);
288: return bean;
289: }
290: } catch (BeansException ex) {
291: throw new BeanCreationException(this .beanDefinition
292: .getResourceDescription(), this .beanName,
293: "Cannot resolve reference to bean '"
294: + ref.getBeanName() + "' while setting "
295: + argName, ex);
296: }
297: }
298:
299: /**
300: * For each element in the ManagedList, resolve reference if necessary.
301: */
302: private List resolveManagedList(Object argName, List ml) {
303: List resolved = new ArrayList(ml.size());
304: for (int i = 0; i < ml.size(); i++) {
305: resolved.add(resolveValueIfNecessary(argName + " with key "
306: + BeanWrapper.PROPERTY_KEY_PREFIX + i
307: + BeanWrapper.PROPERTY_KEY_SUFFIX, ml.get(i)));
308: }
309: return resolved;
310: }
311:
312: /**
313: * For each element in the ManagedList, resolve reference if necessary.
314: */
315: private Set resolveManagedSet(Object argName, Set ms) {
316: Set resolved = new LinkedHashSet(ms.size());
317: int i = 0;
318: for (Iterator it = ms.iterator(); it.hasNext();) {
319: resolved.add(resolveValueIfNecessary(argName + " with key "
320: + BeanWrapper.PROPERTY_KEY_PREFIX + i
321: + BeanWrapper.PROPERTY_KEY_SUFFIX, it.next()));
322: i++;
323: }
324: return resolved;
325: }
326:
327: /**
328: * For each element in the ManagedMap, resolve reference if necessary.
329: */
330: private Map resolveManagedMap(Object argName, Map mm) {
331: Map resolved = new LinkedHashMap(mm.size());
332: Iterator it = mm.entrySet().iterator();
333: while (it.hasNext()) {
334: Map.Entry entry = (Map.Entry) it.next();
335: Object resolvedKey = resolveValueIfNecessary(argName, entry
336: .getKey());
337: Object resolvedValue = resolveValueIfNecessary(argName
338: + " with key " + BeanWrapper.PROPERTY_KEY_PREFIX
339: + entry.getKey() + BeanWrapper.PROPERTY_KEY_SUFFIX,
340: entry.getValue());
341: resolved.put(resolvedKey, resolvedValue);
342: }
343: return resolved;
344: }
345:
346: }
|