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