From ff124040623879bc48a0ba5cf06a841642adef53 Mon Sep 17 00:00:00 2001 From: jsdelfino Date: Mon, 30 Nov 2009 08:36:32 +0000 Subject: Fixes to the http client, httpd modules and memcached component to get the store and shopping cart test case working end to end. git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@885349 13f79535-47bb-0310-9956-ffa450edef68 --- .../trunk/components/cache/mcache-client-test.cpp | 138 +++++++++++++++++ sca-cpp/trunk/components/cache/mcache-test.cpp | 10 +- sca-cpp/trunk/components/cache/mcache.composite | 2 +- sca-cpp/trunk/components/cache/mcache.cpp | 10 +- sca-cpp/trunk/components/cache/mcache.hpp | 47 ++++-- .../trunk/components/cache/memcached-server-test | 47 ++++++ sca-cpp/trunk/components/cache/memcached-test | 6 +- sca-cpp/trunk/modules/atom/atom.hpp | 14 +- sca-cpp/trunk/modules/http/curl-test.cpp | 6 +- sca-cpp/trunk/modules/http/curl.hpp | 166 ++++++++++++++------- sca-cpp/trunk/modules/http/http-test | 2 +- sca-cpp/trunk/modules/http/httpd-test | 2 +- sca-cpp/trunk/modules/http/httpd.hpp | 91 ++++------- sca-cpp/trunk/modules/server/client-test.cpp | 79 ++++++---- sca-cpp/trunk/modules/server/client-test.scm | 25 ++++ sca-cpp/trunk/modules/server/domain-test.composite | 49 ++++++ sca-cpp/trunk/modules/server/http-test | 43 ------ sca-cpp/trunk/modules/server/httpd-client.scm | 25 ---- sca-cpp/trunk/modules/server/httpd-test | 6 +- sca-cpp/trunk/modules/server/httpd-test.composite | 42 ------ sca-cpp/trunk/modules/server/httpd-test.scm | 29 ---- sca-cpp/trunk/modules/server/impl-test.cpp | 76 ++++++++++ sca-cpp/trunk/modules/server/mod-cpp.hpp | 20 +-- sca-cpp/trunk/modules/server/mod-eval.cpp | 104 +++++++------ sca-cpp/trunk/modules/server/mod-scm.hpp | 30 ++-- sca-cpp/trunk/modules/server/server-test | 50 +++++++ sca-cpp/trunk/modules/server/server-test.scm | 29 ++++ sca-cpp/trunk/modules/server/wiring-test | 14 +- .../trunk/test/store-script/currency-converter.scm | 1 + sca-cpp/trunk/test/store-script/fruits-catalog.scm | 4 +- sca-cpp/trunk/test/store-script/htdocs/store.html | 14 +- sca-cpp/trunk/test/store-script/shopping-cart.scm | 52 +++++-- .../trunk/test/store-script/store-composite-test | 87 +++++++++++ sca-cpp/trunk/test/store-script/store-http-test | 76 ---------- sca-cpp/trunk/test/store-script/store.composite | 10 ++ sca-cpp/trunk/test/store-script/store.scm | 6 +- 36 files changed, 927 insertions(+), 485 deletions(-) create mode 100644 sca-cpp/trunk/components/cache/mcache-client-test.cpp create mode 100755 sca-cpp/trunk/components/cache/memcached-server-test create mode 100644 sca-cpp/trunk/modules/server/client-test.scm create mode 100644 sca-cpp/trunk/modules/server/domain-test.composite delete mode 100755 sca-cpp/trunk/modules/server/http-test delete mode 100644 sca-cpp/trunk/modules/server/httpd-client.scm delete mode 100644 sca-cpp/trunk/modules/server/httpd-test.composite delete mode 100644 sca-cpp/trunk/modules/server/httpd-test.scm create mode 100644 sca-cpp/trunk/modules/server/impl-test.cpp create mode 100755 sca-cpp/trunk/modules/server/server-test create mode 100644 sca-cpp/trunk/modules/server/server-test.scm create mode 100755 sca-cpp/trunk/test/store-script/store-composite-test delete mode 100755 sca-cpp/trunk/test/store-script/store-http-test diff --git a/sca-cpp/trunk/components/cache/mcache-client-test.cpp b/sca-cpp/trunk/components/cache/mcache-client-test.cpp new file mode 100644 index 0000000000..0a560bc05f --- /dev/null +++ b/sca-cpp/trunk/components/cache/mcache-client-test.cpp @@ -0,0 +1,138 @@ +/* + * 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$ */ + +/** + * Test Memcached access functions. + */ + +#include +#include +#include +#include +#include + +#include "list.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "../../modules/http/curl.hpp" + +namespace tuscany { +namespace cache { + +const std::string url("http://localhost:8090/mcache"); + +bool testCache() { + http::CURLSession cs; + + const list i = list() + << (list() << "name" << std::string("Apple")) + << (list() << "price" << std::string("$2.99")); + const list a = mklist(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); + + const failable id = http::post(a, url, cs); + assert(hasContent(id)); + { + const failable val = http::get(url + "/" + std::string(content(id)), cs); + assert(hasContent(val)); + assert(content(val) == a); + } + + const list j = list() + << (list() << "name" << std::string("Apple")) + << (list() << "price" << std::string("$3.55")); + const list b = mklist(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), j); + + { + const failable r = http::put(b, url + "/" + std::string(content(id)), cs); + assert(hasContent(r)); + assert(content(r) == value(true)); + } + { + const failable val = http::get(url + "/" + std::string(content(id)), cs); + assert(hasContent(val)); + assert(content(val) == b); + } + { + const failable r = http::del(url + "/" + std::string(content(id)), cs); + assert(hasContent(r)); + assert(content(r) == value(true)); + } + { + const failable val = http::get(url + "/" + std::string(content(id)), cs); + assert(!hasContent(val)); + } + + return true; +} + +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; +} + +bool testGetLoop(const int count, const value& id, const value& entry, http::CURLSession& cs) { + if (count == 0) + return true; + const failable val = http::get(url + "/" + std::string(id), cs); + assert(hasContent(val)); + assert(content(val) == entry); + return testGetLoop(count - 1, id, entry, cs); +} + +bool testGetPerf() { + const int count = 50; + struct timeval start; + struct timeval end; + { + const list i = list() + << (list() << "name" << std::string("Apple")) + << (list() << "price" << std::string("$4.55")); + const list a = mklist(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); + + http::CURLSession cs; + const failable id = http::post(a, url, cs); + assert(hasContent(id)); + + testGetLoop(5, content(id), a, cs); + + gettimeofday(&start, NULL); + + testGetLoop(count, content(id), a, cs); + + gettimeofday(&end, NULL); + std::cout << "Cache get test " << duration(start, end, count) << " ms" << std::endl; + } + return true; +} + +} +} + +int main() { + std::cout << "Testing..." << std::endl; + + tuscany::cache::testCache(); + tuscany::cache::testGetPerf(); + + std::cout << "OK" << std::endl; + + return 0; +} diff --git a/sca-cpp/trunk/components/cache/mcache-test.cpp b/sca-cpp/trunk/components/cache/mcache-test.cpp index 3f02b649d6..78f0da0465 100644 --- a/sca-cpp/trunk/components/cache/mcache-test.cpp +++ b/sca-cpp/trunk/components/cache/mcache-test.cpp @@ -35,11 +35,11 @@ namespace tuscany { namespace cache { bool testMemCached() { - MemCached ch("localhost", 11311); + MemCached ch; - assert(hasContent(post("a", "AAA", ch))); + assert(hasContent(post("a", std::string("AAA"), ch))); assert(get("a", ch) == value(std::string("AAA"))); - assert(hasContent(put("a", "aaa", ch))); + assert(hasContent(put("a", std::string("aaa"), ch))); assert(get("a", ch) == value(std::string("aaa"))); assert(hasContent(del("a", ch))); assert(!hasContent(get("a", ch))); @@ -64,8 +64,8 @@ bool testGetPerf() { struct timeval start; struct timeval end; { - MemCached ch("localhost", 11311); - assert(hasContent(post("c", "CCC", ch))); + MemCached ch; + assert(hasContent(post("c", std::string("CCC"), ch))); testGetLoop(5, ch); diff --git a/sca-cpp/trunk/components/cache/mcache.composite b/sca-cpp/trunk/components/cache/mcache.composite index ccbced6fb4..99364104fb 100644 --- a/sca-cpp/trunk/components/cache/mcache.composite +++ b/sca-cpp/trunk/components/cache/mcache.composite @@ -23,7 +23,7 @@ name="mcache"> - + diff --git a/sca-cpp/trunk/components/cache/mcache.cpp b/sca-cpp/trunk/components/cache/mcache.cpp index af51f508e2..b60301bbf2 100644 --- a/sca-cpp/trunk/components/cache/mcache.cpp +++ b/sca-cpp/trunk/components/cache/mcache.cpp @@ -20,7 +20,7 @@ /* $Rev$ $Date$ */ /** - * memcached-based cache component implementation. + * Memcached-based cache component implementation. */ #include @@ -45,6 +45,9 @@ const failable get(const list& params) { return cache::get(car(params), ch); } +/** + * Post an item to the cache. + */ const value uuidValue() { apr_uuid_t uuid; apr_uuid_get(&uuid); @@ -53,9 +56,6 @@ const value uuidValue() { return value(std::string(buf, APR_UUID_FORMATTED_LENGTH)); } -/** - * Post an item to the cache. - */ const failable post(const list& params) { const value id = uuidValue(); const failable val = cache::post(id, car(params), ch); @@ -96,7 +96,7 @@ const tuscany::failable eval(const tuscany::value& return tuscany::cache::post(params); if (func == "put") return tuscany::cache::put(params); - if (func == "del") + if (func == "delete") return tuscany::cache::del(params); return tuscany::mkfailure(std::string("Function not supported: ") + std::string(func)); } diff --git a/sca-cpp/trunk/components/cache/mcache.hpp b/sca-cpp/trunk/components/cache/mcache.hpp index aae298d80d..d1fe182946 100644 --- a/sca-cpp/trunk/components/cache/mcache.hpp +++ b/sca-cpp/trunk/components/cache/mcache.hpp @@ -35,9 +35,12 @@ #include "apr_network_io.h" #include +#include #include "list.hpp" #include "value.hpp" #include "monad.hpp" +#include "debug.hpp" +#include "../../modules/eval/eval.hpp" namespace tuscany { namespace cache { @@ -77,7 +80,7 @@ private: */ const failable init(const std::string& host, const int port) { apr_memcache_server_t *server; - const apr_status_t sc = apr_memcache_server_create(pool, host.c_str(), port, 0, 1, 1, 60, &server); + const apr_status_t sc = apr_memcache_server_create(pool, host.c_str(), (apr_port_t)port, 0, 1, 1, 60, &server); if (sc != APR_SUCCESS) return mkfailure("Could not create server"); const apr_status_t as = apr_memcache_add_server(mc, server); @@ -92,21 +95,33 @@ private: * Post a new item to the cache. */ const failable post(const value& key, const value& val, const MemCached& cache) { - const std::string v(val); - const apr_status_t rc = apr_memcache_add(cache.mc, std::string(key).c_str(), const_cast(v.c_str()), v.size(), 0, 27); + debug(key, "cache::post::key"); + debug(val, "cache::post::value"); + + const std::string ks(eval::writeValue(key)); + const std::string vs(eval::writeValue(val)); + const apr_status_t rc = apr_memcache_add(cache.mc, ks.c_str(), const_cast(vs.c_str()), vs.size(), 0, 27); if (rc != APR_SUCCESS) return mkfailure("Could not add entry"); + + debug(true, "cache::post::result"); return true; } /** - * Update an item in the cache. + * Update an item in the cache. If the item doesn't exist it is added. */ const failable put(const value& key, const value& val, const MemCached& cache) { - const std::string v(val); - const apr_status_t rc = apr_memcache_replace(cache.mc, std::string(key).c_str(), const_cast(v.c_str()), v.size(), 0, 27); + debug(key, "cache::put::key"); + debug(val, "cache::put::value"); + + const std::string ks(eval::writeValue(key)); + const std::string vs(eval::writeValue(val)); + const apr_status_t rc = apr_memcache_set(cache.mc, ks.c_str(), const_cast(vs.c_str()), vs.size(), 0, 27); if (rc != APR_SUCCESS) return mkfailure("Could not add entry"); + + debug(true, "cache::put::result"); return true; } @@ -114,6 +129,10 @@ const failable put(const value& key, const value& val, const * Get an item from the cache. */ const failable get(const value& key, const MemCached& cache) { + debug(key, "cache::get::key"); + + const std::string ks(eval::writeValue(key)); + apr_pool_t* vpool; const apr_status_t pc = apr_pool_create(&vpool, cache.pool); if (pc != APR_SUCCESS) @@ -121,14 +140,16 @@ const failable get(const value& key, const MemCached& cache) char *data; apr_size_t size; - const apr_status_t rc = apr_memcache_getp(cache.mc, cache.pool, std::string(key).c_str(), &data, &size, NULL); + const apr_status_t rc = apr_memcache_getp(cache.mc, cache.pool, ks.c_str(), &data, &size, NULL); if (rc != APR_SUCCESS) { apr_pool_destroy(vpool); return mkfailure("Could not get entry"); } - const value val(std::string(data, size)); + const value val(eval::readValue(std::string(data, size))); apr_pool_destroy(vpool); + + debug(val, "cache::get::result"); return val; } @@ -136,9 +157,15 @@ const failable get(const value& key, const MemCached& cache) * Delete an item from the cache */ const failable del(const value& key, const MemCached& cache) { - const apr_status_t rc = apr_memcache_delete(cache.mc, std::string(key).c_str(), 0); + debug(key, "cache::delete::key"); + + std::ostringstream kos; + kos << key; + const apr_status_t rc = apr_memcache_delete(cache.mc, kos.str().c_str(), 0); if (rc != APR_SUCCESS) - return mkfailure("Could not add entry"); + return mkfailure("Could not delete entry"); + + debug(true, "cache::delete::result"); return true; } diff --git a/sca-cpp/trunk/components/cache/memcached-server-test b/sca-cpp/trunk/components/cache/memcached-server-test new file mode 100755 index 0000000000..2da53fbdb4 --- /dev/null +++ b/sca-cpp/trunk/components/cache/memcached-server-test @@ -0,0 +1,47 @@ +#!/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. + +# Setup +../../modules/http/httpd-conf tmp 8090 ../../modules/http/htdocs +../../modules/server/server-conf tmp +cat >>tmp/conf/httpd.conf < +SetHandler mod_tuscany_eval +SCAContribution `pwd`/ +SCAComposite mcache.composite +SCAComponent mcache + +EOF + +apachectl -k start -d `pwd`/tmp + +mc="memcached -l 127.0.0.1 -m 4 -p 11211" +$mc & +sleep 1 + +# Test +./mcache-client-test +rc=$? + +# Cleanup +kill `ps -f | grep -v grep | grep "$mc" | awk '{ print $2 }'` +apachectl -k stop -d `pwd`/tmp +sleep 1 +return $rc diff --git a/sca-cpp/trunk/components/cache/memcached-test b/sca-cpp/trunk/components/cache/memcached-test index 6ca779cce5..df21d32a57 100755 --- a/sca-cpp/trunk/components/cache/memcached-test +++ b/sca-cpp/trunk/components/cache/memcached-test @@ -18,8 +18,8 @@ # under the License. # Setup -cmd="memcached -l 127.0.0.1 -m 4 -p 11311" -$cmd & +mc="memcached -l 127.0.0.1 -m 4 -p 11211" +$mc & sleep 1 # Test @@ -27,5 +27,5 @@ sleep 1 rc=$? # Cleanup -ps -f | grep -v grep | grep "$cmd" | awk '{ print $2 }' | xargs kill +kill `ps -f | grep -v grep | grep "$mc" | awk '{ print $2 }'` return $rc diff --git a/sca-cpp/trunk/modules/atom/atom.hpp b/sca-cpp/trunk/modules/atom/atom.hpp index ac19b93e38..22489fd1d1 100644 --- a/sca-cpp/trunk/modules/atom/atom.hpp +++ b/sca-cpp/trunk/modules/atom/atom.hpp @@ -38,7 +38,7 @@ namespace atom { /** * Convert a list of elements to a list of values representing an ATOM entry. */ -const list entryValue(const list& e) { +const list entryValues(const list& e) { const list lt = filter(selector(mklist(element, "title")), e); const value t = isNil(lt)? value(std::string("")) : elementValue(car(lt)); const list li = filter(selector(mklist(element, "id")), e); @@ -53,7 +53,7 @@ const list entryValue(const list& e) { const list entriesValues(const list& e) { if (isNil(e)) return e; - return cons(entryValue(car(e)), entriesValues(cdr(e))); + return cons(entryValues(car(e)), entriesValues(cdr(e))); } /** @@ -63,7 +63,15 @@ const failable, std::string> readEntry(const list& ilis const list e = readXML(ilist); if (isNil(e)) return mkfailure, std::string>("Empty entry"); - return entryValue(car(e)); + return entryValues(car(e)); +} + +/** + * Convert a list of values representing an ATOM entry to a value. + */ +const value entryValue(const list& e) { + const list v = elementsToValues(mklist(caddr(e))); + return cons(car(e), mklist(cadr(e), cdr(car(v)))); } /** diff --git a/sca-cpp/trunk/modules/http/curl-test.cpp b/sca-cpp/trunk/modules/http/curl-test.cpp index 0a6fbcd8a6..59944546a1 100644 --- a/sca-cpp/trunk/modules/http/curl-test.cpp +++ b/sca-cpp/trunk/modules/http/curl-test.cpp @@ -50,7 +50,7 @@ std::ostringstream* curlWriter(const std::string& s, std::ostringstream* os) { } const bool testGet() { - CURLHandle ch; + CURLSession ch; { std::ostringstream os; const failable, std::string> r = get(curlWriter, &os, "http://localhost:8090", ch); @@ -66,7 +66,7 @@ const bool testGet() { return true; } -const bool testGetLoop(const int count, CURLHandle& ch) { +const bool testGetLoop(const int count, CURLSession& ch) { if (count == 0) return true; const failable r = get("http://localhost:8090", ch); @@ -77,7 +77,7 @@ const bool testGetLoop(const int count, CURLHandle& ch) { const bool testGetPerf() { const int count = 50; - CURLHandle ch; + CURLSession ch; struct timeval start; struct timeval end; { diff --git a/sca-cpp/trunk/modules/http/curl.hpp b/sca-cpp/trunk/modules/http/curl.hpp index 6c3a3a47dc..fd2e9857d1 100644 --- a/sca-cpp/trunk/modules/http/curl.hpp +++ b/sca-cpp/trunk/modules/http/curl.hpp @@ -30,6 +30,7 @@ #include #include #include +#include "gc.hpp" #include "list.hpp" #include "value.hpp" #include "element.hpp" @@ -40,11 +41,6 @@ namespace tuscany { namespace http { -/** - * Set to true to log HTTP content. - */ -bool logContent = false; - /** * CURL library context, one per process. */ @@ -63,21 +59,44 @@ CURLContext curlContext; /** * Represents a CURL session handle. */ -class CURLHandle { +class CURLSession { public: - CURLHandle() : h(curl_easy_init()) { + CURLSession() : ch(new CURLHandle()) { } - ~CURLHandle() { - curl_easy_cleanup(h); + + ~CURLSession() { } - operator CURL*() const { - return h; + CURLSession(const CURLSession& c) : ch(c.ch) { } + private: - CURL* h; + class CURLHandle { + public: + CURLHandle() : h(curl_easy_init()) { + } + ~CURLHandle() { + curl_easy_cleanup(h); + h = NULL; + } + private: + CURL* h; + + friend CURL* handle(const CURLSession& c); + }; + + const gc_ptr ch; + + friend CURL* handle(const CURLSession& c); }; +/** + * Returns the CURL handle used by a CURL session. + */ +CURL* handle(const CURLSession& c) { + return c.ch->h; +} + /** * Context passed to the read callback function. */ @@ -143,9 +162,10 @@ curl_slist* headers(curl_slist* cl, const list& h) { return headers(curl_slist_append(cl, std::string(car(h)).c_str()), cdr(h)); } -template const failable, std::string> apply(const list >& req, const lambda& reduce, const R& initial, const std::string& url, const std::string& verb, const CURLHandle& ch) { +template const failable, std::string> apply(const list >& req, const lambda& reduce, const R& initial, const std::string& url, const std::string& verb, const CURLSession& cs) { // Init the curl session + CURL* ch = handle(cs); curl_easy_reset(ch); curl_easy_setopt(ch, CURLOPT_USERAGENT, "libcurl/1.0"); @@ -204,7 +224,9 @@ template const failable, std::string> apply(const list evalExpr(const value& expr, const std::string& url, const CURLHandle& ch) { +const failable evalExpr(const value& expr, const std::string& url, const CURLSession& ch) { + debug(url, "http::evalExpr::url"); + debug(expr, "http::evalExpr::input"); // Convert expression to a JSON-RPC request json::JSONContext cx; @@ -212,13 +234,6 @@ const failable evalExpr(const value& expr, const std::string if (!hasContent(jsreq)) return mkfailure(reason(jsreq)); - if (logContent) { - std::cout<< "content: " << std::endl; - write(content(jsreq), std::cout); - std::cout<< std::endl; - std::cout.flush(); - } - // POST it to the URL const list h = mklist("Content-Type: application/json-rpc"); const failable >, std::string> res = apply >(mklist >(h, content(jsreq)), rcons, list(), url, "POST", ch); @@ -226,19 +241,58 @@ const failable evalExpr(const value& expr, const std::string return mkfailure(reason(res)); // Return result - if (logContent) { - std::cout << "content:" << std::endl; - write(cadr >(content(res)), std::cout); - std::cout << std::endl; - } - const list val = elementsToValues(content(json::readJSON(cadr >(content(res)), cx))); - return cadr(cadr(val)); + failable, std::string> jsres = json::readJSON(cadr >(content(res)), cx); + if (!hasContent(jsres)) + return mkfailure(reason(jsres)); + const list val = elementsToValues(content(jsres)); + + const value rval(cadr(cadr(val))); + debug(rval, "http::evalExpr::result"); + return rval; +} + +/** + * Find and return a header. + */ +const failable header(const std::string& prefix, const list& h) { + if (isNil(h)) + return mkfailure(std::string("Couldn't find header: ") + prefix); + const std::string s = car(h); + if (s.find(prefix) != 0) + return header(prefix, cdr(h)); + const std::string l(s.substr(prefix.length())); + return l.substr(0, l.find_first_of("\r\n")); +} + +/** + * Find and return a location header. + */ +const failable location(const list& h) { + return header("Location: ", h); +} + +/** + * Convert a location to an entry id. + */ +const failable entryId(const failable l) { + if (!hasContent(l)) + return mkfailure(reason(l)); + const std::string ls(content(l)); + return value(ls.substr(ls.find_last_of("/") + 1)); +} + +/** + * Find and return a content-type header. + */ +const failable contentType(const list& h) { + return header("Content-Type: ", h); } /** * HTTP GET, return the resource at the given URL. */ -template const failable, std::string> get(const lambda& reduce, const R& initial, const std::string& url, const CURLHandle& ch) { +template const failable, std::string> get(const lambda& reduce, const R& initial, const std::string& url, const CURLSession& ch) { + debug(url, "http::get::url"); const list > req = mklist(list(), list()); return apply(req, reduce, initial, url, "GET", ch); } @@ -246,38 +300,41 @@ template const failable, std::string> get(const lambda get(const std::string& url, const CURLHandle& ch) { +const failable get(const std::string& url, const CURLSession& ch) { + debug(url, "http::get::url"); // Get the contents of the resource at the given URL const failable >, std::string> res = get >(rcons, list(), url, ch); if (!hasContent(res)) return mkfailure(reason(res)); + const list ls(reverse(cadr(content(res)))); - const std::string ct; - if (ct.find("application/atom+xml") != std::string::npos) { - // TODO Return an ATOM feed + const std::string ct(content(contentType(car(content(res))))); + if (ct == "application/atom+xml;type=entry") { + const value val(atom::entryValue(content(atom::readEntry(ls)))); + debug(val, "http::get::result"); + return val; } // Return the content as a string value std::ostringstream os; - write(reverse(cadr(content(res))), os); - return value(os.str()); + write(ls, os); + const value val(os.str()); + debug(val, "http::get::result"); + return val; } /** * HTTP POST. */ -const failable post(const value& val, const std::string& url, const CURLHandle& ch) { +const failable post(const value& val, const std::string& url, const CURLSession& ch) { // Convert value to an ATOM entry const failable, std::string> entry = atom::writeATOMEntry(atom::entryValuesToElements(val)); if (!hasContent(entry)) return mkfailure(reason(entry)); - if (logContent) { - std::cout << "content:" << std::endl; - write(list(content(entry)), std::cout); - std::cout << std::endl; - } + debug(url, "http::post::url"); + debug(content(entry), "http::post::input"); // POST it to the URL const list h = mklist("Content-Type: application/atom+xml"); @@ -285,23 +342,24 @@ const failable post(const value& val, const std::string& url const failable >, std::string> res = apply >(req, rcons, list(), url, "POST", ch); if (!hasContent(res)) return mkfailure(reason(res)); - return value(true); + + // Return the new entry id from the HTTP location header + const failable eid(entryId(location(car(content(res))))); + debug(eid, "http::post::result"); + return eid; } /** * HTTP PUT. */ -const failable put(const value& val, const std::string& url, const CURLHandle& ch) { +const failable put(const value& val, const std::string& url, const CURLSession& ch) { // Convert value to an ATOM entry const failable, std::string> entry = atom::writeATOMEntry(atom::entryValuesToElements(val)); if (!hasContent(entry)) return mkfailure(reason(entry)); - if (logContent) { - std::cout << "content:" << std::endl; - write(list(content(entry)), std::cout); - std::cout << std::endl; - } + debug(url, "http::put::url"); + debug(content(entry), "http::put::input"); // PUT it to the URL const list h = mklist("Content-Type: application/atom+xml"); @@ -309,17 +367,23 @@ const failable put(const value& val, const std::string& url, const failable >, std::string> res = apply >(req, rcons, list(), url, "PUT", ch); if (!hasContent(res)) return mkfailure(reason(res)); + + debug(true, "http::put::result"); return value(true); } /** * HTTP DELETE. */ -const failable del(const std::string& url, const CURLHandle& ch) { +const failable del(const std::string& url, const CURLSession& ch) { + debug(url, "http::delete::url"); + const list > req = mklist(list(), list()); const failable >, std::string> res = apply >(req, rcons, list(), url, "DELETE", ch); if (!hasContent(res)) return mkfailure(reason(res)); + + debug(true, "http::delete::result"); return value(true); } @@ -327,7 +391,7 @@ const failable del(const std::string& url, const CURLHandle& * HTTP client proxy function. */ struct proxy { - proxy(const std::string& url, const CURLHandle& ch) : url(url), ch(ch) { + proxy(const std::string& url, const CURLSession& ch) : url(url), ch(ch) { } const value operator()(const list& args) const { @@ -338,7 +402,7 @@ struct proxy { } const std::string url; - const CURLHandle& ch; + const CURLSession ch; }; } diff --git a/sca-cpp/trunk/modules/http/http-test b/sca-cpp/trunk/modules/http/http-test index 1ab0da64b9..0aaaec48df 100755 --- a/sca-cpp/trunk/modules/http/http-test +++ b/sca-cpp/trunk/modules/http/http-test @@ -28,5 +28,5 @@ rc=$? # Cleanup apachectl -k stop -d `pwd`/tmp -sleep 2 +sleep 1 return $rc diff --git a/sca-cpp/trunk/modules/http/httpd-test b/sca-cpp/trunk/modules/http/httpd-test index 57c35c5cc9..8e1d681d84 100755 --- a/sca-cpp/trunk/modules/http/httpd-test +++ b/sca-cpp/trunk/modules/http/httpd-test @@ -31,7 +31,7 @@ rc=$? # Cleanup apachectl -k stop -d `pwd`/tmp -sleep 2 +sleep 1 if [ "$rc" = "0" ]; then echo "OK" fi diff --git a/sca-cpp/trunk/modules/http/httpd.hpp b/sca-cpp/trunk/modules/http/httpd.hpp index a9ced05208..05b959f1d2 100644 --- a/sca-cpp/trunk/modules/http/httpd.hpp +++ b/sca-cpp/trunk/modules/http/httpd.hpp @@ -50,17 +50,12 @@ #include "list.hpp" #include "value.hpp" +#include "debug.hpp" namespace tuscany { namespace httpd { -/** - * Set to true to log requests and content. - */ -bool logRequests = false; -bool logContent = false; - /** * Returns a server-scoped module configuration. */ @@ -120,7 +115,7 @@ const std::string path(const list& p) { */ const char* optional(const char* s) { if (s == NULL) - return "(null)"; + return ""; return s; } @@ -128,59 +123,40 @@ const std::string contentType(const request_rec* r) { return optional(apr_table_get(r->headers_in, "Content-Type")); } +#ifdef _DEBUG + /** - * Log HTTP request info. + * Debug log. */ -int logHeader(void* r, const char* key, const char* value) { - std::cout << "header key: " << key << ", value: " << value << std::endl; +int debugHeader(unused void* r, const char* key, const char* value) { + std::cerr << " header key: " << key << ", value: " << value << std::endl; return 1; } -const bool logRequest(request_rec* r, const std::string& msg) { - if (!logRequests) - return true; - 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(); +const bool debugRequest(request_rec* r, const std::string& msg) { + std::cerr << msg << ":" << std::endl; + std::cerr << " protocol: " << optional(r->protocol) << std::endl; + std::cerr << " method: " << optional(r->method) << std::endl; + std::cerr << " method number: " << r->method_number << std::endl; + std::cerr << " content type: " << contentType(r) << std::endl; + std::cerr << " content encoding: " << optional(r->content_encoding) << std::endl; + apr_table_do(debugHeader, r, r->headers_in, NULL); + std::cerr << " uri: " << optional(r->unparsed_uri) << std::endl; + std::cerr << " path: " << optional(r->uri) << std::endl; + std::cerr << " path info: " << optional(r->path_info) << std::endl; + std::cerr << " filename: " << optional(r->filename) << std::endl; + std::cerr << " path tokens: " << pathTokens(r->uri) << std::endl; + std::cerr << " args: " << optional(r->args) << std::endl; return true; } -const bool logValue(const value& v, const std::string& msg) { - if (!logContent) - return true; - std::cout<< msg << ": " << v << std::endl; - std::cout.flush(); - return true; -} +#define httpdDebugRequest(r, msg) httpd::debugRequest(r, msg) -const bool logValue(const failable& v, const std::string& msg) { - if (!logContent) - return true; - std::cout<< msg << ": " << v << std::endl; - std::cout.flush(); - return true; -} +#else -const bool logStrings(const list& ls, const std::string& msg) { - if (!logContent) - return true; - std::cout<< msg << ": " << std::endl; - write(ls, std::cout); - std::cout<< std::endl; - std::cout.flush(); - return true; -} +#define httpdDebugRequest(r, msg) + +#endif /** * Returns a list of key value pairs from the args in a query string. @@ -255,14 +231,6 @@ const char* url(const value& v, request_rec* r) { 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)))); -} - /** * Write an HTTP result. */ @@ -271,10 +239,7 @@ const failable writeResult(const failable, s return mkfailure(reason(ls)); std::ostringstream os; write(content(ls), os); - if (logContent) { - std::cout<< "content: " << std::endl << os.str() << std::endl; - std::cout.flush(); - } + debug(os.str(), "httpd::result"); 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"); @@ -283,7 +248,7 @@ const failable writeResult(const failable, s r->status = HTTP_NOT_MODIFIED; return OK; } - ap_set_content_type(r, ct.c_str()); + ap_set_content_type(r, apr_pstrdup(r->pool, ct.c_str())); ap_rputs(std::string(os.str()).c_str(), r); return OK; } diff --git a/sca-cpp/trunk/modules/server/client-test.cpp b/sca-cpp/trunk/modules/server/client-test.cpp index b43cb92c52..5888c481e3 100644 --- a/sca-cpp/trunk/modules/server/client-test.cpp +++ b/sca-cpp/trunk/modules/server/client-test.cpp @@ -50,7 +50,7 @@ std::ostringstream* curlWriter(const std::string& s, std::ostringstream* os) { } const bool testGet() { - http::CURLHandle ch; + http::CURLSession ch; { std::ostringstream os; const failable, std::string> r = http::get(curlWriter, &os, "http://localhost:8090", ch); @@ -66,7 +66,7 @@ const bool testGet() { return true; } -const bool testGetLoop(const int count, http::CURLHandle& ch) { +const bool testGetLoop(const int count, http::CURLSession& ch) { if (count == 0) return true; const failable r = get("http://localhost:8090", ch); @@ -77,16 +77,13 @@ const bool testGetLoop(const int count, http::CURLHandle& ch) { const bool testGetPerf() { const int count = 50; - http::CURLHandle ch; + http::CURLSession 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; } @@ -94,13 +91,13 @@ const bool testGetPerf() { } const bool testEval() { - http::CURLHandle ch; + http::CURLSession ch; const value val = content(http::evalExpr(mklist(std::string("echo"), std::string("Hello")), "http://localhost:8090/test", ch)); assert(val == std::string("Hello")); return true; } -const bool testEvalLoop(const int count, http::CURLHandle& ch) { +const bool testEvalLoop(const int count, http::CURLSession& ch) { if (count == 0) return true; const value val = content(http::evalExpr(mklist(std::string("echo"), std::string("Hello")), "http://localhost:8090/test", ch)); @@ -111,7 +108,7 @@ const bool testEvalLoop(const int count, http::CURLHandle& ch) { const value blob(std::string(3000, 'A')); const list blobs = mklist(blob, blob, blob, blob, blob); -const bool testBlobEvalLoop(const int count, http::CURLHandle& ch) { +const bool testBlobEvalLoop(const int count, http::CURLSession& ch) { if (count == 0) return true; const value val = content(http::evalExpr(mklist(std::string("echo"), blobs), "http://localhost:8090/test", ch)); @@ -121,26 +118,20 @@ const bool testBlobEvalLoop(const int count, http::CURLHandle& ch) { const bool testEvalPerf() { const int count = 50; - http::CURLHandle ch; + http::CURLSession ch; struct timeval start; struct timeval end; { testEvalLoop(5, ch); - gettimeofday(&start, NULL); - testEvalLoop(count, ch); - gettimeofday(&end, NULL); std::cout << "JSON-RPC eval echo test " << duration(start, end, count) << " ms" << std::endl; } { testBlobEvalLoop(5, ch); - gettimeofday(&start, NULL); - testBlobEvalLoop(count, ch); - gettimeofday(&end, NULL); std::cout << "JSON-RPC eval blob test " << duration(start, end, count) << " ms" << std::endl; } @@ -156,23 +147,23 @@ bool testPost() { << (list() << "name" << std::string("Apple")) << (list() << "price" << std::string("$2.99")); const list a = mklist(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); - http::CURLHandle ch; - value rc = content(http::post(a, "http://localhost:8090/test", ch)); - assert(rc == value(true)); + http::CURLSession ch; + const failable id = http::post(a, "http://localhost:8090/test", ch); + assert(hasContent(id)); return true; } -const bool testPostLoop(const int count, const value& val, http::CURLHandle& ch) { +const bool testPostLoop(const int count, const value& val, http::CURLSession& ch) { if (count == 0) return true; - const value rc = content(http::post(val, "http://localhost:8090/test", ch)); - assert(rc == value(true)); + const failable id = http::post(val, "http://localhost:8090/test", ch); + assert(hasContent(id)); return testPostLoop(count - 1, val, ch); } const bool testPostPerf() { const int count = 50; - http::CURLHandle ch; + http::CURLSession ch; struct timeval start; struct timeval end; { @@ -181,11 +172,8 @@ const bool testPostPerf() { << (list() << "price" << std::string("$2.99")); const list val = mklist(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); testPostLoop(5, val, ch); - gettimeofday(&start, NULL); - testPostLoop(count, val, ch); - gettimeofday(&end, NULL); std::cout << "ATOMPub POST small test " << duration(start, end, count) << " ms" << std::endl; } @@ -200,11 +188,8 @@ const bool testPostPerf() { << (list() << "price" << std::string("$2.99")); const list val = mklist(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); testPostLoop(5, val, ch); - gettimeofday(&start, NULL); - testPostLoop(count, val, ch); - gettimeofday(&end, NULL); std::cout << "ATOMPub POST blob test " << duration(start, end, count) << " ms" << std::endl; } @@ -216,19 +201,49 @@ const bool testPut() { << (list() << "name" << std::string("Apple")) << (list() << "price" << std::string("$2.99")); const list a = mklist(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); - http::CURLHandle ch; + http::CURLSession ch; value rc = content(http::put(a, "http://localhost:8090/test/111", ch)); assert(rc == value(true)); return true; } const bool testDel() { - http::CURLHandle ch; + http::CURLSession ch; value rc = content(http::del("http://localhost:8090/test/123456789", ch)); assert(rc == value(true)); return true; } +const bool testEvalCpp() { + http::CURLSession ch; + const value val = content(http::evalExpr(mklist(std::string("hello"), std::string("world")), "http://localhost:8090/cpp", ch)); + assert(val == std::string("hello world")); + return true; +} + +const bool testEvalCppLoop(const int count, http::CURLSession& ch) { + if (count == 0) + return true; + const value val = content(http::evalExpr(mklist(std::string("hello"), std::string("world")), "http://localhost:8090/cpp", ch)); + assert(val == std::string("hello world")); + return testEvalCppLoop(count - 1, ch); +} + +const bool testEvalCppPerf() { + const int count = 50; + http::CURLSession ch; + struct timeval start; + struct timeval end; + { + testEvalCppLoop(5, ch); + gettimeofday(&start, NULL); + testEvalCppLoop(count, ch); + gettimeofday(&end, NULL); + std::cout << "JSON-RPC C++ eval test " << duration(start, end, count) << " ms" << std::endl; + } + return true; +} + } } @@ -244,6 +259,8 @@ int main() { tuscany::server::testFeed(); tuscany::server::testPut(); tuscany::server::testDel(); + tuscany::server::testEvalCpp(); + tuscany::server::testEvalCppPerf(); std::cout << "OK" << std::endl; diff --git a/sca-cpp/trunk/modules/server/client-test.scm b/sca-cpp/trunk/modules/server/client-test.scm new file mode 100644 index 0000000000..12275693f4 --- /dev/null +++ b/sca-cpp/trunk/modules/server/client-test.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/sca-cpp/trunk/modules/server/domain-test.composite b/sca-cpp/trunk/modules/server/domain-test.composite new file mode 100644 index 0000000000..48570e64d3 --- /dev/null +++ b/sca-cpp/trunk/modules/server/domain-test.composite @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sca-cpp/trunk/modules/server/http-test b/sca-cpp/trunk/modules/server/http-test deleted file mode 100755 index 6d23911c31..0000000000 --- a/sca-cpp/trunk/modules/server/http-test +++ /dev/null @@ -1,43 +0,0 @@ -#!/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. - -# Setup -../http/httpd-conf tmp 8090 htdocs -./server-conf tmp -cat >>tmp/conf/httpd.conf < -SetHandler mod_tuscany_eval -SCAContribution `pwd`/ -SCAComposite httpd-test.composite -SCAComponent httpd-test - -EOF - -apachectl -k start -d `pwd`/tmp -sleep 1 - -# Test -./client-test -rc=$? - -# Cleanup -apachectl -k stop -d `pwd`/tmp -sleep 2 -return $rc diff --git a/sca-cpp/trunk/modules/server/httpd-client.scm b/sca-cpp/trunk/modules/server/httpd-client.scm deleted file mode 100644 index 12275693f4..0000000000 --- a/sca-cpp/trunk/modules/server/httpd-client.scm +++ /dev/null @@ -1,25 +0,0 @@ -; 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/sca-cpp/trunk/modules/server/httpd-test b/sca-cpp/trunk/modules/server/httpd-test index 7fa2112f75..2f50c9f3c3 100755 --- a/sca-cpp/trunk/modules/server/httpd-test +++ b/sca-cpp/trunk/modules/server/httpd-test @@ -27,8 +27,8 @@ cat >>tmp/conf/httpd.conf < SetHandler mod_tuscany_eval SCAContribution `pwd`/ -SCAComposite httpd-test.composite -SCAComponent httpd-test +SCAComposite domain-test.composite +SCAComponent server-test EOF @@ -73,7 +73,7 @@ fi # Cleanup apachectl -k stop -d `pwd`/tmp -sleep 2 +sleep 1 if [ "$rc" = "0" ]; then echo "OK" fi diff --git a/sca-cpp/trunk/modules/server/httpd-test.composite b/sca-cpp/trunk/modules/server/httpd-test.composite deleted file mode 100644 index 875d26ae1b..0000000000 --- a/sca-cpp/trunk/modules/server/httpd-test.composite +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/sca-cpp/trunk/modules/server/httpd-test.scm b/sca-cpp/trunk/modules/server/httpd-test.scm deleted file mode 100644 index 0566eaf36f..0000000000 --- a/sca-cpp/trunk/modules/server/httpd-test.scm +++ /dev/null @@ -1,29 +0,0 @@ -; JSON-RPC test case - -(define (echo x) x) - -; ATOMPub test case - -(define (getall) - '("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)))) -) - -(define (get id) - (define entry '((javaClass "services.Item") (name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99))) - (cons "Item" (list id entry)) -) - -(define (post entry) - "123456789" -) - -(define (put id entry) - true -) - -(define (delete id) - true -) diff --git a/sca-cpp/trunk/modules/server/impl-test.cpp b/sca-cpp/trunk/modules/server/impl-test.cpp new file mode 100644 index 0000000000..217c6f5171 --- /dev/null +++ b/sca-cpp/trunk/modules/server/impl-test.cpp @@ -0,0 +1,76 @@ +/* + * 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$ */ + +/** + * Test component implementation. + */ + +#include + +#include "function.hpp" +#include "list.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "debug.hpp" + +namespace tuscany { +namespace server { + +const failable get(unused const list& params) { + return value(std::string("Hey")); +} + +const failable post(unused const list& params) { + return value(std::string("1234")); +} + +const failable put(unused const list& params) { + return value(true); +} + +const failable del(unused const list& params) { + return value(true); +} + +const failable hello(const list& params) { + return value("hello " + std::string(car(params))); +} + +} +} + +extern "C" { + +const tuscany::failable eval(const tuscany::value& func, const tuscany::list& params) { + if (func == "get") + return tuscany::server::get(params); + if (func == "post") + return tuscany::server::post(params); + if (func == "put") + return tuscany::server::put(params); + if (func == "del") + return tuscany::server::del(params); + if (func == "hello") + return tuscany::server::hello(params); + return tuscany::mkfailure(std::string("Function not supported: ") + std::string(func)); +} + +} diff --git a/sca-cpp/trunk/modules/server/mod-cpp.hpp b/sca-cpp/trunk/modules/server/mod-cpp.hpp index cb24b76f6c..99b69a0d9f 100644 --- a/sca-cpp/trunk/modules/server/mod-cpp.hpp +++ b/sca-cpp/trunk/modules/server/mod-cpp.hpp @@ -34,6 +34,7 @@ #include "function.hpp" #include "list.hpp" #include "value.hpp" +#include "debug.hpp" #include "monad.hpp" #include "dynlib.hpp" #include "cache.hpp" @@ -52,12 +53,13 @@ namespace cpp { struct evalImplementation { const lib ilib; const ilambda impl; - evalImplementation(const lib& ilib, const ilambda& impl) : ilib(ilib), impl(impl) { + const list px; + evalImplementation(const lib& ilib, const ilambda& impl, const list& px) : ilib(ilib), impl(impl), px(px) { } const failable operator()(const value& func, const list& params) const { - httpd::logValue(cons(func, params), "expr"); - const failable val = impl(func, params); - httpd::logValue(content(val), "val"); + debug(cons(func, params), "modeval::cpp::evalImplementation::input"); + const failable val = impl(func, append(params, px)); + debug(content(val), "modeval::cpp::evalImplementation::result"); return val; } }; @@ -65,7 +67,7 @@ struct evalImplementation { /** * Read a C++ component implementation. */ -const failable readLatestImplementation(const std::string path) { +const failable readLatestImplementation(const std::string path, const list& px) { const failable ilib(dynlib(path)); if (!hasContent(ilib)) return mkfailure(reason(ilib)); @@ -73,14 +75,14 @@ const failable readLatestImplementation(const std::string const failable impl(dynlambda(value, list)>("eval", content(ilib))); if (!hasContent(impl)) return impl; - return ilambda(evalImplementation(content(ilib), content(impl))); + return ilambda(evalImplementation(content(ilib), content(impl), px)); } -const cached > readImplementation(const std::string& path) { - const lambda(std::string)> ri(readLatestImplementation); +const cached > readImplementation(const std::string& path, const list& px) { + const lambda(std::string, list)> ri(readLatestImplementation); const lambda ft(latestFileTime); const std::string p(path + dynlibExt); - return cached >(curry(ri, p), curry(ft, p)); + return cached >(curry(ri, p, px), curry(ft, p)); } } diff --git a/sca-cpp/trunk/modules/server/mod-eval.cpp b/sca-cpp/trunk/modules/server/mod-eval.cpp index f843b9bdc5..aa0a6067a1 100644 --- a/sca-cpp/trunk/modules/server/mod-eval.cpp +++ b/sca-cpp/trunk/modules/server/mod-eval.cpp @@ -57,10 +57,11 @@ namespace modeval { */ class ServerConf { public: - ServerConf(server_rec* s) : s(s), home("") { + ServerConf(server_rec* s) : s(s), home(""), wiringHost("") { } - server_rec* s; + const server_rec* s; std::string home; + std::string wiringHost; }; /** @@ -77,7 +78,7 @@ class DirConf { public: DirConf(char* dirspec) : dirspec(dirspec), contributionPath(""), compositeName(""), componentName(""), implementationPath("") { } - char* dirspec; + const char* dirspec; std::string contributionPath; std::string compositeName; std::string componentName; @@ -89,7 +90,8 @@ public: /** * Handle an HTTP GET. */ -const failable get(request_rec* r, const ilambda& impl, const list& px) { +const failable get(request_rec* r, const ilambda& impl) { + debug(r->uri, "modeval::get::url"); // Inspect the query string const list > args = httpd::queryArgs(r); @@ -105,7 +107,7 @@ const failable get(request_rec* r, const ilambda& impl, const const list params = httpd::queryParams(args); // Apply the requested function - const failable val = impl(func, append(params, px)); + const failable val = impl(func, params); if (!hasContent(val)) return mkfailure(reason(val)); @@ -117,14 +119,14 @@ const failable get(request_rec* r, const ilambda& impl, const // Evaluate an ATOM GET request and return an ATOM feed const list id(httpd::path(r->path_info)); if (isNil(id)) { - const failable val = impl("getall", px); + const failable val = impl("getall", list()); if (!hasContent(val)) return mkfailure(reason(val)); return httpd::writeResult(atom::writeATOMFeed(atom::feedValuesToElements(content(val))), "application/atom+xml;type=feed", r); } // Evaluate an ATOM GET and return an ATOM entry - const failable val = impl("get", cons(car(id), px)); + const failable val = impl("get", mklist(car(id))); if (!hasContent(val)) return mkfailure(reason(val)); return httpd::writeResult(atom::writeATOMEntry(atom::entryValuesToElements(content(val))), "application/atom+xml;type=entry", r); @@ -133,9 +135,10 @@ const failable get(request_rec* r, const ilambda& impl, const /** * Handle an HTTP POST. */ -const failable post(request_rec* r, const ilambda& impl, const list& px) { +const failable post(request_rec* r, const ilambda& impl) { const list ls = httpd::read(r); - httpd::logStrings(ls, "content"); + debug(r->uri, "modeval::post::url"); + debug(ls, "modeval::post::input"); // Evaluate a JSON-RPC request and return a JSON result const std::string ct = httpd::contentType(r); @@ -150,7 +153,7 @@ const failable post(request_rec* r, const ilambda& impl, const const list params = (list)cadr(assoc(value("params"), args)); // Evaluate the request expression - const failable val = impl(func, append(params, px)); + const failable val = impl(func, params); if (!hasContent(val)) return mkfailure(reason(val)); @@ -162,8 +165,8 @@ const failable post(request_rec* r, const ilambda& impl, const if (ct.find("application/atom+xml") != std::string::npos) { // Evaluate the request expression - const value entry = httpd::feedEntry(content(atom::readEntry(ls))); - const failable val = impl("post", cons(entry, px)); + const value entry = atom::entryValue(content(atom::readEntry(ls))); + const failable val = impl("post", mklist(entry)); if (!hasContent(val)) return mkfailure(reason(val)); @@ -179,14 +182,15 @@ const failable post(request_rec* r, const ilambda& impl, const /** * Handle an HTTP PUT. */ -const failable put(request_rec* r, const ilambda& impl, const list& px) { +const failable put(request_rec* r, const ilambda& impl) { const list ls = httpd::read(r); - httpd::logStrings(ls, "content"); + debug(r->uri, "modeval::put::url"); + debug(ls, "modeval::put::input"); // Evaluate an ATOM PUT request const list id(httpd::path(r->path_info)); - const value entry = httpd::feedEntry(content(atom::readEntry(ls))); - const failable val = impl("put", append(mklist(entry, car(id)), px)); + const value entry = atom::entryValue(content(atom::readEntry(ls))); + const failable val = impl("put", mklist(car(id), entry)); if (!hasContent(val)) return mkfailure(reason(val)); if (val == value(false)) @@ -197,11 +201,12 @@ const failable put(request_rec* r, const ilambda& impl, const /** * Handle an HTTP DELETE. */ -const failable del(request_rec* r, const ilambda& impl, const list& px) { +const failable del(request_rec* r, const ilambda& impl) { + debug(r->uri, "modeval::delete::url"); // Evaluate an ATOM delete request const list id(httpd::path(r->path_info)); - const failable val = impl("delete", cons(car(id), px)); + const failable val = impl("delete", mklist(car(id))); if (!hasContent(val)) return mkfailure(reason(val)); if (val == value(false)) @@ -237,11 +242,11 @@ const cached > component(DirConf* conf) { /** * 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 value mkproxy(const value& ref, const std::string& base, const http::CURLSession& ch) { + return lambda&)>(http::proxy(base + std::string(scdl::name(ref)), ch)); } -const list proxies(const list& refs, const std::string& base, const http::CURLHandle& ch) { +const list proxies(const list& refs, const std::string& base, const http::CURLSession& ch) { if (isNil(refs)) return refs; return cons(mkproxy(car(refs), base, ch), proxies(cdr(refs), base, ch)); @@ -251,11 +256,11 @@ const list proxies(const list& refs, const std::string& base, cons * Returns the component implementation with the given implementation type and path. * For now only Scheme and C++ implementations are supported. */ -const cached > implementation(const std::string& itype, const std::string& path) { +const cached > implementation(const std::string& itype, const std::string& path, const list& px) { if (itype.find(".scheme") != std::string::npos) - return latest(scm::readImplementation(path)); + return latest(scm::readImplementation(path, px)); if (itype.find(".cpp") != std::string::npos) - return latest(cpp::readImplementation(path)); + return latest(cpp::readImplementation(path, px)); return cached >(); } @@ -265,7 +270,7 @@ const cached > implementation(const std::string& int handler(request_rec *r) { if(strcmp(r->handler, "mod_tuscany_eval")) return DECLINED; - httpd::logRequest(r, "mod_tuscany_eval::handler"); + httpdDebugRequest(r, "modeval::handler::input"); // Set up the read policy const int rc = httpd::setupReadPolicy(r); @@ -283,8 +288,19 @@ int handler(request_rec *r) { const value ielement= scdl::implementation(content(comp)); const std::string path = conf.contributionPath + std::string(scdl::uri(ielement)); if (path != conf.implementationPath) { + + // Convert component references to configured proxy lambdas + const ServerConf& sconf = httpd::serverConf(r, &mod_tuscany_eval); + std::ostringstream base; + if (sconf.wiringHost == "") + base << "http://localhost:" << ap_get_server_port(r) << "/references/" << std::string(scdl::name(content(comp))) << "/"; + else + base << "http://" << sconf.wiringHost << "/references/" << std::string(scdl::name(content(comp))) << "/"; + http::CURLSession ch; + const list px(proxies(scdl::references(content(comp)), base.str(), ch)); + + conf.implementation = implementation(elementName(ielement), path, px); conf.implementationPath = path; - conf.implementation = implementation(elementName(ielement), path); } else conf.implementation = latest(conf.implementation); @@ -292,47 +308,46 @@ int handler(request_rec *r) { if (!hasContent(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(content(comp))) << "/"; - http::CURLHandle ch; - const list px(proxies(scdl::references(content(comp)), base.str(), ch)); - // Handle HTTP method if (r->header_only) return OK; if(r->method_number == M_GET) - return httpd::reportStatus(get(r, content(impl), px)); + return httpd::reportStatus(get(r, content(impl))); if(r->method_number == M_POST) - return httpd::reportStatus(post(r, content(impl), px)); + return httpd::reportStatus(post(r, content(impl))); if(r->method_number == M_PUT) - return httpd::reportStatus(put(r, content(impl), px)); + return httpd::reportStatus(put(r, content(impl))); if(r->method_number == M_DELETE) - return httpd::reportStatus(del(r, content(impl), px)); + return httpd::reportStatus(del(r, content(impl))); return HTTP_NOT_IMPLEMENTED; } /** * Configuration commands. */ -const char *confHome(cmd_parms *cmd, void *dummy, const char *arg) { +const char *confHome(cmd_parms *cmd, unused 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) { +const char *confWiringHost(cmd_parms *cmd, unused void *dummy, const char *arg) { + ServerConf *c = (ServerConf*)ap_get_module_config(cmd->server->module_config, &mod_tuscany_eval); + c->wiringHost = arg; + return NULL; +} +const char *confContribution(unused 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) { +const char *confComposite(unused 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) { +const char *confComponent(unused cmd_parms *cmd, void *c, const char *arg) { DirConf* conf = (DirConf*)c; conf->componentName = arg; conf->component = component(conf); @@ -344,17 +359,18 @@ const char *confComponent(cmd_parms *cmd, void *c, const char *arg) { */ const command_rec commands[] = { AP_INIT_TAKE1("TuscanyHome", (const char*(*)())confHome, NULL, RSRC_CONF, "Tuscany home directory"), + AP_INIT_TAKE1("SCAWiringHost", (const char*(*)())confWiringHost, NULL, RSRC_CONF, "SCA wiring host"), 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} + {NULL, NULL, NULL, 0, NO_ARGS, NULL} }; -int postConfig(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { +int postConfig(unused apr_pool_t *p, unused apr_pool_t *plog, unused apr_pool_t *ptemp, unused server_rec *s) { return OK; } -void childInit(apr_pool_t* p, server_rec* svr_rec) { +void childInit(unused 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; @@ -362,7 +378,7 @@ void childInit(apr_pool_t* p, server_rec* svr_rec) { } } -void registerHooks(apr_pool_t *p) { +void registerHooks(unused 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); diff --git a/sca-cpp/trunk/modules/server/mod-scm.hpp b/sca-cpp/trunk/modules/server/mod-scm.hpp index 386d032695..586b90190f 100644 --- a/sca-cpp/trunk/modules/server/mod-scm.hpp +++ b/sca-cpp/trunk/modules/server/mod-scm.hpp @@ -34,8 +34,10 @@ #include "function.hpp" #include "list.hpp" #include "value.hpp" +#include "debug.hpp" #include "monad.hpp" #include "cache.hpp" +#include "../eval/primitive.hpp" #include "../eval/driver.hpp" #include "../http/httpd.hpp" #include "mod-eval.hpp" @@ -45,20 +47,30 @@ namespace server { namespace modeval { namespace scm { +/** + * Convert proxy lambdas to evaluator primitive procedures. + */ +const list primitiveProcedures(const list& l) { + if (isNil(l)) + return l; + return cons(mklist(eval::primitiveSymbol, car(l)), primitiveProcedures(cdr(l))); +} + /** * Evaluate a script component implementation function. */ struct evalImplementation { const value impl; - evalImplementation(const value& impl) : impl(impl) { + const list px; + evalImplementation(const value& impl, const list& px) : impl(impl), px(eval::quotedParameters(primitiveProcedures(px))) { } const failable operator()(const value& func, const list& params) const { - const value expr = cons(func, eval::quotedParameters(params)); - httpd::logValue(expr, "expr"); + const value expr = cons(func, append(eval::quotedParameters(params), px)); + debug(expr, "modeval::scm::evalImplementation::input"); gc_pool pool; eval::Env globalEnv = eval::setupEnvironment(pool); const value val = eval::evalScript(expr, impl, globalEnv, pool); - httpd::logValue(val, "val"); + debug(val, "modeval::scm::evalImplementation::result"); if (isNil(val)) return mkfailure("Could not evaluate expression"); return val; @@ -68,20 +80,20 @@ struct evalImplementation { /** * Read a script component implementation. */ -const failable readLatestImplementation(const std::string path) { +const failable readLatestImplementation(const std::string path, const list& px) { 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 ilambda(evalImplementation(impl)); + return ilambda(evalImplementation(impl, px)); } -const cached > readImplementation(const std::string& path) { - const lambda(std::string)> ri(readLatestImplementation); +const cached > readImplementation(const std::string& path, const list& px) { + const lambda(std::string, list)> ri(readLatestImplementation); const lambda ft(latestFileTime); - return cached >(curry(ri, path), curry(ft, path)); + return cached >(curry(ri, path, px), curry(ft, path)); } } diff --git a/sca-cpp/trunk/modules/server/server-test b/sca-cpp/trunk/modules/server/server-test new file mode 100755 index 0000000000..9c517f38b6 --- /dev/null +++ b/sca-cpp/trunk/modules/server/server-test @@ -0,0 +1,50 @@ +#!/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. + +# Setup +../http/httpd-conf tmp 8090 htdocs +./server-conf tmp +cat >>tmp/conf/httpd.conf < +SetHandler mod_tuscany_eval +SCAContribution `pwd`/ +SCAComposite domain-test.composite +SCAComponent server-test + + + +SetHandler mod_tuscany_eval +SCAContribution `pwd`/ +SCAComposite domain-test.composite +SCAComponent cpp-test + +EOF + +apachectl -k start -d `pwd`/tmp +sleep 1 + +# Test +./client-test 2>/dev/null +rc=$? + +# Cleanup +apachectl -k stop -d `pwd`/tmp +sleep 1 +return $rc diff --git a/sca-cpp/trunk/modules/server/server-test.scm b/sca-cpp/trunk/modules/server/server-test.scm new file mode 100644 index 0000000000..0566eaf36f --- /dev/null +++ b/sca-cpp/trunk/modules/server/server-test.scm @@ -0,0 +1,29 @@ +; JSON-RPC test case + +(define (echo x) x) + +; ATOMPub test case + +(define (getall) + '("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)))) +) + +(define (get id) + (define entry '((javaClass "services.Item") (name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99))) + (cons "Item" (list id entry)) +) + +(define (post entry) + "123456789" +) + +(define (put id entry) + true +) + +(define (delete id) + true +) diff --git a/sca-cpp/trunk/modules/server/wiring-test b/sca-cpp/trunk/modules/server/wiring-test index c50732ddb2..5f77d4c6f8 100755 --- a/sca-cpp/trunk/modules/server/wiring-test +++ b/sca-cpp/trunk/modules/server/wiring-test @@ -27,21 +27,21 @@ cat >>tmp/conf/httpd.conf < SetHandler mod_tuscany_eval SCAContribution `pwd`/ -SCAComposite httpd-test.composite -SCAComponent httpd-test +SCAComposite domain-test.composite +SCAComponent server-test SetHandler mod_tuscany_eval -SCAContribution /home/delfinoj/SCAZone/Source/tuscany-cpp/sca/modules/http/ -SCAComposite httpd-test.composite -SCAComponent httpd-client +SCAContribution `pwd`/ +SCAComposite domain-test.composite +SCAComponent client-test SetHandler mod_tuscany_wiring SCAContribution `pwd`/ -SCAComposite httpd-test.composite +SCAComposite domain-test.composite EOF @@ -86,7 +86,7 @@ fi # Cleanup apachectl -k stop -d `pwd`/tmp -sleep 2 +sleep 1 if [ "$rc" = "0" ]; then echo "OK" fi diff --git a/sca-cpp/trunk/test/store-script/currency-converter.scm b/sca-cpp/trunk/test/store-script/currency-converter.scm index 7f58335951..498ac5da5b 100644 --- a/sca-cpp/trunk/test/store-script/currency-converter.scm +++ b/sca-cpp/trunk/test/store-script/currency-converter.scm @@ -7,3 +7,4 @@ (define (symbol currency) (if (equal? currency "EUR") "E" "$") ) + diff --git a/sca-cpp/trunk/test/store-script/fruits-catalog.scm b/sca-cpp/trunk/test/store-script/fruits-catalog.scm index 390068d71a..18128f0137 100644 --- a/sca-cpp/trunk/test/store-script/fruits-catalog.scm +++ b/sca-cpp/trunk/test/store-script/fruits-catalog.scm @@ -1,12 +1,9 @@ ; 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))) @@ -17,3 +14,4 @@ ; TODO remove these JSON-RPC specific functions (define (system.listMethods converter) (list "Service.get")) (define Service.get get) + diff --git a/sca-cpp/trunk/test/store-script/htdocs/store.html b/sca-cpp/trunk/test/store-script/htdocs/store.html index 4d300f1adb..0378b454cc 100644 --- a/sca-cpp/trunk/test/store-script/htdocs/store.html +++ b/sca-cpp/trunk/test/store-script/htdocs/store.html @@ -94,11 +94,15 @@ var j = 0; for (var i=0; i' + - '' + catalogItems[i].name + '' + '' + catalogItems[i].price + '' + - '' + ''; + var entry = 'Item' + + '' + + '' + catalogItems[i].javaClass + '' + + '' + catalogItems[i].name + '' + + '' + catalogItems[i].currencyCode + '' + + '' + catalogItems[i].currencySymbol + '' + + '' + catalogItems[i].price + '' + + '' + + ''; shoppingCart.post(entry, shoppingCart_postResponse); items[i].checked = false; } diff --git a/sca-cpp/trunk/test/store-script/shopping-cart.scm b/sca-cpp/trunk/test/store-script/shopping-cart.scm index ebb3504e25..01ca62df40 100644 --- a/sca-cpp/trunk/test/store-script/shopping-cart.scm +++ b/sca-cpp/trunk/test/store-script/shopping-cart.scm @@ -1,26 +1,58 @@ ; Shopping cart implementation -(define (post item) - (uuid) +(define cartId "1234") + +; Get the shopping cart from the cache +; Return an empty cart if not found +(define (getcart id cache) + (define cart (cache "get" id)) + (if (nul cart) + (list) + cart) +) + +; Post a new item to the cart, create a new cart if necessary +(define (post item cache) + (define id (uuid)) + (define cart (cons item (getcart cartId cache))) + (cache "put" cartId cart) + id ) -(define (getall) - (cons "Sample Feed" (cons (uuid) '())) +; Return the content of the cart +(define (getall cache) + (define cart (getcart cartId cache)) + (cons "Your Cart" (cons cartId cart)) ) -(define (get id) +; Get an item from the cart +(define (get id cache) (define entry (list (list 'name "Apple") (list 'currencyCode "USD") (list 'currencySymbol "$") (list 'price 2.99))) (cons "Item" (list id entry)) ) -(define (delete id) - true +; Delete the cart +(define (delete id cache) + (cache "delete" cartId) +) + +; Return the price of an item +(define (price item) + (car (cdr (car (cdr (cdr (cdr (cdr (car (cdr (cdr item)))))))))) +) + +; Sum the prices of a list of items +(define (sum items) + (if (nul items) 0 (+ (price (car items)) (sum (cdr items)))) ) -(define (gettotal) - 11.0 +; Return the total price of the items in the cart +(define (gettotal cache) + (define cart (getcart cartId cache)) + (sum cart) ) ; TODO remove these JSON-RPC specific functions -(define (system.listMethods) (list "Service.getTotal")) +(define (system.listMethods cache) (list "Service.getTotal")) (define Service.getTotal gettotal) + diff --git a/sca-cpp/trunk/test/store-script/store-composite-test b/sca-cpp/trunk/test/store-script/store-composite-test new file mode 100755 index 0000000000..43923fca36 --- /dev/null +++ b/sca-cpp/trunk/test/store-script/store-composite-test @@ -0,0 +1,87 @@ +#!/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 +../../modules/http/httpd-conf tmp 8090 htdocs +../../modules/server/server-conf tmp +cat >>tmp/conf/httpd.conf < +SetHandler mod_tuscany_eval +SCAContribution `pwd`/ +SCAComposite store.composite +SCAComponent Catalog + + + +SetHandler mod_tuscany_eval +SCAContribution `pwd`/ +SCAComposite store.composite +SCAComponent ShoppingCart + + + +SetHandler mod_tuscany_eval +SCAContribution `pwd`/ +SCAComposite store.composite +SCAComponent ShoppingCart + + + +SetHandler mod_tuscany_eval +SCAContribution `pwd`/ +SCAComposite store.composite +SCAComponent CurrencyConverter + + + +SetHandler mod_tuscany_eval +SCAContribution `pwd`/ +SCAComposite store.composite +SCAComponent Cache + + + +SetHandler mod_tuscany_wiring +SCAContribution `pwd`/ +SCAComposite store.composite + +EOF + +apachectl -k start -d `pwd`/tmp + +mc="memcached -l 127.0.0.1 -m 4 -p 11211" +$mc & +sleep 1 + +# Test HTTP GET +curl http://localhost:8090/store.html 2>/dev/null >tmp/store.html +diff tmp/store.html htdocs/store.html +rc=$? + +# Cleanup +apachectl -k stop -d `pwd`/tmp +kill `ps -f | grep -v grep | grep "$mc" | awk '{ print $2 }'` +sleep 1 +if [ "$rc" = "0" ]; then + echo "OK" +fi +return $rc diff --git a/sca-cpp/trunk/test/store-script/store-http-test b/sca-cpp/trunk/test/store-script/store-http-test deleted file mode 100755 index d04eab8a6c..0000000000 --- a/sca-cpp/trunk/test/store-script/store-http-test +++ /dev/null @@ -1,76 +0,0 @@ -#!/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 -../../modules/http/httpd-conf tmp 8090 htdocs -../../modules/server/server-conf tmp -cat >>tmp/conf/httpd.conf < -SetHandler mod_tuscany_eval -SCAContribution `pwd`/ -SCAComposite store.composite -SCAComponent Catalog - - - -SetHandler mod_tuscany_eval -SCAContribution `pwd`/ -SCAComposite store.composite -SCAComponent ShoppingCart - - - -SetHandler mod_tuscany_eval -SCAContribution `pwd`/ -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:8090/store.html 2>/dev/null >tmp/store.html -diff tmp/store.html htdocs/store.html -rc=$? - -# Cleanup -apachectl -k stop -d `pwd`/tmp -sleep 2 -if [ "$rc" = "0" ]; then - echo "OK" -fi -return $rc diff --git a/sca-cpp/trunk/test/store-script/store.composite b/sca-cpp/trunk/test/store-script/store.composite index cd34f81840..2cfadf3f71 100644 --- a/sca-cpp/trunk/test/store-script/store.composite +++ b/sca-cpp/trunk/test/store-script/store.composite @@ -57,6 +57,9 @@ + + + @@ -66,4 +69,11 @@ + + + + + + + diff --git a/sca-cpp/trunk/test/store-script/store.scm b/sca-cpp/trunk/test/store-script/store.scm index 2434b18b51..01f72d0bea 100644 --- a/sca-cpp/trunk/test/store-script/store.scm +++ b/sca-cpp/trunk/test/store-script/store.scm @@ -24,8 +24,8 @@ (shoppingCart "delete" id) ) -(define (system.listMethods) (list "Service.get" "Service.getTotal")) - +; TODO remove these JSON-RPC specific functions +(define (system.listMethods catalog shoppingCart shoppingTotal) (list "Service.get" "Service.getTotal")) (define Service.getCatalog getcatalog) - (define Service.getTotal gettotal) + -- cgit v1.2.3