1 /***
2 *
3 * Copyright 2003-2004 The Apache Software Foundation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.codehaus.activemq.jndi;
19
20 import javax.naming.*;
21 import javax.naming.spi.NamingManager;
22 import java.io.Serializable;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.Hashtable;
26 import java.util.Iterator;
27 import java.util.Map;
28
29 /***
30 * A read-only Context
31 * <p/>
32 * This version assumes it and all its subcontext are read-only and any attempt
33 * to modify (e.g. through bind) will result in an OperationNotSupportedException.
34 * Each Context in the tree builds a cache of the entries in all sub-contexts
35 * to optimise the performance of lookup.
36 * </p>
37 * <p>This implementation is intended to optimise the performance of lookup(String)
38 * to about the level of a HashMap get. It has been observed that the scheme
39 * resolution phase performed by the JVM takes considerably longer, so for
40 * optimum performance lookups should be coded like:</p>
41 * <code>
42 * Context componentContext = (Context)new InitialContext().lookup("java:comp");
43 * String envEntry = (String) componentContext.lookup("env/myEntry");
44 * String envEntry2 = (String) componentContext.lookup("env/myEntry2");
45 * </code>
46 *
47 * @version $Revision: 1.3 $ $Date: 2004/09/01 10:18:41 $
48 */
49 public class ReadOnlyContext implements Context, Serializable {
50 private static final long serialVersionUID = -5754338187296859149L;
51 protected static final NameParser nameParser = new NameParserImpl();
52
53 protected final Hashtable environment;
54 protected final Map bindings;
55 protected final Map treeBindings;
56
57 private boolean frozen = false;
58
59 public ReadOnlyContext() {
60 environment = new Hashtable();
61 bindings = new HashMap();
62 treeBindings = new HashMap();
63 }
64
65 public ReadOnlyContext(Hashtable env) {
66 if (env == null) {
67 this.environment = new Hashtable();
68 }
69 else {
70 this.environment = new Hashtable(env);
71 }
72 this.bindings = Collections.EMPTY_MAP;
73 this.treeBindings = Collections.EMPTY_MAP;
74 }
75
76 public ReadOnlyContext(Hashtable environment, Map bindings) {
77 if (environment == null) {
78 this.environment = new Hashtable();
79 }
80 else {
81 this.environment = new Hashtable(environment);
82 }
83 this.bindings = bindings;
84 treeBindings = new HashMap();
85 frozen = true;
86 }
87
88 protected ReadOnlyContext(ReadOnlyContext clone, Hashtable env) {
89 this.bindings = clone.bindings;
90 this.treeBindings = clone.treeBindings;
91 this.environment = new Hashtable(env);
92 }
93
94 public void freeze() {
95 frozen = true;
96 }
97
98 boolean isFrozen() {
99 return frozen;
100 }
101
102 /***
103 * internalBind is intended for use only during setup or possibly by suitably synchronized superclasses.
104 * It binds every possible lookup into a map in each context. To do this, each context
105 * strips off one name segment and if necessary creates a new context for it. Then it asks that context
106 * to bind the remaining name. It returns a map containing all the bindings from the next context, plus
107 * the context it just created (if it in fact created it). (the names are suitably extended by the segment
108 * originally lopped off).
109 *
110 * @param name
111 * @param value
112 * @return
113 * @throws javax.naming.NamingException
114 */
115 protected Map internalBind(String name, Object value) throws NamingException {
116 assert name != null && name.length() > 0;
117 assert !frozen;
118
119 Map newBindings = new HashMap();
120 int pos = name.indexOf('/');
121 if (pos == -1) {
122 if (treeBindings.put(name, value) != null) {
123 throw new NamingException("Something already bound at " + name);
124 }
125 bindings.put(name, value);
126 newBindings.put(name, value);
127 }
128 else {
129 String segment = name.substring(0, pos);
130 assert segment != null;
131 assert !segment.equals("");
132 Object o = treeBindings.get(segment);
133 if (o == null) {
134 o = newContext();
135 treeBindings.put(segment, o);
136 bindings.put(segment, o);
137 newBindings.put(segment, o);
138 }
139 else if (!(o instanceof ReadOnlyContext)) {
140 throw new NamingException("Something already bound where a subcontext should go");
141 }
142 ReadOnlyContext readOnlyContext = (ReadOnlyContext) o;
143 String remainder = name.substring(pos + 1);
144 Map subBindings = readOnlyContext.internalBind(remainder, value);
145 for (Iterator iterator = subBindings.entrySet().iterator(); iterator.hasNext();) {
146 Map.Entry entry = (Map.Entry) iterator.next();
147 String subName = segment + "/" + (String) entry.getKey();
148 Object bound = entry.getValue();
149 treeBindings.put(subName, bound);
150 newBindings.put(subName, bound);
151 }
152 }
153 return newBindings;
154 }
155
156 protected ReadOnlyContext newContext() {
157 return new ReadOnlyContext();
158 }
159
160 public Object addToEnvironment(String propName, Object propVal) throws NamingException {
161 return environment.put(propName, propVal);
162 }
163
164 public Hashtable getEnvironment() throws NamingException {
165 return (Hashtable) environment.clone();
166 }
167
168 public Object removeFromEnvironment(String propName) throws NamingException {
169 return environment.remove(propName);
170 }
171
172 public Object lookup(String name) throws NamingException {
173 if (name.length() == 0) {
174 return this;
175 }
176 Object result = treeBindings.get(name);
177 if (result == null) {
178 int pos = name.indexOf(':');
179 if (pos > 0) {
180 String scheme = name.substring(0, pos);
181 Context ctx = NamingManager.getURLContext(scheme, environment);
182 if (ctx == null) {
183 throw new NamingException("scheme " + scheme + " not recognized");
184 }
185 return ctx.lookup(name);
186 }
187 else {
188
189
190 CompositeName path = new CompositeName(name);
191
192 if (path.size() == 0) {
193 return this;
194 }
195 else {
196 Object obj = bindings.get(path.get(0));
197 if (obj == null) {
198 throw new NameNotFoundException(name);
199 }
200 else if (obj instanceof Context && path.size() > 1) {
201 Context subContext = (Context) obj;
202 obj = subContext.lookup(path.getSuffix(1));
203 }
204 return obj;
205 }
206 }
207 }
208 if (result instanceof LinkRef) {
209 LinkRef ref = (LinkRef) result;
210 result = lookup(ref.getLinkName());
211 }
212 if (result instanceof Reference) {
213 try {
214 result = NamingManager.getObjectInstance(result, null, null, this.environment);
215 }
216 catch (NamingException e) {
217 throw e;
218 }
219 catch (Exception e) {
220 throw (NamingException) new NamingException("could not look up : " + name).initCause(e);
221 }
222 }
223 if (result instanceof ReadOnlyContext) {
224 result = new ReadOnlyContext((ReadOnlyContext) result, environment);
225 }
226 return result;
227 }
228
229 public Object lookup(Name name) throws NamingException {
230 return lookup(name.toString());
231 }
232
233 public Object lookupLink(String name) throws NamingException {
234 return lookup(name);
235 }
236
237 public Name composeName(Name name, Name prefix) throws NamingException {
238 Name result = (Name) prefix.clone();
239 result.addAll(name);
240 return result;
241 }
242
243 public String composeName(String name, String prefix) throws NamingException {
244 CompositeName result = new CompositeName(prefix);
245 result.addAll(new CompositeName(name));
246 return result.toString();
247 }
248
249 public NamingEnumeration list(String name) throws NamingException {
250 Object o = lookup(name);
251 if (o == this) {
252 return new ListEnumeration();
253 }
254 else if (o instanceof Context) {
255 return ((Context) o).list("");
256 }
257 else {
258 throw new NotContextException();
259 }
260 }
261
262 public NamingEnumeration listBindings(String name) throws NamingException {
263 Object o = lookup(name);
264 if (o == this) {
265 return new ListBindingEnumeration();
266 }
267 else if (o instanceof Context) {
268 return ((Context) o).listBindings("");
269 }
270 else {
271 throw new NotContextException();
272 }
273 }
274
275 public Object lookupLink(Name name) throws NamingException {
276 return lookupLink(name.toString());
277 }
278
279 public NamingEnumeration list(Name name) throws NamingException {
280 return list(name.toString());
281 }
282
283 public NamingEnumeration listBindings(Name name) throws NamingException {
284 return listBindings(name.toString());
285 }
286
287 public void bind(Name name, Object obj) throws NamingException {
288 throw new OperationNotSupportedException();
289 }
290
291 public void bind(String name, Object obj) throws NamingException {
292 throw new OperationNotSupportedException();
293 }
294
295 public void close() throws NamingException {
296
297 }
298
299 public Context createSubcontext(Name name) throws NamingException {
300 throw new OperationNotSupportedException();
301 }
302
303 public Context createSubcontext(String name) throws NamingException {
304 throw new OperationNotSupportedException();
305 }
306
307 public void destroySubcontext(Name name) throws NamingException {
308 throw new OperationNotSupportedException();
309 }
310
311 public void destroySubcontext(String name) throws NamingException {
312 throw new OperationNotSupportedException();
313 }
314
315 public String getNameInNamespace() throws NamingException {
316 throw new OperationNotSupportedException();
317 }
318
319 public NameParser getNameParser(Name name) throws NamingException {
320 return nameParser;
321 }
322
323 public NameParser getNameParser(String name) throws NamingException {
324 return nameParser;
325 }
326
327 public void rebind(Name name, Object obj) throws NamingException {
328 throw new OperationNotSupportedException();
329 }
330
331 public void rebind(String name, Object obj) throws NamingException {
332 throw new OperationNotSupportedException();
333 }
334
335 public void rename(Name oldName, Name newName) throws NamingException {
336 throw new OperationNotSupportedException();
337 }
338
339 public void rename(String oldName, String newName) throws NamingException {
340 throw new OperationNotSupportedException();
341 }
342
343 public void unbind(Name name) throws NamingException {
344 throw new OperationNotSupportedException();
345 }
346
347 public void unbind(String name) throws NamingException {
348 throw new OperationNotSupportedException();
349 }
350
351 private abstract class LocalNamingEnumeration implements NamingEnumeration {
352 private Iterator i = bindings.entrySet().iterator();
353
354 public boolean hasMore() throws NamingException {
355 return i.hasNext();
356 }
357
358 public boolean hasMoreElements() {
359 return i.hasNext();
360 }
361
362 protected Map.Entry getNext() {
363 return (Map.Entry) i.next();
364 }
365
366 public void close() throws NamingException {
367 }
368 }
369
370 private class ListEnumeration extends LocalNamingEnumeration {
371 public Object next() throws NamingException {
372 return nextElement();
373 }
374
375 public Object nextElement() {
376 Map.Entry entry = getNext();
377 return new NameClassPair((String) entry.getKey(), entry.getValue().getClass().getName());
378 }
379 }
380
381 private class ListBindingEnumeration extends LocalNamingEnumeration {
382 public Object next() throws NamingException {
383 return nextElement();
384 }
385
386 public Object nextElement() {
387 Map.Entry entry = getNext();
388 return new Binding((String) entry.getKey(), entry.getValue());
389 }
390 }
391 }