/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package services;
import services.utils.TicketConvertor;
import domain.Collaborator;
import domain.shedule.WorkType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.log4j.Logger;
import utils.SimplePair;
import web.rechelper.Ticket;
/**
*
* @author petr
*/
public class ReceptionUpdater {
/**
* Класс хранящий обновления для странички с заданным уидом
*/
class PageHolder {
private int pageUID;
private Date lastUpdate;
private ArrayList<TicketConvertor> tickets;
public PageHolder() {
pageUID = ((int) (Math.random() * Integer.MAX_VALUE));
lastUpdate = new Date();
tickets = new ArrayList<TicketConvertor>();
}
public TicketConvertor[] getTickets() {
TicketConvertor[] res = tickets.toArray(new TicketConvertor[tickets.size()]);
tickets.clear();
lastUpdate = new Date();
return res;
}
public void addTicket(Ticket ticket) {
tickets.add(new TicketConvertor(ticket));
}
public boolean check() {
boolean c = new Date().getTime() - lastUpdate.getTime() < timeout;
if (!c) {
log.debug("check in: " + getPageUID() + " = " + c);
}
return c;
}
public int getPageUID() {
return pageUID;
}
}
protected static final Logger log = Logger.getLogger(ReceptionUpdater.class);
/**сколько мс страничка не должна посылать запросов, что б отвалиться*/
private static long timeout = 20000l;
/**таймер, рубит странички по таймауту*/
private Timer t;
/**замок для синхронизации*/
private final Lock lock = new ReentrantLock();
/**
* мап (сотрудник, тип работы) - список уидов страничек
*/
private static Map<Integer, PageHolder> holderMap = new HashMap<Integer, PageHolder>();
/**
* мап пара айдишнегов (коллаборатор, тип работы) - список уидов страничек
*/
private static Map<SimplePair, List<Integer>> ticketMap = new HashMap<SimplePair, List<Integer>>();
private int id;
/**
* Конструктор, инициализирует таймер
*/
public ReceptionUpdater() {
id = ((int) (Math.random() * Integer.MAX_VALUE));
t = new Timer();
t.schedule(new TimerTask() {//
@Override
public void run() {
clearHolder();
}
}, timeout, timeout);
}
///////////////// торчат наружу /////////////////////////////////
/**
* Генерит странице уид, по которому она потом буде обновлятьси
* запоминает его
* @param collaborator
* @param workType
* @return
*/
public int registerPage(Collaborator collaborator, WorkType workType) {
PageHolder pageHolder = new PageHolder();
holderMap.put(pageHolder.getPageUID(), pageHolder);
findInTicketMap(collaborator, workType).add(pageHolder.pageUID);
log.debug("createPageUID for: " + collaborator + ", " + workType + " = " + pageHolder.getPageUID());
return pageHolder.getPageUID();
}
/**
* Сию функцию вопрошает страничка через двр, на предмет обновлений
* @param uids идентификатор страницы
* @return список обновлений
*/
public TicketConvertor[] getUpdatesP(int uid) {
return getUpdates(uid);
}
/**
* добавляет тикет
* @param collaborator
* @param workType
* @param ticket
*/
public void addTicketP(Collaborator collaborator, WorkType workType, Ticket ticket) {
addTicket(collaborator, workType, ticket);
}
//////////////// дёргаются харатерными функциями, к-ые торчат наружу //////////////
/**
* Вызывает отцепление страниц по таймауту
*/
private void clearHolder() {
clearHolders();
}
/**
* добавляет тикет
* @param collaborator
* @param workType
* @param ticket
*/
private void addTicket(Collaborator collaborator, WorkType workType, Ticket ticket) {
lock.lock();
try {
log.debug("addTicket in " + Thread.currentThread().toString());
List<Integer> uids = findInTicketMap(collaborator, workType);
log.debug("Найдены страницы: " + uids);
for (int i = uids.size() - 1; i >= 0; i--) {
Integer uid = uids.get(i);
PageHolder holder = holderMap.get(uid);
if (holder == null/*|| !holder.check()*/) {
// getHolderMap().remove(uid);
// removeFromTicketMap(uid);
log.debug("Страница c uid" + uid + " не зарегестрирована");
} else {
log.debug("Билет добавлен странице с uid " + uid);
holder.addTicket(ticket);
}
}
} finally {
lock.unlock();
}
}
/**
* возвращает обновления для запрошеной страницы
* @param uid уид страницы
* @return
*/
private TicketConvertor[] getUpdates(int uid) {
lock.lock();
try {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
PageHolder holder = holderMap.get(uid);
if (holder == null) {
log.debug("holder == null uid: " + uid);
return null;
} else {
if (!holder.check()) {
log.debug("!holder.check id: " + holder.getPageUID());
return null;
} else {
TicketConvertor[] tickets = holder.getTickets();
if (tickets.length > 0) {
log.debug("getUpdates in " + Thread.currentThread().toString());
log.debug("getBusyTikets for " + uid + " return: " + tickets.length + " elements");
}
return tickets;
}
}
} finally {
lock.unlock();
}
}
/**
* чистит отвалившиеся страницы
*/
private void clearHolders() {
lock.lock();
try {
Set<Integer> toRemove = new HashSet<Integer>();
Map<Integer, PageHolder> holderMapLocal = holderMap;
if (holderMapLocal != null && holderMapLocal.keySet() != null) {
Set<Integer> keys = Collections.synchronizedSet(holderMapLocal.keySet());
Vector<Integer> uids = new Vector<Integer>(keys);
for (int i = uids.size() - 1; i >= 0; i--) {
// for (Integer uid : holderMapLocal.keySet()) {
Integer uid = uids.elementAt(i);
if (!holderMapLocal.get(uid).check()) {
holderMapLocal.remove(uid);
toRemove.add(uid);
}
}
if (!toRemove.isEmpty()) {
removeFromTicketMap(toRemove);
log.debug("clearHolders in " + Thread.currentThread().toString());
log.debug("clearHolders: remove " + toRemove);
}
}
} finally {
lock.unlock();
}
}
/////////////////////// сервисные /////////////////////////////////////
/**
* Сервисная, удаляет по ид списком страницы из мапа
* @param uids
*/
private void removeFromTicketMap(Set<Integer> uids) {
// threadControl("removeFromTicketMap");
log.debug("removeFromTicketMap: " + uids);
Collection<List<Integer>> values = ticketMap.values();
for (List<Integer> list : values) {
if (list != null) {
list.removeAll(uids);
}
}
}
/**
* сервисная, ищет список уидов страницек в мапе по сотруднику и типу работы
* @param collaborator
* @param workType
* @return
*/
private List<Integer> findInTicketMap(Collaborator collaborator, WorkType workType) {
log.debug("findInTicketMap: " + collaborator + ", " + workType);
SimplePair findPair = new SimplePair(collaborator.getId(), workType.getId());
List<Integer> list = ticketMap.get(findPair);
if (list == null) {
list = new Vector<Integer>();
ticketMap.put(findPair, list);
}
log.debug("findInTicketMap return " + list);
return list;
}
}