import json import logging from pathlib import Path import sys import time import traceback from typing import List, Optional from pyson.ObjectMapper import ObjectMapper from tudelft.utilities.listener.Listener import Listener from tudelft_utilities_logging.Reporter import Reporter from geniusweb.events.ProtocolEvent import ProtocolEvent from geniusweb.protocol.CurrentNegoState import CurrentNegoState from geniusweb.protocol.NegoProtocol import NegoProtocol from geniusweb.protocol.NegoSettings import NegoSettings from geniusweb.protocol.NegoState import NegoState from geniusweb.simplerunner.ClassPathConnectionFactory import ClassPathConnectionFactory class StdOutReporter (Reporter): def log(self, level:int , msg:str, exc:Optional[BaseException]=None): if level >= logging.WARNING : print(logging.getLevelName(level) + ":" + msg, file=sys.stderr) else: print(logging.getLevelName(level) + ":" + msg) class NegoRunner: ''' WARNING DO NOT USE. NOT WORKING YET. 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

''' _properlyStopped:bool = False _LOOPTIME = 200 # ms _FINALWAITTIME = 5000 # ms def __init__(self, settings:NegoSettings , connectionfactory:ClassPathConnectionFactory , logger:Reporter , maxruntime:int): ''' @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 ''' if settings == None or connectionfactory == None: raise ValueError("Arguments must be not null"); self._settings = settings; self._log = logger; self._protocol = settings.getProtocol(self._log); self._connectionfactory = connectionfactory; self._maxruntime = maxruntime; self._jackson=ObjectMapper() def isProperlyStopped(self)->bool: ''' @return true if the runner has finished ''' return self._properlyStopped def run(self): this=self class protocolListener(Listener[ProtocolEvent]): def notifyChange(self, evt: ProtocolEvent): this._handle(evt) self._protocol.addListener(protocolListener()) self._protocol.start(self._connectionfactory) remainingtime = self._maxruntime; while not self._properlyStopped and (self._maxruntime == 0 or remainingtime > 0): time.sleep(self._LOOPTIME/1000.) remainingtime -= self._LOOPTIME self._log.log(logging.INFO, "Waiting for connection closure") remainingtime = self._FINALWAITTIME; while remainingtime > 0 and\ len(self._connectionfactory.getOpenConnections())!=0: time.sleep(self._LOOPTIME/1000.) remainingtime -= self._LOOPTIME openconn = self._connectionfactory.getOpenConnections() if len(openconn)!=0: self._log.log(logging.WARNING, "Connections " + str(openconn)\ + " did not close properly at end of run") self._log.log(logging.INFO, "end run") def _handle(self, evt:ProtocolEvent ): if isinstance(evt , CurrentNegoState) and \ evt.getState().isFinal(1000*time.time()): self._stop() def _stop(self): self._logFinal(logging.INFO, self._protocol.getState()) self._properlyStopped =True def _logFinal(self, level:int , state: NegoState): ''' 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}, eg logging.WARNING @param state the {@link NegoState} to log ''' try: self._log.log(level, "protocol ended normally: " + json.dumps(self._jackson.toJson(self._protocol.getState()))) except Exception as e: # catch json issues traceback.print_exc() def getProtocol(self) ->NegoProtocol : ''' @return protocol that runs/ran the session. ''' return self._protocol @staticmethod def main( args:List[str]) : ''' The main runner @param args should have 1 argument, the settings.json file to be used. @throws IOException if problem occurs ''' if len(args) != 1: NegoRunner.showusage() return; serialized = Path(args[0]).read_text("utf-8") settings:NegoSettings = ObjectMapper().parse(json.loads(serialized), NegoSettings) #type:ignore runner = NegoRunner(settings, ClassPathConnectionFactory(), StdOutReporter(), 0) runner.run() @staticmethod def showusage(): print("GeniusWeb stand-alone runner.") print("first argument should be .") print("The settings.json file should contain the NegoSettings.") print("See the settings.json example file and the GeniusWeb wiki pages. ") if __name__ == '__main__': NegoRunner.main([sys.argv[1]])