summaryrefslogtreecommitdiffstats
path: root/cpp/sca
diff options
context:
space:
mode:
authorjsdelfino <jsdelfino@13f79535-47bb-0310-9956-ffa450edef68>2009-11-16 06:01:41 +0000
committerjsdelfino <jsdelfino@13f79535-47bb-0310-9956-ffa450edef68>2009-11-16 06:01:41 +0000
commit51a97b5d9350b37f95d6f0c00d013b886e64fcd3 (patch)
tree480092faac7c7deb40265fb070d1b4b059638814 /cpp/sca
parentada8802640aa232d34b1fe2793b9f52cd62b41f1 (diff)
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
Diffstat (limited to '')
-rw-r--r--cpp/sca/modules/http/curl-test.cpp92
-rw-r--r--cpp/sca/modules/http/htdocs/entry.xml2
-rw-r--r--cpp/sca/modules/http/htdocs/feed.xml2
-rwxr-xr-xcpp/sca/modules/http/http-test8
-rw-r--r--cpp/sca/modules/http/httpd-client.scm25
-rwxr-xr-xcpp/sca/modules/http/httpd-conf3
-rwxr-xr-xcpp/sca/modules/http/httpd-test8
-rw-r--r--cpp/sca/modules/http/httpd-test.composite (renamed from cpp/sca/test/store-script/catalogs.composite)41
-rw-r--r--cpp/sca/modules/http/httpd-test.scm20
-rw-r--r--cpp/sca/modules/http/httpd.hpp159
-rw-r--r--cpp/sca/modules/http/mod-eval.cpp532
-rw-r--r--cpp/sca/modules/http/mod-wiring.cpp276
-rw-r--r--cpp/sca/modules/http/mod.cpp555
-rwxr-xr-xcpp/sca/modules/http/wiring-test92
-rw-r--r--cpp/sca/modules/json/json-test.cpp9
-rw-r--r--cpp/sca/test/store-script/currency-converter.scm9
-rw-r--r--cpp/sca/test/store-script/currency.composite16
-rw-r--r--cpp/sca/test/store-script/fruits-catalog.scm19
-rw-r--r--cpp/sca/test/store-script/shopping-cart.scm26
-rwxr-xr-xcpp/sca/test/store-script/store-http-test39
-rw-r--r--cpp/sca/test/store-script/store-script-test.cpp28
-rw-r--r--cpp/sca/test/store-script/store-script.scm149
-rw-r--r--cpp/sca/test/store-script/store.composite71
-rw-r--r--cpp/sca/test/store-script/store.scm147
24 files changed, 1497 insertions, 831 deletions
diff --git a/cpp/sca/modules/http/curl-test.cpp b/cpp/sca/modules/http/curl-test.cpp
index b02a2fac4d..863aa98828 100644
--- a/cpp/sca/modules/http/curl-test.cpp
+++ b/cpp/sca/modules/http/curl-test.cpp
@@ -20,7 +20,7 @@
/* $Rev$ $Date$ */
/**
- * Test JSON data conversion functions.
+ * Test HTTP client functions.
*/
#include <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();
diff --git a/cpp/sca/modules/http/htdocs/entry.xml b/cpp/sca/modules/http/htdocs/entry.xml
index 4906fbedc8..86b8a10547 100644
--- a/cpp/sca/modules/http/htdocs/entry.xml
+++ b/cpp/sca/modules/http/htdocs/entry.xml
@@ -1,2 +1,2 @@
<?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>
diff --git a/cpp/sca/modules/http/htdocs/feed.xml b/cpp/sca/modules/http/htdocs/feed.xml
index 4ca3183739..5e37de6580 100644
--- a/cpp/sca/modules/http/htdocs/feed.xml
+++ b/cpp/sca/modules/http/htdocs/feed.xml
@@ -1,2 +1,2 @@
<?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>
diff --git a/cpp/sca/modules/http/http-test b/cpp/sca/modules/http/http-test
index e369cc34f7..d70db8d469 100755
--- a/cpp/sca/modules/http/http-test
+++ b/cpp/sca/modules/http/http-test
@@ -20,13 +20,15 @@
# Setup
./httpd-conf tmp 8091 htdocs
cat >>tmp/conf/httpd.conf <<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
diff --git a/cpp/sca/modules/http/httpd-client.scm b/cpp/sca/modules/http/httpd-client.scm
new file mode 100644
index 0000000000..12275693f4
--- /dev/null
+++ b/cpp/sca/modules/http/httpd-client.scm
@@ -0,0 +1,25 @@
+; JSON-RPC test case
+
+(define (echo x ref) (ref "echo" x))
+
+; ATOMPub test case
+
+(define (getall ref)
+ (ref "getall")
+)
+
+(define (get id ref)
+ (ref "get" id)
+)
+
+(define (post entry ref)
+ (ref "post" entry)
+)
+
+(define (put id entry ref)
+ (ref "put" id entry)
+)
+
+(define (delete id ref)
+ (ref "delete" id)
+)
diff --git a/cpp/sca/modules/http/httpd-conf b/cpp/sca/modules/http/httpd-conf
index e076e22bb1..10a5b47ac2 100755
--- a/cpp/sca/modules/http/httpd-conf
+++ b/cpp/sca/modules/http/httpd-conf
@@ -31,6 +31,7 @@ ServerName 127.0.0.1
Listen $port
DocumentRoot $htdocs
TypesConfig $here/conf/mime.types
-LoadModule mod_tuscany $here/.libs/libmod_tuscany.so
+LoadModule mod_tuscany_eval $here/.libs/libmod_tuscany_eval.so
+LoadModule mod_tuscany_wiring $here/.libs/libmod_tuscany_wiring.so
EOF
diff --git a/cpp/sca/modules/http/httpd-test b/cpp/sca/modules/http/httpd-test
index 04c584496c..1d9b3cb34d 100755
--- a/cpp/sca/modules/http/httpd-test
+++ b/cpp/sca/modules/http/httpd-test
@@ -22,13 +22,15 @@ echo "Testing..."
# Setup
./httpd-conf tmp 8090 htdocs
cat >>tmp/conf/httpd.conf <<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
diff --git a/cpp/sca/test/store-script/catalogs.composite b/cpp/sca/modules/http/httpd-test.composite
index 1638ed0a05..875d26ae1b 100644
--- a/cpp/sca/test/store-script/catalogs.composite
+++ b/cpp/sca/modules/http/httpd-test.composite
@@ -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>
diff --git a/cpp/sca/modules/http/httpd-test.scm b/cpp/sca/modules/http/httpd-test.scm
index a3ddf8dda8..0566eaf36f 100644
--- a/cpp/sca/modules/http/httpd-test.scm
+++ b/cpp/sca/modules/http/httpd-test.scm
@@ -1,33 +1,29 @@
-(; "JSON-RPC test case")
+; JSON-RPC test case
(define (echo x) x)
-(; "ATOMPub test case")
+; ATOMPub test case
(define (getall)
'("Sample Feed" "123456789"
- ("Item" "111" (javaClass "services.Item") (name "Apple") (currency "USD") (symbol "$") (price 2.99))
- ("Item" "222" (javaClass "services.Item") (name "Orange") (currency "USD") (symbol "$") (price 3.55))
- ("Item" "333" (javaClass "services.Item") (name "Pear") (currency "USD") (symbol "$") (price 1.55)))
+ ("Item" "111" ((javaClass "services.Item") (name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99)))
+ ("Item" "222" ((javaClass "services.Item") (name "Orange") (currencyCode "USD") (currencySymbol "$") (price 3.55)))
+ ("Item" "333" ((javaClass "services.Item") (name "Pear") (currencyCode "USD") (currencySymbol "$") (price 1.55))))
)
(define (get id)
- (define entry '((javaClass "services.Item") (name "Apple") (currency "USD") (symbol "$") (price 2.99)))
+ (define entry '((javaClass "services.Item") (name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99)))
(cons "Item" (list id entry))
)
(define (post entry)
- (display entry)
"123456789"
)
-(define (put entry)
- (display entry)
+(define (put id entry)
true
)
-(define (delete . args)
- (display args)
+(define (delete id)
true
)
-
diff --git a/cpp/sca/modules/http/httpd.hpp b/cpp/sca/modules/http/httpd.hpp
new file mode 100644
index 0000000000..1271afc03c
--- /dev/null
+++ b/cpp/sca/modules/http/httpd.hpp
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+#ifndef tuscany_httpd_hpp
+#define tuscany_httpd_hpp
+
+/**
+ * HTTPD module utility functions.
+ */
+
+#include <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 */
diff --git a/cpp/sca/modules/http/mod-eval.cpp b/cpp/sca/modules/http/mod-eval.cpp
new file mode 100644
index 0000000000..6fef2be2cb
--- /dev/null
+++ b/cpp/sca/modules/http/mod-eval.cpp
@@ -0,0 +1,532 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * HTTPD module used to eval component implementations.
+ */
+
+#include <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
+};
+
+}
diff --git a/cpp/sca/modules/http/mod-wiring.cpp b/cpp/sca/modules/http/mod-wiring.cpp
new file mode 100644
index 0000000000..965d5a87fb
--- /dev/null
+++ b/cpp/sca/modules/http/mod-wiring.cpp
@@ -0,0 +1,276 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * HTTPD module used to wire components.
+ */
+
+#include <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
+};
+
+}
diff --git a/cpp/sca/modules/http/mod.cpp b/cpp/sca/modules/http/mod.cpp
deleted file mode 100644
index 811ee0c633..0000000000
--- a/cpp/sca/modules/http/mod.cpp
+++ /dev/null
@@ -1,555 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-/* $Rev$ $Date$ */
-
-/**
- * Tuscany HTTPD server module.
- */
-
-#include <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
-};
-
-}
diff --git a/cpp/sca/modules/http/wiring-test b/cpp/sca/modules/http/wiring-test
new file mode 100755
index 0000000000..0c3f36b513
--- /dev/null
+++ b/cpp/sca/modules/http/wiring-test
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+echo "Testing..."
+
+# Setup
+./httpd-conf tmp 8092 htdocs
+cat >>tmp/conf/httpd.conf <<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
diff --git a/cpp/sca/modules/json/json-test.cpp b/cpp/sca/modules/json/json-test.cpp
index 0d4e8f6a16..fd506cea65 100644
--- a/cpp/sca/modules/json/json-test.cpp
+++ b/cpp/sca/modules/json/json-test.cpp
@@ -114,6 +114,15 @@ bool testJSONRPC() {
write(writeJSON(e, cx), os);
assert(os.str() == "{\"id\":1,\"result\":[\"Service.get\",\"Service.getTotal\"]}");
}
+ {
+ const std::string f("{\"id\":1,\"result\":[\"Sample Feed\",\"123456789\",[\"Item\",\"111\",{\"javaClass\":\"services.Item\",\"name\":\"Apple\",\"currencyCode\":\"USD\",\"currencySymbol\":\"$\",\"price\":2.99}],[\"Item\",\"222\",{\"javaClass\":\"services.Item\",\"name\":\"Orange\",\"currencyCode\":\"USD\",\"currencySymbol\":\"$\",\"price\":3.55}],[\"Item\",\"333\",{\"javaClass\":\"services.Item\",\"name\":\"Pear\",\"currencyCode\":\"USD\",\"currencySymbol\":\"$\",\"price\":1.55}]]}");
+ const list<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;
}
diff --git a/cpp/sca/test/store-script/currency-converter.scm b/cpp/sca/test/store-script/currency-converter.scm
new file mode 100644
index 0000000000..7f58335951
--- /dev/null
+++ b/cpp/sca/test/store-script/currency-converter.scm
@@ -0,0 +1,9 @@
+; Currency converter implementation
+
+(define (convert from to amount)
+ (if (equal? to "EUR") (* amount 0.70) amount)
+)
+
+(define (symbol currency)
+ (if (equal? currency "EUR") "E" "$")
+)
diff --git a/cpp/sca/test/store-script/currency.composite b/cpp/sca/test/store-script/currency.composite
index aefd474f1f..eaea331dbe 100644
--- a/cpp/sca/test/store-script/currency.composite
+++ b/cpp/sca/test/store-script/currency.composite
@@ -19,14 +19,14 @@
-->
<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>
diff --git a/cpp/sca/test/store-script/fruits-catalog.scm b/cpp/sca/test/store-script/fruits-catalog.scm
new file mode 100644
index 0000000000..390068d71a
--- /dev/null
+++ b/cpp/sca/test/store-script/fruits-catalog.scm
@@ -0,0 +1,19 @@
+; Catalog implementation
+
+(define (get converter)
+ (display "catalog")
+ (define (convert price) (converter "convert" "USD" "USD" price))
+
+ (define code "USD")
+ (define symbol (converter "symbol" code))
+
+ (list
+ (list (list 'javaClass "services.Item") (list 'name "Apple") (list 'currencyCode code) (list 'currencySymbol symbol) (list 'price (convert 2.99)))
+ (list (list 'javaClass "services.Item") (list 'name "Orange") (list 'currencyCode code) (list 'currencySymbol symbol) (list 'price (convert 3.55)))
+ (list (list 'javaClass "services.Item") (list 'name "Pear") (list 'currencyCode code) (list 'currencySymbol symbol) (list 'price (convert 1.55)))
+ )
+)
+
+; TODO remove these JSON-RPC specific functions
+(define (system.listMethods converter) (list "Service.get"))
+(define Service.get get)
diff --git a/cpp/sca/test/store-script/shopping-cart.scm b/cpp/sca/test/store-script/shopping-cart.scm
new file mode 100644
index 0000000000..ebb3504e25
--- /dev/null
+++ b/cpp/sca/test/store-script/shopping-cart.scm
@@ -0,0 +1,26 @@
+; Shopping cart implementation
+
+(define (post item)
+ (uuid)
+)
+
+(define (getall)
+ (cons "Sample Feed" (cons (uuid) '()))
+)
+
+(define (get id)
+ (define entry (list (list 'name "Apple") (list 'currencyCode "USD") (list 'currencySymbol "$") (list 'price 2.99)))
+ (cons "Item" (list id entry))
+)
+
+(define (delete id)
+ true
+)
+
+(define (gettotal)
+ 11.0
+)
+
+; TODO remove these JSON-RPC specific functions
+(define (system.listMethods) (list "Service.getTotal"))
+(define Service.getTotal gettotal)
diff --git a/cpp/sca/test/store-script/store-http-test b/cpp/sca/test/store-script/store-http-test
index 4a5a3df685..551731e856 100755
--- a/cpp/sca/test/store-script/store-http-test
+++ b/cpp/sca/test/store-script/store-http-test
@@ -20,40 +20,55 @@
echo "Testing..."
# Setup
-../../modules/http/httpd-conf tmp 8092 htdocs
+../../modules/http/httpd-conf tmp 8093 htdocs
cat >>tmp/conf/httpd.conf <<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
diff --git a/cpp/sca/test/store-script/store-script-test.cpp b/cpp/sca/test/store-script/store-script-test.cpp
index 5a29d8caba..add5cb75e5 100644
--- a/cpp/sca/test/store-script/store-script-test.cpp
+++ b/cpp/sca/test/store-script/store-script-test.cpp
@@ -43,50 +43,42 @@ bool contains(const std::string& str, const std::string& pattern) {
}
bool testScript() {
- std::ifstream is("store.scm", std::ios_base::in);
+ std::ifstream is("store-script.scm", std::ios_base::in);
std::ostringstream os;
eval::evalDriverRun(is, os);
assert(contains(os.str(), "(\"Sample Feed\" \""));
assert(contains(os.str(), "\" (\"Item\" \""));
- assert(contains(os.str(), "\" ((javaClass \"services.Item\") (name \"Orange\") (currency \"USD\") (symbol \"$\") (price 3.55))) (\"Item\" \""));
- assert(contains(os.str(), "\" ((javaClass \"services.Item\") (name \"Apple\") (currency \"USD\") (symbol \"$\") (price 2.99))))"));
+ assert(contains(os.str(), "\" ((javaClass \"services.Item\") (name \"Orange\") (currencyCode \"USD\") (currencySymbol \"$\") (price 3.55))) (\"Item\" \""));
+ assert(contains(os.str(), "\" ((javaClass \"services.Item\") (name \"Apple\") (currencyCode \"USD\") (currencySymbol \"$\") (price 2.99))))"));
return true;
}
-const value evalLoop(std::istream& is, const value& req, eval::Env& globalEnv, const gc_pool& pool) {
- value in = eval::readValue(is);
- if(isNil(in))
- return eval::evalExpr(req, globalEnv, pool);
- eval::evalExpr(in, globalEnv, pool);
- return evalLoop(is, req, globalEnv, pool);
-}
-
bool testEval() {
{
- std::ifstream is("store.scm", std::ios_base::in);
+ std::ifstream is("store-script.scm", std::ios_base::in);
std::ostringstream os;
eval::setupDisplay(os);
gc_pool pool;
eval::Env globalEnv = eval::setupEnvironment(pool);
- const value req(mklist<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;
diff --git a/cpp/sca/test/store-script/store-script.scm b/cpp/sca/test/store-script/store-script.scm
new file mode 100644
index 0000000000..30c10d8184
--- /dev/null
+++ b/cpp/sca/test/store-script/store-script.scm
@@ -0,0 +1,149 @@
+; Currency implementation
+
+(define (currency_convert from to amount)
+ (if (equal? to "EUR") (* amount 0.70) amount)
+)
+
+(define (currency_symbol currency)
+ (if (equal? currency "EUR") "E" "$")
+)
+
+(define (currency_impl op args)
+ (cond
+ ((equal? op "convert") (apply currency_convert args))
+ ((equal? op "symbol") (apply currency_symbol args))
+ )
+)
+
+; Currency composite
+
+(define (currency_service op . args) (currency_impl op args))
+
+; Catalog implementation
+
+(define (catalog_get converter)
+ (define (convert price) (converter "convert" "USD" "USD" price))
+
+ (define code "USD")
+ (define symbol (converter "symbol" code))
+
+ (list
+ (list (list 'javaClass "services.Item") (list 'name "Apple") (list 'currencyCode code) (list 'currencySymbol symbol) (list 'price 2.99))
+ (list (list 'javaClass "services.Item") (list 'name "Orange") (list 'currencyCode code) (list 'currencySymbol symbol) (list 'price 3.55))
+ (list (list 'javaClass "services.Item") (list 'name "Pear") (list 'currencyCode code) (list 'currencySymbol symbol) (list 'price 1.55))
+ )
+)
+
+(define (catalog_impl converter op args)
+ (cond
+ ((equal? op "get") (apply catalog_get (cons converter args)))
+ )
+)
+
+; Catalog composite
+
+(define (catalog_service op . args) (catalog_impl currency_service op args))
+
+; Cart implementation
+
+(define (cart_post content item)
+ (cons (cons "Item" (list (uuid) item)) content)
+)
+
+(define (cart_getall content)
+ (cons "Sample Feed" (cons (uuid) content))
+)
+
+(define (cart_getentry id)
+ (define entry (list (list 'name "Apple") (list 'currencyCode "USD") (list 'currencySymbol "$") (list 'price 2.99)))
+ (cons "Item" (list id entry))
+)
+
+(define (cart_gettotal)
+ 10.0
+)
+
+(define (cart_impl op args)
+ (cond
+ ((equal? op "post") (apply cart_post args))
+ ((equal? op "getall") (apply cart_getall args))
+ ((equal? op "getentry") (apply cart_getentry args))
+ ((equal? op "gettotal") (apply cart_gettotal args))
+ )
+)
+
+; Store UI implementation
+
+(define (storeui_post cart content item)
+ (cart "post" content item)
+)
+
+(define (storeui_getcart cart content)
+ (cart "getall" content)
+)
+
+(define (storeui_getentry cart id)
+ (cart "getentry" id)
+)
+
+(define (storeui_getcatalog catalog)
+ (catalog "get")
+)
+
+(define (storeui_gettotal cart)
+ (cart "gettotal")
+)
+
+(define (storeui_impl cart catalog op args)
+ (cond
+ ((equal? op "post") (apply storeui_post (cons cart args)))
+ ((equal? op "getall") (apply storeui_getcart (cons cart args)))
+ ((equal? op "getentry") (apply storeui_getentry (cons cart args)))
+ ((equal? op "getcatalog") (apply storeui_getcatalog (cons catalog args)))
+ ((equal? op "gettotal") (apply storeui_gettotal (cons cart args)))
+ )
+)
+
+; Store UI composite
+
+(define (cart_service op . args) (cart_impl op args))
+
+(define (storeui_service op . args) (storeui_impl cart_service catalog_service op args))
+
+; Store UI test case
+
+(define catalog (storeui_service "getcatalog"))
+(define empty (list))
+(define apple (car catalog))
+(define orange (car (cdr catalog)))
+(define added1 (storeui_service "post" empty apple))
+(define added2 (storeui_service "post" added1 orange))
+(display (storeui_service "getall" added2))
+(display (storeui_service "gettotal"))
+
+; Store UI JSON-RPC interop test case
+
+(define (system.listMethods) (list "Service.get" "Service.getTotal"))
+
+(define (Service.get) (storeui_service "getcatalog"))
+
+(define (.get) (storeui_service "getcatalog"))
+
+(define (Service.getTotal) (storeui_service "gettotal"))
+
+; Store UI ATOMPub interop test case
+
+(define (getall) (storeui_service "getall" added2))
+
+(define (get id) (storeui_service "getentry" id))
+
+(define (post entry)
+ (display entry)
+ (uuid)
+)
+
+(define (delete id)
+ (display id)
+ true
+)
+
diff --git a/cpp/sca/test/store-script/store.composite b/cpp/sca/test/store-script/store.composite
index 124adff853..cd34f81840 100644
--- a/cpp/sca/test/store-script/store.composite
+++ b/cpp/sca/test/store-script/store.composite
@@ -21,44 +21,49 @@
xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
targetNamespace="http://store"
name="store">
-
+
<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>
diff --git a/cpp/sca/test/store-script/store.scm b/cpp/sca/test/store-script/store.scm
index 709fd943f1..2434b18b51 100644
--- a/cpp/sca/test/store-script/store.scm
+++ b/cpp/sca/test/store-script/store.scm
@@ -1,150 +1,31 @@
+; Store implementation
-(; "Currency implementation")
-
-(define (currency_convert from to amount)
- (if (equal? to "EUR") (* amount 0.70) amount)
-)
-
-(define (currency_symbol currency)
- (if (equal? currency "EUR") "E" "$")
-)
-
-(define (currency_impl op args)
- (cond
- ((equal? op "convert") (apply currency_convert args))
- ((equal? op "symbol") (apply currency_symbol args))
- )
-)
-
-(; "Currency composite")
-
-(define (currency_service op . args) (currency_impl op args))
-
-(; "Catalog implementation")
-
-(define (catalog_get converter)
- (define (convert price) (converter "convert" "USD" "USD" price))
-
- (define code "USD")
- (define symbol (converter "symbol" code))
-
- (list
- (list (list 'javaClass "services.Item") (list 'name "Apple") (list 'currency code) (list 'symbol symbol) (list 'price 2.99))
- (list (list 'javaClass "services.Item") (list 'name "Orange") (list 'currency code) (list 'symbol symbol) (list 'price 3.55))
- (list (list 'javaClass "services.Item") (list 'name "Pear") (list 'currency code) (list 'symbol symbol) (list 'price 1.55))
- )
-)
-
-(define (catalog_impl converter op args)
- (cond
- ((equal? op "get") (apply catalog_get (cons converter args)))
- )
-)
-
-(; "Catalog composite")
-
-(define (catalog_service op . args) (catalog_impl currency_service op args))
-
-(; "Cart implementation")
-
-(define (cart_post content item)
- (cons (cons "Item" (list (uuid) item)) content)
-)
-
-(define (cart_getall content)
- (cons "Sample Feed" (cons (uuid) content))
-)
-
-(define (cart_getentry id)
- (define entry (list (list 'name "Apple") (list 'currency "USD") (list 'symbol "$") (list 'price 2.99)))
- (cons "Item" (list id entry))
+(define (post item catalog shoppingCart shoppingTotal)
+ (shoppingCart "post" item)
)
-(define (cart_gettotal)
- 10.0
+(define (getall catalog shoppingCart shoppingTotal)
+ (shoppingCart "getall")
)
-(define (cart_impl op args)
- (cond
- ((equal? op "post") (apply cart_post args))
- ((equal? op "getall") (apply cart_getall args))
- ((equal? op "getentry") (apply cart_getentry args))
- ((equal? op "gettotal") (apply cart_gettotal args))
- )
+(define (get id catalog shoppingCart shoppingTotal)
+ (shoppingCart "get" id)
)
-(; "Store UI implementation")
-
-(define (storeui_post cart content item)
- (cart "post" content item)
-)
-
-(define (storeui_getcart cart content)
- (cart "getall" content)
-)
-
-(define (storeui_getentry cart id)
- (cart "getentry" id)
-)
-
-(define (storeui_getcatalog catalog)
+(define (getcatalog catalog shoppingCart shoppingTotal)
(catalog "get")
)
-(define (storeui_gettotal cart)
- (cart "gettotal")
+(define (gettotal catalog shoppingCart shoppingTotal)
+ (shoppingCart "gettotal")
)
-(define (storeui_impl cart catalog op args)
- (cond
- ((equal? op "post") (apply storeui_post (cons cart args)))
- ((equal? op "getall") (apply storeui_getcart (cons cart args)))
- ((equal? op "getentry") (apply storeui_getentry (cons cart args)))
- ((equal? op "getcatalog") (apply storeui_getcatalog (cons catalog args)))
- ((equal? op "gettotal") (apply storeui_gettotal (cons cart args)))
- )
+(define (delete id catalog shoppingCart shoppingTotal)
+ (shoppingCart "delete" id)
)
-(; "Store UI composite")
-
-(define (cart_service op . args) (cart_impl op args))
-
-(define (storeui_service op . args) (storeui_impl cart_service catalog_service op args))
-
-(; "Store UI test case")
-
-(define catalog (storeui_service "getcatalog"))
-(define empty (list))
-(define apple (car catalog))
-(define orange (car (cdr catalog)))
-(define added1 (storeui_service "post" empty apple))
-(define added2 (storeui_service "post" added1 orange))
-(display (storeui_service "getall" added2))
-(display (storeui_service "gettotal"))
-
-(; "Store UI JSON-RPC interop test case")
-
(define (system.listMethods) (list "Service.get" "Service.getTotal"))
-(define (Service.get) (storeui_service "getcatalog"))
-
-(define (.get) (storeui_service "getcatalog"))
-
-(define (Service.getTotal) (storeui_service "gettotal"))
-
-(; "Store UI ATOMPub interop test case")
-
-(define (getall) (storeui_service "getall" added2))
-
-(define (get id) (storeui_service "getentry" id))
-
-(define (post entry)
- (display entry)
- (uuid)
-)
-
-(define (delete . args)
- (display args)
- true
-)
+(define Service.getCatalog getcatalog)
+(define Service.getTotal gettotal)