package aQute.remote.util;
import java.io.File;
import java.io.IOException;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import aQute.bnd.util.dto.DTO;
import aQute.lib.collections.MultiMap;
import aQute.lib.io.IO;
import aQute.libg.cryptography.SHA1;
/**
* This is a base class that provides the basic functionality of a supervisor.
* In general an actual supervisor extends this class to provide the
* functionality to use on the client side.
*
* @param <Supervisor> The supervisor type
* @param <Agent> The agent type
*/
public class AgentSupervisor<Supervisor, Agent> {
private static final Map<File,Info> fileInfo = new ConcurrentHashMap<File,AgentSupervisor.Info>();
private static final MultiMap<String,String> shaInfo = new MultiMap<String,String>();
private static final int connectWait = 200;
private static byte[] EMPTY = new byte[0];
private Agent agent;
private CountDownLatch latch = new CountDownLatch(1);
protected volatile int exitCode;
private Link<Supervisor,Agent> link;
private AtomicBoolean quit = new AtomicBoolean(false);
static class Info extends DTO {
public String sha;
public long lastModified;
}
protected void connect(Class<Agent> agent, Supervisor supervisor, String host, int port) throws Exception {
connect(agent, supervisor, host, port, -1);
}
protected void connect(Class<Agent> agent, Supervisor supervisor, String host, int port, final int timeout)
throws Exception {
if (timeout < -1) {
throw new IllegalArgumentException("timeout can not be less than -1");
}
int retryTimeout = timeout;
while (true) {
try {
Socket socket = new Socket();
socket.connect(new InetSocketAddress(host, port), Math.max(timeout, 0));
link = new Link<Supervisor,Agent>(agent, supervisor, socket);
this.setAgent(link);
link.open();
return;
} catch (ConnectException e) {
if (retryTimeout == 0) {
throw e;
}
if (retryTimeout > 0) {
retryTimeout = Math.max(retryTimeout - connectWait, 0);
}
Thread.sleep(connectWait);
}
}
}
public byte[] getFile(String sha) throws Exception {
List<String> copy;
synchronized (shaInfo) {
List<String> list = shaInfo.get(sha);
if (list == null)
return EMPTY;
copy = new ArrayList<String>(list);
}
for (String path : copy) {
File f = new File(path);
if (f.isFile()) {
byte[] data = IO.read(f);
return data;
}
}
return EMPTY;
}
public void setAgent(Link<Supervisor,Agent> link) {
this.agent = link.getRemote();
this.link = link;
}
public void close() throws IOException {
if (quit.getAndSet(true))
return;
if (link.isOpen())
link.close();
latch.countDown();
}
public int join() throws InterruptedException {
latch.await();
return exitCode;
}
protected void exit(int exitCode) {
this.exitCode = exitCode;
try {
close();
} catch (Exception e) {
// ignore
}
}
public Agent getAgent() {
return agent;
}
public String addFile(File file) throws NoSuchAlgorithmException, Exception {
file = file.getAbsoluteFile();
Info info = fileInfo.get(file);
if (info == null) {
info = new Info();
fileInfo.put(file, info);
info.lastModified = -1;
}
synchronized (shaInfo) {
if (info.lastModified != file.lastModified()) {
String sha = SHA1.digest(file).asHex();
if (info.sha != null && !sha.equals(info.sha))
shaInfo.removeValue(info.sha, file.getAbsolutePath());
info.sha = sha;
info.lastModified = file.lastModified();
shaInfo.add(sha, file.getAbsolutePath());
}
return info.sha;
}
}
public boolean isOpen() {
return link.isOpen();
}
}