From caf895ebaddcba6b09bbe29eee23862ba1ca0126 Mon Sep 17 00:00:00 2001 From: jsdelfino Date: Sat, 6 Mar 2010 09:23:34 +0000 Subject: Translated XML, ATOM and JSON conversion functions from C++ to Python. Added support for JSON-RPC and ATOM to the WSGI integration. Adjusted server module test cases to help test the WSGI server. git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@919721 13f79535-47bb-0310-9956-ffa450edef68 --- sca-cpp/trunk/etc/git-exclude | 1 + sca-cpp/trunk/modules/python/server-test.py | 4 +- sca-cpp/trunk/modules/wsgi/Makefile.am | 4 +- sca-cpp/trunk/modules/wsgi/app.yaml | 22 ++- sca-cpp/trunk/modules/wsgi/atom-test.py | 153 +++++++++++++++++++ sca-cpp/trunk/modules/wsgi/atomutil.py | 103 +++++++++++++ sca-cpp/trunk/modules/wsgi/client-test.cpp | 4 +- sca-cpp/trunk/modules/wsgi/composite.py | 123 +++++++++++---- sca-cpp/trunk/modules/wsgi/elemutil.py | 168 +++++++++++++++++++++ sca-cpp/trunk/modules/wsgi/htdocs/entry.xml | 2 + sca-cpp/trunk/modules/wsgi/htdocs/feed.xml | 2 + sca-cpp/trunk/modules/wsgi/htdocs/index.html | 1 + sca-cpp/trunk/modules/wsgi/htdocs/json-request.txt | 1 + sca-cpp/trunk/modules/wsgi/htdocs/json-result.txt | 1 + sca-cpp/trunk/modules/wsgi/json-test.py | 59 ++++++++ sca-cpp/trunk/modules/wsgi/jsonutil.py | 140 +++++++++++++++++ sca-cpp/trunk/modules/wsgi/runtime.py | 30 ---- sca-cpp/trunk/modules/wsgi/scdl.py | 85 +++++++++-- sca-cpp/trunk/modules/wsgi/server-test | 2 +- sca-cpp/trunk/modules/wsgi/server-test.py | 4 +- sca-cpp/trunk/modules/wsgi/util-test | 27 ++++ sca-cpp/trunk/modules/wsgi/util.py | 49 +++--- sca-cpp/trunk/modules/wsgi/wiring-test | 32 ++-- sca-cpp/trunk/modules/wsgi/wsgi-test | 69 +++++++++ sca-cpp/trunk/modules/wsgi/xml-test.py | 74 +++++++++ sca-cpp/trunk/modules/wsgi/xmlutil.py | 114 ++++++++++++++ sca-cpp/trunk/test/store-python/shopping-cart.py | 2 +- 27 files changed, 1156 insertions(+), 120 deletions(-) create mode 100755 sca-cpp/trunk/modules/wsgi/atom-test.py create mode 100644 sca-cpp/trunk/modules/wsgi/atomutil.py create mode 100644 sca-cpp/trunk/modules/wsgi/elemutil.py create mode 100644 sca-cpp/trunk/modules/wsgi/htdocs/entry.xml create mode 100644 sca-cpp/trunk/modules/wsgi/htdocs/feed.xml create mode 100644 sca-cpp/trunk/modules/wsgi/htdocs/index.html create mode 100644 sca-cpp/trunk/modules/wsgi/htdocs/json-request.txt create mode 100644 sca-cpp/trunk/modules/wsgi/htdocs/json-result.txt create mode 100755 sca-cpp/trunk/modules/wsgi/json-test.py create mode 100644 sca-cpp/trunk/modules/wsgi/jsonutil.py delete mode 100644 sca-cpp/trunk/modules/wsgi/runtime.py create mode 100755 sca-cpp/trunk/modules/wsgi/util-test create mode 100755 sca-cpp/trunk/modules/wsgi/wsgi-test create mode 100755 sca-cpp/trunk/modules/wsgi/xml-test.py create mode 100644 sca-cpp/trunk/modules/wsgi/xmlutil.py diff --git a/sca-cpp/trunk/etc/git-exclude b/sca-cpp/trunk/etc/git-exclude index 113a953648..9b81c5e267 100644 --- a/sca-cpp/trunk/etc/git-exclude +++ b/sca-cpp/trunk/etc/git-exclude @@ -66,6 +66,7 @@ doxygen *.stamp *.jar *.prefix +index.yaml # Specific ignores kernel-test diff --git a/sca-cpp/trunk/modules/python/server-test.py b/sca-cpp/trunk/modules/python/server-test.py index 8e6b79b56a..dcda763043 100644 --- a/sca-cpp/trunk/modules/python/server-test.py +++ b/sca-cpp/trunk/modules/python/server-test.py @@ -36,7 +36,7 @@ def post(collection, item): return ("123456789",) def put(id, item): - return true + return True def delete(id): - return true + return True diff --git a/sca-cpp/trunk/modules/wsgi/Makefile.am b/sca-cpp/trunk/modules/wsgi/Makefile.am index d6e2a6f957..9df8135ab2 100644 --- a/sca-cpp/trunk/modules/wsgi/Makefile.am +++ b/sca-cpp/trunk/modules/wsgi/Makefile.am @@ -19,13 +19,13 @@ if WANT_PYTHON INCLUDES = -I${PYTHON_INCLUDE} -mod_SCRIPTS = composite.py runtime.py scdl.py util.py wsgi-start wsgi-stop +mod_SCRIPTS = composite.py scdl.py util.py elemutil.py xmlutil.py atomutil.py jsonutil.py wsgi-start wsgi-stop moddir = $(prefix)/modules/wsgi client_test_SOURCES = client-test.cpp client_test_LDFLAGS = -lxml2 -lcurl -lmozjs noinst_PROGRAMS = client-test -#TESTS = server-test +TESTS = util-test wsgi-test wiring-test server-test endif diff --git a/sca-cpp/trunk/modules/wsgi/app.yaml b/sca-cpp/trunk/modules/wsgi/app.yaml index dc9ad1bdf7..40addef28f 100644 --- a/sca-cpp/trunk/modules/wsgi/app.yaml +++ b/sca-cpp/trunk/modules/wsgi/app.yaml @@ -15,10 +15,30 @@ # specific language governing permissions and limitations # under the License. -application: wsgi-app +application: sca-wsgi version: 1 runtime: python api_version: 1 +skip_files: +- ^(.*/)?app\.yaml +- ^(.*/)?app\.yml +- ^(.*/)?index\.yaml +- ^(.*/)?index\.yml +- ^(.*/)?#.*# +- ^(.*/)?.*~ +- ^(.*/)?.*\.py[co] +- ^(.*/)?.*/RCS/.* +- ^(.*/)?\..* +- ^(.*/)?.*-test$ +- ^(.*/)?.*\.cpp$ +- ^(.*/)?.*\.o$ +- ^(.*/)?core$ +- ^(.*/)?.*\.out$ +- ^(.*/)?.*\.log$ +- ^(.*/)?Makefile.* +- ^(.*/)?tmp/.* +- ^(.*/)?wsgi-start +- ^(.*/)?wsgi-stop handlers: - url: /.* diff --git a/sca-cpp/trunk/modules/wsgi/atom-test.py b/sca-cpp/trunk/modules/wsgi/atom-test.py new file mode 100755 index 0000000000..61435bf438 --- /dev/null +++ b/sca-cpp/trunk/modules/wsgi/atom-test.py @@ -0,0 +1,153 @@ +#!/usr/bin/python +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Test ATOM data conversion functions + +import unittest +from elemutil import * +from atomutil import * + +itemEntry = \ + "\n" \ + "" \ + "item" \ + "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b" \ + "" \ + "" \ + "Apple$2.99" \ + "" \ + "" \ + "" \ + "\n" + +incompleteEntry = \ + "" \ + "item" \ + "" \ + "Orange" \ + "3.55" \ + "" \ + "" \ + "" + +completedEntry = \ + "\n" \ + "" \ + "item" \ + "" \ + "" \ + "" \ + "Orange" \ + "3.55" \ + "" \ + "" \ + "\n" + +def testEntry(): + i = (element, "'item", (element, "'name", "Apple"), (element, "'price", "$2.99")) + a = ("item", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b", i) + s = writeATOMEntry(a); + assert car(s) == itemEntry + + a2 = readATOMEntry((itemEntry,)) + s2 = writeATOMEntry(a2) + assert car(s2) == itemEntry + + a3 = readATOMEntry((incompleteEntry,)) + s3 = writeATOMEntry(a3) + assert car(s3) == completedEntry + return True + +emptyFeed = \ + "\n" \ + "" \ + "Feed" \ + "1234" \ + "\n" + +itemFeed = \ + "\n" \ + "" \ + "Feed" \ + "1234" \ + "" \ + "item" \ + "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b" \ + "" \ + "" \ + "Apple$2.99" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "item" \ + "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c" \ + "" \ + "" \ + "Orange$3.55" \ + "" \ + "" \ + "" \ + "" \ + "\n" + +def testFeed(): + s = writeATOMFeed(("Feed", "1234")) + assert car(s) == emptyFeed + + a2 = readATOMFeed((emptyFeed,)) + s2 = writeATOMFeed(a2) + assert car(s2) == emptyFeed + + i3 = (("item", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b", + (element, "'item", (element, "'name", "Apple"), (element, "'price", "$2.99"))), + ("item", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c", + (element, "'item", (element, "'name", "Orange"), (element, "'price", "$3.55")))) + a3 = cons("Feed", cons("1234", i3)) + s3 = writeATOMFeed(a3) + assert car(s3) == itemFeed + + i4 = (("item", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b", + valueToElement(("'item", ("'name", "Apple"), ("'price", "$2.99")))), + ("item", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c", + valueToElement(("'item", ("'name", "Orange"), ("'price", "$3.55"))))) + a4 = cons("Feed", cons("1234", i4)) + s4 = writeATOMFeed(a4) + assert car(s4) == itemFeed + + a5 = readATOMFeed((itemFeed,)); + s5 = writeATOMFeed(a5); + assert car(s5) == itemFeed + + i6 = (("item", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b", + (("'name", "Apple"), ("'price", "$2.99"))), + ("item", "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c", + (("'name", "Orange"), ("'price", "$3.55")))) + a6 = cons("Feed", cons("1234", i6)) + s6 = writeATOMFeed(feedValuesToElements(a6)) + assert car(s6) == itemFeed + + return True + +if __name__ == "__main__": + print "Testing..." + testEntry() + testFeed() + print "OK" + diff --git a/sca-cpp/trunk/modules/wsgi/atomutil.py b/sca-cpp/trunk/modules/wsgi/atomutil.py new file mode 100644 index 0000000000..3e3e08b27e --- /dev/null +++ b/sca-cpp/trunk/modules/wsgi/atomutil.py @@ -0,0 +1,103 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# ATOM data conversion functions + +from util import * +from elemutil import * +from xmlutil import * + +# Convert a list of elements to a list of values representing an ATOM entry +def entryElementsToValues(e): + lt = filter(selector((element, "'title")), e) + t = "" if isNil(lt) else elementValue(car(lt)) + li = filter(selector((element, "'id")), e) + i = "" if isNil(li) else elementValue(car(li)) + lc = filter(selector((element, "'content")), e) + return (t, i, cadr(elementChildren(car(lc)))) + +# Convert a list of elements to a list of values representing ATOM entries +def entriesElementsToValues(e): + if isNil(e): + return e + return cons(entryElementsToValues(car(e)), entriesElementsToValues(cdr(e))) + +# Convert a list of strings to a list of values representing an ATOM entry +def readATOMEntry(l): + e = readXML(l) + if isNil(e): + return () + return entryElementsToValues(car(e)) + +# Convert a list of values representy an ATOM entry to a value +def entryValue(e): + v = elementsToValues((caddr(e))) + return cons(car(e), (cadr(e), cdr(car(v)))) + +# Convert a list of strings to a list of values representing an ATOM feed +def readATOMFeed(l): + f = readXML(l) + if isNil(f): + return () + t = filter(selector((element, "'title")), car(f)) + i = filter(selector((element, "'id")), car(f)) + e = filter(selector((element, "'entry")), car(f)) + if isNil(e): + return (elementValue(car(t)), elementValue(car(i))) + return cons(elementValue(car(t)), cons(elementValue(car(i)), entriesElementsToValues(e))) + +# Convert a list of values representy an ATOM entry to a list of elements +def entryElement(l): + return (element, "'entry", (attribute, "'xmlns", "http://www.w3.org/2005/Atom"), + (element, "'title", (attribute, "'type", "text"), car(l)), + (element, "'id", cadr(l)), + (element, "'content", (attribute, "'type", "application/xml"), caddr(l)), + (element, "'link", (attribute, "'href", cadr(l)))) + +# Convert a list of values representing ATOM entries to a list of elements +def entriesElements(l): + if isNil(l): + return l + return cons(entryElement(car(l)), entriesElements(cdr(l))) + +# Convert a list of values representing an ATOM entry to an ATOM entry +def writeATOMEntry(l): + return writeXML((entryElement(l),), True) + +# Convert a list of values representing an ATOM feed to an ATOM feed +def writeATOMFeed(l): + f = (element, "'feed", (attribute, "'xmlns", "http://www.w3.org/2005/Atom"), + (element, "'title", (attribute, "'type", "text"), car(l)), + (element, "'id", cadr(l))) + if isNil(cddr(l)): + return writeXML((f,), True) + fe = f + entriesElements(cddr(l)) + return writeXML((fe,), True) + +# Convert an ATOM entry containing a value to an ATOM entry containing an item element +def entryValuesToElements(v): + return cons(car(v), cons(cadr(v), valuesToElements((cons("'item", caddr(v)),)))) + +# Convert an ATOM feed containing values to an ATOM feed containing elements +def feedValuesToElementsLoop(v): + if isNil(v): + return v + return cons(entryValuesToElements(car(v)), feedValuesToElementsLoop(cdr(v))) + +def feedValuesToElements(v): + return cons(car(v), cons(cadr(v), feedValuesToElementsLoop(cddr(v)))) + diff --git a/sca-cpp/trunk/modules/wsgi/client-test.cpp b/sca-cpp/trunk/modules/wsgi/client-test.cpp index 60241991a5..da4fff973b 100644 --- a/sca-cpp/trunk/modules/wsgi/client-test.cpp +++ b/sca-cpp/trunk/modules/wsgi/client-test.cpp @@ -27,9 +27,9 @@ #include "string.hpp" #include "../server/client-test.hpp" -int main() { +int main(const int argc, const char** argv) { tuscany::cout << "Testing..." << tuscany::endl; - tuscany::server::testURI = "http://localhost:8090/wsgi"; + tuscany::server::testURI = argc < 2? "http://localhost:8090/wsgi" : argv[1]; tuscany::server::testBlobs = false; tuscany::server::testServer(); diff --git a/sca-cpp/trunk/modules/wsgi/composite.py b/sca-cpp/trunk/modules/wsgi/composite.py index 3d55aeb0ab..3cccaa4ae4 100755 --- a/sca-cpp/trunk/modules/wsgi/composite.py +++ b/sca-cpp/trunk/modules/wsgi/composite.py @@ -20,70 +20,130 @@ from wsgiref.simple_server import make_server from wsgiref.handlers import CGIHandler +from wsgiref.util import request_uri from os import environ from sys import stderr, argv from util import * from scdl import * +from atomutil import * +from jsonutil import * +# Return the path of an HTTP request def requestPath(e): return e.get("PATH_INFO", "") +# Return the method of an HTTP request def requestMethod(e): return e.get("REQUEST_METHOD", "") -def contentType(ct): - if ct == None: - return [] - return [("Content-type", ct)] +# Return the method of an HTTP request +def requestContentType(e): + return e.get("CONTENT_TYPE", "") -def status(st): +# Return the request body input stream +def requestBody(e): + i = e.get("wsgi.input", None) + if i == None: + return () + l = int(e.get("CONTENT_LENGTH", "0")) + return (i.read(l),) + +# Return an HTTP status +def errstatus(st): + return (st, ""+ st + "

