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.config;
018:
019: import java.lang.reflect.Field;
020:
021: import org.springframework.beans.factory.BeanClassLoaderAware;
022: import org.springframework.beans.factory.BeanFactoryUtils;
023: import org.springframework.beans.factory.BeanNameAware;
024: import org.springframework.beans.factory.FactoryBean;
025: import org.springframework.beans.factory.FactoryBeanNotInitializedException;
026: import org.springframework.beans.factory.InitializingBean;
027: import org.springframework.util.ClassUtils;
028: import org.springframework.util.ReflectionUtils;
029: import org.springframework.util.StringUtils;
030:
031: /**
032: * FactoryBean which retrieves a static or non-static field value.
033: *
034: * <p>Typically used for retrieving public static final constants. Usage example:
035: *
036: * <pre class="code">// standard definition for exposing a static field, specifying the "staticField" property
037: * <bean id="myField" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
038: * <property name="staticField" value="java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
039: * </bean>
040: *
041: * // convenience version that specifies a static field pattern as bean name
042: * <bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"
043: * class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/></pre>
044: * </pre>
045: *
046: * <p>If you are using Spring 2.0, you can also use the following style of configuration for
047: * public static fields.
048: *
049: * <pre class="code"><util:constant static-field="java.sql.Connection.TRANSACTION_SERIALIZABLE"/></pre>
050: *
051: * @author Juergen Hoeller
052: * @since 1.1
053: * @see #setStaticField
054: */
055: public class FieldRetrievingFactoryBean implements FactoryBean,
056: BeanNameAware, BeanClassLoaderAware, InitializingBean {
057:
058: private Class targetClass;
059:
060: private Object targetObject;
061:
062: private String targetField;
063:
064: private String staticField;
065:
066: private String beanName;
067:
068: private ClassLoader beanClassLoader = ClassUtils
069: .getDefaultClassLoader();
070:
071: // the field we will retrieve
072: private Field fieldObject;
073:
074: /**
075: * Set the target class on which the field is defined.
076: * Only necessary when the target field is static; else,
077: * a target object needs to be specified anyway.
078: * @see #setTargetObject
079: * @see #setTargetField
080: */
081: public void setTargetClass(Class targetClass) {
082: this .targetClass = targetClass;
083: }
084:
085: /**
086: * Return the target class on which the field is defined.
087: */
088: public Class getTargetClass() {
089: return targetClass;
090: }
091:
092: /**
093: * Set the target object on which the field is defined.
094: * Only necessary when the target field is not static;
095: * else, a target class is sufficient.
096: * @see #setTargetClass
097: * @see #setTargetField
098: */
099: public void setTargetObject(Object targetObject) {
100: this .targetObject = targetObject;
101: }
102:
103: /**
104: * Return the target object on which the field is defined.
105: */
106: public Object getTargetObject() {
107: return this .targetObject;
108: }
109:
110: /**
111: * Set the name of the field to be retrieved.
112: * Refers to either a static field or a non-static field,
113: * depending on a target object being set.
114: * @see #setTargetClass
115: * @see #setTargetObject
116: */
117: public void setTargetField(String targetField) {
118: this .targetField = StringUtils.trimAllWhitespace(targetField);
119: }
120:
121: /**
122: * Return the name of the field to be retrieved.
123: */
124: public String getTargetField() {
125: return this .targetField;
126: }
127:
128: /**
129: * Set a fully qualified static field name to retrieve,
130: * e.g. "example.MyExampleClass.MY_EXAMPLE_FIELD".
131: * Convenient alternative to specifying targetClass and targetField.
132: * @see #setTargetClass
133: * @see #setTargetField
134: */
135: public void setStaticField(String staticField) {
136: this .staticField = StringUtils.trimAllWhitespace(staticField);
137: }
138:
139: /**
140: * The bean name of this FieldRetrievingFactoryBean will be interpreted
141: * as "staticField" pattern, if neither "targetClass" nor "targetObject"
142: * nor "targetField" have been specified.
143: * This allows for concise bean definitions with just an id/name.
144: */
145: public void setBeanName(String beanName) {
146: this .beanName = StringUtils.trimAllWhitespace(BeanFactoryUtils
147: .originalBeanName(beanName));
148: }
149:
150: public void setBeanClassLoader(ClassLoader classLoader) {
151: this .beanClassLoader = classLoader;
152: }
153:
154: public void afterPropertiesSet() throws ClassNotFoundException,
155: NoSuchFieldException {
156: if (this .targetClass != null && this .targetObject != null) {
157: throw new IllegalArgumentException(
158: "Specify either targetClass or targetObject, not both");
159: }
160:
161: if (this .targetClass == null && this .targetObject == null) {
162: if (this .targetField != null) {
163: throw new IllegalArgumentException(
164: "Specify targetClass or targetObject in combination with targetField");
165: }
166:
167: // If no other property specified, consider bean name as static field expression.
168: if (this .staticField == null) {
169: this .staticField = this .beanName;
170: }
171:
172: // Try to parse static field into class and field.
173: int lastDotIndex = this .staticField.lastIndexOf('.');
174: if (lastDotIndex == -1
175: || lastDotIndex == this .staticField.length()) {
176: throw new IllegalArgumentException(
177: "staticField must be a fully qualified class plus method name: "
178: + "e.g. 'example.MyExampleClass.MY_EXAMPLE_FIELD'");
179: }
180: String className = this .staticField.substring(0,
181: lastDotIndex);
182: String fieldName = this .staticField
183: .substring(lastDotIndex + 1);
184: this .targetClass = ClassUtils.forName(className,
185: this .beanClassLoader);
186: this .targetField = fieldName;
187: }
188:
189: else if (this .targetField == null) {
190: // Either targetClass or targetObject specified.
191: throw new IllegalArgumentException(
192: "targetField is required");
193: }
194:
195: // Try to get the exact method first.
196: Class targetClass = (this .targetObject != null) ? this .targetObject
197: .getClass()
198: : this .targetClass;
199: this .fieldObject = targetClass.getField(this .targetField);
200: }
201:
202: public Object getObject() throws IllegalAccessException {
203: if (this .fieldObject == null) {
204: throw new FactoryBeanNotInitializedException();
205: }
206: ReflectionUtils.makeAccessible(this .fieldObject);
207: if (this .targetObject != null) {
208: // instance field
209: return this .fieldObject.get(this .targetObject);
210: } else {
211: // class field
212: return this .fieldObject.get(null);
213: }
214: }
215:
216: public Class getObjectType() {
217: return (this .fieldObject != null ? this .fieldObject.getType()
218: : null);
219: }
220:
221: public boolean isSingleton() {
222: return false;
223: }
224:
225: }
|