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 018 package org.apache.activemq.jndi; 019 020 import java.io.Serializable; 021 import java.util.Collections; 022 import java.util.HashMap; 023 import java.util.Hashtable; 024 import java.util.Iterator; 025 import java.util.Map; 026 027 import javax.naming.Binding; 028 import javax.naming.CompositeName; 029 import javax.naming.Context; 030 import javax.naming.LinkRef; 031 import javax.naming.Name; 032 import javax.naming.NameClassPair; 033 import javax.naming.NameNotFoundException; 034 import javax.naming.NameParser; 035 import javax.naming.NamingEnumeration; 036 import javax.naming.NamingException; 037 import javax.naming.NotContextException; 038 import javax.naming.OperationNotSupportedException; 039 import javax.naming.Reference; 040 import javax.naming.spi.NamingManager; 041 042 /** 043 * A read-only Context <p/> This version assumes it and all its subcontext are 044 * read-only and any attempt to modify (e.g. through bind) will result in an 045 * OperationNotSupportedException. Each Context in the tree builds a cache of 046 * the entries in all sub-contexts to optimise the performance of lookup. 047 * </p> 048 * <p> 049 * This implementation is intended to optimise the performance of lookup(String) 050 * to about the level of a HashMap get. It has been observed that the scheme 051 * resolution phase performed by the JVM takes considerably longer, so for 052 * optimum performance lookups should be coded like: 053 * </p> 054 * <code> 055 * Context componentContext = (Context)new InitialContext().lookup("java:comp"); 056 * String envEntry = (String) componentContext.lookup("env/myEntry"); 057 * String envEntry2 = (String) componentContext.lookup("env/myEntry2"); 058 * </code> 059 * 060 * @version $Revision: 1.2 $ $Date: 2005/08/27 03:52:39 $ 061 */ 062 @SuppressWarnings("unchecked") 063 public class ReadOnlyContext implements Context, Serializable { 064 065 public static final String SEPARATOR = "/"; 066 protected static final NameParser NAME_PARSER = new NameParserImpl(); 067 private static final long serialVersionUID = -5754338187296859149L; 068 069 protected final Hashtable<String, Object> environment; // environment for this context 070 protected final Map<String, Object> bindings; // bindings at my level 071 protected final Map<String, Object> treeBindings; // all bindings under me 072 073 private boolean frozen; 074 private String nameInNamespace = ""; 075 076 public ReadOnlyContext() { 077 environment = new Hashtable<String, Object>(); 078 bindings = new HashMap<String, Object>(); 079 treeBindings = new HashMap<String, Object>(); 080 } 081 082 public ReadOnlyContext(Hashtable env) { 083 if (env == null) { 084 this.environment = new Hashtable<String, Object>(); 085 } else { 086 this.environment = new Hashtable<String, Object>(env); 087 } 088 this.bindings = Collections.EMPTY_MAP; 089 this.treeBindings = Collections.EMPTY_MAP; 090 } 091 092 public ReadOnlyContext(Hashtable environment, Map<String, Object> bindings) { 093 if (environment == null) { 094 this.environment = new Hashtable<String, Object>(); 095 } else { 096 this.environment = new Hashtable<String, Object>(environment); 097 } 098 this.bindings = bindings; 099 treeBindings = new HashMap<String, Object>(); 100 frozen = true; 101 } 102 103 public ReadOnlyContext(Hashtable environment, Map bindings, String nameInNamespace) { 104 this(environment, bindings); 105 this.nameInNamespace = nameInNamespace; 106 } 107 108 protected ReadOnlyContext(ReadOnlyContext clone, Hashtable env) { 109 this.bindings = clone.bindings; 110 this.treeBindings = clone.treeBindings; 111 this.environment = new Hashtable<String, Object>(env); 112 } 113 114 protected ReadOnlyContext(ReadOnlyContext clone, Hashtable<String, Object> env, String nameInNamespace) { 115 this(clone, env); 116 this.nameInNamespace = nameInNamespace; 117 } 118 119 public void freeze() { 120 frozen = true; 121 } 122 123 boolean isFrozen() { 124 return frozen; 125 } 126 127 /** 128 * internalBind is intended for use only during setup or possibly by 129 * suitably synchronized superclasses. It binds every possible lookup into a 130 * map in each context. To do this, each context strips off one name segment 131 * and if necessary creates a new context for it. Then it asks that context 132 * to bind the remaining name. It returns a map containing all the bindings 133 * from the next context, plus the context it just created (if it in fact 134 * created it). (the names are suitably extended by the segment originally 135 * lopped off). 136 * 137 * @param name 138 * @param value 139 * @return 140 * @throws javax.naming.NamingException 141 */ 142 protected Map<String, Object> internalBind(String name, Object value) throws NamingException { 143 assert name != null && name.length() > 0; 144 assert !frozen; 145 146 Map<String, Object> newBindings = new HashMap<String, Object>(); 147 int pos = name.indexOf('/'); 148 if (pos == -1) { 149 if (treeBindings.put(name, value) != null) { 150 throw new NamingException("Something already bound at " + name); 151 } 152 bindings.put(name, value); 153 newBindings.put(name, value); 154 } else { 155 String segment = name.substring(0, pos); 156 assert segment != null; 157 assert !segment.equals(""); 158 Object o = treeBindings.get(segment); 159 if (o == null) { 160 o = newContext(); 161 treeBindings.put(segment, o); 162 bindings.put(segment, o); 163 newBindings.put(segment, o); 164 } else if (!(o instanceof ReadOnlyContext)) { 165 throw new NamingException("Something already bound where a subcontext should go"); 166 } 167 ReadOnlyContext readOnlyContext = (ReadOnlyContext)o; 168 String remainder = name.substring(pos + 1); 169 Map<String, Object> subBindings = readOnlyContext.internalBind(remainder, value); 170 for (Iterator iterator = subBindings.entrySet().iterator(); iterator.hasNext();) { 171 Map.Entry entry = (Map.Entry)iterator.next(); 172 String subName = segment + "/" + (String)entry.getKey(); 173 Object bound = entry.getValue(); 174 treeBindings.put(subName, bound); 175 newBindings.put(subName, bound); 176 } 177 } 178 return newBindings; 179 } 180 181 protected ReadOnlyContext newContext() { 182 return new ReadOnlyContext(); 183 } 184 185 public Object addToEnvironment(String propName, Object propVal) throws NamingException { 186 return environment.put(propName, propVal); 187 } 188 189 public Hashtable<String, Object> getEnvironment() throws NamingException { 190 return (Hashtable<String, Object>)environment.clone(); 191 } 192 193 public Object removeFromEnvironment(String propName) throws NamingException { 194 return environment.remove(propName); 195 } 196 197 public Object lookup(String name) throws NamingException { 198 if (name.length() == 0) { 199 return this; 200 } 201 Object result = treeBindings.get(name); 202 if (result == null) { 203 result = bindings.get(name); 204 } 205 if (result == null) { 206 int pos = name.indexOf(':'); 207 if (pos > 0) { 208 String scheme = name.substring(0, pos); 209 Context ctx = NamingManager.getURLContext(scheme, environment); 210 if (ctx == null) { 211 throw new NamingException("scheme " + scheme + " not recognized"); 212 } 213 return ctx.lookup(name); 214 } else { 215 // Split out the first name of the path 216 // and look for it in the bindings map. 217 CompositeName path = new CompositeName(name); 218 219 if (path.size() == 0) { 220 return this; 221 } else { 222 String first = path.get(0); 223 Object obj = bindings.get(first); 224 if (obj == null) { 225 throw new NameNotFoundException(name); 226 } else if (obj instanceof Context && path.size() > 1) { 227 Context subContext = (Context)obj; 228 obj = subContext.lookup(path.getSuffix(1)); 229 } 230 return obj; 231 } 232 } 233 } 234 if (result instanceof LinkRef) { 235 LinkRef ref = (LinkRef)result; 236 result = lookup(ref.getLinkName()); 237 } 238 if (result instanceof Reference) { 239 try { 240 result = NamingManager.getObjectInstance(result, null, null, this.environment); 241 } catch (NamingException e) { 242 throw e; 243 } catch (Exception e) { 244 throw (NamingException)new NamingException("could not look up : " + name).initCause(e); 245 } 246 } 247 if (result instanceof ReadOnlyContext) { 248 String prefix = getNameInNamespace(); 249 if (prefix.length() > 0) { 250 prefix = prefix + SEPARATOR; 251 } 252 result = new ReadOnlyContext((ReadOnlyContext)result, environment, prefix + name); 253 } 254 return result; 255 } 256 257 public Object lookup(Name name) throws NamingException { 258 return lookup(name.toString()); 259 } 260 261 public Object lookupLink(String name) throws NamingException { 262 return lookup(name); 263 } 264 265 public Name composeName(Name name, Name prefix) throws NamingException { 266 Name result = (Name)prefix.clone(); 267 result.addAll(name); 268 return result; 269 } 270 271 public String composeName(String name, String prefix) throws NamingException { 272 CompositeName result = new CompositeName(prefix); 273 result.addAll(new CompositeName(name)); 274 return result.toString(); 275 } 276 277 public NamingEnumeration list(String name) throws NamingException { 278 Object o = lookup(name); 279 if (o == this) { 280 return new ListEnumeration(); 281 } else if (o instanceof Context) { 282 return ((Context)o).list(""); 283 } else { 284 throw new NotContextException(); 285 } 286 } 287 288 public NamingEnumeration listBindings(String name) throws NamingException { 289 Object o = lookup(name); 290 if (o == this) { 291 return new ListBindingEnumeration(); 292 } else if (o instanceof Context) { 293 return ((Context)o).listBindings(""); 294 } else { 295 throw new NotContextException(); 296 } 297 } 298 299 public Object lookupLink(Name name) throws NamingException { 300 return lookupLink(name.toString()); 301 } 302 303 public NamingEnumeration list(Name name) throws NamingException { 304 return list(name.toString()); 305 } 306 307 public NamingEnumeration listBindings(Name name) throws NamingException { 308 return listBindings(name.toString()); 309 } 310 311 public void bind(Name name, Object obj) throws NamingException { 312 throw new OperationNotSupportedException(); 313 } 314 315 public void bind(String name, Object obj) throws NamingException { 316 throw new OperationNotSupportedException(); 317 } 318 319 public void close() throws NamingException { 320 // ignore 321 } 322 323 public Context createSubcontext(Name name) throws NamingException { 324 throw new OperationNotSupportedException(); 325 } 326 327 public Context createSubcontext(String name) throws NamingException { 328 throw new OperationNotSupportedException(); 329 } 330 331 public void destroySubcontext(Name name) throws NamingException { 332 throw new OperationNotSupportedException(); 333 } 334 335 public void destroySubcontext(String name) throws NamingException { 336 throw new OperationNotSupportedException(); 337 } 338 339 public String getNameInNamespace() throws NamingException { 340 return nameInNamespace; 341 } 342 343 public NameParser getNameParser(Name name) throws NamingException { 344 return NAME_PARSER; 345 } 346 347 public NameParser getNameParser(String name) throws NamingException { 348 return NAME_PARSER; 349 } 350 351 public void rebind(Name name, Object obj) throws NamingException { 352 throw new OperationNotSupportedException(); 353 } 354 355 public void rebind(String name, Object obj) throws NamingException { 356 throw new OperationNotSupportedException(); 357 } 358 359 public void rename(Name oldName, Name newName) throws NamingException { 360 throw new OperationNotSupportedException(); 361 } 362 363 public void rename(String oldName, String newName) throws NamingException { 364 throw new OperationNotSupportedException(); 365 } 366 367 public void unbind(Name name) throws NamingException { 368 throw new OperationNotSupportedException(); 369 } 370 371 public void unbind(String name) throws NamingException { 372 throw new OperationNotSupportedException(); 373 } 374 375 private abstract class LocalNamingEnumeration implements NamingEnumeration { 376 private Iterator i = bindings.entrySet().iterator(); 377 378 public boolean hasMore() throws NamingException { 379 return i.hasNext(); 380 } 381 382 public boolean hasMoreElements() { 383 return i.hasNext(); 384 } 385 386 protected Map.Entry getNext() { 387 return (Map.Entry)i.next(); 388 } 389 390 public void close() throws NamingException { 391 } 392 } 393 394 private class ListEnumeration extends LocalNamingEnumeration { 395 ListEnumeration() { 396 } 397 398 public Object next() throws NamingException { 399 return nextElement(); 400 } 401 402 public Object nextElement() { 403 Map.Entry entry = getNext(); 404 return new NameClassPair((String)entry.getKey(), entry.getValue().getClass().getName()); 405 } 406 } 407 408 private class ListBindingEnumeration extends LocalNamingEnumeration { 409 ListBindingEnumeration() { 410 } 411 412 public Object next() throws NamingException { 413 return nextElement(); 414 } 415 416 public Object nextElement() { 417 Map.Entry entry = getNext(); 418 return new Binding((String)entry.getKey(), entry.getValue()); 419 } 420 } 421 }