001/* 002 * Copyright 2002-2020 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.mock.jndi; 018 019import java.util.HashMap; 020import java.util.Hashtable; 021import java.util.Iterator; 022import java.util.Map; 023 024import javax.naming.Binding; 025import javax.naming.Context; 026import javax.naming.Name; 027import javax.naming.NameClassPair; 028import javax.naming.NameNotFoundException; 029import javax.naming.NameParser; 030import javax.naming.NamingEnumeration; 031import javax.naming.NamingException; 032import javax.naming.OperationNotSupportedException; 033 034import org.apache.commons.logging.Log; 035import org.apache.commons.logging.LogFactory; 036 037import org.springframework.lang.Nullable; 038import org.springframework.util.StringUtils; 039 040/** 041 * Simple implementation of a JNDI naming context. 042 * Only supports binding plain Objects to String names. 043 * Mainly for test environments, but also usable for standalone applications. 044 * 045 * <p>This class is not intended for direct usage by applications, although it 046 * can be used for example to override JndiTemplate's {@code createInitialContext} 047 * method in unit tests. Typically, SimpleNamingContextBuilder will be used to 048 * set up a JVM-level JNDI environment. 049 * 050 * @author Rod Johnson 051 * @author Juergen Hoeller 052 * @see SimpleNamingContextBuilder 053 * @see org.springframework.jndi.JndiTemplate#createInitialContext 054 * @deprecated Deprecated as of Spring Framework 5.2 in favor of complete solutions from 055 * third parties such as <a href="https://github.com/h-thurow/Simple-JNDI">Simple-JNDI</a> 056 */ 057@Deprecated 058public class SimpleNamingContext implements Context { 059 060 private final Log logger = LogFactory.getLog(getClass()); 061 062 private final String root; 063 064 private final Hashtable<String, Object> boundObjects; 065 066 private final Hashtable<String, Object> environment = new Hashtable<>(); 067 068 069 /** 070 * Create a new naming context. 071 */ 072 public SimpleNamingContext() { 073 this(""); 074 } 075 076 /** 077 * Create a new naming context with the given naming root. 078 */ 079 public SimpleNamingContext(String root) { 080 this.root = root; 081 this.boundObjects = new Hashtable<>(); 082 } 083 084 /** 085 * Create a new naming context with the given naming root, 086 * the given name/object map, and the JNDI environment entries. 087 */ 088 public SimpleNamingContext( 089 String root, Hashtable<String, Object> boundObjects, @Nullable Hashtable<String, Object> env) { 090 091 this.root = root; 092 this.boundObjects = boundObjects; 093 if (env != null) { 094 this.environment.putAll(env); 095 } 096 } 097 098 099 // Actual implementations of Context methods follow 100 101 @Override 102 public NamingEnumeration<NameClassPair> list(String root) throws NamingException { 103 if (logger.isDebugEnabled()) { 104 logger.debug("Listing name/class pairs under [" + root + "]"); 105 } 106 return new NameClassPairEnumeration(this, root); 107 } 108 109 @Override 110 public NamingEnumeration<Binding> listBindings(String root) throws NamingException { 111 if (logger.isDebugEnabled()) { 112 logger.debug("Listing bindings under [" + root + "]"); 113 } 114 return new BindingEnumeration(this, root); 115 } 116 117 /** 118 * Look up the object with the given name. 119 * <p>Note: Not intended for direct use by applications. 120 * Will be used by any standard InitialContext JNDI lookups. 121 * @throws javax.naming.NameNotFoundException if the object could not be found 122 */ 123 @Override 124 public Object lookup(String lookupName) throws NameNotFoundException { 125 String name = this.root + lookupName; 126 if (logger.isDebugEnabled()) { 127 logger.debug("Static JNDI lookup: [" + name + "]"); 128 } 129 if (name.isEmpty()) { 130 return new SimpleNamingContext(this.root, this.boundObjects, this.environment); 131 } 132 Object found = this.boundObjects.get(name); 133 if (found == null) { 134 if (!name.endsWith("/")) { 135 name = name + "/"; 136 } 137 for (String boundName : this.boundObjects.keySet()) { 138 if (boundName.startsWith(name)) { 139 return new SimpleNamingContext(name, this.boundObjects, this.environment); 140 } 141 } 142 throw new NameNotFoundException( 143 "Name [" + this.root + lookupName + "] not bound; " + this.boundObjects.size() + " bindings: [" + 144 StringUtils.collectionToDelimitedString(this.boundObjects.keySet(), ",") + "]"); 145 } 146 return found; 147 } 148 149 @Override 150 public Object lookupLink(String name) throws NameNotFoundException { 151 return lookup(name); 152 } 153 154 /** 155 * Bind the given object to the given name. 156 * Note: Not intended for direct use by applications 157 * if setting up a JVM-level JNDI environment. 158 * Use SimpleNamingContextBuilder to set up JNDI bindings then. 159 * @see org.springframework.mock.jndi.SimpleNamingContextBuilder#bind 160 */ 161 @Override 162 public void bind(String name, Object obj) { 163 if (logger.isInfoEnabled()) { 164 logger.info("Static JNDI binding: [" + this.root + name + "] = [" + obj + "]"); 165 } 166 this.boundObjects.put(this.root + name, obj); 167 } 168 169 @Override 170 public void unbind(String name) { 171 if (logger.isInfoEnabled()) { 172 logger.info("Static JNDI remove: [" + this.root + name + "]"); 173 } 174 this.boundObjects.remove(this.root + name); 175 } 176 177 @Override 178 public void rebind(String name, Object obj) { 179 bind(name, obj); 180 } 181 182 @Override 183 public void rename(String oldName, String newName) throws NameNotFoundException { 184 Object obj = lookup(oldName); 185 unbind(oldName); 186 bind(newName, obj); 187 } 188 189 @Override 190 public Context createSubcontext(String name) { 191 String subcontextName = this.root + name; 192 if (!subcontextName.endsWith("/")) { 193 subcontextName += "/"; 194 } 195 Context subcontext = new SimpleNamingContext(subcontextName, this.boundObjects, this.environment); 196 bind(name, subcontext); 197 return subcontext; 198 } 199 200 @Override 201 public void destroySubcontext(String name) { 202 unbind(name); 203 } 204 205 @Override 206 public String composeName(String name, String prefix) { 207 return prefix + name; 208 } 209 210 @Override 211 public Hashtable<String, Object> getEnvironment() { 212 return this.environment; 213 } 214 215 @Override 216 @Nullable 217 public Object addToEnvironment(String propName, Object propVal) { 218 return this.environment.put(propName, propVal); 219 } 220 221 @Override 222 public Object removeFromEnvironment(String propName) { 223 return this.environment.remove(propName); 224 } 225 226 @Override 227 public void close() { 228 } 229 230 231 // Unsupported methods follow: no support for javax.naming.Name 232 233 @Override 234 public NamingEnumeration<NameClassPair> list(Name name) throws NamingException { 235 throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]"); 236 } 237 238 @Override 239 public NamingEnumeration<Binding> listBindings(Name name) throws NamingException { 240 throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]"); 241 } 242 243 @Override 244 public Object lookup(Name name) throws NamingException { 245 throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]"); 246 } 247 248 @Override 249 public Object lookupLink(Name name) throws NamingException { 250 throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]"); 251 } 252 253 @Override 254 public void bind(Name name, Object obj) throws NamingException { 255 throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]"); 256 } 257 258 @Override 259 public void unbind(Name name) throws NamingException { 260 throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]"); 261 } 262 263 @Override 264 public void rebind(Name name, Object obj) throws NamingException { 265 throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]"); 266 } 267 268 @Override 269 public void rename(Name oldName, Name newName) throws NamingException { 270 throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]"); 271 } 272 273 @Override 274 public Context createSubcontext(Name name) throws NamingException { 275 throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]"); 276 } 277 278 @Override 279 public void destroySubcontext(Name name) throws NamingException { 280 throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]"); 281 } 282 283 @Override 284 public String getNameInNamespace() throws NamingException { 285 throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]"); 286 } 287 288 @Override 289 public NameParser getNameParser(Name name) throws NamingException { 290 throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]"); 291 } 292 293 @Override 294 public NameParser getNameParser(String name) throws NamingException { 295 throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]"); 296 } 297 298 @Override 299 public Name composeName(Name name, Name prefix) throws NamingException { 300 throw new OperationNotSupportedException("SimpleNamingContext does not support [javax.naming.Name]"); 301 } 302 303 304 private abstract static class AbstractNamingEnumeration<T> implements NamingEnumeration<T> { 305 306 private final Iterator<T> iterator; 307 308 private AbstractNamingEnumeration(SimpleNamingContext context, String proot) throws NamingException { 309 if (!proot.isEmpty() && !proot.endsWith("/")) { 310 proot = proot + "/"; 311 } 312 String root = context.root + proot; 313 Map<String, T> contents = new HashMap<>(); 314 for (String boundName : context.boundObjects.keySet()) { 315 if (boundName.startsWith(root)) { 316 int startIndex = root.length(); 317 int endIndex = boundName.indexOf('/', startIndex); 318 String strippedName = 319 (endIndex != -1 ? boundName.substring(startIndex, endIndex) : boundName.substring(startIndex)); 320 if (!contents.containsKey(strippedName)) { 321 try { 322 contents.put(strippedName, createObject(strippedName, context.lookup(proot + strippedName))); 323 } 324 catch (NameNotFoundException ex) { 325 // cannot happen 326 } 327 } 328 } 329 } 330 if (contents.size() == 0) { 331 throw new NamingException("Invalid root: [" + context.root + proot + "]"); 332 } 333 this.iterator = contents.values().iterator(); 334 } 335 336 protected abstract T createObject(String strippedName, Object obj); 337 338 @Override 339 public boolean hasMore() { 340 return this.iterator.hasNext(); 341 } 342 343 @Override 344 public T next() { 345 return this.iterator.next(); 346 } 347 348 @Override 349 public boolean hasMoreElements() { 350 return this.iterator.hasNext(); 351 } 352 353 @Override 354 public T nextElement() { 355 return this.iterator.next(); 356 } 357 358 @Override 359 public void close() { 360 } 361 } 362 363 364 private static final class NameClassPairEnumeration extends AbstractNamingEnumeration<NameClassPair> { 365 366 private NameClassPairEnumeration(SimpleNamingContext context, String root) throws NamingException { 367 super(context, root); 368 } 369 370 @Override 371 protected NameClassPair createObject(String strippedName, Object obj) { 372 return new NameClassPair(strippedName, obj.getClass().getName()); 373 } 374 } 375 376 377 private static final class BindingEnumeration extends AbstractNamingEnumeration<Binding> { 378 379 private BindingEnumeration(SimpleNamingContext context, String root) throws NamingException { 380 super(context, root); 381 } 382 383 @Override 384 protected Binding createObject(String strippedName, Object obj) { 385 return new Binding(strippedName, obj); 386 } 387 } 388 389}