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: 659638 $ $Date: 2008-05-23 15:37:19 -0400 (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 }