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; } }