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    }