package com.robonobo.robotest;
import java.io.*;
import java.net.InetAddress;
import java.util.*;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.robonobo.common.concurrent.CatchingRunnable;
import com.robonobo.common.exceptions.SeekInnerCalmException;
import com.robonobo.core.RobonoboController;
import com.robonobo.core.api.RobonoboException;
import com.robonobo.core.api.proto.CoreApi.EndPoint;
import com.robonobo.core.api.proto.CoreApi.Node;
import com.robonobo.mina.external.MinaConfig;
import com.robonobo.mina.external.NodeFilter;
import com.robonobo.mina.external.node.EonEndPoint;
public class RoboTest {
static int SECS_BETWEEN_DOWNLOADS = 60;
static Pattern testFilePat = Pattern.compile("^(\\S+)\\s+(.+)$");
private static RoboTest instance;
private RobonoboController control;
private List<String> streamIds = new ArrayList<String>();
private List<File> inFiles = new ArrayList<File>();
private int myIndex;
private boolean started = false;
private ScheduledFuture<?> addDownloadsTask;
private Log log = LogFactory.getLog(getClass());
public static RoboTest getInstance() {
return instance;
}
public RoboTest(RobonoboController control, File testFile, int myIndex) throws IOException {
instance = this;
this.myIndex = myIndex;
this.control = control;
// We expect a file containing a series of lines each with a stream id followed by a space followed by a file
// path
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(testFile)));
String line;
while ((line = in.readLine()) != null) {
Matcher m = testFilePat.matcher(line.replaceAll("\\s+$", ""));
if (!m.matches())
throw new SeekInnerCalmException("Format of file must be <streamid> <filepath>");
String sid = m.group(1);
String filePath = m.group(2);
File f = new File(filePath);
if (!f.exists())
throw new SeekInnerCalmException("File '" + f.getAbsolutePath()
+ "' specified in test file does not exist");
streamIds.add(sid);
inFiles.add(f);
}
}
public void printStatus(PrintWriter out) {
if (!started) {
out.println("Robotest not yet started");
return;
}
if (finished()) {
out.println("Robotest completed successfully");
return;
}
Set<String> shareSids = control.getShareStreamIds();
int numShares = 0;
for (String sid : streamIds) {
if (shareSids.contains(sid))
numShares++;
}
out.println("Robotest running, completed " + numShares + "/" + streamIds.size() + " streams");
}
public void start() throws IOException, RobonoboException {
log.debug("Robotest starting");
// Tell mina to ignore nodes on the same ip as us
MinaConfig mc = (MinaConfig) control.getConfig("mina");
InetAddress myIp = InetAddress.getByName(mc.getLocalAddress());
control.addNodeFilter(new RejectSameIPFilter(myIp));
// We initially share the stream specified by myIndex
String sharePath = inFiles.get(myIndex).getAbsolutePath();
log.debug("Robotest adding share for " + sharePath);
control.addShare(sharePath);
// Then we run a regular pFetcher that adds a download for every other stream - shuffle the order
List<String> sidsToDownload = new ArrayList<String>(streamIds);
sidsToDownload.remove(myIndex);
Collections.shuffle(sidsToDownload);
addDownloadsTask = control.getExecutor().scheduleAtFixedRate(new AddDownloadsTask(sidsToDownload), 0, SECS_BETWEEN_DOWNLOADS, TimeUnit.SECONDS);
}
public boolean finished() {
Set<String> shareSids = control.getShareStreamIds();
for (String sid : streamIds) {
if (!shareSids.contains(sid))
return false;
}
return true;
}
class AddDownloadsTask extends CatchingRunnable {
private List<String> sidsToDownload;
public AddDownloadsTask(List<String> sidsToDownload) {
this.sidsToDownload = sidsToDownload;
}
@Override
public void doRun() throws Exception {
if(sidsToDownload.size() == 0) {
addDownloadsTask.cancel(false);
return;
}
String sid = sidsToDownload.remove(0);
log.debug("RoboTest adding download for sid "+sid);
control.addDownload(sid);
}
}
class RejectSameIPFilter implements NodeFilter {
private InetAddress myIp;
public RejectSameIPFilter(InetAddress myIp) {
this.myIp = myIp;
}
@Override
public String getFilterName() {
return "SameIPFilter";
}
@Override
public boolean acceptNode(Node node) {
for (EndPoint ep : node.getEndPointList()) {
EonEndPoint eep = EonEndPoint.parse(ep.getUrl());
if (eep.getAddress().equals(myIp))
return false;
}
return true;
}
}
}