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
This commit is contained in:
jsdelfino 2009-11-30 08:36:32 +00:00
commit ff12404062
31 changed files with 723 additions and 281 deletions

View file

@ -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 <assert.h>
#include <sys/time.h>
#include <time.h>
#include <iostream>
#include <string>
#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<value> i = list<value>()
<< (list<value>() << "name" << std::string("Apple"))
<< (list<value>() << "price" << std::string("$2.99"));
const list<value> a = mklist<value>(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i);
const failable<value, std::string> id = http::post(a, url, cs);
assert(hasContent(id));
{
const failable<value, std::string> val = http::get(url + "/" + std::string(content(id)), cs);
assert(hasContent(val));
assert(content(val) == a);
}
const list<value> j = list<value>()
<< (list<value>() << "name" << std::string("Apple"))
<< (list<value>() << "price" << std::string("$3.55"));
const list<value> b = mklist<value>(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), j);
{
const failable<value, std::string> r = http::put(b, url + "/" + std::string(content(id)), cs);
assert(hasContent(r));
assert(content(r) == value(true));
}
{
const failable<value, std::string> val = http::get(url + "/" + std::string(content(id)), cs);
assert(hasContent(val));
assert(content(val) == b);
}
{
const failable<value, std::string> r = http::del(url + "/" + std::string(content(id)), cs);
assert(hasContent(r));
assert(content(r) == value(true));
}
{
const failable<value, std::string> 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<value, std::string> 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<value> i = list<value>()
<< (list<value>() << "name" << std::string("Apple"))
<< (list<value>() << "price" << std::string("$4.55"));
const list<value> a = mklist<value>(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i);
http::CURLSession cs;
const failable<value, std::string> 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;
}

View file

@ -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);

View file

@ -23,7 +23,7 @@
name="mcache">
<component name="mcache">
<t:implementation.cpp uri="libmcache"/>
<t:implementation.cpp uri=".libs/libmcache"/>
<service name="mcache">
<t:binding.http uri="mcache"/>
</service>

View file

