From 51a97b5d9350b37f95d6f0c00d013b886e64fcd3 Mon Sep 17 00:00:00 2001 From: jsdelfino Date: Mon, 16 Nov 2009 06:01:41 +0000 Subject: Added test cases and scripts to test the HTTP binding support. Refactored httpd module and added a wiring httpd module. Implementation of the store demo prepared for ApacheCon. git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@880601 13f79535-47bb-0310-9956-ffa450edef68 --- cpp/sca/modules/http/curl-test.cpp | 92 ++-- cpp/sca/modules/http/htdocs/entry.xml | 2 +- cpp/sca/modules/http/htdocs/feed.xml | 2 +- cpp/sca/modules/http/http-test | 8 +- cpp/sca/modules/http/httpd-client.scm | 25 + cpp/sca/modules/http/httpd-conf | 3 +- cpp/sca/modules/http/httpd-test | 8 +- cpp/sca/modules/http/httpd-test.composite | 42 ++ cpp/sca/modules/http/httpd-test.scm | 20 +- cpp/sca/modules/http/httpd.hpp | 159 +++++++ cpp/sca/modules/http/mod-eval.cpp | 532 ++++++++++++++++++++++ cpp/sca/modules/http/mod-wiring.cpp | 276 +++++++++++ cpp/sca/modules/http/mod.cpp | 555 ----------------------- cpp/sca/modules/http/wiring-test | 92 ++++ cpp/sca/modules/json/json-test.cpp | 9 + cpp/sca/test/store-script/catalogs.composite | 43 -- cpp/sca/test/store-script/currency-converter.scm | 9 + cpp/sca/test/store-script/currency.composite | 16 +- cpp/sca/test/store-script/fruits-catalog.scm | 19 + cpp/sca/test/store-script/shopping-cart.scm | 26 ++ cpp/sca/test/store-script/store-http-test | 39 +- cpp/sca/test/store-script/store-script-test.cpp | 28 +- cpp/sca/test/store-script/store-script.scm | 149 ++++++ cpp/sca/test/store-script/store.composite | 71 +-- cpp/sca/test/store-script/store.scm | 147 +----- 25 files changed, 1519 insertions(+), 853 deletions(-) create mode 100644 cpp/sca/modules/http/httpd-client.scm create mode 100644 cpp/sca/modules/http/httpd-test.composite create mode 100644 cpp/sca/modules/http/httpd.hpp create mode 100644 cpp/sca/modules/http/mod-eval.cpp create mode 100644 cpp/sca/modules/http/mod-wiring.cpp delete mode 100644 cpp/sca/modules/http/mod.cpp create mode 100755 cpp/sca/modules/http/wiring-test delete mode 100644 cpp/sca/test/store-script/catalogs.composite create mode 100644 cpp/sca/test/store-script/currency-converter.scm create mode 100644 cpp/sca/test/store-script/fruits-catalog.scm create mode 100644 cpp/sca/test/store-script/shopping-cart.scm create mode 100644 cpp/sca/test/store-script/store-script.scm (limited to 'cpp/sca') diff --git a/cpp/sca/modules/http/curl-test.cpp b/cpp/sca/modules/http/curl-test.cpp index b02a2fac4d..863aa98828 100644 --- a/cpp/sca/modules/http/curl-test.cpp +++ b/cpp/sca/modules/http/curl-test.cpp @@ -20,7 +20,7 @@ /* $Rev$ $Date$ */ /** - * Test JSON data conversion functions. + * Test HTTP client functions. */ #include @@ -35,15 +35,66 @@ namespace tuscany { namespace http { -bool contains(const std::string& str, const std::string& pattern) { +const bool contains(const std::string& str, const std::string& pattern) { return str.find(pattern) != str.npos; } +const double duration(struct timeval start, struct timeval end, int count) { + long t = (end.tv_sec * 1000 + end.tv_usec / 1000) - (start.tv_sec * 1000 + start.tv_usec / 1000); + return (double)t / (double)count; +} + std::ostringstream* curlWriter(const std::string& s, std::ostringstream* os) { (*os) << s; return os; } +const bool testGet() { + CURLHandle ch; + { + std::ostringstream os; + const failable, std::string> r = get(curlWriter, &os, "http://localhost:8091", ch); + assert(hasValue(r)); + assert(contains(os.str(), "HTTP/1.1 200 OK")); + assert(contains(os.str(), "It works")); + } + { + const failable r = get("http://localhost:8091", ch); + assert(hasValue(r)); + const value val = r; + assert(contains(val, "It works")); + } + return true; +} + +const bool testGetLoop(const int count, CURLHandle& ch) { + if (count == 0) + return true; + const failable r = get("http://localhost:8091", ch); + assert(hasValue(r)); + const value val = r; + assert(contains(val, "It works")); + return testGetLoop(count - 1, ch); +} + +const bool testGetPerf() { + const int count = 50; + CURLHandle ch; + struct timeval start; + struct timeval end; + { + testGetLoop(5, ch); + + gettimeofday(&start, NULL); + + testGetLoop(count, ch); + + gettimeofday(&end, NULL); + std::cout << "Static GET test " << duration(start, end, count) << " ms" << std::endl; + } + return true; +} + const bool testEval() { CURLHandle ch; const value val = evalExpr(mklist(std::string("echo"), std::string("Hello")), "http://localhost:8091/test", ch); @@ -83,8 +134,7 @@ const bool testEvalPerf() { testEvalLoop(count, ch); gettimeofday(&end, NULL); - long t = (end.tv_sec * 1000 + end.tv_usec / 1000) - (start.tv_sec * 1000 + start.tv_usec / 1000); - std::cout << "JSON-RPC eval echo test " << (t / count) << " ms" << std::endl; + std::cout << "JSON-RPC eval echo test " << duration(start, end, count) << " ms" << std::endl; } { testBlobEvalLoop(5, ch); @@ -94,25 +144,7 @@ const bool testEvalPerf() { testBlobEvalLoop(count, ch); gettimeofday(&end, NULL); - long t = (end.tv_sec * 1000 + end.tv_usec / 1000) - (start.tv_sec * 1000 + start.tv_usec / 1000); - std::cout << "JSON-RPC eval blob test " << (t / count) << " ms" << std::endl; - } -} - -const bool testGet() { - CURLHandle ch; - { - std::ostringstream os; - const failable, std::string> r = get(curlWriter, &os, "http://localhost:8091", ch); - assert(hasValue(r)); - assert(contains(os.str(), "HTTP/1.1 200 OK")); - assert(contains(os.str(), "It works")); - } - { - const failable r = get("http://localhost:8091", ch); - assert(hasValue(r)); - const value val = r; - assert(contains(val, "It works")); + std::cout << "JSON-RPC eval blob test " << duration(start, end, count) << " ms" << std::endl; } return true; } @@ -157,8 +189,7 @@ const bool testPostPerf() { testPostLoop(count, val, ch); gettimeofday(&end, NULL); - long t = (end.tv_sec * 1000 + end.tv_usec / 1000) - (start.tv_sec * 1000 + start.tv_usec / 1000); - std::cout << "ATOMPub POST small test " << (t / count) << " ms" << std::endl; + std::cout << "ATOMPub POST small test " << duration(start, end, count) << " ms" << std::endl; } { const list i = list() @@ -177,9 +208,9 @@ const bool testPostPerf() { testPostLoop(count, val, ch); gettimeofday(&end, NULL); - long t = (end.tv_sec * 1000 + end.tv_usec / 1000) - (start.tv_sec * 1000 + start.tv_usec / 1000); - std::cout << "ATOMPub POST blob test " << (t / count) << " ms" << std::endl; + std::cout << "ATOMPub POST blob test " << duration(start, end, count) << " ms" << std::endl; } + return true; } const bool testPut() { @@ -188,7 +219,7 @@ const bool testPut() { << (list() << "price" << std::string("$2.99")); const list a = mklist(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); CURLHandle ch; - value rc = put(a, "http://localhost:8091/test", ch); + value rc = put(a, "http://localhost:8091/test/111", ch); assert(rc == value(true)); return true; } @@ -207,10 +238,11 @@ int main() { std::cout << "Testing..." << std::endl; tuscany::http::testGet(); + tuscany::http::testGetPerf(); tuscany::http::testPost(); - //tuscany::http::testPostPerf(); + tuscany::http::testPostPerf(); tuscany::http::testEval(); - //tuscany::http::testEvalPerf(); + tuscany::http::testEvalPerf(); tuscany::http::testFeed(); tuscany::http::testPut(); tuscany::http::testDel(); diff --git a/cpp/sca/modules/http/htdocs/entry.xml b/cpp/sca/modules/http/htdocs/entry.xml index 4906fbedc8..86b8a10547 100644 --- a/cpp/sca/modules/http/htdocs/entry.xml +++ b/cpp/sca/modules/http/htdocs/entry.xml @@ -1,2 +1,2 @@ -Item111services.ItemAppleUSD$2.99 +Item111services.ItemAppleUSD$2.99 diff --git a/cpp/sca/modules/http/htdocs/feed.xml b/cpp/sca/modules/http/htdocs/feed.xml index 4ca3183739..5e37de6580 100644 --- a/cpp/sca/modules/http/htdocs/feed.xml +++ b/cpp/sca/modules/http/htdocs/feed.xml @@ -1,2 +1,2 @@ -Sample Feed123456789Item111javaClassItem222javaClassItem333javaClass +Sample Feed123456789Item111services.ItemAppleUSD$2.99Item222services.ItemOrangeUSD$3.55Item333services.ItemPearUSD$1.55 diff --git a/cpp/sca/modules/http/http-test b/cpp/sca/modules/http/http-test index e369cc34f7..d70db8d469 100755 --- a/cpp/sca/modules/http/http-test +++ b/cpp/sca/modules/http/http-test @@ -20,13 +20,15 @@ # Setup ./httpd-conf tmp 8091 htdocs cat >>tmp/conf/httpd.conf < -SetHandler mod_tuscany +SetHandler mod_tuscany_eval SCAContribution `pwd`/ +SCAComposite httpd-test.composite SCAComponent httpd-test -SCAImplementation httpd-test.scm EOF + apachectl -k start -d `pwd`/tmp sleep 1 @@ -36,5 +38,5 @@ rc=$? # Cleanup apachectl -k stop -d `pwd`/tmp -sleep 1 +sleep 2 return $rc diff --git a/cpp/sca/modules/http/httpd-client.scm b/cpp/sca/modules/http/httpd-client.scm new file mode 100644 index 0000000000..12275693f4 --- /dev/null +++ b/cpp/sca/modules/http/httpd-client.scm @@ -0,0 +1,25 @@ +; JSON-RPC test case + +(define (echo x ref) (ref "echo" x)) + +; ATOMPub test case + +(define (getall ref) + (ref "getall") +) + +(define (get id ref) + (ref "get" id) +) + +(define (post entry ref) + (ref "post" entry) +) + +(define (put id entry ref) + (ref "put" id entry) +) + +(define (delete id ref) + (ref "delete" id) +) diff --git a/cpp/sca/modules/http/httpd-conf b/cpp/sca/modules/http/httpd-conf index e076e22bb1..10a5b47ac2 100755 --- a/cpp/sca/modules/http/httpd-conf +++ b/cpp/sca/modules/http/httpd-conf @@ -31,6 +31,7 @@ ServerName 127.0.0.1 Listen $port DocumentRoot $htdocs TypesConfig $here/conf/mime.types -LoadModule mod_tuscany $here/.libs/libmod_tuscany.so +LoadModule mod_tuscany_eval $here/.libs/libmod_tuscany_eval.so +LoadModule mod_tuscany_wiring $here/.libs/libmod_tuscany_wiring.so EOF diff --git a/cpp/sca/modules/http/httpd-test b/cpp/sca/modules/http/httpd-test index 04c584496c..1d9b3cb34d 100755 --- a/cpp/sca/modules/http/httpd-test +++ b/cpp/sca/modules/http/httpd-test @@ -22,13 +22,15 @@ echo "Testing..." # Setup ./httpd-conf tmp 8090 htdocs cat >>tmp/conf/httpd.conf < -SetHandler mod_tuscany +SetHandler mod_tuscany_eval SCAContribution `pwd`/ +SCAComposite httpd-test.composite SCAComponent httpd-test -SCAImplementation httpd-test.scm EOF + apachectl -k start -d `pwd`/tmp sleep 1 @@ -70,7 +72,7 @@ fi # Cleanup apachectl -k stop -d `pwd`/tmp -sleep 1 +sleep 2 if [ "$rc" = "0" ]; then echo "OK" fi diff --git a/cpp/sca/modules/http/httpd-test.composite b/cpp/sca/modules/http/httpd-test.composite new file mode 100644 index 0000000000..875d26ae1b --- /dev/null +++ b/cpp/sca/modules/http/httpd-test.composite @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/cpp/sca/modules/http/httpd-test.scm b/cpp/sca/modules/http/httpd-test.scm index a3ddf8dda8..0566eaf36f 100644 --- a/cpp/sca/modules/http/httpd-test.scm +++ b/cpp/sca/modules/http/httpd-test.scm @@ -1,33 +1,29 @@ -(; "JSON-RPC test case") +; JSON-RPC test case (define (echo x) x) -(; "ATOMPub test case") +; ATOMPub test case (define (getall) '("Sample Feed" "123456789" - ("Item" "111" (javaClass "services.Item") (name "Apple") (currency "USD") (symbol "$") (price 2.99)) - ("Item" "222" (javaClass "services.Item") (name "Orange") (currency "USD") (symbol "$") (price 3.55)) - ("Item" "333" (javaClass "services.Item") (name "Pear") (currency "USD") (symbol "$") (price 1.55))) + ("Item" "111" ((javaClass "services.Item") (name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99))) + ("Item" "222" ((javaClass "services.Item") (name "Orange") (currencyCode "USD") (currencySymbol "$") (price 3.55))) + ("Item" "333" ((javaClass "services.Item") (name "Pear") (currencyCode "USD") (currencySymbol "$") (price 1.55)))) ) (define (get id) - (define entry '((javaClass "services.Item") (name "Apple") (currency "USD") (symbol "$") (price 2.99))) + (define entry '((javaClass "services.Item") (name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99))) (cons "Item" (list id entry)) ) (define (post entry) - (display entry) "123456789" ) -(define (put entry) - (display entry) +(define (put id entry) true ) -(define (delete . args) - (display args) +(define (delete id) true ) - diff --git a/cpp/sca/modules/http/httpd.hpp b/cpp/sca/modules/http/httpd.hpp new file mode 100644 index 0000000000..1271afc03c --- /dev/null +++ b/cpp/sca/modules/http/httpd.hpp @@ -0,0 +1,159 @@ +/* + * 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. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_httpd_hpp +#define tuscany_httpd_hpp + +/** + * HTTPD module utility functions. + */ + +#include +#include + +#include "apr_strings.h" +#include "apr_fnmatch.h" +#include "apr_lib.h" +#define APR_WANT_STRFUNC +#include "apr_want.h" +#include "ap_config.h" + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_request.h" +#include "http_protocol.h" +#include "http_log.h" +#include "http_main.h" +#include "util_script.h" +#include "util_md5.h" + +#include "mod_core.h" + +#include "list.hpp" +#include "value.hpp" + + +namespace tuscany { +namespace httpd { + +/** + * Set to true to log requests and content. + */ +bool logRequests = false; +bool logContent = false; + +/** + * Convert a path string to a list of values. + */ +const list pathTokens(const char* p) { + if (p == NULL || p[0] == '\0') + return list(); + return tokenize("/", p + 1); +} + +const list pathValues(const list& l) { + if (isNil(l)) + return list(); + return cons(car(l), pathValues(cdr(l))); +} + +const list path(const char* p) { + return pathValues(pathTokens(p)); +} + +/** + * Convert a path represented as a list of values to a string. + */ +const std::string path(const list& p) { + if (isNil(p)) + return ""; + return "/" + car(p) + path(cdr(p)); +} + +/** + * Return the content type of a request. + */ +const char* optional(const char* s) { + if (s == NULL) + return "(null)"; + return s; +} + +const std::string contentType(const request_rec* r) { + return optional(apr_table_get(r->headers_in, "Content-Type")); +} + +/** + * Log HTTP request info. + */ +int logHeader(void* r, const char* key, const char* value) { + std::cout << "header key: " << key << ", value: " << value << std::endl; + return 1; +} + +const bool logRequest(request_rec* r, const std::string& msg) { + std::cout << msg << std::endl; + std::cout << "protocol: " << optional(r->protocol) << std::endl; + std::cout << "method: " << optional(r->method) << std::endl; + std::cout << "method number: " << r->method_number << std::endl; + std::cout << "content type: " << contentType(r) << std::endl; + std::cout << "content encoding: " << optional(r->content_encoding) << std::endl; + apr_table_do(logHeader, r, r->headers_in, NULL); + std::cout << "uri: " << optional(r->unparsed_uri) << std::endl; + std::cout << "path: " << optional(r->uri) << std::endl; + std::cout << "path info: " << optional(r->path_info) << std::endl; + std::cout << "filename: " << optional(r->filename) << std::endl; + std::cout << "path tokens: " << pathTokens(r->uri) << std::endl; + std::cout << "args: " << optional(r->args) << std::endl; + std::cout.flush(); + return true; +} + +/** + * Returns a list of key value pairs from the args in a query string. + */ +const list queryArg(const std::string& s) { + const list t = tokenize("=", s); + return mklist(car(t).c_str(), cadr(t)); +} + +const list > queryArgs(const request_rec* r) { + const char* a = r->args; + if (a == NULL) + return list >(); + return map>(queryArg, tokenize("&", a)); +} + +/** + * Converts the args received in a POST to a list of key value pairs. + */ +const list > postArgs(const list& a) { + if (isNil(a)) + return list >(); + const list l = car(a); + return cons(l, postArgs(cdr(a))); +} + +} +} + +#endif /* tuscany_httpd_hpp */ diff --git a/cpp/sca/modules/http/mod-eval.cpp b/cpp/sca/modules/http/mod-eval.cpp new file mode 100644 index 0000000000..6fef2be2cb --- /dev/null +++ b/cpp/sca/modules/http/mod-eval.cpp @@ -0,0 +1,532 @@ +/* + * 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. + */ + +/* $Rev$ $Date$ */ + +/** + * HTTPD module used to eval component implementations. + */ + +#include + +#include +#include +#include +#include + +#include "list.hpp" +#include "slist.hpp" +#include "value.hpp" +#include "element.hpp" +#include "monad.hpp" +#include "../atom/atom.hpp" +#include "../json/json.hpp" +#include "../eval/driver.hpp" +#include "../scdl/scdl.hpp" +#include "../cache/cache.hpp" +#include "curl.hpp" +#include "httpd.hpp" + +extern "C" { +extern module AP_MODULE_DECLARE_DATA mod_tuscany_eval; +} + +namespace tuscany { +namespace httpd { +namespace modeval { + +/** + * Server configuration. + */ +class ServerConf { +public: + ServerConf() : home("") { + } + std::string home; +}; + +const ServerConf& serverConf(const request_rec* r) { + return *(ServerConf*)ap_get_module_config(r->server->module_config, &mod_tuscany_eval); +} + +/** + * Port number used for wiring requests. Set it to zero to use the current + * server port number, set it to a port number to direct wiring requests + * to that port, for debugging or tracing for example. + */ +int debugWiringPort = 0; + +/** + * Directory configuration. + */ +class DirConf { +public: + DirConf() : contributionPath(""), compositeName(""), componentName(""), implementationPath("") { + } + std::string contributionPath; + std::string compositeName; + std::string componentName; + std::string implementationPath; + cache::cached > component; + cache::cached > implementation; +}; + +DirConf& dirConf(const request_rec* r) { + return *(DirConf*)ap_get_module_config(r->per_dir_config, &mod_tuscany_eval); +} + +/** + * Evaluate an expression against a component implementation. + */ +const failable evalExpr(const value& expr, const value& impl) { + gc_pool pool; + eval::Env globalEnv = eval::setupEnvironment(pool); + if (logContent) { + std::cout<< "expr: " << expr << std::endl; + std::cout.flush(); + } + const value val = eval::evalScript(expr, impl, globalEnv, pool); + if (logContent) { + std::cout<< "val: " << val << std::endl; + std::cout.flush(); + } + + if (isNil(val)) + return mkfailure("Could not evaluate expression"); + return val; +} + +/** + * Returns a list of param values other than the id and method args from a list + * of key value pairs. + */ +const list queryParams(const list >& a) { + if (isNil(a)) + return list(); + const list p = car(a); + if (car(p) == value("id") || car(p) == value("method")) + return queryParams(cdr(a)); + return cons(cadr(p), queryParams(cdr(a))); +} + +/** + * Write an HTTP result. + */ +const failable writeResult(const failable, std::string>& ls, const std::string& ct, request_rec* r) { + if (!hasValue(ls)) + return mkfailure(reason(ls)); + std::ostringstream os; + write(ls, os); + if (logContent) { + std::cout<< "content: " << std::endl << os.str() << std::endl; + std::cout.flush(); + } + + const std::string etag(ap_md5(r->pool, (const unsigned char*)std::string(os.str()).c_str())); + const char* match = apr_table_get(r->headers_in, "If-None-Match"); + apr_table_setn(r->headers_out, "ETag", apr_pstrdup(r->pool, etag.c_str())); + if (match != NULL && etag == match) { + r->status = HTTP_NOT_MODIFIED; + return OK; + } + ap_set_content_type(r, ct.c_str()); + ap_rputs(std::string(os.str()).c_str(), r); + return OK; +} + +/** + * Handle an HTTP GET. + */ +const failable get(request_rec* r, const value& impl, const list& px) { + + // Inspect the query string + const list > args = queryArgs(r); + const list ia = assoc(value("id"), args); + const list ma = assoc(value("method"), args); + + // Evaluate a JSON-RPC request and return a JSON result + if (!isNil(ia) && !isNil(ma)) { + + // Extract the request id, method and params + const value id = cadr(ia); + const value func = std::string(cadr(ma)).c_str(); + const list params = queryParams(args); + + // Evaluate the request expression + const failable val = evalExpr(cons(func, eval::quotedParameters(append(params, px))), impl); + if (!hasValue(val)) + return mkfailure(reason(val)); + + // Return JSON result + json::JSONContext cx; + return writeResult(json::jsonResult(id, val, cx), "application/json-rpc", r); + } + + // Evaluate an ATOM GET request and return an ATOM feed + const list id(path(r->path_info)); + if (isNil(id)) { + const failable val = evalExpr(cons("getall", eval::quotedParameters(px)), impl); + if (!hasValue(val)) + return mkfailure(reason(val)); + const value feed = val; + return writeResult(atom::writeATOMFeed(atom::feedValuesToElements(feed)), "application/atom+xml;type=feed", r); + } + + // Evaluate an ATOM GET and return an ATOM entry + const failable val = evalExpr(cons("get", eval::quotedParameters(cons(car(id), px))), impl); + if (!hasValue(val)) + return mkfailure(reason(val)); + const value entry = val; + return writeResult(atom::writeATOMEntry(atom::entryValuesToElements(entry)), "application/atom+xml;type=entry", r); + +} + +/** + * Read the content of a POST. + */ +const list read(request_rec* r) { + char b[2048]; + const int n = ap_get_client_block(r, b, 2048); + if (n <= 0) + return list(); + return cons(std::string(b, n), read(r)); +} + +/** + * Convert a URI value to an absolute URL. + */ +const char* url(const value& v, request_rec* r) { + std::string u = r->uri; + u.append("/"); + u.append(v); + return ap_construct_url(r->pool, u.c_str(), r); +} + +/** + * Convert an ATOM entry to a value. + */ +const value feedEntry(const list& e) { + const list v = elementsToValues(mklist(caddr(e))); + return cons(car(e), mklist(cadr(e), cdr(car(v)))); +} + +/** + * Handle an HTTP POST. + */ +const failable post(request_rec* r, const value& impl, const list& px) { + const list ls = read(r); + if (logContent) { + std::cout<< "content: " << std::endl; + write(ls, std::cout); + std::cout<< std::endl; + std::cout.flush(); + } + + // Evaluate a JSON-RPC request and return a JSON result + const std::string ct = contentType(r); + if (ct.find("application/json-rpc") != std::string::npos || ct.find("text/plain") != std::string::npos) { + json::JSONContext cx; + const list json = elementsToValues(json::readJSON(ls, cx)); + const list > args = postArgs(json); + + // Extract the request id, method and params + const value id = cadr(assoc(value("id"), args)); + const value func = std::string(cadr(assoc(value("method"), args))).c_str(); + const list params = (list)cadr(assoc(value("params"), args)); + + // Evaluate the request expression + const failable val = evalExpr(cons(func, eval::quotedParameters(append(params, px))), impl); + if (!hasValue(val)) + return mkfailure(reason(val)); + + // Return JSON result + return writeResult(json::jsonResult(id, val, cx), "application/json-rpc", r); + } + + // Evaluate an ATOM POST request and return the created resource location + if (ct.find("application/atom+xml") != std::string::npos) { + + // Evaluate the request expression + const value entry = feedEntry(atom::readEntry(ls)); + const failable val = evalExpr(cons("post", eval::quotedParameters(cons(entry, px))), impl); + if (!hasValue(val)) + return mkfailure(reason(val)); + + // Return the created resource location + apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, url(val, r))); + r->status = HTTP_CREATED; + return OK; + } + + return HTTP_NOT_IMPLEMENTED; +} + +/** + * Handle an HTTP PUT. + */ +const failable put(request_rec* r, const value& impl, const list& px) { + const list ls = read(r); + if (logContent) { + std::cout<< "content: " << std::endl; + write(ls, std::cout); + std::cout<< std::endl; + std::cout.flush(); + } + + // Evaluate an ATOM PUT request + const list id(path(r->path_info)); + const value entry = feedEntry(atom::readEntry(ls)); + const failable val = evalExpr(cons("put", eval::quotedParameters(append(mklist(entry, car(id)), px))), impl); + if (!hasValue(val)) + return mkfailure(reason(val)); + if (val == value(false)) + return HTTP_NOT_FOUND; + return OK; +} + +/** + * Handle an HTTP DELETE. + */ +const failable del(request_rec* r, const value& impl, const list& px) { + + // Evaluate an ATOM delete request + const list id(path(r->path_info)); + const failable val = evalExpr(cons("delete", eval::quotedParameters(cons(car(id), px))), impl); + if (!hasValue(val)) + return mkfailure(reason(val)); + if (val == value(false)) + return HTTP_NOT_FOUND; + return OK; +} + +/** + * Report request execution status. + */ +const int reportStatus(const failable& rc) { + if (!hasValue(rc)) + return HTTP_INTERNAL_SERVER_ERROR; + return rc; +} + +/** + * Read the SCDL configuration of a component. + */ +const failable readComponent(const std::string& path, const std::string& name) { + + // Read composite + std::ifstream is(path); + if (is.fail() || is.bad()) + return mkfailure("Could not read composite: " + path); + + // Return the component + const list c = scdl::components(readXML(streamList(is))); + const value comp = scdl::named(name, c); + if (isNil(comp)) + return mkfailure("Could not find component: " + name); + return comp; +} + +const cache::cached > component(DirConf* conf) { + const std::string path(conf->contributionPath + conf->compositeName); + const lambda(std::string, std::string)> rc(readComponent); + const lambda ft(cache::latestFileTime); + return cache::cached >(curry(rc, path, conf->componentName), curry(ft, path)); +} + +/** + * Read a component implementation. + */ +const failable readImplementation(const std::string path) { + std::ifstream is(path.c_str(), std::ios_base::in); + if (is.fail() || is.bad()) + return mkfailure("Could not read implementation: " + path); + const value impl = eval::readScript(is); + if (isNil(impl)) + return mkfailure("Could not read implementation: " + path); + return impl; +} + +const cache::cached > implementation(const std::string& path) { + const lambda(std::string)> ri(readImplementation); + const lambda ft(cache::latestFileTime); + return cache::cached >(curry(ri, path), curry(ft, path)); +} + +/** + * Convert a list of component references to a list of HTTP proxy lambdas. + */ +const value mkproxy(const value& ref, const std::string& base, const http::CURLHandle& ch) { + return eval::primitiveProcedure(http::proxy(base + std::string(scdl::name(ref)), ch)); +} + +const list proxies(const list& refs, const std::string& base, const http::CURLHandle& ch) { + if (isNil(refs)) + return refs; + return cons(mkproxy(car(refs), base, ch), proxies(cdr(refs), base, ch)); +} + +/** + * HTTP request handler. + */ +int handler(request_rec *r) { + if(strcmp(r->handler, "mod_tuscany_eval")) + return DECLINED; + + // Log the request + if(logRequests) + logRequest(r, "mod_tuscany_eval::handler"); + + // Set up the read policy + const int rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK); + if(rc != OK) + return rc; + ap_should_client_block(r); + if(r->read_chunked == true && r->remaining == 0) + r->chunked = true; + //apr_table_setn(r->headers_out, "Connection", "close"); + + // Retrieve the latest component configuration + DirConf& conf = dirConf(r); + conf.component = cache::latest(conf.component); + const failable comp(conf.component); + if (!hasValue(comp)) + return HTTP_NOT_FOUND; + + // Retrieve the latest implementation + const std::string path = conf.contributionPath + std::string(scdl::uri(scdl::implementation(comp))); + if (path != conf.implementationPath) { + conf.implementationPath = path; + conf.implementation = cache::latest(implementation(path)); + } + else + conf.implementation = cache::latest(conf.implementation); + const failable impl(conf.implementation); + if (!hasValue(impl)) + return HTTP_NOT_FOUND; + + // Convert component references to configured proxy lambdas + std::ostringstream base; + base << "http://localhost:" << (debugWiringPort == 0? ap_get_server_port(r) : debugWiringPort) << "/references/" << std::string(scdl::name(comp)) << "/"; + http::CURLHandle ch; + const list px(proxies(scdl::references(comp), base.str(), ch)); + + // Handle HTTP method + if (r->header_only) + return OK; + if(r->method_number == M_GET) + return reportStatus(get(r, impl, px)); + if(r->method_number == M_POST) + return reportStatus(post(r, impl, px)); + if(r->method_number == M_PUT) + return reportStatus(put(r, impl, px)); + if(r->method_number == M_DELETE) + return reportStatus(del(r, impl, px)); + return HTTP_NOT_IMPLEMENTED; +} + +/** + * Configuration commands. + */ +const char *confHome(cmd_parms *cmd, void *dummy, const char *arg) { + ServerConf *c = (ServerConf*)ap_get_module_config(cmd->server->module_config, &mod_tuscany_eval); + c->home = arg; + return NULL; +} +const char *confContribution(cmd_parms *cmd, void *c, const char *arg) { + DirConf* conf = (DirConf*)c; + conf->contributionPath = arg; + conf->component = component(conf); + return NULL; +} +const char *confComposite(cmd_parms *cmd, void *c, const char *arg) { + DirConf* conf = (DirConf*)c; + conf->compositeName = arg; + conf->component = component(conf); + return NULL; +} +const char *confComponent(cmd_parms *cmd, void *c, const char *arg) { + DirConf* conf = (DirConf*)c; + conf->componentName = arg; + conf->component = component(conf); + return NULL; +} + +void *makeDirConf(apr_pool_t *p, char *dirspec) { + DirConf* c = new (apr_palloc(p, sizeof(DirConf))) DirConf(); + apr_pool_cleanup_register(p, c, gc_pool_cleanupCallback, apr_pool_cleanup_null) ; + return c; +} +void* makeServerConf(apr_pool_t *p, server_rec *s) { + ServerConf* c = new (apr_palloc(p, sizeof(ServerConf))) ServerConf(); + apr_pool_cleanup_register(p, c, gc_pool_cleanupCallback, apr_pool_cleanup_null) ; + return c; +} + +/** + * HTTP server module declaration. + */ +const command_rec commands[] = { + AP_INIT_TAKE1("TuscanyHome", (const char*(*)())confHome, NULL, RSRC_CONF, "Tuscany home directory"), + AP_INIT_TAKE1("SCAContribution", (const char*(*)())confContribution, NULL, ACCESS_CONF, "SCA contribution location"), + AP_INIT_TAKE1("SCAComposite", (const char*(*)())confComposite, NULL, ACCESS_CONF, "SCA composite location"), + AP_INIT_TAKE1("SCAComponent", (const char*(*)())confComponent, NULL, ACCESS_CONF, "SCA component name"), + {NULL} +}; + +int postConfig(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { + return OK; +} + +void childInit(apr_pool_t* p, server_rec* svr_rec) { + ServerConf *c = (ServerConf*)ap_get_module_config(svr_rec->module_config, &mod_tuscany_eval); + if(c == NULL) { + std::cerr << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << std::endl; + exit(APEXIT_CHILDFATAL); + } +} + +void registerHooks(apr_pool_t *p) { + ap_hook_post_config(postConfig, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_child_init(childInit, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_handler(handler, NULL, NULL, APR_HOOK_MIDDLE); +} + +} +} +} + +extern "C" { + +module AP_MODULE_DECLARE_DATA mod_tuscany_eval = { + STANDARD20_MODULE_STUFF, + // dir config + tuscany::httpd::modeval::makeDirConf, + // dir merger, default is to override + NULL, + // server config + tuscany::httpd::modeval::makeServerConf, + // merge server config + NULL, + // command table + tuscany::httpd::modeval::commands, + // register hooks + tuscany::httpd::modeval::registerHooks +}; + +} diff --git a/cpp/sca/modules/http/mod-wiring.cpp b/cpp/sca/modules/http/mod-wiring.cpp new file mode 100644 index 0000000000..965d5a87fb --- /dev/null +++ b/cpp/sca/modules/http/mod-wiring.cpp @@ -0,0 +1,276 @@ +/* + * 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. + */ + +/* $Rev$ $Date$ */ + +/** + * HTTPD module used to wire components. + */ + +#include + +#include +#include +#include +#include + +#include "list.hpp" +#include "slist.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "../scdl/scdl.hpp" +#include "../cache/cache.hpp" +#include "httpd.hpp" + +extern "C" { +extern module AP_MODULE_DECLARE_DATA mod_tuscany_wiring; +} + +namespace tuscany { +namespace httpd { +namespace modwiring { + +/** + * Server configuration. + */ +class ServerConf { +public: + std::string home; + ServerConf() : home("") { + } +}; + +const ServerConf& serverConf(const request_rec* r) { + return *(ServerConf*)ap_get_module_config(r->server->module_config, &mod_tuscany_wiring); +} + +/** + * Set to true to wire using mod_proxy, false to wire using HTTP client redirects. + */ +const bool useModProxy = true; + +/** + * Directory configuration. + */ +class DirConf { +public: + DirConf() : contributionPath(""), compositeName("") { + } + std::string contributionPath; + std::string compositeName; + cache::cached, std::string> > components; +}; + +DirConf& dirConf(const request_rec* r) { + return *(DirConf*)ap_get_module_config(r->per_dir_config, &mod_tuscany_wiring); +} + +/** + * Read the SCDL configuration of the deployed components. + */ +const failable, std::string> readComponents(const std::string& path) { + std::ifstream is(path); + if (is.fail() || is.bad()) + return mkfailure, std::string>("Could not read composite: " + path); + return scdl::components(readXML(streamList(is))); +} + +const cache::cached, std::string> > components(DirConf* conf) { + const std::string path(conf->contributionPath + conf->compositeName); + const lambda, std::string>(std::string)> rc(readComponents); + const lambda ft(cache::latestFileTime); + return cache::cached, std::string> >(curry(rc, path), curry(ft, path)); +} + +/** + * Returns true if a URI is absolute. + */ +const bool isAbsolute(const std::string& uri) { + return uri.find("://") != std::string::npos; +} + +/** + * Translate an HTTP request URI. Wire a URI in the form /references/component-name/reference-name + * to the target of the reference. + */ +int translate(request_rec *r) { + if (strncmp(r->uri, "/references/", 12) != 0) + return DECLINED; + const list rpath(path(r->uri)); + + // Log the request + if(logRequests) + logRequest(r, "mod_tuscany_wiring::translate"); + + // Find the requested component, reference and its target configuration + DirConf& conf = dirConf(r); + conf.components = cache::latest(conf.components); + const failable, std::string> comps(conf.components); + if (!hasValue(comps)) + return HTTP_INTERNAL_SERVER_ERROR; + const value comp(scdl::named(cadr(rpath), list(comps))); + if (isNil(comp)) + return HTTP_NOT_FOUND; + const value ref(scdl::named(caddr(rpath), scdl::references(comp))); + if (isNil(ref)) + return HTTP_NOT_FOUND; + const std::string target(scdl::target(ref)); + + // Absolute target URI + if (isAbsolute(target)) { + + // Wire using mod_proxy + if (useModProxy) { + r->filename = apr_pstrdup(r->pool, std::string("proxy:" + target).c_str()); + r->proxyreq = PROXYREQ_REVERSE; + r->handler = "proxy-server"; + return OK; + } + + // Wire using an HTTP client redirect + r->status = HTTP_MOVED_TEMPORARILY; + apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, target.c_str())); + r->handler = "mod_tuscany_wiring"; + return OK; + + } + + // Local internal redirect + r->filename = apr_pstrdup(r->pool, std::string("/redirect:/" + target).c_str()); + r->handler = "mod_tuscany_wiring"; + return OK; +} + +/** + * Construct a redirect URI. + */ +const std::string redirect(const std::string& file, const std::string& pi) { + return file + pi; +} + +const std::string redirect(const std::string& file, const std::string& pi, const std::string& args) { + return file + pi + "?" + args; +} + +/** + * HTTP request internal redirect handler. + */ +int handler(request_rec *r) { + if(strcmp(r->handler, "mod_tuscany_wiring")) + return DECLINED; + + // Log the request + if(logRequests) + logRequest(r, "mod_tuscany_wiring::handler"); + + // Do an internal redirect + if (r->filename == NULL || strncmp(r->filename, "/redirect:", 10) != 0) + return DECLINED; + if (r->args == NULL) { + ap_internal_redirect(apr_pstrdup(r->pool, redirect(r->filename + 10, r->path_info).c_str()), r); + return OK; + } + ap_internal_redirect(apr_pstrdup(r->pool, redirect(r->filename + 10, r->path_info, r->args).c_str()), r); + return OK; +} + +/** + * Configuration commands. + */ +const char *confHome(cmd_parms *cmd, void *dummy, const char *arg) { + ServerConf *c = (ServerConf*)ap_get_module_config(cmd->server->module_config, &mod_tuscany_wiring); + c->home = arg; + return NULL; +} +const char *confContribution(cmd_parms *cmd, void *c, const char *arg) { + DirConf* conf = (DirConf*)c; + conf->contributionPath = arg; + conf->components = components(conf); + return NULL; +} +const char *confComposite(cmd_parms *cmd, void *c, const char *arg) { + DirConf* conf = (DirConf*)c; + conf->compositeName = arg; + conf->components = components(conf); + return NULL; +} + +void *makeDirConf(apr_pool_t *p, char *dirspec) { + DirConf* c = new (apr_palloc(p, sizeof(DirConf))) DirConf(); + apr_pool_cleanup_register(p, c, gc_pool_cleanupCallback, apr_pool_cleanup_null) ; + return c; +} +void* makeServerConf(apr_pool_t *p, server_rec *s) { + ServerConf* c = new (apr_palloc(p, sizeof(ServerConf))) ServerConf(); + apr_pool_cleanup_register(p, c, gc_pool_cleanupCallback, apr_pool_cleanup_null) ; + return c; +} + +/** + * HTTP server module declaration. + */ +const command_rec commands[] = { + AP_INIT_TAKE1("TuscanyHome", (const char*(*)())confHome, NULL, RSRC_CONF, "Tuscany home directory"), + AP_INIT_TAKE1("SCAContribution", (const char*(*)())confContribution, NULL, ACCESS_CONF, "SCA contribution location"), + AP_INIT_TAKE1("SCAComposite", (const char*(*)())confComposite, NULL, ACCESS_CONF, "SCA composite location"), + {NULL} +}; + +int postConfig(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { + return OK; +} + +void childInit(apr_pool_t* p, server_rec* svr_rec) { + ServerConf *conf = (ServerConf*)ap_get_module_config(svr_rec->module_config, &mod_tuscany_wiring); + if(conf == NULL) { + std::cerr << "[Tuscany] Due to one or more errors mod_tuscany_wiring loading failed. Causing apache to stop loading." << std::endl; + exit(APEXIT_CHILDFATAL); + } +} + +void registerHooks(apr_pool_t *p) { + ap_hook_post_config(postConfig, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_child_init(childInit, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_handler(handler, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_translate_name(translate, NULL, NULL, APR_HOOK_FIRST); +} + +} +} +} + +extern "C" { + +module AP_MODULE_DECLARE_DATA mod_tuscany_wiring = { + STANDARD20_MODULE_STUFF, + // dir config + tuscany::httpd::modwiring::makeDirConf, + // dir merger, default is to override + NULL, + // server config + tuscany::httpd::modwiring::makeServerConf, + // merge server config + NULL, + // command table + tuscany::httpd::modwiring::commands, + // register hooks + tuscany::httpd::modwiring::registerHooks +}; + +} diff --git a/cpp/sca/modules/http/mod.cpp b/cpp/sca/modules/http/mod.cpp deleted file mode 100644 index 811ee0c633..0000000000 --- a/cpp/sca/modules/http/mod.cpp +++ /dev/null @@ -1,555 +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. - */ - -/* $Rev$ $Date$ */ - -/** - * Tuscany HTTPD server module. - */ - -#include -#include -#include -#include - -#include "apr_strings.h" -#include "apr_fnmatch.h" -#include "apr_lib.h" -#define APR_WANT_STRFUNC -#include "apr_want.h" -#include "ap_config.h" - -#include "httpd.h" -#include "http_config.h" -#include "http_core.h" -#include "http_request.h" -#include "http_protocol.h" -#include "http_log.h" -#include "http_main.h" -#include "util_script.h" -#include "util_md5.h" - -#include "mod_core.h" - -#include "list.hpp" -#include "slist.hpp" -#include "value.hpp" -#include "element.hpp" -#include "monad.hpp" -#include "../atom/atom.hpp" -#include "../json/json.hpp" -#include "../eval/driver.hpp" - -extern "C" { -extern module AP_MODULE_DECLARE_DATA mod_tuscany; -} - -namespace tuscany { -namespace httpd { - -/** - * Set to true to log requests and content. - */ -bool logRequests = false; -bool logContent = false; - -/** - * Server configuration. - */ -struct ServerConf { - const char* home; -}; - -/** - * Directory configuration. - */ -struct DirConf { - const char* contribution; - const char* component; - const char* implementation; -}; - -/** - * Returns the server conf for a request. - */ -const ServerConf& serverConf(const request_rec* r) { - return *(ServerConf*)ap_get_module_config(r->server->module_config, &mod_tuscany); -} -const std::string home(request_rec* r) { - return serverConf(r).home; -} - -/** - * Returns the dir conf for a request. - */ -const DirConf& dirConf(const request_rec* r) { - return *(DirConf*)ap_get_module_config(r->per_dir_config, &mod_tuscany); -} -const std::string contribution(request_rec* r) { - return dirConf(r).contribution; -} -const std::string component(request_rec* r) { - return dirConf(r).component; -} -const std::string implementation(request_rec* r) { - return dirConf(r).implementation; -} - -/** - * Returns an HTTP request path as a list of strings. - */ -const list pathTokens(const request_rec* r) { - const char* p = r->path_info; - if (p == NULL || p[0] == '\0') - return list(); - return tokenize("/", p + 1); -} - -const list pathValues(const list& l) { - if (isNil(l)) - return list(); - return cons(car(l), pathValues(cdr(l))); -} - -const list path(const request_rec* r) { - return pathValues(pathTokens(r)); -} - -/** - * Return the content type of a request. - */ -const char* optional(const char* s) { - if (s == NULL) - return "(null)"; - return s; -} - -const std::string contentType(const request_rec* r) { - return optional(apr_table_get(r->headers_in, "Content-Type")); -} - -/** - * Log HTTP request info. - */ -int logHeader(void* r, const char* key, const char* value) { - std::cout << "header key: " << key << ", value: " << value << std::endl; - return 1; -} - -const bool logRequest(request_rec* r) { - std::cout << "mod-tuscany..." << std::endl; - std::cout << "tuscany home: " << home(r) << std::endl; - std::cout << "contribution: " << contribution(r) << std::endl; - std::cout << "component: " << component(r) << std::endl; - std::cout << "protocol: " << optional(r->protocol) << std::endl; - std::cout << "method: " << optional(r->method) << std::endl; - std::cout << "method number: " << r->method_number << std::endl; - std::cout << "content type: " << contentType(r) << std::endl; - std::cout << "content encoding: " << optional(r->content_encoding) << std::endl; - apr_table_do(logHeader, r, r->headers_in, NULL); - std::cout << "uri: " << optional(r->uri) << std::endl; - std::cout << "path info: " << optional(r->path_info) << std::endl; - std::cout << "filename: " << optional(r->filename) << std::endl; - std::cout << "path: " << pathTokens(r) << std::endl; - std::cout << "args: " << optional(r->args) << std::endl; - std::cout.flush(); - return true; -} - -/** - * Evaluate an expression against a component implementation. - */ -const value evalExprLoop(std::istream& is, const value& expr, eval::Env& globalEnv, const gc_pool& pool) { - value in = eval::readValue(is); - if (isNil(in)) - return eval::evalExpr(expr, globalEnv, pool); - eval::evalExpr(in, globalEnv, pool); - return evalExprLoop(is, expr, globalEnv, pool); -} - -const failable evalExpr(const value& expr, const std::string& contrib, const std::string& impl) { - // Retrieve the component implementation - const std::string path = contrib + impl; - std::ifstream is(path.c_str(), std::ios_base::in); - if (is.fail() || is.bad()) - return std::string("HTTP_NOT_FOUND"); - - // Evaluate the expr - gc_pool pool; - eval::Env globalEnv = eval::setupEnvironment(pool); - std::cout<< "expr: " << expr << std::endl; - std::cout.flush(); - const value val = evalExprLoop(is, expr, globalEnv, pool); - std::cout<< "val: " << val << std::endl; - std::cout.flush(); - - if (isNil(val)) - return std::string("Could not evaluate expression"); - return val; -} - -/** - * Returns a list of key value pairs from the args in a query string. - */ -const list queryArg(std::string s) { - const list t = tokenize("=", s); - return mklist(car(t).c_str(), cadr(t)); -} - -const list > queryArgs(const request_rec* r) { - const char* a = r->args; - if (a == NULL) - return list >(); - return map>(queryArg, tokenize("&", a)); -} - -/** - * Returns a list of param values other than the id and method args from a list - * of key value pairs. - */ -const list queryParams(list > a) { - if (isNil(a)) - return list(); - const list p = car(a); - if (car(p) == value("id") || car(p) == value("method")) - return queryParams(cdr(a)); - return cons(cadr(p), queryParams(cdr(a))); -} - -/** - * Write an HTTP result. - */ -const failable writeResult(const failable, std::string> ls, const std::string& ct, request_rec* r) { - if (!hasValue(ls)) - return std::string(ls); - std::ostringstream os; - write(ls, os); - if (logContent) { - std::cout<< "content: " << std::endl << os.str() << std::endl; - std::cout.flush(); - } - - std::string etag(ap_md5(r->pool, (const unsigned char*)std::string(os.str()).c_str())); - const char* match = apr_table_get(r->headers_in, "If-None-Match"); - apr_table_setn(r->headers_out, "ETag", etag.c_str()); - if (match != NULL && etag == match) { - r->status = HTTP_NOT_MODIFIED; - return OK; - } - ap_set_content_type(r, ct.c_str()); - ap_rputs(std::string(os.str()).c_str(), r); - return OK; -} - -/** - * Handle an HTTP GET. - */ -const failable get(request_rec* r) { - - // Inspect the query string - const list > args = queryArgs(r); - const list ia = assoc(value("id"), args); - const list ma = assoc(value("method"), args); - - // Evaluate a JSON-RPC request and return a JSON result - if (!isNil(ia) && !isNil(ma)) { - - // Extract the request id, method and params - const value id = cadr(ia); - const value func = std::string(cadr(ma)).c_str(); - const list params = queryParams(args); - - // Evaluate the request expression - const failable val = evalExpr(cons(func, eval::quotedParameters(params)), contribution(r), implementation(r)); - if (!hasValue(val)) - return std::string(val); - - // Return JSON result - json::JSONContext cx; - return writeResult(json::jsonResult(id, val, cx), "application/json-rpc", r); - } - - // Evaluate an ATOM GET request and return an ATOM feed - if (isNil(path(r))) { - const failable val = evalExpr(mklist("getall"), contribution(r), implementation(r)); - if (!hasValue(val)) - return std::string(val); - const value feed = val; - return writeResult(atom::writeATOMFeed(atom::feedValuesToElements(feed)), "application/atom+xml;type=feed", r); - } - - // Evaluate an ATOM GET and return an ATOM entry - const failable val = evalExpr(cons("get", path(r)), contribution(r), implementation(r)); - if (!hasValue(val)) - return std::string(val); - const value entry = val; - return writeResult(atom::writeATOMEntry(atom::entryValuesToElements(entry)), "application/atom+xml;type=entry", r); - -} - -/** - * Read the content of a POST. - */ -const list read(request_rec* r) { - char b[2048]; - const int n = ap_get_client_block(r, b, 2048); - if (n <= 0) - return list(); - return cons(std::string(b, n), read(r)); -} - -/** - * Converts the args received in a POST to a list of key value pairs. - */ -const list > postArgs(list a) { - if (isNil(a)) - return list >(); - const list l = car(a); - return cons(l, postArgs(cdr(a))); -} - -const char* url(const value& v, request_rec* r) { - std::string u = r->uri; - u.append("/"); - u.append(v); - return ap_construct_url(r->pool, u.c_str(), r); -} - -/** - * Convert an ATOM entry to a value. - */ -const value feedEntry(const list e) { - const list v = elementsToValues(mklist(caddr(e))); - return cons(car(e), mklist(cadr(e), cdr(car(v)))); -} - -/** - * Handle an HTTP POST. - */ -const failable post(request_rec* r) { - const list ls = read(r); - if (logContent) { - std::cout<< "content: " << std::endl; - write(ls, std::cout); - std::cout<< std::endl; - std::cout.flush(); - } - - // Evaluate a JSON-RPC request and return a JSON result - const std::string ct = contentType(r); - if (ct.find("application/json-rpc") != std::string::npos || ct.find("text/plain") != std::string::npos) { - json::JSONContext cx; - const list json = elementsToValues(json::readJSON(ls, cx)); - const list > args = postArgs(json); - - // Extract the request id, method and params - const value id = cadr(assoc(value("id"), args)); - const value func = std::string(cadr(assoc(value("method"), args))).c_str(); - const list params = (list)cadr(assoc(value("params"), args)); - - // Evaluate the request expression - const failable val = evalExpr(cons(func, eval::quotedParameters(params)), contribution(r), implementation(r)); - if (!hasValue(val)) - return std::string(val); - - // Return JSON result - return writeResult(json::jsonResult(id, val, cx), "application/json-rpc", r); - } - - // Evaluate an ATOM POST request and return the created resource location - if (ct.find("application/atom+xml") != std::string::npos) { - - // Evaluate the request expression - const value entry = feedEntry(atom::readEntry(ls)); - const failable val = evalExpr(cons("post", eval::quotedParameters(mklist(entry))), contribution(r), implementation(r)); - if (!hasValue(val)) - return std::string(val); - - // Return the created resource location - apr_table_setn(r->headers_out, "Location", url(val, r)); - r->status = HTTP_CREATED; - return OK; - } - - return HTTP_NOT_IMPLEMENTED; -} - -/** - * Handle an HTTP PUT. - */ -const failable put(request_rec* r) { - const list ls = read(r); - std::cout<< "content: " << std::endl; - write(ls, std::cout); - std::cout<< std::endl; - std::cout.flush(); - - // Evaluate an ATOM PUT request - const value entry = feedEntry(atom::readEntry(ls)); - const failable val = evalExpr(cons("put", eval::quotedParameters(mklist(entry))), contribution(r), implementation(r)); - if (!hasValue(val)) - return std::string(val); - if (val == value(false)) - return HTTP_NOT_FOUND; - return OK; -} - -/** - * Handle an HTTP DELETE. - */ -const failable del(request_rec* r) { - - // Evaluate an ATOM delete request - const failable val = evalExpr(cons("delete", path(r)), contribution(r), implementation(r)); - if (!hasValue(val)) - return std::string(val); - if (val == value(false)) - return HTTP_NOT_FOUND; - return OK; -} - -/** - * Report request execution status. - */ -const int reportStatus(const failable rc) { - if (!hasValue(rc)) - return HTTP_INTERNAL_SERVER_ERROR; - return rc; -} - -/** - * HTTP request handler entry point. - */ -int handler(request_rec *r) { - if(strcmp(r->handler, "mod_tuscany")) - return DECLINED; - - // Log the request - if(logRequests) - logRequest(r); - - // Set up the read policy - const int rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK); - if(rc != OK) - return rc; - ap_should_client_block(r); - if(r->read_chunked == true && r->remaining == 0) - r->chunked = true; - //apr_table_setn(r->headers_out, "Connection", "close"); - - // Handle HTTP method - if (r->header_only) - return OK; - if(r->method_number == M_GET) - return reportStatus(get(r)); - if(r->method_number == M_POST) - return reportStatus(post(r)); - if(r->method_number == M_PUT) - return reportStatus(put(r)); - if(r->method_number == M_DELETE) - return reportStatus(del(r)); - return HTTP_NOT_IMPLEMENTED; -} - -/** - * Configuration commands. - */ -const char *confHome(cmd_parms *cmd, void *dummy, const char *arg) { - ServerConf *conf = (ServerConf*)ap_get_module_config(cmd->server->module_config, &mod_tuscany); - conf->home = apr_pstrdup(cmd->pool, arg); - return NULL; -} -const char *confContribution(cmd_parms *cmd, void *c, const char *arg) { - DirConf *conf = (DirConf*)c; - conf->contribution = apr_pstrdup(cmd->pool, arg); - return NULL; -} -const char *confComponent(cmd_parms *cmd, void *c, const char *arg) { - DirConf *conf = (DirConf*)c; - conf->component = apr_pstrdup(cmd->pool, arg); - return NULL; -} -const char *confImplementation(cmd_parms *cmd, void *c, const char *arg) { - DirConf *conf = (DirConf*)c; - conf->implementation = apr_pstrdup(cmd->pool, arg); - return NULL; -} -void *makeDirConf(apr_pool_t *p, char *dirspec) { - DirConf* conf = (DirConf*)apr_palloc(p, sizeof(*conf)); - conf->contribution = ""; - conf->component = ""; - return conf; -} -void* makeServerConf(apr_pool_t *p, server_rec *s) { - ServerConf* conf = (ServerConf* )apr_palloc(p, sizeof(*conf)); - conf->home = ""; - return conf; -} - -/** - * HTTP server module declarations. - */ -const command_rec commands[] = { - AP_INIT_TAKE1("TuscanyHome", (const char*(*)())confHome, NULL, RSRC_CONF, "Tuscany home directory"), - AP_INIT_TAKE1("SCAContribution", (const char*(*)())confContribution, NULL, ACCESS_CONF, "SCA contribution location"), - AP_INIT_TAKE1("SCAComponent", (const char*(*)())confComponent, NULL, ACCESS_CONF, "SCA component name"), - AP_INIT_TAKE1("SCAImplementation", (const char*(*)())confImplementation, NULL, ACCESS_CONF, "SCA component implementation"), - {NULL} -}; - -int init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { - return OK; -} - -void childInit(apr_pool_t* p, server_rec* svr_rec) { - ServerConf *conf = (ServerConf*)ap_get_module_config(svr_rec->module_config, &mod_tuscany); - if(conf == NULL) { - std::cerr << "[Tuscany] Due to one or more errors mod_rest loading failed. Causing apache2 to stop loading" << std::endl; - exit(APEXIT_CHILDFATAL); - } -} - -void registerHooks(apr_pool_t *p) { - ap_hook_handler(handler, NULL, NULL, APR_HOOK_MIDDLE); - ap_hook_post_config(init, NULL, NULL, APR_HOOK_MIDDLE); - ap_hook_child_init(childInit, NULL, NULL, APR_HOOK_MIDDLE); -} - -} -} - -extern "C" { - -module AP_MODULE_DECLARE_DATA mod_tuscany = { - STANDARD20_MODULE_STUFF, - // dir config - tuscany::httpd::makeDirConf, - // dir merger, default is to override - NULL, - // server config - tuscany::httpd::makeServerConf, - // merge server config - NULL, - // command table - tuscany::httpd::commands, - // register hooks - tuscany::httpd::registerHooks -}; - -} diff --git a/cpp/sca/modules/http/wiring-test b/cpp/sca/modules/http/wiring-test new file mode 100755 index 0000000000..0c3f36b513 --- /dev/null +++ b/cpp/sca/modules/http/wiring-test @@ -0,0 +1,92 @@ +#!/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 +./httpd-conf tmp 8092 htdocs +cat >>tmp/conf/httpd.conf < +SetHandler mod_tuscany_eval +SCAContribution `pwd`/ +SCAComposite httpd-test.composite +SCAComponent httpd-test + + + +SetHandler mod_tuscany_eval +SCAContribution /home/delfinoj/SCAZone/Source/tuscany-cpp/sca/modules/http/ +SCAComposite httpd-test.composite +SCAComponent httpd-client + + + +SetHandler mod_tuscany_wiring +SCAContribution `pwd`/ +SCAComposite httpd-test.composite + +EOF + +apachectl -k start -d `pwd`/tmp +sleep 1 + +# Test HTTP GET +curl http://localhost:8092/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:8092/client/ >tmp/feed.xml 2>/dev/null + diff tmp/feed.xml htdocs/feed.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + curl http://localhost:8092/client/111 >tmp/entry.xml 2>/dev/null + diff tmp/entry.xml htdocs/entry.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + curl http://localhost:8092/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:8092/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:8092/client/111 -X DELETE 2>/dev/null + rc=$? +fi + +# Test JSON-RPC +if [ "$rc" = "0" ]; then + curl http://localhost:8092/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 + +# Cleanup +apachectl -k stop -d `pwd`/tmp +sleep 2 +if [ "$rc" = "0" ]; then + echo "OK" +fi +return $rc diff --git a/cpp/sca/modules/json/json-test.cpp b/cpp/sca/modules/json/json-test.cpp index 0d4e8f6a16..fd506cea65 100644 --- a/cpp/sca/modules/json/json-test.cpp +++ b/cpp/sca/modules/json/json-test.cpp @@ -114,6 +114,15 @@ bool testJSONRPC() { write(writeJSON(e, cx), os); assert(os.str() == "{\"id\":1,\"result\":[\"Service.get\",\"Service.getTotal\"]}"); } + { + const std::string f("{\"id\":1,\"result\":[\"Sample Feed\",\"123456789\",[\"Item\",\"111\",{\"javaClass\":\"services.Item\",\"name\":\"Apple\",\"currencyCode\":\"USD\",\"currencySymbol\":\"$\",\"price\":2.99}],[\"Item\",\"222\",{\"javaClass\":\"services.Item\",\"name\":\"Orange\",\"currencyCode\":\"USD\",\"currencySymbol\":\"$\",\"price\":3.55}],[\"Item\",\"333\",{\"javaClass\":\"services.Item\",\"name\":\"Pear\",\"currencyCode\":\"USD\",\"currencySymbol\":\"$\",\"price\":1.55}]]}"); + const list r = readJSON(mklist(f), cx); + const list v = elementsToValues(r); + const list e = valuesToElements(v); + std::ostringstream os; + write(writeJSON(e, cx), os); + assert(os.str() == f); + } return true; } diff --git a/cpp/sca/test/store-script/catalogs.composite b/cpp/sca/test/store-script/catalogs.composite deleted file mode 100644 index 1638ed0a05..0000000000 --- a/cpp/sca/test/store-script/catalogs.composite +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - USD - - - - - - - - - - - - - diff --git a/cpp/sca/test/store-script/currency-converter.scm b/cpp/sca/test/store-script/currency-converter.scm new file mode 100644 index 0000000000..7f58335951 --- /dev/null +++ b/cpp/sca/test/store-script/currency-converter.scm @@ -0,0 +1,9 @@ +; Currency converter implementation + +(define (convert from to amount) + (if (equal? to "EUR") (* amount 0.70) amount) +) + +(define (symbol currency) + (if (equal? currency "EUR") "E" "$") +) diff --git a/cpp/sca/test/store-script/currency.composite b/cpp/sca/test/store-script/currency.composite index aefd474f1f..eaea331dbe 100644 --- a/cpp/sca/test/store-script/currency.composite +++ b/cpp/sca/test/store-script/currency.composite @@ -19,14 +19,14 @@ --> - - - - - - - + + + + + + + diff --git a/cpp/sca/test/store-script/fruits-catalog.scm b/cpp/sca/test/store-script/fruits-catalog.scm new file mode 100644 index 0000000000..390068d71a --- /dev/null +++ b/cpp/sca/test/store-script/fruits-catalog.scm @@ -0,0 +1,19 @@ +; Catalog implementation + +(define (get converter) + (display "catalog") + (define (convert price) (converter "convert" "USD" "USD" price)) + + (define code "USD") + (define symbol (converter "symbol" code)) + + (list + (list (list 'javaClass "services.Item") (list 'name "Apple") (list 'currencyCode code) (list 'currencySymbol symbol) (list 'price (convert 2.99))) + (list (list 'javaClass "services.Item") (list 'name "Orange") (list 'currencyCode code) (list 'currencySymbol symbol) (list 'price (convert 3.55))) + (list (list 'javaClass "services.Item") (list 'name "Pear") (list 'currencyCode code) (list 'currencySymbol symbol) (list 'price (convert 1.55))) + ) +) + +; TODO remove these JSON-RPC specific functions +(define (system.listMethods converter) (list "Service.get")) +(define Service.get get) diff --git a/cpp/sca/test/store-script/shopping-cart.scm b/cpp/sca/test/store-script/shopping-cart.scm new file mode 100644 index 0000000000..ebb3504e25 --- /dev/null +++ b/cpp/sca/test/store-script/shopping-cart.scm @@ -0,0 +1,26 @@ +; Shopping cart implementation + +(define (post item) + (uuid) +) + +(define (getall) + (cons "Sample Feed" (cons (uuid) '())) +) + +(define (get id) + (define entry (list (list 'name "Apple") (list 'currencyCode "USD") (list 'currencySymbol "$") (list 'price 2.99))) + (cons "Item" (list id entry)) +) + +(define (delete id) + true +) + +(define (gettotal) + 11.0 +) + +; TODO remove these JSON-RPC specific functions +(define (system.listMethods) (list "Service.getTotal")) +(define Service.getTotal gettotal) diff --git a/cpp/sca/test/store-script/store-http-test b/cpp/sca/test/store-script/store-http-test index 4a5a3df685..551731e856 100755 --- a/cpp/sca/test/store-script/store-http-test +++ b/cpp/sca/test/store-script/store-http-test @@ -20,40 +20,55 @@ echo "Testing..." # Setup -../../modules/http/httpd-conf tmp 8092 htdocs +../../modules/http/httpd-conf tmp 8093 htdocs cat >>tmp/conf/httpd.conf < -SetHandler mod_tuscany +SetHandler mod_tuscany_eval SCAContribution `pwd`/ -SCAComponent store -SCAImplementation store.scm +SCAComposite store.composite +SCAComponent Catalog -SetHandler mod_tuscany +SetHandler mod_tuscany_eval SCAContribution `pwd`/ -SCAComponent store -SCAImplementation store.scm +SCAComposite store.composite +SCAComponent ShoppingCart -SetHandler mod_tuscany +SetHandler mod_tuscany_eval SCAContribution `pwd`/ -SCAComponent store -SCAImplementation store.scm +SCAComposite store.composite +SCAComponent ShoppingCart + + + +SetHandler mod_tuscany_eval +SCAContribution `pwd`/ +SCAComposite store.composite +SCAComponent CurrencyConverter + + + +SetHandler mod_tuscany_wiring +SCAContribution `pwd`/ +SCAComposite store.composite EOF + apachectl -k start -d `pwd`/tmp sleep 1 # Test HTTP GET -curl http://localhost:8092/store.html 2>/dev/null >tmp/store.html +curl http://localhost:8093/store.html 2>/dev/null >tmp/store.html diff tmp/store.html htdocs/store.html rc=$? # Cleanup apachectl -k stop -d `pwd`/tmp -sleep 1 +sleep 2 if [ "$rc" = "0" ]; then echo "OK" fi diff --git a/cpp/sca/test/store-script/store-script-test.cpp b/cpp/sca/test/store-script/store-script-test.cpp index 5a29d8caba..add5cb75e5 100644 --- a/cpp/sca/test/store-script/store-script-test.cpp +++ b/cpp/sca/test/store-script/store-script-test.cpp @@ -43,50 +43,42 @@ bool contains(const std::string& str, const std::string& pattern) { } bool testScript() { - std::ifstream is("store.scm", std::ios_base::in); + std::ifstream is("store-script.scm", std::ios_base::in); std::ostringstream os; eval::evalDriverRun(is, os); assert(contains(os.str(), "(\"Sample Feed\" \"")); assert(contains(os.str(), "\" (\"Item\" \"")); - assert(contains(os.str(), "\" ((javaClass \"services.Item\") (name \"Orange\") (currency \"USD\") (symbol \"$\") (price 3.55))) (\"Item\" \"")); - assert(contains(os.str(), "\" ((javaClass \"services.Item\") (name \"Apple\") (currency \"USD\") (symbol \"$\") (price 2.99))))")); + assert(contains(os.str(), "\" ((javaClass \"services.Item\") (name \"Orange\") (currencyCode \"USD\") (currencySymbol \"$\") (price 3.55))) (\"Item\" \"")); + assert(contains(os.str(), "\" ((javaClass \"services.Item\") (name \"Apple\") (currencyCode \"USD\") (currencySymbol \"$\") (price 2.99))))")); return true; } -const value evalLoop(std::istream& is, const value& req, eval::Env& globalEnv, const gc_pool& pool) { - value in = eval::readValue(is); - if(isNil(in)) - return eval::evalExpr(req, globalEnv, pool); - eval::evalExpr(in, globalEnv, pool); - return evalLoop(is, req, globalEnv, pool); -} - bool testEval() { { - std::ifstream is("store.scm", std::ios_base::in); + std::ifstream is("store-script.scm", std::ios_base::in); std::ostringstream os; eval::setupDisplay(os); gc_pool pool; eval::Env globalEnv = eval::setupEnvironment(pool); - const value req(mklist("storeui_service", std::string("getcatalog"))); - const value val = evalLoop(is, req, globalEnv, pool); + const value exp(mklist("storeui_service", std::string("getcatalog"))); + const value val = eval::evalScript(exp, is, globalEnv, pool); std::ostringstream vs; vs << val; - assert(contains(vs.str(), "(((javaClass \"services.Item\") (name \"Apple\") (currency \"USD\") (symbol \"$\") (price 2.99)) ((javaClass \"services.Item\") (name \"Orange\") (currency \"USD\") (symbol \"$\") (price 3.55)) ((javaClass \"services.Item\") (name \"Pear\") (currency \"USD\") (symbol \"$\") (price 1.55)))")); + assert(contains(vs.str(), "(((javaClass \"services.Item\") (name \"Apple\") (currencyCode \"USD\") (currencySymbol \"$\") (price 2.99)) ((javaClass \"services.Item\") (name \"Orange\") (currencyCode \"USD\") (currencySymbol \"$\") (price 3.55)) ((javaClass \"services.Item\") (name \"Pear\") (currencyCode \"USD\") (currencySymbol \"$\") (price 1.55)))")); } { - std::ifstream is("store.scm", std::ios_base::in); + std::ifstream is("store-script.scm", std::ios_base::in); std::ostringstream os; eval::setupDisplay(os); gc_pool pool; eval::Env globalEnv = eval::setupEnvironment(pool); - const value req(mklist("storeui_service", std::string("gettotal"))); - const value res = evalLoop(is, req, globalEnv, pool); + const value exp(mklist("storeui_service", std::string("gettotal"))); + const value res = eval::evalScript(exp, is, globalEnv, pool); std::ostringstream rs; rs << res; diff --git a/cpp/sca/test/store-script/store-script.scm b/cpp/sca/test/store-script/store-script.scm new file mode 100644 index 0000000000..30c10d8184 --- /dev/null +++ b/cpp/sca/test/store-script/store-script.scm @@ -0,0 +1,149 @@ +; Currency implementation + +(define (currency_convert from to amount) + (if (equal? to "EUR") (* amount 0.70) amount) +) + +(define (currency_symbol currency) + (if (equal? currency "EUR") "E" "$") +) + +(define (currency_impl op args) + (cond + ((equal? op "convert") (apply currency_convert args)) + ((equal? op "symbol") (apply currency_symbol args)) + ) +) + +; Currency composite + +(define (currency_service op . args) (currency_impl op args)) + +; Catalog implementation + +(define (catalog_get converter) + (define (convert price) (converter "convert" "USD" "USD" price)) + + (define code "USD") + (define symbol (converter "symbol" code)) + + (list + (list (list 'javaClass "services.Item") (list 'name "Apple") (list 'currencyCode code) (list 'currencySymbol symbol) (list 'price 2.99)) + (list (list 'javaClass "services.Item") (list 'name "Orange") (list 'currencyCode code) (list 'currencySymbol symbol) (list 'price 3.55)) + (list (list 'javaClass "services.Item") (list 'name "Pear") (list 'currencyCode code) (list 'currencySymbol symbol) (list 'price 1.55)) + ) +) + +(define (catalog_impl converter op args) + (cond + ((equal? op "get") (apply catalog_get (cons converter args))) + ) +) + +; Catalog composite + +(define (catalog_service op . args) (catalog_impl currency_service op args)) + +; Cart implementation + +(define (cart_post content item) + (cons (cons "Item" (list (uuid) item)) content) +) + +(define (cart_getall content) + (cons "Sample Feed" (cons (uuid) content)) +) + +(define (cart_getentry id) + (define entry (list (list 'name "Apple") (list 'currencyCode "USD") (list 'currencySymbol "$") (list 'price 2.99))) + (cons "Item" (list id entry)) +) + +(define (cart_gettotal) + 10.0 +) + +(define (cart_impl op args) + (cond + ((equal? op "post") (apply cart_post args)) + ((equal? op "getall") (apply cart_getall args)) + ((equal? op "getentry") (apply cart_getentry args)) + ((equal? op "gettotal") (apply cart_gettotal args)) + ) +) + +; Store UI implementation + +(define (storeui_post cart content item) + (cart "post" content item) +) + +(define (storeui_getcart cart content) + (cart "getall" content) +) + +(define (storeui_getentry cart id) + (cart "getentry" id) +) + +(define (storeui_getcatalog catalog) + (catalog "get") +) + +(define (storeui_gettotal cart) + (cart "gettotal") +) + +(define (storeui_impl cart catalog op args) + (cond + ((equal? op "post") (apply storeui_post (cons cart args))) + ((equal? op "getall") (apply storeui_getcart (cons cart args))) + ((equal? op "getentry") (apply storeui_getentry (cons cart args))) + ((equal? op "getcatalog") (apply storeui_getcatalog (cons catalog args))) + ((equal? op "gettotal") (apply storeui_gettotal (cons cart args))) + ) +) + +; Store UI composite + +(define (cart_service op . args) (cart_impl op args)) + +(define (storeui_service op . args) (storeui_impl cart_service catalog_service op args)) + +; Store UI test case + +(define catalog (storeui_service "getcatalog")) +(define empty (list)) +(define apple (car catalog)) +(define orange (car (cdr catalog))) +(define added1 (storeui_service "post" empty apple)) +(define added2 (storeui_service "post" added1 orange)) +(display (storeui_service "getall" added2)) +(display (storeui_service "gettotal")) + +; Store UI JSON-RPC interop test case + +(define (system.listMethods) (list "Service.get" "Service.getTotal")) + +(define (Service.get) (storeui_service "getcatalog")) + +(define (.get) (storeui_service "getcatalog")) + +(define (Service.getTotal) (storeui_service "gettotal")) + +; Store UI ATOMPub interop test case + +(define (getall) (storeui_service "getall" added2)) + +(define (get id) (storeui_service "getentry" id)) + +(define (post entry) + (display entry) + (uuid) +) + +(define (delete id) + (display id) + true +) + diff --git a/cpp/sca/test/store-script/store.composite b/cpp/sca/test/store-script/store.composite index 124adff853..cd34f81840 100644 --- a/cpp/sca/test/store-script/store.composite +++ b/cpp/sca/test/store-script/store.composite @@ -21,44 +21,49 @@ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1" targetNamespace="http://store" name="store"> - + - + + + - + - - - - - - - - - + + + + + + + + + - - - USD - - - - - - - - - - - - - - - + + + USD + + + + + + + + + + + + + + + - - - + + + + + + diff --git a/cpp/sca/test/store-script/store.scm b/cpp/sca/test/store-script/store.scm index 709fd943f1..2434b18b51 100644 --- a/cpp/sca/test/store-script/store.scm +++ b/cpp/sca/test/store-script/store.scm @@ -1,150 +1,31 @@ +; Store implementation -(; "Currency implementation") - -(define (currency_convert from to amount) - (if (equal? to "EUR") (* amount 0.70) amount) -) - -(define (currency_symbol currency) - (if (equal? currency "EUR") "E" "$") -) - -(define (currency_impl op args) - (cond - ((equal? op "convert") (apply currency_convert args)) - ((equal? op "symbol") (apply currency_symbol args)) - ) -) - -(; "Currency composite") - -(define (currency_service op . args) (currency_impl op args)) - -(; "Catalog implementation") - -(define (catalog_get converter) - (define (convert price) (converter "convert" "USD" "USD" price)) - - (define code "USD") - (define symbol (converter "symbol" code)) - - (list - (list (list 'javaClass "services.Item") (list 'name "Apple") (list 'currency code) (list 'symbol symbol) (list 'price 2.99)) - (list (list 'javaClass "services.Item") (list 'name "Orange") (list 'currency code) (list 'symbol symbol) (list 'price 3.55)) - (list (list 'javaClass "services.Item") (list 'name "Pear") (list 'currency code) (list 'symbol symbol) (list 'price 1.55)) - ) -) - -(define (catalog_impl converter op args) - (cond - ((equal? op "get") (apply catalog_get (cons converter args))) - ) -) - -(; "Catalog composite") - -(define (catalog_service op . args) (catalog_impl currency_service op args)) - -(; "Cart implementation") - -(define (cart_post content item) - (cons (cons "Item" (list (uuid) item)) content) -) - -(define (cart_getall content) - (cons "Sample Feed" (cons (uuid) content)) -) - -(define (cart_getentry id) - (define entry (list (list 'name "Apple") (list 'currency "USD") (list 'symbol "$") (list 'price 2.99))) - (cons "Item" (list id entry)) +(define (post item catalog shoppingCart shoppingTotal) + (shoppingCart "post" item) ) -(define (cart_gettotal) - 10.0 +(define (getall catalog shoppingCart shoppingTotal) + (shoppingCart "getall") ) -(define (cart_impl op args) - (cond - ((equal? op "post") (apply cart_post args)) - ((equal? op "getall") (apply cart_getall args)) - ((equal? op "getentry") (apply cart_getentry args)) - ((equal? op "gettotal") (apply cart_gettotal args)) - ) +(define (get id catalog shoppingCart shoppingTotal) + (shoppingCart "get" id) ) -(; "Store UI implementation") - -(define (storeui_post cart content item) - (cart "post" content item) -) - -(define (storeui_getcart cart content) - (cart "getall" content) -) - -(define (storeui_getentry cart id) - (cart "getentry" id) -) - -(define (storeui_getcatalog catalog) +(define (getcatalog catalog shoppingCart shoppingTotal) (catalog "get") ) -(define (storeui_gettotal cart) - (cart "gettotal") +(define (gettotal catalog shoppingCart shoppingTotal) + (shoppingCart "gettotal") ) -(define (storeui_impl cart catalog op args) - (cond - ((equal? op "post") (apply storeui_post (cons cart args))) - ((equal? op "getall") (apply storeui_getcart (cons cart args))) - ((equal? op "getentry") (apply storeui_getentry (cons cart args))) - ((equal? op "getcatalog") (apply storeui_getcatalog (cons catalog args))) - ((equal? op "gettotal") (apply storeui_gettotal (cons cart args))) - ) +(define (delete id catalog shoppingCart shoppingTotal) + (shoppingCart "delete" id) ) -(; "Store UI composite") - -(define (cart_service op . args) (cart_impl op args)) - -(define (storeui_service op . args) (storeui_impl cart_service catalog_service op args)) - -(; "Store UI test case") - -(define catalog (storeui_service "getcatalog")) -(define empty (list)) -(define apple (car catalog)) -(define orange (car (cdr catalog))) -(define added1 (storeui_service "post" empty apple)) -(define added2 (storeui_service "post" added1 orange)) -(display (storeui_service "getall" added2)) -(display (storeui_service "gettotal")) - -(; "Store UI JSON-RPC interop test case") - (define (system.listMethods) (list "Service.get" "Service.getTotal")) -(define (Service.get) (storeui_service "getcatalog")) - -(define (.get) (storeui_service "getcatalog")) - -(define (Service.getTotal) (storeui_service "gettotal")) - -(; "Store UI ATOMPub interop test case") - -(define (getall) (storeui_service "getall" added2)) - -(define (get id) (storeui_service "getentry" id)) - -(define (post entry) - (display entry) - (uuid) -) - -(define (delete . args) - (display args) - true -) +(define Service.getCatalog getcatalog) +(define Service.getTotal gettotal) -- cgit v1.2.3