• Facebook
  • Twitter
  • Reddit
  • StumbleUpon
  • Digg
  • email

"""
 
Toolserver Framework for Python - Simple Hello World Tool
 
Copyright (c) 2002, Georg Bauer <gb@rfc1437.de>
 
Permission is hereby granted, free of charge, to any person obtaining a copy of 
this software and associated documentation files (the "Software"), to deal in 
the Software without restriction, including without limitation the rights to 
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 
the Software, and to permit persons to whom the Software is furnished to do so, 
subject to the following conditions:
 
The above copyright notice and this permission notice shall be included in all 
copies or substantial portions of the Software.
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
"""
 
import zlib
import time
 
from Toolserver.Config import config
from Toolserver.Context import context
from Toolserver.Tool import registerTool, StandardTool
from Toolserver.Utils import parseQueryFromRequest, parseQueryFromString, ForbiddenError, AuthError, quote
from Toolserver.TagRenderer import xml
 
from Toolserver.RewriteHandler import patchHostAndPath
from Toolserver.ReactorChain import addChainHead
 
def patchRequest(request):
	(path, params, query, fragment) = request.split_uri()
	print 'checking ', path
	if path == '/greeting/greet':
		patchHostAndPath(request, request.host, '/greeting/greeting')
	return request
 
addChainHead('system.request.rewrite', patchRequest)
 
# fetch different renderer for different documents
response = xml('response')
 
