View Javadoc

1   /*
2    * DistributorSingleUserInteractionLayer.java Created @ Jun 8, 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  
14  package org.gscg.singleuser.interactionlayer;
15  
16  import java.io.IOException;
17  import java.io.ObjectOutputStream;
18  import java.rmi.RemoteException;
19  import java.rmi.server.UnicastRemoteObject;
20  import java.util.ArrayList;
21  import java.util.HashMap;
22  import java.util.HashSet;
23  import java.util.Map;
24  import java.util.Set;
25  import java.util.TreeSet;
26  
27  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
28  import nl.tudelft.simulation.event.Event;
29  import nl.tudelft.simulation.event.EventInterface;
30  import nl.tudelft.simulation.event.EventListenerInterface;
31  import nl.tudelft.simulation.event.EventType;
32  import nl.tudelft.simulation.event.remote.RemoteEventListener;
33  import nl.tudelft.simulation.event.remote.RemoteEventListenerInterface;
34  import nl.tudelft.simulation.event.remote.RemoteEventProducerInterface;
35  import nl.tudelft.simulation.logger.Logger;
36  import nl.tudelft.simulation.naming.InitialEventContext;
37  import nl.tudelft.simulation.supplychain.actor.SupplyChainActor;
38  import nl.tudelft.simulation.supplychain.actor.Trader;
39  import nl.tudelft.simulation.supplychain.stock.StockInterface;
40  
41  import org.gscg.common.gui.ClientInterface;
42  import org.gscg.common.gui.exceptions.ReceivedUnknownEventException;
43  import org.gscg.common.interactionlayer.AnnounceInterface;
44  import org.gscg.common.interactionlayer.ThreadedEventProducer;
45  import org.gscg.common.interactionlayer.location.YellowPage;
46  import org.gscg.common.interactionlayer.messaging.ScenarioText;
47  import org.gscg.common.interactionlayer.messaging.SocialMessaging;
48  import org.gscg.common.interactionlayer.timecontrol.CurrentRowOrColumnNumber;
49  import org.gscg.common.interactionlayer.timecontrol.GlobalRowOrColumnNumber;
50  import org.gscg.common.interactionlayer.timecontrol.ProgressDateAndTime;
51  import org.gscg.game.GameActorContentStore;
52  import org.gscg.game.GameGlobalData;
53  import org.gscg.gameactors.GameActorInteractiveInterface;
54  import org.gscg.gameactors.GameDistributor;
55  import org.gscg.gameactors.statistics.ContentStatisticsLayer;
56  import org.gscg.gameleader.interactionlayer.util.EventTypeComparator;
57  import org.gscg.singleuser.handlers.CommittedOrderHandler;
58  import org.gscg.singleuser.handlers.ConfirmedOrderHandler;
59  import org.gscg.singleuser.interactionlayer.business.BusinessPurchase;
60  import org.gscg.singleuser.interactionlayer.business.BusinessSales;
61  import org.gscg.singleuser.interactionlayer.business.BusinessStock;
62  import org.gscg.singleuser.interactionlayer.business.statistics.BankStatistics;
63  import org.gscg.singleuser.interactionlayer.business.statistics.StockStatistics;
64  import org.gscg.singleuser.interactionlayer.dataobjects.content.RFQDataSuppliers;
65  import org.gscg.singleuser.interactionlayer.economics.Economics;
66  
67  /***
68   * The DistributorSingleUserInteractionLayer takes care of the communication
69   * between the simulation server side and a client side application for a
70   * distributor.
71   * 
72   * <p>
73   * Copyright (c) 2003-2005 Delft University of Technology, Jaffalaan 5, 2628 BX
74   * Delft, the Netherlands. All rights reserved.
75   * 
76   * See for project information <a href="http://www.simulation.tudelft.nl/">
77   * www.simulation.tudelft.nl </a>.
78   * 
79   * The source code and binary code of this software is proprietary information
80   * of Delft University of Technology.
81   * 
82   * @author <a
83   *         href="http://www.tbm.tudelft.nl/webstaf/stijnh/index.htm">Stijn-Pieter
84   *         van Houten </a>
85   * @version $Revision: 1.1 $ $Date: 2005/08/09 15:43:41 $
86   * @since 1.0.0
87   */
88  public class DistributorSingleUserInteractionLayer extends
89  		ThreadedEventProducer implements RemoteInteractionLayerInterface,
90  		SingleUserInteractionLayerInterface
91  {
92  	/*** the serial version uid */
93  	private static final long serialVersionUID = 13L;
94  
95  	/*** the static list of ids in this game for identification mapping */
96  	protected static Map idMap = new HashMap();
97  
98  	/*** the remote event listener */
99  	private transient RemoteEventListener remoteEventListener = null;
100 
101 	/*** the id is used by a client to perform a lookup in de remote context */
102 	private String id = "";
103 
104 	/*** the simulator */
105 	private SimulatorInterface simulator = null;
106 
107 	/*** the owner of the interaction layer */
108 	private SupplyChainActor owner = null;
109 
110 	/*** the event types this layer is subscribed to */
111 	private Set eventTypes = new HashSet();
112 
113 	/*** the event types this layer is subscribed to */
114 	private Set eventsSentByClient = new HashSet();
115 
116 	/*** the semaphore to use for the announce calls */
117 	private transient Object semaphore = new Object();
118 
119 	/*** indicates if all announce events have been processed client side */
120 	private boolean clientIsReady = false;
121 
122 	/*** the event types this layer is subscribed to */
123 	private Set statisticEventTypes = new TreeSet(new EventTypeComparator());
124 
125 	/*** the overall game data */
126 	private GameGlobalData globalSupplyChainData;
127 
128 	/*** the yellowpage */
129 	private YellowPage yellowPage = null;
130 
131 	/*** maps event types and corresponding announce objects */
132 	private HashMap announceObjects = new HashMap();
133 
134 	/***
135 	 * indicates whether caching is necessary (during initialization of a
136 	 * client)
137 	 */
138 	private boolean shouldBeCaching = false;
139 
140 	/*** the cache to store mesages during intialization of a client */
141 	private ArrayList cache = new ArrayList();
142 
143 	/***
144 	 * constructs a new DistributorSingleUserInteractionLayer
145 	 * 
146 	 * @param simulator the simulator
147 	 * @param id the id of the single user interaction layer
148 	 * @param owner the owner of the interaction layer
149 	 * @param globalSupplyChainData the global supply chain data
150 	 */
151 	public DistributorSingleUserInteractionLayer(
152 			final SimulatorInterface simulator, final String id,
153 			final SupplyChainActor owner,
154 			final GameGlobalData globalSupplyChainData)
155 	{
156 		super();
157 		try
158 		{
159 			UnicastRemoteObject.exportObject(this);
160 		} catch (RemoteException remoteException)
161 		{
162 			Logger.severe(this, "<init>", remoteException);
163 		}
164 		try
165 		{
166 			this.globalSupplyChainData = globalSupplyChainData;
167 			this.remoteEventListener = new RemoteEventListener(this);
168 
169 			this.simulator = simulator;
170 			this.simulator.addListener(this, SimulatorInterface.START_EVENT);
171 			this.eventTypes.add(SimulatorInterface.START_EVENT);
172 			this.simulator.addListener(this, SimulatorInterface.STOP_EVENT);
173 			this.eventTypes.add(SimulatorInterface.STOP_EVENT);
174 
175 			this.id = id;
176 			this.owner = owner;
177 			this.yellowPage = this.globalSupplyChainData.getYellowPage();
178 
179 			// add actor to the location and name class
180 			this.globalSupplyChainData.getYellowPage().addListener(this,
181 					YellowPage.UPDATE_ACTORS, false);
182 			this.globalSupplyChainData.getYellowPage().addListener(this,
183 					YellowPage.UPDATE_INTERACTIVE_PLAYER_STATUS, false);
184 
185 			//
186 			// initialize components
187 			//
188 			new ProgressDateAndTime(this);
189 			new CurrentRowOrColumnNumber(this);
190 
191 			new SocialMessaging(this, this.yellowPage);
192 			new Economics(this);
193 			new BusinessStock(this);
194 			new BusinessPurchase(this);
195 			new BusinessSales(this);
196 			new BankStatistics(this, (Trader) this.owner,
197 					GlobalRowOrColumnNumber.getNumberOfDays());
198 			new StockStatistics(this, (Trader) this.owner,
199 					GlobalRowOrColumnNumber.getNumberOfDays());
200 
201 			// content statistics layer
202 			new ContentStatisticsLayer(this);
203 
204 			// stock events
205 			((Trader) this.owner).getStock().addListener(this,
206 					StockInterface.STOCK_CHANGE_EVENT);
207 			this.eventTypes.add(StockInterface.STOCK_CHANGE_EVENT);
208 
209 			// handlers
210 			new ConfirmedOrderHandler(this);
211 			new CommittedOrderHandler(this);
212 
213 			// we bind ourselves, may throw a naming exception
214 			// note: the actor role (i.e. distributor) is also used while a
215 			// player logs in
216 			new InitialEventContext().bind("distributor_" + this.id, this);
217 
218 			// tell the SupplyChainActor that we are there
219 			((GameActorInteractiveInterface) this.owner)
220 					.setSingleUserInteractionLayer(this);
221 		} catch (Exception exception)
222 		{
223 			Logger.severe(this, "DistributorSingleUserInteractionLayer",
224 					exception);
225 		}
226 	}
227 
228 	/***
229 	 * @see org.gscg.singleuser.interactionlayer.SingleUserInteractionLayerInterface#addEventType(nl.tudelft.simulation.event.EventType)
230 	 */
231 	public void addEventType(final EventType eventType)
232 	{
233 		if (!this.eventTypes.contains(eventType))
234 		{
235 			this.eventTypes.add((eventType));
236 		}
237 	}
238 
239 	/***
240 	 * @see org.gscg.common.interactionlayer.GlobalInteractionLayerInterface#addEventTypeSentByClient(nl.tudelft.simulation.event.EventType)
241 	 */
242 	public void addEventTypeSentByClient(final EventType eventType)
243 	{
244 		if (!this.eventsSentByClient.contains(eventType))
245 		{
246 			this.eventsSentByClient.add((eventType));
247 		}
248 	}
249 
250 	/***
251 	 * @see org.gscg.common.interactionlayer.GlobalInteractionLayerInterface#addEventTypeToAnnounceList(nl.tudelft.simulation.event.EventType,
252 	 *      org.gscg.common.interactionlayer.AnnounceInterface)
253 	 */
254 	public void addEventTypeToAnnounceList(final EventType eventType,
255 			final AnnounceInterface announceObject)
256 	{
257 		String key = eventType.toString();
258 		this.announceObjects.put(key, announceObject);
259 	}
260 
261 	/***
262 	 * @see org.gscg.singleuser.interactionlayer.SingleUserInteractionLayerInterface#addStatisticEventType(nl.tudelft.simulation.event.EventType)
263 	 */
264 	public void addStatisticEventType(final EventType eventType)
265 	{
266 		if (!this.statisticEventTypes.contains(eventType))
267 		{
268 			this.statisticEventTypes.add(eventType);
269 		}
270 	}
271 
272 	/***
273 	 * @see org.gscg.common.interactionlayer.GlobalInteractionLayerInterface#getCache(java.lang.Object,
274 	 *      nl.tudelft.simulation.event.EventType, boolean)
275 	 */
276 	public synchronized void getCache(final Object remoteEventProducer,
277 			final EventType eventType, final boolean cache)
278 			throws RemoteException
279 	{
280 		if (cache)
281 		{
282 			// only when there is no client logged in already we may update a
283 			// client with cached information this code is meant to prevent
284 			// initialization of a client user interface twice; which may occur
285 			// if somebody uses a wrong user-name
286 			if (!this.clientIsReady)
287 			{
288 				// client attempts to log on, start caching of events
289 				this.shouldBeCaching = true;
290 
291 				synchronized (this.semaphore)
292 				{
293 					if (eventType
294 							.equals(ScenarioText.CACHE_LARGE_NEWS_MESSAGE_EVENT))
295 					{
296 						ScenarioText.getScenarioText().announce(eventType,
297 								true, this);
298 						return;
299 					}
300 					if (this.announceObjects.containsKey(eventType.toString()))
301 					{
302 						AnnounceInterface object = (AnnounceInterface) this.announceObjects
303 								.get(eventType.toString());
304 						object.announce(eventType, true);
305 						return;
306 					}
307 					if (eventType.equals(YellowPage.UPDATE_ACTORS))
308 					{
309 						// pass the locations
310 						this.globalSupplyChainData.getYellowPage().update(true,
311 								this);
312 						// we need to add this event here, instead of in the
313 						// location and name class
314 						this.eventTypes.add(YellowPage.UPDATE_ACTORS);
315 						this.eventTypes
316 								.add(YellowPage.UPDATE_INTERACTIVE_PLAYER_STATUS);
317 						return;
318 					}
319 
320 					// update all the numbers for the messages in the game
321 					if (eventType
322 							.equals(GameActorContentStore.UPDATE_ALL_NUMBER_DATA_EVENT))
323 					{
324 						this.fireAllNumberData();
325 						return;
326 					}
327 
328 					// SPECIAL ANNOUNCE EVENTS USED DURING CLIENT INITIALIZATION
329 					if (eventType
330 							.equals(ClientInterface.INITIALIZATION_COMPLETED_EVENT))
331 					{
332 						if (this.simulator.isRunning())
333 						{
334 							this
335 									.notifyAnnounced(new Event(
336 											SimulatorInterface.START_EVENT,
337 											this, null));
338 						}
339 
340 						// as soon as the client disconnects, the remove
341 						// listener method is invoked, as soon as the
342 						// SimulatorInterface.TIME_CHANGED_EVENT event
343 						// is received, the status is set to false
344 						this.isReady(true);
345 
346 						try
347 						{
348 							// update the client with the cached events
349 							// must be placed after this.isReady(boolean arg),
350 							// otherwise a loop is created
351 							while (this.cache.size() > 0)
352 							{
353 								this.notify((EventInterface) this.cache
354 										.remove(0));
355 							}
356 						} catch (RemoteException remoteException)
357 						{
358 							// something went wrong during notification of the
359 							// client; let's quit and log off the client
360 							this.removeListener(null,
361 									SimulatorInterface.TIME_CHANGED_EVENT);
362 						}
363 
364 						this.shouldBeCaching = false;
365 
366 						Logger.info(this, "getCache",
367 								"DistributorSingleUserInteractionLayer: last cache event fired. Player: \""
368 										+ this.owner.getName()
369 										+ "\" is ready to play.");
370 						return;
371 					}
372 					new ReceivedUnknownEventException(this, "getCache1",
373 							eventType);
374 				}
375 			}
376 		} else
377 		{
378 			// no announce, for example for updating statistics
379 			if (this.announceObjects.containsKey(eventType.toString()))
380 			{
381 				AnnounceInterface object = (AnnounceInterface) this.announceObjects
382 						.get(eventType.toString());
383 				object.announce(eventType, false);
384 				return;
385 			}
386 
387 			// the events fired by the client
388 			if (this.eventsSentByClient.contains(eventType))
389 			{
390 				if (remoteEventProducer != null)
391 				{
392 					((RemoteEventProducerInterface) remoteEventProducer)
393 							.addListener(this.remoteEventListener, eventType,
394 									false);
395 					System.out.println("eveny sent by client event: "
396 							+ eventType);
397 				}
398 				return;
399 			}
400 			new ReceivedUnknownEventException(this, "getCache2", eventType);
401 		}
402 	}
403 
404 	/***
405 	 * @see nl.tudelft.simulation.event.EventListenerInterface#notify(nl.tudelft.simulation.event.EventInterface)
406 	 */
407 	public void notify(final EventInterface event) throws RemoteException
408 	{
409 		// we only use the commented code for testing
410 		if (this.clientIsReady)
411 		{
412 			// if (this.eventTypes.contains(event.getType()))
413 			// {
414 			super.fireEvent(event);
415 			// return;
416 			// }
417 			// if (this.eventsSentByClient.contains(event.getType()))
418 			// {
419 			// super.fireEvent(event);
420 			// return;
421 			// }
422 			// new ReceivedUnknownEventException(this, event.getType());
423 		} else
424 		{
425 			// only these messages must be forwarded to be stored
426 			if (event.getType().equals(SocialMessaging.RECEIVE_SOCIAL_MESSAGE)
427 					|| event.getType().equals(
428 							SocialMessaging.SOCIAL_MESSAGE_SENT)
429 					|| event.getType().equals(
430 							SocialMessaging.UPDATE_SENT_SOCIAL_MESSAGE)
431 					|| event.getType().equals(
432 							SocialMessaging.UPDATE_RECEIVED_SOCIAL_MESSAGE))
433 			{
434 				this.fireEvent(new Event(event.getType(), this, event
435 						.getContent()));
436 				return;
437 			}
438 			if (this.shouldBeCaching)
439 			{
440 				Logger.info(this, "notify",
441 						"DistributorSingleUserInteractionLayer: player: "
442 								+ this.owner.getName() + " is caching");
443 				// we do not cache time changed events
444 				if (!event.getType().equals(
445 						SimulatorInterface.TIME_CHANGED_EVENT))
446 				{
447 					// TODO should we set a limit to the size of the cache?
448 					// if something goes wrong during initialization; caching
449 					// keeps going on; leading eventually to an out-of-memory
450 					// exception
451 
452 					// temp caching of events
453 					this.cache.add(event);
454 				}
455 				return;
456 			}
457 		}
458 	}
459 
460 	/***
461 	 * @see org.gscg.singleuser.interactionlayer.SingleUserInteractionLayerInterface#notifyAnnounced(nl.tudelft.simulation.event.EventInterface)
462 	 */
463 	public void notifyAnnounced(final EventInterface event)
464 	{
465 		this.fireEvent(event);
466 	}
467 
468 
469 	/***
470 	 * @see org.gscg.common.interactionlayer.GlobalInteractionLayerInterface#login(nl.tudelft.simulation.event.remote.RemoteEventListenerInterface)
471 	 */
472 	public synchronized boolean login(final RemoteEventListenerInterface client)
473 			throws RemoteException
474 	{
475 		// TODO boolean should be set earlier; simultaneous login is possible
476 		// while saving a game
477 		if (this.clientIsReady)
478 		{
479 			// another player application is logged in
480 			return true;
481 		}
482 		if (this.shouldBeCaching)
483 		{
484 			// another player application is logging in
485 			return true;
486 		}
487 		return false;
488 	}
489 
490 	/***
491 	 * Method publicFireEvent.
492 	 * 
493 	 * @param event the event to fire
494 	 */
495 	public void publicFireEvent(final EventInterface event)
496 	{
497 		super.fireEvent(event);
498 	}
499 
500 	/***
501 	 * Method fireAllNumberData.
502 	 */
503 	public void fireAllNumberData()
504 	{
505 		((GameDistributor) this.owner).fireAllNumberData();
506 	}
507 
508 	/***
509 	 * @see nl.tudelft.simulation.event.EventProducerInterface#removeListener(nl.tudelft.simulation.event.EventListenerInterface,
510 	 *      nl.tudelft.simulation.event.EventType)
511 	 */
512 	public synchronized boolean removeListener(
513 			final EventListenerInterface listener, final EventType eventType)
514 	{
515 		// we assume that when a client is no longer interested in time changed
516 		// events it is definitively out of the game...
517 		if (eventType.equals(SimulatorInterface.TIME_CHANGED_EVENT))
518 		{
519 
520 			this.shouldBeCaching = false;
521 			this.cache.clear();
522 
523 			this.isReady(false);
524 			Logger.info(this, "removeListener",
525 					"DistributorSingleUserInteractionLayer: status of player: \""
526 							+ this.owner.getName() + "" + "\" set to "
527 							+ this.clientIsReady);
528 		}
529 		boolean result = super.removeListener(listener, eventType);
530 		return result;
531 	}
532 
533 	//
534 	// GETTERS *****************************************************************
535 	//
536 
537 	/***
538 	 * @see org.gscg.singleuser.interactionlayer.RemoteInteractionLayerInterface#getContentList(java.lang.Class,
539 	 *      boolean, java.lang.String)
540 	 */
541 	public void getContentList(final Class contentClass, final boolean sent,
542 			final String productName) throws RemoteException
543 	{
544 		((GameDistributor) this.owner).fireAllContentData(contentClass, sent,
545 				productName);
546 	}
547 
548 	/***
549 	 * @see org.gscg.singleuser.interactionlayer.SingleUserInteractionLayerInterface#getGlobalSupplyChainData()
550 	 */
551 	public GameGlobalData getGlobalSupplyChainData()
552 	{
553 		return this.globalSupplyChainData;
554 	}
555 
556 	/***
557 	 * @see org.gscg.common.interactionlayer.GlobalInteractionLayerInterface#getStatisticEventTypes()
558 	 */
559 	public EventType[] getStatisticEventTypes()
560 	{
561 		return (EventType[]) this.statisticEventTypes
562 				.toArray(new EventType[this.statisticEventTypes.size()]);
563 	}
564 
565 	/***
566 	 * @see org.gscg.singleuser.interactionlayer.RemoteInteractionLayerInterface#getSuppliers(java.lang.String)
567 	 */
568 	public RFQDataSuppliers getSuppliers(final String productName)
569 			throws RemoteException
570 	{
571 		return ((GameDistributor) this.owner).getInitialRFQData(productName);
572 	}
573 
574 	/***
575 	 * @see org.gscg.common.interactionlayer.GlobalInteractionLayerInterface#getTotalNumberOfDays()
576 	 */
577 	public int getTotalNumberOfDays() throws RemoteException
578 	{
579 		return GlobalRowOrColumnNumber.getNumberOfDays();
580 	}
581 
582 	/***
583 	 * @see org.gscg.singleuser.interactionlayer.SingleUserInteractionLayerInterface#getOwner()
584 	 */
585 	public SupplyChainActor getOwner()
586 	{
587 		return this.owner;
588 	}
589 
590 	/***
591 	 * @see org.gscg.singleuser.interactionlayer.SingleUserInteractionLayerInterface#getSimulator()
592 	 */
593 	public SimulatorInterface getSimulator()
594 	{
595 		return this.simulator;
596 	}
597 
598 	//
599 	// PRIVATE METHODS *********************************************************
600 	//
601 	/***
602 	 * Method isReady.
603 	 * 
604 	 * @param ready true if the client is connected, false otherwise
605 	 */
606 	private void isReady(final boolean ready)
607 	{
608 		this.clientIsReady = ready;
609 		this.yellowPage.updateInteractiveOnlineStatus(this.owner, ready, false);
610 	}
611 
612 	/***
613 	 * writes a serializable method to stream
614 	 * 
615 	 * @param out the outputstream
616 	 * @throws IOException on IOException
617 	 */
618 	private synchronized void writeObject(final ObjectOutputStream out)
619 			throws IOException
620 	{
621 		// we set our status to offline;
622 		this.yellowPage.updateInteractiveOnlineStatus(this.owner, false, true);
623 
624 		out.defaultWriteObject();
625 		out.writeObject(this.simulator.getReplication().getRunControl()
626 				.getTreatment().getProperties());
627 
628 		// we reset our status before saving
629 		this.yellowPage.updateInteractiveOnlineStatus(this.owner,
630 				this.clientIsReady, true);
631 	}
632 
633 	/***
634 	 * reads a serializable method from stream
635 	 * 
636 	 * @param in the inputstream
637 	 */
638 	private synchronized void readObject(final java.io.ObjectInputStream in)
639 	{
640 		try
641 		{
642 			in.defaultReadObject();
643 			try
644 			{
645 				UnicastRemoteObject.exportObject(this);
646 			} catch (RemoteException remoteException)
647 			{
648 				Logger.severe(this, "<init>", remoteException);
649 			}
650 			// custom deserialize methods
651 			this.remoteEventListener = new RemoteEventListener(this);
652 			this.semaphore = new Object();
653 			new InitialEventContext().bind(this.id, this);
654 
655 			this.clientIsReady = false;
656 
657 		} catch (Exception exception)
658 		{
659 			Logger.severe(this, "readObject", exception);
660 		}
661 	}
662 }