package uc.files.transfer;
import helpers.GH;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import logger.LoggerFactory;
import org.apache.log4j.Logger;
import org.eclipse.core.runtime.Platform;
import uc.DCClient;
import uc.ISlotManager;
import uc.IUser;
import uc.InfoChange;
import uc.PI;
import uc.files.filelist.FileListFile;
import uc.protocols.TransferType;
public class SlotManager implements ISlotManager {
private static final Logger logger = LoggerFactory.make();
private static final long RemoveAfterSeconds = 1000 * 60 *10 ; //10 Minutes
private static final long OnlineAndInterestedTime = 1000 * 60 *3; // at least every 3 minutes the user must request a slot to be seen as interested
private static final long MinislotSize = 64 * 1024;
private static final int MaxTransfers = 100;
private final DCClient dcc;
private final Object slotsSynch = new Object();
private final Set<Slot> currentSlots = new HashSet<Slot>();
private final Set<Slot> currentExtraSlots = new HashSet<Slot>();
private final Map<IUser,WaitingUser> usersThatReceivedSlot =
new HashMap<IUser,WaitingUser>();
private final CopyOnWriteArrayList<WaitingUser> waitingQueue =
new CopyOnWriteArrayList<WaitingUser>();
public SlotManager(DCClient client) {
this.dcc = client;
}
/* (non-Javadoc)
* @see uc.ISlotManager#init()
*/
public void init() {
dcc.getSchedulerDir().schedule(new Runnable() {
public void run() {
removeUsersNotRecentlyOnlineAndInterested();
}
}, 60, TimeUnit.SECONDS);
}
/* (non-Javadoc)
* @see uc.ISlotManager#getSlot(uc.User, uc.protocols.TransferType, java.io.File)
*/
public Slot getSlot(IUser usr ,TransferType type,FileListFile f) {
if (usr == null || (type == TransferType.FILE && f == null )) {
if (Platform.inDevelopmentMode()) {
logger.error("user "+(usr==null? "null":usr)+" file:"+(f==null?"null":f));
}
return null;
}
synchronized(slotsSynch) {
if (usersThatReceivedSlot.containsKey(usr)) {
logger.debug(usr.getNick()+" tried getting upload even if he already had an upload running");
return null; //no slot for that user
}
if (currentExtraSlots.size() >= MaxTransfers && !usr.isOp()) { //Too many uploads running.. no slots anymore. To prevent DoS
return null;
}
updateUserOnlineAndInterested(usr);
Slot s = getSlotPriv(usr,type,f);
WaitingUser wu = getWU(usr);
if (s == null) {
waitingQueue.addIfAbsent(wu);
//logger.debug("User in Queu: "+usr.getNick()+" Position: "+getPositionInQueue(usr));
} else {
waitingQueue.remove(wu);
usersThatReceivedSlot.put(usr, wu);
}
return s;
}
}
private Slot getSlotPriv(IUser usr ,TransferType type,FileListFile f) {
switch(type) {
case TTHL:
case FILELIST:
Slot extra = new Slot(false);
currentExtraSlots.add(extra);
return extra;
case FILE:
int slotsAvailable = getTotalSlots() - currentSlots.size();
boolean eligibleForSlotByQueue = isOneofThFirstXUsers(slotsAvailable,usr);
if (slotsAvailable > 0 && eligibleForSlotByQueue) {
// logger.info("User "+usr.getNick()+" got Slot over Queue: "+usr);
Slot normal = new Slot(true);
currentSlots.add(normal);
dcc.notifyChangedInfo(InfoChange.CurrentSlots);
return normal;
} else if (usr.hasCurrentlyAutogrant() || f.getSize() <= MinislotSize || f.automaticExtraSlot()) {
Slot autogrant = new Slot(false);
currentSlots.add(autogrant);
currentExtraSlots.add(autogrant);
dcc.notifyChangedInfo(InfoChange.CurrentSlots);
return autogrant;
} else {
// logger.info("No Slot for: "+usr.getNick());
return null;
}
}
throw new IllegalStateException("no known TransferType was set");
}
/* (non-Javadoc)
* @see uc.ISlotManager#returnSlot(uc.files.transfer.Slot, uc.IUser)
*/
public void returnSlot(Slot slot,IUser usr) {
if (usr == null && Platform.inDevelopmentMode()) {
logger.error("user was null");
}
synchronized(slotsSynch) {
currentSlots.remove(slot);
currentExtraSlots.remove(slot);
dcc.notifyChangedInfo(InfoChange.CurrentSlots);
WaitingUser wu = usersThatReceivedSlot.remove(usr);
if (wu != null) {
waitingQueue.add(wu);
// logger.info("Readding user: "+wu.usr.getNick());
}
ArrayList<WaitingUser> wus = new ArrayList<WaitingUser>(waitingQueue);
Collections.sort(wus,WaitingUserComp);
waitingQueue.clear();
waitingQueue.addAll(wus);
// if (slot != null) {
// logger.info("Return slot: "+usr.getNick()+" pos: "+waitingQueue.indexOf(wu));
// }
}
}
/* (non-Javadoc)
* @see uc.ISlotManager#getCurrentSlots()
*/
public int getCurrentSlots() {
synchronized (slotsSynch) {
return Math.max(0,getTotalSlots() - currentSlots.size());
}
}
/* (non-Javadoc)
* @see uc.ISlotManager#getTotalSlots()
*/
public int getTotalSlots() {
return PI.getInt(PI.slots);
}
/* (non-Javadoc)
* @see uc.ISlotManager#getPositionInQueue(uc.IUser)
*/
public int getPositionInQueue(IUser usr) {
int x = -1;
synchronized (slotsSynch) {
for (WaitingUser wu:waitingQueue) {
if (wu.isOnlineAndInterested()) {
x++;
}
if (wu.usr.equals(usr)) {
return x;
}
}
}
return -1;
}
private boolean isOneofThFirstXUsers(int x,IUser usr) {
for (WaitingUser wu:waitingQueue) {
if (wu.isOnlineAndInterested()) {
if (wu.usr.equals(usr)) {
return true;
} else if ( --x <= 0) {
return false;
}
}
}
return true; //not in Queue -> well give him a slot then..
}
private void updateUserOnlineAndInterested(IUser user) {
for (WaitingUser wu:waitingQueue) {
if (wu.usr.equals(user)) {
wu.stillInterestedReceived();
break;
}
}
}
/**
*
* @param usr - for which usr...
* @return a waiting user object for the given username
*/
private WaitingUser getWU(IUser usr) {
synchronized (slotsSynch) {
for (WaitingUser wu:waitingQueue) {
if (wu.usr.equals(usr)) {
return wu;
}
}
}
// logger.info("Creating waiting user: "+usr);
return new WaitingUser(usr);
}
public void printQueue() {
dcc.logEvent(waitingQueue.toString());
}
private void removeUsersNotRecentlyOnlineAndInterested() {
for (WaitingUser wu:waitingQueue) {
if (wu.checkLastTimeInterestedAndOnline()) {
waitingQueue.remove(wu);
}
}
}
private static class WaitingUser implements Comparable<WaitingUser> {
private final IUser usr;
private final long waitingSince;
private long lastInterestedReceived;
public WaitingUser(IUser usr) {
if (usr == null) {
throw new IllegalArgumentException("User null");
}
this.usr = usr;
waitingSince = System.currentTimeMillis();
lastInterestedReceived = System.currentTimeMillis();
}
/**
*
* @return if the users has been offline for such a long time that he should be removed..
* true if should be removed..
*/
private void stillInterestedReceived() {
// if (usr.isOnline()) {
lastInterestedReceived = System.currentTimeMillis();
//} else {
//}
}
private boolean isOnlineAndInterested() {
return usr.isOnline() && System.currentTimeMillis() - lastInterestedReceived < OnlineAndInterestedTime;
}
private boolean checkLastTimeInterestedAndOnline() {
return System.currentTimeMillis() - lastInterestedReceived > RemoveAfterSeconds ;
}
@Override
public int hashCode() {
return usr.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
return usr.equals(((WaitingUser)obj).usr);
}
public int compareTo(WaitingUser o) {
if (o == null) {
return Long.valueOf(waitingSince).compareTo(Long.MAX_VALUE);
}
int comp = Long.valueOf(waitingSince).compareTo(o.waitingSince);
if (comp == 0) {
//if several users wait for the same duration this preserves from problems!
return GH.compareTo(hashCode(), o.hashCode());
}
return comp;
}
public String toString() {
return usr.getNick()+" Wait: "+((System.currentTimeMillis()-waitingSince)/1000)
+" LIR: "+((System.currentTimeMillis()-lastInterestedReceived)/1000);
}
}
private static final Comparator<WaitingUser> WaitingUserComp = new Comparator<WaitingUser>() {
public int compare(WaitingUser o1, WaitingUser o2) {
long a = /*o1 == null? Long.MAX_VALUE : */o1.waitingSince;
long b = /*o2 == null? Long.MAX_VALUE : */ o2.waitingSince;
return Long.valueOf(a).compareTo(b);
}
};
/*
* [
* Hedayat Wait: 27656 LIR: 25028,
* nick-try-16-16 Wait: 27656 LIR: 9432,
* Ara Wait: 27656 LIR: 183,
* GrillBill2 Wait: 27656 LIR: 48,
* YODA-41 Wait: 27656 LIR: 47,
* Powolniak Wait: 27656 LIR: 39,
* Kaptain_Komfy Wait: 27656 LIR: 28,
* °^Kumba^° Wait: 27656 LIR: 18,
* GrillBill Wait: 27656 LIR: 5,
* buffy Wait: 27342 LIR: 0,
* °^hAliGalLiE@works^° Wait: 26678 LIR: 13829,
* stroncium12800 Wait: 23384 LIR: 17,
* [RO][BUC][ISP]eruho Wait: 20820 LIR: 20820,
* kolina Wait: 11502 LIR: 2,
* tempestuous Wait: 11426 LIR: 11
* ]
*
*/
}