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