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 */
017package org.apache.xbean.naming.context;
018
019import org.apache.xbean.naming.reference.SimpleReference;
020
021import javax.naming.Binding;
022import javax.naming.CompoundName;
023import javax.naming.Context;
024import javax.naming.Name;
025import javax.naming.NameClassPair;
026import javax.naming.NameParser;
027import javax.naming.NamingEnumeration;
028import javax.naming.NamingException;
029import javax.naming.Reference;
030import javax.naming.spi.NamingManager;
031import java.util.Enumeration;
032import java.util.HashMap;
033import java.util.Hashtable;
034import java.util.Iterator;
035import java.util.Map;
036import java.util.Properties;
037
038/**
039 * @version $Rev$ $Date$
040 */
041public 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}