package geniusweb.simplerunner;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.LinkedList;
import java.util.List;
import geniusweb.actions.Action;
import geniusweb.actions.PartyId;
import geniusweb.inform.Inform;
import geniusweb.party.Party;
import geniusweb.protocol.partyconnection.ProtocolToPartyConn;
import geniusweb.protocol.partyconnection.ProtocolToPartyConnFactory;
import geniusweb.references.Reference;
import tudelft.utilities.repository.NoResourcesNowException;
/**
* A connectionfactory that only accepts URLs of the form
* classpath:org/my/package/class
*
*/
public class ClassPathConnectionFactory implements ProtocolToPartyConnFactory {
private List allConnections = new LinkedList<>();
/**
* @param reference the party class path reference4
*/
@Override
public ProtocolToPartyConn connect(Reference reference) {
ConnectionPair connpair = new ConnectionPair(reference);
allConnections.add(connpair);
return connpair.getProtocolToPartyConn();
}
@Override
public List connect(List references)
throws IOException, NoResourcesNowException {
List connections = new LinkedList<>();
for (Reference partyref : references) {
connections.add(connect(partyref));
}
return connections;
}
/**
* @return list of connections that are still open.
*/
public List getOpenConnections() {
List open = new LinkedList<>();
for (ConnectionPair connpair : allConnections) {
open.addAll(connpair.getOpenConnections());
}
return open;
}
}
class BasicConnectionWithParty extends BasicConnection
implements ProtocolToPartyConn {
private PartyId id;
public BasicConnectionWithParty(Reference reference, URI uri) {
super(reference, uri);
this.id = new PartyId(uri.toString().replace("classpath:", "")
.replaceAll("\\.", "_"));
}
@Override
public PartyId getParty() {
return id;
}
@Override
public String toString() {
return "WP" + super.toString();
}
}
/**
* Contains the connection from protocol to party and from party to protocol.
* These two are dependent on each other, and must close together.
*
*/
class ConnectionPair {
private static int serialcounter = 1;
private static final String SCHEME = "classpath";
private final static URI PROTOCOLURI = uri("protocol:protocol");
private final BasicConnection party2protocol;
private final BasicConnectionWithParty protocol2party;
/**
*
* @param reference the party class path reference4
*
*/
public ConnectionPair(Reference reference) {
// set up the whole other party including the connection to it.
String classpath = getClassPath(reference.getURI());
Party party = instantiate(classpath);
// if call to protocol would fail, we should also close the
// protocol2party connection. But we assume that will work fine.
party2protocol = new BasicConnection(reference,
PROTOCOLURI) {
@Override
public void close() {
if (isOpen()) {
super.close();
protocol2party.close();
}
}
};
// if callback from protocol to party fails, then also close the
// other direction.
protocol2party = new BasicConnectionWithParty(reference,
uri("classpath:" + reference.getURI().getSchemeSpecificPart()
+ "." + (serialcounter++))) {
@Override
public void close() {
if (isOpen()) {
super.close();
party2protocol.close();
}
}
};
party2protocol.init(action -> protocol2party.notifyListeners(action));
protocol2party.init(info -> party2protocol.notifyListeners(info));
party.connect(party2protocol);
}
/**
*
* @return the open connections
*/
public List getOpenConnections() {
List open = new LinkedList<>();
if (party2protocol.isOpen())
open.add(party2protocol);
if (protocol2party.isOpen())
open.add(protocol2party);
return open;
}
public BasicConnectionWithParty getProtocolToPartyConn() {
return protocol2party;
}
/**
* Internal use for testing
*/
protected BasicConnection getPartyToProtocolConn() {
return party2protocol;
}
/**
*
* @param classpath
* @return instance of the given {@link Party} on the classpath
* @throws IllegalArgumentException if the Party can not be instantiated, eg
* because of a ClassCastException,
* ClassNotFoundException,
* IllegalAccessException etc. Such an
* exception is considered a bug by the
* programmer, because this is a
* stand-alone runner.
*/
@SuppressWarnings("unchecked")
private Party instantiate(String classpath) {
System.out.println("Connecting with party '" + classpath + "'");
Class partyclass;
try {
partyclass = (Class) Class.forName(classpath);
Constructor ctor = partyclass.getConstructor();
return ctor.newInstance(new Object[] {});
} catch (ClassNotFoundException | NoSuchMethodException
| SecurityException | InstantiationException
| IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
throw new IllegalArgumentException(
"Failed to create connection to party " + classpath, e);
}
}
private static URI uri(String ref) {
try {
return new URI(ref);
} catch (URISyntaxException e) {
throw new IllegalStateException("failed to create basic URI", e);
}
}
private String getClassPath(URI uri) {
if (!SCHEME.equals(uri.getScheme())) {
throw new IllegalArgumentException("Required the " + SCHEME
+ " protocol but found " + uri.getScheme());
}
String path = uri.getSchemeSpecificPart();
if (path == null) {
throw new IllegalArgumentException(
"Expected classpath but found null in " + uri);
}
return path;
}
}