001 /*
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements. See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership. The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License. You may obtain a copy of the License at
010 *
011 * http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied. See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 *
020 */
021 package org.apache.qpid.jndi;
022
023 import java.io.Serializable;
024 import java.util.Collections;
025 import java.util.HashMap;
026 import java.util.Hashtable;
027 import java.util.Iterator;
028 import java.util.Map;
029
030 import javax.naming.Binding;
031 import javax.naming.CompositeName;
032 import javax.naming.Context;
033 import javax.naming.LinkRef;
034 import javax.naming.Name;
035 import javax.naming.NameClassPair;
036 import javax.naming.NameNotFoundException;
037 import javax.naming.NameParser;
038 import javax.naming.NamingEnumeration;
039 import javax.naming.NamingException;
040 import javax.naming.NotContextException;
041 import javax.naming.OperationNotSupportedException;
042 import javax.naming.Reference;
043 import javax.naming.spi.NamingManager;
044
045 /**
046 * Based on class from ActiveMQ.
047 * A read-only Context
048 * <p/>
049 * This version assumes it and all its subcontext are read-only and any attempt
050 * to modify (e.g. through bind) will result in an OperationNotSupportedException.
051 * Each Context in the tree builds a cache of the entries in all sub-contexts
052 * to optimise the performance of lookup.
053 * </p>
054 * <p>This implementation is intended to optimise the performance of lookup(String)
055 * to about the level of a HashMap get. It has been observed that the scheme
056 * resolution phase performed by the JVM takes considerably longer, so for
057 * optimum performance lookups should be coded like:</p>
058 * <code>
059 * Context componentContext = (Context)new InitialContext().lookup("java:comp");
060 * String envEntry = (String) componentContext.lookup("env/myEntry");
061 * String envEntry2 = (String) componentContext.lookup("env/myEntry2");
062 * </code>
063 */
064 public class ReadOnlyContext implements Context, Serializable
065 {
066 private static final long serialVersionUID = -5754338187296859149L;
067 protected static final NameParser nameParser = new NameParserImpl();
068
069 protected final Hashtable environment; // environment for this context
070 protected final Map bindings; // bindings at my level
071 protected final Map treeBindings; // all bindings under me
072
073 private boolean frozen = false;
074 private String nameInNamespace = "";
075 public static final String SEPARATOR = "/";
076
077 public ReadOnlyContext()
078 {
079 environment = new Hashtable();
080 bindings = new HashMap();
081 treeBindings = new HashMap();
082 }
083
084 public ReadOnlyContext(Hashtable env)
085 {
086 if (env == null)
087 {
088 this.environment = new Hashtable();
089 }
090 else
091 {
092 this.environment = new Hashtable(env);
093 }
094
095 this.bindings = Collections.EMPTY_MAP;
096 this.treeBindings = Collections.EMPTY_MAP;
097 }
098
099 public ReadOnlyContext(Hashtable environment, Map bindings)
100 {
101 if (environment == null)
102 {
103 this.environment = new Hashtable();
104 }
105 else
106 {
107 this.environment = new Hashtable(environment);
108 }
109
110 this.bindings = bindings;
111 treeBindings = new HashMap();
112 frozen = true;
113 }
114
115 public ReadOnlyContext(Hashtable environment, Map bindings, String nameInNamespace)
116 {
117 this(environment, bindings);
118 this.nameInNamespace = nameInNamespace;
119 }
120
121 protected ReadOnlyContext(ReadOnlyContext clone, Hashtable env)
122 {
123 this.bindings = clone.bindings;
124 this.treeBindings = clone.treeBindings;
125 this.environment = new Hashtable(env);
126 }
127
128 protected ReadOnlyContext(ReadOnlyContext clone, Hashtable env, String nameInNamespace)
129 {
130 this(clone, env);
131 this.nameInNamespace = nameInNamespace;
132 }
133
134 public void freeze()
135 {
136 frozen = true;
137 }
138
139 boolean isFrozen()
140 {
141 return frozen;
142 }
143
144 /**
145 * internalBind is intended for use only during setup or possibly by suitably synchronized superclasses.
146 * It binds every possible lookup into a map in each context. To do this, each context
147 * strips off one name segment 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 from the next context, plus
149 * the context it just created (if it in fact created it). (the names are suitably extended by the segment
150 * originally lopped off).
151 *
152 * @param name
153 * @param value
154 * @return
155 * @throws javax.naming.NamingException
156 */
157 protected Map internalBind(String name, Object value) throws NamingException
158 {
159 assert (name != null) && (name.length() > 0);
160 assert !frozen;
161
162 Map newBindings = new HashMap();
163 int pos = name.indexOf('/');
164 if (pos == -1)
165 {
166 if (treeBindings.put(name, value) != null)
167 {
168 throw new NamingException("Something already bound at " + name);
169 }
170
171 bindings.put(name, value);
172 newBindings.put(name, value);
173 }
174 else
175 {
176 String segment = name.substring(0, pos);
177 assert segment != null;
178 assert !segment.equals("");
179 Object o = treeBindings.get(segment);
180 if (o == null)
181 {
182 o = newContext();
183 treeBindings.put(segment, o);
184 bindings.put(segment, o);
185 newBindings.put(segment, o);
186 }
187 else if (!(o instanceof ReadOnlyContext))
188 {
189 throw new NamingException("Something already bound where a subcontext should go");
190 }
191
192 ReadOnlyContext readOnlyContext = (ReadOnlyContext) o;
193 String remainder = name.substring(pos + 1);
194 Map subBindings = readOnlyContext.internalBind(remainder, value);
195 for (Iterator iterator = subBindings.entrySet().iterator(); iterator.hasNext();)
196 {
197 Map.Entry entry = (Map.Entry) iterator.next();
198 String subName = segment + "/" + (String) entry.getKey();
199 Object bound = entry.getValue();
200 treeBindings.put(subName, bound);
201 newBindings.put(subName, bound);
202 }
203 }
204
205 return newBindings;
206 }
207
208 protected ReadOnlyContext newContext()
209 {
210 return new ReadOnlyContext();
211 }
212
213 public Object addToEnvironment(String propName, Object propVal) throws NamingException
214 {
215 return environment.put(propName, propVal);
216 }
217
218 public Hashtable getEnvironment() throws NamingException
219 {
220 return (Hashtable) environment.clone();
221 }
222
223 public Object removeFromEnvironment(String propName) throws NamingException
224 {
225 return environment.remove(propName);
226 }
227
228 public Object lookup(String name) throws NamingException
229 {
230 if (name.length() == 0)
231 {
232 return this;
233 }
234
235 Object result = treeBindings.get(name);
236 if (result == null)
237 {
238 result = bindings.get(name);
239 }
240
241 if (result == null)
242 {
243 int pos = name.indexOf(':');
244 if (pos > 0)
245 {
246 String scheme = name.substring(0, pos);
247 Context ctx = NamingManager.getURLContext(scheme, environment);
248 if (ctx == null)
249 {
250 throw new NamingException("scheme " + scheme + " not recognized");
251 }
252
253 return ctx.lookup(name);
254 }
255 else
256 {
257 // Split out the first name of the path
258 // and look for it in the bindings map.
259 CompositeName path = new CompositeName(name);
260
261 if (path.size() == 0)
262 {
263 return this;
264 }
265 else
266 {
267 String first = path.get(0);
268 Object obj = bindings.get(first);
269 if (obj == null)
270 {
271 throw new NameNotFoundException(name);
272 }
273 else if ((obj instanceof Context) && (path.size() > 1))
274 {
275 Context subContext = (Context) obj;
276 obj = subContext.lookup(path.getSuffix(1));
277 }
278
279 return obj;
280 }
281 }
282 }
283
284 if (result instanceof LinkRef)
285 {
286 LinkRef ref = (LinkRef) result;
287 result = lookup(ref.getLinkName());
288 }
289
290 if (result instanceof Reference)
291 {
292 try
293 {
294 result = NamingManager.getObjectInstance(result, null, null, this.environment);
295 }
296 catch (NamingException e)
297 {
298 throw e;
299 }
300 catch (Exception e)
301 {
302 throw (NamingException) new NamingException("could not look up : " + name).initCause(e);
303 }
304 }
305
306 if (result instanceof ReadOnlyContext)
307 {
308 String prefix = getNameInNamespace();
309 if (prefix.length() > 0)
310 {
311 prefix = prefix + SEPARATOR;
312 }
313
314 result = new ReadOnlyContext((ReadOnlyContext) result, environment, prefix + name);
315 }
316
317 return result;
318 }
319
320 public Object lookup(Name name) throws NamingException
321 {
322 return lookup(name.toString());
323 }
324
325 public Object lookupLink(String name) throws NamingException
326 {
327 return lookup(name);
328 }
329
330 public Name composeName(Name name, Name prefix) throws NamingException
331 {
332 Name result = (Name) prefix.clone();
333 result.addAll(name);
334
335 return result;
336 }
337
338 public String composeName(String name, String prefix) throws NamingException
339 {
340 CompositeName result = new CompositeName(prefix);
341 result.addAll(new CompositeName(name));
342
343 return result.toString();
344 }
345
346 public NamingEnumeration list(String name) throws NamingException
347 {
348 Object o = lookup(name);
349 if (o == this)
350 {
351 return new ListEnumeration();
352 }
353 else if (o instanceof Context)
354 {
355 return ((Context) o).list("");
356 }
357 else
358 {
359 throw new NotContextException();
360 }
361 }
362
363 public NamingEnumeration listBindings(String name) throws NamingException
364 {
365 Object o = lookup(name);
366 if (o == this)
367 {
368 return new ListBindingEnumeration();
369 }
370 else if (o instanceof Context)
371 {
372 return ((Context) o).listBindings("");
373 }
374 else
375 {
376 throw new NotContextException();
377 }
378 }
379
380 public Object lookupLink(Name name) throws NamingException
381 {
382 return lookupLink(name.toString());
383 }
384
385 public NamingEnumeration list(Name name) throws NamingException
386 {
387 return list(name.toString());
388 }
389
390 public NamingEnumeration listBindings(Name name) throws NamingException
391 {
392 return listBindings(name.toString());
393 }
394
395 public void bind(Name name, Object obj) throws NamingException
396 {
397 throw new OperationNotSupportedException();
398 }
399
400 public void bind(String name, Object obj) throws NamingException
401 {
402 throw new OperationNotSupportedException();
403 }
404
405 public void close() throws NamingException
406 {
407 // ignore
408 }
409
410 public Context createSubcontext(Name name) throws NamingException
411 {
412 throw new OperationNotSupportedException();
413 }
414
415 public Context createSubcontext(String name) throws NamingException
416 {
417 throw new OperationNotSupportedException();
418 }
419
420 public void destroySubcontext(Name name) throws NamingException
421 {
422 throw new OperationNotSupportedException();
423 }
424
425 public void destroySubcontext(String name) throws NamingException
426 {
427 throw new OperationNotSupportedException();
428 }
429
430 public String getNameInNamespace() throws NamingException
431 {
432 return nameInNamespace;
433 }
434
435 public NameParser getNameParser(Name name) throws NamingException
436 {
437 return nameParser;
438 }
439
440 public NameParser getNameParser(String name) throws NamingException
441 {
442 return nameParser;
443 }
444
445 public void rebind(Name name, Object obj) throws NamingException
446 {
447 throw new OperationNotSupportedException();
448 }
449
450 public void rebind(String name, Object obj) throws NamingException
451 {
452 throw new OperationNotSupportedException();
453 }
454
455 public void rename(Name oldName, Name newName) throws NamingException
456 {
457 throw new OperationNotSupportedException();
458 }
459
460 public void rename(String oldName, String newName) throws NamingException
461 {
462 throw new OperationNotSupportedException();
463 }
464
465 public void unbind(Name name) throws NamingException
466 {
467 throw new OperationNotSupportedException();
468 }
469
470 public void unbind(String name) throws NamingException
471 {
472 throw new OperationNotSupportedException();
473 }
474
475 private abstract class LocalNamingEnumeration implements NamingEnumeration
476 {
477 private Iterator i = bindings.entrySet().iterator();
478
479 public boolean hasMore() throws NamingException
480 {
481 return i.hasNext();
482 }
483
484 public boolean hasMoreElements()
485 {
486 return i.hasNext();
487 }
488
489 protected Map.Entry getNext()
490 {
491 return (Map.Entry) i.next();
492 }
493
494 public void close() throws NamingException
495 { }
496 }
497
498 private class ListEnumeration extends LocalNamingEnumeration
499 {
500 public Object next() throws NamingException
501 {
502 return nextElement();
503 }
504
505 public Object nextElement()
506 {
507 Map.Entry entry = getNext();
508
509 return new NameClassPair((String) entry.getKey(), entry.getValue().getClass().getName());
510 }
511 }
512
513 private class ListBindingEnumeration extends LocalNamingEnumeration
514 {
515 public Object next() throws NamingException
516 {
517 return nextElement();
518 }
519
520 public Object nextElement()
521 {
522 Map.Entry entry = getNext();
523
524 return new Binding((String) entry.getKey(), entry.getValue());
525 }
526 }
527 }
|