001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.xbean.spring.jndi; 018 019 import javax.naming.Binding; 020 import javax.naming.CompositeName; 021 import javax.naming.Context; 022 import javax.naming.LinkRef; 023 import javax.naming.Name; 024 import javax.naming.NameClassPair; 025 import javax.naming.NameNotFoundException; 026 import javax.naming.NameParser; 027 import javax.naming.NamingEnumeration; 028 import javax.naming.NamingException; 029 import javax.naming.NotContextException; 030 import javax.naming.OperationNotSupportedException; 031 import javax.naming.Reference; 032 import javax.naming.spi.NamingManager; 033 034 import java.io.Serializable; 035 import java.util.HashMap; 036 import java.util.Hashtable; 037 import java.util.Iterator; 038 import java.util.Map; 039 040 /** 041 * A simple spring based JNDI context which is mutable 042 * 043 * @version $Revision: 657 $ 044 */ 045 public class DefaultContext implements Context, Serializable { 046 047 private static final long serialVersionUID = -5754338187296859149L; 048 protected static final NameParser nameParser = new NameParserImpl(); 049 050 private boolean freeze = false; 051 052 protected final Hashtable environment; // environment for this context 053 protected final Map bindings; // bindings at my level 054 protected final Map treeBindings; // all bindings under me 055 056 private boolean frozen = false; 057 private String nameInNamespace = ""; 058 public static final String SEPARATOR = "/"; 059 060 public DefaultContext() { 061 environment = new Hashtable(); 062 bindings = new HashMap(); 063 treeBindings = new HashMap(); 064 } 065 066 public DefaultContext(Hashtable env) { 067 if (env == null) { 068 this.environment = new Hashtable(); 069 } 070 else { 071 this.environment = new Hashtable(env); 072 } 073 this.bindings = new HashMap(); 074 this.treeBindings = new HashMap(); 075 } 076 077 public DefaultContext(Hashtable environment, Map bindings) { 078 if (environment == null) { 079 this.environment = new Hashtable(); 080 } 081 else { 082 this.environment = new Hashtable(environment); 083 } 084 this.bindings = bindings; 085 treeBindings = new HashMap(); 086 frozen = true; 087 } 088 089 public DefaultContext(Hashtable environment, Map bindings, String nameInNamespace) { 090 this(environment, bindings); 091 this.nameInNamespace = nameInNamespace; 092 } 093 094 protected DefaultContext(DefaultContext clone, Hashtable env) { 095 this.bindings = clone.bindings; 096 this.treeBindings = clone.treeBindings; 097 this.environment = new Hashtable(env); 098 } 099 100 protected DefaultContext(DefaultContext clone, Hashtable env, String nameInNamespace) { 101 this(clone, env); 102 this.nameInNamespace = nameInNamespace; 103 } 104 105 public Object addToEnvironment(String propName, Object propVal) throws NamingException { 106 return environment.put(propName, propVal); 107 } 108 109 public Hashtable getEnvironment() throws NamingException { 110 return (Hashtable) environment.clone(); 111 } 112 113 public Object removeFromEnvironment(String propName) throws NamingException { 114 return environment.remove(propName); 115 } 116 117 public Object lookup(String name) throws NamingException { 118 if (name.length() == 0) { 119 return this; 120 } 121 Object result = treeBindings.get(name); 122 if (result == null) { 123 result = bindings.get(name); 124 } 125 if (result == null) { 126 int pos = name.indexOf(':'); 127 if (pos > 0) { 128 String scheme = name.substring(0, pos); 129 Context ctx = NamingManager.getURLContext(scheme, environment); 130 if (ctx == null) { 131 throw new NamingException("scheme " + scheme + " not recognized"); 132 } 133 return ctx.lookup(name); 134 } 135 else { 136 // Split out the first name of the path 137 // and look for it in the bindings map. 138 CompositeName path = new CompositeName(name); 139 140 if (path.size() == 0) { 141 return this; 142 } 143 else { 144 String first = path.get(0); 145 Object obj = bindings.get(first); 146 if (obj == null) { 147 throw new NameNotFoundException(name); 148 } 149 else if (obj instanceof Context && path.size() > 1) { 150 Context subContext = (Context) obj; 151 obj = subContext.lookup(path.getSuffix(1)); 152 } 153 return obj; 154 } 155 } 156 } 157 if (result instanceof LinkRef) { 158 LinkRef ref = (LinkRef) result; 159 result = lookup(ref.getLinkName()); 160 } 161 if (result instanceof Reference) { 162 try { 163 result = NamingManager.getObjectInstance(result, null, null, this.environment); 164 } 165 catch (NamingException e) { 166 throw e; 167 } 168 catch (Exception e) { 169 throw (NamingException) new NamingException("could not look up : " + name).initCause(e); 170 } 171 } 172 if (result instanceof DefaultContext) { 173 String prefix = getNameInNamespace(); 174 if (prefix.length() > 0) { 175 prefix = prefix + SEPARATOR; 176 } 177 result = new DefaultContext((DefaultContext) result, environment, prefix + name); 178 } 179 return result; 180 } 181 182 public Object lookup(Name name) throws NamingException { 183 return lookup(name.toString()); 184 } 185 186 public Object lookupLink(String name) throws NamingException { 187 return lookup(name); 188 } 189 190 public Name composeName(Name name, Name prefix) throws NamingException { 191 Name result = (Name) prefix.clone(); 192 result.addAll(name); 193 return result; 194 } 195 196 public String composeName(String name, String prefix) throws NamingException { 197 CompositeName result = new CompositeName(prefix); 198 result.addAll(new CompositeName(name)); 199 return result.toString(); 200 } 201 202 public NamingEnumeration list(String name) throws NamingException { 203 Object o = lookup(name); 204 if (o == this) { 205 return new DefaultContext.ListEnumeration(); 206 } 207 else if (o instanceof Context) { 208 return ((Context) o).list(""); 209 } 210 else { 211 throw new NotContextException(); 212 } 213 } 214 215 public NamingEnumeration listBindings(String name) throws NamingException { 216 Object o = lookup(name); 217 if (o == this) { 218 return new DefaultContext.ListBindingEnumeration(); 219 } 220 else if (o instanceof Context) { 221 return ((Context) o).listBindings(""); 222 } 223 else { 224 throw new NotContextException(); 225 } 226 } 227 228 public Object lookupLink(Name name) throws NamingException { 229 return lookupLink(name.toString()); 230 } 231 232 public NamingEnumeration list(Name name) throws NamingException { 233 return list(name.toString()); 234 } 235 236 public NamingEnumeration listBindings(Name name) throws NamingException { 237 return listBindings(name.toString()); 238 } 239 240 public void bind(Name name, Object value) throws NamingException { 241 bind(name.toString(), value); 242 } 243 244 public void bind(String name, Object value) throws NamingException { 245 checkFrozen(); 246 internalBind(name, value); 247 } 248 249 public void close() throws NamingException { 250 // ignore 251 } 252 253 public Context createSubcontext(Name name) throws NamingException { 254 throw new OperationNotSupportedException(); 255 } 256 257 public Context createSubcontext(String name) throws NamingException { 258 throw new OperationNotSupportedException(); 259 } 260 261 public void destroySubcontext(Name name) throws NamingException { 262 throw new OperationNotSupportedException(); 263 } 264 265 public void destroySubcontext(String name) throws NamingException { 266 throw new OperationNotSupportedException(); 267 } 268 269 public String getNameInNamespace() throws NamingException { 270 return nameInNamespace; 271 } 272 273 public NameParser getNameParser(Name name) throws NamingException { 274 return nameParser; 275 } 276 277 public NameParser getNameParser(String name) throws NamingException { 278 return nameParser; 279 } 280 281 public void rebind(Name name, Object value) throws NamingException { 282 rebind(name.toString(), value); 283 } 284 285 public void rebind(String name, Object value) throws NamingException { 286 checkFrozen(); 287 internalBind(name, value, true); 288 } 289 290 public void rename(Name oldName, Name newName) throws NamingException { 291 checkFrozen(); 292 Object value = lookup(oldName); 293 unbind(oldName); 294 bind(newName, value); 295 } 296 297 public void rename(String oldName, String newName) throws NamingException { 298 Object value = lookup(oldName); 299 unbind(oldName); 300 bind(newName, value); 301 } 302 303 public void unbind(Name name) throws NamingException { 304 unbind(name.toString()); 305 } 306 307 public void unbind(String name) throws NamingException { 308 checkFrozen(); 309 internalBind(name, null, true); 310 } 311 312 private abstract class LocalNamingEnumeration implements NamingEnumeration { 313 private Iterator i = bindings.entrySet().iterator(); 314 315 public boolean hasMore() throws NamingException { 316 return i.hasNext(); 317 } 318 319 public boolean hasMoreElements() { 320 return i.hasNext(); 321 } 322 323 protected Map.Entry getNext() { 324 return (Map.Entry) i.next(); 325 } 326 327 public void close() throws NamingException { 328 } 329 } 330 331 private class ListEnumeration extends DefaultContext.LocalNamingEnumeration { 332 public Object next() throws NamingException { 333 return nextElement(); 334 } 335 336 public Object nextElement() { 337 Map.Entry entry = getNext(); 338 return new NameClassPair((String) entry.getKey(), entry.getValue().getClass().getName()); 339 } 340 } 341 342 private class ListBindingEnumeration extends DefaultContext.LocalNamingEnumeration { 343 public Object next() throws NamingException { 344 return nextElement(); 345 } 346 347 public Object nextElement() { 348 Map.Entry entry = getNext(); 349 return new Binding((String) entry.getKey(), entry.getValue()); 350 } 351 } 352 353 public Map getEntries() { 354 return new HashMap(bindings); 355 } 356 357 public void setEntries(Map entries) throws NamingException { 358 if (entries != null) { 359 for (Iterator iter = entries.entrySet().iterator(); iter.hasNext();) { 360 Map.Entry entry = (Map.Entry) iter.next(); 361 String name = (String) entry.getKey(); 362 Object value = entry.getValue(); 363 internalBind(name, value); 364 } 365 } 366 } 367 368 public boolean isFreeze() { 369 return freeze; 370 } 371 372 public void setFreeze(boolean freeze) { 373 this.freeze = freeze; 374 } 375 376 /** 377 * internalBind is intended for use only during setup or possibly by suitably synchronized superclasses. 378 * It binds every possible lookup into a map in each context. To do this, each context 379 * strips off one name segment and if necessary creates a new context for it. Then it asks that context 380 * to bind the remaining name. It returns a map containing all the bindings from the next context, plus 381 * the context it just created (if it in fact created it). (the names are suitably extended by the segment 382 * originally lopped off). 383 * 384 * @param name 385 * @param value 386 * @return 387 * @throws javax.naming.NamingException 388 */ 389 protected Map internalBind(String name, Object value) throws NamingException { 390 return internalBind(name, value, false); 391 392 } 393 protected Map internalBind(String name, Object value, boolean allowRebind) throws NamingException { 394 395 if (name == null || name.length() == 0){ 396 throw new NamingException("Invalid Name " + name); 397 } 398 if (frozen){ 399 throw new NamingException("Read only"); 400 } 401 402 Map newBindings = new HashMap(); 403 int pos = name.indexOf('/'); 404 if (pos == -1) { 405 Object oldValue = treeBindings.put(name, value); 406 if (!allowRebind && oldValue != null) { 407 throw new NamingException("Something already bound at " + name); 408 } 409 bindings.put(name, value); 410 newBindings.put(name, value); 411 } 412 else { 413 String segment = name.substring(0, pos); 414 415 if (segment == null || segment.length()==0){ 416 throw new NamingException("Invalid segment " + segment); 417 } 418 Object o = treeBindings.get(segment); 419 if (o == null) { 420 o = newContext(); 421 treeBindings.put(segment, o); 422 bindings.put(segment, o); 423 newBindings.put(segment, o); 424 } 425 else if (!(o instanceof DefaultContext)) { 426 throw new NamingException("Something already bound where a subcontext should go"); 427 } 428 DefaultContext defaultContext = (DefaultContext) o; 429 String remainder = name.substring(pos + 1); 430 Map subBindings = defaultContext.internalBind(remainder, value, allowRebind); 431 for (Iterator iterator = subBindings.entrySet().iterator(); iterator.hasNext();) { 432 Map.Entry entry = (Map.Entry) iterator.next(); 433 String subName = segment + "/" + (String) entry.getKey(); 434 Object bound = entry.getValue(); 435 treeBindings.put(subName, bound); 436 newBindings.put(subName, bound); 437 } 438 } 439 return newBindings; 440 } 441 442 protected void checkFrozen() throws OperationNotSupportedException { 443 if (isFreeze()) { 444 throw new OperationNotSupportedException("JNDI context is frozen!"); 445 } 446 } 447 448 protected DefaultContext newContext() { 449 return new DefaultContext(); 450 } 451 452 }