001/* 002 * Copyright 2002-2015 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 * https://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 017package org.springframework.jndi.support; 018 019import java.util.Arrays; 020import java.util.HashMap; 021import java.util.HashSet; 022import java.util.Map; 023import java.util.Set; 024import javax.naming.NameNotFoundException; 025import javax.naming.NamingException; 026 027import org.springframework.beans.BeansException; 028import org.springframework.beans.factory.BeanDefinitionStoreException; 029import org.springframework.beans.factory.BeanFactory; 030import org.springframework.beans.factory.BeanNotOfRequiredTypeException; 031import org.springframework.beans.factory.NoSuchBeanDefinitionException; 032import org.springframework.core.ResolvableType; 033import org.springframework.jndi.JndiLocatorSupport; 034import 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} 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 */ 060public class SimpleJndiBeanFactory extends JndiLocatorSupport implements BeanFactory { 061 062 /** JNDI names of resources that are known to be shareable, i.e. can be cached */ 063 private final Set<String> shareableResources = new HashSet<String>(); 064 065 /** Cache of shareable singleton objects: bean name --> bean instance */ 066 private final Map<String, Object> singletonObjects = new HashMap<String, Object>(); 067 068 /** Cache of the types of nonshareable resources: bean name --> bean type */ 069 private final Map<String, Class<?>> resourceTypes = new HashMap<String, Class<?>>(); 070 071 072 public SimpleJndiBeanFactory() { 073 setResourceRef(true); 074 } 075 076 077 /** 078 * Add the name of a shareable JNDI resource, 079 * which this factory is allowed to cache once obtained. 080 * @param shareableResource the JNDI name 081 * (typically within the "java:comp/env/" namespace) 082 */ 083 public void addShareableResource(String shareableResource) { 084 this.shareableResources.add(shareableResource); 085 } 086 087 /** 088 * Set a list of names of shareable JNDI resources, 089 * which this factory is allowed to cache once obtained. 090 * @param shareableResources the JNDI names 091 * (typically within the "java:comp/env/" namespace) 092 */ 093 public void setShareableResources(String... shareableResources) { 094 this.shareableResources.addAll(Arrays.asList(shareableResources)); 095 } 096 097 098 //--------------------------------------------------------------------- 099 // Implementation of BeanFactory interface 100 //--------------------------------------------------------------------- 101 102 103 @Override 104 public Object getBean(String name) throws BeansException { 105 return getBean(name, Object.class); 106 } 107 108 @Override 109 public <T> T getBean(String name, Class<T> requiredType) throws BeansException { 110 try { 111 if (isSingleton(name)) { 112 return doGetSingleton(name, requiredType); 113 } 114 else { 115 return lookup(name, requiredType); 116 } 117 } 118 catch (NameNotFoundException ex) { 119 throw new NoSuchBeanDefinitionException(name, "not found in JNDI environment"); 120 } 121 catch (TypeMismatchNamingException ex) { 122 throw new BeanNotOfRequiredTypeException(name, ex.getRequiredType(), ex.getActualType()); 123 } 124 catch (NamingException ex) { 125 throw new BeanDefinitionStoreException("JNDI environment", name, "JNDI lookup failed", ex); 126 } 127 } 128 129 @Override 130 public <T> T getBean(Class<T> requiredType) throws BeansException { 131 return getBean(requiredType.getSimpleName(), requiredType); 132 } 133 134 @Override 135 public Object getBean(String name, Object... args) throws BeansException { 136 if (args != null) { 137 throw new UnsupportedOperationException( 138 "SimpleJndiBeanFactory does not support explicit bean creation arguments"); 139 } 140 return getBean(name); 141 } 142 143 @Override 144 public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException { 145 if (args != null) { 146 throw new UnsupportedOperationException( 147 "SimpleJndiBeanFactory does not support explicit bean creation arguments"); 148 } 149 return getBean(requiredType); 150 } 151 152 @Override 153 public boolean containsBean(String name) { 154 if (this.singletonObjects.containsKey(name) || this.resourceTypes.containsKey(name)) { 155 return true; 156 } 157 try { 158 doGetType(name); 159 return true; 160 } 161 catch (NamingException ex) { 162 return false; 163 } 164 } 165 166 @Override 167 public boolean isSingleton(String name) throws NoSuchBeanDefinitionException { 168 return this.shareableResources.contains(name); 169 } 170 171 @Override 172 public boolean isPrototype(String name) throws NoSuchBeanDefinitionException { 173 return !this.shareableResources.contains(name); 174 } 175 176 @Override 177 public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException { 178 Class<?> type = getType(name); 179 return (type != null && typeToMatch.isAssignableFrom(type)); 180 } 181 182 @Override 183 public boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException { 184 Class<?> type = getType(name); 185 return (typeToMatch == null || (type != null && typeToMatch.isAssignableFrom(type))); 186 } 187 188 @Override 189 public Class<?> getType(String name) throws NoSuchBeanDefinitionException { 190 try { 191 return doGetType(name); 192 } 193 catch (NameNotFoundException ex) { 194 throw new NoSuchBeanDefinitionException(name, "not found in JNDI environment"); 195 } 196 catch (NamingException ex) { 197 return null; 198 } 199 } 200 201 @Override 202 public String[] getAliases(String name) { 203 return new String[0]; 204 } 205 206 207 @SuppressWarnings("unchecked") 208 private <T> T doGetSingleton(String name, Class<T> requiredType) throws NamingException { 209 synchronized (this.singletonObjects) { 210 if (this.singletonObjects.containsKey(name)) { 211 Object jndiObject = this.singletonObjects.get(name); 212 if (requiredType != null && !requiredType.isInstance(jndiObject)) { 213 throw new TypeMismatchNamingException( 214 convertJndiName(name), requiredType, (jndiObject != null ? jndiObject.getClass() : null)); 215 } 216 return (T) jndiObject; 217 } 218 T jndiObject = lookup(name, requiredType); 219 this.singletonObjects.put(name, jndiObject); 220 return jndiObject; 221 } 222 } 223 224 private Class<?> doGetType(String name) throws NamingException { 225 if (isSingleton(name)) { 226 Object jndiObject = doGetSingleton(name, null); 227 return (jndiObject != null ? jndiObject.getClass() : null); 228 } 229 else { 230 synchronized (this.resourceTypes) { 231 if (this.resourceTypes.containsKey(name)) { 232 return this.resourceTypes.get(name); 233 } 234 else { 235 Object jndiObject = lookup(name, null); 236 Class<?> type = (jndiObject != null ? jndiObject.getClass() : null); 237 this.resourceTypes.put(name, type); 238 return type; 239 } 240 } 241 } 242 } 243 244}