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