]> git.somenet.org - pub/jan/sbc.git/blob - src/main/java/at/ac/tuwien/sbc/valesriegler/xvsm/WaiterXVSM.java
Make cook and waiter look around for work to do if they are idle
[pub/jan/sbc.git] / src / main / java / at / ac / tuwien / sbc / valesriegler / xvsm / WaiterXVSM.java
1 package at.ac.tuwien.sbc.valesriegler.xvsm;
2
3 import java.io.Serializable;
4 import java.net.URI;
5 import java.util.Arrays;
6 import java.util.Collections;
7 import java.util.Iterator;
8 import java.util.List;
9
10 import org.mozartspaces.capi3.AnyCoordinator;
11 import org.mozartspaces.core.ContainerReference;
12 import org.mozartspaces.core.MzsConstants;
13 import org.mozartspaces.core.MzsConstants.RequestTimeout;
14 import org.mozartspaces.core.MzsCoreException;
15 import org.mozartspaces.core.TransactionException;
16 import org.mozartspaces.core.TransactionReference;
17 import org.mozartspaces.notifications.Notification;
18 import org.mozartspaces.notifications.NotificationListener;
19 import org.mozartspaces.notifications.Operation;
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
22
23 import at.ac.tuwien.sbc.valesriegler.common.Util;
24 import at.ac.tuwien.sbc.valesriegler.types.GroupData;
25 import at.ac.tuwien.sbc.valesriegler.types.GroupState;
26 import at.ac.tuwien.sbc.valesriegler.types.Order;
27 import at.ac.tuwien.sbc.valesriegler.types.OrderStatus;
28 import at.ac.tuwien.sbc.valesriegler.types.Pizza;
29 import at.ac.tuwien.sbc.valesriegler.types.Table;
30
31 public class WaiterXVSM extends AbstractXVSMConnector {
32         private static final Logger log = LoggerFactory.getLogger(WaiterXVSM.class);
33         private final int waiterId;
34
35         public WaiterXVSM(int waiterId) {
36                 super();
37
38                 this.waiterId = waiterId;
39
40                 freeTablesContainer = useContainer(Util.FREE_TABLES);
41                 assignTableContainer = useContainer(Util.ASSIGN_TABLE);
42                 takeOrderContainer = useContainer(Util.TAKE_ORDER);
43                 orderTakenContainer = useContainer(Util.ORDER_TAKEN);
44                 preparePizzasContainer = useContainer(Util.PREPARE_PIZZAS);
45                 orderCompleteContainer = useContainer(Util.ORDER_COMPLETE);
46                 deliverPizzasContainer = useContainer(Util.DELIVER_PIZZAS);
47                 paymentRequestContainer = useContainer(Util.PAYMENT_REQUEST);
48                 paymentDoneContainer = useContainer(Util.PAYMENT_DONE);
49                 tableAssignedContainer = useContainer(Util.TABLE_ASSIGNED);
50         }
51
52         public void listenForFreeTable() {
53                 SpaceListener listener = new SpaceListenerImpl(capi, freeTablesContainer) {
54
55                         @Override
56                         void onEntriesWritten(List<? extends Serializable> entries) throws Exception {
57
58 //                              log.info("{} tables have become free", entries.size());
59
60                                 List<Table> tables = castEntries(entries);
61                                 Collections.rotate(tables, waiterId);
62
63                                 for (Table table : tables) {
64
65                                         TransactionReference tx = capi.createTransaction(
66                                                         Util.SPACE_TRANSACTION_TIMEOUT,
67                                                         URI.create(Util.SERVER_ADDR));
68
69                                         // Acquire a lock for the free table in the
70                                         // FreeTableContainer
71                                         int id = table.getId();
72
73                                         Table tableTemplate = new Table(id);
74                                         Table lockedFreeTable = takeMatchingEntity(tableTemplate,
75                                                         freeTablesContainer, tx, RequestTimeout.DEFAULT,
76                                                         String.format("There was no free table found with id %d", id));
77
78
79                                         GroupData groupTemplate = new GroupData();
80                                         GroupData lockedGroup = takeMatchingEntity(groupTemplate,
81                                                         assignTableContainer, tx, RequestTimeout.DEFAULT,
82                                                         "There is no group waiting for a table at the moment");
83
84                                         assignGroupToTable(lockedGroup, lockedFreeTable, tx);
85                                 }
86                         }
87                 };
88
89                 createNotification(listener, freeTablesContainer);
90         }
91
92
93
94         public void listenForNewGuests() {
95                 SpaceListener listener = new SpaceListenerImpl(capi, assignTableContainer) {
96                         
97                         @Override
98                         void onEntriesWritten(List<? extends Serializable> entries)
99                                         throws Exception {
100                                 log.info("New guest groups have arrived");
101
102                                 List<GroupData> groups = castEntries(entries);
103                                 Collections.rotate(groups, waiterId);
104
105                                 for (GroupData group : groups) {
106                                         
107                                         TransactionReference tx = capi.createTransaction(
108                                                 Util.SPACE_TRANSACTION_TIMEOUT,
109                                                 URI.create(Util.SERVER_ADDR));
110
111                                         // Acquire a lock for the group in the
112                                         // AssignTableContainer
113                                         String groupNotFound = String.format("Group with id %d was already assigned a table by another waiter!", group.getId());
114
115                                         GroupData lockedGroup = takeMatchingEntity(
116                                                         new GroupData(group.getId()),
117                                                         assignTableContainer, tx,
118                                                         RequestTimeout.DEFAULT, groupNotFound);
119                                         // Acquire a lock for one free table in the
120                                         // TablesContainer
121                                         String noFreeTable = String.format("No free table for group with id %d could be found", group.getId());
122                                         Table lockedFreeTable = takeMatchingEntity(new Table(null), freeTablesContainer, tx, RequestTimeout.DEFAULT,
123                                                         noFreeTable);
124
125                                         assignGroupToTable(lockedGroup, lockedFreeTable, tx);
126                                 }
127                         }
128                 };
129                 
130                 createNotification(listener, assignTableContainer);
131         }
132
133         public void listenForPaymentRequest() {
134                 SpaceListener paymentListener = new SpaceListenerImpl(capi, paymentRequestContainer) {
135                         
136                         @Override
137                         void onEntriesWritten(List<? extends Serializable> entries)
138                                         throws Exception {
139
140                                 List<GroupData> groups = castEntries(entries);
141                                 
142                                 for (GroupData groupData : groups) {
143                                         TransactionReference tx = capi.createTransaction(
144                                                         Util.SPACE_TRANSACTION_TIMEOUT,
145                                                         URI.create(Util.SERVER_ADDR));
146                                         GroupData entity = new GroupData(groupData.getId());
147
148                                         // Acquire the lock so that another waiter can't do the same
149                                         // thing!
150                                         String paymentRequestTakenByOtherWaiter = String.format(
151                                                         "The payment request for group %d was already taken by an other waiter!",
152                                                         groupData.getId());
153                                         takeMatchingEntity(entity, paymentRequestContainer, tx, RequestTimeout.DEFAULT, paymentRequestTakenByOtherWaiter);
154
155                                         groupData.setPayingWaiter(waiterId);
156
157                                         sendItemsToContainer(Arrays.asList(groupData), paymentDoneContainer, RequestTimeout.ZERO, tx);
158
159                                         capi.commitTransaction(tx);
160                                 }
161                         }
162                 };
163
164                 createNotification(paymentListener, paymentRequestContainer);
165         }
166
167         public void listenForOrderRequests() {
168                 SpaceListener ordersListener = new SpaceListenerImpl(capi, takeOrderContainer) {
169                         
170                         @Override
171                         void onEntriesWritten(List<? extends Serializable> entries)
172                                         throws Exception {
173
174                                 List<GroupData> groups = castEntries(entries);
175                 
176                                 for (GroupData groupData : groups) {
177
178                                         TransactionReference tx = capi.createTransaction(
179                                                         Util.SPACE_TRANSACTION_TIMEOUT,
180                                                         URI.create(Util.SERVER_ADDR));
181                                         GroupData entity = new GroupData(groupData.getId());
182                                         entity.setState(GroupState.SITTING);
183
184                                         // Acquire the lock so that another waiter can't do the same thing!
185                                         String orderTakenByOtherWaiter = String.format(
186                                                         "The order for group %d was already taken by an other waiter!",
187                                                         groupData.getId());
188                                         takeMatchingEntity(entity, takeOrderContainer, tx, RequestTimeout.DEFAULT, orderTakenByOtherWaiter);
189
190                                         groupData.setOrderWaiter(waiterId);
191                                         groupData.setState(GroupState.ORDERED);
192                                         Order order = groupData.getOrder();
193                                         order.setStatus(OrderStatus.ORDERED);
194
195                                         sendItemsToContainer(Arrays.asList(groupData),
196                                                         orderTakenContainer, RequestTimeout.ZERO, tx);
197                                         sendItemsToContainer(order.getOrderedPizzas(),
198                                                         preparePizzasContainer, RequestTimeout.ZERO, tx);
199                                         capi.commitTransaction(tx);
200
201                                         log.info("Waiter has taken order from group {}",
202                                                         groupData.getId());
203                                 } 
204                         }
205                 };
206                 
207                 createNotification(ordersListener, takeOrderContainer);
208         }
209
210         public void listenForPreparedPizzas() {
211                 /**
212                  * A waiter gets informed when a new pizza is complete. He takes the
213                  * orderId of the pizza and looks up the corresponding order from which
214                  * he gets the number of necessary pizzas of the order. He then tries to
215                  * fetch all pizzas with the corresponding orderId and compares the
216                  * number of those pizzas with the number of necessary pizzas. If all
217                  * pizzas of an order are complete he then delivers them!
218                  */
219                 SpaceListener preparedPizzasListener = new SpaceListenerImpl(capi, deliverPizzasContainer) {
220                                         
221                         @Override
222                         void onEntriesWritten(List<? extends Serializable> entries)
223                                         throws Exception {
224
225                                 List<Pizza> pizzas = castEntries(entries);
226                 
227                                 for (Pizza pizza : pizzas) {
228                                         int orderId = pizza.getOrderId();
229
230                                         TransactionReference tx = capi.createTransaction(
231                                                         Util.SPACE_TRANSACTION_TIMEOUT,
232                                                         URI.create(Util.SERVER_ADDR));
233
234                                         try {
235                                                 GroupData entity = new GroupData();
236                                                 entity.setState(null);
237                                                 Order order = new Order();
238                                                 order.setId(orderId);
239                                                 entity.setOrder(order);
240
241                                                 GroupData groupData = takeMatchingEntity(entity,
242                                                                 orderTakenContainer, tx, RequestTimeout.DEFAULT,
243                                                                 "Another waiter just checks if the order is complete");
244                                                 int groupId = groupData.getId();
245                                                 int numberOfPizzas = groupData.getOrder().getNumberOfPizzas();
246
247                                                 Pizza pizzaTemplate = new Pizza();
248                                                 pizzaTemplate.setOrderId(orderId);
249
250                                                 List<Pizza> pizzasOfOrder = takeMatchingEntities(
251                                                                 pizzaTemplate, deliverPizzasContainer, tx,
252                                                                 RequestTimeout.DEFAULT,
253                                                                 "Cannot take the pizzas from the deliverPizzasContainer");
254
255                                                 // for an unkown reason even pizzas not matching the orderid
256                                                 // are returned. that's why we have to check again if the
257                                                 // pizza really belongs to the order in question
258                                                 Iterator<Pizza> it = pizzasOfOrder.iterator();
259                                                 while (it.hasNext()) {
260                                                         Pizza nextPizza = it.next();
261                                                         if (nextPizza.getOrderId() != orderId) {
262                                                                 log.info("WRONG!");
263                                                                 it.remove();
264                                                         }
265                                                 }
266
267                                                 if (pizzasOfOrder.size() == numberOfPizzas) {
268                                                         GroupData group = new GroupData();
269                                                         group.setServingWaiter(waiterId);
270                                                         Order completeOrder = new Order();
271                                                         completeOrder.setId(orderId);
272                                                         completeOrder.setGroupId(groupId);
273                                                         group.setOrder(completeOrder);
274                                                         sendItemsToContainer(Arrays.asList(group),
275                                                                         orderCompleteContainer, RequestTimeout.DEFAULT,
276                                                                         tx);
277                                                         capi.commitTransaction(tx);
278                                                 } else {
279                                                         log.info("Not yet all pizzas prepared! Order with id "
280                                                                         + orderId + " has " + numberOfPizzas
281                                                                         + " pizzas, but only " + pizzasOfOrder.size()
282                                                                         + " pizzas are ready by now!");
283                                                         capi.rollbackTransaction(tx);
284                                                 }
285                                         } catch (NullPointerException e) {
286                                                 
287                                         } catch (Exception e) {
288                                                 capi.rollbackTransaction(tx);
289                                         }
290                                 }
291                         }
292                 };
293
294                 createNotification(preparedPizzasListener, deliverPizzasContainer);
295         }
296
297         private void assignGroupToTable(GroupData lockedGroup,
298                         Table lockedFreeTable, TransactionReference tx)
299                         throws MzsCoreException {
300                 // The new group sits down at the table
301                 lockedFreeTable.setGroupId(lockedGroup.getId());
302
303                 // The new group now wants to order
304                 lockedGroup.setState(GroupState.SITTING);
305                 lockedGroup.setTable(lockedFreeTable);
306                 lockedGroup.setTableWaiter(waiterId);
307
308                 sendItemsToContainer(Arrays.asList(lockedFreeTable),
309                                 tableAssignedContainer, RequestTimeout.ZERO, tx);
310                 sendItemsToContainer(Arrays.asList(lockedGroup), takeOrderContainer,
311                                 RequestTimeout.ZERO, tx);
312
313                 try {
314                         capi.commitTransaction(tx);
315                         log.info("Assigned table to group with groupId {}",
316                                         lockedGroup.getId());
317                 } catch (Exception e) {
318                         log.info("Assigning a table to group with groupId {} failed",
319                                         lockedGroup.getId());
320                 }
321         }
322
323 }