1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 package org.osjava.sj.jndi;
34
35 import javax.naming.Context;
36 import javax.naming.ContextNotEmptyException;
37 import javax.naming.NamingException;
38 import javax.naming.NameParser;
39 import javax.naming.InvalidNameException;
40 import javax.naming.NameAlreadyBoundException;
41 import javax.naming.NotContextException;
42 import javax.naming.NameNotFoundException;
43 import javax.naming.NamingEnumeration;
44 import javax.naming.Name;
45
46 import java.util.HashMap;
47 import java.util.Hashtable;
48 import java.util.Iterator;
49 import java.util.Map;
50
51 import java.util.List;
52 import java.util.LinkedList;
53
54 /***
55 * The heart of the system, the abstract implementation of context for
56 * simple-jndi. There are no abstract methods in this class, but it is
57 * not meant to be instantiated, but extended instead.
58 *
59 * @author Robert M. Zigweid
60 * @since Simple-JNDI 0.11
61 * @version $Rev: 2684 $ $Date: 2008-05-24 22:19:06 -0700 (Sat, 24 May 2008) $
62 */
63 public abstract class AbstractContext
64 implements Cloneable, Context {
65
66
67 private static final Hashtable TABLE = new Hashtable();
68 private static final Hashtable SUB_CONTEXTS = new Hashtable();
69
70
71
72 private Hashtable table = new Hashtable();
73 private Hashtable subContexts = new Hashtable();
74 private Hashtable env = new Hashtable();
75 private NameParser nameParser;
76
77
78
79 private Name nameInNamespace = null;
80 private boolean nameLock = false;
81 private boolean closing;
82
83
84
85
86
87
88
89 /***
90 * Creates a AbstractContext.
91 */
92 protected AbstractContext() {
93 this((Hashtable)null);
94 }
95
96 /***
97 * Creates a AbstractContext.
98 *
99 * @param env a Hashtable containing the Context's environemnt.
100 */
101 protected AbstractContext(Hashtable env) {
102
103 this(env, true, null);
104 }
105
106 /***
107 * Creates a AbstractContext.
108 *
109 * @param env a Hashtable containing the Context's environment.
110 * @param systemOverride allow System Parameters to override the
111 * environment that is passed in.
112 */
113 protected AbstractContext(Hashtable env, boolean systemOverride) {
114 this(env, systemOverride, null);
115 }
116
117 /***
118 * Creates a AbstractContext.
119 *
120 * @param env a Hashtable containing the Context's environment.
121 * @param parser the NameParser being used by the Context.
122 */
123 protected AbstractContext(Hashtable env, NameParser parser) {
124 this(env, true, parser);
125 }
126
127 /***
128 * Creates a AbstractContext.
129 *
130 * @param systemOverride allow System Parameters to override the
131 * environment that is passed in.
132 */
133 protected AbstractContext(boolean systemOverride) {
134 this(null, systemOverride, null);
135 }
136
137 /***
138 * Creates a AbstractContext.
139 *
140 * @param systemOverride allow System Parameters to override the
141 * environment that is passed in.
142 * @param parser the NameParser being used by the Context.
143 */
144 protected AbstractContext(boolean systemOverride, NameParser parser) {
145 this(null, systemOverride, parser);
146 }
147
148 /***
149 * Creates a AbstractContext.
150 *
151 * @param parser the NameParser being used by the Context.
152 */
153 protected AbstractContext(NameParser parser) {
154 this(null, true, parser);
155 }
156
157 /***
158 * Creates a AbstractContext.
159 *
160 * @param env a Hashtable containing the Context's environment.
161 * @param systemOverride allow System Parameters to override the
162 * environment that is passed in.
163 * @param parser the NameParser being used by the Context.
164 */
165 protected AbstractContext(Hashtable env, boolean systemOverride, NameParser parser) {
166 String shared = null;
167 if(env != null) {
168 this.env = (Hashtable)env.clone();
169 shared = (String)this.env.get("org.osjava.sj.jndi.shared");
170 }
171
172
173
174 if(systemOverride) {
175 if(System.getProperty("org.osjava.sj.jndi.shared") != null) {
176 shared = System.getProperty("org.osjava.sj.jndi.shared");
177 }
178 }
179
180 if("true".equals(shared)) {
181 this.table = new StaticHashtable(TABLE);
182 this.subContexts = new StaticHashtable(SUB_CONTEXTS);
183 }
184
185 if(parser == null) {
186 try {
187 nameParser = new SimpleNameParser(this);
188 } catch (NamingException e) {
189
190
191
192
193
194
195
196 e.printStackTrace();
197 }
198 }
199 try {
200 nameInNamespace = nameParser.parse("");
201 } catch (NamingException e) {
202
203
204 e.printStackTrace();
205 }
206 }
207
208 /***
209 * Create a new context based upon the environment of the passed context.
210 * @param that
211 */
212 protected AbstractContext(AbstractContext that) {
213 this(that.env);
214 }
215
216
217
218
219
220 /***
221 * Return the named object.
222 * This implementation looks for things in the following order:<br/>
223 * <ol>
224 * <li>The empty element to duplicate the context.</li>
225 * <li>A named object in the environment.</li>
226 * <li>A named object in the Context's store</li>
227 * <li>A named sub-Context of this Context</li>
228 * </ol>
229 * Unlike many implementations of the JNDI specification, this
230 * implementation returns null when a name is not found, rather than
231 * throwing an exception. The specification does not appear to state
232 * which way it should be.
233 *
234 * @see javax.naming.Context#lookup(javax.naming.Name)
235 */
236 public Object lookup(Name name) throws NamingException {
237
238
239
240
241 String stringName = name.toString();
242
243
244
245
246 if(name.size() == 0) {
247 Object ret = null;
248 try {
249 ret = (AbstractContext)this.clone();
250 } catch(CloneNotSupportedException e) {
251
252
253
254
255 e.printStackTrace();
256 }
257 if(ret != null) {
258 return ret;
259 }
260 }
261
262
263 if(System.getProperty(stringName) != null) {
264 return System.getProperty(stringName);
265 }
266
267 Name objName = name.getPrefix(1);
268 if(name.size() > 1) {
269
270 if(subContexts.containsKey(objName)) {
271 return ((Context)subContexts.get(objName)).lookup(name.getSuffix(1));
272 }
273 throw new NamingException("Invalid subcontext '" + objName.toString() + "' in context '"
274 + getNameInNamespace() + "'");
275 }
276
277
278 if(table.containsKey(name)) {
279 return table.get(objName);
280 }
281
282
283
284
285 if(subContexts.containsKey(name)) {
286 return subContexts.get(name);
287 }
288
289
290 if(env.containsKey(name)) {
291 return env.get(name.toString());
292 }
293
294
295
296
297
298 return null;
299 }
300
301 /***
302 * @see javax.naming.Context#lookup(java.lang.String)
303 */
304 public Object lookup(String name) throws NamingException {
305 return lookup(nameParser.parse(name));
306 }
307
308 /***
309 * @see javax.naming.Context#bind(javax.naming.Name, java.lang.Object)
310 */
311 public void bind(Name name, Object object) throws NamingException {
312
313
314
315
316 if(name.size() > 1) {
317 Name prefix = name.getPrefix(1);
318 if(subContexts.containsKey(prefix)) {
319 ((Context)subContexts.get(prefix)).bind(name.getSuffix(1), object);
320 return;
321 }
322 }
323 if(name.size() == 0) {
324 throw new InvalidNameException("Cannot bind to an empty name");
325 }
326
327 if(table.containsKey(name) ||
328 subContexts.containsKey(name) ||
329 env.containsKey(name.toString())) {
330 throw new NameAlreadyBoundException("Name " + name.toString()
331 + " already bound. Use rebind() to override");
332 }
333
334 if(object instanceof Context) {
335 subContexts.put(name, object);
336 } else {
337 table.put(name, object);
338 }
339 }
340
341 /***
342 * @see javax.naming.Context#bind(java.lang.String, java.lang.Object)
343 */
344 public void bind(String name, Object object) throws NamingException {
345 bind(nameParser.parse(name), object);
346 }
347
348 /***
349 * @see javax.naming.Context#rebind(javax.naming.Name, java.lang.Object)
350 */
351 public void rebind(Name name, Object object) throws NamingException {
352 if(name.isEmpty()) {
353 throw new InvalidNameException("Cannot bind to empty name");
354 }
355
356 Object targetContext = lookup(name.getPrefix(name.size() - 1));
357 if(targetContext == null || !(targetContext instanceof Context)) {
358 throw new NamingException("Cannot bind object. Target context does not exist.");
359 }
360 unbind(name);
361 bind(name, object);
362 }
363
364 /***
365 * @see javax.naming.Context#rebind(java.lang.String, java.lang.Object)
366 */
367 public void rebind(String name, Object object) throws NamingException {
368 rebind(nameParser.parse(name), object);
369 }
370
371 /***
372 * @see javax.naming.Context#unbind(javax.naming.Name)
373 */
374 public void unbind(Name name) throws NamingException {
375 if(name.isEmpty()) {
376 throw new InvalidNameException("Cannot unbind to empty name");
377 }
378
379 if(name.size() == 1) {
380 if(table.containsKey(name)) {
381 table.remove(name);
382 }
383 return;
384 }
385
386
387 Object targetContext = lookup(name.getPrefix(name.size() - 1));
388 if(targetContext == null || !(targetContext instanceof Context)) {
389 throw new NamingException("Cannot unbind object. Target context does not exist.");
390 }
391 ((Context)targetContext).unbind(name.getSuffix(name.size() - 1));
392 }
393
394 /***
395 * @see javax.naming.Context#unbind(java.lang.String)
396 */
397 public void unbind(String name) throws NamingException {
398 unbind(nameParser.parse(name));
399 }
400
401 /***
402 * @see javax.naming.Context#rename(javax.naming.Name, javax.naming.Name)
403 */
404 public void rename(Name oldName, Name newName) throws NamingException {
405
406 Object old = lookup(oldName);
407 if(newName.isEmpty()) {
408 throw new InvalidNameException("Cannot bind to empty name");
409 }
410
411 if(old == null) {
412 throw new NamingException("Name '" + oldName + "' not found.");
413 }
414
415
416 if(lookup(newName) != null) {
417 throw new NameAlreadyBoundException("Name '" + newName + "' already bound");
418 }
419
420 unbind(oldName);
421 unbind(newName);
422 bind(newName, old);
423
424
425
426
427 if(old instanceof Thread) {
428 ((Thread)old).setName(newName.toString());
429 }
430 }
431
432 /***
433 * @see javax.naming.Context#rename(java.lang.String, java.lang.String)
434 */
435 public void rename(String oldName, String newName) throws NamingException {
436 rename(nameParser.parse(oldName), nameParser.parse(newName));
437 }
438
439
440
441 /***
442 * @see javax.naming.Context#list(javax.naming.Name)
443 */
444 public NamingEnumeration list(Name name) throws NamingException {
445
446
447
448 if(name == null || name.isEmpty()) {
449
450
451
452
453
454
455 Map enumStore = new HashMap();
456 enumStore.putAll(table);
457 enumStore.putAll(subContexts);
458 NamingEnumeration enumerator = new ContextNames(enumStore);
459 return enumerator;
460 }
461
462 Name subName = name.getPrefix(1);
463 if(table.containsKey(subName)) {
464
465 throw new NotContextException(name + " cannot be listed");
466 }
467 if(subContexts.containsKey(subName)) {
468 return ((Context)subContexts.get(subName)).list(name.getSuffix(1));
469 }
470
471
472
473
474
475 throw new NamingException();
476 }
477
478
479 /***
480 * @see javax.naming.Context#list(java.lang.String)
481 */
482 public NamingEnumeration list(String name) throws NamingException {
483 return list(nameParser.parse(name));
484 }
485
486 /***
487 * @see javax.naming.Context#listBindings(javax.naming.Name)
488 */
489 public NamingEnumeration listBindings(Name name) throws NamingException {
490 if(name == null || name.isEmpty()) {
491
492
493
494
495
496
497 Map enumStore = new HashMap();
498 enumStore.putAll(table);
499 enumStore.putAll(subContexts);
500 return new ContextBindings(enumStore);
501 }
502
503 Name subName = name.getPrefix(1);
504 if(table.containsKey(subName)) {
505
506 throw new NotContextException(name + " cannot be listed");
507 }
508 if(subContexts.containsKey(subName)) {
509 return ((Context)subContexts.get(subName)).listBindings(name.getSuffix(1));
510 }
511
512
513
514
515 throw new NamingException();
516 }
517
518 /***
519 * @see javax.naming.Context#listBindings(java.lang.String)
520 */
521 public NamingEnumeration listBindings(String name) throws NamingException {
522 return listBindings(nameParser.parse(name));
523 }
524
525
526 /***
527 * @see javax.naming.Context#destroySubcontext(javax.naming.Name)
528 */
529 public void destroySubcontext(Name name) throws NamingException {
530 if(name.size() > 1) {
531 if(subContexts.containsKey(name.getPrefix(1))) {
532 Context subContext = (Context)subContexts.get(name.getPrefix(1));
533 subContext.destroySubcontext(name.getSuffix(1));
534 return;
535 }
536
537 throw new NameNotFoundException();
538 }
539
540 if(table.containsKey(name)) {
541 throw new NotContextException();
542 }
543
544 if(!subContexts.containsKey(name)) {
545 throw new NameNotFoundException();
546 }
547 Context subContext = (Context)subContexts.get(name);
548
549 NamingEnumeration names = subContext.list("");
550 if(names.hasMore()) {
551 throw new ContextNotEmptyException();
552 }
553 ((Context)subContexts.get(name)).close();
554 subContexts.remove(name);
555 }
556
557 /***
558 * @see javax.naming.Context#destroySubcontext(java.lang.String)
559 */
560 public void destroySubcontext(String name) throws NamingException {
561 destroySubcontext(nameParser.parse(name));
562 }
563
564 /***
565 * @see javax.naming.Context#createSubcontext(javax.naming.Name)
566 */
567 public abstract Context createSubcontext(Name name) throws NamingException;
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598 /***
599 * @see javax.naming.Context#createSubcontext(java.lang.String)
600 */
601 public Context createSubcontext(String name) throws NamingException {
602 return createSubcontext(nameParser.parse(name));
603 }
604
605
606 /***
607 * @see javax.naming.Context#lookupLink(javax.naming.Name)
608 */
609 public Object lookupLink(Name name) throws NamingException {
610 return lookup(name);
611 }
612
613 /***
614 * @see javax.naming.Context#lookupLink(java.lang.String)
615 */
616 public Object lookupLink(String name) throws NamingException {
617 return lookup(nameParser.parse(name));
618 }
619
620 /***
621 * @see javax.naming.Context#getNameParser(javax.naming.Name)
622 */
623 public NameParser getNameParser(Name name) throws NamingException {
624
625
626
627
628 if(name == null ||
629 name.isEmpty() ||
630 (name.size() == 1 && name.toString().equals(getNameInNamespace()))) {
631 return nameParser;
632 }
633 Name subName = name.getPrefix(1);
634 if(subContexts.containsKey(subName)) {
635 return ((Context)subContexts.get(subName)).getNameParser(name.getSuffix(1));
636 }
637 throw new NotContextException();
638 }
639
640 /***
641 * @see javax.naming.Context#getNameParser(java.lang.String)
642 */
643 public NameParser getNameParser(String name) throws NamingException {
644 return getNameParser(nameParser.parse(name));
645 }
646
647 /***
648 * @see javax.naming.Context#composeName(javax.naming.Name, javax.naming.Name)
649 */
650 public Name composeName(Name name, Name prefix) throws NamingException {
651
652 if(name == null || prefix == null) {
653 throw new NamingException("Arguments must not be null");
654 }
655 Name retName = (Name)prefix.clone();
656 retName.addAll(name);
657 return retName;
658 }
659
660 /***
661 * @see javax.naming.Context#composeName(java.lang.String, java.lang.String)
662 */
663 public String composeName(String name, String prefix) throws NamingException {
664 Name retName = composeName(nameParser.parse(name), nameParser.parse(prefix));
665
666 return retName.toString();
667 }
668
669 /***
670 * @see javax.naming.Context#addToEnvironment(java.lang.String, java.lang.Object)
671 */
672 public Object addToEnvironment(String name, Object object) throws NamingException {
673 if(this.env == null) {
674 return null;
675 }
676 return this.env.put(name, object);
677 }
678
679 /***
680 * @see javax.naming.Context#removeFromEnvironment(java.lang.String)
681 */
682 public Object removeFromEnvironment(String name) throws NamingException {
683 if(this.env == null) {
684 return null;
685 }
686 return this.env.remove(name);
687 }
688
689 /***
690 * @see javax.naming.Context#getEnvironment()
691 */
692 public Hashtable getEnvironment() throws NamingException {
693 if(this.env == null) {
694 return new Hashtable();
695 }
696 return (Hashtable)this.env.clone();
697 }
698
699 /***
700 * @see javax.naming.Context#close()
701 */
702 public void close() throws NamingException {
703
704
705 if(closing) {
706 return;
707 }
708 Iterator it = subContexts.keySet().iterator();
709 while(it.hasNext()) {
710 destroySubcontext((Name)it.next());
711 }
712
713 while(table.size() > 0 || subContexts.size() > 0) {
714 it = table.keySet().iterator();
715 List toRemove = new LinkedList();
716 while(it.hasNext()) {
717 Name name = (Name)it.next();
718
719 Object entry = table.get(name);
720
721 if(entry instanceof Thread) {
722 Thread thread = (Thread) entry;
723 if(thread.isAlive()) {
724 toRemove.add(name);
725 }
726 } else {
727 toRemove.add(name);
728 }
729 }
730 for(it = toRemove.iterator(); it.hasNext();) {
731 table.remove(it.next());
732 }
733
734 toRemove.clear();
735 it = subContexts.keySet().iterator();
736 while(it.hasNext()) {
737 Name name = (Name)it.next();
738 AbstractContext context = (AbstractContext)subContexts.get(name);
739 if(context.isEmpty()) {
740 toRemove.add(name);
741 }
742 }
743 for(it = toRemove.iterator(); it.hasNext();) {
744 subContexts.remove(it.next());
745 }
746 }
747 this.env = null;
748 this.table = null;
749 }
750
751 /***
752 * @see javax.naming.Context#getNameInNamespace()
753 */
754 public String getNameInNamespace() throws NamingException {
755 return nameInNamespace.toString();
756 }
757
758
759
760
761 /***
762 * Determine whether or not the context is empty. Objects bound directly
763 * to the context or subcontexts are all that is considered. The
764 * environment of the context is not considered.
765 *
766 * @return true of the context is empty, else false.
767 */
768 public boolean isEmpty() {
769 return (table.size() > 0 || subContexts.size() > 0);
770 }
771
772 /***
773 * Set the name of the Context. This is only used from createSubcontext.
774 * It might get replaced by adding more constructors, but there is really
775 * no reason to expose it publicly anyway.
776 *
777 * @param name the Name of the context.
778 * @throws NamingException if the subContext already has a name.
779 */
780 public void setNameInNamespace(Name name) throws NamingException {
781 if(nameLock) {
782 if(nameInNamespace != null || !nameInNamespace.isEmpty()) {
783 throw new NamingException("Name already set.");
784 }
785 }
786 nameInNamespace = name;
787 nameLock = true;
788 }
789
790 /***
791 * Set the name of the Context. This is only used from createSubcontext.
792 * It might get replaced by adding more constructors, but there is really
793 * no reason to expose it publicly anyway.
794 *
795 * @param name a String representation of the Name of the context.
796 * @throws NamingException if the subContext already has a name.
797 */
798 protected void setNameInNamespace(String name) throws NamingException {
799 setNameInNamespace(nameParser.parse(name));
800 }
801
802 /***
803 * Convenience method returning the subcontexts that this context parents.
804 * @return a Hashtable of context objects that are parented by this
805 * context.
806 */
807 protected Hashtable getSubContexts() {
808 return (Hashtable)subContexts.clone();
809 }
810
811 /***
812 * Whether this context is running in shared mode AND has already
813 * been loaded with data.
814 */
815 public static boolean isSharedAndLoaded() {
816 return (TABLE.size() != 0 || SUB_CONTEXTS.size() != 0);
817 }
818
819 }
820
821