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