summaryrefslogtreecommitdiffstats
path: root/sca-cpp/trunk/modules
diff options
context:
space:
mode:
Diffstat (limited to 'sca-cpp/trunk/modules')
-rw-r--r--sca-cpp/trunk/modules/Makefile.am19
-rw-r--r--sca-cpp/trunk/modules/atom/Makefile.am28
-rw-r--r--sca-cpp/trunk/modules/atom/atom-test.cpp194
-rw-r--r--sca-cpp/trunk/modules/atom/atom.hpp170
-rw-r--r--sca-cpp/trunk/modules/cache/Makefile.am31
-rw-r--r--sca-cpp/trunk/modules/cache/cache.hpp122
-rw-r--r--sca-cpp/trunk/modules/cache/diskcache-test.cpp81
-rwxr-xr-xsca-cpp/trunk/modules/cache/diskcached-test27
-rw-r--r--sca-cpp/trunk/modules/cache/memcache-test.cpp96
-rwxr-xr-xsca-cpp/trunk/modules/cache/memcached-test31
-rw-r--r--sca-cpp/trunk/modules/cache/memcached.hpp143
-rw-r--r--sca-cpp/trunk/modules/eval/Makefile.am34
-rw-r--r--sca-cpp/trunk/modules/eval/driver.hpp77
-rw-r--r--sca-cpp/trunk/modules/eval/environment.hpp178
-rw-r--r--sca-cpp/trunk/modules/eval/eval-shell.cpp35
-rw-r--r--sca-cpp/trunk/modules/eval/eval-test.cpp240
-rw-r--r--sca-cpp/trunk/modules/eval/eval.hpp290
-rw-r--r--sca-cpp/trunk/modules/eval/io.hpp206
-rw-r--r--sca-cpp/trunk/modules/eval/primitive.hpp197
-rw-r--r--sca-cpp/trunk/modules/eval/tuscany-sca-1.1-implementation-eval.xsd43
-rw-r--r--sca-cpp/trunk/modules/http/Makefile.am37
-rw-r--r--sca-cpp/trunk/modules/http/conf/mime.types607
-rw-r--r--sca-cpp/trunk/modules/http/curl-test.cpp253
-rw-r--r--sca-cpp/trunk/modules/http/curl.hpp348
-rw-r--r--sca-cpp/trunk/modules/http/htdocs/entry.xml2
-rw-r--r--sca-cpp/trunk/modules/http/htdocs/feed.xml2
-rw-r--r--sca-cpp/trunk/modules/http/htdocs/index.html21
-rw-r--r--sca-cpp/trunk/modules/http/htdocs/json-request.txt1
-rw-r--r--sca-cpp/trunk/modules/http/htdocs/json-result.txt1
-rwxr-xr-xsca-cpp/trunk/modules/http/http-test42
-rw-r--r--sca-cpp/trunk/modules/http/httpd-client.scm25
-rwxr-xr-xsca-cpp/trunk/modules/http/httpd-conf37
-rwxr-xr-xsca-cpp/trunk/modules/http/httpd-test79
-rw-r--r--sca-cpp/trunk/modules/http/httpd-test.composite42
-rw-r--r--sca-cpp/trunk/modules/http/httpd-test.scm29
-rw-r--r--sca-cpp/trunk/modules/http/httpd.hpp159
-rw-r--r--sca-cpp/trunk/modules/http/mod-eval.cpp532
-rw-r--r--sca-cpp/trunk/modules/http/mod-wiring.cpp276
-rwxr-xr-xsca-cpp/trunk/modules/http/wiring-test92
-rw-r--r--sca-cpp/trunk/modules/json/Makefile.am28
-rw-r--r--sca-cpp/trunk/modules/json/json-test.cpp142
-rw-r--r--sca-cpp/trunk/modules/json/json.hpp396
-rw-r--r--sca-cpp/trunk/modules/scdl/Makefile.am28
-rwxr-xr-xsca-cpp/trunk/modules/scdl/scdl-testbin0 -> 477277 bytes
-rw-r--r--sca-cpp/trunk/modules/scdl/scdl-test.cpp119
-rw-r--r--sca-cpp/trunk/modules/scdl/scdl.hpp158
-rw-r--r--sca-cpp/trunk/modules/scdl/test.composite67
47 files changed, 5765 insertions, 0 deletions
diff --git a/sca-cpp/trunk/modules/Makefile.am b/sca-cpp/trunk/modules/Makefile.am
new file mode 100644
index 0000000000..89256ae1a0
--- /dev/null
+++ b/sca-cpp/trunk/modules/Makefile.am
@@ -0,0 +1,19 @@
+# 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.
+
+SUBDIRS = atom cache eval http json scdl
+
diff --git a/sca-cpp/trunk/modules/atom/Makefile.am b/sca-cpp/trunk/modules/atom/Makefile.am
new file mode 100644
index 0000000000..61998ece8b
--- /dev/null
+++ b/sca-cpp/trunk/modules/atom/Makefile.am
@@ -0,0 +1,28 @@
+# 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.
+
+noinst_PROGRAMS = atom-test
+
+nobase_include_HEADERS = *.hpp
+
+INCLUDES = -I. -I$(top_builddir)/kernel -I${LIBXML2_INCLUDE} -I${APR_INCLUDE}
+
+atom_test_SOURCES = atom-test.cpp
+atom_test_LDADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1
+
+TESTS = atom-test
+
diff --git a/sca-cpp/trunk/modules/atom/atom-test.cpp b/sca-cpp/trunk/modules/atom/atom-test.cpp
new file mode 100644
index 0000000000..7c14b954a0
--- /dev/null
+++ b/sca-cpp/trunk/modules/atom/atom-test.cpp
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test ATOM data conversion functions.
+ */
+
+#include <assert.h>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include "slist.hpp"
+#include "atom.hpp"
+
+namespace tuscany {
+namespace atom {
+
+std::ostringstream* writer(const std::string& s, std::ostringstream* os) {
+ (*os) << s;
+ return os;
+}
+
+std::string itemEntry("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<entry xmlns=\"http://www.w3.org/2005/Atom\">"
+ "<title type=\"text\">item</title>"
+ "<id>cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b</id>"
+ "<content type=\"application/xml\">"
+ "<item>"
+ "<name>Apple</name><price>$2.99</price>"
+ "</item>"
+ "</content>"
+ "<link href=\"cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b\"/>"
+ "</entry>\n");
+
+std::string incompleteEntry("<entry xmlns=\"http://www.w3.org/2005/Atom\">"
+ "<title>item</title><content type=\"text/xml\">"
+ "<Item xmlns=\"http://services/\">"
+ "<name xmlns=\"\">Orange</name>"
+ "<price xmlns=\"\">3.55</price>"
+ "</Item>"
+ "</content>"
+ "</entry>");
+
+std::string completedEntry("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<entry xmlns=\"http://www.w3.org/2005/Atom\">"
+ "<title type=\"text\">item</title>"
+ "<id></id>"
+ "<content type=\"application/xml\">"
+ "<Item xmlns=\"http://services/\">"
+ "<name xmlns=\"\">Orange</name>"
+ "<price xmlns=\"\">3.55</price>"
+ "</Item>"
+ "</content><link href=\"\"/>"
+ "</entry>\n");
+
+bool testEntry() {
+ {
+ const list<value> i = list<value>() << element << "item"
+ << (list<value>() << element << "name" << std::string("Apple"))
+ << (list<value>() << element << "price" << std::string("$2.99"));
+ const list<value> a = mklist<value>(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i);
+ std::ostringstream os;
+ writeATOMEntry<std::ostringstream*>(writer, &os, a);
+ assert(os.str() == itemEntry);
+ }
+ {
+ const list<value> a = readEntry(mklist(itemEntry));
+ std::ostringstream os;
+ writeATOMEntry<std::ostringstream*>(writer, &os, a);
+ assert(os.str() == itemEntry);
+ }
+ {
+ const list<value> a = readEntry(mklist(incompleteEntry));
+ std::ostringstream os;
+ writeATOMEntry<std::ostringstream*>(writer, &os, a);
+ assert(os.str() == completedEntry);
+ }
+ return true;
+}
+
+std::string emptyFeed("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<feed xmlns=\"http://www.w3.org/2005/Atom\">"
+ "<title type=\"text\">Feed</title>"
+ "<id>1234</id>"
+ "</feed>\n");
+
+std::string itemFeed("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<feed xmlns=\"http://www.w3.org/2005/Atom\">"
+ "<title type=\"text\">Feed</title>"
+ "<id>1234</id>"
+ "<entry xmlns=\"http://www.w3.org/2005/Atom\">"
+ "<title type=\"text\">item</title>"
+ "<id>cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b</id>"
+ "<content type=\"application/xml\">"
+ "<item>"
+ "<name>Apple</name><price>$2.99</price>"
+ "</item>"
+ "</content>"
+ "<link href=\"cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b\"/>"
+ "</entry>"
+ "<entry xmlns=\"http://www.w3.org/2005/Atom\">"
+ "<title type=\"text\">item</title>"
+ "<id>cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c</id>"
+ "<content type=\"application/xml\">"
+ "<item>"
+ "<name>Orange</name><price>$3.55</price>"
+ "</item>"
+ "</content>"
+ "<link href=\"cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c\"/>"
+ "</entry>"
+ "</feed>\n");
+
+bool testFeed() {
+ {
+ std::ostringstream os;
+ writeATOMFeed<std::ostringstream*>(writer, &os, mklist<value>("Feed", "1234"));
+ assert(os.str() == emptyFeed);
+ }
+ {
+ const list<value> a = readFeed(mklist(emptyFeed));
+ std::ostringstream os;
+ writeATOMFeed<std::ostringstream*>(writer, &os, a);
+ assert(os.str() == emptyFeed);
+ }
+ {
+ const list<value> i = list<value>()
+ << (list<value>() << "item" << "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"
+ << (list<value>() << element << "item"
+ << (list<value>() << element << "name" << "Apple")
+ << (list<value>() << element << "price" << "$2.99")))
+ << (list<value>() << "item" << "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c"
+ << (list<value>() << element << "item"
+ << (list<value>() << element << "name" << "Orange")
+ << (list<value>() << element << "price" << "$3.55")));
+ const list<value> a = cons<value>("Feed", cons<value>("1234", i));
+ std::ostringstream os;
+ writeATOMFeed<std::ostringstream*>(writer, &os, a);
+ assert(os.str() == itemFeed);
+ }
+ {
+ const list<value> i = list<value>()
+ << (list<value>() << "item" << "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"
+ << valueToElement(list<value>() << "item"
+ << (list<value>() << "name" << "Apple")
+ << (list<value>() << "price" << "$2.99")))
+ << (list<value>() << "item" << "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c"
+ << valueToElement(list<value>() << "item"
+ << (list<value>() << "name" << "Orange")
+ << (list<value>() << "price" << "$3.55")));
+ const list<value> a = cons<value>("Feed", cons<value>("1234", i));
+ std::ostringstream os;
+ writeATOMFeed<std::ostringstream*>(writer, &os, a);
+ assert(os.str() == itemFeed);
+ }
+ {
+ const list<value> a = readFeed(mklist(itemFeed));
+ std::ostringstream os;
+ writeATOMFeed<std::ostringstream*>(writer, &os, a);
+ assert(os.str() == itemFeed);
+ }
+ return true;
+}
+
+}
+}
+
+int main() {
+ std::cout << "Testing..." << std::endl;
+
+ tuscany::atom::testEntry();
+ tuscany::atom::testFeed();
+
+ std::cout << "OK" << std::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/trunk/modules/atom/atom.hpp b/sca-cpp/trunk/modules/atom/atom.hpp
new file mode 100644
index 0000000000..5054c635a0
--- /dev/null
+++ b/sca-cpp/trunk/modules/atom/atom.hpp
@@ -0,0 +1,170 @@
+/*
+ * 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_atom_hpp
+#define tuscany_atom_hpp
+
+/**
+ * ATOM data conversion functions.
+ */
+
+#include <string>
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "xml.hpp"
+
+namespace tuscany {
+namespace atom {
+
+/**
+ * Convert a list of elements to a list of values representing an ATOM entry.
+ */
+const list<value> entryValue(const list<value>& e) {
+ const list<value> lt = filter<value>(selector(mklist<value>(element, "title")), e);
+ const value t = isNil(lt)? value(std::string("")) : elementValue(car(lt));
+ const list<value> li = filter<value>(selector(mklist<value>(element, "id")), e);
+ const value i = isNil(li)? value(std::string("")) : elementValue(car(li));
+ const list<value> lc = filter<value>(selector(mklist<value>(element, "content")), e);
+ return mklist<value>(t, i, cadr(elementChildren(car(lc))));
+}
+
+/**
+ * Convert a list of elements to a list of values representing ATOM entries.
+ */
+const list<value> entriesValues(const list<value>& e) {
+ if (isNil(e))
+ return e;
+ return cons<value>(entryValue(car(e)), entriesValues(cdr(e)));
+}
+
+/**
+ * Convert a list of strings to a list of values representing an ATOM entry.
+ */
+const failable<list<value>, std::string> readEntry(const list<std::string>& ilist) {
+ const list<value> e = readXML(ilist);
+ if (isNil(e))
+ return mkfailure<list<value>, std::string>("Empty entry");
+ return entryValue(car(e));
+}
+
+/**
+ * Convert a list of strings to a list of values representing an ATOM feed.
+ */
+const failable<list<value>, std::string> readFeed(const list<std::string>& ilist) {
+ const list<value> f = readXML(ilist);
+ if (isNil(f))
+ return mkfailure<list<value>, std::string>("Empty feed");
+ const list<value> t = filter<value>(selector(mklist<value>(element, "title")), car(f));
+ const list<value> i = filter<value>(selector(mklist<value>(element, "id")), car(f));
+ const list<value> e = filter<value>(selector(mklist<value>(element, "entry")), car(f));
+ if (isNil(e))
+ return mklist<value>(elementValue(car(t)), elementValue(car(i)));
+ return cons<value>(elementValue(car(t)), cons(elementValue(car(i)), entriesValues(e)));
+}
+
+/**
+ * Convert a list of values representing an ATOM entry to a list of elements.
+ * The first two values in the list are the entry title and id.
+ */
+const list<value> entryElement(const list<value>& l) {
+ return list<value>()
+ << element << "entry" << (list<value>() << attribute << "xmlns" << "http://www.w3.org/2005/Atom")
+ << (list<value>() << element << "title" << (list<value>() << attribute << "type" << "text") << car(l))
+ << (list<value>() << element << "id" << cadr(l))
+ << (list<value>() << element << "content" << (list<value>() << attribute << "type" << "application/xml") << caddr(l))
+ << (list<value>() << element << "link" << (list<value>() << attribute << "href" << cadr(l)));
+}
+
+/**
+ * Convert a list of values representing ATOM entries to a list of elements.
+ */
+const list<value> entriesElements(const list<value>& l) {
+ if (isNil(l))
+ return l;
+ return cons<value>(entryElement(car(l)), entriesElements(cdr(l)));
+}
+
+/**
+ * Convert a list of values representing an ATOM entry to an ATOM entry.
+ * The first two values in the list are the entry id and title.
+ */
+template<typename R> const failable<R, std::string> writeATOMEntry(const lambda<R(std::string, R)>& reduce, const R& initial, const list<value>& l) {
+ return writeXML<R>(reduce, initial, mklist<value>(entryElement(l)));
+}
+
+const failable<list<std::string>, std::string> writeATOMEntry(const list<value>& l) {
+ const failable<list<std::string>, std::string> ls = writeATOMEntry<list<std::string> >(rcons<std::string>, list<std::string>(), l);
+ if (!hasValue(ls))
+ return ls;
+ return reverse(list<std::string>(ls));
+}
+
+/**
+ * Convert a list of values representing an ATOM feed to an ATOM feed.
+ * The first two values in the list are the feed id and title.
+ */
+template<typename R> const failable<R, std::string> writeATOMFeed(const lambda<R(std::string, R)>& reduce, const R& initial, const list<value>& l) {
+ const list<value> f = list<value>()
+ << element << "feed" << (list<value>() << attribute << "xmlns" << "http://www.w3.org/2005/Atom")
+ << (list<value>() << element << "title" << (list<value>() << attribute << "type" << "text") << car(l))
+ << (list<value>() << element << "id" << cadr(l));
+ if (isNil(cddr(l)))
+ return writeXML<R>(reduce, initial, mklist<value>(f));
+ const list<value> fe = append(f, entriesElements(cddr(l)));
+ return writeXML<R>(reduce, initial, mklist<value>(fe));
+}
+
+/**
+ * Convert a list of values representing an ATOM feed to a list of strings.
+ * The first two values in the list are the feed id and title.
+ */
+const failable<list<std::string>, std::string> writeATOMFeed(const list<value>& l) {
+ const failable<list<std::string>, std::string> ls = writeATOMFeed<list<std::string> >(rcons<std::string>, list<std::string>(), l);
+ if (!hasValue(ls))
+ return ls;
+ return reverse(list<std::string>(ls));
+}
+
+/**
+ * Convert an ATOM entry containing a value to an ATOM entry containing an item element.
+ */
+const list<value> entryValuesToElements(const list<value> val) {
+ return cons(car(val), cons(cadr(val), valuesToElements(mklist<value>(cons<value>("item", (list<value>)caddr(val))))));
+}
+
+/**
+ * Convert an ATOM feed containing values to an ATOM feed containing elements.
+ */
+const list<value> feedValuesToElementsLoop(const list<value> val) {
+ if (isNil(val))
+ return val;
+ return cons<value>(entryValuesToElements(car(val)), feedValuesToElementsLoop(cdr(val)));
+}
+
+const list<value> feedValuesToElements(const list<value>& val) {
+ return cons(car<value>(val), cons<value>(cadr<value>(val), feedValuesToElementsLoop(cddr<value>(val))));
+}
+
+}
+}
+
+#endif /* tuscany_atom_hpp */
diff --git a/sca-cpp/trunk/modules/cache/Makefile.am b/sca-cpp/trunk/modules/cache/Makefile.am
new file mode 100644
index 0000000000..0597e91804
--- /dev/null
+++ b/sca-cpp/trunk/modules/cache/Makefile.am
@@ -0,0 +1,31 @@
+# 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.
+
+noinst_PROGRAMS = diskcache-test memcache-test
+
+nobase_include_HEADERS = *.hpp
+
+INCLUDES = -I. -I$(top_builddir)/kernel -I${LIBXML2_INCLUDE} -I${APR_INCLUDE}
+
+diskcache_test_SOURCES = diskcache-test.cpp
+diskcache_test_LDADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1
+
+memcache_test_SOURCES = memcache-test.cpp
+memcache_test_LDADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1
+
+TESTS = memcached-test diskcached-test
+
diff --git a/sca-cpp/trunk/modules/cache/cache.hpp b/sca-cpp/trunk/modules/cache/cache.hpp
new file mode 100644
index 0000000000..ba66021f3d
--- /dev/null
+++ b/sca-cpp/trunk/modules/cache/cache.hpp
@@ -0,0 +1,122 @@
+/*
+ * 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_cache_hpp
+#define tuscany_cache_hpp
+
+/**
+ * Simple cache monad implementation.
+ */
+
+#include <sys/stat.h>
+
+#include <string>
+#include "value.hpp"
+#include "monad.hpp"
+
+namespace tuscany {
+namespace cache {
+
+/**
+ * Cached monad. Used to represent a value that can be cached.
+ * To get the value in the monad, just cast it to the value type.
+ */
+template<typename V> class cached {
+public:
+ cached() : mtime(0) {
+ }
+
+ cached(const lambda<V()>& lvalue, const lambda<unsigned long()> ltime) : lvalue(lvalue), ltime(ltime), mtime(0) {
+ }
+
+ cached(const lambda<V()>& lvalue, const lambda<unsigned long()> ltime, const unsigned long mtime, const V& v) : lvalue(lvalue), ltime(ltime), mtime(mtime), v(v) {
+ }
+
+ cached(const cached<V>& c) : lvalue(c.lvalue), ltime(c.ltime), mtime(c.mtime), v(c.v) {
+ }
+
+ operator const V() const {
+ return v;
+ }
+
+ const cached<V>& operator=(const cached<V>& c) {
+ if(this == &c)
+ return *this;
+ this->lvalue = c.lvalue;
+ this->ltime = c.ltime;
+ this->mtime = c.mtime;
+ this->v = c.v;
+ return *this;
+ }
+
+ const bool operator!=(const cached<V>& m) const {
+ return !this->operator==(m);
+ }
+
+ const bool operator==(const cached<V>& m) const {
+ if (this == &m)
+ return true;
+ return mtime == m.mtime && v == m.v;
+ }
+
+private:
+ lambda<V()> lvalue;
+ lambda<time_t()> ltime;
+ unsigned long mtime;
+ V v;
+
+ template<typename X> friend const cached<X> latest(const cached<X>& c);
+ template<typename X> friend std::ostream& operator<<(std::ostream& out, const cached<X>& c);
+};
+
+/**
+ * Write a cached monad to a stream.
+ */
+template<typename V> std::ostream& operator<<(std::ostream& out, const cached<V>& c) {
+ out << c.v;
+ return out;
+}
+
+/**
+ * Returns the latest value of a cached monad.
+ */
+template<typename V> const cached<V> latest(const cached<V>& c) {
+ unsigned long nt = c.ltime();
+ if (nt == c.mtime)
+ return c;
+ return cached<V>(c.lvalue, c.ltime, nt, c.lvalue());
+}
+
+/**
+ * Returns the latest modification time of a file.
+ */
+const unsigned long latestFileTime(const std::string& path) {
+ struct stat st;
+ int rc = stat(path.c_str(), &st);
+ if (rc < 0)
+ return 0;
+ return st.st_mtim.tv_nsec;
+}
+
+}
+}
+
+#endif /* tuscany_cache_hpp */
diff --git a/sca-cpp/trunk/modules/cache/diskcache-test.cpp b/sca-cpp/trunk/modules/cache/diskcache-test.cpp
new file mode 100644
index 0000000000..3b21de7b9e
--- /dev/null
+++ b/sca-cpp/trunk/modules/cache/diskcache-test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test cache functions.
+ */
+
+#include <assert.h>
+#include <sys/time.h>
+#include <time.h>
+#include <iostream>
+#include <string>
+#include <fstream>
+#include "slist.hpp"
+#include "cache.hpp"
+
+namespace tuscany {
+namespace cache {
+
+const std::string fileRead(const std::string path) {
+ std::ifstream is(path);
+ return car(streamList(is));
+}
+
+bool testCache() {
+ const std::string p("tmp/test.txt");
+ const lambda<std::string(std::string)> fr(fileRead);
+ const lambda<time_t(std::string)> ft(latestFileTime);
+
+ const cached<std::string> c(curry(fr, p), curry(ft, p));
+
+ {
+ std::ofstream os(p);
+ os << "initial";
+ os.close();
+ assert(std::string(latest(c)) == std::string("initial"));
+ }
+
+ usleep(1000000);
+
+ {
+ std::ofstream os(p);
+ os << "updated";
+ os.close();
+ assert(latest(c) != c);
+ assert(std::string(latest(c)) == std::string("updated"));
+ assert(latest(c) == latest(c));
+ }
+ return true;
+}
+
+}
+}
+
+int main() {
+ std::cout << "Testing..." << std::endl;
+
+ tuscany::cache::testCache();
+
+ std::cout << "OK" << std::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/trunk/modules/cache/diskcached-test b/sca-cpp/trunk/modules/cache/diskcached-test
new file mode 100755
index 0000000000..4c10abeceb
--- /dev/null
+++ b/sca-cpp/trunk/modules/cache/diskcached-test
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Setup
+mkdir -p tmp
+
+# Test
+./diskcache-test
+rc=$?
+
+return $rc
diff --git a/sca-cpp/trunk/modules/cache/memcache-test.cpp b/sca-cpp/trunk/modules/cache/memcache-test.cpp
new file mode 100644
index 0000000000..3522094126
--- /dev/null
+++ b/sca-cpp/trunk/modules/cache/memcache-test.cpp
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test Memcached access functions.
+ */
+
+#include <assert.h>
+#include <sys/time.h>
+#include <time.h>
+#include <iostream>
+#include <string>
+#include <fstream>
+#include "memcached.hpp"
+
+namespace tuscany {
+namespace cache {
+
+bool testMemCached() {
+ memcached::Cache cache;
+ memcached::addServer("localhost", 11311, cache);
+
+ assert(hasValue(memcached::post("a", "AAA", cache)));
+ assert(memcached::get("a", cache) == value(std::string("AAA")));
+ assert(hasValue(memcached::put("a", "aaa", cache)));
+ assert(memcached::get("a", cache) == value(std::string("aaa")));
+ assert(hasValue(memcached::del("a", cache)));
+ assert(!hasValue(memcached::get("a", cache)));
+
+ return true;
+}
+
+const double duration(struct timeval start, struct timeval end, int count) {
+ long t = (end.tv_sec * 1000 + end.tv_usec / 1000) - (start.tv_sec * 1000 + start.tv_usec / 1000);
+ return (double)t / (double)count;
+}
+
+bool testGetLoop(const int count, memcached::Cache& cache) {
+ if (count == 0)
+ return true;
+ assert(memcached::get("c", cache) == value(std::string("CCC")));
+ return testGetLoop(count - 1, cache);
+}
+
+bool testGetPerf() {
+ const int count = 50;
+ struct timeval start;
+ struct timeval end;
+ {
+ memcached::Cache cache;
+ memcached::addServer("localhost", 11311, cache);
+ assert(hasValue(memcached::post("c", "CCC", cache)));
+
+ testGetLoop(5, cache);
+
+ gettimeofday(&start, NULL);
+
+ testGetLoop(count, cache);
+
+ gettimeofday(&end, NULL);
+ std::cout << "Memcached get test " << duration(start, end, count) << " ms" << std::endl;
+ }
+ return true;
+}
+
+}
+}
+
+int main() {
+ std::cout << "Testing..." << std::endl;
+
+ tuscany::cache::testMemCached();
+ tuscany::cache::testGetPerf();
+
+ std::cout << "OK" << std::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/trunk/modules/cache/memcached-test b/sca-cpp/trunk/modules/cache/memcached-test
new file mode 100755
index 0000000000..d0e0dff3de
--- /dev/null
+++ b/sca-cpp/trunk/modules/cache/memcached-test
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Setup
+cmd="memcached -l 127.0.0.1 -m 4 -p 11311"
+$cmd &
+sleep 1
+
+# Test
+./memcache-test
+rc=$?
+
+# Cleanup
+ps -f | grep -v grep | grep "$cmd" | awk '{ print $2 }' | xargs kill
+return $rc
diff --git a/sca-cpp/trunk/modules/cache/memcached.hpp b/sca-cpp/trunk/modules/cache/memcached.hpp
new file mode 100644
index 0000000000..28bdc0fc89
--- /dev/null
+++ b/sca-cpp/trunk/modules/cache/memcached.hpp
@@ -0,0 +1,143 @@
+/*
+ * 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_memcached_hpp
+#define tuscany_memcached_hpp
+
+/**
+ * Memcached access functions.
+ */
+
+#include "apr.h"
+#include "apu.h"
+#include "apr_general.h"
+#include "apr_strings.h"
+#include "apr_hash.h"
+#include "apr_memcache.h"
+#include "apr_network_io.h"
+
+#include <string>
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+
+namespace tuscany {
+namespace memcached {
+
+/**
+ * Represents a memcached context.
+ */
+class Cache {
+public:
+ Cache() {
+ apr_pool_create(&pool, NULL);
+ apr_memcache_create(pool, 1, 0, &mc);
+ }
+ ~Cache() {
+ apr_pool_destroy(pool);
+ }
+
+ operator apr_memcache_t*() const {
+ return mc;
+ }
+
+ operator apr_pool_t*() const {
+ return pool;
+ }
+
+private:
+ apr_pool_t* pool;
+ apr_memcache_t* mc;
+
+};
+
+/**
+ * Add a server to the memcached context.
+ */
+const failable<bool, std::string> addServer(const std::string& host, const int port, const Cache& cache) {
+ apr_memcache_server_t *server;
+ const apr_status_t sc = apr_memcache_server_create(cache, host.c_str(), port, 0, 1, 1, 60, &server);
+ if (sc != APR_SUCCESS)
+ return mkfailure<bool, std::string>("Could not create server");
+ const apr_status_t as = apr_memcache_add_server(cache, server);
+ if (as != APR_SUCCESS)
+ return mkfailure<bool, std::string>("Could not add server");
+ return true;
+}
+
+/**
+ * Post a new item to the cache.
+ */
+const failable<bool, std::string> post(const value& key, const value& val, const Cache& cache) {
+ const std::string v(val);
+ const apr_status_t rc = apr_memcache_add(cache, std::string(key).c_str(), const_cast<char*>(v.c_str()), v.size(), 0, 27);
+ if (rc != APR_SUCCESS)
+ return mkfailure<bool, std::string>("Could not add entry");
+ return true;
+}
+
+/**
+ * Update an item in the cache.
+ */
+const failable<bool, std::string> put(const value& key, const value& val, const Cache& cache) {
+ const std::string v(val);
+ const apr_status_t rc = apr_memcache_replace(cache, std::string(key).c_str(), const_cast<char*>(v.c_str()), v.size(), 0, 27);
+ if (rc != APR_SUCCESS)
+ return mkfailure<bool, std::string>("Could not add entry");
+ return true;
+}
+
+/**
+ * Get an item from the cache.
+ */
+const failable<value, std::string> get(const value& key, const Cache& cache) {
+ apr_pool_t* vpool;
+ const apr_status_t pc = apr_pool_create(&vpool, cache);
+ if (pc != APR_SUCCESS)
+ return mkfailure<value, std::string>("Could not allocate memory");
+
+ char *data;
+ apr_size_t size;
+ const apr_status_t rc = apr_memcache_getp(cache, cache, std::string(key).c_str(), &data, &size, NULL);
+ if (rc != APR_SUCCESS) {
+ apr_pool_destroy(vpool);
+ return mkfailure<value, std::string>("Could not get entry");
+ }
+
+ const value val(std::string(data, size));
+ apr_pool_destroy(vpool);
+ return val;
+}
+
+/**
+ * Delete an item from the cache
+ */
+const failable<bool, std::string> del(const value& key, const Cache& cache) {
+ const apr_status_t rc = apr_memcache_delete(cache, std::string(key).c_str(), 0);
+ if (rc != APR_SUCCESS)
+ return mkfailure<bool, std::string>("Could not add entry");
+ return true;
+}
+
+}
+}
+
+#endif /* tuscany_memcached_hpp */
diff --git a/sca-cpp/trunk/modules/eval/Makefile.am b/sca-cpp/trunk/modules/eval/Makefile.am
new file mode 100644
index 0000000000..68e01d42e2
--- /dev/null
+++ b/sca-cpp/trunk/modules/eval/Makefile.am
@@ -0,0 +1,34 @@
+# 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.
+
+noinst_PROGRAMS = eval-test eval-shell
+
+datadir=$(prefix)/modules/eval
+nobase_data_DATA = *.xsd
+
+nobase_include_HEADERS = *.hpp
+
+INCLUDES = -I. -I$(top_builddir)/kernel -I${LIBXML2_INCLUDE} -I${APR_INCLUDE}
+
+eval_test_SOURCES = eval-test.cpp
+eval_test_LDADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1
+
+eval_shell_SOURCES = eval-shell.cpp
+eval_shell_LDADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1
+
+TESTS = eval-test
+
diff --git a/sca-cpp/trunk/modules/eval/driver.hpp b/sca-cpp/trunk/modules/eval/driver.hpp
new file mode 100644
index 0000000000..4c69ecb0a1
--- /dev/null
+++ b/sca-cpp/trunk/modules/eval/driver.hpp
@@ -0,0 +1,77 @@
+/*
+ * 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_eval_driver_hpp
+#define tuscany_eval_driver_hpp
+
+/**
+ * Script evaluator main driver loop.
+ */
+
+#include <string>
+#include <iostream>
+#include "eval.hpp"
+
+namespace tuscany {
+namespace eval {
+
+const std::string evalOutputPrompt("; ");
+const std::string evalInputPrompt("=> ");
+
+const bool promptForInput(const std::string str, std::ostream& out) {
+ out << "\n\n" << str;
+ return true;
+}
+
+const bool announceOutput(const std::string str, std::ostream& out) {
+ out << "\n" << str;
+ return true;
+}
+
+const bool userPrint(const value val, std::ostream& out) {
+ if(isCompoundProcedure(val))
+ writeValue(mklist<value>(compoundProcedureSymbol, procedureParameters(val), procedureBody(val), "<procedure-env>"), out);
+ writeValue(val, out);
+ return true;
+}
+
+const value evalDriverLoop(std::istream& in, std::ostream& out, Env& globalEnv, const gc_pool& pool) {
+ promptForInput(evalInputPrompt, out);
+ value input = readValue(in);
+ if (isNil(input))
+ return input;
+ const value output = evalExpr(input, globalEnv, pool);
+ announceOutput(evalOutputPrompt, out);
+ userPrint(output, out);
+ return evalDriverLoop(in, out, globalEnv, pool);
+}
+
+const bool evalDriverRun(std::istream& in, std::ostream& out) {
+ gc_pool pool;
+ setupDisplay(out);
+ Env globalEnv = setupEnvironment(pool);
+ evalDriverLoop(in, out, globalEnv, pool);
+ return true;
+}
+
+}
+}
+#endif /* tuscany_eval_driver_hpp */
diff --git a/sca-cpp/trunk/modules/eval/environment.hpp b/sca-cpp/trunk/modules/eval/environment.hpp
new file mode 100644
index 0000000000..fa9667b1ba
--- /dev/null
+++ b/sca-cpp/trunk/modules/eval/environment.hpp
@@ -0,0 +1,178 @@
+/*
+ * 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_eval_environment_hpp
+#define tuscany_eval_environment_hpp
+
+/**
+ * Script evaluator environment implementation.
+ */
+
+#include <string>
+#include "list.hpp"
+#include "value.hpp"
+#include "primitive.hpp"
+
+namespace tuscany {
+namespace eval {
+
+typedef value Frame;
+typedef list<value> Env;
+
+const value trueSymbol("true");
+const value falseSymbol("false");
+const value defineSymbol("define");
+const value setSymbol("set!");
+const value dotSymbol(".");
+
+const Env theEmptyEnvironment() {
+ return list<value>();
+}
+
+const bool isDefinition(const value& exp) {
+ return isTaggedList(exp, defineSymbol);
+}
+
+const bool isAssignment(const value& exp) {
+ return isTaggedList(exp, setSymbol);
+}
+
+const bool isVariable(const value& exp) {
+ return isSymbol(exp);
+}
+
+const Env enclosingEnvironment(const Env& env) {
+ return cdr(env);
+}
+
+const gc_pool_ptr<Frame> firstFrame(const Env& env) {
+ return car(env);
+}
+
+list<value> frameVariables(const Frame& frame) {
+ return car((list<value> )frame);
+}
+
+list<value> frameValues(const Frame& frame) {
+ return cdr((list<value> )frame);
+}
+
+const bool isDotVariable(const value& var) {
+ return var == dotSymbol;
+}
+
+const Frame makeBinding(const Frame& frameSoFar, const list<value>& variables, const list<value> values) {
+ if (isNil(variables)) {
+ if (!isNil(values))
+ std::cout << "Too many arguments supplied " << values << "\n";
+ return frameSoFar;
+ }
+ if (isDotVariable(car(variables)))
+ return makeBinding(frameSoFar, cdr(variables), mklist<value>(values));
+
+ if (isNil(values)) {
+ if (!isNil(variables))
+ std::cout << "Too few arguments supplied " << variables << "\n";
+ return frameSoFar;
+ }
+
+ const list<value> vars = cons(car(variables), frameVariables(frameSoFar));
+ const list<value> vals = cons(car(values), frameValues(frameSoFar));
+ const Frame newFrame = cons(value(vars), vals);
+
+ return makeBinding(newFrame, cdr(variables), cdr(values));
+}
+
+const gc_pool_ptr<Frame> makeFrame(const list<value>& variables, const list<value> values, const gc_pool& pool) {
+ gc_pool_ptr<Frame> frame = gc_pool_new<Frame>(pool);
+ *frame = value(makeBinding(cons(value(list<value>()), list<value>()), variables, values));
+ return frame;
+}
+
+const value definitionVariable(const value& exp) {
+ const list<value> exps(exp);
+ if(isSymbol(car(cdr(exps))))
+ return car(cdr(exps));
+ const list<value> lexps(car(cdr(exps)));
+ return car(lexps);
+}
+
+const value definitionValue(const value& exp) {
+ const list<value> exps(exp);
+ if(isSymbol(car(cdr(exps))))
+ return car(cdr(cdr(exps)));
+ const list<value> lexps(car(cdr(exps)));
+ return makeLambda(cdr(lexps), cdr(cdr(exps)));
+}
+
+const value assignmentVariable(const value& exp) {
+ return car(cdr((list<value> )exp));
+}
+
+const value assignmentValue(const value& exp) {
+ return car(cdr(cdr((list<value> )exp)));
+}
+
+const Frame addBindingToFrame(const value& var, const value& val, const Frame& frame) {
+ return cons(value(cons(var, frameVariables(frame))), cons(val, frameValues(frame)));
+}
+
+const bool defineVariable(const value& var, const value& val, Env& env) {
+ *firstFrame(env) = addBindingToFrame(var, val, *firstFrame(env));
+ return true;
+}
+
+const Env extendEnvironment(const list<value>& vars, const list<value>& vals, const Env& baseEnv, const gc_pool& pool) {
+ return cons<value>(makeFrame(vars, vals, pool), baseEnv);
+}
+
+const Env setupEnvironment(const gc_pool& pool) {
+ Env env = extendEnvironment(primitiveProcedureNames(), primitiveProcedureObjects(), theEmptyEnvironment(), pool);
+ defineVariable(trueSymbol, true, env);
+ defineVariable(falseSymbol, false, env);
+ return env;
+}
+
+const value lookupEnvLoop(const value& var, const Env& env);
+
+const value lookupEnvScan(const value& var, const list<value>& vars, const list<value>& vals, const Env& env) {
+ if(isNil(vars))
+ return lookupEnvLoop(var, enclosingEnvironment(env));
+ if(var == car(vars))
+ return car(vals);
+ return lookupEnvScan(var, cdr(vars), cdr(vals), env);
+}
+
+const value lookupEnvLoop(const value& var, const Env& env) {
+ if(env == theEmptyEnvironment()) {
+ std::cout << "Unbound variable " << var << "\n";
+ return value();
+ }
+ return lookupEnvScan(var, frameVariables(*firstFrame(env)), frameValues(*firstFrame(env)), env);
+}
+
+const value lookupVariableValue(const value& var, const Env& env) {
+ return lookupEnvLoop(var, env);
+}
+
+}
+}
+#endif /* tuscany_eval_environment_hpp */
diff --git a/sca-cpp/trunk/modules/eval/eval-shell.cpp b/sca-cpp/trunk/modules/eval/eval-shell.cpp
new file mode 100644
index 0000000000..e1c90101da
--- /dev/null
+++ b/sca-cpp/trunk/modules/eval/eval-shell.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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$ */
+
+/**
+ * Script evaluator shell, used for interactive testing of scripts.
+ */
+
+#include <assert.h>
+#include <iostream>
+#include <string>
+#include <sstream>
+#include "driver.hpp"
+
+int main() {
+ tuscany::eval::evalDriverRun(std::cin, std::cout);
+ return 0;
+}
diff --git a/sca-cpp/trunk/modules/eval/eval-test.cpp b/sca-cpp/trunk/modules/eval/eval-test.cpp
new file mode 100644
index 0000000000..984b17b26d
--- /dev/null
+++ b/sca-cpp/trunk/modules/eval/eval-test.cpp
@@ -0,0 +1,240 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test script evaluator.
+ */
+
+#include <assert.h>
+#include <iostream>
+#include <string>
+#include <sstream>
+#include "driver.hpp"
+
+namespace tuscany {
+namespace eval {
+
+bool testEnv() {
+ gc_pool pool;
+ Env globalEnv = list<value>();
+ Env env = extendEnvironment(mklist<value>("a"), mklist<value>(1), globalEnv, pool);
+ defineVariable("x", env, env);
+ assert(lookupVariableValue(value("x"), env) == env);
+ assert(lookupVariableValue("a", env) == value(1));
+ return true;
+}
+
+bool testEnvGC() {
+ resetLambdaCounters();
+ resetListCounters();
+ resetValueCounters();
+ testEnv();
+ assert(countValues == 0);
+ assert(countLambdas == 0);
+ assert(countlists == 0);
+ //printLambdaCounters();
+ //printListCounters();
+ //printValueCounters();
+ return true;
+}
+
+bool testRead() {
+ std::istringstream is("abcd");
+ assert(readValue(is) == "abcd");
+
+ std::istringstream is2("123");
+ assert(readValue(is2) == value(123));
+
+ std::istringstream is3("(abcd)");
+ assert(readValue(is3) == mklist(value("abcd")));
+
+ std::istringstream is4("(abcd xyz)");
+ assert(readValue(is4) == mklist<value>("abcd", "xyz"));
+
+ std::istringstream is5("(abcd (xyz tuv))");
+ assert(readValue(is5) == mklist<value>("abcd", mklist<value>("xyz", "tuv")));
+
+ return true;
+}
+
+bool testWrite() {
+ const list<value> i = list<value>()
+ << (list<value>() << "item" << "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"
+ << (list<value>() << "item"
+ << (list<value>() << "name" << "Apple")
+ << (list<value>() << "price" << "$2.99")))
+ << (list<value>() << "item" << "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c"
+ << (list<value>() << "item"
+ << (list<value>() << "name" << "Orange")
+ << (list<value>() << "price" << "$3.55")));
+ const list<value> a = cons<value>("Feed", cons<value>("feed-1234", i));
+ std::ostringstream os;
+ writeValue(a, os);
+ std::istringstream is(os.str());
+ assert(readValue(is) == a);
+ return true;
+}
+
+const std::string testSchemeNumber(
+ "(define (testNumber) (if (= 1 1) (display \"testNumber ok\") (error \"testNumber\"))) "
+ "(testNumber)");
+
+const std::string testSchemeString(
+ "(define (testString) (if (= \"abc\" \"abc\") (display \"testString ok\") (error \"testString\"))) "
+ "(testString)");
+
+const std::string testSchemeDefinition(
+ "(define a \"abc\") (define (testDefinition) (if (= a \"abc\") (display \"testDefinition ok\") (error \"testDefinition\"))) "
+ "(testDefinition)");
+
+const std::string testSchemeIf(
+ "(define (testIf) (if (= \"abc\" \"abc\") (if (= \"xyz\" \"xyz\") (display \"testIf ok\") (error \"testNestedIf\")) (error \"testIf\"))) "
+ "(testIf)");
+
+const std::string testSchemeCond(
+ "(define (testCond) (cond ((= \"abc\" \"abc\") (display \"testCond ok\")) (else (error \"testIf\"))))"
+ "(testCond)");
+
+const std::string testSchemeBegin(
+ "(define (testBegin) "
+ "(begin "
+ "(define a \"abc\") "
+ "(if (= a \"abc\") (display \"testBegin1 ok\") (error \"testBegin\")) "
+ "(define x \"xyz\") "
+ "(if (= x \"xyz\") (display \"testBegin2 ok\") (error \"testBegin\")) "
+ ") "
+ ") "
+ "(testBegin)");
+
+const std::string testSchemeLambda(
+ "(define sqrt (lambda (x) (* x x))) "
+ "(define (testLambda) (if (= 4 (sqrt 2)) (display \"testLambda ok\") (error \"testLambda\"))) "
+ "(testLambda)");
+
+const std::string testSchemeForward(
+ "(define (testLambda) (if (= 4 (sqrt 2)) (display \"testForward ok\") (error \"testForward\"))) "
+ "(define sqrt (lambda (x) (* x x))) "
+ "(testLambda)");
+
+bool contains(const std::string& str, const std::string& pattern) {
+ return str.find(pattern) != str.npos;
+}
+
+const std::string evalOutput(const std::string& scm) {
+ std::istringstream is(scm);
+ std::ostringstream os;
+ evalDriverRun(is, os);
+ return os.str();
+}
+
+bool testEval() {
+ assert(contains(evalOutput(testSchemeNumber), "testNumber ok"));
+ assert(contains(evalOutput(testSchemeString), "testString ok"));
+ assert(contains(evalOutput(testSchemeDefinition), "testDefinition ok"));
+ assert(contains(evalOutput(testSchemeIf), "testIf ok"));
+ assert(contains(evalOutput(testSchemeCond), "testCond ok"));
+ assert(contains(evalOutput(testSchemeBegin), "testBegin1 ok"));
+ assert(contains(evalOutput(testSchemeBegin), "testBegin2 ok"));
+ assert(contains(evalOutput(testSchemeLambda), "testLambda ok"));
+ assert(contains(evalOutput(testSchemeForward), "testForward ok"));
+ return true;
+}
+
+bool testEvalExpr() {
+ gc_pool pool;
+ const value exp = mklist<value>("+", 2, 3);
+ Env env = setupEnvironment(pool);
+ const value r = evalExpr(exp, env, pool);
+ assert(r == value(5));
+ return true;
+}
+
+bool testEvalRun() {
+ evalDriverRun(std::cin, std::cout);
+ return true;
+}
+
+const value mult(const list<value>& args) {
+ const double x = car(args);
+ const double y = cadr(args);
+ return x * y;
+}
+
+const std::string testReturnLambda(
+ "(define (testReturnLambda) * )");
+
+const std::string testCallLambda(
+ "(define (testCallLambda l x y) (l x y))");
+
+bool testEvalLambda() {
+ gc_pool pool;
+ Env env = setupEnvironment(pool);
+
+ const value trl = mklist<value>("testReturnLambda");
+ std::istringstream trlis(testReturnLambda);
+ const value trlv = evalScript(trl, trlis, env, pool);
+
+ std::istringstream tclis(testCallLambda);
+ const value tcl = cons<value>("testCallLambda", quotedParameters(mklist<value>(trlv, 2, 3)));
+ const value tclv = evalScript(tcl, tclis, env, pool);
+ assert(tclv == value(6));
+
+ std::istringstream tcelis(testCallLambda);
+ const value tcel = cons<value>("testCallLambda", quotedParameters(mklist<value>(primitiveProcedure(mult), 3, 4)));
+ const value tcelv = evalScript(tcel, tcelis, env, pool);
+ assert(tcelv == value(12));
+ return true;
+}
+
+bool testEvalGC() {
+ resetLambdaCounters();
+ resetListCounters();
+ resetValueCounters();
+ testEval();
+ testEvalExpr();
+ testEvalLambda();
+ assert(countValues == 0);
+ assert(countLambdas == 0);
+ assert(countlists == 0);
+ //printLambdaCounters();
+ //printListCounters();
+ //printValueCounters();
+ return true;
+}
+
+}
+}
+
+int main() {
+ std::cout << "Testing..." << std::endl;
+
+ tuscany::eval::testEnv();
+ tuscany::eval::testEnvGC();
+ tuscany::eval::testRead();
+ tuscany::eval::testWrite();
+ tuscany::eval::testEval();
+ tuscany::eval::testEvalExpr();
+ tuscany::eval::testEvalLambda();
+ tuscany::eval::testEvalGC();
+
+ std::cout << "OK" << std::endl;
+ return 0;
+}
diff --git a/sca-cpp/trunk/modules/eval/eval.hpp b/sca-cpp/trunk/modules/eval/eval.hpp
new file mode 100644
index 0000000000..8c9ecfdecc
--- /dev/null
+++ b/sca-cpp/trunk/modules/eval/eval.hpp
@@ -0,0 +1,290 @@
+/*
+ * 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_eval_eval_hpp
+#define tuscany_eval_eval_hpp
+
+/**
+ * Core script evaluation logic.
+ */
+
+#include <string.h>
+#include "list.hpp"
+#include "value.hpp"
+#include "primitive.hpp"
+#include "io.hpp"
+#include "environment.hpp"
+
+namespace tuscany {
+namespace eval {
+
+const value evalExpr(const value& exp, Env& env, const gc_pool& pool);
+
+const value compoundProcedureSymbol("compound-procedure");
+const value procedureSymbol("procedure");
+const value applySymbol("apply");
+const value beginSymbol("begin");
+const value condSymbol("cond");
+const value elseSymbol("else");
+const value ifSymbol("if");
+
+const bool isBegin(const value& exp) {
+ return isTaggedList(exp, beginSymbol);
+}
+
+const list<value> beginActions(const value& exp) {
+ return cdr((list<value> )exp);
+}
+
+const bool isLambdaExpr(const value& exp) {
+ return isTaggedList(exp, lambdaSymbol);
+}
+
+const list<value> lambdaParameters(const value& exp) {
+ return car(cdr((list<value> )exp));
+}
+
+static list<value> lambdaBody(const value& exp) {
+ return cdr(cdr((list<value> )exp));
+}
+
+const value makeProcedure(const list<value>& parameters, const value& body, const Env& env) {
+ return mklist<value>(procedureSymbol, parameters, body, env);
+}
+
+const bool isApply(const value& exp) {
+ return isTaggedList(exp, applySymbol);
+}
+
+const bool isApplication(const value& exp) {
+ return isList(exp);
+}
+
+const value operat(const value& exp) {
+ return car((list<value> )exp);
+}
+
+const list<value> operands(const value& exp) {
+ return cdr((list<value> )exp);
+}
+
+const list<value> listOfValues(const list<value> exps, Env& env, const gc_pool& pool) {
+ if(isNil(exps))
+ return list<value> ();
+ return cons(evalExpr(car(exps), env, pool), listOfValues(cdr(exps), env, pool));
+}
+
+const value applyOperat(const value& exp) {
+ return cadr((list<value> )exp);
+}
+
+const value applyOperand(const value& exp) {
+ return caddr((list<value> )exp);
+}
+
+const bool isCompoundProcedure(const value& procedure) {
+ return isTaggedList(procedure, procedureSymbol);
+}
+
+const list<value> procedureParameters(const value& exp) {
+ return car(cdr((list<value> )exp));
+}
+
+const value procedureBody(const value& exp) {
+ return car(cdr(cdr((list<value> )exp)));
+}
+
+const Env procedureEnvironment(const value& exp) {
+ return (Env)car(cdr(cdr(cdr((list<value> )exp))));
+}
+
+const bool isLastExp(const list<value>& seq) {
+ return isNil(cdr(seq));
+}
+
+const value firstExp(const list<value>& seq) {
+ return car(seq);
+}
+
+const list<value> restExp(const list<value>& seq) {
+ return cdr(seq);
+}
+
+const value makeBegin(const list<value> seq) {
+ return cons(beginSymbol, seq);
+}
+
+const value evalSequence(const list<value>& exps, Env& env, const gc_pool& pool) {
+ if(isLastExp(exps))
+ return evalExpr(firstExp(exps), env, pool);
+ evalExpr(firstExp(exps), env, pool);
+ return evalSequence(restExp(exps), env, pool);
+}
+
+const value applyProcedure(const value& procedure, list<value>& arguments, const gc_pool& pool) {
+ if(isPrimitiveProcedure(procedure))
+ return applyPrimitiveProcedure(procedure, arguments, pool);
+ if(isCompoundProcedure(procedure)) {
+ Env env = extendEnvironment(procedureParameters(procedure), arguments, procedureEnvironment(procedure), pool);
+ return evalSequence(procedureBody(procedure), env, pool);
+ }
+ std::cout << "Unknown procedure type " << procedure << "\n";
+ return value();
+}
+
+const value sequenceToExp(const list<value> exps) {
+ if(isNil(exps))
+ return exps;
+ if(isLastExp(exps))
+ return firstExp(exps);
+ return makeBegin(exps);
+}
+
+const list<value> condClauses(const value& exp) {
+ return cdr((list<value> )exp);
+}
+
+const value condPredicate(const value& clause) {
+ return car((list<value> )clause);
+}
+
+const list<value> condActions(const value& clause) {
+ return cdr((list<value> )clause);
+}
+
+const value ifPredicate(const value& exp) {
+ return car(cdr((list<value> )exp));
+}
+
+const value ifConsequent(const value& exp) {
+ return car(cdr(cdr((list<value> )exp)));
+}
+
+const value ifAlternative(const value& exp) {
+ if(!isNil(cdr(cdr(cdr((list<value> )exp)))))
+ return car(cdr(cdr(cdr((list<value> )exp))));
+ return false;
+}
+
+const bool isCond(const value& exp) {
+ return isTaggedList(exp, condSymbol);
+}
+
+const bool isCondElseClause(const value& clause) {
+ return condPredicate(clause) == elseSymbol;
+}
+
+const bool isIf(const value& exp) {
+ return isTaggedList(exp, ifSymbol);
+}
+
+const value makeIf(value predicate, value consequent, value alternative) {
+ return mklist(ifSymbol, predicate, consequent, alternative);
+}
+
+const value expandClauses(const list<value>& clauses) {
+ if(isNil(clauses))
+ return false;
+ const value first = car(clauses);
+ const list<value> rest = cdr(clauses);
+ if(isCondElseClause(first)) {
+ if(isNil(rest))
+ return sequenceToExp(condActions(first));
+ std::cout << "else clause isn't last " << clauses << "\n";
+ return value();
+ }
+ return makeIf(condPredicate(first), sequenceToExp(condActions(first)), expandClauses(rest));
+}
+
+value condToIf(const value& exp) {
+ return expandClauses(condClauses(exp));
+}
+
+value evalIf(const value& exp, Env& env, const gc_pool& pool) {
+ if(isTrue(evalExpr(ifPredicate(exp), env, pool)))
+ return evalExpr(ifConsequent(exp), env, pool);
+ return evalExpr(ifAlternative(exp), env, pool);
+}
+
+const value evalDefinition(const value& exp, Env& env, const gc_pool& pool) {
+ defineVariable(definitionVariable(exp), evalExpr(definitionValue(exp), env, pool), env);
+ return definitionVariable(exp);
+}
+
+const value evalExpr(const value& exp, Env& env, const gc_pool& pool) {
+ if(isSelfEvaluating(exp))
+ return exp;
+ if(isQuoted(exp))
+ return textOfQuotation(exp);
+ if(isDefinition(exp))
+ return evalDefinition(exp, env, pool);
+ if(isIf(exp))
+ return evalIf(exp, env, pool);
+ if(isBegin(exp))
+ return evalSequence(beginActions(exp), env, pool);
+ if(isCond(exp))
+ return evalExpr(condToIf(exp), env, pool);
+ if(isLambdaExpr(exp))
+ return makeProcedure(lambdaParameters(exp), lambdaBody(exp), env);
+ if(isVariable(exp))
+ return lookupVariableValue(exp, env);
+ if(isApply(exp)) {
+ list<value> applyOperandValues = evalExpr(applyOperand(exp), env, pool);
+ return applyProcedure(evalExpr(applyOperat(exp), env, pool), applyOperandValues, pool);
+ }
+ if(isApplication(exp)) {
+ list<value> operandValues = listOfValues(operands(exp), env, pool);
+ return applyProcedure(evalExpr(operat(exp), env, pool), operandValues, pool);
+ }
+ std::cout << "Unknown expression type " << exp << "\n";
+ return value();
+}
+
+const list<value> quotedParameters(const list<value>& p) {
+ if (isNil(p))
+ return p;
+ return cons<value>(mklist<value>(quoteSymbol, car(p)), quotedParameters(cdr(p)));
+}
+
+/**
+ * Evaluate an expression against a script provided as a list of values.
+ */
+const value evalScriptLoop(const value& expr, const list<value>& script, eval::Env& globalEnv, const gc_pool& pool) {
+ if (isNil(script))
+ return eval::evalExpr(expr, globalEnv, pool);
+ eval::evalExpr(car(script), globalEnv, pool);
+ return evalScriptLoop(expr, cdr(script), globalEnv, pool);
+}
+
+const value evalScript(const value& expr, const value& script, Env& env, const gc_pool& pool) {
+ return evalScriptLoop(expr, script, env, pool);
+}
+
+/**
+ * Evaluate an expression against a script provided as an input stream.
+ */
+const value evalScript(const value& expr, std::istream& is, Env& env, const gc_pool& pool) {
+ return evalScript(expr, readScript(is), env, pool);
+}
+
+}
+}
+#endif /* tuscany_eval_eval_hpp */
diff --git a/sca-cpp/trunk/modules/eval/io.hpp b/sca-cpp/trunk/modules/eval/io.hpp
new file mode 100644
index 0000000000..a898a11440
--- /dev/null
+++ b/sca-cpp/trunk/modules/eval/io.hpp
@@ -0,0 +1,206 @@
+/*
+ * 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_eval_io_hpp
+#define tuscany_eval_io_hpp
+
+/**
+ * Script evaluator IO functions.
+ */
+
+#include <iostream>
+#include <string>
+#include <sstream>
+#include <ctype.h>
+
+#include "list.hpp"
+#include "value.hpp"
+#include "primitive.hpp"
+
+namespace tuscany {
+namespace eval {
+
+const value rightParenthesis(mklist<value>(")"));
+const value leftParenthesis(mklist<value>("("));
+const value comment(mklist<value>(";"));
+
+const double stringToNumber(const std::string& str) {
+ double d;
+ std::istringstream is(str);
+ is >> d;
+ return d;
+}
+
+const bool isWhitespace(const char ch) {
+ return ch != -1 && isspace(ch);
+}
+
+const bool isIdentifierStart(const char ch) {
+ return ch != -1 && !isspace(ch) && !isdigit(ch);
+}
+
+const bool isIdentifierPart(const char ch) {
+ return ch != -1 && !isspace(ch) && ch != '(' && ch != ')';
+}
+
+const bool isDigit(const char ch) {
+ return isdigit(ch) || ch == '.';
+}
+
+const bool isLeftParenthesis(const value& token) {
+ return leftParenthesis == token;
+}
+
+const bool isRightParenthesis(const value& token) {
+ return rightParenthesis == token;
+}
+
+const char readChar(std::istream& in) {
+ if(in.eof()) {
+ return -1;
+ }
+ char c = in.get();
+ return c;
+}
+
+const char peekChar(std::istream& in) {
+ if(in.eof())
+ return -1;
+ char c = in.peek();
+ return c;
+}
+
+const bool isQuote(const value& token) {
+ return token == quoteSymbol;
+}
+
+const value skipComment(std::istream& in);
+const value readQuoted(std::istream& in);
+const value readIdentifier(const char chr, std::istream& in);
+const value readString(const char chr, std::istream& in);
+const value readNumber(const char chr, std::istream& in);
+const value readValue(std::istream& in);
+
+const value readToken(std::istream& in) {
+ const char firstChar = readChar(in);
+ if(isWhitespace(firstChar))
+ return readToken(in);
+ if(firstChar == ';')
+ return skipComment(in);
+ if(firstChar == '\'')
+ return readQuoted(in);
+ if(firstChar == '(')
+ return leftParenthesis;
+ if(firstChar == ')')
+ return rightParenthesis;
+ if(firstChar == '"')
+ return readString(firstChar, in);
+ if(isIdentifierStart(firstChar))
+ return readIdentifier(firstChar, in);
+ if(isDigit(firstChar))
+ return readNumber(firstChar, in);
+ if(firstChar == -1)
+ return value();
+ std::cout << "Illegal lexical syntax '" << firstChar << "'" << "\n";
+ return readToken(in);
+}
+
+const value skipComment(std::istream& in) {
+ const char nextChar = readChar(in);
+ if (nextChar == '\n')
+ return readToken(in);
+ return skipComment(in);
+}
+
+const value readQuoted(std::istream& in) {
+ return mklist(quoteSymbol, readValue(in));
+}
+
+const list<value> readList(const list<value>& listSoFar, std::istream& in) {
+ const value token = readToken(in);
+ if(isNil(token) || isRightParenthesis(token))
+ return reverse(listSoFar);
+ if(isLeftParenthesis(token))
+ return readList(cons(value(readList(list<value> (), in)), listSoFar), in);
+ return readList(cons(token, listSoFar), in);
+}
+
+const std::string listToString(const list<char>& l) {
+ if(isNil(l))
+ return "";
+ return car(l) + listToString(cdr(l));
+}
+
+const list<char> readIdentifierHelper(const list<char>& listSoFar, std::istream& in) {
+ const char nextChar = peekChar(in);
+ if(isIdentifierPart(nextChar))
+ return readIdentifierHelper(cons(readChar(in), listSoFar), in);
+ return reverse(listSoFar);
+}
+
+const value readIdentifier(const char chr, std::istream& in) {
+ return listToString(readIdentifierHelper(mklist(chr), in)).c_str();
+}
+
+const list<char> readStringHelper(const list<char>& listSoFar, std::istream& in) {
+ const char nextChar = readChar(in);
+ if(nextChar != -1 && nextChar != '"')
+ return readStringHelper(cons(nextChar, listSoFar), in);
+ return reverse(listSoFar);
+}
+
+const value readString(const char chr, std::istream& in) {
+ return listToString(readStringHelper(list<char>(), in));
+}
+
+const list<char> readNumberHelper(const list<char>& listSoFar, std::istream& in) {
+ const char nextChar = peekChar(in);
+ if(isDigit(nextChar))
+ return readNumberHelper(cons(readChar(in), listSoFar), in);
+ return reverse(listSoFar);
+}
+
+const value readNumber(const char chr, std::istream& in) {
+ return stringToNumber(listToString(readNumberHelper(mklist(chr), in)));
+}
+
+const value readValue(std::istream& in) {
+ const value nextToken = readToken(in);
+ if(isLeftParenthesis(nextToken))
+ return readList(list<value> (), in);
+ return nextToken;
+}
+
+const bool writeValue(const value& val, std::ostream& out) {
+ out << val;
+ return true;
+}
+
+const value readScript(std::istream& in) {
+ const value val = readValue(in);
+ if (isNil(val))
+ return list<value>();
+ return cons(val, (list<value>)readScript(in));
+}
+
+}
+}
+#endif /* tuscany_eval_io_hpp */
diff --git a/sca-cpp/trunk/modules/eval/primitive.hpp b/sca-cpp/trunk/modules/eval/primitive.hpp
new file mode 100644
index 0000000000..bd36c0e226
--- /dev/null
+++ b/sca-cpp/trunk/modules/eval/primitive.hpp
@@ -0,0 +1,197 @@
+/*
+ * 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_eval_primitive_hpp
+#define tuscany_eval_primitive_hpp
+
+/**
+ * Script evaluator primitive functions.
+ */
+
+#include <apr_general.h>
+#include <apr_uuid.h>
+#include <iostream>
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+
+namespace tuscany {
+namespace eval {
+
+const value primitiveSymbol("primitive");
+const value quoteSymbol("'");
+const value lambdaSymbol("lambda");
+
+std::ostream* displayOut = &std::cout;
+
+const bool setupDisplay(std::ostream& out) {
+ displayOut = &out;
+ return true;
+}
+
+const value carProc(const list<value>& args) {
+ return car((list<value> )car(args));
+}
+
+const value cdrProc(const list<value>& args) {
+ return cdr((list<value> )car(args));
+}
+
+const value consProc(const list<value>& args) {
+ return cons(car(args), (list<value> )cadr(args));
+}
+
+const value listProc(const list<value>& args) {
+ return args;
+}
+
+const value nulProc(const list<value>& args) {
+ return (bool)isNil(car(args));
+}
+
+const value equalProc(const list<value>& args) {
+ return (bool)(car(args) == cadr(args));
+}
+
+const value addProc(const list<value>& args) {
+ if (isNil(cdr(args)))
+ return (double)car(args);
+ return (double)car(args) + (double)cadr(args);
+}
+
+const value subProc(const list<value>& args) {
+ if (isNil(cdr(args)))
+ return (double)0 - (double)car(args);
+ return (double)car(args) - (double)cadr(args);
+}
+
+const value mulProc(const list<value>& args) {
+ return (double)car(args) * (double)cadr(args);
+}
+
+const value divProc(const list<value>& args) {
+ return (double)car(args) / (double)cadr(args);
+}
+
+const value displayProc(const list<value>& args) {
+ *displayOut << car(args);
+ (*displayOut).flush();
+ return true;
+}
+
+const value uuidProc(const list<value>& args) {
+ apr_uuid_t uuid;
+ apr_uuid_get(&uuid);
+ char buf[APR_UUID_FORMATTED_LENGTH];
+ apr_uuid_format(buf, &uuid);
+ return std::string(buf, APR_UUID_FORMATTED_LENGTH);
+}
+
+const value applyPrimitiveProcedure(const value& proc, list<value>& args, const gc_pool& pool) {
+ const lambda<value(list<value>&)> func(cadr((list<value>)proc));
+ return func(args);
+}
+
+const bool isPrimitiveProcedure(const value& proc) {
+ return isTaggedList(proc, primitiveSymbol);
+}
+
+const bool isSelfEvaluating(const value& exp) {
+ if(isNil(exp))
+ return true;
+ if(isNumber(exp))
+ return true;
+ if(isString(exp))
+ return true;
+ if(isBool(exp))
+ return true;
+ if(isChar(exp))
+ return true;
+ if(isLambda(exp))
+ return true;
+ return false;
+}
+
+const value primitiveImplementation(const list<value>& proc) {
+ return car(cdr(proc));
+}
+
+template<typename F> const value primitiveProcedure(const F& f) {
+ return mklist<value>(primitiveSymbol, (lambda<value(list<value>&)>)f);
+}
+
+const list<value> primitiveProcedureNames() {
+ list<value> l = mklist<value>("car");
+ l = cons<value>("cdr", l);
+ l = cons<value>("cons", l);
+ l = cons<value>("list", l);
+ l = cons<value>("nul", l);
+ l = cons<value>("=", l);
+ l = cons<value>("+", l);
+ l = cons<value>("-", l);
+ l = cons<value>("*", l);
+ l = cons<value>("/", l);
+ l = cons<value>("equal?", l);
+ l = cons<value>("display", l);
+ l = cons<value>("uuid", l);
+ return l;
+}
+
+const list<value> primitiveProcedureObjects() {
+ list<value> l = mklist(primitiveProcedure(carProc));
+ l = cons(primitiveProcedure(cdrProc), l);
+ l = cons(primitiveProcedure(consProc), l);
+ l = cons(primitiveProcedure(listProc), l);
+ l = cons(primitiveProcedure(nulProc), l);
+ l = cons(primitiveProcedure(equalProc), l);
+ l = cons(primitiveProcedure(addProc), l);
+ l = cons(primitiveProcedure(subProc), l);
+ l = cons(primitiveProcedure(mulProc), l);
+ l = cons(primitiveProcedure(divProc), l);
+ l = cons(primitiveProcedure(equalProc), l);
+ l = cons(primitiveProcedure(displayProc), l);
+ l = cons(primitiveProcedure(uuidProc), l);
+ return l;
+}
+
+const bool isFalse(const value& exp) {
+ return (bool)exp == false;
+}
+
+const bool isTrue(const value& exp) {
+ return (bool)exp == true;
+}
+
+const bool isQuoted(const value& exp) {
+ return isTaggedList(exp, quoteSymbol);
+}
+
+const value textOfQuotation(const value& exp) {
+ return car(cdr((list<value> )exp));
+}
+
+const value makeLambda(const list<value>& parameters, const list<value>& body) {
+ return cons(lambdaSymbol, cons<value>(parameters, body));
+}
+
+}
+}
+#endif /* tuscany_eval_primitive_hpp */
diff --git a/sca-cpp/trunk/modules/eval/tuscany-sca-1.1-implementation-eval.xsd b/sca-cpp/trunk/modules/eval/tuscany-sca-1.1-implementation-eval.xsd
new file mode 100644
index 0000000000..bbf4935346
--- /dev/null
+++ b/sca-cpp/trunk/modules/eval/tuscany-sca-1.1-implementation-eval.xsd
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * 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.
+-->
+<schema xmlns="http://www.w3.org/2001/XMLSchema"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/1.1"
+ xmlns:sca="http://docs.oasis-open.org/ns/opencsa/sca/200903"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ elementFormDefault="qualified">
+
+ <import namespace="http://docs.oasis-open.org/ns/opencsa/sca/200903" schemaLocation="sca-1.1-cd04.xsd"/>
+
+ <element name="implementation.eval" type="t:EvalImplementation" substitutionGroup="sca:implementation"/>
+
+ <complexType name="EvalImplementation">
+ <complexContent>
+ <extension base="sca:Implementation">
+ <sequence>
+ <any namespace="##targetNamespace" processContents="lax"
+ minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="location" type="anyURI" use="required"/>
+ <anyAttribute namespace="##any" processContents="lax"/>
+ </extension>
+ </complexContent>
+ </complexType>
+
+</schema>
diff --git a/sca-cpp/trunk/modules/http/Makefile.am b/sca-cpp/trunk/modules/http/Makefile.am
new file mode 100644
index 0000000000..ba5eadbab4
--- /dev/null
+++ b/sca-cpp/trunk/modules/http/Makefile.am
@@ -0,0 +1,37 @@
+# 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.
+
+noinst_PROGRAMS = curl-test
+
+libdir=$(prefix)/lib
+lib_LTLIBRARIES = libmod_tuscany_eval.la libmod_tuscany_wiring.la
+
+nobase_include_HEADERS = *.hpp
+
+INCLUDES = -I. -I$(top_builddir)/kernel -I${LIBXML2_INCLUDE} -I${HTTPD_INCLUDE} -I${APR_INCLUDE} -I${LIBMOZJS_INCLUDE} -I${CURL_INCLUDE}
+
+libmod_tuscany_eval_la_SOURCES = mod-eval.cpp
+libmod_tuscany_eval_la_LIBADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1 -L${CURL_LIB} -lcurl -L${LIBMOZJS_LIB} -lmozjs
+
+libmod_tuscany_wiring_la_SOURCES = mod-wiring.cpp
+libmod_tuscany_wiring_la_LIBADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1 -L${CURL_LIB} -lcurl -L${LIBMOZJS_LIB} -lmozjs
+
+curl_test_SOURCES = curl-test.cpp
+curl_test_LDADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1 -L${CURL_LIB} -lcurl -L${LIBMOZJS_LIB} -lmozjs
+
+TESTS = httpd-test http-test wiring-test
+
diff --git a/sca-cpp/trunk/modules/http/conf/mime.types b/sca-cpp/trunk/modules/http/conf/mime.types
new file mode 100644
index 0000000000..4279f51bca
--- /dev/null
+++ b/sca-cpp/trunk/modules/http/conf/mime.types
@@ -0,0 +1,607 @@
+# 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.
+
+# This file controls what Internet media types are sent to the client for
+# given file extension(s). Sending the correct media type to the client
+# is important so they know how to handle the content of the file.
+# Extra types can either be added here or by using an AddType directive
+# in your config files. For more information about Internet media types,
+# please read RFC 2045, 2046, 2047, 2048, and 2077. The Internet media type
+# registry is at <http://www.iana.org/assignments/media-types/>.
+
+# MIME type Extensions
+application/activemessage
+application/andrew-inset ez
+application/applefile
+application/atom+xml atom
+application/atomicmail
+application/batch-smtp
+application/beep+xml
+application/cals-1840
+application/cnrp+xml
+application/commonground
+application/cpl+xml
+application/cybercash
+application/dca-rft
+application/dec-dx
+application/dvcs
+application/edi-consent
+application/edifact
+application/edi-x12
+application/eshop
+application/font-tdpfr
+application/http
+application/hyperstudio
+application/iges
+application/index
+application/index.cmd
+application/index.obj
+application/index.response
+application/index.vnd
+application/iotp
+application/ipp
+application/isup
+application/mac-binhex40 hqx
+application/mac-compactpro cpt
+application/macwriteii
+application/marc
+application/mathematica
+application/mathml+xml mathml
+application/msword doc
+application/news-message-id
+application/news-transmission
+application/ocsp-request
+application/ocsp-response
+application/octet-stream bin dms lha lzh exe class so dll dmg
+application/oda oda
+application/ogg ogg
+application/parityfec
+application/pdf pdf
+application/pgp-encrypted
+application/pgp-keys
+application/pgp-signature
+application/pkcs10
+application/pkcs7-mime
+application/pkcs7-signature
+application/pkix-cert
+application/pkix-crl
+application/pkixcmp
+application/postscript ai eps ps
+application/prs.alvestrand.titrax-sheet
+application/prs.cww
+application/prs.nprend
+application/prs.plucker
+application/qsig
+application/rdf+xml rdf
+application/reginfo+xml
+application/remote-printing
+application/riscos
+application/rtf
+application/sdp
+application/set-payment
+application/set-payment-initiation
+application/set-registration
+application/set-registration-initiation
+application/sgml
+application/sgml-open-catalog
+application/sieve
+application/slate
+application/smil smi smil
+application/srgs gram
+application/srgs+xml grxml
+application/timestamp-query
+application/timestamp-reply
+application/tve-trigger
+application/vemmi
+application/vnd.3gpp.pic-bw-large
+application/vnd.3gpp.pic-bw-small
+application/vnd.3gpp.pic-bw-var
+application/vnd.3gpp.sms
+application/vnd.3m.post-it-notes
+application/vnd.accpac.simply.aso
+application/vnd.accpac.simply.imp
+application/vnd.acucobol
+application/vnd.acucorp
+application/vnd.adobe.xfdf
+application/vnd.aether.imp
+application/vnd.amiga.ami
+application/vnd.anser-web-certificate-issue-initiation
+application/vnd.anser-web-funds-transfer-initiation
+application/vnd.audiograph
+application/vnd.blueice.multipass
+application/vnd.bmi
+application/vnd.businessobjects
+application/vnd.canon-cpdl
+application/vnd.canon-lips
+application/vnd.cinderella
+application/vnd.claymore
+application/vnd.commerce-battelle
+application/vnd.commonspace
+application/vnd.contact.cmsg
+application/vnd.cosmocaller
+application/vnd.criticaltools.wbs+xml
+application/vnd.ctc-posml
+application/vnd.cups-postscript
+application/vnd.cups-raster
+application/vnd.cups-raw
+application/vnd.curl
+application/vnd.cybank
+application/vnd.data-vision.rdz
+application/vnd.dna
+application/vnd.dpgraph
+application/vnd.dreamfactory
+application/vnd.dxr
+application/vnd.ecdis-update
+application/vnd.ecowin.chart
+application/vnd.ecowin.filerequest
+application/vnd.ecowin.fileupdate
+application/vnd.ecowin.series
+application/vnd.ecowin.seriesrequest
+application/vnd.ecowin.seriesupdate
+application/vnd.enliven
+application/vnd.epson.esf
+application/vnd.epson.msf
+application/vnd.epson.quickanime
+application/vnd.epson.salt
+application/vnd.epson.ssf
+application/vnd.ericsson.quickcall
+application/vnd.eudora.data
+application/vnd.fdf
+application/vnd.ffsns
+application/vnd.fints
+application/vnd.flographit
+application/vnd.framemaker
+application/vnd.fsc.weblaunch
+application/vnd.fujitsu.oasys
+application/vnd.fujitsu.oasys2
+application/vnd.fujitsu.oasys3
+application/vnd.fujitsu.oasysgp
+application/vnd.fujitsu.oasysprs
+application/vnd.fujixerox.ddd
+application/vnd.fujixerox.docuworks
+application/vnd.fujixerox.docuworks.binder
+application/vnd.fut-misnet
+application/vnd.grafeq
+application/vnd.groove-account
+application/vnd.groove-help
+application/vnd.groove-identity-message
+application/vnd.groove-injector
+application/vnd.groove-tool-message
+application/vnd.groove-tool-template
+application/vnd.groove-vcard
+application/vnd.hbci
+application/vnd.hhe.lesson-player
+application/vnd.hp-hpgl
+application/vnd.hp-hpid
+application/vnd.hp-hps
+application/vnd.hp-pcl
+application/vnd.hp-pclxl
+application/vnd.httphone
+application/vnd.hzn-3d-crossword
+application/vnd.ibm.afplinedata
+application/vnd.ibm.electronic-media
+application/vnd.ibm.minipay
+application/vnd.ibm.modcap
+application/vnd.ibm.rights-management
+application/vnd.ibm.secure-container
+application/vnd.informix-visionary
+application/vnd.intercon.formnet
+application/vnd.intertrust.digibox
+application/vnd.intertrust.nncp
+application/vnd.intu.qbo
+application/vnd.intu.qfx
+application/vnd.irepository.package+xml
+application/vnd.is-xpr
+application/vnd.japannet-directory-service
+application/vnd.japannet-jpnstore-wakeup
+application/vnd.japannet-payment-wakeup
+application/vnd.japannet-registration
+application/vnd.japannet-registration-wakeup
+application/vnd.japannet-setstore-wakeup
+application/vnd.japannet-verification
+application/vnd.japannet-verification-wakeup
+application/vnd.jisp
+application/vnd.kde.karbon
+application/vnd.kde.kchart
+application/vnd.kde.kformula
+application/vnd.kde.kivio
+application/vnd.kde.kontour
+application/vnd.kde.kpresenter
+application/vnd.kde.kspread
+application/vnd.kde.kword
+application/vnd.kenameaapp
+application/vnd.koan
+application/vnd.liberty-request+xml
+application/vnd.llamagraphics.life-balance.desktop
+application/vnd.llamagraphics.life-balance.exchange+xml
+application/vnd.lotus-1-2-3
+application/vnd.lotus-approach
+application/vnd.lotus-freelance
+application/vnd.lotus-notes
+application/vnd.lotus-organizer
+application/vnd.lotus-screencam
+application/vnd.lotus-wordpro
+application/vnd.mcd
+application/vnd.mediastation.cdkey
+application/vnd.meridian-slingshot
+application/vnd.micrografx.flo
+application/vnd.micrografx.igx
+application/vnd.mif mif
+application/vnd.minisoft-hp3000-save
+application/vnd.mitsubishi.misty-guard.trustweb
+application/vnd.mobius.daf
+application/vnd.mobius.dis
+application/vnd.mobius.mbk
+application/vnd.mobius.mqy
+application/vnd.mobius.msl
+application/vnd.mobius.plc
+application/vnd.mobius.txf
+application/vnd.mophun.application
+application/vnd.mophun.certificate
+application/vnd.motorola.flexsuite
+application/vnd.motorola.flexsuite.adsi
+application/vnd.motorola.flexsuite.fis
+application/vnd.motorola.flexsuite.gotap
+application/vnd.motorola.flexsuite.kmr
+application/vnd.motorola.flexsuite.ttc
+application/vnd.motorola.flexsuite.wem
+application/vnd.mozilla.xul+xml xul
+application/vnd.ms-artgalry
+application/vnd.ms-asf
+application/vnd.ms-excel xls
+application/vnd.ms-lrm
+application/vnd.ms-powerpoint ppt
+application/vnd.ms-project
+application/vnd.ms-tnef
+application/vnd.ms-works
+application/vnd.ms-wpl
+application/vnd.mseq
+application/vnd.msign
+application/vnd.music-niff
+application/vnd.musician
+application/vnd.netfpx
+application/vnd.noblenet-directory
+application/vnd.noblenet-sealer
+application/vnd.noblenet-web
+application/vnd.novadigm.edm
+application/vnd.novadigm.edx
+application/vnd.novadigm.ext
+application/vnd.obn
+application/vnd.osa.netdeploy
+application/vnd.palm
+application/vnd.pg.format
+application/vnd.pg.osasli
+application/vnd.powerbuilder6
+application/vnd.powerbuilder6-s
+application/vnd.powerbuilder7
+application/vnd.powerbuilder7-s
+application/vnd.powerbuilder75
+application/vnd.powerbuilder75-s
+application/vnd.previewsystems.box
+application/vnd.publishare-delta-tree
+application/vnd.pvi.ptid1
+application/vnd.pwg-multiplexed
+application/vnd.pwg-xhtml-print+xml
+application/vnd.quark.quarkxpress
+application/vnd.rapid
+application/vnd.s3sms
+application/vnd.sealed.net
+application/vnd.seemail
+application/vnd.shana.informed.formdata
+application/vnd.shana.informed.formtemplate
+application/vnd.shana.informed.interchange
+application/vnd.shana.informed.package
+application/vnd.smaf
+application/vnd.sss-cod
+application/vnd.sss-dtf
+application/vnd.sss-ntf
+application/vnd.street-stream
+application/vnd.svd
+application/vnd.swiftview-ics
+application/vnd.triscape.mxs
+application/vnd.trueapp
+application/vnd.truedoc
+application/vnd.ufdl
+application/vnd.uplanet.alert
+application/vnd.uplanet.alert-wbxml
+application/vnd.uplanet.bearer-choice
+application/vnd.uplanet.bearer-choice-wbxml
+application/vnd.uplanet.cacheop
+application/vnd.uplanet.cacheop-wbxml
+application/vnd.uplanet.channel
+application/vnd.uplanet.channel-wbxml
+application/vnd.uplanet.list
+application/vnd.uplanet.list-wbxml
+application/vnd.uplanet.listcmd
+application/vnd.uplanet.listcmd-wbxml
+application/vnd.uplanet.signal
+application/vnd.vcx
+application/vnd.vectorworks
+application/vnd.vidsoft.vidconference
+application/vnd.visio
+application/vnd.visionary
+application/vnd.vividence.scriptfile
+application/vnd.vsf
+application/vnd.wap.sic
+application/vnd.wap.slc
+application/vnd.wap.wbxml wbxml
+application/vnd.wap.wmlc wmlc
+application/vnd.wap.wmlscriptc wmlsc
+application/vnd.webturbo
+application/vnd.wrq-hp3000-labelled
+application/vnd.wt.stf
+application/vnd.wv.csp+wbxml
+application/vnd.xara
+application/vnd.xfdl
+application/vnd.yamaha.hv-dic
+application/vnd.yamaha.hv-script
+application/vnd.yamaha.hv-voice
+application/vnd.yellowriver-custom-menu
+application/voicexml+xml vxml
+application/watcherinfo+xml
+application/whoispp-query
+application/whoispp-response
+application/wita
+application/wordperfect5.1
+application/x-bcpio bcpio
+application/x-cdlink vcd
+application/x-chess-pgn pgn
+application/x-compress
+application/x-cpio cpio
+application/x-csh csh
+application/x-director dcr dir dxr
+application/x-dvi dvi
+application/x-futuresplash spl
+application/x-gtar gtar
+application/x-gzip
+application/x-hdf hdf
+application/x-javascript js
+application/x-koan skp skd skt skm
+application/x-latex latex
+application/x-netcdf nc cdf
+application/x-sh sh
+application/x-shar shar
+application/x-shockwave-flash swf
+application/x-stuffit sit
+application/x-sv4cpio sv4cpio
+application/x-sv4crc sv4crc
+application/x-tar tar
+application/x-tcl tcl
+application/x-tex tex
+application/x-texinfo texinfo texi
+application/x-troff t tr roff
+application/x-troff-man man
+application/x-troff-me me
+application/x-troff-ms ms
+application/x-ustar ustar
+application/x-wais-source src
+application/x400-bp
+application/xhtml+xml xhtml xht
+application/xslt+xml xslt
+application/xml xml xsl
+application/xml-dtd dtd
+application/xml-external-parsed-entity
+application/zip zip
+audio/32kadpcm
+audio/amr
+audio/amr-wb
+audio/basic au snd
+audio/cn
+audio/dat12
+audio/dsr-es201108
+audio/dvi4
+audio/evrc
+audio/evrc0
+audio/g722
+audio/g.722.1
+audio/g723
+audio/g726-16
+audio/g726-24
+audio/g726-32
+audio/g726-40
+audio/g728
+audio/g729
+audio/g729D
+audio/g729E
+audio/gsm
+audio/gsm-efr
+audio/l8
+audio/l16
+audio/l20
+audio/l24
+audio/lpc
+audio/midi mid midi kar
+audio/mpa
+audio/mpa-robust
+audio/mp4a-latm
+audio/mpeg mpga mp2 mp3
+audio/parityfec
+audio/pcma
+audio/pcmu
+audio/prs.sid
+audio/qcelp
+audio/red
+audio/smv
+audio/smv0
+audio/telephone-event
+audio/tone
+audio/vdvi
+audio/vnd.3gpp.iufp
+audio/vnd.cisco.nse
+audio/vnd.cns.anp1
+audio/vnd.cns.inf1
+audio/vnd.digital-winds
+audio/vnd.everad.plj
+audio/vnd.lucent.voice
+audio/vnd.nortel.vbk
+audio/vnd.nuera.ecelp4800
+audio/vnd.nuera.ecelp7470
+audio/vnd.nuera.ecelp9600
+audio/vnd.octel.sbc
+audio/vnd.qcelp
+audio/vnd.rhetorex.32kadpcm
+audio/vnd.vmx.cvsd
+audio/x-aiff aif aiff aifc
+audio/x-alaw-basic
+audio/x-mpegurl m3u
+audio/x-pn-realaudio ram ra
+audio/x-pn-realaudio-plugin
+application/vnd.rn-realmedia rm
+audio/x-wav wav
+chemical/x-pdb pdb
+chemical/x-xyz xyz
+image/bmp bmp
+image/cgm cgm
+image/g3fax
+image/gif gif
+image/ief ief
+image/jpeg jpeg jpg jpe
+image/naplps
+image/png png
+image/prs.btif
+image/prs.pti
+image/svg+xml svg
+image/t38
+image/tiff tiff tif
+image/tiff-fx
+image/vnd.cns.inf2
+image/vnd.djvu djvu djv
+image/vnd.dwg
+image/vnd.dxf
+image/vnd.fastbidsheet
+image/vnd.fpx
+image/vnd.fst
+image/vnd.fujixerox.edmics-mmr
+image/vnd.fujixerox.edmics-rlc
+image/vnd.globalgraphics.pgb
+image/vnd.mix
+image/vnd.ms-modi
+image/vnd.net-fpx
+image/vnd.svf
+image/vnd.wap.wbmp wbmp
+image/vnd.xiff
+image/x-cmu-raster ras
+image/x-icon ico
+image/x-portable-anymap pnm
+image/x-portable-bitmap pbm
+image/x-portable-graymap pgm
+image/x-portable-pixmap ppm
+image/x-rgb rgb
+image/x-xbitmap xbm
+image/x-xpixmap xpm
+image/x-xwindowdump xwd
+message/delivery-status
+message/disposition-notification
+message/external-body
+message/http
+message/news
+message/partial
+message/rfc822
+message/s-http
+message/sip
+message/sipfrag
+model/iges igs iges
+model/mesh msh mesh silo
+model/vnd.dwf
+model/vnd.flatland.3dml
+model/vnd.gdl
+model/vnd.gs-gdl
+model/vnd.gtw
+model/vnd.mts
+model/vnd.parasolid.transmit.binary
+model/vnd.parasolid.transmit.text
+model/vnd.vtu
+model/vrml wrl vrml
+multipart/alternative
+multipart/appledouble
+multipart/byteranges
+multipart/digest
+multipart/encrypted
+multipart/form-data
+multipart/header-set
+multipart/mixed
+multipart/parallel
+multipart/related
+multipart/report
+multipart/signed
+multipart/voice-message
+text/calendar ics ifb
+text/css css
+text/directory
+text/enriched
+text/html html htm
+text/parityfec
+text/plain asc txt
+text/prs.lines.tag
+text/rfc822-headers
+text/richtext rtx
+text/rtf rtf
+text/sgml sgml sgm
+text/t140
+text/tab-separated-values tsv
+text/uri-list
+text/vnd.abc
+text/vnd.curl
+text/vnd.dmclientscript
+text/vnd.fly
+text/vnd.fmi.flexstor
+text/vnd.in3d.3dml
+text/vnd.in3d.spot
+text/vnd.iptc.nitf
+text/vnd.iptc.newsml
+text/vnd.latex-z
+text/vnd.motorola.reflex
+text/vnd.ms-mediapackage
+text/vnd.net2phone.commcenter.command
+text/vnd.sun.j2me.app-descriptor
+text/vnd.wap.si
+text/vnd.wap.sl
+text/vnd.wap.wml wml
+text/vnd.wap.wmlscript wmls
+text/x-setext etx
+text/xml
+text/xml-external-parsed-entity
+video/bmpeg
+video/bt656
+video/celb
+video/dv
+video/h261
+video/h263
+video/h263-1998
+video/h263-2000
+video/jpeg
+video/mp1s
+video/mp2p
+video/mp2t
+video/mp4v-es
+video/mpv
+video/mpeg mpeg mpg mpe
+video/nv
+video/parityfec
+video/pointer
+video/quicktime qt mov
+video/smpte292m
+video/vnd.fvt
+video/vnd.motorola.video
+video/vnd.motorola.videop
+video/vnd.mpegurl mxu m4u
+video/vnd.nokia.interleaved-multimedia
+video/vnd.objectvideo
+video/vnd.vivo
+video/x-msvideo avi
+video/x-sgi-movie movie
+x-conference/x-cooltalk ice
diff --git a/sca-cpp/trunk/modules/http/curl-test.cpp b/sca-cpp/trunk/modules/http/curl-test.cpp
new file mode 100644
index 0000000000..863aa98828
--- /dev/null
+++ b/sca-cpp/trunk/modules/http/curl-test.cpp
@@ -0,0 +1,253 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test HTTP client functions.
+ */
+
+#include <assert.h>
+#include <sys/time.h>
+#include <time.h>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include "slist.hpp"
+#include "curl.hpp"
+
+namespace tuscany {
+namespace http {
+
+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);
+ assert(val == std::string("Hello"));
+ return true;
+}
+
+const bool testEvalLoop(const int count, CURLHandle& ch) {
+ if (count == 0)
+ return true;
+ const value val = evalExpr(mklist<value>(std::string("echo"), std::string("Hello")), "http://localhost:8091/test", ch);
+ assert(val == std::string("Hello"));
+ return testEvalLoop(count - 1, ch);
+}
+
+const value blob(std::string(3000, 'A'));
+const list<value> blobs = mklist(blob, blob, blob, blob, blob);
+
+const bool testBlobEvalLoop(const int count, CURLHandle& ch) {
+ if (count == 0)
+ return true;
+ const value val = evalExpr(mklist<value>(std::string("echo"), blobs), "http://localhost:8091/test", ch);
+ assert(val == blobs);
+ return testBlobEvalLoop(count - 1, ch);
+}
+
+const bool testEvalPerf() {
+ const int count = 50;
+ CURLHandle ch;
+ struct timeval start;
+ struct timeval end;
+ {
+ testEvalLoop(5, ch);
+
+ gettimeofday(&start, NULL);
+
+ testEvalLoop(count, ch);
+
+ gettimeofday(&end, NULL);
+ std::cout << "JSON-RPC eval echo test " << duration(start, end, count) << " ms" << std::endl;
+ }
+ {
+ testBlobEvalLoop(5, ch);
+
+ gettimeofday(&start, NULL);
+
+ testBlobEvalLoop(count, ch);
+
+ gettimeofday(&end, NULL);
+ std::cout << "JSON-RPC eval blob test " << duration(start, end, count) << " ms" << std::endl;
+ }
+ return true;
+}
+
+const bool testFeed() {
+ return true;
+}
+
+bool testPost() {
+ const list<value> i = list<value>()
+ << (list<value>() << "name" << std::string("Apple"))
+ << (list<value>() << "price" << std::string("$2.99"));
+ const list<value> a = mklist<value>(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i);
+ CURLHandle ch;
+ value rc = post(a, "http://localhost:8091/test", ch);
+ assert(rc == value(true));
+ return true;
+}
+
+const bool testPostLoop(const int count, const value& val, CURLHandle& ch) {
+ if (count == 0)
+ return true;
+ const value rc = post(val, "http://localhost:8091/test", ch);
+ assert(rc == value(true));
+ return testPostLoop(count - 1, val, ch);
+}
+
+const bool testPostPerf() {
+ const int count = 50;
+ CURLHandle ch;
+ struct timeval start;
+ struct timeval end;
+ {
+ const list<value> i = list<value>()
+ << (list<value>() << "name" << std::string("Apple"))
+ << (list<value>() << "price" << std::string("$2.99"));
+ const list<value> val = mklist<value>(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i);
+ testPostLoop(5, val, ch);
+
+ gettimeofday(&start, NULL);
+
+ testPostLoop(count, val, ch);
+
+ gettimeofday(&end, NULL);
+ std::cout << "ATOMPub POST small test " << duration(start, end, count) << " ms" << std::endl;
+ }
+ {
+ const list<value> i = list<value>()
+ << (list<value>() << "name" << std::string("Apple"))
+ << (list<value>() << "blob1" << blob)
+ << (list<value>() << "blob2" << blob)
+ << (list<value>() << "blob3" << blob)
+ << (list<value>() << "blob4" << blob)
+ << (list<value>() << "blob5" << blob)
+ << (list<value>() << "price" << std::string("$2.99"));
+ const list<value> val = mklist<value>(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i);
+ testPostLoop(5, val, ch);
+
+ gettimeofday(&start, NULL);
+
+ testPostLoop(count, val, ch);
+
+ gettimeofday(&end, NULL);
+ std::cout << "ATOMPub POST blob test " << duration(start, end, count) << " ms" << std::endl;
+ }
+ return true;
+}
+
+const bool testPut() {
+ const list<value> i = list<value>()
+ << (list<value>() << "name" << std::string("Apple"))
+ << (list<value>() << "price" << std::string("$2.99"));
+ const list<value> a = mklist<value>(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i);
+ CURLHandle ch;
+ value rc = put(a, "http://localhost:8091/test/111", ch);
+ assert(rc == value(true));
+ return true;
+}
+
+const bool testDel() {
+ CURLHandle ch;
+ value rc = del("http://localhost:8091/test/123456789", ch);
+ assert(rc == value(true));
+ return true;
+}
+
+}
+}
+
+int main() {
+ std::cout << "Testing..." << std::endl;
+
+ tuscany::http::testGet();
+ tuscany::http::testGetPerf();
+ tuscany::http::testPost();
+ tuscany::http::testPostPerf();
+ tuscany::http::testEval();
+ tuscany::http::testEvalPerf();
+ tuscany::http::testFeed();
+ tuscany::http::testPut();
+ tuscany::http::testDel();
+
+ std::cout << "OK" << std::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/trunk/modules/http/curl.hpp b/sca-cpp/trunk/modules/http/curl.hpp
new file mode 100644
index 0000000000..5ee3a090b0
--- /dev/null
+++ b/sca-cpp/trunk/modules/http/curl.hpp
@@ -0,0 +1,348 @@
+/*
+ * 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_curl_hpp
+#define tuscany_curl_hpp
+
+/**
+ * CURL HTTP client functions.
+ */
+
+#include <curl/curl.h>
+#include <curl/types.h>
+#include <curl/easy.h>
+#include <string>
+#include "list.hpp"
+#include "value.hpp"
+#include "element.hpp"
+#include "monad.hpp"
+#include "../atom/atom.hpp"
+#include "../json/json.hpp"
+
+namespace tuscany {
+namespace http {
+
+/**
+ * Set to true to log HTTP content.
+ */
+bool logContent = false;
+
+/**
+ * CURL library context, one per process.
+ */
+class CURLContext {
+public:
+ CURLContext() {
+ curl_global_init(CURL_GLOBAL_ALL);
+ }
+ ~CURLContext() {
+ curl_global_cleanup();
+ }
+};
+
+CURLContext curlContext;
+
+/**
+ * Represents a CURL session handle.
+ */
+class CURLHandle {
+public:
+ CURLHandle() : h(curl_easy_init()) {
+ }
+ ~CURLHandle() {
+ curl_easy_cleanup(h);
+ }
+
+ operator CURL*() const {
+ return h;
+ }
+private:
+ CURL* h;
+};
+
+/**
+ * Context passed to the read callback function.
+ */
+class CURLReadContext {
+public:
+ CURLReadContext(const list<std::string>& ilist) : ilist(ilist) {
+ }
+ list<std::string> ilist;
+};
+
+/**
+ * Called by CURL to read data to send.
+ */
+size_t readCallback(void *ptr, size_t size, size_t nmemb, void *data) {
+ CURLReadContext& rcx = *static_cast<CURLReadContext*>(data);
+ if (isNil(rcx.ilist))
+ return 0;
+ rcx.ilist = fragment(rcx.ilist, size * nmemb);
+ const std::string s = car(rcx.ilist);
+ rcx.ilist = cdr(rcx.ilist);
+ s.copy((char*)ptr, s.length());
+ return s.length();
+}
+
+/**
+ * Context passed to CURL write callback function.
+ */
+template<typename R> class CURLWriteContext {
+public:
+ CURLWriteContext(const lambda<R(std::string, R)>& reduce, const R& accum) : reduce(reduce), accum(accum) {
+ }
+ const lambda<R(std::string, R)> reduce;
+ R accum;
+};
+
+/**
+ * Called by CURL to write received data.
+ */
+template<typename R> size_t writeCallback(void *ptr, size_t size, size_t nmemb, void *data) {
+ CURLWriteContext<R>& wcx = *(static_cast<CURLWriteContext<R>*> (data));
+ const size_t realsize = size * nmemb;
+ wcx.accum = wcx.reduce(std::string((const char*)ptr, realsize), wcx.accum);
+ return realsize;
+}
+
+/**
+ * Called by CURL to write received header data.
+ */
+template<typename R> size_t headerCallback(void *ptr, size_t size, size_t nmemb, void *data) {
+ CURLWriteContext<R>& wcx = *(static_cast<CURLWriteContext<R>*> (data));
+ const size_t realsize = size * nmemb;
+ wcx.accum = wcx.reduce(std::string((const char*)ptr, realsize), wcx.accum);
+ return realsize;
+}
+
+/**
+ * Apply an HTTP verb to a list containing a list of headers and a list of content, and
+ * a reduce function used to process the response.
+ */
+curl_slist* headers(curl_slist* cl, const list<std::string>& h) {
+ if (isNil(h))
+ return cl;
+ return headers(curl_slist_append(cl, std::string(car(h)).c_str()), cdr(h));
+}
+
+template<typename R> const failable<list<R>, std::string> apply(const list<list<std::string> >& req, const lambda<R(std::string, R)>& reduce, const R& initial, const std::string& url, const std::string& verb, const CURLHandle& ch) {
+
+ // Init the curl session
+ curl_easy_reset(ch);
+ curl_easy_setopt(ch, CURLOPT_USERAGENT, "libcurl/1.0");
+
+ //TODO use HTTP chunking, for now just convert request to a single string
+ std::ostringstream os;
+ write(cadr(req), os);
+ const std::string s = os.str();
+ const int sz = s.length();
+ if (sz < 1400)
+ curl_easy_setopt(ch, CURLOPT_TCP_NODELAY, true);
+
+ // Setup the read, header and write callbacks
+ CURLReadContext rcx(mklist(s));
+ curl_easy_setopt(ch, CURLOPT_READFUNCTION, (size_t (*)(void*, size_t, size_t, void*))readCallback);
+ curl_easy_setopt(ch, CURLOPT_READDATA, &rcx);
+ CURLWriteContext<R> hcx(reduce, initial);
+ curl_easy_setopt(ch, CURLOPT_HEADERFUNCTION, (size_t (*)(void*, size_t, size_t, void*))headerCallback<R>);
+ curl_easy_setopt(ch, CURLOPT_HEADERDATA, &hcx);
+ CURLWriteContext<R> wcx(reduce, initial);
+ curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, (size_t (*)(void*, size_t, size_t, void*))writeCallback<R>);
+ curl_easy_setopt(ch, CURLOPT_WRITEDATA, &wcx);
+
+ // Set the request headers
+ curl_slist* hl = headers(NULL, car(req));
+ if (hl != NULL)
+ curl_easy_setopt(ch, CURLOPT_HTTPHEADER, hl);
+
+ // Apply the HTTP verb
+ curl_easy_setopt(ch, CURLOPT_URL, url.c_str());
+ if (verb == "POST") {
+ curl_easy_setopt(ch, CURLOPT_POST, true);
+ curl_easy_setopt(ch, CURLOPT_POSTFIELDSIZE, sz);
+ } else if (verb == "PUT") {
+ curl_easy_setopt(ch, CURLOPT_UPLOAD, true);
+ curl_easy_setopt(ch, CURLOPT_INFILESIZE, sz);
+ } else if (verb == "DELETE")
+ curl_easy_setopt(ch, CURLOPT_CUSTOMREQUEST, "DELETE");
+ const CURLcode rc = curl_easy_perform(ch);
+
+ if (hl != NULL)
+ curl_slist_free_all(hl);
+
+ // Return the HTTP return code or content
+ if (rc)
+ return mkfailure<list<R>, std::string>(curl_easy_strerror(rc));
+ long httprc;
+ curl_easy_getinfo (ch, CURLINFO_RESPONSE_CODE, &httprc);
+ if (httprc != 200 && httprc != 201) {
+ std::ostringstream es;
+ es << "HTTP code " << httprc;
+ return mkfailure<list<R>, std::string>(es.str());
+ }
+ return mklist<R>(hcx.accum, wcx.accum);
+}
+
+/**
+ * Evaluate an expression remotely, at the given URL.
+ */
+const failable<value, std::string> evalExpr(const value& expr, const std::string& url, const CURLHandle& ch) {
+
+ // Convert expression to a JSON-RPC request
+ json::JSONContext cx;
+ const failable<list<std::string>, std::string> jsreq = jsonRequest(1, car<value>(expr), cdr<value>(expr), cx);
+ if (!hasValue(jsreq))
+ return mkfailure<value, std::string>(reason(jsreq));
+
+ if (logContent) {
+ std::cout<< "content: " << std::endl;
+ write(jsreq, std::cout);
+ std::cout<< std::endl;
+ std::cout.flush();
+ }
+
+ // POST it to the URL
+ const list<std::string> h = mklist<std::string>("Content-Type: application/json-rpc");
+ const failable<list<list<std::string> >, std::string> res = apply<list<std::string> >(mklist<list<std::string> >(h, jsreq), rcons<std::string>, list<std::string>(), url, "POST", ch);
+ if (!hasValue(res))
+ return mkfailure<value, std::string>(reason(res));
+
+ // Return result
+ if (logContent) {
+ std::cout << "content:" << std::endl;
+ write(cadr<list<std::string> >(res), std::cout);
+ std::cout << std::endl;
+ }
+ const list<value> val = elementsToValues(json::readJSON(cadr<list<std::string> >(res), cx));
+ return cadr<value>(cadr<value>(val));
+}
+
+/**
+ * HTTP GET, return the resource at the given URL.
+ */
+template<typename R> const failable<list<R>, std::string> get(const lambda<R(std::string, R)>& reduce, const R& initial, const std::string& url, const CURLHandle& ch) {
+ const list<list<std::string> > req = mklist(list<std::string>(), list<std::string>());
+ return apply(req, reduce, initial, url, "GET", ch);
+}
+
+/**
+ * HTTP GET, return a list of values representing the resource at the given URL.
+ */
+const failable<value, std::string> get(const std::string& url, const CURLHandle& ch) {
+
+ // Get the contents of the resource at the given URL
+ const failable<list<list<std::string> >, std::string> res = get<list<std::string> >(rcons<std::string>, list<std::string>(), url, ch);
+ if (!hasValue(res))
+ return mkfailure<value, std::string>(reason(res));
+ const list<list<std::string> > ls = res;
+
+ const std::string ct;
+ if (ct.find("application/atom+xml") != std::string::npos) {
+ // TODO Return an ATOM feed
+ }
+
+ // Return the content as a string value
+ std::ostringstream os;
+ write(reverse(cadr(ls)), os);
+ return value(os.str());
+}
+
+/**
+ * HTTP POST.
+ */
+const failable<value, std::string> post(const value& val, const std::string& url, const CURLHandle& ch) {
+
+ // Convert value to an ATOM entry
+ const failable<list<std::string>, std::string> entry = atom::writeATOMEntry(atom::entryValuesToElements(val));
+ if (!hasValue(entry))
+ return mkfailure<value, std::string>(reason(entry));
+ if (logContent) {
+ std::cout << "content:" << std::endl;
+ write(list<std::string>(entry), std::cout);
+ std::cout << std::endl;
+ }
+
+ // POST it to the URL
+ const list<std::string> h = mklist<std::string>("Content-Type: application/atom+xml");
+ const list<list<std::string> > req = mklist<list<std::string> >(h, entry);
+ const failable<list<list<std::string> >, std::string> res = apply<list<std::string> >(req, rcons<std::string>, list<std::string>(), url, "POST", ch);
+ if (!hasValue(res))
+ return mkfailure<value, std::string>(reason(res));
+ return value(true);
+}
+
+/**
+ * HTTP PUT.
+ */
+const failable<value, std::string> put(const value& val, const std::string& url, const CURLHandle& ch) {
+
+ // Convert value to an ATOM entry
+ const failable<list<std::string>, std::string> entry = atom::writeATOMEntry(atom::entryValuesToElements(val));
+ if (!hasValue(entry))
+ return mkfailure<value, std::string>(reason(entry));
+ if (logContent) {
+ std::cout << "content:" << std::endl;
+ write(list<std::string>(entry), std::cout);
+ std::cout << std::endl;
+ }
+
+ // PUT it to the URL
+ const list<std::string> h = mklist<std::string>("Content-Type: application/atom+xml");
+ const list<list<std::string> > req = mklist<list<std::string> >(h, entry);
+ const failable<list<list<std::string> >, std::string> res = apply<list<std::string> >(req, rcons<std::string>, list<std::string>(), url, "PUT", ch);
+ if (!hasValue(res))
+ return mkfailure<value, std::string>(reason(res));
+ return value(true);
+}
+
+/**
+ * HTTP DELETE.
+ */
+const failable<value, std::string> del(const std::string& url, const CURLHandle& ch) {
+ const list<list<std::string> > req = mklist(list<std::string>(), list<std::string>());
+ const failable<list<list<std::string> >, std::string> res = apply<list<std::string> >(req, rcons<std::string>, list<std::string>(), url, "DELETE", ch);
+ if (!hasValue(res))
+ return mkfailure<value, std::string>(reason(res));
+ return value(true);
+}
+
+/**
+ * HTTP client proxy function.
+ */
+struct proxy {
+ proxy(const std::string& url, const CURLHandle& ch) : url(url), ch(ch) {
+ }
+
+ const value operator()(const list<value>& args) const {
+ failable<value, std::string> val = evalExpr(args, url, ch);
+ if (!hasValue(val))
+ return value();
+ return val;
+ }
+
+ const std::string url;
+ const CURLHandle& ch;
+};
+
+}
+}
+
+#endif /* tuscany_curl_hpp */
diff --git a/sca-cpp/trunk/modules/http/htdocs/entry.xml b/sca-cpp/trunk/modules/http/htdocs/entry.xml
new file mode 100644
index 0000000000..86b8a10547
--- /dev/null
+++ b/sca-cpp/trunk/modules/http/htdocs/entry.xml
@@ -0,0 +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><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>2.99</price></item></content><link href="111"/></entry>
diff --git a/sca-cpp/trunk/modules/http/htdocs/feed.xml b/sca-cpp/trunk/modules/http/htdocs/feed.xml
new file mode 100644
index 0000000000..5e37de6580
--- /dev/null
+++ b/sca-cpp/trunk/modules/http/htdocs/feed.xml
@@ -0,0 +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>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/sca-cpp/trunk/modules/http/htdocs/index.html b/sca-cpp/trunk/modules/http/htdocs/index.html
new file mode 100644
index 0000000000..1bfb3e30c2
--- /dev/null
+++ b/sca-cpp/trunk/modules/http/htdocs/index.html
@@ -0,0 +1,21 @@
+<!--
+ 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.
+-->
+
+<html><body><h1>It works!</h1></body></html>
+
diff --git a/sca-cpp/trunk/modules/http/htdocs/json-request.txt b/sca-cpp/trunk/modules/http/htdocs/json-request.txt
new file mode 100644
index 0000000000..b4bd07fc46
--- /dev/null
+++ b/sca-cpp/trunk/modules/http/htdocs/json-request.txt
@@ -0,0 +1 @@
+{"id":1,"method":"echo","params":["Hello"]}
diff --git a/sca-cpp/trunk/modules/http/htdocs/json-result.txt b/sca-cpp/trunk/modules/http/htdocs/json-result.txt
new file mode 100644
index 0000000000..121bf74902
--- /dev/null
+++ b/sca-cpp/trunk/modules/http/htdocs/json-result.txt
@@ -0,0 +1 @@
+{"id":1,"result":"Hello"} \ No newline at end of file
diff --git a/sca-cpp/trunk/modules/http/http-test b/sca-cpp/trunk/modules/http/http-test
new file mode 100755
index 0000000000..d70db8d469
--- /dev/null
+++ b/sca-cpp/trunk/modules/http/http-test
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Setup
+./httpd-conf tmp 8091 htdocs
+cat >>tmp/conf/httpd.conf <<EOF
+
+<Location /test>
+SetHandler mod_tuscany_eval
+SCAContribution `pwd`/
+SCAComposite httpd-test.composite
+SCAComponent httpd-test
+</Location>
+EOF
+
+apachectl -k start -d `pwd`/tmp
+sleep 1
+
+# Test
+./curl-test
+rc=$?
+
+# Cleanup
+apachectl -k stop -d `pwd`/tmp
+sleep 2
+return $rc
diff --git a/sca-cpp/trunk/modules/http/httpd-client.scm b/sca-cpp/trunk/modules/http/httpd-client.scm
new file mode 100644
index 0000000000..12275693f4
--- /dev/null
+++ b/sca-cpp/trunk/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/sca-cpp/trunk/modules/http/httpd-conf b/sca-cpp/trunk/modules/http/httpd-conf
new file mode 100755
index 0000000000..10a5b47ac2
--- /dev/null
+++ b/sca-cpp/trunk/modules/http/httpd-conf
@@ -0,0 +1,37 @@
+#!/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.
+
+# Generate a minimal httpd.conf
+here=`readlink -f $0`; here=`dirname $here`
+root=`readlink -f $1`
+port=$2
+htdocs=`readlink -f $3`
+
+mkdir -p $root
+mkdir -p $root/logs
+mkdir -p $root/conf
+cat >$root/conf/httpd.conf <<EOF
+ServerName 127.0.0.1
+Listen $port
+DocumentRoot $htdocs
+TypesConfig $here/conf/mime.types
+LoadModule mod_tuscany_eval $here/.libs/libmod_tuscany_eval.so
+LoadModule mod_tuscany_wiring $here/.libs/libmod_tuscany_wiring.so
+EOF
+
diff --git a/sca-cpp/trunk/modules/http/httpd-test b/sca-cpp/trunk/modules/http/httpd-test
new file mode 100755
index 0000000000..1d9b3cb34d
--- /dev/null
+++ b/sca-cpp/trunk/modules/http/httpd-test
@@ -0,0 +1,79 @@
+#!/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 8090 htdocs
+cat >>tmp/conf/httpd.conf <<EOF
+
+<Location /test>
+SetHandler mod_tuscany_eval
+SCAContribution `pwd`/
+SCAComposite httpd-test.composite
+SCAComponent httpd-test
+</Location>
+EOF
+
+apachectl -k start -d `pwd`/tmp
+sleep 1
+
+# Test HTTP GET
+curl http://localhost:8090/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:8090/test/ >tmp/feed.xml 2>/dev/null
+ diff tmp/feed.xml htdocs/feed.xml
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/test/111 >tmp/entry.xml 2>/dev/null
+ diff tmp/entry.xml htdocs/entry.xml
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/test/ -X POST -H "Content-type: application/atom+xml" --data @htdocs/entry.xml 2>/dev/null
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/test/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:8090/test/111 -X DELETE 2>/dev/null
+ rc=$?
+fi
+
+# Test JSON-RPC
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/test/ -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/sca-cpp/trunk/modules/http/httpd-test.composite b/sca-cpp/trunk/modules/http/httpd-test.composite
new file mode 100644
index 0000000000..875d26ae1b
--- /dev/null
+++ b/sca-cpp/trunk/modules/http/httpd-test.composite
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * 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.
+-->
+<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200903"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://test"
+ name="httpd-test">
+
+ <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/sca-cpp/trunk/modules/http/httpd-test.scm b/sca-cpp/trunk/modules/http/httpd-test.scm
new file mode 100644
index 0000000000..0566eaf36f
--- /dev/null
+++ b/sca-cpp/trunk/modules/http/httpd-test.scm
@@ -0,0 +1,29 @@
+; JSON-RPC test case
+
+(define (echo x) x)
+
+; ATOMPub test case
+
+(define (getall)
+ '("Sample Feed" "123456789"
+ ("Item" "111" ((javaClass "services.Item") (name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99)))
+ ("Item" "222" ((javaClass "services.Item") (name "Orange") (currencyCode "USD") (currencySymbol "$") (price 3.55)))
+ ("Item" "333" ((javaClass "services.Item") (name "Pear") (currencyCode "USD") (currencySymbol "$") (price 1.55))))
+)
+
+(define (get id)
+ (define entry '((javaClass "services.Item") (name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99)))
+ (cons "Item" (list id entry))
+)
+
+(define (post entry)
+ "123456789"
+)
+
+(define (put id entry)
+ true
+)
+
+(define (delete id)
+ true
+)
diff --git a/sca-cpp/trunk/modules/http/httpd.hpp b/sca-cpp/trunk/modules/http/httpd.hpp
new file mode 100644
index 0000000000..1271afc03c
--- /dev/null
+++ b/sca-cpp/trunk/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/sca-cpp/trunk/modules/http/mod-eval.cpp b/sca-cpp/trunk/modules/http/mod-eval.cpp
new file mode 100644
index 0000000000..6fef2be2cb
--- /dev/null
+++ b/sca-cpp/trunk/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/sca-cpp/trunk/modules/http/mod-wiring.cpp b/sca-cpp/trunk/modules/http/mod-wiring.cpp
new file mode 100644
index 0000000000..965d5a87fb
--- /dev/null
+++ b/sca-cpp/trunk/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/sca-cpp/trunk/modules/http/wiring-test b/sca-cpp/trunk/modules/http/wiring-test
new file mode 100755
index 0000000000..0c3f36b513
--- /dev/null
+++ b/sca-cpp/trunk/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/sca-cpp/trunk/modules/json/Makefile.am b/sca-cpp/trunk/modules/json/Makefile.am
new file mode 100644
index 0000000000..ab85d910ad
--- /dev/null
+++ b/sca-cpp/trunk/modules/json/Makefile.am
@@ -0,0 +1,28 @@
+# 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.
+
+noinst_PROGRAMS = json-test
+
+nobase_include_HEADERS = *.hpp
+
+INCLUDES = -I. -I$(top_builddir)/kernel -I${LIBXML2_INCLUDE} -I${APR_INCLUDE} -I${LIBMOZJS_INCLUDE}
+
+json_test_SOURCES = json-test.cpp
+json_test_LDADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1 -L${LIBMOZJS_LIB} -lmozjs
+
+TESTS = json-test
+
diff --git a/sca-cpp/trunk/modules/json/json-test.cpp b/sca-cpp/trunk/modules/json/json-test.cpp
new file mode 100644
index 0000000000..fd506cea65
--- /dev/null
+++ b/sca-cpp/trunk/modules/json/json-test.cpp
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test JSON data conversion functions.
+ */
+
+#include <assert.h>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include "slist.hpp"
+#include "json.hpp"
+
+namespace tuscany {
+namespace json {
+
+bool testJSEval() {
+ JSONContext cx;
+ const std::string script("(function testJSON(n){ return JSON.parse(JSON.stringify(n)) })(5)");
+ jsval rval;
+ assert(JS_EvaluateScript(cx, cx.getGlobal(), script.c_str(), script.length(), "testJSON.js", 1, &rval));
+ const std::string r(JS_GetStringBytes(JS_ValueToString(cx, rval)));
+ assert(r == "5");
+ return true;
+}
+
+std::ostringstream* jsonWriter(const std::string& s, std::ostringstream* os) {
+ (*os) << s;
+ return os;
+}
+
+bool testJSON() {
+ const JSONContext cx;
+
+ {
+ const list<value> ad = mklist<value>(mklist<value>(attribute, "city", std::string("san francisco")), mklist<value>(attribute, "state", std::string("ca")));
+ const list<value> ac = mklist<value>(mklist<value>(element, "id", std::string("1234")), mklist<value>(attribute, "balance", 1000));
+ const list<value> cr = mklist<value>(mklist<value> (attribute, "name", std::string("jdoe")), cons<value>(element, cons<value>("address", ad)), cons<value>(element, cons<value>("account", ac)));
+ const list<value> c = mklist<value>(cons<value>(element, cons<value>("customer", cr)));
+ std::ostringstream os;
+ writeJSON<std::ostringstream*>(jsonWriter, &os, c, cx);
+ assert(os.str() == "{\"customer\":{\"name\":\"jdoe\",\"address\":{\"city\":\"san francisco\",\"state\":\"ca\"},\"account\":{\"id\":\"1234\",\"balance\":1000}}}");
+ }
+ {
+ const list<value> phones = mklist<value> (std::string("408-1234"), std::string("650-1234"));
+ const list<value> l = mklist<value> (mklist<value> (element, "phones", phones), mklist<value> (element, "lastName", std::string("test\ttab")), mklist<value> (element, "firstName", std::string("test1")));
+
+ std::ostringstream os;
+ writeJSON<std::ostringstream*>(jsonWriter, &os, l, cx);
+ assert(os.str() == "{\"phones\":[\"408-1234\",\"650-1234\"],\"lastName\":\"test\\u0009tab\",\"firstName\":\"test1\"}");
+
+ std::istringstream is(os.str());
+ const list<std::string> il = streamList(is);
+ const list<value> r = readJSON(il, cx);
+ assert(r == l);
+
+ std::ostringstream wos;
+ write(writeJSON(r, cx), wos);
+ assert(wos.str() == os.str());
+ }
+ return true;
+}
+
+bool testJSONRPC() {
+ JSONContext cx;
+ {
+ const std::string lm("{\"id\": 1, \"method\": \"system.listMethods\", \"params\": []}");
+ const list<value> e = readJSON(mklist(lm), cx);
+ const list<value> v = elementsToValues(e);
+ assert(assoc<value>("id", v) == mklist<value>("id", 1));
+ assert(assoc<value>("method", v) == mklist<value>("method", std::string("system.listMethods")));
+ assert(assoc<value>("params", v) == mklist<value>("params", list<value>()));
+ }
+ {
+ const std::string i("{\"id\":3,\"result\":[{\"price\":\"$2.99\",\"name\":\"Apple\"},{\"price\":\"$3.55\",\"name\":\"Orange\"},{\"price\":\"$1.55\",\"name\":\"Pear\"}]}");
+ const list<value> e = readJSON(mklist(i), cx);
+ const std::string i2("{\"id\":3,\"result\":{\"0\":{\"price\":\"$2.99\",\"name\":\"Apple\"},\"1\":{\"price\":\"$3.55\",\"name\":\"Orange\"},\"2\":{\"price\":\"$1.55\",\"name\":\"Pear\"}}}");
+ const list<value> e2 = readJSON(mklist(i), cx);
+ assert(e == e2);
+ }
+ {
+ const std::string i("{\"id\":3,\"result\":[{\"price\":\"$2.99\",\"name\":\"Apple\"},{\"price\":\"$3.55\",\"name\":\"Orange\"},{\"price\":\"$1.55\",\"name\":\"Pear\"}]}");
+ const list<value> e = readJSON(mklist(i), cx);
+ std::ostringstream os;
+ write(writeJSON(e, cx), os);
+ assert(os.str() == i);
+ const list<value> v = elementsToValues(e);
+ const list<value> r = valuesToElements(v);
+ assert(r == e);
+ }
+ {
+ const list<value> r = mklist<value>(mklist<value>("id", 1), mklist<value>("result", mklist<value>(std::string("Service.get"), std::string("Service.getTotal"))));
+ const list<value> e = valuesToElements(r);
+ std::ostringstream os;
+ 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;
+}
+
+}
+}
+
+int main() {
+ std::cout << "Testing..." << std::endl;
+
+ tuscany::json::testJSEval();
+ tuscany::json::testJSON();
+ tuscany::json::testJSONRPC();
+
+ std::cout << "OK" << std::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/trunk/modules/json/json.hpp b/sca-cpp/trunk/modules/json/json.hpp
new file mode 100644
index 0000000000..f6c8eb5fe8
--- /dev/null
+++ b/sca-cpp/trunk/modules/json/json.hpp
@@ -0,0 +1,396 @@
+/*
+ * 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_json_hpp
+#define tuscany_json_hpp
+
+/**
+ * JSON data conversion functions.
+ */
+
+#define XP_UNIX
+#include <jsapi.h>
+#include <string>
+#include "list.hpp"
+#include "value.hpp"
+#include "element.hpp"
+#include "monad.hpp"
+
+namespace tuscany {
+namespace json {
+
+/**
+ * Report JSON errors.
+ */
+void reportError(JSContext *cx, const char *message, JSErrorReport *report) {
+ std::cerr << (const char*)(report->filename? report->filename : "<no filename>") << ":"
+ << (unsigned int)report->lineno << ":" << message << std::endl;
+}
+
+/**
+ * Encapsulates a JavaScript runtime. Can be shared by multiple threads in
+ * a process.
+ */
+class JSONRuntime {
+public:
+ JSONRuntime() {
+ // Create JS runtime
+ rt = JS_NewRuntime(8L * 1024L * 1024L);
+ if(rt == NULL)
+ cleanup();
+ }
+
+ ~JSONRuntime() {
+ }
+
+ operator JSRuntime*() const {
+ return rt;
+ }
+private:
+ bool cleanup() {
+ if(rt != NULL) {
+ JS_DestroyRuntime(rt);
+ rt = NULL;
+ }
+ JS_ShutDown();
+ return true;
+ }
+
+ JSRuntime* rt;
+};
+
+/**
+ * Global JavaScript runtime instance.
+ */
+JSONRuntime jsRuntime;
+
+JSClass jsGlobalClass = { "global", JSCLASS_GLOBAL_FLAGS, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS};
+
+
+/**
+ * Represents a JavaScript context. Create one per thread.
+ */
+class JSONContext {
+public:
+ JSONContext() {
+ // Create JS context
+ cx = JS_NewContext(jsRuntime, 8192);
+ if(cx == NULL)
+ return;
+ JS_SetOptions(cx, JSOPTION_VAROBJFIX);
+ JS_SetVersion(cx, JSVERSION_DEFAULT);
+ JS_SetErrorReporter(cx, reportError);
+
+ // Create global JS object
+ global = JS_NewObject(cx, &jsGlobalClass, NULL, NULL);
+ if(global == NULL) {
+ cleanup();
+ return;
+ }
+
+ // Populate global object with the standard globals, like Object and Array
+ if(!JS_InitStandardClasses(cx, global)) {
+ cleanup();
+ return;
+ }
+ }
+
+ ~JSONContext() {
+ cleanup();
+ }
+
+ operator JSContext*() const {
+ return cx;
+ }
+
+ JSObject* getGlobal() const {
+ return global;
+ }
+
+private:
+ bool cleanup() {
+ if(cx != NULL) {
+ JS_DestroyContext(cx);
+ cx = NULL;
+ }
+ return true;
+ }
+
+ JSContext* cx;
+ JSObject* global;
+};
+
+/**
+ * Converts JS properties to values.
+ */
+const list<value> jsPropertiesToValues(const list<value>& propertiesSoFar, JSObject* o, JSObject* i, const JSONContext& cx) {
+
+ const value jsValToValue(const jsval& jsv, const JSONContext& cx);
+
+ jsid id;
+ if(!JS_NextProperty(cx, i, &id) || id == JSVAL_VOID)
+ return propertiesSoFar;
+ jsval jsv;
+ if(!JS_GetPropertyById(cx, o, id, &jsv))
+ return propertiesSoFar;
+ const value val = jsValToValue(jsv, cx);
+ jsval idv;
+ JS_IdToValue(cx, id, &idv);
+ if(JSVAL_IS_STRING(idv)) {
+ const value type = isList(val)? element : element;
+ return jsPropertiesToValues(cons<value> (mklist<value> (type, JS_GetStringBytes(JSVAL_TO_STRING(idv)), val), propertiesSoFar), o, i, cx);
+ }
+ return jsPropertiesToValues(cons(val, propertiesSoFar), o, i, cx);
+}
+
+/**
+ * Converts a JS val to a value.
+ */
+const value jsValToValue(const jsval& jsv, const JSONContext& cx) {
+ switch(JS_TypeOfValue(cx, jsv)) {
+ case JSTYPE_STRING: {
+ return value(std::string(JS_GetStringBytes(JSVAL_TO_STRING(jsv))));
+ }
+ case JSTYPE_BOOLEAN: {
+ return value((bool)JSVAL_TO_BOOLEAN(jsv));
+ }
+ case JSTYPE_NUMBER: {
+ jsdouble jsd;
+ JS_ValueToNumber(cx, jsv, &jsd);
+ return value((double)jsd);
+ }
+ case JSTYPE_OBJECT: {
+ JSObject* o = JSVAL_TO_OBJECT(jsv);
+ JSObject* i = JS_NewPropertyIterator(cx, o);
+ if(i == NULL)
+ return value(list<value> ());
+ const value pv = jsPropertiesToValues(list<value> (), o, i, cx);
+ return pv;
+ }
+ default: {
+ return value();
+ }
+ }
+}
+
+/**
+ * Consumes JSON strings and populates a JS object.
+ */
+failable<bool, std::string> consume(JSONParser* parser, const list<std::string>& ilist, const JSONContext& cx) {
+ if (isNil(ilist))
+ return true;
+ JSString* jstr = JS_NewStringCopyZ(cx, car(ilist).c_str());
+ if(!JS_ConsumeJSONText(cx, parser, JS_GetStringChars(jstr), JS_GetStringLength(jstr)))
+ return mkfailure<bool, std::string>("JS_ConsumeJSONText failed");
+ return consume(parser, cdr(ilist), cx);
+}
+
+/**
+ * Convert a list of strings representing a JSON document to a list of values.
+ */
+const failable<list<value>, std::string> readJSON(const list<std::string>& ilist, const JSONContext& cx) {
+ jsval val;
+ JSONParser* parser = JS_BeginJSONParse(cx, &val);
+ if(parser == NULL)
+ return mkfailure<list<value>, std::string>("JS_BeginJSONParse failed");
+
+ const failable<bool, std::string> consumed = consume(parser, ilist, cx);
+
+ if(!JS_FinishJSONParse(cx, parser, JSVAL_NULL))
+ return mkfailure<list<value>, std::string>("JS_FinishJSONParse failed");
+ if(!hasValue(consumed))
+ return mkfailure<list<value>, std::string>(reason(consumed));
+
+ return list<value>(jsValToValue(val, cx));
+}
+
+/**
+ * Converts a list of values to JS array elements.
+ */
+JSObject* valuesToJSElements(JSObject* a, const list<value>& l, int i, const JSONContext& cx) {
+ const jsval valueToJSVal(const value& val, const JSONContext& cx);
+ if (isNil(l))
+ return a;
+ jsval pv = valueToJSVal(car(l), cx);
+ JS_SetElement(cx, a, i, &pv);
+ return valuesToJSElements(a, cdr(l), ++i, cx);
+}
+
+/**
+ * Returns true if a list represents a JS array.
+ */
+const bool isJSArray(const list<value>& l) {
+ if(isNil(l))
+ return false;
+ const value v = car(l);
+ if(isList(v)) {
+ const list<value> p = v;
+ if(isSymbol(car(p)))
+ return false;
+ }
+ return true;
+}
+
+
+
+/**
+ * Converts a list of values to JS properties.
+ */
+JSObject* valuesToJSProperties(JSObject* o, const list<value>& l, const JSONContext& cx) {
+ const jsval valueToJSVal(const value& val, const JSONContext& cx);
+ if(isNil(l))
+ return o;
+ const list<value> p = car(l);
+ jsval pv = valueToJSVal(caddr(p), cx);
+ JS_SetProperty(cx, o, ((std::string)cadr(p)).c_str(), &pv);
+ return valuesToJSProperties(o, cdr(l), cx);
+}
+
+/**
+ * Converts a value to a JS val.
+ */
+const jsval valueToJSVal(const value& val, const JSONContext& cx) {
+ switch(type(val)) {
+ case value::String:
+ case value::Symbol: {
+ return STRING_TO_JSVAL(JS_NewStringCopyZ(cx, ((std::string)val).c_str()));
+ }
+ case value::Bool: {
+ return BOOLEAN_TO_JSVAL((bool)val);
+ }
+ case value::Number: {
+ return DOUBLE_TO_JSVAL(JS_NewDouble(cx, (double)val));
+ }
+ case value::List: {
+ if (isJSArray(val))
+ return OBJECT_TO_JSVAL(valuesToJSElements(JS_NewArrayObject(cx, 0, NULL), val, 0, cx));
+ return OBJECT_TO_JSVAL(valuesToJSProperties(JS_NewObject(cx, NULL, NULL, NULL), val, cx));
+ }
+ default: {
+ return JSVAL_VOID;
+ }
+ }
+}
+
+const failable<bool, std::string> writeList(const list<value>& l, JSObject* o, const JSONContext& cx) {
+ if (isNil(l))
+ return true;
+
+ // Write an attribute
+ const value token(car(l));
+
+ if (isTaggedList(token, attribute)) {
+ jsval pv = valueToJSVal(attributeValue(token), cx);
+ JS_SetProperty(cx, o, std::string(attributeName(token)).c_str(), &pv);
+
+ } else if (isTaggedList(token, element)) {
+
+ // Write the value of an element
+ if (elementHasValue(token)) {
+ jsval pv = valueToJSVal(elementValue(token), cx);
+ JS_SetProperty(cx, o, std::string(elementName(token)).c_str(), &pv);
+
+ } else {
+
+ // Write a parent element
+ JSObject* child = JS_NewObject(cx, NULL, NULL, NULL);
+ jsval pv = OBJECT_TO_JSVAL(child);
+ JS_SetProperty(cx, o, std::string(elementName(token)).c_str(), &pv);
+
+ // Write its children
+ const failable<bool, std::string> w = writeList(elementChildren(token), child, cx);
+ if (!hasValue(w))
+ return w;
+ }
+ }
+
+ // Go on
+ return writeList(cdr(l), o, cx);
+}
+
+/**
+ * Context passed to the JSON write callback function.
+ */
+template<typename R> class WriteContext {
+public:
+ WriteContext(const lambda<R(std::string, R)>& reduce, const R& accum, const JSONContext& cx) : cx(cx), reduce(reduce), accum(accum) {
+ }
+ const JSONContext& cx;
+ const lambda<R(std::string, R)> reduce;
+ R accum;
+};
+
+/**
+ * Called by JS_Stringify to write JSON out.
+ */
+template<typename R> JSBool writeCallback(const jschar *buf, uint32 len, void *data) {
+ WriteContext<R>& wcx = *(static_cast<WriteContext<R>*> (data));
+ JSString* jstr = JS_NewUCStringCopyN(wcx.cx, buf, len);
+ wcx.accum = wcx.reduce(std::string(JS_GetStringBytes(jstr), JS_GetStringLength(jstr)), wcx.accum);
+ return JS_TRUE;
+}
+
+/**
+ * Convert a list of values to a JSON document.
+ */
+template<typename R> const failable<R, std::string> writeJSON(const lambda<R(std::string, R)>& reduce, const R& initial, const list<value>& l, const JSONContext& cx) {
+ JSObject* o = JS_NewObject(cx, NULL, NULL, NULL);
+ jsval val = OBJECT_TO_JSVAL(o);
+ const failable<bool, std::string> w = writeList(l, o, cx);
+ if (!hasValue(w))
+ return mkfailure<R, std::string>(reason(w));
+
+ WriteContext<R> wcx(reduce, initial, cx);
+ if (!JS_Stringify(cx, &val, NULL, JSVAL_NULL, writeCallback<R>, &wcx))
+ return mkfailure<R, std::string>("JS_Stringify failed");
+ return wcx.accum;
+}
+
+/**
+ * Convert a list of values to a list of strings representing a JSON document.
+ */
+const failable<list<std::string>, std::string> writeJSON(const list<value>& l, const JSONContext& cx) {
+ const failable<list<std::string>, std::string> ls = writeJSON<list<std::string> >(rcons<std::string>, list<std::string>(), l, cx);
+ if (!hasValue(ls))
+ return ls;
+ return reverse(list<std::string>(ls));
+}
+
+/**
+ * Convert a function + params to a JSON request.
+ */
+const failable<list<std::string>, std::string> jsonRequest(const value& id, const value& func, const value& params, json::JSONContext& cx) {
+ const list<value> r = mklist<value>(mklist<value>("id", id), mklist<value>("method", func), mklist<value>("params", params));
+ return writeJSON(valuesToElements(r), cx);
+}
+
+/**
+ * Convert a value to a JSON result.
+ */
+const failable<list<std::string>, std::string> jsonResult(const value& id, const value& val, JSONContext& cx) {
+ return writeJSON(valuesToElements(mklist<value>(mklist<value>("id", id), mklist<value>("result", val))), cx);
+}
+
+}
+}
+
+#endif /* tuscany_json_hpp */
diff --git a/sca-cpp/trunk/modules/scdl/Makefile.am b/sca-cpp/trunk/modules/scdl/Makefile.am
new file mode 100644
index 0000000000..55a190579c
--- /dev/null
+++ b/sca-cpp/trunk/modules/scdl/Makefile.am
@@ -0,0 +1,28 @@
+# 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.
+
+noinst_PROGRAMS = scdl-test
+
+nobase_include_HEADERS = *.hpp
+
+INCLUDES = -I. -I$(top_builddir)/kernel -I${LIBXML2_INCLUDE} -I${APR_INCLUDE}
+
+scdl_test_SOURCES = scdl-test.cpp
+scdl_test_LDADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1
+
+TESTS = scdl-test
+
diff --git a/sca-cpp/trunk/modules/scdl/scdl-test b/sca-cpp/trunk/modules/scdl/scdl-test
new file mode 100755
index 0000000000..acef45d225
--- /dev/null
+++ b/sca-cpp/trunk/modules/scdl/scdl-test
Binary files differ
diff --git a/sca-cpp/trunk/modules/scdl/scdl-test.cpp b/sca-cpp/trunk/modules/scdl/scdl-test.cpp
new file mode 100644
index 0000000000..c3da1013a8
--- /dev/null
+++ b/sca-cpp/trunk/modules/scdl/scdl-test.cpp
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test SCDL read functions.
+ */
+
+#include <assert.h>
+#include <iostream>
+#include <sstream>
+#include <fstream>
+#include <string>
+#include "slist.hpp"
+#include "scdl.hpp"
+
+namespace tuscany {
+namespace scdl {
+
+bool testComposite() {
+ std::ifstream is("test.composite");
+ const list<value> c = readXML(streamList(is));
+ return true;
+}
+
+bool testComponents() {
+ std::ifstream is("test.composite");
+ const list<value> c = components(readXML(streamList(is)));
+ assert(length(c) == 4);
+
+ const value store = car(c);
+ assert(name(store) == std::string("Store"));
+ const value impl = implementation(store);
+ assert(uri(impl) == std::string("store.html"));
+ assert(implementationType(impl) == "t:implementation.widget");
+
+ const value catalog = named(std::string("Catalog"), c);
+ assert(name(catalog) == std::string("Catalog"));
+ return true;
+}
+
+bool testServices() {
+ std::ifstream is("test.composite");
+ const list<value> c = components(readXML(streamList(is)));
+ const value store = car(c);
+
+ assert(length(services(store)) == 1);
+ const value widget = car(services(store));
+ assert(name(widget) == std::string("Widget"));
+
+ assert(length(bindings(widget)) == 1);
+ const value binding = car(bindings(widget));
+ assert(uri(binding) == std::string("/store"));
+ assert(bindingType(binding) == "t:binding.http");
+ return true;
+}
+
+bool testReferences() {
+ std::ifstream is("test.composite");
+ const list<value> c = components(readXML(streamList(is)));
+ const value store = car(c);
+
+ assert(length(references(store)) == 3);
+ const value catalog = car(references(store));
+ assert(name(catalog) == std::string("catalog"));
+ assert(target(catalog) == std::string("Catalog"));
+
+ assert(length(bindings(catalog)) == 1);
+ const value binding = car(bindings(catalog));
+ assert(uri(binding) == value());
+ assert(bindingType(binding) == "t:binding.jsonrpc");
+ return true;
+}
+
+bool testProperties() {
+ std::ifstream is("test.composite");
+ const list<value> c = components(readXML(streamList(is)));
+ const value catalog = named(std::string("Catalog"), c);
+
+ assert(length(properties(catalog)) == 1);
+ const value currencyCode = car(properties(catalog));
+ assert(name(currencyCode) == std::string("currencyCode"));
+ assert(propertyValue(currencyCode) == std::string("USD"));
+ return true;
+}
+
+}
+}
+
+int main() {
+ std::cout << "Testing..." << std::endl;
+
+ tuscany::scdl::testComposite();
+ tuscany::scdl::testComponents();
+ tuscany::scdl::testServices();
+ tuscany::scdl::testReferences();
+ tuscany::scdl::testProperties();
+
+ std::cout << "OK" << std::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/trunk/modules/scdl/scdl.hpp b/sca-cpp/trunk/modules/scdl/scdl.hpp
new file mode 100644
index 0000000000..4d84610fb0
--- /dev/null
+++ b/sca-cpp/trunk/modules/scdl/scdl.hpp
@@ -0,0 +1,158 @@
+/*
+ * 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_scdl_hpp
+#define tuscany_scdl_hpp
+
+/**
+ * SCDL read functions.
+ */
+
+#include <string>
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "xml.hpp"
+
+namespace tuscany {
+namespace scdl {
+
+/**
+ * Returns a list of components in a composite.
+ */
+const list<value> components(const value& l) {
+ const list<value> cs = elementChildren("composite", l);
+ if (isNil(cs))
+ return cs;
+ return elementChildren("component", car(cs));
+}
+
+/**
+ * Returns the name of a component, service or reference.
+ */
+const value name(const value& l) {
+ return attributeValue("name", l);
+}
+
+/**
+ * Returns the scdl declaration with the given name.
+ */
+struct filterName {
+ const value n;
+ filterName(const value& n) : n(n) {
+ }
+ const bool operator()(const value& v) const {
+ return name(v) == n;
+ }
+};
+
+const value named(const value& name, const value& l) {
+ const list<value> c = filter<value>(filterName(name), l);
+ if (isNil(c))
+ return value();
+ return car(c);
+}
+
+/**
+ * Returns the implementation of a component.
+ */
+const bool filterImplementation(const value& v) {
+ return isElement(v) && std::string(cadr<value>(v)).find("implementation.") != std::string::npos;
+}
+
+const value implementation(const value& l) {
+ const list<value> n = filter<value>(filterImplementation, l);
+ if (isNil(n))
+ return value();
+ return car(n);
+}
+
+/**
+ * Returns the URI of a service, reference or implementation.
+ */
+const value uri(const value& l) {
+ return attributeValue("uri", l);
+}
+
+/**
+ * Returns a list of services in a component.
+ */
+const list<value> services(const value& l) {
+ return elementChildren("service", l);
+}
+
+/**
+ * Returns a list of references in a component.
+ */
+const list<value> references(const value& l) {
+ return elementChildren("reference", l);
+}
+
+/**
+ * Returns a list of properties in a component.
+ */
+const list<value> properties(const value& l) {
+ return elementChildren("property", l);
+}
+
+/**
+ * Returns the target of a reference.
+ */
+const value target(const value& l) {
+ return attributeValue("target", l);
+}
+
+/**
+ * Returns a list of bindings in a service or reference.
+ */
+const bool filterBinding(const value& v) {
+ return isElement(v) && std::string(cadr<value>(v)).find("binding.") != std::string::npos;
+}
+
+const list<value> bindings(const value& l) {
+ return filter<value>(filterBinding, l);
+}
+
+/**
+ * Returns the type of an implementation.
+ */
+const value implementationType(const value& l) {
+ return elementName(l);
+}
+
+/**
+ * Returns the type of a binding.
+ */
+const value bindingType(const value& l) {
+ return elementName(l);
+}
+
+/**
+ * Returns the value of a property.
+ */
+const value propertyValue(const value& l) {
+ return elementValue(l);
+}
+
+}
+}
+
+#endif /* tuscany_scdl_hpp */
diff --git a/sca-cpp/trunk/modules/scdl/test.composite b/sca-cpp/trunk/modules/scdl/test.composite
new file mode 100644
index 0000000000..b315f23f9a
--- /dev/null
+++ b/sca-cpp/trunk/modules/scdl/test.composite
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * 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.
+-->
+<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200903"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://store"
+ name="store">
+
+ <component name="Store">
+ <t:implementation.widget uri="store.html"/>
+ <service name="Widget">
+ <t:binding.http uri="/store"/>
+ </service>
+ <reference name="catalog" target="Catalog">
+ <t:binding.jsonrpc/>
+ </reference>
+ <reference name="shoppingCart" target="ShoppingCart/Cart">
+ <t:binding.atom/>
+ </reference>
+ <reference name="shoppingTotal" target="ShoppingCart/Total">
+ <t:binding.jsonrpc/>
+ </reference>
+ </component>
+
+ <component name="Catalog">
+ <t:implementation.scheme uri="fruits-catalog.scm"/>
+ <property name="currencyCode">USD</property>
+ <service name="Catalog">
+ <t:binding.jsonrpc/>
+ </service>
+ <reference name="currencyConverter" target="CurrencyConverter"/>
+ </component>
+
+ <component name="ShoppingCart">
+ <t:implementation.scheme uri="shopping-cart.scm"/>
+ <service name="Cart">
+ <t:binding.atom uri="/ShoppingCart"/>
+ </service>
+ <service name="Total">
+ <t:binding.jsonrpc/>
+ </service>
+ <reference name="cache">
+ <binding.memcached uri="localhost:11311"/>
+ </reference>
+ </component>
+
+ <component name="CurrencyConverter">
+ <t:implementation.scheme uri="currency-converter.scm"/>
+ </component>
+
+</composite>