View Javadoc

1   /*
2    * @(#) GameLeaderInteractionLayer.java Nov 9, 2004
3    * 
4    * Copyright (c) 2003-2005 Delft University of Technology, Jaffalaan 5, 2628 BX
5    * Delft, the Netherlands. All rights reserved.
6    * 
7    * See for project information <a href="http://www.simulation.tudelft.nl/">
8    * www.simulation.tudelft.nl </a>.
9    * 
10   * The source code and binary code of this software is proprietary information
11   * of Delft University of Technology.
12   */
13  package org.gscg.gameleader.interactionlayer;
14  
15  import java.awt.Dimension;
16  import java.io.FileOutputStream;
17  import java.io.IOException;
18  import java.io.ObjectOutputStream;
19  import java.io.Serializable;
20  import java.rmi.RemoteException;
21  import java.rmi.server.UnicastRemoteObject;
22  import java.text.DateFormat;
23  import java.util.ArrayList;
24  import java.util.Calendar;
25  import java.util.Collections;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Set;
32  import java.util.SortedSet;
33  import java.util.Timer;
34  import java.util.TimerTask;
35  import java.util.TreeSet;
36  
37  import javax.naming.Binding;
38  import javax.naming.NamingEnumeration;
39  import javax.naming.event.EventContext;
40  import javax.naming.event.NamespaceChangeListener;
41  import javax.naming.event.NamingEvent;
42  import javax.naming.event.NamingExceptionEvent;
43  
44  import nl.tudelft.simulation.actor.ActorInterface;
45  import nl.tudelft.simulation.dsol.SimRuntimeException;
46  import nl.tudelft.simulation.dsol.animation.D2.ImageRenderable;
47  import nl.tudelft.simulation.dsol.animation.D2.Renderable2DInterface;
48  import nl.tudelft.simulation.dsol.animation.D2.SingleImageRenderable;
49  import nl.tudelft.simulation.dsol.experiment.Experiment;
50  import nl.tudelft.simulation.dsol.experiment.Replication;
51  import nl.tudelft.simulation.dsol.experiment.TimeUnit;
52  import nl.tudelft.simulation.dsol.experiment.TimeUnitInterface;
53  import nl.tudelft.simulation.dsol.simulators.AnimatorInterface;
54  import nl.tudelft.simulation.dsol.simulators.DESSSimulatorInterface;
55  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
56  import nl.tudelft.simulation.event.Event;
57  import nl.tudelft.simulation.event.EventInterface;
58  import nl.tudelft.simulation.event.EventListenerInterface;
59  import nl.tudelft.simulation.event.EventType;
60  import nl.tudelft.simulation.event.remote.RemoteEventListenerInterface;
61  import nl.tudelft.simulation.language.d3.DirectedPoint;
62  import nl.tudelft.simulation.language.swing.SwingWorker;
63  import nl.tudelft.simulation.logger.Logger;
64  import nl.tudelft.simulation.naming.InitialEventContext;
65  import nl.tudelft.simulation.supplychain.actor.SupplyChainActor;
66  import nl.tudelft.simulation.supplychain.animation.ContentAnimation;
67  
68  import org.gscg.common.GameAnimator;
69  import org.gscg.common.gui.exceptions.ReceivedUnknownEventException;
70  import org.gscg.common.interactionlayer.AnnounceInterface;
71  import org.gscg.common.interactionlayer.ThreadedEventProducer;
72  import org.gscg.common.interactionlayer.dataobjects.ProgressionData;
73  import org.gscg.common.interactionlayer.location.YellowPage;
74  import org.gscg.common.interactionlayer.messaging.ScenarioText;
75  import org.gscg.common.interactionlayer.messaging.SocialMessaging;
76  import org.gscg.common.interactionlayer.timecontrol.CurrentRowOrColumnNumber;
77  import org.gscg.common.interactionlayer.timecontrol.GlobalProgressDateAndTime;
78  import org.gscg.common.interactionlayer.timecontrol.GlobalRowOrColumnNumber;
79  import org.gscg.common.interactionlayer.timecontrol.ProgressDateAndTime;
80  import org.gscg.game.GameGlobalData;
81  import org.gscg.gameactors.GameMarket;
82  import org.gscg.gameleader.animation2D.GisActorAnimation;
83  import org.gscg.gameleader.animation2D.GisActorImageDataElement;
84  import org.gscg.gameleader.animation2D.GisGraphicsRenderable;
85  import org.gscg.gameleader.animation2D.GisRenderable2D;
86  import org.gscg.gameleader.animation2D.ImageDataElement;
87  import org.gscg.gameleader.animation2D.ImageDataInterface;
88  import org.gscg.gameleader.animation2D.SingleImageDataElement;
89  import org.gscg.gameleader.animation2D.mouse.IntrospectionData;
90  import org.gscg.gameleader.interactionlayer.experiment.ExperimentParser;
91  import org.gscg.gameleader.interactionlayer.experiment.ExperimentParsingThread;
92  import org.gscg.gameleader.interactionlayer.statistics.DemandStatistics;
93  import org.gscg.gameleader.interactionlayer.util.EventTypeComparator;
94  import org.gscg.gameleader.interactionlayer.util.IntrospectionUtil;
95  import org.jdom.Element;
96  
97  /***
98   * The game leader interaction layer is the layer between server-side game
99   * control logic and a client-side game administrator application.
100  * <p>
101  * Copyright (c) 2003-2005 Delft University of Technology, Jaffalaan 5, 2628 BX
102  * Delft, the Netherlands. All rights reserved.
103  * 
104  * See for project information <a href="http://www.simulation.tudelft.nl/">
105  * www.simulation.tudelft.nl </a>.
106  * 
107  * The source code and binary code of this software is proprietary information
108  * of Delft University of Technology.
109  * 
110  * @author <a
111  *         href="http://www.tbm.tudelft.nl/webstaf/stijnh/index.htm">Stijn-Pieter
112  *         van Houten </a>
113  * @version $Revision: 1.2 $ $Date: 2005/08/03 08:52:50 $
114  * @since 1.0.0 <br>
115  */
116 public class GameLeaderInteractionLayer extends ThreadedEventProducer implements
117 		RemoteInteractionLayerInterface, NamespaceChangeListener
118 {
119 	/*** the serial version uid */
120 	private static final long serialVersionUID = 13L;
121 
122 	/*** the object added event */
123 	public static final EventType OBJECT_ADDED_EVENT = new EventType(
124 			"OBJECT_ADDED_EVENT");
125 
126 	/*** the object removed event */
127 	public static final EventType OBJECT_REMOVED_EVENT = new EventType(
128 			"OBJECT_REMOVED_EVENT");
129 
130 	/*** the animation changed event */
131 	public static final EventType ANIMATION_UPDATE_EVENT = new EventType(
132 			"ANIMATION_UPDATE_EVENT");
133 
134 	/*** indicates whether this class has been deserialized */
135 	private static boolean deserialized = false;
136 
137 	/*** indicates whether the bindings have already been looked up */
138 	private static boolean bindingsLookedUp = false;
139 
140 	/*** the simulator events types for the interaction layer to subscribe to */
141 	private EventType[] simulatorEventTypes = {
142 			AnimatorInterface.ANIMATION_DELAY_CHANGED_EVENT,
143 			AnimatorInterface.UPDATE_ANIMATION_EVENT,
144 			SimulatorInterface.STOP_EVENT, SimulatorInterface.START_EVENT};
145 
146 	/*** the experiment */
147 	private Experiment experiment = null;
148 
149 	/*** the client side event listeners */
150 	private transient List clients = new ArrayList();
151 
152 	/*** indicates whether the client side game administrator is online */
153 	private int numberLoggedIn = 0;
154 
155 	/*** the id of the interaction layer */
156 	private String id = null;
157 
158 	/*** the event types this layer is subscribed to */
159 	private Set eventsSentByClient = new HashSet();
160 
161 	/*** the elements of the animation panel */
162 	private transient Set elements = Collections.synchronizedSet(new HashSet());
163 
164 	/*** contains the changed elements during an animation refreshment */
165 	private transient Map changedElements = Collections
166 			.synchronizedMap(new HashMap());
167 
168 	/*** the map with data elements of the images */
169 	private transient Map imageDataElements = Collections
170 			.synchronizedMap(new HashMap());
171 
172 	/*** a map with a key - source mappings */
173 	private transient Map keySourceMap = Collections
174 			.synchronizedMap(new HashMap());
175 
176 	/*** maps a remote event listener to its refresh rate */
177 	private transient Map listenersRefreshRates = Collections
178 			.synchronizedMap(new HashMap());
179 
180 	/*** maps a remote event listener to its timer */
181 	private transient Map listenersTimers = Collections
182 			.synchronizedMap(new HashMap());
183 
184 	/*** the map with actor keys */
185 	private transient Map actorKeys = new HashMap();
186 
187 	/*** the timed animation refresher */
188 	private TimedAnimationRefresher timedAnimationRefresher = null;
189 
190 	/*** we use this for persistency reasons */
191 	private int numberOfTreatment = 0;
192 
193 	/*** we use this for persistency reasons */
194 	private int numberOfReplication = 0;
195 
196 	/*** we use this for persistency reasons */
197 	private String run = null;
198 
199 	/*** maps event types and corresponding announce objects */
200 	private HashMap announceObjects = new HashMap();
201 
202 	/*** the event types this layer is subscribed to */
203 	private Set statisticEventTypes = new TreeSet(new EventTypeComparator());
204 
205 	/*** the global supply chain data */
206 	private GameGlobalData globalSupplyChainData = null;
207 
208 	/*** the scenario text */
209 	private ScenarioText scenarioText = null;
210 
211 	/*** the social messaging */
212 	private SocialMessaging socialMessaging = null;
213 
214 	/*** the yellowpage */
215 	private YellowPage yellowPage = null;
216 
217 	/***
218 	 * constructs a new GameLeaderInteractionLayer
219 	 * 
220 	 * @param id the id of the game administrator
221 	 */
222 	public GameLeaderInteractionLayer(final String id)
223 	{
224 		super();
225 		this.id = id;
226 		try
227 		{
228 			UnicastRemoteObject.exportObject(this);
229 		} catch (RemoteException remoteException)
230 		{
231 			Logger.severe(this, "<init>", remoteException);
232 		}
233 
234 		try
235 		{
236 			// we bind ourselves, may throw a naming exception
237 			new InitialEventContext().bind(this.id, this);
238 		} catch (Exception exception)
239 		{
240 			Logger.severe(this, "<init>", exception);
241 		}
242 	}
243 
244 	/***
245 	 * @see nl.tudelft.simulation.event.EventListenerInterface#notify(nl.tudelft.simulation.event.EventInterface)
246 	 */
247 	public void notify(final EventInterface event) throws RemoteException
248 	{
249 		if (this.numberLoggedIn > 0)
250 		{
251 			if (event.getType()
252 					.equals(AnimatorInterface.UPDATE_ANIMATION_EVENT))
253 			{
254 				synchronized (this.elements)
255 				{
256 					for (Iterator i = this.elements.iterator(); i.hasNext();)
257 					{
258 						Renderable2DInterface renderable = ((Renderable2DInterface) i
259 								.next());
260 						ImageDataInterface element = (ImageDataInterface) this.imageDataElements
261 								.get(renderable.getSource().toString()
262 										+ renderable.getSource().hashCode());
263 						try
264 						{
265 							if (element != null)
266 							{
267 								if (!renderable.getSource().getLocation()
268 										.equals(
269 												new DirectedPoint(element
270 														.getPoint()[0], element
271 														.getPoint()[1], element
272 														.getPoint()[2])))
273 								{
274 									synchronized (this.changedElements)
275 									{
276 										// we have a different location
277 										element.setPoints(new double[]{
278 												renderable.getSource()
279 														.getLocation().x,
280 												renderable.getSource()
281 														.getLocation().y,
282 												renderable.getSource()
283 														.getLocation().z});
284 										// for each of the remote event
285 										// listeners we
286 										// put the changed element in the array
287 										// of
288 										// changed elements
289 										for (Iterator it = this.changedElements
290 												.keySet().iterator(); it
291 												.hasNext();)
292 										{
293 											((List) this.changedElements.get(it
294 													.next())).add(element);
295 										}
296 									}
297 								}
298 							}
299 						} catch (NullPointerException nullPointerException)
300 						{
301 							Logger.severe(this, "notify", nullPointerException);
302 						}
303 					}
304 					return;
305 				}
306 			}
307 			super.fireEvent(event);
308 		}
309 	}
310 
311 	/***
312 	 * Method addStatisticEventType adds an event to a set with event types only
313 	 * used for statistical updates. Upon initialization, a remote game leader
314 	 * graphical user interface asks for this set. EventTypes which are added to
315 	 * this set should follow the following convention ACTORROLE_ + description
316 	 * of eventtype. E.g. CUSTOMER_DEMAND_CHANGED_EVENT.
317 	 * 
318 	 * @param eventType the event type to add
319 	 */
320 	public void addStatisticEventType(final EventType eventType)
321 	{
322 		if (!this.statisticEventTypes.contains(eventType))
323 		{
324 			this.statisticEventTypes.add(eventType);
325 		}
326 	}
327 
328 	/***
329 	 * @see org.gscg.common.interactionlayer.GlobalInteractionLayerInterface#getCache(java.lang.Object,
330 	 *      nl.tudelft.simulation.event.EventType, boolean)
331 	 */
332 	public void getCache(final Object remoteEventListener,
333 			final EventType eventType, final boolean cache)
334 			throws RemoteException
335 	{
336 
337 		if (cache)
338 		{
339 			if (eventType.equals(YellowPage.UPDATE_ACTORS))
340 			{
341 				// pass the locations
342 				this.globalSupplyChainData.getYellowPage().update(true, this);
343 				return;
344 			}
345 			// since it is possible that multiple game leaders login, we need
346 			// only to notify the game leader that is 'new', therefore, instead
347 			// of firing an event, we directly notify the listener
348 			if (eventType
349 					.equals(SocialMessaging.UPDATE_RECEIVED_SOCIAL_MESSAGES))
350 			{
351 				for (int i = 0; i < this.socialMessaging
352 						.getReceivedSocialMessages().size(); i++)
353 				{
354 					((RemoteEventListenerInterface) remoteEventListener)
355 							.notify(new Event(
356 									SocialMessaging.UPDATE_RECEIVED_SOCIAL_MESSAGE,
357 									this, this.socialMessaging
358 											.getReceivedSocialMessages().get(i)));
359 				}
360 				return;
361 			}
362 			if (eventType.equals(SocialMessaging.UPDATE_SENT_SOCIAL_MESSAGES))
363 			{
364 				for (int i = 0; i < this.socialMessaging
365 						.getSentSocialMessages().size(); i++)
366 				{
367 					((RemoteEventListenerInterface) remoteEventListener)
368 							.notify(new Event(
369 									SocialMessaging.UPDATE_SENT_SOCIAL_MESSAGE,
370 									this, this.socialMessaging
371 											.getSentSocialMessages().get(i)));
372 				}
373 				return;
374 			}
375 			if (eventType.equals(SimulatorInterface.TIME_CHANGED_EVENT))
376 			{
377 				ProgressionData data = new ProgressionData(
378 						GlobalProgressDateAndTime.getDate(),
379 						GlobalProgressDateAndTime.getTime(),
380 						GlobalProgressDateAndTime.getStopTime(),
381 						GlobalProgressDateAndTime.getProgress(),
382 						GlobalProgressDateAndTime.getProgressTime());
383 				((RemoteEventListenerInterface) remoteEventListener)
384 						.notify(new Event(
385 								SimulatorInterface.TIME_CHANGED_EVENT, this,
386 								data));
387 				return;
388 			}
389 			new ReceivedUnknownEventException(this, "getCache", eventType);
390 		} else
391 		{
392 			// no announce, for example for updating statistics
393 			if (this.announceObjects.containsKey(eventType.toString()))
394 			{
395 				AnnounceInterface object = (AnnounceInterface) this.announceObjects
396 						.get(eventType.toString());
397 				object.announce(eventType, false);
398 				return;
399 			}
400 			new ReceivedUnknownEventException(this, "getCache", eventType);
401 		}
402 	}
403 
404 	/***
405 	 * @see org.gscg.common.interactionlayer.GlobalInteractionLayerInterface#login(nl.tudelft.simulation.event.remote.RemoteEventListenerInterface)
406 	 */
407 	public synchronized boolean login(final RemoteEventListenerInterface client)
408 	{
409 		if (!GameLeaderInteractionLayer.bindingsLookedUp)
410 		{
411 			GameLeaderInteractionLayer.bindingsLookedUp = true;
412 			this.resolveBindings(GameLeaderInteractionLayer.deserialized);
413 		}
414 		boolean loggedIn = false;
415 		if (this.numberLoggedIn > 0)
416 		{
417 			loggedIn = true;
418 		}
419 		this.numberLoggedIn++;
420 		this.clients.add(client);
421 		this.globalSupplyChainData.getYellowPage()
422 				.updateInteractiveOnlineStatus(this, true, false);
423 		return loggedIn;
424 	}
425 
426 	/***
427 	 * @see org.gscg.gameleader.interactionlayer.experiment.ExperimentInterface#parseExperiment(org.jdom.Element)
428 	 */
429 	public void parseExperiment(final Element experimentElement)
430 			throws RemoteException
431 	{
432 		if (this.experiment != null)
433 		{
434 			try
435 			{
436 				// we stop any running experiment
437 				if (!this.experiment.getSimulator().isRunning())
438 				{
439 					this.experiment.getSimulator().stop();
440 				}
441 			} catch (Exception exception)
442 			{
443 				Logger.severe(this, "parseExperiment", exception);
444 			}
445 		}
446 
447 		try
448 		{
449 			this.experiment = ExperimentParser
450 					.parseExperiment(experimentElement);
451 
452 			if (this.experiment != null)
453 			{
454 				this.experiment.setSimulator(new GameAnimator());
455 				this.experiment.start();
456 				this.experiment.getSimulator().stop();
457 
458 				// subscribe to simulator events
459 				for (int i = 0; i < this.simulatorEventTypes.length; i++)
460 				{
461 					this.experiment.getSimulator().addListener(this,
462 							this.simulatorEventTypes[i], false);
463 				}
464 
465 				// we clean our animation history
466 				this.elements.clear();
467 				this.changedElements.clear();
468 				this.imageDataElements.clear();
469 				this.keySourceMap.clear();
470 				this.listenersRefreshRates.clear();
471 				this.listenersTimers.clear();
472 
473 				// create the timer for the animation
474 				this.timedAnimationRefresher = new TimedAnimationRefresher(this);
475 
476 				// we set our experiment related numbers
477 				this.numberOfTreatment = this.experiment.getSimulator()
478 						.getReplication().getRunControl().getTreatment()
479 						.getNumber();
480 				this.numberOfReplication = this.experiment.getSimulator()
481 						.getReplication().getNumber();
482 				this.run = this.experiment.getRun();
483 
484 				// lookup the global supply chain data
485 				this.globalSupplyChainData = (GameGlobalData) new InitialEventContext()
486 						.lookup(GameGlobalData.CONTEXT_NAME);
487 
488 				// listen to global time control
489 				new ProgressDateAndTime(this);
490 				new CurrentRowOrColumnNumber(this);
491 
492 				this.socialMessaging = new SocialMessaging(this,
493 						this.globalSupplyChainData.getYellowPage());
494 
495 				// the yellow page
496 				this.yellowPage = this.globalSupplyChainData.getYellowPage();
497 				this.yellowPage.addGameLeader(this);
498 
499 				// add actor to the location and name class
500 				this.yellowPage.addListener(this, YellowPage.UPDATE_ACTORS,
501 						false);
502 				this.globalSupplyChainData.getYellowPage().addListener(this,
503 						YellowPage.UPDATE_INTERACTIVE_PLAYER_STATUS, false);
504 
505 				// scenario text
506 				this.scenarioText = (ScenarioText) new InitialEventContext()
507 						.lookup(ScenarioText.CONTEXT_NAME);
508 				this.scenarioText.addInteractionLayer(this);
509 
510 
511 				// init statistics
512 				for (Iterator it = this.globalSupplyChainData
513 						.getSupplyChainActors().iterator(); it.hasNext();)
514 				{
515 					Object obj = it.next();
516 					if (obj instanceof GameMarket)
517 					{
518 						new DemandStatistics(this, (GameMarket) obj);
519 					}
520 				}
521 
522 				// we notify the client
523 				for (int i = 0; i < this.clients.size(); i++)
524 				{
525 					((RemoteEventListenerInterface) this.clients.get(i))
526 							.notify(new Event(
527 									ExperimentParsingThread.EXPERIMENT_PARSED_EVENT,
528 									this, null));
529 				}
530 				// TODO do we need to implement something for a regular save,
531 				// e.g. based on a timer task? We cannot schedule such a save on
532 				// the event list, since saving and exeuting an event requires
533 				// the same semaphore. --> deadlock....
534 			}
535 		} catch (Exception exception)
536 		{
537 			exception.printStackTrace();
538 		}
539 	}
540 
541 	/***
542 	 * @see nl.tudelft.simulation.dsol.simulators.SimulatorInterface#getSimulatorTime()
543 	 */
544 	public double getSimulatorTime() throws RemoteException
545 	{
546 		return this.experiment.getSimulator().getSimulatorTime();
547 	}
548 
549 	/***
550 	 * @see nl.tudelft.simulation.dsol.simulators.SimulatorInterface#getReplication()
551 	 */
552 	public Replication getReplication() throws RemoteException
553 	{
554 		return this.experiment.getTreatments()[0].getRunControl()
555 				.getReplications()[0];
556 	}
557 
558 	/***
559 	 * @see nl.tudelft.simulation.dsol.simulators.SimulatorInterface#initialize(nl.tudelft.simulation.dsol.experiment.Replication)
560 	 */
561 	public void initialize(final Replication replication)
562 			throws RemoteException, SimRuntimeException
563 	{
564 		this.experiment.getSimulator().initialize(replication);
565 	}
566 
567 	/***
568 	 * @see nl.tudelft.simulation.dsol.simulators.SimulatorInterface#isRunning()
569 	 */
570 	public boolean isRunning() throws RemoteException
571 	{
572 		if (this.experiment != null)
573 		{
574 			return this.experiment.getSimulator().isRunning();
575 		}
576 		return false;
577 	}
578 
579 	/***
580 	 * @see nl.tudelft.simulation.dsol.simulators.SimulatorInterface#start()
581 	 */
582 	public void start() throws RemoteException, SimRuntimeException
583 	{
584 		this.experiment.getSimulator().start();
585 	}
586 
587 	/***
588 	 * @see nl.tudelft.simulation.dsol.simulators.SimulatorInterface#step()
589 	 */
590 	public void step() throws RemoteException, SimRuntimeException
591 	{
592 		this.experiment.getSimulator().step();
593 	}
594 
595 	/***
596 	 * @see nl.tudelft.simulation.dsol.simulators.SimulatorInterface#stop()
597 	 */
598 	public void stop() throws RemoteException, SimRuntimeException
599 	{
600 		this.experiment.getSimulator().stop();
601 	}
602 
603 	/***
604 	 * @see nl.tudelft.simulation.event.EventProducerInterface#removeListener(nl.tudelft.simulation.event.EventListenerInterface,
605 	 *      nl.tudelft.simulation.event.EventType)
606 	 */
607 	public synchronized boolean removeListener(
608 			final EventListenerInterface listener, final EventType eventType)
609 	{
610 		SwingWorker worker = new SwingWorker()
611 		{
612 			/***
613 			 * @see nl.tudelft.simulation.language.swing.SwingWorker#construct()
614 			 */
615 			public Object construct()
616 			{
617 				Logger.info(this, "removeListener",
618 						"Listener removed for event type: " + eventType);
619 
620 				return null;
621 			}
622 		};
623 		worker.start();
624 
625 		// we assume that the game leader is offline
626 		if (eventType.equals(SimulatorInterface.TIME_CHANGED_EVENT))
627 		{
628 			this.numberLoggedIn--;
629 			if (this.clients.remove(listener))
630 			{
631 				Logger.info(this, "removeListener", "Game leader is offline");
632 			}
633 			if (this.numberLoggedIn == 0)
634 			{ // no more game leaders, set status to inactive
635 				this.yellowPage.updateInteractiveOnlineStatus(this, false,
636 						false);
637 			}
638 		}
639 		return super.removeListener(listener, eventType);
640 	}
641 
642 
643 	/***
644 	 * @return Returns the id
645 	 */
646 	public String getId()
647 	{
648 		return this.id;
649 	}
650 
651 	/***
652 	 * @see nl.tudelft.simulation.dsol.simulators.DESSSimulatorInterface#getTimeStep()
653 	 */
654 	public double getTimeStep() throws RemoteException
655 	{
656 		return ((DESSSimulatorInterface) this.experiment.getSimulator())
657 				.getTimeStep();
658 	}
659 
660 	/***
661 	 * @see nl.tudelft.simulation.dsol.simulators.DESSSimulatorInterface#setTimeStep(double)
662 	 */
663 	public void setTimeStep(final double arg0) throws RemoteException
664 	{
665 		((DESSSimulatorInterface) this.experiment.getSimulator())
666 				.setTimeStep(arg0);
667 	}
668 
669 	/***
670 	 * @see org.gscg.gameleader.interactionlayer.RemoteInteractionLayerInterface#getAnimationDelay()
671 	 */
672 	public long getAnimationDelay() throws RemoteException
673 	{
674 		return ((AnimatorInterface) this.experiment.getSimulator())
675 				.getAnimationDelay();
676 	}
677 
678 	/***
679 	 * @see org.gscg.gameleader.interactionlayer.RemoteInteractionLayerInterface#setAnimationDelay(long)
680 	 */
681 	public void setAnimationDelay(final long arg0) throws RemoteException
682 	{
683 		((AnimatorInterface) this.experiment.getSimulator())
684 				.setAnimationDelay(arg0);
685 	}
686 
687 	/***
688 	 * @see org.gscg.gameleader.interactionlayer.RemoteInteractionLayerInterface#getActorKeys()
689 	 */
690 	public Map getActorKeys() throws RemoteException
691 	{
692 		return this.actorKeys;
693 	}
694 
695 	/***
696 	 * @see org.gscg.common.interactionlayer.GlobalInteractionLayerInterface#getTotalNumberOfDays()
697 	 */
698 	public int getTotalNumberOfDays() throws RemoteException
699 	{
700 		return GlobalRowOrColumnNumber.getNumberOfDays();
701 	}
702 
703 	/***
704 	 * @see javax.naming.event.NamespaceChangeListener#objectAdded(javax.naming.event.NamingEvent)
705 	 */
706 	public void objectAdded(final NamingEvent namingEvent)
707 	{
708 		Renderable2DInterface element = null;
709 		ImageDataElement dataElement = null;
710 		try
711 		{
712 			element = (Renderable2DInterface) namingEvent.getNewBinding()
713 					.getObject();
714 			this.elements.add(element);
715 			SingleImageRenderable object = (SingleImageRenderable) element;
716 			String imagePath = null;
717 			if (ContentAnimation.class.isAssignableFrom(object.getSource()
718 					.getClass()))
719 			{
720 				imagePath = ((ContentAnimation) object.getSource())
721 						.getImageURLlName();
722 				String[] array = imagePath.split("!");
723 				if (array.length == 1)
724 				{
725 					array = imagePath.split("classes");
726 				}
727 				imagePath = array[1];
728 				dataElement = new SingleImageDataElement(element.getClass()
729 						.getName(), element.getSource().toString()
730 						+ element.getSource().hashCode(), imagePath, object
731 						.getSource().getLocation(), new Dimension(25, 25),
732 						object.isFlip(), object.getOrientation(), object
733 								.isRotate(), object.isScale(), object
734 								.isTranslate());
735 
736 				this.keySourceMap.put(element.getSource().toString()
737 						+ element.getSource().hashCode(),
738 						((ContentAnimation) element.getSource()).getContent());
739 			} else
740 			{
741 				if (SingleImageRenderable.class.isAssignableFrom(element
742 						.getClass()))
743 				{
744 					if (object.getImages() != null)
745 					{
746 						imagePath = object.getImages()[0].toString();
747 						String[] array = imagePath.split("!");
748 						if (array.length == 1)
749 						{
750 							array = imagePath.split("classes");
751 						}
752 
753 						// we create our data elements and add it to the list
754 						dataElement = new SingleImageDataElement(element
755 								.getClass().getName(), element.getSource()
756 								.toString()
757 								+ element.getSource().hashCode(), array[1],
758 								object.getSource().getLocation(),
759 								new Dimension(object.getImages()[0]
760 										.getIconWidth(), object.getImages()[0]
761 										.getIconHeight()), object.isFlip(),
762 								object.getOrientation(), object.isRotate(),
763 								object.isScale(), object.isTranslate());
764 					}
765 
766 					this.keySourceMap.put(element.getSource().toString()
767 							+ element.getSource().hashCode(), element
768 							.getSource());
769 				} else
770 				{
771 					Logger.severe(this, "objectAdded",
772 							"GameLeaderInteractionLayer object.getSource().getClass(): "
773 									+ object.getSource().getClass());
774 				}
775 			}
776 		} catch (Exception exception)
777 		{
778 			Logger.severe(this, "objectAdded", exception);
779 		}
780 
781 		if (dataElement != null)
782 		{
783 			this.imageDataElements.put(element.getSource().toString()
784 					+ element.getSource().hashCode(), dataElement);
785 			super.fireEvent(new Event(
786 					GameLeaderInteractionLayer.OBJECT_ADDED_EVENT, this,
787 					dataElement));
788 		}
789 	}
790 
791 	/***
792 	 * @see javax.naming.event.NamespaceChangeListener#objectRemoved(javax.naming.event.NamingEvent)
793 	 */
794 	public void objectRemoved(final NamingEvent namingEvent)
795 	{
796 		try
797 		{
798 			synchronized (this.elements)
799 			{ // we must synchronize
800 				this.elements.remove(namingEvent.getOldBinding().getObject());
801 
802 				ImageDataInterface element = (ImageDataInterface) this.imageDataElements
803 						.remove(((Renderable2DInterface) namingEvent
804 								.getOldBinding().getObject()).getSource()
805 								.toString()
806 								+ ((Renderable2DInterface) namingEvent
807 										.getOldBinding().getObject())
808 										.getSource().hashCode());
809 
810 				this.keySourceMap.remove(((Renderable2DInterface) namingEvent
811 						.getOldBinding().getObject()).getSource().toString()
812 						+ namingEvent.getOldBinding().getObject().hashCode());
813 
814 				synchronized (this.changedElements)
815 				{ // if the element exists in a changed list, we remove it
816 					if (element != null)
817 					{
818 						for (Iterator it = this.changedElements.keySet()
819 								.iterator(); it.hasNext();)
820 						{
821 							List list = (List) this.changedElements.get(it
822 									.next());
823 							list.remove(element);
824 						}
825 					}
826 				}
827 			}
828 			super.fireEvent(new Event(
829 					GameLeaderInteractionLayer.OBJECT_REMOVED_EVENT, this,
830 					new SingleImageDataElement("",
831 							((Renderable2DInterface) namingEvent
832 									.getOldBinding().getObject()).getSource()
833 									.toString()
834 									+ ((Renderable2DInterface) namingEvent
835 											.getOldBinding().getObject())
836 											.getSource().hashCode(), null,
837 							new DirectedPoint(0.0, 0.0, 0.0), null, false,
838 							ImageRenderable.CC, false, false, false)));
839 		} catch (Exception exception)
840 		{
841 			Logger.severe(this, "objectRemoved", exception);
842 		}
843 	}
844 
845 	/***
846 	 * @see javax.naming.event.NamespaceChangeListener#objectRenamed(javax.naming.event.NamingEvent)
847 	 */
848 	public void objectRenamed(final NamingEvent evt)
849 	{
850 		// nothing to do
851 	}
852 
853 	/***
854 	 * @see javax.naming.event.NamingListener#namingExceptionThrown(javax.naming.event.NamingExceptionEvent)
855 	 */
856 	public void namingExceptionThrown(final NamingExceptionEvent evt)
857 	{
858 		// nothing to do
859 	}
860 
861 	/***
862 	 * @see org.gscg.gameleader.interactionlayer.RemoteInteractionLayerInterface#getBindings()
863 	 */
864 	public Map getBindings() throws RemoteException
865 	{
866 		return this.imageDataElements;
867 	}
868 
869 
870 	/***
871 	 * @see org.gscg.gameleader.interactionlayer.RemoteInteractionLayerInterface#getIntrospectedObjectData(java.lang.String)
872 	 */
873 	public IntrospectionData getIntrospectedObjectData(final String key)
874 			throws RemoteException
875 	{
876 		return IntrospectionUtil.introspectObject(this.keySourceMap.get(key),
877 				key);
878 	}
879 
880 	/***
881 	 * @see org.gscg.gameleader.interactionlayer.RemoteInteractionLayerInterface#updateIntrospectedObject(org.gscg.gameleader.animation2D.mouse.IntrospectionData)
882 	 */
883 	public void updateIntrospectedObject(final IntrospectionData data)
884 			throws RemoteException
885 	{
886 		IntrospectionUtil.updateIntrospectedObject(this.keySourceMap.get(data
887 				.getKey()), data);
888 	}
889 
890 	/***
891 	 * @see org.gscg.gameleader.interactionlayer.RemoteInteractionLayerInterface#setAnimationRefreshDelay(int,
892 	 *      nl.tudelft.simulation.event.remote.RemoteEventListenerInterface)
893 	 */
894 	public void setAnimationRefreshDelay(final double times,
895 			final RemoteEventListenerInterface listener) throws RemoteException
896 	{
897 		synchronized (this.changedElements)
898 		{
899 			if (!this.listenersRefreshRates.containsKey(listener))
900 			{
901 				this.changedElements.put(listener, Collections
902 						.synchronizedList(new ArrayList()));
903 				this.listenersRefreshRates.put(listener, new Double(times));
904 
905 				// we schedule a task
906 				if (this.timedAnimationRefresher == null)
907 				{
908 					this.timedAnimationRefresher = new TimedAnimationRefresher(
909 							this);
910 				}
911 				this.timedAnimationRefresher.scheduleTask(listener, times);
912 			} else
913 			{
914 				this.listenersRefreshRates.put(listener, new Double(times));
915 			}
916 		}
917 	}
918 
919 	/***
920 	 * @see org.gscg.gameleader.interactionlayer.RemoteInteractionLayerInterface#removeAnimationRefreshDelayListener(nl.tudelft.simulation.event.remote.RemoteEventListenerInterface)
921 	 */
922 	public void removeAnimationRefreshDelayListener(
923 			final RemoteEventListenerInterface listener) throws RemoteException
924 	{
925 		synchronized (this.changedElements)
926 		{
927 			// there is an error, lets remove the listener and
928 			// all its objects belonging to him
929 			this.changedElements.remove(listener);
930 			this.listenersRefreshRates.remove(listener);
931 			this.listenersTimers.remove(listener);
932 		}
933 	}
934 
935 	/***
936 	 * @see org.gscg.gameleader.interactionlayer.RemoteInteractionLayerInterface#saveSimulation()
937 	 */
938 	public void saveSimulation() throws RemoteException
939 	{
940 		boolean running = false;
941 
942 		try
943 		{
944 			if (this.experiment.getSimulator().isRunning())
945 			{
946 				running = true;
947 				try
948 				{
949 					this.experiment.getSimulator().stop();
950 				} catch (Exception exception)
951 				{
952 					Logger.severe(this, "saveSimulation", exception);
953 				}
954 			}
955 		} catch (RemoteException remoteException)
956 		{
957 			remoteException.printStackTrace();
958 		}
959 
960 		try
961 		{
962 			SaveSimulationRunnable runnable = new SaveSimulationRunnable();
963 			Thread thread = new Thread(runnable, "Save Simulation Thread");
964 			thread.setPriority(Thread.MAX_PRIORITY);
965 
966 			thread.start();
967 			// we join, and therefore wait till the thread has finished or
968 			// died due to an exception
969 			thread.join();
970 		} catch (Throwable throwable)
971 		{
972 			Logger.severe(this, "saveSimulation", throwable);
973 		}
974 
975 		if (running)
976 		{
977 			try
978 			{
979 				this.experiment.getSimulator().start();
980 			} catch (Exception exception)
981 			{
982 				Logger.severe(this, "saveSimulation", exception);
983 			}
984 		}
985 	}
986 
987 	/***
988 	 * @see org.gscg.common.interactionlayer.GlobalInteractionLayerInterface#getStatisticEventTypes()
989 	 */
990 	public EventType[] getStatisticEventTypes()
991 	{
992 		return (EventType[]) this.statisticEventTypes
993 				.toArray(new EventType[this.statisticEventTypes.size()]);
994 	}
995 
996 	/***
997 	 * @see org.gscg.common.interactionlayer.GlobalInteractionLayerInterface#addEventTypeSentByClient(nl.tudelft.simulation.event.EventType)
998 	 */
999 	public void addEventTypeSentByClient(final EventType eventType)
1000 	{
1001 		if (!this.eventsSentByClient.contains(eventType))
1002 		{
1003 			this.eventsSentByClient.add((eventType));
1004 		}
1005 	}
1006 
1007 	/***
1008 	 * @see org.gscg.common.interactionlayer.GlobalInteractionLayerInterface#addEventTypeToAnnounceList(nl.tudelft.simulation.event.EventType,
1009 	 *      org.gscg.common.interactionlayer.AnnounceInterface)
1010 	 */
1011 	public void addEventTypeToAnnounceList(final EventType eventType,
1012 			final AnnounceInterface announceObject)
1013 	{
1014 		String key = eventType.toString();
1015 		this.announceObjects.put(key, announceObject);
1016 	}
1017 
1018 	//
1019 	// private methods
1020 	//
1021 
1022 	/***
1023 	 * Resolves the bindings present in the context
1024 	 * 
1025 	 * @param deserialized indicates whether the simulation has been
1026 	 *        deserialized
1027 	 */
1028 	private void resolveBindings(final boolean deserialized)
1029 	{
1030 		try
1031 		{
1032 			String contextName = this.run + "/treatment("
1033 					+ this.numberOfTreatment + ")/replication("
1034 					+ this.numberOfReplication + ")/animation/2D";
1035 			EventContext context = (EventContext) new InitialEventContext()
1036 					.lookup(contextName);
1037 			NamingEnumeration list = context.listBindings("");
1038 			while (list.hasMore())
1039 			{
1040 				Binding binding = (Binding) list.next();
1041 				Renderable2DInterface element = (Renderable2DInterface) binding
1042 						.getObject();
1043 				this.elements.add(element);
1044 
1045 				Object obj = binding.getObject();
1046 				if (GisActorAnimation.class.isAssignableFrom(obj.getClass()))
1047 				{
1048 					GisActorAnimation object = (GisActorAnimation) obj;
1049 					String imagePath = object.getImages()[0].toString();
1050 					String[] array = imagePath.split("!");
1051 					if (array.length == 1)
1052 					{
1053 						array = imagePath.split("classes");
1054 					}
1055 
1056 					// we create our data elements and add it to the list
1057 					this.imageDataElements.put(element.getSource().toString()
1058 							+ element.getSource().hashCode(),
1059 							new GisActorImageDataElement(element.getClass()
1060 									.getName(), element.getSource().toString()
1061 									+ element.getSource().hashCode(), array[1],
1062 									object.getSource().getLocation(),
1063 									new Dimension(object.getImages()[0]
1064 											.getIconWidth(),
1065 											object.getImages()[0]
1066 													.getIconHeight()), element
1067 											.getSource().getClass().getName(),
1068 									((ActorInterface) element.getSource())
1069 											.getName()));
1070 					this.keySourceMap.put(element.getSource().toString()
1071 							+ element.getSource().hashCode(), element
1072 							.getSource());
1073 				} else if (SingleImageRenderable.class.isAssignableFrom(obj
1074 						.getClass()))
1075 				{
1076 					SingleImageRenderable object = (SingleImageRenderable) obj;
1077 					String imagePath = object.getImages()[0].toString();
1078 					String[] array = imagePath.split("!");
1079 					if (array.length == 1)
1080 					{
1081 						array = imagePath.split("classes");
1082 					}
1083 
1084 					// we create our data elements and add it to the list
1085 					this.imageDataElements.put(element.getSource().toString()
1086 							+ element.getSource().hashCode(),
1087 							new SingleImageDataElement(element.getClass()
1088 									.getName(), element.getSource().toString()
1089 									+ element.getSource().hashCode(), array[1],
1090 									object.getSource().getLocation(),
1091 									new Dimension(object.getImages()[0]
1092 											.getIconWidth(),
1093 											object.getImages()[0]
1094 													.getIconHeight()), object
1095 											.isFlip(), object.getOrientation(),
1096 									object.isRotate(), object.isScale(), object
1097 											.isTranslate()));
1098 					if (ContentAnimation.class.isAssignableFrom(object
1099 							.getSource().getClass()))
1100 					{
1101 						this.keySourceMap.put(element.getSource().toString()
1102 								+ element.getSource().hashCode(),
1103 								((ContentAnimation) element.getSource())
1104 										.getContent());
1105 					} else
1106 					{
1107 						this.keySourceMap.put(element.getSource().toString()
1108 								+ element.getSource().hashCode(), element
1109 								.getSource());
1110 					}
1111 				} else if (GisRenderable2D.class.isAssignableFrom(obj
1112 						.getClass()))
1113 				{
1114 					this.imageDataElements.put(element.getSource().toString()
1115 							+ element.getSource().hashCode(),
1116 							new ImageDataElement(element.getClass().getName(),
1117 									element.getSource().toString()
1118 											+ element.getSource().hashCode(),
1119 									element.getSource().getLocation(),
1120 									"/world.map.xml"));
1121 					this.keySourceMap.put(element.getSource().toString()
1122 							+ element.getSource().hashCode(), element
1123 							.getSource());
1124 				} else if (GisGraphicsRenderable.class.isAssignableFrom(obj
1125 						.getClass()))
1126 				{
1127 					this.imageDataElements.put(element.getSource().toString()
1128 							+ element.getSource().hashCode(),
1129 							new ImageDataElement(element.getClass().getName(),
1130 									element.getSource().toString()
1131 											+ element.getSource().hashCode(),
1132 									element.getSource().getLocation(),
1133 									"undefined"));
1134 					this.keySourceMap.put(element.getSource().toString()
1135 							+ element.getSource().hashCode(), element
1136 							.getSource());
1137 				} else
1138 				{
1139 					Logger.severe(this, "resolveBindings",
1140 							"GameLeaderInteractionLayer: unknown renderable implementation: "
1141 									+ obj.getClass());
1142 				}
1143 
1144 				// we check whether it is a supply chain actor, if so
1145 				// we add the actor key to our map of keys
1146 				if (element.getSource() instanceof SupplyChainActor)
1147 				{
1148 					String actorKey = element.getSource().toString()
1149 							+ element.getSource().hashCode();
1150 
1151 					// we split the actors based on their reference type
1152 					String[] split = element.getSource().getClass().getName()
1153 							.split("//.");
1154 					String mapKey = split[split.length - 1];
1155 					// is the key already present?
1156 					if (this.actorKeys.get(mapKey) == null)
1157 					{
1158 						// let's sort the actor keys alfabetically
1159 						SortedSet set = new TreeSet();
1160 						this.actorKeys.put(mapKey, set);
1161 					}
1162 					((Set) this.actorKeys.get(mapKey)).add(actorKey);
1163 				}
1164 			}
1165 
1166 			// we listen for new events
1167 			context.addNamingListener("", EventContext.SUBTREE_SCOPE, this);
1168 
1169 			if (!deserialized)
1170 			{
1171 				// we start listening for animation update events
1172 				this.experiment.getSimulator().addListener(this,
1173 						AnimatorInterface.UPDATE_ANIMATION_EVENT, false);
1174 			}
1175 		} catch (Exception exception)
1176 		{
1177 			Logger.severe(this, "resolveBindings", exception);
1178 		}
1179 	}
1180 
1181 	/***
1182 	 * writes a serializable method to stream
1183 	 * 
1184 	 * @param out the outputstream
1185 	 * @throws IOException on IOException
1186 	 */
1187 	private synchronized void writeObject(final ObjectOutputStream out)
1188 			throws IOException
1189 	{
1190 		// we set our status to offline;
1191 		this.yellowPage.updateInteractiveOnlineStatus(this, false, true);
1192 
1193 		this.numberOfTreatment = this.experiment.getSimulator()
1194 				.getReplication().getRunControl().getTreatment().getNumber();
1195 		this.numberOfReplication = this.experiment.getSimulator()
1196 				.getReplication().getNumber();
1197 		out.defaultWriteObject();
1198 		// we reset our status to the original one
1199 		if (this.numberLoggedIn > 0)
1200 		{
1201 			this.yellowPage.updateInteractiveOnlineStatus(this, false, true);
1202 		}
1203 	}
1204 
1205 	/***
1206 	 * reads a serializable method from stream
1207 	 * 
1208 	 * @param in the inputstream
1209 	 */
1210 	private synchronized void readObject(final java.io.ObjectInputStream in)
1211 	{
1212 		try
1213 		{
1214 			in.defaultReadObject();
1215 
1216 			// custom deserialize methods
1217 			this.clients = new ArrayList();
1218 			this.listenersRefreshRates = Collections
1219 					.synchronizedMap(new HashMap());
1220 			this.listenersTimers = Collections.synchronizedMap(new HashMap());
1221 			this.actorKeys = new HashMap();
1222 
1223 			try
1224 			{
1225 				UnicastRemoteObject.exportObject(this);
1226 			} catch (RemoteException remoteException)
1227 			{
1228 				Logger.severe(this, "<readObject>", remoteException);
1229 			}
1230 
1231 			new InitialEventContext().bind(this.id, this);
1232 
1233 			// we clean our animation history
1234 			this.elements = Collections.synchronizedSet(new HashSet());
1235 			this.changedElements = Collections.synchronizedMap(new HashMap());
1236 			this.imageDataElements = Collections.synchronizedMap(new HashMap());
1237 			this.keySourceMap = Collections.synchronizedMap(new HashMap());
1238 
1239 			GameLeaderInteractionLayer.deserialized = true;
1240 
1241 			// create the timer for the animation
1242 			this.timedAnimationRefresher = new TimedAnimationRefresher(this);
1243 
1244 			// reset online status
1245 			this.numberLoggedIn = 0;
1246 
1247 		} catch (Exception exception)
1248 		{
1249 			Logger.severe(this, "readObject", exception);
1250 		}
1251 	}
1252 
1253 	/***
1254 	 * A Runnable used for saving the simulation.
1255 	 * <p>
1256 	 * (c) copyright 2005 <a href="http://www.simulation.tudelft.nl">Delft
1257 	 * University of Technology </a>, the Netherlands. <br>
1258 	 * See for project information <a
1259 	 * href="http://www.simulation.tudelft.nl">www.simulation.tudelft.nl </a>
1260 	 * <br>
1261 	 * 
1262 	 * Copyright (c) 2003-2005 Delft University of Technology, Jaffalaan 5, 2628
1263 	 * BX Delft, the Netherlands. All rights reserved.
1264 	 * 
1265 	 * See for project information <a href="http://www.simulation.tudelft.nl/">
1266 	 * www.simulation.tudelft.nl </a>.
1267 	 * 
1268 	 * The source code and binary code of this software is proprietary
1269 	 * information of Delft University of Technology.
1270 	 * 
1271 	 * @author <a
1272 	 *         href="http://www.tbm.tudelft.nl/webstaf/stijnh/index.htm">Stijn-Pieter
1273 	 *         van Houten </a>
1274 	 * @version $Revision: 1.2 $ $Date: 2005/08/03 08:52:50 $
1275 	 * @since 1.1.3
1276 	 */
1277 	private class SaveSimulationRunnable implements Runnable
1278 	{
1279 		/***
1280 		 * constructs a new SaveSimulationRunnable
1281 		 */
1282 		public SaveSimulationRunnable()
1283 		{
1284 			super();
1285 		}
1286 
1287 		/***
1288 		 * @see java.lang.Runnable#run()
1289 		 */
1290 		public void run()
1291 		{
1292 			// we sleep one second to give the simulator the chance to fire the
1293 			// SimulatorInterface.STOP_EVENT; otherwise a client side gui looks
1294 			// like it 'hangs', while in effect the simulation is paused.
1295 			try
1296 			{
1297 				Thread.sleep(1000);
1298 			} catch (InterruptedException interruptedException)
1299 			{
1300 				Logger.severe(this, "run", interruptedException);
1301 			}
1302 			// we synchronize with the animator; during serialization the
1303 			// animator is not able to advance in time nor executing an event of
1304 			// its eventlist
1305 			synchronized (((GameAnimator) GameLeaderInteractionLayer.this.experiment
1306 					.getSimulator()).gameSemaphore)
1307 			{
1308 				try
1309 				{
1310 					ThreadedEventProducer.DIJKSTRASEMAPHORE.acquireAll();
1311 
1312 					double time = Math.round(TimeUnit.convert(
1313 							GameLeaderInteractionLayer.this.experiment
1314 									.getSimulator().getSimulatorTime(),
1315 							GameLeaderInteractionLayer.this.experiment
1316 									.getSimulator().getReplication()
1317 									.getRunControl().getTreatment()
1318 									.getTimeUnit(), TimeUnitInterface.DAY));
1319 
1320 					// some formatting to get a nicely sorted list of saved
1321 					// simulations
1322 					String doubleAsString = "" + time;
1323 					String[] split = doubleAsString.split("//.");
1324 					String toFormat = split[0];
1325 					while (toFormat.length() <= 3)
1326 					{
1327 						toFormat = "0" + toFormat;
1328 					}
1329 					doubleAsString = toFormat + "." + split[1];
1330 
1331 					// the calendar for the wall clock time
1332 					Calendar calendar = Calendar.getInstance();
1333 					calendar.setTimeInMillis(System.currentTimeMillis());
1334 					String date = DateFormat.getDateTimeInstance().format(
1335 							calendar.getTime());
1336 					// replace any :
1337 					String[] dates = date.split(":");
1338 					date = dates[0] + "_" + dates[1] + "_" + dates[2];
1339 
1340 					FileOutputStream file = new FileOutputStream(
1341 							GameLeaderInteractionLayer.this.experiment
1342 									.getProperty(Experiment.EXPERIMENT_NAME)
1343 									+ " time in days "
1344 									+ doubleAsString
1345 									+ " wallclock " + date + ".ids");
1346 
1347 					// we remove the animation panel extent since it is not
1348 					// serializable
1349 					GameLeaderInteractionLayer.this.experiment.getSimulator()
1350 							.getReplication().getRunControl().getTreatment()
1351 							.getProperties().put("animationPanel.extent", "");
1352 
1353 					ObjectOutputStream fos = new ObjectOutputStream(file);
1354 					// we write these objects first since they are used to
1355 					// create a context
1356 					try
1357 					{
1358 						fos
1359 								.writeObject(GameLeaderInteractionLayer.this.experiment);
1360 						fos
1361 								.writeObject(GameLeaderInteractionLayer.this.experiment
1362 										.getSimulator().getReplication()
1363 										.getRunControl());
1364 					} catch (Exception exception)
1365 					{
1366 						fos.reset();
1367 						fos.flush();
1368 						fos.close();
1369 						file.close();
1370 						exception.printStackTrace();
1371 					} finally
1372 					{
1373 						try
1374 						{
1375 							// close the streams
1376 							fos.flush();
1377 							fos.close();
1378 							file.close();
1379 							fos = null;
1380 							file = null;
1381 						} catch (Exception exception2)
1382 						{
1383 							// we may ignore
1384 							exception2 = null;
1385 						}
1386 					}
1387 
1388 					Logger
1389 							.info(
1390 									this,
1391 									"saveSimulation",
1392 									"GameLeaderInteractionLayer: succesfully saved simulation to persistent storage");
1393 					ThreadedEventProducer.DIJKSTRASEMAPHORE.releaseAll();
1394 				} catch (Throwable exception)
1395 				{
1396 					ThreadedEventProducer.DIJKSTRASEMAPHORE.releaseAll();
1397 
1398 					// if a StackOverFlowError is thrown, most likely the Stack
1399 					// became too big due to the serialization process and NOT
1400 					// due to a programming mistake.... :-)
1401 					// we use"-Xmx512m" and -Xss2m for the VM while running the
1402 					// management application most likely the stack trace is
1403 					// never shown, since the stack is full....
1404 					exception.printStackTrace();
1405 				}
1406 			}
1407 		}
1408 	}
1409 
1410 	/***
1411 	 * 
1412 	 * The TimedAnimationRefresher manages refreshing the client side animation
1413 	 * panels.
1414 	 * <p>
1415 	 * Copyright (c) 2003-2005 Delft University of Technology, Jaffalaan 5, 2628
1416 	 * BX Delft, the Netherlands. All rights reserved.
1417 	 * 
1418 	 * See for project information <a href="http://www.simulation.tudelft.nl/">
1419 	 * www.simulation.tudelft.nl </a>.
1420 	 * 
1421 	 * The source code and binary code of this software is proprietary
1422 	 * information of Delft University of Technology.
1423 	 * 
1424 	 * @author <a
1425 	 *         href="http://www.tbm.tudelft.nl/webstaf/stijnh/index.htm">Stijn-Pieter
1426 	 *         van Houten </a>
1427 	 * @version $Revision: 1.2 $ $Date: 2005/08/03 08:52:50 $
1428 	 * @since 1.0.6 <br>
1429 	 */
1430 	private class TimedAnimationRefresher implements Serializable
1431 	{
1432 		/*** the serial version uid */
1433 		private static final long serialVersionUID = 13L;
1434 
1435 		/*** the timer */
1436 		private Timer timer = null;
1437 
1438 		/*** the owner */
1439 		private GameLeaderInteractionLayer owner = null;
1440 
1441 		/***
1442 		 * constructs a new TimedAnimationRefresher
1443 		 * 
1444 		 * @param owner the owner
1445 		 */
1446 		public TimedAnimationRefresher(final GameLeaderInteractionLayer owner)
1447 
1448 		{
1449 			this.timer = new Timer();
1450 			this.owner = owner;
1451 		}
1452 
1453 		/***
1454 		 * Method scheduleTask schedules a new timer task
1455 		 * 
1456 		 * @param listener the listener
1457 		 * @param times the number of times
1458 		 */
1459 		protected void scheduleTask(
1460 				final RemoteEventListenerInterface listener, final double times)
1461 		{
1462 			AnimationRefreshTask task = new AnimationRefreshTask(this,
1463 					listener, times);
1464 			this.timer.schedule(task, (long) ((1 / times) * 1000), // initial
1465 					// delay
1466 					(long) ((1 / times) * 1000)); // subsequent rate
1467 			this.owner.listenersTimers.put(listener, task);
1468 		}
1469 
1470 		/***
1471 		 * writes a serializable method to stream
1472 		 * 
1473 		 * @param out the outputstream
1474 		 * @throws IOException on IOException
1475 		 */
1476 		private synchronized void writeObject(final ObjectOutputStream out)
1477 				throws IOException
1478 		{
1479 			out.writeObject(this.owner);
1480 		}
1481 
1482 		/***
1483 		 * reads a serializable method from stream
1484 		 * 
1485 		 * @param in the inputstream
1486 		 */
1487 		private synchronized void readObject(final java.io.ObjectInputStream in)
1488 		{
1489 			try
1490 			{
1491 				this.owner = (GameLeaderInteractionLayer) in.readObject();
1492 				this.timer = new Timer();
1493 			} catch (Exception exception)
1494 			{
1495 				Logger.severe(this, "readObject", exception);
1496 			}
1497 		}
1498 
1499 		/***
1500 		 * <p>
1501 		 * Copyright (c) 2003-2005 Delft University of Technology, Jaffalaan 5,
1502 		 * 2628 BX Delft, the Netherlands. All rights reserved.
1503 		 * 
1504 		 * See for project information <a
1505 		 * href="http://www.simulation.tudelft.nl/"> www.simulation.tudelft.nl
1506 		 * </a>.
1507 		 * 
1508 		 * The source code and binary code of this software is proprietary
1509 		 * information of Delft University of Technology.
1510 		 * 
1511 		 * @author <a
1512 		 *         href="http://www.tbm.tudelft.nl/webstaf/stijnh/index.htm">Stijn-Pieter
1513 		 *         van Houten </a>
1514 		 * @version $Revision: 1.2 $ $Date: 2005/08/03 08:52:50 $
1515 		 * @since 1.0.0 <br>
1516 		 */
1517 		class AnimationRefreshTask extends TimerTask
1518 		{
1519 			/*** the owner */
1520 			private TimedAnimationRefresher owner = null;
1521 
1522 			/*** the listener */
1523 			private RemoteEventListenerInterface listener = null;
1524 
1525 			/***
1526 			 * constructs a new RemindTask
1527 			 * 
1528 			 * @param owner the owner
1529 			 * @param listener the listener
1530 			 * @param times the number of times
1531 			 */
1532 			public AnimationRefreshTask(final TimedAnimationRefresher owner,
1533 					final RemoteEventListenerInterface listener,
1534 					final double times)
1535 			{
1536 				super();
1537 				this.owner = owner;
1538 				this.listener = listener;
1539 			}
1540 
1541 			/***
1542 			 * @see java.lang.Runnable#run()
1543 			 */
1544 			public void run()
1545 			{
1546 				try
1547 				{
1548 					synchronized (this.owner.owner.changedElements)
1549 					{
1550 						List changes = (List) this.owner.owner.changedElements
1551 								.get(this.listener);
1552 
1553 						if (changes.size() > 0)
1554 						{
1555 							// we notify the listener directly and as such do
1556 							// not delay the running of the game thread while
1557 							// using animation as well, however, there is always
1558 							// the constraint of bandwith usage
1559 							this.listener
1560 									.notify(new Event(
1561 											GameLeaderInteractionLayer.ANIMATION_UPDATE_EVENT,
1562 											this.owner.owner, changes));
1563 							// we empty the list
1564 							changes.clear();
1565 						}
1566 
1567 						this.cancel();
1568 						// we schedule ourselves for a new round
1569 						// based on the latest number of times to refresh
1570 						this.owner
1571 								.scheduleTask(
1572 										this.listener,
1573 										((Double) this.owner.owner.listenersRefreshRates
1574 												.get(this.listener))
1575 												.doubleValue());
1576 					}
1577 				} catch (Exception exception)
1578 				{
1579 					// we cancel the task
1580 					this.cancel();
1581 					synchronized (this.owner.owner.changedElements)
1582 					{
1583 						try
1584 						{
1585 							// there is an error, lets remove the listener and
1586 							// all its objects belonging to him
1587 							this.owner.owner.changedElements
1588 									.remove(this.listener);
1589 							this.owner.owner.listenersRefreshRates
1590 									.remove(this.listener);
1591 							this.owner.owner.listenersTimers
1592 									.remove(this.listener);
1593 						} catch (Exception exception2)
1594 						{
1595 							// ignore
1596 							exception2 = null;
1597 						}
1598 					}
1599 					Logger.info(this, "AnimationRefreshTask",
1600 							"RemoteListener removed from timer task");
1601 				}
1602 			}
1603 		}
1604 	}
1605 }