summaryrefslogtreecommitdiffstats
path: root/sca-cpp/branches/cpp-contrib/modules
diff options
context:
space:
mode:
authorjsdelfino <jsdelfino@13f79535-47bb-0310-9956-ffa450edef68>2010-02-28 19:40:06 +0000
committerjsdelfino <jsdelfino@13f79535-47bb-0310-9956-ffa450edef68>2010-02-28 19:40:06 +0000
commite982b4ef38fd043c15e89bdd60763b10434a087e (patch)
tree6b9d9c3fc9aff22edb0f137040164c1cbf2359af /sca-cpp/branches/cpp-contrib/modules
parent64e2486555a0a14f7d9690c2fc62c30bde803a91 (diff)
Moving old inactive code to a branch as it's confusing code assist, searches and indexing in trunk.
git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@917273 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to '')
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/Makefile.am22
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/atom/Makefile.am23
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/atom/atom-test.cpp192
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/atom/atom.hpp178
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/http/Makefile.am31
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/http/conf/mime.types607
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/http/curl-test.cpp88
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/http/curl.hpp401
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/http/htdocs/index.html21
-rwxr-xr-xsca-cpp/branches/cpp-contrib/modules/http/http-test32
-rwxr-xr-xsca-cpp/branches/cpp-contrib/modules/http/httpd-conf36
-rwxr-xr-xsca-cpp/branches/cpp-contrib/modules/http/httpd-restart25
-rwxr-xr-xsca-cpp/branches/cpp-contrib/modules/http/httpd-start25
-rwxr-xr-xsca-cpp/branches/cpp-contrib/modules/http/httpd-stop25
-rwxr-xr-xsca-cpp/branches/cpp-contrib/modules/http/httpd-test38
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/http/httpd.hpp396
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/java/Makefile.am60
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/java/client-test.cpp46
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/java/domain-test.composite42
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/java/driver.hpp61
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/java/eval.hpp527
-rwxr-xr-xsca-cpp/branches/cpp-contrib/modules/java/java-conf27
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/java/java-shell.cpp40
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/java/java-test.cpp138
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/java/mod-java.cpp80
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/java/mod-java.hpp77
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/java/org/apache/tuscany/ClassLoader.java76
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/java/org/apache/tuscany/InvocationHandler.java60
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/java/org/apache/tuscany/IterableUtil.java378
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/java/org/apache/tuscany/Service.java53
-rwxr-xr-xsca-cpp/branches/cpp-contrib/modules/java/server-test41
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/java/test/Adder.java26
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/java/test/AdderImpl.java28
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/java/test/CalcImpl.java52
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/java/test/Client.java34
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/java/test/ClientImpl.java44
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/java/test/Server.java34
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/java/test/ServerImpl.java51
-rwxr-xr-xsca-cpp/branches/cpp-contrib/modules/java/wiring-test78
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/json/Makefile.am23
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/json/json-test.cpp176
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/json/json.hpp403
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/python/Makefile.am45
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/python/client-test.cpp46
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/python/client-test.py35
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/python/domain-test.composite42
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/python/driver.hpp63
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/python/eval.hpp300
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/python/mod-python.cpp66
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/python/mod-python.hpp79
-rwxr-xr-xsca-cpp/branches/cpp-contrib/modules/python/python-conf26
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/python/python-shell.cpp40
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/python/python-test.cpp109
-rwxr-xr-xsca-cpp/branches/cpp-contrib/modules/python/server-test39
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/python/server-test.py42
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/python/tuscany-sca-1.1-implementation-python.xsd43
-rwxr-xr-xsca-cpp/branches/cpp-contrib/modules/python/wiring-test76
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/scdl/Makefile.am23
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/scdl/scdl-test.cpp124
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/scdl/scdl.hpp178
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/scdl/test.composite64
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/scheme/Makefile.am27
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/scheme/driver.hpp76
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/scheme/environment.hpp179
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/scheme/eval-shell.cpp36
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/scheme/eval-test.cpp231
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/scheme/eval.hpp290
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/scheme/io.hpp217
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/scheme/primitive.hpp285
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/scheme/tuscany-sca-1.1-implementation-scheme.xsd43
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/server/Makefile.am39
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/server/client-test.cpp46
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/server/client-test.hpp346
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/server/client-test.scm38
-rwxr-xr-xsca-cpp/branches/cpp-contrib/modules/server/cpp-conf26
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/server/domain-test.composite49
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/server/htdocs/entry.xml2
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/server/htdocs/feed.xml2
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/server/htdocs/index.html21
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/server/htdocs/json-request.txt1
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/server/htdocs/json-result.txt1
-rwxr-xr-xsca-cpp/branches/cpp-contrib/modules/server/httpd-test76
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/server/impl-test.cpp76
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/server/mod-cpp.hpp102
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/server/mod-eval.cpp62
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/server/mod-eval.hpp583
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/server/mod-scheme.hpp89
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/server/mod-wiring.cpp383
-rwxr-xr-xsca-cpp/branches/cpp-contrib/modules/server/scheme-conf26
-rwxr-xr-xsca-cpp/branches/cpp-contrib/modules/server/server-conf29
-rwxr-xr-xsca-cpp/branches/cpp-contrib/modules/server/server-test39
-rw-r--r--sca-cpp/branches/cpp-contrib/modules/server/server-test.scm44
-rwxr-xr-xsca-cpp/branches/cpp-contrib/modules/server/wiring-test76
93 files changed, 9775 insertions, 0 deletions
diff --git a/sca-cpp/branches/cpp-contrib/modules/Makefile.am b/sca-cpp/branches/cpp-contrib/modules/Makefile.am
new file mode 100644
index 0000000000..4332bbe7c8
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/Makefile.am
@@ -0,0 +1,22 @@
+# 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 = scheme atom json scdl http server python java
+
+includedir = $(prefix)/include/modules
+nobase_include_HEADERS = */*.hpp
+
diff --git a/sca-cpp/branches/cpp-contrib/modules/atom/Makefile.am b/sca-cpp/branches/cpp-contrib/modules/atom/Makefile.am
new file mode 100644
index 0000000000..c39caa34c3
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/atom/Makefile.am
@@ -0,0 +1,23 @@
+# 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
+
+atom_test_SOURCES = atom-test.cpp
+atom_test_LDFLAGS = -lxml2
+
+TESTS = atom-test
diff --git a/sca-cpp/branches/cpp-contrib/modules/atom/atom-test.cpp b/sca-cpp/branches/cpp-contrib/modules/atom/atom-test.cpp
new file mode 100644
index 0000000000..560c383a23
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/atom/atom-test.cpp
@@ -0,0 +1,192 @@
+/*
+ * 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 "stream.hpp"
+#include "string.hpp"
+#include "atom.hpp"
+
+namespace tuscany {
+namespace atom {
+
+ostream* writer(const string& s, ostream* os) {
+ (*os) << s;
+ return os;
+}
+
+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");
+
+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>");
+
+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 + value("item")
+ + value(list<value>() + element + value("name") + value(string("Apple")))
+ + value(list<value>() + element + value("price") + value(string("$2.99")));
+ const list<value> a = mklist<value>(string("item"), string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i);
+ ostringstream os;
+ writeATOMEntry<ostream*>(writer, &os, a);
+ assert(str(os) == itemEntry);
+ }
+ {
+ const list<value> a = content(readEntry(mklist(itemEntry)));
+ ostringstream os;
+ writeATOMEntry<ostream*>(writer, &os, a);
+ assert(str(os) == itemEntry);
+ }
+ {
+ const list<value> a = content(readEntry(mklist(incompleteEntry)));
+ ostringstream os;
+ writeATOMEntry<ostream*>(writer, &os, a);
+ assert(str(os) == completedEntry);
+ }
+ return true;
+}
+
+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");
+
+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() {
+ {
+ ostringstream os;
+ writeATOMFeed<ostream*>(writer, &os, mklist<value>("Feed", "1234"));
+ assert(str(os) == emptyFeed);
+ }
+ {
+ const list<value> a = content(readFeed(mklist(emptyFeed)));
+ ostringstream os;
+ writeATOMFeed<ostream*>(writer, &os, a);
+ assert(str(os) == 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));
+ ostringstream os;
+ writeATOMFeed<ostream*>(writer, &os, a);
+ assert(str(os) == 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));
+ ostringstream os;
+ writeATOMFeed<ostream*>(writer, &os, a);
+ assert(str(os) == itemFeed);
+ }
+ {
+ const list<value> a = content(readFeed(mklist(itemFeed)));
+ ostringstream os;
+ writeATOMFeed<ostream*>(writer, &os, a);
+ assert(str(os) == itemFeed);
+ }
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::atom::testEntry();
+ tuscany::atom::testFeed();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/atom/atom.hpp b/sca-cpp/branches/cpp-contrib/modules/atom/atom.hpp
new file mode 100644
index 0000000000..4dbf7c7728
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/atom/atom.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_atom_hpp
+#define tuscany_atom_hpp
+
+/**
+ * ATOM data conversion functions.
+ */
+
+#include "string.hpp"
+#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> entryValues(const list<value>& e) {
+ const list<value> lt = filter<value>(selector(mklist<value>(element, "title")), e);
+ const value t = isNil(lt)? value(emptyString) : elementValue(car(lt));
+ const list<value> li = filter<value>(selector(mklist<value>(element, "id")), e);
+ const value i = isNil(li)? value(emptyString) : 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>(entryValues(car(e)), entriesValues(cdr(e)));
+}
+
+/**
+ * Convert a list of strings to a list of values representing an ATOM entry.
+ */
+const failable<list<value> > readEntry(const list<string>& ilist) {
+ const list<value> e = readXML(ilist);
+ if (isNil(e))
+ return mkfailure<list<value> >("Empty entry");
+ return entryValues(car(e));
+}
+
+/**
+ * Convert a list of values representing an ATOM entry to a value.
+ */
+const value entryValue(const list<value>& e) {
+ const list<value> v = elementsToValues(mklist<value>(caddr(e)));
+ return cons(car(e), mklist<value>(cadr(e), cdr<value>(car(v))));
+}
+
+/**
+ * Convert a list of strings to a list of values representing an ATOM feed.
+ */
+const failable<list<value> > readFeed(const list<string>& ilist) {
+ const list<value> f = readXML(ilist);
+ if (isNil(f))
+ return mkfailure<list<value> >("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> writeATOMEntry(const lambda<R(const string&, const R)>& reduce, const R& initial, const list<value>& l) {
+ return writeXML<R>(reduce, initial, mklist<value>(entryElement(l)));
+}
+
+const failable<list<string> > writeATOMEntry(const list<value>& l) {
+ const failable<list<string> > ls = writeATOMEntry<list<string> >(rcons<string>, list<string>(), l);
+ if (!hasContent(ls))
+ return ls;
+ return reverse(list<string>(content(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> writeATOMFeed(const lambda<R(const string&, const 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<string> > writeATOMFeed(const list<value>& l) {
+ const failable<list<string> > ls = writeATOMFeed<list<string>>(rcons<string>, list<string>(), l);
+ if (!hasContent(ls))
+ return ls;
+ return reverse(list<string>(content(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/branches/cpp-contrib/modules/http/Makefile.am b/sca-cpp/branches/cpp-contrib/modules/http/Makefile.am
new file mode 100644
index 0000000000..b522113423
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/http/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 = curl-test
+
+INCLUDES = -I${HTTPD_INCLUDE}
+
+httpdir=$(prefix)/modules/http
+http_DATA = httpd.prefix
+
+curl_test_SOURCES = curl-test.cpp
+curl_test_LDFLAGS = -lxml2 -lcurl -lmozjs
+
+httpd.prefix: $(top_builddir)/config.status
+ echo ${HTTPD_PREFIX} >httpd.prefix
+
+TESTS = httpd-test http-test
diff --git a/sca-cpp/branches/cpp-contrib/modules/http/conf/mime.types b/sca-cpp/branches/cpp-contrib/modules/http/conf/mime.types
new file mode 100644
index 0000000000..4279f51bca
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/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/branches/cpp-contrib/modules/http/curl-test.cpp b/sca-cpp/branches/cpp-contrib/modules/http/curl-test.cpp
new file mode 100644
index 0000000000..4305d2ac38
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/http/curl-test.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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 "stream.hpp"
+#include "string.hpp"
+#include "perf.hpp"
+#include "curl.hpp"
+
+namespace tuscany {
+namespace http {
+
+ostream* curlWriter(const string& s, ostream* os) {
+ (*os) << s;
+ return os;
+}
+
+const bool testGet() {
+ CURLSession ch;
+ {
+ ostringstream os;
+ const failable<list<ostream*> > r = get<ostream*>(curlWriter, &os, "http://localhost:8090", ch);
+ assert(hasContent(r));
+ assert(contains(str(os), "HTTP/1.1 200 OK"));
+ assert(contains(str(os), "It works"));
+ }
+ {
+ const failable<value> r = getcontent("http://localhost:8090", ch);
+ assert(hasContent(r));
+ assert(contains(car(reverse(list<value>(content(r)))), "It works"));
+ }
+ return true;
+}
+
+struct getLoop {
+ CURLSession& ch;
+ getLoop(CURLSession& ch) : ch(ch) {
+ }
+ const bool operator()() const {
+ const failable<value> r = getcontent("http://localhost:8090", ch);
+ assert(hasContent(r));
+ assert(contains(car(reverse(list<value>(content(r)))), "It works"));
+ return true;
+ }
+};
+
+const bool testGetPerf() {
+ CURLSession ch;
+ lambda<bool()> gl = getLoop(ch);
+ cout << "Static GET test " << time(gl, 5, 200) << " ms" << endl;
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::http::testGet();
+ tuscany::http::testGetPerf();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/http/curl.hpp b/sca-cpp/branches/cpp-contrib/modules/http/curl.hpp
new file mode 100644
index 0000000000..f2c9458f42
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/http/curl.hpp
@@ -0,0 +1,401 @@
+/*
+ * 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.hpp"
+#include "gc.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "element.hpp"
+#include "monad.hpp"
+#include "parallel.hpp"
+#include "../atom/atom.hpp"
+#include "../json/json.hpp"
+
+namespace tuscany {
+namespace http {
+
+/**
+ * CURL library runtime, one per process.
+ */
+class CURLRuntime {
+public:
+ CURLRuntime() {
+ curl_global_init(CURL_GLOBAL_ALL);
+ }
+} curlRuntime;
+
+/**
+ * Represents a CURL session handle.
+ */
+class CURLSession {
+public:
+ CURLSession() : h(curl_easy_init()), owner(true) {
+ }
+
+ CURLSession(const CURLSession& c) : h(c.h), owner(false) {
+ }
+
+ ~CURLSession() {
+ if (!owner)
+ return;
+ if (h == NULL)
+ return;
+ curl_easy_cleanup(h);
+ }
+
+private:
+ CURL* h;
+ const bool owner;
+
+ friend CURL* handle(const CURLSession& c);
+};
+
+/**
+ * Returns the CURL handle used by a CURL session.
+ */
+CURL* handle(const CURLSession& c) {
+ return c.h;
+}
+
+/**
+ * Context passed to the read callback function.
+ */
+class CURLReadContext {
+public:
+ CURLReadContext(const list<string>& ilist) : ilist(ilist) {
+ }
+ list<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;
+ const list<string> f(fragment(rcx.ilist, size * nmemb));
+ const string s = car(f);
+ rcx.ilist = cdr(f);
+ memcpy(ptr, c_str(s), length(s));
+ return length(s);
+}
+
+/**
+ * Context passed to CURL write callback function.
+ */
+template<typename R> class CURLWriteContext {
+public:
+ CURLWriteContext(const lambda<R(const string&, const R)>& reduce, const R& accum) : reduce(reduce), accum(accum) {
+ }
+ const lambda<R(const string&, const 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(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<string>& h) {
+ if (isNil(h))
+ return cl;
+ return headers(curl_slist_append(cl, c_str(string(car(h)))), cdr(h));
+}
+
+template<typename R> const failable<list<R> > apply(const list<list<string> >& hdr, const lambda<R(const string&, const R)>& reduce, const R& initial, const string& url, const string& verb, const CURLSession& cs) {
+
+ // Init the curl session
+ CURL* ch = handle(cs);
+ curl_easy_reset(ch);
+ curl_easy_setopt(ch, CURLOPT_USERAGENT, "libcurl/1.0");
+
+ //TODO use HTTP chunking, for now just convert request to a single string
+ ostringstream os;
+ write(cadr(hdr), os);
+ const string s = str(os);
+ const int sz = length(s);
+
+ // 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*))(writeCallback<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);
+ curl_easy_setopt(ch, CURLOPT_TCP_NODELAY, true);
+
+ // Set the request headers
+ curl_slist* hl = headers(NULL, car(hdr));
+ if (hl != NULL)
+ curl_easy_setopt(ch, CURLOPT_HTTPHEADER, hl);
+
+ // Apply the HTTP verb
+ curl_easy_setopt(ch, CURLOPT_URL, c_str(url));
+ 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> >(string(curl_easy_strerror(rc)));
+ long httprc;
+ curl_easy_getinfo (ch, CURLINFO_RESPONSE_CODE, &httprc);
+ if (httprc != 200 && httprc != 201) {
+ ostringstream es;
+ es << "HTTP code " << httprc;
+ return mkfailure<list<R> >(str(es));
+ }
+ return mklist<R>(hcx.accum, wcx.accum);
+}
+
+/**
+ * Evaluate an expression remotely, at the given URL.
+ */
+const failable<value> evalExpr(const value& expr, const string& url, const CURLSession& ch) {
+ debug(url, "http::evalExpr::url");
+ debug(expr, "http::evalExpr::input");
+
+ // Convert expression to a JSON-RPC request
+ json::JSONContext cx;
+ const failable<list<string> > jsreq = json::jsonRequest(1, car<value>(expr), cdr<value>(expr), cx);
+ if (!hasContent(jsreq))
+ return mkfailure<value>(reason(jsreq));
+
+ // POST it to the URL
+ const list<string> h = mklist<string>("Content-Type: application/json-rpc");
+ const failable<list<list<string> > > res = apply<list<string> >(mklist<list<string> >(h, content(jsreq)), rcons<string>, list<string>(), url, "POST", ch);
+ if (!hasContent(res))
+ return mkfailure<value>(reason(res));
+
+ // Parse and return JSON-RPC result
+ const failable<value> rval = json::jsonResultValue(cadr<list<string> >(content(res)), cx);
+ if (!hasContent(rval))
+ return mkfailure<value>(reason(rval));
+ debug(content(rval), "http::evalExpr::result");
+ return content(rval);
+}
+
+/**
+ * Find and return a header.
+ */
+const failable<string> header(const char* prefix, const list<string>& h) {
+ if (isNil(h))
+ return mkfailure<string>(string("Couldn't find header: ") + prefix);
+ const string s = car(h);
+ if (find(s, prefix) != 0)
+ return header(prefix, cdr(h));
+ const string l(substr(s, length(prefix)));
+ return substr(l, 0, find_first_of(l, "\r\n"));
+}
+
+/**
+ * Find and return a location header.
+ */
+const failable<string> location(const list<string>& h) {
+ return header("Location: ", h);
+}
+
+/**
+ * Convert a location to an entry id.
+ */
+const failable<value> entryId(const failable<string> l) {
+ if (!hasContent(l))
+ return mkfailure<value>(reason(l));
+ const string ls(content(l));
+ return value(mklist<value>(string(substr(ls, find_last(ls, '/') + 1))));
+}
+
+/**
+ * Find and return a content-type header.
+ */
+const failable<string> contentType(const list<string>& h) {
+ return header("Content-Type: ", h);
+}
+
+/**
+ * HTTP GET, return the resource at the given URL.
+ */
+template<typename R> const failable<list<R> > get(const lambda<R(const string&, const R)>& reduce, const R& initial, const string& url, const CURLSession& ch) {
+ debug(url, "http::get::url");
+ const list<list<string> > req = mklist(list<string>(), list<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> getcontent(const string& url, const CURLSession& ch) {
+ debug(url, "http::get::url");
+
+ // Get the contents of the resource at the given URL
+ const failable<list<list<string> > > res = get<list<string>>(rcons<string>, list<string>(), url, ch);
+ if (!hasContent(res))
+ return mkfailure<value>(reason(res));
+ const list<string> ls(reverse(cadr(content(res))));
+
+ // Return the content as a list of values
+ const value val(mkvalues(ls));
+ debug(val, "http::get::result");
+ return val;
+}
+
+/**
+ * HTTP GET, return a list of values representing the resource at the given URL.
+ */
+const failable<value> get(const string& url, const CURLSession& ch) {
+ debug(url, "http::get::url");
+
+ // Get the contents of the resource at the given URL
+ const failable<list<list<string> > > res = get<list<string> >(rcons<string>, list<string>(), url, ch);
+ if (!hasContent(res))
+ return mkfailure<value>(reason(res));
+ const list<string> ls(reverse(cadr(content(res))));
+
+ const string ct(content(contentType(car(content(res)))));
+ if (ct == "application/atom+xml;type=entry") {
+ const value val(atom::entryValue(content(atom::readEntry(ls))));
+ debug(val, "http::get::result");
+ return val;
+ }
+
+ // Return the content as a list of values
+ const value val(mkvalues(ls));
+ debug(val, "http::get::result");
+ return val;
+}
+
+/**
+ * HTTP POST.
+ */
+const failable<value> post(const value& val, const string& url, const CURLSession& ch) {
+
+ // Convert value to an ATOM entry
+ const failable<list<string> > entry = atom::writeATOMEntry(atom::entryValuesToElements(val));
+ if (!hasContent(entry))
+ return mkfailure<value>(reason(entry));
+ debug(url, "http::post::url");
+ debug(content(entry), "http::post::input");
+
+ // POST it to the URL
+ const list<string> h = mklist<string>("Content-Type: application/atom+xml");
+ const list<list<string> > req = mklist<list<string> >(h, content(entry));
+ const failable<list<list<string> > > res = apply<list<string>>(req, rcons<string>, list<string>(), url, "POST", ch);
+ if (!hasContent(res))
+ return mkfailure<value>(reason(res));
+
+ // Return the new entry id from the HTTP location header
+ const failable<value> eid(entryId(location(car(content(res)))));
+ debug(eid, "http::post::result");
+ return eid;
+}
+
+/**
+ * HTTP PUT.
+ */
+const failable<value> put(const value& val, const string& url, const CURLSession& ch) {
+
+ // Convert value to an ATOM entry
+ const failable<list<string> > entry = atom::writeATOMEntry(atom::entryValuesToElements(val));
+ if (!hasContent(entry))
+ return mkfailure<value>(reason(entry));
+ debug(url, "http::put::url");
+ debug(content(entry), "http::put::input");
+
+ // PUT it to the URL
+ const list<string> h = mklist<string>("Content-Type: application/atom+xml");
+ const list<list<string> > req = mklist<list<string> >(h, content(entry));
+ const failable<list<list<string> > > res = apply<list<string> >(req, rcons<string>, list<string>(), url, "PUT", ch);
+ if (!hasContent(res))
+ return mkfailure<value>(reason(res));
+
+ debug(true, "http::put::result");
+ return value(true);
+}
+
+/**
+ * HTTP DELETE.
+ */
+const failable<value, string> del(const string& url, const CURLSession& ch) {
+ debug(url, "http::delete::url");
+
+ const list<list<string> > req = mklist(list<string>(), list<string>());
+ const failable<list<list<string> > > res = apply<list<string> >(req, rcons<string>, list<string>(), url, "DELETE", ch);
+ if (!hasContent(res))
+ return mkfailure<value>(reason(res));
+
+ debug(true, "http::delete::result");
+ return value(true);
+}
+
+/**
+ * HTTP client proxy function.
+ */
+struct proxy {
+ proxy(const string& uri) : uri(uri) {
+ }
+
+ const value operator()(const list<value>& args) const {
+ CURLSession cs;
+ failable<value> val = evalExpr(args, uri, cs);
+ if (!hasContent(val))
+ return value();
+ return content(val);
+ }
+
+ const string uri;
+};
+
+}
+}
+
+#endif /* tuscany_curl_hpp */
diff --git a/sca-cpp/branches/cpp-contrib/modules/http/htdocs/index.html b/sca-cpp/branches/cpp-contrib/modules/http/htdocs/index.html
new file mode 100644
index 0000000000..1bfb3e30c2
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/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/branches/cpp-contrib/modules/http/http-test b/sca-cpp/branches/cpp-contrib/modules/http/http-test
new file mode 100755
index 0000000000..9f65b37eec
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/http/http-test
@@ -0,0 +1,32 @@
+#!/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 8090 htdocs
+./httpd-start tmp
+sleep 2
+
+# Test
+./curl-test
+rc=$?
+
+# Cleanup
+./httpd-stop tmp
+sleep 2
+return $rc
diff --git a/sca-cpp/branches/cpp-contrib/modules/http/httpd-conf b/sca-cpp/branches/cpp-contrib/modules/http/httpd-conf
new file mode 100755
index 0000000000..434040803d
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/http/httpd-conf
@@ -0,0 +1,36 @@
+#!/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
+ErrorLog $root/logs/error_log
+ServerName http://127.0.0.1:$port
+Listen $port
+DocumentRoot $htdocs
+TypesConfig $here/conf/mime.types
+EOF
+
diff --git a/sca-cpp/branches/cpp-contrib/modules/http/httpd-restart b/sca-cpp/branches/cpp-contrib/modules/http/httpd-restart
new file mode 100755
index 0000000000..92f27eb0df
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/http/httpd-restart
@@ -0,0 +1,25 @@
+#!/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.
+
+# Restart httpd server
+here=`readlink -f $0`; here=`dirname $here`
+root=`readlink -f $1`
+
+httpd_prefix=`cat $here/httpd.prefix`
+$httpd_prefix/bin/apachectl -k graceful -d $root
diff --git a/sca-cpp/branches/cpp-contrib/modules/http/httpd-start b/sca-cpp/branches/cpp-contrib/modules/http/httpd-start
new file mode 100755
index 0000000000..91fc5284af
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/http/httpd-start
@@ -0,0 +1,25 @@
+#!/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.
+
+# Start httpd server
+here=`readlink -f $0`; here=`dirname $here`
+root=`readlink -f $1`
+
+httpd_prefix=`cat $here/httpd.prefix`
+$httpd_prefix/bin/apachectl -E $root/logs/error_log -k start -d $root
diff --git a/sca-cpp/branches/cpp-contrib/modules/http/httpd-stop b/sca-cpp/branches/cpp-contrib/modules/http/httpd-stop
new file mode 100755
index 0000000000..7f4fe94629
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/http/httpd-stop
@@ -0,0 +1,25 @@
+#!/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.
+
+# Stop httpd server
+here=`readlink -f $0`; here=`dirname $here`
+root=`readlink -f $1`
+
+httpd_prefix=`cat $here/httpd.prefix`
+$httpd_prefix/bin/apachectl -k graceful-stop -d $root
diff --git a/sca-cpp/branches/cpp-contrib/modules/http/httpd-test b/sca-cpp/branches/cpp-contrib/modules/http/httpd-test
new file mode 100755
index 0000000000..952a58937a
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/http/httpd-test
@@ -0,0 +1,38 @@
+#!/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
+./httpd-start tmp
+sleep 2
+
+# Test HTTP GET
+curl http://localhost:8090/index.html 2>/dev/null >tmp/index.html
+diff tmp/index.html htdocs/index.html
+rc=$?
+
+# Cleanup
+./httpd-stop tmp
+sleep 2
+if [ "$rc" = "0" ]; then
+ echo "OK"
+fi
+return $rc
diff --git a/sca-cpp/branches/cpp-contrib/modules/http/httpd.hpp b/sca-cpp/branches/cpp-contrib/modules/http/httpd.hpp
new file mode 100644
index 0000000000..bd0e23f76b
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/http/httpd.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_httpd_hpp
+#define tuscany_httpd_hpp
+
+/**
+ * HTTPD module implementation functions.
+ */
+
+#include "apr_strings.h"
+#include "apr_fnmatch.h"
+#include "apr_lib.h"
+#define APR_WANT_STRFUNC
+#include "apr_want.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 "http_config.h"
+#include "ap_mpm.h"
+#include "mod_core.h"
+
+#include "string.hpp"
+#include "stream.hpp"
+#include "list.hpp"
+#include "value.hpp"
+
+
+namespace tuscany {
+namespace httpd {
+
+/**
+ * Returns a server-scoped module configuration.
+ */
+template<typename C> void* makeServerConf(apr_pool_t *p, server_rec *s) {
+ return new (gc_new<C>(p)) C(s);
+}
+
+template<typename C> const C& serverConf(const request_rec* r, const module* mod) {
+ return *(C*)ap_get_module_config(r->server->module_config, mod);
+}
+
+template<typename C> C& serverConf(const server_rec* s, const module* mod) {
+ return *(C*)ap_get_module_config(s->module_config, mod);
+}
+
+template<typename C> C& serverConf(const cmd_parms *cmd, const module* mod) {
+ return *(C*)ap_get_module_config(cmd->server->module_config, mod);
+}
+
+
+/**
+ * Return the content type of a request.
+ */
+const char* optional(const char* s) {
+ if (s == NULL)
+ return "";
+ return s;
+}
+
+const string contentType(const request_rec* r) {
+ return optional(apr_table_get(r->headers_in, "Content-Type"));
+}
+
+#ifdef WANT_MAINTAINER_MODE
+
+/**
+ * Debug log.
+ */
+int debugHeader(unused void* r, const char* key, const char* value) {
+ cerr << " header key: " << key << ", value: " << value << endl;
+ return 1;
+}
+
+const bool debugRequest(request_rec* r, const string& msg) {
+ cerr << msg << ":" << endl;
+ cerr << " protocol: " << optional(r->protocol) << endl;
+ cerr << " method: " << optional(r->method) << endl;
+ cerr << " method number: " << r->method_number << endl;
+ cerr << " content type: " << contentType(r) << endl;
+ cerr << " content encoding: " << optional(r->content_encoding) << endl;
+ apr_table_do(debugHeader, r, r->headers_in, NULL);
+ cerr << " unparsed uri: " << optional(r->unparsed_uri) << endl;
+ cerr << " uri: " << optional(r->uri) << endl;
+ cerr << " path info: " << optional(r->path_info) << endl;
+ cerr << " filename: " << optional(r->filename) << endl;
+ cerr << " uri tokens: " << pathTokens(r->uri) << endl;
+ cerr << " args: " << optional(r->args) << endl;
+ return true;
+}
+
+#define httpdDebugRequest(r, msg) httpd::debugRequest(r, msg)
+
+#else
+
+#define httpdDebugRequest(r, msg)
+
+#endif
+
+/**
+ * Return the remaining part of a uri after the given path (aka the path info.)
+ */
+const list<value> pathInfo(const list<value>& uri, const list<value>& path) {
+ if (isNil(path))
+ return uri;
+ return pathInfo(cdr(uri), cdr(path));
+}
+
+/**
+ * Returns a list of key value pairs from the args in a query string.
+ */
+const list<value> queryArg(const string& s) {
+ const list<string> t = tokenize("=", s);
+ return mklist<value>(c_str(car(t)), 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<string, list<value>>(queryArg, tokenize("&", a));
+}
+
+/**
+ * Returns a list of param values other than the id and method args from a list
+ * of key value pairs.
+ */
+const list<value> queryParams(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)));
+}
+
+/**
+ * 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)));
+}
+
+/**
+ * Setup the HTTP read policy.
+ */
+const int setupReadPolicy(request_rec* r) {
+ 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");
+ return OK;
+}
+
+/**
+ * Read the content of a POST or PUT.
+ */
+const list<string> read(request_rec* r) {
+ char b[1024];
+ const int n = ap_get_client_block(r, b, sizeof(b));
+ if (n <= 0)
+ return list<string>();
+ return cons(string(b, n), read(r));
+}
+
+/**
+ * Convert a URI represented as a list to an absolute URL.
+ */
+const char* url(const list<value>& v, request_rec* r) {
+ const string u = string(r->uri) + path(v);
+ return ap_construct_url(r->pool, c_str(u), r);
+}
+
+/**
+ * Write an HTTP result.
+ */
+const failable<int> writeResult(const failable<list<string> >& ls, const string& ct, request_rec* r) {
+ if (!hasContent(ls))
+ return mkfailure<int>(reason(ls));
+ ostringstream os;
+ write(content(ls), os);
+ const string ob(str(os));
+ debug(ob, "httpd::result");
+
+ const string etag(ap_md5(r->pool, (const unsigned char*)c_str(ob)));
+ const char* match = apr_table_get(r->headers_in, "If-None-Match");
+ apr_table_setn(r->headers_out, "ETag", apr_pstrdup(r->pool, c_str(etag)));
+ if (match != NULL && etag == match) {
+ r->status = HTTP_NOT_MODIFIED;
+ return OK;
+ }
+ ap_set_content_type(r, apr_pstrdup(r->pool, c_str(ct)));
+ ap_rputs(c_str(ob), r);
+ return OK;
+}
+
+/**
+ * Report request execution status.
+ */
+const int reportStatus(const failable<int>& rc) {
+ if (!hasContent(rc))
+ return HTTP_INTERNAL_SERVER_ERROR;
+ return content(rc);
+}
+
+/**
+ * Construct a redirect URI.
+ */
+const string redirectURI(const string& file, const string& pi) {
+ return file + pi;
+}
+
+const string redirectURI(const string& file, const string& pi, const string& args) {
+ return file + pi + "?" + args;
+}
+
+/**
+ * Convert a value to an HTTPD request struc
+ */
+request_rec* request(const value& v) {
+ return (request_rec*)(long)(double)v;
+}
+
+/**
+ * Convert an HTTPD request struct to a value
+ */
+const value requestValue(request_rec* r) {
+ return value((double)(long)r);
+}
+
+/**
+ * Update filters in an HTTPD redirect request.
+ * Similar to httpd/modules/http/http_request.c::update_r_in_filters.
+ */
+const bool redirectFilters(ap_filter_t* f, request_rec* from, request_rec* to) {
+ if (f == NULL)
+ return true;
+ if (f->r == from)
+ f->r = to;
+ return redirectFilters(f->next, from, to);
+}
+
+/**
+ * Create an HTTPD redirect request.
+ * Similar to httpd/modules/http/http_request.c::internal_internal_redirect.
+ */
+extern "C" {
+ AP_DECLARE(ap_conf_vector_t*) ap_create_request_config(apr_pool_t *p);
+}
+
+const failable<request_rec*, int> internalRedirectRequest(const string& nr_uri, request_rec* r) {
+ if (ap_is_recursion_limit_exceeded(r))
+ return mkfailure<request_rec*, int>(HTTP_INTERNAL_SERVER_ERROR);
+
+ // Create a new request
+ request_rec* nr = (request_rec*)apr_pcalloc(r->pool, sizeof(request_rec));
+ nr->connection = r->connection;
+ nr->server = r->server;
+ nr->pool = r->pool;
+ nr->method = r->method;
+ nr->method_number = r->method_number;
+ nr->allowed_methods = ap_make_method_list(nr->pool, 2);
+ ap_parse_uri(nr, apr_pstrdup(nr->pool, c_str(nr_uri)));
+ nr->request_config = ap_create_request_config(r->pool);
+ nr->per_dir_config = r->server->lookup_defaults;
+ nr->prev = r;
+ r->next = nr;
+
+ // Run create request hook
+ ap_run_create_request(nr);
+
+ // Inherit protocol info from the original request
+ nr->the_request = r->the_request;
+ nr->allowed = r->allowed;
+ nr->status = r->status;
+ nr->assbackwards = r->assbackwards;
+ nr->header_only = r->header_only;
+ nr->protocol = r->protocol;
+ nr->proto_num = r->proto_num;
+ nr->hostname = r->hostname;
+ nr->request_time = r->request_time;
+ nr->main = r->main;
+ nr->headers_in = r->headers_in;
+ nr->headers_out = apr_table_make(r->pool, 12);
+ nr->err_headers_out = r->err_headers_out;
+ nr->subprocess_env = r->subprocess_env;
+ nr->notes = apr_table_make(r->pool, 5);
+ nr->allowed_methods = ap_make_method_list(nr->pool, 2);
+ nr->htaccess = r->htaccess;
+ nr->no_cache = r->no_cache;
+ nr->expecting_100 = r->expecting_100;
+ nr->no_local_copy = r->no_local_copy;
+ nr->read_length = r->read_length;
+ nr->vlist_validator = r->vlist_validator;
+
+ // Setup input and output filters
+ nr->proto_output_filters = r->proto_output_filters;
+ nr->proto_input_filters = r->proto_input_filters;
+ nr->output_filters = nr->proto_output_filters;
+ nr->input_filters = nr->proto_input_filters;
+ if (nr->main)
+ ap_add_output_filter_handle(ap_subreq_core_filter_handle, NULL, nr, nr->connection);
+ redirectFilters(nr->input_filters, r, nr);
+ redirectFilters(nr->output_filters, r, nr);
+ const int rrc = ap_run_post_read_request(nr);
+ if (rrc != OK && rrc != DECLINED)
+ return mkfailure<request_rec*, int>(rrc);
+
+ return nr;
+}
+
+/**
+ * Process an HTTPD internal redirect request.
+ * Similar to httpd/modules/http/http_request.c::ap_internal_redirect.
+ */
+extern "C" {
+ AP_DECLARE(int) ap_invoke_handler(request_rec *r);
+}
+
+const int internalRedirect(request_rec* nr) {
+ int status = ap_run_quick_handler(nr, 0);
+ if (status == DECLINED) {
+ status = ap_process_request_internal(nr);
+ if (status == OK)
+ status = ap_invoke_handler(nr);
+ }
+ if (status != OK) {
+ nr->status = status;
+ return OK;
+ }
+ ap_finalize_request_protocol(nr);
+ return OK;
+}
+
+/**
+ * Create and process an HTTPD redirect request.
+ */
+const int internalRedirect(const string& uri, request_rec* r) {
+ const failable<request_rec*, int> nr = httpd::internalRedirectRequest(uri, r);
+ if (!hasContent(nr))
+ return reason(nr);
+ return httpd::internalRedirect(content(nr));
+}
+
+/**
+ * Put a value in the process user data.
+ */
+const bool putUserData(const string& k, const void* v, const server_rec* s) {
+ apr_pool_userdata_set((const void *)v, c_str(k), apr_pool_cleanup_null, s->process->pool);
+ return true;
+}
+
+/**
+ * Return a user data value.
+ */
+const void* userData(const string& k, const server_rec* s) {
+ void* v = NULL;
+ apr_pool_userdata_get(&v, c_str(k), s->process->pool);
+ return v;
+}
+
+}
+}
+
+#endif /* tuscany_httpd_hpp */
diff --git a/sca-cpp/branches/cpp-contrib/modules/java/Makefile.am b/sca-cpp/branches/cpp-contrib/modules/java/Makefile.am
new file mode 100644
index 0000000000..5402ab495e
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/java/Makefile.am
@@ -0,0 +1,60 @@
+# 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.
+
+JAVAROOT = $(top_builddir)/modules/java
+libdir = $(prefix)/lib
+
+if WANT_JAVA
+
+noinst_PROGRAMS = java-test java-shell client-test
+
+lib_LTLIBRARIES = libmod_tuscany_java.la
+
+INCLUDES = -I${JAVA_INCLUDE}
+
+libmod_tuscany_java_la_SOURCES = mod-java.cpp
+libmod_tuscany_java_la_LDFLAGS = -lxml2 -lcurl -lmozjs -L${JAVA_LIB} -R${JAVA_LIB} -R${JAVA_LIB}/server -ljava
+
+java_test_SOURCES = java-test.cpp
+java_test_LDFLAGS = -L${JAVA_LIB} -R${JAVA_LIB} -R${JAVA_LIB}/server -ljava
+
+java_shell_SOURCES = java-shell.cpp
+java_shell_LDFLAGS = -L${JAVA_LIB} -R${JAVA_LIB} -R${JAVA_LIB}/server -ljava
+
+noinst_JAVA = org/apache/tuscany/*.java test/*.java
+
+jarfile = libmod-tuscany-java-${PACKAGE_VERSION}.jar
+jardir = ${libdir}
+jar_DATA = ${jarfile}
+
+${jarfile}: ${noinst_JAVA}
+ ${JAR} cf $@ org/apache/tuscany/*.class
+
+CLEANFILES = ${jarfile} org/apache/tuscany/*.class test/*.class
+
+client_test_SOURCES = client-test.cpp
+client_test_LDFLAGS = -lxml2 -lcurl -lmozjs
+
+prefixdir = $(top_builddir)/modules/java
+prefix_DATA = java.prefix
+
+java.prefix: $(top_builddir)/config.status
+ echo ${JAVA_PREFIX} >java.prefix
+
+TESTS = java-test server-test
+
+endif
diff --git a/sca-cpp/branches/cpp-contrib/modules/java/client-test.cpp b/sca-cpp/branches/cpp-contrib/modules/java/client-test.cpp
new file mode 100644
index 0000000000..d4a4d65ab4
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/java/client-test.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 "stream.hpp"
+#include "string.hpp"
+#include "../server/client-test.hpp"
+
+namespace tuscany {
+namespace server {
+
+string testURI = "http://localhost:8090/java";
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::server::testServer();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/java/domain-test.composite b/sca-cpp/branches/cpp-contrib/modules/java/domain-test.composite
new file mode 100644
index 0000000000..190f2ff5bb
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/java/domain-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/200912"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://domain/test"
+ name="domain-test">
+
+ <component name="java-test">
+ <implementation.java class="test.ServerImpl"/>
+ <service name="test">
+ <t:binding.http uri="java"/>
+ </service>
+ </component>
+
+ <component name="client-test">
+ <implementation.java class="test.ClientImpl"/>
+ <service name="client">
+ <t:binding.http uri="client"/>
+ </service>
+ <reference name="ref" target="java-test">
+ <t:binding.http/>
+ </reference>
+ </component>
+
+</composite>
diff --git a/sca-cpp/branches/cpp-contrib/modules/java/driver.hpp b/sca-cpp/branches/cpp-contrib/modules/java/driver.hpp
new file mode 100644
index 0000000000..ddfc057940
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/java/driver.hpp
@@ -0,0 +1,61 @@
+/*
+ * 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_java_driver_hpp
+#define tuscany_java_driver_hpp
+
+/**
+ * Java evaluator main driver loop.
+ */
+
+#include "string.hpp"
+#include "stream.hpp"
+#include "monad.hpp"
+#include "../scheme/driver.hpp"
+#include "eval.hpp"
+
+namespace tuscany {
+namespace java {
+
+const value evalDriverLoop(const JavaRuntime& jr, const JavaClass jc, istream& in, ostream& out) {
+ scheme::promptForInput(scheme::evalInputPrompt, out);
+ value input = scheme::readValue(in);
+ if (isNil(input))
+ return input;
+ const failable<value> output = evalClass(jr, input, jc);
+ scheme::announceOutput(scheme::evalOutputPrompt, out);
+ scheme::userPrint(content(output), out);
+ return evalDriverLoop(jr, jc, in, out);
+}
+
+const bool evalDriverRun(const char* name, istream& in, ostream& out) {
+ scheme::setupDisplay(out);
+ JavaRuntime javaRuntime;
+ const failable<JavaClass> jc = readClass(javaRuntime, ".", name);
+ if (!hasContent(jc))
+ return true;
+ evalDriverLoop(javaRuntime, content(jc), in, out);
+ return true;
+}
+
+}
+}
+#endif /* tuscany_java_driver_hpp */
diff --git a/sca-cpp/branches/cpp-contrib/modules/java/eval.hpp b/sca-cpp/branches/cpp-contrib/modules/java/eval.hpp
new file mode 100644
index 0000000000..741dda52ca
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/java/eval.hpp
@@ -0,0 +1,527 @@
+/*
+ * 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_java_eval_hpp
+#define tuscany_java_eval_hpp
+
+/**
+ * Java component implementation evaluation logic.
+ */
+#include <jni.h>
+
+#include "list.hpp"
+#include "value.hpp"
+
+namespace tuscany {
+namespace java {
+
+/**
+ * Represent a Java VM runtime.
+ */
+jobject JNICALL nativeInvoke(JNIEnv *env, jobject self, jobject proxy, jobject method, jobjectArray args);
+
+class JavaRuntime {
+public:
+ JavaRuntime() {
+
+ // Get existing JVM
+ jsize nvms = 0;
+ JNI_GetCreatedJavaVMs(&jvm, 1, &nvms);
+ if (nvms == 0) {
+
+ // Create a new JVM
+ JavaVMInitArgs args;
+ args.version = JNI_VERSION_1_6;
+ args.ignoreUnrecognized = JNI_FALSE;
+ JavaVMOption options[3];
+ args.options = options;
+ args.nOptions = 0;
+
+ // Configure classpath
+ const char* envcp = getenv("CLASSPATH");
+ const string cp = string("-Djava.class.path=") + (envcp == NULL? "." : envcp);
+ options[args.nOptions++].optionString = const_cast<char*>(c_str(cp));
+
+#ifdef WANT_MAINTAINER_MODE
+ // Enable assertions
+ options[args.nOptions++].optionString = const_cast<char*>("-ea");
+#endif
+
+ // Configure Java debugging
+ const char* jpdaopts = getenv("JPDA_OPTS");
+ if (jpdaopts != NULL) {
+ options[args.nOptions++].optionString = const_cast<char*>(jpdaopts);
+ } else {
+ const char* jpdaaddr = getenv("JPDA_ADDRESS");
+ if (jpdaaddr != NULL) {
+ const string jpda = string("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=") + jpdaaddr;
+ options[args.nOptions++].optionString = const_cast<char*>(c_str(jpda));
+ }
+ }
+
+ // Create the JVM
+ JNI_CreateJavaVM(&jvm, (void**)&env, &args);
+
+ } else {
+
+ // Just point to existing JVM
+ jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+ }
+
+ // Lookup System classes and methods
+ classClass = env->FindClass("java/lang/Class");
+ methodClass = env->FindClass("java/lang/reflect/Method");
+ objectClass = env->FindClass("java/lang/Object");
+ doubleClass = env->FindClass("java/lang/Double");
+ booleanClass = env->FindClass("java/lang/Boolean");
+ stringClass = env->FindClass("java/lang/String");
+ objectArrayClass = env->FindClass("[Ljava/lang/Object;");
+ iterableClass = env->FindClass("Ljava/lang/Iterable;");
+ classForName = env->GetStaticMethodID(classClass, "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
+ doubleValueOf = env->GetStaticMethodID(doubleClass, "valueOf", "(D)Ljava/lang/Double;");
+ doubleValue = env->GetMethodID(doubleClass, "doubleValue", "()D");
+ booleanValueOf = env->GetStaticMethodID(booleanClass, "valueOf", "(Z)Ljava/lang/Boolean;");
+ booleanValue = env->GetMethodID(booleanClass, "booleanValue", "()Z");
+ declaredMethods = env->GetMethodID(classClass, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;");
+ methodName = env->GetMethodID(methodClass, "getName", "()Ljava/lang/String;");
+ parameterTypes = env->GetMethodID(methodClass, "getParameterTypes", "()[Ljava/lang/Class;");
+
+ // Lookup Tuscany classes and methods
+ loaderClass = env->FindClass("org/apache/tuscany/ClassLoader");
+ loaderValueOf = env->GetStaticMethodID(loaderClass, "valueOf", "(Ljava/lang/String;)Ljava/lang/ClassLoader;");
+ loaderForName = env->GetStaticMethodID(loaderClass, "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
+ invokerClass = env->FindClass("org/apache/tuscany/InvocationHandler");
+ invokerValueOf = env->GetStaticMethodID(invokerClass, "valueOf", "(Ljava/lang/Class;J)Ljava/lang/Object;");
+ invokerStackTrace = env->GetStaticMethodID(invokerClass, "stackTrace", "(Ljava/lang/Throwable;)Ljava/lang/String;");
+ invokerLambda = env->GetFieldID(invokerClass, "lambda", "J");
+ iterableUtilClass = env->FindClass("org/apache/tuscany/IterableUtil");
+ iterableValueOf = env->GetStaticMethodID(iterableUtilClass, "list", "([Ljava/lang/Object;)Ljava/lang/Iterable;");
+ iterableIsNil = env->GetStaticMethodID(iterableUtilClass, "isNil", "(Ljava/lang/Object;)Z");
+ iterableCar = env->GetStaticMethodID(iterableUtilClass, "car", "(Ljava/lang/Object;)Ljava/lang/Object;");
+ iterableCdr = env->GetStaticMethodID(iterableUtilClass, "cdr", "(Ljava/lang/Object;)Ljava/lang/Iterable;");
+
+ // Register our native invocation handler function
+ JNINativeMethod nm;
+ nm.name = const_cast<char*>("invoke");
+ nm.signature = const_cast<char*>("(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");
+ nm.fnPtr = (void*)nativeInvoke;
+ env->RegisterNatives(invokerClass, &nm, 1);
+ }
+
+ JavaVM* jvm;
+ JNIEnv* env;
+
+ jclass classClass;
+ jclass methodClass;
+ jclass objectClass;
+ jclass doubleClass;
+ jclass booleanClass;
+ jclass stringClass;
+ jclass objectArrayClass;
+ jclass iterableClass;
+ jmethodID doubleValueOf;
+ jmethodID doubleValue;
+ jmethodID booleanValueOf;
+ jmethodID booleanValue;
+ jmethodID declaredMethods;
+ jmethodID methodName;
+ jmethodID parameterTypes;
+ jmethodID classForName;
+ jclass loaderClass;
+ jmethodID loaderValueOf;
+ jmethodID loaderForName;
+ jclass invokerClass;
+ jmethodID invokerValueOf;
+ jmethodID invokerStackTrace;
+ jfieldID invokerLambda;
+ jclass iterableUtilClass;
+ jmethodID iterableValueOf;
+ jmethodID iterableCar;
+ jmethodID iterableCdr;
+ jmethodID iterableIsNil;
+};
+
+/**
+ * Return the last exception that occurred in a JVM.
+ */
+string lastException(const JavaRuntime& jr) {
+ if (!jr.env->ExceptionCheck())
+ return "No Exception";
+ const jthrowable ex = jr.env->ExceptionOccurred();
+ const jstring trace = (jstring)jr.env->CallStaticObjectMethod(jr.invokerClass, jr.invokerStackTrace, ex);
+ const char* c = jr.env->GetStringUTFChars(trace, NULL);
+ const string msg(c);
+ jr.env->ReleaseStringUTFChars(trace, c);
+ jr.env->ExceptionClear();
+ return msg;
+}
+
+/**
+ * Declare conversion functions.
+ */
+const jobject valueToJobject(const JavaRuntime& jr, const value& jtype, const value& v);
+const value jobjectToValue(const JavaRuntime& jr, const jobject o);
+const jobjectArray valuesToJarray(const JavaRuntime& jr, const list<value>& v);
+const list<value> jarrayToValues(const JavaRuntime& jr, const jobjectArray o);
+const list<value> jiterableToValues(const JavaRuntime& jr, const jobject o);
+
+/**
+ * Convert a Java class name to a JNI class name.
+ */
+const bool jniClassNameHelper(char* to, const char* from) {
+ if (*from == '\0') {
+ *to = '\0';
+ return true;
+ }
+ *to = *from == '.'? '/' : *from;
+ return jniClassNameHelper(to + 1, from + 1);
+}
+
+const string jniClassName(const string& from) {
+ char buf[length(from) + 1];
+ jniClassNameHelper(buf, c_str(from));
+ return string(buf);
+}
+
+/**
+ * Create a new Java object representing a lambda expression.
+ */
+class javaLambda {
+public:
+ javaLambda(const JavaRuntime& jr, const value& iface, const lambda<value(const list<value>&)>& func) : jr(jr), iface(iface), func(func) {
+ }
+
+ const value operator()(const list<value>& expr) const {
+ if (isNil(expr))
+ return func(expr);
+ const value& op(car(expr));
+ if (op == "equals")
+ return value(cadr(expr) == this);
+ if (op == "hashCode")
+ return value((double)(long)this);
+ if (op == "toString") {
+ ostringstream os;
+ os << this;
+ return value(string("org.apache.tuscany.InvocationHandler@") + (c_str(str(os)) + 2));
+ }
+ return func(expr);
+ }
+
+ const JavaRuntime& jr;
+ const value iface;
+ const lambda<value(const list<value>&)> func;
+};
+
+/**
+ * Invocation handler invoke method, dispatches to the lambda function wrapped
+ * in the invocation handler.
+ */
+jobject JNICALL nativeInvoke(JNIEnv* env, jobject self, unused jobject proxy, jobject method, jobjectArray args) {
+
+ // Retrieve the lambda function from the invocation handler
+ jclass clazz = env->GetObjectClass(self);
+ jfieldID f = env->GetFieldID(clazz, "lambda", "J");
+ const javaLambda& jl = *(javaLambda*)(long)env->GetLongField(self, f);
+
+ // Retrieve the function name
+ const jstring s = (jstring)env->CallObjectMethod(method, jl.jr.methodName);
+ const char* c = env->GetStringUTFChars(s, NULL);
+ const value func(c);
+ env->ReleaseStringUTFChars(s, c);
+
+ // Build the expression to evaluate, either (func, args[0], args[1], args[2]...)
+ // or just args[0] for the special eval(...) function
+ const list<value> expr = func == "eval"? (list<value>)car<value>(jarrayToValues(jl.jr, args)) : cons<value>(func, jarrayToValues(jl.jr, args));
+ debug(expr, "java::nativeInvoke::expr");
+
+ // Invoke the lambda function
+ value result = jl(expr);
+ debug(result, "java::nativeInvoke::result");
+
+ // Convert result to a jobject
+ return valueToJobject(jl.jr, value(), result);
+}
+
+/**
+ * Convert a lambda function to Java proxy.
+ */
+const jobject mkJavaLambda(const JavaRuntime& jr, unused const value& iface, const lambda<value(const list<value>&)>& l) {
+ const gc_ptr<javaLambda> jl = new (gc_new<javaLambda>()) javaLambda(jr, iface, l);
+ jclass jc = (jclass)(long)(double)iface;
+ const jobject obj = jr.env->CallStaticObjectMethod(jr.invokerClass, jr.invokerValueOf, jc, (long)(javaLambda*)jl);
+ return obj;
+}
+
+/**
+ * Convert a list of values to a Java jobjectArray.
+ */
+const jobjectArray valuesToJarrayHelper(const JavaRuntime& jr, jobjectArray a, const list<value>& v, const int i) {
+ if (isNil(v))
+ return a;
+ jr.env->SetObjectArrayElement(a, i, valueToJobject(jr, value(), car(v)));
+ return valuesToJarrayHelper(jr, a, cdr(v), i + 1);
+}
+
+const jobjectArray valuesToJarray(const JavaRuntime& jr, const list<value>& v) {
+ jobjectArray a = jr.env->NewObjectArray(length(v), jr.objectClass, NULL);
+ return valuesToJarrayHelper(jr, a, v, 0);
+}
+
+/**
+ * Convert a Java jobjectArray to a Java iterable.
+ */
+const jobject jarrayToJiterable(const JavaRuntime& jr, jobjectArray a) {
+ return jr.env->CallStaticObjectMethod(jr.iterableClass, jr.iterableValueOf, a);
+}
+
+/**
+ * Convert a value to a Java jobject.
+ */
+const jobject valueToJobject(const JavaRuntime& jr, const value& jtype, const value& v) {
+ switch (type(v)) {
+ case value::List:
+ return jarrayToJiterable(jr, valuesToJarray(jr, v));
+ case value::Lambda:
+ return mkJavaLambda(jr, jtype, v);
+ case value::Symbol:
+ return jr.env->NewStringUTF(c_str(string("'") + v));
+ case value::String:
+ return jr.env->NewStringUTF(c_str(v));
+ case value::Number:
+ return jr.env->CallStaticObjectMethod(jr.doubleClass, jr.doubleValueOf, (double)v);
+ case value::Bool:
+ return jr.env->CallStaticObjectMethod(jr.booleanClass, jr.booleanValueOf, (bool)v);
+ default:
+ return NULL;
+ }
+}
+
+/**
+ * Convert a list of values to an array of jvalues.
+ */
+const jvalue* valuesToJvaluesHelper(const JavaRuntime& jr, jvalue* a, const list<value>& types, const list<value>& v) {
+ if (isNil(v))
+ return a;
+ a->l = valueToJobject(jr, car(types), car(v));
+ return valuesToJvaluesHelper(jr, a + 1, cdr(types), cdr(v));
+}
+
+const jvalue* valuesToJvalues(const JavaRuntime& jr, const list<value>& types, const list<value>& v) {
+ const int n = length(v);
+ jvalue* a = new (gc_anew<jvalue>(n)) jvalue[n];
+ valuesToJvaluesHelper(jr, a, types, v);
+ return a;
+}
+
+/**
+ * Convert a Java jobjectArray to a list of values.
+ */
+const list<value> jarrayToValuesHelper(const JavaRuntime& jr, jobjectArray a, const int i, const int size) {
+ if (i == size)
+ return list<value>();
+ return cons(jobjectToValue(jr, jr.env->GetObjectArrayElement(a, i)), jarrayToValuesHelper(jr, a, i + 1, size));
+}
+
+const list<value> jarrayToValues(const JavaRuntime& jr, jobjectArray o) {
+ if (o == NULL)
+ return list<value>();
+ return jarrayToValuesHelper(jr, o, 0, jr.env->GetArrayLength(o));
+}
+
+/**
+ * Convert a Java Iterable to a list of values.
+ */
+const list<value> jiterableToValuesHelper(const JavaRuntime& jr, jobject o) {
+ if ((bool)jr.env->CallStaticBooleanMethod(jr.iterableUtilClass, jr.iterableIsNil, o))
+ return list<value>();
+ jobject car = jr.env->CallStaticObjectMethod(jr.iterableUtilClass, jr.iterableCar, o);
+ jobject cdr = jr.env->CallStaticObjectMethod(jr.iterableUtilClass, jr.iterableCdr, o);
+ return cons(jobjectToValue(jr, car), jiterableToValuesHelper(jr, cdr));
+}
+
+const list<value> jiterableToValues(const JavaRuntime& jr, jobject o) {
+ if (o == NULL)
+ return list<value>();
+ return jiterableToValuesHelper(jr, o);
+}
+
+/**
+ * Lambda function used to represent a Java callable object.
+ */
+struct javaCallable {
+ const JavaRuntime& jr;
+ const jobject obj;
+
+ javaCallable(const JavaRuntime& jr, const jobject obj) : jr(jr), obj(obj) {
+ }
+
+ const value operator()(const list<value>& args) const {
+ jobjectArray jargs = valuesToJarray(jr, args);
+ jobject result = jargs; //CallObject(func, jargs);
+ return jobjectToValue(jr, result);
+ }
+};
+
+/**
+ * Convert a Java jobject to a value.
+ */
+const value jobjectToValue(const JavaRuntime& jr, const jobject o) {
+ if (o == NULL)
+ return value();
+ const jclass clazz = jr.env->GetObjectClass(o);
+ if ((jr.env->IsSameObject(clazz, jr.stringClass))) {
+ const char* s = jr.env->GetStringUTFChars((jstring)o, NULL);
+ if (*s == '\'') {
+ const value v(s + 1);
+ jr.env->ReleaseStringUTFChars((jstring)o, s);
+ return v;
+ }
+ const value v = string(s);
+ jr.env->ReleaseStringUTFChars((jstring)o, s);
+ return v;
+ }
+ if (jr.env->IsSameObject(clazz, jr.booleanClass))
+ return value((bool)jr.env->CallBooleanMethod(o, jr.booleanValue));
+ if (jr.env->IsSameObject(clazz, jr.doubleClass))
+ return value((double)jr.env->CallDoubleMethod(o, jr.doubleValue));
+ if (jr.env->IsAssignableFrom(clazz, jr.iterableClass))
+ return jiterableToValues(jr, o);
+ if (jr.env->IsAssignableFrom(clazz, jr.objectArrayClass))
+ return jarrayToValues(jr, (jobjectArray)o);
+ return lambda<value(const list<value>&)>(javaCallable(jr, o));
+}
+
+/**
+ * Returns a balanced tree of the methods of a class.
+ */
+const value parameterTypeToValue(const jobject t) {
+ return value((double)(long)t);
+}
+
+const list<value> parameterTypesToValues(const JavaRuntime& jr, const jobjectArray t, const int i) {
+ if (i == 0)
+ return list<value>();
+ return cons<value>(parameterTypeToValue(jr.env->GetObjectArrayElement(t, i - 1)), parameterTypesToValues(jr, t, i - 1));
+}
+
+const value methodToValue(const JavaRuntime& jr, const jobject m) {
+ const jobject s = jr.env->CallObjectMethod(m, jr.methodName);
+ const char* c = jr.env->GetStringUTFChars((jstring)s, NULL);
+ const string& name = string(c);
+ jr.env->ReleaseStringUTFChars((jstring)s, c);
+
+ const jmethodID mid = jr.env->FromReflectedMethod(m);
+
+ const jobjectArray t = (jobjectArray)jr.env->CallObjectMethod(m, jr.parameterTypes);
+ const list<value> types = reverse(parameterTypesToValues(jr, t, jr.env->GetArrayLength(t)));
+
+ return cons<value>(c_str(name), cons<value>((double)(long)mid, types));
+}
+
+const list<value> methodsToValues(const JavaRuntime& jr, const jobjectArray m, const int i) {
+ if (i == 0)
+ return list<value>();
+ return cons<value>(methodToValue(jr, jr.env->GetObjectArrayElement(m, i - 1)), methodsToValues(jr, m, i - 1));
+}
+
+const list<value> methodsToValues(const JavaRuntime& jr, const jclass clazz) {
+ const jobjectArray m = (jobjectArray)jr.env->CallObjectMethod(clazz, jr.declaredMethods);
+ return methodsToValues(jr, m, jr.env->GetArrayLength(m));
+}
+
+/**
+ * Represents a Java Class.
+ */
+class JavaClass {
+public:
+ JavaClass() : loader(NULL), clazz(NULL), obj(NULL) {
+ }
+ JavaClass(const jobject loader, const jclass clazz, const jobject obj, const list<value> m) : loader(loader), clazz(clazz), obj(obj), m(m) {
+ }
+
+ const jobject loader;
+ const jclass clazz;
+ const jobject obj;
+ const list<value> m;
+};
+
+/**
+ * Read a class.
+ */
+const failable<JavaClass> readClass(const JavaRuntime& jr, const string& path, const string& name) {
+
+ // Create a class loader from the given path
+ const jobject jpath = jr.env->NewStringUTF(c_str(path));
+ jobject loader = jr.env->CallStaticObjectMethod(jr.loaderClass, jr.loaderValueOf, jpath);
+
+ // Load the class
+ const jobject jname = jr.env->NewStringUTF(c_str(name));
+ const jclass clazz = (jclass)jr.env->CallStaticObjectMethod(jr.loaderClass, jr.loaderForName, jname, JNI_TRUE, loader);
+ if (clazz == NULL)
+ return mkfailure<JavaClass>(string("Couldn't load class: ") + name + " : " + lastException(jr));
+
+ // Create an instance
+ const jmethodID constr = jr.env->GetMethodID(clazz, "<init>", "()V");
+ if (constr == NULL)
+ return mkfailure<JavaClass>(string("Couldn't find constructor: ") + name + " : " + lastException(jr));
+ const jobject obj = jr.env->NewObject(clazz, constr);
+ if (obj == NULL)
+ return mkfailure<JavaClass>(string("Couldn't construct object: ") + name + " : " + lastException(jr));
+
+ return JavaClass(loader, clazz, obj, methodsToValues(jr, clazz));
+}
+
+/**
+ * Evaluate an expression against a Java class.
+ */
+const failable<value> evalClass(const JavaRuntime& jr, const value& expr, const JavaClass jc) {
+ debug(expr, "java::evalClass::expr");
+
+ // Lookup the Java function named as the expression operand
+ const list<value> func = assoc<value>(car<value>(expr), jc.m);
+ if (isNil(func)) {
+
+ // The start, stop, and restart functions are optional
+ const value fn = car<value>(expr);
+ if (fn == "start" || fn == "stop")
+ return value(lambda<value(const list<value>&)>());
+
+ return mkfailure<value>(string("Couldn't find function: ") + car<value>(expr) + " : " + lastException(jr));
+ }
+ const jmethodID fid = (jmethodID)(long)(double)cadr(func);
+
+ // Convert args to Java jvalues
+ const jvalue *args = valuesToJvalues(jr, cddr(func), cdr<value>(expr));
+
+ // Call the Java function
+ const jobject result = jr.env->CallObjectMethodA(jc.obj, fid, args);
+ if (result == NULL)
+ return mkfailure<value>(string("Function call failed: ") + car<value>(expr) + " : " + lastException(jr));
+
+ // Convert Java result to a value
+ const value v = jobjectToValue(jr, result);
+ debug(v, "java::evalClass::result");
+ return v;
+}
+
+}
+}
+#endif /* tuscany_java_eval_hpp */
diff --git a/sca-cpp/branches/cpp-contrib/modules/java/java-conf b/sca-cpp/branches/cpp-contrib/modules/java/java-conf
new file mode 100755
index 0000000000..fb02f7fe57
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/java/java-conf
@@ -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.
+
+# Generate a Java server conf
+here=`readlink -f $0`; here=`dirname $here`
+root=`readlink -f $1`
+
+cat >>$root/conf/httpd.conf <<EOF
+LoadModule mod_tuscany_eval $here/.libs/libmod_tuscany_java.so
+EOF
+
diff --git a/sca-cpp/branches/cpp-contrib/modules/java/java-shell.cpp b/sca-cpp/branches/cpp-contrib/modules/java/java-shell.cpp
new file mode 100644
index 0000000000..51df513990
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/java/java-shell.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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$ */
+
+/**
+ * Java evaluator shell, used for interactive testing of Java classes.
+ */
+
+#include <assert.h>
+#include "gc.hpp"
+#include "stream.hpp"
+#include "string.hpp"
+#include "driver.hpp"
+
+int main(const int argc, char** argv) {
+ tuscany::gc_scoped_pool pool;
+ if (argc != 2) {
+ tuscany::cerr << "Usage: java-shell <class name>" << tuscany::endl;
+ return 1;
+ }
+ tuscany::java::evalDriverRun(argv[1], tuscany::cin, tuscany::cout);
+ return 0;
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/java/java-test.cpp b/sca-cpp/branches/cpp-contrib/modules/java/java-test.cpp
new file mode 100644
index 0000000000..f811a4f58d
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/java/java-test.cpp
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test Java evaluator.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "driver.hpp"
+
+namespace tuscany {
+namespace java {
+
+bool testEvalExpr() {
+ gc_scoped_pool pool;
+ JavaRuntime javaRuntime;
+ {
+ const failable<JavaClass> obj = readClass(javaRuntime, ".", "test.CalcImpl");
+ assert(hasContent(obj));
+ const value exp = mklist<value>("mult", 2, 3);
+ const failable<value> r = evalClass(javaRuntime, exp, content(obj));
+ assert(hasContent(r));
+ assert(content(r) == value(6));
+ }
+ {
+ const failable<JavaClass> obj = readClass(javaRuntime, ".", "test.CalcImpl");
+ assert(hasContent(obj));
+ const value exp = mklist<value>("even", 2);
+ const failable<value> r = evalClass(javaRuntime, exp, content(obj));
+ assert(hasContent(r));
+ assert(content(r) == value(true));
+ }
+ {
+ const failable<JavaClass> obj = readClass(javaRuntime, ".", "test.AdderImpl");
+ assert(hasContent(obj));
+ const value exp = mklist<value>("add", 2, 3);
+ const failable<value> r = evalClass(javaRuntime, exp, content(obj));
+ assert(hasContent(r));
+ assert(content(r) == value(5));
+ }
+ {
+ const failable<JavaClass> obj = readClass(javaRuntime, ".", "test.CalcImpl");
+ assert(hasContent(obj));
+ const value exp = mklist<value>("square", mklist<value>(1, 2, 3));
+ const failable<value> r = evalClass(javaRuntime, exp, content(obj));
+ assert(hasContent(r));
+ assert(content(r) == mklist<value>(1, 4, 9));
+ }
+ return true;
+}
+
+const value add(const list<value>& args) {
+ assert(car(args) == "add");
+ const double x = cadr(args);
+ const double y = caddr(args);
+ return x + y;
+}
+
+bool testEvalLambda() {
+ gc_scoped_pool pool;
+ JavaRuntime javaRuntime;
+ {
+ const failable<JavaClass> obj = readClass(javaRuntime, ".", "test.CalcImpl");
+ assert(hasContent(obj));
+ const value tcel = mklist<value>("add", 3, 4, lambda<value(const list<value>&)>(add));
+ const failable<value> r = evalClass(javaRuntime, tcel, content(obj));
+ assert(hasContent(r));
+ assert(content(r) == value(7));
+ }
+ {
+ const failable<JavaClass> obj = readClass(javaRuntime, ".", "test.CalcImpl");
+ assert(hasContent(obj));
+ const value tcel = mklist<value>("addEval", 3, 4, lambda<value(const list<value>&)>(add));
+ const failable<value> r = evalClass(javaRuntime, tcel, content(obj));
+ assert(hasContent(r));
+ assert(content(r) == value(7));
+ }
+ return true;
+}
+
+bool testClassLoader() {
+ gc_scoped_pool pool;
+ JavaRuntime javaRuntime;
+ const failable<JavaClass> obj = readClass(javaRuntime, ".", "org.apache.tuscany.ClassLoader$Test");
+ assert(hasContent(obj));
+ const value exp = mklist<value>("testClassLoader");
+ const failable<value> r = evalClass(javaRuntime, exp, content(obj));
+ assert(hasContent(r));
+ assert(content(r) == value(true));
+ return true;
+}
+
+bool testIterableUtil() {
+ gc_scoped_pool pool;
+ JavaRuntime javaRuntime;
+ const failable<JavaClass> obj = readClass(javaRuntime, ".", "org.apache.tuscany.IterableUtil$Test");
+ assert(hasContent(obj));
+ const value exp = mklist<value>("testList");
+ const failable<value> r = evalClass(javaRuntime, exp, content(obj));
+ assert(hasContent(r));
+ assert(content(r) == value(true));
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::java::testEvalExpr();
+ tuscany::java::testEvalLambda();
+ tuscany::java::testClassLoader();
+ tuscany::java::testIterableUtil();
+
+ tuscany::cout << "OK" << tuscany::endl;
+ return 0;
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/java/mod-java.cpp b/sca-cpp/branches/cpp-contrib/modules/java/mod-java.cpp
new file mode 100644
index 0000000000..510f9574b0
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/java/mod-java.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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 Java component implementations.
+ */
+
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "../server/mod-cpp.hpp"
+#include "../server/mod-eval.hpp"
+#include "mod-java.hpp"
+
+namespace tuscany {
+namespace server {
+namespace modeval {
+
+/**
+ * Apply a lifecycle start or restart event.
+ */
+struct javaLifecycle {
+ javaLifecycle(java::JavaRuntime& jr) : jr(jr) {
+ }
+ const value operator()(const list<value>& params) const {
+ const value func = car(params);
+ if (func == "javaRuntime")
+ return (gc_ptr<value>)(value*)(void*)&jr;
+ return lambda<value(const list<value>&)>();
+ }
+ java::JavaRuntime& jr;
+};
+
+const value applyLifecycle(unused const list<value>& params) {
+
+ // Create a Java runtime
+ java::JavaRuntime& jr = *(new (gc_new<java::JavaRuntime>()) java::JavaRuntime());
+
+ // Return the function to invoke on subsequent events
+ return failable<value>(lambda<value(const list<value>&)>(javaLifecycle(jr)));
+}
+
+/**
+ * Evaluate a Java component implementation and convert it to an applicable
+ * lambda function.
+ */
+const failable<lambda<value(const list<value>&)> > evalImplementation(const string& path, const value& impl, const list<value>& px, const lambda<value(const list<value>&)>& lifecycle) {
+ const string itype(elementName(impl));
+ if (contains(itype, ".java")) {
+ const void* p = (gc_ptr<value>)lifecycle(mklist<value>("javaRuntime"));
+ return modjava::evalImplementation(path, impl, px, *(java::JavaRuntime*)p);
+ }
+ if (contains(itype, ".cpp"))
+ return modcpp::evalImplementation(path, impl, px);
+ return mkfailure<lambda<value(const list<value>&)> >(string("Unsupported implementation type: ") + itype);
+}
+
+}
+}
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/java/mod-java.hpp b/sca-cpp/branches/cpp-contrib/modules/java/mod-java.hpp
new file mode 100644
index 0000000000..e7da06e930
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/java/mod-java.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_modjava_hpp
+#define tuscany_modjava_hpp
+
+/**
+ * Evaluation functions used by mod-eval to evaluate Java
+ * component implementations.
+ */
+
+#include "string.hpp"
+#include "stream.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "eval.hpp"
+
+namespace tuscany {
+namespace server {
+namespace modjava {
+
+/**
+ * Apply a Java component implementation function.
+ */
+struct applyImplementation {
+ java::JavaClass impl;
+ const list<value> px;
+ java::JavaRuntime& jr;
+ applyImplementation(const java::JavaClass& impl, const list<value>& px, java::JavaRuntime& jr) : impl(impl), px(px), jr(jr) {
+ }
+ const value operator()(const list<value>& params) const {
+ const value expr = append<value>(params, px);
+ debug(expr, "modeval::java::applyImplementation::input");
+ const failable<value> res = java::evalClass(jr, expr, impl);
+ const value val = !hasContent(res)? mklist<value>(value(), reason(res)) : mklist<value>(content(res));
+ debug(val, "modeval::java::applyImplementation::result");
+ return val;
+ }
+};
+
+/**
+ * Evaluate a Java component implementation and convert it to an applicable
+ * lambda function.
+ */
+const failable<lambda<value(const list<value>&)> > evalImplementation(const string& path, const value& impl, const list<value>& px, java::JavaRuntime& jr) {
+ const string cn(attributeValue("class", impl));
+ const failable<java::JavaClass> jc = java::readClass(jr, path, cn);
+ if (!hasContent(jc))
+ return mkfailure<lambda<value(const list<value>&)> >(reason(jc));
+ return lambda<value(const list<value>&)>(applyImplementation(content(jc), px, jr));
+}
+
+}
+}
+}
+
+#endif /* tuscany_modjava_hpp */
diff --git a/sca-cpp/branches/cpp-contrib/modules/java/org/apache/tuscany/ClassLoader.java b/sca-cpp/branches/cpp-contrib/modules/java/org/apache/tuscany/ClassLoader.java
new file mode 100644
index 0000000000..7fb6519472
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/java/org/apache/tuscany/ClassLoader.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+/**
+ * Class loader used to load SCA component implementation classes.
+ */
+class ClassLoader extends URLClassLoader {
+
+ ClassLoader(final URL... urls) {
+ super(urls);
+ }
+
+ /**
+ * Create a class loader for an SCA contribution path.
+ */
+ static java.lang.ClassLoader valueOf(final String path) throws MalformedURLException {
+ return new ClassLoader(new File(path).toURI().toURL());
+ }
+
+ /**
+ * Load a class.
+ */
+ static Class<?> forName(final String name, final boolean resolve, final java.lang.ClassLoader loader) throws ClassNotFoundException {
+ return Class.forName(name, resolve, loader);
+ }
+
+ /**
+ * Test the class loader.
+ */
+ static class Test {
+ Boolean testClassLoader() {
+ try {
+ final Class<?> clazz = ClassLoader.forName("test.CalcImpl", true, ClassLoader.valueOf("."));
+ assert clazz != null;
+ } catch(final MalformedURLException e) {
+ throw new RuntimeException(e);
+ } catch(final ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ return true;
+ }
+ }
+
+ public static void main(final String[] args) {
+ System.out.println("Testing...");
+
+ Test.class.getClassLoader().setDefaultAssertionStatus(true);
+ new Test().testClassLoader();
+
+ System.out.println("OK");
+ }
+
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/java/org/apache/tuscany/InvocationHandler.java b/sca-cpp/branches/cpp-contrib/modules/java/org/apache/tuscany/InvocationHandler.java
new file mode 100644
index 0000000000..c5ec6005a1
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/java/org/apache/tuscany/InvocationHandler.java
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+package org.apache.tuscany;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/**
+ * Proxy Invocation handler used to represent SCA component references.
+ */
+class InvocationHandler implements java.lang.reflect.InvocationHandler {
+ final long lambda;
+
+ InvocationHandler(final long lambda) {
+ this.lambda = lambda;
+ }
+
+ /**
+ * Create a proxy for an interface and the lambda function representing
+ * an SCA component reference.
+ */
+ static Object valueOf(final Class<?> iface, final long lambda) {
+ return Proxy.newProxyInstance(iface.getClassLoader(), new Class[]{iface}, new InvocationHandler(lambda));
+ }
+
+ /**
+ * Proxy invocation of a C++ function.
+ */
+ @Override
+ public native Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable;
+
+ /**
+ * Return the stack trace of an exception.
+ */
+ static String stackTrace(final Throwable e) {
+ final StringWriter sw = new StringWriter();
+ final PrintWriter pw = new PrintWriter(sw);
+ e.printStackTrace(pw);
+ return sw.toString();
+ }
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/java/org/apache/tuscany/IterableUtil.java b/sca-cpp/branches/cpp-contrib/modules/java/org/apache/tuscany/IterableUtil.java
new file mode 100644
index 0000000000..85872cba72
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/java/org/apache/tuscany/IterableUtil.java
@@ -0,0 +1,378 @@
+/*
+ * 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.
+ */
+
+package org.apache.tuscany;
+
+import static java.util.Arrays.*;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Utility functions to help work efficiently with iterable lists, inspired from Lisp.
+ */
+public class IterableUtil {
+
+ /**
+ * Convert an array or a variable list of arguments to an iterable list.
+ */
+ public static <T> Iterable<T> list(final Object... a) {
+ return new ArrayIterable<T>(a, 0);
+ }
+
+ /**
+ * Convert an iterable list to a java.util.Collection.
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> Collection<T> collection(final Object l) {
+ final Collection<T> c = new ArrayList<T>();
+ for(final Object x : (Iterable<?>)l)
+ c.add((T)x);
+ return c;
+ }
+
+ /**
+ * Construct a new list from an element and a list.
+ */
+ public static <T> Iterable<T> cons(final Object car, final Iterable<?> cdr) {
+ return new PairIterable<T>(car, cdr);
+ }
+
+ /**
+ * Return true if a list is nil (empty).
+ */
+ public static boolean isNil(final Object l) {
+ if(l instanceof BasicIterable<?>)
+ return ((BasicIterable<?>)l).isNil();
+ if(l instanceof Collection<?>)
+ return ((Collection<?>)l).isEmpty();
+ return !((Iterable<?>)l).iterator().hasNext();
+ }
+
+ /**
+ * Return the car (first element) of a list.
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> T car(final Object l) {
+ if(l instanceof BasicIterable<?>)
+ return ((BasicIterable<T>)l).car();
+ if(l instanceof List<?>)
+ return (T)((List<?>)l).get(0);
+ return (T)((Iterable<?>)l).iterator().next();
+ }
+
+ /**
+ * Return the cdr (rest after the first element) of a list.
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> Iterable<T> cdr(final Object l) {
+ if(l instanceof BasicIterable<?>)
+ return ((BasicIterable<T>)l).cdr();
+ if(l instanceof List<?>)
+ return new ListIterable<T>((List<?>)l, 1);
+ if(l instanceof Collection<?>)
+ return new ArrayIterable<T>(((Collection<?>)l).toArray(), 1);
+ return new Iterable<T>() {
+ @Override
+ public Iterator<T> iterator() {
+ final Iterator<T> i = ((Iterable<T>)l).iterator();
+ i.next();
+ return i;
+ }
+ };
+ }
+
+ /**
+ * Return the car of the cdr of a list.
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> T cadr(final Object l) {
+ return (T)car(cdr(l));
+ }
+
+ /**
+ * Return the cdr of the cdr of a list.
+ */
+ public static <T> Iterable<T> cddr(final Object l) {
+ return cdr(cdr(l));
+ }
+
+ /**
+ * Return the car of the cdr of the cdr of a list.
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> T caddr(final Object l) {
+ return (T)car(cddr(l));
+ }
+
+ /**
+ * Return the first pair matching a key from a list of key value pairs.
+ */
+ public static <T> Iterable<T> assoc(final Object k, final Object l) {
+ if(isNil(l))
+ return list();
+ if(k.equals(car(car(l))))
+ return car(l);
+ return assoc(k, cdr(l));
+ }
+
+ /**
+ * Internal base implementation class for iterable and immutable lists.
+ */
+ static abstract class BasicIterable<T> extends AbstractList<T> {
+ abstract T car();
+
+ abstract Iterable<T> cdr();
+
+ abstract Boolean isNil();
+
+ @Override
+ public int size() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public T get(final int index) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Internal implementation of a list backed by an array.
+ */
+ static class ArrayIterable<T> extends BasicIterable<T> {
+ final Object[] a;
+ final int start;
+
+ ArrayIterable(final Object[] a, final int start) {
+ this.a = a;
+ this.start = start;
+ }
+
+ @Override
+ Boolean isNil() {
+ return this.a.length - this.start == 0;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ T car() {
+ return (T)this.a[this.start];
+ }
+
+ @Override
+ BasicIterable<T> cdr() {
+ return new ArrayIterable<T>(this.a, this.start + 1);
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ return new Iterator<T>() {
+ int i = ArrayIterable.this.start;
+
+ @Override
+ public boolean hasNext() {
+ return this.i < ArrayIterable.this.a.length;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T next() {
+ return (T)ArrayIterable.this.a[this.i++];
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+ }
+
+ /**
+ * Internal implementation of a list backed by a java.util.List.
+ */
+ static class ListIterable<T> extends BasicIterable<T> {
+ final List<?> l;
+ final int start;
+
+ ListIterable(final List<?> l, final int start) {
+ this.l = l;
+ this.start = start;
+ }
+
+ @Override
+ Boolean isNil() {
+ return this.l.size() - this.start == 0;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ T car() {
+ return (T)this.l.get(this.start);
+ }
+
+ @Override
+ BasicIterable<T> cdr() {
+ return new ListIterable<T>(this.l, this.start + 1);
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ return new Iterator<T>() {
+ int i = ListIterable.this.start;
+
+ @Override
+ public boolean hasNext() {
+ return this.i < ListIterable.this.l.size();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T next() {
+ return (T)ListIterable.this.l.get(this.i++);
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+ }
+
+ /**
+ * Internal implementation of a list backed by an element / iterable pair.
+ */
+ static class PairIterable<T> extends BasicIterable<T> {
+ final Object car;
+ final Iterable<?> cdr;
+
+ PairIterable(final Object car, final Iterable<?> cdr) {
+ this.car = car;
+ this.cdr = cdr;
+ }
+
+ @Override
+ Boolean isNil() {
+ return false;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ T car() {
+ return (T)this.car;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ Iterable<T> cdr() {
+ return (Iterable<T>)this.cdr;
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ return new Iterator<T>() {
+ boolean carIterator = true;
+ Iterator<?> cdrIterator = PairIterable.this.cdr.iterator();
+
+ @Override
+ public boolean hasNext() {
+ if(this.carIterator)
+ return true;
+ return this.cdrIterator.hasNext();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T next() {
+ if(this.carIterator) {
+ this.carIterator = false;
+ return (T)PairIterable.this.car;
+ }
+ return (T)this.cdrIterator.next();
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+ }
+
+ /**
+ * Test the list functions.
+ */
+ static class Test {
+ Boolean testList() {
+ final Iterable<Object> l = list(2, 3, 4);
+ assert car(l) == Integer.valueOf(2);
+ assert cadr(l) == Integer.valueOf(3);
+ assert caddr(l) == Integer.valueOf(4);
+
+ final Iterable<Object> c = cons(0, cons(1, l));
+ assert car(c) == Integer.valueOf(0);
+ assert cadr(c) == Integer.valueOf(1);
+ assert caddr(c) == Integer.valueOf(2);
+ assert c.toString().equals("[0, 1, 2, 3, 4]");
+
+ final Iterable<Object> cl = cons(0, cons(1, new ArrayList<Object>(asList(2, 3, 4))));
+ assert car(cl) == Integer.valueOf(0);
+ assert cadr(cl) == Integer.valueOf(1);
+ assert caddr(cl) == Integer.valueOf(2);
+ assert cl.toString().equals("[0, 1, 2, 3, 4]");
+
+ final List<Object> jl = new ArrayList<Object>(collection(cl));
+ assert jl.size() == 5;
+ assert jl.get(0) == Integer.valueOf(0);
+ assert jl.get(1) == Integer.valueOf(1);
+ assert jl.get(2) == Integer.valueOf(2);
+
+ final Iterable<Object> n = list();
+ assert isNil(n);
+ assert n.toString().equals("[]");
+
+ final Iterable<Object> cn = cons(0, n);
+ assert !isNil(cn);
+ assert isNil(cdr(cn));
+ assert cn.toString().equals("[0]");
+
+ final Iterable<Object> al = new ArrayList<Object>(Arrays.asList(1, 2, 3));
+ assert car(al) == Integer.valueOf(1);
+ assert cadr(al) == Integer.valueOf(2);
+ assert caddr(al) == Integer.valueOf(3);
+ return true;
+ }
+ }
+
+ public static void main(final String[] args) {
+ System.out.println("Testing...");
+
+ Test.class.getClassLoader().setDefaultAssertionStatus(true);
+ new Test().testList();
+
+ System.out.println("OK");
+ }
+
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/java/org/apache/tuscany/Service.java b/sca-cpp/branches/cpp-contrib/modules/java/org/apache/tuscany/Service.java
new file mode 100644
index 0000000000..a00d5b1b53
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/java/org/apache/tuscany/Service.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+package org.apache.tuscany;
+
+/**
+ * Interface used to represent SCA component references providing both REST
+ * access to a resource and function application.
+ */
+public interface Service {
+
+ /**
+ * Post a new item to a collection of items.
+ */
+ Iterable<String> post(Iterable<String> collection, Iterable<?> item);
+
+ /**
+ * Return an item.
+ */
+ Iterable<?> get(Iterable<String> id);
+
+ /**
+ * Update an item.
+ */
+ boolean put(Iterable<String> id, Iterable<?> item);
+
+ /**
+ * Delete an item.
+ */
+ boolean delete(Iterable<String> id);
+
+ /**
+ * Evaluate an expression.
+ */
+ <T> T eval(Object... params);
+
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/java/server-test b/sca-cpp/branches/cpp-contrib/modules/java/server-test
new file mode 100755
index 0000000000..fbd12e5542
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/java/server-test
@@ -0,0 +1,41 @@
+#!/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
+../http/httpd-conf tmp 8090 ../server/htdocs
+../server/server-conf tmp
+./java-conf tmp
+cat >>tmp/conf/httpd.conf <<EOF
+SCAContribution `pwd`/
+SCAComposite domain-test.composite
+EOF
+
+export CLASSPATH="`pwd`/libmod-tuscany-java-1.0.jar:`pwd`"
+
+../http/httpd-start tmp
+sleep 2
+
+# Test
+./client-test 2>/dev/null
+rc=$?
+
+# Cleanup
+../http/httpd-stop tmp
+sleep 2
+return $rc
diff --git a/sca-cpp/branches/cpp-contrib/modules/java/test/Adder.java b/sca-cpp/branches/cpp-contrib/modules/java/test/Adder.java
new file mode 100644
index 0000000000..7236548c41
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/java/test/Adder.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+package test;
+
+public interface Adder {
+
+ Double add(Double x, Double y);
+
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/java/test/AdderImpl.java b/sca-cpp/branches/cpp-contrib/modules/java/test/AdderImpl.java
new file mode 100644
index 0000000000..e607012b78
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/java/test/AdderImpl.java
@@ -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.
+ */
+
+package test;
+
+public class AdderImpl {
+
+ public Double add(Double x, Double y) {
+ return x + y;
+ }
+
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/java/test/CalcImpl.java b/sca-cpp/branches/cpp-contrib/modules/java/test/CalcImpl.java
new file mode 100644
index 0000000000..5bea01a43f
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/java/test/CalcImpl.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+package test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.tuscany.Service;
+
+public class CalcImpl {
+
+ public Double add(final Double x, final Double y, final Adder adder) {
+ return adder.add(x, y);
+ }
+
+ public Double addEval(final Double x, final Double y, final Service adder) {
+ return adder.eval("add", x, y);
+ }
+
+ public Double mult(final Double x, final Double y) {
+ return x * y;
+ }
+
+ public Boolean even(final Double x) {
+ return (double)((int)(double)x / 2 * 2) == (double)x;
+ }
+
+ public Iterable<Double> square(final Iterable<Double> l) {
+ final List<Double> r = new ArrayList<Double>();
+ for(final Double x : l)
+ r.add(x * x);
+ return r;
+ }
+
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/java/test/Client.java b/sca-cpp/branches/cpp-contrib/modules/java/test/Client.java
new file mode 100644
index 0000000000..c3bd875fcc
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/java/test/Client.java
@@ -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.
+ */
+
+package test;
+
+public interface Client {
+
+ String echo(String x);
+
+ Iterable<?> get(Iterable<String> id);
+
+ Iterable<String> post(Iterable<String> collection, Iterable<?> item);
+
+ Boolean put(Iterable<String> id, Iterable<?> item);
+
+ Boolean delete(Iterable<String> id);
+
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/java/test/ClientImpl.java b/sca-cpp/branches/cpp-contrib/modules/java/test/ClientImpl.java
new file mode 100644
index 0000000000..ade2ba302e
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/java/test/ClientImpl.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+package test;
+
+public class ClientImpl {
+
+ public String echo(String x, Server server) {
+ return server.echo(x);
+ }
+
+ public Iterable<?> get(Iterable<String> id, Server server) {
+ return server.get(id);
+ }
+
+ public Iterable<String> post(Iterable<String> collection, Iterable<?> item, Server server) {
+ return server.post(collection, item);
+ }
+
+ public Boolean put(Iterable<String> id, Iterable<?> item, Server server) {
+ return server.put(id, item);
+ }
+
+ public Boolean delete(Iterable<String> id, Server server) {
+ return server.delete(id);
+ }
+
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/java/test/Server.java b/sca-cpp/branches/cpp-contrib/modules/java/test/Server.java
new file mode 100644
index 0000000000..3dfe3c84ef
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/java/test/Server.java
@@ -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.
+ */
+
+package test;
+
+public interface Server {
+
+ String echo(String x);
+
+ Iterable<?> get(Iterable<String> id);
+
+ Iterable<String> post(Iterable<String> collection, Iterable<?> item);
+
+ Boolean put(Iterable<String> id, Iterable<?> item);
+
+ Boolean delete(Iterable<String> id);
+
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/java/test/ServerImpl.java b/sca-cpp/branches/cpp-contrib/modules/java/test/ServerImpl.java
new file mode 100644
index 0000000000..05012c22cf
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/java/test/ServerImpl.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package test;
+
+import static org.apache.tuscany.IterableUtil.*;
+
+public class ServerImpl {
+
+ public String echo(final String x) {
+ return x;
+ }
+
+ public Iterable<?> get(final Iterable<String> id) {
+ if (isNil(id))
+ return list("Sample Feed", "123456789",
+ list("Item", "111", list(list("'javaClass", "services.Item"), list("'name", "Apple"), list("'currencyCode", "USD"), list("'currencySymbol", "$"), list("'price", 2.99))),
+ list("Item", "222", list(list("'javaClass", "services.Item"), list("'name", "Orange"), list("'currencyCode", "USD"), list("'currencySymbol", "$"), list("'price", 3.55))),
+ list("Item", "333", list(list("'javaClass", "services.Item"), list("'name", "Pear"), list("'currencyCode", "USD"), list("'currencySymbol", "$"), list("'price", 1.55))));
+ final Iterable<?> entry = list(list("'javaClass", "services.Item"), list("'name", "Apple"), list("'currencyCode", "USD"), list("'currencySymbol", "$"), list("'price", 2.99));
+ return list("Item", car(id), entry);
+ }
+
+ public Iterable<String> post(final Iterable<String> collection, final Iterable<?> item) {
+ return list("123456789");
+ }
+
+ public Boolean put(final Iterable<String> id, final Iterable<?> item) {
+ return true;
+ }
+
+ public Boolean delete(final Iterable<String> id) {
+ return true;
+ }
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/java/wiring-test b/sca-cpp/branches/cpp-contrib/modules/java/wiring-test
new file mode 100755
index 0000000000..c0b6cbfd62
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/java/wiring-test
@@ -0,0 +1,78 @@
+#!/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
+../http/httpd-conf tmp 8090 ../server/htdocs
+../server/server-conf tmp
+./java-conf tmp
+cat >>tmp/conf/httpd.conf <<EOF
+SCAContribution `pwd`/
+SCAComposite domain-test.composite
+EOF
+
+export CLASSPATH="`pwd`/libmod-tuscany-java-1.0.jar:`pwd`"
+
+../http/httpd-start tmp
+sleep 2
+
+# Test HTTP GET
+curl http://localhost:8090/index.html 2>/dev/null >tmp/index.html
+diff tmp/index.html ../server/htdocs/index.html
+rc=$?
+
+# Test ATOMPub
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/client/ >tmp/feed.xml 2>/dev/null
+ diff tmp/feed.xml ../server/htdocs/feed.xml
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/client/111 >tmp/entry.xml 2>/dev/null
+ diff tmp/entry.xml ../server/htdocs/entry.xml
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/client/ -X POST -H "Content-type: application/atom+xml" --data @../server/htdocs/entry.xml 2>/dev/null
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/client/111 -X PUT -H "Content-type: application/atom+xml" --data @../server/htdocs/entry.xml 2>/dev/null
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/client/111 -X DELETE 2>/dev/null
+ rc=$?
+fi
+
+# Test JSON-RPC
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/client/ -X POST -H "Content-type: application/json-rpc" --data @../server/htdocs/json-request.txt >tmp/json-result.txt 2>/dev/null
+ diff tmp/json-result.txt ../server/htdocs/json-result.txt
+ rc=$?
+fi
+
+# Cleanup
+../http/httpd-stop tmp
+sleep 2
+if [ "$rc" = "0" ]; then
+ echo "OK"
+fi
+return $rc
diff --git a/sca-cpp/branches/cpp-contrib/modules/json/Makefile.am b/sca-cpp/branches/cpp-contrib/modules/json/Makefile.am
new file mode 100644
index 0000000000..5bd435fe99
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/json/Makefile.am
@@ -0,0 +1,23 @@
+# 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
+
+json_test_SOURCES = json-test.cpp
+json_test_LDFLAGS = -lmozjs
+
+TESTS = json-test
diff --git a/sca-cpp/branches/cpp-contrib/modules/json/json-test.cpp b/sca-cpp/branches/cpp-contrib/modules/json/json-test.cpp
new file mode 100644
index 0000000000..b74f068710
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/json/json-test.cpp
@@ -0,0 +1,176 @@
+/*
+ * 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 "stream.hpp"
+#include "string.hpp"
+#include "json.hpp"
+
+namespace tuscany {
+namespace json {
+
+bool testJSEval() {
+ JSONContext cx;
+ const string script("(function testJSON(n){ return JSON.parse(JSON.stringify(n)) })(5)");
+ jsval rval;
+ assert(JS_EvaluateScript(cx, cx.getGlobal(), c_str(script), length(script), "testJSON.js", 1, &rval));
+ const string r(JS_GetStringBytes(JS_ValueToString(cx, rval)));
+ assert(r == "5");
+ return true;
+}
+
+ostream* jsonWriter(const string& s, ostream* os) {
+ (*os) << s;
+ return os;
+}
+
+bool testJSON() {
+ const JSONContext cx;
+
+ {
+ const list<value> ad = mklist<value>(mklist<value>(attribute, "city", string("san francisco")), mklist<value>(attribute, "state", string("ca")));
+ const list<value> ac = mklist<value>(mklist<value>(element, "id", string("1234")), mklist<value>(attribute, "balance", 1000));
+ const list<value> cr = mklist<value>(mklist<value> (attribute, "name", 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)));
+
+ ostringstream os;
+ writeJSON<ostream*>(jsonWriter, &os, c, cx);
+ assert(str(os) == "{\"customer\":{\"@name\":\"jdoe\",\"address\":{\"@city\":\"san francisco\",\"@state\":\"ca\"},\"account\":{\"id\":\"1234\",\"@balance\":1000}}}");
+ }
+ {
+ const list<value> phones = mklist<value> (string("408-1234"), string("650-1234"));
+ const list<value> l = mklist<value> (mklist<value> (element, "phones", phones), mklist<value> (element, "lastName", string("test\ttab")), mklist<value> (attribute, "firstName", string("test1")));
+
+ ostringstream os;
+ writeJSON<ostream*>(jsonWriter, &os, l, cx);
+ assert(str(os) == "{\"phones\":[\"408-1234\",\"650-1234\"],\"lastName\":\"test\\u0009tab\",\"@firstName\":\"test1\"}");
+
+ istringstream is(str(os));
+ const list<string> il = streamList(is);
+ const list<value> r = content(readJSON(il, cx));
+ assert(r == l);
+
+ ostringstream wos;
+ write(content(writeJSON(r, cx)), wos);
+ assert(str(wos) == str(os));
+ }
+ {
+ const list<value> l = mklist<value>(list<value>() + "ns1:echoString" + (list<value>() + "@xmlns:ns1" + string("http://ws.apache.org/axis2/services/echo")) + (list<value>() + "text" + string("Hello World!")));
+ ostringstream wos;
+ write(content(writeJSON(valuesToElements(l), cx)), wos);
+ assert(str(wos) == "{\"ns1:echoString\":{\"@xmlns:ns1\":\"http://ws.apache.org/axis2/services/echo\",\"text\":\"Hello World!\"}}");
+
+ istringstream is(str(wos));
+ const list<string> il = streamList(is);
+ const list<value> r = elementsToValues(content(readJSON(il, cx)));
+ assert(r == l);
+ }
+ return true;
+}
+
+bool testJSONRPC() {
+ JSONContext cx;
+ {
+ const string lm("{\"id\": 1, \"method\": \"system.listMethods\", \"params\": []}");
+ const list<value> e = content(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", string("system.listMethods")));
+ assert(assoc<value>("params", v) == mklist<value>("params", list<value>()));
+ }
+ {
+ const string i("{\"id\":3,\"result\":[{\"price\":\"$2.99\",\"name\":\"Apple\"},{\"price\":\"$3.55\",\"name\":\"Orange\"},{\"price\":\"$1.55\",\"name\":\"Pear\"}]}");
+ const list<value> e = content(readJSON(mklist(i), cx));
+ const 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 = content(readJSON(mklist(i), cx));
+ assert(e == e2);
+ }
+ {
+ const string i("{\"id\":3,\"result\":[{\"price\":\"$2.99\",\"name\":\"Apple\"},{\"price\":\"$3.55\",\"name\":\"Orange\"},{\"price\":\"$1.55\",\"name\":\"Pear\"}]}");
+ const list<value> e = content(readJSON(mklist(i), cx));
+ ostringstream os;
+ write(content(writeJSON(e, cx)), os);
+ assert(str(os) == 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>(string("Service.get"), string("Service.getTotal"))));
+ const list<value> e = valuesToElements(r);
+ ostringstream os;
+ write(content(writeJSON(e, cx)), os);
+ assert(str(os) == "{\"id\":1,\"result\":[\"Service.get\",\"Service.getTotal\"]}");
+ }
+ {
+ const 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 = content(readJSON(mklist(f), cx));
+ const list<value> v = elementsToValues(r);
+ const list<value> e = valuesToElements(v);
+ ostringstream os;
+ write(content(writeJSON(e, cx)), os);
+ assert(str(os) == f);
+ }
+ {
+ const list<value> arg = mklist<value>(list<value>() + "ns1:echoString" + (list<value>() + "@xmlns:ns1" + string("http://ws.apache.org/axis2/services/echo")) + (list<value>() + "text" + string("Hello World!")));
+ const failable<list<string> > r = jsonRequest(1, "echo", mklist<value>(arg), cx);
+ ostringstream os;
+ write(content(r), os);
+ assert(str(os) == "{\"id\":1,\"method\":\"echo\",\"params\":[{\"ns1:echoString\":{\"@xmlns:ns1\":\"http://ws.apache.org/axis2/services/echo\",\"text\":\"Hello World!\"}}]}");
+
+ istringstream is(str(os));
+ const list<string> il = streamList(is);
+ const list<value> ir = elementsToValues(content(readJSON(il, cx)));
+ assert(car<value>(cadr<value>(caddr<value>(ir))) == arg);
+ }
+ {
+ const list<value> res = mklist<value>(list<value>() + "ns1:echoString" + (list<value>() + "@xmlns:ns1" + string("http://ws.apache.org/axis2/c/samples")) + (list<value>() + "text" + string("Hello World!")));
+ const failable<list<string> > r = jsonResult(1, res, cx);
+ ostringstream os;
+ write(content(r), os);
+ assert(str(os) == "{\"id\":1,\"result\":{\"ns1:echoString\":{\"@xmlns:ns1\":\"http://ws.apache.org/axis2/c/samples\",\"text\":\"Hello World!\"}}}");
+
+ istringstream is(str(os));
+ const list<string> il = streamList(is);
+ const list<value> ir = elementsToValues(content(readJSON(il, cx)));
+ assert(cdr<value>(cadr<value>(ir)) == res);
+ }
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::json::testJSEval();
+ tuscany::json::testJSON();
+ tuscany::json::testJSONRPC();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/json/json.hpp b/sca-cpp/branches/cpp-contrib/modules/json/json.hpp
new file mode 100644
index 0000000000..1d966d3f67
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/json/json.hpp
@@ -0,0 +1,403 @@
+/*
+ * 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.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "element.hpp"
+#include "monad.hpp"
+
+namespace tuscany {
+namespace json {
+
+/**
+ * Report JSON errors.
+ */
+void reportError(unused JSContext *cx, const char *message, JSErrorReport *report) {
+ cerr << (const char*)(report->filename? report->filename : "<no filename>") << ":"
+ << (int)report->lineno << ":" << message << endl;
+}
+
+/**
+ * Encapsulates a JavaScript runtime. Shared by multiple threads in
+ * a process.
+ */
+class JSONRuntime {
+public:
+ JSONRuntime() {
+ // Create JS runtime
+ rt = JS_NewRuntime(8L * 1024L * 1024L);
+ if(rt == NULL)
+ cleanup();
+ }
+
+ operator JSRuntime*() const {
+ return rt;
+ }
+private:
+ bool cleanup() {
+ if(rt != NULL) {
+ JS_DestroyRuntime(rt);
+ rt = NULL;
+ }
+ JS_ShutDown();
+ return true;
+ }
+
+ JSRuntime* rt;
+} 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;
+};
+
+/**
+ * Returns true if a list represents a JS array.
+ */
+const bool isJSArray(const list<value>& l) {
+ if(isNil(l))
+ return true;
+ const value v = car(l);
+ if (isSymbol(v))
+ return false;
+ if(isList(v)) {
+ if(!isNil((list<value>)v) && isSymbol(car<value>(v)))
+ return false;
+ }
+ return true;
+}
+
+/**
+ * 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 string name = JS_GetStringBytes(JSVAL_TO_STRING(idv));
+ if (substr(name, 0, 1) == atsign)
+ return jsPropertiesToValues(cons<value>(mklist<value>(attribute, c_str(substr(name, 1)), val), propertiesSoFar), o, i, cx);
+ if (isList(val) && !isJSArray(val))
+ return jsPropertiesToValues(cons<value>(cons<value>(element, cons<value>(c_str(name), list<value>(val))), propertiesSoFar), o, i, cx);
+ return jsPropertiesToValues(cons<value> (mklist<value> (element, c_str(name), 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(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> consume(JSONParser* parser, const list<string>& ilist, const JSONContext& cx) {
+ if (isNil(ilist))
+ return true;
+ JSString* jstr = JS_NewStringCopyZ(cx, c_str(car(ilist)));
+ if(!JS_ConsumeJSONText(cx, parser, JS_GetStringChars(jstr), JS_GetStringLength(jstr)))
+ return mkfailure<bool>("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> > readJSON(const list<string>& ilist, const JSONContext& cx) {
+ jsval val;
+ JSONParser* parser = JS_BeginJSONParse(cx, &val);
+ if(parser == NULL)
+ return mkfailure<list<value> >("JS_BeginJSONParse failed");
+
+ const failable<bool> consumed = consume(parser, ilist, cx);
+
+ if(!JS_FinishJSONParse(cx, parser, JSVAL_NULL))
+ return mkfailure<list<value> >("JS_FinishJSONParse failed");
+ if(!hasContent(consumed))
+ return mkfailure<list<value> >(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);
+}
+
+/**
+ * Converts a value to a JS val.
+ */
+const jsval valueToJSVal(const value& val, const JSONContext& cx) {
+ JSObject* valuesToJSProperties(JSObject* o, const list<value>& l, const JSONContext& cx);
+
+ switch(type(val)) {
+ case value::String:
+ case value::Symbol: {
+ return STRING_TO_JSVAL(JS_NewStringCopyZ(cx, c_str((string)val)));
+ }
+ 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;
+ }
+ }
+}
+
+/**
+ * Converts a list of values to JS properties.
+ */
+JSObject* valuesToJSProperties(JSObject* o, const list<value>& l, const JSONContext& cx) {
+ if (isNil(l))
+ return o;
+
+ // Write an attribute
+ const value token(car(l));
+
+ if (isTaggedList(token, attribute)) {
+ jsval pv = valueToJSVal(attributeValue(token), cx);
+ JS_SetProperty(cx, o, c_str(atsign + string(attributeName(token))), &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, c_str(string(elementName(token))), &pv);
+
+ } else {
+
+ // Write a parent element
+ JSObject* child = JS_NewObject(cx, NULL, NULL, NULL);
+ jsval pv = OBJECT_TO_JSVAL(child);
+ JS_SetProperty(cx, o, c_str(string(elementName(token))), &pv);
+
+ // Write its children
+ valuesToJSProperties(child, elementChildren(token), cx);
+ }
+ }
+
+ // Go on
+ return valuesToJSProperties(o, cdr(l), cx);
+}
+
+/**
+ * Context passed to the JSON write callback function.
+ */
+template<typename R> class WriteContext {
+public:
+ WriteContext(const lambda<R(const string&, const R)>& reduce, const R& accum, const JSONContext& cx) : cx(cx), reduce(reduce), accum(accum) {
+ }
+ const JSONContext& cx;
+ const lambda<R(const string&, const 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(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> writeJSON(const lambda<R(const string&, const R)>& reduce, const R& initial, const list<value>& l, const JSONContext& cx) {
+ jsval val = OBJECT_TO_JSVAL(valuesToJSProperties(JS_NewObject(cx, NULL, NULL, NULL), l, cx));
+
+ WriteContext<R> wcx(reduce, initial, cx);
+ if (!JS_Stringify(cx, &val, NULL, JSVAL_NULL, writeCallback<R>, &wcx))
+ return mkfailure<R>("JS_Stringify failed");
+ return wcx.accum;
+}
+
+/**
+ * Convert a list of values to a list of strings representing a JSON document.
+ */
+const failable<list<string> > writeJSON(const list<value>& l, const JSONContext& cx) {
+ const failable<list<string> > ls = writeJSON<list<string>>(rcons<string>, list<string>(), l, cx);
+ if (!hasContent(ls))
+ return ls;
+ return reverse(list<string>(content(ls)));
+}
+
+/**
+ * Convert a list of function + params to a JSON-RPC request.
+ */
+const failable<list<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", string(func)), mklist<value>("params", params));
+ return writeJSON(valuesToElements(r), cx);
+}
+
+/**
+ * Convert a value to a JSON-RPC result.
+ */
+const failable<list<string> > jsonResult(const value& id, const value& val, JSONContext& cx) {
+ return writeJSON(valuesToElements(mklist<value>(mklist<value>("id", id), mklist<value>("result", val))), cx);
+}
+
+/**
+ * Convert a JSON-RPC result to a value.
+ */
+const failable<value> jsonResultValue(const list<string>& s, JSONContext& cx) {
+ const failable<list<value> > jsres = json::readJSON(s, cx);
+ if (!hasContent(jsres))
+ return mkfailure<value>(reason(jsres));
+ const list<value> rval(cadr<value>(elementsToValues(content(jsres))));
+ const value val = cadr(rval);
+ if (isList(val) && !isJSArray(val))
+ return value(mklist<value>(val));
+ return val;
+}
+
+/**
+ * Return a portable function name from a JSON-RPC function name.
+ * Strip the "system." and "Service." prefixes added by some JSON-RPC clients.
+ */
+const string funcName(const string& f) {
+ if (length(f) > 7 && find(f, "system.", 0) == 0)
+ return c_str(f) + 7;
+ if (length(f) > 8 && find(f, "Service.", 0) == 0)
+ return c_str(f) + 8;
+ return f;
+}
+
+}
+}
+
+#endif /* tuscany_json_hpp */
diff --git a/sca-cpp/branches/cpp-contrib/modules/python/Makefile.am b/sca-cpp/branches/cpp-contrib/modules/python/Makefile.am
new file mode 100644
index 0000000000..5d634a446c
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/python/Makefile.am
@@ -0,0 +1,45 @@
+# 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.
+
+datadir=$(prefix)/modules/python
+libdir=$(prefix)/lib
+
+if WANT_PYTHON
+
+noinst_PROGRAMS = python-test python-shell client-test
+
+lib_LTLIBRARIES = libmod_tuscany_python.la
+
+nobase_data_DATA = *.xsd
+
+INCLUDES = -I${PYTHON_INCLUDE}
+
+libmod_tuscany_python_la_SOURCES = mod-python.cpp
+libmod_tuscany_python_la_LDFLAGS = -lxml2 -lcurl -lmozjs -L${PYTHON_LIB} -R${PYTHON_LIB} -lpython2.6
+
+python_test_SOURCES = python-test.cpp
+python_test_LDFLAGS = -L${PYTHON_LIB} -R${PYTHON_LIB} -lpython2.6
+
+python_shell_SOURCES = python-shell.cpp
+python_shell_LDFLAGS = -L${PYTHON_LIB} -R${PYTHON_LIB} -lpython2.6
+
+client_test_SOURCES = client-test.cpp
+client_test_LDFLAGS = -lxml2 -lcurl -lmozjs
+
+TESTS = python-test server-test
+
+endif
diff --git a/sca-cpp/branches/cpp-contrib/modules/python/client-test.cpp b/sca-cpp/branches/cpp-contrib/modules/python/client-test.cpp
new file mode 100644
index 0000000000..b070f6a798
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/python/client-test.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 "stream.hpp"
+#include "string.hpp"
+#include "../server/client-test.hpp"
+
+namespace tuscany {
+namespace server {
+
+string testURI = "http://localhost:8090/python";
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::server::testServer();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/python/client-test.py b/sca-cpp/branches/cpp-contrib/modules/python/client-test.py
new file mode 100644
index 0000000000..47e6cf4bda
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/python/client-test.py
@@ -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.
+
+# JSON-RPC test case
+
+def echo(x, ref):
+ return ref("echo", x)
+
+# ATOMPub test case
+
+def get(id, ref):
+ return ref("get", id)
+
+def post(collection, item, ref):
+ return ref("post", collection, item)
+
+def put(id, item, ref):
+ return ref("put", id, item)
+
+def delete(id, ref):
+ return ref("delete", id)
diff --git a/sca-cpp/branches/cpp-contrib/modules/python/domain-test.composite b/sca-cpp/branches/cpp-contrib/modules/python/domain-test.composite
new file mode 100644
index 0000000000..c8e92b286e
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/python/domain-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/200912"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://domain/test"
+ name="domain-test">
+
+ <component name="python-test">
+ <t:implementation.python script="server-test.py"/>
+ <service name="test">
+ <t:binding.http uri="python"/>
+ </service>
+ </component>
+
+ <component name="client-test">
+ <t:implementation.python script="client-test.py"/>
+ <service name="client">
+ <t:binding.http uri="client"/>
+ </service>
+ <reference name="ref" target="python-test">
+ <t:binding.http/>
+ </reference>
+ </component>
+
+</composite>
diff --git a/sca-cpp/branches/cpp-contrib/modules/python/driver.hpp b/sca-cpp/branches/cpp-contrib/modules/python/driver.hpp
new file mode 100644
index 0000000000..2820201057
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/python/driver.hpp
@@ -0,0 +1,63 @@
+/*
+ * 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_python_driver_hpp
+#define tuscany_python_driver_hpp
+
+/**
+ * Python evaluator main driver loop.
+ */
+
+#include "string.hpp"
+#include "stream.hpp"
+#include "monad.hpp"
+#include "../scheme/driver.hpp"
+#include "eval.hpp"
+
+namespace tuscany {
+namespace python {
+
+const value evalDriverLoop(PyObject* script, istream& in, ostream& out) {
+ scheme::promptForInput(scheme::evalInputPrompt, out);
+ value input = scheme::readValue(in);
+ if (isNil(input))
+ return input;
+ const failable<value> output = evalScript(input, script);
+ scheme::announceOutput(scheme::evalOutputPrompt, out);
+ scheme::userPrint(content(output), out);
+ return evalDriverLoop(script, in, out);
+}
+
+const bool evalDriverRun(const char* path, istream& in, ostream& out) {
+ PythonRuntime py;
+ scheme::setupDisplay(out);
+ ifstream is(path);
+ failable<PyObject*> script = readScript(path, is);
+ if (!hasContent(script))
+ return true;
+ evalDriverLoop(content(script), in, out);
+ Py_DECREF(content(script));
+ return true;
+}
+
+}
+}
+#endif /* tuscany_python_driver_hpp */
diff --git a/sca-cpp/branches/cpp-contrib/modules/python/eval.hpp b/sca-cpp/branches/cpp-contrib/modules/python/eval.hpp
new file mode 100644
index 0000000000..855804a5ae
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/python/eval.hpp
@@ -0,0 +1,300 @@
+/*
+ * 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_python_eval_hpp
+#define tuscany_python_eval_hpp
+
+/**
+ * Python script evaluation logic.
+ */
+#include <python2.6/Python.h>
+
+#include "list.hpp"
+#include "value.hpp"
+
+namespace tuscany {
+namespace python {
+
+/**
+ * Represent a Python runtime.
+ */
+class PythonRuntime {
+public:
+ PythonRuntime() {
+ if (Py_IsInitialized())
+ return;
+ Py_InitializeEx(0);
+ }
+};
+
+/**
+ * Return the last python error.
+ */
+const string lastError() {
+ if(PyErr_Occurred()) {
+ PyObject* type;
+ PyObject* val;
+ PyObject* trace;
+ PyErr_Fetch(&type, &val, &trace);
+ if (type != NULL && val != NULL) {
+ PyObject* stype = PyObject_Str(type);
+ PyObject* sval = PyObject_Str(val);
+ string msg = string() + PyString_AsString(stype) + " : " + PyString_AsString(sval);
+ Py_DECREF(stype);
+ Py_DECREF(sval);
+ Py_DECREF(type);
+ Py_DECREF(val);
+ Py_XDECREF(trace);
+ PyErr_Print();
+ return msg;
+ }
+ PyErr_Print();
+ Py_XDECREF(type);
+ Py_XDECREF(val);
+ Py_XDECREF(trace);
+ PyErr_Print();
+ return "Unknown Python error";
+ }
+ return "";
+}
+
+/**
+ * Declare conversion functions.
+ */
+PyObject* valueToPyObject(const value& v);
+const value pyObjectToValue(PyObject *o);
+PyObject* valuesToPyTuple(const list<value>& v);
+const list<value> pyTupleToValues(PyObject* o);
+
+/**
+ * Callable python type used to represent a lambda expression.
+ */
+typedef struct {
+ PyObject_HEAD
+ lambda<value(const list<value>&)> func;
+} pyLambda;
+
+void pyLambda_dealloc(PyObject* self) {
+ PyMem_DEL(self);
+}
+
+PyObject* pyLambda_call(PyObject* self, PyObject* args, unused PyObject* kwds) {
+ const pyLambda* pyl = (pyLambda*)self;
+ const value result = pyl->func(pyTupleToValues(args));
+ Py_DECREF(args);
+ PyObject *pyr = valueToPyObject(result);
+ return pyr;
+}
+
+PyTypeObject pyLambda_type = {
+ PyObject_HEAD_INIT(0)
+ 0,
+ "lambda",
+ sizeof(pyLambda),
+ 0,
+ (destructor)pyLambda_dealloc,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ (ternaryfunc)pyLambda_call,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+
+/**
+ * Create a new python object representing a lambda expression.
+ */
+PyObject *mkPyLambda(const lambda<value(const list<value>&)>& l) {
+ pyLambda* pyl = NULL;
+ pyl = PyObject_NEW(pyLambda, &pyLambda_type);
+ if (pyl != NULL)
+ pyl->func = l;
+ return (PyObject *)pyl;
+}
+
+/**
+ * Convert a list of values to a python list.
+ */
+
+PyObject* valuesToPyListHelper(PyObject* l, const list<value>& v) {
+ if (isNil(v))
+ return l;
+ PyList_Append(l, valueToPyObject(car(v)));
+ return valuesToPyListHelper(l, cdr(v));
+}
+
+PyObject* valuesToPyTuple(const list<value>& v) {
+ return PyList_AsTuple(valuesToPyListHelper(PyList_New(0), v));
+}
+
+/**
+ * Convert a value to a python object.
+ */
+PyObject* valueToPyObject(const value& v) {
+ switch (type(v)) {
+ case value::List:
+ return valuesToPyTuple(v);
+ case value::Lambda:
+ return mkPyLambda(v);
+ case value::Symbol:
+ return PyString_FromString(c_str(string("'") + v));
+ case value::String:
+ return PyString_FromString(c_str(v));
+ case value::Number:
+ return PyFloat_FromDouble((double)v);
+ case value::Bool:
+ return (bool)v? Py_True : Py_False;
+ default:
+ return Py_None;
+ }
+}
+
+/**
+ * Convert a python tuple to a list of values.
+ */
+
+const list<value> pyTupleToValuesHelper(PyObject* o, const int i, const int size) {
+ if (i == size)
+ return list<value>();
+ return cons(pyObjectToValue(PyTuple_GetItem(o, i)), pyTupleToValuesHelper(o, i + 1, size));
+}
+
+const list<value> pyTupleToValues(PyObject* o) {
+ return pyTupleToValuesHelper(o, 0, PyTuple_Size(o));
+}
+
+/**
+ * Lambda function used to represent a python callable object.
+ */
+struct pyCallable {
+ PyObject* func;
+
+ pyCallable(PyObject* func) : func(func) {
+ Py_INCREF(func);
+ }
+
+ ~pyCallable() {
+ Py_DECREF(func);
+ }
+
+ const value operator()(const list<value>& args) const {
+ PyObject* pyargs = valuesToPyTuple(args);
+ PyObject* result = PyObject_CallObject(func, pyargs);
+ Py_DECREF(pyargs);
+ const value v = pyObjectToValue(result);
+ Py_DECREF(result);
+ return v;
+ }
+};
+
+/**
+ * Convert a python object to a value.
+ */
+const value pyObjectToValue(PyObject *o) {
+ if (PyString_Check(o)) {
+ const char* s = PyString_AsString(o);
+ if (*s == '\'')
+ return value(s + 1);
+ return value(string(s));
+ }
+ if (PyBool_Check(o))
+ return value(o == Py_True);
+ if (PyInt_Check(o))
+ return value((double)PyInt_AsLong(o));
+ if (PyLong_Check(o))
+ return value((double)PyLong_AsLong(o));
+ if (PyFloat_Check(o))
+ return value((double)PyFloat_AsDouble(o));
+ if (PyTuple_Check(o))
+ return pyTupleToValues(o);
+ if (PyCallable_Check(o))
+ return lambda<value(const list<value>&)>(pyCallable(o));
+ return value();
+}
+
+/**
+ * Evaluate an expression against a script provided as a python object.
+ */
+const failable<value> evalScript(const value& expr, PyObject* script) {
+
+ // Get the requested function
+ PyObject* func = PyObject_GetAttrString(script, c_str(car<value>(expr)));
+ if (func == NULL) {
+
+ // The start, stop, and restart functions are optional
+ const value fn = car<value>(expr);
+ if (fn == "start" || fn == "stop") {
+ PyErr_Clear();
+ return value(lambda<value(const list<value>&)>());
+ }
+
+ return mkfailure<value>(string("Couldn't find function: ") + car<value>(expr) + " : " + lastError());
+ }
+ if (!PyCallable_Check(func)) {
+ Py_DECREF(func);
+ return mkfailure<value>(string("Couldn't find callable function: ") + car<value>(expr));
+ }
+
+ // Convert args to python objects
+ PyObject* args = valuesToPyTuple(cdr<value>(expr));
+
+ // Call the function
+ PyObject* result = PyObject_CallObject(func, args);
+ Py_DECREF(args);
+ Py_DECREF(func);
+ if (result == NULL)
+ return mkfailure<value>(string("Function call failed: ") + car<value>(expr) + " : " + lastError());
+
+ // Convert python result to a value
+ const value v = pyObjectToValue(result);
+ Py_DECREF(result);
+ return v;
+}
+
+/**
+ * Read a python script from an input stream.
+ */
+const failable<PyObject*> readScript(const string& path, istream& is) {
+ const list<string> ls = streamList(is);
+ ostringstream os;
+ write(ls, os);
+ PyObject* code = Py_CompileStringFlags(c_str(str(os)), c_str(path), Py_file_input, NULL);
+ if (code == NULL)
+ return mkfailure<PyObject*>(string("Couldn't compile script: ") + path + " : " + lastError());
+ PyObject* mod = PyImport_ExecCodeModule(const_cast<char*>(c_str(path)), code);
+ if (mod == NULL)
+ return mkfailure<PyObject*>(string("Couldn't import module: ") + path + " : " + lastError());
+ return mod;
+}
+
+/**
+ * Evaluate an expression against a script provided as an input stream.
+ */
+const failable<value> evalScript(const value& expr, istream& is) {
+ failable<PyObject*> script = readScript("script", is);
+ if (!hasContent(script))
+ return mkfailure<value>(reason(script));
+ return evalScript(expr, content(script));
+}
+
+}
+}
+#endif /* tuscany_python_eval_hpp */
diff --git a/sca-cpp/branches/cpp-contrib/modules/python/mod-python.cpp b/sca-cpp/branches/cpp-contrib/modules/python/mod-python.cpp
new file mode 100644
index 0000000000..8561a1fbf4
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/python/mod-python.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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 Python component implementations.
+ */
+
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "../server/mod-cpp.hpp"
+#include "../server/mod-eval.hpp"
+#include "mod-python.hpp"
+
+namespace tuscany {
+namespace server {
+namespace modeval {
+
+/**
+ * Apply a lifecycle start or restart event.
+ */
+const value applyLifecycle(unused const list<value>& params) {
+
+ // Create a Python runtime
+ new (gc_new<python::PythonRuntime>()) python::PythonRuntime();
+
+ // Return a nil function as we don't need to handle the stop event
+ return failable<value>(lambda<value(const list<value>&)>());
+}
+
+/**
+ * Evaluate a Python component implementation and convert it to an applicable
+ * lambda function.
+ */
+const failable<lambda<value(const list<value>&)> > evalImplementation(const string& path, const value& impl, const list<value>& px, unused const lambda<value(const list<value>&)>& lifecycle) {
+ const string itype(elementName(impl));
+ if (contains(itype, ".python"))
+ return modpython::evalImplementation(path, impl, px);
+ if (contains(itype, ".cpp"))
+ return modcpp::evalImplementation(path, impl, px);
+ return mkfailure<lambda<value(const list<value>&)> >(string("Unsupported implementation type: ") + itype);
+}
+
+}
+}
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/python/mod-python.hpp b/sca-cpp/branches/cpp-contrib/modules/python/mod-python.hpp
new file mode 100644
index 0000000000..d13f2227ab
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/python/mod-python.hpp
@@ -0,0 +1,79 @@
+/*
+ * 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_modpython_hpp
+#define tuscany_modpython_hpp
+
+/**
+ * Evaluation functions used by mod-eval to evaluate Python
+ * component implementations.
+ */
+
+#include "string.hpp"
+#include "stream.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "eval.hpp"
+
+namespace tuscany {
+namespace server {
+namespace modpython {
+
+/**
+ * Apply a Python component implementation function.
+ */
+struct applyImplementation {
+ PyObject* impl;
+ const list<value> px;
+ applyImplementation(PyObject* impl, const list<value>& px) : impl(impl), px(px) {
+ }
+ const value operator()(const list<value>& params) const {
+ const value expr = append<value>(params, px);
+ debug(expr, "modeval::python::applyImplementation::input");
+ const failable<value> res = python::evalScript(expr, impl);
+ const value val = !hasContent(res)? mklist<value>(value(), reason(res)) : mklist<value>(content(res));
+ debug(val, "modeval::python::applyImplementation::result");
+ return val;
+ }
+};
+
+/**
+ * Evaluate a Python component implementation and convert it to an applicable
+ * lambda function.
+ */
+const failable<lambda<value(const list<value>&)> > evalImplementation(const string& path, const value& impl, const list<value>& px) {
+ const string fpath(path + attributeValue("script", impl));
+ ifstream is(fpath);
+ if (fail(is))
+ return mkfailure<lambda<value(const list<value>&)> >(string("Could not read implementation: ") + fpath);
+ const failable<PyObject*> script = python::readScript(fpath, is);
+ if (!hasContent(script))
+ return mkfailure<lambda<value(const list<value>&)> >(reason(script));
+ return lambda<value(const list<value>&)>(applyImplementation(content(script), px));
+}
+
+}
+}
+}
+
+#endif /* tuscany_modpython_hpp */
diff --git a/sca-cpp/branches/cpp-contrib/modules/python/python-conf b/sca-cpp/branches/cpp-contrib/modules/python/python-conf
new file mode 100755
index 0000000000..9bc99e2bbe
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/python/python-conf
@@ -0,0 +1,26 @@
+#!/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 Python server conf
+here=`readlink -f $0`; here=`dirname $here`
+root=`readlink -f $1`
+
+cat >>$root/conf/httpd.conf <<EOF
+LoadModule mod_tuscany_eval $here/.libs/libmod_tuscany_python.so
+EOF
diff --git a/sca-cpp/branches/cpp-contrib/modules/python/python-shell.cpp b/sca-cpp/branches/cpp-contrib/modules/python/python-shell.cpp
new file mode 100644
index 0000000000..89b47b8d44
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/python/python-shell.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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$ */
+
+/**
+ * Python script evaluator shell, used for interactive testing of scripts.
+ */
+
+#include <assert.h>
+#include "gc.hpp"
+#include "stream.hpp"
+#include "string.hpp"
+#include "driver.hpp"
+
+int main(const int argc, char** argv) {
+ tuscany::gc_scoped_pool pool;
+ if (argc != 2) {
+ tuscany::cerr << "Usage: python-shell <script.py>" << tuscany::endl;
+ return 1;
+ }
+ tuscany::python::evalDriverRun(argv[1], tuscany::cin, tuscany::cout);
+ return 0;
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/python/python-test.cpp b/sca-cpp/branches/cpp-contrib/modules/python/python-test.cpp
new file mode 100644
index 0000000000..c4ffcee57e
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/python/python-test.cpp
@@ -0,0 +1,109 @@
+/*
+ * 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 Python script evaluator.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "driver.hpp"
+
+namespace tuscany {
+namespace python {
+
+const string testPythonAdd =
+ "def add(x, y):\n"
+ " return x + y\n";
+
+bool testEvalExpr() {
+ gc_scoped_pool pool;
+ PythonRuntime py;
+
+ istringstream is(testPythonAdd);
+ failable<PyObject*> script = readScript("script", is);
+ assert(hasContent(script));
+
+ const value exp = mklist<value>("add", 2, 3);
+ const failable<value> r = evalScript(exp, content(script));
+ assert(hasContent(r));
+ assert(content(r) == value(5));
+
+ return true;
+}
+
+const value mult(const list<value>& args) {
+ const double x = car(args);
+ const double y = cadr(args);
+ return x * y;
+}
+
+const string testReturnLambda(
+ "def mul(x, y):\n"
+ " return x * y\n"
+ "\n"
+ "def testReturnLambda():\n"
+ " return mul\n");
+
+const string testCallLambda(
+ "def testCallLambda(l, x, y):\n"
+ " return l(x, y)\n");
+
+bool testEvalLambda() {
+ gc_scoped_pool pool;
+ PythonRuntime py;
+
+ const value trl = mklist<value>("testReturnLambda");
+ istringstream trlis(testReturnLambda);
+ const failable<value> trlv = evalScript(trl, trlis);
+
+ assert(hasContent(trlv));
+ assert(isLambda(content(trlv)));
+ const lambda<value(const list<value>&)> trll(content(trlv));
+ assert(trll(mklist<value>(2, 3)) == value(6));
+
+ istringstream tclis(testCallLambda);
+ const value tcl = mklist<value>("testCallLambda", content(trlv), 2, 3);
+ const failable<value> tclv = evalScript(tcl, tclis);
+ assert(hasContent(tclv));
+ assert(content(tclv) == value(6));
+
+ istringstream tcelis(testCallLambda);
+ const value tcel = mklist<value>("testCallLambda", lambda<value(const list<value>&)>(mult), 3, 4);
+ const failable<value> tcelv = evalScript(tcel, tcelis);
+ assert(hasContent(tcelv));
+ assert(content(tcelv) == value(12));
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::python::testEvalExpr();
+ tuscany::python::testEvalLambda();
+
+ tuscany::cout << "OK" << tuscany::endl;
+ return 0;
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/python/server-test b/sca-cpp/branches/cpp-contrib/modules/python/server-test
new file mode 100755
index 0000000000..fe1ff7a486
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/python/server-test
@@ -0,0 +1,39 @@
+#!/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
+../http/httpd-conf tmp 8090 ../server/htdocs
+../server/server-conf tmp
+./python-conf tmp
+cat >>tmp/conf/httpd.conf <<EOF
+SCAContribution `pwd`/
+SCAComposite domain-test.composite
+EOF
+
+../http/httpd-start tmp
+sleep 2
+
+# Test
+./client-test 2>/dev/null
+rc=$?
+
+# Cleanup
+../http/httpd-stop tmp
+sleep 2
+return $rc
diff --git a/sca-cpp/branches/cpp-contrib/modules/python/server-test.py b/sca-cpp/branches/cpp-contrib/modules/python/server-test.py
new file mode 100644
index 0000000000..8e6b79b56a
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/python/server-test.py
@@ -0,0 +1,42 @@
+# 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.
+
+# JSON-RPC test case
+
+def echo(x):
+ return x
+
+# ATOMPub test case
+
+def get(id):
+ if id == ():
+ return ("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))))
+
+ entry = (("'javaClass", "services.Item"), ("'name", "Apple"), ("'currencyCode", "USD"), ("'currencySymbol", "$"), ("'price", 2.99))
+ return ("Item", id[0], entry)
+
+def post(collection, item):
+ return ("123456789",)
+
+def put(id, item):
+ return true
+
+def delete(id):
+ return true
diff --git a/sca-cpp/branches/cpp-contrib/modules/python/tuscany-sca-1.1-implementation-python.xsd b/sca-cpp/branches/cpp-contrib/modules/python/tuscany-sca-1.1-implementation-python.xsd
new file mode 100644
index 0000000000..c811ba6729
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/python/tuscany-sca-1.1-implementation-python.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/200912"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ elementFormDefault="qualified">
+
+ <import namespace="http://docs.oasis-open.org/ns/opencsa/sca/200912" schemaLocation="sca-1.1-cd04.xsd"/>
+
+ <element name="implementation.python" type="t:PythonImplementation" substitutionGroup="sca:implementation"/>
+
+ <complexType name="PythonImplementation">
+ <complexContent>
+ <extension base="sca:Implementation">
+ <sequence>
+ <any namespace="##targetNamespace" processContents="lax"
+ minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="script" type="anyURI" use="required"/>
+ <anyAttribute namespace="##any" processContents="lax"/>
+ </extension>
+ </complexContent>
+ </complexType>
+
+</schema>
diff --git a/sca-cpp/branches/cpp-contrib/modules/python/wiring-test b/sca-cpp/branches/cpp-contrib/modules/python/wiring-test
new file mode 100755
index 0000000000..a3c85838fd
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/python/wiring-test
@@ -0,0 +1,76 @@
+#!/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
+../http/httpd-conf tmp 8090 ../server/htdocs
+../server/server-conf tmp
+./python-conf tmp
+cat >>tmp/conf/httpd.conf <<EOF
+SCAContribution `pwd`/
+SCAComposite domain-test.composite
+EOF
+
+../http/httpd-start tmp
+sleep 2
+
+# Test HTTP GET
+curl http://localhost:8090/index.html 2>/dev/null >tmp/index.html
+diff tmp/index.html ../server/htdocs/index.html
+rc=$?
+
+# Test ATOMPub
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/client/ >tmp/feed.xml 2>/dev/null
+ diff tmp/feed.xml ../server/htdocs/feed.xml
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/client/111 >tmp/entry.xml 2>/dev/null
+ diff tmp/entry.xml ../server/htdocs/entry.xml
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/client/ -X POST -H "Content-type: application/atom+xml" --data @../server/htdocs/entry.xml 2>/dev/null
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/client/111 -X PUT -H "Content-type: application/atom+xml" --data @../server/htdocs/entry.xml 2>/dev/null
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/client/111 -X DELETE 2>/dev/null
+ rc=$?
+fi
+
+# Test JSON-RPC
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/client/ -X POST -H "Content-type: application/json-rpc" --data @../server/htdocs/json-request.txt >tmp/json-result.txt 2>/dev/null
+ diff tmp/json-result.txt ../server/htdocs/json-result.txt
+ rc=$?
+fi
+
+# Cleanup
+../http/httpd-stop tmp
+sleep 2
+if [ "$rc" = "0" ]; then
+ echo "OK"
+fi
+return $rc
diff --git a/sca-cpp/branches/cpp-contrib/modules/scdl/Makefile.am b/sca-cpp/branches/cpp-contrib/modules/scdl/Makefile.am
new file mode 100644
index 0000000000..afbd35a8a7
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/scdl/Makefile.am
@@ -0,0 +1,23 @@
+# 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
+
+scdl_test_SOURCES = scdl-test.cpp
+scdl_test_LDFLAGS = -lxml2
+
+TESTS = scdl-test
diff --git a/sca-cpp/branches/cpp-contrib/modules/scdl/scdl-test.cpp b/sca-cpp/branches/cpp-contrib/modules/scdl/scdl-test.cpp
new file mode 100644
index 0000000000..e8ee77eb4e
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/scdl/scdl-test.cpp
@@ -0,0 +1,124 @@
+/*
+ * 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 "stream.hpp"
+#include "string.hpp"
+#include "list.hpp"
+#include "tree.hpp"
+#include "scdl.hpp"
+
+namespace tuscany {
+namespace scdl {
+
+bool testComposite() {
+ ifstream is("test.composite");
+ const list<value> c = readXML(streamList(is));
+ return true;
+}
+
+bool testComponents() {
+ ifstream is("test.composite");
+ const list<value> c = components(readXML(streamList(is)));
+ assert(length(c) == 4);
+
+ const value store = car(c);
+ assert(name(store) == string("Store"));
+ const value impl = implementation(store);
+ assert(uri(impl) == string("store.html"));
+ assert(implementationType(impl) == "t:implementation.scheme");
+
+ const value catalog = named(string("Catalog"), c);
+ assert(name(catalog) == string("Catalog"));
+
+ const list<value> t = mkbtree(sort(nameToElementAssoc(c)));
+ assert(assoctree<value>("Catalog", t) == mklist<value>("Catalog" , cadr(c)));
+ return true;
+}
+
+bool testServices() {
+ 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) == string("Widget"));
+
+ assert(length(bindings(widget)) == 1);
+ const value binding = car(bindings(widget));
+ assert(uri(binding) == string("/store"));
+ assert(bindingType(binding) == "t:binding.http");
+ return true;
+}
+
+bool testReferences() {
+ 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) == string("catalog"));
+ assert(target(catalog) == string("Catalog"));
+
+ assert(length(bindings(catalog)) == 1);
+ const value binding = car(bindings(catalog));
+ assert(uri(binding) == value());
+ assert(bindingType(binding) == "t:binding.jsonrpc");
+
+ const list<value> t = mkbtree(sort(referenceToTargetAssoc(references(store))));
+ assert(assoctree<value>("shoppingCart", t) == mklist<value>(string("shoppingCart"), string("ShoppingCart/Cart")));
+ return true;
+}
+
+bool testProperties() {
+ ifstream is("test.composite");
+ const list<value> c = components(readXML(streamList(is)));
+ const value catalog = named(string("Catalog"), c);
+
+ assert(length(properties(catalog)) == 1);
+ const value currencyCode = car(properties(catalog));
+ assert(name(currencyCode) == string("currencyCode"));
+ assert(propertyValue(currencyCode) == string("USD"));
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::scdl::testComposite();
+ tuscany::scdl::testComponents();
+ tuscany::scdl::testServices();
+ tuscany::scdl::testReferences();
+ tuscany::scdl::testProperties();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/scdl/scdl.hpp b/sca-cpp/branches/cpp-contrib/modules/scdl/scdl.hpp
new file mode 100644
index 0000000000..531144e219
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/scdl/scdl.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_scdl_hpp
+#define tuscany_scdl_hpp
+
+/**
+ * SCDL read functions.
+ */
+
+#include "string.hpp"
+#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);
+}
+
+/**
+ * Convert a list of elements to a name -> element assoc list.
+ */
+const list<value> nameToElementAssoc(const list<value>& l) {
+ if (isNil(l))
+ return l;
+ const value e(car(l));
+ return cons<value>(mklist<value>(name(e), e), nameToElementAssoc(cdr(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) && contains(string(cadr<value>(v)), "implementation.");
+}
+
+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 the target of a reference.
+ */
+const value target(const value& l) {
+ return attributeValue("target", l);
+}
+
+/**
+ * Convert a list of references to a reference name -> target assoc list.
+ */
+const list<value> referenceToTargetAssoc(const list<value>& r) {
+ if (isNil(r))
+ return r;
+ const value ref(car(r));
+ return cons<value>(mklist<value>(scdl::name(ref), scdl::target(ref)), referenceToTargetAssoc(cdr(r)));
+}
+
+/**
+ * Returns a list of bindings in a service or reference.
+ */
+const bool filterBinding(const value& v) {
+ return isElement(v) && contains(string(cadr<value>(v)), "binding.");
+}
+
+const list<value> bindings(const value& l) {
+ return filter<value>(filterBinding, l);
+}
+
+/**
+ * Returns a list of properties in a component.
+ */
+const list<value> properties(const value& l) {
+ return elementChildren("property", 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/branches/cpp-contrib/modules/scdl/test.composite b/sca-cpp/branches/cpp-contrib/modules/scdl/test.composite
new file mode 100644
index 0000000000..f6fdba7f5f
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/scdl/test.composite
@@ -0,0 +1,64 @@
+<?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/200912"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://store"
+ name="store">
+
+ <component name="Store">
+ <t:implementation.scheme script="store.scm"/>
+ <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 script="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 script="shopping-cart.scm"/>
+ <service name="Cart">
+ <t:binding.atom uri="/ShoppingCart"/>
+ </service>
+ <service name="Total">
+ <t:binding.jsonrpc/>
+ </service>
+ </component>
+
+ <component name="CurrencyConverter">
+ <t:implementation.scheme script="currency-converter.scm"/>
+ </component>
+
+</composite>
diff --git a/sca-cpp/branches/cpp-contrib/modules/scheme/Makefile.am b/sca-cpp/branches/cpp-contrib/modules/scheme/Makefile.am
new file mode 100644
index 0000000000..f9bfe4e392
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/scheme/Makefile.am
@@ -0,0 +1,27 @@
+# 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
+
+eval_test_SOURCES = eval-test.cpp
+
+eval_shell_SOURCES = eval-shell.cpp
+
+TESTS = eval-test
diff --git a/sca-cpp/branches/cpp-contrib/modules/scheme/driver.hpp b/sca-cpp/branches/cpp-contrib/modules/scheme/driver.hpp
new file mode 100644
index 0000000000..112c226ed1
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/scheme/driver.hpp
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+#ifndef tuscany_scheme_driver_hpp
+#define tuscany_scheme_driver_hpp
+
+/**
+ * Script evaluator main driver loop.
+ */
+
+#include "string.hpp"
+#include "stream.hpp"
+#include "eval.hpp"
+
+namespace tuscany {
+namespace scheme {
+
+const string evalOutputPrompt("; ");
+const string evalInputPrompt("=> ");
+
+const bool promptForInput(const string& str, ostream& out) {
+ out << endl << endl << str;
+ return true;
+}
+
+const bool announceOutput(const string str, ostream& out) {
+ out << endl << str;
+ return true;
+}
+
+const bool userPrint(const value val, ostream& out) {
+ if(isCompoundProcedure(val))
+ writeValue(mklist<value>(compoundProcedureSymbol, procedureParameters(val), procedureBody(val), "<procedure-env>"), out);
+ writeValue(val, out);
+ return true;
+}
+
+const value evalDriverLoop(istream& in, ostream& out, Env& env) {
+ promptForInput(evalInputPrompt, out);
+ value input = readValue(in);
+ if (isNil(input))
+ return input;
+ const value output = evalExpr(input, env);
+ announceOutput(evalOutputPrompt, out);
+ userPrint(output, out);
+ return evalDriverLoop(in, out, env);
+}
+
+const bool evalDriverRun(istream& in, ostream& out) {
+ setupDisplay(out);
+ Env env = setupEnvironment();
+ evalDriverLoop(in, out, env);
+ return true;
+}
+
+}
+}
+#endif /* tuscany_scheme_driver_hpp */
diff --git a/sca-cpp/branches/cpp-contrib/modules/scheme/environment.hpp b/sca-cpp/branches/cpp-contrib/modules/scheme/environment.hpp
new file mode 100644
index 0000000000..bfb415a978
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/scheme/environment.hpp
@@ -0,0 +1,179 @@
+/*
+ * 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_scheme_environment_hpp
+#define tuscany_scheme_environment_hpp
+
+/**
+ * Script evaluator environment implementation.
+ */
+
+#include "string.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "primitive.hpp"
+#include <string>
+
+namespace tuscany {
+namespace scheme {
+
+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_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))
+ logStream() << "Too many arguments supplied " << values << endl;
+ return frameSoFar;
+ }
+ if (isDotVariable(car(variables)))
+ return makeBinding(frameSoFar, cdr(variables), mklist<value>(values));
+
+ if (isNil(values)) {
+ if (!isNil(variables))
+ logStream() << "Too few arguments supplied " << variables << endl;
+ 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_ptr<Frame> makeFrame(const list<value>& variables, const list<value> values) {
+ gc_ptr<Frame> frame = new (gc_new<Frame>()) Frame();
+ *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) {
+ return cons<value>(makeFrame(vars, vals), baseEnv);
+}
+
+const Env setupEnvironment() {
+ Env env = extendEnvironment(primitiveProcedureNames(), primitiveProcedureObjects(), theEmptyEnvironment());
+ 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()) {
+ logStream() << "Unbound variable " << var << endl;
+ 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_scheme_environment_hpp */
diff --git a/sca-cpp/branches/cpp-contrib/modules/scheme/eval-shell.cpp b/sca-cpp/branches/cpp-contrib/modules/scheme/eval-shell.cpp
new file mode 100644
index 0000000000..4aa67c2375
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/scheme/eval-shell.cpp
@@ -0,0 +1,36 @@
+/*
+ * 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 "gc.hpp"
+#include "stream.hpp"
+#include "string.hpp"
+#include "driver.hpp"
+
+int main() {
+ tuscany::gc_scoped_pool pool;
+ tuscany::scheme::evalDriverRun(tuscany::cin, tuscany::cout);
+ return 0;
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/scheme/eval-test.cpp b/sca-cpp/branches/cpp-contrib/modules/scheme/eval-test.cpp
new file mode 100644
index 0000000000..7c4c0c69c4
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/scheme/eval-test.cpp
@@ -0,0 +1,231 @@
+/*
+ * 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 "stream.hpp"
+#include "string.hpp"
+#include "driver.hpp"
+
+namespace tuscany {
+namespace scheme {
+
+bool testEnv() {
+ gc_scoped_pool pool;
+ Env globalEnv = list<value>();
+ Env env = extendEnvironment(mklist<value>("a"), mklist<value>(1), globalEnv);
+ 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(checkValueCounters());
+ assert(checkLambdaCounters());
+ assert(checkListCounters());
+ return true;
+}
+
+bool testRead() {
+ istringstream is("abcd");
+ assert(readValue(is) == "abcd");
+
+ istringstream is2("123");
+ assert(readValue(is2) == value(123));
+
+ istringstream is3("(abcd)");
+ assert(readValue(is3) == mklist(value("abcd")));
+
+ istringstream is4("(abcd xyz)");
+ assert(readValue(is4) == mklist<value>("abcd", "xyz"));
+
+ 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));
+ ostringstream os;
+ writeValue(a, os);
+ istringstream is(str(os));
+ assert(readValue(is) == a);
+ return true;
+}
+
+const string testSchemeNumber(
+ "(define (testNumber) (if (= 1 1) (display \"testNumber ok\") (error \"testNumber\"))) "
+ "(testNumber)");
+
+const string testSchemeString(
+ "(define (testString) (if (= \"abc\" \"abc\") (display \"testString ok\") (error \"testString\"))) "
+ "(testString)");
+
+const string testSchemeDefinition(
+ "(define a \"abc\") (define (testDefinition) (if (= a \"abc\") (display \"testDefinition ok\") (error \"testDefinition\"))) "
+ "(testDefinition)");
+
+const string testSchemeIf(
+ "(define (testIf) (if (= \"abc\" \"abc\") (if (= \"xyz\" \"xyz\") (display \"testIf ok\") (error \"testNestedIf\")) (error \"testIf\"))) "
+ "(testIf)");
+
+const string testSchemeCond(
+ "(define (testCond) (cond ((= \"abc\" \"abc\") (display \"testCond ok\")) (else (error \"testIf\"))))"
+ "(testCond)");
+
+const 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 string testSchemeLambda(
+ "(define sqrt (lambda (x) (* x x))) "
+ "(define (testLambda) (if (= 4 (sqrt 2)) (display \"testLambda ok\") (error \"testLambda\"))) "
+ "(testLambda)");
+
+const string testSchemeForward(
+ "(define (testLambda) (if (= 4 (sqrt 2)) (display \"testForward ok\") (error \"testForward\"))) "
+ "(define sqrt (lambda (x) (* x x))) "
+ "(testLambda)");
+
+const string evalOutput(const string& scm) {
+ istringstream is(scm);
+ ostringstream os;
+ evalDriverRun(is, os);
+ return str(os);
+}
+
+bool testEval() {
+ gc_scoped_pool pool;
+ 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_scoped_pool pool;
+ const value exp = mklist<value>("+", 2, 3);
+ Env env = setupEnvironment();
+ const value r = evalExpr(exp, env);
+ assert(r == value(5));
+ return true;
+}
+
+bool testEvalRun() {
+ gc_scoped_pool pool;
+ evalDriverRun(cin, cout);
+ return true;
+}
+
+const value mult(const list<value>& args) {
+ const double x = car(args);
+ const double y = cadr(args);
+ return x * y;
+}
+
+const string testReturnLambda(
+ "(define (testReturnLambda) * )");
+
+const string testCallLambda(
+ "(define (testCallLambda l x y) (l x y))");
+
+bool testEvalLambda() {
+ gc_scoped_pool pool;
+ Env env = setupEnvironment();
+
+ const value trl = mklist<value>("testReturnLambda");
+ istringstream trlis(testReturnLambda);
+ const value trlv = evalScript(trl, trlis, env);
+
+ istringstream tclis(testCallLambda);
+ const value tcl = cons<value>("testCallLambda", quotedParameters(mklist<value>(trlv, 2, 3)));
+ const value tclv = evalScript(tcl, tclis, env);
+ assert(tclv == value(6));
+
+ istringstream tcelis(testCallLambda);
+ const value tcel = cons<value>("testCallLambda", quotedParameters(mklist<value>(primitiveProcedure(mult), 3, 4)));
+ const value tcelv = evalScript(tcel, tcelis, env);
+ assert(tcelv == value(12));
+ return true;
+}
+
+bool testEvalGC() {
+ resetLambdaCounters();
+ resetListCounters();
+ resetValueCounters();
+ testEval();
+ testEvalExpr();
+ testEvalLambda();
+ assert(checkValueCounters());
+ assert(checkLambdaCounters());
+ assert(checkListCounters());
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::scheme::testEnv();
+ tuscany::scheme::testEnvGC();
+ tuscany::scheme::testRead();
+ tuscany::scheme::testWrite();
+ tuscany::scheme::testEval();
+ tuscany::scheme::testEvalExpr();
+ tuscany::scheme::testEvalLambda();
+ tuscany::scheme::testEvalGC();
+
+ tuscany::cout << "OK" << tuscany::endl;
+ return 0;
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/scheme/eval.hpp b/sca-cpp/branches/cpp-contrib/modules/scheme/eval.hpp
new file mode 100644
index 0000000000..34d1a7bc17
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/scheme/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_scheme_eval_hpp
+#define tuscany_scheme_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 scheme {
+
+const value evalExpr(const value& exp, Env& env);
+
+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) {
+ if(isNil(exps))
+ return list<value> ();
+ return cons(evalExpr(car(exps), env), listOfValues(cdr(exps), env));
+}
+
+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) {
+ if(isLastExp(exps))
+ return evalExpr(firstExp(exps), env);
+ evalExpr(firstExp(exps), env);
+ return evalSequence(restExp(exps), env);
+}
+
+const value applyProcedure(const value& procedure, list<value>& arguments) {
+ if(isPrimitiveProcedure(procedure))
+ return applyPrimitiveProcedure(procedure, arguments);
+ if(isCompoundProcedure(procedure)) {
+ Env env = extendEnvironment(procedureParameters(procedure), arguments, procedureEnvironment(procedure));
+ return evalSequence(procedureBody(procedure), env);
+ }
+ logStream() << "Unknown procedure type " << procedure << endl;
+ 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));
+ logStream() << "else clause isn't last " << clauses << endl;
+ 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) {
+ if(isTrue(evalExpr(ifPredicate(exp), env)))
+ return evalExpr(ifConsequent(exp), env);
+ return evalExpr(ifAlternative(exp), env);
+}
+
+const value evalDefinition(const value& exp, Env& env) {
+ defineVariable(definitionVariable(exp), evalExpr(definitionValue(exp), env), env);
+ return definitionVariable(exp);
+}
+
+const value evalExpr(const value& exp, Env& env) {
+ if(isSelfEvaluating(exp))
+ return exp;
+ if(isQuoted(exp))
+ return textOfQuotation(exp);
+ if(isDefinition(exp))
+ return evalDefinition(exp, env);
+ if(isIf(exp))
+ return evalIf(exp, env);
+ if(isBegin(exp))
+ return evalSequence(beginActions(exp), env);
+ if(isCond(exp))
+ return evalExpr(condToIf(exp), env);
+ 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);
+ return applyProcedure(evalExpr(applyOperat(exp), env), applyOperandValues);
+ }
+ if(isApplication(exp)) {
+ list<value> operandValues = listOfValues(operands(exp), env);
+ return applyProcedure(evalExpr(operat(exp), env), operandValues);
+ }
+ logStream() << "Unknown expression type " << exp << endl;
+ 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, scheme::Env& env) {
+ if (isNil(script))
+ return scheme::evalExpr(expr, env);
+ scheme::evalExpr(car(script), env);
+ return evalScriptLoop(expr, cdr(script), env);
+}
+
+const value evalScript(const value& expr, const value& script, Env& env) {
+ return evalScriptLoop(expr, script, env);
+}
+
+/**
+ * Evaluate an expression against a script provided as an input stream.
+ */
+const value evalScript(const value& expr, istream& is, Env& env) {
+ return evalScript(expr, readScript(is), env);
+}
+
+}
+}
+#endif /* tuscany_scheme_eval_hpp */
diff --git a/sca-cpp/branches/cpp-contrib/modules/scheme/io.hpp b/sca-cpp/branches/cpp-contrib/modules/scheme/io.hpp
new file mode 100644
index 0000000000..5e5397cfeb
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/scheme/io.hpp
@@ -0,0 +1,217 @@
+/*
+ * 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_scheme_io_hpp
+#define tuscany_scheme_io_hpp
+
+/**
+ * Script evaluator IO functions.
+ */
+
+#include <ctype.h>
+#include "stream.hpp"
+#include "string.hpp"
+
+#include "list.hpp"
+#include "value.hpp"
+#include "primitive.hpp"
+
+namespace tuscany {
+namespace scheme {
+
+const value rightParenthesis(mklist<value>(")"));
+const value leftParenthesis(mklist<value>("("));
+const value comment(mklist<value>(";"));
+
+const double stringToNumber(const string& str) {
+ return atof(c_str(str));
+}
+
+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(istream& in) {
+ if(in.eof()) {
+ return -1;
+ }
+ char c = (char)get(in);
+ return c;
+}
+
+const char peekChar(istream& in) {
+ if(eof(in))
+ return -1;
+ char c = (char)peek(in);
+ return c;
+}
+
+const bool isQuote(const value& token) {
+ return token == quoteSymbol;
+}
+
+const value skipComment(istream& in);
+const value readQuoted(istream& in);
+const value readIdentifier(const char chr, istream& in);
+const value readString(istream& in);
+const value readNumber(const char chr, istream& in);
+const value readValue(istream& in);
+
+const value readToken(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(in);
+ if(isIdentifierStart(firstChar))
+ return readIdentifier(firstChar, in);
+ if(isDigit(firstChar))
+ return readNumber(firstChar, in);
+ if(firstChar == -1)
+ return value();
+ logStream() << "Illegal lexical syntax '" << firstChar << "'" << endl;
+ return readToken(in);
+}
+
+const value skipComment(istream& in) {
+ const char nextChar = readChar(in);
+ if (nextChar == '\n')
+ return readToken(in);
+ return skipComment(in);
+}
+
+const value readQuoted(istream& in) {
+ return mklist(quoteSymbol, readValue(in));
+}
+
+const list<value> readList(const list<value>& listSoFar, 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 string listToString(const list<char>& l) {
+ if(isNil(l))
+ return "";
+ const char buf[1] = { car(l) };
+ return string(buf, 1) + listToString(cdr(l));
+}
+
+const list<char> readIdentifierHelper(const list<char>& listSoFar, 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, istream& in) {
+ return c_str(listToString(readIdentifierHelper(mklist(chr), in)));
+}
+
+const list<char> readStringHelper(const list<char>& listSoFar, istream& in) {
+ const char nextChar = readChar(in);
+ if(nextChar != -1 && nextChar != '"')
+ return readStringHelper(cons(nextChar, listSoFar), in);
+ return reverse(listSoFar);
+}
+
+const value readString(istream& in) {
+ return listToString(readStringHelper(list<char>(), in));
+}
+
+const list<char> readNumberHelper(const list<char>& listSoFar, 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, istream& in) {
+ return stringToNumber(listToString(readNumberHelper(mklist(chr), in)));
+}
+
+const value readValue(istream& in) {
+ const value nextToken = readToken(in);
+ if(isLeftParenthesis(nextToken))
+ return readList(list<value> (), in);
+ return nextToken;
+}
+
+const value readValue(const string s) {
+ istringstream in(s);
+ const value nextToken = readToken(in);
+ if(isLeftParenthesis(nextToken))
+ return readList(list<value> (), in);
+ return nextToken;
+}
+
+const bool writeValue(const value& val, ostream& out) {
+ out << val;
+ return true;
+}
+
+const string writeValue(const value& val) {
+ ostringstream out;
+ out << val;
+ return str(out);
+}
+
+const value readScript(istream& in) {
+ const value val = readValue(in);
+ if (isNil(val))
+ return list<value>();
+ return cons(val, (list<value>)readScript(in));
+}
+
+}
+}
+#endif /* tuscany_scheme_io_hpp */
diff --git a/sca-cpp/branches/cpp-contrib/modules/scheme/primitive.hpp b/sca-cpp/branches/cpp-contrib/modules/scheme/primitive.hpp
new file mode 100644
index 0000000000..fd5f3e9755
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/scheme/primitive.hpp
@@ -0,0 +1,285 @@
+/*
+ * 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_scheme_primitive_hpp
+#define tuscany_scheme_primitive_hpp
+
+/**
+ * Script evaluator primitive functions.
+ */
+
+#include <apr_general.h>
+#include <apr_uuid.h>
+#include "stream.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+
+namespace tuscany {
+namespace scheme {
+
+const value primitiveSymbol("primitive");
+const value quoteSymbol("'");
+const value lambdaSymbol("lambda");
+
+#ifdef WANT_THREADS
+__thread
+#endif
+ostream* displayOutStream = NULL;
+
+#ifdef WANT_THREADS
+__thread
+#endif
+ostream* logOutStream = NULL;
+
+const bool setupDisplay(ostream& out) {
+ displayOutStream = &out;
+ return true;
+}
+
+ostream& displayStream() {
+ if (displayOutStream == NULL)
+ return cout;
+ return *displayOutStream;
+}
+
+const bool setupLog(ostream& out) {
+ logOutStream = &out;
+ return true;
+}
+
+ostream& logStream() {
+ if (logOutStream == NULL)
+ return cerr;
+ return *logOutStream;
+}
+
+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 assocProc(const list<value>& args) {
+ return assoc(car(args), (list<list<value> >)cadr(args));
+}
+
+const value nulProc(const list<value>& args) {
+ const value v(car(args));
+ if (isNil(v))
+ return true;
+ if (isList(v))
+ return isNil(list<value>(v));
+ return false;
+}
+
+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) {
+ if (isNil(args)) {
+ displayStream() << endl;
+ return true;
+ }
+ displayStream() << car(args);
+ return displayProc(cdr(args));
+}
+
+const value logProc(const list<value>& args) {
+ if (isNil(args)) {
+ logStream() << endl;
+ return true;
+ }
+ logStream() << car(args);
+ return logProc(cdr(args));
+}
+
+const value uuidProc(unused const list<value>& args) {
+ apr_uuid_t uuid;
+ apr_uuid_get(&uuid);
+ char buf[APR_UUID_FORMATTED_LENGTH];
+ apr_uuid_format(buf, &uuid);
+ return string(buf, APR_UUID_FORMATTED_LENGTH);
+}
+
+const value cadrProc(unused const list<value>& args) {
+ return cadr((list<value> )car(args));
+}
+
+const value caddrProc(unused const list<value>& args) {
+ return caddr((list<value> )car(args));
+}
+
+const value cadddrProc(unused const list<value>& args) {
+ return cadddr((list<value> )car(args));
+}
+
+const value cddrProc(unused const list<value>& args) {
+ return cddr((list<value> )car(args));
+}
+
+const value cdddrProc(unused const list<value>& args) {
+ return cdddr((list<value> )car(args));
+}
+
+const value startProc(unused const list<value>& args) {
+ return lambda<value(const list<value>&)>();
+}
+
+const value stopProc(unused const list<value>& args) {
+ return lambda<value(const list<value>&)>();
+}
+
+const value applyPrimitiveProcedure(const value& proc, list<value>& args) {
+ const lambda<value(const 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(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(const list<value>&)>)f);
+}
+
+const list<value> primitiveProcedureNames() {
+ return mklist<value>("car")
+ + "cdr"
+ + "cons"
+ + "list"
+ + "nul"
+ + "="
+ + "equal?"
+ + "+"
+ + "-"
+ + "*"
+ + "/"
+ + "assoc"
+ + "cadr"
+ + "caddr"
+ + "cadddr"
+ + "cddr"
+ + "cdddr"
+ + "display"
+ + "log"
+ + "uuid"
+ + "start"
+ + "stop";
+}
+
+const list<value> primitiveProcedureObjects() {
+ return mklist(primitiveProcedure(carProc))
+ + primitiveProcedure(cdrProc)
+ + primitiveProcedure(consProc)
+ + primitiveProcedure(listProc)
+ + primitiveProcedure(nulProc)
+ + primitiveProcedure(equalProc)
+ + primitiveProcedure(equalProc)
+ + primitiveProcedure(addProc)
+ + primitiveProcedure(subProc)
+ + primitiveProcedure(mulProc)
+ + primitiveProcedure(divProc)
+ + primitiveProcedure(assocProc)
+ + primitiveProcedure(cadrProc)
+ + primitiveProcedure(caddrProc)
+ + primitiveProcedure(cadddrProc)
+ + primitiveProcedure(cddrProc)
+ + primitiveProcedure(cdddrProc)
+ + primitiveProcedure(displayProc)
+ + primitiveProcedure(logProc)
+ + primitiveProcedure(uuidProc)
+ + primitiveProcedure(startProc)
+ + primitiveProcedure(stopProc);
+}
+
+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_scheme_primitive_hpp */
diff --git a/sca-cpp/branches/cpp-contrib/modules/scheme/tuscany-sca-1.1-implementation-scheme.xsd b/sca-cpp/branches/cpp-contrib/modules/scheme/tuscany-sca-1.1-implementation-scheme.xsd
new file mode 100644
index 0000000000..5b32834832
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/scheme/tuscany-sca-1.1-implementation-scheme.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/200912"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ elementFormDefault="qualified">
+
+ <import namespace="http://docs.oasis-open.org/ns/opencsa/sca/200912" schemaLocation="sca-1.1-cd04.xsd"/>
+
+ <element name="implementation.scheme" type="t:SchemeImplementation" substitutionGroup="sca:implementation"/>
+
+ <complexType name="SchemeImplementation">
+ <complexContent>
+ <extension base="sca:Implementation">
+ <sequence>
+ <any namespace="##targetNamespace" processContents="lax"
+ minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="script" type="anyURI" use="required"/>
+ <anyAttribute namespace="##any" processContents="lax"/>
+ </extension>
+ </complexContent>
+ </complexType>
+
+</schema>
diff --git a/sca-cpp/branches/cpp-contrib/modules/server/Makefile.am b/sca-cpp/branches/cpp-contrib/modules/server/Makefile.am
new file mode 100644
index 0000000000..9c18b45dad
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/server/Makefile.am
@@ -0,0 +1,39 @@
+# 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 = client-test
+
+libdir=$(prefix)/lib
+lib_LTLIBRARIES = libmod_tuscany_eval.la libmod_tuscany_wiring.la
+
+INCLUDES = -I${HTTPD_INCLUDE}
+
+libmod_tuscany_eval_la_SOURCES = mod-eval.cpp
+libmod_tuscany_eval_la_LDFLAGS = -lxml2 -lcurl -lmozjs
+
+libmod_tuscany_wiring_la_SOURCES = mod-wiring.cpp
+libmod_tuscany_wiring_la_LDFLAGS = -lxml2 -lcurl -lmozjs
+
+testdir=$(prefix)/test
+test_LTLIBRARIES = libimpl-test.la
+
+libimpl_test_la_SOURCES = impl-test.cpp
+
+client_test_SOURCES = client-test.cpp
+client_test_LDFLAGS = -lxml2 -lcurl -lmozjs
+
+TESTS = httpd-test server-test wiring-test
diff --git a/sca-cpp/branches/cpp-contrib/modules/server/client-test.cpp b/sca-cpp/branches/cpp-contrib/modules/server/client-test.cpp
new file mode 100644
index 0000000000..3bc8aa7d10
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/server/client-test.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 "stream.hpp"
+#include "string.hpp"
+#include "client-test.hpp"
+
+namespace tuscany {
+namespace server {
+
+string testURI = "http://localhost:8090/test";
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::server::testServer();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/server/client-test.hpp b/sca-cpp/branches/cpp-contrib/modules/server/client-test.hpp
new file mode 100644
index 0000000000..651a9016b2
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/server/client-test.hpp
@@ -0,0 +1,346 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "parallel.hpp"
+#include "perf.hpp"
+#include "../http/curl.hpp"
+
+namespace tuscany {
+namespace server {
+
+extern string testURI;
+
+ostream* curlWriter(const string& s, ostream* os) {
+ (*os) << s;
+ return os;
+}
+
+const bool testGet() {
+ gc_scoped_pool pool;
+ http::CURLSession ch;
+ {
+ ostringstream os;
+ const failable<list<ostream*> > r = http::get<ostream*>(curlWriter, &os, "http://localhost:8090", ch);
+ assert(hasContent(r));
+ assert(contains(str(os), "HTTP/1.1 200 OK"));
+ assert(contains(str(os), "It works"));
+ }
+ {
+ const failable<value> r = http::getcontent("http://localhost:8090", ch);
+ assert(hasContent(r));
+ assert(contains(car(reverse(list<value>(content(r)))), "It works"));
+ }
+ return true;
+}
+
+struct getLoop {
+ http::CURLSession ch;
+ getLoop(http::CURLSession& ch) : ch(ch) {
+ }
+ const bool operator()() const {
+ const failable<value> r = http::getcontent("http://localhost:8090", ch);
+ assert(hasContent(r));
+ assert(contains(car(reverse(list<value>(content(r)))), "It works"));
+ return true;
+ }
+};
+
+const bool testGetPerf() {
+ gc_scoped_pool pool;
+ http::CURLSession ch;
+ const lambda<bool()> gl = getLoop(ch);
+ cout << "Static GET test " << time(gl, 5, 200) << " ms" << endl;
+ return true;
+}
+
+const bool testEval() {
+ gc_scoped_pool pool;
+ http::CURLSession ch;
+ const value val = content(http::evalExpr(mklist<value>(string("echo"), string("Hello")), testURI, ch));
+ assert(val == string("Hello"));
+ return true;
+}
+
+struct evalLoop {
+ const string uri;
+ http::CURLSession ch;
+ evalLoop(const string& uri, http::CURLSession& ch) : uri(uri), ch(ch) {
+ }
+ const bool operator()() const {
+ const value val = content(http::evalExpr(mklist<value>(string("echo"), string("Hello")), uri, ch));
+ assert(val == string("Hello"));
+ return true;
+ }
+};
+
+const value blob(string(2048, 'A'));
+const list<value> blobs = mklist(blob, blob);
+
+struct blobEvalLoop {
+ const string uri;
+ http::CURLSession ch;
+ blobEvalLoop(const string& uri, http::CURLSession& ch) : uri(uri), ch(ch) {
+ }
+ const bool operator()() const {
+ const value val = content(http::evalExpr(mklist<value>(string("echo"), blobs), uri, ch));
+ assert(val == blobs);
+ return true;
+ }
+};
+
+const bool testEvalPerf() {
+ gc_scoped_pool pool;
+ http::CURLSession ch;
+ const lambda<bool()> el = evalLoop(testURI, ch);
+ cout << "JSON-RPC eval echo test " << time(el, 5, 200) << " ms" << endl;
+ const lambda<bool()> bel = blobEvalLoop(testURI, ch);
+ cout << "JSON-RPC eval blob test " << time(bel, 5, 200) << " ms" << endl;
+ return true;
+}
+
+bool testPost() {
+ gc_scoped_pool pool;
+ const list<value> i = list<value>()
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "price" + string("$2.99"));
+ const list<value> a = mklist<value>(string("item"), string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i);
+ http::CURLSession ch;
+ const failable<value> id = http::post(a, testURI, ch);
+ assert(hasContent(id));
+ return true;
+}
+
+struct postLoop {
+ const string uri;
+ const value val;
+ http::CURLSession ch;
+ postLoop(const string& uri, const value& val, http::CURLSession& ch) : uri(uri), val(val), ch(ch) {
+ }
+ const bool operator()() const {
+ const failable<value> id = http::post(val, uri, ch);
+ assert(hasContent(id));
+ return true;
+ }
+};
+
+struct postBlobLoop {
+ const string uri;
+ const value val;
+ http::CURLSession ch;
+ postBlobLoop(const string& uri, const value& val, http::CURLSession& ch) : uri(uri), val(val), ch(ch) {
+ }
+ const bool operator()() const {
+ gc_scoped_pool pool;
+ const failable<value> id = http::post(val, uri, ch);
+ assert(hasContent(id));
+ return true;
+ }
+};
+
+const bool testPostPerf() {
+ gc_scoped_pool pool;
+ http::CURLSession ch;
+ {
+ const list<value> i = list<value>()
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "price" + string("$2.99"));
+ const list<value> val = mklist<value>(string("item"), string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i);
+ const lambda<bool()> pl = postLoop(testURI, val, ch);
+ cout << "ATOMPub POST small test " << time(pl, 5, 200) << " ms" << endl;
+ }
+ {
+ const list<value> i = list<value>()
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "blob1" + blob)
+ + (list<value>() + "blob2" + blob)
+ + (list<value>() + "price" + string("$2.99"));
+ const list<value> val = mklist<value>(string("item"), string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i);
+ const lambda<bool()> pl = postBlobLoop(testURI, val, ch);
+ cout << "ATOMPub POST blob test " << time(pl, 5, 200) << " ms" << endl;
+ }
+ return true;
+}
+
+#ifdef WANT_THREADS
+
+const bool postThread(const string& uri, const int count, const value& val) {
+ gc_scoped_pool pool;
+ http::CURLSession ch;
+ const lambda<bool()> pl = postLoop(uri, val, ch);
+ time(pl, 0, count);
+ return true;
+}
+
+const list<future<bool> > startPost(worker& w, const int threads, const lambda<bool()>& l) {
+ if (threads == 0)
+ return list<future<bool> >();
+ return cons(submit(w, l), startPost(w, threads - 1, l));
+}
+
+const bool checkPost(const list<future<bool> >& r) {
+ if (isNil(r))
+ return true;
+ assert(car(r) == true);
+ return checkPost(cdr(r));
+}
+
+struct postThreadLoop {
+ const lambda<bool()> l;
+ const int threads;
+ const gc_ptr<worker> w;
+ postThreadLoop(const lambda<bool()>& l, const int threads) : l(l), threads(threads), w(new (gc_new<worker>()) worker(threads)) {
+ }
+ const bool operator()() const {
+ list<future<bool> > r = startPost(*w, threads, l);
+ checkPost(r);
+ return true;
+ }
+};
+
+const bool testPostThreadPerf() {
+ gc_scoped_pool pool;
+ const int count = 50;
+ const int threads = 10;
+
+ const list<value> i = list<value>()
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "price" + string("$2.99"));
+ const value val = mklist<value>(string("item"), string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i);
+
+ const lambda<bool()> pl= curry(lambda<bool(const string, const int, const value)>(postThread), testURI, count, val);
+ const lambda<bool()> ptl = postThreadLoop(pl, threads);
+ double t = time(ptl, 0, 1) / (threads * count);
+ cout << "ATOMPub POST thread test " << t << " ms" << endl;
+
+ return true;
+}
+
+#else
+
+const bool postProc(const string& uri, const int count, const value& val) {
+ gc_scoped_pool pool;
+ http::CURLSession ch;
+ const lambda<bool()> pl = postLoop(uri, val, ch);
+ time(pl, 0, count);
+ return true;
+}
+
+const list<pid_t> startPost(const int procs, const lambda<bool()>& l) {
+ if (procs == 0)
+ return list<pid_t>();
+ pid_t pid = fork();
+ if (pid == 0) {
+ assert(l() == true);
+ exit(0);
+ }
+ return cons(pid, startPost(procs - 1, l));
+}
+
+const bool checkPost(const list<pid_t>& r) {
+ if (isNil(r))
+ return true;
+ int status;
+ waitpid(car(r), &status, 0);
+ assert(status == 0);
+ return checkPost(cdr(r));
+}
+
+struct postForkLoop {
+ const lambda<bool()> l;
+ const int procs;
+ postForkLoop(const lambda<bool()>& l, const int procs) : l(l), procs(procs) {
+ }
+ const bool operator()() const {
+ list<pid_t> r = startPost(procs, l);
+ checkPost(r);
+ return true;
+ }
+};
+
+const bool testPostForkPerf() {
+ gc_scoped_pool pool;
+ const int count = 50;
+ const int procs = 10;
+
+ const list<value> i = list<value>()
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "price" + string("$2.99"));
+ const value val = mklist<value>(string("item"), string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i);
+
+ const lambda<bool()> pl= curry(lambda<bool(const string, const int, const value)>(postProc), testURI, count, val);
+ const lambda<bool()> ptl = postForkLoop(pl, procs);
+ double t = time(ptl, 0, 1) / (procs * count);
+ cout << "ATOMPub POST fork test " << t << " ms" << endl;
+
+ return true;
+}
+
+#endif
+
+const bool testPut() {
+ gc_scoped_pool pool;
+ const list<value> i = list<value>()
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "price" + string("$2.99"));
+ const list<value> a = mklist<value>(string("item"), string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i);
+ http::CURLSession ch;
+ value rc = content(http::put(a, testURI + "/111", ch));
+ assert(rc == value(true));
+ return true;
+}
+
+const bool testDel() {
+ gc_scoped_pool pool;
+ http::CURLSession ch;
+ value rc = content(http::del(testURI + "/111", ch));
+ assert(rc == value(true));
+ return true;
+}
+
+const bool testServer() {
+ tuscany::server::testGet();
+ tuscany::server::testPost();
+ tuscany::server::testPut();
+ tuscany::server::testDel();
+ tuscany::server::testEval();
+ tuscany::server::testGetPerf();
+ tuscany::server::testPostPerf();
+#ifdef WANT_THREADS
+ tuscany::server::testPostThreadPerf();
+#else
+ tuscany::server::testPostForkPerf();
+#endif
+ tuscany::server::testEvalPerf();
+ return true;
+}
+
+}
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/server/client-test.scm b/sca-cpp/branches/cpp-contrib/modules/server/client-test.scm
new file mode 100644
index 0000000000..47b799d390
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/server/client-test.scm
@@ -0,0 +1,38 @@
+; 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.
+
+; JSON-RPC test case
+
+(define (echo x ref) (ref "echo" x))
+
+; ATOMPub test case
+
+(define (get id ref)
+ (ref "get" id)
+)
+
+(define (post coll entry ref)
+ (ref "post" coll entry)
+)
+
+(define (put id entry ref)
+ (ref "put" id entry)
+)
+
+(define (delete id ref)
+ (ref "delete" id)
+)
diff --git a/sca-cpp/branches/cpp-contrib/modules/server/cpp-conf b/sca-cpp/branches/cpp-contrib/modules/server/cpp-conf
new file mode 100755
index 0000000000..b376585f8c
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/server/cpp-conf
@@ -0,0 +1,26 @@
+#!/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 C++ server conf
+here=`readlink -f $0`; here=`dirname $here`
+root=`readlink -f $1`
+
+cat >>$root/conf/httpd.conf <<EOF
+LoadModule mod_tuscany_eval $here/.libs/libmod_tuscany_eval.so
+EOF
diff --git a/sca-cpp/branches/cpp-contrib/modules/server/domain-test.composite b/sca-cpp/branches/cpp-contrib/modules/server/domain-test.composite
new file mode 100644
index 0000000000..ef85bb919b
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/server/domain-test.composite
@@ -0,0 +1,49 @@
+<?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/200912"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://domain/test"
+ name="domain-test">
+
+ <component name="scheme-test">
+ <t:implementation.scheme script="server-test.scm"/>
+ <service name="test">
+ <t:binding.http uri="test"/>
+ </service>
+ </component>
+
+ <component name="cpp-test">
+ <implementation.cpp path=".libs" library="libimpl-test"/>
+ <service name="cpp">
+ <t:binding.http uri="cpp"/>
+ </service>
+ </component>
+
+ <component name="client-test">
+ <t:implementation.scheme script="client-test.scm"/>
+ <service name="client">
+ <t:binding.http uri="client"/>
+ </service>
+ <reference name="ref" target="scheme-test">
+ <t:binding.http/>
+ </reference>
+ </component>
+
+</composite>
diff --git a/sca-cpp/branches/cpp-contrib/modules/server/htdocs/entry.xml b/sca-cpp/branches/cpp-contrib/modules/server/htdocs/entry.xml
new file mode 100644
index 0000000000..86b8a10547
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/server/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/branches/cpp-contrib/modules/server/htdocs/feed.xml b/sca-cpp/branches/cpp-contrib/modules/server/htdocs/feed.xml
new file mode 100644
index 0000000000..5e37de6580
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/server/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/branches/cpp-contrib/modules/server/htdocs/index.html b/sca-cpp/branches/cpp-contrib/modules/server/htdocs/index.html
new file mode 100644
index 0000000000..1bfb3e30c2
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/server/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/branches/cpp-contrib/modules/server/htdocs/json-request.txt b/sca-cpp/branches/cpp-contrib/modules/server/htdocs/json-request.txt
new file mode 100644
index 0000000000..b4bd07fc46
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/server/htdocs/json-request.txt
@@ -0,0 +1 @@
+{"id":1,"method":"echo","params":["Hello"]}
diff --git a/sca-cpp/branches/cpp-contrib/modules/server/htdocs/json-result.txt b/sca-cpp/branches/cpp-contrib/modules/server/htdocs/json-result.txt
new file mode 100644
index 0000000000..121bf74902
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/server/htdocs/json-result.txt
@@ -0,0 +1 @@
+{"id":1,"result":"Hello"} \ No newline at end of file
diff --git a/sca-cpp/branches/cpp-contrib/modules/server/httpd-test b/sca-cpp/branches/cpp-contrib/modules/server/httpd-test
new file mode 100755
index 0000000000..86718f96c5
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/server/httpd-test
@@ -0,0 +1,76 @@
+#!/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
+../http/httpd-conf tmp 8090 htdocs
+./server-conf tmp
+./scheme-conf tmp
+cat >>tmp/conf/httpd.conf <<EOF
+SCAContribution `pwd`/
+SCAComposite domain-test.composite
+EOF
+
+../http/httpd-start tmp
+sleep 2
+
+# 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
+../http/httpd-stop tmp
+sleep 2
+if [ "$rc" = "0" ]; then
+ echo "OK"
+fi
+return $rc
diff --git a/sca-cpp/branches/cpp-contrib/modules/server/impl-test.cpp b/sca-cpp/branches/cpp-contrib/modules/server/impl-test.cpp
new file mode 100644
index 0000000000..1bd0843745
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/server/impl-test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test component implementation.
+ */
+
+#include "string.hpp"
+
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+
+namespace tuscany {
+namespace server {
+
+const failable<value> get(unused const list<value>& params) {
+ return value(mklist<value>("text/html", mklist<value>("Hey")));
+}
+
+const failable<value> post(unused const list<value>& params) {
+ return value(mklist<value>(string("123456789")));
+}
+
+const failable<value> put(unused const list<value>& params) {
+ return value(true);
+}
+
+const failable<value> del(unused const list<value>& params) {
+ return value(true);
+}
+
+const failable<value> echo(const list<value>& params) {
+ return value(car(params));
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "get")
+ return tuscany::server::get(cdr(params));
+ if (func == "post")
+ return tuscany::server::post(cdr(params));
+ if (func == "put")
+ return tuscany::server::put(cdr(params));
+ if (func == "delete")
+ return tuscany::server::del(cdr(params));
+ if (func == "echo")
+ return tuscany::server::echo(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/server/mod-cpp.hpp b/sca-cpp/branches/cpp-contrib/modules/server/mod-cpp.hpp
new file mode 100644
index 0000000000..b3828dd7d2
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/server/mod-cpp.hpp
@@ -0,0 +1,102 @@
+/*
+ * 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_modcpp_hpp
+#define tuscany_modcpp_hpp
+
+/**
+ * Evaluation functions used by mod-eval to evaluate C++
+ * component implementations.
+ */
+
+#include "string.hpp"
+#include "stream.hpp"
+
+#include "function.hpp"
+#include "list.hpp"
+#include "element.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "dynlib.hpp"
+#include "../scheme/driver.hpp"
+
+namespace tuscany {
+namespace server {
+namespace modcpp {
+
+/**
+ * Apply a C++ component implementation function.
+ */
+const list<value> failableResult(const value& func, const list<value>& v) {
+ if (isNil(cdr(v)))
+ return v;
+
+ // Report a failure with an empty reason as 'function not supported'
+ // Except for the start, and stop functions, which are optional
+ const value reason = cadr(v);
+ if (length(reason) == 0) {
+ if (func == "start" || func == "stop")
+ return mklist<value>(lambda<value(const list<value>&)>());
+ return mklist<value>(value(), string("Function not supported: ") + func);
+ }
+ return v;
+}
+
+struct applyImplementation {
+ const lib ilib;
+ const lambda<value(const list<value>&)> impl;
+ const list<value> px;
+ applyImplementation(const lib& ilib, const lambda<value(const list<value>&)>& impl, const list<value>& px) : ilib(ilib), impl(impl), px(px) {
+ }
+ const value operator()(const list<value>& params) const {
+ debug(params, "modeval::cpp::applyImplementation::input");
+
+ // Apply the component implementation function
+ const value val = failableResult(car(params), impl(append(params, px)));
+
+ debug(val, "modeval::cpp::applyImplementation::result");
+ return val;
+ }
+};
+
+/**
+ * Evaluate a C++ component implementation and convert it to
+ * an applicable lambda function.
+ */
+const failable<lambda<value(const list<value>&)> > evalImplementation(const string& path, const value& impl, const list<value>& px) {
+
+ // Configure the implementation's lambda function
+ const value ipath(attributeValue("path", impl));
+ const value iname(attributeValue("library", impl));
+ const string fpath(isNil(ipath)? path + iname : path + ipath + "/" + iname);
+ const lib ilib(*(new (gc_new<lib>()) lib(fpath + dynlibExt)));
+ const failable<lambda<value(const list<value>&)> > evalf(dynlambda<value(const list<value>&)>("apply", ilib));
+ if (!hasContent(evalf))
+ return evalf;
+ const lambda<value(const list<value>&)> l(applyImplementation(ilib, content(evalf), px));
+ return l;
+}
+
+}
+}
+}
+
+#endif /* tuscany_modcpp_hpp */
diff --git a/sca-cpp/branches/cpp-contrib/modules/server/mod-eval.cpp b/sca-cpp/branches/cpp-contrib/modules/server/mod-eval.cpp
new file mode 100644
index 0000000000..a94ccf5bbe
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/server/mod-eval.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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 C++ and Scheme component implementations.
+ */
+
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "mod-eval.hpp"
+#include "mod-scheme.hpp"
+#include "mod-cpp.hpp"
+
+namespace tuscany {
+namespace server {
+namespace modeval {
+
+/**
+ * Apply a lifecycle start or restart event.
+ */
+const value applyLifecycle(unused const list<value>& params) {
+ // Return a nil function as we don't need to handle any subsequent events
+ return failable<value>(lambda<value(const list<value>&)>());
+}
+
+/**
+ * Evaluate a Scheme or C++ component implementation and convert it to an
+ * applicable lambda function.
+ */
+const failable<lambda<value(const list<value>&)> > evalImplementation(const string& path, const value& impl, const list<value>& px, unused const lambda<value(const list<value>&)>& lifecycle) {
+ const string itype(elementName(impl));
+ if (contains(itype, ".scheme"))
+ return modscheme::evalImplementation(path, impl, px);
+ if (contains(itype, ".cpp"))
+ return modcpp::evalImplementation(path, impl, px);
+ return mkfailure<lambda<value(const list<value>&)> >(string("Unsupported implementation type: ") + itype);
+}
+
+}
+}
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/server/mod-eval.hpp b/sca-cpp/branches/cpp-contrib/modules/server/mod-eval.hpp
new file mode 100644
index 0000000000..f319942f8d
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/server/mod-eval.hpp
@@ -0,0 +1,583 @@
+/*
+ * 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_modeval_hpp
+#define tuscany_modeval_hpp
+
+/**
+ * HTTPD module used to eval component implementations.
+ */
+
+#include "string.hpp"
+#include "stream.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "tree.hpp"
+#include "value.hpp"
+#include "element.hpp"
+#include "monad.hpp"
+#include "../atom/atom.hpp"
+#include "../json/json.hpp"
+#include "../scdl/scdl.hpp"
+#include "../http/curl.hpp"
+#include "../http/httpd.hpp"
+
+extern "C" {
+extern module AP_MODULE_DECLARE_DATA mod_tuscany_eval;
+}
+
+namespace tuscany {
+namespace server {
+namespace modeval {
+
+/**
+ * Server configuration.
+ */
+class ServerConf {
+public:
+ ServerConf(server_rec* s) : s(s), home(""), wiringServerName(""), contributionPath(""), compositeName("") {
+ }
+
+ const server_rec* s;
+ lambda<value(const list<value>&)> lifecycle;
+ string home;
+ string wiringServerName;
+ string contributionPath;
+ string compositeName;
+ list<value> implementations;
+ list<value> implTree;
+};
+
+/**
+ * Convert a result represented as a content + failure pair to a
+ * failable monad.
+ */
+const failable<value> failableResult(const list<value>& v) {
+ if (isNil(cdr(v)))
+ return car(v);
+ return mkfailure<value>(string(cadr(v)));
+}
+
+/**
+ * Handle an HTTP GET.
+ */
+const failable<int> get(request_rec* r, const lambda<value(const list<value>&)>& impl) {
+ debug(r->uri, "modeval::get::uri");
+
+ // Inspect the query string
+ const list<list<value> > args = httpd::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 = c_str(json::funcName(string(cadr(ma))));
+
+ // Apply the requested function
+ const failable<value> val = failableResult(impl(cons(func, httpd::queryParams(args))));
+ if (!hasContent(val))
+ return mkfailure<int>(reason(val));
+
+ // Return JSON result
+ json::JSONContext cx;
+ return httpd::writeResult(json::jsonResult(id, content(val), cx), "application/json-rpc", r);
+ }
+
+ // Evaluate the GET expression
+ const list<value> path(pathValues(r->uri));
+ const failable<value> val = failableResult(impl(cons<value>("get", mklist<value>(cddr(path)))));
+ if (!hasContent(val))
+ return mkfailure<int>(reason(val));
+ const value c = content(val);
+
+ // Write returned content-type / content-list pair
+ if (isList(cadr<value>(c)))
+ return httpd::writeResult(convertValues<string>(cadr<value>(c)), car<value>(c), r);
+
+ // Write returned ATOM feed or entry
+ if (isNil(cddr(path)))
+ return httpd::writeResult(atom::writeATOMFeed(atom::feedValuesToElements(c)), "application/atom+xml;type=feed", r);
+ else
+ return httpd::writeResult(atom::writeATOMEntry(atom::entryValuesToElements(c)), "application/atom+xml;type=entry", r);
+}
+
+/**
+ * Handle an HTTP POST.
+ */
+const failable<int> post(request_rec* r, const lambda<value(const list<value>&)>& impl) {
+ debug(r->uri, "modeval::post::url");
+
+ // Evaluate a JSON-RPC request and return a JSON result
+ const string ct = httpd::contentType(r);
+ if (contains(ct, "application/json-rpc") || contains(ct, "text/plain")) {
+
+ // Read the JSON request
+ const int rc = httpd::setupReadPolicy(r);
+ if(rc != OK)
+ return rc;
+ const list<string> ls = httpd::read(r);
+ debug(ls, "modeval::post::input");
+ json::JSONContext cx;
+ const list<value> json = elementsToValues(content(json::readJSON(ls, cx)));
+ const list<list<value> > args = httpd::postArgs(json);
+
+ // Extract the request id, method and params
+ const value id = cadr(assoc(value("id"), args));
+ const value func = c_str(json::funcName(cadr(assoc(value("method"), args))));
+ const list<value> params = (list<value>)cadr(assoc(value("params"), args));
+
+ // Evaluate the request expression
+ const failable<value> val = failableResult(impl(cons<value>(func, params)));
+ if (!hasContent(val))
+ return mkfailure<int>(reason(val));
+
+ // Return JSON result
+ return httpd::writeResult(json::jsonResult(id, content(val), cx), "application/json-rpc", r);
+ }
+
+ // Evaluate an ATOM POST request and return the location of the corresponding created resource
+ if (contains(ct, "application/atom+xml")) {
+
+ // Read the ATOM entry
+ const list<value> path(pathValues(r->uri));
+ const int rc = httpd::setupReadPolicy(r);
+ if(rc != OK)
+ return rc;
+ const list<string> ls = httpd::read(r);
+ debug(ls, "modeval::post::input");
+ const value entry = atom::entryValue(content(atom::readEntry(ls)));
+
+ // Evaluate the POST expression
+ const failable<value> val = failableResult(impl(cons<value>("post", mklist<value>(cddr(path), entry))));
+ if (!hasContent(val))
+ return mkfailure<int>(reason(val));
+
+ // Return the created resource location
+ debug(content(val), "modeval::post::location");
+ apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, httpd::url(content(val), r)));
+ r->status = HTTP_CREATED;
+ return OK;
+ }
+
+ // Unknown content type, wrap the HTTP request struct in a value and pass it to
+ // the component implementation function
+ const failable<value> val = failableResult(impl(cons<value>("handle", mklist<value>(httpd::requestValue(r)))));
+ if (!hasContent(val))
+ return mkfailure<int>(reason(val));
+ return (int)content(val);
+}
+
+/**
+ * Handle an HTTP PUT.
+ */
+const failable<int> put(request_rec* r, const lambda<value(const list<value>&)>& impl) {
+ debug(r->uri, "modeval::put::url");
+
+ // Read the ATOM entry
+ const list<value> path(pathValues(r->uri));
+ const int rc = httpd::setupReadPolicy(r);
+ if(rc != OK)
+ return rc;
+ const list<string> ls = httpd::read(r);
+ debug(ls, "modeval::put::input");
+ const value entry = atom::entryValue(content(atom::readEntry(ls)));
+
+ // Evaluate the PUT expression and update the corresponding resource
+ const failable<value> val = failableResult(impl(cons<value>("put", mklist<value>(cddr(path), entry))));
+ if (!hasContent(val))
+ return mkfailure<int>(reason(val));
+ if (val == value(false))
+ return HTTP_NOT_FOUND;
+ return OK;
+}
+
+/**
+ * Handle an HTTP DELETE.
+ */
+const failable<int> del(request_rec* r, const lambda<value(const list<value>&)>& impl) {
+ debug(r->uri, "modeval::delete::url");
+
+ // Evaluate an ATOM delete request
+ const list<value> path(pathValues(r->uri));
+ const failable<value> val = failableResult(impl(cons<value>("delete", mklist<value>(cddr(path)))));
+ if (!hasContent(val))
+ return mkfailure<int>(reason(val));
+ if (val == value(false))
+ return HTTP_NOT_FOUND;
+ return OK;
+}
+
+/**
+ * Translate a component request.
+ */
+int translate(request_rec *r) {
+ gc_scoped_pool pool(r->pool);
+ if (strncmp(r->uri, "/components/", 12) != 0)
+ return DECLINED;
+ r->handler = "mod_tuscany_eval";
+ return OK;
+}
+
+/**
+ * HTTP request handler.
+ */
+int handler(request_rec *r) {
+ gc_scoped_pool pool(r->pool);
+ if(strcmp(r->handler, "mod_tuscany_eval"))
+ return DECLINED;
+ httpdDebugRequest(r, "modeval::handler::input");
+
+ // Get the component implementation lambda
+ const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_eval);
+ const list<value> path(pathValues(r->uri));
+ const list<value> impl(assoctree<value>(cadr(path), sc.implTree));
+ if (isNil(impl))
+ return HTTP_NOT_FOUND;
+
+ // Handle HTTP method
+ const lambda<value(const list<value>&)> l(cadr<value>(impl));
+ if (r->header_only)
+ return OK;
+ if(r->method_number == M_GET)
+ return httpd::reportStatus(get(r, l));
+ if(r->method_number == M_POST)
+ return httpd::reportStatus(post(r, l));
+ if(r->method_number == M_PUT)
+ return httpd::reportStatus(put(r, l));
+ if(r->method_number == M_DELETE)
+ return httpd::reportStatus(del(r, l));
+ return HTTP_NOT_IMPLEMENTED;
+}
+
+/**
+ * Convert a list of component references to a list of HTTP proxy lambdas.
+ */
+const value mkrefProxy(const value& ref, const string& base) {
+ return lambda<value(const list<value>&)>(http::proxy(base + string(scdl::name(ref))));
+}
+
+const list<value> refProxies(const list<value>& refs, const string& base) {
+ if (isNil(refs))
+ return refs;
+ return cons(mkrefProxy(car(refs), base), refProxies(cdr(refs), base));
+}
+
+/**
+ * Convert a list of component properties to a list of lambda functions that just return
+ * the property value.
+ */
+struct propProxy {
+ const value v;
+ propProxy(const value& v) : v(v) {
+ }
+ const value operator()(unused const list<value>& params) const {
+ return v;
+ }
+};
+
+const value mkpropProxy(const value& prop) {
+ return lambda<value(const list<value>&)>(propProxy(elementValue(prop)));
+}
+
+const list<value> propProxies(const list<value>& props) {
+ if (isNil(props))
+ return props;
+ return cons(mkpropProxy(car(props)), propProxies(cdr(props)));
+}
+
+/**
+ * Evaluate a component and convert it to an applicable lambda function.
+ */
+const value evalComponent(ServerConf& sc, server_rec& server, const value& comp) {
+ extern const failable<lambda<value(const list<value>&)> > evalImplementation(const string& cpath, const value& impl, const list<value>& px, const lambda<value(const list<value>&)>& lifecycle);
+
+ const value impl = scdl::implementation(comp);
+
+ // Convert component references to configured proxy lambdas
+ ostringstream base;
+ if (sc.wiringServerName == "")
+ base << (server.server_scheme == NULL? "http" : server.server_scheme)
+ << "://" << (server.server_hostname == NULL? "localhost" : server.server_hostname)
+ << ":" << (server.port == 0? 80 : server.port)
+ << "/references/" << string(scdl::name(comp)) << "/";
+ else
+ base << sc.wiringServerName << "/references/" << string(scdl::name(comp)) << "/";
+ const list<value> rpx(refProxies(scdl::references(comp), str(base)));
+
+ // Convert component proxies to configured proxy lambdas
+ const list<value> ppx(propProxies(scdl::properties(comp)));
+
+ // Evaluate the component implementation and convert it to an applicable lambda function
+ const failable<lambda<value(const list<value>&)> > cimpl(evalImplementation(sc.contributionPath, impl, append(rpx, ppx), sc.lifecycle));
+ if (!hasContent(cimpl))
+ return reason(cimpl);
+ return content(cimpl);
+}
+
+/**
+ * Return a list of component-name + configured-implementation pairs.
+ */
+const list<value> componentToImplementationAssoc(ServerConf& sc, server_rec& server, const list<value>& c) {
+ if (isNil(c))
+ return c;
+ return cons<value>(mklist<value>(scdl::name(car(c)), evalComponent(sc, server, car(c))), componentToImplementationAssoc(sc, server, cdr(c)));
+}
+
+/**
+ * Read the components declared in a composite.
+ */
+const failable<list<value> > readComponents(const string& path) {
+ ifstream is(path);
+ if (fail(is))
+ return mkfailure<list<value> >(string("Could not read composite: ") + path);
+ return scdl::components(readXML(streamList(is)));
+}
+
+/**
+ * Apply a list of component implementations to a start or restart lifecycle expression.
+ * Return the functions returned by the component implementations.
+ */
+const failable<list<value> > applyLifecycleExpr(const list<value>& impls, const list<value>& expr) {
+ if (isNil(impls))
+ return list<value>();
+
+ // Evaluate lifecycle expression against a component implementation lambda
+ const lambda<value(const list<value>&)> l = cadr<value>(car(impls));
+ const failable<value> r = failableResult(l(expr));
+ if (!hasContent(r))
+ return mkfailure<list<value> >(reason(r));
+ const lambda<value(const list<value>&)> rl = content(r);
+
+ // Use the returned lambda function, if any, from now on
+ const lambda<value(const list<value>&)> al = isNil(rl)? l : rl;
+
+ // Continue with the rest of the list
+ const failable<list<value> > nr = applyLifecycleExpr(cdr(impls), expr);
+ if (!hasContent(nr))
+ return nr;
+ return cons<value>(mklist<value>(car<value>(car(impls)), value(al)), content(nr));
+}
+
+/**
+ * Configure the components declared in the deployed composite.
+ */
+const failable<bool> confComponents(ServerConf& sc, server_rec& server) {
+ if (sc.contributionPath == "" || sc.compositeName == "")
+ return false;
+
+ // Read the components and get their implementation lambda functions
+ const failable<list<value> > comps = readComponents(sc.contributionPath + sc.compositeName);
+ if (!hasContent(comps))
+ return mkfailure<bool>(reason(comps));
+ sc.implementations = componentToImplementationAssoc(sc, server, content(comps));
+ debug(sc.implementations, "modeval::confComponents::implementations");
+
+ // Store the implementation lambda functions in a tree for fast retrieval
+ sc.implTree = mkbtree(sort(sc.implementations));
+ return true;
+}
+
+/**
+ * Start the components declared in the deployed composite.
+ */
+const failable<bool> startComponents(ServerConf& sc) {
+
+ // Start the components and record the returned implementation lambda functions
+ debug(sc.implementations, "modeval::startComponents::start");
+ const failable<list<value> > impls = applyLifecycleExpr(sc.implementations, mklist<value>("start"));
+ if (!hasContent(impls))
+ return mkfailure<bool>(reason(impls));
+ sc.implementations = content(impls);
+ debug(sc.implementations, "modeval::startComponents::implementations");
+
+ // Store the implementation lambda functions in a tree for fast retrieval
+ sc.implTree = mkbtree(sort(sc.implementations));
+ return true;
+}
+
+/**
+ * Cleanup callback, called when the server is stopped or restarted.
+ */
+apr_status_t serverCleanup(void* v) {
+ gc_pool pool;
+ ServerConf& sc = *(ServerConf*)v;
+ debug("modeval::serverCleanup");
+
+ // Stop the component implementations
+ applyLifecycleExpr(sc.implementations, mklist<value>("stop"));
+
+ // Call the module lifecycle function
+ if (isNil(sc.lifecycle))
+ return APR_SUCCESS;
+ debug("modeval::serverCleanup::stop");
+ sc.lifecycle(mklist<value>("stop"));
+
+ return APR_SUCCESS;
+}
+
+/**
+ * Called after all the configuration commands have been run.
+ * Process the server configuration and configure the deployed components.
+ */
+int postConfig(apr_pool_t *p, unused apr_pool_t *plog, unused apr_pool_t *ptemp, server_rec *s) {
+ extern const value applyLifecycle(const list<value>&);
+
+ gc_scoped_pool pool(p);
+ ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_eval);
+
+ // Count the calls to post config
+ const string k("tuscany::modeval::postConfig");
+ const int count = (int)httpd::userData(k, s);
+ httpd::putUserData(k, (void*)(count + 1), s);
+
+ // Count == 0, do nothing as post config is always called twice,
+ // count == 1 is the first start, count > 1 is a restart
+ if (count == 0)
+ return OK;
+ if (count == 1) {
+ debug("modeval::postConfig::start");
+ const failable<value> r = failableResult(applyLifecycle(mklist<value>("start")));
+ if (!hasContent(r))
+ return -1;
+ sc.lifecycle = content(r);
+ }
+ if (count > 1) {
+ debug("modeval::postConfig::restart");
+ const failable<value> r = failableResult(applyLifecycle(mklist<value>("restart")));
+ if (!hasContent(r))
+ return -1;
+ sc.lifecycle = content(r);
+ }
+
+ // Configure the deployed components
+ debug(sc.wiringServerName, "modeval::postConfig::wiringServerName");
+ debug(sc.contributionPath, "modeval::postConfig::contributionPath");
+ debug(sc.compositeName, "modeval::postConfig::compositeName");
+ const failable<bool> res = confComponents(sc, *s);
+ if (!hasContent(res)) {
+ cerr << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << endl;
+ return -1;
+ }
+
+ // Register a cleanup callback, called when the server is stopped or restarted
+ apr_pool_pre_cleanup_register(p, (void*)&sc, serverCleanup);
+
+ return OK;
+}
+
+/**
+ * Child process initialization.
+ */
+void childInit(apr_pool_t* p, server_rec* s) {
+ gc_scoped_pool pool(p);
+ ServerConf* sc = (ServerConf*)ap_get_module_config(s->module_config, &mod_tuscany_eval);
+ if(sc == NULL) {
+ cerr << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << endl;
+ exit(APEXIT_CHILDFATAL);
+ }
+
+ // Start the components in the child process
+ const failable<bool> res = startComponents(*sc);
+ if (!hasContent(res)) {
+ cerr << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << endl;
+ exit(APEXIT_CHILDFATAL);
+ }
+
+ // Register a cleanup callback, called when the child is stopped or restarted
+ apr_pool_pre_cleanup_register(p, (void*)sc, serverCleanup);
+}
+
+/**
+ * Configuration commands.
+ */
+const char* confHome(cmd_parms *cmd, unused void *c, const char *arg) {
+ gc_scoped_pool pool(cmd->pool);
+ ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval);
+ sc.home = arg;
+ return NULL;
+}
+const char* confWiringServerName(cmd_parms *cmd, unused void *c, const char *arg) {
+ gc_scoped_pool pool(cmd->pool);
+ ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval);
+ sc.wiringServerName = arg;
+ return NULL;
+}
+const char* confContribution(cmd_parms *cmd, unused void *c, const char *arg) {
+ gc_scoped_pool pool(cmd->pool);
+ ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval);
+ sc.contributionPath = arg;
+ return NULL;
+}
+const char* confComposite(cmd_parms *cmd, unused void *c, const char *arg) {
+ gc_scoped_pool pool(cmd->pool);
+ ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval);
+ sc.compositeName = arg;
+ return NULL;
+}
+
+const char* confEnv(unused cmd_parms *cmd, unused void *c, const char *name, const char *value) {
+ gc_scoped_pool pool(cmd->pool);
+ setenv(name, value != NULL? value : "", 1);
+ return NULL;
+}
+
+/**
+ * HTTP server module declaration.
+ */
+const command_rec commands[] = {
+ AP_INIT_TAKE1("TuscanyHome", (const char*(*)())confHome, NULL, RSRC_CONF, "Tuscany home directory"),
+ AP_INIT_TAKE1("SCAWiringServerName", (const char*(*)())confWiringServerName, NULL, RSRC_CONF, "SCA wiring server name"),
+ AP_INIT_TAKE1("SCAContribution", (const char*(*)())confContribution, NULL, RSRC_CONF, "SCA contribution location"),
+ AP_INIT_TAKE1("SCAComposite", (const char*(*)())confComposite, NULL, RSRC_CONF, "SCA composite location"),
+ AP_INIT_TAKE12("SetEnv", (const char*(*)())confEnv, NULL, OR_FILEINFO, "Environment variable name and optional value"),
+ {NULL, NULL, NULL, 0, NO_ARGS, NULL}
+};
+
+
+void registerHooks(unused apr_pool_t *p) {
+ ap_hook_post_config(postConfig, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_child_init(childInit, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_handler(handler, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_translate_name(translate, NULL, NULL, APR_HOOK_FIRST);
+}
+
+}
+}
+}
+
+extern "C" {
+
+module AP_MODULE_DECLARE_DATA mod_tuscany_eval = {
+ STANDARD20_MODULE_STUFF,
+ // dir config and merger
+ NULL, NULL,
+ // server config and merger
+ tuscany::httpd::makeServerConf<tuscany::server::modeval::ServerConf>, NULL,
+ // commands and hooks
+ tuscany::server::modeval::commands, tuscany::server::modeval::registerHooks
+};
+
+}
+
+#endif
diff --git a/sca-cpp/branches/cpp-contrib/modules/server/mod-scheme.hpp b/sca-cpp/branches/cpp-contrib/modules/server/mod-scheme.hpp
new file mode 100644
index 0000000000..f5b9554c3f
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/server/mod-scheme.hpp
@@ -0,0 +1,89 @@
+/*
+ * 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_modscheme_hpp
+#define tuscany_modscheme_hpp
+
+/**
+ * Evaluation functions used by mod-eval to evaluate Scheme
+ * component implementations.
+ */
+
+#include "string.hpp"
+#include "stream.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "../scheme/eval.hpp"
+
+namespace tuscany {
+namespace server {
+namespace modscheme {
+
+/**
+ * Convert proxy lambdas to evaluator primitive procedures.
+ */
+const list<value> primitiveProcedures(const list<value>& l) {
+ if (isNil(l))
+ return l;
+ return cons<value>(mklist<value>(scheme::primitiveSymbol, car(l)), primitiveProcedures(cdr(l)));
+}
+
+/**
+ * Apply a Scheme component implementation function.
+ */
+struct applyImplementation {
+ const value impl;
+ const list<value> px;
+ applyImplementation(const value& impl, const list<value>& px) : impl(impl), px(scheme::quotedParameters(primitiveProcedures(px))) {
+ }
+ const value operator()(const list<value>& params) const {
+ const value expr = cons<value>(car(params), append(scheme::quotedParameters(cdr(params)), px));
+ debug(expr, "modeval::scheme::applyImplementation::input");
+ scheme::Env env = scheme::setupEnvironment();
+ const value res = scheme::evalScript(expr, impl, env);
+ const value val = isNil(res)? mklist<value>(value(), string("Could not evaluate expression")) : mklist<value>(res);
+ debug(val, "modeval::scheme::applyImplementation::result");
+ return val;
+ }
+};
+
+/**
+ * Evaluate a Scheme component implementation and convert it to an
+ * applicable lambda function.
+ */
+const failable<lambda<value(const list<value>&)> > evalImplementation(const string& path, const value& impl, const list<value>& px) {
+ const string fpath(path + attributeValue("script", impl));
+ ifstream is(fpath);
+ if (fail(is))
+ return mkfailure<lambda<value(const list<value>&)> >(string("Could not read implementation: ") + fpath);
+ const value script = scheme::readScript(is);
+ if (isNil(script))
+ return mkfailure<lambda<value(const list<value>&)> >(string("Could not read implementation: ") + fpath);
+ return lambda<value(const list<value>&)>(applyImplementation(script, px));
+}
+
+}
+}
+}
+
+#endif /* tuscany_modscheme_hpp */
diff --git a/sca-cpp/branches/cpp-contrib/modules/server/mod-wiring.cpp b/sca-cpp/branches/cpp-contrib/modules/server/mod-wiring.cpp
new file mode 100644
index 0000000000..c21b0fe254
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/server/mod-wiring.cpp
@@ -0,0 +1,383 @@
+/*
+ * 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 component references and route requests to
+ * target service components.
+ */
+
+#include <sys/stat.h>
+
+#include "string.hpp"
+#include "stream.hpp"
+#include "list.hpp"
+#include "tree.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "../scdl/scdl.hpp"
+#include "../http/httpd.hpp"
+
+extern "C" {
+extern module AP_MODULE_DECLARE_DATA mod_tuscany_wiring;
+}
+
+namespace tuscany {
+namespace server {
+namespace modwiring {
+
+/**
+ * Server configuration.
+ */
+class ServerConf {
+public:
+ ServerConf(server_rec* s) : s(s), start(false), home(""), wiringServerName(""), contributionPath(""), compositeName("") {
+ }
+ const server_rec* s;
+ bool start;
+ string home;
+ string wiringServerName;
+ string contributionPath;
+ string compositeName;
+ list<value> references;
+ list<value> services;
+};
+
+/**
+ * Set to true to wire using mod_proxy, false to wire using HTTP client redirects.
+ */
+const bool useModProxy = true;
+
+/**
+ * Returns true if a URI is absolute.
+ */
+const bool isAbsolute(const string& uri) {
+ return contains(uri, "://");
+}
+
+/**
+ * Route a /references/component-name/reference-name request,
+ * to the target of the component reference.
+ */
+int translateReference(request_rec *r) {
+ httpdDebugRequest(r, "modwiring::translateReference::input");
+ debug(r->uri, "modwiring::translateReference::uri");
+
+ // Find the requested component
+ const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_wiring);
+ const list<value> rpath(pathValues(r->uri));
+ const list<value> comp(assoctree(cadr(rpath), sc.references));
+ if (isNil(comp))
+ return HTTP_NOT_FOUND;
+
+ // Find the requested reference and target configuration
+ const list<value> ref(assoctree<value>(caddr(rpath), cadr(comp)));
+ if (isNil(ref))
+ return HTTP_NOT_FOUND;
+ const string target(cadr(ref));
+ debug(target, "modwiring::translateReference::target");
+
+ // Route to an absolute target URI using mod_proxy or an HTTP client redirect
+ if (isAbsolute(target)) {
+ if (useModProxy) {
+ r->filename = apr_pstrdup(r->pool, c_str(string("proxy:") + target));
+ r->proxyreq = PROXYREQ_REVERSE;
+ r->handler = "proxy-server";
+ return OK;
+ }
+
+ r->status = HTTP_MOVED_TEMPORARILY;
+ apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, c_str(target)));
+ r->handler = "mod_tuscany_wiring";
+ return OK;
+ }
+
+ // Route to a relative target URI using a local internal redirect
+ r->filename = apr_pstrdup(r->pool, c_str(string("/redirect:/components/") + target));
+ r->handler = "mod_tuscany_wiring";
+ return OK;
+}
+
+/**
+ * Find a leaf matching a request path in a tree of URI paths.
+ */
+const int matchPath(const list<value>& k, const list<value>& p) {
+ if (isNil(p))
+ return true;
+ if (isNil(k))
+ return false;
+ if (car(k) != car(p))
+ return false;
+ return matchPath(cdr(k), cdr(p));
+}
+
+const list<value> assocPath(const value& k, const list<value>& tree) {
+ if (isNil(tree))
+ return tree;
+ if (matchPath(k, car<value>(car(tree))))
+ return car(tree);
+ if (k < car<value>(car(tree)))
+ return assocPath(k, cadr(tree));
+ return assocPath(k, caddr(tree));
+}
+
+/**
+ * Route a service request to the component providing the requested service.
+ */
+int translateService(request_rec *r) {
+ httpdDebugRequest(r, "modwiring::translateService::input");
+ debug(r->uri, "modwiring::translateService::uri");
+
+ // Find the requested component
+ const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_wiring);
+ const list<value> p(pathValues(r->uri));
+ const list<value> svc(assocPath(p, sc.services));
+ if (isNil(svc))
+ return DECLINED;
+ debug(svc, "modwiring::translateService::service");
+
+ // Build a component-name + path-info URI
+ const list<value> target(cons<value>(cadr(svc), httpd::pathInfo(p, car(svc))));
+ debug(target, "modwiring::translateService::target");
+
+ // Dispatch to the target component using a local internal redirect
+ const string tp(path(target));
+ debug(tp, "modwiring::translateService::path");
+ const string redir(string("/redirect:/components") + tp);
+ debug(redir, "modwiring::translateService::redirect");
+ r->filename = apr_pstrdup(r->pool, c_str(redir));
+ r->handler = "mod_tuscany_wiring";
+ return OK;
+}
+
+/**
+ * Translate an HTTP service or reference request and route it
+ * to the target component.
+ */
+int translate(request_rec *r) {
+ gc_scoped_pool pool(r->pool);
+ if (!strncmp(r->uri, "/components/", 12) != 0)
+ return DECLINED;
+
+ // Translate a component reference request
+ if (!strncmp(r->uri, "/references/", 12) != 0)
+ return translateReference(r);
+
+ // Translate a service request
+ return translateService(r);
+}
+
+/**
+ * HTTP request handler, redirect to a target component.
+ */
+int handler(request_rec *r) {
+ gc_scoped_pool pool(r->pool);
+ if(strcmp(r->handler, "mod_tuscany_wiring"))
+ return DECLINED;
+ httpdDebugRequest(r, "modwiring::handler::input");
+
+ // Do an internal redirect
+ if (r->filename == NULL || strncmp(r->filename, "/redirect:", 10) != 0)
+ return DECLINED;
+ debug(r->uri, "modwiring::handler::uri");
+ debug(r->filename, "modwiring::handler::filename");
+ debug(r->path_info, "modwiring::handler::path info");
+
+ if (r->args == NULL)
+ return httpd::internalRedirect(httpd::redirectURI(string(r->filename + 10), string(r->path_info)), r);
+ return httpd::internalRedirect(httpd::redirectURI(string(r->filename + 10), string(r->path_info), string(r->args)), r);
+}
+
+/**
+ * Read the components declared in a composite.
+ */
+const failable<list<value> > readComponents(const string& path) {
+ ifstream is(path);
+ if (fail(is))
+ return mkfailure<list<value> >(string("Could not read composite: ") + path);
+ return scdl::components(readXML(streamList(is)));
+}
+
+/**
+ * Return a list of component-name + references pairs. The references are
+ * arranged in trees of reference-name + reference-target pairs.
+ */
+const list<value> componentReferenceToTargetTree(const value& c) {
+ return mklist<value>(scdl::name(c), mkbtree(sort(scdl::referenceToTargetAssoc(scdl::references(c)))));
+}
+
+const list<value> componentReferenceToTargetAssoc(const list<value>& c) {
+ if (isNil(c))
+ return c;
+ return cons<value>(componentReferenceToTargetTree(car(c)), componentReferenceToTargetAssoc(cdr(c)));
+}
+
+/**
+ * Return a list of service-URI-path + component-name pairs. Service-URI-paths are
+ * represented as lists of URI path fragments.
+ */
+const list<value> defaultBindingURI(const string& cn, const string& sn) {
+ return mklist<value>(cn, sn);
+}
+
+const list<value> bindingToComponentAssoc(const string& cn, const string& sn, const list<value>& b) {
+ if (isNil(b))
+ return b;
+ const value uri(scdl::uri(car(b)));
+ if (isNil(uri))
+ return cons<value>(mklist<value>(defaultBindingURI(cn, sn), cn), bindingToComponentAssoc(cn, sn, cdr(b)));
+ return cons<value>(mklist<value>(pathValues(c_str(string(uri))), cn), bindingToComponentAssoc(cn, sn, cdr(b)));
+}
+
+const list<value> serviceToComponentAssoc(const string& cn, const list<value>& s) {
+ if (isNil(s))
+ return s;
+ const string sn(scdl::name(car(s)));
+ const list<value> btoc(bindingToComponentAssoc(cn, sn, scdl::bindings(car(s))));
+ if (isNil(btoc))
+ return cons<value>(mklist<value>(defaultBindingURI(cn, sn), cn), serviceToComponentAssoc(cn, cdr(s)));
+ return append<value>(btoc, serviceToComponentAssoc(cn, cdr(s)));
+}
+
+const list<value> uriToComponentAssoc(const list<value>& c) {
+ if (isNil(c))
+ return c;
+ return append<value>(serviceToComponentAssoc(scdl::name(car(c)), scdl::services(car(c))), uriToComponentAssoc(cdr(c)));
+}
+
+/**
+ * Configure the components declared in the server's deployment composite.
+ */
+const bool confComponents(ServerConf& sc) {
+ if (sc.contributionPath == "" || sc.compositeName == "")
+ return true;
+
+ // Read the component configuration and store the references and service URIs
+ // in trees for fast retrieval later
+ const failable<list<value> > comps = readComponents(sc.contributionPath + sc.compositeName);
+ if (!hasContent(comps))
+ return true;
+ const list<value> refs = componentReferenceToTargetAssoc(content(comps));
+ debug(refs, "modwiring::confComponents::references");
+ sc.references = mkbtree(sort(refs));
+
+ const list<value> svcs = uriToComponentAssoc(content(comps));
+ debug(svcs, "modwiring::confComponents::services");
+ sc.services = mkbtree(sort(svcs));
+ return true;
+}
+
+/**
+ * Called after all the configuration commands have been run.
+ * Process the server configuration and configure the wiring for the deployed components.
+ */
+int postConfig(unused apr_pool_t *p, unused apr_pool_t *plog, unused apr_pool_t *ptemp, server_rec *s) {
+ // Count the calls to post config, skip the first one as
+ // postConfig is always called twice
+ const string k("tuscany::modwiring::postConfig");
+ const int count = (int)httpd::userData(k, s);
+ httpd::putUserData(k, (void*)(count + 1), s);
+ if (count == 0)
+ return OK;
+
+ // Configure the wiring for the deployed components
+ ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_wiring);
+ debug(sc.wiringServerName, "modwiring::postConfig::wiringServerName");
+ debug(sc.contributionPath, "modwiring::postConfig::contributionPath");
+ debug(sc.compositeName, "modwiring::postConfig::compositeName");
+ confComponents(sc);
+ return OK;
+}
+
+/**
+ * Child process initialization.
+ */
+void childInit(apr_pool_t* p, server_rec* svr_rec) {
+ gc_scoped_pool pool(p);
+ ServerConf *conf = (ServerConf*)ap_get_module_config(svr_rec->module_config, &mod_tuscany_wiring);
+ if(conf == NULL) {
+ cerr << "[Tuscany] Due to one or more errors mod_tuscany_wiring loading failed. Causing apache to stop loading." << endl;
+ exit(APEXIT_CHILDFATAL);
+ }
+}
+
+/**
+ * Configuration commands.
+ */
+const char *confHome(cmd_parms *cmd, unused void *c, const char *arg) {
+ gc_scoped_pool pool(cmd->pool);
+ ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_wiring);
+ sc.home = arg;
+ return NULL;
+}
+const char *confWiringServerName(cmd_parms *cmd, unused void *c, const char *arg) {
+ gc_scoped_pool pool(cmd->pool);
+ ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_wiring);
+ sc.wiringServerName = arg;
+ return NULL;
+}
+const char *confContribution(cmd_parms *cmd, unused void *c, const char *arg) {
+ gc_scoped_pool pool(cmd->pool);
+ ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_wiring);
+ sc.contributionPath = arg;
+ return NULL;
+}
+const char *confComposite(cmd_parms *cmd, unused void *c, const char *arg) {
+ gc_scoped_pool pool(cmd->pool);
+ ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_wiring);
+ sc.compositeName = arg;
+ return NULL;
+}
+
+/**
+ * HTTP server module declaration.
+ */
+const command_rec commands[] = {
+ AP_INIT_TAKE1("TuscanyHome", (const char*(*)())confHome, NULL, RSRC_CONF, "Tuscany home directory"),
+ AP_INIT_TAKE1("SCAWiringServerName", (const char*(*)())confWiringServerName, NULL, RSRC_CONF, "SCA wiring server name"),
+ AP_INIT_TAKE1("SCAContribution", (const char*(*)())confContribution, NULL, RSRC_CONF, "SCA contribution location"),
+ AP_INIT_TAKE1("SCAComposite", (const char*(*)())confComposite, NULL, RSRC_CONF, "SCA composite location"),
+ {NULL, NULL, NULL, 0, NO_ARGS, NULL}
+};
+
+void registerHooks(unused apr_pool_t *p) {
+ ap_hook_post_config(postConfig, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_child_init(childInit, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_handler(handler, NULL, NULL, APR_HOOK_MIDDLE);
+ 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 and merger
+ NULL, NULL,
+ // server config and merger
+ tuscany::httpd::makeServerConf<tuscany::server::modwiring::ServerConf>, NULL,
+ // commands and hooks
+ tuscany::server::modwiring::commands, tuscany::server::modwiring::registerHooks
+};
+
+}
diff --git a/sca-cpp/branches/cpp-contrib/modules/server/scheme-conf b/sca-cpp/branches/cpp-contrib/modules/server/scheme-conf
new file mode 100755
index 0000000000..248255608e
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/server/scheme-conf
@@ -0,0 +1,26 @@
+#!/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 Scheme server conf
+here=`readlink -f $0`; here=`dirname $here`
+root=`readlink -f $1`
+
+cat >>$root/conf/httpd.conf <<EOF
+LoadModule mod_tuscany_eval $here/.libs/libmod_tuscany_eval.so
+EOF
diff --git a/sca-cpp/branches/cpp-contrib/modules/server/server-conf b/sca-cpp/branches/cpp-contrib/modules/server/server-conf
new file mode 100755
index 0000000000..ae175ee2f8
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/server/server-conf
@@ -0,0 +1,29 @@
+#!/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 server conf
+here=`readlink -f $0`; here=`dirname $here`
+root=`readlink -f $1`
+
+mkdir -p $root
+mkdir -p $root/logs
+mkdir -p $root/conf
+cat >>$root/conf/httpd.conf <<EOF
+LoadModule mod_tuscany_wiring $here/.libs/libmod_tuscany_wiring.so
+EOF
diff --git a/sca-cpp/branches/cpp-contrib/modules/server/server-test b/sca-cpp/branches/cpp-contrib/modules/server/server-test
new file mode 100755
index 0000000000..e1d9932a5d
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/server/server-test
@@ -0,0 +1,39 @@
+#!/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
+../http/httpd-conf tmp 8090 htdocs
+./server-conf tmp
+./scheme-conf tmp
+cat >>tmp/conf/httpd.conf <<EOF
+SCAContribution `pwd`/
+SCAComposite domain-test.composite
+EOF
+
+../http/httpd-start tmp
+sleep 2
+
+# Test
+./client-test 2>/dev/null
+rc=$?
+
+# Cleanup
+../http/httpd-stop tmp
+sleep 2
+return $rc
diff --git a/sca-cpp/branches/cpp-contrib/modules/server/server-test.scm b/sca-cpp/branches/cpp-contrib/modules/server/server-test.scm
new file mode 100644
index 0000000000..5d545ecf8b
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/server/server-test.scm
@@ -0,0 +1,44 @@
+; 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.
+
+; JSON-RPC test case
+
+(define (echo x) x)
+
+; ATOMPub test case
+
+(define (get id)
+ (if (nul id)
+ '("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))))
+
+ (list "Item" (car id) '((javaClass "services.Item") (name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99))))
+)
+
+(define (post collection item)
+ '("123456789")
+)
+
+(define (put id item)
+ true
+)
+
+(define (delete id)
+ true
+)
diff --git a/sca-cpp/branches/cpp-contrib/modules/server/wiring-test b/sca-cpp/branches/cpp-contrib/modules/server/wiring-test
new file mode 100755
index 0000000000..0deab33d68
--- /dev/null
+++ b/sca-cpp/branches/cpp-contrib/modules/server/wiring-test
@@ -0,0 +1,76 @@
+#!/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
+../http/httpd-conf tmp 8090 htdocs
+./server-conf tmp
+./scheme-conf tmp
+cat >>tmp/conf/httpd.conf <<EOF
+SCAContribution `pwd`/
+SCAComposite domain-test.composite
+EOF
+
+../http/httpd-start tmp
+sleep 2
+
+# 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/client/ >tmp/feed.xml 2>/dev/null
+ diff tmp/feed.xml htdocs/feed.xml
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/client/111 >tmp/entry.xml 2>/dev/null
+ diff tmp/entry.xml htdocs/entry.xml
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/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:8090/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:8090/client/111 -X DELETE 2>/dev/null
+ rc=$?
+fi
+
+# Test JSON-RPC
+if [ "$rc" = "0" ]; then
+ curl http://localhost:8090/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
+../http/httpd-stop tmp
+sleep 2
+if [ "$rc" = "0" ]; then
+ echo "OK"
+fi
+return $rc