summaryrefslogtreecommitdiffstats
path: root/sandbox/sebastien/cpp/apr-2/modules/server
diff options
context:
space:
mode:
Diffstat (limited to 'sandbox/sebastien/cpp/apr-2/modules/server')
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/server/Makefile.am55
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/server/client-test.cpp39
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/server/client-test.hpp350
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/server/client-test.scm38
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/modules/server/cpp-conf30
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/server/domain-test.composite49
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/server/htdocs/index.html21
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/entry.xml2
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/feed.xml2
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/json-request.txt1
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/json-result.txt1
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/server/htdocs/wiring/ref.js574
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/modules/server/httpd-test78
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/server/impl-test.cpp76
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/server/mod-cpp.hpp104
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/server/mod-eval.cpp62
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/server/mod-eval.hpp896
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/server/mod-scheme.hpp91
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/server/mod-wiring.cpp472
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/modules/server/scheme-conf30
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/modules/server/server-conf103
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/modules/server/server-test39
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/server/server-test.scm44
-rwxr-xr-xsandbox/sebastien/cpp/apr-2/modules/server/wiring-test78
24 files changed, 3235 insertions, 0 deletions
diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/Makefile.am b/sandbox/sebastien/cpp/apr-2/modules/server/Makefile.am
new file mode 100644
index 0000000000..30c89da85d
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/server/Makefile.am
@@ -0,0 +1,55 @@
+# 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.
+
+INCLUDES = -I${HTTPD_INCLUDE}
+
+incl_HEADERS = *.hpp
+incldir = $(prefix)/include/modules/server
+
+dist_mod_SCRIPTS = cpp-conf scheme-conf server-conf
+moddir = $(prefix)/modules/server
+
+EXTRA_DIST = domain-test.composite client-test.scm server-test.scm htdocs/*.xml htdocs/*.txt htdocs/*.html
+
+mod_LTLIBRARIES = libmod_tuscany_eval.la libmod_tuscany_wiring.la
+noinst_DATA = libmod_tuscany_eval.so libmod_tuscany_wiring.so
+
+libmod_tuscany_eval_la_SOURCES = mod-eval.cpp
+libmod_tuscany_eval_la_LDFLAGS = -lxml2 -lcurl -lmozjs
+libmod_tuscany_eval.so:
+ ln -s .libs/libmod_tuscany_eval.so
+
+libmod_tuscany_wiring_la_SOURCES = mod-wiring.cpp
+libmod_tuscany_wiring_la_LDFLAGS = -lxml2 -lcurl -lmozjs
+libmod_tuscany_wiring.so:
+ ln -s .libs/libmod_tuscany_wiring.so
+
+noinst_test_LTLIBRARIES = libimpl-test.la
+noinst_testdir = `pwd`/tmp
+noinst_DATA += libimpl-test.so
+
+libimpl_test_la_SOURCES = impl-test.cpp
+libimpl-test.so:
+ ln -s .libs/libimpl-test.so
+
+client_test_SOURCES = client-test.cpp
+client_test_LDFLAGS = -lxml2 -lcurl -lmozjs
+
+dist_noinst_SCRIPTS = httpd-test server-test wiring-test
+noinst_PROGRAMS = client-test
+TESTS = httpd-test server-test wiring-test
+
diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/client-test.cpp b/sandbox/sebastien/cpp/apr-2/modules/server/client-test.cpp
new file mode 100644
index 0000000000..5de7ab6e7b
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/server/client-test.cpp
@@ -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.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test HTTP client functions.
+ */
+
+#include "stream.hpp"
+#include "string.hpp"
+#include "client-test.hpp"
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+ tuscany::server::testURI = "http://localhost:8090/test";
+
+ tuscany::server::testServer();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/client-test.hpp b/sandbox/sebastien/cpp/apr-2/modules/server/client-test.hpp
new file mode 100644
index 0000000000..c17f012012
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/server/client-test.hpp
@@ -0,0 +1,350 @@
+/*
+ * 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/http.hpp"
+
+namespace tuscany {
+namespace server {
+
+string testURI = "http://localhost:8090/test";
+bool testBlobs = true;
+
+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/index.html", ch);
+ assert(hasContent(r));
+ assert(contains(str(os), "HTTP/1.1 200") || contains(str(os), "HTTP/1.0 200"));
+ assert(contains(str(os), "It works"));
+ }
+ {
+ const failable<value> r = http::getcontent("http://localhost:8090/index.html", 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/index.html", 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;
+
+ if (testBlobs) {
+ 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;
+ }
+ if (testBlobs) {
+ 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/sandbox/sebastien/cpp/apr-2/modules/server/client-test.scm b/sandbox/sebastien/cpp/apr-2/modules/server/client-test.scm
new file mode 100644
index 0000000000..47b799d390
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/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/sandbox/sebastien/cpp/apr-2/modules/server/cpp-conf b/sandbox/sebastien/cpp/apr-2/modules/server/cpp-conf
new file mode 100755
index 0000000000..086bb49d38
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/server/cpp-conf
@@ -0,0 +1,30 @@
+#!/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`
+mkdir -p $1
+root=`readlink -f $1`
+
+cat >>$root/conf/modules.conf <<EOF
+# Generated by: cpp-conf $*
+# Support for C++ SCA components
+LoadModule mod_tuscany_eval $here/libmod_tuscany_eval.so
+
+EOF
diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/domain-test.composite b/sandbox/sebastien/cpp/apr-2/modules/server/domain-test.composite
new file mode 100644
index 0000000000..048c451ef6
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/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="." 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/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/index.html b/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/index.html
new file mode 100644
index 0000000000..1bfb3e30c2
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/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/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/entry.xml b/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/entry.xml
new file mode 100644
index 0000000000..6528c793e3
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/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><name>Apple</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>2.99</price></item></content><link href="111"/></entry>
diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/feed.xml b/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/feed.xml
new file mode 100644
index 0000000000..bcb304f9c2
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/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><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><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><name>Pear</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>1.55</price></item></content><link href="333"/></entry></feed>
diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/json-request.txt b/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/json-request.txt
new file mode 100644
index 0000000000..b4bd07fc46
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/json-request.txt
@@ -0,0 +1 @@
+{"id":1,"method":"echo","params":["Hello"]}
diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/json-result.txt b/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/json-result.txt
new file mode 100644
index 0000000000..121bf74902
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/test/json-result.txt
@@ -0,0 +1 @@
+{"id":1,"result":"Hello"} \ No newline at end of file
diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/wiring/ref.js b/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/wiring/ref.js
new file mode 100644
index 0000000000..95a84c01a5
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/server/htdocs/wiring/ref.js
@@ -0,0 +1,574 @@
+/*
+ * 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.
+ *
+ * The JSON-RPC client code is based on Jan-Klaas' JavaScript
+ * o lait library (jsolait).
+ *
+ * $Id: jsonrpc.js,v 1.36.2.3 2006/03/08 15:09:37 mclark Exp $
+ *
+ * Copyright (c) 2003-2004 Jan-Klaas Kollhof
+ * Copyright (c) 2005 Michael Clark, Metaparadigm Pte Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ */
+
+/**
+ * Escape a character.
+ */
+var JSONClient = new Object();
+
+JSONClient.escapeJSONChar = function(c) {
+ if(c == "\"" || c == "\\") return "\\" + c;
+ else if (c == "\b") return "\\b";
+ else if (c == "\f") return "\\f";
+ else if (c == "\n") return "\\n";
+ else if (c == "\r") return "\\r";
+ else if (c == "\t") return "\\t";
+ var hex = c.charCodeAt(0).toString(16);
+ if(hex.length == 1) return "\\u000" + hex;
+ else if(hex.length == 2) return "\\u00" + hex;
+ else if(hex.length == 3) return "\\u0" + hex;
+ else return "\\u" + hex;
+};
+
+/**
+ * Encode a string into JSON format.
+ */
+JSONClient.escapeJSONString = function(s) {
+ /* The following should suffice but Safari's regex is broken
+ (doesn't support callback substitutions)
+ return "\"" + s.replace(/([^\u0020-\u007f]|[\\\"])/g,
+ JSONClient.escapeJSONChar) + "\"";
+ */
+
+ /* Rather inefficient way to do it */
+ var parts = s.split("");
+ for(var i = 0; i < parts.length; i++) {
+ var c = parts[i];
+ if(c == '"' ||
+ c == '\\' ||
+ c.charCodeAt(0) < 32 ||
+ c.charCodeAt(0) >= 128)
+ parts[i] = JSONClient.escapeJSONChar(parts[i]);
+ }
+ return "\"" + parts.join("") + "\"";
+};
+
+/**
+ * Marshall objects to JSON format.
+ */
+JSONClient.toJSON = function(o) {
+ if(o == null) {
+ return "null";
+ } else if(o.constructor == String) {
+ return JSONClient.escapeJSONString(o);
+ } else if(o.constructor == Number) {
+ return o.toString();
+ } else if(o.constructor == Boolean) {
+ return o.toString();
+ } else if(o.constructor == Date) {
+ return '{javaClass: "java.util.Date", time: ' + o.valueOf() +'}';
+ } else if(o.constructor == Array) {
+ var v = [];
+ for(var i = 0; i < o.length; i++) v.push(JSONClient.toJSON(o[i]));
+ return "[" + v.join(", ") + "]";
+ } else {
+ var v = [];
+ for(attr in o) {
+ if(o[attr] == null) v.push("\"" + attr + "\": null");
+ else if(typeof o[attr] == "function"); /* skip */
+ else v.push(JSONClient.escapeJSONString(attr) + ": " + JSONClient.toJSON(o[attr]));
+ }
+ return "{" + v.join(", ") + "}";
+ }
+};
+
+/**
+ * HTTPBindingClient.Exception.
+ */
+HTTPBindingClient.Exception = function(code, message, javaStack) {
+ this.code = code;
+ var name;
+ if(javaStack) {
+ this.javaStack = javaStack;
+ var m = javaStack.match(/^([^:]*)/);
+ if(m) name = m[0];
+ }
+ if(name) this.name = name;
+ else this.name = "HTTPBindingClientException";
+ this.message = message;
+};
+
+HTTPBindingClient.Exception.CODE_REMOTE_EXCEPTION = 490;
+HTTPBindingClient.Exception.CODE_ERR_CLIENT = 550;
+HTTPBindingClient.Exception.CODE_ERR_PARSE = 590;
+HTTPBindingClient.Exception.CODE_ERR_NOMETHOD = 591;
+HTTPBindingClient.Exception.CODE_ERR_UNMARSHALL = 592;
+HTTPBindingClient.Exception.CODE_ERR_MARSHALL = 593;
+
+HTTPBindingClient.Exception.prototype = new Error();
+HTTPBindingClient.Exception.prototype.toString = function(code, msg) {
+ return this.name + ": " + this.message;
+};
+
+/**
+ * Default top level exception handler.
+ */
+HTTPBindingClient.default_ex_handler = function(e) {
+ alert(e);
+};
+
+/**
+ * Client settable variables
+ */
+HTTPBindingClient.toplevel_ex_handler = HTTPBindingClient.default_ex_handler;
+HTTPBindingClient.profile_async = false;
+HTTPBindingClient.max_req_active = 1;
+HTTPBindingClient.requestId = 1;
+
+/**
+ * HTTPBindingClient implementation
+ */
+HTTPBindingClient.prototype._createApplyMethod = function() {
+ var fn = function() {
+ var args = [];
+ var callback = null;
+ var methodName = arguments[0];
+ for(var i = 1; i < arguments.length; i++) args.push(arguments[i]);
+
+ if(typeof args[args.length - 1] == "function") callback = args.pop();
+
+ var req = fn.client._makeRequest.call(fn.client, methodName, args, callback);
+ if(callback == null) {
+ return fn.client._sendRequest.call(fn.client, req);
+ } else {
+ HTTPBindingClient.async_requests.push(req);
+ HTTPBindingClient.kick_async();
+ return req.requestId;
+ }
+ };
+ fn.client = this;
+ return fn;
+};
+
+HTTPBindingClient._getCharsetFromHeaders = function(http) {
+ try {
+ var contentType = http.getResponseHeader("Content-type");
+ var parts = contentType.split(/\s*;\s*/);
+ for(var i = 0; i < parts.length; i++) {
+ if(parts[i].substring(0, 8) == "charset=")
+ return parts[i].substring(8, parts[i].length);
+ }
+ } catch (e) {}
+ return "UTF-8";
+};
+
+/**
+ * Async queue globals
+ */
+HTTPBindingClient.async_requests = [];
+HTTPBindingClient.async_inflight = {};
+HTTPBindingClient.async_responses = [];
+HTTPBindingClient.async_timeout = null;
+HTTPBindingClient.num_req_active = 0;
+
+HTTPBindingClient._async_handler = function() {
+ HTTPBindingClient.async_timeout = null;
+
+ while(HTTPBindingClient.async_responses.length > 0) {
+ var res = HTTPBindingClient.async_responses.shift();
+ if(res.canceled) continue;
+ if(res.profile) res.profile.dispatch = new Date();
+ try {
+ res.cb(res.result, res.ex, res.profile);
+ } catch(e) {
+ HTTPBindingClient.toplevel_ex_handler(e);
+ }
+ }
+
+ while(HTTPBindingClient.async_requests.length > 0 && HTTPBindingClient.num_req_active < HTTPBindingClient.max_req_active) {
+ var req = HTTPBindingClient.async_requests.shift();
+ if(req.canceled) continue;
+ req.client._sendRequest.call(req.client, req);
+ }
+};
+
+HTTPBindingClient.kick_async = function() {
+ if(HTTPBindingClient.async_timeout == null)
+ HTTPBindingClient.async_timeout = setTimeout(HTTPBindingClient._async_handler, 0);
+};
+
+HTTPBindingClient.cancelRequest = function(requestId) {
+ /* If it is in flight then mark it as canceled in the inflight map
+ and the XMLHttpRequest callback will discard the reply. */
+ if(HTTPBindingClient.async_inflight[requestId]) {
+ HTTPBindingClient.async_inflight[requestId].canceled = true;
+ return true;
+ }
+
+ /* If its not in flight yet then we can just mark it as canceled in
+ the the request queue and it will get discarded before being sent. */
+ for(var i in HTTPBindingClient.async_requests) {
+ if(HTTPBindingClient.async_requests[i].requestId == requestId) {
+ HTTPBindingClient.async_requests[i].canceled = true;
+ return true;
+ }
+ }
+
+ /* It may have returned from the network and be waiting for its callback
+ to be dispatched, so mark it as canceled in the response queue
+ and the response will get discarded before calling the callback. */
+ for(var i in HTTPBindingClient.async_responses) {
+ if(HTTPBindingClient.async_responses[i].requestId == requestId) {
+ HTTPBindingClient.async_responses[i].canceled = true;
+ return true;
+ }
+ }
+
+ return false;
+};
+
+HTTPBindingClient.prototype._makeRequest = function(methodName, args, cb) {
+ var req = {};
+ req.client = this;
+ req.requestId = HTTPBindingClient.requestId++;
+
+ var obj = {};
+ obj.id = req.requestId;
+ if (this.objectID)
+ obj.method = ".obj#" + this.objectID + "." + methodName;
+ else
+ obj.method = methodName;
+ obj.params = args;
+
+ if (cb) req.cb = cb;
+ if (HTTPBindingClient.profile_async)
+ req.profile = { "submit": new Date() };
+ req.data = JSONClient.toJSON(obj);
+
+ return req;
+};
+
+HTTPBindingClient.prototype._sendRequest = function(req) {
+ if(req.profile) req.profile.start = new Date();
+
+ /* Get free http object from the pool */
+ var http = HTTPBindingClient.poolGetHTTPRequest();
+ HTTPBindingClient.num_req_active++;
+
+ /* Send the request */
+ http.open("POST", this.uri, (req.cb != null));
+
+ /* setRequestHeader is missing in Opera 8 Beta */
+ try {
+ http.setRequestHeader("Content-type", "text/plain");
+ } catch(e) {}
+
+ /* Construct call back if we have one */
+ if(req.cb) {
+ var self = this;
+ http.onreadystatechange = function() {
+ if(http.readyState == 4) {
+ http.onreadystatechange = function () {};
+ var res = { "cb": req.cb, "result": null, "ex": null};
+ if (req.profile) {
+ res.profile = req.profile;
+ res.profile.end = new Date();
+ }
+ try { res.result = self._handleResponse(http); }
+ catch(e) { res.ex = e; }
+ if(!HTTPBindingClient.async_inflight[req.requestId].canceled)
+ HTTPBindingClient.async_responses.push(res);
+ delete HTTPBindingClient.async_inflight[req.requestId];
+ HTTPBindingClient.kick_async();
+ }
+ };
+ } else {
+ http.onreadystatechange = function() {};
+ }
+
+ HTTPBindingClient.async_inflight[req.requestId] = req;
+
+ try {
+ http.send(req.data);
+ } catch(e) {
+ HTTPBindingClient.poolReturnHTTPRequest(http);
+ HTTPBindingClient.num_req_active--;
+ throw new HTTPBindingClient.Exception(HTTPBindingClient.Exception.CODE_ERR_CLIENT, "Connection failed");
+ }
+
+ if(!req.cb) return this._handleResponse(http);
+};
+
+HTTPBindingClient.prototype._handleResponse = function(http) {
+ /* Get the charset */
+ if(!this.charset) {
+ this.charset = HTTPBindingClient._getCharsetFromHeaders(http);
+ }
+
+ /* Get request results */
+ var status, statusText, data;
+ try {
+ status = http.status;
+ statusText = http.statusText;
+ data = http.responseText;
+ } catch(e) {
+ HTTPBindingClient.poolReturnHTTPRequest(http);
+ HTTPBindingClient.num_req_active--;
+ HTTPBindingClient.kick_async();
+ throw new HTTPBindingClient.Exception(HTTPBindingClient.Exception.CODE_ERR_CLIENT, "Connection failed");
+ }
+
+ /* Return http object to the pool; */
+ HTTPBindingClient.poolReturnHTTPRequest(http);
+ HTTPBindingClient.num_req_active--;
+
+ /* Unmarshall the response */
+ if(status != 200) {
+ throw new HTTPBindingClient.Exception(status, statusText);
+ }
+ var obj;
+ try {
+ eval("obj = " + data);
+ } catch(e) {
+ throw new HTTPBindingClient.Exception(550, "error parsing result");
+ }
+ if(obj.error)
+ throw new HTTPBindingClient.Exception(obj.error.code, obj.error.msg, obj.error.trace);
+ var res = obj.result;
+
+ /* Handle CallableProxy */
+ if(res && res.objectID && res.JSONRPCType == "CallableReference")
+ return new HTTPBindingClient(this.uri, res.objectID);
+
+ return res;
+};
+
+
+/**
+ * XMLHttpRequest wrapper code
+ */
+HTTPBindingClient.http_spare = [];
+HTTPBindingClient.http_max_spare = 8;
+
+HTTPBindingClient.poolGetHTTPRequest = function() {
+ if(HTTPBindingClient.http_spare.length > 0) {
+ return HTTPBindingClient.http_spare.pop();
+ }
+ return HTTPBindingClient.getHTTPRequest();
+};
+
+HTTPBindingClient.poolReturnHTTPRequest = function(http) {
+ if(HTTPBindingClient.http_spare.length >= HTTPBindingClient.http_max_spare)
+ delete http;
+ else
+ HTTPBindingClient.http_spare.push(http);
+};
+
+HTTPBindingClient.msxmlNames = [ "MSXML2.XMLHTTP.5.0",
+ "MSXML2.XMLHTTP.4.0",
+ "MSXML2.XMLHTTP.3.0",
+ "MSXML2.XMLHTTP",
+ "Microsoft.XMLHTTP" ];
+
+HTTPBindingClient.getHTTPRequest = function() {
+ /* Mozilla XMLHttpRequest */
+ try {
+ HTTPBindingClient.httpObjectName = "XMLHttpRequest";
+ return new XMLHttpRequest();
+ } catch(e) {}
+
+ /* Microsoft MSXML ActiveX */
+ for (var i=0; i < HTTPBindingClient.msxmlNames.length; i++) {
+ try {
+ HTTPBindingClient.httpObjectName = HTTPBindingClient.msxmlNames[i];
+ return new ActiveXObject(HTTPBindingClient.msxmlNames[i]);
+ } catch (e) {}
+ }
+
+ /* None found */
+ HTTPBindingClient.httpObjectName = null;
+ throw new HTTPBindingClient.Exception(0, "Can't create XMLHttpRequest object");
+};
+
+
+HTTPBindingClient.prototype.get = function(id, responseFunction) {
+ var xhr = this.createXMLHttpRequest();
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState == 4) {
+ if (xhr.status == 200) {
+ var strDocument = xhr.responseText;
+ var xmlDocument = xhr.responseXML;
+ if(!xmlDocument || xmlDocument.childNodes.length==0){
+ xmlDocument = (new DOMParser()).parseFromString(strDocument, "text/xml");
+ }
+ if (responseFunction != null) responseFunction(xmlDocument);
+ } else {
+ alert("get - Error getting data from the server");
+ }
+ }
+ }
+ xhr.open("GET", this.uri + '/' + id, true);
+ xhr.send(null);
+};
+
+HTTPBindingClient.prototype.post = function (entry, responseFunction) {
+ var xhr = this.createXMLHttpRequest();
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState == 4) {
+ if (xhr.status == 201) {
+ var strDocument = xhr.responseText;
+ var xmlDocument = xhr.responseXML;
+ if(!xmlDocument || xmlDocument.childNodes.length==0){
+ xmlDocument = (new DOMParser()).parseFromString(strDocument, "text/xml");
+ }
+ if (responseFunction != null) responseFunction(xmlDocument);
+ } else {
+ alert("post - Error getting data from the server");
+ }
+ }
+ }
+ xhr.open("POST", this.uri, true);
+ xhr.setRequestHeader("Content-Type", "application/atom+xml");
+ xhr.send(entry);
+};
+
+HTTPBindingClient.prototype.put = function (id, entry, responseFunction) {
+ var xhr = this.createXMLHttpRequest();
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState == 4) {
+ if (xhr.status == 200) {
+ var strDocument = xhr.responseText;
+ var xmlDocument = xhr.responseXML;
+ if(!xmlDocument || xmlDocument.childNodes.length==0){
+ xmlDocument = (new DOMParser()).parseFromString(strDocument, "text/xml");
+ }
+ if (responseFunction != null) responseFunction(xmlDocument);
+ } else {
+ alert("put - Error getting data from the server");
+ }
+ }
+ }
+ xhr.open("PUT", this.uri + '/' + id, true);
+ xhr.setRequestHeader("Content-Type", "application/atom+xml");
+ xhr.send(entry);
+};
+
+HTTPBindingClient.prototype.del = function (id, responseFunction) {
+ var xhr = this.createXMLHttpRequest();
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState == 4) {
+ if (xhr.status == 200) {
+ if (responseFunction != null) responseFunction();
+ } else {
+ alert("delete - Error getting data from the server");
+ }
+ }
+ }
+ xhr.open("DELETE", this.uri + '/' + id, true);
+ xhr.send(null);
+};
+
+HTTPBindingClient.prototype.createXMLHttpRequest = function () {
+ /* Mozilla XMLHttpRequest */
+ try { return new XMLHttpRequest(); } catch(e) {}
+
+ /* Microsoft MSXML ActiveX */
+ for (var i = 0; i < HTTPBindingClient.msxmlNames.length; i++) {
+ try { return new ActiveXObject(HTTPBindingClient.msxmlNames[i]); } catch (e) {}
+ }
+ alert("XML http request not supported");
+ return null;
+}
+
+/**
+ * Construct an HTTPBindingClient.
+ */
+function HTTPBindingClient(cname, uri, objectID) {
+ this.uri = "/references/" + cname + "/" + uri;
+ this.objectID = objectID;
+ this.apply = this._createApplyMethod();
+
+ if (typeof DOMParser == "undefined") {
+ DOMParser = function () {}
+
+ DOMParser.prototype.parseFromString = function (str, contentType) {
+ if (typeof ActiveXObject != "undefined") {
+ var d = new ActiveXObject("MSXML.DomDocument");
+ d.loadXML(str);
+ return d;
+ } else if (typeof XMLHttpRequest != "undefined") {
+ var req = new XMLHttpRequest;
+ req.open("GET", "data:" + (contentType || "application/xml") +
+ ";charset=utf-8," + encodeURIComponent(str), false);
+ if (req.overrideMimeType) {
+ req.overrideMimeType(contentType);
+ }
+ req.send(null);
+ return req.responseXML;
+ }
+ }
+ }
+};
+
+/**
+ * Construct a component.
+ */
+function ClientComponent(name) {
+ this.name = name;
+}
+
+/**
+ * Public API.
+ */
+
+/**
+ * Return a component.
+ */
+function component(name) {
+ return new ClientComponent(name);
+}
+
+/**
+ * Return a reference proxy.
+ */
+function reference(comp, name) {
+ return new HTTPBindingClient(comp.name, name);
+};
+
+/**
+ * Add proxy functions to a reference proxy.
+ */
+function defun(ref) {
+ function defapply(name) {
+ return function() {
+ var args = new Array();
+ args[0] = name;
+ for (i = 0, n = arguments.length; i < n; i++)
+ args[i + 1] = arguments[i];
+ return this.apply.apply(this, args);
+ };
+ }
+
+ for (f = 1; f < arguments.length; f++) {
+ var fn = arguments[f];
+ ref[fn]= defapply(fn);
+ }
+ return ref;
+};
+
diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/httpd-test b/sandbox/sebastien/cpp/apr-2/modules/server/httpd-test
new file mode 100755
index 0000000000..050becdb24
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/server/httpd-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..."
+here=`readlink -f $0`; here=`dirname $here`
+curl_prefix=`cat $here/../http/curl.prefix`
+
+# Setup
+../http/httpd-conf tmp localhost 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_prefix/bin/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_prefix/bin/curl http://localhost:8090/test/ >tmp/feed.xml 2>/dev/null
+ diff tmp/feed.xml htdocs/test/feed.xml
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl http://localhost:8090/test/111 >tmp/entry.xml 2>/dev/null
+ diff tmp/entry.xml htdocs/test/entry.xml
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl http://localhost:8090/test/ -X POST -H "Content-type: application/atom+xml" --data @htdocs/test/entry.xml 2>/dev/null
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl http://localhost:8090/test/111 -X PUT -H "Content-type: application/atom+xml" --data @htdocs/test/entry.xml 2>/dev/null
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl http://localhost:8090/test/111 -X DELETE 2>/dev/null
+ rc=$?
+fi
+
+# Test JSON-RPC
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl http://localhost:8090/test/ -X POST -H "Content-type: application/json-rpc" --data @htdocs/test/json-request.txt >tmp/json-result.txt 2>/dev/null
+ diff tmp/json-result.txt htdocs/test/json-result.txt
+ rc=$?
+fi
+
+# Cleanup
+../http/httpd-stop tmp
+sleep 2
+if [ "$rc" = "0" ]; then
+ echo "OK"
+fi
+return $rc
diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/impl-test.cpp b/sandbox/sebastien/cpp/apr-2/modules/server/impl-test.cpp
new file mode 100644
index 0000000000..1bd0843745
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/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/sandbox/sebastien/cpp/apr-2/modules/server/mod-cpp.hpp b/sandbox/sebastien/cpp/apr-2/modules/server/mod-cpp.hpp
new file mode 100644
index 0000000000..8cae35e493
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/server/mod-cpp.hpp
@@ -0,0 +1,104 @@
+/*
+ * 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/sandbox/sebastien/cpp/apr-2/modules/server/mod-eval.cpp b/sandbox/sebastien/cpp/apr-2/modules/server/mod-eval.cpp
new file mode 100644
index 0000000000..a94ccf5bbe
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/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/sandbox/sebastien/cpp/apr-2/modules/server/mod-eval.hpp b/sandbox/sebastien/cpp/apr-2/modules/server/mod-eval.hpp
new file mode 100644
index 0000000000..b8d3147459
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/server/mod-eval.hpp
@@ -0,0 +1,896 @@
+/*
+ * 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/http.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(apr_pool_t* p, server_rec* s) : p(p), server(s), wiringServerName(""), contributionPath(""), compositeName(""), virtualHostContributionPath(""), virtualHostCompositeName(""), ca(""), cert(""), key("") {
+ }
+
+ const gc_pool p;
+ server_rec* server;
+ lambda<value(const list<value>&)> lifecycle;
+ string wiringServerName;
+ string contributionPath;
+ string compositeName;
+ string virtualHostContributionPath;
+ string virtualHostCompositeName;
+ string ca;
+ string cert;
+ string key;
+ list<value> implementations;
+ list<value> implTree;
+};
+
+/**
+ * Return true if a server contains a composite configuration.
+ */
+const bool hasCompositeConf(const ServerConf& sc) {
+ return sc.contributionPath != "" && sc.compositeName != "";
+}
+
+/**
+ * Return true if a server contains a virtual host composite configuration.
+ */
+const bool hasVirtualCompositeConf(const ServerConf& sc) {
+ return sc.virtualHostContributionPath != "" && sc.virtualHostCompositeName != "";
+}
+
+/**
+ * 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, json::queryParams(args))));
+ if (!hasContent(val))
+ return mkfailure<int>(reason(val));
+
+ // Return JSON result
+ js::JSContext 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 content-type / content-list pair
+ if (isString(car<value>(c)) && isList(cadr<value>(c)))
+ return httpd::writeResult(convertValues<string>(cadr<value>(c)), car<value>(c), r);
+
+ // Write ATOM feed or entry
+ if (isString(car<value>(c)) && isString(cadr<value>(c))) {
+ 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);
+ }
+
+ // Write JSON value
+ js::JSContext cx;
+ return httpd::writeResult(json::writeJSON(valuesToElements(c), cx), "application/json", 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") || contains(ct, "application/x-www-form-urlencoded")) {
+
+ // 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");
+ js::JSContext 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::readATOMEntry(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, c_str(httpd::url(r->uri, 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::readATOMEntry(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) {
+ if(r->method_number != M_GET && r->method_number != M_POST && r->method_number != M_PUT && r->method_number != M_DELETE)
+ return DECLINED;
+ if (strncmp(r->uri, "/components/", 12) != 0)
+ return DECLINED;
+ r->handler = "mod_tuscany_eval";
+ return OK;
+}
+
+/**
+ * Make an HTTP proxy lambda.
+ */
+const value mkhttpProxy(const ServerConf& sc, const string& ref, const string& base) {
+ const string uri = base + ref;
+ debug(uri, "modeval::mkhttpProxy::uri");
+ return lambda<value(const list<value>&)>(http::proxy(uri, sc.ca, sc.cert, sc.key, sc.p));
+}
+
+/**
+ * Return a component implementation proxy lambda.
+ */
+class implProxy {
+public:
+ implProxy(const ServerConf& sc, const value& name) : sc(sc), name(name) {
+ }
+
+ const value operator()(const list<value>& params) const {
+ debug(params, "modeval::implProxy::input");
+
+ // Lookup the component implementation
+ const list<value> impl(assoctree<value>(name, sc.implTree));
+ if (isNil(impl))
+ return mkfailure<value>(string("Couldn't find component implementation: ") + name);
+
+ // Call its lambda function
+ const lambda<value(const list<value>&)> l(cadr<value>(impl));
+ const value func = c_str(car(params));
+ const failable<value> val = failableResult(l(cons(func, cdr(params))));
+ debug(val, "modeval::implProxy::result");
+ if (!hasContent(val))
+ return value();
+ return content(val);
+ }
+
+private:
+ const ServerConf& sc;
+ const value name;
+};
+
+const value mkimplProxy(const ServerConf& sc, const value& name) {
+ debug(name, "modeval::implProxy::impl");
+ return lambda<value(const list<value>&)>(implProxy(sc, name));
+}
+
+/**
+ * Convert a list of component references to a list of proxy lambdas.
+ */
+const value mkrefProxy(const ServerConf& sc, const value& ref, const string& base) {
+ const value target = scdl::target(ref);
+ debug(ref, "modeval::mkrefProxy::ref");
+ debug(target, "modeval::mkrefProxy::target");
+
+ // Use an HTTP proxy or an internal proxy to the component implementation
+ if (isNil(target) || httpd::isAbsolute(target))
+ return mkhttpProxy(sc, scdl::name(ref), base);
+ return mkimplProxy(sc, car(pathValues(target)));
+}
+
+const list<value> refProxies(const ServerConf& sc, const list<value>& refs, const string& base) {
+ if (isNil(refs))
+ return refs;
+ return cons(mkrefProxy(sc, car(refs), base), refProxies(sc, cdr(refs), base));
+}
+
+/**
+ * Store current HTTP request for access from property lambda functions.
+ */
+#ifdef WANT_THREADS
+__thread
+#endif
+request_rec* currentRequest = NULL;
+
+class ScopedRequest {
+public:
+ ScopedRequest(request_rec* r) {
+ currentRequest = r;
+ }
+
+ ~ScopedRequest() {
+ currentRequest = NULL;
+ }
+};
+
+/**
+ * Convert a list of component properties to a list of lambda functions that just return
+ * the property value. The host, user and email properties are configured with the values
+ * from the HTTP request, if any.
+ */
+struct propProxy {
+ const value v;
+ propProxy(const value& v) : v(v) {
+ }
+ const value operator()(unused const list<value>& params) const {
+ return v;
+ }
+};
+
+struct hostPropProxy {
+ const value v;
+ hostPropProxy(const value& v) : v(v) {
+ }
+ const value operator()(unused const list<value>& params) const {
+ return httpd::hostName(currentRequest, v);
+ }
+};
+
+struct envPropProxy {
+ const string name;
+ const value v;
+ envPropProxy(const string& name, const value& v) : name(name), v(v) {
+ }
+ const value operator()(unused const list<value>& params) const {
+ const char* env = apr_table_get(currentRequest->subprocess_env, c_str(name));
+ if (env == NULL || *env == '\0')
+ return v;
+ return string(env);
+ }
+};
+
+struct userPropProxy {
+ const value v;
+ userPropProxy(const value& v) : v(v) {
+ }
+ const value operator()(unused const list<value>& params) const {
+ if (currentRequest->user == NULL)
+ return v;
+ return string(currentRequest->user);
+ }
+};
+
+const value mkpropProxy(const value& prop) {
+ if (scdl::name(prop) == "host")
+ return lambda<value(const list<value>&)>(hostPropProxy(elementValue(prop)));
+ if (scdl::name(prop) == "user")
+ return lambda<value(const list<value>&)>(userPropProxy(elementValue(prop)));
+ if (scdl::name(prop) == "realm")
+ return lambda<value(const list<value>&)>(envPropProxy("REALM", elementValue(prop)));
+ if (scdl::name(prop) == "email")
+ return lambda<value(const list<value>&)>(envPropProxy("EMAIL", elementValue(prop)));
+ if (scdl::name(prop) == "nickname")
+ return lambda<value(const list<value>&)>(envPropProxy("NICKNAME", elementValue(prop)));
+ if (scdl::name(prop) == "fullname")
+ return lambda<value(const list<value>&)>(envPropProxy("FULLNAME", elementValue(prop)));
+ if (scdl::name(prop) == "firstname")
+ return lambda<value(const list<value>&)>(envPropProxy("FIRSTNAME", elementValue(prop)));
+ if (scdl::name(prop) == "lastname")
+ return lambda<value(const list<value>&)>(envPropProxy("LASTNAME", elementValue(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.
+ */
+struct implementationFailure {
+ const value reason;
+ implementationFailure(const value& r) : reason(r) {
+ }
+
+ // Default implementation representing an implementation that
+ // couldn't be evaluated. Report the evaluation error on all
+ // subsequent calls expect start/stop.
+ const value operator()(unused const list<value>& params) const {
+ const value func = car(params);
+ if (func == "start" || func == "stop")
+ return mklist<value>(lambda<value(const list<value>&)>());
+ return mklist<value>(value(), reason);
+ }
+};
+
+const value evalComponent(ServerConf& sc, 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);
+ debug(comp, "modeval::evalComponent::comp");
+ debug(impl, "modeval::evalComponent::impl");
+
+ // Convert component references to configured proxy lambdas
+ ostringstream base;
+ base << sc.wiringServerName << "/references/" << string(scdl::name(comp)) << "/";
+ const list<value> rpx(refProxies(sc, 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 lambda<value(const list<value>&)>(implementationFailure(reason(cimpl)));
+ return content(cimpl);
+}
+
+/**
+ * Return a list of component-name + configured-implementation pairs.
+ */
+const list<value> componentToImplementationAssoc(ServerConf& sc, const list<value>& c) {
+ if (isNil(c))
+ return c;
+ return cons<value>(mklist<value>(scdl::name(car(c)), evalComponent(sc, car(c))), componentToImplementationAssoc(sc, 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) {
+ if (!hasCompositeConf(sc))
+ return false;
+ debug(sc.contributionPath, "modeval::confComponents::contributionPath");
+ debug(sc.compositeName, "modeval::confComponents::compositeName");
+ if (sc.ca != "") debug(sc.ca, "modeval::confComponents::sslCA");
+ if (sc.cert != "") debug(sc.cert, "modeval::confComponents::sslCert");
+ if (sc.key != "") debug(sc.key, "modeval::confComponents::sslKey");
+
+ // 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, content(comps));
+ debug(sc.implementations, "modeval::confComponents::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");
+ return true;
+}
+
+/**
+ * Virtual host scoped server configuration.
+ */
+class VirtualHostConf {
+public:
+ VirtualHostConf(const gc_pool& p, const ServerConf& sc) : sc(sc), vsc(pool(p), sc.server) {
+ vsc.virtualHostContributionPath = sc.virtualHostContributionPath;
+ vsc.virtualHostCompositeName = sc.virtualHostCompositeName;
+ vsc.ca = sc.ca;
+ vsc.cert = sc.cert;
+ vsc.key = sc.key;
+ }
+
+ ~VirtualHostConf() {
+ extern const failable<bool> virtualHostCleanup(const ServerConf& vsc, const ServerConf& sc);
+ virtualHostCleanup(vsc, sc);
+ }
+
+ const ServerConf& sc;
+ ServerConf vsc;
+};
+
+/**
+ * Configure and start the components deployed in a virtual host.
+ */
+const failable<bool> virtualHostConfig(ServerConf& vsc, const ServerConf& sc, request_rec* r) {
+
+ // Determine the server name and wiring server name
+ debug(httpd::serverName(vsc.server), "modeval::virtualHostConfig::serverName");
+ debug(httpd::serverName(r), "modeval::virtualHostConfig::virtualHostName");
+ vsc.wiringServerName = httpd::serverName(r);
+ debug(vsc.wiringServerName, "modeval::virtualHostConfig::wiringServerName");
+ debug(vsc.virtualHostContributionPath, "modwiring::virtualHostConfig::virtualHostContributionPath");
+
+ // Resolve the configured virtual contribution under
+ // the virtual host's SCA contribution root
+ vsc.contributionPath = vsc.virtualHostContributionPath + httpd::subdomain(httpd::hostName(r)) + "/";
+ vsc.compositeName = vsc.virtualHostCompositeName;
+
+ // Chdir to the virtual host's contribution
+ if (chdir(c_str(sc.contributionPath)) != 0)
+ return mkfailure<bool>(string("Couldn't chdir to the deployed contribution: ") + sc.contributionPath);
+
+ // Configure the deployed components
+ const failable<bool> cr = confComponents(vsc);
+ if (!hasContent(cr))
+ return cr;
+
+ // Start the configured components
+ const failable<bool> sr = startComponents(vsc);
+ if (!hasContent(sr))
+ return sr;
+
+ // Store the implementation lambda functions (from both the virtual host and the
+ // main server) in a tree for fast retrieval
+ vsc.implTree = mkbtree(sort(append(vsc.implementations, sc.implementations)));
+ return true;
+}
+
+/**
+ * Cleanup a virtual host.
+ */
+const failable<bool> virtualHostCleanup(const ServerConf& vsc, const ServerConf& sc) {
+ if (!hasCompositeConf(vsc))
+ return true;
+ debug("modeval::virtualHostCleanup");
+
+ // Stop the component implementations
+ applyLifecycleExpr(vsc.implementations, mklist<value>("stop"));
+
+ // Chdir back to the main server's contribution
+ if (chdir(c_str(sc.contributionPath)) != 0)
+ return mkfailure<bool>(string("Couldn't chdir to the deployed contribution: ") + sc.contributionPath);
+ return true;
+}
+
+/**
+ * HTTP request handler.
+ */
+int handler(request_rec *r) {
+ if(r->method_number != M_GET && r->method_number != M_POST && r->method_number != M_PUT && r->method_number != M_DELETE)
+ return DECLINED;
+ if(strcmp(r->handler, "mod_tuscany_eval"))
+ return DECLINED;
+
+ gc_scoped_pool pool(r->pool);
+ ScopedRequest sr(r);
+ httpdDebugRequest(r, "modeval::handler::input");
+
+ // Get the server configuration
+ const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_eval);
+
+ // Process dynamic virtual host configuration, if any
+ VirtualHostConf vhc(gc_pool(r->pool), sc);
+ const bool usevh = hasVirtualCompositeConf(vhc.vsc) && httpd::isVirtualHostRequest(sc.server, r);
+ if (usevh) {
+ const failable<bool> cr = virtualHostConfig(vhc.vsc, sc, r);
+ if (!hasContent(cr))
+ return httpd::reportStatus(mkfailure<int>(reason(cr)));
+ }
+
+ // Get the component implementation lambda
+ const list<value> path(pathValues(r->uri));
+ const list<value> impl(assoctree<value>(cadr(path), usevh? vhc.vsc.implTree : sc.implTree));
+ if (isNil(impl))
+ return httpd::reportStatus(mkfailure<int>(string("Couldn't find component implementation: ") + cadr(path)));
+
+ // 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;
+}
+
+/**
+ * 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.
+ */
+const int postConfigMerge(const ServerConf& mainsc, server_rec* s) {
+ if (s == NULL)
+ return OK;
+ ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_eval);
+ debug(httpd::serverName(s), "modeval::postConfigMerge::serverName");
+ if (sc.wiringServerName == "")
+ sc.wiringServerName = mainsc.wiringServerName != ""? mainsc.wiringServerName : httpd::serverName(s);
+ debug(sc.wiringServerName, "modeval::postConfigMerge::wiringServerName");
+ sc.contributionPath = mainsc.contributionPath;
+ sc.compositeName = mainsc.compositeName;
+ sc.virtualHostContributionPath = mainsc.virtualHostContributionPath;
+ sc.virtualHostCompositeName = mainsc.virtualHostCompositeName;
+ if (sc.ca == "") sc.ca = mainsc.ca;
+ if (sc.cert == "") sc.cert = mainsc.cert;
+ if (sc.key == "") sc.key = mainsc.key;
+ sc.implementations = mainsc.implementations;
+ sc.implTree = mainsc.implTree;
+ return postConfigMerge(mainsc, s->next);
+}
+
+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);
+
+ // Get the server configuration and determine the wiring server name
+ ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_eval);
+ debug(httpd::serverName(s), "modeval::postConfig::serverName");
+ if (sc.wiringServerName == "") sc.wiringServerName = httpd::serverName(s);
+ debug(sc.wiringServerName, "modeval::postConfig::wiringServerName");
+
+ // Count the calls to post config
+ const string k("tuscany::modeval::postConfig");
+ const long int count = (long 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) {
+ // Chdir to the deployed contribution
+ if (chdir(c_str(sc.contributionPath)) != 0) {
+ mkfailure<bool>(string("Couldn't chdir to the deployed contribution: ") + sc.contributionPath);
+ return -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
+ const failable<bool> res = confComponents(sc);
+ if (!hasContent(res)) {
+ cfailure << "[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);
+
+ // Merge the configuration into the virtual hosts
+ return postConfigMerge(sc, s->next);
+}
+
+/**
+ * Child process initialization.
+ */
+void childInit(apr_pool_t* p, server_rec* s) {
+ gc_scoped_pool pool(p);
+ ServerConf* psc = (ServerConf*)ap_get_module_config(s->module_config, &mod_tuscany_eval);
+ if(psc == NULL) {
+ cfailure << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << endl;
+ exit(APEXIT_CHILDFATAL);
+ }
+ ServerConf& sc = *psc;
+
+ // Start the components in the child process
+ const failable<bool> res = startComponents(sc);
+ if (!hasContent(res)) {
+ cfailure << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << endl;
+ exit(APEXIT_CHILDFATAL);
+ }
+
+ // Store the implementation lambda functions in a tree for fast retrieval
+ sc.implTree = mkbtree(sort(sc.implementations));
+
+ // Merge the updated configuration into the virtual hosts
+ postConfigMerge(sc, s->next);
+
+ // Register a cleanup callback, called when the child is stopped or restarted
+ apr_pool_pre_cleanup_register(p, (void*)psc, serverCleanup);
+}
+
+/**
+ * Configuration commands.
+ */
+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* confVirtualContribution(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.virtualHostContributionPath = arg;
+ return NULL;
+}
+const char* confVirtualComposite(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.virtualHostCompositeName = arg;
+ return NULL;
+}
+const char* confCAFile(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.ca = arg;
+ return NULL;
+}
+const char* confCertFile(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.cert = arg;
+ return NULL;
+}
+const char* confCertKeyFile(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.key = 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("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_TAKE1("SCAVirtualContribution", (const char*(*)())confVirtualContribution, NULL, RSRC_CONF, "SCA virtual host contribution location"),
+ AP_INIT_TAKE1("SCAVirtualComposite", (const char*(*)())confVirtualComposite, NULL, RSRC_CONF, "SCA virtual composite location"),
+ AP_INIT_TAKE12("SCASetEnv", (const char*(*)())confEnv, NULL, OR_FILEINFO, "Environment variable name and optional value"),
+ AP_INIT_TAKE1("SCAWiringSSLCACertificateFile", (const char*(*)())confCAFile, NULL, RSRC_CONF, "SCA wiring SSL CA certificate file"),
+ AP_INIT_TAKE1("SCAWiringSSLCertificateFile", (const char*(*)())confCertFile, NULL, RSRC_CONF, "SCA wiring SSL certificate file"),
+ AP_INIT_TAKE1("SCAWiringSSLCertificateKeyFile", (const char*(*)())confCertKeyFile, NULL, RSRC_CONF, "SCA wiring SSL certificate key file"),
+ {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/sandbox/sebastien/cpp/apr-2/modules/server/mod-scheme.hpp b/sandbox/sebastien/cpp/apr-2/modules/server/mod-scheme.hpp
new file mode 100644
index 0000000000..43d7bf4041
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/server/mod-scheme.hpp
@@ -0,0 +1,91 @@
+/*
+ * 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/sandbox/sebastien/cpp/apr-2/modules/server/mod-wiring.cpp b/sandbox/sebastien/cpp/apr-2/modules/server/mod-wiring.cpp
new file mode 100644
index 0000000000..0ab61f5af8
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/server/mod-wiring.cpp
@@ -0,0 +1,472 @@
+/*
+ * 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 {
+
+/**
+ * Set to true to wire using mod_proxy, false to wire using HTTP client redirects.
+ */
+const bool useModProxy = true;
+
+/**
+ * Server configuration.
+ */
+class ServerConf {
+public:
+ ServerConf(apr_pool_t* p, server_rec* s) : p(p), server(s), contributionPath(""), compositeName(""), virtualHostContributionPath(""), virtualHostCompositeName("") {
+ }
+
+ const gc_pool p;
+ server_rec* server;
+ string contributionPath;
+ string compositeName;
+ string virtualHostContributionPath;
+ string virtualHostCompositeName;
+ list<value> references;
+ list<value> services;
+};
+
+/**
+ * Return true if a server contains a composite configuration.
+ */
+const bool hasCompositeConf(const ServerConf& sc) {
+ return sc.contributionPath != "" && sc.compositeName != "";
+}
+
+/**
+ * Return true if a server contains a virtual host composite configuration.
+ */
+const bool hasVirtualCompositeConf(const ServerConf& sc) {
+ return sc.virtualHostContributionPath != "" && sc.virtualHostCompositeName != "";
+}
+
+/**
+ * Route a /references/component-name/reference-name request,
+ * to the target of the component reference.
+ */
+int translateReference(const ServerConf& sc, request_rec *r) {
+ httpdDebugRequest(r, "modwiring::translateReference::input");
+ debug(r->uri, "modwiring::translateReference::uri");
+
+ // Find the requested component
+ 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
+ const list<value> pathInfo = cdddr(rpath);
+ if (httpd::isAbsolute(target)) {
+ if (useModProxy) {
+ // Build proxy URI
+ // current request's protocol scheme, reference target uri and request path info
+ string turi = httpd::scheme(r) + substr(target, find(target, "://")) + path(pathInfo);
+ r->filename = apr_pstrdup(r->pool, c_str(string("proxy:") + turi));
+ debug(r->filename, "modwiring::translateReference::filename");
+ r->proxyreq = PROXYREQ_REVERSE;
+ r->handler = "proxy-server";
+ apr_table_setn(r->notes, "proxy-nocanon", "1");
+ return OK;
+ }
+
+ debug(target, "modwiring::translateReference::location");
+ r->handler = "mod_tuscany_wiring";
+ return httpd::externalRedirect(target, r);
+ }
+
+ // Route to a relative target URI using a local internal redirect
+ // /components/, target component name and request path info
+ const value tname = substr(target, 0, find(target, '/'));
+ const string tpath = path(cons(tname, pathInfo));
+ r->filename = apr_pstrdup(r->pool, c_str(string("/redirect:/components") + tpath));
+ debug(r->filename, "modwiring::translateReference::filename");
+ 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(const ServerConf& sc, request_rec *r) {
+ httpdDebugRequest(r, "modwiring::translateService::input");
+ debug(r->uri, "modwiring::translateService::uri");
+
+ // Find the requested component
+ debug(sc.services, "modwiring::translateService::services");
+ 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;
+}
+
+/**
+ * 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 (!hasCompositeConf(sc))
+ return true;
+ debug(sc.contributionPath, "modwiring::confComponents::contributionPath");
+ debug(sc.compositeName, "modwiring::confComponents::compositeName");
+
+ // 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;
+}
+
+/**
+ * Virtual host scoped server configuration.
+ */
+class VirtualHostConf {
+public:
+ VirtualHostConf(const gc_pool& p, const ServerConf& ssc) : sc(pool(p), ssc.server) {
+ sc.virtualHostContributionPath = ssc.virtualHostContributionPath;
+ sc.virtualHostCompositeName = ssc.virtualHostCompositeName;
+ }
+
+ ~VirtualHostConf() {
+ }
+
+ ServerConf sc;
+};
+
+/**
+ * Configure and start the components deployed in a virtual host.
+ */
+const failable<bool> virtualHostConfig(ServerConf& sc, request_rec* r) {
+ debug(httpd::serverName(sc.server), "modwiring::virtualHostConfig::serverName");
+ debug(httpd::serverName(r), "modwiring::virtualHostConfig::virtualHostName");
+ debug(sc.virtualHostContributionPath, "modwiring::virtualHostConfig::virtualHostContributionPath");
+
+ // Resolve the configured virtual contribution under
+ // the virtual host's SCA contribution root
+ sc.contributionPath = sc.virtualHostContributionPath + httpd::subdomain(httpd::hostName(r)) + "/";
+ sc.compositeName = sc.virtualHostCompositeName;
+
+ // Configure the wiring for the deployed components
+ confComponents(sc);
+ return true;
+}
+
+/**
+ * Translate an HTTP service or reference request and route it
+ * to the target component.
+ */
+int translate(request_rec *r) {
+ if(r->method_number != M_GET && r->method_number != M_POST && r->method_number != M_PUT && r->method_number != M_DELETE)
+ return DECLINED;
+
+ // No translation needed for a component or tunnel request
+ if (!strncmp(r->uri, "/components/", 12))
+ return DECLINED;
+
+ // Get the server configuration
+ gc_scoped_pool pool(r->pool);
+ const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_wiring);
+
+ // Process dynamic virtual host configuration
+ VirtualHostConf vhc(gc_pool(r->pool), sc);
+ const bool usevh = hasVirtualCompositeConf(vhc.sc) && httpd::isVirtualHostRequest(sc.server, r);
+ if (usevh) {
+ const failable<bool> cr = virtualHostConfig(vhc.sc, r);
+ if (!hasContent(cr))
+ return -1;
+ }
+
+ // Translate a component reference request
+ if (!strncmp(r->uri, "/references/", 12))
+ return translateReference(usevh? vhc.sc: sc, r);
+
+ // Translate a service request
+ return translateService(usevh? vhc.sc : sc, r);
+}
+
+/**
+ * HTTP request handler, redirect to a target component.
+ */
+int handler(request_rec *r) {
+ if(r->method_number != M_GET && r->method_number != M_POST && r->method_number != M_PUT && r->method_number != M_DELETE)
+ return DECLINED;
+ if(strcmp(r->handler, "mod_tuscany_wiring"))
+ return DECLINED;
+ if (r->filename == NULL || strncmp(r->filename, "/redirect:", 10) != 0)
+ return DECLINED;
+
+ // Nothing to do for an external redirect
+ if (r->status == HTTP_MOVED_TEMPORARILY)
+ return OK;
+
+ // Do an internal redirect
+ gc_scoped_pool pool(r->pool);
+ httpdDebugRequest(r, "modwiring::handler::input");
+
+ 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);
+}
+
+/**
+ * Called after all the configuration commands have been run.
+ * Process the server configuration and configure the wiring for the deployed components.
+ */
+const int postConfigMerge(const ServerConf& mainsc, server_rec* s) {
+ if (s == NULL)
+ return OK;
+ debug(httpd::serverName(s), "modwiring::postConfigMerge::serverName");
+ ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_wiring);
+ sc.contributionPath = mainsc.contributionPath;
+ sc.compositeName = mainsc.compositeName;
+ sc.virtualHostContributionPath = mainsc.virtualHostContributionPath;
+ sc.virtualHostCompositeName = mainsc.virtualHostCompositeName;
+ sc.references = mainsc.references;
+ sc.services = mainsc.services;
+ return postConfigMerge(mainsc, s->next);
+}
+
+int postConfig(apr_pool_t *p, unused apr_pool_t *plog, unused apr_pool_t *ptemp, server_rec *s) {
+ gc_scoped_pool pool(p);
+
+ // Count the calls to post config, skip the first one as
+ // postConfig is always called twice
+ const string k("tuscany::modwiring::postConfig");
+ const long int count = (long int)httpd::userData(k, s);
+ httpd::putUserData(k, (void*)(count + 1), s);
+ if (count == 0)
+ return OK;
+
+ // Configure the wiring for the deployed components
+ debug(httpd::serverName(s), "modwiring::postConfig::serverName");
+ ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_wiring);
+ confComponents(sc);
+
+ // Merge the config into any virtual hosts
+ return postConfigMerge(sc, s->next);
+}
+
+/**
+ * Child process initialization.
+ */
+void childInit(apr_pool_t* p, server_rec* s) {
+ gc_scoped_pool pool(p);
+ if(ap_get_module_config(s->module_config, &mod_tuscany_wiring) == NULL) {
+ cfailure << "[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 *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;
+}
+const char *confVirtualContribution(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.virtualHostContributionPath = arg;
+ return NULL;
+}
+const char *confVirtualComposite(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.virtualHostCompositeName = arg;
+ return NULL;
+}
+
+/**
+ * HTTP server module declaration.
+ */
+const command_rec commands[] = {
+ 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_TAKE1("SCAVirtualContribution", (const char*(*)())confVirtualContribution, NULL, RSRC_CONF, "SCA virtual host contribution location"),
+ AP_INIT_TAKE1("SCAVirtualComposite", (const char*(*)())confVirtualComposite, NULL, RSRC_CONF, "SCA virtual host 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/sandbox/sebastien/cpp/apr-2/modules/server/scheme-conf b/sandbox/sebastien/cpp/apr-2/modules/server/scheme-conf
new file mode 100755
index 0000000000..cd3c82b280
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/server/scheme-conf
@@ -0,0 +1,30 @@
+#!/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`
+mkdir -p $1
+root=`readlink -f $1`
+
+cat >>$root/conf/modules.conf <<EOF
+# Generated by: scheme-conf $*
+# Support for Scheme SCA components
+LoadModule mod_tuscany_eval $here/libmod_tuscany_eval.so
+
+EOF
diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/server-conf b/sandbox/sebastien/cpp/apr-2/modules/server/server-conf
new file mode 100755
index 0000000000..742d48d614
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/server/server-conf
@@ -0,0 +1,103 @@
+#!/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`
+mkdir -p $1
+root=`readlink -f $1`
+
+jsprefix=`readlink -f $here/../js`
+
+conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"`
+host=`echo $conf | awk '{ print $6 }'`
+port=`echo $conf | awk '{ print $7 }' | awk -F "/" '{ print $1 }'`
+pport=`echo $conf | awk '{ print $7 }' | awk -F "/" '{ print $2 }'`
+if [ "$pport" = "" ]; then
+ pport=$port
+fi
+servername="http://$host:$pport"
+
+sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"`
+if [ "$sslconf" != "" ]; then
+ sslport=`echo $sslconf | awk '{ print $6 }' | awk -F "/" '{ print $1 }'`
+ sslpport=`echo $sslconf | awk '{ print $6 }' | awk -F "/" '{ print $2 }'`
+ if [ "$sslpport" = "" ]; then
+ sslpport=$sslport
+ fi
+ servername="https://$host:$sslpport"
+fi
+
+cat >>$root/conf/modules.conf <<EOF
+# Generated by: server-conf $*
+# Support for SCA component wiring
+LoadModule mod_tuscany_wiring $here/libmod_tuscany_wiring.so
+
+EOF
+
+cat >>$root/conf/httpd.conf <<EOF
+# Generated by: server-conf $*
+# Route all wiring through the configured server name
+SCAWiringServerName $servername
+
+# Serve JavaScript client scripts
+Alias /component.js $jsprefix/htdocs/component.js
+Alias /util.js $jsprefix/htdocs/util.js
+Alias /elemutil.js $jsprefix/htdocs/elemutil.js
+Alias /xmlutil.js $jsprefix/htdocs/xmlutil.js
+Alias /atomutil.js $jsprefix/htdocs/atomutil.js
+Alias /ui.js $jsprefix/htdocs/ui.js
+Alias /ui.css $jsprefix/htdocs/ui.css
+Alias /scdl.js $jsprefix/htdocs/scdl.js
+Alias /graph.js $jsprefix/htdocs/graph.js
+
+<Location /component.js>
+AuthType None
+Require all granted
+</Location>
+<Location /scdl.js>
+AuthType None
+Require all granted
+</Location>
+<Location /util.js>
+AuthType None
+Require all granted
+</Location>
+<Location /ui.js>
+AuthType None
+Require all granted
+</Location>
+<Location /ui.css>
+AuthType None
+Require all granted
+</Location>
+
+EOF
+
+ssl=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"`
+if [ "$ssl" != "" ]; then
+ cat >>$root/conf/httpd.conf <<EOF
+# Configure SSL certificates
+SCAWiringSSLCACertificateFile "$root/cert/ca.crt"
+SCAWiringSSLCertificateFile "$root/cert/server.crt"
+SCAWiringSSLCertificateKeyFile "$root/cert/server.key"
+
+EOF
+
+fi
+
diff --git a/sandbox/sebastien/cpp/apr-2/modules/server/server-test b/sandbox/sebastien/cpp/apr-2/modules/server/server-test
new file mode 100755
index 0000000000..e53c7f5ef1
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/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 localhost 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/sandbox/sebastien/cpp/apr-2/modules/server/server-test.scm b/sandbox/sebastien/cpp/apr-2/modules/server/server-test.scm
new file mode 100644
index 0000000000..c23adb7f51
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/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" ((name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99)))
+ ("Item" "222" ((name "Orange") (currencyCode "USD") (currencySymbol "$") (price 3.55)))
+ ("Item" "333" ((name "Pear") (currencyCode "USD") (currencySymbol "$") (price 1.55))))
+
+ (list "Item" (car id) '((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/sandbox/sebastien/cpp/apr-2/modules/server/wiring-test b/sandbox/sebastien/cpp/apr-2/modules/server/wiring-test
new file mode 100755
index 0000000000..e791ec555b
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/server/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..."
+here=`readlink -f $0`; here=`dirname $here`
+curl_prefix=`cat $here/../http/curl.prefix`
+
+# Setup
+../http/httpd-conf tmp localhost 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_prefix/bin/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_prefix/bin/curl http://localhost:8090/client/ >tmp/feed.xml 2>/dev/null
+ diff tmp/feed.xml htdocs/test/feed.xml
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl http://localhost:8090/client/111 >tmp/entry.xml 2>/dev/null
+ diff tmp/entry.xml htdocs/test/entry.xml
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl http://localhost:8090/client/ -X POST -H "Content-type: application/atom+xml" --data @htdocs/test/entry.xml 2>/dev/null
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl http://localhost:8090/client/111 -X PUT -H "Content-type: application/atom+xml" --data @htdocs/test/entry.xml 2>/dev/null
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl http://localhost:8090/client/111 -X DELETE 2>/dev/null
+ rc=$?
+fi
+
+# Test JSON-RPC
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl http://localhost:8090/client/ -X POST -H "Content-type: application/json-rpc" --data @htdocs/test/json-request.txt >tmp/json-result.txt 2>/dev/null
+ diff tmp/json-result.txt htdocs/test/json-result.txt
+ rc=$?
+fi
+
+# Cleanup
+../http/httpd-stop tmp
+sleep 2
+if [ "$rc" = "0" ]; then
+ echo "OK"
+fi
+return $rc