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}