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 }