/*
* Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
* PeerfactSim.KOM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* PeerfactSim.KOM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PeerfactSim.KOM. If not, see .
*
*/
package de.tud.kom.p2psim.impl.churn;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import de.tud.kom.p2psim.api.churn.ChurnModel;
import de.tud.kom.p2psim.api.common.SimHost;
import de.tud.kom.p2psim.impl.simengine.Simulator;
import de.tudarmstadt.maki.simonstrator.api.Monitor;
import de.tudarmstadt.maki.simonstrator.api.Monitor.Level;
import de.tudarmstadt.maki.simonstrator.api.Time;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
/**
* AVT Churn Model implements an AVT file parser and generates events based on the given file.
* @author Alexander Nigl
* @version 1.0, 08/14/2011
*/
public class AVTChurnModel implements ChurnModel {
/**
* AVTNode calculates the times on which this node has to be online or offline.
* @author Alexander Nigl
* @version 1.0, 08/14/2011
*/
public class AVTNode {
/**
* id of the node (currently node used)
*/
String id;
/**
* Array of up- and downtimes
*/
long[] times;
/**
*
* @param config single line of AVT file
* @param unit time unit to calculate the simulator times
*/
public AVTNode(String config, Long unit) {
String[] splited = config.split("(\\s)+");
this.id = splited[0];
int numberOfValues = Integer.valueOf(splited[1]);
times = new long[numberOfValues * 2];
BigDecimal unitCon = new BigDecimal(unit);
for (int i = 0; i < numberOfValues * 2; i++) {
times[i] = new BigDecimal(splited[2 + i]).multiply(unitCon)
.longValue();
}
}
/**
* Returns offset until next downtime based on Simulator.getCurrentTime().
* @return offset of downtime,
* if host should be online it returns 0,
* if host will not return it returns the offset to Simulator.getEndTime() +1
*/
public long getNextDowntime() {
long curTime = Time.getCurrentTime();
if (curTime < times[0]) {
return times[0] - curTime;
}
if (curTime >= times[times.length-1]) {
return Simulator.getEndTime() - curTime + 1;
}
int insertPlace = Arrays.binarySearch(times, curTime);
if (insertPlace%2 == 1) {
if (curTime < times[insertPlace]) return 0l;
if (curTime == times[insertPlace]) return times[insertPlace+1] - curTime;
}
if (insertPlace%2 == 0) {
if (curTime < times[insertPlace]) return times[insertPlace] -curTime;
if (curTime == times[insertPlace]) return 0l;
}
throw new RuntimeException("Should never ever happen");
}
/**
* Returns offset until next uptime
* @return offset of uptime,
* if it should be offline it returns 0
*/
public long getNextUptime() {
long curTime = Time.getCurrentTime();
if (curTime < times[0]) {
return 0l;
}
if (curTime >= times[times.length-1]) {
return 0l;
}
int insertPlace = Arrays.binarySearch(times, curTime);
if (insertPlace%2 == 1) {
if (curTime < times[insertPlace]) return times[insertPlace]-curTime;
if (curTime == times[insertPlace]) return 0l;
}
if (insertPlace%2 == 0) {
if (curTime < times[insertPlace]) return 0l;
if (curTime == times[insertPlace]) return times[insertPlace+1]-curTime;
}
throw new RuntimeException("Should never ever happen");
}
}
/**
* all churn nodes in the same order as in the AVT file
*/
private LinkedBlockingQueue churnNodes;
/**
* Mapping between churn enabled hosts an the associated AVTNode
*/
private HashMap hosts;
/**
*
* @param avtFilePath Path to file
* @param unit time unit in which the AVT file was written in (most of the time this will be 1s)
* @throws IOException if file is not readable
*/
@XMLConfigurableConstructor({"avtFilePath", "unit"})
public AVTChurnModel(String avtFilePath, long unit) throws IOException {
FileInputStream fstream = new FileInputStream(avtFilePath);
DataInputStream in = new DataInputStream(fstream);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String strLine;
churnNodes = new LinkedBlockingQueue();
while ((strLine = br.readLine()) != null) {
int commentIndex = strLine.indexOf("#"); //ignore comments
if (commentIndex == 0) {
continue;
} else if (commentIndex != -1) {
strLine = strLine.substring(0, commentIndex);
}
churnNodes.add(new AVTNode(strLine, unit));
}
in.close();
hosts = new HashMap();
}
/**
* {@inheritDoc}
*/
@Override
public long getNextDowntime(SimHost host) {
return this.hosts.get(host).getNextDowntime();
}
/**
* {@inheritDoc}
*/
@Override
public long getNextUptime(SimHost host) {
return this.hosts.get(host).getNextUptime();
}
/**
* {@inheritDoc}
*
Throws an Exception if number of churnHosts exceeds available nodes from the AVT File.
*/
@Override
public void prepare(List churnHosts) {
if (churnHosts.size() > churnNodes.size()) {
throw new IllegalArgumentException(
"Number of churn affected hosts is greater than the number of hosts prepared from the AVTModel.");
} else if (churnHosts.size() < churnNodes.size()) {
Monitor.log(AVTChurnModel.class, Level.WARN,
"churnHosts (%s) < Hosts in AVT File(%s)",
churnHosts.size(), churnNodes.size());
}
for (SimHost host : churnHosts) {
this.hosts.put(host, churnNodes.poll());
}
this.churnNodes = null; //Delete unused churnNodes
}
}