" + st[4:] + "

") + +def status(st, b): if st == 200: - return "200 OK" + return cons("200 OK", b) if st == 404: - return "404 Not Found" + return errstatus("404 Not Found") if st == 201: - return "201 Created" - return "500 Internal Server Error" + return cons("201 Created", b) + return errstatus("500 Internal Server Error") + +# Return an HTTP result +def result(r, st, h = (), b = ()): + s = status(st, b) + r(car(s), list(h)) + return cdr(s) -def result(r, st, ct = None, b = ()): - r(status(st), contentType(ct)) - return b + +# Converts the args received in a POST to a list of key value pairs +def postArgs(a): + if isNil(a): + return ((),) + l = car(a); + return cons(l, postArgs(cdr(a))) # WSGI application function def application(e, r): # Read the deployed composite - comps = components(parse("domain-test.composite")) - print >> stderr, comps + compos = components(parse("domain-test.composite")) + #print >> stderr, compos + + # Evaluate the deployed components + comps = evalComponents(compos) - # Find the requested component + # Get the request path and method path = tokens(requestPath(e)) + m = requestMethod(e) + if (isNil(path) or path == ("index.html",)) and m == "GET": + return result(r, 200, (("Content-type", "text/html"),), ("

It works!

",)) + + # Find the requested component uc = uriToComponent(path, comps) uri = car(uc) if uri == None: return result(r, 404) - comp = cadr(uc) - mod = __import__(cadr(comp)) - + comp = cadr(uc) + # Call the requested component function id = path[len(uri):] - print >> stderr, id - m = requestMethod(e) if (m == "GET"): - v = mod.get(id) - print >> stderr, v + v = comp("get", id) - # write returned content-type / content pair + # Write returned content-type / content pair if not isinstance(cadr(v), basestring): - return result(r, 200, car(v), cadr(v)) + return result(r, 200, (("Content-type", car(v)),), cadr(v)) - # TODO write an ATOM feed or entry - if nil(id): - return result(r, 200, "application/atom+xml;type=feed", ("Atom feed")) - return result(r, 200, "application/atom+xml;type=entry", ("Atom entry")) + # Write an ATOM feed or entry + if isNil(id): + return result(r, 200, (("Content-type", "application/atom+xml;type=feed"),), writeATOMFeed(feedValuesToElements(v))) + return result(r, 200, (("Content-type", "application/atom+xml;type=entry"),), writeATOMEntry(entryValuesToElements(v))) + + if m == "POST": + ct = requestContentType(e) + # Handle a JSON-RPC function call + if ct.find("application/json-rpc") != -1 or ct.find("text/plain") != -1: + json = elementsToValues(readJSON(requestBody(e))) + args = postArgs(json) + jid = cadr(assoc("'id", args)) + func = funcName(cadr(assoc("'method", args))) + params = cadr(assoc("'params", args)) + v = comp(func, *params) + return result(r, 200, (("Content-type", "application/json-rpc"),), jsonResult(jid, v)) + + # Handle an ATOM entry POST + if ct.find("application/atom+xml") != -1: + ae = entryValue(readATOMEntry(requestBody(e))) + v = comp("post", id, ae) + if isNil(v): + return result(r, 500) + return result(r, 201, (("Location", request_uri(e) + "/" + "/".join(v)),)) + return result(r, 500) + + if m == "PUT": + # Handle an ATOM entry PUT + ae = entryValue(readATOMEntry(requestBody(e))) + v = comp("put", id, ae) + if v == False: + return result(r, 404) + return result(r, 200) + if m == "DELETE": - v = mod.delete(id) + v = comp("delete", id) if v == False: return result(r, 404) return result(r, 200) @@ -91,6 +151,7 @@ def application(e, r): # TODO implement POST and PUT methods return result(r, 500) +# Return the WSGI server type def serverType(e): return e.get("SERVER_SOFTWARE", "") @@ -99,5 +160,9 @@ if __name__ == "__main__": st = serverType(environ) if st == "": make_server("", int(argv[1]), application).serve_forever() + elif st == "Development/1.0": + from google.appengine.ext.webapp.util import run_wsgi_app + run_wsgi_app(application) else: CGIHandler().run(application) + diff --git a/sca-cpp/trunk/modules/wsgi/elemutil.py b/sca-cpp/trunk/modules/wsgi/elemutil.py new file mode 100644 index 0000000000..ad971ba6ba --- /dev/null +++ b/sca-cpp/trunk/modules/wsgi/elemutil.py @@ -0,0 +1,168 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Functions to help represent data as lists of elements and attributes + +from util import * + +element = "'element" +attribute = "'attribute" +atsign = "'@" + +# Return true if a value is an element +def isElement(v): + if not isList(v) or isNil(v) or v == None or car(v) != element: + return False + return True + +# Return true if a value is an attribute +def isAttribute(v): + if not isList(v) or isNil(v) or v == None or car(v) != attribute: + return False + return True + +# Return the name of attribute +def attributeName(l): + return cadr(l) + +# Return the value of attribute +def attributeValue(l): + return caddr(l) + +# Return the name of an element +def elementName(l): + return cadr(l) + +# Return true if an element has children +def elementHasChildren(l): + return not isNil(cddr(l)) + +# Return the children of an element +def elementChildren(l): + return cddr(l) + +# Return true if an element has a value +def elementHasValue(l): + r = reverse(l) + if isSymbol(car(r)): + return False + if isList(car(r)) and not isNil(car(r)) and isSymbol(car(car(r))): + return False + return True + +# Return the value of an element +def elementValue(l): + return car(reverse(l)) + +# Convert an element to a value +def elementToValueIsList(v): + if not isList(v): + return False + return isNil(v) or not isSymbol(car(v)) + +def elementToValue(t): + if isTaggedList(t, attribute): + return (atsign + attributeName(t)[1:], attributeValue(t)) + if isTaggedList(t, element): + if elementHasValue(t): + if not elementToValueIsList(elementValue(t)): + return (elementName(t), elementValue(t)) + return cons(elementName(t), (elementsToValues(elementValue(t)),)) + return cons(elementName(t), elementsToValues(elementChildren(t))) + if not isList(t): + return t + return elementsToValues(t) + +# Convert a list of elements to a list of values +def elementToValueIsSymbol(v): + if not isList(v): + return False + if (isNil(v)): + return False + if not isSymbol(car(v)): + return False + return True + +def elementToValueGroupValues(v, l): + if isNil(l) or not elementToValueIsSymbol(v) or not elementToValueIsSymbol(car(l)): + return cons(v, l) + if car(car(l)) != car(v): + return cons(v, l) + if not elementToValueIsList(cadr(car(l))): + g = (car(v), (cdr(v), cdr(car(l)))) + return elementToValueGroupValues(g, cdr(l)) + g = (car(v), cons(cdr(v), cadr(car(l)))) + return elementToValueGroupValues(g, cdr(l)) + +def elementsToValues(e): + if isNil(e): + return e + return elementToValueGroupValues(elementToValue(car(e)), elementsToValues(cdr(e))) + +# Convert a value to an element +def valueToElement(t): + if isList(t) and not isNil(t) and isSymbol(car(t)): + n = car(t) + v = cadr(t) + if not isList(v): + if n[0:2] == atsign: + return (attribute, n[1:], v) + return (element, n, v) + if isNil(v) or not isSymbol(car(v)): + return cons(element, cons(n, (valuesToElements(v),))) + return cons(element, cons(n, valuesToElements(cdr(t)))) + if not isList(t): + return t + return valuesToElements(t) + +# Convert a list of values to a list of elements +def valuesToElements(l): + if isNil(l): + return l + return cons(valueToElement(car(l)), valuesToElements(cdr(l))) + +# Return a selector lambda function which can be used to filter elements +def evalSelect(s, v): + if isNil(s): + return True + if isNil(v): + return False + if car(s) != car(v): + return False + return evalSelect(cdr(s), cdr(v)) + +def selector(s): + return lambda v: evalSelect(s, v) + +# Return the value of the attribute with the given name +def namedAttributeValue(name, l): + f = filter(lambda v: isAttribute(v) and attributeName(v) == name, l) + if isNil(f): + return None + return caddr(car(f)) + +# Return child elements with the given name +def namedElementChildren(name, l): + return filter(lambda v: isElement(v) and elementName(v) == name, l) + +# Return the child element with the given name +def namedElementChild(name, l): + f = namedElementChildren(name, l) + if isNil(f): + return None + return car(f) + diff --git a/sca-cpp/trunk/modules/wsgi/htdocs/entry.xml b/sca-cpp/trunk/modules/wsgi/htdocs/entry.xml new file mode 100644 index 0000000000..5796cd655f --- /dev/null +++ b/sca-cpp/trunk/modules/wsgi/htdocs/entry.xml @@ -0,0 +1,2 @@ + +Item111services.ItemAppleUSD$2.99 diff --git a/sca-cpp/trunk/modules/wsgi/htdocs/feed.xml b/sca-cpp/trunk/modules/wsgi/htdocs/feed.xml new file mode 100644 index 0000000000..d15b265f15 --- /dev/null +++ b/sca-cpp/trunk/modules/wsgi/htdocs/feed.xml @@ -0,0 +1,2 @@ + +Sample Feed123456789Item111services.ItemAppleUSD$2.99Item222services.ItemOrangeUSD$3.55Item333('name', 'Pear')services.ItemUSD$1.55 diff --git a/sca-cpp/trunk/modules/wsgi/htdocs/index.html b/sca-cpp/trunk/modules/wsgi/htdocs/index.html new file mode 100644 index 0000000000..cd25bf17b3 --- /dev/null +++ b/sca-cpp/trunk/modules/wsgi/htdocs/index.html @@ -0,0 +1 @@ +

