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.jndi.support;
018:
019: import java.util.Arrays;
020: import java.util.HashMap;
021: import java.util.HashSet;
022: import java.util.Map;
023: import java.util.Set;
024:
025: import javax.naming.NameNotFoundException;
026: import javax.naming.NamingException;
027:
028: import org.springframework.beans.BeansException;
029: import org.springframework.beans.factory.BeanDefinitionStoreException;
030: import org.springframework.beans.factory.BeanFactory;
031: import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
032: import org.springframework.beans.factory.NoSuchBeanDefinitionException;
033: import org.springframework.jndi.JndiLocatorSupport;
034: import org.springframework.jndi.TypeMismatchNamingException;
035:
036: /**
037: * Simple JNDI-based implementation of Spring's
038: * {@link org.springframework.beans.factory.BeanFactory} interface.
039: * Does not support enumerating bean definitions, hence doesn't implement
040: * the {@link org.springframework.beans.factory.ListableBeanFactory} interface.
041: *
042: * <p>This factory resolves given bean names as JNDI names within the
043: * J2EE application's "java:comp/env/" namespace. It caches the resolved
044: * types for all obtained objects, and optionally also caches shareable
045: * objects (if they are explicitly marked as
046: * {@link #addShareableResource shareable resource}.
047: *
048: * <p>The main intent of this factory is usage in combination with Spring's
049: * {@link org.springframework.context.annotation.CommonAnnotationBeanPostProcessor},
050: * configured as "resourceFactory" for resolving <code>@Resource</code>
051: * annotations as JNDI objects without intermediate bean definitions.
052: * It may be used for similar lookup scenarios as well, of course,
053: * in particular if BeanFactory-style type checking is required.
054: *
055: * @author Juergen Hoeller
056: * @since 2.5
057: * @see org.springframework.beans.factory.support.DefaultListableBeanFactory
058: * @see org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
059: */
060: public class SimpleJndiBeanFactory extends JndiLocatorSupport implements
061: BeanFactory {
062:
063: /** JNDI names of resources that are known to be shareable, i.e. can be cached */
064: private final Set shareableResources = new HashSet();
065:
066: /** Cache of shareable singleton objects: bean name --> bean instance */
067: private final Map singletonObjects = new HashMap();
068:
069: /** Cache of the types of nonshareable resources: bean name --> bean type */
070: private final Map resourceTypes = new HashMap();
071:
072: public SimpleJndiBeanFactory() {
073: setResourceRef(true);
074: }
075:
076: /**
077: * Set a list of names of shareable JNDI resources,
078: * which this factory is allowed to cache once obtained.
079: * @param shareableResources the JNDI names
080: * (typically within the "java:comp/env/" namespace)
081: */
082: public void setShareableResources(String[] shareableResources) {
083: this .shareableResources.addAll(Arrays
084: .asList(shareableResources));
085: }
086:
087: /**
088: * Add the name of a shareable JNDI resource,
089: * which this factory is allowed to cache once obtained.
090: * @param shareableResource the JNDI name
091: * (typically within the "java:comp/env/" namespace)
092: */
093: public void addShareableResource(String shareableResource) {
094: this .shareableResources.add(shareableResource);
095: }
096:
097: public Object getBean(String name) throws BeansException {
098: return getBean(name, (Class) null);
099: }
100:
101: public Object getBean(String name, Class requiredType)
102: throws BeansException {
103: try {
104: if (isSingleton(name)) {
105: return doGetSingleton(name, requiredType);
106: } else {
107: return lookup(name, requiredType);
108: }
109: } catch (NameNotFoundException ex) {
110: throw new NoSuchBeanDefinitionException(name,
111: "not found in JNDI environment");
112: } catch (TypeMismatchNamingException ex) {
113: throw new BeanNotOfRequiredTypeException(name, ex
114: .getRequiredType(), ex.getActualType());
115: } catch (NamingException ex) {
116: throw new BeanDefinitionStoreException("JNDI environment",
117: name, "JNDI lookup failed", ex);
118: }
119: }
120:
121: public Object getBean(String name, Object[] args)
122: throws BeansException {
123: if (args != null) {
124: throw new UnsupportedOperationException(
125: "SimpleJndiBeanFactory does not support explicit bean creation arguments)");
126: }
127: return getBean(name);
128: }
129:
130: public boolean containsBean(String name) {
131: if (this .singletonObjects.containsKey(name)
132: || this .resourceTypes.containsKey(name)) {
133: return true;
134: }
135: try {
136: doGetType(name);
137: return true;
138: } catch (NamingException ex) {
139: return false;
140: }
141: }
142:
143: public boolean isSingleton(String name)
144: throws NoSuchBeanDefinitionException {
145: return this .shareableResources.contains(name);
146: }
147:
148: public boolean isPrototype(String name)
149: throws NoSuchBeanDefinitionException {
150: return !this .shareableResources.contains(name);
151: }
152:
153: public boolean isTypeMatch(String name, Class targetType)
154: throws NoSuchBeanDefinitionException {
155: Class type = getType(name);
156: return (targetType == null || (type != null && targetType
157: .isAssignableFrom(type)));
158: }
159:
160: public Class getType(String name)
161: throws NoSuchBeanDefinitionException {
162: try {
163: return doGetType(name);
164: } catch (NameNotFoundException ex) {
165: throw new NoSuchBeanDefinitionException(name,
166: "not found in JNDI environment");
167: } catch (NamingException ex) {
168: return null;
169: }
170: }
171:
172: public String[] getAliases(String name) {
173: return new String[0];
174: }
175:
176: private Object doGetSingleton(String name, Class requiredType)
177: throws NamingException {
178: synchronized (this .singletonObjects) {
179: if (this .singletonObjects.containsKey(name)) {
180: Object jndiObject = this .singletonObjects.get(name);
181: if (requiredType != null
182: && !requiredType.isInstance(jndiObject)) {
183: throw new TypeMismatchNamingException(
184: convertJndiName(name), requiredType,
185: (jndiObject != null ? jndiObject.getClass()
186: : null));
187: }
188: return jndiObject;
189: }
190: Object jndiObject = lookup(name, requiredType);
191: this .singletonObjects.put(name, jndiObject);
192: return jndiObject;
193: }
194: }
195:
196: private Class doGetType(String name) throws NamingException {
197: if (isSingleton(name)) {
198: Object jndiObject = doGetSingleton(name, null);
199: return (jndiObject != null ? jndiObject.getClass() : null);
200: } else {
201: synchronized (this .resourceTypes) {
202: if (this .resourceTypes.containsKey(name)) {
203: return (Class) this .resourceTypes.get(name);
204: } else {
205: Object jndiObject = lookup(name, null);
206: Class type = (jndiObject != null ? jndiObject
207: .getClass() : null);
208: this.resourceTypes.put(name, type);
209: return type;
210: }
211: }
212: }
213: }
214:
215: }
|