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