From 2bd6d16e529ade64dc6272868a79b7d02b963b32 Mon Sep 17 00:00:00 2001 From: Gregor Riegler Date: Wed, 8 May 2013 17:25:50 +0200 Subject: [PATCH] Waiter listens for free tables and for new guests. When a table gets free he tries to assign a waiting group to it. If a new guest arrives, he tries to found a free table and assign it. --- .../tuwien/sbc/valesriegler/common/Util.java | 7 + .../tuwien/sbc/valesriegler/group/Group.java | 6 + .../valesriegler/pizzeria/PizzeriaAgent.java | 8 +- .../gui/tablemodels/TablesOverviewModel.java | 2 +- .../sbc/valesriegler/types/GroupData.java | 10 +- .../tuwien/sbc/valesriegler/types/Table.java | 36 ++- .../xvsm/EntityNotFoundByTemplate.java | 25 ++ .../sbc/valesriegler/xvsm/XVSMConnector.java | 247 +++++++++++++++++- .../sbc/valesriegler/xvsm/waiter/Waiter.java | 44 ++-- 9 files changed, 344 insertions(+), 41 deletions(-) create mode 100644 src/main/java/at/ac/tuwien/sbc/valesriegler/xvsm/EntityNotFoundByTemplate.java diff --git a/src/main/java/at/ac/tuwien/sbc/valesriegler/common/Util.java b/src/main/java/at/ac/tuwien/sbc/valesriegler/common/Util.java index 1f0a1bc..d95ad31 100644 --- a/src/main/java/at/ac/tuwien/sbc/valesriegler/common/Util.java +++ b/src/main/java/at/ac/tuwien/sbc/valesriegler/common/Util.java @@ -16,7 +16,14 @@ public abstract class Util { public static final String TABLES_CONTAINER = "tables"; public static final String GROUPS_CONTAINER = "groups"; + public static final String ASSIGN_TABLE = "assignTable"; + public static final String TAKE_ORDER = "takeOrder"; + public static final String DELIVER_PIZZAS = "deliverPizzas"; + public static final String PAYMENT = "payment"; + public static final String FREE_TABLES = "freeTables"; + public static final String SERVER_ADDR = "xvsm://localhost:9876"; + public static ContainerReference getOrCreateNamedContainer(final String spaceUri, final String containerName, final Capi capi, final List coordinators) throws MzsCoreException { diff --git a/src/main/java/at/ac/tuwien/sbc/valesriegler/group/Group.java b/src/main/java/at/ac/tuwien/sbc/valesriegler/group/Group.java index 03fec01..816b280 100644 --- a/src/main/java/at/ac/tuwien/sbc/valesriegler/group/Group.java +++ b/src/main/java/at/ac/tuwien/sbc/valesriegler/group/Group.java @@ -1,5 +1,7 @@ package at.ac.tuwien.sbc.valesriegler.group; +import java.io.Serializable; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,6 +34,10 @@ public class Group implements Runnable, HasId { public Group() { groupData = new GroupData(++idNext); } + + public Group(Integer id) { + groupData = new GroupData(id); + } @Override public void run() { diff --git a/src/main/java/at/ac/tuwien/sbc/valesriegler/pizzeria/PizzeriaAgent.java b/src/main/java/at/ac/tuwien/sbc/valesriegler/pizzeria/PizzeriaAgent.java index 6732999..76d73aa 100644 --- a/src/main/java/at/ac/tuwien/sbc/valesriegler/pizzeria/PizzeriaAgent.java +++ b/src/main/java/at/ac/tuwien/sbc/valesriegler/pizzeria/PizzeriaAgent.java @@ -82,11 +82,13 @@ public class PizzeriaAgent { xvsm.initSpaceCommunication(); xvsm.useTablesContainer(); xvsm.useGroupsContainer(); + xvsm.useFreeTablesContainer(); NotificationListener tablesListener = new NotificationListener() { @Override public void entryOperationFinished(final Notification notification, final Operation operation, final List entries) { - + log.info("Tables Change notified"); + final List tables = new ArrayList<>(); List entryList = (List) entries; for (Entry entry : entryList) { @@ -108,7 +110,7 @@ public class PizzeriaAgent { PizzeriaAgent.getInstance().getGroupModel().addItems(groups); } }; - xvsm.initTablesNotifications(tablesListener); +// xvsm.initTablesNotifications(tablesListener); xvsm.initGroupNotifications(groupsListener); } @@ -126,7 +128,7 @@ public class PizzeriaAgent { @Override public void freeTablesCreated(List
tables) { if(!Util.useJMS) { - xvsm.sendTablesToSpace(tables); + xvsm.sendFreeTablesToSpace(tables); } } diff --git a/src/main/java/at/ac/tuwien/sbc/valesriegler/pizzeria/gui/tablemodels/TablesOverviewModel.java b/src/main/java/at/ac/tuwien/sbc/valesriegler/pizzeria/gui/tablemodels/TablesOverviewModel.java index c67a67d..a53474b 100644 --- a/src/main/java/at/ac/tuwien/sbc/valesriegler/pizzeria/gui/tablemodels/TablesOverviewModel.java +++ b/src/main/java/at/ac/tuwien/sbc/valesriegler/pizzeria/gui/tablemodels/TablesOverviewModel.java @@ -60,7 +60,7 @@ public class TablesOverviewModel extends TableModel
{ tables.add(table); } - setItems(tables); +// setItems(tables); return tables; } diff --git a/src/main/java/at/ac/tuwien/sbc/valesriegler/types/GroupData.java b/src/main/java/at/ac/tuwien/sbc/valesriegler/types/GroupData.java index 3370d5e..383a439 100644 --- a/src/main/java/at/ac/tuwien/sbc/valesriegler/types/GroupData.java +++ b/src/main/java/at/ac/tuwien/sbc/valesriegler/types/GroupData.java @@ -21,7 +21,7 @@ import at.ac.tuwien.sbc.valesriegler.common.HasId; public class GroupData implements Serializable, HasId { private static final Logger log = LoggerFactory.getLogger(GroupData.class); - final private Integer id; + private Integer id; private GroupState state = GroupState.NEW; private Integer size; @@ -35,10 +35,18 @@ public class GroupData implements Serializable, HasId { @Deprecated private List pizzas = new ArrayList(); + + public GroupData() { + + } public GroupData(Integer id) { this.id = id; } + + public void setId(Integer id) { + this.id = id; + } public int getId() { return id; diff --git a/src/main/java/at/ac/tuwien/sbc/valesriegler/types/Table.java b/src/main/java/at/ac/tuwien/sbc/valesriegler/types/Table.java index 887be61..ba31ff5 100644 --- a/src/main/java/at/ac/tuwien/sbc/valesriegler/types/Table.java +++ b/src/main/java/at/ac/tuwien/sbc/valesriegler/types/Table.java @@ -2,6 +2,8 @@ package at.ac.tuwien.sbc.valesriegler.types; import java.io.Serializable; +import org.mozartspaces.capi3.Queryable; + import at.ac.tuwien.sbc.valesriegler.common.HasId; @@ -12,13 +14,14 @@ import at.ac.tuwien.sbc.valesriegler.common.HasId; * @author jan * */ +@Queryable(autoindex=true) public class Table implements Serializable, HasId { private static int idNext = 0; - private final int id; + private final Integer id; - private int groupId = -1; + private Integer groupId = -1; - public Table(int id) { + public Table(Integer id) { this.id = id; } @@ -49,4 +52,31 @@ public class Table implements Serializable, HasId { return "Table [id=" + id + "]"; } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((id == null) ? 0 : id.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Table other = (Table) obj; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + return true; + } + + + } diff --git a/src/main/java/at/ac/tuwien/sbc/valesriegler/xvsm/EntityNotFoundByTemplate.java b/src/main/java/at/ac/tuwien/sbc/valesriegler/xvsm/EntityNotFoundByTemplate.java new file mode 100644 index 0000000..3c3884c --- /dev/null +++ b/src/main/java/at/ac/tuwien/sbc/valesriegler/xvsm/EntityNotFoundByTemplate.java @@ -0,0 +1,25 @@ +package at.ac.tuwien.sbc.valesriegler.xvsm; + +public class EntityNotFoundByTemplate extends RuntimeException { + + public EntityNotFoundByTemplate() { + } + + public EntityNotFoundByTemplate(String message) { + super(message); + } + + public EntityNotFoundByTemplate(Throwable cause) { + super(cause); + } + + public EntityNotFoundByTemplate(String message, Throwable cause) { + super(message, cause); + } + + public EntityNotFoundByTemplate(String message, Throwable cause, + boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + +} diff --git a/src/main/java/at/ac/tuwien/sbc/valesriegler/xvsm/XVSMConnector.java b/src/main/java/at/ac/tuwien/sbc/valesriegler/xvsm/XVSMConnector.java index b79fa63..df53848 100644 --- a/src/main/java/at/ac/tuwien/sbc/valesriegler/xvsm/XVSMConnector.java +++ b/src/main/java/at/ac/tuwien/sbc/valesriegler/xvsm/XVSMConnector.java @@ -4,16 +4,19 @@ import java.io.Serializable; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; -import javax.swing.SwingUtilities; - +import org.bouncycastle.crypto.RuntimeCryptoException; import org.mozartspaces.capi3.AnyCoordinator; +import org.mozartspaces.capi3.AnyCoordinator.AnySelector; import org.mozartspaces.capi3.Coordinator; +import org.mozartspaces.capi3.CountNotMetException; import org.mozartspaces.capi3.FifoCoordinator; import org.mozartspaces.capi3.LindaCoordinator; import org.mozartspaces.capi3.LindaCoordinator.LindaSelector; import org.mozartspaces.core.Capi; +import org.mozartspaces.core.CapiUtil; import org.mozartspaces.core.ContainerReference; import org.mozartspaces.core.DefaultMzsCore; import org.mozartspaces.core.Entry; @@ -29,8 +32,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import at.ac.tuwien.sbc.valesriegler.common.Util; -import at.ac.tuwien.sbc.valesriegler.group.GroupAgent; -import at.ac.tuwien.sbc.valesriegler.pizzeria.PizzeriaAgent; import at.ac.tuwien.sbc.valesriegler.types.GroupData; import at.ac.tuwien.sbc.valesriegler.types.GroupState; import at.ac.tuwien.sbc.valesriegler.types.Order; @@ -47,10 +48,16 @@ public class XVSMConnector { private ContainerReference tablesContainer; private ContainerReference groupsContainer; + private ContainerReference assignTableContainer; + private ContainerReference takeOrderContainer; + private ContainerReference deliverPizzasContainer; + private ContainerReference paymentContainer; + private ContainerReference freeTablesContainer; // private ContainerReference notificationContainer; private Capi capi; private NotificationManager notificationMgr; + public void initSpaceCommunication() { try { @@ -64,25 +71,46 @@ public class XVSMConnector { } public void useTablesContainer() { - try { - tablesContainer = Util.getOrCreateNamedContainer(Util.SERVER_ADDR, Util.TABLES_CONTAINER, capi, createCoordinators(new AnyCoordinator())); - } catch (MzsCoreException e) { - handleSpaceErrorAndTerminate(e); - } + tablesContainer = useContainer(Util.TABLES_CONTAINER, createCoordinators(new AnyCoordinator(), new LindaCoordinator(false))) ; + } + + public void useTakeOrderContainer() { + takeOrderContainer = useContainer(Util.TAKE_ORDER, createCoordinators(new AnyCoordinator(), new LindaCoordinator(false))) ; } public void useGroupsContainer() { + groupsContainer = useContainer(Util.GROUPS_CONTAINER, createCoordinators(new AnyCoordinator(), new LindaCoordinator(false))) ; + } + + public void useAssignTableContainer() { + assignTableContainer = useContainer(Util.ASSIGN_TABLE, createCoordinators(new AnyCoordinator(), new LindaCoordinator(false))) ; + } + + public void useFreeTablesContainer() { + freeTablesContainer = useContainer(Util.FREE_TABLES, createCoordinators(new AnyCoordinator(), new LindaCoordinator(false))); + } + + + + private ContainerReference useContainer(String containerName, List coordinators) { try { - groupsContainer = Util.getOrCreateNamedContainer(Util.SERVER_ADDR, Util.GROUPS_CONTAINER, capi, createCoordinators(new AnyCoordinator(), new LindaCoordinator(false))); + return Util.getOrCreateNamedContainer(Util.SERVER_ADDR, containerName, capi, coordinators); } catch (MzsCoreException e) { handleSpaceErrorAndTerminate(e); } + + return null; } public void sendTablesToSpace(List
tables) { sendItemsToContainer(tables, tablesContainer); } + + public void sendFreeTablesToSpace(List
tables) { + sendItemsToContainer(tables, freeTablesContainer); + sendTablesToSpace(tables); + } public void sendGroupsToSpace(List newGroups) { @@ -214,18 +242,215 @@ public class XVSMConnector { private void sendItemsToContainer(List items, ContainerReference cref) { + try { List entries = new ArrayList<>(); for (Serializable item : items) { entries.add(new Entry(item)); } capi.write(entries, cref); + } catch (Exception e) { + log.info(e.getMessage()); + e.printStackTrace(); + } + } + + public void listenForFreeTable() { + + NotificationListener freeTableListener = new NotificationListener() { + @Override + public void entryOperationFinished(final Notification notification, final Operation operation, final List entries) { + + log.info("{} tables have become free", entries.size()); + + List
tables = castEntries(entries); + Collections.shuffle(tables); + + for (Table table : tables) { + try { + TransactionReference tx = capi.createTransaction(RequestTimeout.INFINITE, URI.create(Util.SERVER_ADDR)); + + try { + // Acquire a lock for the free table in the FreeTableContainer + int id = table.getId(); + log.info("Try to find the table with id {}", id); + + Table lockedFreeTable = takeEntityByTemplateFromContainer(new Table(id), freeTablesContainer, tx, 1000, String.format("There was no free table found with id %d", id)); + + log.info("Table with id {} was found", id); + GroupData lockedGroup = takeEntityByTemplateFromContainer(new GroupData(), assignTableContainer, tx, 1000, "There is no group waiting for a table at the moment"); + + // The new group sits down at the table + lockedFreeTable.setGroupId(lockedGroup.getId()); + lockedGroup.setTable(lockedFreeTable); + + // The new group now wants to order + lockedGroup.setState(GroupState.ORDER_PENDING); + + sendItemsToContainer(Arrays.asList(lockedFreeTable), tablesContainer); + sendItemsToContainer(Arrays.asList(lockedGroup), groupsContainer); + sendItemsToContainer(Arrays.asList(lockedGroup), takeOrderContainer); + + log.info("Assigned Table to group with groupId {}", lockedGroup.getId()); + capi.commitTransaction(tx); + + } catch (IllegalArgumentException e) { + log.error("SHOULD NEVER HAPPEN!"); + System.exit(1); + } catch (CountNotMetException e) { + log.error("SHOULD NEVER HAPPEN!"); + System.exit(1); + log.info(e.getMessage()); + } catch (EntityNotFoundByTemplate e) { + // TODO check why another exception is thrown +// capi.rollbackTransaction(tx); + } catch (Exception e) { + log.error("SHOULD NEVER HAPPEN!"); + e.printStackTrace(); + log.info(e.getMessage()); + + capi.rollbackTransaction(tx); + } + } catch (Exception e) { + log.error("SHOULD NEVER HAPPEN!"); + + e.printStackTrace(); + } + + } + } + }; + try { + notificationMgr.createNotification(freeTablesContainer, freeTableListener, Operation.WRITE); + log.info("Created freeTablesContainer notification for a waiter"); + } catch (MzsCoreException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + } + + + public void listenForNewGuests() { + NotificationListener newGroupsListener = new NotificationListener() { + @Override + public void entryOperationFinished(final Notification notification, final Operation operation, final List entries) { + + log.info("New guest groups have arrived"); + + List groups = castEntries(entries); + Collections.shuffle(groups); + + for (GroupData group : groups) { + try { + TransactionReference tx = capi.createTransaction(1500, URI.create(Util.SERVER_ADDR)); + + try { + // Acquire a lock for the group in the AssignTableContainer + String groupNotFound = String.format("Group with id %d was already assigned a table by another waiter!", group.getId()); + GroupData lockedGroup = takeEntityByTemplateFromContainer(new GroupData(group.getId()), assignTableContainer, tx, 1000, groupNotFound); + // Acquire a lock for one free table in the TablesContainer + Table lockedFreeTable = takeEntityByTemplateFromContainer(new Table(), freeTablesContainer, tx, 1000, String.format("No free table for group with id %d could be found", group.getId())); + + // The new group sits down at the table + lockedFreeTable.setGroupId(lockedGroup.getId()); + lockedGroup.setTable(lockedFreeTable); + + // The new group now wants to order + lockedGroup.setState(GroupState.ORDER_PENDING); + + sendItemsToContainer(Arrays.asList(lockedFreeTable), tablesContainer); + sendItemsToContainer(Arrays.asList(lockedGroup), groupsContainer); + sendItemsToContainer(Arrays.asList(lockedGroup), takeOrderContainer); + + log.info("Assigned Table to group with groupId {}", lockedGroup.getId()); + capi.commitTransaction(tx); + + } catch (IllegalArgumentException e) { + log.error("SHOULD NEVER HAPPEN!"); + System.exit(1); + } catch (CountNotMetException e) { + log.error("SHOULD NEVER HAPPEN!"); + System.exit(1); + log.info(e.getMessage()); + } catch (EntityNotFoundByTemplate e) { +// capi.rollbackTransaction(tx); + } catch (Exception e) { + log.error("SHOULD NEVER HAPPEN!"); + e.printStackTrace(); + log.info(e.getMessage()); + + capi.rollbackTransaction(tx); + } + } catch (Exception e) { + log.error("SHOULD NEVER HAPPEN!"); + + e.printStackTrace(); + } + + } + } + }; + try { + notificationMgr.createNotification(assignTableContainer, newGroupsListener, Operation.WRITE); + log.info("Created assingTableContainer notification for a waiter"); } catch (MzsCoreException e) { - log.info(e.getMessage()); + e.printStackTrace(); + } catch (InterruptedException e) { e.printStackTrace(); } } + + private GroupData getGroupWaitingForTable(GroupData group, + TransactionReference tx) throws MzsCoreException { + + + GroupData singleGroup = takeEntityByTemplateFromContainer(new GroupData(group.getId()), assignTableContainer, tx, 1000, String.format("Group with id %d was already assigned a table by another waiter!", group.getId())); + + + return singleGroup; + + } + + private Table getFreeTableForGroup(GroupData group, + TransactionReference tx) throws MzsCoreException { + + return takeEntityByTemplateFromContainer(new Table(), freeTablesContainer, tx, 1000, String.format("No free table for group with id %d could be found", group.getId())); + + + } + + @SuppressWarnings("unchecked") + private T takeEntityByTemplateFromContainer(T entity, ContainerReference ref, TransactionReference tx, long timeout, String errorMsg) throws MzsCoreException { + try { + LindaSelector sel = LindaCoordinator.newSelector(entity, 1); + T singleEntity = null; + + ArrayList entities = capi.take(ref, sel, RequestTimeout.DEFAULT, tx); + log.info("Returned {} entities", entities.size()); + + return (T) CapiUtil.getSingleEntry(entities); + } catch (CountNotMetException e) { + log.info(errorMsg); + capi.rollbackTransaction(tx); + + throw new EntityNotFoundByTemplate(errorMsg); + } + } + + + + private List castEntries(List entries) { + List newList = new ArrayList(); + List newEntries = (List) entries; + for (Entry entry : newEntries) { + newList.add((T) entry.getValue()); + } + return newList; + } + } diff --git a/src/main/java/at/ac/tuwien/sbc/valesriegler/xvsm/waiter/Waiter.java b/src/main/java/at/ac/tuwien/sbc/valesriegler/xvsm/waiter/Waiter.java index 9c724bc..8942ea7 100644 --- a/src/main/java/at/ac/tuwien/sbc/valesriegler/xvsm/waiter/Waiter.java +++ b/src/main/java/at/ac/tuwien/sbc/valesriegler/xvsm/waiter/Waiter.java @@ -1,10 +1,21 @@ package at.ac.tuwien.sbc.valesriegler.xvsm.waiter; import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import javax.swing.SwingUtilities; + +import org.mozartspaces.core.Entry; +import org.mozartspaces.notifications.Notification; +import org.mozartspaces.notifications.NotificationListener; +import org.mozartspaces.notifications.Operation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import at.ac.tuwien.sbc.valesriegler.pizzeria.PizzeriaAgent; +import at.ac.tuwien.sbc.valesriegler.types.GroupData; +import at.ac.tuwien.sbc.valesriegler.types.Table; import at.ac.tuwien.sbc.valesriegler.xvsm.XVSMConnector; /** @@ -38,31 +49,20 @@ public class Waiter implements Serializable { } private void start() { - initSpaceCommunication(); - - while(true) { - try { - Thread.sleep(3000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - xvsm.handleWaitingGroup(id); - - xvsm.handleOrderRequest(id); - - xvsm.handlePaymentRequest(); - - xvsm.handlePizzaDistribution(); - } - } - - - private void initSpaceCommunication() { xvsm = new XVSMConnector(); xvsm.initSpaceCommunication(); + + xvsm.useFreeTablesContainer(); + xvsm.useAssignTableContainer(); xvsm.useTablesContainer(); - xvsm.useGroupsContainer(); + xvsm.useTakeOrderContainer(); + + // when new guests arrive the waiter should try to assign a table to them + xvsm.listenForNewGuests(); + + // when tables get free the waiter should have a look if there are waiting guests and assign the new table to them + xvsm.listenForFreeTable(); + } -- 2.43.0