@ -20,7 +20,7 @@
/* $Rev$ $Date$ */
/**
* memcached-based cache component implementation.
* Memcached-based cache component implementation.
*/
#include <apr_uuid.h>
@ -45,6 +45,9 @@ const failable<value, std::string> get(const list<value>& 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<value, std::string> post(const list<value>& params) {
const value id = uuidValue();
const failable<bool, std::string> val = cache::post(id, car(params), ch);
@ -96,7 +96,7 @@ const tuscany::failable<tuscany::value, std::string> 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<tuscany::value, std::string>(std::string("Function not supported: ") + std::string(func));
}

View file

@ -35,9 +35,12 @@
#include "apr_network_io.h"
#include <string>
#include <sstream>
#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<bool, std::string> 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<bool, std::string>("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<bool, std::string> 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<char*>(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<char*>(vs.c_str()), vs.size(), 0, 27);
if (rc != APR_SUCCESS)
return mkfailure<bool, std::string>("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<bool, std::string> 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<char*>(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<char*>(vs.c_str()), vs.size(), 0, 27);
if (rc != APR_SUCCESS)
return mkfailure<bool, std::string>("Could not add entry");
debug(true, "cache::put::result");
return true;
}
@ -114,6 +129,10 @@ const failable<bool, std::string> put(const value& key, const value& val, const
* Get an item from the cache.
*/
const failable<value, std::string> 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<value, std::string> 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<value, std::string>("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<value, std::string> get(const value& key, const MemCached& cache)
* Delete an item from the cache
*/
const failable<bool, std::string> 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<bool, std::string>("Could not add entry");
return mkfailure<bool, std::string>("Could not delete entry");
debug(true, "cache::delete::result");
return true;
}

View file

@ -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 <<EOF
<Location /mcache>
SetHandler mod_tuscany_eval
SCAContribution `pwd`/
SCAComposite mcache.composite
SCAComponent mcache
</Location>
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

View file

@ -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

View file

@ -38,7 +38,7 @@ namespace atom {
/**
* Convert a list of elements to a list of values representing an ATOM entry.
*/
const list<value> entryValue(const list<value>& e) {
const list<value> entryValues(const list<value>& e) {
const list<value> lt = filter<value>(selector(mklist<value>(element, "title")), e);
const value t = isNil(lt)? value(std::string("")) : elementValue(car(lt));
const list<value> li = filter<value>(selector(mklist<value>(element, "id")), e);
@ -53,7 +53,7 @@ const list<value> entryValue(const list<value>& e) {
const list<value> entriesValues(const list<value>& e) {
if (isNil(e))
return e;
return cons<value>(entryValue(car(e)), entriesValues(cdr(e)));
return cons<value>(entryValues(car(e)), entriesValues(cdr(e)));
}
/**
@ -63,7 +63,15 @@ const failable<list<value>, std::string> readEntry(const list<std::string>& ilis
const list<value> e = readXML(ilist);
if (isNil(e))
return mkfailure<list<value>, 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<value>& e) {
const list<value> v = elementsToValues(mklist<value>(caddr(e)));
return cons(car(e), mklist<value>(cadr(e), cdr<value>(car(v))));
}
/**

View file

@ -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<list<std::ostringstream*>, std::string> r = get<std::ostringstream*>(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<value, std::string> 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;
{

View file

@ -30,6 +30,7 @@
#include <curl/types.h>
#include <curl/easy.h>
#include <string>
#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()) {
}
~CURLHandle() {
curl_easy_cleanup(h);
CURLSession() : ch(new CURLHandle()) {
}
operator CURL*() const {
return h;
~CURLSession() {
}
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<CURLHandle> 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<std::string>& h) {
return headers(curl_slist_append(cl, std::string(car(h)).c_str()), cdr(h));
}
template<typename R> const failable<list<R>, std::string> apply(const list<list<std::string> >& req, const lambda<R(std::string, R)>& reduce, const R& initial, const std::string& url, const std::string& verb, const CURLHandle& ch) {
template<typename R> const failable<list<R>, std::string> apply(const list<list<std::string> >& req, const lambda<R(std::string, R)>& 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<typename R> const failable<list<R>, std::string> apply(const list<list<
/**
* Evaluate an expression remotely, at the given URL.
*/
const failable<value, std::string> evalExpr(const value& expr, const std::string& url, const CURLHandle& ch) {
const failable<value, std::string> 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<value, std::string> evalExpr(const value& expr, const std::string
if (!hasContent(jsreq))
return mkfailure<value, std::string>(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<std::string> h = mklist<std::string>("Content-Type: application/json-rpc");
const failable<list<list<std::string> >, std::string> res = apply<list<std::string> >(mklist<list<std::string> >(h, content(jsreq)), rcons<std::string>, list<std::string>(), url, "POST", ch);
@ -226,19 +241,58 @@ const failable<value, std::string> evalExpr(const value& expr, const std::string
return mkfailure<value, std::string>(reason(res));
// Return result
if (logContent) {
std::cout << "content:" << std::endl;
write(cadr<list<std::string> >(content(res)), std::cout);
std::cout << std::endl;
}
const list<value> val = elementsToValues(content(json::readJSON(cadr<list<std::string> >(content(res)), cx)));
return cadr<value>(cadr<value>(val));
failable<list<value>, std::string> jsres = json::readJSON(cadr<list<std::string> >(content(res)), cx);
if (!hasContent(jsres))
return mkfailure<value, std::string>(reason(jsres));
const list<value> val = elementsToValues(content(jsres));
const value rval(cadr<value>(cadr<value>(val)));
debug(rval, "http::evalExpr::result");
return rval;
}
/**
* Find and return a header.
*/
const failable<std::string, std::string> header(const std::string& prefix, const list<std::string>& h) {
if (isNil(h))
return mkfailure<std::string, std::string>(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<std::string, std::string> location(const list<std::string>& h) {
return header("Location: ", h);
}
/**
* Convert a location to an entry id.
*/
const failable<value, std::string> entryId(const failable<std::string, std::string> l) {
if (!hasContent(l))
return mkfailure<value, std::string>(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<std::string, std::string> contentType(const list<std::string>& h) {
return header("Content-Type: ", h);
}
/**
* HTTP GET, return the resource at the given URL.
*/
template<typename R> const failable<list<R>, std::string> get(const lambda<R(std::string, R)>& reduce, const R& initial, const std::string& url, const CURLHandle& ch) {
template<typename R> const failable<list<R>, std::string> get(const lambda<R(std::string, R)>& reduce, const R& initial, const std::string& url, const CURLSession& ch) {
debug(url, "http::get::url");
const list<list<std::string> > req = mklist(list<std::string>(), list<std::string>());
return apply(req, reduce, initial, url, "GET", ch);
}
@ -246,38 +300,41 @@ template<typename R> const failable<list<R>, std::string> get(const lambda<R(std
/**
* HTTP GET, return a list of values representing the resource at the given URL.
*/
const failable<value, std::string> get(const std::string& url, const CURLHandle& ch) {
const failable<value, std::string> 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<list<list<std::string> >, std::string> res = get<list<std::string> >(rcons<std::string>, list<std::string>(), url, ch);
if (!hasContent(res))
return mkfailure<value, std::string>(reason(res));
const list<std::string> 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<value, std::string> post(const value& val, const std::string& url, const CURLHandle& ch) {
const failable<value, std::string> post(const value& val, const std::string& url, const CURLSession& ch) {
// Convert value to an ATOM entry
const failable<list<std::string>, std::string> entry = atom::writeATOMEntry(atom::entryValuesToElements(val));
if (!hasContent(entry))
return mkfailure<value, std::string>(reason(entry));
if (logContent) {
std::cout << "content:" << std::endl;
write(list<std::string>(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<std::string> h = mklist<std::string>("Content-Type: application/atom+xml");
@ -285,23 +342,24 @@ const failable<value, std::string> post(const value& val, const std::string& url
const failable<list<list<std::string> >, std::string> res = apply<list<std::string> >(req, rcons<std::string>, list<std::string>(), url, "POST", ch);
if (!hasContent(res))
return mkfailure<value, std::string>(reason(res));
return value(true);
// Return the new entry id from the HTTP location header
const failable<value, std::string> eid(entryId(location(car(content(res)))));
debug(eid, "http::post::result");
return eid;
}
/**
* HTTP PUT.
*/
const failable<value, std::string> put(const value& val, const std::string& url, const CURLHandle& ch) {
const failable<value, std::string> put(const value& val, const std::string& url, const CURLSession& ch) {
// Convert value to an ATOM entry
const failable<list<std::string>, std::string> entry = atom::writeATOMEntry(atom::entryValuesToElements(val));
if (!hasContent(entry))
return mkfailure<value, std::string>(reason(entry));
if (logContent) {
std::cout << "content:" << std::endl;
write(list<std::string>(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<std::string> h = mklist<std::string>("Content-Type: application/atom+xml");
@ -309,17 +367,23 @@ const failable<value, std::string> put(const value& val, const std::string& url,
const failable<list<list<std::string> >, std::string> res = apply<list<std::string> >(req, rcons<std::string>, list<std::string>(), url, "PUT", ch);
if (!hasContent(res))
return mkfailure<value, std::string>(reason(res));
debug(true, "http::put::result");
return value(true);
}
/**
* HTTP DELETE.
*/
const failable<value, std::string> del(const std::string& url, const CURLHandle& ch) {
const failable<value, std::string> del(const std::string& url, const CURLSession& ch) {
debug(url, "http::delete::url");
const list<list<std::string> > req = mklist(list<std::string>(), list<std::string>());
const failable<list<list<std::string> >, std::string> res = apply<list<std::string> >(req, rcons<std::string>, list<std::string>(), url, "DELETE", ch);
if (!hasContent(res))
return mkfailure<value, std::string>(reason(res));
debug(true, "http::delete::result");
return value(true);
}
@ -327,7 +391,7 @@ const failable<value, std::string> 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<value>& args) const {
@ -338,7 +402,7 @@ struct proxy {
}
const std::string url;
const CURLHandle& ch;
const CURLSession ch;
};
}

View file

@ -28,5 +28,5 @@ rc=$?
# Cleanup
apachectl -k stop -d `pwd`/tmp
sleep 2
sleep 1
return $rc

View file

@ -31,7 +31,7 @@ rc=$?
# Cleanup
apachectl -k stop -d `pwd`/tmp
sleep 2
sleep 1
if [ "$rc" = "0" ]; then
echo "OK"
fi

View file

@ -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<value>& 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<value, std::string>& 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<std::string>& 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<value>& e) {
const list<value> v = elementsToValues(mklist<value>(caddr(e)));
return cons(car(e), mklist<value>(cadr(e), cdr<value>(car(v))));
}
/**
* Write an HTTP result.
*/
@ -271,10 +239,7 @@ const failable<int, std::string> writeResult(const failable<list<std::string>, s
return mkfailure<int, std::string>(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<int, std::string> writeResult(const failable<list<std::string>, 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;
}

View file

@ -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<list<std::ostringstream*>, std::string> r = http::get<std::ostringstream*>(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<value, std::string> 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<value>(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<value>(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<value> 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<value>(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<value>() << "name" << std::string("Apple"))
<< (list<value>() << "price" << std::string("$2.99"));
const list<value> a = mklist<value>(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<value, std::string> 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<value, std::string> 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<value>() << "price" << std::string("$2.99"));
const list<value> val = mklist<value>(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<value>() << "price" << std::string("$2.99"));
const list<value> val = mklist<value>(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<value>() << "name" << std::string("Apple"))
<< (list<value>() << "price" << std::string("$2.99"));
const list<value> a = mklist<value>(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<value>(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<value>(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;

View file

@ -19,18 +19,25 @@
-->
<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200903"
xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
targetNamespace="http://test"
name="httpd-test">
targetNamespace="http://domain/test"
name="domain-test">
<component name="httpd-test">
<t:implementation.scheme uri="httpd-test.scm"/>
<component name="server-test">
<t:implementation.scheme uri="server-test.scm"/>
<service name="test">
<t:binding.http uri="test"/>
</service>
</component>
<component name="httpd-client">
<t:implementation.scheme uri="httpd-client.scm"/>
<component name="cpp-test">
<t:implementation.cpp uri=".libs/libimpl-test"/>
<service name="cpp">
<t:binding.http uri="cpp"/>
</service>
</component>
<component name="client-test">
<t:implementation.scheme uri="client-test.scm"/>
<service name="client">
<t:binding.http uri="client"/>
</service>

View file

@ -27,8 +27,8 @@ cat >>tmp/conf/httpd.conf <<EOF
<Location /test>
SetHandler mod_tuscany_eval
SCAContribution `pwd`/
SCAComposite httpd-test.composite
SCAComponent httpd-test
SCAComposite domain-test.composite
SCAComponent server-test
</Location>
EOF
@ -73,7 +73,7 @@ fi
# Cleanup
apachectl -k stop -d `pwd`/tmp
sleep 2
sleep 1
if [ "$rc" = "0" ]; then
echo "OK"
fi

View file

@ -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 <string>
#include "function.hpp"
#include "list.hpp"
#include "value.hpp"
#include "monad.hpp"
#include "debug.hpp"
namespace tuscany {
namespace server {
const failable<value, std::string> get(unused const list<value>& params) {
return value(std::string("Hey"));
}
const failable<value, std::string> post(unused const list<value>& params) {
return value(std::string("1234"));
}
const failable<value, std::string> put(unused const list<value>& params) {
return value(true);
}
const failable<value, std::string> del(unused const list<value>& params) {
return value(true);
}
const failable<value, std::string> hello(const list<value>& params) {
return value("hello " + std::string(car(params)));
}
}
}
extern "C" {
const tuscany::failable<tuscany::value, std::string> eval(const tuscany::value& func, const tuscany::list<tuscany::value>& 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<tuscany::value, std::string>(std::string("Function not supported: ") + std::string(func));
}
}

View file

@ -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<value> px;
evalImplementation(const lib& ilib, const ilambda& impl, const list<value>& px) : ilib(ilib), impl(impl), px(px) {
}
const failable<value, std::string> operator()(const value& func, const list<value>& params) const {
httpd::logValue(cons<value>(func, params), "expr");
const failable<value, std::string> val = impl(func, params);
httpd::logValue(content(val), "val");
debug(cons<value>(func, params), "modeval::cpp::evalImplementation::input");
const failable<value, std::string> 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<ilambda, std::string> readLatestImplementation(const std::string path) {
const failable<ilambda, std::string> readLatestImplementation(const std::string path, const list<value>& px) {
const failable<lib, std::string> ilib(dynlib(path));
if (!hasContent(ilib))
return mkfailure<ilambda, std::string>(reason(ilib));
@ -73,14 +75,14 @@ const failable<ilambda, std::string> readLatestImplementation(const std::string
const failable<ilambda, std::string> impl(dynlambda<failable<value, std::string>(value, list<value>)>("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<failable<ilambda, std::string> > readImplementation(const std::string& path) {
const lambda<failable<ilambda, std::string>(std::string)> ri(readLatestImplementation);
const cached<failable<ilambda, std::string> > readImplementation(const std::string& path, const list<value>& px) {
const lambda<failable<ilambda, std::string>(std::string, list<value>)> ri(readLatestImplementation);
const lambda<unsigned long(std::string)> ft(latestFileTime);
const std::string p(path + dynlibExt);
return cached<failable<ilambda, std::string> >(curry(ri, p), curry(ft, p));
return cached<failable<ilambda, std::string> >(curry(ri, p, px), curry(ft, p));
}
}

View file

@ -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<int, std::string> get(request_rec* r, const ilambda& impl, const list<value>& px) {
const failable<int, std::string> get(request_rec* r, const ilambda& impl) {
debug(r->uri, "modeval::get::url");
// Inspect the query string
const list<list<value> > args = httpd::queryArgs(r);
@ -105,7 +107,7 @@ const failable<int, std::string> get(request_rec* r, const ilambda& impl, const
const list<value> params = httpd::queryParams(args);
// Apply the requested function
const failable<value, std::string> val = impl(func, append(params, px));
const failable<value, std::string> val = impl(func, params);
if (!hasContent(val))
return mkfailure<int, std::string>(reason(val));
@ -117,14 +119,14 @@ const failable<int, std::string> get(request_rec* r, const ilambda& impl, const
// Evaluate an ATOM GET request and return an ATOM feed
const list<value> id(httpd::path(r->path_info));
if (isNil(id)) {
const failable<value, std::string> val = impl("getall", px);
const failable<value, std::string> val = impl("getall", list<value>());
if (!hasContent(val))
return mkfailure<int, std::string>(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<value, std::string> val = impl("get", cons<value>(car(id), px));
const failable<value, std::string> val = impl("get", mklist<value>(car(id)));
if (!hasContent(val))
return mkfailure<int, std::string>(reason(val));
return httpd::writeResult(atom::writeATOMEntry(atom::entryValuesToElements(content(val))), "application/atom+xml;type=entry", r);
@ -133,9 +135,10 @@ const failable<int, std::string> get(request_rec* r, const ilambda& impl, const
/**
* Handle an HTTP POST.
*/
const failable<int, std::string> post(request_rec* r, const ilambda& impl, const list<value>& px) {
const failable<int, std::string> post(request_rec* r, const ilambda& impl) {
const list<std::string> 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<int, std::string> post(request_rec* r, const ilambda& impl, const
const list<value> params = (list<value>)cadr(assoc(value("params"), args));
// Evaluate the request expression
const failable<value, std::string> val = impl(func, append(params, px));
const failable<value, std::string> val = impl(func, params);
if (!hasContent(val))
return mkfailure<int, std::string>(reason(val));
@ -162,8 +165,8 @@ const failable<int, std::string> 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<value, std::string> val = impl("post", cons<value>(entry, px));
const value entry = atom::entryValue(content(atom::readEntry(ls)));
const failable<value, std::string> val = impl("post", mklist<value>(entry));
if (!hasContent(val))
return mkfailure<int, std::string>(reason(val));
@ -179,14 +182,15 @@ const failable<int, std::string> post(request_rec* r, const ilambda& impl, const
/**
* Handle an HTTP PUT.
*/
const failable<int, std::string> put(request_rec* r, const ilambda& impl, const list<value>& px) {
const failable<int, std::string> put(request_rec* r, const ilambda& impl) {
const list<std::string> 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<value> id(httpd::path(r->path_info));
const value entry = httpd::feedEntry(content(atom::readEntry(ls)));
const failable<value, std::string> val = impl("put", append(mklist<value>(entry, car(id)), px));
const value entry = atom::entryValue(content(atom::readEntry(ls)));
const failable<value, std::string> val = impl("put", mklist<value>(car(id), entry));
if (!hasContent(val))
return mkfailure<int, std::string>(reason(val));
if (val == value(false))
@ -197,11 +201,12 @@ const failable<int, std::string> put(request_rec* r, const ilambda& impl, const
/**
* Handle an HTTP DELETE.
*/
const failable<int, std::string> del(request_rec* r, const ilambda& impl, const list<value>& px) {
const failable<int, std::string> del(request_rec* r, const ilambda& impl) {
debug(r->uri, "modeval::delete::url");
// Evaluate an ATOM delete request
const list<value> id(httpd::path(r->path_info));
const failable<value, std::string> val = impl("delete", cons<value>(car(id), px));
const failable<value, std::string> val = impl("delete", mklist<value>(car(id)));
if (!hasContent(val))
return mkfailure<int, std::string>(reason(val));
if (val == value(false))
@ -237,11 +242,11 @@ const cached<failable<value, std::string> > 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<value(list<value>&)>(http::proxy(base + std::string(scdl::name(ref)), ch));
}
const list<value> proxies(const list<value>& refs, const std::string& base, const http::CURLHandle& ch) {
const list<value> proxies(const list<value>& 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<value> proxies(const list<value>& 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<failable<ilambda, std::string> > implementation(const std::string& itype, const std::string& path) {
const cached<failable<ilambda, std::string> > implementation(const std::string& itype, const std::string& path, const list<value>& 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<failable<ilambda, std::string> >();
}
@ -265,7 +270,7 @@ const cached<failable<ilambda, std::string> > 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<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<value> 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<value> 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);

View file

@ -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<value> primitiveProcedures(const list<value>& l) {
if (isNil(l))
return l;
return cons<value>(mklist<value>(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<value> px;
evalImplementation(const value& impl, const list<value>& px) : impl(impl), px(eval::quotedParameters(primitiveProcedures(px))) {
}
const failable<value, std::string> operator()(const value& func, const list<value>& params) const {
const value expr = cons<value>(func, eval::quotedParameters(params));
httpd::logValue(expr, "expr");
const value expr = cons<value>(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<value, std::string>("Could not evaluate expression");
return val;
@ -68,20 +80,20 @@ struct evalImplementation {
/**
* Read a script component implementation.
*/
const failable<ilambda, std::string> readLatestImplementation(const std::string path) {
const failable<ilambda, std::string> readLatestImplementation(const std::string path, const list<value>& px) {
std::ifstream is(path.c_str(), std::ios_base::in);
if (is.fail() || is.bad())
return mkfailure<ilambda, std::string>("Could not read implementation: " + path);
const value impl = eval::readScript(is);
if (isNil(impl))
return mkfailure<ilambda, std::string>("Could not read implementation: " + path);
return ilambda(evalImplementation(impl));
return ilambda(evalImplementation(impl, px));
}
const cached<failable<ilambda, std::string> > readImplementation(const std::string& path) {
const lambda<failable<ilambda, std::string>(std::string)> ri(readLatestImplementation);
const cached<failable<ilambda, std::string> > readImplementation(const std::string& path, const list<value>& px) {
const lambda<failable<ilambda, std::string>(std::string, list<value>)> ri(readLatestImplementation);
const lambda<unsigned long(std::string)> ft(latestFileTime);
return cached<failable<ilambda, std::string> >(curry(ri, path), curry(ft, path));
return cached<failable<ilambda, std::string> >(curry(ri, path, px), curry(ft, path));
}
}

View file

@ -25,8 +25,15 @@ cat >>tmp/conf/httpd.conf <<EOF
<Location /test>
SetHandler mod_tuscany_eval
SCAContribution `pwd`/
SCAComposite httpd-test.composite
SCAComponent httpd-test
SCAComposite domain-test.composite
SCAComponent server-test
</Location>
<Location /cpp>
SetHandler mod_tuscany_eval
SCAContribution `pwd`/
SCAComposite domain-test.composite
SCAComponent cpp-test
</Location>
EOF
@ -34,10 +41,10 @@ apachectl -k start -d `pwd`/tmp
sleep 1
# Test
./client-test
./client-test 2>/dev/null
rc=$?
# Cleanup
apachectl -k stop -d `pwd`/tmp
sleep 2
sleep 1
return $rc

View file

@ -27,21 +27,21 @@ cat >>tmp/conf/httpd.conf <<EOF
<Location /test>
SetHandler mod_tuscany_eval
SCAContribution `pwd`/
SCAComposite httpd-test.composite
SCAComponent httpd-test
SCAComposite domain-test.composite
SCAComponent server-test
</Location>
<Location /client>
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
</Location>
<Location /references>
SetHandler mod_tuscany_wiring
SCAContribution `pwd`/
SCAComposite httpd-test.composite
SCAComposite domain-test.composite
</Location>
EOF
@ -86,7 +86,7 @@ fi
# Cleanup
apachectl -k stop -d `pwd`/tmp
sleep 2
sleep 1
if [ "$rc" = "0" ]; then
echo "OK"
fi

View file

@ -7,3 +7,4 @@
(define (symbol currency)
(if (equal? currency "EUR") "E" "$")
)

View file

@ -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)

View file

@ -94,11 +94,15 @@
var j = 0;
for (var i=0; i<items.length; i++)
if (items[i].checked) {
var entry = '<entry xmlns="http://www.w3.org/2005/Atom"><title>item</title><content type="text/xml">' +
'<Item xmlns="http://services/">' +
'<name xmlns="">' + catalogItems[i].name + '</name>' + '<price xmlns="">' + catalogItems[i].price + '</price>' +
'</Item>' + '</content></entry>';
var entry = '<entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><content type="application/xml">' +
'<item>' +
'<javaClass>' + catalogItems[i].javaClass + '</javaClass>' +
'<name>' + catalogItems[i].name + '</name>' +
'<currencyCode>' + catalogItems[i].currencyCode + '</currencyCode>' +
'<currencySymbol>' + catalogItems[i].currencySymbol + '</currencySymbol>' +
'<price>' + catalogItems[i].price + '</price>' +
'</item>' +
'</content></entry>';
shoppingCart.post(entry, shoppingCart_postResponse);
items[i].checked = false;
}

View file

@ -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)
)
(define (getall)
(cons "Sample Feed" (cons (uuid) '()))
; 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 (get id)
; Return the content of the cart
(define (getall cache)
(define cart (getcart cartId cache))
(cons "Your Cart" (cons cartId cart))
)
; 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)
)
(define (gettotal)
11.0
; 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))))
)
; 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)

View file

@ -52,6 +52,13 @@ SCAComposite store.composite
SCAComponent CurrencyConverter
</Location>
<Location /Cache>
SetHandler mod_tuscany_eval
SCAContribution `pwd`/
SCAComposite store.composite
SCAComponent Cache
</Location>
<Location /references>
SetHandler mod_tuscany_wiring
SCAContribution `pwd`/
@ -60,6 +67,9 @@ 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
@ -69,7 +79,8 @@ rc=$?
# Cleanup
apachectl -k stop -d `pwd`/tmp
sleep 2
kill `ps -f | grep -v grep | grep "$mc" | awk '{ print $2 }'`
sleep 1
if [ "$rc" = "0" ]; then
echo "OK"
fi

View file

@ -57,6 +57,9 @@
<service name="Total">
<t:binding.jsonrpc uri="Total"/>
</service>
<reference name="cache" target="Cache">
<t:binding.atom/>
</reference>
</component>
<component name="CurrencyConverter">
@ -66,4 +69,11 @@
</service>
</component>
<component name="Cache">
<t:implementation.cpp uri="../../components/cache/.libs/libmcache"/>
<service name="Cache">
<t:binding.atom uri="Cache"/>
</service>
</component>
</composite>

View file

@ -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)