root/XMLRPCMethod/XMLRPCMethod-1-1/XMLRPCMethod.py

Revision 1058 (checked in by mylan, 10 months ago)

Load XMLRPCMethod/ into XMLRPCMethod/current.

  • Property svn:eol-style set to native
Line 
1 # The contents of this file are subject to the Mozilla Public
2 # License Version 1.1 (the "License"); you may not use this file
3 # except in compliance with the License. You may obtain a copy of
4 # the License at http://www.mozilla.org/MPL/
5 #
6 # Software distributed under the License is distributed on an "AS
7 # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
8 # implied. See the License for the specific language governing
9 # rights and limitations under the License.
10 #
11 # The Original Code is XMLRPCMethod version 1.0.
12 #
13 # The Initial Developer of the Original Code is European Environment
14 # Agency (EEA).  Portions created by Finsiel Romania are
15 # Copyright (C) European Environment Agency.  All
16 # Rights Reserved.
17 #
18 # Contributor(s):
19 # Soren Roug, EEA
20 # Cornel Nitu, Finsiel Romania
21
22 """ XMLRPC Method Product
23
24     This product provides support for remote methods.
25 """
26 __version__='$Revision: 1.11 $'[11:-2]
27 import xmlrpclib, httplib
28 from base64 import encodestring
29 from string import split, join, lower, replace
30 import sys, os, stat, traceback
31 from cgi import escape
32 from urllib import splithost, splitpasswd, splittype, splituser
33 from threading import Thread
34
35 from Globals import Persistent, DTMLFile, MessageDialog, HTML
36 import OFS.SimpleItem, Acquisition
37 from OFS.Cache import Cacheable
38 import AccessControl.Role
39 from OFS.SimpleItem import pretty_tb
40 from App.Management import Navigation
41
42 _marker = []  # Create a new marker object
43
44 manage_addXMLRPCMethodForm=DTMLFile('dtml/methodAdd', globals())
45
46 def manage_addXMLRPCMethod(self, id, title, remoteurl, function, timeout, REQUEST=None):
47     """ Add an external method to a folder
48
49         An addition to the standard object-creation arguments,
50         'id' and title, the following arguments are defined:
51
52         method -- The name of the remote method. This can be a
53           an ordinary Python function, or a bound method.
54
55         remoteurl -- The URL of the RPC dispatcher to call.
56     """
57     id=str(id)
58     title=str(title)
59     remoteurl=str(remoteurl)
60     function=str(function)
61     timeout=float(timeout)
62
63     i=XMLRPCMethod(id,title,remoteurl,function,timeout)
64     self._setObject(id,i)
65     return self.manage_main(self,REQUEST)
66
67 class XMLRPCMethod(OFS.SimpleItem.Item, Persistent, Acquisition.Explicit,
68                      AccessControl.Role.RoleManager, Navigation, Cacheable):
69     """ Web-callable functions that encapsulate remote methods.
70
71     """
72
73     meta_type='XMLRPC Method'
74
75     ZopeTime=Acquisition.Acquired
76     HelpSys=Acquisition.Acquired
77
78     manage_options=(
79         (
80         {'label':'Properties', 'action':'manage_main',
81          'help':('XMLRPCMethod','XMLRPC-Method_Properties.stx')},
82         {'label':'Test', 'action':'',
83          'help':('XMLRPCMethod','XMLRPC-Method_Try-It.stx')},
84         )
85         +OFS.SimpleItem.Item.manage_options
86         +AccessControl.Role.RoleManager.manage_options
87         +Cacheable.manage_options
88         )
89
90     __ac_permissions__=(
91         ('View management screens', ('manage_main',)),
92         ('Change XMLRPC Methods', ('manage_edit',)),
93         ('View', ('__call__','')),
94         )
95
96     def __init__(self, id, title, remoteurl, function,timeout):
97         self.id=id
98         self.manage_edit(title, remoteurl, function,timeout)
99
100     def __setstate__(self,state):
101         XMLRPCMethod.inheritedAttribute('__setstate__')(self, state)
102         if not hasattr(self,'_timeout'):
103             self._timeout = 5.0
104
105     manage_main=DTMLFile('dtml/methodEdit', globals())
106
107     def manage_edit(self, title, remoteurl, function, timeout, REQUEST=None):
108         """Change the external method
109
110         See the description of manage_addXMLRPCMethod for a
111         description of the arguments 'remoteurl' and 'function'.
112         """
113         title=str(title)
114         remoteurl=str(remoteurl)
115         function=str(function)
116         timeout=float(timeout)
117
118         self.title=title
119         if remoteurl:
120             urltype, url = splittype(remoteurl)
121         if urltype != "http":
122             raise IOError, "Only http scheme supported"
123
124         host, selector = splithost(url)
125
126         if not host: raise IOError, ('http error', 'No host given')
127
128         self._remoteurl = remoteurl
129         self._function = function
130         self._timeout = timeout
131         self.ZCacheable_invalidate()
132         if REQUEST:
133             message="XMLRPC Method Created."
134             return self.manage_main(self,REQUEST,manage_tabs_message=message)
135
136     def __call__(self, *args):
137         """ Call an XMLRPC Method """
138
139         __traceback_info__ = args
140         # Retrieve the value from the cache.
141         keyset = None
142         if self.ZCacheable_isCachingEnabled():
143             # Strange; I can't just use args
144             keyset = { '*':args }
145             # Prepare a cache key.
146             results = self.ZCacheable_get(keywords=keyset, default=_marker)
147             if results is not _marker:
148                 return results
149
150         ut = RPCThread(self._remoteurl, self._function, args=args)
151         ut.start()
152         ut.join(self._timeout)
153
154         results = ut.getresult()
155         if keyset is not None:
156             if results is not None:
157                 self.ZCacheable_set(results, keywords=keyset)
158         return results
159
160     def function(self): return self._function
161     def remoteurl(self): return self._remoteurl
162     def timeout(self): return self._timeout
163
164 class RPCThread(Thread):
165
166     def __init__(self,remoteurl,function,args=()):
167         Thread.__init__(self)
168         self.args=args
169         self.result = None
170         self.rpcerror = None
171         self.remoteurl = remoteurl
172         self.function = function
173
174     def getresult(self):
175         if self.rpcerror:
176             raise IOError, escape(self.rpcerror)
177         else:
178             return self.result
179
180     def run(self):
181         self.rpcerror = None
182         urltype, url = splittype(self.remoteurl)
183         if urltype != "http":
184             self.rpcerror = "Only http scheme supported"
185             raise IOError, "Only http scheme supported"
186
187         host, selector = splithost(url)
188
189         if not host: raise IOError, ('http error', 'No host given')
190
191         user_passwd, host = splituser(host)
192
193         url = "http://"+ host + selector
194
195         if user_passwd:
196             server = xmlrpclib.Server(url, BasicAuthTransport(user_passwd))
197         else:
198             server = xmlrpclib.Server(url)
199         f = server.__getattr__(self.function)
200         try: res = apply(f,self.args)
201         except (xmlrpclib.Fault,xmlrpclib.ProtocolError,xmlrpclib.ResponseError), v:
202             self.rpcerror = str(v)
203         #raise IOError, escape(self.rpcerror)
204         else:   
205             self.result = res
206
207
208 class BasicAuthTransport(xmlrpclib.Transport):
209     def __init__(self, userpassword=None):
210         self.userpassword=userpassword
211
212     def request(self, host, handler, request_body):
213         """ issue XML-RPC request """
214
215         h = httplib.HTTP(host)
216         h.putrequest("POST", handler)
217
218         # required by HTTP/1.1
219         h.putheader("Host", host)
220
221         # required by XML-RPC
222         h.putheader("User-Agent", self.user_agent)
223         h.putheader("Content-Type", "text/xml")
224         h.putheader("Content-Length", str(len(request_body)))
225
226         # basic auth
227         if self.userpassword is not None:
228             h.putheader("AUTHORIZATION", "Basic %s" % replace(
229                 encodestring("%s" % (self.userpassword)),
230                 "\012", ""))
231         h.endheaders()
232
233         if request_body:
234             h.send(request_body)
235
236         errcode, errmsg, headers = h.getreply()
237
238         if errcode != 200:
239             raise xmlrpclib.ProtocolError(
240             host + handler,
241             errcode, errmsg,
242             headers
243             )
244
245         return self.parse_response(h.getfile())
Note: See TracBrowser for help on using the browser.