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