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.spring.jndi;
018
019 import javax.naming.Binding;
020 import javax.naming.CompositeName;
021 import javax.naming.Context;
022 import javax.naming.LinkRef;
023 import javax.naming.Name;
024 import javax.naming.NameClassPair;
025 import javax.naming.NameNotFoundException;
026 import javax.naming.NameParser;
027 import javax.naming.NamingEnumeration;
028 import javax.naming.NamingException;
029 import javax.naming.NotContextException;
030 import javax.naming.OperationNotSupportedException;
031 import javax.naming.Reference;
032 import javax.naming.spi.NamingManager;
033
034 import java.io.Serializable;
035 import java.util.HashMap;
036 import java.util.Hashtable;
037 import java.util.Iterator;
038 import java.util.Map;
039
040 /**
041 * A simple spring based JNDI context which is mutable
042 *
043 * @version $Revision: 657 $
044 */
045 public class DefaultContext implements Context, Serializable {
046
047 private static final long serialVersionUID = -5754338187296859149L;
048 protected static final NameParser nameParser = new NameParserImpl();
049
050 private boolean freeze = false;
051
052 protected final Hashtable environment; // environment for this context
053 protected final Map bindings; // bindings at my level
054 protected final Map treeBindings; // all bindings under me
055
056 private boolean frozen = false;
057 private String nameInNamespace = "";
058 public static final String SEPARATOR = "/";
059
060 public DefaultContext() {
061 environment = new Hashtable();
062 bindings = new HashMap();
063 treeBindings = new HashMap();
064 }
065
066 public DefaultContext(Hashtable env) {
067 if (env == null) {
068 this.environment = new Hashtable();
069 }
070 else {
071 this.environment = new Hashtable(env);
072 }
073 this.bindings = new HashMap();
074 this.treeBindings = new HashMap();
075 }
076
077 public DefaultContext(Hashtable environment, Map bindings) {
078 if (environment == null) {
079 this.environment = new Hashtable();
080 }
081 else {
082 this.environment = new Hashtable(environment);
083 }
084 this.bindings = bindings;
085 treeBindings = new HashMap();
086 frozen = true;
087 }
088
089 public DefaultContext(Hashtable environment, Map bindings, String nameInNamespace) {
090 this(environment, bindings);
091 this.nameInNamespace = nameInNamespace;
092 }
093
094 protected DefaultContext(DefaultContext clone, Hashtable env) {
095 this.bindings = clone.bindings;
096 this.treeBindings = clone.treeBindings;
097 this.environment = new Hashtable(env);
098 }
099
100 protected DefaultContext(DefaultContext clone, Hashtable env, String nameInNamespace) {
101 this(clone, env);
102 this.nameInNamespace = nameInNamespace;
103 }
104
105 public Object addToEnvironment(String propName, Object propVal) throws NamingException {
106 return environment.put(propName, propVal);
107 }
108
109 public Hashtable getEnvironment() throws NamingException {
110 return (Hashtable) environment.clone();
111 }
112
113 public Object removeFromEnvironment(String propName) throws NamingException {
114 return environment.remove(propName);
115 }
116
117 public Object lookup(String name) throws NamingException {
118 if (name.length() == 0) {
119 return this;
120 }
121 Object result = treeBindings.get(name);
122 if (result == null) {
123 result = bindings.get(name);
124 }
125 if (result == null) {
126 int pos = name.indexOf(':');
127 if (pos > 0) {
128 String scheme = name.substring(0, pos);
129 Context ctx = NamingManager.getURLContext(scheme, environment);
130 if (ctx == null) {
131 throw new NamingException("scheme " + scheme + " not recognized");
132 }
133 return ctx.lookup(name);
134 }
135 else {
136 // Split out the first name of the path
137 // and look for it in the bindings map.
138 CompositeName path = new CompositeName(name);
139
140 if (path.size() == 0) {
141 return this;
142 }
143 else {
144 String first = path.get(0);
145 Object obj = bindings.get(first);
146 if (obj == null) {
147 throw new NameNotFoundException(name);
148 }
149 else if (obj instanceof Context && path.size() > 1) {
150 Context subContext = (Context) obj;
151 obj = subContext.lookup(path.getSuffix(1));
152 }
153 return obj;
154 }
155 }
156 }
157 if (result instanceof LinkRef) {
158 LinkRef ref = (LinkRef) result;
159 result = lookup(ref.getLinkName());
160 }
161 if (result instanceof Reference) {
162 try {
163 result = NamingManager.getObjectInstance(result, null, null, this.environment);
164 }
165 catch (NamingException e) {
166 throw e;
167 }
168 catch (Exception e) {
169 throw (NamingException) new NamingException("could not look up : " + name).initCause(e);
170 }
171 }
172 if (result instanceof DefaultContext) {
173 String prefix = getNameInNamespace();
174 if (prefix.length() > 0) {
175 prefix = prefix + SEPARATOR;
176 }
177 result = new DefaultContext((DefaultContext) result, environment, prefix + name);
178 }
179 return result;
180 }
181
182 public Object lookup(Name name) throws NamingException {
183 return lookup(name.toString());
184 }
185
186 public Object lookupLink(String name) throws NamingException {
187 return lookup(name);
188 }
189
190 public Name composeName(Name name, Name prefix) throws NamingException {
191 Name result = (Name) prefix.clone();
192 result.addAll(name);
193 return result;
194 }
195
196 public String composeName(String name, String prefix) throws NamingException {
197 CompositeName result = new CompositeName(prefix);
198 result.addAll(new CompositeName(name));
199 return result.toString();
200 }
201
202 public NamingEnumeration list(String name) throws NamingException {
203 Object o = lookup(name);
204 if (o == this) {
205 return new DefaultContext.ListEnumeration();
206 }
207 else if (o instanceof Context) {
208 return ((Context) o).list("");
209 }
210 else {
211 throw new NotContextException();
212 }
213 }
214
215 public NamingEnumeration listBindings(String name) throws NamingException {
216 Object o = lookup(name);
217 if (o == this) {
218 return new DefaultContext.ListBindingEnumeration();
219 }
220 else if (o instanceof Context) {
221 return ((Context) o).listBindings("");
222 }
223 else {
224 throw new NotContextException();
225 }
226 }
227
228 public Object lookupLink(Name name) throws NamingException {
229 return lookupLink(name.toString());
230 }
231
232 public NamingEnumeration list(Name name) throws NamingException {
233 return list(name.toString());
234 }
235
236 public NamingEnumeration listBindings(Name name) throws NamingException {
237 return listBindings(name.toString());
238 }
239
240 public void bind(Name name, Object value) throws NamingException {
241 bind(name.toString(), value);
242 }
243
244 public void bind(String name, Object value) throws NamingException {
245 checkFrozen();
246 internalBind(name, value);
247 }
248
249 public void close() throws NamingException {
250 // ignore
251 }
252
253 public Context createSubcontext(Name name) throws NamingException {
254 throw new OperationNotSupportedException();
255 }
256
257 public Context createSubcontext(String name) throws NamingException {
258 throw new OperationNotSupportedException();
259 }
260
261 public void destroySubcontext(Name name) throws NamingException {
262 throw new OperationNotSupportedException();
263 }
264
265 public void destroySubcontext(String name) throws NamingException {
266 throw new OperationNotSupportedException();
267 }
268
269 public String getNameInNamespace() throws NamingException {
270 return nameInNamespace;
271 }
272
273 public NameParser getNameParser(Name name) throws NamingException {
274 return nameParser;
275 }
276
277 public NameParser getNameParser(String name) throws NamingException {
278 return nameParser;
279 }
280
281 public void rebind(Name name, Object value) throws NamingException {
282 rebind(name.toString(), value);
283 }
284
285 public void rebind(String name, Object value) throws NamingException {
286 checkFrozen();
287 internalBind(name, value, true);
288 }
289
290 public void rename(Name oldName, Name newName) throws NamingException {
291 checkFrozen();
292 Object value = lookup(oldName);
293 unbind(oldName);
294 bind(newName, value);
295 }
296
297 public void rename(String oldName, String newName) throws NamingException {
298 Object value = lookup(oldName);
299 unbind(oldName);
300 bind(newName, value);
301 }
302
303 public void unbind(Name name) throws NamingException {
304 unbind(name.toString());
305 }
306
307 public void unbind(String name) throws NamingException {
308 checkFrozen();
309 internalBind(name, null, true);
310 }
311
312 private abstract class LocalNamingEnumeration implements NamingEnumeration {
313 private Iterator i = bindings.entrySet().iterator();
314
315 public boolean hasMore() throws NamingException {
316 return i.hasNext();
317 }
318
319 public boolean hasMoreElements() {
320 return i.hasNext();
321 }
322
323 protected Map.Entry getNext() {
324 return (Map.Entry) i.next();
325 }
326
327 public void close() throws NamingException {
328 }
329 }
330
331 private class ListEnumeration extends DefaultContext.LocalNamingEnumeration {
332 public Object next() throws NamingException {
333 return nextElement();
334 }
335
336 public Object nextElement() {
337 Map.Entry entry = getNext();
338 return new NameClassPair((String) entry.getKey(), entry.getValue().getClass().getName());
339 }
340 }
341
342 private class ListBindingEnumeration extends DefaultContext.LocalNamingEnumeration {
343 public Object next() throws NamingException {
344 return nextElement();
345 }
346
347 public Object nextElement() {
348 Map.Entry entry = getNext();
349 return new Binding((String) entry.getKey(), entry.getValue());
350 }
351 }
352
353 public Map getEntries() {
354 return new HashMap(bindings);
355 }
356
357 public void setEntries(Map entries) throws NamingException {
358 if (entries != null) {
359 for (Iterator iter = entries.entrySet().iterator(); iter.hasNext();) {
360 Map.Entry entry = (Map.Entry) iter.next();
361 String name = (String) entry.getKey();
362 Object value = entry.getValue();
363 internalBind(name, value);
364 }
365 }
366 }
367
368 public boolean isFreeze() {
369 return freeze;
370 }
371
372 public void setFreeze(boolean freeze) {
373 this.freeze = freeze;
374 }
375
376 /**
377 * internalBind is intended for use only during setup or possibly by suitably synchronized superclasses.
378 * It binds every possible lookup into a map in each context. To do this, each context
379 * strips off one name segment and if necessary creates a new context for it. Then it asks that context
380 * to bind the remaining name. It returns a map containing all the bindings from the next context, plus
381 * the context it just created (if it in fact created it). (the names are suitably extended by the segment
382 * originally lopped off).
383 *
384 * @param name
385 * @param value
386 * @return
387 * @throws javax.naming.NamingException
388 */
389 protected Map internalBind(String name, Object value) throws NamingException {
390 return internalBind(name, value, false);
391
392 }
393 protected Map internalBind(String name, Object value, boolean allowRebind) throws NamingException {
394
395 if (name == null || name.length() == 0){
396 throw new NamingException("Invalid Name " + name);
397 }
398 if (frozen){
399 throw new NamingException("Read only");
400 }
401
402 Map newBindings = new HashMap();
403 int pos = name.indexOf('/');
404 if (pos == -1) {
405 Object oldValue = treeBindings.put(name, value);
406 if (!allowRebind && oldValue != null) {
407 throw new NamingException("Something already bound at " + name);
408 }
409 bindings.put(name, value);
410 newBindings.put(name, value);
411 }
412 else {
413 String segment = name.substring(0, pos);
414
415 if (segment == null || segment.length()==0){
416 throw new NamingException("Invalid segment " + segment);
417 }
418 Object o = treeBindings.get(segment);
419 if (o == null) {
420 o = newContext();
421 treeBindings.put(segment, o);
422 bindings.put(segment, o);
423 newBindings.put(segment, o);
424 }
425 else if (!(o instanceof DefaultContext)) {
426 throw new NamingException("Something already bound where a subcontext should go");
427 }
428 DefaultContext defaultContext = (DefaultContext) o;
429 String remainder = name.substring(pos + 1);
430 Map subBindings = defaultContext.internalBind(remainder, value, allowRebind);
431 for (Iterator iterator = subBindings.entrySet().iterator(); iterator.hasNext();) {
432 Map.Entry entry = (Map.Entry) iterator.next();
433 String subName = segment + "/" + (String) entry.getKey();
434 Object bound = entry.getValue();
435 treeBindings.put(subName, bound);
436 newBindings.put(subName, bound);
437 }
438 }
439 return newBindings;
440 }
441
442 protected void checkFrozen() throws OperationNotSupportedException {
443 if (isFreeze()) {
444 throw new OperationNotSupportedException("JNDI context is frozen!");
445 }
446 }
447
448 protected DefaultContext newContext() {
449 return new DefaultContext();
450 }
451
452 }