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 value) throws 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 (!(o instanceof ReadOnlyContext))
170 {
171 throw new NamingException("Something already bound where a subcontext should go");
172 }
173
174 ReadOnlyContext readOnlyContext = (ReadOnlyContext) o;
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.Entry) iterator.next();
180 String subName = segment + "/" + (String) entry.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 propVal) throws NamingException
196 {
197 return environment.put(propName, propVal);
198 }
199
200 public Hashtable getEnvironment() throws NamingException
201 {
202 return (Hashtable) environment.clone();
203 }
204
205 public Object removeFromEnvironment(String propName) throws NamingException
206 {
207 return environment.remove(propName);
208 }
209
210 public Object lookup(String name) throws 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 = (Context) obj;
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 = (LinkRef) result;
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 (NamingException) new 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((ReadOnlyContext) result, environment, prefix + name);
297 }
298
299 return result;
300 }
301
302 public Object lookup(Name name) throws NamingException
303 {
304 return lookup(name.toString());
305 }
306
307 public Object lookupLink(String name) throws NamingException
308 {
309 return lookup(name);
310 }
311
312 public Name composeName(Name name, Name prefix) throws NamingException
313 {
314 Name result = (Name) prefix.clone();
315 result.addAll(name);
316
317 return result;
318 }
319
320 public String composeName(String name, String prefix) throws 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 name) throws NamingException
329 {
330 Object o = lookup(name);
331 if (o == this)
332 {
333 return new ReadOnlyContext.ListEnumeration();
334 }
335 else if (o instanceof Context)
336 {
337 return ((Context) o).list("");
338 }
339 else
340 {
341 throw new NotContextException();
342 }
343 }
344
345 public NamingEnumeration listBindings(String name) throws NamingException
346 {
347 Object o = lookup(name);
348 if (o == this)
349 {
350 return new ReadOnlyContext.ListBindingEnumeration();
351 }
352 else if (o instanceof Context)
353 {
354 return ((Context) o).listBindings("");
355 }
356 else
357 {
358 throw new NotContextException();
359 }
360 }
361
362 public Object lookupLink(Name name) throws NamingException
363 {
364 return lookupLink(name.toString());
365 }
366
367 public NamingEnumeration list(Name name) throws NamingException
368 {
369 return list(name.toString());
370 }
371
372 public NamingEnumeration listBindings(Name name) throws NamingException
373 {
374 return listBindings(name.toString());
375 }
376
377 public void bind(Name name, Object obj) throws NamingException
378 {
379 throw new OperationNotSupportedException();
380 }
381
382 public void bind(String name, Object obj) throws NamingException
383 {
384 throw new OperationNotSupportedException();
385 }
386
387 public void close() throws NamingException
388 {
389 // ignore
390 }
391
392 public Context createSubcontext(Name name) throws NamingException
393 {
394 throw new OperationNotSupportedException();
395 }
396
397 public Context createSubcontext(String name) throws NamingException
398 {
399 throw new OperationNotSupportedException();
400 }
401
402 public void destroySubcontext(Name name) throws NamingException
403 {
404 throw new OperationNotSupportedException();
405 }
406
407 public void destroySubcontext(String name) throws 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 name) throws NamingException
418 {
419 return nameParser;
420 }
421
422 public NameParser getNameParser(String name) throws NamingException
423 {
424 return nameParser;
425 }
426
427 public void rebind(Name name, Object obj) throws NamingException
428 {
429 throw new OperationNotSupportedException();
430 }
431
432 public void rebind(String name, Object obj) throws NamingException
433 {
434 throw new OperationNotSupportedException();
435 }
436
437 public void rename(Name oldName, Name newName) throws NamingException
438 {
439 throw new OperationNotSupportedException();
440 }
441
442 public void rename(String oldName, String newName) throws NamingException
443 {
444 throw new OperationNotSupportedException();
445 }
446
447 public void unbind(Name name) throws NamingException
448 {
449 throw new OperationNotSupportedException();
450 }
451
452 public void unbind(String name) throws 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.Entry) i.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((String) entry.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((String) entry.getKey(), entry.getValue());
507 }
508 }
509 }
|