source: geniuswebcore/geniusweb/simplerunner/NegoRunner.py@ 77

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

Fix for IssueValue hashcode.

File size: 5.1 KB
RevLine 
[73]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 StdOutReporter (Reporter):
22 def log(self, level:int , msg:str, exc:Optional[BaseException]=None):
23 if level >= logging.WARNING :
24 print(logging.getLevelName(level) + ":" + msg, file=sys.stderr)
25 else:
26 print(logging.getLevelName(level) + ":" + msg)
27
28
29class NegoRunner:
30 '''
31 WARNING DO NOT USE. NOT WORKING YET.
32
33 A simple tool to run a negotiation stand-alone, without starting the servers.
34 All referred files and classes need to be stored locally (or be in the
35 dependency list if you use maven).
36 <p>
37 <em>IMPORTANT</em> SimpleRunner has a number of restrictions, compared to a
38 run using a runserver and partyserver
39 <ul>
40 <li>With stand-alone runner, your parties are run together in a single
41 classloader. The main implication is that there may arise version conflicts
42 between parties.
43 <li>Stand-alone runner does NOT enforce the time deadline. Parties may
44 continue running indefinitely and thus bog down the JVM and stalling
45 tournaments.
46 </ul>
47 '''
48
49 _properlyStopped:bool = False
50 _LOOPTIME = 200 # ms
51 _FINALWAITTIME = 5000 # ms
52
53 def __init__(self, settings:NegoSettings ,
54 connectionfactory:ClassPathConnectionFactory , logger:Reporter ,
55 maxruntime:int):
56 '''
57 @param settings the {@link NegoSettings}
58 @param connectionfactory the {@link ProtocolToPartyConnFactory}
59 @param logger the {@link Reporter} to log problems
60 @param maxruntime limit in millisecs. Ignored if 0
61 '''
62 if settings == None or connectionfactory == None:
63 raise ValueError("Arguments must be not null");
64 self._settings = settings;
65 self._log = logger;
66 self._protocol = settings.getProtocol(self._log);
67 self._connectionfactory = connectionfactory;
68 self._maxruntime = maxruntime;
69 self._jackson=ObjectMapper()
70
71 def isProperlyStopped(self)->bool:
72 '''
73 @return true if the runner has finished
74 '''
75 return self._properlyStopped
76
77 def run(self):
78 this=self
79 class protocolListener(Listener[ProtocolEvent]):
80 def notifyChange(self, evt: ProtocolEvent):
81 this._handle(evt)
82
83 self._protocol.addListener(protocolListener())
84 self._protocol.start(self._connectionfactory)
85 remainingtime = self._maxruntime;
86 while not self._properlyStopped and (self._maxruntime == 0 or remainingtime > 0):
87 time.sleep(self._LOOPTIME/1000.)
88 remainingtime -= self._LOOPTIME
89 self._log.log(logging.INFO, "Waiting for connection closure")
90
91 remainingtime = self._FINALWAITTIME;
92 while remainingtime > 0 and\
93 len(self._connectionfactory.getOpenConnections())!=0:
94 time.sleep(self._LOOPTIME/1000.)
95 remainingtime -= self._LOOPTIME
96
97 openconn = self._connectionfactory.getOpenConnections()
98 if len(openconn)!=0:
99 self._log.log(logging.WARNING, "Connections " + str(openconn)\
100 + " did not close properly at end of run")
101 self._log.log(logging.INFO, "end run")
102
103 def _handle(self, evt:ProtocolEvent ):
104 if isinstance(evt , CurrentNegoState) and \
105 evt.getState().isFinal(1000*time.time()):
106 self._stop()
107
108 def _stop(self):
109 self._logFinal(logging.INFO, self._protocol.getState())
110 self._properlyStopped =True
111
112 def _logFinal(self, level:int , state: NegoState):
113 '''
114 Separate so that we can intercept this when mocking, as this will crash
115 on mocks because {@link #jackson} can not handle mocks.
116
117 @param level the log {@link Level}, eg logging.WARNING
118 @param state the {@link NegoState} to log
119 '''
120 try:
121 self._log.log(level, "protocol ended normally: "
122 + json.dumps(self._jackson.toJson(self._protocol.getState())))
123 except Exception as e: # catch json issues
124 traceback.print_exc()
125
126 def getProtocol(self) ->NegoProtocol :
127 '''
128 @return protocol that runs/ran the session.
129 '''
130 return self._protocol
131
132 @staticmethod
133 def main( args:List[str]) :
134 '''
135 The main runner
136
137 @param args should have 1 argument, the settings.json file to be used.
138 @throws IOException if problem occurs
139 '''
140 if len(args) != 1:
141 NegoRunner.showusage()
142 return;
143
144 serialized = Path(args[0]).read_text("utf-8")
145 settings:NegoSettings = ObjectMapper().parse(json.loads(serialized),
146 NegoSettings) #type:ignore
147
148 runner = NegoRunner(settings,
149 ClassPathConnectionFactory(), StdOutReporter(), 0)
150 runner.run()
151
152 @staticmethod
153 def showusage():
154 print("GeniusWeb stand-alone runner.")
155 print("first argument should be <settings.json>.")
156 print("The settings.json file should contain the NegoSettings.")
157 print("See the settings.json example file and the GeniusWeb wiki pages. ")
158
159
160
161if __name__ == '__main__':
162 NegoRunner.main([sys.argv[1]])
163
Note: See TracBrowser for help on using the repository browser.