001 /**
002 *
003 * Copyright 2003-2004 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * 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
018 package org.activemq.jndi;
019
020 import javax.naming.*;
021 import javax.naming.spi.NamingManager;
022 import java.io.Serializable;
023 import java.util.Collections;
024 import java.util.HashMap;
025 import java.util.Hashtable;
026 import java.util.Iterator;
027 import java.util.Map;
028
029 /**
030 * A read-only Context
031 * <p/>
032 * This version assumes it and all its subcontext are read-only and any attempt
033 * to modify (e.g. through bind) will result in an OperationNotSupportedException.
034 * Each Context in the tree builds a cache of the entries in all sub-contexts
035 * to optimise the performance of lookup.
036 * </p>
037 * <p>This implementation is intended to optimise the performance of lookup(String)
038 * to about the level of a HashMap get. It has been observed that the scheme
039 * resolution phase performed by the JVM takes considerably longer, so for
040 * optimum performance lookups should be coded like:</p>
041 * <code>
042 * Context componentContext = (Context)new InitialContext().lookup("java:comp");
043 * String envEntry = (String) componentContext.lookup("env/myEntry");
044 * String envEntry2 = (String) componentContext.lookup("env/myEntry2");
045 * </code>
046 *
047 * @version $Revision: 1.1.1.1 $ $Date: 2005/03/11 21:14:27 $
048 */
049 public class ReadOnlyContext implements Context, Serializable {
050 private static final long serialVersionUID = -5754338187296859149L;
051 protected static final NameParser nameParser = new NameParserImpl();
052
053 protected final Hashtable environment; // environment for this context
054 protected final Map bindings; // bindings at my level
055 protected final Map treeBindings; // all bindings under me
056
057 private boolean frozen = false;
058 private String nameInNamespace = "";
059 public static final String SEPARATOR = "/";
060
061 public ReadOnlyContext() {
062 environment = new Hashtable();
063 bindings = new HashMap();
064 treeBindings = new HashMap();
065 }
066
067 public ReadOnlyContext(Hashtable env) {
068 if (env == null) {
069 this.environment = new Hashtable();
070 }
071 else {
072 this.environment = new Hashtable(env);
073 }
074 this.bindings = Collections.EMPTY_MAP;
075 this.treeBindings = Collections.EMPTY_MAP;
076 }
077
078 public ReadOnlyContext(Hashtable environment, Map bindings) {
079 if (environment == null) {
080 this.environment = new Hashtable();
081 }
082 else {
083 this.environment = new Hashtable(environment);
084 }
085 this.bindings = bindings;
086 treeBindings = new HashMap();
087 frozen = true;
088 }
089
090 public ReadOnlyContext(Hashtable environment, Map bindings, String nameInNamespace) {
091 this(environment, bindings);
092 this.nameInNamespace = nameInNamespace;
093 }
094
095 protected ReadOnlyContext(ReadOnlyContext clone, Hashtable env) {
096 this.bindings = clone.bindings;
097 this.treeBindings = clone.treeBindings;
098 this.environment = new Hashtable(env);
099 }
100
101 protected ReadOnlyContext(ReadOnlyContext clone, Hashtable env, String nameInNamespace) {
102 this(clone, env);
103 this.nameInNamespace = nameInNamespace;
104 }
105
106 public void freeze() {
107 frozen = true;
108 }
109
110 boolean isFrozen() {
111 return frozen;
112 }
113
114 /**
115 * internalBind is intended for use only during setup or possibly by suitably synchronized superclasses.
116 * It binds every possible lookup into a map in each context. To do this, each context
117 * strips off one name segment and if necessary creates a new context for it. Then it asks that context
118 * to bind the remaining name. It returns a map containing all the bindings from the next context, plus
119 * the context it just created (if it in fact created it). (the names are suitably extended by the segment
120 * originally lopped off).
121 *
122 * @param name
123 * @param value
124 * @return
125 * @throws javax.naming.NamingException
126 */
127 protected Map internalBind(String name, Object value) throws NamingException {
128 assert name != null && name.length() > 0;
129 assert !frozen;
130
131 Map newBindings = new HashMap();
132 int pos = name.indexOf('/');
133 if (pos == -1) {
134 if (treeBindings.put(name, value) != null) {
135 throw new NamingException("Something already bound at " + name);
136 }
137 bindings.put(name, value);
138 newBindings.put(name, value);
139 }
140 else {
141 String segment = name.substring(0, pos);
142 assert segment != null;
143 assert !segment.equals("");
144 Object o = treeBindings.get(segment);
145 if (o == null) {
146 o = newContext();
147 treeBindings.put(segment, o);
148 bindings.put(segment, o);
149 newBindings.put(segment, o);
150 }
151 else if (!(o instanceof ReadOnlyContext)) {
152 throw new NamingException("Something already bound where a subcontext should go");
153 }
154 ReadOnlyContext readOnlyContext = (ReadOnlyContext) o;
155 String remainder = name.substring(pos + 1);
156 Map subBindings = readOnlyContext.internalBind(remainder, value);
157 for (Iterator iterator = subBindings.entrySet().iterator(); iterator.hasNext();) {
158 Map.Entry entry = (Map.Entry) iterator.next();
159 String subName = segment + "/" + (String) entry.getKey();
160 Object bound = entry.getValue();
161 treeBindings.put(subName, bound);
162 newBindings.put(subName, bound);
163 }
164 }
165 return newBindings;
166 }
167
168 protected ReadOnlyContext newContext() {
169 return new ReadOnlyContext();
170 }
171
172 public Object addToEnvironment(String propName, Object propVal) throws NamingException {
173 return environment.put(propName, propVal);
174 }
175
176 public Hashtable getEnvironment() throws NamingException {
177 return (Hashtable) environment.clone();
178 }
179
180 public Object removeFromEnvironment(String propName) throws NamingException {
181 return environment.remove(propName);
182 }
183
184 public Object lookup(String name) throws NamingException {
185 if (name.length() == 0) {
186 return this;
187 }
188 Object result = treeBindings.get(name);
189 if (result == null) {
190 result = bindings.get(name);
191 }
192 if (result == null) {
193 int pos = name.indexOf(':');
194 if (pos > 0) {
195 String scheme = name.substring(0, pos);
196 Context ctx = NamingManager.getURLContext(scheme, environment);
197 if (ctx == null) {
198 throw new NamingException("scheme " + scheme + " not recognized");
199 }
200 return ctx.lookup(name);
201 }
202 else {
203 // Split out the first name of the path
204 // and look for it in the bindings map.
205 CompositeName path = new CompositeName(name);
206
207 if (path.size() == 0) {
208 return this;
209 }
210 else {
211 String first = path.get(0);
212 Object obj = bindings.get(first);
213 if (obj == null) {
214 throw new NameNotFoundException(name);
215 }
216 else if (obj instanceof Context && path.size() > 1) {
217 Context subContext = (Context) obj;
218 obj = subContext.lookup(path.getSuffix(1));
219 }
220 return obj;
221 }
222 }
223 }
224 if (result instanceof LinkRef) {
225 LinkRef ref = (LinkRef) result;
226 result = lookup(ref.getLinkName());
227 }
228 if (result instanceof Reference) {
229 try {
230 result = NamingManager.getObjectInstance(result, null, null, this.environment);
231 }
232 catch (NamingException e) {
233 throw e;
234 }
235 catch (Exception e) {
236 throw (NamingException) new NamingException("could not look up : " + name).initCause(e);
237 }
238 }
239 if (result instanceof ReadOnlyContext) {
240 String prefix = getNameInNamespace();
241 if (prefix.length() > 0) {
242 prefix = prefix + SEPARATOR;
243 }
244 result = new ReadOnlyContext((ReadOnlyContext) result, environment, prefix + name);
245 }
246 return result;
247 }
248
249 public Object lookup(Name name) throws NamingException {
250 return lookup(name.toString());
251 }
252
253 public Object lookupLink(String name) throws NamingException {
254 return lookup(name);
255 }
256
257 public Name composeName(Name name, Name prefix) throws NamingException {
258 Name result = (Name) prefix.clone();
259 result.addAll(name);
260 return result;
261 }
262
263 public String composeName(String name, String prefix) throws NamingException {
264 CompositeName result = new CompositeName(prefix);
265 result.addAll(new CompositeName(name));
266 return result.toString();
267 }
268
269 public NamingEnumeration list(String name) throws NamingException {
270 Object o = lookup(name);
271 if (o == this) {
272 return new ListEnumeration();
273 }
274 else if (o instanceof Context) {
275 return ((Context) o).list("");
276 }
277 else {
278 throw new NotContextException();
279 }
280 }
281
282 public NamingEnumeration listBindings(String name) throws NamingException {
283 Object o = lookup(name);
284 if (o == this) {
285 return new ListBindingEnumeration();
286 }
287 else if (o instanceof Context) {
288 return ((Context) o).listBindings("");
289 }
290 else {
291 throw new NotContextException();
292 }
293 }
294
295 public Object lookupLink(Name name) throws NamingException {
296 return lookupLink(name.toString());
297 }
298
299 public NamingEnumeration list(Name name) throws NamingException {
300 return list(name.toString());
301 }
302
303 public NamingEnumeration listBindings(Name name) throws NamingException {
304 return listBindings(name.toString());
305 }
306
307 public void bind(Name name, Object obj) throws NamingException {
308 throw new OperationNotSupportedException();
309 }
310
311 public void bind(String name, Object obj) throws NamingException {
312 throw new OperationNotSupportedException();
313 }
314
315 public void close() throws NamingException {
316 // ignore
317 }
318
319 public Context createSubcontext(Name name) throws NamingException {
320 throw new OperationNotSupportedException();
321 }
322
323 public Context createSubcontext(String name) throws NamingException {
324 throw new OperationNotSupportedException();
325 }
326
327 public void destroySubcontext(Name name) throws NamingException {
328 throw new OperationNotSupportedException();
329 }
330
331 public void destroySubcontext(String name) throws NamingException {
332 throw new OperationNotSupportedException();
333 }
334
335 public String getNameInNamespace() throws NamingException {
336 return nameInNamespace;
337 }
338
339 public NameParser getNameParser(Name name) throws NamingException {
340 return nameParser;
341 }
342
343 public NameParser getNameParser(String name) throws NamingException {
344 return nameParser;
345 }
346
347 public void rebind(Name name, Object obj) throws NamingException {
348 throw new OperationNotSupportedException();
349 }
350
351 public void rebind(String name, Object obj) throws NamingException {
352 throw new OperationNotSupportedException();
353 }
354
355 public void rename(Name oldName, Name newName) throws NamingException {
356 throw new OperationNotSupportedException();
357 }
358
359 public void rename(String oldName, String newName) throws NamingException {
360 throw new OperationNotSupportedException();
361 }
362
363 public void unbind(Name name) throws NamingException {
364 throw new OperationNotSupportedException();
365 }
366
367 public void unbind(String name) throws NamingException {
368 throw new OperationNotSupportedException();
369 }
370
371 private abstract class LocalNamingEnumeration implements NamingEnumeration {
372 private Iterator i = bindings.entrySet().iterator();
373
374 public boolean hasMore() throws NamingException {
375 return i.hasNext();
376 }
377
378 public boolean hasMoreElements() {
379 return i.hasNext();
380 }
381
382 protected Map.Entry getNext() {
383 return (Map.Entry) i.next();
384 }
385
386 public void close() throws NamingException {
387 }
388 }
389
390 private class ListEnumeration extends LocalNamingEnumeration {
391 public Object next() throws NamingException {
392 return nextElement();
393 }
394
395 public Object nextElement() {
396 Map.Entry entry = getNext();
397 return new NameClassPair((String) entry.getKey(), entry.getValue().getClass().getName());
398 }
399 }
400
401 private class ListBindingEnumeration extends LocalNamingEnumeration {
402 public Object next() throws NamingException {
403 return nextElement();
404 }
405
406 public Object nextElement() {
407 Map.Entry entry = getNext();
408 return new Binding((String) entry.getKey(), entry.getValue());
409 }
410 }
411 }