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