Added test cases and scripts to test the HTTP binding support. Refactored httpd module and added a wiring httpd module. Implementation of the store demo prepared for ApacheCon.

git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@880601 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
jsdelfino 2009-11-16 06:01:41 +00:00
parent ada8802640
commit 51a97b5d93
24 changed files with 1497 additions and 831 deletions

View file

@ -20,7 +20,7 @@
/* $Rev$ $Date$ */
/**
* Test JSON data conversion functions.
* Test HTTP client functions.
*/
#include <assert.h>
@ -35,15 +35,66 @@
namespace tuscany {
namespace http {
bool contains(const std::string& str, const std::string& pattern) {
const bool contains(const std::string& str, const std::string& pattern) {
return str.find(pattern) != str.npos;
}
const double duration(struct timeval start, struct timeval end, int count) {
long t = (end.tv_sec * 1000 + end.tv_usec / 1000) - (start.tv_sec * 1000 + start.tv_usec / 1000);
return (double)t / (double)count;
}
std::ostringstream* curlWriter(const std::string& s, std::ostringstream* os) {
(*os) << s;
return os;
}
const bool testGet() {
CURLHandle ch;
{
std::ostringstream os;
const failable<list<std::ostringstream*>, std::string> r = get<std::ostringstream*>(curlWriter, &os, "http://localhost:8091", ch);
assert(hasValue(r));
assert(contains(os.str(), "HTTP/1.1 200 OK"));
assert(contains(os.str(), "It works"));
}
{
const failable<value, std::string> r = get("http://localhost:8091", ch);
assert(hasValue(r));
const value val = r;
assert(contains(val, "It works"));
}
return true;
}
const bool testGetLoop(const int count, CURLHandle& ch) {
if (count == 0)
return true;
const failable<value, std::string> r = get("http://localhost:8091", ch);
assert(hasValue(r));
const value val = r;
assert(contains(val, "It works"));
return testGetLoop(count - 1, ch);
}
const bool testGetPerf() {
const int count = 50;
CURLHandle ch;
struct timeval start;
struct timeval end;
{
testGetLoop(5, ch);
gettimeofday(&start, NULL);
testGetLoop(count, ch);
gettimeofday(&end, NULL);
std::cout << "Static GET test " << duration(start, end, count) << " ms" << std::endl;
}
return true;
}
const bool testEval() {
CURLHandle ch;
const value val = evalExpr(mklist<value>(std::string("echo"), std::string("Hello")), "http://localhost:8091/test", ch);
@ -83,8 +134,7 @@ const bool testEvalPerf() {
testEvalLoop(count, ch);
gettimeofday(&end, NULL);
long t = (end.tv_sec * 1000 + end.tv_usec / 1000) - (start.tv_sec * 1000 + start.tv_usec / 1000);
std::cout << "JSON-RPC eval echo test " << (t / count) << " ms" << std::endl;
std::cout << "JSON-RPC eval echo test " << duration(start, end, count) << " ms" << std::endl;
}
{
testBlobEvalLoop(5, ch);
@ -94,25 +144,7 @@ const bool testEvalPerf() {
testBlobEvalLoop(count, ch);
gettimeofday(&end, NULL);
long t = (end.tv_sec * 1000 + end.tv_usec / 1000) - (start.tv_sec * 1000 + start.tv_usec / 1000);
std::cout << "JSON-RPC eval blob test " << (t / count) << " ms" << std::endl;
}
}
const bool testGet() {
CURLHandle ch;
{
std::ostringstream os;
const failable<list<std::ostringstream*>, std::string> r = get<std::ostringstream*>(curlWriter, &os, "http://localhost:8091", ch);
assert(hasValue(r));
assert(contains(os.str(), "HTTP/1.1 200 OK"));
assert(contains(os.str(), "It works"));
}
{
const failable<value, std::string> r = get("http://localhost:8091", ch);
assert(hasValue(r));
const value val = r;
assert(contains(val, "It works"));
std::cout << "JSON-RPC eval blob test " << duration(start, end, count) << " ms" << std::endl;
}
return true;
}
@ -157,8 +189,7 @@ const bool testPostPerf() {
testPostLoop(count, val, ch);
gettimeofday(&end, NULL);
long t = (end.tv_sec * 1000 + end.tv_usec / 1000) - (start.tv_sec * 1000 + start.tv_usec / 1000);
std::cout << "ATOMPub POST small test " << (t / count) << " ms" << std::endl;
std::cout << "ATOMPub POST small test " << duration(start, end, count) << " ms" << std::endl;
}
{
const list<value> i = list<value>()
@ -177,9 +208,9 @@ const bool testPostPerf() {
testPostLoop(count, val, ch);
gettimeofday(&end, NULL);
long t = (end.tv_sec * 1000 + end.tv_usec / 1000) - (start.tv_sec * 1000 + start.tv_usec / 1000);
std::cout << "ATOMPub POST blob test " << (t / count) << " ms" << std::endl;
std::cout << "ATOMPub POST blob test " << duration(start, end, count) << " ms" << std::endl;
}
return true;
}
const bool testPut() {
@ -188,7 +219,7 @@ const bool testPut() {
<< (list<value>() << "price" << std::string("$2.99"));
const list<value> a = mklist<value>(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i);
CURLHandle ch;
value rc = put(a, "http://localhost:8091/test", ch);
value rc = put(a, "http://localhost:8091/test/111", ch);
assert(rc == value(true));
return true;
}
@ -207,10 +238,11 @@ int main() {
std::cout << "Testing..." << std::endl;
tuscany::http::testGet();
tuscany::http::testGetPerf();
tuscany::http::testPost();
//tuscany::http::testPostPerf();
tuscany::http::testPostPerf();
tuscany::http::testEval();
//tuscany::http::testEvalPerf();
tuscany::http::testEvalPerf();
tuscany::http::testFeed();
tuscany::http::testPut();
tuscany::http::testDel();

View file

@ -1,2 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>111</id><content type="application/xml"><item><javaClass>services.Item</javaClass><name>Apple</name><currency>USD</currency><symbol>$</symbol><price>2.99</price></item></content><link href="111"/></entry>
<entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>111</id><content type="application/xml"><item><javaClass>services.Item</javaClass><name>Apple</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>2.99</price></item></content><link href="111"/></entry>

View file

@ -1,2 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title type="text">Sample Feed</title><id>123456789</id><entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>111</id><content type="application/xml"><item>javaClass</item></content><link href="111"/></entry><entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>222</id><content type="application/xml"><item>javaClass</item></content><link href="222"/></entry><entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>333</id><content type="application/xml"><item>javaClass</item></content><link href="333"/></entry></feed>
<feed xmlns="http://www.w3.org/2005/Atom"><title type="text">Sample Feed</title><id>123456789</id><entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>111</id><content type="application/xml"><item><javaClass>services.Item</javaClass><name>Apple</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>2.99</price></item></content><link href="111"/></entry><entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>222</id><content type="application/xml"><item><javaClass>services.Item</javaClass><name>Orange</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>3.55</price></item></content><link href="222"/></entry><entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>333</id><content type="application/xml"><item><javaClass>services.Item</javaClass><name>Pear</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>1.55</price></item></content><link href="333"/></entry></feed>

View file

@ -20,13 +20,15 @@
# Setup
./httpd-conf tmp 8091 htdocs
cat >>tmp/conf/httpd.conf <<EOF
<Location /test>
SetHandler mod_tuscany
SetHandler mod_tuscany_eval
SCAContribution `pwd`/
SCAComposite httpd-test.composite
SCAComponent httpd-test
SCAImplementation httpd-test.scm
</Location>
EOF
apachectl -k start -d `pwd`/tmp
sleep 1
@ -36,5 +38,5 @@ rc=$?
# Cleanup
apachectl -k stop -d `pwd`/tmp
sleep 1
sleep 2
return $rc

View file

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

View file

@ -31,6 +31,7 @@ ServerName 127.0.0.1
Listen $port
DocumentRoot $htdocs
TypesConfig $here/conf/mime.types
LoadModule mod_tuscany $here/.libs/libmod_tuscany.so
LoadModule mod_tuscany_eval $here/.libs/libmod_tuscany_eval.so
LoadModule mod_tuscany_wiring $here/.libs/libmod_tuscany_wiring.so
EOF

View file

@ -22,13 +22,15 @@ echo "Testing..."
# Setup
./httpd-conf tmp 8090 htdocs
cat >>tmp/conf/httpd.conf <<EOF
<Location /test>
SetHandler mod_tuscany
SetHandler mod_tuscany_eval
SCAContribution `pwd`/
SCAComposite httpd-test.composite
SCAComponent httpd-test
SCAImplementation httpd-test.scm
</Location>
EOF
apachectl -k start -d `pwd`/tmp
sleep 1
@ -70,7 +72,7 @@ fi
# Cleanup
apachectl -k stop -d `pwd`/tmp
sleep 1
sleep 2
if [ "$rc" = "0" ]; then
echo "OK"
fi

View file

@ -19,25 +19,24 @@
-->
<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200903"
xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
targetNamespace="http://services"
name="catalogs">
<component name="FruitsCatalogWebService">
<implementation.java class="services.FruitsCatalogImpl"/>
<service name="Catalog">
<binding.ws/>
</service>
<property name="currencyCode">USD</property>
<reference name="currencyConverter" target="CurrencyConverterWebService">
<binding.ws/>
</reference>
</component>
<component name="VegetablesCatalogWebService">
<implementation.java class="services.VegetablesCatalogImpl"/>
<service name="Catalog">
<binding.ws/>
</service>
</component>
targetNamespace="http://test"
name="httpd-test">
<component name="httpd-test">
<t:implementation.scheme uri="httpd-test.scm"/>
<service name="test">
<t:binding.http uri="test"/>
</service>
</component>
<component name="httpd-client">
<t:implementation.scheme uri="httpd-client.scm"/>
<service name="client">
<t:binding.http uri="client"/>
</service>
<reference name="ref" target="test">
<t:binding.http/>
</reference>
</component>
</composite>

View file

@ -1,33 +1,29 @@
(; "JSON-RPC test case")
; JSON-RPC test case
(define (echo x) x)
(; "ATOMPub test case")
; ATOMPub test case
(define (getall)
'("Sample Feed" "123456789"
("Item" "111" (javaClass "services.Item") (name "Apple") (currency "USD") (symbol "$") (price 2.99))
("Item" "222" (javaClass "services.Item") (name "Orange") (currency "USD") (symbol "$") (price 3.55))
("Item" "333" (javaClass "services.Item") (name "Pear") (currency "USD") (symbol "$") (price 1.55)))
("Item" "111" ((javaClass "services.Item") (name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99)))
("Item" "222" ((javaClass "services.Item") (name "Orange") (currencyCode "USD") (currencySymbol "$") (price 3.55)))
("Item" "333" ((javaClass "services.Item") (name "Pear") (currencyCode "USD") (currencySymbol "$") (price 1.55))))
)
(define (get id)
(define entry '((javaClass "services.Item") (name "Apple") (currency "USD") (symbol "$") (price 2.99)))
(define entry '((javaClass "services.Item") (name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99)))
(cons "Item" (list id entry))
)
(define (post entry)
(display entry)
"123456789"
)
(define (put entry)
(display entry)
(define (put id entry)
true
)
(define (delete . args)
(display args)
(define (delete id)
true
)

View file

@ -0,0 +1,159 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/* $Rev$ $Date$ */
#ifndef tuscany_httpd_hpp
#define tuscany_httpd_hpp
/**
* HTTPD module utility functions.
*/
#include <string>
#include <iostream>
#include "apr_strings.h"
#include "apr_fnmatch.h"
#include "apr_lib.h"
#define APR_WANT_STRFUNC
#include "apr_want.h"
#include "ap_config.h"
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_request.h"
#include "http_protocol.h"
#include "http_log.h"
#include "http_main.h"
#include "util_script.h"
#include "util_md5.h"
#include "mod_core.h"
#include "list.hpp"
#include "value.hpp"
namespace tuscany {
namespace httpd {
/**
* Set to true to log requests and content.
*/
bool logRequests = false;
bool logContent = false;
/**
* Convert a path string to a list of values.
*/
const list<std::string> pathTokens(const char* p) {
if (p == NULL || p[0] == '\0')
return list<std::string>();
return tokenize("/", p + 1);
}
const list<value> pathValues(const list<std::string>& l) {
if (isNil(l))
return list<value>();
return cons<value>(car(l), pathValues(cdr(l)));
}
const list<value> path(const char* p) {
return pathValues(pathTokens(p));
}
/**
* Convert a path represented as a list of values to a string.
*/
const std::string path(const list<value>& p) {
if (isNil(p))
return "";
return "/" + car(p) + path(cdr(p));
}
/**
* Return the content type of a request.
*/
const char* optional(const char* s) {
if (s == NULL)
return "(null)";
return s;
}
const std::string contentType(const request_rec* r) {
return optional(apr_table_get(r->headers_in, "Content-Type"));
}
/**
* Log HTTP request info.
*/
int logHeader(void* r, const char* key, const char* value) {
std::cout << "header key: " << key << ", value: " << value << std::endl;
return 1;
}
const bool logRequest(request_rec* r, const std::string& msg) {
std::cout << msg << std::endl;
std::cout << "protocol: " << optional(r->protocol) << std::endl;
std::cout << "method: " << optional(r->method) << std::endl;
std::cout << "method number: " << r->method_number << std::endl;
std::cout << "content type: " << contentType(r) << std::endl;
std::cout << "content encoding: " << optional(r->content_encoding) << std::endl;
apr_table_do(logHeader, r, r->headers_in, NULL);
std::cout << "uri: " << optional(r->unparsed_uri) << std::endl;
std::cout << "path: " << optional(r->uri) << std::endl;
std::cout << "path info: " << optional(r->path_info) << std::endl;
std::cout << "filename: " << optional(r->filename) << std::endl;
std::cout << "path tokens: " << pathTokens(r->uri) << std::endl;
std::cout << "args: " << optional(r->args) << std::endl;
std::cout.flush();
return true;
}
/**
* Returns a list of key value pairs from the args in a query string.
*/
const list<value> queryArg(const std::string& s) {
const list<std::string> t = tokenize("=", s);
return mklist<value>(car(t).c_str(), cadr(t));
}
const list<list<value> > queryArgs(const request_rec* r) {
const char* a = r->args;
if (a == NULL)
return list<list<value> >();
return map<std::string, list<value>>(queryArg, tokenize("&", a));
}
/**
* Converts the args received in a POST to a list of key value pairs.
*/
const list<list<value> > postArgs(const list<value>& a) {
if (isNil(a))
return list<list<value> >();
const list<value> l = car(a);
return cons(l, postArgs(cdr(a)));
}
}
}
#endif /* tuscany_httpd_hpp */

View file

@ -0,0 +1,532 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/* $Rev$ $Date$ */
/**
* HTTPD module used to eval component implementations.
*/
#include <sys/stat.h>
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include "list.hpp"
#include "slist.hpp"
#include "value.hpp"
#include "element.hpp"
#include "monad.hpp"
#include "../atom/atom.hpp"
#include "../json/json.hpp"
#include "../eval/driver.hpp"
#include "../scdl/scdl.hpp"
#include "../cache/cache.hpp"
#include "curl.hpp"
#include "httpd.hpp"
extern "C" {
extern module AP_MODULE_DECLARE_DATA mod_tuscany_eval;
}
namespace tuscany {
namespace httpd {
namespace modeval {
/**
* Server configuration.
*/
class ServerConf {
public:
ServerConf() : home("") {
}
std::string home;
};
const ServerConf& serverConf(const request_rec* r) {
return *(ServerConf*)ap_get_module_config(r->server->module_config, &mod_tuscany_eval);
}
/**
* Port number used for wiring requests. Set it to zero to use the current
* server port number, set it to a port number to direct wiring requests
* to that port, for debugging or tracing for example.
*/
int debugWiringPort = 0;
/**
* Directory configuration.
*/
class DirConf {
public:
DirConf() : contributionPath(""), compositeName(""), componentName(""), implementationPath("") {
}
std::string contributionPath;
std::string compositeName;
std::string componentName;
std::string implementationPath;
cache::cached<failable<value, std::string> > component;
cache::cached<failable<value, std::string> > implementation;
};
DirConf& dirConf(const request_rec* r) {
return *(DirConf*)ap_get_module_config(r->per_dir_config, &mod_tuscany_eval);
}
/**
* Evaluate an expression against a component implementation.
*/
const failable<value, std::string> evalExpr(const value& expr, const value& impl) {
gc_pool pool;
eval::Env globalEnv = eval::setupEnvironment(pool);
if (logContent) {
std::cout<< "expr: " << expr << std::endl;
std::cout.flush();
}
const value val = eval::evalScript(expr, impl, globalEnv, pool);
if (logContent) {
std::cout<< "val: " << val << std::endl;
std::cout.flush();
}
if (isNil(val))
return mkfailure<value, std::string>("Could not evaluate expression");
return val;
}
/**
* Returns a list of param values other than the id and method args from a list
* of key value pairs.
*/
const list<value> queryParams(const list<list<value> >& a) {
if (isNil(a))
return list<value>();
const list<value> p = car(a);
if (car(p) == value("id") || car(p) == value("method"))
return queryParams(cdr(a));
return cons(cadr(p), queryParams(cdr(a)));
}
/**
* Write an HTTP result.
*/
const failable<int, std::string> writeResult(const failable<list<std::string>, std::string>& ls, const std::string& ct, request_rec* r) {
if (!hasValue(ls))
return mkfailure<int, std::string>(reason(ls));
std::ostringstream os;
write(ls, os);
if (logContent) {
std::cout<< "content: " << std::endl << os.str() << std::endl;
std::cout.flush();
}
const std::string etag(ap_md5(r->pool, (const unsigned char*)std::string(os.str()).c_str()));
const char* match = apr_table_get(r->headers_in, "If-None-Match");
apr_table_setn(r->headers_out, "ETag", apr_pstrdup(r->pool, etag.c_str()));
if (match != NULL && etag == match) {
r->status = HTTP_NOT_MODIFIED;
return OK;
}
ap_set_content_type(r, ct.c_str());
ap_rputs(std::string(os.str()).c_str(), r);
return OK;
}
/**
* Handle an HTTP GET.
*/
const failable<int, std::string> get(request_rec* r, const value& impl, const list<value>& px) {
// Inspect the query string
const list<list<value> > args = queryArgs(r);
const list<value> ia = assoc(value("id"), args);
const list<value> ma = assoc(value("method"), args);
// Evaluate a JSON-RPC request and return a JSON result
if (!isNil(ia) && !isNil(ma)) {
// Extract the request id, method and params
const value id = cadr(ia);
const value func = std::string(cadr(ma)).c_str();
const list<value> params = queryParams(args);
// Evaluate the request expression
const failable<value, std::string> val = evalExpr(cons<value>(func, eval::quotedParameters(append(params, px))), impl);
if (!hasValue(val))
return mkfailure<int, std::string>(reason(val));
// Return JSON result
json::JSONContext cx;
return writeResult(json::jsonResult(id, val, cx), "application/json-rpc", r);
}
// Evaluate an ATOM GET request and return an ATOM feed
const list<value> id(path(r->path_info));
if (isNil(id)) {
const failable<value, std::string> val = evalExpr(cons<value>("getall", eval::quotedParameters(px)), impl);
if (!hasValue(val))
return mkfailure<int, std::string>(reason(val));
const value feed = val;
return writeResult(atom::writeATOMFeed(atom::feedValuesToElements(feed)), "application/atom+xml;type=feed", r);
}
// Evaluate an ATOM GET and return an ATOM entry
const failable<value, std::string> val = evalExpr(cons<value>("get", eval::quotedParameters(cons<value>(car(id), px))), impl);
if (!hasValue(val))
return mkfailure<int, std::string>(reason(val));
const value entry = val;
return writeResult(atom::writeATOMEntry(atom::entryValuesToElements(entry)), "application/atom+xml;type=entry", r);
}
/**
* Read the content of a POST.
*/
const list<std::string> read(request_rec* r) {
char b[2048];
const int n = ap_get_client_block(r, b, 2048);
if (n <= 0)
return list<std::string>();
return cons(std::string(b, n), read(r));
}
/**
* Convert a URI value to an absolute URL.
*/
const char* url(const value& v, request_rec* r) {
std::string u = r->uri;
u.append("/");
u.append(v);
return ap_construct_url(r->pool, u.c_str(), r);
}
/**
* Convert an ATOM entry to a value.
*/
const value feedEntry(const list<value>& e) {
const list<value> v = elementsToValues(mklist<value>(caddr(e)));
return cons(car(e), mklist<value>(cadr(e), cdr<value>(car(v))));
}
/**
* Handle an HTTP POST.
*/
const failable<int, std::string> post(request_rec* r, const value& impl, const list<value>& px) {
const list<std::string> ls = read(r);
if (logContent) {
std::cout<< "content: " << std::endl;
write(ls, std::cout);
std::cout<< std::endl;
std::cout.flush();
}
// Evaluate a JSON-RPC request and return a JSON result
const std::string ct = contentType(r);
if (ct.find("application/json-rpc") != std::string::npos || ct.find("text/plain") != std::string::npos) {
json::JSONContext cx;
const list<value> json = elementsToValues(json::readJSON(ls, cx));
const list<list<value> > args = postArgs(json);
// Extract the request id, method and params
const value id = cadr(assoc(value("id"), args));
const value func = std::string(cadr(assoc(value("method"), args))).c_str();
const list<value> params = (list<value>)cadr(assoc(value("params"), args));
// Evaluate the request expression
const failable<value, std::string> val = evalExpr(cons<value>(func, eval::quotedParameters(append(params, px))), impl);
if (!hasValue(val))
return mkfailure<int, std::string>(reason(val));
// Return JSON result
return writeResult(json::jsonResult(id, val, cx), "application/json-rpc", r);
}
// Evaluate an ATOM POST request and return the created resource location
if (ct.find("application/atom+xml") != std::string::npos) {
// Evaluate the request expression
const value entry = feedEntry(atom::readEntry(ls));
const failable<value, std::string> val = evalExpr(cons<value>("post", eval::quotedParameters(cons<value>(entry, px))), impl);
if (!hasValue(val))
return mkfailure<int, std::string>(reason(val));
// Return the created resource location
apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, url(val, r)));
r->status = HTTP_CREATED;
return OK;
}
return HTTP_NOT_IMPLEMENTED;
}
/**
* Handle an HTTP PUT.
*/
const failable<int, std::string> put(request_rec* r, const value& impl, const list<value>& px) {
const list<std::string> ls = read(r);
if (logContent) {
std::cout<< "content: " << std::endl;
write(ls, std::cout);
std::cout<< std::endl;
std::cout.flush();
}
// Evaluate an ATOM PUT request
const list<value> id(path(r->path_info));
const value entry = feedEntry(atom::readEntry(ls));
const failable<value, std::string> val = evalExpr(cons<value>("put", eval::quotedParameters(append(mklist<value>(entry, car(id)), px))), impl);
if (!hasValue(val))
return mkfailure<int, std::string>(reason(val));
if (val == value(false))
return HTTP_NOT_FOUND;
return OK;
}
/**
* Handle an HTTP DELETE.
*/
const failable<int, std::string> del(request_rec* r, const value& impl, const list<value>& px) {
// Evaluate an ATOM delete request
const list<value> id(path(r->path_info));
const failable<value, std::string> val = evalExpr(cons<value>("delete", eval::quotedParameters(cons<value>(car(id), px))), impl);
if (!hasValue(val))
return mkfailure<int, std::string>(reason(val));
if (val == value(false))
return HTTP_NOT_FOUND;
return OK;
}
/**
* Report request execution status.
*/
const int reportStatus(const failable<int, std::string>& rc) {
if (!hasValue(rc))
return HTTP_INTERNAL_SERVER_ERROR;
return rc;
}
/**
* Read the SCDL configuration of a component.
*/
const failable<value, std::string> readComponent(const std::string& path, const std::string& name) {
// Read composite
std::ifstream is(path);
if (is.fail() || is.bad())
return mkfailure<value, std::string>("Could not read composite: " + path);
// Return the component
const list<value> c = scdl::components(readXML(streamList(is)));
const value comp = scdl::named(name, c);
if (isNil(comp))
return mkfailure<value, std::string>("Could not find component: " + name);
return comp;
}
const cache::cached<failable<value, std::string> > component(DirConf* conf) {
const std::string path(conf->contributionPath + conf->compositeName);
const lambda<failable<value, std::string>(std::string, std::string)> rc(readComponent);
const lambda<unsigned long(std::string)> ft(cache::latestFileTime);
return cache::cached<failable<value, std::string> >(curry(rc, path, conf->componentName), curry(ft, path));
}
/**
* Read a component implementation.
*/
const failable<value, std::string> readImplementation(const std::string path) {
std::ifstream is(path.c_str(), std::ios_base::in);
if (is.fail() || is.bad())
return mkfailure<value, std::string>("Could not read implementation: " + path);
const value impl = eval::readScript(is);
if (isNil(impl))
return mkfailure<value, std::string>("Could not read implementation: " + path);
return impl;
}
const cache::cached<failable<value, std::string> > implementation(const std::string& path) {
const lambda<failable<value, std::string>(std::string)> ri(readImplementation);
const lambda<unsigned long(std::string)> ft(cache::latestFileTime);
return cache::cached<failable<value, std::string> >(curry(ri, path), curry(ft, path));
}
/**
* Convert a list of component references to a list of HTTP proxy lambdas.
*/
const value mkproxy(const value& ref, const std::string& base, const http::CURLHandle& ch) {
return eval::primitiveProcedure(http::proxy(base + std::string(scdl::name(ref)), ch));
}
const list<value> proxies(const list<value>& refs, const std::string& base, const http::CURLHandle& ch) {
if (isNil(refs))
return refs;
return cons(mkproxy(car(refs), base, ch), proxies(cdr(refs), base, ch));
}
/**
* HTTP request handler.
*/
int handler(request_rec *r) {
if(strcmp(r->handler, "mod_tuscany_eval"))
return DECLINED;
// Log the request
if(logRequests)
logRequest(r, "mod_tuscany_eval::handler");
// Set up the read policy
const int rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK);
if(rc != OK)
return rc;
ap_should_client_block(r);
if(r->read_chunked == true && r->remaining == 0)
r->chunked = true;
//apr_table_setn(r->headers_out, "Connection", "close");
// Retrieve the latest component configuration
DirConf& conf = dirConf(r);
conf.component = cache::latest(conf.component);
const failable<value, std::string> comp(conf.component);
if (!hasValue(comp))
return HTTP_NOT_FOUND;
// Retrieve the latest implementation
const std::string path = conf.contributionPath + std::string(scdl::uri(scdl::implementation(comp)));
if (path != conf.implementationPath) {
conf.implementationPath = path;
conf.implementation = cache::latest(implementation(path));
}
else
conf.implementation = cache::latest(conf.implementation);
const failable<value, std::string> impl(conf.implementation);
if (!hasValue(impl))
return HTTP_NOT_FOUND;
// Convert component references to configured proxy lambdas
std::ostringstream base;
base << "http://localhost:" << (debugWiringPort == 0? ap_get_server_port(r) : debugWiringPort) << "/references/" << std::string(scdl::name(comp)) << "/";
http::CURLHandle ch;
const list<value> px(proxies(scdl::references(comp), base.str(), ch));
// Handle HTTP method
if (r->header_only)
return OK;
if(r->method_number == M_GET)
return reportStatus(get(r, impl, px));
if(r->method_number == M_POST)
return reportStatus(post(r, impl, px));
if(r->method_number == M_PUT)
return reportStatus(put(r, impl, px));
if(r->method_number == M_DELETE)
return reportStatus(del(r, impl, px));
return HTTP_NOT_IMPLEMENTED;
}
/**
* Configuration commands.
*/
const char *confHome(cmd_parms *cmd, void *dummy, const char *arg) {
ServerConf *c = (ServerConf*)ap_get_module_config(cmd->server->module_config, &mod_tuscany_eval);
c->home = arg;
return NULL;
}
const char *confContribution(cmd_parms *cmd, void *c, const char *arg) {
DirConf* conf = (DirConf*)c;
conf->contributionPath = arg;
conf->component = component(conf);
return NULL;
}
const char *confComposite(cmd_parms *cmd, void *c, const char *arg) {
DirConf* conf = (DirConf*)c;
conf->compositeName = arg;
conf->component = component(conf);
return NULL;
}
const char *confComponent(cmd_parms *cmd, void *c, const char *arg) {
DirConf* conf = (DirConf*)c;
conf->componentName = arg;
conf->component = component(conf);
return NULL;
}
void *makeDirConf(apr_pool_t *p, char *dirspec) {
DirConf* c = new (apr_palloc(p, sizeof(DirConf))) DirConf();
apr_pool_cleanup_register(p, c, gc_pool_cleanupCallback<DirConf>, apr_pool_cleanup_null) ;
return c;
}
void* makeServerConf(apr_pool_t *p, server_rec *s) {
ServerConf* c = new (apr_palloc(p, sizeof(ServerConf))) ServerConf();
apr_pool_cleanup_register(p, c, gc_pool_cleanupCallback<ServerConf>, apr_pool_cleanup_null) ;
return c;
}
/**
* HTTP server module declaration.
*/
const command_rec commands[] = {
AP_INIT_TAKE1("TuscanyHome", (const char*(*)())confHome, NULL, RSRC_CONF, "Tuscany home directory"),
AP_INIT_TAKE1("SCAContribution", (const char*(*)())confContribution, NULL, ACCESS_CONF, "SCA contribution location"),
AP_INIT_TAKE1("SCAComposite", (const char*(*)())confComposite, NULL, ACCESS_CONF, "SCA composite location"),
AP_INIT_TAKE1("SCAComponent", (const char*(*)())confComponent, NULL, ACCESS_CONF, "SCA component name"),
{NULL}
};
int postConfig(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) {
return OK;
}
void childInit(apr_pool_t* p, server_rec* svr_rec) {
ServerConf *c = (ServerConf*)ap_get_module_config(svr_rec->module_config, &mod_tuscany_eval);
if(c == NULL) {
std::cerr << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << std::endl;
exit(APEXIT_CHILDFATAL);
}
}
void registerHooks(apr_pool_t *p) {
ap_hook_post_config(postConfig, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_child_init(childInit, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_handler(handler, NULL, NULL, APR_HOOK_MIDDLE);
}
}
}
}
extern "C" {
module AP_MODULE_DECLARE_DATA mod_tuscany_eval = {
STANDARD20_MODULE_STUFF,
// dir config
tuscany::httpd::modeval::makeDirConf,
// dir merger, default is to override
NULL,
// server config
tuscany::httpd::modeval::makeServerConf,
// merge server config
NULL,
// command table
tuscany::httpd::modeval::commands,
// register hooks
tuscany::httpd::modeval::registerHooks
};
}

View file

@ -0,0 +1,276 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/* $Rev$ $Date$ */
/**
* HTTPD module used to wire components.
*/
#include <sys/stat.h>
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include "list.hpp"
#include "slist.hpp"
#include "value.hpp"
#include "monad.hpp"
#include "../scdl/scdl.hpp"
#include "../cache/cache.hpp"
#include "httpd.hpp"
extern "C" {
extern module AP_MODULE_DECLARE_DATA mod_tuscany_wiring;
}
namespace tuscany {
namespace httpd {
namespace modwiring {
/**
* Server configuration.
*/
class ServerConf {
public:
std::string home;
ServerConf() : home("") {
}
};
const ServerConf& serverConf(const request_rec* r) {
return *(ServerConf*)ap_get_module_config(r->server->module_config, &mod_tuscany_wiring);
}
/**
* Set to true to wire using mod_proxy, false to wire using HTTP client redirects.
*/
const bool useModProxy = true;
/**
* Directory configuration.
*/
class DirConf {
public:
DirConf() : contributionPath(""), compositeName("") {
}
std::string contributionPath;
std::string compositeName;
cache::cached<failable<list<value>, std::string> > components;
};
DirConf& dirConf(const request_rec* r) {
return *(DirConf*)ap_get_module_config(r->per_dir_config, &mod_tuscany_wiring);
}
/**
* Read the SCDL configuration of the deployed components.
*/
const failable<list<value>, std::string> readComponents(const std::string& path) {
std::ifstream is(path);
if (is.fail() || is.bad())
return mkfailure<list<value>, std::string>("Could not read composite: " + path);
return scdl::components(readXML(streamList(is)));
}
const cache::cached<failable<list<value>, std::string> > components(DirConf* conf) {
const std::string path(conf->contributionPath + conf->compositeName);
const lambda<failable<list<value>, std::string>(std::string)> rc(readComponents);
const lambda<unsigned long(std::string)> ft(cache::latestFileTime);
return cache::cached<failable<list<value>, std::string> >(curry(rc, path), curry(ft, path));
}
/**
* Returns true if a URI is absolute.
*/
const bool isAbsolute(const std::string& uri) {
return uri.find("://") != std::string::npos;
}
/**
* Translate an HTTP request URI. Wire a URI in the form /references/component-name/reference-name
* to the target of the reference.
*/
int translate(request_rec *r) {
if (strncmp(r->uri, "/references/", 12) != 0)
return DECLINED;
const list<value> rpath(path(r->uri));
// Log the request
if(logRequests)
logRequest(r, "mod_tuscany_wiring::translate");
// Find the requested component, reference and its target configuration
DirConf& conf = dirConf(r);
conf.components = cache::latest(conf.components);
const failable<list<value>, std::string> comps(conf.components);
if (!hasValue(comps))
return HTTP_INTERNAL_SERVER_ERROR;
const value comp(scdl::named(cadr(rpath), list<value>(comps)));
if (isNil(comp))
return HTTP_NOT_FOUND;
const value ref(scdl::named(caddr(rpath), scdl::references(comp)));
if (isNil(ref))
return HTTP_NOT_FOUND;
const std::string target(scdl::target(ref));
// Absolute target URI
if (isAbsolute(target)) {
// Wire using mod_proxy
if (useModProxy) {
r->filename = apr_pstrdup(r->pool, std::string("proxy:" + target).c_str());
r->proxyreq = PROXYREQ_REVERSE;
r->handler = "proxy-server";
return OK;
}
// Wire using an HTTP client redirect
r->status = HTTP_MOVED_TEMPORARILY;
apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, target.c_str()));
r->handler = "mod_tuscany_wiring";
return OK;
}
// Local internal redirect
r->filename = apr_pstrdup(r->pool, std::string("/redirect:/" + target).c_str());
r->handler = "mod_tuscany_wiring";
return OK;
}
/**
* Construct a redirect URI.
*/
const std::string redirect(const std::string& file, const std::string& pi) {
return file + pi;
}
const std::string redirect(const std::string& file, const std::string& pi, const std::string& args) {
return file + pi + "?" + args;
}
/**
* HTTP request internal redirect handler.
*/
int handler(request_rec *r) {
if(strcmp(r->handler, "mod_tuscany_wiring"))
return DECLINED;
// Log the request
if(logRequests)
logRequest(r, "mod_tuscany_wiring::handler");
// Do an internal redirect
if (r->filename == NULL || strncmp(r->filename, "/redirect:", 10) != 0)
return DECLINED;
if (r->args == NULL) {
ap_internal_redirect(apr_pstrdup(r->pool, redirect(r->filename + 10, r->path_info).c_str()), r);
return OK;
}
ap_internal_redirect(apr_pstrdup(r->pool, redirect(r->filename + 10, r->path_info, r->args).c_str()), r);
return OK;
}
/**
* Configuration commands.
*/
const char *confHome(cmd_parms *cmd, void *dummy, const char *arg) {
ServerConf *c = (ServerConf*)ap_get_module_config(cmd->server->module_config, &mod_tuscany_wiring);
c->home = arg;
return NULL;
}
const char *confContribution(cmd_parms *cmd, void *c, const char *arg) {
DirConf* conf = (DirConf*)c;
conf->contributionPath = arg;
conf->components = components(conf);
return NULL;
}
const char *confComposite(cmd_parms *cmd, void *c, const char *arg) {
DirConf* conf = (DirConf*)c;
conf->compositeName = arg;
conf->components = components(conf);
return NULL;
}
void *makeDirConf(apr_pool_t *p, char *dirspec) {
DirConf* c = new (apr_palloc(p, sizeof(DirConf))) DirConf();
apr_pool_cleanup_register(p, c, gc_pool_cleanupCallback<DirConf>, apr_pool_cleanup_null) ;
return c;
}
void* makeServerConf(apr_pool_t *p, server_rec *s) {
ServerConf* c = new (apr_palloc(p, sizeof(ServerConf))) ServerConf();
apr_pool_cleanup_register(p, c, gc_pool_cleanupCallback<ServerConf>, apr_pool_cleanup_null) ;
return c;
}
/**
* HTTP server module declaration.
*/
const command_rec commands[] = {
AP_INIT_TAKE1("TuscanyHome", (const char*(*)())confHome, NULL, RSRC_CONF, "Tuscany home directory"),
AP_INIT_TAKE1("SCAContribution", (const char*(*)())confContribution, NULL, ACCESS_CONF, "SCA contribution location"),
AP_INIT_TAKE1("SCAComposite", (const char*(*)())confComposite, NULL, ACCESS_CONF, "SCA composite location"),
{NULL}
};
int postConfig(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) {
return OK;
}
void childInit(apr_pool_t* p, server_rec* svr_rec) {
ServerConf *conf = (ServerConf*)ap_get_module_config(svr_rec->module_config, &mod_tuscany_wiring);
if(conf == NULL) {
std::cerr << "[Tuscany] Due to one or more errors mod_tuscany_wiring loading failed. Causing apache to stop loading." << std::endl;
exit(APEXIT_CHILDFATAL);
}
}
void registerHooks(apr_pool_t *p) {
ap_hook_post_config(postConfig, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_child_init(childInit, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_handler(handler, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_translate_name(translate, NULL, NULL, APR_HOOK_FIRST);
}
}
}
}
extern "C" {
module AP_MODULE_DECLARE_DATA mod_tuscany_wiring = {
STANDARD20_MODULE_STUFF,
// dir config
tuscany::httpd::modwiring::makeDirConf,
// dir merger, default is to override
NULL,
// server config
tuscany::httpd::modwiring::makeServerConf,
// merge server config
NULL,
// command table
tuscany::httpd::modwiring::commands,
// register hooks
tuscany::httpd::modwiring::registerHooks
};
}

View file

@ -1,555 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/* $Rev$ $Date$ */
/**
* Tuscany HTTPD server module.
*/
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include "apr_strings.h"
#include "apr_fnmatch.h"
#include "apr_lib.h"
#define APR_WANT_STRFUNC
#include "apr_want.h"
#include "ap_config.h"
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_request.h"
#include "http_protocol.h"
#include "http_log.h"
#include "http_main.h"
#include "util_script.h"
#include "util_md5.h"
#include "mod_core.h"
#include "list.hpp"
#include "slist.hpp"
#include "value.hpp"
#include "element.hpp"
#include "monad.hpp"
#include "../atom/atom.hpp"
#include "../json/json.hpp"
#include "../eval/driver.hpp"
extern "C" {
extern module AP_MODULE_DECLARE_DATA mod_tuscany;
}
namespace tuscany {
namespace httpd {
/**
* Set to true to log requests and content.
*/
bool logRequests = false;
bool logContent = false;
/**
* Server configuration.
*/
struct ServerConf {
const char* home;
};
/**
* Directory configuration.
*/
struct DirConf {
const char* contribution;
const char* component;
const char* implementation;
};
/**
* Returns the server conf for a request.
*/
const ServerConf& serverConf(const request_rec* r) {
return *(ServerConf*)ap_get_module_config(r->server->module_config, &mod_tuscany);
}
const std::string home(request_rec* r) {
return serverConf(r).home;
}
/**
* Returns the dir conf for a request.
*/
const DirConf& dirConf(const request_rec* r) {
return *(DirConf*)ap_get_module_config(r->per_dir_config, &mod_tuscany);
}
const std::string contribution(request_rec* r) {
return dirConf(r).contribution;
}
const std::string component(request_rec* r) {
return dirConf(r).component;
}
const std::string implementation(request_rec* r) {
return dirConf(r).implementation;
}
/**
* Returns an HTTP request path as a list of strings.
*/
const list<std::string> pathTokens(const request_rec* r) {
const char* p = r->path_info;
if (p == NULL || p[0] == '\0')
return list<std::string>();
return tokenize("/", p + 1);
}
const list<value> pathValues(const list<std::string>& l) {
if (isNil(l))
return list<value>();
return cons<value>(car(l), pathValues(cdr(l)));
}
const list<value> path(const request_rec* r) {
return pathValues(pathTokens(r));
}
/**
* Return the content type of a request.
*/
const char* optional(const char* s) {
if (s == NULL)
return "(null)";
return s;
}
const std::string contentType(const request_rec* r) {
return optional(apr_table_get(r->headers_in, "Content-Type"));
}
/**
* Log HTTP request info.
*/
int logHeader(void* r, const char* key, const char* value) {
std::cout << "header key: " << key << ", value: " << value << std::endl;
return 1;
}
const bool logRequest(request_rec* r) {
std::cout << "mod-tuscany..." << std::endl;
std::cout << "tuscany home: " << home(r) << std::endl;
std::cout << "contribution: " << contribution(r) << std::endl;
std::cout << "component: " << component(r) << std::endl;
std::cout << "protocol: " << optional(r->protocol) << std::endl;
std::cout << "method: " << optional(r->method) << std::endl;
std::cout << "method number: " << r->method_number << std::endl;
std::cout << "content type: " << contentType(r) << std::endl;
std::cout << "content encoding: " << optional(r->content_encoding) << std::endl;
apr_table_do(logHeader, r, r->headers_in, NULL);
std::cout << "uri: " << optional(r->uri) << std::endl;
std::cout << "path info: " << optional(r->path_info) << std::endl;
std::cout << "filename: " << optional(r->filename) << std::endl;
std::cout << "path: " << pathTokens(r) << std::endl;
std::cout << "args: " << optional(r->args) << std::endl;
std::cout.flush();
return true;
}
/**
* Evaluate an expression against a component implementation.
*/
const value evalExprLoop(std::istream& is, const value& expr, eval::Env& globalEnv, const gc_pool& pool) {
value in = eval::readValue(is);
if (isNil(in))
return eval::evalExpr(expr, globalEnv, pool);
eval::evalExpr(in, globalEnv, pool);
return evalExprLoop(is, expr, globalEnv, pool);
}
const failable<value, std::string> evalExpr(const value& expr, const std::string& contrib, const std::string& impl) {
// Retrieve the component implementation
const std::string path = contrib + impl;
std::ifstream is(path.c_str(), std::ios_base::in);
if (is.fail() || is.bad())
return std::string("HTTP_NOT_FOUND");
// Evaluate the expr
gc_pool pool;
eval::Env globalEnv = eval::setupEnvironment(pool);
std::cout<< "expr: " << expr << std::endl;
std::cout.flush();
const value val = evalExprLoop(is, expr, globalEnv, pool);
std::cout<< "val: " << val << std::endl;
std::cout.flush();
if (isNil(val))
return std::string("Could not evaluate expression");
return val;
}
/**
* Returns a list of key value pairs from the args in a query string.
*/
const list<value> queryArg(std::string s) {
const list<std::string> t = tokenize("=", s);
return mklist<value>(car(t).c_str(), cadr(t));
}
const list<list<value> > queryArgs(const request_rec* r) {
const char* a = r->args;
if (a == NULL)
return list<list<value> >();
return map<std::string, list<value>>(queryArg, tokenize("&", a));
}
/**
* Returns a list of param values other than the id and method args from a list
* of key value pairs.
*/
const list<value> queryParams(list<list<value> > a) {
if (isNil(a))
return list<value>();
const list<value> p = car(a);
if (car(p) == value("id") || car(p) == value("method"))
return queryParams(cdr(a));
return cons(cadr(p), queryParams(cdr(a)));
}
/**
* Write an HTTP result.
*/
const failable<int, std::string> writeResult(const failable<list<std::string>, std::string> ls, const std::string& ct, request_rec* r) {
if (!hasValue(ls))
return std::string(ls);
std::ostringstream os;
write(ls, os);
if (logContent) {
std::cout<< "content: " << std::endl << os.str() << std::endl;
std::cout.flush();
}
std::string etag(ap_md5(r->pool, (const unsigned char*)std::string(os.str()).c_str()));
const char* match = apr_table_get(r->headers_in, "If-None-Match");
apr_table_setn(r->headers_out, "ETag", etag.c_str());
if (match != NULL && etag == match) {
r->status = HTTP_NOT_MODIFIED;
return OK;
}
ap_set_content_type(r, ct.c_str());
ap_rputs(std::string(os.str()).c_str(), r);
return OK;
}
/**
* Handle an HTTP GET.
*/
const failable<int, std::string> get(request_rec* r) {
// Inspect the query string
const list<list<value> > args = queryArgs(r);
const list<value> ia = assoc(value("id"), args);
const list<value> ma = assoc(value("method"), args);
// Evaluate a JSON-RPC request and return a JSON result
if (!isNil(ia) && !isNil(ma)) {
// Extract the request id, method and params
const value id = cadr(ia);
const value func = std::string(cadr(ma)).c_str();
const list<value> params = queryParams(args);
// Evaluate the request expression
const failable<value, std::string> val = evalExpr(cons(func, eval::quotedParameters(params)), contribution(r), implementation(r));
if (!hasValue(val))
return std::string(val);
// Return JSON result
json::JSONContext cx;
return writeResult(json::jsonResult(id, val, cx), "application/json-rpc", r);
}
// Evaluate an ATOM GET request and return an ATOM feed
if (isNil(path(r))) {
const failable<value, std::string> val = evalExpr(mklist<value>("getall"), contribution(r), implementation(r));
if (!hasValue(val))
return std::string(val);
const value feed = val;
return writeResult(atom::writeATOMFeed(atom::feedValuesToElements(feed)), "application/atom+xml;type=feed", r);
}
// Evaluate an ATOM GET and return an ATOM entry
const failable<value, std::string> val = evalExpr(cons<value>("get", path(r)), contribution(r), implementation(r));
if (!hasValue(val))
return std::string(val);
const value entry = val;
return writeResult(atom::writeATOMEntry(atom::entryValuesToElements(entry)), "application/atom+xml;type=entry", r);
}
/**
* Read the content of a POST.
*/
const list<std::string> read(request_rec* r) {
char b[2048];
const int n = ap_get_client_block(r, b, 2048);
if (n <= 0)
return list<std::string>();
return cons(std::string(b, n), read(r));
}
/**
* Converts the args received in a POST to a list of key value pairs.
*/
const list<list<value> > postArgs(list<value> a) {
if (isNil(a))
return list<list<value> >();
const list<value> l = car(a);
return cons(l, postArgs(cdr(a)));
}
const char* url(const value& v, request_rec* r) {
std::string u = r->uri;
u.append("/");
u.append(v);
return ap_construct_url(r->pool, u.c_str(), r);
}
/**
* Convert an ATOM entry to a value.
*/
const value feedEntry(const list<value> e) {
const list<value> v = elementsToValues(mklist<value>(caddr(e)));
return cons(car(e), mklist<value>(cadr(e), cdr<value>(car(v))));
}
/**
* Handle an HTTP POST.
*/
const failable<int, std::string> post(request_rec* r) {
const list<std::string> ls = read(r);
if (logContent) {
std::cout<< "content: " << std::endl;
write(ls, std::cout);
std::cout<< std::endl;
std::cout.flush();
}
// Evaluate a JSON-RPC request and return a JSON result
const std::string ct = contentType(r);
if (ct.find("application/json-rpc") != std::string::npos || ct.find("text/plain") != std::string::npos) {
json::JSONContext cx;
const list<value> json = elementsToValues(json::readJSON(ls, cx));
const list<list<value> > args = postArgs(json);
// Extract the request id, method and params
const value id = cadr(assoc(value("id"), args));
const value func = std::string(cadr(assoc(value("method"), args))).c_str();
const list<value> params = (list<value>)cadr(assoc(value("params"), args));
// Evaluate the request expression
const failable<value, std::string> val = evalExpr(cons(func, eval::quotedParameters(params)), contribution(r), implementation(r));
if (!hasValue(val))
return std::string(val);
// Return JSON result
return writeResult(json::jsonResult(id, val, cx), "application/json-rpc", r);
}
// Evaluate an ATOM POST request and return the created resource location
if (ct.find("application/atom+xml") != std::string::npos) {
// Evaluate the request expression
const value entry = feedEntry(atom::readEntry(ls));
const failable<value, std::string> val = evalExpr(cons<value>("post", eval::quotedParameters(mklist<value>(entry))), contribution(r), implementation(r));
if (!hasValue(val))
return std::string(val);
// Return the created resource location
apr_table_setn(r->headers_out, "Location", url(val, r));
r->status = HTTP_CREATED;
return OK;
}
return HTTP_NOT_IMPLEMENTED;
}
/**
* Handle an HTTP PUT.
*/
const failable<int, std::string> put(request_rec* r) {
const list<std::string> ls = read(r);
std::cout<< "content: " << std::endl;
write(ls, std::cout);
std::cout<< std::endl;
std::cout.flush();
// Evaluate an ATOM PUT request
const value entry = feedEntry(atom::readEntry(ls));
const failable<value, std::string> val = evalExpr(cons<value>("put", eval::quotedParameters(mklist<value>(entry))), contribution(r), implementation(r));
if (!hasValue(val))
return std::string(val);
if (val == value(false))
return HTTP_NOT_FOUND;
return OK;
}
/**
* Handle an HTTP DELETE.
*/
const failable<int, std::string> del(request_rec* r) {
// Evaluate an ATOM delete request
const failable<value, std::string> val = evalExpr(cons<value>("delete", path(r)), contribution(r), implementation(r));
if (!hasValue(val))
return std::string(val);
if (val == value(false))
return HTTP_NOT_FOUND;
return OK;
}
/**
* Report request execution status.
*/
const int reportStatus(const failable<int, std::string> rc) {
if (!hasValue(rc))
return HTTP_INTERNAL_SERVER_ERROR;
return rc;
}
/**
* HTTP request handler entry point.
*/
int handler(request_rec *r) {
if(strcmp(r->handler, "mod_tuscany"))
return DECLINED;
// Log the request
if(logRequests)
logRequest(r);
// Set up the read policy
const int rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK);
if(rc != OK)
return rc;
ap_should_client_block(r);
if(r->read_chunked == true && r->remaining == 0)
r->chunked = true;
//apr_table_setn(r->headers_out, "Connection", "close");
// Handle HTTP method
if (r->header_only)
return OK;
if(r->method_number == M_GET)
return reportStatus(get(r));
if(r->method_number == M_POST)
return reportStatus(post(r));
if(r->method_number == M_PUT)
return reportStatus(put(r));
if(r->method_number == M_DELETE)
return reportStatus(del(r));
return HTTP_NOT_IMPLEMENTED;
}
/**
* Configuration commands.
*/
const char *confHome(cmd_parms *cmd, void *dummy, const char *arg) {
ServerConf *conf = (ServerConf*)ap_get_module_config(cmd->server->module_config, &mod_tuscany);
conf->home = apr_pstrdup(cmd->pool, arg);
return NULL;
}
const char *confContribution(cmd_parms *cmd, void *c, const char *arg) {
DirConf *conf = (DirConf*)c;
conf->contribution = apr_pstrdup(cmd->pool, arg);
return NULL;
}
const char *confComponent(cmd_parms *cmd, void *c, const char *arg) {
DirConf *conf = (DirConf*)c;
conf->component = apr_pstrdup(cmd->pool, arg);
return NULL;
}
const char *confImplementation(cmd_parms *cmd, void *c, const char *arg) {
DirConf *conf = (DirConf*)c;
conf->implementation = apr_pstrdup(cmd->pool, arg);
return NULL;
}
void *makeDirConf(apr_pool_t *p, char *dirspec) {
DirConf* conf = (DirConf*)apr_palloc(p, sizeof(*conf));
conf->contribution = "";
conf->component = "";
return conf;
}
void* makeServerConf(apr_pool_t *p, server_rec *s) {
ServerConf* conf = (ServerConf* )apr_palloc(p, sizeof(*conf));
conf->home = "";
return conf;
}
/**
* HTTP server module declarations.
*/
const command_rec commands[] = {
AP_INIT_TAKE1("TuscanyHome", (const char*(*)())confHome, NULL, RSRC_CONF, "Tuscany home directory"),
AP_INIT_TAKE1("SCAContribution", (const char*(*)())confContribution, NULL, ACCESS_CONF, "SCA contribution location"),
AP_INIT_TAKE1("SCAComponent", (const char*(*)())confComponent, NULL, ACCESS_CONF, "SCA component name"),
AP_INIT_TAKE1("SCAImplementation", (const char*(*)())confImplementation, NULL, ACCESS_CONF, "SCA component implementation"),
{NULL}
};
int init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) {
return OK;
}
void childInit(apr_pool_t* p, server_rec* svr_rec) {
ServerConf *conf = (ServerConf*)ap_get_module_config(svr_rec->module_config, &mod_tuscany);
if(conf == NULL) {
std::cerr << "[Tuscany] Due to one or more errors mod_rest loading failed. Causing apache2 to stop loading" << std::endl;
exit(APEXIT_CHILDFATAL);
}
}
void registerHooks(apr_pool_t *p) {
ap_hook_handler(handler, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_post_config(init, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_child_init(childInit, NULL, NULL, APR_HOOK_MIDDLE);
}
}
}
extern "C" {
module AP_MODULE_DECLARE_DATA mod_tuscany = {
STANDARD20_MODULE_STUFF,
// dir config
tuscany::httpd::makeDirConf,
// dir merger, default is to override
NULL,
// server config
tuscany::httpd::makeServerConf,
// merge server config
NULL,
// command table
tuscany::httpd::commands,
// register hooks
tuscany::httpd::registerHooks
};
}

View file

@ -0,0 +1,92 @@
#!/bin/sh
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
echo "Testing..."
# Setup
./httpd-conf tmp 8092 htdocs
cat >>tmp/conf/httpd.conf <<EOF
<Location /test>
SetHandler mod_tuscany_eval
SCAContribution `pwd`/
SCAComposite httpd-test.composite
SCAComponent httpd-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
</Location>
<Location /references>
SetHandler mod_tuscany_wiring
SCAContribution `pwd`/
SCAComposite httpd-test.composite
</Location>
EOF
apachectl -k start -d `pwd`/tmp
sleep 1
# Test HTTP GET
curl http://localhost:8092/index.html 2>/dev/null >tmp/index.html
diff tmp/index.html htdocs/index.html
rc=$?
# Test ATOMPub
if [ "$rc" = "0" ]; then
curl http://localhost:8092/client/ >tmp/feed.xml 2>/dev/null
diff tmp/feed.xml htdocs/feed.xml
rc=$?
fi
if [ "$rc" = "0" ]; then
curl http://localhost:8092/client/111 >tmp/entry.xml 2>/dev/null
diff tmp/entry.xml htdocs/entry.xml
rc=$?
fi
if [ "$rc" = "0" ]; then
curl http://localhost:8092/client/ -X POST -H "Content-type: application/atom+xml" --data @htdocs/entry.xml 2>/dev/null
rc=$?
fi
if [ "$rc" = "0" ]; then
curl http://localhost:8092/client/111 -X PUT -H "Content-type: application/atom+xml" --data @htdocs/entry.xml 2>/dev/null
rc=$?
fi
if [ "$rc" = "0" ]; then
curl http://localhost:8092/client/111 -X DELETE 2>/dev/null
rc=$?
fi
# Test JSON-RPC
if [ "$rc" = "0" ]; then
curl http://localhost:8092/client/ -X POST -H "Content-type: application/json-rpc" --data @htdocs/json-request.txt >tmp/json-result.txt 2>/dev/null
diff tmp/json-result.txt htdocs/json-result.txt
rc=$?
fi
# Cleanup
apachectl -k stop -d `pwd`/tmp
sleep 2
if [ "$rc" = "0" ]; then
echo "OK"
fi
return $rc

View file

@ -114,6 +114,15 @@ bool testJSONRPC() {
write(writeJSON(e, cx), os);
assert(os.str() == "{\"id\":1,\"result\":[\"Service.get\",\"Service.getTotal\"]}");
}
{
const std::string f("{\"id\":1,\"result\":[\"Sample Feed\",\"123456789\",[\"Item\",\"111\",{\"javaClass\":\"services.Item\",\"name\":\"Apple\",\"currencyCode\":\"USD\",\"currencySymbol\":\"$\",\"price\":2.99}],[\"Item\",\"222\",{\"javaClass\":\"services.Item\",\"name\":\"Orange\",\"currencyCode\":\"USD\",\"currencySymbol\":\"$\",\"price\":3.55}],[\"Item\",\"333\",{\"javaClass\":\"services.Item\",\"name\":\"Pear\",\"currencyCode\":\"USD\",\"currencySymbol\":\"$\",\"price\":1.55}]]}");
const list<value> r = readJSON(mklist(f), cx);
const list<value> v = elementsToValues(r);
const list<value> e = valuesToElements(v);
std::ostringstream os;
write(writeJSON(e, cx), os);
assert(os.str() == f);
}
return true;
}

View file

@ -0,0 +1,9 @@
; Currency converter implementation
(define (convert from to amount)
(if (equal? to "EUR") (* amount 0.70) amount)
)
(define (symbol currency)
(if (equal? currency "EUR") "E" "$")
)

View file

@ -19,14 +19,14 @@
-->
<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200903"
xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
targetNamespace="http://services"
targetNamespace="http://services"
name="currency">
<component name="CurrencyConverterWebService">
<implementation.java class="services.CurrencyConverterImpl"/>
<service name="CurrencyConverter">
<binding.ws/>
</service>
</component>
<component name="CurrencyConverterWebService">
<t:implementation.scheme script="currency-converter.scm"/>
<service name="CurrencyConverter">
<binding.http uri="currency-converter"/>
</service>
</component>
</composite>

View file

@ -0,0 +1,19 @@
; Catalog implementation
(define (get converter)
(display "catalog")
(define (convert price) (converter "convert" "USD" "USD" price))
(define code "USD")
(define symbol (converter "symbol" code))
(list
(list (list 'javaClass "services.Item") (list 'name "Apple") (list 'currencyCode code) (list 'currencySymbol symbol) (list 'price (convert 2.99)))
(list (list 'javaClass "services.Item") (list 'name "Orange") (list 'currencyCode code) (list 'currencySymbol symbol) (list 'price (convert 3.55)))
(list (list 'javaClass "services.Item") (list 'name "Pear") (list 'currencyCode code) (list 'currencySymbol symbol) (list 'price (convert 1.55)))
)
)
; TODO remove these JSON-RPC specific functions
(define (system.listMethods converter) (list "Service.get"))
(define Service.get get)

View file

@ -0,0 +1,26 @@
; Shopping cart implementation
(define (post item)
(uuid)
)
(define (getall)
(cons "Sample Feed" (cons (uuid) '()))
)
(define (get id)
(define entry (list (list 'name "Apple") (list 'currencyCode "USD") (list 'currencySymbol "$") (list 'price 2.99)))
(cons "Item" (list id entry))
)
(define (delete id)
true
)
(define (gettotal)
11.0
)
; TODO remove these JSON-RPC specific functions
(define (system.listMethods) (list "Service.getTotal"))
(define Service.getTotal gettotal)

View file

@ -20,40 +20,55 @@
echo "Testing..."
# Setup
../../modules/http/httpd-conf tmp 8092 htdocs
../../modules/http/httpd-conf tmp 8093 htdocs
cat >>tmp/conf/httpd.conf <<EOF
<Location /Catalog>
SetHandler mod_tuscany
SetHandler mod_tuscany_eval
SCAContribution `pwd`/
SCAComponent store
SCAImplementation store.scm
SCAComposite store.composite
SCAComponent Catalog
</Location>
<Location /Total>
SetHandler mod_tuscany
SetHandler mod_tuscany_eval
SCAContribution `pwd`/
SCAComponent store
SCAImplementation store.scm
SCAComposite store.composite
SCAComponent ShoppingCart
</Location>
<Location /ShoppingCart>
SetHandler mod_tuscany
SetHandler mod_tuscany_eval
SCAContribution `pwd`/
SCAComponent store
SCAImplementation store.scm
SCAComposite store.composite
SCAComponent ShoppingCart
</Location>
<Location /CurrencyConverter>
SetHandler mod_tuscany_eval
SCAContribution `pwd`/
SCAComposite store.composite
SCAComponent CurrencyConverter
</Location>
<Location /references>
SetHandler mod_tuscany_wiring
SCAContribution `pwd`/
SCAComposite store.composite
</Location>
EOF
apachectl -k start -d `pwd`/tmp
sleep 1
# Test HTTP GET
curl http://localhost:8092/store.html 2>/dev/null >tmp/store.html
curl http://localhost:8093/store.html 2>/dev/null >tmp/store.html
diff tmp/store.html htdocs/store.html
rc=$?
# Cleanup
apachectl -k stop -d `pwd`/tmp
sleep 1
sleep 2
if [ "$rc" = "0" ]; then
echo "OK"
fi

View file

@ -43,50 +43,42 @@ bool contains(const std::string& str, const std::string& pattern) {
}
bool testScript() {
std::ifstream is("store.scm", std::ios_base::in);
std::ifstream is("store-script.scm", std::ios_base::in);
std::ostringstream os;
eval::evalDriverRun(is, os);
assert(contains(os.str(), "(\"Sample Feed\" \""));
assert(contains(os.str(), "\" (\"Item\" \""));
assert(contains(os.str(), "\" ((javaClass \"services.Item\") (name \"Orange\") (currency \"USD\") (symbol \"$\") (price 3.55))) (\"Item\" \""));
assert(contains(os.str(), "\" ((javaClass \"services.Item\") (name \"Apple\") (currency \"USD\") (symbol \"$\") (price 2.99))))"));
assert(contains(os.str(), "\" ((javaClass \"services.Item\") (name \"Orange\") (currencyCode \"USD\") (currencySymbol \"$\") (price 3.55))) (\"Item\" \""));
assert(contains(os.str(), "\" ((javaClass \"services.Item\") (name \"Apple\") (currencyCode \"USD\") (currencySymbol \"$\") (price 2.99))))"));
return true;
}
const value evalLoop(std::istream& is, const value& req, eval::Env& globalEnv, const gc_pool& pool) {
value in = eval::readValue(is);
if(isNil(in))
return eval::evalExpr(req, globalEnv, pool);
eval::evalExpr(in, globalEnv, pool);
return evalLoop(is, req, globalEnv, pool);
}
bool testEval() {
{
std::ifstream is("store.scm", std::ios_base::in);
std::ifstream is("store-script.scm", std::ios_base::in);
std::ostringstream os;
eval::setupDisplay(os);
gc_pool pool;
eval::Env globalEnv = eval::setupEnvironment(pool);
const value req(mklist<value>("storeui_service", std::string("getcatalog")));
const value val = evalLoop(is, req, globalEnv, pool);
const value exp(mklist<value>("storeui_service", std::string("getcatalog")));
const value val = eval::evalScript(exp, is, globalEnv, pool);
std::ostringstream vs;
vs << val;
assert(contains(vs.str(), "(((javaClass \"services.Item\") (name \"Apple\") (currency \"USD\") (symbol \"$\") (price 2.99)) ((javaClass \"services.Item\") (name \"Orange\") (currency \"USD\") (symbol \"$\") (price 3.55)) ((javaClass \"services.Item\") (name \"Pear\") (currency \"USD\") (symbol \"$\") (price 1.55)))"));
assert(contains(vs.str(), "(((javaClass \"services.Item\") (name \"Apple\") (currencyCode \"USD\") (currencySymbol \"$\") (price 2.99)) ((javaClass \"services.Item\") (name \"Orange\") (currencyCode \"USD\") (currencySymbol \"$\") (price 3.55)) ((javaClass \"services.Item\") (name \"Pear\") (currencyCode \"USD\") (currencySymbol \"$\") (price 1.55)))"));
}
{
std::ifstream is("store.scm", std::ios_base::in);
std::ifstream is("store-script.scm", std::ios_base::in);
std::ostringstream os;
eval::setupDisplay(os);
gc_pool pool;
eval::Env globalEnv = eval::setupEnvironment(pool);
const value req(mklist<value>("storeui_service", std::string("gettotal")));
const value res = evalLoop(is, req, globalEnv, pool);
const value exp(mklist<value>("storeui_service", std::string("gettotal")));
const value res = eval::evalScript(exp, is, globalEnv, pool);
std::ostringstream rs;
rs << res;

View file

@ -0,0 +1,149 @@
; Currency implementation
(define (currency_convert from to amount)
(if (equal? to "EUR") (* amount 0.70) amount)
)
(define (currency_symbol currency)
(if (equal? currency "EUR") "E" "$")
)
(define (currency_impl op args)
(cond
((equal? op "convert") (apply currency_convert args))
((equal? op "symbol") (apply currency_symbol args))
)
)
; Currency composite
(define (currency_service op . args) (currency_impl op args))
; Catalog implementation
(define (catalog_get converter)
(define (convert price) (converter "convert" "USD" "USD" price))
(define code "USD")
(define symbol (converter "symbol" code))
(list
(list (list 'javaClass "services.Item") (list 'name "Apple") (list 'currencyCode code) (list 'currencySymbol symbol) (list 'price 2.99))
(list (list 'javaClass "services.Item") (list 'name "Orange") (list 'currencyCode code) (list 'currencySymbol symbol) (list 'price 3.55))
(list (list 'javaClass "services.Item") (list 'name "Pear") (list 'currencyCode code) (list 'currencySymbol symbol) (list 'price 1.55))
)
)
(define (catalog_impl converter op args)
(cond
((equal? op "get") (apply catalog_get (cons converter args)))
)
)
; Catalog composite
(define (catalog_service op . args) (catalog_impl currency_service op args))
; Cart implementation
(define (cart_post content item)
(cons (cons "Item" (list (uuid) item)) content)
)
(define (cart_getall content)
(cons "Sample Feed" (cons (uuid) content))
)
(define (cart_getentry id)
(define entry (list (list 'name "Apple") (list 'currencyCode "USD") (list 'currencySymbol "$") (list 'price 2.99)))
(cons "Item" (list id entry))
)
(define (cart_gettotal)
10.0
)
(define (cart_impl op args)
(cond
((equal? op "post") (apply cart_post args))
((equal? op "getall") (apply cart_getall args))
((equal? op "getentry") (apply cart_getentry args))
((equal? op "gettotal") (apply cart_gettotal args))
)
)
; Store UI implementation
(define (storeui_post cart content item)
(cart "post" content item)
)
(define (storeui_getcart cart content)
(cart "getall" content)
)
(define (storeui_getentry cart id)
(cart "getentry" id)
)
(define (storeui_getcatalog catalog)
(catalog "get")
)
(define (storeui_gettotal cart)
(cart "gettotal")
)
(define (storeui_impl cart catalog op args)
(cond
((equal? op "post") (apply storeui_post (cons cart args)))
((equal? op "getall") (apply storeui_getcart (cons cart args)))
((equal? op "getentry") (apply storeui_getentry (cons cart args)))
((equal? op "getcatalog") (apply storeui_getcatalog (cons catalog args)))
((equal? op "gettotal") (apply storeui_gettotal (cons cart args)))
)
)
; Store UI composite
(define (cart_service op . args) (cart_impl op args))
(define (storeui_service op . args) (storeui_impl cart_service catalog_service op args))
; Store UI test case
(define catalog (storeui_service "getcatalog"))
(define empty (list))
(define apple (car catalog))
(define orange (car (cdr catalog)))
(define added1 (storeui_service "post" empty apple))
(define added2 (storeui_service "post" added1 orange))
(display (storeui_service "getall" added2))
(display (storeui_service "gettotal"))
; Store UI JSON-RPC interop test case
(define (system.listMethods) (list "Service.get" "Service.getTotal"))
(define (Service.get) (storeui_service "getcatalog"))
(define (.get) (storeui_service "getcatalog"))
(define (Service.getTotal) (storeui_service "gettotal"))
; Store UI ATOMPub interop test case
(define (getall) (storeui_service "getall" added2))
(define (get id) (storeui_service "getentry" id))
(define (post entry)
(display entry)
(uuid)
)
(define (delete id)
(display id)
true
)

View file

@ -21,44 +21,49 @@
xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
targetNamespace="http://store"
name="store">
<component name="Store">
<t:implementation.widget location="uiservices/store.html"/>
<!-- <t:implementation.widget location="store.html"/> -->
<t:implementation.scheme uri="store.scm"/>
<service name="Widget">
<t:binding.http uri="/ui"/>
<t:binding.http uri="store"/>
</service>
<reference name="catalog" target="StoreCatalog">
<t:binding.jsonrpc/>
</reference>
<reference name="shoppingCart" target="StoreShoppingCart/Cart">
<t:binding.atom/>
</reference>
<reference name="shoppingTotal" target="StoreShoppingCart/Total">
<t:binding.jsonrpc/>
</reference>
<reference name="catalog" target="Catalog">
<t:binding.jsonrpc uri="Catalog"/>
</reference>
<reference name="shoppingCart" target="ShoppingCart/Cart">
<t:binding.atom/>
</reference>
<reference name="shoppingTotal" target="ShoppingCart/Total">
<t:binding.jsonrpc/>
</reference>
</component>
<component name="StoreCatalog">
<implementation.java class="services.FruitsCatalogImpl"/>
<property name="currencyCode">USD</property>
<service name="Catalog">
<t:binding.jsonrpc/>
</service>
<reference name="currencyConverter" target="StoreCurrencyConverter"/>
</component>
<component name="StoreShoppingCart">
<implementation.java class="services.ShoppingCartImpl"/>
<service name="Cart">
<t:binding.atom uri="/ShoppingCart/Cart"/>
</service>
<service name="Total">
<t:binding.jsonrpc/>
</service>
</component>
<component name="Catalog">
<t:implementation.scheme uri="fruits-catalog.scm"/>
<property name="currencyCode">USD</property>
<service name="Catalog">
<t:binding.jsonrpc uri="Catalog"/>
</service>
<reference name="currencyConverter" target="CurrencyConverter"/>
</component>
<component name="ShoppingCart">
<t:implementation.scheme uri="shopping-cart.scm"/>
<service name="ShoppingCart">
<t:binding.atom uri="ShoppingCart"/>
</service>
<service name="Total">
<t:binding.jsonrpc uri="Total"/>
</service>
</component>
<component name="StoreCurrencyConverter">
<implementation.java class="services.CurrencyConverterImpl"/>
</component>
<component name="CurrencyConverter">
<t:implementation.scheme uri="currency-converter.scm"/>
<service name="CurrencyConverter">
<t:binding.jsonrpc uri="CurrencyConverter"/>
</service>
</component>
</composite>

View file

@ -1,150 +1,31 @@
; Store implementation
(; "Currency implementation")
(define (currency_convert from to amount)
(if (equal? to "EUR") (* amount 0.70) amount)
(define (post item catalog shoppingCart shoppingTotal)
(shoppingCart "post" item)
)
(define (currency_symbol currency)
(if (equal? currency "EUR") "E" "$")
(define (getall catalog shoppingCart shoppingTotal)
(shoppingCart "getall")
)
(define (currency_impl op args)
(cond
((equal? op "convert") (apply currency_convert args))
((equal? op "symbol") (apply currency_symbol args))
)
(define (get id catalog shoppingCart shoppingTotal)
(shoppingCart "get" id)
)
(; "Currency composite")
(define (currency_service op . args) (currency_impl op args))
(; "Catalog implementation")
(define (catalog_get converter)
(define (convert price) (converter "convert" "USD" "USD" price))
(define code "USD")
(define symbol (converter "symbol" code))
(list
(list (list 'javaClass "services.Item") (list 'name "Apple") (list 'currency code) (list 'symbol symbol) (list 'price 2.99))
(list (list 'javaClass "services.Item") (list 'name "Orange") (list 'currency code) (list 'symbol symbol) (list 'price 3.55))
(list (list 'javaClass "services.Item") (list 'name "Pear") (list 'currency code) (list 'symbol symbol) (list 'price 1.55))
)
)
(define (catalog_impl converter op args)
(cond
((equal? op "get") (apply catalog_get (cons converter args)))
)
)
(; "Catalog composite")
(define (catalog_service op . args) (catalog_impl currency_service op args))
(; "Cart implementation")
(define (cart_post content item)
(cons (cons "Item" (list (uuid) item)) content)
)
(define (cart_getall content)
(cons "Sample Feed" (cons (uuid) content))
)
(define (cart_getentry id)
(define entry (list (list 'name "Apple") (list 'currency "USD") (list 'symbol "$") (list 'price 2.99)))
(cons "Item" (list id entry))
)
(define (cart_gettotal)
10.0
)
(define (cart_impl op args)
(cond
((equal? op "post") (apply cart_post args))
((equal? op "getall") (apply cart_getall args))
((equal? op "getentry") (apply cart_getentry args))
((equal? op "gettotal") (apply cart_gettotal args))
)
)
(; "Store UI implementation")
(define (storeui_post cart content item)
(cart "post" content item)
)
(define (storeui_getcart cart content)
(cart "getall" content)
)
(define (storeui_getentry cart id)
(cart "getentry" id)
)
(define (storeui_getcatalog catalog)
(define (getcatalog catalog shoppingCart shoppingTotal)
(catalog "get")
)
(define (storeui_gettotal cart)
(cart "gettotal")
(define (gettotal catalog shoppingCart shoppingTotal)
(shoppingCart "gettotal")
)
(define (storeui_impl cart catalog op args)
(cond
((equal? op "post") (apply storeui_post (cons cart args)))
((equal? op "getall") (apply storeui_getcart (cons cart args)))
((equal? op "getentry") (apply storeui_getentry (cons cart args)))
((equal? op "getcatalog") (apply storeui_getcatalog (cons catalog args)))
((equal? op "gettotal") (apply storeui_gettotal (cons cart args)))
)
(define (delete id catalog shoppingCart shoppingTotal)
(shoppingCart "delete" id)
)
(; "Store UI composite")
(define (cart_service op . args) (cart_impl op args))
(define (storeui_service op . args) (storeui_impl cart_service catalog_service op args))
(; "Store UI test case")
(define catalog (storeui_service "getcatalog"))
(define empty (list))
(define apple (car catalog))
(define orange (car (cdr catalog)))
(define added1 (storeui_service "post" empty apple))
(define added2 (storeui_service "post" added1 orange))
(display (storeui_service "getall" added2))
(display (storeui_service "gettotal"))
(; "Store UI JSON-RPC interop test case")
(define (system.listMethods) (list "Service.get" "Service.getTotal"))
(define (Service.get) (storeui_service "getcatalog"))
(define (.get) (storeui_service "getcatalog"))
(define (Service.getTotal) (storeui_service "gettotal"))
(; "Store UI ATOMPub interop test case")
(define (getall) (storeui_service "getall" added2))
(define (get id) (storeui_service "getentry" id))
(define (post entry)
(display entry)
(uuid)
)
(define (delete . args)
(display args)
true
)
(define Service.getCatalog getcatalog)
(define Service.getTotal gettotal)