View Javadoc

1   /*
2    * @(#)GameDistributorInteractive.java Jul 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.gameactors;
15  
16  import java.io.IOException;
17  import java.io.ObjectOutputStream;
18  import java.io.Serializable;
19  import java.net.URL;
20  import java.rmi.RemoteException;
21  import java.util.HashSet;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Properties;
25  import java.util.Set;
26  
27  import javax.vecmath.Point3d;
28  
29  import nl.tudelft.simulation.actor.messagehandlers.HandleAllMessages;
30  import nl.tudelft.simulation.actor.messagehandlers.MessageHandlerInterface;
31  import nl.tudelft.simulation.dsol.experiment.TimeUnit;
32  import nl.tudelft.simulation.dsol.experiment.TimeUnitInterface;
33  import nl.tudelft.simulation.dsol.simulators.AnimatorInterface;
34  import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface;
35  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
36  import nl.tudelft.simulation.dsol.statistics.charts.XYChart;
37  import nl.tudelft.simulation.event.Event;
38  import nl.tudelft.simulation.event.EventInterface;
39  import nl.tudelft.simulation.event.EventType;
40  import nl.tudelft.simulation.jstats.distributions.DistConstant;
41  import nl.tudelft.simulation.jstats.distributions.DistContinuous;
42  import nl.tudelft.simulation.jstats.streams.StreamInterface;
43  import nl.tudelft.simulation.language.io.URLResource;
44  import nl.tudelft.simulation.logger.Logger;
45  import nl.tudelft.simulation.messaging.devices.reference.FaxDevice;
46  import nl.tudelft.simulation.supplychain.actor.SupplyChainActor;
47  import nl.tudelft.simulation.supplychain.actor.Trader;
48  import nl.tudelft.simulation.supplychain.banking.Bank;
49  import nl.tudelft.simulation.supplychain.banking.BankAccount;
50  import nl.tudelft.simulation.supplychain.content.Bill;
51  import nl.tudelft.simulation.supplychain.content.Content;
52  import nl.tudelft.simulation.supplychain.content.InternalDemand;
53  import nl.tudelft.simulation.supplychain.content.Order;
54  import nl.tudelft.simulation.supplychain.content.OrderBasedOnQuote;
55  import nl.tudelft.simulation.supplychain.content.OrderConfirmation;
56  import nl.tudelft.simulation.supplychain.content.Payment;
57  import nl.tudelft.simulation.supplychain.content.Quote;
58  import nl.tudelft.simulation.supplychain.content.RequestForQuote;
59  import nl.tudelft.simulation.supplychain.product.Product;
60  import nl.tudelft.simulation.supplychain.roles.Role;
61  import nl.tudelft.simulation.supplychain.stock.StockInterface;
62  import nl.tudelft.simulation.supplychain.stock.StockUpdateData;
63  import nl.tudelft.simulation.supplychain.transport.TransportMode;
64  
65  import org.gscg.common.gui.exceptions.ReceivedUnknownEventException;
66  import org.gscg.common.interactionlayer.AnnounceInterface;
67  import org.gscg.common.interactionlayer.timecontrol.GlobalRowOrColumnNumber;
68  import org.gscg.experiment.HandlerParser;
69  import org.gscg.game.GameGlobalData;
70  import org.gscg.singleuser.handlers.InteractiveOrderHandlerStock;
71  import org.gscg.singleuser.interactionlayer.SingleUserInteractionLayerInterface;
72  import org.gscg.singleuser.interactionlayer.business.statistics.CustomerStatistics;
73  import org.gscg.singleuser.interactionlayer.dataobjects.DateIntData;
74  import org.gscg.singleuser.interactionlayer.dataobjects.content.BillData;
75  import org.gscg.singleuser.interactionlayer.dataobjects.content.QuoteData;
76  import org.gscg.singleuser.interactionlayer.dataobjects.content.RFQData;
77  import org.gscg.singleuser.interactionlayer.dataobjects.content.RFQDataSuppliers;
78  import org.gscg.singleuser.interactionlayer.dataobjects.content.SentOrderConfirmationData;
79  import org.gscg.singleuser.interactionlayer.dataobjects.content.SentQuoteData;
80  import org.jdom.Element;
81  
82  /***
83   * The GameDistributorInteractive extends a GameDistributor from the
84   * supplychain-game project and provides additional interactive functionalities.
85   * <br>
86   * Copyright (c) 2003-2005 Delft University of Technology, Jaffalaan 5, 2628 BX
87   * Delft, the Netherlands. All rights reserved.
88   * 
89   * See for project information <a href="http://www.simulation.tudelft.nl/">
90   * www.simulation.tudelft.nl </a>.
91   * 
92   * The source code and binary code of this software is proprietary information
93   * of Delft University of Technology.
94   * 
95   * @author <a
96   *         href="http://www.tbm.tudelft.nl/webstaf/alexandv/index.htm">Alexander
97   *         Verbraeck </a>
98   * @version $Revision: 1.2 $ $Date: 2005/08/09 15:43:40 $
99   * @since 1.0.0 <br>
100  */
101 
102 public class GameDistributorInteractive extends GameDistributor implements
103 		GameActorInteractiveInterface, AnnounceInterface
104 {
105 	/*** the map with distributors after deserialization */
106 	private static Set distributors = new HashSet();
107 
108 	/***
109 	 * @return returns the set with distributors of which the interactivity mode
110 	 *         has changed after deseriliazation due to changes in the
111 	 *         distributor.properties file
112 	 */
113 	public static Set getChangedDistributors()
114 	{
115 		return GameDistributorInteractive.distributors;
116 	}
117 
118 	/*** the serial version uid */
119 	private static final long serialVersionUID = 13L;
120 
121 	/*** a client has sent an RFQ which forms the basis for a quote */
122 	public static final EventType RFQ_SENT_BY_CLIENT_EVENT = new EventType(
123 			"RFQ_SENT_BY_CLIENT_EVENT");
124 
125 	/*** a client has selected a quote which forms the basis for an order */
126 	public static final EventType SELECTED_QUOTE_EVENT = new EventType(
127 			"SELECTED_QUOTE_EVENT");
128 
129 	/*** a client has selected a bill to pay */
130 	public static final EventType PAY_BILL_EVENT = new EventType(
131 			"PAY_BILL_EVENT");
132 
133 	/*** a client has selected an rfq and sent a quote */
134 	public static final EventType QUOTE_SENT_BY_CLIENT_EVENT = new EventType(
135 			"QUOTE_SENT_BY_CLIENT_EVENT");
136 
137 	/*** a client has deleted an rfq */
138 	public static final EventType RFQ_DELETED_BY_CLIENT_EVENT = new EventType(
139 			"RFQ_DELETED_BY_CLIENT_EVENT");
140 
141 	/*** a client has confirmed an order */
142 	public static final EventType ORDER_CONFIRMATION_SENT_BY_CLIENT_EVENT = new EventType(
143 			"ORDER_CONFIRMATION_SENT_BY_CLIENT_EVENT");
144 
145 	/*** events fired by client */
146 	private EventType[] eventsSentByClientArray = {
147 			GameDistributorInteractive.ORDER_CONFIRMATION_SENT_BY_CLIENT_EVENT,
148 			GameDistributorInteractive.RFQ_SENT_BY_CLIENT_EVENT,
149 			GameDistributorInteractive.SELECTED_QUOTE_EVENT,
150 			GameDistributorInteractive.PAY_BILL_EVENT,
151 			GameDistributorInteractive.QUOTE_SENT_BY_CLIENT_EVENT,
152 			GameDistributorInteractive.RFQ_DELETED_BY_CLIENT_EVENT};
153 
154 	/*** the SingleUserInteractionLayerInterface */
155 	private SingleUserInteractionLayerInterface singleUserInteractionLayer;
156 
157 	/***
158 	 * constructs a new GameDistributorInteractive; used when dragging and
159 	 * dropping an actor
160 	 * 
161 	 * @param name the name
162 	 * @param simulator the simulator
163 	 * @param position the position
164 	 * @param bank the bank
165 	 */
166 	public GameDistributorInteractive(final String name,
167 			final DEVSSimulatorInterface simulator, final Point3d position,
168 			final Bank bank)
169 	{
170 		super(name, simulator, position, bank);
171 	}
172 
173 	/***
174 	 * constructs a new GameDistributorInteractive
175 	 * 
176 	 * @param globalSupplyChainData the global supply chain data
177 	 * @param name the name
178 	 * @param simulator the simulator
179 	 * @param position the position
180 	 * @param roles the roles to implement
181 	 * @param bank the bank
182 	 * @param initialBankAccount the initial bank account
183 	 * @param product the product
184 	 * @param amount the amount
185 	 * @param manufacturer the manufacturer
186 	 */
187 	public GameDistributorInteractive(
188 			final GameGlobalData globalSupplyChainData, final String name,
189 			final DEVSSimulatorInterface simulator, final Point3d position,
190 			final Role[] roles, final Bank bank,
191 			final double initialBankAccount, final Product[] product,
192 			final Double[] amount, final Trader[] manufacturer)
193 	{
194 		super(name, simulator, position, roles, bank, initialBankAccount,
195 				product, amount, manufacturer, globalSupplyChainData);
196 	}
197 
198 	/***
199 	 * constructs a new GameDistributorInteractive
200 	 * 
201 	 * @param globalSupplyChainData the global supply chain data
202 	 * @param name the name
203 	 * @param simulator the simulator
204 	 * @param position the position
205 	 * @param roles the roles to implement
206 	 * @param bank the bank
207 	 * @param product the product
208 	 * @param amount the amount
209 	 * @param manufacturer the manufacturer
210 	 */
211 	public GameDistributorInteractive(
212 			final GameGlobalData globalSupplyChainData, final String name,
213 			final DEVSSimulatorInterface simulator, final Point3d position,
214 			final Role[] roles, final Bank bank, final Product[] product,
215 			final Double[] amount, final Trader[] manufacturer)
216 	{
217 		super(name, simulator, position, roles, bank, product, amount,
218 				manufacturer, globalSupplyChainData);
219 	}
220 
221 	/***
222 	 * @see org.gscg.gameactors.GameDistributor#init()
223 	 */
224 	protected void init()
225 	{
226 		// check whether we are in an interactive mode or
227 		// computer-controlled mode
228 
229 		// read properties
230 		Properties properties = new Properties();
231 		try
232 		{
233 			properties.load(URLResource
234 					.getResourceAsStream("/distributor.properties"));
235 		} catch (Exception exception)
236 		{
237 			exception.printStackTrace();
238 		}
239 		if (properties.getProperty(this.name) != null)
240 		{
241 			String result = properties.getProperty(this.name);
242 			if (result.equalsIgnoreCase("false"))
243 			{
244 				super.humanControlled = false;
245 				Logger.info(this, "init", "GameDistributorInteractive: "
246 						+ this.name + " set to computer-controlled mode");
247 				this.addComputerControlledContentHandlers();
248 			} else if (result.equalsIgnoreCase("true"))
249 			{
250 				try
251 				{
252 					if (this.simulator.getReplication().getRunControl()
253 							.getWarmupPeriod() > 0.0)
254 					{
255 						// subscribe to warm-up events; these events are used
256 						// for building up a history in a game, after the
257 						// warmu-up period is finished, and if necessary,
258 						// handlers are swapped from a computer-controlled mode
259 						// to an interactive mode
260 						this.simulator.addListener(this,
261 								SimulatorInterface.WARMUP_EVENT);
262 						super.humanControlled = false;
263 						super.warmingUp = true;
264 						Logger
265 								.info(
266 										this,
267 										"init",
268 										"GameDistributorInteractive: "
269 												+ this.name
270 												+ " set to computer-controlled mode due to the presence of a warm-up period.");
271 						this.addComputerControlledContentHandlers();
272 					} else
273 					{
274 						super.humanControlled = true;
275 						Logger.info(this, "init",
276 								"GameDistributorInteractive: " + this.name
277 										+ " set to interactive mode");
278 						this.addInteractiveContentHandlers();
279 					}
280 				} catch (RemoteException remoteException)
281 				{
282 					Logger.severe(this, "init", remoteException);
283 				}
284 			}
285 		} else
286 		{
287 			// no key found
288 			Logger.severe(this, "init",
289 					"GameDistributorInteractive: no key found for " + this.name
290 							+ " in properties: ." + properties);
291 		}
292 
293 		//
294 		// CHARTS
295 		//
296 		try
297 		{
298 			if (this.simulator instanceof AnimatorInterface)
299 			{
300 				XYChart bankChart = new XYChart(this.simulator, "BankAccount "
301 						+ this.name);
302 				bankChart.add("bank account", this.bankAccount,
303 						BankAccount.BANK_ACCOUNT_CHANGED_EVENT);
304 			}
305 		} catch (RemoteException remoteException)
306 		{
307 			Logger.severe(this, "init", remoteException);
308 		}
309 	}
310 
311 	/***
312 	 * @see org.gscg.gameactors.GameDistributor#addDevices()
313 	 */
314 	protected void addDevices()
315 	{
316 		try
317 		{
318 			StreamInterface stream = this.simulator.getReplication().getStream(
319 					"default");
320 			double hour = TimeUnit.convert(1.0, TimeUnitInterface.HOUR,
321 					this.simulator);
322 			DistContinuous hourDist = new DistConstant(stream, hour);
323 			// give the actor a fax device which is checked every hour
324 			FaxDevice fax = new FaxDevice("GameDistributorFax", this.simulator);
325 			addSendingDevice(fax);
326 			MessageHandlerInterface secretary = new HandleAllMessages(this);
327 			addReceivingDevice(fax, secretary, hourDist);
328 		} catch (RemoteException remoteException)
329 		{
330 			Logger.severe(this, "addDevices", remoteException);
331 		}
332 	}
333 
334 	/***
335 	 * @see org.gscg.common.interactionlayer.AnnounceInterface#announce(nl.tudelft.simulation.event.EventType,
336 	 *      boolean)
337 	 */
338 	public void announce(final EventType eventType, final boolean announce)
339 	{
340 		if (eventType.equals(StockInterface.STOCK_CHANGE_EVENT))
341 		{
342 			List products = super.getProductsOnStock();
343 			for (int i = 0; i < products.size(); i++)
344 			{
345 				Product product = (Product) products.get(i);
346 				this.stock.getActualAmount(product);
347 				StockUpdateData data = new StockUpdateData(product.getName(),
348 						this.stock.getActualAmount(product), this.stock
349 								.getClaimedAmount(product), this.stock
350 								.getOrderedAmount(product));
351 				try
352 				{
353 					this.singleUserInteractionLayer.notifyAnnounced(new Event(
354 							StockInterface.STOCK_CHANGE_EVENT, this, data));
355 				} catch (RemoteException remoteException)
356 				{
357 					Logger.severe(this, "announce", remoteException);
358 				}
359 			}
360 			return;
361 		}
362 		new ReceivedUnknownEventException(this, "announce", eventType);
363 	}
364 
365 	/***
366 	 * @see nl.tudelft.simulation.event.EventListenerInterface#notify(nl.tudelft.simulation.event.EventInterface)
367 	 */
368 	public void notify(final EventInterface event)
369 	{
370 		if (event.getType().equals(GlobalRowOrColumnNumber.UPDATE_CURRENT_DAY))
371 		{
372 			// we use these events to update the statistics on a daily basis
373 			this.sendStockUpdateEvent();
374 			this.bankAccount.sendBalanceUpdateEvent();
375 			return;
376 		}
377 		if (event.getType().equals(
378 				GameDistributorInteractive.RFQ_SENT_BY_CLIENT_EVENT))
379 		{
380 			// make the internal demand
381 			RFQDataSuppliers submitRFQData = (RFQDataSuppliers) event
382 					.getContent();
383 			Product product = this.globalSupplyChainData
384 					.getProduct(submitRFQData.getProductName());
385 			double earliestDeliveryDate = DateIntData.makeSimulationDate(
386 					submitRFQData.getEarliestDelivery(), this.simulator);
387 
388 			double latestDeliveryDate = DateIntData.makeSimulationDate(
389 					submitRFQData.getLatestDelivery(), this.simulator);
390 			// we check whether the simulation date is not already further
391 			// than the delivery dates, if so we ignore the rfq
392 			try
393 			{
394 				if (earliestDeliveryDate > this.simulator.getSimulatorTime())
395 				{
396 					InternalDemand internalDemand = new InternalDemand(this,
397 							product, submitRFQData.getAmount(),
398 							earliestDeliveryDate, latestDeliveryDate);
399 					String[] suppliers = submitRFQData.getSuppliers();
400 					handleContent(internalDemand);
401 					// make the RFQ and send it out to the selected actors
402 					for (int i = 0; i < suppliers.length; i++)
403 					{
404 						SupplyChainActor supplier = this.globalSupplyChainData
405 								.getSupplyChainActor(suppliers[i]);
406 						RequestForQuote rfq = new RequestForQuote(this,
407 								supplier, internalDemand, product,
408 								submitRFQData.getAmount());
409 						this.sendContent(rfq, 0.0);
410 					}
411 				} else
412 				{
413 					Logger
414 							.info(
415 									this,
416 									"notify",
417 									"GameDistributorInteractive did not send RFQ since the earliest delivery date has already been passed in the game.");
418 				}
419 			} catch (RemoteException remoteException)
420 			{
421 				Logger.severe(this, "notify", remoteException);
422 			}
423 			return;
424 		}
425 		if (event.getType().equals(
426 				GameDistributorInteractive.SELECTED_QUOTE_EVENT))
427 		{
428 			QuoteData data = (QuoteData) event.getContent();
429 
430 			// first create the quote
431 			SupplyChainActor sender = this.globalSupplyChainData
432 					.getSupplyChainActor(data.getSenderName());
433 			try
434 			{
435 				double proposedDeliveryDate = DateIntData.makeSimulationDate(
436 						data.getProposedDelivery(), this.simulator);
437 
438 				List quotes = super.getGameContentStore().getContentList(
439 						data.getRfqData().getInternalDemandIdentifier(),
440 						Quote.class, false);
441 				OrderBasedOnQuote order = null;
442 				for (int i = 0; i < quotes.size(); i++)
443 				{
444 					Quote quote = (Quote) quotes.get(i);
445 					if (quote.getSender().getName().equalsIgnoreCase(
446 							sender.getName()))
447 					{
448 						order = new OrderBasedOnQuote(quote.getReceiver(),
449 								quote.getSender(), data.getRfqData()
450 										.getInternalDemandIdentifier(),
451 								proposedDeliveryDate, quote);
452 						break;
453 					}
454 				}
455 
456 				// let the interactive quote handler know that
457 				// we have handled a quote for a rfq
458 				// we block further quotes for this rfq
459 				if (order != null)
460 				{
461 					super.interactiveQuoteHandler.addAnsweredQuote(order
462 							.getQuote());
463 				}
464 
465 				this.sendContent(order, 0.0);
466 
467 				// remove the quotes from the content store
468 				for (int i = 0; i < quotes.size(); i++)
469 				{
470 					super.getGameContentStore().removeContent(
471 							(Content) quotes.get(i), false);
472 				}
473 			} catch (Exception exception)
474 			{
475 				Logger.severe(this, "notify", exception);
476 			}
477 			return;
478 		}
479 		if (event.getType().equals(GameDistributorInteractive.PAY_BILL_EVENT))
480 		{
481 			BillData data = (BillData) event.getContent();
482 			Bill bill = (Bill) super.getGameContentStore().getContentList(
483 					data.getInternalDemandID(), Bill.class, false).get(0);
484 			if (bill == null)
485 			{
486 				Logger.severe(this, "notify PAY_BILL_EVENT",
487 						"bill not found in ContentStore");
488 			} else
489 			{
490 				// we pay the bill
491 				this.pay(bill);
492 
493 				// create the payment and send it out
494 				Payment payment = new Payment(bill.getReceiver(), bill
495 						.getSender(), bill.getInternalDemandID(), bill, bill
496 						.getPrice());
497 				this.sendContent(payment, 0.0);
498 			}
499 			return;
500 		}
501 		if (event.getType().equals(
502 				GameDistributorInteractive.QUOTE_SENT_BY_CLIENT_EVENT))
503 		{
504 			SentQuoteData data = (SentQuoteData) event.getContent();
505 			try
506 			{
507 				SupplyChainActor sender = this.globalSupplyChainData
508 						.getSupplyChainActor(data.getSenderName());
509 				SupplyChainActor receiver = this.globalSupplyChainData
510 						.getSupplyChainActor(data.getReceiverName());
511 
512 				// find the rfq
513 				List rfqs = super.getGameContentStore().getContentList(
514 						data.getRfqData().getInternalDemandIdentifier(),
515 						RequestForQuote.class, false);
516 				RequestForQuote rfq = null;
517 				for (int i = 0; i < rfqs.size(); i++)
518 				{
519 					RequestForQuote result = (RequestForQuote) rfqs.get(i);
520 					if (result.getReceiver().getName().equalsIgnoreCase(
521 							sender.getName()))
522 					{
523 						rfq = result;
524 						break;
525 					}
526 				}
527 
528 				double proposedDeliveryDate = DateIntData.makeSimulationDate(
529 						data.getProposedDelivery(), this.simulator);
530 
531 				if (proposedDeliveryDate > this.simulator.getSimulatorTime())
532 				{
533 					double price = data.getPrice();
534 					// we add the transportation costs
535 					double weight = rfq.getAmount()
536 							* rfq.getProduct().getAverageUnitWeight();
537 
538 					// calculate the costs based on the the lat lon distance!
539 					double transportCosts = TransportMode.PLANE.transportCosts(
540 							super.latLonDistanceCalculator.getDistance(rfq
541 									.getSender(), rfq.getReceiver()), weight);
542 
543 					// add the transport costs to the price
544 					price += transportCosts;
545 
546 					Quote quote = new Quote(sender, receiver, data.getRfqData()
547 							.getInternalDemandIdentifier(), rfq, rfq
548 							.getProduct(), data.getAmount(), price,
549 							proposedDeliveryDate, TransportMode.PLANE);
550 					this.sendContent(quote, 0.0);
551 				} else
552 				{
553 					Logger
554 							.info(
555 									this,
556 									"notify",
557 									"GameDistributorInteractive did not send Quote since the proposed delivery date has already been passed in the game. The accompanying rfq has been deleted.");
558 
559 					// delete the rfq from the erp sysem
560 					super.getGameContentStore().removeContent(rfq, false);
561 				}
562 			} catch (Exception exception)
563 			{
564 				Logger.severe(this, "notify", exception);
565 			}
566 			return;
567 		}
568 		if (event.getType().equals(
569 				GameDistributorInteractive.RFQ_DELETED_BY_CLIENT_EVENT))
570 		{
571 			RFQData data = (RFQData) event.getContent();
572 			try
573 			{
574 				SupplyChainActor receiver = this.globalSupplyChainData
575 						.getSupplyChainActor(data.getReceiverName());
576 
577 				// find the rfq
578 				List rfqs = super.getGameContentStore().getContentList(
579 						data.getInternalDemandIdentifier(),
580 						RequestForQuote.class, false);
581 				RequestForQuote rfq = null;
582 				for (int i = 0; i < rfqs.size(); i++)
583 				{
584 					RequestForQuote result = (RequestForQuote) rfqs.get(i);
585 					if (result.getReceiver().getName().equalsIgnoreCase(
586 							receiver.getName()))
587 					{
588 						rfq = result;
589 						break;
590 					}
591 				}
592 
593 				// delete the rfq from the erp sysem
594 				super.getGameContentStore().removeContent(rfq, false);
595 			} catch (Exception exception)
596 			{
597 				Logger.severe(this, "notify", exception);
598 			}
599 			return;
600 
601 		}
602 		if (event
603 				.getType()
604 				.equals(
605 						GameDistributorInteractive.ORDER_CONFIRMATION_SENT_BY_CLIENT_EVENT))
606 		{
607 			SentOrderConfirmationData data = (SentOrderConfirmationData) event
608 					.getContent();
609 			try
610 			{
611 				SupplyChainActor sender = this.globalSupplyChainData
612 						.getSupplyChainActor(data.getSenderName());
613 				SupplyChainActor receiver = this.globalSupplyChainData
614 						.getSupplyChainActor(data.getReceiverName());
615 				Order order = (Order) super.getGameContentStore()
616 						.getContentList(
617 								data.getOrderData().getInternalDemandID(),
618 								Order.class, false).get(0);
619 				if (order == null)
620 				{
621 					Logger.severe(this,
622 							"notify ORDER_CONFIRMATION_SENT_BY_CLIENT_EVENT",
623 							"order not found in ContentStore");
624 				} else
625 				{
626 					OrderConfirmation confirmation = new OrderConfirmation(
627 							sender, receiver, data.getOrderData()
628 									.getInternalDemandID(), order, data
629 									.getConfirmationStatus());
630 
631 					this.sendContent(confirmation, 0.0);
632 					InteractiveOrderHandlerStock handler = new InteractiveOrderHandlerStock(
633 							this, super.stock);
634 					handler.handleOrder(order);
635 
636 					// generate the customer statistics
637 					super.customerStatistics.handleContent(confirmation);
638 				}
639 				return;
640 			} catch (Exception exception)
641 			{
642 				Logger.severe(this, "notify", exception);
643 			}
644 			return;
645 		}
646 		if (event.getType().equals(SimulatorInterface.WARMUP_EVENT))
647 		{
648 			super.humanControlled = true;
649 			super.warmingUp = false;
650 			Logger
651 					.info(
652 							this,
653 							"init",
654 							"GameDistributorInteractive: "
655 									+ this.name
656 									+ " set to interactive mode since the warm-up period has been finished");
657 			this.addInteractiveContentHandlers();
658 			return;
659 		}
660 		new ReceivedUnknownEventException(this, "notify", event.getType());
661 	}
662 
663 	/***
664 	 * pays the bill
665 	 * 
666 	 * @param bill the bill to pay
667 	 */
668 	public void pay(final Bill bill)
669 	{
670 		this.bankAccount.withdrawFromBalance(bill.getPrice());
671 	}
672 
673 	/***
674 	 * @see nl.tudelft.simulation.content.HandlerInterface#handleContent(java.io.Serializable)
675 	 */
676 	public boolean handleContent(final Serializable content)
677 	{
678 		try
679 		{
680 			if (super.humanControlled)
681 			{
682 				if (content instanceof Quote)
683 				{
684 					// handle with the special interactive quote handler
685 					if (super.interactiveQuoteHandler.handleContent(content))
686 					{
687 						// save content in the content store
688 						super.getContentStore().addContent((Content) content,
689 								false);
690 					}
691 					return true;
692 				}
693 			}
694 			return super.handleContent(content);
695 		} catch (Exception exception)
696 		{
697 			Logger.severe(this, "handleContent", exception);
698 			return false;
699 		}
700 	}
701 
702 	/***
703 	 * @see nl.tudelft.simulation.supplychain.actor.SupplyChainActor#sendContent(nl.tudelft.simulation.supplychain.content.Content,
704 	 *      double)
705 	 */
706 	public void sendContent(final Content content,
707 			final double administrativeDelay)
708 	{
709 		super.sendContent(content, administrativeDelay);
710 	}
711 
712 	/***
713 	 * @see org.gscg.gameactors.GameActorInteractiveInterface#getSingleUserInteractionLayer()
714 	 */
715 	public SingleUserInteractionLayerInterface getSingleUserInteractionLayer()
716 	{
717 		return this.singleUserInteractionLayer;
718 	}
719 
720 	/***
721 	 * @see org.gscg.gameactors.GameActorInteractiveInterface#setSingleUserInteractionLayer(org.gscg.singleuser.interactionlayer.SingleUserInteractionLayerInterface)
722 	 */
723 	public void setSingleUserInteractionLayer(
724 			final SingleUserInteractionLayerInterface singleUserInteractionLayer)
725 	{
726 		this.singleUserInteractionLayer = singleUserInteractionLayer;
727 		getGameContentStore().setSingleUserInteractionlayer(
728 				this.singleUserInteractionLayer);
729 
730 		try
731 		{
732 			for (int i = 0; i < this.eventsSentByClientArray.length; i++)
733 			{
734 				this.singleUserInteractionLayer.addListener(this,
735 						this.eventsSentByClientArray[i], false);
736 
737 				this.singleUserInteractionLayer
738 						.addEventTypeSentByClient(this.eventsSentByClientArray[i]);
739 
740 			}
741 
742 			// announce to update the stock
743 			// add the announce events
744 			singleUserInteractionLayer.addEventTypeToAnnounceList(
745 					StockInterface.STOCK_CHANGE_EVENT, this);
746 
747 			//
748 			// listeners
749 			//
750 			// subscribe for time change events on a daily basis these
751 			// events are used to update client side statistics on a daily basis
752 			this.singleUserInteractionLayer.addListener(this,
753 					GlobalRowOrColumnNumber.UPDATE_CURRENT_DAY);
754 		} catch (RemoteException remoteException)
755 		{
756 			Logger.severe(this, "<init>", remoteException);
757 		}
758 
759 		//
760 		// CUSTOMER STATISTICS
761 		//
762 		this.customerStatistics = new CustomerStatistics(this.simulator,
763 				this.singleUserInteractionLayer);
764 	}
765 
766 	/***
767 	 * adds computer-controlled interactive content handlers
768 	 */
769 	public void addComputerControlledContentHandlers()
770 	{
771 		super.addComputerControlledContentHandlers();
772 	}
773 
774 	/***
775 	 * adds interactive content handlers
776 	 */
777 	public void addInteractiveContentHandlers()
778 	{
779 		// remove all previus content handlers
780 		this.removeAllContentHandlers();
781 
782 		// look for our personal xml file with handler description
783 		URL url = URLResource.getResource("/" + this.name + "_interactive.xml");
784 		if (url == null)
785 		{
786 			url = URLResource
787 					.getResource("/distributor_interactive_default_handler.xml");
788 		}
789 		Logger
790 				.info(
791 						this,
792 						"addInteractiveContentHandlers",
793 						"Using: "
794 								+ url.toExternalForm()
795 								+ " as the file containing the configuration for the handlers.");
796 		try
797 		{
798 			// let's parse the xml file data to handlers
799 			Element rootElement = GameDistributor.builder.build(url)
800 					.getRootElement();
801 			HandlerParser.parseAndAddHandler(this, rootElement);
802 		} catch (Exception exception)
803 		{
804 			Logger.severe(this, "addInteractiveContentHandlers", exception);
805 		}
806 	}
807 
808 	// private methods
809 	/***
810 	 * Method sendStockUpdateEvent fires an update for all the products
811 	 * available in the stock
812 	 */
813 	private void sendStockUpdateEvent()
814 	{
815 		for (Iterator i = this.stock.iterator(); i.hasNext();)
816 		{
817 			this.stock.sendStockUpdateEvent((Product) i.next());
818 		}
819 	}
820 
821 	/***
822 	 * writes a serializable method to stream
823 	 * 
824 	 * @param out the outputstream
825 	 * @throws IOException on IOException
826 	 */
827 	private synchronized void writeObject(final ObjectOutputStream out)
828 			throws IOException
829 	{
830 		out.defaultWriteObject();
831 	}
832 
833 	/***
834 	 * reads a serializable method from stream
835 	 * 
836 	 * @param in the inputstream
837 	 */
838 	private synchronized void readObject(final java.io.ObjectInputStream in)
839 	{
840 		try
841 		{
842 			in.defaultReadObject();
843 
844 			// check whether we are in an interactive mode or
845 			// computer-controlled mode
846 			// read properties
847 			Properties properties = new Properties();
848 			try
849 			{
850 				properties.load(URLResource
851 						.getResourceAsStream("/distributor.properties"));
852 			} catch (Exception exception)
853 			{
854 				exception.printStackTrace();
855 			}
856 			if (properties.getProperty(this.name) != null)
857 			{
858 				String result = properties.getProperty(this.name);
859 				if (result.equalsIgnoreCase("false"))
860 				{
861 					// only if there has been a change in the properties file
862 					// we put 'this' in the set, this set is evaluated by the
863 					// game management project (LoadGame.deserializeGame) after
864 					// all the actors have been deserialized, otherwise the
865 					// references between objects are not set yet
866 					if (super.humanControlled)
867 					{
868 						GameDistributorInteractive.distributors.add(this);
869 						super.humanControlled = false;
870 						System.out
871 								.println("GameDistributorInteractive: "
872 										+ this.name
873 										+ ": mode has been changed to computer-controlled mode");
874 					}
875 				} else if (result.equalsIgnoreCase("true"))
876 				{
877 					// only if there has been a change in the properties file
878 					// we put 'this' in the set, this set is evaluated by the
879 					// game management project (LoadGame.deserializeGame) after
880 					// all the actors have been deserialized, otherwise the
881 					// references between objects are not set yet
882 					// furthermore, the warm-up period must be finished
883 					if (!super.humanControlled && !super.warmingUp)
884 					{
885 						System.out
886 								.println("GameDistributorInteractive: Deserializing: "
887 										+ this + " warm-up finished");
888 						GameDistributorInteractive.distributors.add(this);
889 						super.humanControlled = true;
890 						System.out
891 								.println("GameDistributorInteractive: "
892 										+ this.name
893 										+ ": mode has been changed to interactive mode");
894 					} else if (!super.humanControlled)
895 					{
896 						System.out.println("Deserializing: " + this
897 								+ " however warm-up not finished yet");
898 					}
899 				}
900 			} else
901 			{
902 				// no key found
903 				System.out
904 						.println("GameDistributorInteractive: no key found for "
905 								+ this.name + " in properties.");
906 			}
907 		} catch (IOException exception)
908 		{
909 			Logger.severe(this, "readObject", exception);
910 		} catch (ClassNotFoundException exception)
911 		{
912 			Logger.severe(this, "readObject", exception);
913 		}
914 	}
915 }