package geniusweb.simplerunner; import java.util.List; import java.util.logging.Level; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import geniusweb.events.ProtocolEvent; import geniusweb.protocol.CurrentNegoState; import geniusweb.protocol.NegoProtocol; import geniusweb.protocol.NegoSettings; import geniusweb.protocol.NegoState; import geniusweb.protocol.partyconnection.ProtocolToPartyConnFactory; import tudelft.utilities.logging.Reporter; /** * A simple tool to run a negotiation stand-alone, without starting the servers. * All referred files and classes need to be stored locally (or be in the * dependency list if you use maven). *

* IMPORTANT SimpleRunner has a number of restrictions, compared to a * run using a runserver and partyserver *

*/ public class Runner implements Runnable { private final static ObjectMapper jackson = new ObjectMapper(); private final NegoSettings settings; private final NegoProtocol protocol; private final ClassPathConnectionFactory connectionfactory; protected final Reporter log; private boolean properlyStopped = false; private final int LOOPTIME = 200;// ms private final int FINALWAITTIME = 5000;// ms private long maxruntime; /** * * @param settings the {@link NegoSettings} * @param connectionfactory the {@link ProtocolToPartyConnFactory} * @param logger the {@link Reporter} to log problems * @param maxruntime limit in millisecs. Ignored if 0 */ public Runner(NegoSettings settings, ClassPathConnectionFactory connectionfactory, Reporter logger, long maxruntime) { if (settings == null || connectionfactory == null) { throw new NullPointerException("Arguments must be not null"); } this.settings = settings; this.log = logger; this.protocol = settings.getProtocol(log); this.connectionfactory = connectionfactory; this.maxruntime = maxruntime; } /** * * @return true if the runner has finished */ public boolean isProperlyStopped() { return properlyStopped; } @Override public void run() { protocol.addListener(evt -> handle(evt)); protocol.start(connectionfactory); long remainingtime = maxruntime; while (!properlyStopped && (maxruntime == 0 || remainingtime > 0)) { try { Thread.sleep(LOOPTIME); remainingtime -= LOOPTIME; } catch (InterruptedException e) { e.printStackTrace(); } } log.log(Level.INFO, "Waiting for connection closure"); remainingtime = FINALWAITTIME; while (remainingtime > 0 && !connectionfactory.getOpenConnections().isEmpty()) { try { Thread.sleep(LOOPTIME); remainingtime -= LOOPTIME; } catch (InterruptedException e) { e.printStackTrace(); } } List openconn = connectionfactory.getOpenConnections(); if (!openconn.isEmpty()) log.log(Level.WARNING, "Connections " + openconn + " did not close properly at end of run"); log.log(Level.INFO, "end run"); } private void handle(ProtocolEvent evt) { if (evt instanceof CurrentNegoState && ((CurrentNegoState) evt) .getState().isFinal(System.currentTimeMillis())) { stop(); } } protected void stop() { logFinal(Level.INFO, protocol.getState()); properlyStopped = true; } /** * Separate so that we can intercept this when mocking, as this will crash * on mocks because {@link #jackson} can not handle mocks. * * @param level the log {@link Level} * @param state the {@link NegoState} to log */ protected void logFinal(Level level, NegoState state) { try { log.log(level, "protocol ended normally: " + jackson.writeValueAsString(protocol.getState())); } catch (JsonProcessingException e) { e.printStackTrace(); } } /** * @return protocol that runs/ran the session. */ public NegoProtocol getProtocol() { return protocol; } }