Another important milestone. Few months after Odd Simon Simonsen invited me to help in developing the very successful and useful plugin TracXmlRpc plugin, and few days after implementing Hessian support for Trac, it's time to focus on Action Message Format. Yes ! Things move fast because it's so easy.
Action Message Format is a binary protocol designed by Macromedia, actually part of Adobe Systems. It's goal is to serialize and deserialize ActionScript objects in order to exchange data between an Adobe Flash application (e.g. Adobe Flash Player ) and a remote service, usually over the internet. In other words it allows using Flash Remoting MX together with ActionScript so as to develop interactive applications , in this particular, by retrieving the data managed by any Trac environment. Few minutes ago I finally could invoke remote services via this protocol using a Python script. The details ...
... I don't want to reveal all the details since the implementation of the underlying API is not completely stable or ready to be released and probably not a few details will change in a near future. However this example helped to figure out how some method should exchange information needed to process the RPC request.
Well, the whole story starts with an enhancement request and a patch submitted to plugin TracXmlRpc issue tracker by thijs . At that moment the only protocols supported were XML-RPC, JSON-RPC. The former was fully supported by standard xmlrpclib.ServerProxy, whereas the later could be used by installing libraries like wsgi-jsonrpc. On the other side there was no way to add further protocols by installing third-party plugins. In a near future this will be possible. The component I am talking about is consistent with that approach. In this particular case, thijs also provided an example based on PyAMF library. I just needed to tweak it a little in order to get this script :
#!/usr/bin/env python """ AMF client for the Trac RPC plugin. """ import base64 from optparse import OptionParser from socket import error import sys from pyamf.remoting import RemotingError from pyamf.remoting.client import RemotingService p = OptionParser() p.add_option("-U", dest="username", help="Trac USER", metavar="USER") p.add_option("-P", dest="password", metavar="PASSWORD", help="use PASSWORD to authenticate USER") p.add_option("-e", dest="env", metavar="URL", help="base URL of target environment") (opts, args) = p.parse_args() username = opts.username password = opts.password try : url = opts.env + '/rpc' except : sys.exit("Missing Trac environment. Use -e option.") service_name = 'system' gw = RemotingService(url) if (username, password) != (None, None): auth = base64.encodestring('%s:%s' % (username, password))[:-1] gw.addHTTPHeader("Authorization", "Basic %s" % auth) service = gw.getService(service_name) try: print service.getAPIVersion() except RemotingError, e: print e except error, e: print e
This script shows the installed version of the plugin . As you can see if the base URL of the Trac environment is at e.g. http://localhost/trac then the protocol is available at http://localhost/trac/rpc. In fact this URL can be used to access all (well, most of ;o) the active protocols installed in a given environment. Protocol selection at that path is based on Content-Type header supplied by the client in the HTTP request (e.g. application/x-amf for AMF). That's the main requisite considered by Odd in order to design the underlying API , and it's great !!! .
In order to see it action, you just need to open a console and type
$ python ./amf-client.py -U username -P mypassword -e http://localhost/trac [1, 1, 0]
That's it! . Below you can see a simplified version using just the Python interpreter.
>>> import base64 >>> from pyamf.remoting import RemotingError >>> from pyamf.remoting.client import RemotingService >>> username, password = 'olemis', 'mypassword' >>> url = 'http://localhost/trac/rpc' >>> gw = RemotingService(url) >>> auth = base64.encodestring('%s:%s' % (username, password))[:-1] >>> gw.addHTTPHeader("Authorization", "Basic %s" % auth) >>> service = gw.getService('system') >>> print service.getAPIVersion() [1, 1, 0]
Isn't it simple ? All these implementations will be offered soon by TracRpcProtocols. The underlying API needs to be released before that, of course ;o). If you'd like to follow the development of this features then I invite you to subscribe to the RSS feed. Adding AMF support for Trac is just the second episode of the first season. Thanks osimons and thijs ;o) .