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.context.annotation;
018:
019: import java.beans.Introspector;
020: import java.beans.PropertyDescriptor;
021: import java.io.Serializable;
022: import java.lang.annotation.Annotation;
023: import java.lang.reflect.AnnotatedElement;
024: import java.lang.reflect.Constructor;
025: import java.lang.reflect.Field;
026: import java.lang.reflect.Member;
027: import java.lang.reflect.Method;
028: import java.lang.reflect.Modifier;
029: import java.net.MalformedURLException;
030: import java.net.URL;
031: import java.util.Collections;
032: import java.util.HashSet;
033: import java.util.Iterator;
034: import java.util.Map;
035: import java.util.Set;
036: import java.util.concurrent.ConcurrentHashMap;
037:
038: import javax.annotation.PostConstruct;
039: import javax.annotation.PreDestroy;
040: import javax.annotation.Resource;
041: import javax.ejb.EJB;
042: import javax.xml.namespace.QName;
043: import javax.xml.ws.Service;
044: import javax.xml.ws.WebServiceClient;
045: import javax.xml.ws.WebServiceRef;
046:
047: import org.springframework.beans.BeanUtils;
048: import org.springframework.beans.BeansException;
049: import org.springframework.beans.PropertyValues;
050: import org.springframework.beans.factory.BeanCreationException;
051: import org.springframework.beans.factory.BeanFactory;
052: import org.springframework.beans.factory.BeanFactoryAware;
053: import org.springframework.beans.factory.NoSuchBeanDefinitionException;
054: import org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor;
055: import org.springframework.beans.factory.annotation.InjectionMetadata;
056: import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
057: import org.springframework.beans.factory.config.ConfigurableBeanFactory;
058: import org.springframework.beans.factory.config.DependencyDescriptor;
059: import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
060: import org.springframework.core.MethodParameter;
061: import org.springframework.jndi.support.SimpleJndiBeanFactory;
062: import org.springframework.util.Assert;
063: import org.springframework.util.ClassUtils;
064: import org.springframework.util.ReflectionUtils;
065: import org.springframework.util.StringUtils;
066:
067: /**
068: * {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation
069: * that supports common JSR-250 annotations out of the box.
070: *
071: * <p>This includes support for the {@link javax.annotation.PostConstruct} and
072: * {@link javax.annotation.PreDestroy} annotations (as init annotation and destroy
073: * annotation, respectively).
074: *
075: * <p>The central element is the {@link javax.annotation.Resource} annotation
076: * for annotation-driven injection of named beans, by default from the containing
077: * Spring BeanFactory, with only <code>mappedName</code> references resolved in JNDI.
078: * The {@link #setAlwaysUseJndiLookup "alwaysUseJndiLookup" flag} enforces JNDI lookups
079: * equivalent to standard Java EE 5 resource injection for <code>name</code> references
080: * and default names as well. The target beans can be simple POJOs, with no special
081: * requirements other than the type having to match.
082: *
083: * <p>The JAX-WS {@link javax.xml.ws.WebServiceRef} annotation is supported too,
084: * analogous to {@link javax.annotation.Resource} but with the capability of creating
085: * specific JAX-WS service endpoints. This may either point to an explicitly defined
086: * resource by name or operate on a locally specified JAX-WS service class. Finally,
087: * this post-processor also supports the EJB 3 {@link javax.ejb.EJB} annotation,
088: * analogous to {@link javax.annotation.Resource} as well, with the capability to
089: * specify both a local bean name and a global JNDI name for fallback retrieval.
090: * The target beans can be plain POJOs as well as EJB 3 Session Beans in this case.
091: *
092: * <p>The common annotations supported by this post-processor are available
093: * in Java 6 (JDK 1.6) as well as in Java EE 5 (which provides a standalone jar for
094: * its common annotations as well, allowing for use in any Java 5 based application).
095: * Hence, this post-processor works out of the box on JDK 1.6, and requires the
096: * JSR-250 API jar (and optionally the JAX-WS API jar and/or the EJB 3 API jar)
097: * to be added to the classpath on JDK 1.5 (when running outside of Java EE 5).
098: *
099: * <p>For default usage, resolving resource names as Spring bean names,
100: * simply define the following in your application context:
101: *
102: * <pre class="code">
103: * <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/></pre>
104: *
105: * For direct JNDI access, resolving resource names as JNDI resource references
106: * within the Java EE application's "java:comp/env/" namespace, use the following:
107: *
108: * <pre class="code">
109: * <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor">
110: * <property name="alwaysUseJndiLookup" value="true"/>
111: * </bean></pre>
112: *
113: * Note: <code>mappedName</code> references will always be resolved in JNDI,
114: * allowing for global JNDI names (including "java:" prefix) as well. The
115: * "alwaysUseJndiLookup" flag just affects <code>name</code> references and
116: * default names (inferred from the field name / property name).
117: *
118: * <p>A default CommonAnnotationBeanPostProcessor will be registered
119: * by the "context:annotation-config" and "context:component-scan" XML tags.
120: * Remove or turn off the default annotation configuration there if you intend
121: * to specify a custom CommonAnnotationBeanPostProcessor bean definition.
122: *
123: * @author Juergen Hoeller
124: * @since 2.5
125: * @see #setAlwaysUseJndiLookup
126: * @see #setResourceFactory
127: * @see org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor
128: * @see org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
129: */
130: public class CommonAnnotationBeanPostProcessor extends
131: InitDestroyAnnotationBeanPostProcessor implements
132: InstantiationAwareBeanPostProcessor, BeanFactoryAware,
133: Serializable {
134:
135: private static Class<? extends Annotation> webServiceRefClass = null;
136:
137: private static Class<? extends Annotation> ejbRefClass = null;
138:
139: static {
140: try {
141: webServiceRefClass = ClassUtils.forName(
142: "javax.xml.ws.WebServiceRef",
143: CommonAnnotationBeanPostProcessor.class
144: .getClassLoader());
145: } catch (ClassNotFoundException ex) {
146: webServiceRefClass = null;
147: }
148: try {
149: ejbRefClass = ClassUtils.forName("javax.ejb.EJB",
150: CommonAnnotationBeanPostProcessor.class
151: .getClassLoader());
152: } catch (ClassNotFoundException ex) {
153: ejbRefClass = null;
154: }
155: }
156:
157: private boolean fallbackToDefaultTypeMatch = true;
158:
159: private boolean alwaysUseJndiLookup = false;
160:
161: private transient BeanFactory jndiFactory = new SimpleJndiBeanFactory();
162:
163: private transient BeanFactory resourceFactory;
164:
165: private transient BeanFactory beanFactory;
166:
167: private transient final Map<Class<?>, InjectionMetadata> injectionMetadataCache = new ConcurrentHashMap<Class<?>, InjectionMetadata>();
168:
169: /**
170: * Create a new CommonAnnotationBeanPostProcessor,
171: * with the init and destroy annotation types set to
172: * {@link javax.annotation.PostConstruct} and {@link javax.annotation.PreDestroy},
173: * respectively.
174: */
175: public CommonAnnotationBeanPostProcessor() {
176: setInitAnnotationType(PostConstruct.class);
177: setDestroyAnnotationType(PreDestroy.class);
178: }
179:
180: /**
181: * Set whether to allow a fallback to a type match if no explicit name has been
182: * specified. The default name (i.e. the field name or bean property name) will
183: * still be checked first; if a bean of that name exists, it will be taken.
184: * However, if no bean of that name exists, a by-type resolution of the
185: * dependency will be attempted if this flag is "true".
186: * <p>Default is "true". Switch this flag to "false" in order to enforce a
187: * by-name lookup in all cases, throwing an exception in case of no name match.
188: * @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#resolveDependency
189: */
190: public void setFallbackToDefaultTypeMatch(
191: boolean fallbackToDefaultTypeMatch) {
192: this .fallbackToDefaultTypeMatch = fallbackToDefaultTypeMatch;
193: }
194:
195: /**
196: * Set whether to always use JNDI lookups equivalent to standard Java EE 5 resource
197: * injection, <b>even for <code>name</code> attributes and default names</b>.
198: * <p>Default is "false": Resource names are used for Spring bean lookups in the
199: * containing BeanFactory; only <code>mappedName</code> attributes point directly
200: * into JNDI. Switch this flag to "true" for enforcing Java EE style JNDI lookups
201: * in any case, even for <code>name</code> attributes and default names.
202: * @see #setJndiFactory
203: * @see #setResourceFactory
204: */
205: public void setAlwaysUseJndiLookup(boolean alwaysUseJndiLookup) {
206: this .alwaysUseJndiLookup = alwaysUseJndiLookup;
207: }
208:
209: /**
210: * Specify the factory for objects to be injected into <code>@Resource</code> /
211: * <code>@WebServiceRef</code> / <code>@EJB</code> annotated fields and setter methods,
212: * <b>for <code>mappedName</code> attributes that point directly into JNDI</b>.
213: * This factory will also be used if "alwaysUseJndiLookup" is set to "true" in order
214: * to enforce JNDI lookups even for <code>name</code> attributes and default names.
215: * <p>The default is a {@link org.springframework.jndi.support.SimpleJndiBeanFactory}
216: * for JNDI lookup behavior equivalent to standard Java EE 5 resource injection.
217: * @see #setResourceFactory
218: * @see #setAlwaysUseJndiLookup
219: */
220: public void setJndiFactory(BeanFactory jndiFactory) {
221: Assert.notNull(jndiFactory, "BeanFactory must not be null");
222: this .jndiFactory = jndiFactory;
223: }
224:
225: /**
226: * Specify the factory for objects to be injected into <code>@Resource</code> /
227: * <code>@WebServiceRef</code> / <code>@EJB</code> annotated fields and setter methods,
228: * <b>for <code>name</code> attributes and default names</b>.
229: * <p>The default is the BeanFactory that this post-processor is defined in,
230: * if any, looking up resource names as Spring bean names. Specify the resource
231: * factory explicitly for programmatic usage of this post-processor.
232: * <p>Specifying Spring's {@link org.springframework.jndi.support.SimpleJndiBeanFactory}
233: * leads to JNDI lookup behavior equivalent to standard Java EE 5 resource injection,
234: * even for <code>name</code> attributes and default names. This is the same behavior
235: * that the "alwaysUseJndiLookup" flag enables.
236: * @see #setAlwaysUseJndiLookup
237: */
238: public void setResourceFactory(BeanFactory resourceFactory) {
239: Assert.notNull(resourceFactory, "BeanFactory must not be null");
240: this .resourceFactory = resourceFactory;
241: }
242:
243: public void setBeanFactory(BeanFactory beanFactory)
244: throws BeansException {
245: Assert.notNull(beanFactory, "BeanFactory must not be null");
246: this .beanFactory = beanFactory;
247: if (this .resourceFactory == null) {
248: this .resourceFactory = beanFactory;
249: }
250: }
251:
252: public Object postProcessBeforeInstantiation(Class beanClass,
253: String beanName) throws BeansException {
254: return null;
255: }
256:
257: public boolean postProcessAfterInstantiation(Object bean,
258: String beanName) throws BeansException {
259: InjectionMetadata metadata = findResourceMetadata(bean
260: .getClass());
261: try {
262: metadata.injectFields(bean, beanName);
263: } catch (Throwable ex) {
264: throw new BeanCreationException(beanName,
265: "Injection of resource fields failed", ex);
266: }
267: return true;
268: }
269:
270: public PropertyValues postProcessPropertyValues(PropertyValues pvs,
271: PropertyDescriptor[] pds, Object bean, String beanName)
272: throws BeansException {
273:
274: InjectionMetadata metadata = findResourceMetadata(bean
275: .getClass());
276: try {
277: metadata.injectMethods(bean, beanName, pvs);
278: } catch (Throwable ex) {
279: throw new BeanCreationException(beanName,
280: "Injection of resource methods failed", ex);
281: }
282: return pvs;
283: }
284:
285: private InjectionMetadata findResourceMetadata(final Class clazz) {
286: // Quick check on the concurrent map first, with minimal locking.
287: InjectionMetadata metadata = this .injectionMetadataCache
288: .get(clazz);
289: if (metadata == null) {
290: synchronized (this .injectionMetadataCache) {
291: metadata = this .injectionMetadataCache.get(clazz);
292: if (metadata == null) {
293: final InjectionMetadata newMetadata = new InjectionMetadata();
294: ReflectionUtils.doWithFields(clazz,
295: new ReflectionUtils.FieldCallback() {
296: public void doWith(Field field) {
297: if (webServiceRefClass != null
298: && field
299: .isAnnotationPresent(webServiceRefClass)) {
300: if (Modifier.isStatic(field
301: .getModifiers())) {
302: throw new IllegalStateException(
303: "@WebServiceRef annotation is not supported on static fields");
304: }
305: newMetadata
306: .addInjectedField(new WebServiceRefElement(
307: field, null));
308: } else if (ejbRefClass != null
309: && field
310: .isAnnotationPresent(ejbRefClass)) {
311: if (Modifier.isStatic(field
312: .getModifiers())) {
313: throw new IllegalStateException(
314: "@EJB annotation is not supported on static fields");
315: }
316: newMetadata
317: .addInjectedField(new EjbRefElement(
318: field, null));
319: } else if (field
320: .isAnnotationPresent(Resource.class)) {
321: if (Modifier.isStatic(field
322: .getModifiers())) {
323: throw new IllegalStateException(
324: "@Resource annotation is not supported on static fields");
325: }
326: newMetadata
327: .addInjectedField(new ResourceElement(
328: field, null));
329: }
330: }
331: });
332: ReflectionUtils.doWithMethods(clazz,
333: new ReflectionUtils.MethodCallback() {
334: public void doWith(Method method) {
335: if (webServiceRefClass != null
336: && method
337: .isAnnotationPresent(webServiceRefClass)) {
338: if (Modifier.isStatic(method
339: .getModifiers())) {
340: throw new IllegalStateException(
341: "@WebServiceRef annotation is not supported on static methods");
342: }
343: if (method.getParameterTypes().length != 1) {
344: throw new IllegalStateException(
345: "@WebServiceRef annotation requires a single-arg method: "
346: + method);
347: }
348: PropertyDescriptor pd = BeanUtils
349: .findPropertyForMethod(method);
350: newMetadata
351: .addInjectedMethod(new WebServiceRefElement(
352: method, pd));
353: } else if (ejbRefClass != null
354: && method
355: .isAnnotationPresent(ejbRefClass)) {
356: if (Modifier.isStatic(method
357: .getModifiers())) {
358: throw new IllegalStateException(
359: "@EJB annotation is not supported on static methods");
360: }
361: if (method.getParameterTypes().length != 1) {
362: throw new IllegalStateException(
363: "@EJB annotation requires a single-arg method: "
364: + method);
365: }
366: PropertyDescriptor pd = BeanUtils
367: .findPropertyForMethod(method);
368: newMetadata
369: .addInjectedMethod(new EjbRefElement(
370: method, pd));
371: } else if (method
372: .isAnnotationPresent(Resource.class)) {
373: if (Modifier.isStatic(method
374: .getModifiers())) {
375: throw new IllegalStateException(
376: "@Resource annotation is not supported on static methods");
377: }
378: if (method.getParameterTypes().length != 1) {
379: throw new IllegalStateException(
380: "@Resource annotation requires a single-arg method: "
381: + method);
382: }
383: PropertyDescriptor pd = BeanUtils
384: .findPropertyForMethod(method);
385: newMetadata
386: .addInjectedMethod(new ResourceElement(
387: method, pd));
388: }
389: }
390: });
391: metadata = newMetadata;
392: this .injectionMetadataCache.put(clazz, metadata);
393: }
394: }
395: }
396: return metadata;
397: }
398:
399: /**
400: * Obtain the resource object for the given name and type.
401: * @param resourceElement the descriptor for the annotated field/method
402: * @param requestingBeanName the name of the requesting bean
403: * @return the resource object (never <code>null</code>)
404: * @throws BeansException if we failed to obtain the target resource
405: */
406: protected Object getResource(ResourceElement resourceElement,
407: String requestingBeanName) throws BeansException {
408:
409: Object resource = null;
410: Set autowiredBeanNames = null;
411: String name = resourceElement.name;
412: Class type = resourceElement.lookupType;
413: String mappedName = resourceElement.mappedName;
414:
415: if (StringUtils.hasLength(mappedName)) {
416: return this .jndiFactory.getBean(mappedName, type);
417: }
418: if (this .alwaysUseJndiLookup) {
419: return this .jndiFactory.getBean(name, type);
420: }
421:
422: if (this .resourceFactory == null) {
423: throw new NoSuchBeanDefinitionException(
424: type,
425: "No resource factory configured - "
426: + "override the getResource method or specify the 'resourceFactory' property");
427: }
428:
429: if (this .fallbackToDefaultTypeMatch
430: && resourceElement.isDefaultName
431: && this .resourceFactory instanceof AutowireCapableBeanFactory
432: && !this .resourceFactory.containsBean(name)) {
433: autowiredBeanNames = new HashSet();
434: resource = ((AutowireCapableBeanFactory) this .resourceFactory)
435: .resolveDependency(resourceElement
436: .getDependencyDescriptor(),
437: requestingBeanName, autowiredBeanNames,
438: null);
439: } else {
440: resource = this .resourceFactory.getBean(name, type);
441: autowiredBeanNames = Collections.singleton(name);
442: }
443:
444: if (this .resourceFactory instanceof ConfigurableBeanFactory) {
445: ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) this .resourceFactory;
446: for (Iterator it = autowiredBeanNames.iterator(); it
447: .hasNext();) {
448: String autowiredBeanName = (String) it.next();
449: beanFactory.registerDependentBean(autowiredBeanName,
450: requestingBeanName);
451: }
452: }
453:
454: return resource;
455: }
456:
457: /**
458: * Class representing injection information about an annotated field
459: * or setter method, supporting the @Resource annotation.
460: */
461: private class ResourceElement extends
462: InjectionMetadata.InjectedElement {
463:
464: protected String name;
465:
466: protected boolean isDefaultName = false;
467:
468: protected Class<?> lookupType;
469:
470: protected String mappedName;
471:
472: protected boolean shareable = true;
473:
474: public ResourceElement(Member member, PropertyDescriptor pd) {
475: super (member, pd);
476: initAnnotation((AnnotatedElement) member);
477: }
478:
479: protected void initAnnotation(AnnotatedElement ae) {
480: Resource resource = ae.getAnnotation(Resource.class);
481: String resourceName = resource.name();
482: Class resourceType = resource.type();
483: this .isDefaultName = !StringUtils.hasLength(resourceName);
484: if (this .isDefaultName) {
485: resourceName = this .member.getName();
486: if (this .member instanceof Method
487: && resourceName.startsWith("set")
488: && resourceName.length() > 3) {
489: resourceName = Introspector
490: .decapitalize(resourceName.substring(3));
491: }
492: }
493: if (resourceType != null
494: && !Object.class.equals(resourceType)) {
495: checkResourceType(resourceType);
496: } else {
497: // No resource type specified... check field/method.
498: resourceType = getResourceType();
499: }
500: this .name = resourceName;
501: this .lookupType = resourceType;
502: this .mappedName = resource.mappedName();
503: this .shareable = resource.shareable();
504: }
505:
506: /**
507: * Build a DependencyDescriptor for the underlying field/method.
508: */
509: public DependencyDescriptor getDependencyDescriptor() {
510: if (this .isField) {
511: return new ResourceDependencyDescriptor(
512: (Field) this .member, this .lookupType);
513: } else {
514: return new ResourceDependencyDescriptor(
515: (Method) this .member, this .lookupType);
516: }
517: }
518:
519: @Override
520: protected Object getResourceToInject(Object target,
521: String requestingBeanName) {
522: return getResource(this , requestingBeanName);
523: }
524: }
525:
526: /**
527: * Class representing injection information about an annotated field
528: * or setter method, supporting the @WebServiceRef annotation.
529: */
530: private class WebServiceRefElement extends ResourceElement {
531:
532: private Class elementType;
533:
534: private String wsdlLocation;
535:
536: public WebServiceRefElement(Member member, PropertyDescriptor pd) {
537: super (member, pd);
538: }
539:
540: protected void initAnnotation(AnnotatedElement ae) {
541: WebServiceRef resource = ae
542: .getAnnotation(WebServiceRef.class);
543: String resourceName = resource.name();
544: Class resourceType = resource.type();
545: this .isDefaultName = !StringUtils.hasLength(resourceName);
546: if (this .isDefaultName) {
547: resourceName = this .member.getName();
548: if (this .member instanceof Method
549: && resourceName.startsWith("set")
550: && resourceName.length() > 3) {
551: resourceName = Introspector
552: .decapitalize(resourceName.substring(3));
553: }
554: }
555: if (resourceType != null
556: && !Object.class.equals(resourceType)) {
557: checkResourceType(resourceType);
558: } else {
559: // No resource type specified... check field/method.
560: resourceType = getResourceType();
561: }
562: this .name = resourceName;
563: this .elementType = resourceType;
564: if (Service.class.isAssignableFrom(resourceType)) {
565: this .lookupType = resourceType;
566: } else {
567: this .lookupType = (!Object.class.equals(resource
568: .value()) ? resource.value() : Service.class);
569: }
570: this .mappedName = resource.mappedName();
571: this .wsdlLocation = resource.wsdlLocation();
572: }
573:
574: @Override
575: protected Object getResourceToInject(Object target,
576: String requestingBeanName) {
577: Service service = null;
578: try {
579: service = (Service) getResource(this ,
580: requestingBeanName);
581: } catch (NoSuchBeanDefinitionException notFound) {
582: // Service to be created through generated class.
583: if (Service.class.equals(this .lookupType)) {
584: throw new IllegalStateException(
585: "No resource with name '"
586: + this .name
587: + "' found in context, "
588: + "and no specific JAX-WS Service subclass specified. The typical solution is to either specify "
589: + "a LocalJaxWsServiceFactoryBean with the given name or to specify the (generated) Service "
590: + "subclass as @WebServiceRef(...) value.");
591: }
592: if (StringUtils.hasLength(this .wsdlLocation)) {
593: try {
594: Constructor ctor = this .lookupType
595: .getConstructor(new Class[] {
596: URL.class, QName.class });
597: WebServiceClient clientAnn = this .lookupType
598: .getAnnotation(WebServiceClient.class);
599: if (clientAnn == null) {
600: throw new IllegalStateException(
601: "JAX-WS Service class ["
602: + this .lookupType.getName()
603: + "] does not carry a WebServiceClient annotation");
604: }
605: service = (Service) BeanUtils.instantiateClass(
606: ctor, new Object[] {
607: new URL(this .wsdlLocation),
608: new QName(clientAnn
609: .targetNamespace(),
610: clientAnn.name()) });
611: } catch (NoSuchMethodException ex) {
612: throw new IllegalStateException(
613: "JAX-WS Service class ["
614: + this .lookupType.getName()
615: + "] does not have a (URL, QName) constructor. Cannot apply specified WSDL location ["
616: + this .wsdlLocation + "].");
617: } catch (MalformedURLException ex) {
618: throw new IllegalArgumentException(
619: "Specified WSDL location ["
620: + this .wsdlLocation
621: + "] isn't a valid URL");
622: }
623: } else {
624: service = (Service) BeanUtils
625: .instantiateClass(this .lookupType);
626: }
627: }
628: return service.getPort(this .elementType);
629: }
630: }
631:
632: /**
633: * Class representing injection information about an annotated field
634: * or setter method, supporting the @EJB annotation.
635: */
636: private class EjbRefElement extends ResourceElement {
637:
638: private String beanName;
639:
640: public EjbRefElement(Member member, PropertyDescriptor pd) {
641: super (member, pd);
642: }
643:
644: protected void initAnnotation(AnnotatedElement ae) {
645: EJB resource = ae.getAnnotation(EJB.class);
646: String resourceBeanName = resource.beanName();
647: String resourceName = resource.name();
648: this .isDefaultName = !StringUtils.hasLength(resourceName);
649: if (this .isDefaultName) {
650: resourceName = this .member.getName();
651: if (this .member instanceof Method
652: && resourceName.startsWith("set")
653: && resourceName.length() > 3) {
654: resourceName = Introspector
655: .decapitalize(resourceName.substring(3));
656: }
657: }
658: Class resourceType = resource.beanInterface();
659: if (resourceType != null
660: && !Object.class.equals(resourceType)) {
661: checkResourceType(resourceType);
662: } else {
663: // No resource type specified... check field/method.
664: resourceType = getResourceType();
665: }
666: this .beanName = resourceBeanName;
667: this .name = resourceName;
668: this .lookupType = resourceType;
669: this .mappedName = resource.mappedName();
670: }
671:
672: @Override
673: protected Object getResourceToInject(Object target,
674: String requestingBeanName) {
675: if (StringUtils.hasLength(this .beanName)) {
676: if (beanFactory != null
677: && beanFactory.containsBean(this .beanName)) {
678: // Local match found for explicitly specified local bean name.
679: Object bean = beanFactory.getBean(this .beanName,
680: this .lookupType);
681: if (beanFactory instanceof ConfigurableBeanFactory) {
682: ((ConfigurableBeanFactory) beanFactory)
683: .registerDependentBean(this .beanName,
684: requestingBeanName);
685: }
686: return bean;
687: } else if (this .isDefaultName
688: && !StringUtils.hasLength(this .mappedName)) {
689: throw new NoSuchBeanDefinitionException(
690: this .beanName,
691: "Cannot resolve 'beanName' in local BeanFactory. Consider specifying a general 'name' value instead.");
692: }
693: }
694: // JNDI name lookup - may still go to a local BeanFactory.
695: return getResource(this , requestingBeanName);
696: }
697: }
698:
699: /**
700: * Extension of the DependencyDescriptor class,
701: * overriding the dependency type with the specified resource type.
702: */
703: private static class ResourceDependencyDescriptor extends
704: DependencyDescriptor {
705:
706: private final Class resourceType;
707:
708: public ResourceDependencyDescriptor(Field field,
709: Class resourceType) {
710: super (field, true);
711: this .resourceType = resourceType;
712: }
713:
714: public ResourceDependencyDescriptor(Method method,
715: Class resourceType) {
716: super (new MethodParameter(method, 0), true);
717: this .resourceType = resourceType;
718: }
719:
720: public Class getDependencyType() {
721: return this.resourceType;
722: }
723: }
724:
725: }
|