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 import java.util.Map.Entry;
025
026 import javax.naming.Binding;
027 import javax.naming.CompositeName;
028 import javax.naming.Context;
029 import javax.naming.LinkRef;
030 import javax.naming.Name;
031 import javax.naming.NameClassPair;
032 import javax.naming.NameNotFoundException;
033 import javax.naming.NameParser;
034 import javax.naming.NamingEnumeration;
035 import javax.naming.NamingException;
036 import javax.naming.NotContextException;
037 import javax.naming.OperationNotSupportedException;
038 import javax.naming.Reference;
039 import javax.naming.spi.NamingManager;
040
041 import org.apache.camel.spi.Injector;
042 import org.apache.camel.util.CastUtils;
043 import org.apache.camel.util.IntrospectionSupport;
044 import org.apache.camel.util.ObjectHelper;
045 import org.apache.camel.util.ReflectionInjector;
046
047 /**
048 * A default JNDI context
049 *
050 * @version
051 */
052 public 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<String, Object>() : new Hashtable<String, Object>(environment);
078 this.bindings = bindings;
079 treeBindings = new HashMap<String, Object>();
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<String, Object>(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<String, Object>(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<String, Object>();
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 }