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:
parent
ada8802640
commit
51a97b5d93
24 changed files with 1497 additions and 831 deletions
cpp/sca
modules
http
curl-test.cpp
htdocs
http-testhttpd-client.scmhttpd-confhttpd-testhttpd-test.compositehttpd-test.scmhttpd.hppmod-eval.cppmod-wiring.cppmod.cppwiring-testjson
test/store-script
|
@ -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();
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
25
cpp/sca/modules/http/httpd-client.scm
Normal file
25
cpp/sca/modules/http/httpd-client.scm
Normal 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)
|
||||
)
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
159
cpp/sca/modules/http/httpd.hpp
Normal file
159
cpp/sca/modules/http/httpd.hpp
Normal 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 */
|
532
cpp/sca/modules/http/mod-eval.cpp
Normal file
532
cpp/sca/modules/http/mod-eval.cpp
Normal 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
|
||||
};
|
||||
|
||||
}
|
276
cpp/sca/modules/http/mod-wiring.cpp
Normal file
276
cpp/sca/modules/http/mod-wiring.cpp
Normal 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
|
||||
};
|
||||
|
||||
}
|
|
@ -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
|
||||
};
|
||||
|
||||
}
|
92
cpp/sca/modules/http/wiring-test
Executable file
92
cpp/sca/modules/http/wiring-test
Executable 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
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
9
cpp/sca/test/store-script/currency-converter.scm
Normal file
9
cpp/sca/test/store-script/currency-converter.scm
Normal 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" "$")
|
||||
)
|
|
@ -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>
|
||||
|
|
19
cpp/sca/test/store-script/fruits-catalog.scm
Normal file
19
cpp/sca/test/store-script/fruits-catalog.scm
Normal 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)
|
26
cpp/sca/test/store-script/shopping-cart.scm
Normal file
26
cpp/sca/test/store-script/shopping-cart.scm
Normal 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)
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
149
cpp/sca/test/store-script/store-script.scm
Normal file
149
cpp/sca/test/store-script/store-script.scm
Normal 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
|
||||
)
|
||||
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Reference in a new issue