class GreetingTool(StandardTool):
 
	"""
	This tool shows several aspects of the Toolserver Framework for
	Python in action. It has it's own API documentation and generates
	automatically a WSDL document with all methods exposed.
	"""
 
	# These are type declarations for WSDL generation. If you refer to
	# types you define yourself, use the typens: namespace. If you refer
	# to system defined types, use the xsd: namespace!
	_types = StandardTool._types + (
		('stringArray', ['xsd:string']),
		('stringStruct', {'anton':'xsd:string', 'berta':'xsd:string'})
	)
 
	def _initopts(self):
		self.instanceVar = 0
 
	def _defaults(self):
		self.config.configvar = 'anton'
 
	def _invariant(self):
		"""
		Invariant for the tool. This is checked after and before every
		API call to ensure that everything works as specified. It is
		only called when the toolserver is started with the -c switch.
		"""
		assert type(self.instanceVar) == type(1), "instanceVar is an integer"
 
	def _shutdown(self):
		"""
		This method is called under transaction control when the server
		is shut down. If you need to run outside transaction control,
		define _terminate instead.
		"""
		pass
 
	# this method check access to static content below the tools
	# namespace. parts is the list of name elements that lead to the
	# static content, relative to the tools base namespace.
	def _validate_HTTP(self, request, parts):
		if parts[0] == 'blubb':
			raise ForbiddenError('go away')
		elif parts[0] == 'blubber':
			raise AuthError('blubber')
		elif parts[0] == 'blabber':
			return 'blabber'
		else:
			return ''
 
	def greeting(self, name, delay):
		"""
		This method can be called with all three kinds of call:
		XML-RPC, SOAP and REST. To make REST calling possible, there are
		several wrappers defined that parse parameters out of the
		request object and generate results in a format for this
		REST call.
		"""
		if config.verbose:
			print "calling greeting(%s, %s)" % (repr(name), repr(delay))
		time.sleep(delay)
		self.instanceVar = delay
		if name.startswith('uncompressed '):
			context.deflate = 0
		return 'Hello %s!' % name
 
	greeting_signature = ('xsd:string', 'xsd:string', 'xsd:int')
 
	greeting_content_type = 'text/xml'
 
	# this method is guarded with pre and post conditions to ensure
	# that the programming contract is fullfilled.
	# If you run your toolserver with -c, these will be checked
 
	def greeting_pre_condition(self, name, delay):
		assert type(name) in (type(''), type(u'')), "Type of name must be string"
		assert type(delay) == type(1), "Type of delay must be integer"
 
	def greeting_post_condition(self, result):
		assert type(result) in (type(''), type(u'')), "Type of result must be string"
 
	def greeting_parser_GET(self, request, data):
		"""
		The parameters are passed in as named query parameters. The name
		is identical to the API parameter name.
		"""
		form = parseQueryFromRequest(request)
		name = form.get('name', [''])[0]
		delay = int(form.get('delay', [0])[0])
		return ([name, delay], {})
 
	greeting_parser_HEAD = greeting_parser_GET
 
	def greeting_parser_POST(self, request, data):
		"""
		The parameters are passed in as named form parameters whose
		names are identical with the API parameter names.
		"""
		form = parseQueryFromString(data)
		name = form.get('name', [''])[0]
		delay = int(form.get('delay', [0])[0])
		return ([name, delay], {})
 
	def greeting_generator(self, request, result):
		"""
		Turn the resulting message into a XML document.
		"""
		msg = response.response(response.message(result))
		return msg
 
	def _error(self, request, error, detail, traceback):
		"""
		Exceptions are formatted as a special XML document that
		includes the message, the detail and the traceback of the
		exception.
		"""
		lines = []
		for row in traceback:
			lines.append(response.line(
				response.file(row[0]),
				response.line(row[1]),
				response.function(row[2]),
				response.source(quote(row[3]))
			))
		msg = response.response(
			response.error(
				response.message(error),
				response.detail(detail)
			),
			response.traceback(lines)
		)
		return msg
 
	def restricted(self, request, data):
		"""
		This method is restricted by a user and a password. You
		need to use user 'hugo' with password 'blubb' to get
		the result. Only GET is allowed, all other access - even
		RPC type access - is forbidden.
		"""
		msg = response.response(response.message('successfull'))
		return msg
 
	def restricted_validate_GET(self, request):
		if context.client != 'hugo':
			return 'restricted'
		return ''
 
	def restricted_validate(self, request):
		raise ForbiddenError('restricted')
 
	def restricted_validate_RPC(self, *args, **kw):
		raise ForbiddenError('restricted')
 
	compressed_deflate = 0
	def compressed_GET(self, request, data):
		"""
		This method itself deflates it's result, so the server
		shouldn't be allowed to do compression. Otherwise the content
		would be encoded two times.
		"""
		request['Content-encoding'] = 'deflate'
		return zlib.compress('Hello World, this is a compressed string!'*50)
 
	def echoStrings(self, strings):
		"""
		This method returns the strings concatenated. This
		can be used to test complex SOAP signatures.
		"""
		return ' '.join(strings)
 
	echoStrings_signature = ('xsd:string', 'typens:stringArray')
 
	def echoStruct(self, struct):
		"""
		This method returns a string that is concatenated
		from the keys and values of a dictionary. This is used
		to demonstrate complex SOAP signatures, too.
		"""
		return ', '.join(map(lambda k: '%s=%s' % (k, struct[k]), struct.keys()))
 
	echoStruct_signature = ('xsd:string', 'typens:stringStruct')
 
	def start(self, string, seconds):
		"""
		This method starts a background processing and returns
		the identifier to check the status of this processing.
		"""
		if config.verbose:
			print 'starting background thread to output %s' % string
		asyncid = self._async(self._background, string, seconds)
		if config.verbose:
			print '... using async id %s' % asyncid
		return asyncid
 
	start_signature = ('xsd:string', 'xsd:string', 'xsd:int')
 
	def running(self, asyncid):
		"""
		Checks wether a process is still running.
		"""
		return self._asyncActive(asyncid)
 
	running_signature = ('xsd:int', 'xsd:string')
 
	def result(self, asyncid):
		"""
		Returns the result (or the exceptioin) of a background
		process.
		"""
		return self._asyncResult(asyncid)
 
	result_signature = ('xsd:string', 'xsd:string')
 
	def _background(self, string, seconds):
		if config.verbose:
			print "calling _background(%s, %s)" % (repr(string), repr(seconds))
		time.sleep(seconds)
		return string
 
	def blocking(self, seconds):
		"""
		This call will do a blocking sleep so only one of this
		call can process at the same time. This uses the tool
		level locking.
		"""
		try:
			self._acquire()
			time.sleep(seconds)
		finally: self._release()
 
	blocking_signature = ('xsd:string', 'xsd:int')
 
	def blockingRead(self):
		"""
		This call is just a blocking read method - it returns
		directly without delay, but tries to lock the tool.
		"""
		try:
			self._acquire()
			return 'success'
		finally: self._release()
 
	blockingRead_signature = ('xsd:string',)
 
	def startat(self, timespec, string, seconds):
		"""
		This method starts a background process at a defined
		time. It uses the timed execution feature.
		"""
		if config.verbose:
			print 'starting timer in %d seconds' % int(timespec - time.time())
		asyncid = self._timed(timespec, self._background, string, seconds)
		if config.verbose:
			print '... using async id %s' % asyncid
		return asyncid
 
	startat_signature = ('xsd:int', 'xsd:string', 'xsd:int')
 
	def waiting(self, asyncid):
		"""
		Checks wether a background process is still waiting for
		execution.
		"""
		return self._timedWaiting(asyncid)
 
	waiting_signature = ('xsd:int', 'xsd:string')
 
registerTool(GreetingTool, 'greeting')