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    package org.apache.xbean.naming.context;
018    
019    import org.apache.xbean.naming.reference.SimpleReference;
020    
021    import javax.naming.Binding;
022    import javax.naming.CompoundName;
023    import javax.naming.Context;
024    import javax.naming.Name;
025    import javax.naming.NameClassPair;
026    import javax.naming.NameParser;
027    import javax.naming.NamingEnumeration;
028    import javax.naming.NamingException;
029    import javax.naming.Reference;
030    import javax.naming.spi.NamingManager;
031    import java.util.Enumeration;
032    import java.util.HashMap;
033    import java.util.Hashtable;
034    import java.util.Iterator;
035    import java.util.Map;
036    import java.util.Properties;
037    
038    /**
039     * @version $Rev$ $Date$
040     */
041    public final class ContextUtil {
042        private ContextUtil() {
043        }
044    
045        public final static NameParser NAME_PARSER = new SimpleNameParser();
046    
047        public static Name parseName(String name) throws NamingException {
048            return NAME_PARSER.parse(name);
049        }
050    
051        public static Object resolve(Object value, String stringName, Name parsedName, Context nameCtx) throws NamingException {
052            if (!(value instanceof Reference)) {
053                return value;
054            }
055    
056            Reference reference = (Reference) value;
057    
058            // for SimpleReference we can just call the getContext method
059            if (reference instanceof SimpleReference) {
060                try {
061                    return ((SimpleReference) reference).getContent();
062                } catch (NamingException e) {
063                    throw e;
064                } catch (Exception e) {
065                    throw (NamingException) new NamingException("Could not look up : " + stringName == null? parsedName.toString(): stringName).initCause(e);
066                }
067            }
068    
069            // for normal References we have to do it the slow way
070            try {
071                if (parsedName == null) {
072                    parsedName = NAME_PARSER.parse(stringName);
073                }
074                return NamingManager.getObjectInstance(reference, parsedName, nameCtx, nameCtx.getEnvironment());
075            } catch (NamingException e) {
076                throw e;
077            } catch (Exception e) {
078                throw (NamingException) new NamingException("Could not look up : " + stringName == null? parsedName.toString(): stringName).initCause(e);
079            }
080        }
081    
082        public static Map<String, String> listToMap(NamingEnumeration enumeration) {
083            Map<String, String> result = new HashMap<String, String>();
084            while (enumeration.hasMoreElements()) {
085                NameClassPair nameClassPair = (NameClassPair) enumeration.nextElement();
086                String name = nameClassPair.getName();
087                result.put(name, nameClassPair.getClassName());
088            }
089            return result;
090        }
091    
092        public static Map<String, Object> listBindingsToMap(NamingEnumeration enumeration) {
093            Map<String, Object> result = new HashMap<String, Object>();
094            while (enumeration.hasMoreElements()) {
095                Binding binding = (Binding) enumeration.nextElement();
096                String name = binding.getName();
097                result.put(name, binding.getObject());
098            }
099            return result;
100        }
101    
102        public static final class ListEnumeration implements NamingEnumeration<NameClassPair> {
103            private final Iterator iterator;
104    
105            public ListEnumeration(Map localBindings) {
106                this.iterator = localBindings.entrySet().iterator();
107            }
108    
109            public boolean hasMore() {
110                return iterator.hasNext();
111            }
112    
113            public boolean hasMoreElements() {
114                return iterator.hasNext();
115            }
116    
117            public NameClassPair next() {
118                return nextElement();
119            }
120    
121            public NameClassPair nextElement() {
122                Map.Entry entry = (Map.Entry) iterator.next();
123                String name = (String) entry.getKey();
124                Object value = entry.getValue();
125                String className;
126                if (value instanceof Reference) {
127                    Reference reference = (Reference) value;
128                    className = reference.getClassName();
129                } else {
130                    className = value.getClass().getName();
131                }
132                return new NameClassPair(name, className);
133            }
134    
135            public void close() {
136            }
137        }
138    
139        public static final class ListBindingEnumeration implements NamingEnumeration<Binding> {
140            private final Iterator iterator;
141            private final Context context;
142    
143            public ListBindingEnumeration(Map localBindings, Context context) {
144                this.iterator = localBindings.entrySet().iterator();
145                this.context = context;
146            }
147    
148            public boolean hasMore() {
149                return iterator.hasNext();
150            }
151    
152            public boolean hasMoreElements() {
153                return iterator.hasNext();
154            }
155    
156            public Binding next() {
157                return nextElement();
158            }
159    
160            public Binding nextElement() {
161                Map.Entry entry = (Map.Entry) iterator.next();
162                String name = (String) entry.getKey();
163                Object value = entry.getValue();
164                return new ReadOnlyBinding(name, value, context);
165            }
166    
167            public void close() {
168            }
169        }
170    
171        public static final class ReadOnlyBinding extends Binding {
172            private final Object value;
173            private final Context context;
174    
175            public ReadOnlyBinding(String name, Object value, Context context) {
176                super(name, value);
177                this.value = value;
178                this.context = context;
179            }
180    
181            public void setName(String name) {
182                throw new UnsupportedOperationException("Context is read only");
183            }
184    
185            public String getClassName() {
186                if (value instanceof Reference) {
187                    Reference reference = (Reference) value;
188                    return reference.getClassName();
189                }
190                return value.getClass().getName();
191            }
192    
193            public void setClassName(String name) {
194                throw new UnsupportedOperationException("Context is read only");
195            }
196    
197            public Object getObject() {
198                try {
199                    return resolve(value, getName(), null, context);
200                } catch (NamingException e) {
201                    throw new RuntimeException(e);
202                }
203            }
204    
205            public void setObject(Object obj) {
206                throw new UnsupportedOperationException("Context is read only");
207            }
208    
209            public boolean isRelative() {
210                return false;
211            }
212    
213            public void setRelative(boolean r) {
214                throw new UnsupportedOperationException("Context is read only");
215            }
216        }
217    
218    
219        private static final class SimpleNameParser implements NameParser {
220            private static final Properties PARSER_PROPERTIES = new Properties();
221    
222            static {
223                PARSER_PROPERTIES.put("jndi.syntax.direction", "left_to_right");
224                PARSER_PROPERTIES.put("jndi.syntax.separator", "/");
225            }
226    
227    
228            private SimpleNameParser() {
229            }
230    
231            public Name parse(String name) throws NamingException {
232                return new CompoundName(name, PARSER_PROPERTIES);
233            }
234        }
235    
236        public static Map<String, Object> createBindings(Map<String, Object> absoluteBindings, NestedContextFactory factory) throws NamingException {
237            // create a tree of Nodes using the absolute bindings
238            Node node = buildMapTree(absoluteBindings);
239    
240            // convert the node tree into a tree of context objects
241    
242            return ContextUtil.createBindings(null, node, factory);
243        }
244    
245        private static Map<String, Object> createBindings(String nameInNameSpace, Node node, NestedContextFactory factory) throws NamingException {
246            Map<String, Object> bindings = new HashMap<String, Object>(node.size());
247            for (Map.Entry<String, Object> entry : node.entrySet()) {
248                String name = entry.getKey();
249                Object value = entry.getValue();
250    
251                // if this is a nested node we need to create a context for the node
252                if (value instanceof Node) {
253                    Node nestedNode = (Node) value;
254    
255                    // recursive call create bindings to cause building the context depth first
256                    String path = nameInNameSpace == null ? name : nameInNameSpace + "/" + name;
257    
258                    Map<String, Object> nestedBindings = createBindings(path, nestedNode, factory);
259                    Context nestedContext = factory.createNestedSubcontext(path, nestedBindings);
260                    bindings.put(name, nestedContext);
261                } else {
262                    bindings.put(name, value);
263                }
264            }
265            return bindings;
266        }
267    
268    
269        /**
270         * Do nothing subclass of hashmap used to differentiate between a Map in the tree an a nested element during tree building
271         */
272        public static final class Node extends HashMap<String, Object> {
273        }
274    
275        public static Node buildMapTree(Map<String, Object> absoluteBindings) throws NamingException {
276            Node rootContext = new Node();
277    
278            for (Map.Entry<String, Object> entry : absoluteBindings.entrySet()) {
279                String name = entry.getKey();
280                Object value = entry.getValue();
281    
282                Node parentContext = rootContext;
283    
284                Name compoundName = ContextUtil.parseName(name);
285                for (Enumeration parts = compoundName.getAll(); parts.hasMoreElements();) {
286                    String part = (String) parts.nextElement();
287                    // the last element in the path is the name of the value
288                    if (parts.hasMoreElements()) {
289                        // nest node into parent
290                        Node bindings = (Node) parentContext.get(part);
291                        if (bindings == null) {
292                            bindings = new Node();
293                            parentContext.put(part, bindings);
294                        }
295    
296                        parentContext = bindings;
297                    }
298                }
299    
300                parentContext.put(compoundName.get(compoundName.size() - 1), value);
301            }
302            return rootContext;
303        }
304    }