1
2
3
4
5
6
7
8
9
10 package nl.tudelft.simulation.naming;
11
12 import java.io.Serializable;
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.HashMap;
16 import java.util.Hashtable;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Properties;
21 import java.util.Set;
22 import java.util.TreeMap;
23
24 import javax.naming.Binding;
25 import javax.naming.CompoundName;
26 import javax.naming.Context;
27 import javax.naming.Name;
28 import javax.naming.NameClassPair;
29 import javax.naming.NameParser;
30 import javax.naming.NamingEnumeration;
31 import javax.naming.NamingException;
32 import javax.naming.event.EventContext;
33 import javax.naming.event.NamingEvent;
34 import javax.naming.event.NamingListener;
35
36 import nl.tudelft.simulation.event.Event;
37 import nl.tudelft.simulation.event.EventProducer;
38 import nl.tudelft.simulation.event.EventProducerInterface;
39 import nl.tudelft.simulation.event.EventType;
40 import nl.tudelft.simulation.logger.Logger;
41
42 /***
43 * The JVMContext as implementation of the Context interface. The JVM context is
44 * an in-memory context implementation
45 * <p>
46 * (c) copyright 2003 <a href="http://www.simulation.tudelft.nl">Delft
47 * University of Technology </a>, the Netherlands. <br>
48 * See for project information <a href="http://www.simulation.tudelft.nl">
49 * www.simulation.tudelft.nl </a> <br>
50 * License of use: <a href="http://www.gnu.org/copyleft/gpl.html">General Public
51 * License (GPL) </a>, no warranty <br>
52 *
53 * @author <a href="http://www.simulation.tudelft.nl/people/jacobs.html">Peter
54 * Jacobs </a> <br>
55 * <a href="mailto:nlang@fbk.eur.nl">Niels Lang </a>
56 * @version 1.5 2004-03-24
57 * @since 1.2
58 */
59 public class JVMContext extends EventProducer implements EventContext,
60 EventProducerInterface, Serializable
61 {
62 /*** NUMBER_CHANGED_EVENT is fired whenever the number of children changes */
63 public static final EventType NUMBER_CHANGED_EVENT = new EventType(
64 "Number changed");
65
66 /*** CHILD_ADDED_EVENT is fired whenever a child is added */
67 public static final EventType CHILD_ADDED_EVENT = new EventType(
68 "Child added");
69
70 /*** CHILD_REMOVED_EVENT is fired whenever a child is removed */
71 public static final EventType CHILD_REMOVED_EVENT = new EventType(
72 "Child removed");
73
74 /*** the syntax of this parser */
75 private static Properties syntax = new Properties();
76
77 static
78 {
79 syntax.put("jndi.syntax.direction", "left_to_right");
80 syntax.put("jndi.syntax.separator", "/");
81 syntax.put("jndi.syntax.escape", "&");
82 syntax.put("jndi.syntax.beginquote", "\"");
83 syntax.put("jndi.syntax.ava", ",");
84 syntax.put("jndi.syntax.typeval", "=");
85 }
86
87 /*** the parent context */
88 protected Context parent;
89
90 /*** the atomicName */
91 private String atomicName;
92
93 /*** the children */
94 protected Map elements = Collections.synchronizedMap(new TreeMap());
95
96 /*** the eventListeners */
97 protected List eventListeners = Collections
98 .synchronizedList(new ArrayList());
99
100 /*** the nameParser */
101 protected NameParser parser = new MyParser(JVMContext.syntax);
102
103 /***
104 * constructs a new JVMContext.
105 */
106 public JVMContext()
107 {
108 this(null, "");
109 }
110
111 /***
112 * constructs a new JVMContext
113 *
114 * @param parent the parent context
115 * @param atomicName the atomicname
116 */
117 public JVMContext(final Context parent, final String atomicName)
118 {
119 this.parent = parent;
120 this.atomicName = atomicName;
121 }
122
123 /***
124 * @see java.lang.Object#clone()
125 */
126 public synchronized Object clone() throws CloneNotSupportedException
127 {
128 JVMContext clone = new JVMContext();
129 Map elements = new HashMap(this.elements);
130 for (Iterator i = elements.keySet().iterator(); i.hasNext();)
131 {
132 Object key = i.next();
133 Object value = elements.get(key);
134 if (value instanceof JVMContext)
135 {
136 JVMContext item = (JVMContext) value;
137 value = item.clone();
138 }
139 elements.put(key, value);
140 }
141 clone.elements = elements;
142 return clone;
143 }
144
145 /***
146 * Returns true when this context is not root itself and name starts with
147 * '/'
148 *
149 * @param name the name
150 * @return boolean
151 * @throws NamingException on parse failure
152 */
153 private boolean isRootForwardable(final Name name) throws NamingException
154 {
155 return (this.parent != null && name.startsWith(this.parser.parse(syntax
156 .getProperty("jndi.syntax.separator"))));
157 }
158
159 /***
160 * returns the root of this context
161 *
162 * @return Context the root
163 * @throws NamingException on lookup exception
164 */
165 private Context getRoot() throws NamingException
166 {
167 return (Context) lookup("");
168 }
169
170 /***
171 * makes the name relative
172 *
173 * @param name the name
174 * @return Name
175 * @throws NamingException on parse failure
176 */
177 private Name makeRelative(final Name name) throws NamingException
178 {
179 if (name.startsWith(this.parser.parse(syntax
180 .getProperty("jndi.syntax.separator"))))
181 {
182 return name.getSuffix(1);
183 }
184 return name;
185 }
186
187 /***
188 * @see javax.naming.Context#lookup(Name)
189 */
190 public synchronized Object lookup(final Name name) throws NamingException
191 {
192
193 if (isRootForwardable(name))
194 {
195 return getRoot().lookup(name);
196 }
197
198
199 if (name.size() == 0 && this.parent == null)
200 {
201 return this;
202 }
203 if (name.size() == 0)
204 {
205 return this.parent.lookup(name);
206 }
207
208 Name relativeName = makeRelative(name);
209
210
211 if (relativeName.size() > 1)
212 {
213 return ((Context) lookup(relativeName.get(0))).lookup(relativeName
214 .getSuffix(1));
215 }
216
217
218 if (!this.elements.containsKey(relativeName.toString()))
219 {
220 throw new NamingException(relativeName + " not found.");
221 }
222 return this.elements.get(relativeName.toString());
223 }
224
225 /***
226 * @see javax.naming.Context#lookup(String)
227 */
228 public Object lookup(final String arg0) throws NamingException
229 {
230 return lookup(this.parser.parse(arg0));
231 }
232
233 /***
234 * @see javax.naming.Context#bind(Name, Object)
235 */
236 public synchronized void bind(final Name name, final Object value)
237 throws NamingException
238 {
239 if (isRootForwardable(name))
240 {
241 getRoot().bind(name, value);
242 return;
243 }
244 Name relativeName = makeRelative(name);
245 if (relativeName.size() > 1)
246 {
247 ((Context) this.lookup(relativeName.get(0))).bind(relativeName
248 .getSuffix(1), value);
249 } else
250 {
251 this.elements.put(relativeName.get(0), value);
252 fireEvent(new Event(NUMBER_CHANGED_EVENT, this, new Integer(
253 this.elements.size())));
254 fireEvent(new Event(CHILD_ADDED_EVENT, this, value));
255 fireContextEvent(true, this.getNameInNamespace()
256 + syntax.getProperty("jndi.syntax.separator")
257 + relativeName, value);
258 }
259 }
260
261 /***
262 * @see javax.naming.Context#bind(String, Object)
263 */
264 public void bind(final String name, final Object value)
265 throws NamingException
266 {
267 bind(this.parser.parse(name), value);
268 }
269
270 /***
271 * @see javax.naming.Context#rebind(Name, Object)
272 */
273 public void rebind(final Name name, final Object value)
274 throws NamingException
275 {
276 this.bind(name, value);
277 }
278
279 /***
280 * @see javax.naming.Context#rebind(String, Object)
281 */
282 public void rebind(final String name, final Object value)
283 throws NamingException
284 {
285 this.bind(name, value);
286 }
287
288 /***
289 * @see javax.naming.Context#unbind(Name)
290 */
291 public synchronized void unbind(final Name name) throws NamingException
292 {
293 if (isRootForwardable(name))
294 {
295 getRoot().unbind(name);
296 return;
297 }
298 Name relativeName = makeRelative(name);
299 if (relativeName.size() > 1)
300 {
301 ((Context) this.lookup(relativeName.get(0))).unbind(relativeName
302 .getSuffix(1));
303 } else
304 {
305 Object old = this.elements.get(relativeName.get(0));
306 this.elements.remove(relativeName.get(0));
307 fireEvent(new Event(NUMBER_CHANGED_EVENT, this, new Integer(
308 this.elements.size())));
309 fireEvent(new Event(CHILD_REMOVED_EVENT, this, old));
310 fireContextEvent(false, this.getNameInNamespace()
311 + syntax.getProperty("jndi.syntax.separator")
312 + relativeName, old);
313 }
314 }
315
316 /***
317 * @see javax.naming.Context#unbind(String)
318 */
319 public void unbind(final String name) throws NamingException
320 {
321 unbind(this.parser.parse(name));
322 }
323
324 /***
325 * @see javax.naming.Context#rename(Name, Name)
326 */
327 public void rename(final Name nameOld, final Name nameNew)
328 throws NamingException
329 {
330 rename(nameOld.toString(), nameNew.toString());
331 }
332
333 /***
334 * @see javax.naming.Context#rename(String, String)
335 */
336 public synchronized void rename(final String nameOld, final String nameNew)
337 throws NamingException
338 {
339 if (!this.elements.containsKey(nameOld))
340 {
341 throw new NamingException("Old name not found. Rename"
342 + " operation canceled.");
343 }
344 Object value = this.elements.get(nameOld);
345 this.elements.remove(nameOld);
346 this.elements.put(nameNew, value);
347 }
348
349 /***
350 * @see javax.naming.Context#list(Name)
351 */
352 public NamingEnumeration list(final Name name)
353 {
354 return list(name.toString());
355 }
356
357 /***
358 * @see javax.naming.Context#list(String)
359 */
360 public NamingEnumeration list(final String name)
361 {
362 if (name == null)
363 {
364 Logger.info(this, "list", "name==null");
365 }
366 return new NamingList(true);
367 }
368
369 /***
370 * @see javax.naming.Context#listBindings(Name)
371 */
372 public NamingEnumeration listBindings(final Name name)
373 {
374 if (name == null)
375 {
376 Logger.info(this, "listBindings", "name==null");
377 }
378 return new NamingList(false);
379 }
380
381 /***
382 * @see javax.naming.Context#listBindings(String)
383 */
384 public NamingEnumeration listBindings(final String name)
385 {
386 if (name == null)
387 {
388 Logger.info(this, "listBindings", "name==null");
389 }
390 return new NamingList(false);
391 }
392
393 /***
394 * @see javax.naming.Context#destroySubcontext(Name)
395 */
396 public void destroySubcontext(final Name name) throws NamingException
397 {
398 this.unbind(name);
399 }
400
401 /***
402 * @see javax.naming.Context#destroySubcontext(String)
403 */
404 public void destroySubcontext(final String name) throws NamingException
405 {
406 this.unbind(name);
407 }
408
409 /***
410 * @see javax.naming.Context#createSubcontext(Name)
411 */
412 public synchronized Context createSubcontext(final Name name)
413 throws NamingException
414 {
415 if (name.size() == 1)
416 {
417 String subName = name.get(0);
418 Context newContext = new JVMContext(this, subName);
419 this.bind(subName, newContext);
420 return newContext;
421 }
422 Context c = (Context) this.lookup(name.get(0));
423 return c.createSubcontext(name.getSuffix(1));
424 }
425
426 /***
427 * @see javax.naming.Context#createSubcontext(String)
428 */
429 public Context createSubcontext(final String arg0) throws NamingException
430 {
431 return createSubcontext(this.parser.parse(arg0));
432 }
433
434 /***
435 * @see javax.naming.Context#lookupLink(Name)
436 */
437 public Object lookupLink(final Name name)
438 {
439 return this.elements.get(name.toString());
440 }
441
442 /***
443 * @see javax.naming.Context#lookupLink(String)
444 */
445 public Object lookupLink(final String name) throws NamingException
446 {
447 return lookup(name);
448 }
449
450 /***
451 * @see javax.naming.Context#getNameParser(Name)
452 */
453 public NameParser getNameParser(final Name name)
454 {
455 if (name == null)
456 {
457 Logger.info(this, "getNameParser", "name==null");
458 }
459 return this.parser;
460 }
461
462 /***
463 * @see javax.naming.Context#getNameParser(String)
464 */
465 public NameParser getNameParser(final String name)
466 {
467 if (name == null)
468 {
469 Logger.info(this, "getNameParser", "name==null");
470 }
471 return this.parser;
472 }
473
474 /***
475 * @see javax.naming.Context#composeName(Name, Name)
476 */
477 public Name composeName(final Name arg0, final Name arg1)
478 throws NamingException
479 {
480 throw new NamingException("composeName " + arg0 + ", " + arg1
481 + " is not supported.");
482 }
483
484 /***
485 * @see javax.naming.Context#composeName(String, String)
486 */
487 public String composeName(final String arg0, final String arg1)
488 throws NamingException
489 {
490 throw new NamingException("composeName " + arg0 + ", " + arg1
491 + " is not supported.");
492 }
493
494 /***
495 * @see javax.naming.Context#addToEnvironment(String, Object)
496 */
497 public Object addToEnvironment(final String arg0, final Object arg1)
498 throws NamingException
499 {
500 throw new NamingException("addToEnvironment " + arg0 + ", " + arg1
501 + " is not supported.");
502 }
503
504 /***
505 * @see javax.naming.Context#removeFromEnvironment(String)
506 */
507 public Object removeFromEnvironment(final String arg0)
508 throws NamingException
509 {
510 throw new NamingException("removeFromEnvironment " + arg0
511 + " is not supported.");
512 }
513
514 /***
515 * @see javax.naming.Context#getEnvironment()
516 */
517 public Hashtable getEnvironment() throws NamingException
518 {
519 throw new NamingException("Not supported.");
520 }
521
522 /***
523 * @see javax.naming.Context#close()
524 */
525 public void close()
526 {
527
528 }
529
530 /***
531 * @see javax.naming.Context#getNameInNamespace()
532 */
533 public synchronized String getNameInNamespace() throws NamingException
534 {
535 if (this.parent != null)
536 {
537 return (this.parent.getNameInNamespace()
538 + syntax.get("jndi.syntax.separator") + this.atomicName);
539 }
540 return this.atomicName;
541 }
542
543 /***
544 * @see javax.naming.event.EventContext #addNamingListener(Name, int,
545 * NamingListener)
546 */
547 public void addNamingListener(final Name target, final int scope,
548 final NamingListener l)
549 {
550 this.eventListeners
551 .add(new EventContextListenerRecord(target, scope, l));
552 }
553
554 /***
555 * @see javax.naming.event.EventContext #addNamingListener(String, int,
556 * NamingListener)
557 */
558 public void addNamingListener(final String target, final int scope,
559 final NamingListener l) throws NamingException
560 {
561 addNamingListener(this.parser.parse(target), scope, l);
562 }
563
564 /***
565 * @see javax.naming.event.EventContext
566 * #removeNamingListener(NamingListener)
567 */
568 public synchronized void removeNamingListener(final NamingListener l)
569 {
570 EventContextListenerRecord removable = null;
571 for (Iterator i = this.eventListeners.iterator(); i.hasNext();)
572 {
573 EventContextListenerRecord current = (EventContextListenerRecord) i
574 .next();
575 if (current.getListener().equals(l))
576 {
577 removable = current;
578 break;
579 }
580 }
581 if (removable != null)
582 {
583 this.eventListeners.remove(removable);
584 }
585 }
586
587 /***
588 * @see javax.naming.event.EventContext#targetMustExist()
589 */
590 public boolean targetMustExist()
591 {
592 return false;
593 }
594
595 /***
596 * fires a contextEvent
597 *
598 * @param isAddition addition
599 * @param name the name
600 * @param value the value
601 * @throws NamingException on failure
602 */
603 private void fireContextEvent(final boolean isAddition, final String name,
604 final Object value) throws NamingException
605 {
606 fireContextEvent(isAddition, this.parser.parse(name), value);
607 }
608
609 /***
610 * fires a contextEvent
611 *
612 * @param isAddition addition
613 * @param name the name
614 * @param value the value
615 */
616 private synchronized void fireContextEvent(final boolean isAddition,
617 final Name name, final Object value)
618 {
619 for (Iterator i = this.eventListeners.iterator(); i.hasNext();)
620 {
621 EventContextListenerRecord record = (EventContextListenerRecord) i
622 .next();
623 int scope = record.getScope();
624 NamingEvent namingEvent = null;
625 if (isAddition)
626 {
627 namingEvent = new NamingEvent(this, NamingEvent.OBJECT_ADDED,
628 new Binding(name.toString(), value), null, null);
629 } else
630 {
631 namingEvent = new NamingEvent(this, NamingEvent.OBJECT_REMOVED,
632 null, new Binding(name.toString(), value), null);
633 }
634 if (name.equals(record.getTarget())
635 || scope == EventContext.SUBTREE_SCOPE)
636 {
637 namingEvent.dispatch(record.getListener());
638 continue;
639 }
640 if (scope == EventContext.ONELEVEL_SCOPE)
641 {
642
643 if (record.getTarget().size() == 1)
644 {
645 namingEvent.dispatch(record.getListener());
646 }
647 continue;
648 }
649 }
650 }
651
652 /***
653 * @see java.lang.Object#toString()
654 */
655 public String toString()
656 {
657 try
658 {
659 return "JVMContext: " + this.getNameInNamespace() + " ";
660 } catch (Exception exception)
661 {
662 return super.toString();
663 }
664 }
665
666 /***
667 * The EventContextListenerRecord
668 */
669 private class EventContextListenerRecord
670 {
671 /*** target name to which a subscription is made */
672 private Name target;
673
674 /*** the scope */
675 private int scope;
676
677 /*** the listener */
678 private NamingListener listener;
679
680 /***
681 * constructs a new EventContextListenerRecord
682 *
683 * @param target the target
684 * @param scope the scope
685 * @param listener the listener
686 */
687 public EventContextListenerRecord(final Name target, final int scope,
688 final NamingListener listener)
689 {
690 this.target = target;
691 this.scope = scope;
692 this.listener = listener;
693 }
694
695 /***
696 * returns the listener
697 *
698 * @return NamingListener listener
699 */
700 public NamingListener getListener()
701 {
702 return this.listener;
703 }
704
705 /***
706 * gets scope
707 *
708 * @return Returns the scope.
709 */
710 public int getScope()
711 {
712 return this.scope;
713 }
714
715 /***
716 * gets target
717 *
718 * @return Returns the target.
719 */
720 public Name getTarget()
721 {
722 return this.target;
723 }
724
725 }
726
727 /***
728 * The NamingList class
729 */
730 private class NamingList extends ArrayList implements NamingEnumeration
731 {
732 /*** the iterator */
733 private Iterator myIterator = null;
734
735 /***
736 * constructs a new NamingList
737 *
738 * @param classList isClassList
739 */
740 public NamingList(final boolean classList)
741 {
742 Set keys = JVMContext.this.elements.keySet();
743 for (Iterator i = keys.iterator(); i.hasNext();)
744 {
745 String currentKey = (String) i.next();
746 if (classList)
747 {
748 this.add(new NameClassPair(currentKey,
749 JVMContext.this.elements.get(currentKey).getClass()
750 .toString()));
751 } else
752 {
753 this.add(new Binding(currentKey, JVMContext.this.elements
754 .get(currentKey)));
755 }
756 }
757 }
758
759 /***
760 * @see javax.naming.NamingEnumeration#close()
761 */
762 public void close()
763 {
764 this.myIterator = null;
765 }
766
767 /***
768 * @see java.util.Enumeration#hasMoreElements()
769 */
770 public boolean hasMoreElements()
771 {
772 if (this.myIterator == null)
773 {
774 this.myIterator = iterator();
775 }
776 boolean hasNext = this.myIterator.hasNext();
777 if (!hasNext)
778 {
779 this.myIterator = null;
780 return false;
781 }
782 return true;
783 }
784
785 /***
786 * @see java.util.Enumeration#nextElement()
787 */
788 public Object nextElement()
789 {
790 if (this.myIterator == null)
791 {
792 this.myIterator = iterator();
793 }
794 return this.myIterator.next();
795 }
796
797 /***
798 * @see javax.naming.NamingEnumeration#hasMore()
799 */
800 public boolean hasMore()
801 {
802 return hasMoreElements();
803 }
804
805 /***
806 * @see javax.naming.NamingEnumeration#next()
807 */
808 public Object next()
809 {
810 return nextElement();
811 }
812 }
813
814 /***
815 * A default name parser
816 */
817 private class MyParser implements NameParser, Serializable
818 {
819 /*** the syntax */
820 private Properties syntax = null;
821
822 /***
823 * constructs a new MyParser
824 *
825 * @param syntax the syntax properties
826 */
827 public MyParser(final Properties syntax)
828 {
829 this.syntax = syntax;
830 }
831
832 /***
833 * @see javax.naming.NameParser#parse(String)
834 */
835 public Name parse(final String name) throws NamingException
836 {
837 Name result = new CompoundName(name, this.syntax);
838 if (result.size() > 0 && result.get(0).equals(".")
839 && JVMContext.this.parent != null)
840 {
841 result = result.getSuffix(1);
842 }
843 return result;
844 }
845 }
846 }