It works!

\ No newline at end of file diff --git a/sca-cpp/trunk/modules/wsgi/htdocs/json-request.txt b/sca-cpp/trunk/modules/wsgi/htdocs/json-request.txt new file mode 100644 index 0000000000..b4bd07fc46 --- /dev/null +++ b/sca-cpp/trunk/modules/wsgi/htdocs/json-request.txt @@ -0,0 +1 @@ +{"id":1,"method":"echo","params":["Hello"]} diff --git a/sca-cpp/trunk/modules/wsgi/htdocs/json-result.txt b/sca-cpp/trunk/modules/wsgi/htdocs/json-result.txt new file mode 100644 index 0000000000..121bf74902 --- /dev/null +++ b/sca-cpp/trunk/modules/wsgi/htdocs/json-result.txt @@ -0,0 +1 @@ +{"id":1,"result":"Hello"} \ No newline at end of file diff --git a/sca-cpp/trunk/modules/wsgi/json-test.py b/sca-cpp/trunk/modules/wsgi/json-test.py new file mode 100755 index 0000000000..2f2a755bff --- /dev/null +++ b/sca-cpp/trunk/modules/wsgi/json-test.py @@ -0,0 +1,59 @@ +#!/usr/bin/python +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Test JSON data conversion functions + +import unittest +from elemutil import * +from jsonutil import * + +def testJSON(): + ad = ((attribute, "'city", "san francisco"), (attribute, "'state", "ca")) + ac = ((element, "'id", "1234"), (attribute, "'balance", 1000)) + cr = ((attribute, "'name", "jdoe"), cons(element, cons("'address", ad)), cons(element, cons("'account", ac))) + c = (cons(element, cons("'customer", cr)),) + s = writeJSON(c); + assert car(s) == "{\"customer\":{\"account\":{\"@balance\":1000,\"id\":\"1234\"},\"@name\":\"jdoe\",\"address\":{\"@city\":\"san francisco\",\"@state\":\"ca\"}}}" + + phones = ("408-1234", "650-1234") + l = ((element, "'phones", phones), (element, "'lastName", "test\ttab"), (attribute, "'firstName", "test1")) + s2 = writeJSON(l); + assert car(s2) == "{\"phones\":[\"408-1234\",\"650-1234\"],\"@firstName\":\"test1\",\"lastName\":\"test\\ttab\"}" + + r = readJSON(s2) + assert r == ((element, "'lastName", "test\ttab"), (attribute, "'firstName", "test1"), (element, "'phones", phones)) + w = writeJSON(r) + assert car(w) == "{\"lastName\":\"test\\ttab\",\"@firstName\":\"test1\",\"phones\":[\"408-1234\",\"650-1234\"]}" + + l4 = (("'ns1:echoString", ("'@xmlns:ns1", "http://ws.apache.org/axis2/services/echo"), ("'text", "Hello World!")),) + s4 = writeJSON(valuesToElements(l4)) + assert car(s4) == "{\"ns1:echoString\":{\"@xmlns:ns1\":\"http://ws.apache.org/axis2/services/echo\",\"text\":\"Hello World!\"}}" + + r4 = elementsToValues(readJSON(s4)) + assert r4 == (("'ns1:echoString", ("'text", 'Hello World!'), ("'@xmlns:ns1", 'http://ws.apache.org/axis2/services/echo')),) + return True + +def testJSONRPC(): + return True + +if __name__ == "__main__": + print "Testing..." + testJSON() + testJSONRPC() + print "OK" + diff --git a/sca-cpp/trunk/modules/wsgi/jsonutil.py b/sca-cpp/trunk/modules/wsgi/jsonutil.py new file mode 100644 index 0000000000..ad34d785bf --- /dev/null +++ b/sca-cpp/trunk/modules/wsgi/jsonutil.py @@ -0,0 +1,140 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# JSON data conversion functions + +try: + import json +except: + from django.utils import simplejson as json + +from StringIO import StringIO +from util import * +from elemutil import * + +# Return true if a list represents a JS array +def isJSArray(l): + if isNil(l): + return True + v = car(l) + if isSymbol(v): + return False + if isList(v): + if not isNil(v) and isSymbol(car(v)): + return False + return True + +# Converts JSON properties to values +def jsPropertiesToValues(propertiesSoFar, o, i): + if isNil(i): + return propertiesSoFar + p = car(i) + jsv = o[p] + v = jsValToValue(jsv) + + if isinstance(p, basestring): + n = str(p) + if n[0:1] == "@": + return jsPropertiesToValues(cons((attribute, "'" + n[1:], v), propertiesSoFar), o, cdr(i)) + if isList(v) and not isJSArray(v): + return jsPropertiesToValues(cons(cons(element, cons("'" + n, v)), propertiesSoFar), o, cdr(i)) + return jsPropertiesToValues(cons((element, "'" + n, v), propertiesSoFar), o, cdr(i)) + return jsPropertiesToValues(cons(v, propertiesSoFar), o, cdr(i)) + +# Converts a JSON val to a value +def jsValToValue(jsv): + if isinstance(jsv, dict): + return jsPropertiesToValues((), jsv, tuple(jsv.keys())) + if isList(jsv): + return jsPropertiesToValues((), jsv, tuple(reversed(range(0, len(jsv))))) + if isinstance(jsv, basestring): + return str(jsv) + return jsv + +# Convert a list of strings representing a JSON document to a list of values +def readJSON(l): + s = StringIO() + writeStrings(l, s) + val = json.loads(s.getvalue()) + return jsValToValue(val) + +# Convert a list of values to JSON array elements +def valuesToJSElements(a, l, i): + if isNil(l): + return a + pv = valueToJSVal(car(l)) + a[i] = pv + return valuesToJSElements(a, cdr(l), i + 1) + +# Convert a value to a JSON value +def valueToJSVal(v): + if not isList(v): + return v + if isJSArray(v): + return valuesToJSElements(list(range(0, len(v))), v, 0) + return valuesToJSProperties({}, v) + +# Convert a list of values to JSON properties +def valuesToJSProperties(o, l): + if isNil(l): + return o + token = car(l) + if isTaggedList(token, attribute): + pv = valueToJSVal(attributeValue(token)) + o["@" + attributeName(token)[1:]] = pv + elif isTaggedList(token, element): + if elementHasValue(token): + pv = valueToJSVal(elementValue(token)) + o[elementName(token)[1:]] = pv + else: + child = {} + o[elementName(token)[1:]] = child + valuesToJSProperties(child, elementChildren(token)) + return valuesToJSProperties(o, cdr(l)) + +# Convert a list of values to a list of strings representing a JSON document +def writeJSON(l): + jsv = valuesToJSProperties({}, l) + s = json.dumps(jsv, separators=(',',':')) + return (s,) + +# Convert a list + params to a JSON-RPC request +def jsonRequest(id, func, params): + r = (("'id", id), ("'method", func), ("'params", params)) + return writeJSON(valuesToElements(r)) + +# Convert a value to a JSON-RPC result +def jsonResult(id, val): + return writeJSON(valuesToElements((("'id", id), ("'result", val)))) + +# Convert a JSON-RPC result to a value +def jsonResultValue(s): + jsres = readJSON(s) + rval = cadr(elementsToValues(jsres)) + val = cadr(rval) + if isList(val) and not isJSArray(val): + return (val,) + return val + +# Return a portalbe function name from a JSON-RPC function name +def funcName(f): + if len(f) > 7 and f.find("system.") == 0: + return f[7:] + if len(f) > 8 and f.find("Service.") == 0: + return f[8:] + return f + diff --git a/sca-cpp/trunk/modules/wsgi/runtime.py b/sca-cpp/trunk/modules/wsgi/runtime.py deleted file mode 100644 index 930351ac3e..0000000000 --- a/sca-cpp/trunk/modules/wsgi/runtime.py +++ /dev/null @@ -1,30 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# Component invocation functions - -from util import * - -def apply(impl, refs, func, params): - m = __import__(impl) - f = m.__getattribute__(func) - p = refs + params - return f(*p) - -def refapply(comps, target): - return comps["target"] - diff --git a/sca-cpp/trunk/modules/wsgi/scdl.py b/sca-cpp/trunk/modules/wsgi/scdl.py index a6bb48bc65..056523fb23 100644 --- a/sca-cpp/trunk/modules/wsgi/scdl.py +++ b/sca-cpp/trunk/modules/wsgi/scdl.py @@ -17,11 +17,45 @@ # SCDL parsing functions +from xml.etree.cElementTree import iterparse from util import * +# Element tree utility functions, used to parse SCDL documents +def parse(file): + return map(lambda x: x, iterparse(file, events=("start", "end"))) + +def evt(e): + return car(e) + +def elt(e): + return cadr(e) + +def att(e): + return elt(e).attrib + +def match(e, ev, tag): + return evt(e) == ev and elt(e).tag.find("}" + tag) != -1 + +# Make a callable component +class component: + def __init__(self, name, impl, svcs, refs): + self.name = name + self.impl = impl + self.mod = None + self.svcs = svcs + self.refs = refs + self.proxies = () + + def __call__(self, func, *args): + return self.mod.__getattribute__(func)(*(args + self.proxies)) + + def __repr__(self): + return repr((self.name, self.impl, self.mod, self.svcs, self.refs, self.proxies)) + def mkcomponent(name, impl, svcs, refs): - return (name, impl, svcs, refs) + return component(name, impl, svcs, refs) +# Return the Python module name of a component implementation def implementation(e): if len(e) == 0 or match(car(e), "end", "component") == True: return "" @@ -32,6 +66,7 @@ def implementation(e): return s[0:len(s) - 3] return None +# Return the URI of a binding under a SCDL service or reference element def binding(e): if len(e) == 0 or match(car(e), "end", "reference") == True or match(car(e), "end", "service") == True: return () @@ -39,6 +74,7 @@ def binding(e): return binding(cdr(e)) return tokens(att(car(e))["uri"]) +# Return the list of references under a SCDL component element def references(e): if len(e) == 0 or match(car(e), "end", "component") == True: return () @@ -48,6 +84,7 @@ def references(e): return (att(car(e))["target"],) + references(cdr(e)) return cons(binding(e), references(cdr(e))) +# Return the list of services under a SCDL component element def services(e): if len(e) == 0 or match(car(e), "end", "component") == True: return () @@ -55,9 +92,11 @@ def services(e): return services(cdr(e)) return cons(binding(e), services(cdr(e))) +# Return the name attribute of a SCDL element def name(e): return att(car(e))["name"] +# Return the list of components under a SCDL composite element def components(e): if len(e) == 0: return () @@ -66,24 +105,38 @@ def components(e): n = name(e) return cons(mkcomponent(n, implementation(e), cons(("components", n), services(e)), references(e)), components(cdr(e))) -def nameToComponent(n, c): - if c == (): +# Find a component with a given name +def nameToComponent(name, comps): + if comps == (): return None - if car(car(c)) == n: - return car(c) - return nameToComponent(n, cdr(c)) + if car(comps).name == name: + return car(comps) + return nameToComponent(name, cdr(comps)) -def matchingURI(u, s): - if s == (): +# Find the URI matching a given URI in a list of service URIs +def matchingURI(u, svcs): + if svcs == (): return None - if car(s) == u[0:len(car(s))]: - return car(s) - return matchingURI(u, cdr(s)) + if car(svcs) == u[0:len(car(svcs))]: + return car(svcs) + return matchingURI(u, cdr(svcs)) -def uriToComponent(u, c): - if c == (): +# Return the (service URI, component) pair matching a given URI +def uriToComponent(u, comps): + if comps == (): return (None, None) - m = matchingURI(u, caddr(car(c))) + m = matchingURI(u, car(comps).svcs) if m != None: - return (m, car(c)) - return uriToComponent(u, cdr(c)) + return (m, car(comps)) + return uriToComponent(u, cdr(comps)) + +# Evaluate a component, resolve its implementation and references +def evalComponent(comp, comps): + comp.mod = __import__(comp.impl) + comp.proxies = tuple(map(lambda r: nameToComponent(r, comps), comp.refs)) + return comp + +# Evaluate a list of components +def evalComponents(comps): + return tuple(map(lambda c: evalComponent(c, comps), comps)) + diff --git a/sca-cpp/trunk/modules/wsgi/server-test b/sca-cpp/trunk/modules/wsgi/server-test index 6cbe9a9864..3220cd2d3e 100755 --- a/sca-cpp/trunk/modules/wsgi/server-test +++ b/sca-cpp/trunk/modules/wsgi/server-test @@ -18,7 +18,7 @@ # under the License. # Setup -./wsgi-start 8090 +./wsgi-start 8090 2>/dev/null sleep 2 # Test diff --git a/sca-cpp/trunk/modules/wsgi/server-test.py b/sca-cpp/trunk/modules/wsgi/server-test.py index 624deb8148..9e084a3f92 100644 --- a/sca-cpp/trunk/modules/wsgi/server-test.py +++ b/sca-cpp/trunk/modules/wsgi/server-test.py @@ -38,7 +38,7 @@ def post(collection, item): return ("123456789",) def put(id, item): - return true + return True def delete(id): - return true + return True diff --git a/sca-cpp/trunk/modules/wsgi/util-test b/sca-cpp/trunk/modules/wsgi/util-test new file mode 100755 index 0000000000..d7a6a34101 --- /dev/null +++ b/sca-cpp/trunk/modules/wsgi/util-test @@ -0,0 +1,27 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Run Python util test cases +here=`readlink -f $0`; here=`dirname $here` +python_prefix=`cat $here/../python/python.prefix` + +$python_prefix/bin/python xml-test.py +$python_prefix/bin/python atom-test.py +$python_prefix/bin/python json-test.py + diff --git a/sca-cpp/trunk/modules/wsgi/util.py b/sca-cpp/trunk/modules/wsgi/util.py index e6319b1ec2..d945c7bdef 100644 --- a/sca-cpp/trunk/modules/wsgi/util.py +++ b/sca-cpp/trunk/modules/wsgi/util.py @@ -17,8 +17,6 @@ # Simple utility functions -from xml.etree.cElementTree import iterparse - # Scheme-like lists def cons(a, b): return (a,) + b @@ -38,13 +36,32 @@ def cddr(l): def caddr(l): return car(cddr(l)) -def nil(l): +def reverse(l): + r = list(l) + r.reverse() + return tuple(r) + +def isNil(l): return l == () +def isSymbol(v): + return isinstance(v, basestring) and v[0:1] == "'" + +def isList(v): + if getattr(v, '__iter__', False) == False: + return False + if isinstance(v, basestring) or isinstance(v, dict): + return False + return True + +def isTaggedList(v, t): + return isList(v) and not isNil(v) and car(v) == t + # Scheme-like associations def assoc(k, l): if l == (): return None + if k == car(car(l)): return car(l) return assoc(k, cdr(l)) @@ -53,22 +70,14 @@ def assoc(k, l): def curry(f, *args): return lambda *a: f(*(args + a)) -# Element tree utility functions -def parse(file): - return map(lambda x: x, iterparse(file, events=("start", "end"))) - -def evt(e): - return car(e) - -def elt(e): - return cadr(e) - -def att(e): - return elt(e).attrib - -def match(e, ev, tag): - return evt(e) == ev and elt(e).tag.find("}" + tag) != -1 - -# Split a path +# Split a path into a list of segments def tokens(path): return tuple(filter(lambda s: len(s) != 0, path.split("/"))) + +# Write a list of strings to a stream +def writeStrings(l, os): + if l == (): + return os + os.write(car(l)) + return writeStrings(cdr(l), os) + diff --git a/sca-cpp/trunk/modules/wsgi/wiring-test b/sca-cpp/trunk/modules/wsgi/wiring-test index 5db99641ce..c3a8d09ae7 100755 --- a/sca-cpp/trunk/modules/wsgi/wiring-test +++ b/sca-cpp/trunk/modules/wsgi/wiring-test @@ -18,45 +18,49 @@ # under the License. echo "Testing..." +uri=$1 +if [ "$uri" = "" ]; then + uri="http://localhost:8090" +fi # Setup -./wsgi-start 8090 +mkdir -p tmp +./wsgi-start 8090 2>/dev/null sleep 2 # Test HTTP GET -#curl http://localhost:8090/index.html 2>/dev/null >tmp/index.html -#diff tmp/index.html ../server/htdocs/index.html -#rc=$? -rc="0" +curl $uri/index.html 2>/dev/null >tmp/index.html +diff tmp/index.html htdocs/index.html +rc=$? # Test ATOMPub if [ "$rc" = "0" ]; then - curl http://localhost:8090/client/ >tmp/feed.xml 2>/dev/null - diff tmp/feed.xml ../server/htdocs/feed.xml + curl $uri/client/ >tmp/feed.xml 2>/dev/null + diff tmp/feed.xml htdocs/feed.xml rc=$? fi if [ "$rc" = "0" ]; then - curl http://localhost:8090/client/111 >tmp/entry.xml 2>/dev/null - diff tmp/entry.xml ../server/htdocs/entry.xml + curl $uri/client/111 >tmp/entry.xml 2>/dev/null + diff tmp/entry.xml htdocs/entry.xml rc=$? fi if [ "$rc" = "0" ]; then - curl http://localhost:8090/client/ -X POST -H "Content-type: application/atom+xml" --data @../server/htdocs/entry.xml 2>/dev/null + curl $uri/client/ -X POST -H "Content-type: application/atom+xml" --data @htdocs/entry.xml 2>/dev/null rc=$? fi if [ "$rc" = "0" ]; then - curl http://localhost:8090/client/111 -X PUT -H "Content-type: application/atom+xml" --data @../server/htdocs/entry.xml 2>/dev/null + curl $uri/client/111 -X PUT -H "Content-type: application/atom+xml" --data @htdocs/entry.xml 2>/dev/null rc=$? fi if [ "$rc" = "0" ]; then - curl http://localhost:8090/client/111 -X DELETE 2>/dev/null + curl $uri/client/111 -X DELETE 2>/dev/null rc=$? fi # Test JSON-RPC if [ "$rc" = "0" ]; then - curl http://localhost:8090/client/ -X POST -H "Content-type: application/json-rpc" --data @../server/htdocs/json-request.txt >tmp/json-result.txt 2>/dev/null - diff tmp/json-result.txt ../server/htdocs/json-result.txt + curl $uri/client/ -X POST -H "Content-type: application/json-rpc" --data @htdocs/json-request.txt >tmp/json-result.txt 2>/dev/null + diff tmp/json-result.txt htdocs/json-result.txt rc=$? fi diff --git a/sca-cpp/trunk/modules/wsgi/wsgi-test b/sca-cpp/trunk/modules/wsgi/wsgi-test new file mode 100755 index 0000000000..8b71f2e839 --- /dev/null +++ b/sca-cpp/trunk/modules/wsgi/wsgi-test @@ -0,0 +1,69 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +echo "Testing..." + +# Setup +mkdir -p tmp +./wsgi-start 8090 2>/dev/null +sleep 2 + +# Test HTTP GET +curl http://localhost:8090/index.html 2>/dev/null >tmp/index.html +diff tmp/index.html htdocs/index.html +rc=$? + +# Test ATOMPub +if [ "$rc" = "0" ]; then + curl http://localhost:8090/wsgi/ >tmp/feed.xml 2>/dev/null + diff tmp/feed.xml htdocs/feed.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + curl http://localhost:8090/wsgi/111 >tmp/entry.xml 2>/dev/null + diff tmp/entry.xml htdocs/entry.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + curl http://localhost:8090/wsgi/ -X POST -H "Content-type: application/atom+xml" --data @htdocs/entry.xml 2>/dev/null + rc=$? +fi +if [ "$rc" = "0" ]; then + curl http://localhost:8090/wsgi/111 -X PUT -H "Content-type: application/atom+xml" --data @htdocs/entry.xml 2>/dev/null + rc=$? +fi +if [ "$rc" = "0" ]; then + curl http://localhost:8090/wsgi/111 -X DELETE 2>/dev/null + rc=$? +fi + +# Test JSON-RPC +if [ "$rc" = "0" ]; then + curl http://localhost:8090/wsgi/ -X POST -H "Content-type: application/json-rpc" --data @htdocs/json-request.txt >tmp/json-result.txt 2>/dev/null + diff tmp/json-result.txt htdocs/json-result.txt + rc=$? +fi + +# Cleanup +./wsgi-stop 8090 +sleep 2 +if [ "$rc" = "0" ]; then + echo "OK" +fi +return $rc diff --git a/sca-cpp/trunk/modules/wsgi/xml-test.py b/sca-cpp/trunk/modules/wsgi/xml-test.py new file mode 100755 index 0000000000..f60322bdc1 --- /dev/null +++ b/sca-cpp/trunk/modules/wsgi/xml-test.py @@ -0,0 +1,74 @@ +#!/usr/bin/python +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Test XML handling functions + +import unittest +from elemutil import * +from xmlutil import * + +customerXML = \ + "\n" \ + "" \ + "jdoe" \ + "
san franciscoca
" \ + "12341000" \ + "67892000" \ + "45673000" \ + "
\n" + +def testElements(): + ad = (("'city", "san francisco"), ("'state", "ca")) + ac1 = (("'id", "1234"), ("'balance", 1000)) + ac2 = (("'id", "6789"), ("'balance", 2000)) + ac3 = (("'id", "4567"), ("'balance", 3000)) + c = (("'customer", ("'name", "jdoe"), cons("'address", ad), ("'account", (ac1, ac2, ac3))),) + e = valuesToElements(c) + v = elementsToValues(e) + assert v == c + s = writeXML(e, True) + assert car(s) == customerXML + + c2 = (("'customer", ("'name", "jdoe"), cons("'address", ad), cons("'account", ac1), cons("'account", ac2), cons("'account", ac3)),) + e2 = valuesToElements(c2); + v2 = elementsToValues(e2); + s2 = writeXML(e2, True) + assert car(s2) == customerXML + + c3 = readXML((customerXML,)) + v3 = elementsToValues(c3) + e3 = valuesToElements(v3) + s3 = writeXML(e3, True) + assert car(s3) == customerXML + return True + +def testValues(): + l = (("'ns1:echoString", ("'@xmlns:ns1", "http://ws.apache.org/axis2/services/echo"), ("'text", "Hello World!")),) + e = valuesToElements(l) + lx = writeXML(e, True) + x = readXML(lx) + v = elementsToValues(x) + assert v == l + return True + +if __name__ == "__main__": + print "Testing..." + testElements() + testValues() + print "OK" + diff --git a/sca-cpp/trunk/modules/wsgi/xmlutil.py b/sca-cpp/trunk/modules/wsgi/xmlutil.py new file mode 100644 index 0000000000..a1bc04629a --- /dev/null +++ b/sca-cpp/trunk/modules/wsgi/xmlutil.py @@ -0,0 +1,114 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# XML handling functions + +from StringIO import StringIO +from xml.parsers import expat +import xml.etree.ElementTree as et +from util import * +from elemutil import * + +# Read a list of XML attributes +def readAttributes(a): + if a == (): + return a + return cons((attribute, "'" + car(car(a)), cadr(car(a))), readAttributes(cdr(a))) + +# Read an XML element +def readElement(e): + l = (element, "'" + e.tag) + readAttributes(tuple(e.items())) + readElements(tuple(e.getchildren())) + if e.text == None: + return l + return l + (e.text,) + +# Read a list of XML elements +def readElements(l): + if l == (): + return l + return cons(readElement(car(l)), readElements(cdr(l))) + +# Parse a list of strings representing an XML document +class NamespaceParser(et.XMLTreeBuilder): + def __init__(self): + et.XMLTreeBuilder.__init__(self) + self._parser = parser = expat.ParserCreate(None) + parser.DefaultHandlerExpand = self._default + parser.StartElementHandler = self._start + parser.EndElementHandler = self._end + parser.CharacterDataHandler = self._data + try: + parser.buffer_text = 1 + except AttributeError: + pass + try: + parser.ordered_attributes = 1 + parser.specified_attributes = 1 + parser.StartElementHandler = self._start_list + except AttributeError: + pass + +def parseXML(l): + s = StringIO() + writeStrings(l, s) + parser = NamespaceParser() + parser.feed(s.getvalue()) + return parser.close() + +# Read a list of values from a list of strings representing an XML document +def readXML(l): + e = parseXML(l) + return (readElement(e),) + +# Write a list of XML element and attribute tokens +def expandElementValues(n, l): + if isNil(l): + return l + return cons(cons(element, cons(n, car(l))), expandElementValues(n, cdr(l))) + +def writeList(l, xml): + if isNil(l): + return xml + token = car(l) + if isTaggedList(token, attribute): + xml.attrib[attributeName(token)[1:]] = str(attributeValue(token)) + elif isTaggedList(token, element): + if elementHasValue(token): + v = elementValue(token) + if isList(v): + e = expandElementValues(elementName(token), v) + writeList(e, xml) + else: + child = et.Element(elementName(token)[1:]) + writeList(elementChildren(token), child) + xml.append(child) + else: + child = et.Element(elementName(token)[1:]) + writeList(elementChildren(token), child) + xml.append(child) + else: + xml.text = str(token) + writeList(cdr(l), xml) + return xml + +# Convert a list of values to a list of strings representing an XML document +def writeXML(l, xmlTag): + e = writeList(l, []) + if not xmlTag: + return (et.tostring(car(e)),) + return (et.tostring(car(e), "UTF-8") + "\n",) + diff --git a/sca-cpp/trunk/test/store-python/shopping-cart.py b/sca-cpp/trunk/test/store-python/shopping-cart.py index 988fe7bea6..2cb6da140a 100644 --- a/sca-cpp/trunk/test/store-python/shopping-cart.py +++ b/sca-cpp/trunk/test/store-python/shopping-cart.py @@ -56,7 +56,7 @@ def get(id, cache): def delete(id, cache): if id == (): return cache("delete", (cartId,)) - return true + return True # Return the price of an item def price(item): -- cgit v1.2.3