3
#include "xmlrpc_embedder.h"
4
#include <wx/listimpl.cpp>
5
#include <wx/arrimpl.cpp>
6
#include "ExecHiddenMSW.h"
7
//WX_DEFINE_OBJARRAY(XmlRpcInstanceCollection);
8
WX_DEFINE_LIST(XmlRpcJobQueue);
11
using namespace XmlRpc;
13
int ID_XMLRPC_PROC=wxNewId();
16
class ExecAsyncJob: public XmlRpcJob
19
ExecAsyncJob(XmlRpcInstance *inst, const wxString &method, const XmlRpc::XmlRpcValue &inarg, wxEvtHandler *hndlr, int id=wxID_ANY): XmlRpcJob(inst,hndlr,id)
21
m_method=wxString(method.mb_str(wxConvUTF8),wxConvUTF8);
24
virtual bool operator()()
26
if(xmlrpc_instance->Exec(m_method,m_inarg,m_result))
28
XmlRpcResponseEvent pe(id,xmlrpc_instance,parent,XMLRPC_STATE_RESPONSE,m_result);
29
::wxPostEvent(parent,pe);
33
XmlRpcResponseEvent pe(id,xmlrpc_instance,parent,XMLRPC_STATE_REQUEST_FAILED,m_result);
34
::wxPostEvent(parent,pe);
40
XmlRpc::XmlRpcValue m_inarg;
41
XmlRpc::XmlRpcValue m_result;
45
//////////////////////////////////////////////////////////////////////////////////
46
//////////////////////////////////////////////////////////////////////////////////
48
//////////////////////////////////////////////////////////////////////////////////
49
//////////////////////////////////////////////////////////////////////////////////
52
//IMPLEMENT_DYNAMIC_CLASS(XmlRpcJob, wxThread)
54
XmlRpcJob::XmlRpcJob(XmlRpcInstance *xmlrpc_instance, wxEvtHandler *p, int id, bool selfdestroy):
55
wxThread(wxTHREAD_JOINABLE)
59
this->xmlrpc_instance=xmlrpc_instance;
62
killonexit=selfdestroy;
65
void XmlRpcJob::Abort()
71
XmlRpcJob::~XmlRpcJob()
76
//void *XmlRpcJob::Entry()
78
// wxMessageBox(_("entered thread"));
79
// XmlRpcNotifyUIEvent pe(id,xmlrpc_instance,parent,PYSTATE_STARTEDJOB);
80
// ::wxPostEvent(xmlrpc_instance,pe);
82
// pe.SetState(PYSTATE_FINISHEDJOB);
84
// pe.SetState(PYSTATE_ABORTEDJOB);
85
// ::wxPostEvent(xmlrpc_instance,pe);
90
void *XmlRpcJob::Entry()
93
wxCommandEvent pe(wxEVT_XMLRPC_STARTED,0);
94
parent->AddPendingEvent(pe);
98
wxCommandEvent pe(wxEVT_XMLRPC_FINISHED,0);
99
xmlrpc_instance->AddPendingEvent(pe);
103
wxCommandEvent pe(wxEVT_XMLRPC_ABORTED,0);
104
xmlrpc_instance->AddPendingEvent(pe);
111
const char REQUEST_BEGIN[] =
112
"<?xml version=\"1.0\"?>\r\n"
113
"<methodCall><methodName>";
114
const char REQUEST_END_METHODNAME[] = "</methodName>\r\n";
115
const char PARAMS_TAG[] = "<params>";
116
const char PARAMS_ETAG[] = "</params>";
117
const char PARAM_TAG[] = "<param>";
118
const char PARAM_ETAG[] = "</param>";
119
const char REQUEST_END[] = "</methodCall>\r\n";
120
const char METHODRESPONSE_TAG[] = "<methodResponse>";
121
const char FAULT_TAG[] = "<fault>";
123
class XmlRpcPipeClient
126
XmlRpcPipeClient(wxProcess *proc)
130
m_istream=proc->GetInputStream();
131
m_ostream=proc->GetOutputStream();
132
m_estream=proc->GetErrorStream();
134
bool set_error(const std::string &s,XmlRpc::XmlRpcValue& result)
145
bool execute(const char* method, XmlRpc::XmlRpcValue const& params, XmlRpc::XmlRpcValue& result)
149
result[0] = "XmlRpc pipe client is in an error state. Clear the error or reset the server.";
153
if(!generateRequest(method,params,msg))
154
return set_error("Bad request value for method call "+std::string(method),result);
155
uint32_t r_size=msg.size(); //TODO: Is it safer to use uint64_t (would need to use long long on the python side)
156
char *cr_size = (char*)&r_size;
157
for(uint32_t i=0;i<sizeof(uint32_t);++i)
161
m_ostream->PutC(cr_size[i]);
162
if(m_ostream->GetLastError()!=wxSTREAM_NO_ERROR && m_ostream->GetLastError()!=wxSTREAM_EOF)
163
return set_error("Broken stream attempting to write request size to pipe",result);
164
} while (m_ostream->LastWrite()!=1);
166
for(uint32_t i=0;i<msg.size();++i)
170
m_ostream->PutC(msg[i]);
171
if(m_ostream->GetLastError()!=wxSTREAM_NO_ERROR && m_ostream->GetLastError()!=wxSTREAM_EOF)
172
return set_error("Broken stream attempting to write request to pipe",result);
173
} while (m_ostream->LastWrite()!=1);
176
//NOW WAIT FOR THE REPLY
177
//FIRST RETRIEVE A SINGLE CHARACTER "M" THAT DENOTES THE START OF THE REPLY
178
//TODO: CHANGE 'M' TO 0 AND ACTUALLY CHECK IT!!!
183
ch=m_istream->GetC();
184
eof = m_istream->GetLastError()==wxSTREAM_EOF;
186
wxMilliSleep(10); //Delay might be necessary to avoid a freeze on MS windows
188
if(m_istream->GetLastError()!=wxSTREAM_NO_ERROR && m_istream->GetLastError()!=wxSTREAM_EOF)
189
return set_error("Broken stream attempting to read message char 'M' from pipe",result);
191
// THEN GET THE SIZE OF THE MESSAGE AS UNSIGNED 32 BIT INT
192
for(uint32_t i=0;i<sizeof(uint32_t);i++)
196
((char*)(&r_size))[i]=m_istream->GetC();
197
eof = m_istream->GetLastError()==wxSTREAM_EOF;
199
wxMilliSleep(10); //Delay might be necessary to avoid a freeze on MS windows
201
if(m_istream->GetLastError()!=wxSTREAM_NO_ERROR && m_istream->GetLastError()!=wxSTREAM_EOF)
202
return set_error("broken stream attempting to read size of buffer",result);
205
// FINALLY RETRIEVE THE ACTUAL MESSAGE
207
buf.resize(r_size+1);
208
for(uint32_t i=0;i<r_size;i++)
212
buf[i]=m_istream->GetC();
213
eof = m_istream->GetLastError()==wxSTREAM_EOF;
215
wxMilliSleep(10); //Delay might be necessary to avoid a freeze on MS windows
217
if(m_istream->GetLastError()!=wxSTREAM_NO_ERROR && m_istream->GetLastError()!=wxSTREAM_EOF)
218
return set_error(std::string(wxString::Format(_T("broken stream attempting to read buffer - chars read %i of %i"),i,r_size).utf8_str()),result);
222
// NOW CONVERT THE XML INTO AN XMLRPCVALUE
223
if(parseResponse(buf, result))
227
wxString s = wxString::Format(_T("error parsing read buffer - chars read %i\n"),r_size);
228
std::string out = std::string(s.utf8_str()) + std::string(buf);
229
return set_error(out,result);
232
// Converts the response xml into a result value
233
bool parseResponse(std::string _response, XmlRpcValue& result)
235
// Parse response xml into result
238
if ( ! XmlRpcUtil::findTag(METHODRESPONSE_TAG,_response,&offset)) {
239
XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no methodResponse. Response:\n%s", _response.c_str());
243
// Expect either <params><param>... or <fault>...
244
if ((XmlRpcUtil::nextTagIs(PARAMS_TAG,_response,&offset) &&
245
XmlRpcUtil::nextTagIs(PARAM_TAG,_response,&offset)) ||
246
XmlRpcUtil::nextTagIs(FAULT_TAG,_response,&offset) && (_isFault = true))
248
if ( ! result.fromXml(_response, &offset)) {
249
XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response value. Response:\n%s", _response.c_str());
254
XmlRpcUtil::error("Error in XmlRpcClient::parseResponse: Invalid response - no param or fault tag. Response:\n%s", _response.c_str());
260
return result.valid();
262
// Encode the request to call the specified method with the specified parameters into xml
263
bool generateRequest(const char* methodName, XmlRpcValue const& params, std::string &request)
265
std::string body = REQUEST_BEGIN;
267
body += REQUEST_END_METHODNAME;
269
// If params is an array, each element is a separate parameter
270
if (params.valid()) {
272
if (params.getType() == XmlRpcValue::TypeArray)
274
for (int i=0; i<params.size(); ++i) {
276
body += params[i].toXml();
283
body += params.toXml();
292
XmlRpcUtil::log(4, "XmlRpcClient::generateRequest: header is %d bytes, content-length is %d.",
293
header.length(), body.length());
302
wxInputStream *m_istream;
303
wxOutputStream *m_ostream;
304
wxInputStream *m_estream;
308
//////////////////////////////////////////////////////////////////////////////////
309
//////////////////////////////////////////////////////////////////////////////////
310
// class XmlRpcInstance
311
//////////////////////////////////////////////////////////////////////////////////
312
//////////////////////////////////////////////////////////////////////////////////
314
//IMPLEMENT_DYNAMIC_CLASS(XmlRpcInstance, wxEvtHandler)
316
IMPLEMENT_CLASS(XmlRpcInstance, wxEvtHandler)
319
BEGIN_EVENT_TABLE(XmlRpcInstance, wxEvtHandler)
320
// EVT_PY_NOTIFY_UI(XmlRpcInstance::OnJobNotify)
321
EVT_COMMAND(0, wxEVT_XMLRPC_FINISHED, XmlRpcInstance::OnJobNotify)
322
EVT_COMMAND(0, wxEVT_XMLRPC_ABORTED, XmlRpcInstance::OnJobNotify)
323
EVT_END_PROCESS(ID_XMLRPC_PROC, XmlRpcInstance::OnEndProcess)
326
//XmlRpcInstance::XmlRpcInstance()
328
// // create python process
329
// // create xmlrpc client
330
// // get & store methods from the xmlrpc server
331
// // set running state flag
334
XmlRpcInstance::~XmlRpcInstance()
336
// kill python process if still running
337
if (!m_proc_dead) //TODO: killing while job running could cause probs...
340
if(wxProcess::Exists(m_proc_id))
341
wxProcess::Kill(m_proc_id,wxSIGKILL,wxKILL_CHILDREN);
343
// check if any running jobs, kill them
345
wxMutexLocker ml(exec_mutex); //TODO: This may result in huge delays?
354
XmlRpcInstance::XmlRpcInstance(const wxString &processcmd, int port, const wxString &hostaddress, wxWindow *parent)
362
m_hostaddress=hostaddress;
367
if (processcmd!=wxEmptyString)
368
LaunchProcess(processcmd); //TODO: The command for the interpreter process should come from the manager (and be stored in a config file)
371
long XmlRpcInstance::LaunchProcess(const wxString &processcmd)
373
// std::cout<<"PyCC: LAUNCHING PROCESS"<<std::endl;
376
if(m_proc) //this should never happen
377
m_proc->Detach(); //self cleanup
378
m_proc=new wxProcess(this,ID_XMLRPC_PROC);
381
// m_proc->Redirect(); //TODO: this only needs to be done on windows and buffers must be flushed periodically if there is any I/O to/from the process
383
//by default wxExecute shows the terminal window on MSW (redirecting would hide it, but that breaks the process if buffers are not flushed periodically)
385
m_proc_id=wxExecute(processcmd,wxEXEC_ASYNC/*|wxEXEC_MAKE_GROUP_LEADER*/,m_proc);
387
m_proc_id=wxExecuteHidden(processcmd,wxEXEC_ASYNC|wxEXEC_MAKE_GROUP_LEADER,m_proc);
388
// m_proc_id=wxExecute(processcmd,wxEXEC_ASYNC/*|wxEXEC_MAKE_GROUP_LEADER*/,m_proc);
390
m_proc_id=wxExecute(processcmd,wxEXEC_ASYNC|wxEXEC_MAKE_GROUP_LEADER,m_proc);
394
// std::cout<<"PyCC: LAUNCHING PROCESS SUCCEEDED"<<std::endl;
398
// Setup XMLRPC client and use introspection API to look up the supported methods
400
m_pipeclient = new XmlRpcPipeClient(m_proc);
402
m_client = new XmlRpc::XmlRpcClient(m_hostaddress.char_str(), m_port);
407
XmlRpcInstance::XmlRpcInstance(const XmlRpcInstance ©)
409
m_parent=copy.m_parent;
410
m_paused=copy.m_paused;
411
m_queue=copy.m_queue;
412
m_hostaddress=copy.m_hostaddress;
415
m_proc_id=copy.m_proc_id;
416
m_proc_killlevel=copy.m_proc_killlevel;
417
m_proc_dead=copy.m_proc_dead;
420
bool XmlRpcInstance::Exec(const wxString &method, const XmlRpc::XmlRpcValue &inarg, XmlRpc::XmlRpcValue &result)
424
wxMutexLocker ml(exec_mutex);
426
return m_client->execute(method.utf8_str(), inarg, result);
427
else if(m_pipeclient)
428
return m_pipeclient->execute(method.utf8_str(), inarg, result);
431
result[0] = "XmlRpc server is not connected.";
435
bool XmlRpcInstance::ExecAsync(const wxString &method, const XmlRpc::XmlRpcValue &inarg, wxEvtHandler *hndlr, int id)
437
return AddJob(new ExecAsyncJob(this,method,inarg,hndlr,id));
440
void XmlRpcInstance::CleanupTerminatedProcess()
452
void XmlRpcInstance::OnEndProcess(wxProcessEvent &event)
454
// std::cout<<"PYCC: PROCESS DIED!!"<<std::endl;
455
//TODO: m_exitcode=event.GetExitCode();
457
wxCommandEvent ce(wxEVT_XMLRPC_PROC_END,0);
459
m_parent->AddPendingEvent(ce);
462
CleanupTerminatedProcess();
463
// XmlRpcMgr::Get().Remove(this);
466
void XmlRpcInstance::Break()
471
if(wxProcess::Exists(pid))
474
//TODO: Verify that this actually works
475
// Use the WIN32 native call to send a CTRL+C signal
476
GenerateConsoleCtrlEvent(CTRL_C_EVENT,pid); //may need to #include <Windows.h> and link to Kernel32.dll
477
// also need to check whether the pid is valid or something else needs to be used.
479
wxProcess::Kill(pid,wxSIGINT); //,wxKILL_CHILDREN?
485
void XmlRpcInstance::KillProcess(bool force)
490
// if(m_proc_killlevel==0)
492
// m_proc_killlevel=1;
493
// if(wxProcess::Exists(pid))
494
// wxProcess::Kill(pid,wxSIGTERM);
497
if(m_proc_killlevel==0)
499
if(wxProcess::Exists(pid))
501
wxProcess::Kill(pid,wxSIGKILL,wxKILL_CHILDREN);
506
bool XmlRpcInstance::NextJob()
508
if(m_queue.GetCount()<=0)
510
XmlRpcJob *newjob=m_queue.GetFirst()->GetData();
511
if(!newjob->finished && !newjob->started)
513
if(newjob->Create()!=wxTHREAD_NO_ERROR)
515
wxMessageBox(_T("PYCC: Error creating a new thread. Unable to continue."));
516
m_queue.DeleteObject(newjob);
520
newjob->started=true;
528
bool XmlRpcInstance::AddJob(XmlRpcJob *job)
537
void XmlRpcInstance::ClearJobs()
539
int i = m_queue.GetCount()-1;
542
XmlRpcJob *job=m_queue.Item(i)->GetData();
543
if(job && !job->started && !job->finished)
545
m_queue.DeleteObject(job);
553
void XmlRpcInstance::OnJobNotify(wxCommandEvent &event)
555
XmlRpcJob *job=m_queue.GetFirst()->GetData();
563
m_queue.DeleteObject(job);
572
CleanupTerminatedProcess();
576
//XmlRpcMgr::XmlRpcMgr()
580
//XmlRpcMgr::~XmlRpcMgr()
582
// m_Interpreters.Clear();
585
//XmlRpcInstance *XmlRpcMgr::LaunchProcess(const wxString &cmd,int port,const wxString &address)
587
// XmlRpcInstance *p=new XmlRpcInstance(cmd,address,port);
589
// m_Interpreters.Add(p);
593
//void XmlRpcMgr::Remove(XmlRpcInstance *p)
595
//// m_Interpreters.Remove(*p);
596
//// for (size_t i=0; i<m_Interpreters.GetCount(); ++i)
598
//// if (m_Interpreters.Item(i) == *p)
600
//// m_Interpreters.RemoveAt(i);
606
//XmlRpcMgr &XmlRpcMgr::Get()
608
// if (theSingleInstance.get() == 0)
609
// theSingleInstance.reset(new XmlRpcMgr);
610
// return *theSingleInstance;
613
//std::auto_ptr<XmlRpcMgr> XmlRpcMgr::theSingleInstance;