Commit 938048d9 by Tuomas Riihimäki

Map queue stuff..

Queues should not be global, but map specific
1 parent 80f50ff5
package fi.codecrew.moya.beans;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Set;
......@@ -25,12 +26,15 @@ public interface QueueBeanLocal {
MapReservationQueueEntry remove(EventMap map, EventUser user);
int getMinimumSlotsInQueue();
Date getReservationTimeout(EventMap map);
void setMinimumSlotsInQueue(int minimumSlotsInQueue);
MapQueueI getMapQueue(EventMap map);
public interface MapQueueI {
Date getReservationTimeout(EventMap map);
Collection<MapReservationQueueEntry> getQueue();
Collection<EventUser> getReserving();
void setReservingSize(int reservingSize);
......@@ -40,21 +44,16 @@ public interface QueueBeanLocal {
int getDefaultTimeoutMin();
MapQueueI getMapQueue(EventMap map);
public interface MapQueueI {
ConcurrentSkipListSet<MapReservationQueueEntry> getQueue();
int getMinimumSlotsInQueue();
Set<EventUser> getReserving();
void setMinimumSlotsInQueue(int minimumSlotsInQueue);
MapReservationQueueEntry getEntry(EventUser u);
}
void forceAddToReserving(EventMap map, EventUser u, Date time);
void forceRemove(EventMap e, EventUser u);
void forceRemove(EventMap e, EventUser u);
}
package fi.codecrew.moya.beans;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.security.DeclareRoles;
......@@ -26,11 +24,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import fi.codecrew.moya.enums.apps.MapPermission;
import fi.codecrew.moya.enums.apps.UserPermission;
import fi.codecrew.moya.facade.PlaceSlotFacade;
import fi.codecrew.moya.model.EventMap;
import fi.codecrew.moya.model.EventUser;
import fi.codecrew.moya.model.LanEventProperty;
import fi.codecrew.moya.model.LanEventPropertyKey;
import fi.codecrew.moya.model.PlaceSlot;
import fi.codecrew.moya.model.map.MapReservationQueueEntry;
......@@ -56,17 +52,25 @@ public class QueueBean implements QueueBeanLocal {
private PlaceBeanLocal placebean;
private final ConcurrentHashMap<Integer, MapQueue> mapqueues = new ConcurrentHashMap<Integer, QueueBean.MapQueue>();
private int defaultTimeoutMin = 10;
private int minimumSlotsInQueue = 9999;
private int reservingSize = 1;
private class MapQueue implements MapQueueI {
private static class MapQueue implements MapQueueI {
private int defaultTimeoutMin = 10;
private int minimumSlotsInQueue = 1;
private int reservingSize = 5;
// private final Set<MapReservationQueueEntry> reserving = new HashSet<>();
private final Set<EventUser> reserving = Collections.newSetFromMap(new ConcurrentHashMap<EventUser, Boolean>());
private final ConcurrentSkipListSet<MapReservationQueueEntry> queue = new ConcurrentSkipListSet<>();
private volatile Set<EventUser> reserving = Collections.newSetFromMap(new ConcurrentHashMap<>());
private final PriorityQueue<MapReservationQueueEntry> queue = new PriorityQueue<>();
private final ConcurrentMap<EventUser, MapReservationQueueEntry> queEntries = new ConcurrentHashMap<>();
private final Integer mapId;
private static final Logger logger = LoggerFactory.getLogger(MapQueue.class);
public MapQueue(EventMap map) {
this.mapId = map.getId();
private void timeoutEntries() {
}
void timeoutEntries() {
logger.info("Timeouting entries");
// give 10 seconds mercy ( and give us some time to go through all entries)
Date now = new Date(System.currentTimeMillis() + 1000 * 15);
......@@ -107,14 +111,17 @@ public class QueueBean implements QueueBeanLocal {
}
private final Object reservingLock = new Object();
private void checkReservingEntry() {
if (reserving.size() < reservingSize) {
synchronized (queue) {
if (reserving.size() < reservingSize) {
MapReservationQueueEntry queEntry = queue.pollLast();
if (reserving.size() < getReservingSize()) {
synchronized (reservingLock) {
if (reserving.size() < getReservingSize()) {
MapReservationQueueEntry queEntry = queue.poll();
if (queEntry != null) {
reserving.add(queEntry.getUser());
queEntry.setReservationTimeout(new Date(System.currentTimeMillis() + defaultTimeoutMin * 60 * 1000));
queEntry.setReservationTimeout(new Date(System.currentTimeMillis() + getDefaultTimeoutMin() * 60 * 1000));
}
}
}
......@@ -127,37 +134,23 @@ public class QueueBean implements QueueBeanLocal {
return null;
}
MapReservationQueueEntry ret = null;
synchronized (queue) {
if (reserving.remove(user)) {
logger.info("Removed user {} from reserving queue", user);
}
MapReservationQueueEntry entry = queEntries.get(user);
reserving.remove(user);
MapReservationQueueEntry entry = queEntries.remove(user);
if (entry != null) {
// There should neve be more than one instance, but make sure
while (queue.remove(entry)) {
logger.info("Removed user {} from queue");
}
ret = queEntries.remove(user);
checkReservingEntry();
}
checkReservingEntry();
return ret;
}
public MapReservationQueueEntry enter(EventUser user) {
MapReservationQueueEntry ret = queEntries.get(user);
synchronized (queue) {
if (!reserving.contains(user) && (ret == null || !queue.contains(ret))) {
if (ret == null) {
ret = new MapReservationQueueEntry();
ret.setUser(user);
ret.setSeenTime(new Date());
ret.setPlaceslotcount(placebean.getPlaceslots(user).size());
}
queEntries.put(user, ret);
boolean queStat = queue.add(ret);
logger.info("User {} not in queue, offer state {}", user, queStat);
public MapReservationQueueEntry enter(EventUser user, List<PlaceSlot> slots) {
MapReservationQueueEntry ret = initEntry(user, slots);
if (!reserving.contains(user) && !queue.contains(ret)) {
queue.add(ret);
// Check if the user can be put to reservation queue immediately
checkReservingEntry();
......@@ -165,32 +158,36 @@ public class QueueBean implements QueueBeanLocal {
ret = queEntries.get(user);
logger.info("User {} already in queue. Not entering again {}", user, ret);
}
}
return ret;
}
public void forceAdd(EventUser u, Date time) {
MapReservationQueueEntry entry = queEntries.get(u);
synchronized (queue)
{
if (entry == null) {
entry = new MapReservationQueueEntry();
entry.setCreated(new Date());
entry.setUser(u);
entry.setPlaceslotcount(placebean.getPlaceslots(u).size());
queEntries.put(u, entry);
}
public void forceAdd(EventUser u, Date time, List<PlaceSlot> slots) {
MapReservationQueueEntry entry = initEntry(u, slots);
entry.setReservationTimeout(time);
entry.setSeenTime(new Date());
reserving.remove(u);
queue.remove(entry);
reserving.add(u);
}
private MapReservationQueueEntry initEntry(EventUser user, List<PlaceSlot> slots) {
MapReservationQueueEntry ret = queEntries.get(user);
if (ret == null) {
ret = new MapReservationQueueEntry();
ret.setUser(user);
ret.setSeenTime(new Date());
ret.setPlaceslotcount(slots.size());
MapReservationQueueEntry old = queEntries.putIfAbsent(user, ret);
if (old != null) {
ret = old;
}
}
queEntries.put(user, ret);
return ret;
}
public Integer getPosition(EventUser user) {
......@@ -201,18 +198,15 @@ public class QueueBean implements QueueBeanLocal {
logger.info("User in reserving queue {}", user);
} else if (entry != null && queue.contains(entry)) {
ret = 1;
for (Iterator<MapReservationQueueEntry> iterator = queue.descendingIterator(); iterator.hasNext();) {
MapReservationQueueEntry eu = iterator.next();
if (eu.getUser().equals(user)) {
for (Iterator<MapReservationQueueEntry> iterator = queue.iterator(); iterator.hasNext();) {
if (iterator.next().getUser().equals(user)) {
break;
}
++ret;
}
logger.info("User is in queue {}, position {}", user, ret);
} else {
logger.info("Not in queue, while checking position");
}
logger.info("Got position {} for user {}", ret, user);
return ret;
}
......@@ -225,15 +219,39 @@ public class QueueBean implements QueueBeanLocal {
return queEntries.get(user);
}
public Set<EventUser> getReserving() {
public Collection<EventUser> getReserving() {
return reserving;
}
@Override
public ConcurrentSkipListSet<MapReservationQueueEntry> getQueue() {
public PriorityQueue<MapReservationQueueEntry> getQueue() {
return queue;
}
public int getMinimumSlotsInQueue() {
return minimumSlotsInQueue;
}
public void setMinimumSlotsInQueue(int minimumSlotsInQueue) {
this.minimumSlotsInQueue = minimumSlotsInQueue;
}
public int getDefaultTimeoutMin() {
return defaultTimeoutMin;
}
public void setDefaultTimeoutMin(int defaultTimeoutMin) {
this.defaultTimeoutMin = defaultTimeoutMin;
}
public int getReservingSize() {
return reservingSize;
}
public void setReservingSize(int reservingSize) {
this.reservingSize = reservingSize;
}
}
@EJB
......@@ -287,7 +305,7 @@ public class QueueBean implements QueueBeanLocal {
// DO housekeeping every 10 seconds.
nextReservingTimeoutCheck.compareAndSet(oldTime, System.currentTimeMillis() + 1000 * 10);
} catch (Throwable t) {
} catch (Exception t) {
logger.warn("Exception while checking reservingTimeouts", t);
}
}
......@@ -300,7 +318,7 @@ public class QueueBean implements QueueBeanLocal {
MapQueue ret = mapqueues.get(map.getId());
if (ret == null) {
ret = new MapQueue();
ret = new MapQueue(map);
MapQueue nret = mapqueues.putIfAbsent(map.getId(), ret);
if (nret != null) {
ret = nret;
......@@ -361,7 +379,8 @@ public class QueueBean implements QueueBeanLocal {
@RolesAllowed(MapPermission.S_MANAGE_MAPS)
public void forceAddToReserving(EventMap map, EventUser u, Date time) {
MapQueue q = getMapque(map);
q.forceAdd(u, time);
List<PlaceSlot> slots = slotfacade.findFreePlaceSlots(u, map);
q.forceAdd(u, time, slots);
}
......@@ -376,7 +395,6 @@ public class QueueBean implements QueueBeanLocal {
throw new RuntimeException("Use " + permbean.getCurrentUser() + "tried to enter to queue in behalf of another user: " + user);
if (user == null || user.isAnonymous()) {
return null;
}
MapQueue queue = getMapque(map);
......@@ -386,30 +404,16 @@ public class QueueBean implements QueueBeanLocal {
logger.info("User {} already in queue");
ret = queue.getEntry(user);
} else {
List<PlaceSlot> slots = slotfacade.findFreePlaceSlotsForProduct(user, null);
List<PlaceSlot> slots = slotfacade.findFreePlaceSlots(user, map);
logger.info("User {} not yet in queue. User has {} slots", user, slots.size());
if (!slots.isEmpty() && slots.size() >= getMinimumSlotsInQueue()) {
ret = queue.enter(user);
logger.info("Entered queue: {}", ret);
if (!slots.isEmpty() && slots.size() >= queue.getMinimumSlotsInQueue()) {
ret = queue.enter(user, slots);
}
}
return ret;
}
@Override
@Lock(LockType.READ)
public int getMinimumSlotsInQueue() {
return minimumSlotsInQueue;
}
@Override
@Lock(LockType.READ)
@RolesAllowed(MapPermission.S_MANAGE_MAPS)
public void setMinimumSlotsInQueue(int minimumSlotsInQueue) {
this.minimumSlotsInQueue = minimumSlotsInQueue;
}
@Lock(LockType.READ)
@RolesAllowed(MapPermission.S_MANAGE_MAPS)
@Override
......@@ -420,31 +424,6 @@ public class QueueBean implements QueueBeanLocal {
@Override
@Lock(LockType.READ)
public int getDefaultTimeoutMin() {
return defaultTimeoutMin;
}
@Override
@Lock(LockType.READ)
public void setDefaultTimeoutMin(int defaultTimeoutMin) {
this.defaultTimeoutMin = defaultTimeoutMin;
}
@Override
@Lock(LockType.READ)
public int getReservingSize() {
return reservingSize;
}
@Override
@Lock(LockType.READ)
@RolesAllowed(MapPermission.S_MANAGE_MAPS)
public void setReservingSize(int reservingSize) {
this.reservingSize = reservingSize;
}
@Override
@Lock(LockType.READ)
@RolesAllowed(MapPermission.S_MANAGE_MAPS)
public void forceRemove(EventMap e, EventUser u) {
MapQueue q = getMapque(e);
......
......@@ -31,6 +31,7 @@ public class MapReservationQueueEntry extends GenericEntity implements Comparabl
*
*/
private static final long serialVersionUID = -1529588850152306791L;
private static final Logger logger = LoggerFactory.getLogger(MapReservationQueueEntry.class);
@Column()
@JoinColumn(nullable = false)
......@@ -46,41 +47,12 @@ public class MapReservationQueueEntry extends GenericEntity implements Comparabl
private Date reservationTimeout;
@Transient
private Date seenTime;
private Date seenTime = new Date();
/** Initialized in bean and repopulated when fetching from database */
@Transient
private Integer placeslotcount;
// @OneToOne()
// @JoinColumn(nullable = true)
// private MapReservationQueueEntry previous;
//
// @OneToOne(mappedBy = "previous")
// private MapReservationQueueEntry next;
private static final Logger logger = LoggerFactory.getLogger(MapReservationQueueEntry.class);
// public void removeFromQueue() {
// if (previous != null) {
// if (!this.equals(previous.getNext())) {
// logger.warn("WTF!! Previous entrys next value does not match this! This '{}', Previous '{}', Next of previous '{}'", this, getPrevious(), getPrevious().getNext());
// }
// previous.setNext(next);
// previous = null;
// next = null;
// }
// }
//
// public void addToQueue(MapReservationQueueEntry previous) {
// if (previous != null) {
// next = previous.getNext();
// previous.setNext(this);
// if (next != null) {
// next.setPrevious(this);
// }
// }
// }
public MapReservationQueue getQueue() {
return queue;
}
......@@ -127,7 +99,7 @@ public class MapReservationQueueEntry extends GenericEntity implements Comparabl
return getPlaceslotcount().compareTo(o.getPlaceslotcount());
}
private Integer getPlaceslotcount() {
public Integer getPlaceslotcount() {
if (placeslotcount == null) {
placeslotcount = 0;
}
......
......@@ -3,6 +3,7 @@ package fi.codecrew.moya.web.cdiview.map;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.ejb.EJB;
import javax.enterprise.context.RequestScoped;
......@@ -17,6 +18,7 @@ import fi.codecrew.moya.beans.EventBeanLocal;
import fi.codecrew.moya.beans.PermissionBeanLocal;
import fi.codecrew.moya.beans.PlaceBeanLocal;
import fi.codecrew.moya.beans.QueueBeanLocal;
import fi.codecrew.moya.beans.QueueBeanLocal.MapQueueI;
import fi.codecrew.moya.enums.apps.MapPermission;
import fi.codecrew.moya.exceptions.BortalCatchableException;
import fi.codecrew.moya.model.EventMap;
......@@ -89,13 +91,13 @@ public class AjaxMapView extends GenericCDIView {
EventUser u = userview.getSelectedUser();
EventMap map = initMap();
List<PlaceSlot> usersPlaceslots = placebean.getPlaceslots(u);
if (usersPlaceslots.isEmpty()) {
MapQueueI mapqueue = quebean.getMapQueue(map);
if (usersPlaceslots == null || usersPlaceslots.isEmpty()) {
logger.info("There are no placeslots available for user");
map = null;
super.navihandler.forward("/shop/createBill?faces-redirect=true");
} else if (usersPlaceslots.size() < quebean.getMinimumSlotsInQueue()) {
} else if (usersPlaceslots.size() < mapqueue.getMinimumSlotsInQueue()) {
map = null;
super.navihandler.forward("/neomap/notenoughslots?faces-redirect=true");
} else {
......@@ -107,7 +109,7 @@ public class AjaxMapView extends GenericCDIView {
private static Collection<Slotcounter> countPlaceslots(List<PlaceSlot> slots)
{
HashMap<Product, Slotcounter> prodmap = new HashMap<Product, Slotcounter>();
Map<Product, Slotcounter> prodmap = new HashMap<>();
for (PlaceSlot p : slots) {
if (!prodmap.containsKey(p.getProduct())) {
prodmap.put(p.getProduct(), new Slotcounter(p.getProduct()));
......@@ -135,16 +137,16 @@ public class AjaxMapView extends GenericCDIView {
return queEnabled;
}
public String enterQueue()
{
logger.info("Entering queue");
if (isQueueEnabled())
queueEntry = quebean.enterQueue(initMap(), userview.getSelectedUser());
else {
logger.warn("QueueNot enabled. Not entering queue");
}
return null;
}
// public String enterQueue()
// {
// logger.info("Entering queue");
// if (isQueueEnabled())
// queueEntry = quebean.enterQueue(initMap(), userview.getSelectedUser());
// else {
// logger.warn("QueueNot enabled. Not entering queue");
// }
// return null;
// }
private EventMap initMap() {
if (map == null && mapId != null) {
......
......@@ -6,7 +6,6 @@ import java.util.List;
import javax.ejb.EJB;
import javax.enterprise.context.ConversationScoped;
import javax.enterprise.context.RequestScoped;
import javax.faces.model.ListDataModel;
import javax.inject.Named;
......@@ -39,7 +38,7 @@ public class QueueManageView extends GenericCDIView {
private EventMap map;
private MapQueueI queue;
private fi.codecrew.moya.beans.QueueBeanLocal.MapQueueI queue;
private String username;
private Date time = new Date(System.currentTimeMillis() + 1000 * 60 * 60);
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!