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