source: geniuswebcore/geniusweb/simplerunner/Runner.py@ 92

Last change on this file since 92 was 90, checked in by Bart Vastenhouw, 3 years ago

Refactor to help reusing partiesserver.

File size: 4.0 KB
RevLine 
[90]1import json
2import logging
3from pathlib import Path
4import sys
5import time
6import traceback
7from typing import List, Optional
8
9from pyson.ObjectMapper import ObjectMapper
10from tudelft.utilities.listener.Listener import Listener
11from tudelft_utilities_logging.Reporter import Reporter
12
13from geniusweb.events.ProtocolEvent import ProtocolEvent
14from geniusweb.protocol.CurrentNegoState import CurrentNegoState
15from geniusweb.protocol.NegoProtocol import NegoProtocol
16from geniusweb.protocol.NegoSettings import NegoSettings
17from geniusweb.protocol.NegoState import NegoState
18from geniusweb.simplerunner.ClassPathConnectionFactory import ClassPathConnectionFactory
19
20
21class Runner:
22 '''
23 A simple tool to run a negotiation stand-alone, without starting the servers.
24 All referred files and classes need to be stored locally (or be in the
25 dependency list if you use maven).
26 <p>
27 <em>IMPORTANT</em> SimpleRunner has a number of restrictions, compared to a
28 run using a runserver and partyserver
29 <ul>
30 <li>With stand-alone runner, your parties are run together in a single
31 classloader. The main implication is that there may arise version conflicts
32 between parties.
33 <li>Stand-alone runner does NOT enforce the time deadline. Parties may
34 continue running indefinitely and thus bog down the JVM and stalling
35 tournaments.
36 </ul>
37 '''
38
39 _properlyStopped:bool = False
40 _LOOPTIME = 200 # ms
41 _FINALWAITTIME = 5000 # ms
42
43 def __init__(self, settings:NegoSettings ,
44 connectionfactory:ClassPathConnectionFactory , logger:Reporter ,
45 maxruntime:int):
46 '''
47 @param settings the {@link NegoSettings}
48 @param connectionfactory the {@link ProtocolToPartyConnFactory}
49 @param logger the {@link Reporter} to log problems
50 @param maxruntime limit in millisecs. Ignored if 0
51 '''
52 if settings == None or connectionfactory == None:
53 raise ValueError("Arguments must be not null");
54 self._settings = settings;
55 self._log = logger;
56 self._protocol = settings.getProtocol(self._log);
57 self._connectionfactory = connectionfactory;
58 self._maxruntime = maxruntime;
59 self._jackson = ObjectMapper()
60
61 def isProperlyStopped(self) -> bool:
62 '''
63 @return true if the runner has finished
64 '''
65 return self._properlyStopped
66
67 def run(self):
68 this = self
69
70 class protocolListener(Listener[ProtocolEvent]):
71
72 def notifyChange(self, evt: ProtocolEvent):
73 this._handle(evt)
74
75 self._protocol.addListener(protocolListener())
76 self._protocol.start(self._connectionfactory)
77 remainingtime = self._maxruntime;
78 while not self._properlyStopped and (self._maxruntime == 0 or remainingtime > 0):
79 time.sleep(self._LOOPTIME / 1000.)
80 remainingtime -= self._LOOPTIME
81 self._log.log(logging.INFO, "Waiting for connection closure")
82
83 remainingtime = self._FINALWAITTIME;
84 while remainingtime > 0 and\
85 len(self._connectionfactory.getOpenConnections()) != 0:
86 time.sleep(self._LOOPTIME / 1000.)
87 remainingtime -= self._LOOPTIME
88
89 openconn = self._connectionfactory.getOpenConnections()
90 if len(openconn) != 0:
91 self._log.log(logging.WARNING, "Connections " + str(openconn)\
92 +" did not close properly at end of run")
93 self._log.log(logging.INFO, "end run")
94
95 def _handle(self, evt:ProtocolEvent):
96 if isinstance(evt , CurrentNegoState) and \
97 evt.getState().isFinal(1000 * time.time()):
98 self._stop()
99
100 def _stop(self):
101 self._logFinal(logging.INFO, self._protocol.getState())
102 self._properlyStopped = True
103
104 def _logFinal(self, level:int , state: NegoState):
105 '''
106 Separate so that we can intercept this when mocking, as this will crash
107 on mocks because {@link #jackson} can not handle mocks.
108
109 @param level the log {@link Level}, eg logging.WARNING
110 @param state the {@link NegoState} to log
111 '''
112 try:
113 self._log.log(level, "protocol ended normally: "
114 +json.dumps(self._jackson.toJson(self._protocol.getState())))
115 except Exception as e: # catch json issues
116 traceback.print_exc()
117
118 def getProtocol(self) -> NegoProtocol:
119 '''
120 @return protocol that runs/ran the session.
121 '''
122 return self._protocol
Note: See TracBrowser for help on using the repository browser.