View Javadoc

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