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