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.spi.Injector; 041 import org.apache.camel.util.IntrospectionSupport; 042 import org.apache.camel.util.ObjectHelper; 043 import org.apache.camel.util.ReflectionInjector; 044 045 /** 046 * A default JNDI context 047 * 048 * @version $Revision: 41822 $ $Date: 2008-05-23 20:44:02 -0230 (Fri, 23 May 2008) $ 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() throws Exception { 067 this(new Hashtable()); 068 } 069 070 public JndiContext(Hashtable env) throws Exception { 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) throws Exception { 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 protected Map internalBind(String name, Object value) throws NamingException { 151 assert name != null && name.length() > 0; 152 assert !frozen; 153 154 Map newBindings = new HashMap(); 155 int pos = name.indexOf('/'); 156 if (pos == -1) { 157 if (treeBindings.put(name, value) != null) { 158 throw new NamingException("Something already bound at " + name); 159 } 160 bindings.put(name, value); 161 newBindings.put(name, value); 162 } else { 163 String segment = name.substring(0, pos); 164 assert segment != null; 165 assert !segment.equals(""); 166 Object o = treeBindings.get(segment); 167 if (o == null) { 168 o = newContext(); 169 treeBindings.put(segment, o); 170 bindings.put(segment, o); 171 newBindings.put(segment, o); 172 } else if (!(o instanceof JndiContext)) { 173 throw new NamingException("Something already bound where a subcontext should go"); 174 } 175 JndiContext defaultContext = (JndiContext)o; 176 String remainder = name.substring(pos + 1); 177 Map subBindings = defaultContext.internalBind(remainder, value); 178 for (Iterator iterator = subBindings.entrySet().iterator(); iterator.hasNext();) { 179 Map.Entry entry = (Map.Entry)iterator.next(); 180 String subName = segment + "/" + (String)entry.getKey(); 181 Object bound = entry.getValue(); 182 treeBindings.put(subName, bound); 183 newBindings.put(subName, bound); 184 } 185 } 186 return newBindings; 187 } 188 189 protected JndiContext newContext() { 190 try { 191 return new JndiContext(); 192 } catch (Exception e) { 193 throw new IllegalArgumentException(e); 194 } 195 } 196 197 public Object addToEnvironment(String propName, Object propVal) throws NamingException { 198 return environment.put(propName, propVal); 199 } 200 201 public Hashtable getEnvironment() throws NamingException { 202 return (Hashtable)environment.clone(); 203 } 204 205 public Object removeFromEnvironment(String propName) throws NamingException { 206 return environment.remove(propName); 207 } 208 209 public Object lookup(String name) throws NamingException { 210 if (name.length() == 0) { 211 return this; 212 } 213 Object result = treeBindings.get(name); 214 if (result == null) { 215 result = bindings.get(name); 216 } 217 if (result == null) { 218 int pos = name.indexOf(':'); 219 if (pos > 0) { 220 String scheme = name.substring(0, pos); 221 Context ctx = NamingManager.getURLContext(scheme, environment); 222 if (ctx == null) { 223 throw new NamingException("scheme " + scheme + " not recognized"); 224 } 225 return ctx.lookup(name); 226 } else { 227 // Split out the first name of the path 228 // and look for it in the bindings map. 229 CompositeName path = new CompositeName(name); 230 231 if (path.size() == 0) { 232 return this; 233 } else { 234 String first = path.get(0); 235 Object value = bindings.get(first); 236 if (value == null) { 237 throw new NameNotFoundException(name); 238 } else if (value instanceof Context && path.size() > 1) { 239 Context subContext = (Context)value; 240 value = subContext.lookup(path.getSuffix(1)); 241 } 242 return value; 243 } 244 } 245 } 246 if (result instanceof LinkRef) { 247 LinkRef ref = (LinkRef)result; 248 result = lookup(ref.getLinkName()); 249 } 250 if (result instanceof Reference) { 251 try { 252 result = NamingManager.getObjectInstance(result, null, null, this.environment); 253 } catch (NamingException e) { 254 throw e; 255 } catch (Exception e) { 256 throw (NamingException)new NamingException("could not look up : " + name).initCause(e); 257 } 258 } 259 if (result instanceof JndiContext) { 260 String prefix = getNameInNamespace(); 261 if (prefix.length() > 0) { 262 prefix = prefix + SEPARATOR; 263 } 264 result = new JndiContext((JndiContext)result, environment, prefix + name); 265 } 266 return result; 267 } 268 269 public Object lookup(Name name) throws NamingException { 270 return lookup(name.toString()); 271 } 272 273 public Object lookupLink(String name) throws NamingException { 274 return lookup(name); 275 } 276 277 public Name composeName(Name name, Name prefix) throws NamingException { 278 Name result = (Name)prefix.clone(); 279 result.addAll(name); 280 return result; 281 } 282 283 public String composeName(String name, String prefix) throws NamingException { 284 CompositeName result = new CompositeName(prefix); 285 result.addAll(new CompositeName(name)); 286 return result.toString(); 287 } 288 289 public NamingEnumeration list(String name) throws NamingException { 290 Object o = lookup(name); 291 if (o == this) { 292 return new ListEnumeration(); 293 } else if (o instanceof Context) { 294 return ((Context)o).list(""); 295 } else { 296 throw new NotContextException(); 297 } 298 } 299 300 public NamingEnumeration listBindings(String name) throws NamingException { 301 Object o = lookup(name); 302 if (o == this) { 303 return new ListBindingEnumeration(); 304 } else if (o instanceof Context) { 305 return ((Context)o).listBindings(""); 306 } else { 307 throw new NotContextException(); 308 } 309 } 310 311 public Object lookupLink(Name name) throws NamingException { 312 return lookupLink(name.toString()); 313 } 314 315 public NamingEnumeration list(Name name) throws NamingException { 316 return list(name.toString()); 317 } 318 319 public NamingEnumeration listBindings(Name name) throws NamingException { 320 return listBindings(name.toString()); 321 } 322 323 public void bind(Name name, Object value) throws NamingException { 324 bind(name.toString(), value); 325 } 326 327 public void bind(String name, Object value) throws NamingException { 328 if (isFrozen()) { 329 throw new OperationNotSupportedException(); 330 } else { 331 internalBind(name, value); 332 } 333 } 334 335 public void close() throws NamingException { 336 // ignore 337 } 338 339 public Context createSubcontext(Name name) throws NamingException { 340 throw new OperationNotSupportedException(); 341 } 342 343 public Context createSubcontext(String name) throws NamingException { 344 throw new OperationNotSupportedException(); 345 } 346 347 public void destroySubcontext(Name name) throws NamingException { 348 throw new OperationNotSupportedException(); 349 } 350 351 public void destroySubcontext(String name) throws NamingException { 352 throw new OperationNotSupportedException(); 353 } 354 355 public String getNameInNamespace() throws NamingException { 356 return nameInNamespace; 357 } 358 359 public NameParser getNameParser(Name name) throws NamingException { 360 return NAME_PARSER; 361 } 362 363 public NameParser getNameParser(String name) throws NamingException { 364 return NAME_PARSER; 365 } 366 367 public void rebind(Name name, Object value) throws NamingException { 368 bind(name, value); 369 } 370 371 public void rebind(String name, Object value) throws NamingException { 372 bind(name, value); 373 } 374 375 public void rename(Name oldName, Name newName) throws NamingException { 376 throw new OperationNotSupportedException(); 377 } 378 379 public void rename(String oldName, String newName) throws NamingException { 380 throw new OperationNotSupportedException(); 381 } 382 383 public void unbind(Name name) throws NamingException { 384 throw new OperationNotSupportedException(); 385 } 386 387 public void unbind(String name) throws NamingException { 388 bindings.remove(name); 389 treeBindings.remove(name); 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) throws Exception { 440 Object value = INJETOR.newInstance(type); 441 IntrospectionSupport.setProperties(value, properties, prefix); 442 return value; 443 } 444 }