View Javadoc

1   /*
2    * @(#)JVMContext.java Feb 1, 2003
3    * 
4    * Copyright (c) 2003 Delft University of Technology Jaffalaan 5, 2628 BX Delft,
5    * the Netherlands All rights reserved.
6    * 
7    * This software is proprietary information of Delft University of Technology
8    * The code is published under the General Public License
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 		// Handle absolute path
193 		if (isRootForwardable(name))
194 		{
195 			return getRoot().lookup(name);
196 		}
197 
198 		// Handle root context lookup
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 		// Check and handle delegation
211 		if (relativeName.size() > 1)
212 		{
213 			return ((Context) lookup(relativeName.get(0))).lookup(relativeName
214 					.getSuffix(1));
215 		}
216 
217 		// Lookup locally
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 		//We don't do anything on close
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 				// (Wrong) assumption that this is the root context
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 }