source: products/XMLRPCMethod/XMLRPCMethod-1-1/XMLRPCMethod.py @ 20

Last change on this file since 20 was 19, checked in by myroslav, 18 years ago

Layout changes, typos corrected

  • Property svn:eol-style set to native
File size: 7.6 KB
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]
27import xmlrpclib, httplib
28from base64 import encodestring
29from string import split, join, lower, replace
30import sys, os, stat, traceback
31from cgi import escape
32from urllib import splithost, splitpasswd, splittype, splituser
33from threading import Thread
34
35from Globals import Persistent, DTMLFile, MessageDialog, HTML
36import OFS.SimpleItem, Acquisition
37from OFS.Cache import Cacheable
38import AccessControl.Role
39from OFS.SimpleItem import pretty_tb
40from App.Management import Navigation
41
42_marker = []  # Create a new marker object
43
44manage_addXMLRPCMethodForm=DTMLFile('dtml/methodAdd', globals())
45
46def 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
67class 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
164class 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
208class 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 repository browser.