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