ReadOnlyContext.java
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 valuethrows 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 (!(instanceof ReadOnlyContext))
188             {
189                 throw new NamingException("Something already bound where a subcontext should go");
190             }
191 
192             ReadOnlyContext readOnlyContext = (ReadOnlyContexto;
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.Entryiterator.next();
198                 String subName = segment + "/" (Stringentry.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 propValthrows NamingException
214     {
215         return environment.put(propName, propVal);
216     }
217 
218     public Hashtable getEnvironment() throws NamingException
219     {
220         return (Hashtableenvironment.clone();
221     }
222 
223     public Object removeFromEnvironment(String propNamethrows NamingException
224     {
225         return environment.remove(propName);
226     }
227 
228     public Object lookup(String namethrows 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 = (Contextobj;
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 = (LinkRefresult;
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 (NamingExceptionnew 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((ReadOnlyContextresult, environment, prefix + name);
315         }
316 
317         return result;
318     }
319 
320     public Object lookup(Name namethrows NamingException
321     {
322         return lookup(name.toString());
323     }
324 
325     public Object lookupLink(String namethrows NamingException
326     {
327         return lookup(name);
328     }
329 
330     public Name composeName(Name name, Name prefixthrows NamingException
331     {
332         Name result = (Nameprefix.clone();
333         result.addAll(name);
334 
335         return result;
336     }
337 
338     public String composeName(String name, String prefixthrows 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 namethrows NamingException
347     {
348         Object o = lookup(name);
349         if (o == this)
350         {
351             return new ListEnumeration();
352         }
353         else if (instanceof Context)
354         {
355             return ((Contexto).list("");
356         }
357         else
358         {
359             throw new NotContextException();
360         }
361     }
362 
363     public NamingEnumeration listBindings(String namethrows NamingException
364     {
365         Object o = lookup(name);
366         if (o == this)
367         {
368             return new ListBindingEnumeration();
369         }
370         else if (instanceof Context)
371         {
372             return ((Contexto).listBindings("");
373         }
374         else
375         {
376             throw new NotContextException();
377         }
378     }
379 
380     public Object lookupLink(Name namethrows NamingException
381     {
382         return lookupLink(name.toString());
383     }
384 
385     public NamingEnumeration list(Name namethrows NamingException
386     {
387         return list(name.toString());
388     }
389 
390     public NamingEnumeration listBindings(Name namethrows NamingException
391     {
392         return listBindings(name.toString());
393     }
394 
395     public void bind(Name name, Object objthrows NamingException
396     {
397         throw new OperationNotSupportedException();
398     }
399 
400     public void bind(String name, Object objthrows NamingException
401     {
402         throw new OperationNotSupportedException();
403     }
404 
405     public void close() throws NamingException
406     {
407         // ignore
408     }
409 
410     public Context createSubcontext(Name namethrows NamingException
411     {
412         throw new OperationNotSupportedException();
413     }
414 
415     public Context createSubcontext(String namethrows NamingException
416     {
417         throw new OperationNotSupportedException();
418     }
419 
420     public void destroySubcontext(Name namethrows NamingException
421     {
422         throw new OperationNotSupportedException();
423     }
424 
425     public void destroySubcontext(String namethrows 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 namethrows NamingException
436     {
437         return nameParser;
438     }
439 
440     public NameParser getNameParser(String namethrows NamingException
441     {
442         return nameParser;
443     }
444 
445     public void rebind(Name name, Object objthrows NamingException
446     {
447         throw new OperationNotSupportedException();
448     }
449 
450     public void rebind(String name, Object objthrows NamingException
451     {
452         throw new OperationNotSupportedException();
453     }
454 
455     public void rename(Name oldName, Name newNamethrows NamingException
456     {
457         throw new OperationNotSupportedException();
458     }
459 
460     public void rename(String oldName, String newNamethrows NamingException
461     {
462         throw new OperationNotSupportedException();
463     }
464 
465     public void unbind(Name namethrows NamingException
466     {
467         throw new OperationNotSupportedException();
468     }
469 
470     public void unbind(String namethrows 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.Entryi.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((Stringentry.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((Stringentry.getKey(), entry.getValue());
525         }
526     }
527 }