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