summaryrefslogtreecommitdiffstats
path: root/sca-cpp/branches/lightweight-sca/modules
diff options
context:
space:
mode:
Diffstat (limited to 'sca-cpp/branches/lightweight-sca/modules')
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/Makefile.am19
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/Makefile.am88
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/alt-host-conf32
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/basic-auth-conf69
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/cache-conf41
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/cache-manifest26
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/cache-ssl-conf41
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/cert-auth-conf74
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/conf/mime.types608
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/curl-connect.cpp98
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/curl-get.cpp53
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/curl-test.cpp92
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/form-auth-conf82
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/group-auth-conf57
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/htdocs/index.html32
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/htdocs/login/index.html51
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/htdocs/logout/index.html44
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/http-test34
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/http.hpp1042
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-addr53
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-conf375
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-debug25
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-event-conf45
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-loglevel-conf32
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-memgrind25
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-restart25
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-ssl-conf259
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-start25
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-stop25
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-test42
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-tunnel-ssl-conf38
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/httpd-worker-conf45
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/httpd.hpp753
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/mass-host-conf62
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/mass-host-ssl-conf68
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/minify-css34
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/minify-html34
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/minify-js34
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/mod-openauth.cpp478
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/mod-security-audit-conf44
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/mod-security-conf184
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/mod-ssltunnel.cpp379
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/open-auth-conf100
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/http/openauth.hpp100
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/passwd-auth-conf31
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/proxy-balancer-conf50
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/proxy-base-conf48
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/proxy-conf60
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/proxy-member-conf45
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-conf67
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-member-conf40
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-nossl-member-conf40
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/proxy-test40
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/ssl-ca-conf96
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/ssl-cert-conf76
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/ssl-cert-find30
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/http/tunnel-ssl-conf55
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/java/Makefile.am70
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/java/client-test.cpp39
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/java/domain-test.composite41
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/java/driver.hpp61
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/java/eval.hpp566
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/java/java-conf38
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/java/java-shell.cpp40
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/java/java-test.cpp138
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/java/jni-test.cpp80
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/java/mod-java.cpp83
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/java/mod-java.hpp77
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/ClassLoader.java76
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/InvocationHandler.java59
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/IterableUtil.java398
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/Service.java53
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/UUIDUtil.java32
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/java/server-test42
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/java/test/Adder.java26
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/java/test/AdderImpl.java28
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/java/test/CalcImpl.java52
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/java/test/Client.java34
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/java/test/ClientImpl.java44
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/java/test/Server.java34
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/java/test/ServerImpl.java55
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/java/wiring-test81
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/js/Makefile.am56
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/js/eval.hpp365
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/js/htdocs/atomutil.js184
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/js/htdocs/component.js637
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/js/htdocs/elemutil.js236
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/js/htdocs/jsonutil.js261
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/js/htdocs/scdl.js250
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/js/htdocs/ui.css709
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/js/htdocs/ui.js409
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/js/htdocs/util.js471
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/js/htdocs/xmlutil.js256
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/js/js-conf55
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/js/js-shell.cpp55
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/js/js-test.cpp53
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/js/json-test.js51
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/js/util-test30
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/opencl/Makefile.am58
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/opencl/client-test.cpp39
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/opencl/domain-test.composite31
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/opencl/driver.hpp62
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/opencl/eval.hpp724
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/opencl/opencl-conf37
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/opencl/opencl-shell.cpp40
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/opencl/opencl-test.cpp332
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/opencl/server-test40
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/opencl/server-test.cl17
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/scdl/Makefile.am27
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/scdl/scdl-test.cpp125
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/scdl/scdl.hpp221
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/scdl/test.composite63
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/scdl/validate-test23
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/scheme/Makefile.am47
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/scheme/driver.hpp76
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/scheme/element-value.cpp46
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/scheme/environment.hpp182
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/scheme/eval.hpp290
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/scheme/io.hpp239
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/scheme/json-value.cpp52
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/scheme/primitive.hpp288
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/scheme/scheme-shell.cpp36
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/scheme/scheme-test.cpp242
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/scheme/value-element.cpp46
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/scheme/value-json.cpp52
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/scheme/value-xml.cpp51
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/scheme/xml-value.cpp47
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/server/Makefile.am50
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/server/client-test.cpp39
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/server/client-test.hpp372
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/server/client-test.scm38
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/server/cpp-conf37
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/server/domain-test.composite58
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/server/htdocs/index.html32
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/entry.xml14
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/feed.xml44
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/json-properties.txt14
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/json-request.txt7
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/json-result.txt4
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/server/httpd-test87
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/server/impl-test.cpp76
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/server/mod-cpp.hpp104
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/server/mod-eval.cpp65
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/server/mod-eval.hpp1600
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/server/mod-scheme.hpp91
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/server/property-test.scm21
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/server/scheme-conf37
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/server/server-conf40
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/server/server-test41
-rw-r--r--sca-cpp/branches/lightweight-sca/modules/server/server-test.scm44
-rwxr-xr-xsca-cpp/branches/lightweight-sca/modules/server/wiring-test80
151 files changed, 19398 insertions, 0 deletions
diff --git a/sca-cpp/branches/lightweight-sca/modules/Makefile.am b/sca-cpp/branches/lightweight-sca/modules/Makefile.am
new file mode 100644
index 0000000000..16fe2791f7
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/Makefile.am
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+SUBDIRS = scheme atom rss js json scdl http server python opencl java openid oauth wsgi
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/Makefile.am b/sca-cpp/branches/lightweight-sca/modules/http/Makefile.am
new file mode 100644
index 0000000000..a504adcda5
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/Makefile.am
@@ -0,0 +1,88 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+INCLUDES = -I${HTTPD_INCLUDE}
+
+incl_HEADERS = *.hpp
+incldir = $(prefix)/include/modules/http
+
+dist_mod_SCRIPTS = httpd-conf httpd-addr httpd-start httpd-stop httpd-restart ssl-ca-conf ssl-cert-conf ssl-cert-find httpd-ssl-conf basic-auth-conf cert-auth-conf form-auth-conf open-auth-conf passwd-auth-conf group-auth-conf cache-conf cache-ssl-conf cache-manifest proxy-conf proxy-base-conf proxy-ssl-conf proxy-balancer-conf proxy-member-conf proxy-ssl-member-conf proxy-ssl-nossl-member-conf alt-host-conf mass-host-conf mass-host-ssl-conf httpd-tunnel-ssl-conf tunnel-ssl-conf httpd-worker-conf httpd-event-conf httpd-loglevel-conf minify-html minify-js minify-css
+moddir = $(prefix)/modules/http
+
+curl_test_SOURCES = curl-test.cpp
+curl_test_LDFLAGS = -lxml2 -lcurl -lmozjs
+
+curl_get_SOURCES = curl-get.cpp
+curl_get_LDFLAGS = -lxml2 -lcurl -lmozjs
+
+curl_connect_SOURCES = curl-connect.cpp
+curl_connect_LDFLAGS = -lxml2 -lcurl -lmozjs
+
+mod_LTLIBRARIES = libmod_tuscany_ssltunnel.la libmod_tuscany_openauth.la
+noinst_DATA = libmod_tuscany_ssltunnel${libsuffix} libmod_tuscany_openauth${libsuffix}
+
+libmod_tuscany_ssltunnel_la_SOURCES = mod-ssltunnel.cpp
+libmod_tuscany_ssltunnel_la_LDFLAGS = -lxml2 -lcurl -lmozjs
+libmod_tuscany_ssltunnel${libsuffix}:
+ ln -s .libs/libmod_tuscany_ssltunnel${libsuffix}
+
+libmod_tuscany_openauth_la_SOURCES = mod-openauth.cpp
+libmod_tuscany_openauth_la_LDFLAGS = -lxml2 -lcurl -lmozjs
+libmod_tuscany_openauth${libsuffix}:
+ ln -s .libs/libmod_tuscany_openauth${libsuffix}
+
+mod_DATA = httpd.prefix httpd-apachectl.prefix httpd-modules.prefix curl.prefix pagespeed.prefix
+nobase_dist_mod_DATA = conf/*
+
+EXTRA_DIST = htdocs/index.html htdocs/login/index.html htdocs/logout/index.html
+
+httpd.prefix: $(top_builddir)/config.status
+ echo ${HTTPD_PREFIX} >httpd.prefix
+httpd-apachectl.prefix: $(top_builddir)/config.status
+ echo ${HTTPD_APACHECTL_PREFIX} >httpd-apachectl.prefix
+httpd-modules.prefix: $(top_builddir)/config.status
+ echo ${HTTPD_MODULES_PREFIX} >httpd-modules.prefix
+curl.prefix: $(top_builddir)/config.status
+ echo ${CURL_PREFIX} >curl.prefix
+
+if WANT_PAGESPEED
+
+pagespeed.prefix: $(top_builddir)/config.status
+ echo ${PAGESPEED_PREFIX} >pagespeed.prefix
+
+else
+
+pagespeed.prefix: $(top_builddir)/config.status
+ echo "" >pagespeed.prefix
+
+endif
+
+if WANT_MODSECURITY
+
+modsecurity.prefix: $(top_builddir)/config.status
+ echo ${MODSECURITY_PREFIX} >modsecurity.prefix
+
+dist_modsecurity_SCRIPTS = mod-security-conf mod-security-audit-conf
+modsecurity_DATA = modsecurity.prefix
+modsecuritydir = $(prefix)/modules/http
+
+endif
+
+dist_noinst_SCRIPTS = httpd-test http-test proxy-test
+noinst_PROGRAMS = curl-test curl-get curl-connect
+TESTS = httpd-test http-test proxy-test
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/alt-host-conf b/sca-cpp/branches/lightweight-sca/modules/http/alt-host-conf
new file mode 100755
index 0000000000..f6148173b4
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/alt-host-conf
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Generate an alternate host name configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+host=$2
+
+cat >>$root/conf/hostcond.conf <<EOF
+# Generated by: alt-host-conf $*
+RewriteCond %{HTTP_HOST} !^$host [NC]
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/basic-auth-conf b/sca-cpp/branches/lightweight-sca/modules/http/basic-auth-conf
new file mode 100755
index 0000000000..8710d1fdf7
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/basic-auth-conf
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Generate a minimal HTTPD basic authentication configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"`
+host=`echo $conf | awk '{ print $6 }'`
+
+if [ "$2" = "" ]; then
+ providers="file"
+else
+ providers="$2 file"
+fi
+
+if [ "$3" = "" ]; then
+ loc="/"
+else
+ loc="$3"
+fi
+
+sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"`
+if [ "$sslconf" = "" ]; then
+ sslsuffix=""
+else
+ sslsuffix="-ssl"
+fi
+
+# Disallow public access to server resources
+cat >$root/conf/noauth$sslsuffix.conf <<EOF
+# Generated by: basic-auth-conf $*
+# Disallow public access to server resources
+
+EOF
+
+# Generate basic authentication configuration
+cat >>$root/conf/locauth$sslsuffix.conf <<EOF
+# Generated by: basic-auth-conf $*
+# Require clients to present a userid + password for HTTP
+# basic authentication
+<Location $loc>
+AuthType Basic
+AuthName "$host"
+AuthBasicProvider socache $providers
+AuthnCacheProvideFor $providers
+AuthnCacheContext /
+Require valid-user
+</Location>
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/cache-conf b/sca-cpp/branches/lightweight-sca/modules/http/cache-conf
new file mode 100755
index 0000000000..09ad5dc444
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/cache-conf
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Generate a minimal HTTPD cache configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+mkdir -p $root/cache
+
+cat >>$root/conf/vhost.conf <<EOF
+# Generated by: cache-conf $*
+
+# Enable caching
+CacheEnable disk /
+CacheQuickHandler Off
+CacheRoot $root/cache
+CacheHeader On
+#CacheDetailHeader On
+CacheIgnoreQueryString On
+CacheIgnoreCacheControl On
+CacheIgnoreHeaders Set-Cookie
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/cache-manifest b/sca-cpp/branches/lightweight-sca/modules/http/cache-manifest
new file mode 100755
index 0000000000..3752a61dde
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/cache-manifest
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Generate a cache-manifest.cmf file from a template and the list
+# of files in the cached application
+root=$1
+shift
+sha=`cat $* | shasum | awk '{ print $1 }'`
+cat $root/cache-template.cmf | sed -e "s/SHA1/$sha/" >$root/cache-manifest.cmf
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/cache-ssl-conf b/sca-cpp/branches/lightweight-sca/modules/http/cache-ssl-conf
new file mode 100755
index 0000000000..7a902c37eb
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/cache-ssl-conf
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Generate a minimal HTTPD cache configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+mkdir -p $root/cache
+
+cat >>$root/conf/vhost-ssl.conf <<EOF
+# Generated by: cache-conf $*
+
+# Enable caching
+CacheEnable disk /
+CacheQuickHandler Off
+CacheRoot $root/cache
+CacheHeader On
+#CacheDetailHeader On
+CacheIgnoreQueryString On
+CacheIgnoreCacheControl On
+CacheIgnoreHeaders Set-Cookie
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/cert-auth-conf b/sca-cpp/branches/lightweight-sca/modules/http/cert-auth-conf
new file mode 100755
index 0000000000..a30fdfff8c
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/cert-auth-conf
@@ -0,0 +1,74 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Generate a minimal HTTPD certificate-based authentication configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"`
+host=`echo $conf | awk '{ print $6 }'`
+
+sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"`
+if [ "$sslconf" = "" ]; then
+ sslsuffix=""
+else
+ sslsuffix="-ssl"
+fi
+
+if [ "$2" = "" ]; then
+ providers="file"
+else
+ providers="$2 file"
+fi
+
+# Disallow public access to server resources
+cat >$root/conf/noauth$sslsuffix.conf <<EOF
+# Generated by: cert-auth-conf $*
+# Disallow public access to server resources
+
+EOF
+
+# Generate authentication configuration
+cat >>$root/conf/locauth$sslsuffix.conf <<EOF
+# Generated by: cert-auth-conf $*
+# Require clients to present a valid client certificate
+SSLVerifyClient require
+SSLVerifyDepth 1
+
+<Location />
+AuthType Basic
+AuthName "$host"
+AuthBasicProvider socache $providers
+AuthnCacheProvideFor $providers
+AuthnCacheContext /
+Require valid-user
+</Location>
+
+EOF
+
+# Create password file and certificate-based users
+cat >>$root/conf/httpd.passwd <<EOF
+/C=US/ST=CA/L=San Francisco/O=$host/OU=server/CN=$host:xxj31ZMTZzkVA
+/C=US/ST=CA/L=San Francisco/O=$host/OU=proxy/CN=$host:xxj31ZMTZzkVA
+/C=US/ST=CA/L=San Francisco/O=$host/OU=tunnel/CN=$host:xxj31ZMTZzkVA
+/C=US/ST=CA/L=San Francisco/O=localhost/OU=server/CN=localhost:xxj31ZMTZzkVA
+/C=US/ST=CA/L=San Francisco/O=localhost/OU=tunnel/CN=localhost:xxj31ZMTZzkVA
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/conf/mime.types b/sca-cpp/branches/lightweight-sca/modules/http/conf/mime.types
new file mode 100644
index 0000000000..3f083f9a32
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/conf/mime.types
@@ -0,0 +1,608 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# This file controls what Internet media types are sent to the client for
+# given file extension(s). Sending the correct media type to the client
+# is important so they know how to handle the content of the file.
+# Extra types can either be added here or by using an AddType directive
+# in your config files. For more information about Internet media types,
+# please read RFC 2045, 2046, 2047, 2048, and 2077. The Internet media type
+# registry is at <http://www.iana.org/assignments/media-types/>.
+
+# MIME type Extensions
+application/activemessage
+application/andrew-inset ez
+application/applefile
+application/atom+xml atom
+application/atomicmail
+application/batch-smtp
+application/beep+xml
+application/cals-1840
+application/cnrp+xml
+application/commonground
+application/cpl+xml
+application/cybercash
+application/dca-rft
+application/dec-dx
+application/dvcs
+application/edi-consent
+application/edifact
+application/edi-x12
+application/eshop
+application/font-tdpfr
+application/http
+application/hyperstudio
+application/iges
+application/index
+application/index.cmd
+application/index.obj
+application/index.response
+application/index.vnd
+application/iotp
+application/ipp
+application/isup
+application/mac-binhex40 hqx
+application/mac-compactpro cpt
+application/macwriteii
+application/marc
+application/mathematica
+application/mathml+xml mathml
+application/msword doc
+application/news-message-id
+application/news-transmission
+application/ocsp-request
+application/ocsp-response
+application/octet-stream bin dms lha lzh exe class so dll dmg
+application/oda oda
+application/ogg ogg
+application/parityfec
+application/pdf pdf
+application/pgp-encrypted
+application/pgp-keys
+application/pgp-signature
+application/pkcs10
+application/pkcs7-mime
+application/pkcs7-signature
+application/pkix-cert
+application/pkix-crl
+application/pkixcmp
+application/postscript ai eps ps
+application/prs.alvestrand.titrax-sheet
+application/prs.cww
+application/prs.nprend
+application/prs.plucker
+application/qsig
+application/rdf+xml rdf
+application/reginfo+xml
+application/remote-printing
+application/riscos
+application/rtf
+application/sdp
+application/set-payment
+application/set-payment-initiation
+application/set-registration
+application/set-registration-initiation
+application/sgml
+application/sgml-open-catalog
+application/sieve
+application/slate
+application/smil smi smil
+application/srgs gram
+application/srgs+xml grxml
+application/timestamp-query
+application/timestamp-reply
+application/tve-trigger
+application/vemmi
+application/vnd.3gpp.pic-bw-large
+application/vnd.3gpp.pic-bw-small
+application/vnd.3gpp.pic-bw-var
+application/vnd.3gpp.sms
+application/vnd.3m.post-it-notes
+application/vnd.accpac.simply.aso
+application/vnd.accpac.simply.imp
+application/vnd.acucobol
+application/vnd.acucorp
+application/vnd.adobe.xfdf
+application/vnd.aether.imp
+application/vnd.amiga.ami
+application/vnd.anser-web-certificate-issue-initiation
+application/vnd.anser-web-funds-transfer-initiation
+application/vnd.audiograph
+application/vnd.blueice.multipass
+application/vnd.bmi
+application/vnd.businessobjects
+application/vnd.canon-cpdl
+application/vnd.canon-lips
+application/vnd.cinderella
+application/vnd.claymore
+application/vnd.commerce-battelle
+application/vnd.commonspace
+application/vnd.contact.cmsg
+application/vnd.cosmocaller
+application/vnd.criticaltools.wbs+xml
+application/vnd.ctc-posml
+application/vnd.cups-postscript
+application/vnd.cups-raster
+application/vnd.cups-raw
+application/vnd.curl
+application/vnd.cybank
+application/vnd.data-vision.rdz
+application/vnd.dna
+application/vnd.dpgraph
+application/vnd.dreamfactory
+application/vnd.dxr
+application/vnd.ecdis-update
+application/vnd.ecowin.chart
+application/vnd.ecowin.filerequest
+application/vnd.ecowin.fileupdate
+application/vnd.ecowin.series
+application/vnd.ecowin.seriesrequest
+application/vnd.ecowin.seriesupdate
+application/vnd.enliven
+application/vnd.epson.esf
+application/vnd.epson.msf
+application/vnd.epson.quickanime
+application/vnd.epson.salt
+application/vnd.epson.ssf
+application/vnd.ericsson.quickcall
+application/vnd.eudora.data
+application/vnd.fdf
+application/vnd.ffsns
+application/vnd.fints
+application/vnd.flographit
+application/vnd.framemaker
+application/vnd.fsc.weblaunch
+application/vnd.fujitsu.oasys
+application/vnd.fujitsu.oasys2
+application/vnd.fujitsu.oasys3
+application/vnd.fujitsu.oasysgp
+application/vnd.fujitsu.oasysprs
+application/vnd.fujixerox.ddd
+application/vnd.fujixerox.docuworks
+application/vnd.fujixerox.docuworks.binder
+application/vnd.fut-misnet
+application/vnd.grafeq
+application/vnd.groove-account
+application/vnd.groove-help
+application/vnd.groove-identity-message
+application/vnd.groove-injector
+application/vnd.groove-tool-message
+application/vnd.groove-tool-template
+application/vnd.groove-vcard
+application/vnd.hbci
+application/vnd.hhe.lesson-player
+application/vnd.hp-hpgl
+application/vnd.hp-hpid
+application/vnd.hp-hps
+application/vnd.hp-pcl
+application/vnd.hp-pclxl
+application/vnd.httphone
+application/vnd.hzn-3d-crossword
+application/vnd.ibm.afplinedata
+application/vnd.ibm.electronic-media
+application/vnd.ibm.minipay
+application/vnd.ibm.modcap
+application/vnd.ibm.rights-management
+application/vnd.ibm.secure-container
+application/vnd.informix-visionary
+application/vnd.intercon.formnet
+application/vnd.intertrust.digibox
+application/vnd.intertrust.nncp
+application/vnd.intu.qbo
+application/vnd.intu.qfx
+application/vnd.irepository.package+xml
+application/vnd.is-xpr
+application/vnd.japannet-directory-service
+application/vnd.japannet-jpnstore-wakeup
+application/vnd.japannet-payment-wakeup
+application/vnd.japannet-registration
+application/vnd.japannet-registration-wakeup
+application/vnd.japannet-setstore-wakeup
+application/vnd.japannet-verification
+application/vnd.japannet-verification-wakeup
+application/vnd.jisp
+application/vnd.kde.karbon
+application/vnd.kde.kchart
+application/vnd.kde.kformula
+application/vnd.kde.kivio
+application/vnd.kde.kontour
+application/vnd.kde.kpresenter
+application/vnd.kde.kspread
+application/vnd.kde.kword
+application/vnd.kenameaapp
+application/vnd.koan
+application/vnd.liberty-request+xml
+application/vnd.llamagraphics.life-balance.desktop
+application/vnd.llamagraphics.life-balance.exchange+xml
+application/vnd.lotus-1-2-3
+application/vnd.lotus-approach
+application/vnd.lotus-freelance
+application/vnd.lotus-notes
+application/vnd.lotus-organizer
+application/vnd.lotus-screencam
+application/vnd.lotus-wordpro
+application/vnd.mcd
+application/vnd.mediastation.cdkey
+application/vnd.meridian-slingshot
+application/vnd.micrografx.flo
+application/vnd.micrografx.igx
+application/vnd.mif mif
+application/vnd.minisoft-hp3000-save
+application/vnd.mitsubishi.misty-guard.trustweb
+application/vnd.mobius.daf
+application/vnd.mobius.dis
+application/vnd.mobius.mbk
+application/vnd.mobius.mqy
+application/vnd.mobius.msl
+application/vnd.mobius.plc
+application/vnd.mobius.txf
+application/vnd.mophun.application
+application/vnd.mophun.certificate
+application/vnd.motorola.flexsuite
+application/vnd.motorola.flexsuite.adsi
+application/vnd.motorola.flexsuite.fis
+application/vnd.motorola.flexsuite.gotap
+application/vnd.motorola.flexsuite.kmr
+application/vnd.motorola.flexsuite.ttc
+application/vnd.motorola.flexsuite.wem
+application/vnd.mozilla.xul+xml xul
+application/vnd.ms-artgalry
+application/vnd.ms-asf
+application/vnd.ms-excel xls
+application/vnd.ms-lrm
+application/vnd.ms-powerpoint ppt
+application/vnd.ms-project
+application/vnd.ms-tnef
+application/vnd.ms-works
+application/vnd.ms-wpl
+application/vnd.mseq
+application/vnd.msign
+application/vnd.music-niff
+application/vnd.musician
+application/vnd.netfpx
+application/vnd.noblenet-directory
+application/vnd.noblenet-sealer
+application/vnd.noblenet-web
+application/vnd.novadigm.edm
+application/vnd.novadigm.edx
+application/vnd.novadigm.ext
+application/vnd.obn
+application/vnd.osa.netdeploy
+application/vnd.palm
+application/vnd.pg.format
+application/vnd.pg.osasli
+application/vnd.powerbuilder6
+application/vnd.powerbuilder6-s
+application/vnd.powerbuilder7
+application/vnd.powerbuilder7-s
+application/vnd.powerbuilder75
+application/vnd.powerbuilder75-s
+application/vnd.previewsystems.box
+application/vnd.publishare-delta-tree
+application/vnd.pvi.ptid1
+application/vnd.pwg-multiplexed
+application/vnd.pwg-xhtml-print+xml
+application/vnd.quark.quarkxpress
+application/vnd.rapid
+application/vnd.s3sms
+application/vnd.sealed.net
+application/vnd.seemail
+application/vnd.shana.informed.formdata
+application/vnd.shana.informed.formtemplate
+application/vnd.shana.informed.interchange
+application/vnd.shana.informed.package
+application/vnd.smaf
+application/vnd.sss-cod
+application/vnd.sss-dtf
+application/vnd.sss-ntf
+application/vnd.street-stream
+application/vnd.svd
+application/vnd.swiftview-ics
+application/vnd.triscape.mxs
+application/vnd.trueapp
+application/vnd.truedoc
+application/vnd.ufdl
+application/vnd.uplanet.alert
+application/vnd.uplanet.alert-wbxml
+application/vnd.uplanet.bearer-choice
+application/vnd.uplanet.bearer-choice-wbxml
+application/vnd.uplanet.cacheop
+application/vnd.uplanet.cacheop-wbxml
+application/vnd.uplanet.channel
+application/vnd.uplanet.channel-wbxml
+application/vnd.uplanet.list
+application/vnd.uplanet.list-wbxml
+application/vnd.uplanet.listcmd
+application/vnd.uplanet.listcmd-wbxml
+application/vnd.uplanet.signal
+application/vnd.vcx
+application/vnd.vectorworks
+application/vnd.vidsoft.vidconference
+application/vnd.visio
+application/vnd.visionary
+application/vnd.vividence.scriptfile
+application/vnd.vsf
+application/vnd.wap.sic
+application/vnd.wap.slc
+application/vnd.wap.wbxml wbxml
+application/vnd.wap.wmlc wmlc
+application/vnd.wap.wmlscriptc wmlsc
+application/vnd.webturbo
+application/vnd.wrq-hp3000-labelled
+application/vnd.wt.stf
+application/vnd.wv.csp+wbxml
+application/vnd.xara
+application/vnd.xfdl
+application/vnd.yamaha.hv-dic
+application/vnd.yamaha.hv-script
+application/vnd.yamaha.hv-voice
+application/vnd.yellowriver-custom-menu
+application/voicexml+xml vxml
+application/watcherinfo+xml
+application/whoispp-query
+application/whoispp-response
+application/wita
+application/wordperfect5.1
+application/x-bcpio bcpio
+application/x-cdlink vcd
+application/x-chess-pgn pgn
+application/x-compress
+application/x-cpio cpio
+application/x-csh csh
+application/x-director dcr dir dxr
+application/x-dvi dvi
+application/x-futuresplash spl
+application/x-gtar gtar
+application/x-gzip
+application/x-hdf hdf
+application/x-javascript js
+application/x-koan skp skd skt skm
+application/x-latex latex
+application/x-netcdf nc cdf
+application/x-sh sh
+application/x-shar shar
+application/x-shockwave-flash swf
+application/x-stuffit sit
+application/x-sv4cpio sv4cpio
+application/x-sv4crc sv4crc
+application/x-tar tar
+application/x-tcl tcl
+application/x-tex tex
+application/x-texinfo texinfo texi
+application/x-troff t tr roff
+application/x-troff-man man
+application/x-troff-me me
+application/x-troff-ms ms
+application/x-ustar ustar
+application/x-wais-source src
+application/x400-bp
+application/xhtml+xml xhtml xht
+application/xslt+xml xslt
+application/xml xml xsl
+application/xml-dtd dtd
+application/xml-external-parsed-entity
+application/zip zip
+audio/32kadpcm
+audio/amr
+audio/amr-wb
+audio/basic au snd
+audio/cn
+audio/dat12
+audio/dsr-es201108
+audio/dvi4
+audio/evrc
+audio/evrc0
+audio/g722
+audio/g.722.1
+audio/g723
+audio/g726-16
+audio/g726-24
+audio/g726-32
+audio/g726-40
+audio/g728
+audio/g729
+audio/g729D
+audio/g729E
+audio/gsm
+audio/gsm-efr
+audio/l8
+audio/l16
+audio/l20
+audio/l24
+audio/lpc
+audio/midi mid midi kar
+audio/mpa
+audio/mpa-robust
+audio/mp4a-latm
+audio/mpeg mpga mp2 mp3
+audio/parityfec
+audio/pcma
+audio/pcmu
+audio/prs.sid
+audio/qcelp
+audio/red
+audio/smv
+audio/smv0
+audio/telephone-event
+audio/tone
+audio/vdvi
+audio/vnd.3gpp.iufp
+audio/vnd.cisco.nse
+audio/vnd.cns.anp1
+audio/vnd.cns.inf1
+audio/vnd.digital-winds
+audio/vnd.everad.plj
+audio/vnd.lucent.voice
+audio/vnd.nortel.vbk
+audio/vnd.nuera.ecelp4800
+audio/vnd.nuera.ecelp7470
+audio/vnd.nuera.ecelp9600
+audio/vnd.octel.sbc
+audio/vnd.qcelp
+audio/vnd.rhetorex.32kadpcm
+audio/vnd.vmx.cvsd
+audio/x-aiff aif aiff aifc
+audio/x-alaw-basic
+audio/x-mpegurl m3u
+audio/x-pn-realaudio ram ra
+audio/x-pn-realaudio-plugin
+application/vnd.rn-realmedia rm
+audio/x-wav wav
+chemical/x-pdb pdb
+chemical/x-xyz xyz
+image/bmp bmp
+image/cgm cgm
+image/g3fax
+image/gif gif
+image/ief ief
+image/jpeg jpeg jpg jpe
+image/naplps
+image/png png b64
+image/prs.btif
+image/prs.pti
+image/svg+xml svg
+image/t38
+image/tiff tiff tif
+image/tiff-fx
+image/vnd.cns.inf2
+image/vnd.djvu djvu djv
+image/vnd.dwg
+image/vnd.dxf
+image/vnd.fastbidsheet
+image/vnd.fpx
+image/vnd.fst
+image/vnd.fujixerox.edmics-mmr
+image/vnd.fujixerox.edmics-rlc
+image/vnd.globalgraphics.pgb
+image/vnd.mix
+image/vnd.ms-modi
+image/vnd.net-fpx
+image/vnd.svf
+image/vnd.wap.wbmp wbmp
+image/vnd.xiff
+image/x-cmu-raster ras
+image/x-icon ico
+image/x-portable-anymap pnm
+image/x-portable-bitmap pbm
+image/x-portable-graymap pgm
+image/x-portable-pixmap ppm
+image/x-rgb rgb
+image/x-xbitmap xbm
+image/x-xpixmap xpm
+image/x-xwindowdump xwd
+message/delivery-status
+message/disposition-notification
+message/external-body
+message/http
+message/news
+message/partial
+message/rfc822
+message/s-http
+message/sip
+message/sipfrag
+model/iges igs iges
+model/mesh msh mesh silo
+model/vnd.dwf
+model/vnd.flatland.3dml
+model/vnd.gdl
+model/vnd.gs-gdl
+model/vnd.gtw
+model/vnd.mts
+model/vnd.parasolid.transmit.binary
+model/vnd.parasolid.transmit.text
+model/vnd.vtu
+model/vrml wrl vrml
+multipart/alternative
+multipart/appledouble
+multipart/byteranges
+multipart/digest
+multipart/encrypted
+multipart/form-data
+multipart/header-set
+multipart/mixed
+multipart/parallel
+multipart/related
+multipart/report
+multipart/signed
+multipart/voice-message
+text/cache-manifest cmf
+text/calendar ics ifb
+text/css css
+text/directory
+text/enriched
+text/html html htm
+text/parityfec
+text/plain asc txt
+text/prs.lines.tag
+text/rfc822-headers
+text/richtext rtx
+text/rtf rtf
+text/sgml sgml sgm
+text/t140
+text/tab-separated-values tsv
+text/uri-list
+text/vnd.abc
+text/vnd.curl
+text/vnd.dmclientscript
+text/vnd.fly
+text/vnd.fmi.flexstor
+text/vnd.in3d.3dml
+text/vnd.in3d.spot
+text/vnd.iptc.nitf
+text/vnd.iptc.newsml
+text/vnd.latex-z
+text/vnd.motorola.reflex
+text/vnd.ms-mediapackage
+text/vnd.net2phone.commcenter.command
+text/vnd.sun.j2me.app-descriptor
+text/vnd.wap.si
+text/vnd.wap.sl
+text/vnd.wap.wml wml
+text/vnd.wap.wmlscript wmls
+text/x-setext etx
+text/xml
+text/xml-external-parsed-entity
+video/bmpeg
+video/bt656
+video/celb
+video/dv
+video/h261
+video/h263
+video/h263-1998
+video/h263-2000
+video/jpeg
+video/mp1s
+video/mp2p
+video/mp2t
+video/mp4v-es
+video/mpv
+video/mpeg mpeg mpg mpe
+video/nv
+video/parityfec
+video/pointer
+video/quicktime qt mov
+video/smpte292m
+video/vnd.fvt
+video/vnd.motorola.video
+video/vnd.motorola.videop
+video/vnd.mpegurl mxu m4u
+video/vnd.nokia.interleaved-multimedia
+video/vnd.objectvideo
+video/vnd.vivo
+video/x-msvideo avi
+video/x-sgi-movie movie
+x-conference/x-cooltalk ice
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/curl-connect.cpp b/sca-cpp/branches/lightweight-sca/modules/http/curl-connect.cpp
new file mode 100644
index 0000000000..9f5ee17368
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/curl-connect.cpp
@@ -0,0 +1,98 @@
+/*
+ * 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$ */
+
+/**
+ * HTTP connect command line test tool.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "perf.hpp"
+#include "http.hpp"
+
+namespace tuscany {
+namespace http {
+
+const bool testConnect(const string& url, const string& ca = "", const string& cert = "", const string& key = "") {
+ gc_scoped_pool p;
+
+ CURLSession cs(ca, cert, key, "", 0);
+ const failable<bool> crc = connect(url, cs);
+ assert(hasContent(crc));
+
+ apr_pollset_t* pollset;
+ apr_status_t cprc = apr_pollset_create(&pollset, 2, pool(p), 0);
+ assert(cprc == APR_SUCCESS);
+ apr_socket_t* csock = sock(0, p);
+ const apr_pollfd_t* cpollfd = pollfd(csock, APR_POLLIN | APR_POLLERR | APR_POLLNVAL | APR_POLLHUP, p);
+ apr_pollset_add(pollset, cpollfd);
+ apr_socket_t* tsock = sock(cs);
+ const apr_pollfd_t* tpollfd = pollfd(tsock, APR_POLLIN | APR_POLLERR | APR_POLLNVAL | APR_POLLHUP, p);
+ apr_pollset_add(pollset, tpollfd);
+
+ const apr_pollfd_t* pollfds;
+ apr_int32_t pollcount;
+ for(;;) {
+ apr_status_t pollrc = apr_pollset_poll(pollset, -1, &pollcount, &pollfds);
+ assert(pollrc == APR_SUCCESS);
+
+ for (; pollcount > 0; pollcount--, pollfds++) {
+ if (pollfds->rtnevents & APR_POLLIN) {
+ char data[8192];
+ if (pollfds->desc.s == csock) {
+ const size_t rl = ::read(0, data, sizeof(data));
+ if (rl == (size_t)-1)
+ return false;
+ if (rl > 0) {
+ const failable<bool> src = http::send(data, rl, cs);
+ assert(hasContent(src));
+ }
+ }
+ else {
+ const failable<size_t> frl = http::recv(data, sizeof(data), cs);
+ assert(hasContent(frl));
+ const size_t rl = content(frl);
+ if (rl == 0)
+ return true;
+ const size_t wl = ::write(0, data, rl);
+ assert(wl == rl);
+ }
+ continue;
+ }
+ assert(!(pollfds->rtnevents & (APR_POLLERR | APR_POLLHUP | APR_POLLNVAL)));
+ }
+ }
+ return true;
+}
+
+}
+}
+
+int main(unused const int argc, const char** argv) {
+ if (argc > 2)
+ tuscany::http::testConnect(tuscany::string(argv[1]), tuscany::string(argv[2]), tuscany::string(argv[3]), tuscany::string(argv[4]));
+ else
+ tuscany::http::testConnect(tuscany::string(argv[1]));
+ return 0;
+}
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/curl-get.cpp b/sca-cpp/branches/lightweight-sca/modules/http/curl-get.cpp
new file mode 100644
index 0000000000..a16daeeae3
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/curl-get.cpp
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * HTTP GET command line test tool.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "perf.hpp"
+#include "http.hpp"
+
+namespace tuscany {
+namespace http {
+
+const bool testGet(const string& url, const string& ca = "", const string& cert = "", const string& key = "") {
+ CURLSession ch(ca, cert, key, "", 0);
+ const failable<value> val = get(url, ch);
+ assert(hasContent(val));
+ cout << content(val) << endl;
+ return true;
+}
+
+}
+}
+
+int main(unused const int argc, const char** argv) {
+ if (argc > 2)
+ tuscany::http::testGet(tuscany::string(argv[1]), tuscany::string(argv[2]), tuscany::string(argv[3]), tuscany::string(argv[4]));
+ else
+ tuscany::http::testGet(tuscany::string(argv[1]));
+ return 0;
+}
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/curl-test.cpp b/sca-cpp/branches/lightweight-sca/modules/http/curl-test.cpp
new file mode 100644
index 0000000000..f0806ea577
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/curl-test.cpp
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test HTTP client functions.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "perf.hpp"
+#include "http.hpp"
+
+namespace tuscany {
+namespace http {
+
+string testURI = "http://localhost:8090";
+
+ostream* curlWriter(const string& s, ostream* os) {
+ (*os) << s;
+ return os;
+}
+
+const bool testGet() {
+ CURLSession ch("", "", "", "", 0);
+ {
+ ostringstream os;
+ const failable<list<ostream*> > r = get<ostream*>(curlWriter, &os, testURI, ch);
+ assert(hasContent(r));
+ assert(contains(str(os), "HTTP/1.1 200 OK"));
+ assert(contains(str(os), "It works"));
+ }
+ {
+ const failable<value> r = getcontent(testURI, ch);
+ assert(hasContent(r));
+ assert(contains(car(reverse(list<value>(content(r)))), "It works"));
+ }
+ return true;
+}
+
+struct getLoop {
+ CURLSession& ch;
+ getLoop(CURLSession& ch) : ch(ch) {
+ }
+ const bool operator()() const {
+ const failable<value> r = getcontent(testURI, ch);
+ assert(hasContent(r));
+ assert(contains(car(reverse(list<value>(content(r)))), "It works"));
+ return true;
+ }
+};
+
+const bool testGetPerf() {
+ CURLSession ch("", "", "", "", 0);
+ lambda<bool()> gl = getLoop(ch);
+ cout << "Static GET test " << time(gl, 5, 200) << " ms" << endl;
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::gc_scoped_pool p;
+ tuscany::cout << "Testing..." << tuscany::endl;
+ //tuscany::http::testURI = tuscany::string("http://") + tuscany::http::hostName() + ":8090";
+
+ tuscany::http::testGet();
+ tuscany::http::testGetPerf();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/form-auth-conf b/sca-cpp/branches/lightweight-sca/modules/http/form-auth-conf
new file mode 100755
index 0000000000..fbe943f3d9
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/form-auth-conf
@@ -0,0 +1,82 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Generate a minimal HTTPD form authentication configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"`
+host=`echo $conf | awk '{ print $6 }'`
+
+if [ "$2" = "" ]; then
+ providers="file"
+else
+ providers="$2 file"
+fi
+
+if [ "$3" = "" ]; then
+ pw=`cat $root/cert/ca.key | head -2 | tail -1`
+else
+ pw="$3"
+fi
+
+sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"`
+if [ "$sslconf" = "" ]; then
+ sslsuffix=""
+else
+ sslsuffix="-ssl"
+fi
+
+# Disallow public access to server resources
+cat >$root/conf/noauth$sslsuffix.conf <<EOF
+# Generated by: form-auth-conf $*
+# Disallow public access to server resources
+
+EOF
+
+# Generate form authentication configuration
+cat >>$root/conf/locauth$sslsuffix.conf <<EOF
+# Generated by: form-auth-conf $*
+# Require clients to present a userid + password through form-based
+# authentication
+<Location />
+AuthType Form
+AuthName "$host"
+AuthFormProvider socache $providers
+AuthnCacheProvideFor $providers
+AuthnCacheContext /
+AuthFormLoginRequiredLocation /login/
+AuthFormLogoutLocation /
+Session On
+SessionCookieName TuscanyFormAuth domain=.$host; path=/
+SessionCryptoPassphrase $pw
+Require valid-user
+</Location>
+
+<Location /login/dologin>
+SetHandler form-login-handler
+</Location>
+
+<Location /logout/dologout>
+SetHandler form-logout-handler
+</Location>
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/group-auth-conf b/sca-cpp/branches/lightweight-sca/modules/http/group-auth-conf
new file mode 100755
index 0000000000..e9617f696a
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/group-auth-conf
@@ -0,0 +1,57 @@
+#!/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.
+
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+user=$2
+group="members"
+
+sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"`
+if [ "$sslconf" = "" ]; then
+ sslsuffix=""
+else
+ sslsuffix="-ssl"
+fi
+
+# Disallow public access to server resources
+cat >$root/conf/noauth$sslsuffix.conf <<EOF
+# Generated by: group-auth-conf $*
+# Disallow public access to server resources
+
+EOF
+
+# Add user to group
+cat $root/conf/httpd.groups | awk " BEGIN { found = 0 } /$group: / { printf \"%s %s\n\", \$0, \"$user\"; found = 1 } !/$group: / { printf \"%s\n\", \$0 } END { if (found == 0) printf \"%s: %s\n\", \"$group\", \"$user\" } " >$root/conf/.httpd.groups.tmp 2>/dev/null
+cp $root/conf/.httpd.groups.tmp $root/conf/httpd.groups
+rm $root/conf/.httpd.groups.tmp
+
+# Generate HTTPD group authorization configuration
+conf=`cat $root/conf/locauth$sslsuffix.conf | grep "Generated by: group-auth-conf"`
+if [ "$conf" = "" ]; then
+ cat >>$root/conf/locauth$sslsuffix.conf <<EOF
+# Generated by: group-auth-conf $1
+# Allow group member access to root location
+<Location />
+Require group members
+</Location>
+
+EOF
+fi
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/htdocs/index.html b/sca-cpp/branches/lightweight-sca/modules/http/htdocs/index.html
new file mode 100644
index 0000000000..236864edfb
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/htdocs/index.html
@@ -0,0 +1,32 @@
+<!--
+ 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>
+<head>
+<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0"/>
+<meta name="apple-mobile-web-app-capable" content="yes"/>
+<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/>
+<link rel="stylesheet" type="text/css" href="/ui-min.css"/>
+<title>It works</title>
+</head>
+<body>
+<h1>It works!</h1>
+</body>
+</html>
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/htdocs/login/index.html b/sca-cpp/branches/lightweight-sca/modules/http/htdocs/login/index.html
new file mode 100644
index 0000000000..fd3bc21889
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/htdocs/login/index.html
@@ -0,0 +1,51 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+
+<html>
+<head>
+<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0"/>
+<meta name="apple-mobile-web-app-capable" content="yes"/>
+<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/>
+<link rel="stylesheet" type="text/css" href="/ui-min.css"/>
+<script type="text/javascript" src="/all-min.js"></script>
+<title>Sign in</title>
+</head>
+<body>
+<h1>Sign in</h1>
+<br/>
+
+<script type="text/javascript">
+function submitFormSignin() {
+ clearauthcookie();
+ document.formSignin.httpd_location.value = '/';
+ document.formSignin.submit();
+}
+</script>
+
+<form name="formSignin" method="POST" action="/login/dologin">
+<table border="0">
+<tr><td>Username:</td><td><input type="text" name="httpd_username" value=""/></td></tr>
+<tr><td>Password:</td><td><input type="password" name="httpd_password" value=""/></td></tr>
+<tr><td><input type="button" onclick="submitFormSignin()" value="Sign in"/></td><td></td></tr>
+</table>
+<input type="hidden" name="httpd_location" value="/"/>
+</form>
+
+</body>
+</html>
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/htdocs/logout/index.html b/sca-cpp/branches/lightweight-sca/modules/http/htdocs/logout/index.html
new file mode 100644
index 0000000000..218dd5d52c
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/htdocs/logout/index.html
@@ -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.
+-->
+
+<html>
+<body>
+<head>
+<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0"/>
+<meta name="apple-mobile-web-app-capable" content="yes"/>
+<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/>
+<link rel="stylesheet" type="text/css" href="/ui-min.css"/>
+<script type="text/javascript" src="/all-min.js"></script>
+<title>Sign out</title>
+</head>
+<h1>Sign out</h1>
+<br/>
+
+<form name="signout" action="/login" method="GET">
+<script type="text/javascript">
+function submitSignout() {
+ clearauthcookie();
+ document.signout.submit();
+ return true;
+}
+</script>
+<input type="button" onclick="submitSignout()" value="Sign out"/>
+</form>
+</body>
+</html>
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/http-test b/sca-cpp/branches/lightweight-sca/modules/http/http-test
new file mode 100755
index 0000000000..956b13a516
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/http-test
@@ -0,0 +1,34 @@
+#!/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
+rm -rf tmp
+./httpd-conf tmp localhost 8090 htdocs
+./httpd-event-conf tmp
+./httpd-start tmp
+sleep 2
+
+# Test
+./curl-test 2>/dev/null
+rc=$?
+
+# Cleanup
+./httpd-stop tmp
+sleep 2
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/http.hpp b/sca-cpp/branches/lightweight-sca/modules/http/http.hpp
new file mode 100644
index 0000000000..408b9fdee5
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/http.hpp
@@ -0,0 +1,1042 @@
+/*
+ * 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_http_hpp
+#define tuscany_http_hpp
+
+/**
+ * CURL HTTP client functions.
+ */
+
+#include <unistd.h>
+#include <curl/curl.h>
+#include <curl/easy.h>
+#include <apr.h>
+#include <apr_lib.h>
+#include <apr_network_io.h>
+#include <apr_portable.h>
+#include <apr_poll.h>
+#include <apr_uri.h>
+
+#include "string.hpp"
+#include "gc.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "element.hpp"
+#include "monad.hpp"
+#include "parallel.hpp"
+#include "../scheme/io.hpp"
+#include "../atom/atom.hpp"
+#include "../rss/rss.hpp"
+#include "../json/json.hpp"
+
+namespace tuscany {
+namespace http {
+
+/**
+ * Enable CURL verbose debug log.
+ */
+//#define WANT_MAINTAINER_CURL_VERBOSE
+
+/**
+ * CURL library runtime, one per process.
+ */
+class CURLRuntime {
+public:
+ CURLRuntime() {
+ curl_global_init(CURL_GLOBAL_ALL);
+ }
+} curlRuntime;
+
+/**
+ * Represents a CURL session handle.
+ */
+class CURLSession {
+public:
+ CURLSession() : h(NULL), p(), sock(NULL), wpollset(NULL), wpollfd(NULL), rpollset(NULL), rpollfd(NULL), owner(false), ca(""), cert(""), key(""), cookie(""), timeout(0) {
+ }
+
+ CURLSession(const string& ca, const string& cert, const string& key, const string& cookie, const int timeout) : h(NULL), p(), sock(NULL), wpollset(NULL), wpollfd(NULL), rpollset(NULL), rpollfd(NULL), owner(true), ca(ca), cert(cert), key(key), cookie(cookie), timeout(timeout) {
+ }
+
+ CURLSession(const CURLSession& c) : h(c.h), p(c.p), sock(c.sock), wpollset(c.wpollset), wpollfd(c.wpollfd), rpollset(c.rpollset), rpollfd(c.rpollfd), owner(false), ca(c.ca), cert(c.cert), key(c.key), cookie(c.cookie), timeout(c.timeout) {
+ }
+
+ const CURLSession& operator=(const CURLSession& c) {
+ if(this == &c)
+ return *this;
+ h = c.h;
+ p = c.p;
+ sock = c.sock;
+ wpollset = c.wpollset;
+ wpollfd = c.wpollfd;
+ rpollset = c.rpollset;
+ rpollfd = c.rpollfd;
+ owner = false;
+ ca = c.ca;
+ cert = c.cert;
+ key = c.key;
+ cookie = c.cookie;
+ timeout = c.timeout;
+ return *this;
+ }
+
+ ~CURLSession() {
+ if (!owner)
+ return;
+ if (h == NULL)
+ return;
+ curl_easy_cleanup(h);
+ }
+
+private:
+ CURL* h;
+ gc_child_pool p;
+ apr_socket_t* sock;
+ apr_pollset_t* wpollset;
+ apr_pollfd_t* wpollfd;
+ apr_pollset_t* rpollset;
+ apr_pollfd_t* rpollfd;
+ bool owner;
+
+ friend CURL* handle(const CURLSession& cs);
+ friend apr_socket_t* sock(const CURLSession& cs);
+ friend const failable<CURL*> setup(const string& url, CURLSession& cs);
+ friend const failable<bool> cleanup(CURLSession& cs);
+ friend const failable<bool> connect(const string& url, CURLSession& cs);
+ friend const failable<bool> send(const char* c, const size_t l, CURLSession& cs);
+ friend const failable<size_t> recv(char* c, const size_t l, CURLSession& cs);
+
+public:
+ string ca;
+ string cert;
+ string key;
+ string cookie;
+ int timeout;
+};
+
+/**
+ * Returns the CURL handle used by a CURL session.
+ */
+CURL* handle(const CURLSession& cs) {
+ return cs.h;
+}
+
+/**
+ * Return an apr_socket_t for the socket used by a CURL session.
+ */
+apr_socket_t* sock(const CURLSession& cs) {
+ return cs.sock;
+}
+
+/**
+ * Convert a socket fd to an apr_socket_t.
+ */
+apr_socket_t* sock(const int sd, const gc_pool& p) {
+ int fd = sd;
+ apr_socket_t* s = NULL;
+ apr_os_sock_put(&s, &fd, pool(p));
+ return s;
+}
+
+/**
+ * Convert a CURL return code to an error string.
+ */
+const string curlreason(CURLcode rc) {
+ return curl_easy_strerror(rc);
+}
+
+/**
+ * Convert an APR status to an error string.
+ */
+const string apreason(apr_status_t rc) {
+ char buf[256];
+ return apr_strerror(rc, buf, sizeof(buf));
+}
+
+/**
+ * Escape a URI or a query argument.
+ */
+const char escape_c2x[] = "0123456789ABCDEF";
+
+const string escape(const string& unesc, const char* reserv) {
+ char* copy = (char*)apr_palloc(gc_current_pool(), 3 * length(unesc) + 3);
+ const unsigned char* s = (const unsigned char *)c_str(unesc);
+ unsigned char* d = (unsigned char*)copy;
+ unsigned c;
+ while ((c = *s)) {
+ if (!apr_isalnum(c) && !strchr(reserv, c)) {
+ *d++ = '%';
+ *d++ = escape_c2x[c >> 4];
+ *d++ = escape_c2x[c & 0xf];
+ }
+ else {
+ *d++ = (unsigned char)c;
+ }
+ ++s;
+ }
+ *d = '\0';
+ return copy;
+}
+
+const string escapeURI(const string& uri) {
+ debug(uri, "http::escapeURI::uri");
+ const string e = escape(uri, "?$-_.+!*'(),:@&=/~%");
+ debug(e, "http::escapeURI::result");
+ return e;
+}
+
+const string escapeArg(const string& arg) {
+ debug(arg, "http::escapeArg::arg");
+ const string e = escape(arg, "-_.~");
+ debug(e, "http::escapeArg::result");
+ return e;
+}
+
+/**
+ * Return true if a URI is absolute.
+ */
+const bool isAbsolute(const string& uri) {
+ return contains(uri, "://");
+}
+
+/**
+ * Parse a URI and return its host name.
+ */
+const string hostName(const string& uri, const gc_pool& p) {
+ apr_uri_t u;
+ const apr_status_t rc = apr_uri_parse(pool(p), c_str(uri), &u);
+ if (rc != APR_SUCCESS)
+ return "";
+ if (u.hostname == NULL)
+ return "";
+ return u.hostname;
+}
+
+/**
+ * Parse a URI and return its scheme.
+ */
+const string scheme(const string& uri, const gc_pool& p) {
+ apr_uri_t u;
+ const apr_status_t rc = apr_uri_parse(pool(p), c_str(uri), &u);
+ if (rc != APR_SUCCESS)
+ return "";
+ if (u.scheme == NULL)
+ return "";
+ return u.scheme;
+}
+
+/**
+ * Return the first subdomain name in a host name.
+ */
+const string subDomain(const string& host) {
+ return substr(host, 0, find(host, '.'));
+}
+
+/**
+ * Return the top domain name in a host name.
+ */
+const string topDomain(const string& host) {
+ const size_t d = find(host, '.');
+ return d == length(host) ? host : substr(host, d + 1);
+}
+
+/**
+ * Setup a CURL session
+ */
+const failable<CURL*> setup(const string& url, CURLSession& cs) {
+
+ // Init CURL session
+ if (cs.h != NULL)
+ cleanup(cs);
+ cs.h = curl_easy_init();
+ CURL* ch = cs.h;
+ curl_easy_setopt(ch, CURLOPT_USERAGENT, "libcurl/1.0");
+#ifdef WANT_MAINTAINER_CURL_VERBOSE
+ curl_easy_setopt(ch, CURLOPT_VERBOSE, true);
+#endif
+
+ // Setup protocol options
+ curl_easy_setopt(ch, CURLOPT_TCP_NODELAY, true);
+ curl_easy_setopt(ch, CURLOPT_FOLLOWLOCATION, true);
+ curl_easy_setopt(ch, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
+ curl_easy_setopt(ch, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(ch, CURLOPT_TIMEOUT, cs.timeout);
+
+ // Setup SSL options
+ if (cs.ca != "") {
+ debug(cs.ca, "http::setup::ca");
+ curl_easy_setopt(ch, CURLOPT_CAINFO, c_str(cs.ca));
+ curl_easy_setopt(ch, CURLOPT_SSL_VERIFYPEER, true);
+ curl_easy_setopt(ch, CURLOPT_SSL_VERIFYHOST, 2);
+ } else
+ curl_easy_setopt(ch, CURLOPT_SSL_VERIFYPEER, false);
+ if (cs.cert != "") {
+ debug(cs.cert, "http::setup::cert");
+ curl_easy_setopt(ch, CURLOPT_SSLCERT, c_str(cs.cert));
+ curl_easy_setopt(ch, CURLOPT_SSLCERTTYPE, "PEM");
+ }
+ if (cs.key != "") {
+ debug(cs.key, "http::setup::key");
+ curl_easy_setopt(ch, CURLOPT_SSLKEY, c_str(cs.key));
+ curl_easy_setopt(ch, CURLOPT_SSLKEYTYPE, "PEM");
+ }
+ if (cs.cookie != "") {
+ debug(cs.cookie, "http::setup::cookie");
+ curl_easy_setopt(ch, CURLOPT_COOKIE, c_str(cs.cookie));
+ }
+
+ // Set up HTTP basic auth if requested
+ apr_uri_t u;
+ apr_pool_t* p = gc_current_pool();
+ const apr_status_t prc = apr_uri_parse(p, c_str(url), &u);
+ if (prc == APR_SUCCESS) {
+ if (u.user != NULL) {
+ debug(u.user, "http::setup::user");
+ curl_easy_setopt(ch, CURLOPT_USERNAME, u.user);
+ }
+ if (u.password != NULL) {
+ debug(u.password, "http::setup::pass");
+ curl_easy_setopt(ch, CURLOPT_PASSWORD, u.password);
+ }
+ if (u.user != NULL || u.password != NULL) {
+ curl_easy_setopt(ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
+
+ // Set target URL, omitting the user:password part
+ curl_easy_setopt(ch, CURLOPT_URL, c_str(escapeURI(apr_uri_unparse(p, &u, APR_URI_UNP_OMITUSERINFO))));
+
+ return ch;
+ }
+ }
+
+ // Set target URL
+ curl_easy_setopt(ch, CURLOPT_URL, c_str(escapeURI(url)));
+
+ return ch;
+}
+
+/**
+ * Cleanup a CURL session
+ */
+const failable<bool> cleanup(CURLSession& cs) {
+ if (cs.h == NULL)
+ return true;
+ curl_easy_cleanup(cs.h);
+ cs.h = NULL;
+ return true;
+}
+
+/**
+ * Context passed to the read callback function.
+ */
+class CURLReadContext {
+public:
+ CURLReadContext(const list<string>& ilist) : ilist(ilist) {
+ }
+ list<string> ilist;
+};
+
+/**
+ * Called by CURL to read data to send.
+ */
+size_t readCallback(void *ptr, size_t size, size_t nmemb, void *data) {
+ CURLReadContext& rcx = *static_cast<CURLReadContext*>(data);
+ if (isNil(rcx.ilist))
+ return 0;
+ const list<string> f(fragment(rcx.ilist, size * nmemb));
+ const string s = car(f);
+ rcx.ilist = cdr(f);
+ memcpy(ptr, c_str(s), length(s));
+ return length(s);
+}
+
+/**
+ * Context passed to CURL write callback function.
+ */
+template<typename R> class CURLWriteContext {
+public:
+ CURLWriteContext(const lambda<R(const string&, const R)>& reduce, const R& accum) : reduce(reduce), accum(accum) {
+ }
+ const lambda<R(const string&, const R)> reduce;
+ R accum;
+};
+
+/**
+ * Called by CURL to write received data.
+ */
+template<typename R> size_t writeCallback(void *ptr, size_t size, size_t nmemb, void *data) {
+ CURLWriteContext<R>& wcx = *(static_cast<CURLWriteContext<R>*> (data));
+ const size_t realsize = size * nmemb;
+ wcx.accum = wcx.reduce(string((const char*)ptr, realsize), wcx.accum);
+ return realsize;
+}
+
+/**
+ * Apply an HTTP verb to a list containing a list of headers and a list of content, and
+ * a reduce function used to process the response.
+ */
+curl_slist* headers(curl_slist* cl, const list<string>& h) {
+ if (isNil(h))
+ return cl;
+ return headers(curl_slist_append(cl, c_str(string(car(h)))), cdr(h));
+}
+
+template<typename R> const failable<list<R> > apply(const list<list<string> >& hdr, const lambda<R(const string&, const R)>& reduce, const R& initial, const string& url, const string& verb, CURLSession& cs) {
+ debug(url, "http::apply::url");
+ debug(verb, "http::apply::verb");
+
+ // Setup the CURL session
+ const failable<CURL*> fch = setup(url, cs);
+ if (!hasContent(fch)) {
+ cleanup(cs);
+ return mkfailure<list<R>>(fch);
+ }
+ CURL* ch = content(fch);
+
+ // Set the request headers
+ curl_slist* hl = headers(NULL, car(hdr));
+ if (hl != NULL)
+ curl_easy_setopt(ch, CURLOPT_HTTPHEADER, hl);
+
+ // Convert request body to a string
+ // TODO use HTTP chunking instead
+ ostringstream os;
+ write(cadr(hdr), os);
+ const string s = str(os);
+ const size_t sz = length(s);
+
+ // Setup the read, write header and write data callbacks
+ CURLReadContext rcx(mklist(s));
+ curl_easy_setopt(ch, CURLOPT_READFUNCTION, (size_t (*)(void*, size_t, size_t, void*))readCallback);
+ curl_easy_setopt(ch, CURLOPT_READDATA, &rcx);
+ CURLWriteContext<R> hcx(reduce, initial);
+ curl_easy_setopt(ch, CURLOPT_HEADERFUNCTION, (size_t (*)(void*, size_t, size_t, void*))(writeCallback<R>));
+ curl_easy_setopt(ch, CURLOPT_HEADERDATA, &hcx);
+ CURLWriteContext<R> wcx(reduce, initial);
+ curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, (size_t (*)(void*, size_t, size_t, void*))(writeCallback<R>));
+ curl_easy_setopt(ch, CURLOPT_WRITEDATA, &wcx);
+
+ // Apply the HTTP verb
+ if (verb == "POST") {
+ curl_easy_setopt(ch, CURLOPT_POST, true);
+ curl_easy_setopt(ch, CURLOPT_POSTFIELDSIZE, sz);
+ } else if (verb == "PUT") {
+ curl_easy_setopt(ch, CURLOPT_UPLOAD, true);
+ curl_easy_setopt(ch, CURLOPT_INFILESIZE, sz);
+ } else if (verb == "PATCH") {
+ curl_easy_setopt(ch, CURLOPT_UPLOAD, true);
+ curl_easy_setopt(ch, CURLOPT_CUSTOMREQUEST, "PATCH");
+ curl_easy_setopt(ch, CURLOPT_INFILESIZE, sz);
+ } else if (verb == "DELETE")
+ curl_easy_setopt(ch, CURLOPT_CUSTOMREQUEST, "DELETE");
+ const CURLcode rc = curl_easy_perform(ch);
+
+ // Free the headers
+ if (hl != NULL)
+ curl_slist_free_all(hl);
+
+ // Return the HTTP return code or content
+ if (rc) {
+ cleanup(cs);
+ return mkfailure<list<R> >(string(curl_easy_strerror(rc)));
+ }
+ long httprc;
+ curl_easy_getinfo (ch, CURLINFO_RESPONSE_CODE, &httprc);
+ if (httprc != 200 && httprc != 201) {
+ cleanup(cs);
+ ostringstream es;
+ es << "HTTP code " << httprc;
+ return mkfailure<list<R> >(str(es));
+ }
+
+ cleanup(cs);
+ return mklist<R>(hcx.accum, wcx.accum);
+}
+
+/**
+ * Evaluate an expression remotely, at the given URL.
+ */
+const failable<value> evalExpr(const value& expr, const string& url, CURLSession& cs) {
+ debug(url, "http::evalExpr::url");
+ debug(expr, "http::evalExpr::input");
+
+ // Convert expression to a JSON-RPC request
+ js::JSContext cx;
+ const failable<list<string> > jsreq = json::jsonRequest(1, car<value>(expr), cdr<value>(expr), cx);
+ if (!hasContent(jsreq))
+ return mkfailure<value>(jsreq);
+
+ // POST it to the URL
+ const list<string> h = mklist<string>("Content-Type: application/json-rpc");
+ const failable<list<list<string> > > res = apply<list<string> >(mklist<list<string> >(h, content(jsreq)), rcons<string>, list<string>(), url, "POST", cs);
+ if (!hasContent(res))
+ return mkfailure<value>(res);
+
+ // Parse and return JSON-RPC result
+ const failable<value> rval = json::jsonResultValue(cadr<list<string> >(content(res)), cx);
+ debug(rval, "http::evalExpr::result");
+ if (!hasContent(rval))
+ return mkfailure<value>(rval);
+ return content(rval);
+}
+
+/**
+ * Find and return a header.
+ */
+const maybe<string> header(const char* prefix, const list<string>& h) {
+ if (isNil(h))
+ return maybe<string>();
+ const string s = car(h);
+ if (find(s, prefix) != 0)
+ return header(prefix, cdr(h));
+ const string l(substr(s, length(prefix)));
+ return substr(l, 0, find_first_of(l, "\r\n"));
+}
+
+/**
+ * Find and return a location header.
+ */
+const string location(const list<string>& h) {
+ const maybe<string> l = header("Location: ", h);
+ return hasContent(l)? content(l) : "";
+}
+
+/**
+ * Convert a location to an entry id.
+ */
+const value entryId(const failable<string> l) {
+ if (!hasContent(l))
+ return list<value>();
+ const string ls(content(l));
+ return value(mklist<value>(string(substr(ls, find_last(ls, '/') + 1))));
+}
+
+/**
+ * Find and return a content-type header.
+ */
+const string contentType(const list<string>& h) {
+ const maybe<string> ct = header("Content-Type: ", h);
+ return hasContent(ct)? content(ct) : "";
+}
+
+/**
+ * HTTP GET, return the resource at the given URL.
+ */
+template<typename R> const failable<list<R> > get(const lambda<R(const string&, const R)>& reduce, const R& initial, const string& url, CURLSession& cs) {
+ debug(url, "http::get::url");
+ const list<list<string> > req = mklist(list<string>(), list<string>());
+ return apply(req, reduce, initial, url, "GET", cs);
+}
+
+/**
+ * HTTP GET, return a list of values representing the resource at the given URL.
+ */
+const failable<value> getcontent(const string& url, CURLSession& cs) {
+ debug(url, "http::get::url");
+
+ // Get the contents of the resource at the given URL
+ const failable<list<list<string> > > res = get<list<string>>(rcons<string>, list<string>(), url, cs);
+ if (!hasContent(res))
+ return mkfailure<value>(res);
+ const list<string> ls(reverse(cadr(content(res))));
+
+ // Return the content as a list of values
+ const value val(mkvalues(ls));
+ debug(val, "http::get::result");
+ return val;
+}
+
+/**
+ * Convert an HTTP content response to a value.
+ */
+const failable<value> responseValue(const list<list<string> > res) {
+
+ // Parse the returned content
+ const string ct = contentType(car(res));
+ debug(ct, "http::responseValue::contentType");
+
+ const list<string> ls(reverse(cadr(res)));
+ debug(ls, "http::responseValue::content");
+
+ if (atom::isATOMEntry(ls)) {
+ // Read an ATOM entry
+ const value val(elementsToValues(content(atom::readATOMEntry(ls))));
+ debug(val, "http::responseValue::result");
+ return val;
+ }
+ if (contains(ct, "application/atom+xml") || atom::isATOMFeed(ls)) {
+ // Read an ATOM feed
+ const value val(elementsToValues(content(atom::readATOMFeed(ls))));
+ debug(val, "http::responseValue::result");
+ return val;
+ }
+ if (contains(ct, "application/rss+xml") || rss::isRSSFeed(ls)) {
+ // Read an RSS feed
+ const value val(elementsToValues(content(rss::readRSSFeed(ls))));
+ debug(val, "http::responseValue::result");
+ return val;
+ }
+ if (contains(ct, "text/javascript") || contains(ct, "application/json") || json::isJSON(ls)) {
+ // Read a JSON document
+ js::JSContext cx;
+ const value val(json::jsonValues(content(json::readJSON(ls, cx))));
+ debug(val, "http::responseValue::result");
+ return val;
+ }
+ if (contains(ct, "application/x-javascript")) {
+ // Read a JSON document enclosed in a javascript function call
+ // Extract the JSON out of the enclosing parenthesis
+ ostringstream os;
+ write(ls, os);
+ const string s = str(os);
+ const size_t fp = find(s, '(');
+ const size_t lp = find_last(s, ')');
+ const list<string> jls = mklist<string>(substr(s, fp + 1, lp - (fp + 1)));
+ debug(jls, "http::responseValue::javascript::content");
+
+ js::JSContext cx;
+ const value val(json::jsonValues(content(json::readJSON(jls, cx))));
+ debug(val, "http::responseValue::result");
+ return val;
+ }
+ if (contains(ct, "text/xml") || contains(ct, "application/xml") || isXML(ls)) {
+ // Read an XML document
+ const value val(elementsToValues(readXML(ls)));
+ debug(val, "http::responseValue::result");
+ return val;
+ }
+
+ // Return the content type and a content list
+ const value val(mklist<value>(ct, mkvalues(ls)));
+ debug(val, "http::responseValue::result");
+ return val;
+}
+
+/**
+ * HTTP GET, return a list of values representing the resource at the given URL.
+ */
+const failable<value> get(const string& url, CURLSession& cs) {
+ debug(url, "http::get::url");
+
+ // Get the contents of the resource at the given URL
+ const failable<list<list<string> > > res = get<list<string> >(rcons<string>, list<string>(), url, cs);
+ if (!hasContent(res))
+ return mkfailure<value>(res);
+
+ // Parse the returned content
+ return responseValue(content(res));
+}
+
+/**
+ * Form an HTTP content request.
+ */
+const failable<list<list<string> > > writeRequest(const failable<list<string> >& ls, const string& ct) {
+ if (!hasContent(ls))
+ return mkfailure<list<list<string> > >(ls);
+ const list<list<string> > req = mklist<list<string> >(mklist<string>(string("Content-Type: ") + ct), content(ls));
+ debug(req, "http::writeRequest::req");
+ return req;
+}
+
+/**
+ * Convert a value to an HTTP content request.
+ */
+const failable<list<list<string> > > contentRequest(const value& c, unused const string& url) {
+
+ // Check if the client requested a specific format
+ //TODO derive that from given URL
+ const list<value> fmt = assoc<value>("format", list<value>());
+
+ // Write as a scheme value if requested by the client
+ if (!isNil(fmt) && cadr(fmt) == "scheme")
+ return writeRequest(mklist<string>(scheme::writeValue(c)), "text/plain; charset=utf-8");
+
+ // Write a simple value as a JSON value
+ if (!isList(c)) {
+ js::JSContext cx;
+ if (isSymbol(c)) {
+ const list<value> lc = mklist<value>(mklist<value>("name", value(string(c))));
+ debug(lc, "http::contentRequest::symbol");
+ return writeRequest(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8");
+ }
+ const list<value> lc = mklist<value>(mklist<value>("value", c));
+ debug(lc, "http::contentRequest::value");
+ return writeRequest(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8");
+ }
+
+ // Write an empty list as a JSON empty value
+ if (isNil((list<value>)c)) {
+ js::JSContext cx;
+ debug(list<value>(), "http::contentRequest::empty");
+ return writeRequest(json::writeJSON(list<value>(), cx), "application/json; charset=utf-8");
+ }
+
+ // Write content-type / content-list pair
+ if (isString(car<value>(c)) && !isNil(cdr<value>(c)) && isList(cadr<value>(c)))
+ return writeRequest(convertValues<string>(cadr<value>(c)), car<value>(c));
+
+ // Write an assoc value as JSON
+ if (isSymbol(car<value>(c)) && !isNil(cdr<value>(c))) {
+ js::JSContext cx;
+ const list<value> lc = mklist<value>(c);
+ debug(lc, "http::contentRequest::assoc");
+ debug(valuesToElements(lc), "http::contentRequest::assoc::element");
+ return writeRequest(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8");
+ }
+
+ // Write value as JSON if requested by the client
+ if (!isNil(fmt) && cadr(fmt) == "json") {
+ js::JSContext cx;
+ return writeRequest(json::writeJSON(valuesToElements(c), cx), "application/json; charset=utf-8");
+ }
+
+ // Convert list of values to element values
+ const list<value> e = valuesToElements(c);
+ debug(e, "http::contentRequest::elements");
+
+ // Write an ATOM feed or entry
+ if (isList(car<value>(e)) && !isNil(car<value>(e))) {
+ const list<value> el = car<value>(e);
+ if (isSymbol(car<value>(el)) && car<value>(el) == element && !isNil(cdr<value>(el)) && isSymbol(cadr<value>(el)) && elementHasChildren(el) && !elementHasValue(el)) {
+ if (cadr<value>(el) == atom::feed)
+ return writeRequest(atom::writeATOMFeed(e), "application/atom+xml; charset=utf-8");
+ if (cadr<value>(el) == atom::entry)
+ return writeRequest(atom::writeATOMEntry(e), "application/atom+xml; charset=utf-8");
+ }
+ }
+
+ // Write any other compound value as a JSON value
+ js::JSContext cx;
+ return writeRequest(json::writeJSON(e, cx), "application/json; charset=utf-8");
+}
+
+/**
+ * HTTP POST.
+ */
+const failable<value> post(const value& val, const string& url, CURLSession& cs) {
+ debug(url, "http::post::url");
+
+ // Convert value to a content request
+ const failable<list<list<string> > > req = contentRequest(val, url);
+ if (!hasContent(req))
+ return mkfailure<value>(req);
+ debug(content(req), "http::post::input");
+
+ // POST it to the URL
+ const failable<list<list<string> > > res = apply<list<string>>(content(req), rcons<string>, list<string>(), url, "POST", cs);
+ if (!hasContent(res))
+ return mkfailure<value>(res);
+
+ // Return the new entry id from the HTTP location header, if any
+ const string loc = location(car(content(res)));
+ if (length(loc) != 0) {
+ const value eid(entryId(location(car(content(res)))));
+ debug(eid, "http::post::result");
+ return eid;
+ }
+
+ // Return the returned content
+ return responseValue(content(res));
+}
+
+/**
+ * HTTP PUT.
+ */
+const failable<value> put(const value& val, const string& url, CURLSession& cs) {
+ debug(url, "http::put::url");
+
+ // Convert value to a content request
+ const failable<list<list<string> > > req = contentRequest(val, url);
+ if (!hasContent(req))
+ return mkfailure<value>(req);
+ debug(content(req), "http::put::input");
+
+ // PUT it to the URL
+ const failable<list<list<string> > > res = apply<list<string> >(content(req), rcons<string>, list<string>(), url, "PUT", cs);
+ if (!hasContent(res))
+ return mkfailure<value>(res);
+
+ debug(true, "http::put::result");
+ return value(true);
+}
+
+/**
+ * HTTP PATCH.
+ */
+const failable<value> patch(const value& val, const string& url, CURLSession& cs) {
+ debug(url, "http::put::patch");
+
+ // Convert value to a content request
+ const failable<list<list<string> > > req = contentRequest(val, url);
+ if (!hasContent(req))
+ return mkfailure<value>(req);
+ debug(content(req), "http::patch::input");
+
+ // PATCH it to the URL
+ const failable<list<list<string> > > res = apply<list<string> >(content(req), rcons<string>, list<string>(), url, "PATCH", cs);
+ if (!hasContent(res))
+ return mkfailure<value>(res);
+
+ debug(true, "http::patch::result");
+ return value(true);
+}
+
+/**
+ * HTTP DELETE.
+ */
+const failable<value, string> del(const string& url, CURLSession& cs) {
+ debug(url, "http::delete::url");
+
+ const list<list<string> > req = mklist(list<string>(), list<string>());
+ const failable<list<list<string> > > res = apply<list<string> >(req, rcons<string>, list<string>(), url, "DELETE", cs);
+ if (!hasContent(res))
+ return mkfailure<value>(res);
+
+ debug(true, "http::delete::result");
+ return value(true);
+}
+
+/**
+ * Returns the current host name.
+ */
+const string hostName() {
+ char h[256];
+ if (gethostname(h, 256) == -1)
+ return "localhost";
+ return h;
+}
+
+/**
+ * Create an APR pollfd for a socket.
+ */
+apr_pollfd_t* pollfd(apr_socket_t* s, const int e, const gc_pool& p) {
+ apr_pollfd_t* pfd = gc_new<apr_pollfd_t>(p);
+ pfd->p = pool(p);
+ pfd->desc_type = APR_POLL_SOCKET;
+ pfd->reqevents = (apr_int16_t)e;
+ pfd->rtnevents = (apr_int16_t)e;
+ pfd->desc.s = s;
+ pfd->client_data = NULL;
+ return pfd;
+}
+
+/**
+ * Connect to a URL.
+ */
+const failable<bool> connect(const string& url, CURLSession& cs) {
+ debug(url, "http::connect::url");
+
+ // Setup the CURL session
+ const failable<CURL*> fch = setup(url, cs);
+ if (!hasContent(fch)) {
+ cleanup(cs);
+ return mkfailure<bool>(fch);
+ }
+ CURL* ch = content(fch);
+
+ // Connect
+ curl_easy_setopt(ch, CURLOPT_CONNECT_ONLY, true);
+ const CURLcode rc = curl_easy_perform(ch);
+ if (rc) {
+ cleanup(cs);
+ return mkfailure<bool>(string(curl_easy_strerror(rc)));
+ }
+
+ // Convert the connected socket to an apr_socket_t
+ int sd;
+ const CURLcode grc = curl_easy_getinfo(ch, CURLINFO_LASTSOCKET, &sd);
+ if (grc) {
+ cleanup(cs);
+ return mkfailure<bool>(string(curl_easy_strerror(grc)));
+ }
+ cs.sock = sock(sd, cs.p);
+
+ // Create pollsets and pollfds which can be used to poll the socket
+ apr_status_t rpcrc = apr_pollset_create(&cs.rpollset, 1, pool(cs.p), 0);
+ if (rpcrc != APR_SUCCESS) {
+ cleanup(cs);
+ return mkfailure<bool>(apreason(rpcrc));
+ }
+ cs.rpollfd = pollfd(cs.sock, APR_POLLIN, cs.p);
+ apr_pollset_add(cs.rpollset, cs.rpollfd);
+ apr_status_t wpcrc = apr_pollset_create(&cs.wpollset, 1, pool(cs.p), 0);
+ if (wpcrc != APR_SUCCESS) {
+ cleanup(cs);
+ return mkfailure<bool>(apreason(wpcrc));
+ }
+ cs.wpollfd = pollfd(cs.sock, APR_POLLOUT, cs.p);
+ apr_pollset_add(cs.wpollset, cs.wpollfd);
+
+ return true;
+}
+
+/**
+ * Send an array of chars.
+ */
+const failable<bool> send(const char* c, const size_t l, CURLSession& cs) {
+
+ // Send the data
+ size_t wl = 0;
+ const CURLcode rc = curl_easy_send(cs.h, c, (size_t)l, &wl);
+ if (rc == CURLE_OK && wl == (size_t)l)
+ return true;
+ if (rc != CURLE_AGAIN) {
+ cleanup(cs);
+ return mkfailure<bool>(curlreason(rc));
+ }
+
+ // If the socket was not ready, wait for it to become ready
+ const apr_pollfd_t* pollfds;
+ apr_int32_t pollcount;
+ apr_status_t pollrc = apr_pollset_poll(cs.wpollset, -1, &pollcount, &pollfds);
+ if (pollrc != APR_SUCCESS)
+ return mkfailure<bool>(apreason(pollrc));
+
+ // Send what's left
+ return send(c + wl, l - wl, cs);
+}
+
+/**
+ * Receive an array of chars.
+ */
+const failable<size_t> recv(char* c, const size_t l, CURLSession& cs) {
+
+ // Receive data
+ size_t rl;
+ const CURLcode rc = curl_easy_recv(cs.h, c, (size_t)l, &rl);
+ if (rc == CURLE_OK)
+ return (size_t)rl;
+ if (rc == 1)
+ return 0;
+ if (rc != CURLE_AGAIN) {
+ cleanup(cs);
+ return mkfailure<size_t>(curlreason(rc));
+ }
+
+ // If the socket was not ready, wait for it to become ready
+ const apr_pollfd_t* pollfds;
+ apr_int32_t pollcount;
+ apr_status_t pollrc = apr_pollset_poll(cs.rpollset, -1, &pollcount, &pollfds);
+ if (pollrc != APR_SUCCESS) {
+ cleanup(cs);
+ return mkfailure<size_t>(apreason(pollrc));
+ }
+
+ // Receive again
+ return recv(c, l, cs);
+}
+
+/**
+ * Converts a list of key value pairs to a query string.
+ */
+ostringstream& queryString(const list<list<value> > args, ostringstream& os) {
+ if (isNil(args))
+ return os;
+ const list<value> arg = car(args);
+ debug(arg, "http::queryString::arg");
+ if (isNil(arg) || isNil(cdr(arg)))
+ return queryString(cdr(args), os);
+ os << car(arg) << "=" << c_str(cadr(arg));
+ if (!isNil(cdr(args)))
+ os << "&";
+ return queryString(cdr(args), os);
+}
+
+const string queryString(const list<list<value> > args) {
+ ostringstream os;
+ return str(queryString(args, os));
+}
+
+/**
+ * Filter path segment in a list of arguments.
+ */
+const bool filterPath(const value& arg) {
+ return isString(arg) || isSymbol(arg);
+}
+
+/**
+ * Filter query string arguments in a list of arguments.
+ */
+const bool filterQuery(const value& arg) {
+ return isList(arg);
+}
+
+/**
+ * Escape a query string argument.
+ */
+const value escapeQuery(const value& arg) {
+ return arg;
+ //return mklist<value>(car<value>(arg), escapeArg(cadr<value>(arg)));
+}
+
+/**
+ * HTTP client proxy function.
+ */
+struct proxy {
+ proxy(const string& uri, const string& ca, const string& cert, const string& key, const string& cookie, const int timeout, const gc_pool& p) : p(p), uri(uri), ca(ca), cert(cert), key(key), cookie(cookie), cs(*(new (gc_new<CURLSession>(p)) CURLSession(ca, cert, key, cookie, timeout))) {
+ }
+
+ const value operator()(const list<value>& args) const {
+ debug(args, "http::proxy::args");
+ const value fun = car(args);
+ if (fun == "get") {
+ const list<value> lp = filter<value>(filterPath, cadr(args));
+ debug(lp, "http::proxy::path");
+ const list<value> lq = map<value, value>(escapeQuery, filter<value>(filterQuery, cadr(args)));
+ debug(lp, "http::proxy::query");
+ const value p = path(lp);
+ const value q = queryString(lq);
+ const failable<value> val = get(uri + p + (q != ""? string("?") + q : string("")), cs);
+ return content(val);
+ }
+ if (fun == "post") {
+ const failable<value> val = post(caddr(args), uri + path(cadr(args)), cs);
+ return content(val);
+ }
+ if (fun == "put") {
+ const failable<value> val = put(caddr(args), uri + path(cadr(args)), cs);
+ return content(val);
+ }
+ if (fun == "patch") {
+ const failable<value> val = patch(caddr(args), uri + path(cadr(args)), cs);
+ return content(val);
+ }
+ if (fun == "delete") {
+ const failable<value> val = del(uri + path(cadr(args)), cs);
+ return content(val);
+ }
+ const failable<value> val = evalExpr(args, uri, cs);
+ return content(val);
+ }
+
+ const gc_pool p;
+ const string uri;
+ const string ca;
+ const string cert;
+ const string key;
+ const string cookie;
+ CURLSession& cs;
+};
+
+}
+}
+
+#endif /* tuscany_http_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-addr b/sca-cpp/branches/lightweight-sca/modules/http/httpd-addr
new file mode 100755
index 0000000000..735c152f43
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-addr
@@ -0,0 +1,53 @@
+#!/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.
+
+# Parse a string in the form ip-addr:local-port/public-port
+addr=`echo $2 | awk -F "/" '{ print $1 }'`
+ip=`echo $addr | awk -F ":" '{ print $1 }'`
+port=`echo $addr | awk -F ":" '{ print $2 }'`
+if [ "$port" = "" ]; then
+ port=$ip
+ ip=""
+ listen=$port
+ vhost="*:$port"
+else
+ listen="$ip:$port"
+ vhost="$ip:$port"
+fi
+pport=`echo $2 | awk -F "/" '{ print $2 }'`
+if [ "$pport" = "" ]; then
+ pport=$port
+fi
+
+# Return the requested part
+if [ "$1" = "ip" ]; then
+ echo $ip
+fi
+if [ "$1" = "port" ]; then
+ echo $port
+fi
+if [ "$1" = "pport" ]; then
+ echo $pport
+fi
+if [ "$1" = "listen" ]; then
+ echo $listen
+fi
+if [ "$1" = "vhost" ]; then
+ echo $vhost
+fi
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-conf b/sca-cpp/branches/lightweight-sca/modules/http/httpd-conf
new file mode 100755
index 0000000000..730775fa89
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-conf
@@ -0,0 +1,375 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Generate a minimal HTTPD configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+host=$2
+port=`$here/httpd-addr port $3`
+pport=`$here/httpd-addr pport $3`
+listen=`$here/httpd-addr listen $3`
+vhost=`$here/httpd-addr vhost $3`
+if [ "$pport" = "80" ]; then
+ pportsuffix=""
+else
+ pportsuffix=":$pport"
+fi
+
+mkdir -p $4
+htdocs=`echo "import os; print os.path.realpath('$4')" | python`
+
+user=`id -un`
+group=`id -gn`
+
+uname=`uname -s`
+if [ $uname = "Darwin" ]; then
+ libsuffix=".dylib"
+ sendfile=Off
+else
+ libsuffix=".so"
+ sendfile=On
+fi
+
+modules_prefix=`cat $here/httpd-modules.prefix`
+
+mkdir -p $root
+mkdir -p $root/logs
+mkdir -p $root/conf
+cat >$root/conf/httpd.conf <<EOF
+# Generated by: httpd-conf $*
+# Apache HTTPD server configuration
+
+# Main server name
+ServerName http://$host$pportsuffix
+PidFile $root/logs/httpd.pid
+
+# Load configured MPM
+Include conf/mpm.conf
+
+# Load required modules
+Include conf/modules.conf
+
+# Basic security precautions
+User $user
+Group $group
+ServerSignature Off
+ServerTokens Prod
+Timeout 45
+RequestReadTimeout header=20-40,MinRate=500 body=20,MinRate=500
+LimitRequestBody 1048576
+HostNameLookups Off
+#MaxKeepAliveRequests 25
+#MaxConnectionsPerChild 100
+
+# Log HTTP requests
+# [timestamp] [access] remote-host remote-ident remote-user "request-line"
+# status response-size "referrer" "user-agent" "user-track" local-IP
+# virtual-host response-time bytes-received bytes-sent
+LogFormat "[%{%a %b %d %H:%M:%S %Y}t] [access] %h %l %u \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\" \"%{cookie}n\" %A %V %D %I %O %{mod_security-message}i" combined
+Include conf/log.conf
+
+# Configure Mime types and default charsets
+TypesConfig $here/conf/mime.types
+AddDefaultCharset utf-8
+AddCharset utf-8 .html .js .css
+
+# Configure cache control
+<Directory />
+ExpiresActive On
+ExpiresDefault A604800
+Header onsuccess merge Cache-Control public env=!private-cache
+</Directory>
+
+# Enable Linux Kernel sendfile
+EnableSendFile $sendfile
+
+# Configure auth modules
+Include conf/auth.conf
+
+# Set default document root
+DocumentRoot $htdocs
+DirectoryIndex index-min.html index.html
+
+# Protect server files
+<Directory />
+Options None
+AllowOverride None
+Require all denied
+</Directory>
+
+# Configure output filters to enable compression and rate limiting
+<Location />
+#SetOutputFilter RATE_LIMIT;DEFLATE
+SetOutputFilter DEFLATE
+
+BrowserMatch ^Mozilla/4 gzip-only-text/html
+BrowserMatch ^Mozilla/4\.0[678] no-gzip
+BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html
+BrowserMatch ^check_http/ check_http
+SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png)$ no-gzip dont-vary
+Header append Vary User-Agent env=!dont-vary
+
+#SetEnv rate-limit 400
+</Location>
+
+# Listen on HTTP port
+Listen $listen
+
+# Setup HTTP virtual host
+<VirtualHost $vhost>
+ServerName http://$host$pportsuffix
+
+<Location />
+RewriteEngine on
+Include conf/hostcond.conf
+RewriteCond %{HTTP:X-Forwarded-Server} ^$ [NC]
+RewriteCond %{REQUEST_URI} !^/server-status [NC]
+RewriteCond %{REQUEST_URI} !^/balancer-manager [NC]
+RewriteCond %{REQUEST_URI} !^/proxy/ [NC]
+RewriteRule .* http://$host$pportsuffix%{REQUEST_URI} [R]
+</Location>
+
+Include conf/svhost.conf
+
+# Configure authentication
+Include conf/noauth.conf
+Include conf/locauth.conf
+Include conf/pubauth.conf
+Include conf/adminauth.conf
+
+</VirtualHost>
+
+EOF
+
+# Configure logging
+cat >$root/conf/log.conf <<EOF
+# Generated by: httpd-conf $*
+ErrorLog $root/logs/error_log
+CustomLog $root/logs/access_log combined
+
+EOF
+
+# Run with the prefork MPM
+cat >$root/conf/mpm.conf <<EOF
+# Generated by: httpd-conf $*
+LoadModule mpm_prefork_module ${modules_prefix}/modules/mod_mpm_prefork.so
+
+EOF
+
+if [ $uname = "Darwin" ]; then
+ cat >>$root/conf/mpm.conf <<EOF
+# Generated by: httpd-conf $*
+# Set thread stack size
+ThreadStackSize 2097152
+
+EOF
+fi
+
+# Generate modules list
+cat >$root/conf/modules.conf <<EOF
+# Generated by: httpd-conf $*
+# Load a minimal set of modules, the load order is important
+# (e.g. load mod_headers before mod_rewrite, so its hooks execute
+# after mod_rewrite's hooks)
+LoadModule headers_module ${modules_prefix}/modules/mod_headers.so
+LoadModule alias_module ${modules_prefix}/modules/mod_alias.so
+LoadModule authn_file_module ${modules_prefix}/modules/mod_authn_file.so
+LoadModule authn_socache_module ${modules_prefix}/modules/mod_authn_socache.so
+LoadModule authn_core_module ${modules_prefix}/modules/mod_authn_core.so
+LoadModule authz_host_module ${modules_prefix}/modules/mod_authz_host.so
+LoadModule authz_groupfile_module ${modules_prefix}/modules/mod_authz_groupfile.so
+LoadModule authz_user_module ${modules_prefix}/modules/mod_authz_user.so
+LoadModule authz_core_module ${modules_prefix}/modules/mod_authz_core.so
+LoadModule auth_basic_module ${modules_prefix}/modules/mod_auth_basic.so
+LoadModule auth_digest_module ${modules_prefix}/modules/mod_auth_digest.so
+LoadModule auth_form_module ${modules_prefix}/modules/mod_auth_form.so
+LoadModule request_module ${modules_prefix}/modules/mod_request.so
+LoadModule deflate_module ${modules_prefix}/modules/mod_deflate.so
+LoadModule filter_module ${modules_prefix}/modules/mod_filter.so
+LoadModule proxy_module ${modules_prefix}/modules/mod_proxy.so
+LoadModule proxy_connect_module ${modules_prefix}/modules/mod_proxy_connect.so
+LoadModule proxy_http_module ${modules_prefix}/modules/mod_proxy_http.so
+LoadModule proxy_balancer_module ${modules_prefix}/modules/mod_proxy_balancer.so
+LoadModule lbmethod_byrequests_module ${modules_prefix}/modules/mod_lbmethod_byrequests.so
+LoadModule socache_shmcb_module ${modules_prefix}/modules/mod_socache_shmcb.so
+LoadModule cache_module ${modules_prefix}/modules/mod_cache.so
+LoadModule cache_disk_module ${modules_prefix}/modules/mod_cache_disk.so
+LoadModule rewrite_module ${modules_prefix}/modules/mod_rewrite.so
+LoadModule mime_module ${modules_prefix}/modules/mod_mime.so
+LoadModule status_module ${modules_prefix}/modules/mod_status.so
+LoadModule negotiation_module ${modules_prefix}/modules/mod_negotiation.so
+LoadModule dir_module ${modules_prefix}/modules/mod_dir.so
+LoadModule setenvif_module ${modules_prefix}/modules/mod_setenvif.so
+LoadModule env_module ${modules_prefix}/modules/mod_env.so
+LoadModule expires_module ${modules_prefix}/modules/mod_expires.so
+<IfModule !log_config_module>
+LoadModule log_config_module ${modules_prefix}/modules/mod_log_config.so
+</IfModule>
+LoadModule logio_module ${modules_prefix}/modules/mod_logio.so
+LoadModule usertrack_module ${modules_prefix}/modules/mod_usertrack.so
+LoadModule vhost_alias_module ${modules_prefix}/modules/mod_vhost_alias.so
+LoadModule cgi_module ${modules_prefix}/modules/mod_cgi.so
+LoadModule actions_module ${modules_prefix}/modules/mod_actions.so
+LoadModule unixd_module ${modules_prefix}/modules/mod_unixd.so
+LoadModule session_module ${modules_prefix}/modules/mod_session.so
+LoadModule session_crypto_module ${modules_prefix}/modules/mod_session_crypto.so
+LoadModule slotmem_shm_module ${modules_prefix}/modules/mod_slotmem_shm.so
+LoadModule ratelimit_module ${modules_prefix}/modules/mod_ratelimit.so
+LoadModule reqtimeout_module ${modules_prefix}/modules/mod_reqtimeout.so
+LoadModule ssl_module ${modules_prefix}/modules/mod_ssl.so
+
+EOF
+
+# Generate auth configuration
+cat >$root/conf/auth.conf <<EOF
+# Generated by: httpd-conf $*
+
+EOF
+
+cat >$root/conf/locauth.conf <<EOF
+# Generated by: httpd-conf $*
+# Authentication and authorization configuration
+
+# Allow authorized access to document root
+<Directory "$htdocs">
+Options FollowSymLinks
+Require all granted
+</Directory>
+
+# Allow authorized access to root location
+<Location />
+Options FollowSymLinks
+AuthUserFile "$root/conf/httpd.passwd"
+AuthGroupFile "$root/conf/httpd.groups"
+Require all granted
+</Location>
+
+EOF
+
+cat >$root/conf/pubauth.conf <<EOF
+# Generated by: httpd-conf $*
+# Allow everyone to access public locations
+<Location /login>
+AuthType None
+Require all granted
+# Mark login page with a header
+Header set X-Login open-auth
+</Location>
+<Location /logout>
+AuthType None
+Require all granted
+</Location>
+<Location /public>
+AuthType None
+Require all granted
+</Location>
+<Location /proxy/public>
+AuthType None
+Require all granted
+</Location>
+<Location /favicon.ico>
+AuthType None
+Require all granted
+</Location>
+<Location /robots.txt>
+AuthType None
+Require all granted
+</Location>
+
+EOF
+
+cat >$root/conf/adminauth.conf <<EOF
+
+# Allow the server admin to view the server status
+<Location /server-status>
+Require user admin
+</Location>
+
+EOF
+
+# Create password and group files
+cat >$root/conf/httpd.passwd <<EOF
+# Generated by: httpd-conf $*
+EOF
+
+cat >$root/conf/httpd.groups <<EOF
+# Generated by: httpd-conf $*
+EOF
+
+# Allow public access to server resources
+cat >$root/conf/noauth.conf <<EOF
+# Generated by: httpd-conf $*
+# Allow public access to server resources
+
+# Allow access to document root
+<Directory "$htdocs">
+AuthType None
+Require all granted
+</Directory>
+
+# Allow everyone to access root location
+<Location />
+AuthType None
+Require all granted
+</Location>
+
+EOF
+
+# Generate vhost configuration
+cat >$root/conf/vhost.conf <<EOF
+# Generated by: httpd-conf $*
+# Virtual host configuration
+UseCanonicalName Off
+
+# Enable HTTP reverse proxy
+ProxyRequests Off
+ProxyPreserveHost On
+ProxyStatus On
+
+# Enable server status
+<Location /server-status>
+SetHandler server-status
+HostnameLookups on
+</Location>
+
+EOF
+
+cat >$root/conf/svhost.conf <<EOF
+# Generated by: httpd-conf $*
+# Static virtual host configuration
+Include conf/vhost.conf
+
+EOF
+
+cat >$root/conf/dvhost.conf <<EOF
+# Generated by: httpd-conf $*
+# Mass dynamic virtual host configuration
+Include conf/vhost.conf
+
+EOF
+
+# Generate host name check condition
+cat >$root/conf/hostcond.conf <<EOF
+# Generated by: httpd-conf $*
+RewriteCond %{HTTP_HOST} !^$host [NC]
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-debug b/sca-cpp/branches/lightweight-sca/modules/http/httpd-debug
new file mode 100755
index 0000000000..df0d21fbe5
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-debug
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Start httpd server
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+httpd=`cat $here/httpd.prefix`
+$httpd/bin/httpd -X -E $root/logs/error_log -d $root -f $root/conf/httpd.conf
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-event-conf b/sca-cpp/branches/lightweight-sca/modules/http/httpd-event-conf
new file mode 100755
index 0000000000..093bbe78d9
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-event-conf
@@ -0,0 +1,45 @@
+#!/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.
+
+# Configure HTTPD to run with the event MPM
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+modules_prefix=`cat $here/httpd-modules.prefix`
+
+mkdir -p $root
+mkdir -p $root/conf
+cat >$root/conf/mpm.conf <<EOF
+# Generated by: httpd-event-conf $*
+# Use HTTPD event MPM
+LoadModule mpm_event_module ${modules_prefix}/modules/mod_mpm_event.so
+
+EOF
+
+uname=`uname -s`
+if [ $uname = "Darwin" ]; then
+ cat >>$root/conf/mpm.conf <<EOF
+# Generated by: httpd-conf $*
+# Set thread stack size
+ThreadStackSize 2097152
+
+EOF
+fi
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-loglevel-conf b/sca-cpp/branches/lightweight-sca/modules/http/httpd-loglevel-conf
new file mode 100755
index 0000000000..c9d2ad81d5
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-loglevel-conf
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Configure HTTPD log level
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+level=$2
+
+# Configure log level
+cat >>$root/conf/log.conf <<EOF
+# Generated by: httpd-loglevel-conf $*
+LogLevel $level
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-memgrind b/sca-cpp/branches/lightweight-sca/modules/http/httpd-memgrind
new file mode 100755
index 0000000000..55e113bbd3
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-memgrind
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Start httpd server
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+httpd=`cat $here/httpd.prefix`
+$here/../../etc/memgrind $httpd/bin/httpd -X -E $root/logs/error_log -d $root -f $root/conf/httpd.conf
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-restart b/sca-cpp/branches/lightweight-sca/modules/http/httpd-restart
new file mode 100755
index 0000000000..81b098d85d
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-restart
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Restart httpd server
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+apachectl=`cat $here/httpd-apachectl.prefix`
+$apachectl -k graceful -d $root -f $root/conf/httpd.conf
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-ssl-conf b/sca-cpp/branches/lightweight-sca/modules/http/httpd-ssl-conf
new file mode 100755
index 0000000000..f99a10071c
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-ssl-conf
@@ -0,0 +1,259 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Generate a minimal HTTPD SSL configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"`
+host=`echo $conf | awk '{ print $6 }'`
+gport=`echo $conf | awk '{ print $7 }'`
+port=`$here/httpd-addr port $gport`
+pport=`$here/httpd-addr pport $gport`
+
+sslpport=`$here/httpd-addr pport $2`
+sslport=`$here/httpd-addr listen $2`
+sslvhost=`$here/httpd-addr vhost $2`
+if [ "$sslpport" = "443" ]; then
+ sslpportsuffix=""
+else
+ sslpportsuffix=":$sslpport"
+fi
+
+dothost=`echo $host | grep "\."`
+
+htdocs=`echo $conf | awk '{ print $8 }'`
+mkdir -p $htdocs
+htdocs=`echo "import os; print os.path.realpath('$htdocs')" | python`
+
+uname=`uname -s`
+if [ $uname = "Darwin" ]; then
+ libsuffix=".dylib"
+else
+ libsuffix=".so"
+fi
+
+modules_prefix=`cat $here/httpd-modules.prefix`
+
+# Extract organization name from our CA certificate
+org=`openssl x509 -noout -subject -nameopt multiline -in $root/cert/ca.crt | grep organizationName | awk -F "= " '{ print $2 }'`
+
+# Generate HTTPD configuration
+cat >>$root/conf/httpd.conf <<EOF
+# Generated by: httpd-ssl-conf $*
+
+# Configure SSL support
+AddType application/x-x509-ca-cert .crt
+AddType application/x-pkcs7-crl .crl
+SSLPassPhraseDialog builtin
+SSLSessionCache "shmcb:$root/logs/ssl_scache(512000)"
+SSLSessionCacheTimeout 300
+Mutex "file:$root/logs" ssl-cache
+SSLRandomSeed startup builtin
+SSLRandomSeed connect builtin
+
+# Listen on HTTPS port
+Listen $sslport
+
+# HTTPS virtual host
+<VirtualHost $sslvhost>
+ServerName https://$host$sslpportsuffix
+
+<Location />
+RewriteEngine on
+Include conf/hostcond.conf
+RewriteCond %{HTTP:X-Forwarded-Server} ^$ [NC]
+RewriteCond %{REQUEST_URI} !^/server-status [NC]
+RewriteCond %{REQUEST_URI} !^/balancer-manager [NC]
+RewriteCond %{REQUEST_URI} !^/proxy/ [NC]
+RewriteRule .* https://$host$sslpportsuffix%{REQUEST_URI} [R]
+</Location>
+
+Include conf/svhost-ssl.conf
+
+# Configure authentication
+Include conf/noauth-ssl.conf
+Include conf/locauth-ssl.conf
+Include conf/pubauth-ssl.conf
+Include conf/adminauth-ssl.conf
+
+# Configure tracking
+Include conf/tracking-ssl.conf
+
+</VirtualHost>
+
+EOF
+
+# Generate auth configuration
+cat >$root/conf/locauth-ssl.conf <<EOF
+# Generated by: httpd-ssl-conf $*
+# Authentication and authorization configuration
+Include conf/locauth.conf
+
+EOF
+
+cat >$root/conf/pubauth-ssl.conf <<EOF
+# Generated by: httpd-ssl-conf $*
+# Allow everyone to access public locations
+Include conf/pubauth.conf
+
+EOF
+
+cat >$root/conf/adminauth-ssl.conf <<EOF
+# Generated by: httpd-ssl-conf $*
+# Allow admin access
+Include conf/adminauth.conf
+
+EOF
+
+# Allow public access to server resources
+cat >$root/conf/noauth-ssl.conf <<EOF
+# Generated by: httpd-conf $*
+# Allow public access to server resources
+Include conf/noauth.conf
+
+EOF
+
+# Generate HTTP vhost configuration
+cat >>$root/conf/svhost.conf <<EOF
+# Generated by: httpd-ssl-conf $*
+# Redirect HTTP traffic to HTTPS
+<Location />
+RewriteEngine on
+RewriteCond %{SERVER_PORT} ^$port$ [OR]
+RewriteCond %{SERVER_PORT} ^$pport$
+RewriteRule .* https://$host$sslpportsuffix%{REQUEST_URI} [R]
+</Location>
+
+EOF
+
+# Redirect HTTP traffic to HTTPS in HTTP vhost
+cat >>$root/conf/dvhost.conf <<EOF
+# Generated by: httpd-ssl-conf $*
+# Redirect HTTP traffic to HTTPS
+<Location />
+RewriteEngine on
+RewriteCond %{SERVER_PORT} ^$port$ [OR]
+RewriteCond %{SERVER_PORT} ^$pport$
+RewriteRule .* https://%{SERVER_NAME}$sslpportsuffix%{REQUEST_URI} [R]
+</Location>
+
+EOF
+
+# Generate HTTPS vhost configuration
+cat >$root/conf/vhost-ssl.conf <<EOF
+# Generated by: httpd-ssl-conf $*
+# Virtual host configuration
+UseCanonicalName Off
+
+# Enable SSL
+SSLEngine on
+SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
+BrowserMatch ".*MSIE.*" nokeepalive ssl-unclean-shutdown downgrade-1.0 force-response-1.0
+SSLOptions +StrictRequire +OptRenegotiate +FakeBasicAuth
+
+# Require clients to use SSL and authenticate
+<Location />
+SSLRequireSSL
+SSLRequire %{SSL_CIPHER_USEKEYSIZE} >= 128
+</Location>
+
+# Log SSL requests
+# [timestamp] [sslaccess] remote-host remote-ident remote-user SSL-protocol
+# SSL-cipher "request-line" status response-size "referrer" "user-agent"
+# "SSL-client-I-DN" "SSL-client-S-DN" "user-track" local-IP virtual-host
+# response-time bytes-received bytes-sent
+LogFormat "[%{%a %b %d %H:%M:%S %Y}t] [sslaccess] %h %l %u %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\" \"%{SSL_CLIENT_I_DN}x\" \"%{SSL_CLIENT_S_DN}x\" \"%{cookie}n\" %A %V %D %I %O %{mod_security-message}i" sslcombined
+Include conf/log-ssl.conf
+
+# Enable HTTPS reverse proxy
+ProxyRequests Off
+ProxyPreserveHost On
+ProxyStatus On
+SSLProxyEngine on
+SSLProxyCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
+
+# Verify server certificates
+SSLProxyVerify require
+SSLProxyVerifyDepth 1
+SSLProxyCheckPeerCN Off
+
+# Enable server status
+<Location /server-status>
+SetHandler server-status
+HostnameLookups on
+</Location>
+
+EOF
+
+# Generate tracking configuration
+cat >$root/conf/tracking-ssl.conf <<EOF
+# Generated by: httpd-ssl-conf $*
+# Configure tracking
+CookieTracking on
+CookieName TuscanyVisitorId
+CookieStyle Cookie
+CookieExpires 31556926
+
+EOF
+
+if [ "$dothost" != "" ]; then
+ cat >>$root/conf/tracking-ssl.conf <<EOF
+# Generated by: httpd-ssl-conf $*
+CookieDomain .$dothost
+
+EOF
+
+fi
+
+# Configure logging
+cat >$root/conf/log-ssl.conf <<EOF
+# Generated by: httpd-ssl-conf $*
+CustomLog $root/logs/ssl_access_log sslcombined
+
+EOF
+
+# Configure virtual hosts
+cat >$root/conf/svhost-ssl.conf <<EOF
+# Generated by: httpd-ssl-conf $*
+# Static virtual host configuration
+Include conf/vhost-ssl.conf
+
+# Declare SSL certificates used in this virtual host
+SSLCACertificateFile "$root/cert/ca.crt"
+SSLCertificateChainFile "$root/cert/ca.crt"
+SSLCertificateFile "$root/cert/server.crt"
+SSLCertificateKeyFile "$root/cert/server.key"
+
+EOF
+
+cat >$root/conf/dvhost-ssl.conf <<EOF
+# Mass dynamic virtual host configuration
+# Generated by: httpd-ssl-conf $*
+Include conf/vhost-ssl.conf
+
+# Declare wildcard SSL certificates used in this virtual host
+SSLCACertificateFile "$root/cert/ca.crt"
+SSLCertificateChainFile "$root/cert/ca.crt"
+SSLCertificateFile "$root/cert/vhost.crt"
+SSLCertificateKeyFile "$root/cert/vhost.key"
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-start b/sca-cpp/branches/lightweight-sca/modules/http/httpd-start
new file mode 100755
index 0000000000..e38c2f9a94
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-start
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Start httpd server
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+apachectl=`cat $here/httpd-apachectl.prefix`
+$apachectl -E $root/logs/error_log -k start -d $root -f $root/conf/httpd.conf
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-stop b/sca-cpp/branches/lightweight-sca/modules/http/httpd-stop
new file mode 100755
index 0000000000..9010e4dd17
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-stop
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Stop httpd server
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+apachectl=`cat $here/httpd-apachectl.prefix`
+$apachectl -k graceful-stop -d $root -f $root/conf/httpd.conf 2>/dev/null
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-test b/sca-cpp/branches/lightweight-sca/modules/http/httpd-test
new file mode 100755
index 0000000000..ab6ab5ad41
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-test
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+echo "Testing..."
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+curl_prefix=`cat $here/../http/curl.prefix`
+
+# Setup
+rm -rf tmp
+./httpd-conf tmp localhost 8090 htdocs
+./httpd-event-conf tmp
+./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=$?
+
+# Cleanup
+./httpd-stop tmp
+sleep 2
+if [ "$rc" = "0" ]; then
+ echo "OK"
+fi
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-tunnel-ssl-conf b/sca-cpp/branches/lightweight-sca/modules/http/httpd-tunnel-ssl-conf
new file mode 100755
index 0000000000..0028576364
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-tunnel-ssl-conf
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Generate a minimal HTTPD SSL Tunnel configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+uname=`uname -s`
+if [ $uname = "Darwin" ]; then
+ libsuffix=".dylib"
+else
+ libsuffix=".so"
+fi
+
+# Generate required modules list
+cat >>$root/conf/modules.conf <<EOF
+# Generated by: httpd-tunnel-ssl-conf $*
+LoadModule mod_tuscany_ssltunnel $here/libmod_tuscany_ssltunnel$libsuffix
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd-worker-conf b/sca-cpp/branches/lightweight-sca/modules/http/httpd-worker-conf
new file mode 100755
index 0000000000..0a061dbffa
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd-worker-conf
@@ -0,0 +1,45 @@
+#!/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.
+
+# Configure HTTPD to run with the worker MPM
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+modules_prefix=`cat $here/httpd-modules.prefix`
+
+mkdir -p $root
+mkdir -p $root/conf
+cat >$root/conf/mpm.conf <<EOF
+# Generated by: httpd-worker-conf $*
+# Use HTTPD worker MPM
+LoadModule mpm_worker_module ${modules_prefix}/modules/mod_mpm_worker.so
+
+EOF
+
+uname=`uname -s`
+if [ $uname = "Darwin" ]; then
+ cat >>$root/conf/mpm.conf <<EOF
+# Generated by: httpd-conf $*
+# Set thread stack size
+ThreadStackSize 2097152
+
+EOF
+fi
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/httpd.hpp b/sca-cpp/branches/lightweight-sca/modules/http/httpd.hpp
new file mode 100644
index 0000000000..6470d6c587
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/httpd.hpp
@@ -0,0 +1,753 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+#ifndef tuscany_httpd_hpp
+#define tuscany_httpd_hpp
+
+/**
+ * HTTPD module implementation functions.
+ */
+
+extern "C" {
+#include <apr_strings.h>
+#include <apr_fnmatch.h>
+#include <apr_lib.h>
+#define APR_WANT_STRFUNC
+#include <apr_want.h>
+#include <apr_base64.h>
+
+#include <httpd.h>
+// Hack to workaround compile error with CLang/LLVM
+#undef strtoul
+// Hack to workaround compile error with HTTPD 2.3.8
+#define new new_
+#include <http_config.h>
+#undef new
+#include <http_core.h>
+#include <http_connection.h>
+#include <http_request.h>
+// Ignore conversion warnings in HTTPD 2.3.15 header
+#ifdef WANT_MAINTAINER_WARNINGS
+#ifndef IS_DARWIN
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
+#endif
+#include <http_protocol.h>
+// Re-enable conversion warnings
+#ifdef WANT_MAINTAINER_WARNINGS
+#ifndef IS_DARWIN
+#pragma GCC diagnostic warning "-Wconversion"
+#endif
+#endif
+// Hack to workaround compile error with HTTPD 2.3.8
+#define aplog_module_index aplog_module_index = 0
+#include <http_log.h>
+#undef aplog_module_index
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX (aplog_module_index ? *aplog_module_index : APLOG_NO_MODULE)
+#include <http_main.h>
+#include <util_script.h>
+#include <util_md5.h>
+#include <http_config.h>
+#include <http_log.h>
+#include <ap_mpm.h>
+#include <mod_core.h>
+#include <ap_provider.h>
+#include <mod_auth.h>
+#include <mod_session.h>
+}
+
+#include "string.hpp"
+#include "stream.hpp"
+#include "sstream.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "http.hpp"
+
+
+namespace tuscany {
+namespace httpd {
+
+/**
+ * Returns a server-scoped module configuration.
+ */
+template<typename C> void* makeServerConf(apr_pool_t* p, server_rec* s) {
+ return new (gc_new<C>(p)) C(p, s);
+}
+
+template<typename C> const C& serverConf(const request_rec* r, const module* mod) {
+ return *(C*)ap_get_module_config(r->server->module_config, mod);
+}
+
+template<typename C> C& serverConf(const server_rec* s, const module* mod) {
+ return *(C*)ap_get_module_config(s->module_config, mod);
+}
+
+template<typename C> C& serverConf(const cmd_parms* cmd, const module* mod) {
+ return *(C*)ap_get_module_config(cmd->server->module_config, mod);
+}
+
+/**
+ * Returns a directory-scoped module configuration.
+ */
+template<typename C> void* makeDirConf(apr_pool_t *p, char* d) {
+ return new (gc_new<C>(p)) C(p, d);
+}
+
+template<typename C> const C& dirConf(const request_rec* r, const module* mod) {
+ return *(C*)ap_get_module_config(r->per_dir_config, mod);
+}
+
+template<typename C> C& dirConf(const void* c) {
+ return *(C*)c;
+}
+
+/**
+ * Returns a request-scoped module configuration.
+ */
+template<typename C> C& makeRequestConf(const request_rec* r, const module* mod) {
+ C* c = new (gc_new<C>(r->pool)) C(r->pool, r);
+ ap_set_module_config(r->request_config, mod, c);
+ return *c;
+}
+
+template<typename C> C& requestConf(const request_rec* r, const module* mod) {
+ C* c = (C*)ap_get_module_config(r->request_config, mod);
+ if (c == NULL)
+ return makeRequestConf<C>(r, mod);
+ return *c;
+}
+
+/**
+ * Return the host name for a server.
+ */
+const string hostName(const server_rec* s, const string& def = "localhost") {
+ return s->server_hostname != NULL? s->server_hostname : def;
+}
+
+/**
+ * Return the host name from an HTTP request.
+ */
+const string hostName(request_rec* r, const string& def = "localhost") {
+ const char* fh = apr_table_get(r->headers_in, "X-Forwarded-Server");
+ if (fh != NULL)
+ return fh;
+ const char* h = ap_get_server_name(r);
+ return h != NULL? h : (r->server->server_hostname != NULL? r->server->server_hostname : def);
+}
+
+/**
+ * Convert a host name to a realm.
+ */
+const string realm(const string& host) {
+ const string pre = substr(host, 0, 4);
+ return pre == "www." || pre == "ww1." || pre == "ww2." || pre == "ww3."? substr(host, 4) : host;
+}
+
+/**
+ * Return the protocol scheme for a server.
+ */
+const string scheme(const server_rec* s, const string& def = "http") {
+ return s->server_scheme != NULL? s->server_scheme : def;
+}
+
+/**
+ * Return the protocol scheme from an HTTP request.
+ */
+const string scheme(request_rec* r, const string& def = "http") {
+ const char* fs = apr_table_get(r->headers_in, "X-Forwarded-HTTPS");
+ if (fs != NULL)
+ return !strcmp(fs, "on")? "https" : "http";
+ return r->server->server_scheme != NULL? r->server->server_scheme : def;
+}
+
+/**
+ * Return the port number for a server.
+ */
+const int port(const server_rec* s, const int def = 80) {
+ return s->port != 0? s->port : def;
+}
+
+/**
+ * Return the port number from an HTTP request.
+ */
+const int port(request_rec* r, const int def = 80) {
+ const char* fp = apr_table_get(r->headers_in, "X-Forwarded-Port");
+ if (fp != NULL)
+ return atoi(fp);
+ const int p = ap_get_server_port(r);
+ return p != 0? p : def;
+}
+
+/**
+ * Return the name of a server.
+ */
+const string serverName(const server_rec* s, const string& def = "localhost") {
+ ostringstream n;
+ const string sc = scheme(s);
+ const string h = hostName(s, def);
+ const int p = port(s, sc == "https"? 443 : 80);
+ n << sc << "://" << h;
+ if (!((sc == "http" && p == 80) || (sc == "https" && p == 443)))
+ n << ":" << p;
+ n << (s->path != NULL? string(s->path, s->pathlen) : "");
+ return str(n);
+}
+
+/**
+ * Determine the name of a server from an HTTP request.
+ */
+const string serverName(request_rec* r, const string& def = "localhost") {
+ ostringstream n;
+ const string s = scheme(r);
+ const string h = hostName(r, def);
+ const int p = port(r, s == "https"? 443 : 80);
+ n << s << "://" << h;
+ if (!((s == "http" && p == 80) || (s == "https" && p == 443)))
+ n << ":" << p;
+ n << (r->server->path != NULL? string(r->server->path, r->server->pathlen) : "");
+ return str(n);
+}
+
+/**
+ * Return true if a request is targeting a virtual host.
+ */
+const bool isVhostRequest(const server_rec* s, const string& d, request_rec* r) {
+ const string rh = hostName(r);
+ return rh != hostName(s) && http::topDomain(rh) == d;
+}
+
+/**
+ * Return the content type of a request.
+ */
+const string contentType(const request_rec* r) {
+ const char* ct = apr_table_get(r->headers_in, "Content-Type");
+ if (ct == NULL)
+ return "";
+ return ct;
+}
+
+/**
+ * Return the cookie header of a request.
+ */
+const string cookie(const request_rec* r) {
+ const char* c = apr_table_get(r->headers_in, "Cookie");
+ if (c == NULL)
+ return "";
+ return c;
+}
+
+/**
+ * Return the remaining part of a uri after the given path (aka the path info.)
+ */
+const list<value> pathInfo(const list<value>& uri, const list<value>& path) {
+ if (isNil(path))
+ return uri;
+ return pathInfo(cdr(uri), cdr(path));
+}
+
+/**
+ * Convert a URI to an absolute URL.
+ */
+const string url(const string& uri, request_rec* r) {
+ if (contains(uri, "://"))
+ return uri;
+ ostringstream n;
+ const string s = scheme(r);
+ const string h = hostName(r, "localhost");
+ const int p = port(r, s == "https"? 443 : 80);
+ n << s << "://" << h;
+ if (!((s == "http" && p == 80) || (s == "https" && p == 443)))
+ n << ":" << p;
+ n << uri;
+ return str(n);
+}
+
+/**
+ * Convert a URI and a path to an absolute URL.
+ */
+const string url(const string& uri, const list<value>& p, request_rec* r) {
+ return url(uri + path(p), r);
+}
+
+/**
+ * Escape a URI.
+ */
+const char escape_c2x[] = "0123456789ABCDEF";
+const string escape(const string& uri) {
+ debug(uri, "httpd::escape::uri");
+ char* copy = (char*)apr_palloc(gc_current_pool(), 3 * length(uri) + 3);
+ const unsigned char* s = (const unsigned char *)c_str(uri);
+ unsigned char* d = (unsigned char*)copy;
+ unsigned c;
+ while ((c = *s)) {
+ if (apr_isalnum(c) || c == '_')
+ *d++ = (unsigned char)c;
+ else if (c == ' ')
+ *d++ = '+';
+ else {
+ *d++ = '%';
+ *d++ = escape_c2x[c >> 4];
+ *d++ = escape_c2x[c & 0xf];
+ }
+ ++s;
+ }
+ *d = '\0';
+ debug(copy, "httpd::escape::result");
+ return copy;
+}
+
+/**
+ * Unescape a URI.
+ */
+const string unescape(const string& uri) {
+ debug(uri, "httpd::unescape::uri");
+ char* b = const_cast<char*>(c_str(string(c_str(uri))));
+ ap_unescape_url(b);
+ debug(b, "httpd::unescape::result");
+ return b;
+}
+
+/**
+ * Unescape a list of key of value pairs representing query args.
+ */
+const list<value> unescapeArg(const list<value> a) {
+ return mklist<value>(car(a), unescape(cadr(a)));
+}
+
+const list<list<value> > unescapeArgs(const list<list<value> > args) {
+ debug(args, "httpd::unescape::args");
+ const list<list<value> > uargs = map<list<value>, list<value>>(unescapeArg, args);
+ debug(uargs, "httpd::unescape::result");
+ return uargs;
+}
+
+/**
+ * Returns a list of key value pairs from the args in a query string.
+ */
+const list<value> queryArg(const string& s) {
+ debug(s, "httpd::queryArg::string");
+ const list<string> t = tokenize("=", s);
+ if (isNil(cdr(t)))
+ return mklist<value>(c_str(car(t)), "");
+ return mklist<value>(c_str(car(t)), cadr(t));
+}
+
+const string fixupQueryArgs(const string& a) {
+ const list<string> t = tokenize("?", a);
+ if (isNil(t) || isNil(cdr(t)))
+ return a;
+ return join("&", t);
+}
+
+const list<list<value> > queryArgs(const string& a) {
+ return map<string, list<value>>(queryArg, tokenize("&", fixupQueryArgs(a)));
+}
+
+/**
+ * Returns a list of key value pairs from the args in an HTTP request.
+ */
+const list<list<value> > queryArgs(const request_rec* r) {
+ if (r->args == NULL)
+ return list<list<value> >();
+ return queryArgs(r->args);
+}
+
+/**
+ * Converts the args received in a POST to a list of key value pairs.
+ */
+const list<list<value> > postArgs(const list<value>& a) {
+ if (isNil(a))
+ return list<list<value> >();
+ const list<value> l = car(a);
+ return cons(l, postArgs(cdr(a)));
+}
+
+/**
+ * Setup the HTTP read policy.
+ */
+const int setupReadPolicy(request_rec* r) {
+ const int rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK);
+ if(rc != OK)
+ return rc;
+ ap_should_client_block(r);
+ if(r->read_chunked == true && r->remaining == 0)
+ r->chunked = true;
+ //apr_table_setn(r->headers_out, "Connection", "close");
+ return OK;
+}
+
+/**
+ * Read the content of a POST or PUT.
+ */
+const list<string> read(request_rec* r) {
+ char b[1024];
+ const size_t n = ap_get_client_block(r, b, sizeof(b));
+ if (n <= 0)
+ return list<string>();
+ return cons(string(b, n), read(r));
+}
+
+/**
+ * Write an HTTP result.
+ */
+const failable<int> writeResult(const failable<list<string> >& ls, const string& ct, request_rec* r) {
+ if (!hasContent(ls))
+ return mkfailure<int>(ls);
+ ostringstream os;
+ write(content(ls), os);
+ const string ob(str(os));
+
+ // Make sure browsers come back and check for updated dynamic content
+ apr_table_set(r->headers_out, "Cache-Control", "must-revalidate, max-age=0");
+ apr_table_set(r->headers_out, "Expires", "Tue, 01 Jan 1980 00:00:00 GMT");
+ apr_table_set(r->subprocess_env, "private-cache", "1");
+
+ // Compute and return an Etag for the returned content
+ const string etag(ap_md5_binary(r->pool, (const unsigned char*)c_str(ob), (int)length(ob)));
+
+ // Check for an If-None-Match header and just return a 304 not-modified status
+ // if the Etag matches the Etag presented by the client, to save bandwith
+ const char* match = apr_table_get(r->headers_in, "If-None-Match");
+ apr_table_setn(r->headers_out, "ETag", apr_pstrdup(r->pool, c_str(etag)));
+ if (match != NULL && etag == match) {
+ r->status = HTTP_NOT_MODIFIED;
+ debug(r->status, "httpd::writeResult::status");
+ return OK;
+ }
+
+ debug(r->status, "httpd::writeResult::status");
+ debug(ct, "httpd::writeResult::contentType");
+ debug(ob, "httpd::writeResult::content");
+ ap_set_content_type(r, apr_pstrdup(r->pool, c_str(ct)));
+ ap_rwrite(c_str(ob), (int)length(ob), r);
+ return OK;
+}
+
+/**
+ * Report a request execution status.
+ */
+const int reportStatus(const failable<int>& rc) {
+ debug(rc, "httpd::reportStatus::rc");
+ if (!hasContent(rc)) {
+ const int r = rcode(rc);
+ return r == -1 ? HTTP_INTERNAL_SERVER_ERROR : r;
+ }
+ return content(rc);
+}
+
+/**
+ * Convert a value to an HTTPD request struc
+ */
+request_rec* request(const value& v) {
+ return (request_rec*)(long)(double)v;
+}
+
+/**
+ * Convert an HTTPD request struct to a value
+ */
+const value requestValue(request_rec* r) {
+ return value((double)(long)r);
+}
+
+/**
+ * Update request filters in an HTTPD redirect request.
+ * Similar to httpd/modules/http/http_request.c::update_r_in_filters.
+ */
+const bool redirectFilters(ap_filter_t* f, request_rec* from, request_rec* to) {
+ if (f == NULL)
+ return true;
+ if (f->r == from)
+ f->r = to;
+ return redirectFilters(f->next, from, to);
+}
+
+/**
+ * Create an HTTPD internal redirect request.
+ * Similar to httpd/modules/http/http_request.c::internal_internal_redirect.
+ */
+const failable<request_rec*> internalRedirectRequest(const string& nr_uri, request_rec* r) {
+ if (ap_is_recursion_limit_exceeded(r))
+ return mkfailure<request_rec*>("Redirect recursion limit exceeded", HTTP_INTERNAL_SERVER_ERROR);
+
+ // Create a new request
+ request_rec* nr = (request_rec*)apr_pcalloc(r->pool, sizeof(request_rec));
+ nr->connection = r->connection;
+ nr->server = r->server;
+ nr->pool = r->pool;
+ nr->method = r->method;
+ nr->method_number = r->method_number;
+ nr->allowed_methods = ap_make_method_list(nr->pool, 2);
+ ap_parse_uri(nr, apr_pstrdup(nr->pool, c_str(nr_uri)));
+ nr->filename = apr_pstrdup(nr->pool, c_str(string("/redirected:") + nr_uri));
+ nr->request_config = ap_create_request_config(r->pool);
+ nr->per_dir_config = r->server->lookup_defaults;
+ nr->prev = r;
+ r->next = nr;
+
+ // Run create request hook
+ ap_run_create_request(nr);
+
+ // Inherit protocol info from the original request
+ nr->the_request = r->the_request;
+ nr->allowed = r->allowed;
+ nr->status = r->status;
+ nr->assbackwards = r->assbackwards;
+ nr->header_only = r->header_only;
+ nr->protocol = r->protocol;
+ nr->proto_num = r->proto_num;
+ nr->hostname = r->hostname;
+ nr->request_time = r->request_time;
+ nr->main = r->main;
+ nr->headers_in = r->headers_in;
+ nr->headers_out = apr_table_make(r->pool, 12);
+ nr->err_headers_out = r->err_headers_out;
+ nr->subprocess_env = r->subprocess_env;
+ nr->notes = apr_table_make(r->pool, 5);
+ nr->allowed_methods = ap_make_method_list(nr->pool, 2);
+ nr->htaccess = r->htaccess;
+ nr->no_cache = r->no_cache;
+ nr->expecting_100 = r->expecting_100;
+ nr->no_local_copy = r->no_local_copy;
+ nr->read_length = r->read_length;
+ nr->vlist_validator = r->vlist_validator;
+ nr->user = r->user;
+
+ // Setup input and output filters
+ nr->proto_output_filters = r->proto_output_filters;
+ nr->proto_input_filters = r->proto_input_filters;
+ nr->output_filters = nr->proto_output_filters;
+ nr->input_filters = nr->proto_input_filters;
+ if (nr->main)
+ ap_add_output_filter_handle(ap_subreq_core_filter_handle, NULL, nr, nr->connection);
+ redirectFilters(nr->input_filters, r, nr);
+ redirectFilters(nr->output_filters, r, nr);
+ const int rrc = ap_run_post_read_request(nr);
+ if (rrc != OK && rrc != DECLINED)
+ return mkfailure<request_rec*>("Error handling internal redirect", rrc);
+
+ return nr;
+}
+
+/**
+ * Process an HTTPD internal redirect request.
+ * Similar to httpd/modules/http/http_request.c::ap_internal_redirect.
+ */
+const int internalRedirect(request_rec* nr) {
+ int status = ap_run_quick_handler(nr, 0);
+ if (status == DECLINED) {
+ status = ap_process_request_internal(nr);
+ if (status == OK)
+ status = ap_invoke_handler(nr);
+ }
+ if (status != OK) {
+ nr->status = status;
+ return OK;
+ }
+ ap_finalize_request_protocol(nr);
+ return OK;
+}
+
+/**
+ * Create and process an HTTPD internal redirect request.
+ */
+const int internalRedirect(const string& uri, request_rec* r) {
+ debug(uri, "httpd::internalRedirect");
+ const failable<request_rec*> nr = httpd::internalRedirectRequest(uri, r);
+ if (!hasContent(nr))
+ return rcode(nr);
+ return httpd::internalRedirect(content(nr));
+}
+
+/**
+ * Create an HTTPD sub request.
+ * Similar to httpd/server/request.c::make_sub_request
+ */
+const failable<request_rec*> internalSubRequest(const string& nr_uri, request_rec* r) {
+ if (ap_is_recursion_limit_exceeded(r))
+ return mkfailure<request_rec*>("Redirect recursion limit exceeded", HTTP_INTERNAL_SERVER_ERROR);
+
+ // Create a new sub pool
+ apr_pool_t *nrp;
+ apr_pool_create(&nrp, r->pool);
+ apr_pool_tag(nrp, "subrequest");
+
+ // Create a new POST request
+ request_rec* nr = (request_rec*)apr_pcalloc(nrp, sizeof(request_rec));
+ nr->connection = r->connection;
+ nr->server = r->server;
+ nr->pool = nrp;
+ nr->method = "POST";
+ nr->method_number = M_POST;
+ nr->allowed_methods = ap_make_method_list(nr->pool, 2);
+ ap_parse_uri(nr, apr_pstrdup(nr->pool, c_str(nr_uri)));
+ nr->filename = apr_pstrdup(nr->pool, c_str(string("/subreq:") + nr_uri));
+ nr->request_config = ap_create_request_config(r->pool);
+ nr->per_dir_config = r->server->lookup_defaults;
+
+ // Inherit some of the protocol info from the parent request
+ nr->the_request = r->the_request;
+ nr->hostname = r->hostname;
+ nr->request_time = r->request_time;
+ nr->allowed = r->allowed;
+ nr->status = HTTP_OK;
+ nr->assbackwards = r->assbackwards;
+ nr->header_only = r->header_only;
+ nr->protocol = const_cast<char*>("INCLUDED");
+ nr->hostname = r->hostname;
+ nr->request_time = r->request_time;
+ nr->main = r;
+ nr->headers_in = apr_table_make(r->pool, 12);
+ nr->headers_out = apr_table_make(r->pool, 12);
+ nr->err_headers_out = apr_table_make(nr->pool, 5);
+ nr->subprocess_env = r->subprocess_env;
+ nr->subprocess_env = apr_table_copy(nr->pool, r->subprocess_env);
+ nr->notes = apr_table_make(r->pool, 5);
+ nr->htaccess = r->htaccess;
+ nr->no_cache = r->no_cache;
+ nr->expecting_100 = r->expecting_100;
+ nr->no_local_copy = r->no_local_copy;
+ nr->read_length = 0;
+ nr->vlist_validator = r->vlist_validator;
+ nr->user = r->user;
+
+ // Setup input and output filters
+ nr->proto_output_filters = r->proto_output_filters;
+ nr->proto_input_filters = r->proto_input_filters;
+ nr->output_filters = nr->proto_output_filters;
+ nr->input_filters = nr->proto_input_filters;
+ ap_add_output_filter_handle(ap_subreq_core_filter_handle, NULL, nr, nr->connection);
+
+ // Run create request hook
+ ap_run_create_request(nr);
+ nr->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
+
+ return nr;
+}
+
+/**
+ * Return an HTTP external redirect request.
+ */
+const int externalRedirect(const string& uri, request_rec* r) {
+ debug(uri, "httpd::externalRedirect");
+ r->status = HTTP_MOVED_TEMPORARILY;
+ apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, c_str(uri)));
+ apr_table_setn(r->headers_out, "Cache-Control", "no-store");
+ apr_table_addn(r->err_headers_out, "Cache-Control", "no-store");
+ r->filename = apr_pstrdup(r->pool, c_str(string("/redirect:/") + uri));
+ return HTTP_MOVED_TEMPORARILY;
+}
+
+/**
+ * Put a value in the process user data.
+ */
+const bool putUserData(const string& k, const void* v, const server_rec* s) {
+ apr_pool_userdata_set((const void *)v, c_str(k), apr_pool_cleanup_null, s->process->pool);
+ return true;
+}
+
+/**
+ * Return a user data value.
+ */
+const void* userData(const string& k, const server_rec* s) {
+ void* v = NULL;
+ apr_pool_userdata_get(&v, c_str(k), s->process->pool);
+ return v;
+}
+
+#ifdef WANT_MAINTAINER_LOG
+
+/**
+ * Debug log.
+ */
+
+/**
+ * Log an optional value.
+ */
+const char* debugOptional(const char* s) {
+ if (s == NULL)
+ return "";
+ return s;
+}
+
+/**
+ * Log a header
+ */
+int debugHeader(unused void* r, const char* key, const char* value) {
+ cdebug << " header key: " << key << ", value: " << value << endl;
+ return 1;
+}
+
+/**
+ * Log an environment variable
+ */
+int debugEnv(unused void* r, const char* key, const char* value) {
+ cdebug << " var key: " << key << ", value: " << value << endl;
+ return 1;
+}
+
+/**
+ * Log a note.
+ */
+int debugNote(unused void* r, const char* key, const char* value) {
+ cdebug << " note key: " << key << ", value: " << value << endl;
+ return 1;
+}
+
+/**
+ * Log a request.
+ */
+const bool debugRequest(request_rec* r, const string& msg) {
+ gc_scoped_pool pool;
+ cdebug << msg << ":" << endl;
+ cdebug << " unparsed uri: " << debugOptional(r->unparsed_uri) << endl;
+ cdebug << " uri: " << debugOptional(r->uri) << endl;
+ cdebug << " path info: " << debugOptional(r->path_info) << endl;
+ cdebug << " filename: " << debugOptional(r->filename) << endl;
+ cdebug << " uri tokens: " << pathTokens(r->uri) << endl;
+ cdebug << " args: " << debugOptional(r->args) << endl;
+ cdebug << " server: " << debugOptional(r->server->server_hostname) << endl;
+ cdebug << " protocol: " << debugOptional(r->protocol) << endl;
+ cdebug << " method: " << debugOptional(r->method) << endl;
+ cdebug << " method number: " << r->method_number << endl;
+ cdebug << " content type: " << contentType(r) << endl;
+ cdebug << " content encoding: " << debugOptional(r->content_encoding) << endl;
+ apr_table_do(debugHeader, r, r->headers_in, NULL);
+ cdebug << " user: " << debugOptional(r->user) << endl;
+ cdebug << " auth type: " << debugOptional(r->ap_auth_type) << endl;
+ apr_table_do(debugEnv, r, r->subprocess_env, NULL);
+ apr_table_do(debugNote, r, r->notes, NULL);
+ return true;
+}
+
+#define debug_httpdRequest(r, msg) do { if (debug_islogging()) httpd::debugRequest(r, msg); } while(0)
+
+#else
+
+#define debug_httpdRequest(r, msg)
+
+#endif
+
+}
+}
+
+#endif /* tuscany_httpd_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/mass-host-conf b/sca-cpp/branches/lightweight-sca/modules/http/mass-host-conf
new file mode 100755
index 0000000000..2da8f4f836
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/mass-host-conf
@@ -0,0 +1,62 @@
+#!/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 mass dynamic virtual hosting configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+vroot=`echo "import os; print os.path.realpath('$2')" | python`
+vhtdocs=$3
+
+conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"`
+host=`echo $conf | awk '{ print $6 }'`
+addr=`echo $conf | awk '{ print $7 }'`
+port=`$here/httpd-addr port $addr`
+pport=`$here/httpd-addr pport $addr`
+vhost=`$here/httpd-addr vhost $addr`
+
+htdocs=`echo $conf | awk '{ print $8 }'`
+mkdir -p $htdocs
+htdocs=`echo "import os; print os.path.realpath('$htdocs')" | python`
+
+cat >>$root/conf/httpd.conf <<EOF
+# Generated by: mass-host-conf $*
+# Enable mass dynamic virtual hosting
+NameVirtualHost $vhost
+
+<VirtualHost $vhost>
+ServerName http://vhost.$host:$pport
+ServerAlias *.$host
+
+# Map /v/<app-name>/<path> to vroot/<app-name>/vhtdocs/<path>
+AliasMatch /v/([^/]+)(.*)$ $vroot/\$1/$vhtdocs/\$2
+
+Include conf/dvhost.conf
+
+# Configure authentication
+Include conf/noauth.conf
+Include conf/locauth.conf
+Include conf/pubauth.conf
+Include conf/adminauth.conf
+
+</VirtualHost>
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/mass-host-ssl-conf b/sca-cpp/branches/lightweight-sca/modules/http/mass-host-ssl-conf
new file mode 100755
index 0000000000..68bd5b4606
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/mass-host-ssl-conf
@@ -0,0 +1,68 @@
+#!/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 mass dynamic virtual hosting configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"`
+host=`echo $conf | awk '{ print $6 }'`
+
+sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"`
+ssladdr=`echo $sslconf | awk '{ print $6 }'`
+sslport=`$here/httpd-addr port $ssladdr`
+sslpport=`$here/httpd-addr pport $ssladdr`
+sslvhost=`$here/httpd-addr vhost $ssladdr`
+
+vhostconf=`cat $root/conf/httpd.conf | grep "# Generated by: mass-host-conf"`
+vroot=`echo $vhostconf | awk '{ print $6 }'`; vroot=`echo "import os; print os.path.realpath('$vroot')" | python`
+vhtdocs=`echo $vhostconf | awk '{ print $7 }'`
+
+htdocs=`echo $conf | awk '{ print $8 }'`
+mkdir -p $htdocs
+htdocs=`echo "import os; print os.path.realpath('$htdocs')" | python`
+
+cat >>$root/conf/httpd.conf <<EOF
+# Generated by: mass-host-ssl-conf $*
+# Enable mass dynamic virtual hosting over HTTPS
+SSLStrictSNIVHostCheck Off
+
+# HTTPS dynamic virtual host
+NameVirtualHost $sslvhost
+<VirtualHost $sslvhost>
+ServerName https://vhost.$host:$sslpport
+ServerAlias *.$host
+
+# Map /v/<app-name>/<path> to vroot/<app-name>/vhtdocs/<path>
+AliasMatch /v/([^/]+)(.*)$ $vroot/\$1/$vhtdocs/\$2
+
+Include conf/dvhost-ssl.conf
+
+# Configure authentication
+Include conf/noauth-ssl.conf
+Include conf/locauth-ssl.conf
+Include conf/pubauth-ssl.conf
+Include conf/adminauth-ssl.conf
+
+# Configure tracking
+Include conf/tracking-ssl.conf
+
+</VirtualHost>
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/minify-css b/sca-cpp/branches/lightweight-sca/modules/http/minify-css
new file mode 100755
index 0000000000..734d041aba
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/minify-css
@@ -0,0 +1,34 @@
+#!/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.
+
+# Minify a CSS file
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+pagespeed_prefix=`cat $here/pagespeed.prefix`
+
+if [ "${pagespeed_prefix}" = "" ]; then
+ minify_css="cat"
+else
+ minify_css="${pagespeed_prefix}/jsmin_bin"
+fi
+
+css="$1"
+mincss="$2"
+
+${minify_css} <${css} >${mincss}
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/minify-html b/sca-cpp/branches/lightweight-sca/modules/http/minify-html
new file mode 100755
index 0000000000..31bb329d94
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/minify-html
@@ -0,0 +1,34 @@
+#!/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.
+
+# Minify an HTML file
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+pagespeed_prefix=`cat $here/pagespeed.prefix`
+
+if [ "${pagespeed_prefix}" = "" ]; then
+ minify_html="cp"
+else
+ minify_html="${pagespeed_prefix}/minify_html_bin"
+fi
+
+html="$1"
+minhtml="$2"
+
+${minify_html} ${html} ${minhtml}
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/minify-js b/sca-cpp/branches/lightweight-sca/modules/http/minify-js
new file mode 100755
index 0000000000..5e7cbb1bc0
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/minify-js
@@ -0,0 +1,34 @@
+#!/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.
+
+# Minify a JS file
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+pagespeed_prefix=`cat $here/pagespeed.prefix`
+
+if [ "${pagespeed_prefix}" = "" ]; then
+ minify_js="cat"
+else
+ minify_js="${pagespeed_prefix}/jsmin_bin"
+fi
+
+js="$1"
+minjs="$2"
+
+${minify_js} <${js} >${minjs}
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/mod-openauth.cpp b/sca-cpp/branches/lightweight-sca/modules/http/mod-openauth.cpp
new file mode 100644
index 0000000000..2e308ecedb
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/mod-openauth.cpp
@@ -0,0 +1,478 @@
+/*
+ * 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 for Tuscany Open authentication.
+ *
+ * This module allows multiple authentication mechanisms to co-exist in a
+ * single Web site:
+ * - OAuth1 using Tuscany's mod-tuscany-oauth1
+ * - OAuth2 using Tuscany's mod-tuscany-oauth2
+ * - OpenID using mod_auth_openid
+ * - Form-based using HTTPD's mod_auth_form
+ * - SSL certificate using SSLFakeBasicAuth and mod_auth_basic
+ */
+
+#include <sys/stat.h>
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "stream.hpp"
+#include "list.hpp"
+#include "tree.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "httpd.hpp"
+#include "http.hpp"
+#include "openauth.hpp"
+
+
+extern "C" {
+extern module AP_MODULE_DECLARE_DATA mod_tuscany_openauth;
+}
+
+namespace tuscany {
+namespace openauth {
+
+/**
+ * Server configuration.
+ */
+class ServerConf {
+public:
+ ServerConf(apr_pool_t* p, server_rec* s) : p(p), server(s) {
+ }
+
+ const gc_pool p;
+ server_rec* server;
+};
+
+/**
+ * Authentication provider configuration.
+ */
+class AuthnProviderConf {
+public:
+ AuthnProviderConf() : name(), provider(NULL) {
+ }
+ AuthnProviderConf(const string name, const authn_provider* provider) : name(name), provider(provider) {
+ }
+
+ string name;
+ const authn_provider* provider;
+};
+
+/**
+ * Directory configuration.
+ */
+class DirConf {
+public:
+ DirConf(apr_pool_t* p, char* d) : p(p), dir(d), enabled(false), login("") {
+ }
+
+ const gc_pool p;
+ const char* dir;
+ bool enabled;
+ string login;
+ list<AuthnProviderConf> apcs;
+};
+
+#ifdef WANT_MAINTAINER_LOG
+
+/**
+ * Log session entries.
+ */
+int debugSessionEntry(unused void* r, const char* key, const char* value) {
+ cdebug << " session key: " << key << ", value: " << value << endl;
+ return 1;
+}
+
+const bool debugSession(request_rec* r, session_rec* z) {
+ apr_table_do(debugSessionEntry, r, z->entries, NULL);
+ return true;
+}
+
+#define debug_authSession(r, z) if (debug_islogging()) openauth::debugSession(r, z)
+
+#else
+
+#define debug_authSession(r, z)
+
+#endif
+
+/**
+ * Run the authnz hooks to authenticate a request.
+ */
+const failable<int> checkAuthnzProviders(const string& user, const string& pw, request_rec* r, const list<AuthnProviderConf>& apcs) {
+ if (isNil(apcs))
+ return mkfailure<int>("Authentication failure for: " + user);
+ const AuthnProviderConf apc = car<AuthnProviderConf>(apcs);
+ if (apc.provider == NULL || !apc.provider->check_password)
+ return checkAuthnzProviders(user, pw, r, cdr(apcs));
+
+ apr_table_setn(r->notes, AUTHN_PROVIDER_NAME_NOTE, c_str(apc.name));
+ const authn_status auth_result = apc.provider->check_password(r, c_str(user), c_str(pw));
+ apr_table_unset(r->notes, AUTHN_PROVIDER_NAME_NOTE);
+ if (auth_result != AUTH_GRANTED)
+ return checkAuthnzProviders(user, pw, r, cdr(apcs));
+ return OK;
+}
+
+const failable<int> checkAuthnz(const string& user, const string& pw, request_rec* r, const DirConf& dc) {
+ if (substr(user, 0, 1) == "/" && pw == "password")
+ return mkfailure<int>(string("Encountered FakeBasicAuth spoof: ") + user, HTTP_UNAUTHORIZED);
+
+ if (isNil(dc.apcs)) {
+ const authn_provider* provider = (const authn_provider*)ap_lookup_provider(AUTHN_PROVIDER_GROUP, AUTHN_DEFAULT_PROVIDER, AUTHN_PROVIDER_VERSION);
+ return checkAuthnzProviders(user, pw, r, mklist<AuthnProviderConf>(AuthnProviderConf(AUTHN_DEFAULT_PROVIDER, provider)));
+ }
+ return checkAuthnzProviders(user, pw, r, dc.apcs);
+}
+
+/**
+ * Return the user info from a form auth encrypted session cookie.
+ */
+static int (*ap_session_load_fn) (request_rec * r, session_rec ** z) = NULL;
+static int (*ap_session_get_fn) (request_rec * r, session_rec * z, const char *key, const char **value) = NULL;
+
+const failable<value> userInfoFromSession(const string& realm, request_rec* r) {
+ debug("modopenauth::userInfoFromSession");
+ if (ap_session_load_fn == NULL)
+ ap_session_load_fn = APR_RETRIEVE_OPTIONAL_FN(ap_session_load);
+ session_rec *z = NULL;
+ ap_session_load_fn(r, &z);
+ if (z == NULL)
+ return mkfailure<value>("Couldn't retrieve user session");
+ debug_authSession(r, z);
+
+ if (ap_session_get_fn == NULL)
+ ap_session_get_fn = APR_RETRIEVE_OPTIONAL_FN(ap_session_get);
+ const char* user = NULL;
+ ap_session_get_fn(r, z, c_str(realm + "-user"), &user);
+ if (user == NULL)
+ return mkfailure<value>("Couldn't retrieve user id");
+ const char* pw = NULL;
+ ap_session_get_fn(r, z, c_str(realm + "-pw"), &pw);
+ if (pw == NULL)
+ return mkfailure<value>("Couldn't retrieve password");
+ return value(mklist<value>(mklist<value>("realm", realm), mklist<value>("id", string(user)), mklist<value>("password", string(pw))));
+}
+
+/**
+ * Return the user info from a form auth session cookie.
+ */
+const failable<value> userInfoFromCookie(const value& sid, const string& realm, request_rec* r) {
+ const list<list<value>> info = httpd::queryArgs(sid);
+ debug(info, "modopenauth::userInfoFromCookie::info");
+ const list<value> user = assoc<value>(realm + "-user", info);
+ if (isNil(user))
+ return userInfoFromSession(realm, r);
+ const list<value> pw = assoc<value>(realm + "-pw", info);
+ if (isNil(pw))
+ return mkfailure<value>("Couldn't retrieve password");
+ return value(mklist<value>(mklist<value>("realm", realm), mklist<value>("id", cadr(user)), mklist<value>("password", cadr(pw))));
+}
+
+/**
+ * Return the user info from a basic auth header.
+ */
+const failable<value> userInfoFromHeader(const char* header, const string& realm, request_rec* r) {
+ debug(header, "modopenauth::userInfoFromHeader::header");
+ if (strcasecmp(ap_getword(r->pool, &header, ' '), "Basic"))
+ return mkfailure<value>("Wrong authentication scheme");
+
+ while (apr_isspace(*header))
+ header++;
+ char *decoded_line = (char*)apr_palloc(r->pool, apr_base64_decode_len(header) + 1);
+ int length = apr_base64_decode(decoded_line, header);
+ decoded_line[length] = '\0';
+
+ const string user(ap_getword_nulls(r->pool, const_cast<const char**>(&decoded_line), ':'));
+ const string pw(decoded_line);
+
+ return value(mklist<value>(mklist<value>("realm", realm), mklist<value>("id", user), mklist<value>("password", pw)));
+}
+
+/**
+ * Handle an authenticated request.
+ */
+const failable<int> authenticated(const list<list<value> >& info, request_rec* r) {
+ debug(info, "modopenauth::authenticated::info");
+
+ // Store user info in the request
+ const list<value> realm = assoc<value>("realm", info);
+ if (isNil(realm) || isNil(cdr(realm)))
+ return mkfailure<int>("Couldn't retrieve realm");
+ apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, "REALM"), apr_pstrdup(r->pool, c_str(cadr(realm))));
+
+ const list<value> id = assoc<value>("id", info);
+ if (isNil(id) || isNil(cdr(id)))
+ return mkfailure<int>("Couldn't retrieve user id");
+ r->user = apr_pstrdup(r->pool, c_str(cadr(id)));
+
+ apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, "NICKNAME"), apr_pstrdup(r->pool, c_str(cadr(id))));
+ return OK;
+}
+
+/**
+ * Check user authentication.
+ */
+static int checkAuthn(request_rec *r) {
+ gc_scoped_pool pool(r->pool);
+
+ // Decline if we're not enabled or AuthType is not set to Open
+ const DirConf& dc = httpd::dirConf<DirConf>(r, &mod_tuscany_openauth);
+ if (!dc.enabled)
+ return DECLINED;
+ const char* atype = ap_auth_type(r);
+ if (atype == NULL || strcasecmp(atype, "Open"))
+ return DECLINED;
+ debug_httpdRequest(r, "modopenauth::checkAuthn::input");
+ debug(atype, "modopenauth::checkAuthn::auth_type");
+
+ // Get the request args
+ const list<list<value> > args = httpd::queryArgs(r);
+
+ // Get session id from the request
+ const maybe<string> sid = sessionID(r, "TuscanyOpenAuth");
+ if (hasContent(sid)) {
+ // Decline if the session id was not created by this module
+ const string stype = substr(content(sid), 0, 7);
+ if (stype == "OAuth2_" || stype == "OAuth1_" || stype == "OpenID_")
+ return DECLINED;
+
+ // Retrieve the auth realm
+ const char* aname = ap_auth_name(r);
+ if (aname == NULL)
+ return httpd::reportStatus(mkfailure<int>("Missing AuthName"));
+
+ // Extract user info from the session id
+ const failable<value> userinfo = userInfoFromCookie(content(sid), aname, r);
+ if (hasContent(userinfo)) {
+
+ // Try to authenticate the request
+ const value uinfo = content(userinfo);
+ const failable<int> authz = checkAuthnz(cadr(assoc<value>("id", uinfo)), cadr(assoc<value>("password", uinfo)), r, dc);
+ if (!hasContent(authz)) {
+
+ // Authentication failed, redirect to login page
+ r->ap_auth_type = const_cast<char*>(atype);
+ return httpd::reportStatus(login(dc.login, value(), 1, r));
+ }
+
+ // Successfully authenticated, store the user info in the request
+ r->ap_auth_type = const_cast<char*>(atype);
+ return httpd::reportStatus(authenticated(uinfo, r));
+ }
+ }
+
+ // Get basic auth header from the request
+ const char* header = apr_table_get(r->headers_in, (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authorization" : "Authorization");
+ if (header != NULL) {
+
+ // Retrieve the auth realm
+ const char* aname = ap_auth_name(r);
+ if (aname == NULL)
+ return httpd::reportStatus(mkfailure<int>("Missing AuthName"));
+
+ // Extract user info from the session id
+ const failable<value> info = userInfoFromHeader(header, aname, r);
+ if (hasContent(info)) {
+
+ // Try to authenticate the request
+ const value uinfo = content(info);
+ const failable<int> authz = checkAuthnz(cadr(assoc<value>("id", uinfo)), cadr(assoc<value>("password", uinfo)), r, dc);
+ if (!hasContent(authz)) {
+
+ // Authentication failed, redirect to login page
+ r->ap_auth_type = const_cast<char*>(atype);
+ return httpd::reportStatus(login(dc.login, value(), 1, r));
+ }
+
+ // Successfully authenticated, store the user info in the request
+ r->ap_auth_type = const_cast<char*>(atype);
+ return httpd::reportStatus(authenticated(uinfo, r));
+ }
+ }
+
+ // Decline if the request is for another authentication provider
+ if (!isNil(assoc<value>("openid_identifier", args)))
+ return DECLINED;
+
+ // Redirect to the login page, unless we have a session id from another module
+ if (hasContent(sessionID(r, "TuscanyOpenIDAuth")) ||
+ hasContent(sessionID(r, "TuscanyOAuth1")) ||
+ hasContent(sessionID(r, "TuscanyOAuth2")))
+ return DECLINED;
+
+ r->ap_auth_type = const_cast<char*>(atype);
+ return httpd::reportStatus(login(dc.login, value(), value(), r));
+}
+
+/**
+ * Save the auth session cookie in the response.
+ */
+static int sessionCookieSave(request_rec* r, session_rec* z) {
+ gc_scoped_pool pool(r->pool);
+
+ const DirConf& dc = httpd::dirConf<DirConf>(r, &mod_tuscany_openauth);
+ if (!dc.enabled)
+ return DECLINED;
+
+ debug(c_str(cookie("TuscanyOpenAuth", z->encoded, httpd::hostName(r))), "modopenauth::setcookie");
+ apr_table_set(r->err_headers_out, "Set-Cookie", c_str(cookie("TuscanyOpenAuth", z->encoded, httpd::hostName(r))));
+ return OK;
+}
+
+/**
+ * Load the auth session cookie from the request. Similar
+ */
+static int sessionCookieLoad(request_rec* r, session_rec** z) {
+ gc_scoped_pool pool(r->pool);
+
+ const DirConf& dc = httpd::dirConf<DirConf>(r, &mod_tuscany_openauth);
+ if (!dc.enabled)
+ return DECLINED;
+
+ // First look in the notes
+ const char* note = apr_pstrcat(r->pool, "mod_openauth", "TuscanyOpenAuth", NULL);
+ session_rec* zz = (session_rec*)(void*)apr_table_get(r->notes, note);
+ if (zz != NULL) {
+ *z = zz;
+ return OK;
+ }
+
+ // Parse the cookie
+ const maybe<string> sid = openauth::sessionID(r, "TuscanyOpenAuth");
+
+ // Create a new session
+ zz = (session_rec*)apr_pcalloc(r->pool, sizeof(session_rec));
+ zz->pool = r->pool;
+ zz->entries = apr_table_make(r->pool, 10);
+ zz->encoded = hasContent(sid)? c_str(content(sid)) : NULL;
+ zz->uuid = (apr_uuid_t *) apr_pcalloc(r->pool, sizeof(apr_uuid_t));
+ *z = zz;
+
+ // Store it in the notes
+ apr_table_setn(r->notes, note, (char*)zz);
+
+ return OK;
+}
+
+/**
+ * Process the module configuration.
+ */
+int postConfigMerge(ServerConf& mainsc, server_rec* s) {
+ if (s == NULL)
+ return OK;
+ debug(httpd::serverName(s), "modopenauth::postConfigMerge::serverName");
+
+ 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);
+
+ ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_openauth);
+ debug(httpd::serverName(s), "modopenauth::postConfig::serverName");
+
+ // Merge server configurations
+ return postConfigMerge(sc, s);
+}
+
+/**
+ * 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_openauth);
+ if(psc == NULL) {
+ cfailure << "[Tuscany] Due to one or more errors mod_tuscany_openauth loading failed. Causing apache to stop loading." << endl;
+ exit(APEXIT_CHILDFATAL);
+ }
+ ServerConf& sc = *psc;
+
+ // Merge the updated configuration into the virtual hosts
+ postConfigMerge(sc, s->next);
+}
+
+/**
+ * Configuration commands.
+ */
+const char* confEnabled(cmd_parms *cmd, void *c, const int arg) {
+ gc_scoped_pool pool(cmd->pool);
+ DirConf& dc = httpd::dirConf<DirConf>(c);
+ dc.enabled = (bool)arg;
+ return NULL;
+}
+const char* confLogin(cmd_parms *cmd, void *c, const char* arg) {
+ gc_scoped_pool pool(cmd->pool);
+ DirConf& dc = httpd::dirConf<DirConf>(c);
+ dc.login = arg;
+ return NULL;
+}
+const char* confAuthnProvider(cmd_parms *cmd, void *c, const char* arg) {
+ gc_scoped_pool pool(cmd->pool);
+ DirConf& dc = httpd::dirConf<DirConf>(c);
+
+ // Lookup and cache the Authn provider
+ const authn_provider* provider = (authn_provider*)ap_lookup_provider(AUTHN_PROVIDER_GROUP, arg, AUTHN_PROVIDER_VERSION);
+ if (provider == NULL)
+ return apr_psprintf(cmd->pool, "Unknown Authn provider: %s", arg);
+ if (!provider->check_password)
+ return apr_psprintf(cmd->pool, "The '%s' Authn provider doesn't support password authentication", arg);
+ dc.apcs = append<AuthnProviderConf>(dc.apcs, mklist<AuthnProviderConf>(AuthnProviderConf(arg, provider)));
+ return NULL;
+}
+
+/**
+ * HTTP server module declaration.
+ */
+const command_rec commands[] = {
+ AP_INIT_ITERATE("AuthOpenAuthProvider", (const char*(*)())confAuthnProvider, NULL, OR_AUTHCFG, "Auth providers for a directory or location"),
+ AP_INIT_FLAG("AuthOpenAuth", (const char*(*)())confEnabled, NULL, OR_AUTHCFG, "Tuscany Open Auth authentication On | Off"),
+ AP_INIT_TAKE1("AuthOpenAuthLoginPage", (const char*(*)())confLogin, NULL, OR_AUTHCFG, "Tuscany Open Auth login page"),
+ {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_check_authn(checkAuthn, NULL, NULL, APR_HOOK_MIDDLE, AP_AUTH_INTERNAL_PER_CONF);
+ ap_hook_session_load(sessionCookieLoad, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_session_save(sessionCookieSave, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+}
+}
+
+extern "C" {
+
+module AP_MODULE_DECLARE_DATA mod_tuscany_openauth = {
+ STANDARD20_MODULE_STUFF,
+ // dir config and merger
+ tuscany::httpd::makeDirConf<tuscany::openauth::DirConf>, NULL,
+ // server config and merger
+ tuscany::httpd::makeServerConf<tuscany::openauth::ServerConf>, NULL,
+ // commands and hooks
+ tuscany::openauth::commands, tuscany::openauth::registerHooks
+};
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/mod-security-audit-conf b/sca-cpp/branches/lightweight-sca/modules/http/mod-security-audit-conf
new file mode 100755
index 0000000000..5914bd1df4
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/mod-security-audit-conf
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Generate a minimal mod-security audit configuration.
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+mkdir -p $root/tmp
+
+cat >>$root/conf/mod-security.conf <<EOF
+# Generated by: mod-security-audit-conf $*
+# Enable mod-security audit log
+SecAuditEngine RelevantOnly
+SecAuditLogRelevantStatus "^(?:5|4(?!04))"
+SecAuditLogParts ABIJDEFHKZ
+SecAuditLogType Serial
+Include conf/mod-security-audit-log.conf
+
+EOF
+
+# Configure audit logging
+cat >$root/conf/mod-security-audit-log.conf <<EOF
+# Generated by: mod-security-audit-conf $*
+SecAuditLog $root/logs/secaudit_log
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/mod-security-conf b/sca-cpp/branches/lightweight-sca/modules/http/mod-security-conf
new file mode 100755
index 0000000000..5d03fc5cfb
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/mod-security-conf
@@ -0,0 +1,184 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Generate a minimal mod-security configuration.
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+modules_prefix=`cat $here/httpd-modules.prefix`
+modsecurity_prefix=`cat $here/modsecurity.prefix`
+
+mkdir -p $root/tmp
+
+cat >>$root/conf/modules.conf <<EOF
+# Generated by: mod-security-conf $*
+# Load support for mod-security
+LoadModule unique_id_module ${modules_prefix}/modules/mod_unique_id.so
+LoadModule security2_module $modsecurity_prefix/lib/mod_security2.so
+
+EOF
+
+cat >>$root/conf/httpd.conf <<EOF
+# Generated by: mod-security-conf $*
+# Enable mod-security
+Include conf/mod-security.conf
+
+EOF
+
+cat >$root/conf/mod-security.conf <<EOF
+# Generated by: mod-security-conf $*
+# Enable mod-security rules
+SecRuleEngine On
+SecDefaultAction "phase:2,pass,nolog,auditlog"
+
+#SecDebugLog $root/logs//modsec_debug_log
+#SecDebugLogLevel 3
+
+# Process request bodies
+SecRequestBodyAccess Off
+SecRule REQUEST_HEADERS:Content-Type "text/xml" "phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
+SecRule REQUEST_HEADERS:Content-Type "application/xml" "phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
+SecRequestBodyLimit 13107200
+SecRequestBodyNoFilesLimit 131072
+SecRequestBodyInMemoryLimit 131072
+SecRequestBodyLimitAction Reject
+
+# Verify that we've correctly processed the request body
+SecRule REQBODY_ERROR "!@eq 0" "phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2"
+
+# By default be strict with what we accept in multipart/form-data request body
+SecRule MULTIPART_STRICT_ERROR "!@eq 0" "phase:2,t:none,log,deny,status:44,msg:'Multipart request body failed strict validation: \
+PE %{REQBODY_PROCESSOR_ERROR}, \
+BQ %{MULTIPART_BOUNDARY_QUOTED}, \
+BW %{MULTIPART_BOUNDARY_WHITESPACE}, \
+DB %{MULTIPART_DATA_BEFORE}, \
+DA %{MULTIPART_DATA_AFTER}, \
+HF %{MULTIPART_HEADER_FOLDING}, \
+LF %{MULTIPART_LF_LINE}, \
+SM %{MULTIPART_SEMICOLON_MISSING}, \
+IQ %{MULTIPART_INVALID_QUOTING}, \
+IH %{MULTIPART_INVALID_HEADER_FOLDING}, \
+IH %{MULTIPART_FILE_LIMIT_EXCEEDED}'"
+
+# Did we see anything that might be a boundary?
+SecRule MULTIPART_UNMATCHED_BOUNDARY "!@eq 0" "phase:2,t:none,log,deny,status:44,msg:'Multipart parser detected a possible unmatched boundary.'"
+
+# Avoid a potential RegEx DoS condition
+SecPcreMatchLimit 50000
+SecPcreMatchLimitRecursion 50000
+SecRule TX:/^MSC_/ "!@streq 0" "phase:2,t:none,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'"
+
+# Detect slow DoS attacks
+SecRule RESPONSE_STATUS "@streq 408" "phase:5,t:none,nolog,pass, setvar:ip.slow_dos_counter=+1,expirevar:ip.slow_dos_counter=60"
+SecRule IP:SLOW_DOS_COUNTER "@gt 5" "phase:1,t:none,log,drop, msg:'Client Connection Dropped due to high # of slow DoS alerts'"
+SecWriteStateLimit 50
+
+# Process response bodies
+SecResponseBodyAccess Off
+SecResponseBodyMimeType text/plain text/html text/xml application/xml
+SecResponseBodyLimit 524288
+SecResponseBodyLimitAction ProcessPartial
+
+# The location where mod-security stores temporary files
+SecTmpDir $root/tmp/
+SecDataDir $root/tmp/
+
+# Use & as application/x-www-form-urlencoded parameter separator
+SecArgumentSeparator &
+
+# Settle on version 0 (zero) cookies.
+SecCookieFormat 0
+
+# Enable anomaly scoring
+SecAction "phase:1,id:'981206',t:none,nolog,pass,setvar:tx.anomaly_score_blocking=on"
+SecAction "phase:1,id:'981207',t:none,nolog,pass, \
+setvar:tx.critical_anomaly_score=5, \
+setvar:tx.error_anomaly_score=4, \
+setvar:tx.warning_anomaly_score=3, \
+setvar:tx.notice_anomaly_score=2"
+SecAction "phase:1,id:'981208',t:none,nolog,pass,setvar:tx.inbound_anomaly_score_level=10"
+SecAction "phase:1,id:'981209',t:none,nolog,pass,setvar:tx.outbound_anomaly_score_level=8"
+
+# Paranoid mode
+SecAction "phase:1,id:'981210',t:none,nolog,pass,setvar:tx.paranoid_mode=0"
+
+# HTTP policy settings
+SecAction "phase:1,id:'981211',t:none,nolog,pass,setvar:tx.max_num_args=255"
+SecAction "phase:1,t:none,nolog,pass,setvar:tx.arg_name_length=100"
+SecAction "phase:1,t:none,nolog,pass,setvar:tx.arg_length=400"
+SecAction "phase:1,t:none,nolog,pass,setvar:tx.total_arg_length=64000"
+SecAction "phase:1,t:none,nolog,pass,setvar:tx.max_file_size=1048576"
+SecAction "phase:1,t:none,nolog,pass,setvar:tx.combined_file_sizes=1048576"
+SecAction "phase:1,id:'981212',t:none,nolog,pass, \
+setvar:'tx.allowed_methods=GET HEAD POST PUT OPTIONS DELETE CONNECT', \
+setvar:'tx.allowed_request_content_type=application/x-www-form-urlencoded multipart/form-data text/xml application/xml application/json application/json-rpc application/atom+xml', \
+setvar:'tx.allowed_http_versions=HTTP/0.9 HTTP/1.0 HTTP/1.1', \
+setvar:'tx.restricted_extensions=.asa/ .asax/ .ascx/ .axd/ .backup/ .bak/ .bat/ .cdx/ .cer/ .cfg/ .cmd/ .com/ .config/ .conf/ .cs/ .csproj/ .csr/ .dat/ .db/ .dbf/ .dll/ .dos/ .htr/ .htw/ .ida/ .idc/ .idq/ .inc/ .ini/ .key/ .licx/ .lnk/ .log/ .mdb/ .old/ .pass/ .pdb/ .pol/ .printer/ .pwd/ .resources/ .resx/ .sql/ .sys/ .vb/ .vbs/ .vbproj/ .vsdisco/ .webinfo/ .xsd/ .xsx/', \
+setvar:'tx.restricted_headers=/Proxy-Connection/ /Lock-Token/ /Content-Range/ /Translate/ /via/ /if/'"
+
+# Brute force protection
+SecAction "phase:1,id:'981214',t:none,nolog,pass, \
+setvar:'tx.brute_force_protected_urls=/login', \
+setvar:'tx.brute_force_burst_time_slice=60', \
+setvar:'tx.brute_force_counter_threshold=10', \
+setvar:'tx.brute_force_block_timeout=300'"
+
+# DoS protection
+SecAction "phase:1,id:'981215',t:none,nolog,pass, \
+setvar:'tx.dos_burst_time_slice=60', \
+setvar:'tx.dos_counter_threshold=100', \
+setvar:'tx.dos_block_timeout=600'"
+
+# Check UTF-8 encoding
+SecAction "phase:1,id:'981216',t:none,nolog,pass,setvar:tx.crs_validate_utf8_encoding=1"
+
+# Global and IP collections
+SecRule REQUEST_HEADERS:User-Agent "^(.*)$" "phase:1,id:'981217',t:none,pass,nolog,t:sha1,t:hexEncode,setvar:tx.ua_hash=%{matched_var}"
+SecRule REQUEST_HEADERS:x-forwarded-for "^\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b" "phase:1,id:'981225',t:none,pass,nolog,capture,setvar:tx.real_ip=%{tx.1}"
+SecRule &TX:REAL_IP "!@eq 0" "phase:1,id:'981226',t:none,pass,nolog,initcol:global=global,initcol:ip=%{tx.real_ip}_%{tx.ua_hash}"
+SecRule &TX:REAL_IP "@eq 0" "phase:1,id:'981218',t:none,pass,nolog,initcol:global=global,initcol:ip=%{remote_addr}_%{tx.ua_hash}"
+
+# Include all base mod-security CRS rules
+Include ${modsecurity_prefix}/base_rules/modsecurity_crs_20_protocol_violations.conf
+#Include ${modsecurity_prefix}/base_rules/modsecurity_crs_41_xss_attacks.conf
+#Include ${modsecurity_prefix}/base_rules/modsecurity_crs_50_outbound.conf
+Include ${modsecurity_prefix}/base_rules/modsecurity_crs_35_bad_robots.conf
+Include ${modsecurity_prefix}/base_rules/modsecurity_crs_47_common_exceptions.conf
+Include ${modsecurity_prefix}/base_rules/modsecurity_crs_60_correlation.conf
+#Include ${modsecurity_prefix}/base_rules/modsecurity_crs_40_generic_attacks.conf
+Include ${modsecurity_prefix}/base_rules/modsecurity_crs_21_protocol_anomalies.conf
+Include ${modsecurity_prefix}/base_rules/modsecurity_crs_30_http_policy.conf
+Include ${modsecurity_prefix}/base_rules/modsecurity_crs_49_inbound_blocking.conf
+Include ${modsecurity_prefix}/base_rules/modsecurity_crs_41_sql_injection_attacks.conf
+#Include ${modsecurity_prefix}/base_rules/modsecurity_crs_45_trojans.conf
+Include ${modsecurity_prefix}/base_rules/modsecurity_crs_59_outbound_blocking.conf
+Include ${modsecurity_prefix}/base_rules/modsecurity_crs_23_request_limits.conf
+Include ${modsecurity_prefix}/base_rules/modsecurity_crs_42_tight_security.conf
+
+# Include some optional mod-security CRS rules
+#Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_10_ignore_static.conf
+#Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_13_xml_enabler.conf
+#Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_25_cc_known.conf
+#Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_42_comment_spam.conf
+#Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_47_skip_outbound_checks.conf
+#Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_55_application_defects.conf
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/mod-ssltunnel.cpp b/sca-cpp/branches/lightweight-sca/modules/http/mod-ssltunnel.cpp
new file mode 100644
index 0000000000..b66cd29959
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/mod-ssltunnel.cpp
@@ -0,0 +1,379 @@
+/*
+ * 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 tunnel traffic over an HTTPS connection.
+ */
+
+#include <sys/stat.h>
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "stream.hpp"
+#include "list.hpp"
+#include "tree.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "httpd.hpp"
+#include "http.hpp"
+
+// Ignore cast align warnings in APR macros
+#ifdef WANT_MAINTAINER_WARNINGS
+#pragma GCC diagnostic ignored "-Wcast-align"
+#endif
+
+extern "C" {
+extern module AP_MODULE_DECLARE_DATA mod_tuscany_ssltunnel;
+}
+
+namespace tuscany {
+namespace httpd {
+namespace modssltunnel {
+
+/**
+ * Server configuration.
+ */
+class ServerConf {
+public:
+ ServerConf(apr_pool_t* p, server_rec* s) : p(p), server(s) {
+ }
+
+ const gc_pool p;
+ server_rec* server;
+ string pass;
+ string host;
+ string path;
+ string ca;
+ string cert;
+ string key;
+};
+
+extern "C" {
+extern module AP_DECLARE_DATA core_module;
+}
+
+/**
+ * Process the module configuration.
+ */
+int M_SSLTUNNEL;
+int postConfigMerge(ServerConf& mainsc, apr_pool_t* p, server_rec* s) {
+ if (s == NULL)
+ return OK;
+ ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_ssltunnel);
+ debug(httpd::serverName(s), "modssltunnel::postConfigMerge::serverName");
+
+ // Merge configuration from main server
+ if (length(sc.ca) == 0 && length(mainsc.ca) !=0)
+ sc.ca = mainsc.ca;
+ if (length(sc.cert) == 0 && length(mainsc.cert) !=0)
+ sc.cert = mainsc.cert;
+ if (length(sc.key) == 0 && length(mainsc.key) !=0)
+ sc.key = mainsc.key;
+
+ // Parse the configured TunnelPass URI
+ if (length(sc.pass) != 0) {
+ apr_uri_t uri;
+ apr_status_t prc = apr_uri_parse(p, c_str(sc.pass), &uri);
+ if (prc != APR_SUCCESS) {
+ mkfailure<int>("Couldn't parse TunnelPass: " + sc.pass + ", " + http::apreason(prc));
+ return prc;
+ }
+ sc.host = uri.hostname;
+ sc.path = uri.path;
+ }
+ return postConfigMerge(mainsc, p, 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);
+
+ ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_ssltunnel);
+ debug(httpd::serverName(s), "modssltunnel::postConfig::serverName");
+
+ // Register the SSLTUNNEL method
+ M_SSLTUNNEL = ap_method_register(p, "SSLTUNNEL");
+
+ // Merge and process server configurations
+ return postConfigMerge(sc, p, s);
+}
+
+/**
+ * Close a connection.
+ */
+const int close(conn_rec* conn, apr_socket_t* csock) {
+ debug("modssltunnel::close");
+ apr_socket_close(csock);
+ conn->aborted = 1;
+ return OK;
+}
+
+/**
+ * Abort a connection.
+ */
+const int abort(conn_rec* conn, apr_socket_t* csock, const string& reason) {
+ debug("modssltunnel::abort");
+ apr_socket_close(csock);
+ conn->aborted = 1;
+ return httpd::reportStatus(mkfailure<int>(reason));
+}
+
+/**
+ * Tunnel traffic from a client connection to a target URL.
+ */
+int tunnel(conn_rec* conn, const string& ca, const string& cert, const string& key, const string& url, const string& preamble, const gc_pool& p, unused ap_filter_t* ifilter, ap_filter_t* ofilter) {
+
+ // Create input/output bucket brigades
+ apr_bucket_brigade* ib = apr_brigade_create(pool(p), conn->bucket_alloc);
+ apr_bucket_brigade* ob = apr_brigade_create(pool(p), conn->bucket_alloc);
+
+ // Get client connection socket
+ apr_socket_t* csock = (apr_socket_t*)ap_get_module_config(conn->conn_config, &core_module);
+
+ // Open connection to target
+ http::CURLSession cs(ca, cert, key, "", 0);
+ const failable<bool> crc = http::connect(url, cs);
+ if (!hasContent(crc))
+ return abort(conn, csock, reason(crc));
+ apr_socket_t* tsock = http::sock(cs);
+
+ // Send preamble
+ if (length(preamble) != 0) {
+ debug(preamble, "modssltunnel::tunnel::sendPreambleToTarget");
+ const failable<bool> src = http::send(c_str(preamble), length(preamble), cs);
+ if (!hasContent(src))
+ return abort(conn, csock, string("Couldn't send to target: ") + reason(src));
+ }
+
+ // Create a pollset for the client and target sockets
+ apr_pollset_t* pollset;
+ apr_status_t cprc = apr_pollset_create(&pollset, 2, pool(p), 0);
+ if (cprc != APR_SUCCESS)
+ return abort(conn, csock, http::apreason(cprc));
+ const apr_pollfd_t* cpollfd = http::pollfd(csock, APR_POLLIN, p);
+ apr_pollset_add(pollset, cpollfd);
+ const apr_pollfd_t* tpollfd = http::pollfd(tsock, APR_POLLIN, p);
+ apr_pollset_add(pollset, tpollfd);
+
+ // Relay traffic in both directions until end of stream
+ const apr_pollfd_t* pollfds = cpollfd;
+ apr_int32_t pollcount = 1;
+ for(;;) {
+ for (; pollcount > 0; pollcount--, pollfds++) {
+ if (pollfds->rtnevents & APR_POLLIN) {
+ if (pollfds->desc.s == csock) {
+
+ // Receive buckets from client
+ const apr_status_t getrc = ap_get_brigade(conn->input_filters, ib, AP_MODE_READBYTES, APR_BLOCK_READ, HUGE_STRING_LEN);
+ if (getrc != APR_SUCCESS)
+ return abort(conn, csock, string("Couldn't receive from client"));
+
+ for (apr_bucket* bucket = APR_BRIGADE_FIRST(ib); bucket != APR_BRIGADE_SENTINEL(ib); bucket = APR_BUCKET_NEXT(bucket)) {
+ if (APR_BUCKET_IS_FLUSH(bucket))
+ continue;
+
+ // Client connection closed
+ if (APR_BUCKET_IS_EOS(bucket))
+ return close(conn, csock);
+
+ const char *data;
+ apr_size_t rl;
+ apr_bucket_read(bucket, &data, &rl, APR_BLOCK_READ);
+ if (rl > 0) {
+ debug(string(data, rl), "modssltunnel::tunnel::sendToTarget");
+
+ // Send to target
+ const failable<bool> src = http::send(data, rl, cs);
+ if (!hasContent(src))
+ return abort(conn, csock, string("Couldn't send to target: ") + reason(src));
+ }
+ }
+ apr_brigade_cleanup(ib);
+ } else {
+
+ // Receive from target
+ char data[8192];
+ const failable<size_t> frl = http::recv(data, sizeof(data), cs);
+ if (!hasContent(frl))
+ return abort(conn, csock, string("Couldn't receive from target") + reason(frl));
+ const size_t rl = content(frl);
+
+ // Target connection closed
+ if (rl == 0)
+ return close(conn, csock);
+
+
+ // Send bucket to client
+ debug(string(data, rl), "modssltunnel::tunnel::sendToClient");
+ APR_BRIGADE_INSERT_TAIL(ob, apr_bucket_transient_create(data, rl, conn->bucket_alloc));
+ APR_BRIGADE_INSERT_TAIL(ob, apr_bucket_flush_create(conn->bucket_alloc));
+ if (ap_pass_brigade(ofilter, ob) != APR_SUCCESS)
+ return abort(conn, csock, "Couldn't send data bucket to client");
+ apr_brigade_cleanup(ob);
+ }
+ }
+
+ // Error
+ if (pollfds->rtnevents & (APR_POLLERR | APR_POLLHUP | APR_POLLNVAL)) {
+ if (pollfds->desc.s == csock)
+ return abort(conn, csock, "Couldn't receive from client");
+ else
+ return abort(conn, csock, "Couldn't receive from target");
+ }
+ }
+
+ // Poll the client and target sockets
+ debug("modssltunnel::tunnel::poll");
+ apr_status_t pollrc = apr_pollset_poll(pollset, -1, &pollcount, &pollfds);
+ if (pollrc != APR_SUCCESS)
+ return abort(conn, csock, "Couldn't poll sockets");
+ debug(pollcount, "modssltunnel::tunnel::pollfds");
+ }
+
+ // Close client connection
+ return close(conn, csock);
+}
+
+/**
+ * Return the first connection filter in a list of filters.
+ */
+ap_filter_t* connectionFilter(ap_filter_t* f) {
+ if (f == NULL)
+ return f;
+ if (f->frec->ftype < AP_FTYPE_CONNECTION)
+ return connectionFilter(f->next);
+ return f;
+}
+
+/**
+ * Process a client connection and relay it to a tunnel.
+ */
+int processConnection(conn_rec *conn) {
+ // Only allow configured virtual hosts
+ if (!conn->base_server->is_virtual)
+ return DECLINED;
+ if (ap_get_module_config(conn->base_server->module_config, &mod_tuscany_ssltunnel) == NULL)
+ return DECLINED;
+
+ gc_scoped_pool pool(conn->pool);
+
+ // Get the server configuration
+ const ServerConf& sc = httpd::serverConf<ServerConf>(conn->base_server, &mod_tuscany_ssltunnel);
+ if (length(sc.pass) == 0)
+ return DECLINED;
+ debug(sc.pass, "modssltunnel::processConnection::pass");
+
+ // Run the tunnel
+ const string preamble = string("SSLTUNNEL ") + sc.path + string(" HTTP/1.1\r\nHost: ") + sc.host + string("\r\n\r\n");
+ debug(preamble, "modssltunnel::processConnection::preamble");
+ return tunnel(conn, sc.ca, sc.cert, sc.key, sc.pass, preamble, gc_pool(conn->pool), connectionFilter(conn->input_filters), connectionFilter(conn->output_filters));
+}
+
+/**
+ * Tunnel a SSLTUNNEL request to a target host/port.
+ */
+int handler(request_rec* r) {
+ if (r->method_number != M_SSLTUNNEL)
+ return DECLINED;
+
+ // Only allow HTTPS
+ if (strcmp(r->server->server_scheme, "https"))
+ return DECLINED;
+
+ gc_scoped_pool pool(r->pool);
+
+ // Build the target URL
+ debug(r->uri, "modssltunnel::handler::uri");
+ const list<value> path(pathValues(r->uri));
+ const string url = string(cadr(path)) + ":" + caddr(path);
+ debug(url, "modssltunnel::handler::target");
+
+ // Run the tunnel
+ return tunnel(r->connection, "", "", "", url, "", gc_pool(r->pool), connectionFilter(r->proto_input_filters), connectionFilter(r->proto_output_filters));
+}
+
+/**
+ * Configuration commands.
+ */
+const char* confTunnelPass(cmd_parms *cmd, unused void *c, const char *arg) {
+ gc_scoped_pool pool(cmd->pool);
+ ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_ssltunnel);
+ sc.pass = 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_ssltunnel);
+ 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_ssltunnel);
+ 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_ssltunnel);
+ sc.key = arg;
+ return NULL;
+}
+
+/**
+ * HTTP server module declaration.
+ */
+const command_rec commands[] = {
+ AP_INIT_TAKE1("TunnelPass", (const char*(*)())confTunnelPass, NULL, RSRC_CONF, "Tunnel server name"),
+ AP_INIT_TAKE1("TunnelSSLCACertificateFile", (const char*(*)())confCAFile, NULL, RSRC_CONF, "Tunnel SSL CA certificate file"),
+ AP_INIT_TAKE1("TunnelSSLCertificateFile", (const char*(*)())confCertFile, NULL, RSRC_CONF, "Tunnel SSL certificate file"),
+ AP_INIT_TAKE1("TunnelSSLCertificateKeyFile", (const char*(*)())confCertKeyFile, NULL, RSRC_CONF, "Tunnel 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_handler(handler, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_process_connection(processConnection, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+}
+}
+}
+
+extern "C" {
+
+module AP_MODULE_DECLARE_DATA mod_tuscany_ssltunnel = {
+ STANDARD20_MODULE_STUFF,
+ // dir config and merger
+ NULL, NULL,
+ // server config and merger
+ tuscany::httpd::makeServerConf<tuscany::httpd::modssltunnel::ServerConf>, NULL,
+ // commands and hooks
+ tuscany::httpd::modssltunnel::commands, tuscany::httpd::modssltunnel::registerHooks
+};
+
+}
+
+// Reenable cast align warnings
+#ifdef WANT_MAINTAINER_WARNINGS
+#pragma GCC diagnostic warning "-Wcast-align"
+#endif
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/open-auth-conf b/sca-cpp/branches/lightweight-sca/modules/http/open-auth-conf
new file mode 100755
index 0000000000..f4715b3a1c
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/open-auth-conf
@@ -0,0 +1,100 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Generate a minimal HTTPD form authentication configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+uname=`uname -s`
+if [ $uname = "Darwin" ]; then
+ libsuffix=".dylib"
+else
+ libsuffix=".so"
+fi
+
+conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"`
+host=`echo $conf | awk '{ print $6 }'`
+
+sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"`
+if [ "$sslconf" = "" ]; then
+ sslsuffix=""
+else
+ sslsuffix="-ssl"
+fi
+
+if [ "$2" = "" ]; then
+ providers="file"
+else
+ providers="$2 file"
+fi
+
+if [ "$3" = "" ]; then
+ pw=`cat $root/cert/ca.key | head -2 | tail -1`
+else
+ pw="$3"
+fi
+
+# Configure HTTPD mod_tuscany_openauth module
+cat >>$root/conf/modules.conf <<EOF
+# Generated by: open-auth-conf $*
+# Load support for Open authentication
+LoadModule mod_tuscany_openauth $here/libmod_tuscany_openauth$libsuffix
+
+EOF
+
+# Disallow public access to server resources
+cat >$root/conf/noauth$sslsuffix.conf <<EOF
+# Generated by: open-auth-conf $*
+# Disallow public access to server resources
+
+EOF
+
+# Generate form authentication configuration
+cat >>$root/conf/locauth$sslsuffix.conf <<EOF
+# Generated by: open-auth-conf $*
+# Enable Tuscany open authentication
+<Location />
+AuthType Open
+AuthName "$host"
+AuthOpenAuthProvider socache $providers
+AuthnCacheProvideFor $providers
+AuthnCacheContext /
+Session On
+SessionCryptoPassphrase $pw
+AuthOpenAuth On
+AuthOpenAuthLoginPage /login/
+Require valid-user
+</Location>
+
+# Use HTTPD form-based authentication
+<Location /login/dologin>
+AuthType Form
+AuthName "$host"
+AuthFormProvider socache $providers
+AuthnCacheProvideFor $providers
+AuthnCacheContext /
+AuthFormLoginRequiredLocation /login/?openauth_attempt=1
+AuthFormLogoutLocation /
+Require valid-user
+SetHandler form-login-handler
+</Location>
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/openauth.hpp b/sca-cpp/branches/lightweight-sca/modules/http/openauth.hpp
new file mode 100644
index 0000000000..3ffa88d362
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/openauth.hpp
@@ -0,0 +1,100 @@
+/*
+ * 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_openauth_hpp
+#define tuscany_openauth_hpp
+
+/**
+ * Tuscany Open auth support utility functions.
+ */
+
+#include "string.hpp"
+#include "stream.hpp"
+#include "list.hpp"
+#include "tree.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "../json/json.hpp"
+#include "../http/httpd.hpp"
+#include "../http/http.hpp"
+
+namespace tuscany {
+namespace openauth {
+
+/**
+ * Return the session id from a request.
+ */
+const char* cookieName(const char* cs) {
+ if (*cs != ' ')
+ return cs;
+ return cookieName(cs + 1);
+}
+const maybe<string> sessionID(const list<string>& c, const string& key) {
+ if (isNil(c))
+ return maybe<string>();
+ const string cn = cookieName(c_str(car(c)));
+ const size_t i = find(cn, "=");
+ if (i < length(cn)) {
+ const list<string> kv = mklist<string>(substr(cn, 0, i), substr(cn, i+1));
+ if (!isNil(kv) && !isNil(cdr(kv))) {
+ if (car(kv) == key)
+ return cadr(kv);
+ }
+ }
+ return sessionID(cdr(c), key);
+}
+
+const maybe<string> sessionID(const request_rec* r, const string& key) {
+ const string c = httpd::cookie(r);
+ debug(c, "openauth::sessionid::cookies");
+ if (length(c) == 0)
+ return maybe<string>();
+ return sessionID(tokenize(";", c), key);
+}
+
+/**
+ * Convert a session id to a cookie string.
+ */
+const string cookie(const string& key, const string& sid, const string& domain) {
+ const time_t t = time(NULL) + 86400;
+ char exp[32];
+ strftime(exp, 32, "%a, %d-%b-%Y %H:%M:%S GMT", gmtime(&t));
+ const string c = key + string("=") + sid + "; expires=" + string(exp) + "; domain=." + httpd::realm(domain) + "; path=/";
+ debug(c, "openauth::cookie");
+ return c;
+}
+
+/**
+ * Redirect to the configured login page.
+ */
+const failable<int> login(const string& page, const value& ref, const value& attempt, request_rec* r) {
+ const list<list<value> > rarg = ref == string("/")? list<list<value> >() : mklist<list<value> >(mklist<value>("openauth_referrer", httpd::escape(httpd::url(isNil(ref)? r->uri : ref, r))));
+ const list<list<value> > aarg = isNil(attempt)? list<list<value> >() : mklist<list<value> >(mklist<value>("openauth_attempt", attempt));
+ const list<list<value> > largs = append<list<value> >(rarg, aarg);
+ const string loc = isNil(largs)? httpd::url(page, r) : httpd::url(page, r) + string("?") + http::queryString(largs);
+ debug(loc, "openauth::login::uri");
+ return httpd::externalRedirect(loc, r);
+}
+
+}
+}
+
+#endif /* tuscany_openauth_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/passwd-auth-conf b/sca-cpp/branches/lightweight-sca/modules/http/passwd-auth-conf
new file mode 100755
index 0000000000..718b96de0a
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/passwd-auth-conf
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+user=$2
+pass=$3
+
+httpd_prefix=`cat $here/httpd.prefix`
+
+# Create password file
+touch $root/conf/httpd.passwd
+$httpd_prefix/bin/htpasswd -b $root/conf/httpd.passwd "$user" "$pass" 2>/dev/null
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/proxy-balancer-conf b/sca-cpp/branches/lightweight-sca/modules/http/proxy-balancer-conf
new file mode 100755
index 0000000000..0b63c9e481
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/proxy-balancer-conf
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Generate a minimal HTTPD proxy balancer configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+bal=$2
+if [ "$bal" = "" ]; then
+ bal="cluster"
+fi
+loc=$3
+if [ "$loc" = "" ]; then
+ loc="/"
+fi
+
+cat >>$root/conf/vhost.conf <<EOF
+# Generated by: proxy-pass-conf $*
+# Enable load balancing
+ProxyPass $loc balancer://$bal/
+
+<Proxy balancer://$bal>
+Require all granted
+ProxySet lbmethod=byrequests
+</Proxy>
+
+<Location $loc>
+RequestHeader set X-Forwarded-HTTPS %{HTTPS}s
+RequestHeader set X-Forwarded-Port %{SERVER_PORT}s
+</Location>
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/proxy-base-conf b/sca-cpp/branches/lightweight-sca/modules/http/proxy-base-conf
new file mode 100755
index 0000000000..377175328d
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/proxy-base-conf
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Generate a minimal HTTPD proxy balancer configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+cat >>$root/conf/vhost.conf <<EOF
+# Generated by: proxy-base-conf $*
+# Do not proxy admin pages
+ProxyPass /balancer-manager !
+ProxyPass /server-status !
+ProxyPass /proxy !
+
+# Enable balancer manager
+<Location /balancer-manager>
+SetHandler balancer-manager
+HostnameLookups on
+</Location>
+
+EOF
+
+cat >>$root/conf/adminauth.conf <<EOF
+# Generated by: proxy-base-conf $*
+# Allow the server admin to manage the load balancer
+<Location /balancer-manager>
+Require user admin
+</Location>
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/proxy-conf b/sca-cpp/branches/lightweight-sca/modules/http/proxy-conf
new file mode 100755
index 0000000000..ce990bbfd3
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/proxy-conf
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Generate a minimal HTTPD proxy balancer configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+cat >>$root/conf/vhost.conf <<EOF
+# Generated by: proxy-conf $*
+# Do not proxy admin pages
+ProxyPass /balancer-manager !
+ProxyPass /server-status !
+ProxyPass /proxy !
+
+# Enable load balancing
+ProxyPass / balancer://cluster/
+<Proxy balancer://cluster>
+Require all granted
+ProxySet lbmethod=byrequests
+</Proxy>
+
+<Location />
+RequestHeader set X-Forwarded-HTTPS %{HTTPS}s
+RequestHeader set X-Forwarded-Port %{SERVER_PORT}s
+</Location>
+
+# Enable balancer manager
+<Location /balancer-manager>
+SetHandler balancer-manager
+HostnameLookups on
+</Location>
+
+EOF
+
+cat >>$root/conf/adminauth.conf <<EOF
+# Generated by: proxy-conf $*
+# Allow the server admin to manage the load balancer
+<Location /balancer-manager>
+Require user admin
+</Location>
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/proxy-member-conf b/sca-cpp/branches/lightweight-sca/modules/http/proxy-member-conf
new file mode 100755
index 0000000000..a046a4fd08
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/proxy-member-conf
@@ -0,0 +1,45 @@
+#!/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.
+
+# Add a proxy balancer member
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+host=$2
+port=`$here/httpd-addr port $3`
+if [ "$port" = "80" ]; then
+ portsuffix=""
+else
+ portsuffix=":$port"
+fi
+
+bal=$4
+if [ "$bal" = "" ]; then
+ bal="cluster"
+fi
+
+cat >>$root/conf/vhost.conf <<EOF
+# Generated by: proxy-member-conf $*
+# Add proxy balancer member
+BalancerMember balancer://$bal http://$host$portsuffix
+ProxyPassReverse / http://$host$portsuffix/
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-conf b/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-conf
new file mode 100755
index 0000000000..ad7f26d83a
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-conf
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Generate a minimal HTTPD proxy balancer configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+cat >>$root/conf/vhost-ssl.conf <<EOF
+# Generated by: proxy-ssl-conf $*
+# Do not proxy admin pages
+ProxyPass /balancer-manager !
+ProxyPass /server-status !
+ProxyPass /proxy !
+
+# Enable load balancing
+ProxyPass / balancer://sslcluster/
+<Proxy balancer://sslcluster>
+Require all granted
+ProxySet lbmethod=byrequests
+</Proxy>
+
+<Location />
+RequestHeader set X-Forwarded-HTTPS %{HTTPS}s
+RequestHeader set X-Forwarded-Port %{SERVER_PORT}s
+</Location>
+
+# Enable balancer manager
+<Location /balancer-manager>
+SetHandler balancer-manager
+HostnameLookups on
+</Location>
+
+EOF
+
+cat >>$root/conf/svhost-ssl.conf <<EOF
+# Generated by: proxy-ssl-conf $*
+# Declare proxy SSL client certificates
+#SSLProxyCACertificateFile "$root/cert/ca.crt"
+#SSLProxyMachineCertificateFile "$root/cert/proxy.pem"
+
+EOF
+
+cat >>$root/conf/dvhost-ssl.conf <<EOF
+# Generated by: proxy-ssl-conf $*
+# Declare proxy SSL client certificates
+#SSLProxyCACertificateFile "$root/cert/ca.crt"
+#SSLProxyMachineCertificateFile "$root/cert/proxy.pem"
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-member-conf b/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-member-conf
new file mode 100755
index 0000000000..cb42a1e9db
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-member-conf
@@ -0,0 +1,40 @@
+#!/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.
+
+# Add a proxy balancer member
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+host=$2
+sslport=`$here/httpd-addr port $3`
+if [ "$sslport" = "443" ]; then
+ sslportsuffix=""
+else
+ sslportsuffix=":$sslport"
+fi
+
+cat >>$root/conf/vhost-ssl.conf <<EOF
+# Generated by: proxy-ssl-member-conf $*
+# Add proxy balancer member
+BalancerMember balancer://sslcluster https://$host$sslportsuffix
+ProxyPassReverse / https://$host$sslportsuffix/
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-nossl-member-conf b/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-nossl-member-conf
new file mode 100755
index 0000000000..17b766d986
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/proxy-ssl-nossl-member-conf
@@ -0,0 +1,40 @@
+#!/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.
+
+# Add a proxy balancer member
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+host=$2
+port=`$here/httpd-addr port $3`
+if [ "$port" = "80" ]; then
+ portsuffix=""
+else
+ portsuffix=":$port"
+fi
+
+cat >>$root/conf/vhost-ssl.conf <<EOF
+# Generated by: proxy-ssl-nossl-member-conf $*
+# Add proxy balancer member
+BalancerMember balancer://sslcluster http://$host$portsuffix
+ProxyPassReverse / http://$host$portsuffix/
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/proxy-test b/sca-cpp/branches/lightweight-sca/modules/http/proxy-test
new file mode 100755
index 0000000000..0333dd280b
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/proxy-test
@@ -0,0 +1,40 @@
+#!/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
+rm -rf tmp
+./httpd-conf tmp localhost 8091/8090 htdocs
+./httpd-event-conf tmp
+./httpd-start tmp
+./httpd-conf tmp/proxy localhost 8090 tmp/proxy/htdocs
+./httpd-event-conf tmp/proxy
+./proxy-conf tmp/proxy
+./proxy-member-conf tmp/proxy localhost 8091
+./httpd-start tmp/proxy
+sleep 2
+
+# Test
+./curl-test 2>/dev/null
+rc=$?
+
+# Cleanup
+./httpd-stop tmp/proxy
+./httpd-stop tmp
+sleep 2
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/ssl-ca-conf b/sca-cpp/branches/lightweight-sca/modules/http/ssl-ca-conf
new file mode 100755
index 0000000000..bceca8f300
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/ssl-ca-conf
@@ -0,0 +1,96 @@
+#!/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 test certification authority certificate
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+host=$2
+
+# Don't override existing certificate
+if [ -f $root/cert/ca.crt ]; then
+ exit 0
+fi
+
+# Generate openssl configuration
+mkdir -p $root/cert
+umask 0007
+cat >$root/cert/openssl-ca.conf <<EOF
+[ req ]
+default_bits = 1024
+encrypt_key = no
+prompt = no
+distinguished_name = req_distinguished_name
+x509_extensions = v3_ca
+
+[ req_distinguished_name ]
+C = US
+ST = CA
+L = San Francisco
+O = $host
+OU = authority
+CN = $host
+emailAddress = admin@$host
+
+[ v3_ca ]
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid:always,issuer:always
+basicConstraints = CA:true
+
+[ca]
+default_ca = ca_default
+
+[ca_default]
+certificate = $root/cert/ca.crt
+private_key = $root/cert/ca.key
+serial = $root/cert/ca-serial
+database = $root/cert/ca-database
+new_certs_dir = $root/cert
+default_md = sha1
+email_in_dn = no
+default_days = 365
+default_crl_days = 30
+policy = policy_any
+copy_extensions = none
+
+[ policy_any ]
+countryName = supplied
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+EOF
+
+rm -rf $root/cert/*.crt $root/cert/*.pem $root/cert/hash
+rm -f $root/cert/ca-database
+echo 1000 > $root/cert/ca-serial
+touch $root/cert/ca-database
+
+# Generate the certification authority certificate
+openssl req -new -x509 -config $root/cert/openssl-ca.conf -out $root/cert/ca.crt -keyout $root/cert/ca.key
+
+# Add to the hash directory and rehash
+mkdir -p $root/cert/hash
+cp $root/cert/ca.crt $root/cert/hash
+perl /usr/bin/c_rehash $root/cert/hash
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/ssl-cert-conf b/sca-cpp/branches/lightweight-sca/modules/http/ssl-cert-conf
new file mode 100755
index 0000000000..9e785ec86e
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/ssl-cert-conf
@@ -0,0 +1,76 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Generate a test certificate
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+host=$2
+if [ "$3" != "" ]; then
+ certname=$3
+else
+ certname="server"
+fi
+
+# Don't regenerate the certificate if it already exists
+if [ -f $root/cert/$certname.crt ]; then
+ exit 0
+fi
+
+# Generate openssl configuration
+mkdir -p $root/cert
+umask 0007
+cat >$root/cert/openssl-cert-$certname.conf <<EOF
+[ req ]
+default_bits = 1024
+encrypt_key = no
+prompt = no
+distinguished_name = req_distinguished_name
+
+[ req_distinguished_name ]
+C = US
+ST = CA
+L = San Francisco
+O = $host
+OU = $certname
+CN = $host
+emailAddress = admin@$host
+EOF
+
+# Generate a certificate request
+openssl req -new -config $root/cert/openssl-cert-$certname.conf -out $root/cert/$certname-req.crt -keyout $root/cert/$certname.key
+
+# Generate a certificate, signed with our test certification authority certificate
+openssl ca -batch -config $root/cert/openssl-ca.conf -out $root/cert/$certname.crt -infiles $root/cert/$certname-req.crt
+
+# Export it to PKCS12 format, that's the format Web browsers want to import
+openssl pkcs12 -export -passout pass: -out $root/cert/$certname.p12 -inkey $root/cert/$certname.key -in $root/cert/$certname.crt -certfile $root/cert/ca.crt
+
+# Convert the certificate to PEM format and concatenate the key to it, for use
+# by mod_proxy
+openssl x509 -in $root/cert/$certname.crt -out $root/cert/$certname.pem
+cat $root/cert/$certname.key >> $root/cert/$certname.pem
+
+# Add to the hash directory and rehash
+mkdir -p $root/cert/hash
+cp $root/cert/$certname.crt $root/cert/hash
+cp $root/cert/$certname.pem $root/cert/hash
+perl /usr/bin/c_rehash $root/cert/hash
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/ssl-cert-find b/sca-cpp/branches/lightweight-sca/modules/http/ssl-cert-find
new file mode 100755
index 0000000000..7a4b4f0220
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/ssl-cert-find
@@ -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.
+
+# List certificate files, useful to distribute them to another host
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+cd $root
+find . -name "*.crt"
+find . -name "*.pem"
+find . -name "*.p12"
+find . -name "*.key"
+find . -name "*.0"
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/http/tunnel-ssl-conf b/sca-cpp/branches/lightweight-sca/modules/http/tunnel-ssl-conf
new file mode 100755
index 0000000000..021d205ed9
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/http/tunnel-ssl-conf
@@ -0,0 +1,55 @@
+#!/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 an SSL tunnel configuration
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"`
+host=`echo $conf | awk '{ print $6 }'`
+
+port=`$here/httpd-addr port $2`
+sslhost=$3
+sslport=$4
+tport=$5
+
+# Generate HTTPD configuration
+cat >>$root/conf/httpd.conf <<EOF
+# Generated by: tunnel-ssl-conf $*
+# Tunnel TCP/IP traffic over HTTPS
+
+# Listen on local port
+Listen 127.0.0.1:$port
+
+# Tunnel virtual host
+<VirtualHost 127.0.0.1:$port>
+ServerName http://localhost:$port
+
+TunnelPass https://$sslhost:$sslport/tunnel/localhost/$tport
+
+# Declare SSL certificates used in this virtual host
+#TunnelSSLCACertificateFile "$root/cert/ca.crt"
+TunnelSSLCertificateFile "$root/cert/tunnel.crt"
+TunnelSSLCertificateKeyFile "$root/cert/tunnel.key"
+
+</VirtualHost>
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/Makefile.am b/sca-cpp/branches/lightweight-sca/modules/java/Makefile.am
new file mode 100644
index 0000000000..8b80276d15
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/Makefile.am
@@ -0,0 +1,70 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+JAVAROOT = $(top_builddir)/modules/java
+
+if WANT_JAVA
+
+INCLUDES = -I${JAVA_INCLUDE}
+
+incl_HEADERS = *.hpp
+incldir = $(prefix)/include/modules/java
+
+dist_mod_SCRIPTS = java-conf
+moddir = $(prefix)/modules/java
+
+prefix_DATA = java.prefix
+prefixdir = $(prefix)/modules/java
+java.prefix: $(top_builddir)/config.status
+ echo ${JAVA_PREFIX} >java.prefix
+
+EXTRA_DIST = domain-test.composite
+
+mod_LTLIBRARIES = libmod_tuscany_java.la
+libmod_tuscany_java_la_SOURCES = mod-java.cpp
+libmod_tuscany_java_la_LDFLAGS = -lxml2 -lcurl -lmozjs ${JAVA_LDFLAGS}
+noinst_DATA = libmod_tuscany_java${libsuffix}
+libmod_tuscany_java${libsuffix}:
+ ln -s .libs/libmod_tuscany_java${libsuffix}
+
+jni_test_SOURCES = jni-test.cpp
+jni_test_LDFLAGS = ${JAVA_LDFLAGS}
+
+java_test_SOURCES = java-test.cpp
+java_test_LDFLAGS = ${JAVA_LDFLAGS}
+
+java_shell_SOURCES = java-shell.cpp
+java_shell_LDFLAGS = ${JAVA_LDFLAGS}
+
+dist_mod_JAVA = org/apache/tuscany/*.java test/*.java
+jardir = ${prefix}/modules/java
+jarfile = libmod-tuscany-java-${PACKAGE_VERSION}.jar
+jar_DATA = ${jarfile}
+${jarfile}: ${dist_mod_JAVA}
+ ${JAR} cf $@ org/apache/tuscany/*.class
+
+CLEANFILES = *.stamp ${jarfile} org/apache/tuscany/*.class test/*.class
+
+client_test_SOURCES = client-test.cpp
+client_test_LDFLAGS = -lxml2 -lcurl -lmozjs
+
+dist_noinst_SCRIPTS = server-test wiring-test
+noinst_PROGRAMS = jni-test java-test client-test
+mod_PROGRAMS = java-shell
+TESTS = jni-test java-test server-test
+
+endif
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/client-test.cpp b/sca-cpp/branches/lightweight-sca/modules/java/client-test.cpp
new file mode 100644
index 0000000000..d06c57721e
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/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 "../server/client-test.hpp"
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+ tuscany::server::testURI = "http://localhost:8090/java";
+
+ tuscany::server::testServer();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/domain-test.composite b/sca-cpp/branches/lightweight-sca/modules/java/domain-test.composite
new file mode 100644
index 0000000000..a4c696c38d
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/domain-test.composite
@@ -0,0 +1,41 @@
+<?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"
+ targetNamespace="http://domain/test"
+ name="domain-test">
+
+ <component name="java-test">
+ <implementation.java class="test.ServerImpl"/>
+ <service name="test">
+ <binding.http uri="java"/>
+ </service>
+ </component>
+
+ <component name="client-test">
+ <implementation.java class="test.ClientImpl"/>
+ <service name="client">
+ <binding.http uri="client"/>
+ </service>
+ <reference name="ref" target="java-test">
+ <binding.http/>
+ </reference>
+ </component>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/driver.hpp b/sca-cpp/branches/lightweight-sca/modules/java/driver.hpp
new file mode 100644
index 0000000000..ddfc057940
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/driver.hpp
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+#ifndef tuscany_java_driver_hpp
+#define tuscany_java_driver_hpp
+
+/**
+ * Java evaluator main driver loop.
+ */
+
+#include "string.hpp"
+#include "stream.hpp"
+#include "monad.hpp"
+#include "../scheme/driver.hpp"
+#include "eval.hpp"
+
+namespace tuscany {
+namespace java {
+
+const value evalDriverLoop(const JavaRuntime& jr, const JavaClass jc, istream& in, ostream& out) {
+ scheme::promptForInput(scheme::evalInputPrompt, out);
+ value input = scheme::readValue(in);
+ if (isNil(input))
+ return input;
+ const failable<value> output = evalClass(jr, input, jc);
+ scheme::announceOutput(scheme::evalOutputPrompt, out);
+ scheme::userPrint(content(output), out);
+ return evalDriverLoop(jr, jc, in, out);
+}
+
+const bool evalDriverRun(const char* name, istream& in, ostream& out) {
+ scheme::setupDisplay(out);
+ JavaRuntime javaRuntime;
+ const failable<JavaClass> jc = readClass(javaRuntime, ".", name);
+ if (!hasContent(jc))
+ return true;
+ evalDriverLoop(javaRuntime, content(jc), in, out);
+ return true;
+}
+
+}
+}
+#endif /* tuscany_java_driver_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/eval.hpp b/sca-cpp/branches/lightweight-sca/modules/java/eval.hpp
new file mode 100644
index 0000000000..31fb09f741
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/eval.hpp
@@ -0,0 +1,566 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+#ifndef tuscany_java_eval_hpp
+#define tuscany_java_eval_hpp
+
+/**
+ * Java component implementation evaluation logic.
+ */
+#include <jni.h>
+
+#include "list.hpp"
+#include "value.hpp"
+
+namespace tuscany {
+namespace java {
+
+/**
+ * Handle differences between various JNI APIs.
+ */
+#ifdef JAVA_HARMONY_VM
+#define JNI_VERSION JNI_VERSION_1_4
+#else
+#define JNI_VERSION JNI_VERSION_1_6
+#endif
+
+/**
+ * Represent a Java VM runtime.
+ */
+jobject JNICALL nativeInvoke(JNIEnv *env, jobject self, jobject proxy, jobject method, jobjectArray args);
+jobject JNICALL nativeUUID(JNIEnv *env);
+
+class JavaRuntime {
+public:
+ JavaRuntime() {
+ debug("java::javaruntime");
+
+ // Get existing JVM
+ jsize nvms = 0;
+ JNI_GetCreatedJavaVMs(&jvm, 1, &nvms);
+ if (nvms == 0) {
+
+ // Create a new JVM
+ JavaVMInitArgs args;
+ args.version = JNI_VERSION;
+ args.ignoreUnrecognized = JNI_FALSE;
+ JavaVMOption options[3];
+ args.options = options;
+ args.nOptions = 0;
+
+ // Configure classpath
+ const char* envcp = getenv("CLASSPATH");
+ const string cp = string("-Djava.class.path=") + (envcp == NULL? "." : envcp);
+ options[args.nOptions].optionString = const_cast<char*>(c_str(cp));
+ options[args.nOptions++].extraInfo = NULL;
+
+#ifdef WANT_MAINTAINER_ASSERT
+ // Enable assertions
+ options[args.nOptions++].optionString = const_cast<char*>("-ea");
+#endif
+
+ // Configure Java debugging
+ const char* jpdaopts = getenv("JPDA_OPTS");
+ if (jpdaopts != NULL) {
+ options[args.nOptions].optionString = const_cast<char*>(jpdaopts);
+ options[args.nOptions++].extraInfo = NULL;
+ } else {
+ const char* jpdaaddr = getenv("JPDA_ADDRESS");
+ if (jpdaaddr != NULL) {
+ const string jpda = string("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=") + jpdaaddr;
+ options[args.nOptions].optionString = const_cast<char*>(c_str(jpda));
+ options[args.nOptions++].extraInfo = NULL;
+ }
+ }
+
+ // Create the JVM
+#ifdef JAVA_HARMONY_VM
+ JNI_CreateJavaVM(&jvm, &env, &args);
+#else
+ JNI_CreateJavaVM(&jvm, (void**)&env, &args);
+#endif
+
+ } else {
+
+ // Just point to existing JVM
+ jvm->GetEnv((void**)&env, JNI_VERSION);
+ }
+
+ // Lookup System classes and methods
+ classClass = env->FindClass("java/lang/Class");
+ methodClass = env->FindClass("java/lang/reflect/Method");
+ objectClass = env->FindClass("java/lang/Object");
+ doubleClass = env->FindClass("java/lang/Double");
+ booleanClass = env->FindClass("java/lang/Boolean");
+ stringClass = env->FindClass("java/lang/String");
+ objectArrayClass = env->FindClass("[Ljava/lang/Object;");
+ iterableClass = env->FindClass("java/lang/Iterable");
+ classForName = env->GetStaticMethodID(classClass, "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
+ doubleValueOf = env->GetStaticMethodID(doubleClass, "valueOf", "(D)Ljava/lang/Double;");
+ doubleValue = env->GetMethodID(doubleClass, "doubleValue", "()D");
+ booleanValueOf = env->GetStaticMethodID(booleanClass, "valueOf", "(Z)Ljava/lang/Boolean;");
+ booleanValue = env->GetMethodID(booleanClass, "booleanValue", "()Z");
+ declaredMethods = env->GetMethodID(classClass, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;");
+ methodName = env->GetMethodID(methodClass, "getName", "()Ljava/lang/String;");
+ parameterTypes = env->GetMethodID(methodClass, "getParameterTypes", "()[Ljava/lang/Class;");
+
+ // Lookup Tuscany classes and methods
+ loaderClass = env->FindClass("org/apache/tuscany/ClassLoader");
+ loaderValueOf = env->GetStaticMethodID(loaderClass, "valueOf", "(Ljava/lang/String;)Ljava/lang/ClassLoader;");
+ loaderForName = env->GetStaticMethodID(loaderClass, "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
+ invokerClass = env->FindClass("org/apache/tuscany/InvocationHandler");
+ invokerValueOf = env->GetStaticMethodID(invokerClass, "valueOf", "(Ljava/lang/Class;J)Ljava/lang/Object;");
+ invokerStackTrace = env->GetStaticMethodID(invokerClass, "stackTrace", "(Ljava/lang/Throwable;)Ljava/lang/String;");
+ invokerLambda = env->GetFieldID(invokerClass, "lambda", "J");
+ iterableUtilClass = env->FindClass("org/apache/tuscany/IterableUtil");
+ iterableValueOf = env->GetStaticMethodID(iterableUtilClass, "list", "([Ljava/lang/Object;)Ljava/lang/Iterable;");
+ iterableIsNil = env->GetStaticMethodID(iterableUtilClass, "isNil", "(Ljava/lang/Object;)Z");
+ iterableCar = env->GetStaticMethodID(iterableUtilClass, "car", "(Ljava/lang/Object;)Ljava/lang/Object;");
+ iterableCdr = env->GetStaticMethodID(iterableUtilClass, "cdr", "(Ljava/lang/Object;)Ljava/lang/Iterable;");
+ uuidClass = env->FindClass("org/apache/tuscany/UUIDUtil");
+
+ // Register our native invocation handler function
+ JNINativeMethod invokenm;
+ invokenm.name = const_cast<char*>("invoke");
+ invokenm.signature = const_cast<char*>("(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");
+ invokenm.fnPtr = (void*)nativeInvoke;
+ env->RegisterNatives(invokerClass, &invokenm, 1);
+
+ // Register our native UUID function
+ JNINativeMethod uuidnm;
+ uuidnm.name = const_cast<char*>("uuid");
+ uuidnm.signature = const_cast<char*>("()Ljava/lang/String;");
+ uuidnm.fnPtr = (void*)nativeUUID;
+ env->RegisterNatives(uuidClass, &uuidnm, 1);
+ }
+
+ ~JavaRuntime() {
+ }
+
+ JavaVM* jvm;
+ JNIEnv* env;
+
+ jclass classClass;
+ jclass methodClass;
+ jclass objectClass;
+ jclass doubleClass;
+ jclass booleanClass;
+ jclass stringClass;
+ jclass objectArrayClass;
+ jclass iterableClass;
+ jmethodID doubleValueOf;
+ jmethodID doubleValue;
+ jmethodID booleanValueOf;
+ jmethodID booleanValue;
+ jmethodID declaredMethods;
+ jmethodID methodName;
+ jmethodID parameterTypes;
+ jmethodID classForName;
+ jclass loaderClass;
+ jmethodID loaderValueOf;
+ jmethodID loaderForName;
+ jclass invokerClass;
+ jmethodID invokerValueOf;
+ jmethodID invokerStackTrace;
+ jfieldID invokerLambda;
+ jclass iterableUtilClass;
+ jmethodID iterableValueOf;
+ jmethodID iterableCar;
+ jmethodID iterableCdr;
+ jmethodID iterableIsNil;
+ jclass uuidClass;
+};
+
+/**
+ * Return the last exception that occurred in a JVM.
+ */
+string lastException(const JavaRuntime& jr) {
+ if (!jr.env->ExceptionCheck())
+ return "No Exception";
+ const jthrowable ex = jr.env->ExceptionOccurred();
+ const jstring trace = (jstring)jr.env->CallStaticObjectMethod(jr.invokerClass, jr.invokerStackTrace, ex);
+ const char* c = jr.env->GetStringUTFChars(trace, NULL);
+ const string msg(c);
+ jr.env->ReleaseStringUTFChars(trace, c);
+ jr.env->ExceptionClear();
+ return msg;
+}
+
+/**
+ * Declare conversion functions.
+ */
+const jobject valueToJobject(const JavaRuntime& jr, const value& jtype, const value& v);
+const value jobjectToValue(const JavaRuntime& jr, const jobject o);
+const jobjectArray valuesToJarray(const JavaRuntime& jr, const list<value>& v);
+const list<value> jarrayToValues(const JavaRuntime& jr, const jobjectArray o);
+const list<value> jiterableToValues(const JavaRuntime& jr, const jobject o);
+
+/**
+ * Convert a Java class name to a JNI class name.
+ */
+const bool jniClassNameHelper(char* to, const char* from) {
+ if (*from == '\0') {
+ *to = '\0';
+ return true;
+ }
+ *to = *from == '.'? '/' : *from;
+ return jniClassNameHelper(to + 1, from + 1);
+}
+
+const string jniClassName(const string& from) {
+ char buf[length(from) + 1];
+ jniClassNameHelper(buf, c_str(from));
+ return string(buf);
+}
+
+/**
+ * Create a new Java object representing a lambda expression.
+ */
+class javaLambda {
+public:
+ javaLambda(const JavaRuntime& jr, const value& iface, const lambda<value(const list<value>&)>& func) : jr(jr), iface(iface), func(func) {
+ }
+
+ const value operator()(const list<value>& expr) const {
+ if (isNil(expr))
+ return func(expr);
+ const value& op(car(expr));
+ if (op == "equals")
+ return value(cadr(expr) == this);
+ if (op == "hashCode")
+ return value((double)(long)this);
+ if (op == "toString") {
+ ostringstream os;
+ os << this;
+ return value(string("org.apache.tuscany.InvocationHandler@") + (c_str(str(os)) + 2));
+ }
+ return func(expr);
+ }
+
+ const JavaRuntime& jr;
+ const value iface;
+ const lambda<value(const list<value>&)> func;
+};
+
+/**
+ * Native implementation of the InvocationHandler.invoke Java method.
+ * Dispatches the call to the lambda function wrapped in the invocation handler.
+ */
+jobject JNICALL nativeInvoke(JNIEnv* env, jobject self, unused jobject proxy, jobject method, jobjectArray args) {
+
+ // Retrieve the lambda function from the invocation handler
+ jclass clazz = env->GetObjectClass(self);
+ jfieldID f = env->GetFieldID(clazz, "lambda", "J");
+ const javaLambda& jl = *(javaLambda*)(long)env->GetLongField(self, f);
+
+ // Retrieve the function name
+ const jstring s = (jstring)env->CallObjectMethod(method, jl.jr.methodName);
+ const char* c = env->GetStringUTFChars(s, NULL);
+ const value func(c);
+ env->ReleaseStringUTFChars(s, c);
+
+ // Build the expression to evaluate, either (func, args[0], args[1], args[2]...)
+ // or just args[0] for the special eval(...) function
+ const list<value> expr = func == "eval"? (list<value>)car<value>(jarrayToValues(jl.jr, args)) : cons<value>(func, jarrayToValues(jl.jr, args));
+ debug(expr, "java::nativeInvoke::expr");
+
+ // Invoke the lambda function
+ value result = jl(expr);
+ debug(result, "java::nativeInvoke::result");
+
+ // Convert result to a jobject
+ return valueToJobject(jl.jr, value(), result);
+}
+
+/**
+ * Native implementation of IterableUtil.uuid. We are providing a native implementation
+ * of this function as java.util.UUID seems to behave differently with different JDKs.
+ */
+jobject JNICALL nativeUUID(JNIEnv* env) {
+ const value uuid = mkuuid();
+ return env->NewStringUTF(c_str(uuid));
+}
+
+/**
+ * Convert a lambda function to Java proxy.
+ */
+const jobject mkJavaLambda(const JavaRuntime& jr, unused const value& iface, const lambda<value(const list<value>&)>& l) {
+ const gc_ptr<javaLambda> jl = new (gc_new<javaLambda>()) javaLambda(jr, iface, l);
+ jclass jc = (jclass)(long)(double)iface;
+ const jobject obj = jr.env->CallStaticObjectMethod(jr.invokerClass, jr.invokerValueOf, jc, (long)(javaLambda*)jl);
+ return obj;
+}
+
+/**
+ * Convert a list of values to a Java jobjectArray.
+ */
+const jobjectArray valuesToJarrayHelper(const JavaRuntime& jr, jobjectArray a, const list<value>& v, const int i) {
+ if (isNil(v))
+ return a;
+ jr.env->SetObjectArrayElement(a, i, valueToJobject(jr, value(), car(v)));
+ return valuesToJarrayHelper(jr, a, cdr(v), i + 1);
+}
+
+const jobjectArray valuesToJarray(const JavaRuntime& jr, const list<value>& v) {
+ jobjectArray a = jr.env->NewObjectArray((jsize)length(v), jr.objectClass, NULL);
+ return valuesToJarrayHelper(jr, a, v, 0);
+}
+
+/**
+ * Convert a Java jobjectArray to a Java iterable.
+ */
+const jobject jarrayToJiterable(const JavaRuntime& jr, jobjectArray a) {
+ return jr.env->CallStaticObjectMethod(jr.iterableClass, jr.iterableValueOf, a);
+}
+
+/**
+ * Convert a value to a Java jobject.
+ */
+const jobject valueToJobject(const JavaRuntime& jr, const value& jtype, const value& v) {
+ switch (type(v)) {
+ case value::List:
+ return jarrayToJiterable(jr, valuesToJarray(jr, v));
+ case value::Lambda:
+ return mkJavaLambda(jr, jtype, v);
+ case value::Symbol:
+ return jr.env->NewStringUTF(c_str(string("'") + v));
+ case value::String:
+ return jr.env->NewStringUTF(c_str(v));
+ case value::Number:
+ return jr.env->CallStaticObjectMethod(jr.doubleClass, jr.doubleValueOf, (double)v);
+ case value::Bool:
+ return jr.env->CallStaticObjectMethod(jr.booleanClass, jr.booleanValueOf, (bool)v);
+ default:
+ return NULL;
+ }
+}
+
+/**
+ * Convert a list of values to an array of jvalues.
+ */
+const jvalue* valuesToJvaluesHelper(const JavaRuntime& jr, jvalue* a, const list<value>& types, const list<value>& v) {
+ if (isNil(v))
+ return a;
+ a->l = valueToJobject(jr, car(types), car(v));
+ return valuesToJvaluesHelper(jr, a + 1, cdr(types), cdr(v));
+}
+
+const jvalue* valuesToJvalues(const JavaRuntime& jr, const list<value>& types, const list<value>& v) {
+ const size_t n = length(v);
+ jvalue* a = new (gc_anew<jvalue>(n)) jvalue[n];
+ valuesToJvaluesHelper(jr, a, types, v);
+ return a;
+}
+
+/**
+ * Convert a Java jobjectArray to a list of values.
+ */
+const list<value> jarrayToValuesHelper(const JavaRuntime& jr, jobjectArray a, const int i, const int size) {
+ if (i == size)
+ return list<value>();
+ return cons(jobjectToValue(jr, jr.env->GetObjectArrayElement(a, i)), jarrayToValuesHelper(jr, a, i + 1, size));
+}
+
+const list<value> jarrayToValues(const JavaRuntime& jr, jobjectArray o) {
+ if (o == NULL)
+ return list<value>();
+ return jarrayToValuesHelper(jr, o, 0, jr.env->GetArrayLength(o));
+}
+
+/**
+ * Convert a Java Iterable to a list of values.
+ */
+const list<value> jiterableToValuesHelper(const JavaRuntime& jr, jobject o) {
+ if ((bool)jr.env->CallStaticBooleanMethod(jr.iterableUtilClass, jr.iterableIsNil, o))
+ return list<value>();
+ jobject car = jr.env->CallStaticObjectMethod(jr.iterableUtilClass, jr.iterableCar, o);
+ jobject cdr = jr.env->CallStaticObjectMethod(jr.iterableUtilClass, jr.iterableCdr, o);
+ return cons(jobjectToValue(jr, car), jiterableToValuesHelper(jr, cdr));
+}
+
+const list<value> jiterableToValues(const JavaRuntime& jr, jobject o) {
+ if (o == NULL)
+ return list<value>();
+ return jiterableToValuesHelper(jr, o);
+}
+
+/**
+ * Lambda function used to represent a Java callable object.
+ */
+struct javaCallable {
+ const JavaRuntime& jr;
+ const jobject obj;
+
+ javaCallable(const JavaRuntime& jr, const jobject obj) : jr(jr), obj(obj) {
+ }
+
+ const value operator()(const list<value>& args) const {
+ jobjectArray jargs = valuesToJarray(jr, args);
+ jobject result = jargs; //CallObject(func, jargs);
+ return jobjectToValue(jr, result);
+ }
+};
+
+/**
+ * Convert a Java jobject to a value.
+ */
+const value jobjectToValue(const JavaRuntime& jr, const jobject o) {
+ if (o == NULL)
+ return value();
+ const jclass clazz = jr.env->GetObjectClass(o);
+ if ((jr.env->IsSameObject(clazz, jr.stringClass))) {
+ const char* s = jr.env->GetStringUTFChars((jstring)o, NULL);
+ if (*s == '\'') {
+ const value v(s + 1);
+ jr.env->ReleaseStringUTFChars((jstring)o, s);
+ return v;
+ }
+ const value v = string(s);
+ jr.env->ReleaseStringUTFChars((jstring)o, s);
+ return v;
+ }
+ if (jr.env->IsSameObject(clazz, jr.booleanClass))
+ return value((bool)jr.env->CallBooleanMethod(o, jr.booleanValue));
+ if (jr.env->IsSameObject(clazz, jr.doubleClass))
+ return value((double)jr.env->CallDoubleMethod(o, jr.doubleValue));
+ if (jr.env->IsAssignableFrom(clazz, jr.iterableClass))
+ return jiterableToValues(jr, o);
+ if (jr.env->IsAssignableFrom(clazz, jr.objectArrayClass))
+ return jarrayToValues(jr, (jobjectArray)o);
+ return lambda<value(const list<value>&)>(javaCallable(jr, o));
+}
+
+/**
+ * Returns a balanced tree of the methods of a class.
+ */
+const value parameterTypeToValue(const jobject t) {
+ return value((double)(long)t);
+}
+
+const list<value> parameterTypesToValues(const JavaRuntime& jr, const jobjectArray t, const int i) {
+ if (i == 0)
+ return list<value>();
+ return cons<value>(parameterTypeToValue(jr.env->GetObjectArrayElement(t, i - 1)), parameterTypesToValues(jr, t, i - 1));
+}
+
+const value methodToValue(const JavaRuntime& jr, const jobject m) {
+ const jobject s = jr.env->CallObjectMethod(m, jr.methodName);
+ const char* c = jr.env->GetStringUTFChars((jstring)s, NULL);
+ const string& name = string(c);
+ jr.env->ReleaseStringUTFChars((jstring)s, c);
+
+ const jmethodID mid = jr.env->FromReflectedMethod(m);
+
+ const jobjectArray t = (jobjectArray)jr.env->CallObjectMethod(m, jr.parameterTypes);
+ const list<value> types = reverse(parameterTypesToValues(jr, t, jr.env->GetArrayLength(t)));
+
+ return cons<value>(c_str(name), cons<value>((double)(long)mid, types));
+}
+
+const list<value> methodsToValues(const JavaRuntime& jr, const jobjectArray m, const int i) {
+ if (i == 0)
+ return list<value>();
+ return cons<value>(methodToValue(jr, jr.env->GetObjectArrayElement(m, i - 1)), methodsToValues(jr, m, i - 1));
+}
+
+const list<value> methodsToValues(const JavaRuntime& jr, const jclass clazz) {
+ const jobjectArray m = (jobjectArray)jr.env->CallObjectMethod(clazz, jr.declaredMethods);
+ return methodsToValues(jr, m, jr.env->GetArrayLength(m));
+}
+
+/**
+ * Represents a Java Class.
+ */
+class JavaClass {
+public:
+ JavaClass() : loader(NULL), clazz(NULL), obj(NULL) {
+ }
+ JavaClass(const jobject loader, const jclass clazz, const jobject obj, const list<value> m) : loader(loader), clazz(clazz), obj(obj), m(m) {
+ }
+
+ const jobject loader;
+ const jclass clazz;
+ const jobject obj;
+ const list<value> m;
+};
+
+/**
+ * Read a class.
+ */
+const failable<JavaClass> readClass(const JavaRuntime& jr, const string& path, const string& name) {
+
+ // Create a class loader from the given path
+ const jobject jpath = jr.env->NewStringUTF(c_str(path));
+ jobject loader = jr.env->CallStaticObjectMethod(jr.loaderClass, jr.loaderValueOf, jpath);
+
+ // Load the class
+ const jobject jname = jr.env->NewStringUTF(c_str(name));
+ const jclass clazz = (jclass)jr.env->CallStaticObjectMethod(jr.loaderClass, jr.loaderForName, jname, JNI_TRUE, loader);
+ if (clazz == NULL)
+ return mkfailure<JavaClass>(string("Couldn't load class: ") + name + " : " + lastException(jr));
+
+ // Create an instance
+ const jmethodID constr = jr.env->GetMethodID(clazz, "<init>", "()V");
+ if (constr == NULL)
+ return mkfailure<JavaClass>(string("Couldn't find constructor: ") + name + " : " + lastException(jr));
+ const jobject obj = jr.env->NewObject(clazz, constr);
+ if (obj == NULL)
+ return mkfailure<JavaClass>(string("Couldn't construct object: ") + name + " : " + lastException(jr));
+
+ return JavaClass(loader, clazz, obj, methodsToValues(jr, clazz));
+}
+
+/**
+ * Evaluate an expression against a Java class.
+ */
+const failable<value> evalClass(const JavaRuntime& jr, const value& expr, const JavaClass jc) {
+ debug(expr, "java::evalClass::expr");
+
+ // Lookup the Java function named as the expression operand
+ const list<value> func = assoc<value>(car<value>(expr), jc.m);
+ if (isNil(func)) {
+
+ // The start, stop, and restart functions are optional
+ const value fn = car<value>(expr);
+ if (fn == "start" || fn == "stop")
+ return value(lambda<value(const list<value>&)>());
+
+ return mkfailure<value>(string("Couldn't find function: ") + car<value>(expr) + " : " + lastException(jr));
+ }
+ const jmethodID fid = (jmethodID)(long)(double)cadr(func);
+
+ // Convert args to Java jvalues
+ const jvalue* args = valuesToJvalues(jr, cddr(func), cdr<value>(expr));
+
+ // Call the Java function
+ const jobject result = jr.env->CallObjectMethodA(jc.obj, fid, const_cast<jvalue*>(args));
+ if (result == NULL)
+ return mkfailure<value>(string("Function call failed: ") + car<value>(expr) + " : " + lastException(jr));
+
+ // Convert Java result to a value
+ const value v = jobjectToValue(jr, result);
+ debug(v, "java::evalClass::result");
+ return v;
+}
+
+}
+}
+#endif /* tuscany_java_eval_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/java-conf b/sca-cpp/branches/lightweight-sca/modules/java/java-conf
new file mode 100755
index 0000000000..baa5c059c2
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/java-conf
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Generate a Java server conf
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+uname=`uname -s`
+if [ $uname = "Darwin" ]; then
+ libsuffix=".dylib"
+else
+ libsuffix=".so"
+fi
+
+cat >>$root/conf/modules.conf <<EOF
+# Generated by: java-conf $*
+# Support for Java SCA components
+LoadModule mod_tuscany_eval $here/libmod_tuscany_java${libsuffix}
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/java-shell.cpp b/sca-cpp/branches/lightweight-sca/modules/java/java-shell.cpp
new file mode 100644
index 0000000000..51df513990
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/java-shell.cpp
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Java evaluator shell, used for interactive testing of Java classes.
+ */
+
+#include <assert.h>
+#include "gc.hpp"
+#include "stream.hpp"
+#include "string.hpp"
+#include "driver.hpp"
+
+int main(const int argc, char** argv) {
+ tuscany::gc_scoped_pool pool;
+ if (argc != 2) {
+ tuscany::cerr << "Usage: java-shell <class name>" << tuscany::endl;
+ return 1;
+ }
+ tuscany::java::evalDriverRun(argv[1], tuscany::cin, tuscany::cout);
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/java-test.cpp b/sca-cpp/branches/lightweight-sca/modules/java/java-test.cpp
new file mode 100644
index 0000000000..f811a4f58d
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/java-test.cpp
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test Java evaluator.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "driver.hpp"
+
+namespace tuscany {
+namespace java {
+
+bool testEvalExpr() {
+ gc_scoped_pool pool;
+ JavaRuntime javaRuntime;
+ {
+ const failable<JavaClass> obj = readClass(javaRuntime, ".", "test.CalcImpl");
+ assert(hasContent(obj));
+ const value exp = mklist<value>("mult", 2, 3);
+ const failable<value> r = evalClass(javaRuntime, exp, content(obj));
+ assert(hasContent(r));
+ assert(content(r) == value(6));
+ }
+ {
+ const failable<JavaClass> obj = readClass(javaRuntime, ".", "test.CalcImpl");
+ assert(hasContent(obj));
+ const value exp = mklist<value>("even", 2);
+ const failable<value> r = evalClass(javaRuntime, exp, content(obj));
+ assert(hasContent(r));
+ assert(content(r) == value(true));
+ }
+ {
+ const failable<JavaClass> obj = readClass(javaRuntime, ".", "test.AdderImpl");
+ assert(hasContent(obj));
+ const value exp = mklist<value>("add", 2, 3);
+ const failable<value> r = evalClass(javaRuntime, exp, content(obj));
+ assert(hasContent(r));
+ assert(content(r) == value(5));
+ }
+ {
+ const failable<JavaClass> obj = readClass(javaRuntime, ".", "test.CalcImpl");
+ assert(hasContent(obj));
+ const value exp = mklist<value>("square", mklist<value>(1, 2, 3));
+ const failable<value> r = evalClass(javaRuntime, exp, content(obj));
+ assert(hasContent(r));
+ assert(content(r) == mklist<value>(1, 4, 9));
+ }
+ return true;
+}
+
+const value add(const list<value>& args) {
+ assert(car(args) == "add");
+ const double x = cadr(args);
+ const double y = caddr(args);
+ return x + y;
+}
+
+bool testEvalLambda() {
+ gc_scoped_pool pool;
+ JavaRuntime javaRuntime;
+ {
+ const failable<JavaClass> obj = readClass(javaRuntime, ".", "test.CalcImpl");
+ assert(hasContent(obj));
+ const value tcel = mklist<value>("add", 3, 4, lambda<value(const list<value>&)>(add));
+ const failable<value> r = evalClass(javaRuntime, tcel, content(obj));
+ assert(hasContent(r));
+ assert(content(r) == value(7));
+ }
+ {
+ const failable<JavaClass> obj = readClass(javaRuntime, ".", "test.CalcImpl");
+ assert(hasContent(obj));
+ const value tcel = mklist<value>("addEval", 3, 4, lambda<value(const list<value>&)>(add));
+ const failable<value> r = evalClass(javaRuntime, tcel, content(obj));
+ assert(hasContent(r));
+ assert(content(r) == value(7));
+ }
+ return true;
+}
+
+bool testClassLoader() {
+ gc_scoped_pool pool;
+ JavaRuntime javaRuntime;
+ const failable<JavaClass> obj = readClass(javaRuntime, ".", "org.apache.tuscany.ClassLoader$Test");
+ assert(hasContent(obj));
+ const value exp = mklist<value>("testClassLoader");
+ const failable<value> r = evalClass(javaRuntime, exp, content(obj));
+ assert(hasContent(r));
+ assert(content(r) == value(true));
+ return true;
+}
+
+bool testIterableUtil() {
+ gc_scoped_pool pool;
+ JavaRuntime javaRuntime;
+ const failable<JavaClass> obj = readClass(javaRuntime, ".", "org.apache.tuscany.IterableUtil$Test");
+ assert(hasContent(obj));
+ const value exp = mklist<value>("testList");
+ const failable<value> r = evalClass(javaRuntime, exp, content(obj));
+ assert(hasContent(r));
+ assert(content(r) == value(true));
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::java::testEvalExpr();
+ tuscany::java::testEvalLambda();
+ tuscany::java::testClassLoader();
+ tuscany::java::testIterableUtil();
+
+ tuscany::cout << "OK" << tuscany::endl;
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/jni-test.cpp b/sca-cpp/branches/lightweight-sca/modules/java/jni-test.cpp
new file mode 100644
index 0000000000..727af13dc6
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/jni-test.cpp
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Basic JNI test.
+ */
+
+#include <assert.h>
+#include <jni.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "fstream.hpp"
+#include "eval.hpp"
+
+namespace tuscany {
+namespace java {
+
+#ifdef JAVA_HARMONY_VM
+#define JNI_VERSION JNI_VERSION_1_4
+#else
+#define JNI_VERSION JNI_VERSION_1_6
+#endif
+
+bool testJNI() {
+ gc_scoped_pool pool;
+ JavaVM* jvm;
+ JNIEnv* env;
+
+ JavaVMInitArgs args;
+ args.version = JNI_VERSION;
+ args.ignoreUnrecognized = JNI_FALSE;
+ JavaVMOption options[3];
+ args.options = options;
+ args.nOptions = 0;
+ const char* envcp = getenv("CLASSPATH");
+ const string cp = string("-Djava.class.path=") + (envcp == NULL? "." : envcp);
+ options[args.nOptions].optionString = const_cast<char*>(c_str(cp));
+ options[args.nOptions++].extraInfo = NULL;
+#ifdef JAVA_HARMONY_VM
+ JNI_CreateJavaVM(&jvm, &env, &args);
+#else
+ JNI_CreateJavaVM(&jvm, (void**)&env, &args);
+#endif
+
+ jclass classClass = env->FindClass("java/lang/Class");
+ assert(classClass != NULL);
+ jclass loaderClass = env->FindClass("org/apache/tuscany/ClassLoader");
+ assert(loaderClass != NULL);
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::java::testJNI();
+
+ tuscany::cout << "OK" << tuscany::endl;
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/mod-java.cpp b/sca-cpp/branches/lightweight-sca/modules/java/mod-java.cpp
new file mode 100644
index 0000000000..d96861cc6d
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/mod-java.cpp
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * HTTPD module used to eval Java component implementations.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "../server/mod-cpp.hpp"
+#include "../server/mod-eval.hpp"
+#include "mod-java.hpp"
+
+namespace tuscany {
+namespace server {
+namespace modeval {
+
+/**
+ * Apply a lifecycle start or restart event.
+ */
+struct javaLifecycle {
+ javaLifecycle(java::JavaRuntime& jr) : jr(jr) {
+ }
+ const value operator()(const list<value>& params) const {
+ const value func = car(params);
+ if (func == "javaRuntime")
+ return (gc_ptr<value>)(value*)(void*)&jr;
+ return lambda<value(const list<value>&)>();
+ }
+ java::JavaRuntime& jr;
+};
+
+const value applyLifecycle(unused const list<value>& params) {
+
+ // Create a Java runtime
+ java::JavaRuntime& jr = *(new (gc_new<java::JavaRuntime>()) java::JavaRuntime());
+
+ // Return the function to invoke on subsequent events
+ return failable<value>(lambda<value(const list<value>&)>(javaLifecycle(jr)));
+}
+
+/**
+ * Evaluate a Java component implementation and convert it to an applicable
+ * lambda function.
+ */
+const failable<lambda<value(const list<value>&)> > evalImplementation(const string& path, const value& impl, const list<value>& px, const lambda<value(const list<value>&)>& lifecycle) {
+ const string itype(elementName(impl));
+ if (contains(itype, ".java")) {
+ const void* p = (gc_ptr<value>)lifecycle(mklist<value>("javaRuntime"));
+ return modjava::evalImplementation(path, impl, px, *(java::JavaRuntime*)p);
+ }
+ if (contains(itype, ".cpp"))
+ return modcpp::evalImplementation(path, impl, px);
+ if (contains(itype, ".widget"))
+ return mkfailure<lambda<value(const list<value>&)> >(string("Unsupported implementation type: ") + itype, -1, false);
+ return mkfailure<lambda<value(const list<value>&)> >(string("Unsupported implementation type: ") + itype);
+}
+
+}
+}
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/mod-java.hpp b/sca-cpp/branches/lightweight-sca/modules/java/mod-java.hpp
new file mode 100644
index 0000000000..b68f17aa3f
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/mod-java.hpp
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+#ifndef tuscany_modjava_hpp
+#define tuscany_modjava_hpp
+
+/**
+ * Evaluation functions used by mod-eval to evaluate Java
+ * component implementations.
+ */
+
+#include "string.hpp"
+#include "stream.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "eval.hpp"
+
+namespace tuscany {
+namespace server {
+namespace modjava {
+
+/**
+ * Apply a Java component implementation function.
+ */
+struct applyImplementation {
+ java::JavaClass impl;
+ const list<value> px;
+ java::JavaRuntime& jr;
+ applyImplementation(const java::JavaClass& impl, const list<value>& px, java::JavaRuntime& jr) : impl(impl), px(px), jr(jr) {
+ }
+ const value operator()(const list<value>& params) const {
+ const value expr = append<value>(params, px);
+ debug(expr, "modeval::java::applyImplementation::input");
+ const failable<value> res = java::evalClass(jr, expr, impl);
+ const value val = !hasContent(res)? mklist<value>(value(), reason(res), rcode(res)) : mklist<value>(content(res));
+ debug(val, "modeval::java::applyImplementation::result");
+ return val;
+ }
+};
+
+/**
+ * Evaluate a Java component implementation and convert it to an applicable
+ * lambda function.
+ */
+const failable<lambda<value(const list<value>&)> > evalImplementation(const string& path, const value& impl, const list<value>& px, java::JavaRuntime& jr) {
+ const string cn(attributeValue("class", impl));
+ const failable<java::JavaClass> jc = java::readClass(jr, path, cn);
+ if (!hasContent(jc))
+ return mkfailure<lambda<value(const list<value>&)> >(jc);
+ return lambda<value(const list<value>&)>(applyImplementation(content(jc), px, jr));
+}
+
+}
+}
+}
+
+#endif /* tuscany_modjava_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/ClassLoader.java b/sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/ClassLoader.java
new file mode 100644
index 0000000000..ef7b2316fb
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/ClassLoader.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+/**
+ * Class loader used to load SCA component implementation classes.
+ */
+class ClassLoader extends URLClassLoader {
+
+ ClassLoader(final URL... urls) {
+ super(urls);
+ }
+
+ /**
+ * Create a class loader for an SCA contribution path.
+ */
+ static java.lang.ClassLoader valueOf(final String path) throws MalformedURLException {
+ return new ClassLoader(new File(path).toURI().toURL());
+ }
+
+ /**
+ * Load a class.
+ */
+ static Class<?> forName(final String name, final boolean resolve, final java.lang.ClassLoader loader) throws ClassNotFoundException {
+ return Class.forName(name, resolve, loader);
+ }
+
+ /**
+ * Test the class loader.
+ */
+ static class Test {
+ Boolean testClassLoader() {
+ try {
+ final Class<?> clazz = ClassLoader.forName("test.CalcImpl", true, ClassLoader.valueOf("."));
+ assert clazz != null;
+ } catch(final MalformedURLException e) {
+ throw new RuntimeException(e);
+ } catch(final ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ return true;
+ }
+ }
+
+ public static void main(final String[] args) {
+ System.out.println("Testing...");
+
+ Test.class.getClassLoader().setDefaultAssertionStatus(true);
+ new Test().testClassLoader();
+
+ System.out.println("OK");
+ }
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/InvocationHandler.java b/sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/InvocationHandler.java
new file mode 100644
index 0000000000..06466fe9fc
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/InvocationHandler.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/**
+ * Proxy Invocation handler used to represent SCA component references.
+ */
+class InvocationHandler implements java.lang.reflect.InvocationHandler {
+ final long lambda;
+
+ InvocationHandler(final long lambda) {
+ this.lambda = lambda;
+ }
+
+ /**
+ * Create a proxy for an interface and the lambda function representing
+ * an SCA component reference.
+ */
+ static Object valueOf(final Class<?> iface, final long lambda) {
+ return Proxy.newProxyInstance(iface.getClassLoader(), new Class[]{iface}, new InvocationHandler(lambda));
+ }
+
+ /**
+ * Proxy invocation of a C++ function.
+ */
+ public native Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable;
+
+ /**
+ * Return the stack trace of an exception.
+ */
+ static String stackTrace(final Throwable e) {
+ final StringWriter sw = new StringWriter();
+ final PrintWriter pw = new PrintWriter(sw);
+ e.printStackTrace(pw);
+ return sw.toString();
+ }
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/IterableUtil.java b/sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/IterableUtil.java
new file mode 100644
index 0000000000..2366d79af6
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/IterableUtil.java
@@ -0,0 +1,398 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany;
+
+import static java.util.Arrays.*;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Utility functions to help work efficiently with iterable lists, inspired from Lisp.
+ */
+public class IterableUtil {
+
+ /**
+ * Convert an array or a variable list of arguments to an iterable list.
+ */
+ public static <T> Iterable<T> list(final Object... a) {
+ return new ArrayIterable<T>(a, 0);
+ }
+
+ /**
+ * Convert an iterable list to a java.util.Collection.
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> Collection<T> collection(final Object l) {
+ final Collection<T> c = new ArrayList<T>();
+ for(final Object x : (Iterable<?>)l)
+ c.add((T)x);
+ return c;
+ }
+
+ /**
+ * Construct a new list from an element and a list.
+ */
+ public static <T> Iterable<T> cons(final Object car, final Iterable<?> cdr) {
+ return new PairIterable<T>(car, cdr);
+ }
+
+ /**
+ * Return true if a list is nil (empty).
+ */
+ public static boolean isNil(final Object l) {
+ if(l instanceof BasicIterable<?>)
+ return ((BasicIterable<?>)l).isNil();
+ if(l instanceof Collection<?>)
+ return ((Collection<?>)l).isEmpty();
+ return !((Iterable<?>)l).iterator().hasNext();
+ }
+
+ /**
+ * Return the car (first element) of a list.
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> T car(final Object l) {
+ if(l instanceof BasicIterable<?>)
+ return ((BasicIterable<T>)l).car();
+ if(l instanceof List<?>)
+ return (T)((List<?>)l).get(0);
+ return (T)((Iterable<?>)l).iterator().next();
+ }
+
+ /**
+ * Return the cdr (rest after the first element) of a list.
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> Iterable<T> cdr(final Object l) {
+ if(l instanceof BasicIterable<?>)
+ return ((BasicIterable<T>)l).cdr();
+ if(l instanceof List<?>)
+ return new ListIterable<T>((List<?>)l, 1);
+ if(l instanceof Collection<?>)
+ return new ArrayIterable<T>(((Collection<?>)l).toArray(), 1);
+ return new Iterable<T>() {
+ public Iterator<T> iterator() {
+ final Iterator<T> i = ((Iterable<T>)l).iterator();
+ i.next();
+ return i;
+ }
+ };
+ }
+
+ /**
+ * Return the car of the cdr of a list.
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> T cadr(final Object l) {
+ return (T)car(cdr(l));
+ }
+
+ /**
+ * Return the cdr of the cdr of a list.
+ */
+ public static <T> Iterable<T> cddr(final Object l) {
+ return cdr(cdr(l));
+ }
+
+ /**
+ * Return the cdr of the cdr of the cdr of a list.
+ */
+ public static <T> Iterable<T> cdddr(final Object l) {
+ return cdr(cdr(cdr(l)));
+ }
+
+ /**
+ * Return the car of the cdr of the cdr of a list.
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> T caddr(final Object l) {
+ return (T)car(cddr(l));
+ }
+
+ /**
+ * Return the car of the cdr of the cdr of the cdr of a list.
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> T cadddr(final Object l) {
+ return (T)car(cdddr(l));
+ }
+
+ /**
+ * Appends a list and another list.
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> Iterable<T> append(final Object a, final Object b) {
+ if (isNil(a))
+ return (Iterable<T>)b;
+ return cons(car(a), append(cdr(a), b));
+ }
+
+ /**
+ * Return the first pair matching a key from a list of key value pairs.
+ */
+ public static <T> Iterable<T> assoc(final Object k, final Object l) {
+ if(isNil(l))
+ return list();
+ if(k.equals(car(car(l))))
+ return car(l);
+ return assoc(k, cdr(l));
+ }
+
+ /**
+ * Internal base implementation class for iterable and immutable lists.
+ */
+ static abstract class BasicIterable<T> extends AbstractList<T> {
+ abstract T car();
+
+ abstract Iterable<T> cdr();
+
+ abstract Boolean isNil();
+
+ @Override
+ public int size() {
+ return this.isNil()? 0 : 1 + ((List<T>)this.cdr()).size();
+ }
+
+ @Override
+ public T get(final int index) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Internal implementation of a list backed by an array.
+ */
+ static class ArrayIterable<T> extends BasicIterable<T> {
+ final Object[] a;
+ final int start;
+
+ ArrayIterable(final Object[] a, final int start) {
+ this.a = a;
+ this.start = start;
+ }
+
+ @Override
+ Boolean isNil() {
+ return this.a.length - this.start == 0;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ T car() {
+ return (T)this.a[this.start];
+ }
+
+ @Override
+ BasicIterable<T> cdr() {
+ return new ArrayIterable<T>(this.a, this.start + 1);
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ return new Iterator<T>() {
+ int i = ArrayIterable.this.start;
+
+ public boolean hasNext() {
+ return this.i < ArrayIterable.this.a.length;
+ }
+
+ @SuppressWarnings("unchecked")
+ public T next() {
+ return (T)ArrayIterable.this.a[this.i++];
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+ }
+
+ /**
+ * Internal implementation of a list backed by a java.util.List.
+ */
+ static class ListIterable<T> extends BasicIterable<T> {
+ final List<?> l;
+ final int start;
+
+ ListIterable(final List<?> l, final int start) {
+ this.l = l;
+ this.start = start;
+ }
+
+ @Override
+ Boolean isNil() {
+ return this.l.size() - this.start == 0;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ T car() {
+ return (T)this.l.get(this.start);
+ }
+
+ @Override
+ BasicIterable<T> cdr() {
+ return new ListIterable<T>(this.l, this.start + 1);
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ return new Iterator<T>() {
+ int i = ListIterable.this.start;
+
+ public boolean hasNext() {
+ return this.i < ListIterable.this.l.size();
+ }
+
+ @SuppressWarnings("unchecked")
+ public T next() {
+ return (T)ListIterable.this.l.get(this.i++);
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+ }
+
+ /**
+ * Internal implementation of a list backed by an element / iterable pair.
+ */
+ static class PairIterable<T> extends BasicIterable<T> {
+ final Object car;
+ final Iterable<?> cdr;
+
+ PairIterable(final Object car, final Iterable<?> cdr) {
+ this.car = car;
+ this.cdr = cdr;
+ }
+
+ @Override
+ Boolean isNil() {
+ return false;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ T car() {
+ return (T)this.car;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ Iterable<T> cdr() {
+ return (Iterable<T>)this.cdr;
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ return new Iterator<T>() {
+ boolean carIterator = true;
+ Iterator<?> cdrIterator = PairIterable.this.cdr.iterator();
+
+ public boolean hasNext() {
+ if(this.carIterator)
+ return true;
+ return this.cdrIterator.hasNext();
+ }
+
+ @SuppressWarnings("unchecked")
+ public T next() {
+ if(this.carIterator) {
+ this.carIterator = false;
+ return (T)PairIterable.this.car;
+ }
+ return (T)this.cdrIterator.next();
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+ }
+
+ /**
+ * Test the list functions.
+ */
+ static class Test {
+ Boolean testList() {
+ final Iterable<Object> l = list(2, 3, 4);
+ assert car(l) == Integer.valueOf(2);
+ assert cadr(l) == Integer.valueOf(3);
+ assert caddr(l) == Integer.valueOf(4);
+
+ final Iterable<Object> c = cons(0, cons(1, l));
+ assert car(c) == Integer.valueOf(0);
+ assert cadr(c) == Integer.valueOf(1);
+ assert caddr(c) == Integer.valueOf(2);
+ assert c.toString().equals("[0, 1, 2, 3, 4]");
+
+ final Iterable<Object> cl = cons(0, cons(1, new ArrayList<Object>(asList(2, 3, 4))));
+ assert car(cl) == Integer.valueOf(0);
+ assert cadr(cl) == Integer.valueOf(1);
+ assert caddr(cl) == Integer.valueOf(2);
+ assert cl.toString().equals("[0, 1, 2, 3, 4]");
+
+ final List<Object> jl = new ArrayList<Object>(collection(cl));
+ assert jl.size() == 5;
+ assert jl.get(0) == Integer.valueOf(0);
+ assert jl.get(1) == Integer.valueOf(1);
+ assert jl.get(2) == Integer.valueOf(2);
+
+ final Iterable<Object> n = list();
+ assert isNil(n);
+ assert n.toString().equals("[]");
+
+ final Iterable<Object> cn = cons(0, n);
+ assert !isNil(cn);
+ assert isNil(cdr(cn));
+ assert cn.toString().equals("[0]");
+
+ final Iterable<Object> al = new ArrayList<Object>(Arrays.asList(1, 2, 3));
+ assert car(al) == Integer.valueOf(1);
+ assert cadr(al) == Integer.valueOf(2);
+ assert caddr(al) == Integer.valueOf(3);
+
+ final Iterable<Object> a = list(0, 1, 2);
+ final Iterable<Object> b = list(3, 4);
+ final Iterable<Object> ab = append(a, b);
+ assert ab.toString().equals("[0, 1, 2, 3, 4]");
+ return true;
+ }
+ }
+
+ public static void main(final String[] args) {
+ System.out.println("Testing...");
+
+ Test.class.getClassLoader().setDefaultAssertionStatus(true);
+ new Test().testList();
+
+ System.out.println("OK");
+ }
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/Service.java b/sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/Service.java
new file mode 100644
index 0000000000..a00d5b1b53
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/Service.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany;
+
+/**
+ * Interface used to represent SCA component references providing both REST
+ * access to a resource and function application.
+ */
+public interface Service {
+
+ /**
+ * Post a new item to a collection of items.
+ */
+ Iterable<String> post(Iterable<String> collection, Iterable<?> item);
+
+ /**
+ * Return an item.
+ */
+ Iterable<?> get(Iterable<String> id);
+
+ /**
+ * Update an item.
+ */
+ boolean put(Iterable<String> id, Iterable<?> item);
+
+ /**
+ * Delete an item.
+ */
+ boolean delete(Iterable<String> id);
+
+ /**
+ * Evaluate an expression.
+ */
+ <T> T eval(Object... params);
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/UUIDUtil.java b/sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/UUIDUtil.java
new file mode 100644
index 0000000000..60076c62ca
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/org/apache/tuscany/UUIDUtil.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany;
+
+/**
+ * A fast and portable UUID generator function.
+ */
+public class UUIDUtil {
+
+ /**
+ * Return a UUID.
+ */
+ public static native String uuid();
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/server-test b/sca-cpp/branches/lightweight-sca/modules/java/server-test
new file mode 100755
index 0000000000..0b45649ace
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/server-test
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Setup
+rm -rf tmp
+../http/httpd-conf tmp localhost 8090 ../server/htdocs
+../server/server-conf tmp
+./java-conf tmp
+cat >>tmp/conf/httpd.conf <<EOF
+SCAContribution `pwd`/
+SCAComposite domain-test.composite
+EOF
+
+export CLASSPATH="`pwd`/libmod-tuscany-java-1.0.jar:`pwd`"
+
+../http/httpd-start tmp
+sleep 2
+
+# Test
+./client-test 2>/dev/null
+rc=$?
+
+# Cleanup
+../http/httpd-stop tmp
+sleep 2
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/test/Adder.java b/sca-cpp/branches/lightweight-sca/modules/java/test/Adder.java
new file mode 100644
index 0000000000..7236548c41
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/test/Adder.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package test;
+
+public interface Adder {
+
+ Double add(Double x, Double y);
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/test/AdderImpl.java b/sca-cpp/branches/lightweight-sca/modules/java/test/AdderImpl.java
new file mode 100644
index 0000000000..e607012b78
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/test/AdderImpl.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package test;
+
+public class AdderImpl {
+
+ public Double add(Double x, Double y) {
+ return x + y;
+ }
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/test/CalcImpl.java b/sca-cpp/branches/lightweight-sca/modules/java/test/CalcImpl.java
new file mode 100644
index 0000000000..5bea01a43f
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/test/CalcImpl.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.tuscany.Service;
+
+public class CalcImpl {
+
+ public Double add(final Double x, final Double y, final Adder adder) {
+ return adder.add(x, y);
+ }
+
+ public Double addEval(final Double x, final Double y, final Service adder) {
+ return adder.eval("add", x, y);
+ }
+
+ public Double mult(final Double x, final Double y) {
+ return x * y;
+ }
+
+ public Boolean even(final Double x) {
+ return (double)((int)(double)x / 2 * 2) == (double)x;
+ }
+
+ public Iterable<Double> square(final Iterable<Double> l) {
+ final List<Double> r = new ArrayList<Double>();
+ for(final Double x : l)
+ r.add(x * x);
+ return r;
+ }
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/test/Client.java b/sca-cpp/branches/lightweight-sca/modules/java/test/Client.java
new file mode 100644
index 0000000000..c3bd875fcc
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/test/Client.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package test;
+
+public interface Client {
+
+ String echo(String x);
+
+ Iterable<?> get(Iterable<String> id);
+
+ Iterable<String> post(Iterable<String> collection, Iterable<?> item);
+
+ Boolean put(Iterable<String> id, Iterable<?> item);
+
+ Boolean delete(Iterable<String> id);
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/test/ClientImpl.java b/sca-cpp/branches/lightweight-sca/modules/java/test/ClientImpl.java
new file mode 100644
index 0000000000..ade2ba302e
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/test/ClientImpl.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package test;
+
+public class ClientImpl {
+
+ public String echo(String x, Server server) {
+ return server.echo(x);
+ }
+
+ public Iterable<?> get(Iterable<String> id, Server server) {
+ return server.get(id);
+ }
+
+ public Iterable<String> post(Iterable<String> collection, Iterable<?> item, Server server) {
+ return server.post(collection, item);
+ }
+
+ public Boolean put(Iterable<String> id, Iterable<?> item, Server server) {
+ return server.put(id, item);
+ }
+
+ public Boolean delete(Iterable<String> id, Server server) {
+ return server.delete(id);
+ }
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/test/Server.java b/sca-cpp/branches/lightweight-sca/modules/java/test/Server.java
new file mode 100644
index 0000000000..3dfe3c84ef
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/test/Server.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package test;
+
+public interface Server {
+
+ String echo(String x);
+
+ Iterable<?> get(Iterable<String> id);
+
+ Iterable<String> post(Iterable<String> collection, Iterable<?> item);
+
+ Boolean put(Iterable<String> id, Iterable<?> item);
+
+ Boolean delete(Iterable<String> id);
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/test/ServerImpl.java b/sca-cpp/branches/lightweight-sca/modules/java/test/ServerImpl.java
new file mode 100644
index 0000000000..ee25cf7bf8
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/test/ServerImpl.java
@@ -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.
+ */
+
+package test;
+
+import static org.apache.tuscany.IterableUtil.*;
+
+public class ServerImpl {
+
+ public String echo(final String x) {
+ return x;
+ }
+
+ public Iterable<?> get(final Iterable<String> id) {
+ if (isNil(id))
+ return list(list("'feed", list("'title", "Sample Feed"), list("'id", "123456789"), list("'entry", list(
+ list(list("'title", "Item"), list("'id", "111"),
+ list("'content", list("'item", list("'name", "Apple"), list("'currencyCode", "USD"), list("'currencySymbol", "$"), list("'price", 2.99)))),
+ list(list("'title", "Item"), list("'id", "222"),
+ list("'content", list("'item", list("'name", "Orange"), list("'currencyCode", "USD"), list("'currencySymbol", "$"), list("'price", 3.55)))),
+ list(list("'title", "Item"), list("'id", "333"),
+ list("'content", list("'item", list("'name", "Pear"), list("'currencyCode", "USD"), list("'currencySymbol", "$"), list("'price", 1.55))))))));
+
+ final Iterable<?> content = list("'content", list("'item", list("'name", "Apple"), list("'currencyCode", "USD"), list("'currencySymbol", "$"), list("'price", 2.99)));
+ return list(list("'entry", list("'title", "Item"), list("'id", car(id)), content));
+ }
+
+ public Iterable<String> post(final Iterable<String> collection, final Iterable<?> item) {
+ return list("123456789");
+ }
+
+ public Boolean put(final Iterable<String> id, final Iterable<?> item) {
+ return true;
+ }
+
+ public Boolean delete(final Iterable<String> id) {
+ return true;
+ }
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/java/wiring-test b/sca-cpp/branches/lightweight-sca/modules/java/wiring-test
new file mode 100755
index 0000000000..dd865c4c66
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/java/wiring-test
@@ -0,0 +1,81 @@
+#!/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=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+curl_prefix=`cat $here/../http/curl.prefix`
+
+# Setup
+rm -rf tmp
+../http/httpd-conf tmp localhost 8090 ../server/htdocs
+../server/server-conf tmp
+./java-conf tmp
+cat >>tmp/conf/httpd.conf <<EOF
+SCAContribution `pwd`/
+SCAComposite domain-test.composite
+EOF
+
+export CLASSPATH="`pwd`/libmod-tuscany-java-1.0.jar:`pwd`"
+
+../http/httpd-start tmp
+sleep 2
+
+# Test HTTP GET
+$curl_prefix/bin/curl http://localhost:8090/index.html 2>/dev/null >tmp/index.html
+diff tmp/index.html ../server/htdocs/index.html
+rc=$?
+
+# Test ATOMPub
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl http://localhost:8090/client/ >tmp/feed.xml 2>/dev/null
+ diff tmp/feed.xml ../server/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 ../server/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 @../server/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 @../server/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 @../server/htdocs/test/json-request.txt >tmp/json-result.txt 2>/dev/null
+ diff tmp/json-result.txt ../server/htdocs/test/json-result.txt
+ rc=$?
+fi
+
+# Cleanup
+../http/httpd-stop tmp
+sleep 2
+if [ "$rc" = "0" ]; then
+ echo "OK"
+fi
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/modules/js/Makefile.am b/sca-cpp/branches/lightweight-sca/modules/js/Makefile.am
new file mode 100644
index 0000000000..38627678a1
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/js/Makefile.am
@@ -0,0 +1,56 @@
+# 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.
+
+incl_HEADERS = *.hpp
+incldir = $(prefix)/include/modules/js
+
+jsfiles = htdocs/util.js htdocs/elemutil.js htdocs/xmlutil.js htdocs/atomutil.js htdocs/jsonutil.js htdocs/scdl.js htdocs/ui.js htdocs/component.js
+
+BUILT_SOURCES = htdocs/all.js
+htdocs/all.js: ${jsfiles}
+ cat $^ >htdocs/all.js
+
+minified = htdocs/all-min.js htdocs/ui-min.css
+
+SUFFIXES = -min.html -min.js -min.css
+.html-min.html:
+ ../../modules/http/minify-html $< $@
+
+.js-min.js:
+ ../../modules/http/minify-js $< $@
+
+.css-min.css:
+ ../../modules/http/minify-css $< $@
+
+CLEANFILES = htdocs/all.js ${minified}
+
+dist_mod_SCRIPTS = js-conf
+moddir = $(prefix)/modules/js
+nobase_dist_mod_DATA = ${minified}
+EXTRA_DIST = ${jsfiles} htdocs/ui.css
+
+js_test_SOURCES = js-test.cpp
+js_test_LDFLAGS = -lmozjs
+
+js_shell_SOURCES = js-shell.cpp
+js_shell_LDFLAGS = -lmozjs
+
+noinst_PROGRAMS = js-test
+mod_PROGRAMS = js-shell
+dist_noinst_SCRIPTS = util-test
+TESTS = js-test util-test
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/js/eval.hpp b/sca-cpp/branches/lightweight-sca/modules/js/eval.hpp
new file mode 100644
index 0000000000..f8f4cbe598
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/js/eval.hpp
@@ -0,0 +1,365 @@
+/*
+ * 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_js_hpp
+#define tuscany_js_hpp
+
+/**
+ * Javascript evaluation functions.
+ */
+
+#define XP_UNIX
+#ifdef WANT_MAINTAINER_WARNINGS
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#pragma GCC diagnostic ignored "-Wsign-compare"
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#endif
+#include <jsapi.h>
+#ifdef WANT_MAINTAINER_WARNINGS
+#pragma GCC diagnostic warning "-Wunused-parameter"
+#pragma GCC diagnostic warning "-Wsign-compare"
+#pragma GCC diagnostic warning "-Wredundant-decls"
+#endif
+#include "string.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "element.hpp"
+#include "monad.hpp"
+#include "parallel.hpp"
+
+namespace tuscany {
+namespace js {
+
+/**
+ * Report Javascript errors.
+ */
+void reportError(unused ::JSContext *cx, const char *message, JSErrorReport *report) {
+ cfailure << (const char*)(report->filename? report->filename : "<no filename>") << ":"
+ << (int)report->lineno << ":" << message << endl;
+}
+
+/**
+ * Encapsulates a JavaScript runtime. Shared by multiple threads in
+ * a process.
+ */
+class JSRuntime {
+public:
+ JSRuntime() {
+ // Create JS runtime
+ debug("js::jsruntime");
+ rt = JS_NewRuntime(1L * 512L * 1024L);
+ if(rt == NULL)
+ cleanup();
+ }
+
+ operator ::JSRuntime*() const {
+ return rt;
+ }
+
+ ~JSRuntime() {
+ debug("js::~jsruntime");
+ }
+
+private:
+ bool cleanup() {
+ if(rt != NULL) {
+ JS_DestroyRuntime(rt);
+ rt = NULL;
+ }
+ JS_ShutDown();
+ return true;
+ }
+
+ ::JSRuntime* rt;
+} jsRuntime;
+
+JSClass jsGlobalClass = { "global", JSCLASS_GLOBAL_FLAGS,
+ JS_PropertyStub, JS_PropertyStub,
+ JS_PropertyStub, JS_StrictPropertyStub,
+ JS_EnumerateStub, JS_ResolveStub,
+ JS_ConvertStub, JS_FinalizeStub,
+ JSCLASS_NO_OPTIONAL_MEMBERS };
+
+/**
+ * Represents a JavaScript context. Maintains one context per thread.
+ */
+#ifdef WANT_THREADS
+perthread_ptr<JSContext> jsContext;
+#else
+::JSContext* jsContext = NULL;
+#endif
+
+class JSContext {
+public:
+ JSContext() {
+ // Create JS context if necessary
+ debug("js::jscontext");
+ if (jsContext != NULL) {
+ cx = jsContext;
+ JS_BeginRequest(cx);
+ return;
+ }
+ debug("js::jsnewcontext");
+ cx = JS_NewContext(jsRuntime, 8192);
+ if(cx == NULL)
+ return;
+ JS_BeginRequest(cx);
+
+ JS_SetOptions(cx, JSOPTION_VAROBJFIX | JSOPTION_JIT | JSOPTION_METHODJIT);
+ JS_SetVersion(cx, JSVERSION_LATEST);
+ JS_SetErrorReporter(cx, reportError);
+ //JS_SetGCZeal(cx, 2);
+
+ // Create global JS object
+ global = JS_NewCompartmentAndGlobalObject(cx, &jsGlobalClass, NULL);
+ if(global == NULL) {
+ cleanup();
+ return;
+ }
+
+ // Populate global object with the standard globals, like Object and Array
+ if(!JS_InitStandardClasses(cx, global)) {
+ cleanup();
+ return;
+ }
+ jsContext = cx;
+ }
+
+ ~JSContext() {
+ debug("js::~jscontext");
+ cleanup();
+ }
+
+ operator ::JSContext*() const {
+ return cx;
+ }
+
+ JSObject* getGlobal() const {
+ return global;
+ }
+
+private:
+ bool cleanup() {
+ if(cx != NULL) {
+ JS_MaybeGC(cx);
+ JS_EndRequest(cx);
+ if (cx != jsContext) {
+ debug("js::jsdestroycontext");
+ JS_DestroyContext(cx);
+ }
+ cx = NULL;
+ }
+ return true;
+ }
+
+ ::JSContext* cx;
+ JSObject* global;
+};
+
+/**
+ * Returns true if a list represents a JS array.
+ */
+const bool isJSArray(const list<value>& l) {
+ if(isNil(l))
+ return true;
+ const value v = car(l);
+ if (isSymbol(v))
+ return false;
+ if(isList(v)) {
+ if(!isNil((list<value>)v) && isSymbol(car<value>(v)))
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Converts JS properties to values.
+ */
+const list<value> jsPropertiesToValues(const list<value>& propertiesSoFar, JSObject* o, JSObject* i, const js::JSContext& cx) {
+
+ const value jsValToValue(const jsval& jsv, const js::JSContext& cx);
+
+ jsid id;
+ if(!JS_NextProperty(cx, i, &id))
+ return propertiesSoFar;
+ jsval idv;
+ JS_IdToValue(cx, id, &idv);
+ if (idv == JSVAL_VOID)
+ return propertiesSoFar;
+
+ jsval jsv;
+ if(!JS_GetPropertyById(cx, o, id, &jsv))
+ return propertiesSoFar;
+ const value val = jsValToValue(jsv, cx);
+
+ if(JSVAL_IS_STRING(idv)) {
+ char* cname = JS_EncodeString(cx, JSVAL_TO_STRING(idv));
+ const string name = cname;
+ JS_free(cx, cname);
+ if (isNil(val) && !isList(val))
+ return jsPropertiesToValues(cons<value> (mklist<value> (element, c_str(name), val), propertiesSoFar), o, i, cx);
+ //return jsPropertiesToValues(propertiesSoFar, o, i, cx);
+ if (substr(name, 0, 1) == atsign)
+ return jsPropertiesToValues(cons<value>(mklist<value>(attribute, c_str(substr(name, 1)), val), propertiesSoFar), o, i, cx);
+ if (isList(val) && !isJSArray(val))
+ return jsPropertiesToValues(cons<value>(cons<value>(element, cons<value>(c_str(name), list<value>(val))), propertiesSoFar), o, i, cx);
+ return jsPropertiesToValues(cons<value> (mklist<value> (element, c_str(name), val), propertiesSoFar), o, i, cx);
+ }
+ return jsPropertiesToValues(cons(val, propertiesSoFar), o, i, cx);
+}
+
+/**
+ * Converts a JS val to a value.
+ */
+const value jsValToValue(const jsval& jsv, const js::JSContext& cx) {
+ switch(JS_TypeOfValue(cx, jsv)) {
+ case JSTYPE_STRING: {
+ char* cvalue = JS_EncodeString(cx, JSVAL_TO_STRING(jsv));
+ const string svalue = string(cvalue);
+ JS_free(cx, cvalue);
+ return value(svalue);
+ }
+ case JSTYPE_BOOLEAN: {
+ return value((bool)JSVAL_TO_BOOLEAN(jsv));
+ }
+ case JSTYPE_NUMBER: {
+ jsdouble jsd;
+ JS_ValueToNumber(cx, jsv, &jsd);
+ return value((double)jsd);
+ }
+ case JSTYPE_OBJECT: {
+ JSObject* o = JSVAL_TO_OBJECT(jsv);
+ if (o == NULL)
+ return value();
+ JSObject* i = JS_NewPropertyIterator(cx, o);
+ if(i == NULL)
+ return value(list<value> ());
+ const value pv = jsPropertiesToValues(list<value> (), o, i, cx);
+ return pv;
+ }
+ default: {
+ return value();
+ }
+ }
+}
+
+/**
+ * Converts a list of values to JS array elements.
+ */
+JSObject* valuesToJSElements(JSObject* a, const list<value>& l, int i, const js::JSContext& cx) {
+ const jsval valueToJSVal(const value& val, const js::JSContext& cx);
+ if (isNil(l))
+ return a;
+ jsval pv = valueToJSVal(car(l), cx);
+ JS_SetElement(cx, a, i, &pv);
+ return valuesToJSElements(a, cdr(l), ++i, cx);
+}
+
+/**
+ * Converts a value to a JS val.
+ */
+const jsval valueToJSVal(const value& val, const js::JSContext& cx) {
+ JSObject* valuesToJSProperties(JSObject* o, const list<value>& l, const js::JSContext& cx);
+
+ switch(type(val)) {
+ case value::String: {
+ return STRING_TO_JSVAL(JS_NewStringCopyZ(cx, c_str((string)val)));
+ }
+ case value::Symbol: {
+ return STRING_TO_JSVAL(JS_NewStringCopyZ(cx, c_str((string)val)));
+ }
+ case value::Bool: {
+ return BOOLEAN_TO_JSVAL((bool)val);
+ }
+ case value::Number: {
+ jsval jsv;
+ if (!JS_NewNumberValue(cx, (jsdouble)val, &jsv))
+ return DOUBLE_TO_JSVAL(0);
+ return jsv;
+ }
+ case value::List: {
+ if (isJSArray(val))
+ return OBJECT_TO_JSVAL(valuesToJSElements(JS_NewArrayObject(cx, 0, NULL), val, 0, cx));
+ return OBJECT_TO_JSVAL(valuesToJSProperties(JS_NewObject(cx, NULL, NULL, NULL), val, cx));
+ }
+ case value::Nil: {
+ return JSVAL_NULL;
+ }
+ default: {
+ return JSVAL_VOID;
+ }
+ }
+}
+
+/**
+ * Converts a list of values to JS properties.
+ */
+JSObject* valuesToJSProperties(JSObject* o, const list<value>& l, const js::JSContext& cx) {
+ if (isNil(l))
+ return o;
+
+ // Write an attribute
+ const value token(car(l));
+
+ if (isTaggedList(token, attribute)) {
+ jsval pv = valueToJSVal(attributeValue(token), cx);
+ JS_SetProperty(cx, o, c_str(atsign + string(attributeName(token))), &pv);
+
+ } else if (isTaggedList(token, element)) {
+
+ // Write the value of an element
+ if (elementHasValue(token)) {
+ jsval pv = valueToJSVal(elementValue(token), cx);
+ JS_SetProperty(cx, o, c_str(string(elementName(token))), &pv);
+
+ } else {
+
+ // Write a parent element
+ JSObject* child = JS_NewObject(cx, NULL, NULL, NULL);
+ jsval pv = OBJECT_TO_JSVAL(child);
+ JS_SetProperty(cx, o, c_str(string(elementName(token))), &pv);
+
+ // Write its children
+ valuesToJSProperties(child, elementChildren(token), cx);
+ }
+ }
+
+ // Go on
+ return valuesToJSProperties(o, cdr(l), cx);
+}
+
+/**
+ * Evaluate a script provided as a string.
+ */
+const failable<value> evalScript(const string& s) {
+ js::JSContext cx;
+ jsval rval;
+ JSBool rc = JS_EvaluateScript(cx, cx.getGlobal(), c_str(s), (uintN)length(s), "eval.js", 1, &rval);
+ if (rc != JS_TRUE) {
+ return mkfailure<value>("Couldn't evaluate Javascript script.");
+ }
+ return jsValToValue(rval, cx);
+}
+
+}
+}
+
+#endif /* tuscany_js_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/modules/js/htdocs/atomutil.js b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/atomutil.js
new file mode 100644
index 0000000000..068b5de2fd
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/atomutil.js
@@ -0,0 +1,184 @@
+/*
+ * 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.
+ */
+
+/**
+ * ATOM data conversion functions.
+ */
+var atom = {};
+
+/**
+ * Convert a list of elements to a list of values representing an ATOM entry.
+ */
+atom.entryElementValues = function(e) {
+ var lt = filter(selector(mklist(element, "'title")), e);
+ var t = mklist(element, "'title", isNil(lt)? '' : elementValue(car(lt)));
+
+ var li = filter(selector(mklist(element, "'id")), e);
+ var i = mklist(element, "'id", isNil(li)? '' : elementValue(car(li)));
+
+ var la = filter(selector(mklist(element, "'author")), e);
+ var lan = isNil(la)? mklist() : filter(selector(mklist(element, "'name")), car(la));
+ var lae = isNil(la)? mklist() : filter(selector(mklist(element, "'email")), car(la));
+ var laa = isNil(lan)? lae : lan;
+ var a = isNil(laa)? mklist() : mklist(mklist(element, "'author", elementValue(car(laa))));
+
+ var lu = filter(selector(mklist(element, "'updated")), e);
+ var u = isNil(lu)? mklist() : mklist(mklist(element, "'updated", elementValue(car(lu))));
+
+ var lc = filter(selector(mklist(element, "'content")), e);
+ var c = isNil(lc)? mklist() : mklist(mklist(element, "'content", elementValue(car(lc))));
+
+ return append(append(append(mklist(element, "'entry", t, i), a), u), c);
+};
+
+/**
+ * Convert a list of elements to a list of values representing ATOM entries.
+ */
+atom.entriesElementValues = function(e) {
+ if (isNil(e))
+ return e;
+ return cons(atom.entryElementValues(car(e)), atom.entriesElementValues(cdr(e)));
+};
+
+/**
+ * Return true if a list of strings represents an ATOM entry.
+ */
+atom.isATOMEntry = function(l) {
+ if (!isXML(l))
+ return false;
+ return car(l).match('<entry') != null && car(l).match('<feed') == null && car(l).match('="http://www.w3.org/2005/Atom"') != null;
+};
+
+/**
+ * Convert a DOM Document to a list of values representing an ATOM entry.
+ */
+atom.readATOMEntryDocument = function(doc) {
+ var e = readXMLDocument(doc);
+ if (isNil(e))
+ return mklist();
+ return mklist(atom.entryElementValues(car(e)));
+};
+
+/**
+ * Convert a list of strings to a list of values representing an ATOM entry.
+ */
+atom.readATOMEntry = function(l) {
+ return atom.readATOMEntryDocument(parseXML(l));
+};
+
+/**
+ * Return true if a list of strings represents an ATOM feed.
+ */
+atom.isATOMFeed = function(l) {
+ if (!isXML(l))
+ return false;
+ return car(l).match('<feed') != null && car(l).match('="http://www.w3.org/2005/Atom"') != null;
+};
+
+/**
+ * Convert a DOM document to a list of values representing an ATOM feed.
+ */
+atom.readATOMFeedDocument = function(doc) {
+ var f = readXMLDocument(doc);
+ if (isNil(f))
+ return mklist();
+ var t = filter(selector(mklist(element, "'title")), car(f));
+ var i = filter(selector(mklist(element, "'id")), car(f));
+ var e = filter(selector(mklist(element, "'entry")), car(f));
+ return mklist(append(
+ mklist(element, "'feed", mklist(element, "'title", elementValue(car(t))), mklist(element, "'id", elementValue(car(i)))),
+ atom.entriesElementValues(e)));
+};
+
+/**
+ * Convert a list of strings to a list of values representing an ATOM feed.
+ */
+atom.readATOMFeed = function(l) {
+ return atom.readATOMFeedDocument(parseXML(l));
+};
+
+/**
+ * Convert a list of values representy an ATOM entry to a list of elements.
+ */
+atom.entryElement = function(l) {
+ var title = elementValue(namedElementChild("'title", l));
+ var id = elementValue(namedElementChild("'id", l));
+ var author = namedElementChild("'author", l);
+ var email = isNil(author)? false : (elementValue(author).indexOf('@') != -1);
+ var updated = namedElementChild("'updated", l);
+ var content = namedElementChild("'content", l);
+ var text = isNil(content)? false : elementHasValue(content);
+ return append(append(append(append(
+ mklist(element, "'entry", mklist(attribute, "'xmlns", "http://www.w3.org/2005/Atom"),
+ mklist(element, "'title", mklist(attribute, "'type", "text"), title), mklist(element, "'id", id)),
+ isNil(author)? mklist() : mklist(element, "'author",
+ (email? mklist(element, "'email", elementValue(author)) : mklist(element, "'name", elementValue(author))))),
+ isNil(updated)? mklist() : mklist(element, "'updated", elementValue(updated))),
+ isNil(content)? mklist() :
+ mklist(append(mklist(element, "'content", mklist(attribute, "'type", text? "text" : "application/xml")),
+ text? mklist(elementValue(content)) : elementChildren(content)))),
+ mklist(mklist(element, "'link", mklist(attribute, "'href", id))));
+};
+
+/**
+ * Convert a list of values representing ATOM entries to a list of elements.
+ */
+atom.entriesElements = function(l) {
+ if (isNil(l))
+ return l;
+ return cons(atom.entryElement(car(l)), atom.entriesElements(cdr(l)));
+};
+
+/**
+ * Convert a list of values representing an ATOM entry to an ATOM entry.
+ */
+atom.writeATOMEntry = function(ll) {
+ var l = isNil(ll)? ll : car(ll);
+ return writeXML(mklist(atom.entryElement(l)), true);
+};
+
+/**
+ * Convert a list of values representing an ATOM feed to an ATOM feed.
+ */
+atom.writeATOMFeed = function(ll) {
+ var l = isNil(ll)? ll : car(ll);
+ var lt = filter(selector(mklist(element, "'title")), l);
+ var t = isNil(lt)? '' : elementValue(car(lt));
+ var li = filter(selector(mklist(element, "'id")), l);
+ var i = isNil(li)? '' : elementValue(car(li));
+ var f = mklist(element, "'feed", mklist(attribute, "'xmlns", "http://www.w3.org/2005/Atom"),
+ mklist(element, "'title", mklist(attribute, "'type", "text"), car(l)),
+ mklist(element, "'id", cadr(l)));
+
+ // Write ATOM entries
+ var le = filter(selector(mklist(element, "'entry")), l);
+ if (isNil(le))
+ return writeXML(mklist(f), true);
+
+ // Write a single ATOM entry element with a list of values
+ if (!isNil(le) && !isNil(car(le)) && isList(car(caddr(car(le))))) {
+ var fe = append(f, atom.entriesElements(caddr(car(le))));
+ return writeXML(mklist(fe), true);
+ }
+
+ // Write separate ATOM entry elements
+ var fe = append(f, atom.entriesElements(le));
+ return writeXML(mklist(fe), true);
+};
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/js/htdocs/component.js b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/component.js
new file mode 100644
index 0000000000..c3799ef708
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/component.js
@@ -0,0 +1,637 @@
+/*
+ * 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").
+ */
+
+/**
+ * Client component wiring API, supporting JSON and ATOM bindings.
+ */
+
+var JSONClient = {};
+
+/**
+ * Escape a character.
+ */
+JSONClient.escapeJSONChar = function(c) {
+ if(c == "\"" || c == "\\") return "\\" + c;
+ if (c == "\b") return "\\b";
+ if (c == "\f") return "\\f";
+ if (c == "\n") return "\\n";
+ if (c == "\r") return "\\r";
+ if (c == "\t") return "\\t";
+ var hex = c.charCodeAt(0).toString(16);
+ if(hex.length == 1) return "\\u000" + hex;
+ if(hex.length == 2) return "\\u00" + hex;
+ if(hex.length == 3) return "\\u0" + hex;
+ 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";
+ if(o.constructor == String)
+ return JSONClient.escapeJSONString(o);
+ if(o.constructor == Number)
+ return o.toString();
+ if(o.constructor == Boolean)
+ return o.toString();
+ if(o.constructor == Date)
+ return '{javaClass: "java.util.Date", time: ' + o.valueOf() +'}';
+ if(o.constructor == Array) {
+ var v = [];
+ for(var i = 0; i < o.length; i++)
+ v.push(JSONClient.toJSON(o[i]));
+ return "[" + v.join(", ") + "]";
+ }
+ 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(", ") + "}";
+};
+
+/**
+ * Construct an HTTPBindingClient.
+ */
+function HTTPBindingClient(name, uri, domain) {
+ this.name = name;
+ this.domain = domain;
+ this.uri = uri;
+ this.apply = this.createApplyMethod();
+}
+
+/**
+ * JSON-RPC request counter.
+ */
+HTTPBindingClient.jsonrpcID = 1;
+
+/**
+ * HTTPBindingClient implementation
+ */
+
+/**
+ * Generate client proxy apply method.
+ */
+HTTPBindingClient.prototype.createApplyMethod = function() {
+ var fn = function() {
+ var methodName = arguments[0];
+ var args = [];
+ for(var i = 1; i < arguments.length; i++)
+ args.push(arguments[i]);
+
+ var cb = null;
+ if (typeof args[args.length - 1] == "function")
+ cb = args.pop();
+
+ var req = HTTPBindingClient.makeJSONRequest(methodName, args, cb);
+ return fn.client.jsonApply(req);
+ };
+ fn.client = this;
+ return fn;
+};
+
+/**
+ * Make a JSON-RPC request.
+ */
+HTTPBindingClient.makeJSONRequest = function(methodName, args, cb) {
+ var req = {};
+ req.id = HTTPBindingClient.jsonrpcID++;
+ if (cb)
+ req.cb = cb;
+ var obj = {};
+ obj.id = req.id;
+ obj.method = methodName;
+ obj.params = args;
+ req.data = JSONClient.toJSON(obj);
+ return req;
+};
+
+/**
+ * Return the JSON result from an XMLHttpRequest.
+ */
+HTTPBindingClient.jsonResult = function(http) {
+ // Get the charset
+ function httpCharset(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";
+ }
+ if(!HTTPBindingClient.charset)
+ HTTPBindingClient.charset = httpCharset(http);
+
+ // Unmarshall the JSON response
+ var obj;
+ eval("obj = " + http.responseText);
+ if(obj.error)
+ throw new HTTPBindingClient.Exception(obj.error.code, obj.error.msg);
+ var res = obj.result;
+ return res;
+};
+
+/**
+ * Apply a function remotely using JSON-RPC.
+ */
+HTTPBindingClient.prototype.jsonApply = function(req) {
+ // Connect to the service
+ var http = HTTPBindingClient.getHTTPRequest();
+ var hascb = req.cb? true : false;
+ http.open("POST", this.uri, hascb);
+ http.setRequestHeader("Accept", "*/*");
+ http.setRequestHeader("Content-Type", "application/json-rpc");
+
+ // Construct call back if we have one
+ if(hascb) {
+ http.onreadystatechange = function() {
+ if(http.readyState == 4) {
+ // Pass the result or exception
+ if(http.status == 200) {
+ var res = null;
+ try {
+ res = HTTPBindingClient.jsonResult(http);
+ try {
+ req.cb(res);
+ } catch(cbe) {}
+ } catch(e) {
+ try {
+ req.cb(null, e);
+ } catch(cbe) {}
+ }
+ } else
+ try {
+ req.cb(null, HTTPBindingClient.Exception(http.status, http.statusText));
+ } catch(cbe) {}
+ }
+ };
+
+ // Send the request
+ http.send(req.data);
+ return req.id;
+ }
+
+ // Send the request and return the result or exception
+ http.send(req.data);
+ if (http.status == 200)
+ return HTTPBindingClient.jsonResult(http);
+ throw new HTTPBindingClient.Exception(http.status, http.statusText);
+};
+
+
+/**
+ * REST GET method.
+ */
+HTTPBindingClient.prototype.get = function(id, cb) {
+ var u = id? (this.uri? this.uri + '/' + id : id) : this.uri;
+ var hascb = cb? true : false;
+
+ // Get from local storage first
+ var ls = window.lstorage || localStorage;
+ var item = null;
+ try { item = ls.getItem(u); } catch(e) {}
+ //log('localStorage.getItem', u, item);
+ if (item != null && item != '') {
+ if (!hascb)
+ return item;
+
+ // Pass local result to callback
+ try {
+ cb(item);
+ } catch (cbe) {}
+ }
+
+ // Connect to the service
+ var http = HTTPBindingClient.getHTTPRequest();
+ http.open("GET", u, hascb);
+ http.setRequestHeader("Accept", "*/*");
+
+ // Construct call back if we have one
+ if (hascb) {
+ http.onreadystatechange = function() {
+ //log('readystate', http.readyState, 'status', http.status, 'headers', http.getAllResponseHeaders());
+ if (http.readyState == 4) {
+ // Pass result if different from local result
+ if (http.status == 200) {
+
+ if (http.getResponseHeader("X-Login") != null) {
+ // Detect redirect to a login page
+ try {
+ var le = new HTTPBindingClient.Exception(403, 'X-Login');
+ if (window.onloginredirect)
+ window.onloginredirect(le);
+ return cb(null, le);
+ } catch(cbe) {}
+
+ } else if (http.responseText == '' || http.getResponseHeader("Content-Type") == null) {
+ // Report empty response
+ try {
+ return cb(null, new HTTPBindingClient.Exception(403, 'No-Content'));
+ } catch(cbe) {}
+
+ } else {
+ if (item == null || http.responseText != item) {
+ // Store retrieved entry in local storage
+ if (http.responseText != null) {
+ //log('localStorage.setItem', u, http.responseText);
+ try { ls.setItem(u, http.responseText); } catch(e) {}
+ }
+ try {
+ return cb(http.responseText);
+ } catch(cbe) {}
+ }
+ }
+ }
+ else {
+ // Pass exception if we didn't have a local result
+ if (item == null) {
+ try {
+ return cb(null, new HTTPBindingClient.Exception(http.status, http.statusText));
+ } catch(cbe) {}
+ }
+ }
+ }
+ };
+
+ // Send the request
+ http.send(null);
+ return true;
+ }
+
+ // Send the request and return the result or exception
+ http.send(null);
+ if (http.status == 200) {
+ if (http.getResponseHeader("X-Login") != null) {
+
+ // Detect redirect to a login page
+ var le = new HTTPBindingClient.Exception(403, 'X-Login');
+ if (window.onloginredirect)
+ window.onloginredirect(le);
+ throw le;
+
+ } else if (http.responseText == '' || http.getResponseHeader("Content-Type") == null) {
+
+ // Report empty response
+ throw new HTTPBindingClient.Exception(403, 'No-Content');
+ }
+ return http.responseText;
+ }
+ throw new HTTPBindingClient.Exception(http.status, http.statusText);
+};
+
+/**
+ * REST GET method, does not use the local cache.
+ */
+HTTPBindingClient.prototype.getnocache = function(id, cb) {
+ var u = id? (this.uri? this.uri + '/' + id : id) : this.uri;
+ var hascb = cb? true : false;
+
+ // Connect to the service
+ var http = HTTPBindingClient.getHTTPRequest();
+ http.open("GET", u, hascb);
+ http.setRequestHeader("Accept", "*/*");
+
+ // Construct call back if we have one
+ if (hascb) {
+ http.onreadystatechange = function() {
+ if (http.readyState == 4) {
+ if (http.status == 200) {
+
+ if (http.getResponseHeader("X-Login") != null) {
+ // Detect redirect to a login page
+ try {
+ var le = new HTTPBindingClient.Exception(403, 'X-Login');
+ if (window.onloginredirect)
+ window.onloginredirect(le);
+ return cb(null, le);
+ } catch(cbe) {}
+
+ } else if (http.responseText == '' || http.getResponseHeader("Content-Type") == null) {
+ // Report empty response
+ try {
+ return cb(null, new HTTPBindingClient.Exception(403, 'No-Content'));
+ } catch(cbe) {}
+
+ } else {
+ try {
+ return cb(http.responseText);
+ } catch(cbe) {}
+ }
+ } else {
+ try {
+ return cb(null, new HTTPBindingClient.Exception(http.status, http.statusText));
+ } catch(cbe) {}
+ }
+ }
+ };
+
+ // Send the request
+ http.send(null);
+ return true;
+ }
+
+ // Send the request and return the result or exception
+ http.send(null);
+ if (http.status == 200) {
+ if (http.getResponseHeader("X-Login") != null) {
+
+ // Detect redirect to a login page
+ var le = new HTTPBindingClient.Exception(403, 'X-Login');
+ if (window.onloginredirect)
+ window.onloginredirect(le);
+ throw le;
+
+ } else if (http.responseText == '' || http.getResponseHeader("Content-Type") == null) {
+
+ // Report empty response
+ throw new HTTPBindingClient.Exception(403, 'No-Content');
+ }
+ return http.responseText;
+ }
+ throw new HTTPBindingClient.Exception(http.status, http.statusText);
+};
+
+/**
+ * REST POST method.
+ */
+HTTPBindingClient.prototype.post = function (entry, cb) {
+
+ // Connect to the service
+ var http = HTTPBindingClient.getHTTPRequest();
+ var hascb = cb? true : false;
+ http.open("POST", this.uri, hascb);
+ http.setRequestHeader("Accept", "*/*");
+ http.setRequestHeader("Content-Type", "application/atom+xml");
+
+ // Construct call back if we have one
+ if (hascb) {
+ http.onreadystatechange = function() {
+ // Pass the result or exception
+ if (http.readyState == 4) {
+ if (http.status == 201) {
+ try {
+ cb(http.responseText);
+ } catch(cbe) {}
+ }
+ else {
+ try {
+ cb(null, new HTTPBindingClient.Exception(http.status, http.statusText));
+ } catch(cbe) {}
+ }
+ }
+ };
+ // Send the request
+ http.send(entry);
+ return true;
+ }
+
+ // Send the request and return the result or exception
+ http.send(entry);
+ if (http.status == 201)
+ return http.responseText;
+ throw new HTTPBindingClient.Exception(http.status, http.statusText);
+};
+
+/**
+ * REST PUT method.
+ */
+HTTPBindingClient.prototype.put = function (id, entry, cb) {
+ var u = this.uri + '/' + id;
+
+ // Update local storage
+ var ls = window.lstorage || localStorage;
+ try { ls.setItem(u, entry); } catch(e) {}
+ //log('localStorage.setItem', u, entry);
+
+ // Connect to the service
+ var http = HTTPBindingClient.getHTTPRequest();
+ var hascb = cb? true : false;
+ http.open("PUT", u, hascb);
+ http.setRequestHeader("Accept", "*/*");
+ http.setRequestHeader("Content-Type", "application/atom+xml");
+
+ // Construct call back if we have one
+ if (hascb) {
+ http.onreadystatechange = function() {
+ if (http.readyState == 4) {
+ // Pass any exception
+ if (http.status == 200) {
+ try {
+ cb();
+ } catch(cbe) {}
+ } else {
+ try {
+ cb(new HTTPBindingClient.Exception(http.status, http.statusText));
+ } catch(cbe) {}
+ }
+ }
+ };
+ // Send the request
+ http.send(entry);
+ return true;
+ }
+
+ // Send the request and return any exception
+ http.send(entry);
+ if (http.status == 200)
+ return true;
+ throw new HTTPBindingClient.Exception(http.status, http.statusText);
+};
+
+/**
+ * REST DELETE method.
+ */
+HTTPBindingClient.prototype.del = function (id, cb) {
+ var u = this.uri + '/' + id;
+
+ // Update local storage
+ var ls = window.lstorage || localStorage;
+ try { ls.removeItem(u); } catch(e) {}
+ //log('localStorage.removeItem', u);
+
+ // Connect to the service
+ var http = HTTPBindingClient.getHTTPRequest();
+ var hascb = cb? true : false;
+ http.open("DELETE", u, hascb);
+ http.setRequestHeader("Accept", "*/*");
+
+ // Construct call back if we have one
+ if (cb) {
+ http.onreadystatechange = function() {
+ if (http.readyState == 4) {
+ // Pass any exception
+ if (http.status == 200) {
+ try {
+ cb();
+ } catch(cbe) {}
+ }
+ else {
+ try {
+ cb(new HTTPBindingClient.Exception(http.status, http.statusText));
+ } catch(cbe) {}
+ }
+ }
+ };
+ // Send the request
+ http.send(null);
+ return true;
+ }
+
+ // Send the request and return any exception
+ http.send(null);
+ if (http.status == 200)
+ return true;
+ throw new HTTPBindingClient.Exception(http.status, http.statusText);
+};
+
+/**
+ * HTTPBindingClient exceptions.
+ */
+HTTPBindingClient.Exception = function(code, message) {
+ this.name = "HTTPBindingClientException";
+ this.code = code;
+ this.message = message;
+};
+
+HTTPBindingClient.Exception.prototype = new Error();
+
+HTTPBindingClient.Exception.prototype.toString = function() {
+ return this.name + ": " + this.message;
+};
+
+/**
+ * XMLHttpRequest wrapper.
+ */
+HTTPBindingClient.msxmlNames = [ "MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP" ];
+
+HTTPBindingClient.getHTTPRequest = function() {
+ if (HTTPBindingClient.httpFactory)
+ return HTTPBindingClient.httpFactory();
+
+ // Mozilla XMLHttpRequest
+ try {
+ HTTPBindingClient.httpFactory = function() {
+ return new XMLHttpRequest();
+ };
+ return HTTPBindingClient.httpFactory();
+ } catch(e) {}
+
+ // Microsoft MSXML ActiveX
+ for (var i = 0; i < HTTPBindingClient.msxmlNames.length; i++) {
+ try {
+ HTTPBindingClient.httpFactory = function() {
+ return new ActiveXObject(HTTPBindingClient.msxmlNames[i]);
+ };
+ return HTTPBindingClient.httpFactory();
+ } catch (e) {}
+ }
+
+ // Can't create XMLHttpRequest
+ HTTPBindingClient.httpFactory = null;
+ throw new HTTPBindingClient.Exception(0, "Can't create XMLHttpRequest object");
+};
+
+/**
+ * Public API.
+ */
+
+var sca = {};
+
+/**
+ * Return an HTTP client proxy.
+ */
+sca.httpclient = function(name, uri, domain) {
+ return new HTTPBindingClient(name, uri, domain);
+};
+
+/**
+ * Return a component proxy.
+ */
+sca.component = function(name, domain) {
+ if (!domain)
+ return new HTTPBindingClient(name, '/c/' + name, domain);
+ return new HTTPBindingClient(name, '/' + domain + '/c/' + name, domain);
+};
+
+/**
+ * Return a reference proxy.
+ */
+sca.reference = function(comp, rname) {
+ if (!comp.domain)
+ return new HTTPBindingClient(comp.name + '/' + rname, '/r/' + comp.name + '/' + rname, comp.domain);
+ return new HTTPBindingClient(comp.name + '/' + rname, '/' + comp.domain + '/r/' + comp.name + '/' + rname, comp.domain);
+};
+
+/**
+ * Add proxy functions to a reference proxy.
+ */
+sca.defun = function(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/sca-cpp/branches/lightweight-sca/modules/js/htdocs/elemutil.js b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/elemutil.js
new file mode 100644
index 0000000000..37d641f7b3
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/elemutil.js
@@ -0,0 +1,236 @@
+/*
+ * 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.
+ */
+
+/**
+ * Functions to help represent data as lists of elements and attributes.
+ */
+
+var element = "'element"
+var attribute = "'attribute"
+var atsign = "'@"
+
+/**
+ * Return true if a value is an element.
+ */
+function isElement(v) {
+ return (!(!isList(v) || isNil(v) || car(v) != element));
+}
+
+/**
+ * Return true if a value is an attribute.
+ */
+function isAttribute(v) {
+ return (!(!isList(v) || isNil(v) || car(v) != attribute));
+}
+
+/**
+ * Return the name of an attribute.
+ */
+attributeName = cadr;
+
+/**
+ * Return the value of an attribute.
+ */
+attributeValue = caddr;
+
+/**
+ * Return the name of an element.
+ */
+elementName = cadr;
+
+/**
+ * Return true if an element has children.
+ */
+function elementHasChildren(l) {
+ return !isNil(cddr(l));
+}
+
+/**
+ * Return the children of an element.
+ */
+elementChildren = cddr;
+
+/**
+ * Return true if an element has a value.
+ */
+function elementHasValue(l) {
+ var r = reverse(l);
+ if (isSymbol(car(r)))
+ return false;
+ return (!(isList(car(r)) && !isNil(car(r)) && isSymbol(car(car(r)))))
+}
+
+/**
+ * Return the value of an element.
+ */
+function elementValue(l) {
+ return car(reverse(l));
+}
+
+/**
+ * Convert an element to a value.
+ */
+function elementToValueIsList(v) {
+ if (!isList(v))
+ return false;
+ return isNil(v) || !isSymbol(car(v));
+}
+
+function elementToValue(t) {
+ if (isTaggedList(t, attribute))
+ return mklist(atsign + attributeName(t).substring(1), attributeValue(t));
+ if (isTaggedList(t, element)) {
+ if (elementHasValue(t)) {
+ if (!elementToValueIsList(elementValue(t)))
+ return mklist(elementName(t), elementValue(t));
+ return cons(elementName(t), mklist(elementsToValues(elementValue(t))));
+ }
+ return cons(elementName(t), elementsToValues(elementChildren(t)));
+ }
+ if (!isList(t))
+ return t;
+ return elementsToValues(t);
+}
+
+/**
+ * Convert a list of elements to a list of values.
+ */
+function elementToValueIsSymbol(v) {
+ return (!(!isList(v)) || isNil(v) || !isSymbol(car(v)));
+}
+
+function elementToValueGroupValues(v, l) {
+ if (isNil(l) || !elementToValueIsSymbol(v) || !elementToValueIsSymbol(car(l)))
+ return cons(v, l);
+ if (car(car(l)) != car(v))
+ return cons(v, l);
+ if (!elementToValueIsList(cadr(car(l)))) {
+ var g = mklist(car(v), mklist(cdr(v), cdr(car(l))));
+ return elementToValueGroupValues(g, cdr(l));
+ }
+ var g = mklist(car(v), cons(cdr(v), cadr(car(l))));
+ return elementToValueGroupValues(g, cdr(l));
+}
+
+function elementsToValues(e) {
+ if (isNil(e))
+ return e;
+ return elementToValueGroupValues(elementToValue(car(e)), elementsToValues(cdr(e)));
+}
+
+/**
+ * Convert a value to an element.
+ */
+function valueToElement(t) {
+ if (isList(t) && !isNil(t) && isSymbol(car(t))) {
+ var n = car(t);
+ var v = isNil(cdr(t))? mklist() : cadr(t);
+ if (!isList(v)) {
+ if (n.substring(0, 2) == atsign)
+ return mklist(attribute, "'" + n.substring(2), v);
+ return mklist(element, n, v);
+ }
+ if (isNil(v) || !isSymbol(car(v)))
+ return cons(element, cons(n, mklist(valuesToElements(v))));
+ return cons(element, cons(n, valuesToElements(cdr(t))));
+ }
+ if (!isList(t))
+ return t;
+ return valuesToElements(t);
+}
+
+/**
+ * Convert a list of values to a list of elements.
+ */
+function valuesToElements(l) {
+ if (isNil(l))
+ return l;
+ return cons(valueToElement(car(l)), valuesToElements(cdr(l)));
+}
+
+/**
+ * Return a selector lambda function which can be used to filter elements.
+ */
+function selector(s) {
+ function evalSelect(s, v) {
+ if (isNil(s))
+ return true;
+ if (isNil(v))
+ return false;
+ if (car(s) != car(v))
+ return false;
+ return evalSelect(cdr(s), cdr(v));
+ }
+
+ return function(v) { return evalSelect(s, v); };
+}
+
+/**
+ * Return the attribute with the given name.
+ */
+function namedAttribute(name, l) {
+ return memo(l, name, function() {
+ var f = filter(function(v) { return isAttribute(v) && attributeName(v) == name; }, l);
+ if (isNil(f))
+ return null;
+ return car(f);
+ });
+}
+
+/**
+ * Return the value of the attribute with the given name.
+ */
+function namedAttributeValue(name, l) {
+ var a = namedAttribute(name, l);
+ if (a == null)
+ return null
+ return attributeValue(a);
+}
+
+/**
+ * Return child elements with the given name.
+ */
+function namedElementChildren(name, l) {
+ return memo(l, name, function() {
+ return filter(function(v) { return isElement(v) && elementName(v) == name; }, l);
+ });
+}
+
+/**
+ * Return the child element with the given name.
+ */
+function namedElementChild(name, l) {
+ var f = namedElementChildren(name, l);
+ if (isNil(f))
+ return null;
+ return car(f);
+}
+
+/**
+ * Side effect functions. Use with moderation.
+ */
+
+/**
+ * Set the contents of an element.
+ */
+function setElement(l, e) {
+ setlist(l, e);
+ l.memo = {};
+}
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/js/htdocs/jsonutil.js b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/jsonutil.js
new file mode 100644
index 0000000000..8aa291bc89
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/jsonutil.js
@@ -0,0 +1,261 @@
+/*
+ * 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 data conversion functions.
+ */
+var json = {};
+
+/**
+ * JSON exceptions.
+ */
+json.Exception = function(code, message) {
+ this.name = "JSONException";
+ this.code = code;
+ this.message = message;
+};
+
+json.Exception.prototype = new Error();
+
+json.Exception.prototype.toString = function() {
+ return this.name + ": " + this.message;
+};
+
+/**
+ * Return true if a list represents a JS array.
+ */
+json.isJSArray = function(l) {
+ if (isNil(l))
+ return true;
+ var v = car(l);
+ if (isSymbol(v))
+ return false;
+ if (isList(v))
+ if (!isNil(v) && isSymbol(car(v)))
+ return false;
+ return true;
+};
+
+/**
+ * Converts JSON properties to values.
+ */
+json.jsPropertiesToValues = function(propertiesSoFar, o, i) {
+ if (isNil(i))
+ return propertiesSoFar;
+ var p = car(i);
+ var jsv = o[p];
+ var v = json.jsValToValue(jsv);
+
+ if (typeof p == 'string') {
+ var n = '' + p;
+ if (n.slice(0, 1) == '@')
+ return json.jsPropertiesToValues(cons(mklist(attribute, "'" + n.slice(1), v), propertiesSoFar), o, cdr(i));
+ if (isList(v) && !json.isJSArray(v))
+ return json.jsPropertiesToValues(cons(cons(element, cons("'" + n, v)), propertiesSoFar), o, cdr(i));
+ return json.jsPropertiesToValues(cons(mklist(element, "'" + n, v), propertiesSoFar), o, cdr(i));
+ }
+ return json.jsPropertiesToValues(cons(v, propertiesSoFar), o, cdr(i));
+};
+
+/**
+ * Converts a JSON val to a value.
+ */
+json.jsValToValue = function(jsv) {
+ if (jsv == null)
+ return null;
+ if (isList(jsv))
+ return json.jsPropertiesToValues(mklist(), jsv, reverse(range(0, jsv.length)));
+ if (typeof jsv == 'object')
+ return json.jsPropertiesToValues(mklist(), jsv, reverse(properties(jsv)));
+ if (typeof jsv == 'string')
+ return '' + jsv;
+ return jsv;
+}
+
+/**
+ * Return true if a list of strings contains a JSON document.
+ */
+json.isJSON = function(l) {
+ if (isNil(l))
+ return false;
+ var s = car(l).slice(0, 1);
+ return s == "[" || s == "{";
+};
+
+/**
+ * Convert a list of strings representing a JSON document to a list of values.
+ */
+json.readJSON = function(l) {
+ var s = writeStrings(l);
+ var obj;
+ eval('obj = { \"val\": ' + s + " }");
+ return json.jsValToValue(obj.val);
+};
+
+/**
+ * Convert a list of values to JSON array elements.
+ */
+json.valuesToJSElements = function(a, l, i) {
+ if (isNil(l))
+ return a;
+ var pv = json.valueToJSVal(car(l));
+ a[i] = pv
+ return json.valuesToJSElements(a, cdr(l), i + 1);
+};
+
+/**
+ * Convert a value to a JSON value.
+ */
+json.valueToJSVal = function(v) {
+ if (!isList(v))
+ return v;
+ if (json.isJSArray(v))
+ return json.valuesToJSElements(range(0, v.length), v, 0);
+ return json.valuesToJSProperties({}, v);
+};
+
+/**
+ * Convert a list of values to JSON properties.
+ */
+json.valuesToJSProperties = function(o, l) {
+ if (isNil(l))
+ return o;
+ var token = car(l);
+ if (isTaggedList(token, attribute)) {
+ var pv = json.valueToJSVal(attributeValue(token));
+ o['@' + attributeName(token).slice(1)] = pv;
+ } else if (isTaggedList(token, element)) {
+ if (elementHasValue(token)) {
+ var pv = json.valueToJSVal(elementValue(token));
+ o[elementName(token).slice(1)] = pv;
+ } else {
+ var child = {};
+ o[elementName(token).slice(1)] = child;
+ json.valuesToJSProperties(child, elementChildren(token));
+ }
+ }
+ return json.valuesToJSProperties(o, cdr(l));
+};
+
+/**
+ * Convert a list of values to a list of strings representing a JSON document.
+ */
+json.writeJSON = function(l) {
+ var jsv;
+ if (json.isJSArray(l))
+ jsv = json.valuesToJSElements(range(0, l.length), l, 0);
+ else
+ jsv = json.valuesToJSProperties({}, l);
+ var s = json.toJSON(jsv);
+ return mklist(s);
+}
+
+/**
+ * Convert a list + params to a JSON-RPC request.
+ */
+json.jsonRequest = function(id, func, params) {
+ var r = mklist(mklist("'id", id), mklist("'method", func), mklist("'params", params));
+ return json.writeJSON(valuesToElements(r));
+};
+
+/**
+ * Convert a value to a JSON-RPC result.
+ */
+json.jsonResult = function(id, val) {
+ return json.writeJSON(valuesToElements(mklist(mklist("'id", id), mklist("'result", val))));
+};
+
+/**
+ * Convert a JSON-RPC result to a value.
+ */
+json.jsonResultValue = function(s) {
+ var jsres = json.readJSON(s);
+ var res = elementsToValues(jsres);
+ var val = cadr(assoc("'result", res));
+ if (isList(val) && !json.isJSArray(val))
+ return mklist(val);
+ return val;
+};
+
+/**
+ * Escape a character.
+ */
+json.escapeJSONChar = function(c) {
+ if(c == "\"" || c == "\\") return "\\" + c;
+ if (c == "\b") return "\\b";
+ if (c == "\f") return "\\f";
+ if (c == "\n") return "\\n";
+ if (c == "\r") return "\\r";
+ if (c == "\t") return "\\t";
+ var hex = c.charCodeAt(0).toString(16);
+ if(hex.length == 1) return "\\u000" + hex;
+ if(hex.length == 2) return "\\u00" + hex;
+ if(hex.length == 3) return "\\u0" + hex;
+ return "\\u" + hex;
+};
+
+/**
+ * Encode a string into JSON format.
+ */
+json.escapeJSONString = function(s) {
+ // The following should suffice but Safari's regex is broken (doesn't support callback substitutions)
+ // return "\"" + s.replace(/([^\u0020-\u007f]|[\\\"])/g, json.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] = json.escapeJSONChar(parts[i]);
+ }
+ return "\"" + parts.join("") + "\"";
+};
+
+/**
+ * Marshall objects to JSON format.
+ */
+json.toJSON = function(o) {
+ if(o == null)
+ return "null";
+ if(o.constructor == String)
+ return json.escapeJSONString(o);
+ if(o.constructor == Number)
+ return o.toString();
+ if(o.constructor == Boolean)
+ return o.toString();
+ if(o.constructor == Date)
+ return '{javaClass: "java.util.Date", time: ' + o.valueOf() +'}';
+ if(o.constructor == Array) {
+ var v = [];
+ for(var i = 0; i < o.length; i++)
+ v.push(json.toJSON(o[i]));
+ return "[" + v.join(", ") + "]";
+ }
+ var v = [];
+ for(attr in o) {
+ if(o[attr] == null)
+ v.push("\"" + attr + "\": null");
+ else if(typeof o[attr] == "function")
+ ; // Skip
+ else
+ v.push(json.escapeJSONString(attr) + ": " + json.toJSON(o[attr]));
+ }
+ return "{" + v.join(", ") + "}";
+};
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/js/htdocs/scdl.js b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/scdl.js
new file mode 100644
index 0000000000..50dab53e7c
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/scdl.js
@@ -0,0 +1,250 @@
+/*
+ * 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.
+ */
+
+/**
+ * SCDL parsing functions.
+ */
+var scdl = {};
+
+/**
+ * Returns a composite element.
+ */
+scdl.composite = function(l) {
+ var cs = namedElementChildren("'composite", l);
+ if (isNil(cs))
+ return cs;
+ return car(cs);
+};
+
+/**
+ * Returns a list of components in a composite.
+ */
+scdl.components = function(l) {
+ var cs = namedElementChildren("'composite", l);
+ if (isNil(cs))
+ return cs;
+ return namedElementChildren("'component", car(cs));
+};
+
+/**
+ * Returns a list of service promotions in a composite.
+ */
+scdl.promotions = function(l) {
+ var cs = namedElementChildren("'composite", l);
+ if (isNil(cs))
+ return cs;
+ return namedElementChildren("'service", car(cs));
+};
+
+/**
+ * Returns the target of a service promotion.
+ */
+scdl.promote = function(l) {
+ var puri = namedAttributeValue("'promote", l);
+ if (isNil(puri))
+ return puri;
+ return car(tokens(puri));
+};
+
+/**
+ * Returns the name of a component, componentType, service or reference.
+ */
+scdl.name = function(l) {
+ return namedAttributeValue("'name", l);
+};
+
+/**
+ * Returns the description of a component, componentType, service or reference.
+ */
+scdl.documentation = function(l) {
+ var d = namedElementChildren("'documentation", l);
+ if (isNil(d))
+ return null;
+ if (!elementHasValue(car(d)))
+ return null;
+ var v = elementValue(car(d));
+ return v;
+};
+
+/**
+ * Returns the title of a component or componentType.
+ */
+scdl.title = function(l) {
+ return namedAttributeValue("'title", l);
+};
+
+/**
+ * Returns the display style of a component, componentType, reference or property.
+ */
+scdl.style = function(l) {
+ return namedAttributeValue("'style", l);
+};
+
+/**
+ * Returns the color of a component or componentType.
+ */
+scdl.color = function(l) {
+ return namedAttributeValue("'color", l);
+};
+
+/**
+ * Returns the x position of a component.
+ */
+scdl.x = function(l) {
+ return namedAttributeValue("'x", l);
+};
+
+/**
+ * Returns the y position of a component.
+ */
+scdl.y = function(l) {
+ return namedAttributeValue("'y", l);
+};
+
+/**
+ * Returns the implementation of a component.
+ */
+scdl.implementation = function(l) {
+ function filterImplementation(v) {
+ return isElement(v) && cadr(v).match("implementation.") != null;
+ }
+
+ var n = filter(filterImplementation, l);
+ if (isNil(n))
+ return null;
+ return car(n);
+};
+
+/**
+ * Returns the type of a component or componentType implementation.
+ */
+scdl.implementationType = function(l) {
+ return elementName(l).substring(1);
+};
+
+/**
+ * Returns the URI of a service, reference or implementation.
+ */
+scdl.uri = function(l) {
+ return namedAttributeValue("'uri", l);
+};
+
+/**
+ * Returns the align attribute of a service or reference.
+ */
+scdl.align = function(l) {
+ return namedAttributeValue("'align", l);
+};
+
+/**
+ * Returns the visible attribute of a service or reference.
+ */
+scdl.visible = function(l) {
+ return namedAttributeValue("'visible", l);
+};
+
+/**
+ * Returns the clonable attribute of a reference.
+ */
+scdl.clonable = function(l) {
+ return namedAttributeValue("'clonable", l);
+};
+
+/**
+ * Returns a list of services in a component or componentType.
+ */
+scdl.services = function(l) {
+ return namedElementChildren("'service", l);
+};
+
+/**
+ * Returns a list of references in a component or componentType.
+ */
+scdl.references = function(l) {
+ return namedElementChildren("'reference", l);
+};
+
+/**
+ * Returns a list of bindings in a service or reference.
+ */
+scdl.bindings = function(l) {
+ function filterBinding(v) {
+ return isElement(v) && cadr(v).match("binding.") != null;
+ }
+
+ return filter(filterBinding, l);
+};
+
+/**
+ * Returns the type of a binding.
+ */
+scdl.bindingType = function(l) {
+ return elementName(l).substring(1);
+};
+
+/**
+ * Returns the target of a reference.
+ */
+scdl.target = function(l) {
+ function targetURI() {
+ function bindingsTarget(l) {
+ if (isNil(l))
+ return null;
+ var u = scdl.uri(car(l));
+ if (!isNil(u))
+ return u;
+ return bindingsTarget(cdr(l));
+ }
+
+ var t = namedAttributeValue("'target", l);
+ if (!isNil(t))
+ return t;
+ return bindingsTarget(scdl.bindings(l));
+ }
+ var turi = targetURI();
+ if (isNil(turi))
+ return turi;
+ return car(tokens(turi));
+};
+
+/**
+ * Returns a list of properties in a component or componentType.
+ */
+scdl.properties = function(l) {
+ return namedElementChildren("'property", l);
+};
+
+/**
+ * Returns the value of a property.
+ */
+scdl.propertyValue = function(l) {
+ if (!elementHasValue(l))
+ return '';
+ return elementValue(l);
+};
+
+/**
+ * Convert a list of elements to a name -> element assoc list.
+ */
+scdl.nameToElementAssoc = function(l) {
+ if (isNil(l))
+ return l;
+ return cons(mklist(scdl.name(car(l)), car(l)), scdl.nameToElementAssoc(cdr(l)));
+};
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/js/htdocs/ui.css b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/ui.css
new file mode 100644
index 0000000000..ddc21b2095
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/ui.css
@@ -0,0 +1,709 @@
+/*
+ * 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.
+ */
+
+body {
+margin-top: 0px; margin-bottom: 2px; margin-left: 2px; margin-right: 2px;
+font-family: Arial; font-style: normal; font-variant: normal; font-size: 14px;
+background-color: #ffffff; opacity: 1;
+-webkit-text-size-adjust: none;
+-webkit-touch-callout: none;
+-webkit-tap-highlight-color: rgba(0,0,0,0);
+-webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none;
+cursor: default;
+-webkit-backface-visibility: hidden;
+}
+
+.delayed {
+visibility: hidden;
+}
+
+.fixed {
+position: fixed;
+}
+
+.devicewidth {
+position: absolute; top: 0px; left: 0px; width: 100%; height: 5000px; overflow: hidden;
+}
+
+.mainbody {
+position: absolute; top: 0px; left: 0px; width: 100%; height: 5000px; overflow: visible;
+-webkit-backface-visibility: hidden;
+}
+
+.viewcontainer3dm {
+position: absolute; left: 0px; top: 35px; width: 100%;
+-webkit-backface-visibility: hidden;
+}
+
+.viewcontainer3d {
+position: absolute; left: 0px; top: 35px; width: 100%;
+-webkit-backface-visibility: hidden;
+}
+
+.leftviewloading3dm {
+position: absolute; top: 0px; left: 0px; width: 100%; height: 5000px; overflow: hidden;
+z-index: -10; background-color: #ffffff;
+-webkit-transform: translate(100%, 0px);
+-moz-transform: translate(100%, 0px);
+-ms-transform: translate(100%, 0px);
+-o-transform: translate(100%, 0px);
+transform: translate(100%, 0px);
+-webkit-backface-visibility: hidden;
+}
+
+.rightviewloading3dm {
+position: absolute; top: 0px; left: 0px; width: 100%; height: 5000px; overflow: hidden;
+z-index: -10; background-color: #ffffff;
+-webkit-transform: translate(-100%, 0px);
+-moz-transform: translate(-100%, 0px);
+-ms-transform: translate(-100%, 0px);
+-o-transform: translate(-100%, 0px);
+transform: translate(-100%, 0px);
+-webkit-backface-visibility: hidden;
+}
+
+.viewloading3d {
+position: absolute; top: 0px; left: 0px; width: 100%; height: 5000px; overflow: hidden;
+visibility: hidden; z-index: -10; background-color: #ffffff;
+-webkit-transform: translate(100%, 0px);
+-moz-transform: translate(100%, 0px);
+-ms-transform: translate(100%, 0px);
+-o-transform: translate(100%, 0px);
+transform: translate(100%, 0px);
+-webkit-backface-visibility: hidden;
+}
+
+.viewloaded3dm {
+position: absolute; top: 0px; left: 0px; width: 100%; height: 5000px; overflow: visible;
+z-index: 0; background-color: #ffffff;
+-webkit-transition: -webkit-transform 0.4s ease-in-out;
+-moz-transition: -moz-transform 0.4s ease-in-out;
+-ms-transition: -ms-transform 0.4s ease-in-out;
+-o-transition: -o-transform 0.4s ease-in-out;
+transition: transform 0.4s ease-in-out;
+-webkit-transform: translate(0px, 0px);
+-moz-transform: translate(0px, 0px);
+-ms-transform: translate(0px, 0px);
+-o-transform: translate(0px, 0px);
+transform: translate(0px, 0px);
+-webkit-backface-visibility: hidden;
+}
+
+.viewloaded3d {
+position: absolute; top: 0px; left: 0px; width: 100%; height: 5000px; overflow: visible;
+z-index: 0; background-color: #ffffff;
+-webkit-transform: translate(0px, 0px);
+-moz-transform: translate(0px, 0px);
+-ms-transform: translate(0px, 0px);
+-o-transform: translate(0px, 0px);
+transform: translate(0px, 0px);
+-webkit-backface-visibility: hidden;
+}
+
+.viewunloading3dm {
+position: absolute; top: 0px; left: 0px; width: 100%; height: 5000px; overflow: hidden;
+z-index: 0; background-color: #ffffff;
+-webkit-transform: translate(0px, 0px);
+-moz-transform: translate(0px, 0px);
+-ms-transform: translate(0px, 0px);
+-o-transform: translate(0px, 0px);
+transform: translate(0px, 0px);
+-webkit-backface-visibility: hidden;
+}
+
+.leftviewunloaded3dm {
+position: absolute; top: 0px; left: 0px; width: 100%; height: 5000px; overflow: hidden;
+z-index: -10; background-color: #ffffff;
+-webkit-transition: -webkit-transform 0.4s ease-in-out;
+-moz-transition: -moz-transform 0.4s ease-in-out;
+-ms-transition: -ms-transform 0.4s ease-in-out;
+-o-transition: -o-transform 0.4s ease-in-out;
+transition: transform 0.4s ease-in-out;
+-webkit-transform: translate(-100%, 0px);
+-moz-transform: translate(-100%, 0px);
+-ms-transform: translate(-100%, 0px);
+-o-transform: translate(-100%, 0px);
+transform: translate(-100%, 0px);
+-webkit-backface-visibility: hidden;
+}
+
+.rightviewunloaded3dm {
+position: absolute; top: 0px; left: 0px; width: 100%; height: 5000px; overflow: hidden;
+z-index: -10; background-color: #ffffff;
+-webkit-transition: -webkit-transform 0.4s ease-in-out;
+-moz-transition: -moz-transform 0.4s ease-in-out;
+-ms-transition: -ms-transform 0.4s ease-in-out;
+-o-transition: -o-transform 0.4s ease-in-out;
+transition: transform 0.4s ease-in-out;
+-webkit-transform: translate(100%, 0px);
+-moz-transform: translate(100%, 0px);
+-ms-transform: translate(100%, 0px);
+-o-transform: translate(100%, 0px);
+transform: translate(100%, 0px);
+-webkit-backface-visibility: hidden;
+}
+
+.body {
+width: 100%; height: 5000px; overflow: visible;
+-webkit-backface-visibility: hidden;
+}
+
+.viewhead {
+position: absolute; left: 0px; top: 35px; height: 35px; line-height: 35px; width: 100%; z-index: 8;
+font-size: 110%; font-weight: bold; background-color: #f1f1f1; color: #000000;
+border-top: 1px; border-bottom: 1px; border-left: 0px; border-right: 0px;
+border-style: solid; border-top-color: #e5e5e5; border-bottom-color: #e5e5e5;
+overflow: hidden;
+-webkit-backface-visibility: hidden;
+}
+
+.viewheadbackground {
+position: absolute; left: 0px; top: 35px; height: 35px; line-height: 35px; width: 2500px; z-index: 7;
+background-color: #f1f1f1;
+border-top: 1px; border-bottom: 1px; border-left: 0px; border-right: 0px;
+border-style: solid; border-top-color: #e5e5e5; border-bottom-color: #e5e5e5;
+overflow: hidden;
+-webkit-backface-visibility: hidden;
+}
+
+.viewfoot {
+position: absolute; left: 0px; bottom: 0px; height: 25px; line-height: 25px; width: 100%; z-index: 8;
+font-size: 12px; background-color: #ffffff; color: #404040;
+border-top: 1px; border-bottom: 1px; border-left: 0px; border-right: 0px;
+border-style: solid; border-top-color: #e5e5e5; border-bottom-color: #ffffff;
+overflow: hidden;
+-webkit-backface-visibility: hidden;
+}
+
+.status {
+position: absolute; left: 0px; bottom: 0px; height: 35px; line-height: 35px; width: 100%; z-index: 9;
+font-size: 12px; background-color: #ffffff; color: #404040;
+border-top: 0px; border-bottom: 0px; border-left: 0px; border-right: 0px;
+border-style: solid; border-top-color: #e5e5e5; border-bottom-color: #ffffff;
+overflow: hidden;
+-webkit-transform: translate(0px, 0px);
+-moz-transform: translate(0px, 0px);
+-ms-transform: translate(0px, 0px);
+-o-transform: translate(0px, 0px);
+transform: translate(0px, 0px);
+-webkit-backface-visibility: hidden;
+}
+
+.statusout3 {
+position: absolute; left: 0px; bottom: 0px; height: 35px; line-height: 35px; width: 100%; z-index: 9;
+font-size: 12px; background-color: #ffffff; color: #404040;
+border-top: 0px; border-bottom: 0px; border-left: 0px; border-right: 0px;
+border-style: solid; border-top-color: #e5e5e5; border-bottom-color: #ffffff;
+overflow: hidden;
+-webkit-transition: -webkit-transform 0.4s ease-in-out 3s;
+-moz-transition: -moz-transform 0.4s ease-in-out 3s;
+-ms-transition: -ms-transform 0.4s ease-in-out 3s;
+-o-transition: -o-transform 0.4s ease-in-out 3s;
+transition: transform 0.4s ease-in-out 3s;
+-webkit-transform: translate(0px, 35px);
+-moz-transform: translate(0px, 35px);
+-ms-transform: translate(0px, 35px);
+-o-transform: translate(0px, 35px);
+transform: translate(0px, 35px);
+-webkit-backface-visibility: hidden;
+}
+
+.statusout1 {
+position: absolute; left: 0px; bottom: 0px; height: 35px; line-height: 35px; width: 100%; z-index: 9;
+font-size: 12px; background-color: #ffffff; color: #404040;
+border-top: 0px; border-bottom: 0px; border-left: 0px; border-right: 0px;
+border-style: solid; border-top-color: #e5e5e5; border-bottom-color: #ffffff;
+overflow: hidden;
+-webkit-transition: -webkit-transform 0.4s ease-in-out 1s;
+-moz-transition: -moz-transform 0.4s ease-in-out 1s;
+-ms-transition: -ms-transform 0.4s ease-in-out 1s;
+-o-transition: -o-transform 0.4s ease-in-out 1s;
+transition: transform 0.4s ease-in-out 1s;
+-webkit-transform: translate(0px, 35px);
+-moz-transform: translate(0px, 35px);
+-ms-transform: translate(0px, 35px);
+-o-transform: translate(0px, 35px);
+transform: translate(0px, 35px);
+-webkit-backface-visibility: hidden;
+}
+
+.okstatus {
+font-size: 110%; font-weight: bold; padding-left: 6px; padding-right: 6px; white-space: nowrap; text-decoration: none;
+background-color: #f1f1f1; color: #000000;
+width: 100%; margin-left: auto; margin-right: auto; text-align: center; float: right;
+}
+
+.errorstatus {
+font-size: 110%; font-weight: bold; padding-left: 6px; padding-right: 6px; white-space: nowrap; text-decoration: none;
+background-color: #d14836; color: #ffffff;
+width: 100%; margin-left: auto; margin-right: auto; text-align: center; float: right;
+}
+
+.viewfootbackground {
+position: absolute; left: 0px; bottom: 0px; height: 25px; line-height: 25px; width: 2500px; z-index: 7;
+background-color: #ffffff;
+border-top: 1px; border-bottom: 1px; border-left: 0px; border-right: 0px;
+border-style: solid; border-top-color: #e5e5e5; border-bottom-color: #ffffff;
+overflow: hidden;
+-webkit-backface-visibility: hidden;
+}
+
+.viewcontent {
+position: absolute; left: 0px; top: 38px; width: 100%;
+-webkit-backface-visibility: hidden;
+}
+
+.viewform {
+position: absolute; left: 0px; top: 40px; width: 100%;
+}
+
+table {
+border: 0px; border-collapse: collapse; border-color: #a2bae7; border-style: solid;
+font-family: Arial; font-style: normal; font-variant: normal; font-size: 14px;
+overflow: visible;
+}
+
+.trb {
+border-bottom: 1px; border-bottom-style: solid; border-color: #dcdcdc;
+}
+
+th {
+font-size: 110%; font-weight: bold; background-color: #f1f1f1; color: #000000;
+text-align: left; padding-left: 2px; padding-right: 8px; padding-top: 0px; padding-bottom: 0px; vertical-align: middle; white-space: nowrap;
+border-top: 1px; border-bottom: 1px; border-left: 1px; border-right: 1px; border-style: solid; border-top-color: #e5e5e5; border-bottom-color: #e5e5e5; border-left-color: #e5e5e5; border-right-color: #e5e5e5;
+overflow: hidden;
+}
+
+.section {
+font-size: 110%; font-weight: bold; background-color: #f1f1f1; color: #000000; height: 30px; line-height: 30px;
+text-align: left; padding-top: 0px; padding-bottom: 0px; padding-left: 2px; padding-right: 2px; white-space: nowrap;
+border-top: 1px; border-bottom: 1px; border-left: 0px; border-right: 0px; border-style: solid; border-top-color: #e5e5e5; border-bottom-color: #e5e5e5; border-left-color: #e5e5e5; border-right-color: #e5e5e5;
+overflow: hidden;
+}
+
+.hsection {
+width: 100%; height: 0px; visibility: hidden;
+border-top: 0px; border-bottom: 0px; border-left: 0px; border-right: 0px; border-style: solid; border-bottom-color: #000000; background-color: #ffffff;
+padding-left: 2px; padding-right: 2px; padding-top: 0px; padding-bottom: 0px; margin-bottom: 0px; margin-left: auto; margin-right: auto; text-align: center;
+}
+
+.fsection{
+width: 100%; height: 0px; visibility: hidden;
+border-top: 0px; border-bottom: 0px; border-left: 0px; border-right: 0px; border-style: solid; border-top-color: #a2bae7;
+padding: 0px; margin-top: 0px; margin-left: auto; margin-right: auto; text-align: center;
+}
+
+.bluetext {
+color: #4787ed;
+}
+
+.redtext {
+color: #d14836;
+}
+
+.mirror {
+display: inline-block;
+-webkit-transform: scaleX(-1);
+-moz-transform: scaleX(-1);
+-ms-transform: scaleX(-1);
+-o-transform: scaleX(-1);
+transform: scaleX(-1);
+}
+
+.greentext {
+color: #009900;
+}
+
+.text {
+padding-top: 3px; padding-bottom: 4px; vertical-align: middle; white-space: nowrap;
+}
+
+.link {
+padding-top: 3px; padding-bottom: 4px; vertical-align: middle; white-space: nowrap;
+}
+
+.checkbox {
+padding-top: 3px; padding-bottom: 4px; vertical-align: middle; white-space: nowrap;
+}
+
+.thl {
+border-left: 0px;
+}
+
+.thr {
+border-right: 0px;
+}
+
+.ths {
+padding: 0px;
+}
+
+td {
+padding-left: 2px; padding-top: 2px; padding-right: 8px; white-space: nowrap; vertical-align: middle; border: 0px;
+}
+
+.tdl {
+border-right: 1px; border-style: solid; border-color: #a2bae7; width: 10px;
+}
+
+.tdr {
+border-left: 1px; border-style: solid; border-color: #a2bae7;
+}
+
+.tdw {
+padding-left: 2px; padding-top: 2px; padding-right: 8px; white-space: normal; vertical-align: middle;
+}
+
+.datatd {
+border-top: 1px; border-bottom: 1px; border-style: solid; border-color: #dcdcdc; width: 10px; vertical-align: middle;
+}
+
+.datatdl {
+border-right: 1px; border-top: 1px; border-bottom: 1px; border-style: solid; border-color: #dcdcdc; width: 10px; vertical-align: middle;
+}
+
+.datatdltop {
+border-right: 1px; border-top: 1px; border-bottom: 1px; border-style: solid; border-color: #dcdcdc; width: 10px; vertical-align: top;
+}
+
+.datatdr {
+border-left: 1px; border-top: 1px; border-bottom: 1px; border-style: solid; border-color: #dcdcdc; vertical-align: middle;
+}
+
+.datatable {
+border-top: 1px; border-bottom: 1px; border-style: solid; border-color: #dcdcdc;
+overflow: visible;
+}
+
+.databg {
+opacity: .6;
+-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)";
+filter: alpha(opacity=60);
+}
+
+.guide {
+border: 1px; border-style: solid; border-color: #c0c0c0;
+}
+
+iframe {
+border: 0px; margin: 0px; padding: 0px;
+}
+
+.fakeframe {
+padding: 3px; background-color: #dcdcdc; color: #000000;
+}
+
+input {
+vertical-align: middle;
+font-family: Arial; font-style: normal; font-variant: normal; font-size: 15px;
+outline: none;
+-webkit-text-size-adjust: 100%;
+}
+
+button {
+vertical-align: middle;
+font-family: Arial; font-style: normal; font-variant: normal; font-size: 15px;
+outline: none;
+-webkit-text-size-adjust: 100%;
+}
+
+textarea {
+font-family: Arial; font-style: normal; font-variant: normal; font-size: 15px;
+outline: none;
+overflow: auto; resize: none;
+}
+
+.flatentry {
+font-family: Arial; font-style: normal; font-variant: normal; font-size: 15px;
+-webkit-appearance: none; appearance: none;
+border: 1px solid #d9d9d9!important; border-top: 1px solid silver!important;
+box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box;
+border-radius: 1px; -webkit-border-radius: 1px; -moz-border-radius: 1px;
+margin: 1px!important; padding: 3px 1px 3px 3px;
+}
+
+.graphentry {
+font-family: Arial; font-style: normal; font-variant: normal; font-size: 13px;
+-webkit-appearance: none; appearance: none;
+border: 1px solid #d9d9d9!important; border-top: 1px solid silver!important;
+box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box;
+border-radius: 1px; -webkit-border-radius: 1px; -moz-border-radius: 1px;
+margin: 0px; padding: 0px;
+}
+
+.flatcheckbox {
+}
+
+.editablewidget {
+-webkit-user-select: text; -moz-user-select: text; -ms-user-select: text; user-select: text;
+outline: none; -moz-outline-style: none;
+}
+
+.noneditablewidget {
+outline: none; -moz-outline-style: none;
+}
+
+.editablesvg {
+background-color: transparent;
+font-family: inherit; font-style: inherit; font-variant: inherit; font-size: inherit; font-weight: inherit;
+padding: 0px; margin: 0px;
+overflow: auto; resize: none;
+outline: none; -moz-outline-style: none;
+-webkit-appearance: none; -webkit-text-size-adjust: 100%;
+border: 0px;
+}
+
+a:link {
+color: #357ae8; text-decoration: none; white-space: nowrap; cursor: pointer;
+}
+
+a:visited {
+color: #357ae8; text-decoration: none; white-space: nowrap; cursor: pointer;
+}
+
+.tbarmenu {
+position: absolute; top: 0px; left: 0px; z-index: 10; width: 100%; margin: 0px; padding: 0px; border-collapse: separate;
+height: 35px; line-height: 35px; background-color: #2c2c2c;
+border-top: 1px; border-bottom: 1px; border-left: 0px; border-right: 0px; border-style: solid; border-top-color: #2c2c2c; border-bottom-color: #2c2c2c;
+-webkit-backface-visibility: hidden;
+}
+
+.tbarbackground {
+position: absolute; top: 0px; left: 0px; z-index: 9; width: 2500px; margin: 0px; padding: 0px; border-collapse: separate;
+height: 35px; line-height: 35px; background-color: #2c2c2c;
+border-top: 1px; border-bottom: 1px; border-left: 0px; border-right: 0px; border-style: solid; border-top-color: #2c2c2c; border-bottom-color: #2c2c2c;
+overflow: hidden;
+-webkit-backface-visibility: hidden;
+}
+
+.tbarleft {
+padding-left: 2px; padding-right: 6px; white-space: nowrap; float: left;
+}
+
+.tbarright {
+padding-left: 6px; padding-right: 2px; white-space: nowrap; float: right;
+}
+
+.tbaramenu {
+font-size: 110%; color: #cccccc; text-decoration: none; white-space: nowrap;
+}
+
+.tbarsmenu {
+font-size: 110%; font-weight: bold; color: #ffffff; text-decoration: none; white-space: nowrap;
+}
+
+.amenu {
+padding-left: 2px; padding-right: 6px; white-space: nowrap; color: #808080; text-decoration: none; float: left;
+}
+
+.smenu {
+padding-left: 2px; padding-right: 6px; white-space: nowrap; color: #000000; text-decoration: none; float: left;
+}
+
+.cmenu {
+font-size: 18px; padding-left: 6px; padding-right: 6px; white-space: nowrap; color: #000000; text-decoration: none;
+width: 100%; margin-left: auto; margin-right: auto; text-align: center; float: right;
+}
+
+.bcmenu {
+font-size: 22px; padding-left: 2px; padding-right: 6px; white-space: nowrap; color: #000000; text-decoration: none;
+}
+
+.rmenu {
+padding-left: 2px; padding-right: 2px; white-space: nowrap; white-space: nowrap; float: right;
+}
+
+h1 {
+font-size: 150%; font-weight: bold; vertical-align: middle; margin-top: 5px; margin-bottom: 5px; margin-left: 2px; margin-right: 2px; white-space: nowrap;
+}
+
+h2 {
+font-size: 120%; font-weight: bold; vertical-align: middle; margin-top: 5px; margin-bottom: 5px; margin-left: 2px; margin-right: 2px; white-space: nowrap;
+}
+
+.hd1 {
+font-size: 150%; font-weight: bold; white-space: nowrap;
+}
+
+.hd2 {
+font-size: 120%; font-weight: bold; white-space: nowrap;
+}
+
+img {
+border: 0px;
+}
+
+.plusminus {
+font-size: 18px; font-family: "Courier New";
+}
+
+.imgbutton {
+width: 142px; height: 64px; margin-left: 20px; margin-right: 20px; padding: 0px; border: 1px; cursor: pointer;
+}
+
+.graybutton {
+display: inline-block; text-align: center; color: #444; font-weight: bold;
+padding-top: 0px; padding-bottom: 0px; padding-left: 4px; padding-right: 4px;
+height: 28px; line-height: 28px; min-width: 30px;
+-webkit-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px;
+border: 1px solid gainsboro; background-color: whiteSmoke;
+background-image: -webkit-gradient(linear,left top,left bottom,from(whiteSmoke),to(#f1f1f1));
+background-image: -webkit-linear-gradient(top,whiteSmoke,#f1f1f1);
+background-image: -moz-linear-gradient(top,whiteSmoke,#f1f1f1);
+background-image: -ms-linear-gradient(top,whiteSmoke,#f1f1f1);
+background-image: -o-linear-gradient(top,whiteSmoke,#f1f1f1);
+background-image: linear-gradient(top,whiteSmoke,#f1f1f1);
+cursor: default;
+}
+
+.graybutton:hover {
+border: 1px solid #c6c6c6; color: #333; text-shadow: 0 1px rgba(0, 0, 0, 0.3); background-color: #f8f8f8;
+background-image: -webkit-gradient(linear,left top,left bottom,from(#f8f8f8),to(#f1f1f1));
+background-image: -webkit-linear-gradient(top,#f8f8f8,#f1f1f1);
+background-image: -moz-linear-gradient(top,#f8f8f8,#f1f1f1);
+background-image: -ms-linear-gradient(top,#f8f8f8,#f1f1f1);
+background-image: -o-linear-gradient(top,#f8f8f8,#f1f1f1);
+background-image: linear-gradient(top,#f8f8f8,#f1f1f1);
+}
+
+.graybutton:active {
+background-color: #f6f6f6;
+background-image: -webkit-gradient(linear,left top,left bottom,from(#f6f6f6),to(#f1f1f1));
+background-image: -webkit-linear-gradient(top,#f6f6f6,#f1f1f1);
+background-image: -moz-linear-gradient(top,#f6f6f6,#f1f1f1);
+background-image: -ms-linear-gradient(top,#f6f6f6,#f1f1f1);
+background-image: -o-linear-gradient(top,#f6f6f6,#f1f1f1);
+background-image: linear-gradient(top,#f6f6f6,#f1f1f1);
+-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+}
+
+.graybutton:disabled {
+color: #c0c0c0;
+}
+
+.bluebutton {
+border: 1px solid #3079ed; color: white; text-shadow: 0 1px rgba(0, 0, 0, 0.1); background-color: #4d90fe;
+background-image: -webkit-gradient(linear,left top,left bottom,from(#4d90fe),to(#4787ed));
+background-image: -webkit-linear-gradient(top,#4d90fe,#4787ed);
+background-image: -moz-linear-gradient(top,#4d90fe,#4787ed);
+background-image: -ms-linear-gradient(top,#4d90fe,#4787ed);
+background-image: -o-linear-gradient(top,#4d90fe,#4787ed);
+background-image: linear-gradient(top,#4d90fe,#4787ed);
+}
+
+.bluebutton:disabled {
+color: #c0c0c0;
+}
+
+.bluebutton:hover {
+border: 1px solid #2f5bb7; color: white; text-shadow: 0 1px rgba(0, 0, 0, 0.3); background-color: #357ae8;
+background-image: -webkit-gradient(linear,left top,left bottom,from(#4d90fe),to(#357ae8));
+background-image: -webkit-linear-gradient(top,#4d90fe,#357ae8);
+background-image: -moz-linear-gradient(top,#4d90fe,#357ae8);
+background-image: -ms-linear-gradient(top,#4d90fe,#357ae8);
+background-image: -o-linear-gradient(top,#4d90fe,#357ae8);
+background-image: linear-gradient(top,#4d90fe,#357ae8);
+}
+
+.bluebutton:hover:disabled {
+color: #c0c0c0;
+}
+
+.redbutton {
+border: 1px solid transparent; color: white; text-shadow: 0 1px rgba(0, 0, 0, 0.1); background-color: #d14836;
+background-image: -webkit-gradient(linear,left top,left bottom,from(#dd4b39),to(#d14836));
+background-image: -webkit-linear-gradient(top,#dd4b39,#d14836);
+background-image: -moz-linear-gradient(top,#dd4b39,#d14836);
+background-image: -ms-linear-gradient(top,#dd4b39,#d14836);
+background-image: -o-linear-gradient(top,#dd4b39,#d14836);
+background-image: linear-gradient(top,#dd4b39,#d14836);
+-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3); -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3); -ms-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3);
+-o-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3);
+}
+
+.redbutton:disabled {
+color: #c0c0c0;
+}
+
+.redbutton:hover {
+border: 1px solid #b0281a; border-bottom-color: #af301f; color: white; background-color: #c53727;
+background-image: -webkit-linear-gradient(top,#dd4b39,#c53727);
+background-image: -moz-linear-gradient(top,#dd4b39,#c53727);
+background-image: -ms-linear-gradient(top,#dd4b39,#c53727);
+background-image: -o-linear-gradient(top,#dd4b39,#c53727);
+background-image: linear-gradient(top,#dd4b39,#c53727);
+-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); -ms-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
+-o-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
+}
+
+.redbutton:hover:disabled {
+color: #c0c0c0;
+}
+
+.box {
+width: 150px; display: inline-block;
+border: 1px; border-style: solid; border-color: #dcdcdc; border-collapse: collapse;
+white-space: nowrap; margin: 2px; padding: 2px; vertical-align: top;
+}
+
+.appicon {
+float: left;
+height: 50px; width: 50px; vertical-align: top; margin: 0px; padding: 0px;
+}
+
+.apptitle {
+font-weight: bold;
+}
+
+.note {
+font-size: 12px; color: #808080;
+}
+
+.pagediv {
+position: absolute; display: block; overflow: visible;
+-webkit-backface-visibility: hidden;
+}
+
+.graphdiv {
+position: absolute; display: block; overflow: visible;
+-webkit-backface-visibility: hidden;
+}
+
+.g {
+position: absolute; display: block; overflow: visible;
+-webkit-backface-visibility: hidden;
+}
+
+.path {
+position: absolute; background: transparent; display: block; overflow: visible;
+visibility: visible;
+-webkit-backface-visibility: hidden;
+}
+
+.gtitle {
+position: absolute;
+margin: 4px; padding: 0px; line-height: 15px; vertical-align: middle; white-space: nowrap;
+font-family: Arial; font-style: normal; font-variant: normal; font-size: 13px; cursor: default;
+background: transparent; display: block; overflow: visible;
+-webkit-text-size-adjust: none;
+-webkit-touch-callout: none;
+-webkit-tap-highlight-color: rgba(0,0,0,0);
+-webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none;
+}
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/js/htdocs/ui.js b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/ui.js
new file mode 100644
index 0000000000..fb53598390
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/ui.js
@@ -0,0 +1,409 @@
+/*
+ * 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.
+ */
+
+/**
+ * UI utility functions.
+ */
+
+var ui = {};
+
+/**
+ * Return a child element of a node with the given id.
+ */
+ui.elementByID = function(node, id) {
+ if (node.skipNode == true)
+ return null;
+ for (var i in node.childNodes) {
+ var child = node.childNodes[i];
+ if (child.id == id)
+ return child;
+ var gchild = ui.elementByID(child, id);
+ if (gchild != null)
+ return gchild;
+ }
+ return null;
+};
+
+/**
+ * Return the current document, or a child element with the given id.
+ */
+function $(id) {
+ if (id == document)
+ return document;
+ return ui.elementByID($(document), id);
+};
+
+/**
+ * Return a list of elements with the given class name.
+ */
+ui.elementsByClassName = function(node, c) {
+ return nodeList(node.getElementsByClassName(c));
+};
+
+/**
+ * Return the query string of a URL.
+ */
+ui.query = function(url) {
+ var u = '' + url;
+ var q = u.indexOf('?');
+ return q >= 0? u.substring(q + 1) : '';
+};
+
+/**
+ * Return the fragment part of a URL.
+ */
+ui.fragment = function(url) {
+ var u = '' + url;
+ var h = u.indexOf('#');
+ return h >= 0? u.substring(h + 1) : '';
+};
+
+/**
+ * Return the path and parameters of a URL.
+ */
+ui.pathandparams = function(url) {
+ var u = '' + url;
+ var ds = u.indexOf('//');
+ var u2 = ds > 0? u.substring(ds + 2) : u;
+ var s = u2.indexOf('/');
+ return s > 0? u2.substring(s) : '';
+};
+
+/**
+ * Return a dictionary of query parameters in a URL.
+ */
+ui.queryParams = function(url) {
+ var qp = new Array();
+ var qs = ui.query(url).split('&');
+ for (var i = 0; i < qs.length; i++) {
+ var e = qs[i].indexOf('=');
+ if (e > 0)
+ qp[qs[i].substring(0, e)] = unescape(qs[i].substring(e + 1));
+ }
+ return qp;
+};
+
+/**
+ * Return a dictionary of fragment parameters in a URL.
+ */
+ui.fragmentParams = function(url) {
+ var qp = new Array();
+ var qs = ui.fragment(url).split('&');
+ for (var i = 0; i < qs.length; i++) {
+ var e = qs[i].indexOf('=');
+ if (e > 0)
+ qp[qs[i].substring(0, e)] = unescape(qs[i].substring(e + 1));
+ }
+ return qp;
+};
+
+/**
+ * Convert a base64-encoded image to a data URL.
+ */
+ui.b64img = function(b64) {
+ return 'data:image/png;base64,' + b64;
+};
+
+/**
+ * Declare a CSS stylesheet.
+ */
+ui.declareCSS = function(s) {
+ var e = document.createElement('style');
+ e.type = 'text/css';
+ e.textContent = s;
+ return e;
+};
+
+/**
+ * Declare a script.
+ */
+ui.declareScript = function(s) {
+ var e = document.createElement('script');
+ e.type = 'text/javascript';
+ e.text = s;
+ return e;
+};
+
+/**
+ * Return the scripts elements under a given element.
+ */
+ui.innerScripts = function(e) {
+ return map(function(s) { return s.text; }, nodeList(e.getElementsByTagName('script')));
+};
+
+/**
+ * Evaluate a script.
+ */
+ui.evalScript = function(s) {
+ return eval('(function() {\n' + s + '\n})();');
+};
+
+/**
+ * Include a script.
+ */
+ui.includeScript = function(s) {
+ //log('include', s);
+ return eval(s);
+};
+
+/**
+ * Return true if the client is a mobile device.
+ */
+ui.mobiledetected = false;
+ui.mobile = false;
+ui.isMobile = function() {
+ if (ui.mobiledetected)
+ return ui.mobile;
+ var ua = navigator.userAgent;
+ if (ua.match(/iPhone/i) || ua.match(/iPad/i) || ua.match(/Android/i) || ua.match(/Blackberry/i) || ua.match(/WebOs/i))
+ ui.mobile = true;
+ ui.mobiledetected = true;
+ return ui.mobile;
+};
+
+/**
+ * Convert a CSS position to a numeric position.
+ */
+ui.numpos = function(p) {
+ return p == ''? 0 : Number(p.substr(0, p.length - 2));
+};
+
+/**
+ * Convert a numeric position to a CSS pixel position.
+ */
+ui.pixpos = function(p) {
+ return p + 'px';
+};
+
+/**
+ * Default orientation change behavior.
+ */
+ui.onorientationchange = function(e) {
+
+ // Scroll to the top and hide the address bar
+ window.scrollTo(0, 0);
+
+ // Change fixed position elements to absolute then back to fixed
+ // to make sure they're correctly layed out after the orientation
+ // change
+ map(function(e) {
+ e.style.position = 'absolute';
+ return e;
+ }, ui.elementsByClassName(document, 'fixed'));
+
+ setTimeout(function() {
+ map(function(e) {
+ e.style.position = 'fixed';
+ return e;
+ }, ui.elementsByClassName(document, 'fixed'));
+ }, 0);
+ return true;
+};
+
+/**
+ * Default page load behavior.
+ */
+ui.onload = function() {
+
+ // Scroll to the top and hide the address bar
+ window.scrollTo(0, 0);
+
+ // Initialize fixed position elements only after the page is loaded,
+ // to workaround layout issues with fixed position on mobile devices
+ setTimeout(function() {
+ map(function(e) {
+ e.style.position = 'fixed';
+ return e;
+ }, ui.elementsByClassName(document, 'fixed'));
+ }, 0);
+ return true;
+};
+
+/**
+ * Navigate to a new document.
+ */
+ui.navigate = function(url, win) {
+ //log('navigate', url, win);
+
+ // Open a new window
+ if (win == '_blank') {
+ window.top.open(url, win);
+ return false;
+ }
+
+ // Open a new document in the current window
+ if (win == '_self') {
+ window.top.open(url, win);
+ return false;
+ }
+
+ // Reload the current window
+ if (win == '_reload') {
+ window.top.location = url;
+ window.top.location.reload();
+ return false;
+ }
+
+ // Let the current top window handle the navigation
+ if (win == '_view') {
+ if (!window.top.onnavigate)
+ return window.top.open(url, '_self');
+ window.top.onnavigate(url);
+ return false;
+ }
+
+ window.top.open(url, win);
+ return false;
+}
+
+/**
+ * Build a portable <a href> tag.
+ */
+ui.href = function(id, loc, target, html) {
+ if (target == '_blank')
+ return '<a id="' + id + '" href="' + loc + '" target="_blank">' + html + '</a>';
+ return '<a id="' + id + '" href="' + loc + '" onclick="return ui.navigate(\'' + loc + '\', \'' + target + '\');">' + html + '</a>';
+};
+
+/**
+ * Build a menu bar.
+ */
+ui.menu = function(id, name, href, target, hilight) {
+ function Menu() {
+ this.content = function() {
+ if (hilight == true)
+ return ui.href(id, href, target, '<span class="tbarsmenu">' + name + '</span>');
+ else if (hilight == false)
+ return ui.href(id, href, target, '<span class="tbaramenu">' + name + '</span>');
+ else
+ return ui.href(id, href, target, '<span class="' + hilight + '">' + name + '</span>');
+ };
+ }
+ return new Menu();
+};
+
+ui.menufunc = function(id, name, fun, hilight) {
+ function Menu() {
+ this.content = function() {
+ function href(id, fun, html) {
+ return '<a id="' + id + '" href="/" onclick="' + fun + '">' + html + '</a>';
+ }
+
+ if (hilight == true)
+ return href(id, fun, '<span class="tbarsmenu">' + name + '</span>');
+ else if (hilight == false)
+ return href(id, fun, '<span class="tbaramenu">' + name + '</span>');
+ else
+ return href(id, fun, '<span class="' + hilight + '">' + name + '</span>');
+ };
+ }
+ return new Menu();
+};
+
+ui.menubar = function(left, right) {
+ var bar = '';
+ for (i in left)
+ bar = bar + '<span class="tbarleft">' + left[i].content() + '</span>';
+ for (i in right)
+ bar = bar + '<span class="tbarright">' + right[i].content() + '</span>';
+ return bar;
+};
+
+/**
+ * Convert a list of elements to an HTML table.
+ */
+ui.datatable = function(l) {
+
+ function indent(i) {
+ if (i == 0)
+ return '';
+ return '&nbsp;&nbsp;' + indent(i - 1);
+ }
+
+ function rows(l, i) {
+ if (isNil(l))
+ return '';
+ var e = car(l);
+
+ // Convert a list of simple values into a list of name value pairs
+ if (!isList(e))
+ return rows(expandElementValues("'value", l), i);
+
+ // Convert a list of complex values into a list of name value pairs
+ if (isList(car(e)))
+ return rows(expandElementValues("'value", l), i);
+
+ // Generate table row for a simple element value
+ if (elementHasValue(e)) {
+ var v = elementValue(e);
+ if (!isList(v)) {
+ return '<tr><td class="datatdl">' + indent(i) + elementName(e).slice(1) + '</td>' +
+ '<td class="datatdr tdw">' + (v != null? v : '') + '</td></tr>' +
+ rows(cdr(l), i);
+ }
+
+ return rows(expandElementValues(elementName(e), v), i) + rows(cdr(l), i);
+ }
+
+ // Generate table row for an element with children
+ return '<tr><td class="datatdl">' + indent(i) + elementName(e).slice(1) + '</td>' +
+ '<td class="datatdr tdw">' + '</td></tr>' +
+ rows(elementChildren(e), i + 1) +
+ rows(cdr(l), i);
+ }
+
+ return '<table class="datatable ' + (window.name == 'dataFrame'? ' databg' : '') + '" style="width: 100%;">' + rows(l, 0) + '</table>';
+}
+
+/**
+ * Convert a list of elements to an HTML single column table.
+ */
+ui.datalist = function(l) {
+
+ function rows(l, i) {
+ if (isNil(l))
+ return '';
+ var e = car(l);
+
+ // Convert a list of simple values into a list of name value pairs
+ if (!isList(e))
+ return rows(expandElementValues("'value", l), i);
+
+ // Convert a list of complex values into a list of name value pairs
+ if (isList(car(e)))
+ return rows(expandElementValues("'value", l), i);
+
+ // Generate table row for a simple element value
+ if (elementHasValue(e)) {
+ var v = elementValue(e);
+ if (!isList(v)) {
+ return '<tr><td class="datatd tdw">' + (v != null? v : '') + '</td></tr>' +
+ rows(cdr(l), i);
+ }
+
+ return rows(expandElementValues(elementName(e), v), i) + rows(cdr(l), i);
+ }
+
+ // Generate rows for an element's children
+ return rows(elementChildren(e), i + 1) + rows(cdr(l), i);
+ }
+
+ return '<table class="datatable ' + (window.name == 'dataFrame'? ' databg' : '') + '" style="width: 100%;">' + rows(l, 0) + '</table>';
+}
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/js/htdocs/util.js b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/util.js
new file mode 100644
index 0000000000..0f7de94289
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/util.js
@@ -0,0 +1,471 @@
+/*
+ * 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.
+ */
+
+/**
+ * Simple utility functions.
+ */
+
+/**
+ * Scheme-like lists.
+ */
+function cons(car, cdr) {
+ var a = new Array();
+ a.push(car);
+ return a.concat(cdr);
+}
+
+function car(l) {
+ return l[0];
+}
+
+function first(l) {
+ return l[0];
+}
+
+function cdr(l) {
+ return l.slice(1);
+}
+
+function rest(l) {
+ return l.slice(1);
+}
+
+function cadr(l) {
+ return l[1];
+}
+
+function cddr(l) {
+ return l.slice(2);
+}
+
+function caddr(l) {
+ return l[2];
+}
+
+function cdddr(l) {
+ return l.slice(3);
+}
+
+function cadddr(l) {
+ return l[3];
+}
+
+function append(a, b) {
+ return a.concat(b);
+}
+
+function reverse(l) {
+ return l.slice(0).reverse();
+}
+
+function range(a, b) {
+ var l = new Array();
+ for (var x = a; x < b; x++)
+ l.push(x);
+ return l;
+}
+
+function isNil(v) {
+ return (v == null || typeof v == 'undefined' || (v.constructor == Array && v.length == 0));
+}
+
+function isSymbol(v) {
+ return (typeof v == 'string' && v.slice(0, 1) == "'");
+}
+
+function isString(v) {
+ return (typeof v == 'string' && v.slice(0, 1) != "'");
+}
+
+function isList(v) {
+ return (v != null && typeof v != 'undefined' && v.constructor == Array);
+}
+
+function isTaggedList(v, t) {
+ return (isList(v) && !isNil(v) && car(v) == t);
+}
+
+var emptylist = new Array();
+
+function mklist() {
+ if (arguments.length == 0)
+ return emptylist;
+ var a = new Array();
+ for (i = 0; i < arguments.length; i++)
+ a[i] = arguments[i];
+ return a;
+}
+
+function length(l) {
+ return l.length;
+}
+
+/**
+ * Scheme-like associations.
+ */
+function assoc(k, l) {
+ if (isNil(l))
+ return emptylist;
+ var n = l.length;
+ for(var i = 0; i < n; i++) {
+ if (k == car(l[i]))
+ return l[i];
+ }
+ return emptylist;
+}
+
+/**
+ * Map, filter and reduce functions.
+ */
+function map(f, l) {
+ if (isNil(l))
+ return l;
+ var n = l.length;
+ var a = new Array();
+ for(var i = 0; i < n; i++) {
+ a.push(f(l[i]));
+ }
+ return a;
+}
+
+function filter(f, l) {
+ if (isNil(l))
+ return l;
+ var n = l.length;
+ var a = new Array();
+ for(var i = 0; i < n; i++) {
+ if (f(l[i]))
+ a.push(l[i]);
+ }
+ return a;
+}
+
+function reduce(f, i, l) {
+ if (isNil(l))
+ return i;
+ return reduce(f, f(i, car(l)), cdr(l));
+}
+
+/**
+ * Split a path into a list of segments.
+ */
+function tokens(path) {
+ return filter(function(s) { return length(s) != 0; }, path.split("/"));
+}
+
+/**
+ * Debug log a value.
+ */
+var rconsole;
+
+function debug(v) {
+ try {
+ var s = '';
+ for (i = 0; i < arguments.length; i++) {
+ s = s + writeValue(arguments[i]);
+ if (i < arguments.length)
+ s = s + ' ';
+ }
+
+ if (rconsole) {
+ try {
+ rconsole.log(s);
+ } catch (e) {}
+ }
+ try {
+ console.log(s);
+ } catch (e) {}
+ } catch (e) {}
+ return true;
+}
+
+/**
+ * Dump an object to the console.
+ */
+function dump(o) {
+ try {
+ for (f in o) {
+ try {
+ debug('dump ' + f + '=' + o[f]);
+ } catch (e) {}
+ }
+ } catch (e) {}
+ return true;
+}
+
+/**
+ * Return true if the current browser is Internet Explorer.
+ */
+function isIE() {
+ if (typeof isIE.detected != 'undefined')
+ return isIE.detected;
+ isIE.detected = navigator.appName == 'Microsoft Internet Explorer';
+ return isIE.detected;
+};
+
+/**
+ * External build configuration.
+ */
+var config;
+if (isNil(config))
+ config = {};
+
+/**
+ * Simple assert function.
+ */
+function AssertException() {
+}
+
+AssertException.prototype.toString = function () {
+ return 'AssertException';
+};
+
+function assert(exp) {
+ if (!exp)
+ throw new AssertException();
+}
+
+/**
+ * Write a list of strings.
+ */
+function writeStrings(l) {
+ if (isNil(l))
+ return '';
+ var s = '';
+ var n = l.length;
+ for(var i = 0; i < n; i++) {
+ s = s + l[i];
+ }
+ return s;
+}
+
+/**
+ * Write a value using a Scheme-like syntax.
+ */
+function writeValue(v) {
+ function writePrimitive(p) {
+ if (isSymbol(p))
+ return '' + p.substring(1);
+ if (isString(p))
+ return '"' + p + '"';
+ return '' + p;
+ }
+
+ function writeList(l) {
+ if (isNil(l))
+ return '';
+ return ' ' + writeValue(car(l)) + writeList(cdr(l));
+ }
+
+ if (!isList(v))
+ return writePrimitive(v);
+ if (isNil(v))
+ return '()';
+ return '(' + writeValue(car(v)) + writeList(cdr(v)) + ')';
+}
+
+/**
+ * Apply a function and memoize its result.
+ */
+function memo(obj, key, f) {
+ if (!('memo' in obj)) {
+ obj.memo = {};
+ return obj.memo[key] = f();
+ }
+ if (key in obj.memo)
+ return obj.memo[key];
+ return obj.memo[key] = f();
+}
+
+/**
+ * Un-memoize stored results.
+ */
+function unmemo(obj) {
+ obj.memo = {};
+ return true;
+}
+
+/**
+ * Local storage.
+ */
+var lstorage = {};
+lstorage.enabled = true;
+
+/**
+ * Get an item.
+ */
+lstorage.getItem = function(k) {
+ if (!lstorage.enabled)
+ return null;
+ try {
+ return localStorage.getItem(k);
+ } catch(e) {
+ return null;
+ }
+}
+
+/**
+ * Set an item.
+ */
+lstorage.setItem = function(k, v) {
+ if (!lstorage.enabled)
+ return null;
+ try {
+ return localStorage.setItem(k, v);
+ } catch(e) {
+ return null;
+ }
+}
+
+/**
+ * Remove an item.
+ */
+lstorage.removeItem = function(k) {
+ if (!lstorage.enabled)
+ return null;
+ try {
+ return localStorage.removeItem(k);
+ } catch(e) {
+ return null;
+ }
+}
+
+/**
+ * Returns a list of the properties of an object.
+ */
+function properties(o) {
+ var a = new Array();
+ for (p in o)
+ a.push(p);
+ return a;
+}
+
+/**
+ * Convert a host name to a domain name.
+ */
+function domainname(host) {
+ var ds = host.indexOf('//');
+ if (ds != -1)
+ return domainname(host.substring(ds + 2));
+ var s = host.indexOf('/');
+ if (s != -1)
+ return domainname(host.substring(0, s));
+ var h = reverse(host.split('.'));
+ var d = (!isNil(cddr(h)) && caddr(h) == 'www')? mklist(car(h), cadr(h), caddr(h)) : mklist(car(h), cadr(h));
+ return reverse(d).join('.');
+}
+
+/**
+ * Return true if a host name is a subdomain.
+ */
+function issubdomain(host) {
+ return host.split('.').length > 2;
+}
+
+/**
+ * Return true if the document cookie contains auth information.
+ */
+function hasauthcookie() {
+ return !isNil(document.cookie) &&
+ (document.cookie.indexOf('TuscanyOpenAuth=') != -1 ||
+ document.cookie.indexOf('TuscanyOAuth1=') != -1 ||
+ document.cookie.indexOf('TuscanyOAuth2=') != -1 ||
+ document.cookie.indexOf('TuscanyOpenIDAuth=') != -1);
+}
+
+/**
+ * Clear auth information from the document cookie.
+ */
+function clearauthcookie() {
+ document.cookie = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+ document.cookie = 'TuscanyOAuth1=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+ document.cookie = 'TuscanyOAuth2=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+ document.cookie = 'TuscanyOpenIDAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+ return true;
+}
+
+/**
+ * Format a string like Python format.
+ */
+function format() {
+ var i = 0;
+ var s = '';
+ for (a in arguments) {
+ s = i == 0? arguments[a] : s.replace('{' + a + '}', arguments[a]);
+ i++;
+ }
+ return s;
+}
+
+/**
+ * Functions with side effects. Use with moderation.
+ */
+
+/**
+ * Set the car of a list.
+ */
+function setcar(l, v) {
+ l[0] = v;
+ return l;
+}
+
+/**
+ * Set the cadr of a list.
+ */
+function setcadr(l, v) {
+ l[1] = v;
+ return l;
+}
+
+/**
+ * Set the caddr of a list.
+ */
+function setcaddr(l, v) {
+ l[2] = v;
+ return l;
+}
+
+/**
+ * Append the elements of a list to a list.
+ */
+function setappend(a, b) {
+ if (isNil(b))
+ return a;
+ a.push(car(b));
+ return setappend(a, cdr(b));
+}
+
+/**
+ * Set the cdr of a list.
+ */
+function setcdr(a, b) {
+ a.length = 1;
+ return setappend(a, b);
+}
+
+/**
+ * Set the contents of a list.
+ */
+function setlist(a, b) {
+ if (b == a)
+ return b;
+ a.length = 0;
+ return setappend(a, b);
+}
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/js/htdocs/xmlutil.js b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/xmlutil.js
new file mode 100644
index 0000000000..4d943cce75
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/js/htdocs/xmlutil.js
@@ -0,0 +1,256 @@
+/*
+ * 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.
+ */
+
+/**
+ * XML handling functions.
+ */
+
+/**
+ * Convert a DOM node list to a regular list.
+ */
+function nodeList(n) {
+ var l = new Array();
+ if (isNil(n))
+ return l;
+ for (var i = 0; i < n.length; i++)
+ l[i] = n[i];
+ return l;
+}
+
+/**
+ * Append a list of nodes to a parent node.
+ */
+function appendNodes(nodes, p) {
+ if (isNil(nodes))
+ return p;
+ p.appendChild(car(nodes));
+ return appendNodes(cdr(nodes), p);
+};
+
+/**
+ * Return the child attributes of an element.
+ */
+function childAttributes(e) {
+ return filter(function(n) { return n.nodeType == 2; }, nodeList(e.attributes));
+}
+
+/**
+ * Return the child elements of an element.
+ */
+function childElements(e) {
+ return filter(function(n) { return n.nodeType == 1; }, nodeList(e.childNodes));
+}
+
+/**
+ * Return the child text nodes of an element.
+ */
+function childText(e) {
+ function trim(s) {
+ return s.replace(/^\s*/, '').replace(/\s*$/, '');
+ }
+ return filter(function(n) { return n.nodeType == 3 && trim(n.nodeValue) != ''; }, nodeList(e.childNodes));
+}
+
+/**
+ * Read a list of XML attributes.
+ */
+function readAttributes(p, a) {
+ if (isNil(a))
+ return a;
+ var x = car(a);
+ return cons(mklist(attribute, "'" + x.nodeName, x.nodeValue), readAttributes(p, cdr(a)));
+}
+
+/**
+ * Read an XML element.
+ */
+function readElement(e, childf) {
+ var l = append(append(mklist(element, "'" + e.nodeName), readAttributes(e, childf(e))), readElements(childElements(e), childf));
+ var t = childText(e);
+ if (isNil(t))
+ return l;
+ return append(l, mklist(car(t).nodeValue));
+}
+
+/**
+ * Read a list of XML elements.
+ */
+function readElements(l, childf) {
+ if (isNil(l))
+ return l;
+ return cons(readElement(car(l), childf), readElements(cdr(l), childf));
+}
+
+/**
+ * Return true if a list of strings contains an XML document.
+ */
+function isXML(l) {
+ if (isNil(l))
+ return false;
+ return car(l).substring(0, 5) == '<?xml';
+}
+
+/**
+ * Parse a list of strings representing an XML document.
+ */
+function parseXML(l) {
+ var s = writeStrings(l);
+ var p = new DOMParser();
+ return p.parseFromString(s, "text/xml");
+}
+
+/**
+ * Read a list of values from an XML document.
+ */
+function readXMLDocument(doc) {
+ var root = childElements(doc);
+ if (isNil(root))
+ return mklist();
+ return mklist(readElement(car(root), childAttributes));
+}
+
+/**
+ * Read a list of values from an XHTML element.
+ */
+function readXHTMLElement(xhtml) {
+ // Special XHTML attribute filtering on IE
+ function ieChildAttributes(e) {
+ var a = filter(function(n) {
+ // Filter out empty and internal DOM attributes
+ if (n.nodeType != 2 || isNil(n.nodeValue) || n.nodeValue == '')
+ return false;
+ if (n.nodeName == 'contentEditable' || n.nodeName == 'maxLength' || n.nodeName == 'loop' || n.nodeName == 'start')
+ return false;
+ return true;
+ }, nodeList(e.attributes));
+
+ if (e.style.cssText == '')
+ return a;
+
+ // Add style attribute
+ var sa = new Object();
+ sa.nodeName = 'style';
+ sa.nodeValue = e.style.cssText;
+ return cons(sa, a);
+ }
+
+ var childf = (typeof(XMLSerializer) != 'undefined')? childAttributes : ieChildAttributes;
+ return mklist(readElement(xhtml, childf));
+}
+
+/**
+ * Read a list of values from a list of strings representing an XML document.
+ */
+function readXML(l) {
+ return readXMLDocument(parseXML(l));
+}
+
+/**
+ * Return a list of strings representing an XML document.
+ */
+function writeXMLDocument(doc) {
+ if (typeof(XMLSerializer) != 'undefined')
+ return mklist(new XMLSerializer().serializeToString(doc));
+ return mklist(doc.xml);
+}
+
+/**
+ * Write a list of XML element and attribute tokens.
+ */
+function expandElementValues(n, l) {
+ if (isNil(l))
+ return l;
+ return cons(cons(element, cons(n, car(l))), expandElementValues(n, cdr(l)));
+}
+
+function writeList(l, node, doc) {
+ if (isNil(l))
+ return node;
+
+ var token = car(l);
+ if (isTaggedList(token, attribute)) {
+ if (isIE()) {
+ var aname = attributeName(token).substring(1);
+ if (aname != 'xmlns')
+ node.setAttribute(aname, '' + attributeValue(token));
+ } else
+ node.setAttribute(attributeName(token).substring(1), '' + attributeValue(token));
+
+ } else if (isTaggedList(token, element)) {
+
+ function mkelem(tok, doc) {
+ function xmlns(l) {
+ if (isNil(l))
+ return null;
+ var t = car(l);
+ if (isTaggedList(t, attribute)) {
+ if (attributeName(t).substring(1) == 'xmlns')
+ return attributeValue(t);
+ }
+ return xmlns(cdr(l));
+ }
+
+ var ns = xmlns(elementChildren(tok));
+ if (isIE())
+ return doc.createElementNS(ns != null? ns : node.namespaceURI, elementName(tok).substring(1));
+ if (ns == null)
+ return doc.createElement(elementName(tok).substring(1));
+ return doc.createElementNS(ns, elementName(tok).substring(1));
+ }
+
+ if (elementHasValue(token)) {
+ var v = elementValue(token);
+ if (isList(v)) {
+ var e = expandElementValues(elementName(token), v);
+ writeList(e, node, doc);
+ } else {
+ var child = mkelem(token, doc);
+ writeList(elementChildren(token), child, doc);
+ node.appendChild(child);
+ }
+ } else {
+ var child = mkelem(token, doc);
+ writeList(elementChildren(token), child, doc);
+ node.appendChild(child);
+ }
+ } else
+ node.appendChild(doc.createTextNode('' + token));
+
+ writeList(cdr(l), node, doc);
+ return node;
+}
+
+/**
+ * Make a new XML document.
+ */
+function mkXMLDocument() {
+ return document.implementation.createDocument('', '', null);
+}
+
+/**
+ * Convert a list of values to a list of strings representing an XML document.
+ */
+function writeXML(l, xmlTag) {
+ var doc = mkXMLDocument();
+ writeList(l, doc, doc);
+ if (!xmlTag)
+ return writeXMLDocument(doc);
+ return mklist('<?xml version="1.0" encoding="UTF-8"?>\n' + writeXMLDocument(doc) + '\n');
+}
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/js/js-conf b/sca-cpp/branches/lightweight-sca/modules/js/js-conf
new file mode 100755
index 0000000000..aa29920619
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/js/js-conf
@@ -0,0 +1,55 @@
+#!/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.
+
+# Add Javascript scripts and CSS to a server conf
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+cat >>$root/conf/httpd.conf <<EOF
+# Generated by: js-conf $*
+# Serve JavaScript scripts and CSS
+Alias /ui-min.css $here/htdocs/ui-min.css
+Alias /all-min.js $here/htdocs/all-min.js
+Alias /proxy/ui-min.css $here/htdocs/ui-min.css
+Alias /proxy/all-min.js $here/htdocs/all-min.js
+
+EOF
+
+cat >>$root/conf/pubauth.conf <<EOF
+# Generated by: js-conf $*
+<Location /ui-min.css>
+AuthType None
+Require all granted
+</Location>
+<Location /all-min.js>
+AuthType None
+Require all granted
+</Location>
+<Location /proxy/ui-min.css>
+AuthType None
+Require all granted
+</Location>
+<Location /proxy/all-min.js>
+AuthType None
+Require all granted
+</Location>
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/js/js-shell.cpp b/sca-cpp/branches/lightweight-sca/modules/js/js-shell.cpp
new file mode 100644
index 0000000000..ee0fa89b31
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/js/js-shell.cpp
@@ -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.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Evaluate a JavaScript script.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "fstream.hpp"
+#include "string.hpp"
+#include "eval.hpp"
+
+namespace tuscany {
+namespace js {
+
+bool eval() {
+ ostringstream os;
+ for(;;) {
+ int c = cin.get();
+ if (c == -1)
+ break;
+ os << (char)c;
+ }
+ failable<value> v = evalScript(str(os));
+ assert(hasContent(v));
+ cout << v;
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::js::eval();
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/js/js-test.cpp b/sca-cpp/branches/lightweight-sca/modules/js/js-test.cpp
new file mode 100644
index 0000000000..a7e5597610
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/js/js-test.cpp
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test JavaScript evaluation functions.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "eval.hpp"
+
+namespace tuscany {
+namespace js {
+
+bool testJSEval() {
+ failable<value> v = evalScript("(function testJSON(n){ return JSON.parse(JSON.stringify(n)) })(5)");
+ assert(hasContent(v));
+ assert(content(v) == value(5));
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::gc_scoped_pool p;
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::js::testJSEval();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/js/json-test.js b/sca-cpp/branches/lightweight-sca/modules/js/json-test.js
new file mode 100644
index 0000000000..fb53fa6347
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/js/json-test.js
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Test JSON data encoding functions.
+ */
+function testJSON() {
+ var ad = mklist(mklist(attribute, "'city", "san francisco"), mklist(attribute, "'state", "ca"));
+ var ac = mklist(mklist(element, "'id", "1234"), mklist(attribute, "'balance", 1000));
+ var cr = mklist(mklist(attribute, "'name", "jdoe"), cons(element, cons("'address", ad)), cons(element, cons("'account", ac)));
+ var c = mklist(cons(element, cons("'customer", cr)));
+ var s = json.writeJSON(c);
+ assert(car(s) == "{\"customer\": {\"@name\": \"jdoe\", \"address\": {\"@city\": \"san francisco\", \"@state\": \"ca\"}, \"account\": {\"id\": \"1234\", \"@balance\": 1000}}}");
+
+ var phones = mklist("408-1234", "650-1234");
+ var l = mklist(mklist(element, "'phones", phones), mklist(element, "'lastName", "test\ttab"), mklist(attribute, "'firstName", "test1"));
+ var s2 = json.writeJSON(l);
+ assert(car(s2) == "{\"phones\": [\"408-1234\", \"650-1234\"], \"lastName\": \"test\\ttab\", \"@firstName\": \"test1\"}");
+
+ var r = json.readJSON(s2);
+ var w = json.writeJSON(r);
+ assert(car(w) == "{\"phones\": [\"408-1234\", \"650-1234\"], \"lastName\": \"test\\ttab\", \"@firstName\": \"test1\"}");
+
+ var l4 = mklist(mklist("'ns1:echoString", mklist("'@xmlns:ns1", "http://ws.apache.org/axis2/services/echo"), mklist("'text", "Hello World!")));
+ var s4 = json.writeJSON(valuesToElements(l4));
+ assert(car(s4) == "{\"ns1:echoString\": {\"@xmlns:ns1\": \"http://ws.apache.org/axis2/services/echo\", \"text\": \"Hello World!\"}}");
+
+ var r5 = elementsToValues(json.readJSON(s4));
+ var s5 = json.writeJSON(valuesToElements(r5));
+ assert(car(s5) == "{\"ns1:echoString\": {\"@xmlns:ns1\": \"http://ws.apache.org/axis2/services/echo\", \"text\": \"Hello World!\"}}");
+ return true;
+}
+
+testJSON();
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/js/util-test b/sca-cpp/branches/lightweight-sca/modules/js/util-test
new file mode 100755
index 0000000000..c69f46c67c
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/js/util-test
@@ -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.
+
+# Run Javascript util test cases
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+
+echo "Testing..."
+cat htdocs/util.js htdocs/elemutil.js htdocs/jsonutil.js json-test.js | ./js-shell 2>/dev/null 1>&2
+rc=$?
+if [ "$rc" = "0" ]; then
+ echo "OK"
+fi
+
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/modules/opencl/Makefile.am b/sca-cpp/branches/lightweight-sca/modules/opencl/Makefile.am
new file mode 100644
index 0000000000..3e76a435c8
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/opencl/Makefile.am
@@ -0,0 +1,58 @@
+# 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.
+
+
+if WANT_OPENCL
+
+INCLUDES = -I${OPENCL_INCLUDE}
+
+incl_HEADERS = *.hpp
+incldir = $(prefix)/include/modules/opencl
+
+dist_mod_SCRIPTS = opencl-conf
+moddir = $(prefix)/modules/opencl
+
+EXTRA_DIST = domain-test.composite server-test.cl
+
+if DARWIN
+OPENCL_FLAGS = -framework OpenCL
+else
+OPENCL_FLAGS = -L${OPENCL_LIB} -R${OPENCL_LIB} -lOpenCL
+endif
+
+#mod_LTLIBRARIES = libmod_tuscany_opencl.la
+#libmod_tuscany_opencl_la_SOURCES = mod-opencl.cpp
+#libmod_tuscany_opencl_la_LDFLAGS = -lxml2 -lcurl -lmozjs -framework OpenCL
+#noinst_DATA = libmod_tuscany_opencl${libsuffix}
+#libmod_tuscany_opencl${libsuffix}:
+# ln -s .libs/libmod_tuscany_opencl${libsuffix}
+
+opencl_test_SOURCES = opencl-test.cpp
+opencl_test_LDFLAGS = ${OPENCL_FLAGS}
+
+opencl_shell_SOURCES = opencl-shell.cpp
+opencl_shell_LDFLAGS = ${OPENCL_FLAGS}
+
+client_test_SOURCES = client-test.cpp
+client_test_LDFLAGS = -lxml2 -lcurl -lmozjs
+
+dist_noinst_SCRIPTS = server-test
+noinst_PROGRAMS = opencl-test client-test
+mod_PROGRAMS = opencl-shell
+TESTS = opencl-test
+
+endif
diff --git a/sca-cpp/branches/lightweight-sca/modules/opencl/client-test.cpp b/sca-cpp/branches/lightweight-sca/modules/opencl/client-test.cpp
new file mode 100644
index 0000000000..7af3cc73d2
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/opencl/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 "../server/client-test.hpp"
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+ tuscany::server::testURI = "http://localhost:8090/opencl";
+
+ tuscany::server::testServer();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/opencl/domain-test.composite b/sca-cpp/branches/lightweight-sca/modules/opencl/domain-test.composite
new file mode 100644
index 0000000000..e69399d581
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/opencl/domain-test.composite
@@ -0,0 +1,31 @@
+<?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"
+ targetNamespace="http://domain/test"
+ name="domain-test">
+
+ <component name="opencl-test">
+ <implementation.opencl kernel="server-test.cl"/>
+ <service name="test">
+ <binding.http uri="opencl"/>
+ </service>
+ </component>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/modules/opencl/driver.hpp b/sca-cpp/branches/lightweight-sca/modules/opencl/driver.hpp
new file mode 100644
index 0000000000..b4b6c2845b
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/opencl/driver.hpp
@@ -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$ */
+
+#ifndef tuscany_opencl_driver_hpp
+#define tuscany_opencl_driver_hpp
+
+/**
+ * OpenCL evaluator main driver loop.
+ */
+
+#include "string.hpp"
+#include "stream.hpp"
+#include "monad.hpp"
+#include "../scheme/driver.hpp"
+#include "eval.hpp"
+
+namespace tuscany {
+namespace opencl {
+
+const value evalDriverLoop(const OpenCLProgram& clprog, istream& in, ostream& out, const OpenCLContext& cl) {
+ scheme::promptForInput(scheme::evalInputPrompt, out);
+ value input = scheme::readValue(in);
+ if (isNil(input))
+ return input;
+ const failable<value> output = evalKernel(createKernel(input, clprog), input, 1, value::String, 512, cl);
+ scheme::announceOutput(scheme::evalOutputPrompt, out);
+ scheme::userPrint(content(output), out);
+ return evalDriverLoop(clprog, in, out, cl);
+}
+
+const bool evalDriverRun(const char* path, istream& in, ostream& out) {
+ OpenCLContext cl(OpenCLContext::DEFAULT);
+ scheme::setupDisplay(out);
+ ifstream is(path);
+ failable<OpenCLProgram> clprog = readProgram(path, is, cl);
+ if (!hasContent(clprog))
+ return true;
+ evalDriverLoop(content(clprog), in, out, cl);
+ return true;
+}
+
+}
+}
+#endif /* tuscany_opencl_driver_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/modules/opencl/eval.hpp b/sca-cpp/branches/lightweight-sca/modules/opencl/eval.hpp
new file mode 100644
index 0000000000..5606b2f57a
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/opencl/eval.hpp
@@ -0,0 +1,724 @@
+/*
+ * 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_opencl_eval_hpp
+#define tuscany_opencl_eval_hpp
+
+/**
+ * OpenCL kernel evaluation logic.
+ */
+#ifdef IS_DARWIN
+#include <OpenCL/opencl.h>
+#else
+#include <CL/cl.h>
+#endif
+
+#include "list.hpp"
+#include "value.hpp"
+#include "perf.hpp"
+
+namespace tuscany {
+namespace opencl {
+
+/**
+ * Convert an OpenCL error code to a string.
+ */
+const string clError(const cl_int e) {
+ ostringstream s;
+ s << "error " << e;
+ return str(s);
+}
+
+/**
+ * OpenCL profiling counters.
+ */
+#ifdef WANT_MAINTAINER_OPENCL_PROF
+
+cl_ulong memtime = 0;
+cl_ulong kernelqtime = 0;
+cl_ulong kerneltime = 0;
+cl_ulong preptime = 0;
+cl_ulong evaltime = 0;
+
+/**
+ * Reset the OpenCL profiling counters.
+ */
+bool resetOpenCLCounters() {
+ memtime = kernelqtime = kerneltime = preptime = evaltime = 0;
+ return true;
+}
+
+/**
+ * Print the OpenCL profiling counters.
+ */
+bool printOpenCLCounters(const long n) {
+ cout << " kernelq " << ((double)kernelqtime / 1000000.0) / (double)n << " ms kernel " << ((double)kerneltime / 1000000.0) / (double)n << " ms memory " << ((double)memtime / 1000000.0) / (double)n << " ms prep " << ((double)preptime / 1000000.0) / (double)n << " ms eval " << ((double)evaltime / 1000000.0) / (double)n << " ms";
+ return true;
+}
+
+/**
+ * Profile a memory event.
+ */
+failable<cl_ulong> profileMemEvent(const cl_event evt) {
+ cl_ulong start = 0;
+ const cl_int serr = clGetEventProfilingInfo(evt, CL_PROFILING_COMMAND_START, sizeof(cl_ulong), &start, NULL);
+ if (serr != CL_SUCCESS)
+ return mkfailure<cl_ulong>("Couldn't profile memory event start: " + clError(serr));
+ cl_ulong end = 0;
+ const cl_int eerr = clGetEventProfilingInfo(evt, CL_PROFILING_COMMAND_END, sizeof(cl_ulong), &end, NULL);
+ if (eerr != CL_SUCCESS)
+ return mkfailure<cl_ulong>("Couldn't profile memory event end: " + clError(eerr));
+ const cl_ulong t = end - start;
+ memtime += t;
+ return t;
+}
+
+/**
+ * Profile a kernel event.
+ */
+failable<cl_ulong> profileKernelEvent(const cl_event evt) {
+ cl_ulong queued = 0;
+ const cl_int qerr = clGetEventProfilingInfo(evt, CL_PROFILING_COMMAND_QUEUED, sizeof(cl_ulong), &queued, NULL);
+ if (qerr != CL_SUCCESS)
+ return mkfailure<cl_ulong>("Couldn't profile kernel event queue: " + clError(qerr));
+ cl_ulong start = 0;
+ const cl_int serr = clGetEventProfilingInfo(evt, CL_PROFILING_COMMAND_START, sizeof(cl_ulong), &start, NULL);
+ if (serr != CL_SUCCESS)
+ return mkfailure<cl_ulong>("Couldn't profile kernel event start: " + clError(serr));
+ cl_ulong end = 0;
+ const cl_int eerr = clGetEventProfilingInfo(evt, CL_PROFILING_COMMAND_END, sizeof(cl_ulong), &end, NULL);
+ if (eerr != CL_SUCCESS)
+ return mkfailure<cl_ulong>("Couldn't profile kernel event end: " + clError(eerr));
+ const cl_ulong q = start - queued;
+ kernelqtime += q;
+ const cl_ulong t = end - start;
+ kerneltime += t;
+ return t;
+}
+
+/**
+ * Profile an array of memory events.
+ */
+failable<cl_ulong> profileMemEvents(const cl_uint n, const cl_event* evt) {
+ if (n == 0)
+ return 0;
+ const failable<cl_ulong> t = profileMemEvent(*evt);
+ if (!hasContent(t))
+ return t;
+ const failable<cl_ulong> r = profileMemEvents(n - 1, evt + 1);
+ if (!hasContent(r))
+ return r;
+ return content(t) + content(r);
+}
+
+#else
+
+#define resetOpenCLCounters()
+#define printOpenCLCounters(n)
+
+#endif
+
+class OpenCLContext;
+class OpenCLProgram;
+class OpenCLKernel;
+class OpenCLBuffer;
+
+/**
+ * Represent an OpenCL context.
+ */
+class OpenCLContext {
+public:
+#define OPENCL_MAX_DEVICES 64
+
+ enum DeviceType {
+ DEFAULT = 0, CPU = 1, GPU = 2
+ };
+
+ OpenCLContext(const OpenCLContext::DeviceType devtype) : dev(OpenCLContext::DEFAULT), ndevs(0), ctx(0) {
+ debug("opencl::OpenCLContext");
+ for (int i = 0; i < OPENCL_MAX_DEVICES; i++)
+ cq[i] = 0;
+
+ // Get the available platforms
+ cl_uint nplatforms;
+ cl_platform_id platforms[16];
+ const cl_int gperr = clGetPlatformIDs(16, platforms, &nplatforms);
+ if(nplatforms == 0 || gperr != CL_SUCCESS) {
+ mkfailure<bool>("Couldn't get OpenCL platforms: " + clError(gperr));
+ return;
+ }
+ for(cl_uint i = 0; i < nplatforms; ++i) {
+ char vendor[256];
+ const cl_int gverr = clGetPlatformInfo(platforms[i], CL_PLATFORM_VENDOR, sizeof(vendor), vendor, NULL);
+ if(gverr != CL_SUCCESS) {
+ mkfailure<bool>("Couldn't get OpenCL platform: " + clError(gverr));
+ return;
+ }
+ debug(vendor, "opencl::OpenCLContext::vendor");
+ }
+
+ // Get the available devices of the requested type
+ if (devtype == OpenCLContext::DEFAULT || devtype == OpenCLContext::GPU) {
+ for(cl_uint i = 0; i < nplatforms; ++i) {
+ const cl_int err = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_GPU, OPENCL_MAX_DEVICES, devid, &ndevs);
+ if (err == CL_SUCCESS) {
+ debug(ndevs, "opencl::OpenCLContext::gcpus");
+ dev = OpenCLContext::GPU;
+ break;
+ }
+ }
+ }
+ if ((devtype == OpenCLContext::DEFAULT && ndevs == 0) || devtype == OpenCLContext::CPU) {
+ for(cl_uint i = 0; i < nplatforms; ++i) {
+ const cl_int err = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_CPU, OPENCL_MAX_DEVICES, devid, &ndevs);
+ if (err == CL_SUCCESS) {
+ debug(ndevs, "opencl::OpenCLContext::ncpus");
+ dev = OpenCLContext::CPU;
+ break;
+ }
+ }
+ }
+ if (ndevs == 0)
+ return;
+
+ // Initialize OpenCL context and command queues
+ cl_int ccerr;
+ ctx = clCreateContext(0, ndevs, devid, NULL, NULL, &ccerr);
+ if(!ctx || ccerr != CL_SUCCESS) {
+ mkfailure<bool>("Couldn't create OpenCL context: " + clError(ccerr));
+ return;
+ }
+
+ for (cl_uint i = 0; i < ndevs; i++) {
+ cl_int cqerr;
+#ifdef WANT_MAINTAINER_OPENCL_PROF
+ cq[i] = clCreateCommandQueue(ctx, devid[i], CL_QUEUE_PROFILING_ENABLE, &cqerr);
+#else
+ cq[i] = clCreateCommandQueue(ctx, devid[i], 0, &cqerr);
+#endif
+ if (!cq[i] || cqerr != CL_SUCCESS) {
+ mkfailure<bool>("Couldn't create OpenCL command queue: " + clError(cqerr));
+ return;
+ }
+ }
+ }
+
+ ~OpenCLContext() {
+ for (cl_uint i = 0; i < ndevs; i++) {
+ if (cq[i] != 0)
+ clReleaseCommandQueue(cq[i]);
+ }
+ if (ctx != 0)
+ clReleaseContext(ctx);
+ }
+
+private:
+ OpenCLContext::DeviceType dev;
+ cl_uint ndevs;
+ cl_device_id devid[OPENCL_MAX_DEVICES];
+ cl_context ctx;
+ cl_command_queue cq[OPENCL_MAX_DEVICES];
+
+ friend const cl_uint devices(const OpenCLContext& cl);
+ friend const cl_command_queue commandq(const OpenCLContext& cl);
+ friend const failable<OpenCLBuffer> readOnlyBuffer(const size_t size, const void* p, const OpenCLContext& cl, cl_command_queue cq);
+ friend const failable<OpenCLBuffer> writeOnlyBuffer(const size_t size, const OpenCLContext& cl);
+ friend const failable<value> evalKernel(const failable<OpenCLKernel>& kernel, const value& expr, const size_t gwsize, const value::ValueType type, const size_t n, const OpenCLContext& cl);
+ friend const failable<OpenCLProgram> readProgram(const string& path, istream& is, const OpenCLContext& cl);
+};
+
+/**
+ * Return the number of computing devices available in a context.
+ */
+const cl_uint devices(const OpenCLContext& cl) {
+ return cl.ndevs;
+}
+
+/**
+ * Return a command queue from a context.
+ */
+const cl_command_queue commandq(const OpenCLContext& cl) {
+ return cl.cq[0];
+}
+
+/**
+ * Represents an OpenCL program.
+ */
+class OpenCLProgram {
+public:
+ OpenCLProgram() : prog(0) {
+ }
+
+ OpenCLProgram(const cl_program prog) : prog(prog) {
+ }
+
+ OpenCLProgram(const OpenCLProgram& c) : prog(c.prog) {
+ const cl_int rperr = clRetainProgram(prog);
+ if (rperr != CL_SUCCESS)
+ mkfailure<bool>(string("Couldn't retain OpenCL program: ") + clError(rperr));
+ }
+
+ ~OpenCLProgram() {
+ if (!prog)
+ return;
+ const cl_int rperr = clReleaseProgram(prog);
+ if (rperr != CL_SUCCESS)
+ mkfailure<bool>(string("Couldn't release OpenCL program: ") + clError(rperr));
+ }
+
+private:
+ const cl_program prog;
+
+ friend const failable<OpenCLKernel> createKernel(const value& expr, const OpenCLProgram& clprog);
+ friend const failable<value> evalKernel(const failable<OpenCLKernel>& kernel, const value& expr, const size_t gwsize, const value::ValueType type, const size_t n, const OpenCLContext& cl);
+};
+
+/**
+ * Represents an OpenCL kernel.
+ */
+class OpenCLKernel {
+public:
+ OpenCLKernel() : k(0) {
+ }
+
+ OpenCLKernel(const cl_kernel k) : k(k) {
+ }
+
+ OpenCLKernel(const OpenCLKernel& c) : k(c.k) {
+ const cl_int rkerr = clRetainKernel(k);
+ if (rkerr != CL_SUCCESS)
+ mkfailure<bool>(string("Couldn't retain OpenCL kernel: ") + clError(rkerr));
+ }
+
+ ~OpenCLKernel() {
+ if (!k)
+ return;
+ const cl_int rkerr = clReleaseKernel(k);
+ if (rkerr != CL_SUCCESS)
+ mkfailure<bool>(string("Couldn't release OpenCL kernel: ") + clError(rkerr));
+ }
+
+private:
+ const cl_kernel k;
+
+ friend const failable<OpenCLBuffer> valueToKernelArg(const cl_uint i, const size_t size, const void* p, const failable<OpenCLBuffer>& buf, const OpenCLKernel& kernel);
+ friend const failable<value> evalKernel(const failable<OpenCLKernel>& kernel, const value& expr, const size_t gwsize, const value::ValueType type, const size_t n, const OpenCLContext& cl);
+};
+
+/**
+ * Represents an OpenCL buffer.
+ */
+class OpenCLBuffer {
+public:
+ OpenCLBuffer() : mem(0), evt(0) {
+ }
+
+ OpenCLBuffer(const cl_mem mem, const cl_event evt) : mem(mem), evt(evt) {
+ }
+
+ OpenCLBuffer(const OpenCLBuffer& c) : mem(c.mem), evt(c.evt) {
+ if (mem != 0) {
+ const cl_int rmerr = clRetainMemObject(mem);
+ if (rmerr != CL_SUCCESS)
+ mkfailure<bool>(string("Couldn't retain OpenCL buffer: ") + clError(rmerr));
+ }
+ if (evt != 0) {
+ const cl_int reerr = clRetainEvent(evt);
+ if (reerr != CL_SUCCESS)
+ mkfailure<bool>(string("Couldn't retain OpenCL event: ") + clError(reerr));
+ }
+ }
+
+ ~OpenCLBuffer() {
+ if (mem != 0) {
+ const cl_int rmerr = clReleaseMemObject(mem);
+ if (rmerr != CL_SUCCESS)
+ mkfailure<bool>(string("Couldn't release OpenCL buffer: ") + clError(rmerr));
+ }
+ if (evt != 0) {
+ const cl_int reerr = clReleaseEvent(evt);
+ if (reerr != CL_SUCCESS)
+ mkfailure<bool>(string("Couldn't release OpenCL event: ") + clError(reerr));
+ }
+ }
+
+private:
+ const cl_mem mem;
+ const cl_event evt;
+
+ friend const cl_uint writeBufferEvents(const list<OpenCLBuffer>& buf, cl_event* evt);
+ friend const failable<OpenCLBuffer> valueToKernelArg(const cl_uint i, const size_t size, const void* p, const failable<OpenCLBuffer>& buf, const OpenCLKernel& kernel);
+ friend const failable<value> evalKernel(const failable<OpenCLKernel>& kernel, const value& expr, const size_t gwsize, const value::ValueType type, const size_t n, const OpenCLContext& cl);
+};
+
+/**
+ * Return a read-only memory buffer.
+ */
+const failable<OpenCLBuffer> readOnlyBuffer(const size_t size, const void* p, const OpenCLContext& cl, const cl_command_queue cq) {
+ if (cl.dev == OpenCLContext::CPU) {
+ cl_int err;
+ const cl_mem buf = clCreateBuffer(cl.ctx, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, size, const_cast<void*>(p), &err);
+ if (!buf || err != CL_SUCCESS)
+ return mkfailure<OpenCLBuffer>(string("Couldn't map OpenCL host memory: ") + clError(err));
+ return OpenCLBuffer(buf, 0);
+ }
+ cl_int berr;
+ const cl_mem buf = clCreateBuffer(cl.ctx, CL_MEM_READ_ONLY, size, NULL, &berr);
+ if (!buf || berr != CL_SUCCESS)
+ return mkfailure<OpenCLBuffer>(string("Couldn't allocate OpenCL device memory: ") + clError(berr));
+ cl_event wevt;
+ const cl_int werr = clEnqueueWriteBuffer(cq, buf, CL_FALSE, 0, size, p, 0, NULL, &wevt);
+ if (werr != CL_SUCCESS) {
+ clReleaseMemObject(buf);
+ return mkfailure<OpenCLBuffer>(string("Couldn't enqueue write to device memory: ") + clError(werr));
+ }
+ return OpenCLBuffer(buf, wevt);
+}
+
+/**
+ * Fill an array of write events for a given list of buffers.
+ */
+const cl_uint writeBufferEvents(const list<OpenCLBuffer>& buf, cl_event* evt) {
+ if (isNil(buf))
+ return 0;
+ const cl_event e = car(buf).evt;
+ if (e == 0)
+ return writeBufferEvents(cdr(buf), evt);
+ *evt = e;
+ return 1 + writeBufferEvents(cdr(buf), evt + 1);
+}
+
+/**
+ * Return a write-only memory buffer.
+ */
+const failable<OpenCLBuffer> writeOnlyBuffer(const size_t size, const OpenCLContext& cl) {
+ if (cl.dev == OpenCLContext::CPU) {
+ cl_int err;
+ const cl_mem buf = clCreateBuffer(cl.ctx, CL_MEM_WRITE_ONLY | CL_MEM_ALLOC_HOST_PTR, size, NULL, &err);
+ if (!buf || err != CL_SUCCESS)
+ return mkfailure<OpenCLBuffer>(string("Couldn't map OpenCL host memory: ") + clError(err));
+ return OpenCLBuffer(buf, 0);
+ }
+ cl_int err;
+ const cl_mem buf = clCreateBuffer(cl.ctx, CL_MEM_WRITE_ONLY, size, NULL, &err);
+ if (!buf || err != CL_SUCCESS)
+ return mkfailure<OpenCLBuffer>(string("Couldn't allocate OpenCL device memory: ") + clError(err));
+ return OpenCLBuffer(buf, 0);
+}
+
+/**
+ * Convert a value to a kernel arg.
+ */
+const failable<OpenCLBuffer> valueToKernelArg(const cl_uint i, const size_t size, const void* p, const failable<OpenCLBuffer>& buf, const OpenCLKernel& kernel) {
+ if (!hasContent(buf))
+ return buf;
+ if (p != NULL) {
+ const cl_int err = clSetKernelArg(kernel.k, (cl_uint)i, size, p);
+ if (err != CL_SUCCESS)
+ return mkfailure<OpenCLBuffer>(string("Couldn't set OpenCL simple kernel arg: ") + clError(err));
+ return buf;
+ }
+ const OpenCLBuffer b = content(buf);
+ const cl_int err = clSetKernelArg(kernel.k, i, sizeof(cl_mem), &b.mem);
+ if (err != CL_SUCCESS)
+ return mkfailure<OpenCLBuffer>(string("Couldn't set OpenCL buffer kernel arg: ") + clError(err));
+ return buf;
+}
+
+const failable<OpenCLBuffer> valueToKernelArg(const value& v, const cl_uint i, const OpenCLKernel& kernel, const OpenCLContext& cl, const cl_command_queue cq) {
+ switch (type(v)) {
+ case value::Symbol: {
+ const string s = string("'") + v;
+ return valueToKernelArg(i, 0, NULL, readOnlyBuffer(length(s) + 1, c_str(s), cl, cq), kernel);
+ }
+ case value::String: {
+ const string s = (string)v;
+ return valueToKernelArg(i, 0, NULL, readOnlyBuffer(length(s) + 1, c_str(s), cl, cq), kernel);
+ }
+ case value::Number: {
+ const cl_float d = (cl_float)((double)v);
+ return valueToKernelArg(i, sizeof(cl_float), &d, OpenCLBuffer(), kernel);
+ }
+ case value::Bool: {
+ const cl_int b = (cl_int)((bool)v);
+ return valueToKernelArg(i, sizeof(cl_int), &b, OpenCLBuffer(), kernel);
+ }
+ default: {
+ return valueToKernelArg(i, sizeof(cl_mem), NULL, readOnlyBuffer(sizeof(value), &v, cl, cq), kernel);
+ }
+ }
+}
+
+/**
+ * Convert a list of values to kernel args.
+ */
+const failable<list<OpenCLBuffer>> valuesToKernelArgsListHelper(const list<value>& v, const cl_uint i, const OpenCLKernel& kernel, const OpenCLContext& cl, const cl_command_queue cq) {
+ if (isNil(v))
+ return list<OpenCLBuffer>();
+ const failable<OpenCLBuffer> a = valueToKernelArg(car(v), i, kernel, cl, cq);
+ if (!hasContent(a))
+ return mkfailure<list<OpenCLBuffer>>(a);
+ const failable<list<OpenCLBuffer>> al = valuesToKernelArgsListHelper(cdr(v), i + 1, kernel, cl, cq);
+ if (!hasContent(al))
+ return al;
+ return cons<OpenCLBuffer>(content(a), content(al));
+}
+
+const failable<list<OpenCLBuffer>> valuesToKernelArgs(const list<value>& v, const OpenCLKernel& kernel, const OpenCLContext& cl, const cl_command_queue cq) {
+ return valuesToKernelArgsListHelper(v, 0, kernel, cl, cq);
+}
+
+/**
+ * Convert a kernel result to a value.
+ */
+const value kernelResultToValue(const void* p, const value::ValueType type) {
+ switch(type) {
+ case value::Symbol: {
+ const char* s = static_cast<const char*>(p);
+ const size_t l = strlen(s);
+ if (l != 0 && *s == '\'')
+ return value(s + 1);
+ return value(s);
+ }
+ case value::String: {
+ const char* s = static_cast<const char*>(p);
+ const size_t l = strlen(s);
+ if (l != 0 && *s == '\'')
+ return value(s + 1);
+ return value(string(s, l));
+ }
+ case value::Number:
+ return (double)(*(static_cast<const cl_float*>(p)));
+ case value::Bool:
+ return (bool)(*(static_cast<const cl_int*>(p)));
+ default:
+ return *(static_cast<const value*>(p));
+ }
+}
+
+/**
+ * Return the value type corresponding to a C99 type name.
+ */
+const value::ValueType valueType(const string& t) {
+ if (t == "float")
+ return value::Number;
+ if (t == "int")
+ return value::Bool;
+ if (t == "char")
+ return value::String;
+ return value::Nil;
+}
+
+/**
+ * Return the size of a C99 type corresponding to a value type.
+ */
+const size_t valueSize(const value::ValueType type) {
+ switch(type) {
+ case value::Number:
+ return sizeof(cl_float);
+ case value::Bool:
+ return sizeof(cl_int);
+ case value::Symbol:
+ return sizeof(cl_char);
+ case value::String:
+ return sizeof(cl_char);
+ default:
+ return sizeof(value);
+ }
+}
+
+/**
+ * Return the result type of a kernel.
+ */
+class OpenCLResultType {
+public:
+ OpenCLResultType(const value::ValueType type, const size_t n, const size_t size) : type(type), n(n), size(size) {}
+ const value::ValueType type;
+ const size_t n;
+ const size_t size;
+};
+
+const OpenCLResultType kernelResultType(const string& fn, value::ValueType type, const size_t n) {
+ if (type != value::Nil)
+ return OpenCLResultType(type, n, valueSize(type));
+ const string s = car(tokenize("_", fn));
+ const size_t d = find_first_of(s, "0123456789");
+ if (d == length(s)) {
+ const value::ValueType vt = valueType(s);
+ return OpenCLResultType(vt, 1, valueSize(vt));
+ }
+ const value::ValueType vt = valueType(substr(s, 0, d));
+ return OpenCLResultType(vt, atoi(c_str(substr(s, d))), valueSize(vt));
+}
+
+/**
+ * Create the kernel implementing an expression.
+ */
+const failable<OpenCLKernel> createKernel(const value& expr, const OpenCLProgram& clprog) {
+
+ // Create an OpenCL kernel for the requested function
+ const value fn = car<value>(expr);
+ cl_int ckerr;
+ const cl_kernel k = clCreateKernel(clprog.prog, c_str(fn), &ckerr);
+ if (k == NULL || ckerr != CL_SUCCESS) {
+
+ // The start, stop, and restart functions are optional
+ //if (fn == "start" || fn == "stop")
+ //return value(lambda<value(const list<value>&)>());
+
+ return mkfailure<OpenCLKernel>(string("Couldn't find function: ") + car<value>(expr) + " : " + clError(ckerr));
+ }
+ return OpenCLKernel(k);
+}
+
+/**
+ * Evaluate an expression implemented by a kernel.
+ */
+const failable<value> evalKernel(const failable<OpenCLKernel>& fkernel, const value& expr, const size_t gwsize, const value::ValueType type, const size_t n, const OpenCLContext& cl) {
+
+#ifdef WANT_MAINTAINER_OPENCL_PROF
+ const cl_uint estart = (cl_uint)timens();
+ const cl_uint pstart = estart;
+#endif
+
+ if (!hasContent(fkernel))
+ return mkfailure<value>(fkernel);
+ const OpenCLKernel kernel = content(fkernel);
+
+ // Get a command queue for the specified device type
+ const cl_command_queue cq = commandq(cl);
+
+ // Set the kernel input args
+ const failable<list<OpenCLBuffer>> args = valuesToKernelArgs(cdr<value>(expr), kernel, cl, cq);
+ if (!hasContent(args)) {
+ return mkfailure<value>(args);
+ }
+
+ // Allocate result buffer in device memory
+ const value fn = car<value>(expr);
+ const OpenCLResultType rtype = kernelResultType(fn, type, n);
+ const size_t rsize = rtype.n * rtype.size;
+ const failable<OpenCLBuffer> rbuf = writeOnlyBuffer(rsize, cl);
+ if (!hasContent(rbuf))
+ return mkfailure<value>(rbuf);
+
+ // Set it as a kernel output arg
+ const cl_mem rmem = content(rbuf).mem;
+ const failable<OpenCLBuffer> rarg = valueToKernelArg((cl_uint)length(cdr<value>(expr)), sizeof(cl_mem), &rmem, rbuf, kernel);
+ if (!hasContent(rarg))
+ return mkfailure<value>(rarg);
+
+ // Enqueue the kernel, to be executed after all the writes complete
+ cl_event wevt[32];
+ const cl_uint nwevt = writeBufferEvents(content(args), wevt);
+ cl_event kevt;
+ const cl_int qerr = clEnqueueNDRangeKernel(cq, kernel.k, 1, NULL, &gwsize, NULL, nwevt, nwevt != 0? wevt : NULL, &kevt);
+ if (qerr != CL_SUCCESS)
+ return mkfailure<value>(string("Couldn't enqueue kernel task: ") + clError(qerr));
+
+ // Enqueue result buffer read, to be executed after the kernel completes
+ char res[rsize];
+ cl_event revt;
+ const cl_int rerr = clEnqueueReadBuffer(cq, rmem, CL_FALSE, 0, rsize, res, 1, &kevt, &revt);
+ if (rerr != CL_SUCCESS) {
+ clReleaseEvent(kevt);
+ return mkfailure<value>(string("Couldn't read from OpenCL device memory: ") + clError(rerr));
+ }
+
+#ifdef WANT_MAINTAINER_OPENCL_PROF
+ const cl_uint pend = (cl_uint)timens();
+ preptime += (pend - pstart);
+#endif
+
+ // Wait for completion
+ const cl_int werr = clWaitForEvents(1, &revt);
+ if (werr != CL_SUCCESS) {
+ clReleaseEvent(revt);
+ clReleaseEvent(kevt);
+ return mkfailure<value>(string("Couldn't wait for kernel completion: ") + clError(werr));
+ }
+
+#ifdef WANT_MAINTAINER_OPENCL_PROF
+ profileMemEvents(nwevt, wevt);
+ profileKernelEvent(kevt);
+ profileMemEvent(revt);
+#endif
+
+ // Convert the result to a value
+ const value v = kernelResultToValue(res, rtype.type);
+
+ // Release OpenCL resources
+ clReleaseEvent(revt);
+ clReleaseEvent(kevt);
+
+#ifdef WANT_MAINTAINER_OPENCL_PROF
+ const cl_uint eend = (cl_uint)timens();
+ evaltime += (eend - estart);
+#endif
+
+ return v;
+}
+
+const failable<value> evalKernel(const failable<OpenCLKernel>& kernel, const value& expr, const OpenCLContext& cl) {
+ return evalKernel(kernel, expr, 1, value::Nil, 0, cl);
+}
+
+/**
+ * Read an opencl program from an input stream.
+ */
+const failable<OpenCLProgram> readProgram(const string& path, istream& is, const OpenCLContext& cl) {
+
+ // Read the program source
+ const list<string> ls = streamList(is);
+ ostringstream os;
+ write(ls, os);
+ const char* cs = c_str(str(os));
+
+ // Create the OpenCL program
+ cl_int cperr;
+ const cl_program prog = clCreateProgramWithSource(cl.ctx, 1, (const char **)&cs, NULL, &cperr);
+ if (!prog || cperr != CL_SUCCESS)
+ return mkfailure<OpenCLProgram>(string("Couldn't create OpenCL program from source: ") + path + " : " + clError(cperr));
+
+ // Built it
+ const cl_int bperr = clBuildProgram(prog, 0, NULL, NULL, NULL, NULL);
+ if(bperr != CL_SUCCESS) {
+ size_t l;
+ char b[2048];
+ clGetProgramBuildInfo(prog, cl.devid[0], CL_PROGRAM_BUILD_LOG, sizeof(b), b, &l);
+ return mkfailure<OpenCLProgram>(string("Couldn't build OpenCL program: ") + path + " : " + clError(bperr) + "\n" + string(b));
+ }
+ return OpenCLProgram(prog);
+}
+
+/**
+ * Evaluate an expression against an OpenCL program provided as an input stream.
+ */
+const failable<value> evalKernel(const value& expr, istream& is, const OpenCLContext& cl) {
+ failable<OpenCLProgram> clprog = readProgram("program.cl", is, cl);
+ if (!hasContent(clprog))
+ return mkfailure<value>(clprog);
+ return evalKernel(createKernel(expr, content(clprog)), expr, 1, value::Nil, 0, cl);
+}
+
+}
+}
+#endif /* tuscany_opencl_eval_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/modules/opencl/opencl-conf b/sca-cpp/branches/lightweight-sca/modules/opencl/opencl-conf
new file mode 100755
index 0000000000..1ba2c336a3
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/opencl/opencl-conf
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Generate an OpenCL server conf
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+uname=`uname -s`
+if [ $uname = "Darwin" ]; then
+ libsuffix=".dylib"
+else
+ libsuffix=".so"
+fi
+
+cat >>$root/conf/modules.conf <<EOF
+# Generated by: opencl-conf $*
+# Support for OpenCL SCA components
+LoadModule mod_tuscany_eval $here/libmod_tuscany_opencl$libsuffix
+
+EOF
diff --git a/sca-cpp/branches/lightweight-sca/modules/opencl/opencl-shell.cpp b/sca-cpp/branches/lightweight-sca/modules/opencl/opencl-shell.cpp
new file mode 100644
index 0000000000..1dfeaeea1d
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/opencl/opencl-shell.cpp
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * OpenCL script evaluator shell, used for interactive testing of scripts.
+ */
+
+#include <assert.h>
+#include "gc.hpp"
+#include "stream.hpp"
+#include "string.hpp"
+#include "driver.hpp"
+
+int main(const int argc, char** argv) {
+ tuscany::gc_scoped_pool pool;
+ if (argc != 2) {
+ tuscany::cerr << "Usage: opencl-shell <kernel.cl>" << tuscany::endl;
+ return 1;
+ }
+ tuscany::opencl::evalDriverRun(argv[1], tuscany::cin, tuscany::cout);
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/opencl/opencl-test.cpp b/sca-cpp/branches/lightweight-sca/modules/opencl/opencl-test.cpp
new file mode 100644
index 0000000000..17bc5ccfa6
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/opencl/opencl-test.cpp
@@ -0,0 +1,332 @@
+/*
+ * 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 OpenCL kernel evaluator.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "driver.hpp"
+#include "parallel.hpp"
+#include "perf.hpp"
+
+namespace tuscany {
+namespace opencl {
+
+const string testFloatsCPU =
+ "kernel void add(const float x, const float y, global float* r) {\n"
+ " float l_x;\n"
+ " float l_y;\n"
+ " l_x = x;\n"
+ " l_y = y;\n"
+ " *r = l_x + l_y;\n"
+ "}\n"
+ "kernel void float_add(const float x, const float y, global float* r) {\n"
+ " add(x, y, r);\n"
+ "}\n";
+
+const string testFloatsGPU =
+ "kernel void add(const float x, const float y, global float* r) {\n"
+ " local float l_x;\n"
+ " local float l_y;\n"
+ " l_x = x;\n"
+ " l_y = y;\n"
+ " barrier(CLK_LOCAL_MEM_FENCE);\n"
+ " *r = l_x + l_y;\n"
+ "}\n"
+ "kernel void float_add(const float x, const float y, global float* r) {\n"
+ " add(x, y, r);\n"
+ "}\n";
+
+const string testBoolsCPU =
+ "kernel void int_or(const int x, const int y, global int* r) {\n"
+ " int l_x;\n"
+ " int l_y;\n"
+ " l_x = x;\n"
+ " l_y = y;\n"
+ " *r = l_x || l_y;\n"
+ "}\n";
+
+const string testBoolsGPU =
+ "kernel void int_or(const int x, const int y, global int* r) {\n"
+ " local int l_x;\n"
+ " local int l_y;\n"
+ " l_x = x;\n"
+ " l_y = y;\n"
+ " barrier(CLK_LOCAL_MEM_FENCE);\n"
+ " *r = l_x || l_y;\n"
+ "}\n";
+
+const string testCharsCPU =
+ "kernel void add(const float xl, global const char* x, const float yl, global const char* y, global char* r) {\n"
+ " const int ixl = (int)xl;\n"
+ " const int iyl = (int)yl;\n"
+ " for (int i = 0; i < ixl; i++)\n"
+ " r[i] = x[i];\n"
+ " for (int i = 0; i < iyl; i++)\n"
+ " r[ixl + i] = y[i];\n"
+ " r[ixl + iyl] = '\\0';\n"
+ "}\n"
+ "kernel void char128_add(const float xl, global const char* x, const float yl, global const char* y, global char* r) {\n"
+ " add(xl, x, yl, y, r);\n"
+ "}\n";
+
+const string testCharsCopyGPU =
+ "kernel void add(const float xl, global const char* x, const float yl, global const char* y, global char* r) {\n"
+ " const int ixl = (int)xl;\n"
+ " const int iyl = (int)yl;\n"
+ " local char l_x[128];\n"
+ " local char l_y[128];\n"
+ " event_t re = async_work_group_copy(l_x, x, ixl, 0);\n"
+ " async_work_group_copy(l_y, y, iyl, re);\n"
+ " wait_group_events(1, &re);\n"
+ " local char l_r[128];\n"
+ " for (int i = 0; i < ixl; i++)\n"
+ " l_r[i] = l_x[i];\n"
+ " for (int i = 0; i < iyl; i++)\n"
+ " l_r[ixl + i] = l_y[i];\n"
+ " l_r[ixl + iyl] = '\\0';\n"
+ " event_t we = async_work_group_copy(r, l_r, ixl + iyl + 1, 0);\n"
+ " wait_group_events(1, &we);\n"
+ "}\n"
+ "kernel void char128(const float xl, global const char* x, const float yl, global const char* y, global char* r) {\n"
+ " add(xl, x, yl, y, r);\n"
+ "}\n";
+
+const string testCharsGPU =
+ "kernel void add(const float xl, global const char* x, const float yl, global const char* y, global char* r) {\n"
+ " const int ixl = (int)xl;\n"
+ " const int iyl = (int)yl;\n"
+ " for (int i = 0; i < ixl; i++)\n"
+ " r[i] = x[i];\n"
+ " for (int i = 0; i < iyl; i++)\n"
+ " r[ixl + i] = y[i];\n"
+ " r[ixl + iyl] = '\\0';\n"
+ "}\n"
+ "kernel void char128(const float xl, global const char* x, const float yl, global const char* y, global char* r) {\n"
+ " add(xl, x, yl, y, r);\n"
+ "}\n";
+
+const string testCharsParallelCPU =
+ "kernel void add(const float xl, global const char* x, const float yl, global const char* y, global char* r) {\n"
+ " const int i = get_global_id(0);\n"
+ " const int ixl = (int)xl;\n"
+ " const int iyl = (int)yl;\n"
+ " r[i] = i < ixl? x[i] : i < ixl + iyl? y[i - ixl] : '\\0';\n"
+ "}\n";
+
+const string testCharsParallelCopyGPU =
+ "kernel void add(const float xl, global const char* x, const float yl, global const char* y, global char* r) {\n"
+ " const int i = get_global_id(0);\n"
+ " const int ixl = (int)xl;\n"
+ " const int iyl = (int)yl;\n"
+ " local char l_x[128];\n"
+ " local char l_y[128];\n"
+ " event_t re = async_work_group_copy(l_x, x, ixl, 0);\n"
+ " async_work_group_copy(l_y, y, iyl, re);\n"
+ " wait_group_events(1, &re);\n"
+ " local char l_r[128];\n"
+ " l_r[i] = i < ixl? l_x[i] : i < ixl + iyl? l_y[i - ixl] : '\\0';\n"
+ " event_t we = async_work_group_copy(r, l_r, ixl + iyl + 1, 0);\n"
+ " wait_group_events(1, &we);\n"
+ "}\n";
+
+const string testCharsParallelGPU =
+ "kernel void add(const float xl, global const char* x, const float yl, global const char* y, global char* r) {\n"
+ " const int i = get_global_id(0);\n"
+ " const int ixl = (int)xl;\n"
+ " const int iyl = (int)yl;\n"
+ " r[i] = i < ixl? x[i] : i < ixl + iyl? y[i - ixl] : '\\0';\n"
+ "}\n";
+
+bool testTaskParallel(const OpenCLContext::DeviceType dev) {
+ gc_scoped_pool pool;
+ OpenCLContext cl(dev);
+ if (!devices(cl) != 0)
+ return true;
+
+ {
+ istringstream is(dev == OpenCLContext::CPU? testFloatsCPU : testFloatsGPU);
+ failable<OpenCLProgram> clprog = readProgram("kernel.cl", is, cl);
+ assert(hasContent(clprog));
+ const value exp = mklist<value>("float_add", 2, 3);
+ const failable<value> r = evalKernel(createKernel(exp, content(clprog)), exp, cl);
+ assert(hasContent(r));
+ assert(content(r) == value(5));
+ }
+ if (true) return true;
+ {
+ istringstream is(dev == OpenCLContext::CPU? testFloatsCPU : testFloatsGPU);
+ failable<OpenCLProgram> clprog = readProgram("kernel.cl", is, cl);
+ assert(hasContent(clprog));
+ const value exp = mklist<value>("add", 2, 3);
+ const failable<value> r = evalKernel(createKernel(exp, content(clprog)), exp, 1, value::Number, 1, cl);
+ assert(hasContent(r));
+ assert(content(r) == value(5));
+ }
+ {
+ istringstream is(dev == OpenCLContext::CPU? testBoolsCPU : testBoolsGPU);
+ failable<OpenCLProgram> clprog = readProgram("kernel.cl", is, cl);
+ assert(hasContent(clprog));
+
+ const value exp = mklist<value>("int_or", true, false);
+ const failable<value> r = evalKernel(createKernel(exp, content(clprog)), exp, cl);
+ assert(hasContent(r));
+ assert(content(r) == value(true));
+
+ const value exp2 = mklist<value>("int_or", false, false);
+ const failable<value> r2 = evalKernel(createKernel(exp2, content(clprog)), exp2, cl);
+ assert(hasContent(r2));
+ assert(content(r2) == value(false));
+ }
+ {
+ istringstream is(dev == OpenCLContext::CPU? testCharsCPU : testCharsGPU);
+ failable<OpenCLProgram> clprog = readProgram("kernel.cl", is, cl);
+ assert(hasContent(clprog));
+
+ const value exp = mklist<value>("char128", 60, string("Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello "), 60, string("World World World World World World World World World World "));
+ const failable<value> r = evalKernel(createKernel(exp, content(clprog)), exp, cl);
+ assert(hasContent(r));
+ assert(content(r) == value(string("Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello World World World World World World World World World World ")));
+ }
+ {
+ istringstream is(dev == OpenCLContext::CPU? testCharsCPU : testCharsGPU);
+ failable<OpenCLProgram> clprog = readProgram("kernel.cl", is, cl);
+ assert(hasContent(clprog));
+
+ const value exp = mklist<value>("add", 60, string("Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello "), 60, string("World World World World World World World World World World "));
+ const failable<value> r = evalKernel(createKernel(exp, content(clprog)), exp, 1, value::String, 128, cl);
+ assert(hasContent(r));
+ assert(content(r) == value(string("Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello World World World World World World World World World World ")));
+ }
+
+ return true;
+}
+
+struct evalTaskParallelLoop {
+ evalTaskParallelLoop(const OpenCLContext& cl, const OpenCLProgram& clprog) : cl(cl), clprog(clprog), exp(mklist<value>("add", 60, string("Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello "), 60, string("World World World World World World World World World World "))) {
+ }
+ const bool operator()() const {
+ const failable<value> r = evalKernel(createKernel(exp, clprog), exp, 1, value::String, 128, cl);
+ assert(hasContent(r));
+ assert(content(r) == value(string("Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello World World World World World World World World World World ")));
+ return true;
+ }
+
+ const OpenCLContext& cl;
+ const OpenCLProgram& clprog;
+ const value exp;
+};
+
+const bool testTaskParallelPerf(const OpenCLContext::DeviceType dev, const bool copy) {
+ gc_scoped_pool pool;
+ OpenCLContext cl(dev);
+ if (!devices(cl) != 0)
+ return true;
+
+ istringstream is(dev == OpenCLContext::CPU? testCharsCPU : copy? testCharsCopyGPU : testCharsGPU);
+ failable<OpenCLProgram> clprog = readProgram("kernel.cl", is, cl);
+ assert(hasContent(clprog));
+
+ resetOpenCLCounters();
+ const lambda<bool()> el = evalTaskParallelLoop(cl, content(clprog));
+ cout << "OpenCL task-parallel eval " << (dev == OpenCLContext::CPU? "CPU" : "GPU") << (copy? " copy" : "") << " test " << time(el, 5, 500) << " ms";
+ printOpenCLCounters(500);
+ cout << endl;
+ return true;
+}
+
+bool testDataParallel(const OpenCLContext::DeviceType dev) {
+ gc_scoped_pool pool;
+ OpenCLContext cl(dev);
+ if (!devices(cl) != 0)
+ return true;
+
+ {
+ istringstream is(dev == OpenCLContext::CPU? testCharsParallelCPU : testCharsParallelGPU);
+ failable<OpenCLProgram> clprog = readProgram("kernel.cl", is, cl);
+ assert(hasContent(clprog));
+
+ const value exp = mklist<value>("add", 6, string("Hello "), 5, string("World"));
+ const failable<value> r = evalKernel(createKernel(exp, content(clprog)), exp, 121, value::String, 128, cl);
+ assert(hasContent(r));
+ assert(content(r) == value(string("Hello World")));
+ }
+
+ return true;
+}
+
+struct evalDataParallelLoop {
+ evalDataParallelLoop(const OpenCLContext& cl, const OpenCLProgram& clprog) : cl(cl), clprog(clprog), exp(mklist<value>("add", 60, string("Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello "), 60, string("World World World World World World World World World World "))) {
+ }
+ const bool operator()() const {
+ const failable<value> r = evalKernel(createKernel(exp, clprog), exp, 121, value::String, 128, cl);
+ assert(hasContent(r));
+ assert(content(r) == value(string("Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello World World World World World World World World World World ")));
+ return true;
+ }
+
+ const OpenCLContext& cl;
+ const OpenCLProgram& clprog;
+ const value exp;
+};
+
+const bool testDataParallelPerf(const OpenCLContext::DeviceType dev, const bool copy) {
+ gc_scoped_pool pool;
+ OpenCLContext cl(dev);
+ if (!devices(cl) != 0)
+ return true;
+
+ istringstream is(dev == OpenCLContext::CPU? testCharsParallelCPU : copy? testCharsParallelCopyGPU : testCharsParallelGPU);
+ failable<OpenCLProgram> clprog = readProgram("kernel.cl", is, cl);
+ assert(hasContent(clprog));
+
+ resetOpenCLCounters();
+ const lambda<bool()> el = evalDataParallelLoop(cl, content(clprog));
+ cout << "OpenCL data-parallel eval " << (dev == OpenCLContext::CPU? "CPU" : "GPU") << (copy? " copy" : "") << " test " << time(el, 5, 500) << " ms";
+ printOpenCLCounters(500);
+ cout << endl;
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::opencl::testTaskParallel(tuscany::opencl::OpenCLContext::CPU);
+ tuscany::opencl::testTaskParallelPerf(tuscany::opencl::OpenCLContext::CPU, false);
+ tuscany::opencl::testDataParallel(tuscany::opencl::OpenCLContext::CPU);
+ tuscany::opencl::testDataParallelPerf(tuscany::opencl::OpenCLContext::CPU, false);
+
+ tuscany::opencl::testTaskParallel(tuscany::opencl::OpenCLContext::GPU);
+ tuscany::opencl::testTaskParallelPerf(tuscany::opencl::OpenCLContext::GPU, false);
+ tuscany::opencl::testTaskParallelPerf(tuscany::opencl::OpenCLContext::GPU, true);
+ tuscany::opencl::testDataParallel(tuscany::opencl::OpenCLContext::GPU);
+ tuscany::opencl::testDataParallelPerf(tuscany::opencl::OpenCLContext::GPU, false);
+ tuscany::opencl::testDataParallelPerf(tuscany::opencl::OpenCLContext::GPU, true);
+
+ tuscany::cout << "OK" << tuscany::endl;
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/opencl/server-test b/sca-cpp/branches/lightweight-sca/modules/opencl/server-test
new file mode 100755
index 0000000000..c103c45487
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/opencl/server-test
@@ -0,0 +1,40 @@
+#!/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
+rm -rf tmp
+../http/httpd-conf tmp localhost 8090 ../server/htdocs
+../server/server-conf tmp
+./opencl-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
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/modules/opencl/server-test.cl b/sca-cpp/branches/lightweight-sca/modules/opencl/server-test.cl
new file mode 100644
index 0000000000..de5c2d1b1e
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/opencl/server-test.cl
@@ -0,0 +1,17 @@
+# 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.
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/scdl/Makefile.am b/sca-cpp/branches/lightweight-sca/modules/scdl/Makefile.am
new file mode 100644
index 0000000000..54a9b46e4f
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/scdl/Makefile.am
@@ -0,0 +1,27 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+incl_HEADERS = *.hpp
+incldir = $(prefix)/include/modules/scdl
+
+scdl_test_SOURCES = scdl-test.cpp
+scdl_test_LDFLAGS = -lxml2
+
+EXTRA_DIST = test.composite
+
+noinst_PROGRAMS = scdl-test
+TESTS = scdl-test
diff --git a/sca-cpp/branches/lightweight-sca/modules/scdl/scdl-test.cpp b/sca-cpp/branches/lightweight-sca/modules/scdl/scdl-test.cpp
new file mode 100644
index 0000000000..4c10f515df
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/scdl/scdl-test.cpp
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test SCDL read functions.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "list.hpp"
+#include "tree.hpp"
+#include "scdl.hpp"
+
+namespace tuscany {
+namespace scdl {
+
+bool testComposite() {
+ ifstream is("test.composite");
+ const list<value> c = readXML(streamList(is));
+ return true;
+}
+
+bool testComponents() {
+ ifstream is("test.composite");
+ const list<value> c = components(readXML(streamList(is)));
+ assert(length(c) == 4);
+
+ const value store = car(c);
+ assert(name(store) == string("Store"));
+ const value impl = implementation(store);
+ assert(implementationType(impl) == "implementation.scheme");
+ assert(attributeValue("script", impl) == string("store.scm"));
+
+ const value catalog = named(string("Catalog"), c);
+ assert(name(catalog) == string("Catalog"));
+
+ const list<value> t = mkbtree(sort(nameToElementAssoc(c)));
+ assert(assoctree<value>("Catalog", t) == mklist<value>("Catalog" , cadr(c)));
+ return true;
+}
+
+bool testServices() {
+ ifstream is("test.composite");
+ const list<value> c = components(readXML(streamList(is)));
+ const value store = car(c);
+
+ assert(length(services(store)) == 1);
+ const value widget = car(services(store));
+ assert(name(widget) == string("Widget"));
+
+ assert(length(bindings(widget)) == 1);
+ const value binding = car(bindings(widget));
+ assert(uri(binding) == string("/store"));
+ assert(bindingType(binding) == "binding.http");
+ return true;
+}
+
+bool testReferences() {
+ ifstream is("test.composite");
+ const list<value> c = components(readXML(streamList(is)));
+ const value store = car(c);
+
+ assert(length(references(store)) == 3);
+ const value catalog = car(references(store));
+ assert(name(catalog) == string("catalog"));
+ assert(target(catalog) == string("Catalog"));
+
+ assert(length(bindings(catalog)) == 1);
+ const value binding = car(bindings(catalog));
+ assert(uri(binding) == value());
+ assert(bindingType(binding) == "binding.jsonrpc");
+
+ const list<value> t = mkbtree(sort(referenceToTargetAssoc(references(store))));
+ assert(assoctree<value>("shoppingCart", t) == mklist<value>(string("shoppingCart"), string("ShoppingCart/Cart")));
+ return true;
+}
+
+bool testProperties() {
+ ifstream is("test.composite");
+ const list<value> c = components(readXML(streamList(is)));
+ const value catalog = named(string("Catalog"), c);
+
+ assert(length(properties(catalog)) == 1);
+ const value currencyCode = car(properties(catalog));
+ assert(name(currencyCode) == string("currencyCode"));
+ assert(propertyValue(currencyCode) == string("USD"));
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::gc_scoped_pool p;
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::scdl::testComposite();
+ tuscany::scdl::testComponents();
+ tuscany::scdl::testServices();
+ tuscany::scdl::testReferences();
+ tuscany::scdl::testProperties();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/scdl/scdl.hpp b/sca-cpp/branches/lightweight-sca/modules/scdl/scdl.hpp
new file mode 100644
index 0000000000..7cf43e3b14
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/scdl/scdl.hpp
@@ -0,0 +1,221 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+#ifndef tuscany_scdl_hpp
+#define tuscany_scdl_hpp
+
+/**
+ * SCDL read functions.
+ */
+
+#include "string.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "xml.hpp"
+
+namespace tuscany {
+namespace scdl {
+
+/**
+ * Returns a list of components in a composite.
+ */
+const list<value> components(const value& l) {
+ const list<value> cs = elementChildren("composite", l);
+ if (isNil(cs))
+ return cs;
+ return elementChildren("component", car(cs));
+}
+
+/**
+ * Returns a list of service promotions in a composite.
+ */
+const list<value> promotions(const value& l) {
+ const list<value> cs = elementChildren("composite", l);
+ if (isNil(cs))
+ return cs;
+ return elementChildren("service", car(cs));
+}
+
+/**
+ * Returns the target or a service promotion.
+ */
+const value promote(const value& l) {
+ return attributeValue("promote", l);
+}
+
+/**
+ * Returns the name of a component, service or reference.
+ */
+const value name(const value& l) {
+ return attributeValue("name", l);
+}
+
+/**
+ * Convert a list of elements to a name -> element assoc list.
+ */
+const list<value> nameToElementAssoc(const list<value>& l) {
+ if (isNil(l))
+ return l;
+ const value e(car(l));
+ return cons<value>(mklist<value>(name(e), e), nameToElementAssoc(cdr(l)));
+}
+
+/**
+ * Returns the scdl declaration with the given name.
+ */
+struct filterName {
+ const value n;
+ filterName(const value& n) : n(n) {
+ }
+ const bool operator()(const value& v) const {
+ return name(v) == n;
+ }
+};
+
+const value named(const value& name, const value& l) {
+ const list<value> c = filter<value>(filterName(name), l);
+ if (isNil(c))
+ return value();
+ return car(c);
+}
+
+/**
+ * Returns the implementation of a component.
+ */
+const bool filterImplementation(const value& v) {
+ return isElement(v) && contains(string(cadr<value>(v)), "implementation.");
+}
+
+const value implementation(const value& l) {
+ const list<value> n = filter<value>(filterImplementation, l);
+ if (isNil(n))
+ return value();
+ return car(n);
+}
+
+/**
+ * Returns the URI of a service, reference or implementation.
+ */
+const value uri(const value& l) {
+ return attributeValue("uri", l);
+}
+
+/**
+ * Returns true if a reference is declared as wired by impl.
+ */
+const bool wiredByImpl(const value& l) {
+ return attributeValue("wiredByImpl", l) == string("true");
+}
+
+/**
+ * Returns a list of services in a component.
+ */
+const list<value> services(const value& l) {
+ return elementChildren("service", l);
+}
+
+/**
+ * Returns a list of references in a component.
+ */
+const list<value> references(const value& l) {
+ return elementChildren("reference", l);
+}
+
+/**
+ * Returns a list of bindings in a service or reference.
+ */
+const bool filterBinding(const value& v) {
+ return isElement(v) && contains(string(cadr<value>(v)), "binding.");
+}
+
+const list<value> bindings(const value& l) {
+ return filter<value>(filterBinding, l);
+}
+
+/**
+ * Returns the target of a reference.
+ */
+const value bindingsTarget(const list<value>& l) {
+ if (isNil(l))
+ return value();
+ const value u = uri(car(l));
+ if (!isNil(u))
+ return u;
+ return bindingsTarget(cdr(l));
+}
+
+const value target(const value& l) {
+ const value target = attributeValue("target", l);
+ if (!isNil(target))
+ return target;
+ return bindingsTarget(bindings(l));
+}
+
+/**
+ * Convert a list of references to a reference name -> target assoc list.
+ */
+const list<value> referenceToTargetAssoc(const list<value>& r) {
+ if (isNil(r))
+ return r;
+ const value ref(car(r));
+ return cons<value>(mklist<value>(scdl::name(ref), scdl::target(ref)), referenceToTargetAssoc(cdr(r)));
+}
+
+/**
+ * Returns a list of properties in a component.
+ */
+const list<value> properties(const value& l) {
+ return elementChildren("property", l);
+}
+
+/**
+ * Returns the type of an implementation.
+ */
+const value implementationType(const value& l) {
+ return elementName(l);
+}
+
+/**
+ * Returns the type of a binding.
+ */
+const value bindingType(const value& l) {
+ return elementName(l);
+}
+
+/**
+ * Returns the value of a property.
+ */
+const value propertyValue(const value& l) {
+ return elementValue(l);
+}
+
+/**
+ * Returns the absolute path of a resource in a contribution.
+ */
+const string resourcePath(const string& contrib, const string& path) {
+ return c_str(path)[0] == '/'? path : contrib + path;
+}
+
+}
+}
+
+#endif /* tuscany_scdl_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/modules/scdl/test.composite b/sca-cpp/branches/lightweight-sca/modules/scdl/test.composite
new file mode 100644
index 0000000000..5d8c5d3b88
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/scdl/test.composite
@@ -0,0 +1,63 @@
+<?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"
+ targetNamespace="http://store"
+ name="store">
+
+ <component name="Store">
+ <implementation.scheme script="store.scm"/>
+ <service name="Widget">
+ <binding.http uri="/store"/>
+ </service>
+ <reference name="catalog" target="Catalog">
+ <binding.jsonrpc/>
+ </reference>
+ <reference name="shoppingCart" target="ShoppingCart/Cart">
+ <binding.atom/>
+ </reference>
+ <reference name="shoppingTotal" target="ShoppingCart/Total">
+ <binding.jsonrpc/>
+ </reference>
+ </component>
+
+ <component name="Catalog">
+ <implementation.scheme script="fruits-catalog.scm"/>
+ <property name="currencyCode">USD</property>
+ <service name="Catalog">
+ <binding.jsonrpc/>
+ </service>
+ <reference name="currencyConverter" target="CurrencyConverter"/>
+ </component>
+
+ <component name="ShoppingCart">
+ <implementation.scheme script="shopping-cart.scm"/>
+ <service name="Cart">
+ <binding.atom uri="/ShoppingCart"/>
+ </service>
+ <service name="Total">
+ <binding.jsonrpc/>
+ </service>
+ </component>
+
+ <component name="CurrencyConverter">
+ <implementation.scheme script="currency-converter.scm"/>
+ </component>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/modules/scdl/validate-test b/sca-cpp/branches/lightweight-sca/modules/scdl/validate-test
new file mode 100755
index 0000000000..9f277cf15a
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/scdl/validate-test
@@ -0,0 +1,23 @@
+#!/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.
+
+# Validate test composite
+../../kernel/xsd-test ../../xsd/tuscany-sca-1.1.xsd test.composite 2>/dev/null
+rc=$?
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/modules/scheme/Makefile.am b/sca-cpp/branches/lightweight-sca/modules/scheme/Makefile.am
new file mode 100644
index 0000000000..130fe14303
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/scheme/Makefile.am
@@ -0,0 +1,47 @@
+# 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.
+
+incl_HEADERS = *.hpp
+incldir = $(prefix)/include/modules/scheme
+
+moddir = $(prefix)/modules/scheme
+
+scheme_test_SOURCES = scheme-test.cpp
+
+scheme_shell_SOURCES = scheme-shell.cpp
+
+value_element_SOURCES = value-element.cpp
+value_element_LDFLAGS =
+
+element_value_SOURCES = element-value.cpp
+element_value_LDFLAGS =
+
+xml_value_SOURCES = xml-value.cpp
+xml_value_LDFLAGS = -lxml2
+
+value_xml_SOURCES = value-xml.cpp
+value_xml_LDFLAGS = -lxml2
+
+json_value_SOURCES = json-value.cpp
+json_value_LDFLAGS = -lmozjs
+
+value_json_SOURCES = value-json.cpp
+value_json_LDFLAGS = -lmozjs
+
+noinst_PROGRAMS = scheme-test
+mod_PROGRAMS = scheme-shell element-value value-element xml-value value-xml json-value value-json
+TESTS = scheme-test
diff --git a/sca-cpp/branches/lightweight-sca/modules/scheme/driver.hpp b/sca-cpp/branches/lightweight-sca/modules/scheme/driver.hpp
new file mode 100644
index 0000000000..112c226ed1
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/scheme/driver.hpp
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+#ifndef tuscany_scheme_driver_hpp
+#define tuscany_scheme_driver_hpp
+
+/**
+ * Script evaluator main driver loop.
+ */
+
+#include "string.hpp"
+#include "stream.hpp"
+#include "eval.hpp"
+
+namespace tuscany {
+namespace scheme {
+
+const string evalOutputPrompt("; ");
+const string evalInputPrompt("=> ");
+
+const bool promptForInput(const string& str, ostream& out) {
+ out << endl << endl << str;
+ return true;
+}
+
+const bool announceOutput(const string str, ostream& out) {
+ out << endl << str;
+ return true;
+}
+
+const bool userPrint(const value val, ostream& out) {
+ if(isCompoundProcedure(val))
+ writeValue(mklist<value>(compoundProcedureSymbol, procedureParameters(val), procedureBody(val), "<procedure-env>"), out);
+ writeValue(val, out);
+ return true;
+}
+
+const value evalDriverLoop(istream& in, ostream& out, Env& env) {
+ promptForInput(evalInputPrompt, out);
+ value input = readValue(in);
+ if (isNil(input))
+ return input;
+ const value output = evalExpr(input, env);
+ announceOutput(evalOutputPrompt, out);
+ userPrint(output, out);
+ return evalDriverLoop(in, out, env);
+}
+
+const bool evalDriverRun(istream& in, ostream& out) {
+ setupDisplay(out);
+ Env env = setupEnvironment();
+ evalDriverLoop(in, out, env);
+ return true;
+}
+
+}
+}
+#endif /* tuscany_scheme_driver_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/modules/scheme/element-value.cpp b/sca-cpp/branches/lightweight-sca/modules/scheme/element-value.cpp
new file mode 100644
index 0000000000..8a443dbdb2
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/scheme/element-value.cpp
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Convert a scheme value representing an element to a value.
+ */
+
+#include "fstream.hpp"
+#include "string.hpp"
+#include "element.hpp"
+#include "eval.hpp"
+
+namespace tuscany {
+namespace scheme {
+
+int elementValue() {
+ const value v = elementsToValues(readValue(cin));
+ cout << writeValue(v);
+ return 0;
+}
+
+}
+}
+
+int main() {
+ return tuscany::scheme::elementValue();
+}
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/scheme/environment.hpp b/sca-cpp/branches/lightweight-sca/modules/scheme/environment.hpp
new file mode 100644
index 0000000000..303a37cb3c
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/scheme/environment.hpp
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+#ifndef tuscany_scheme_environment_hpp
+#define tuscany_scheme_environment_hpp
+
+/**
+ * Script evaluator environment implementation.
+ */
+
+#include "string.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "primitive.hpp"
+#include <string>
+
+namespace tuscany {
+namespace scheme {
+
+typedef value Frame;
+typedef list<value> Env;
+
+const value trueSymbol("true");
+const value falseSymbol("false");
+const value defineSymbol("define");
+const value setSymbol("set!");
+const value dotSymbol(".");
+
+const Env theEmptyEnvironment() {
+ return list<value>();
+}
+
+const bool isDefinition(const value& exp) {
+ return isTaggedList(exp, defineSymbol);
+}
+
+const bool isAssignment(const value& exp) {
+ return isTaggedList(exp, setSymbol);
+}
+
+const bool isVariable(const value& exp) {
+ return isSymbol(exp);
+}
+
+const Env enclosingEnvironment(const Env& env) {
+ return cdr(env);
+}
+
+const gc_ptr<Frame> firstFrame(const Env& env) {
+ return car(env);
+}
+
+list<value> frameVariables(const Frame& frame) {
+ return car((list<value> )frame);
+}
+
+list<value> frameValues(const Frame& frame) {
+ return cdr((list<value> )frame);
+}
+
+const bool isDotVariable(const value& var) {
+ return var == dotSymbol;
+}
+
+const Frame makeBinding(const Frame& frameSoFar, const list<value>& variables, const list<value> values) {
+ if (isNil(variables)) {
+ if (!isNil(values))
+ logStream() << "Too many arguments supplied " << values << endl;
+ return frameSoFar;
+ }
+ if (isDotVariable(car(variables)))
+ return makeBinding(frameSoFar, cdr(variables), mklist<value>(values));
+
+ if (isNil(values)) {
+ if (!isNil(variables))
+ logStream() << "Too few arguments supplied " << variables << endl;
+ return frameSoFar;
+ }
+
+ const list<value> vars = cons(car(variables), frameVariables(frameSoFar));
+ const list<value> vals = cons(car(values), frameValues(frameSoFar));
+ const Frame newFrame = cons(value(vars), vals);
+
+ return makeBinding(newFrame, cdr(variables), cdr(values));
+}
+
+const gc_ptr<Frame> makeFrame(const list<value>& variables, const list<value> values) {
+ gc_ptr<Frame> frame = new (gc_new<Frame>()) Frame();
+ *frame = value(makeBinding(cons(value(list<value>()), list<value>()), variables, values));
+ return frame;
+}
+
+const value definitionVariable(const value& exp) {
+ const list<value> exps(exp);
+ if(isSymbol(car(cdr(exps))))
+ return car(cdr(exps));
+ const list<value> lexps(car(cdr(exps)));
+ return car(lexps);
+}
+
+const value definitionValue(const value& exp) {
+ const list<value> exps(exp);
+ if(isSymbol(car(cdr(exps)))) {
+ if (isNil(cdr(cdr(exps))))
+ return value();
+ return car(cdr(cdr(exps)));
+ }
+ const list<value> lexps(car(cdr(exps)));
+ return makeLambda(cdr(lexps), cdr(cdr(exps)));
+}
+
+const value assignmentVariable(const value& exp) {
+ return car(cdr((list<value> )exp));
+}
+
+const value assignmentValue(const value& exp) {
+ return car(cdr(cdr((list<value> )exp)));
+}
+
+const Frame addBindingToFrame(const value& var, const value& val, const Frame& frame) {
+ return cons(value(cons(var, frameVariables(frame))), cons(val, frameValues(frame)));
+}
+
+const bool defineVariable(const value& var, const value& val, Env& env) {
+ *firstFrame(env) = addBindingToFrame(var, val, *firstFrame(env));
+ return true;
+}
+
+const Env extendEnvironment(const list<value>& vars, const list<value>& vals, const Env& baseEnv) {
+ return cons<value>(makeFrame(vars, vals), baseEnv);
+}
+
+const Env setupEnvironment() {
+ Env env = extendEnvironment(primitiveProcedureNames(), primitiveProcedureObjects(), theEmptyEnvironment());
+ defineVariable(trueSymbol, true, env);
+ defineVariable(falseSymbol, false, env);
+ return env;
+}
+
+const value lookupEnvLoop(const value& var, const Env& env);
+
+const value lookupEnvScan(const value& var, const list<value>& vars, const list<value>& vals, const Env& env) {
+ if(isNil(vars))
+ return lookupEnvLoop(var, enclosingEnvironment(env));
+ if(var == car(vars))
+ return car(vals);
+ return lookupEnvScan(var, cdr(vars), cdr(vals), env);
+}
+
+const value lookupEnvLoop(const value& var, const Env& env) {
+ if(env == theEmptyEnvironment()) {
+ logStream() << "Unbound variable " << var << endl;
+ return value();
+ }
+ return lookupEnvScan(var, frameVariables(*firstFrame(env)), frameValues(*firstFrame(env)), env);
+}
+
+const value lookupVariableValue(const value& var, const Env& env) {
+ return lookupEnvLoop(var, env);
+}
+
+}
+}
+#endif /* tuscany_scheme_environment_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/modules/scheme/eval.hpp b/sca-cpp/branches/lightweight-sca/modules/scheme/eval.hpp
new file mode 100644
index 0000000000..34d1a7bc17
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/scheme/eval.hpp
@@ -0,0 +1,290 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+#ifndef tuscany_scheme_eval_hpp
+#define tuscany_scheme_eval_hpp
+
+/**
+ * Core script evaluation logic.
+ */
+
+#include <string.h>
+#include "list.hpp"
+#include "value.hpp"
+#include "primitive.hpp"
+#include "io.hpp"
+#include "environment.hpp"
+
+namespace tuscany {
+namespace scheme {
+
+const value evalExpr(const value& exp, Env& env);
+
+const value compoundProcedureSymbol("compound-procedure");
+const value procedureSymbol("procedure");
+const value applySymbol("apply");
+const value beginSymbol("begin");
+const value condSymbol("cond");
+const value elseSymbol("else");
+const value ifSymbol("if");
+
+const bool isBegin(const value& exp) {
+ return isTaggedList(exp, beginSymbol);
+}
+
+const list<value> beginActions(const value& exp) {
+ return cdr((list<value> )exp);
+}
+
+const bool isLambdaExpr(const value& exp) {
+ return isTaggedList(exp, lambdaSymbol);
+}
+
+const list<value> lambdaParameters(const value& exp) {
+ return car(cdr((list<value> )exp));
+}
+
+static list<value> lambdaBody(const value& exp) {
+ return cdr(cdr((list<value> )exp));
+}
+
+const value makeProcedure(const list<value>& parameters, const value& body, const Env& env) {
+ return mklist<value>(procedureSymbol, parameters, body, env);
+}
+
+const bool isApply(const value& exp) {
+ return isTaggedList(exp, applySymbol);
+}
+
+const bool isApplication(const value& exp) {
+ return isList(exp);
+}
+
+const value operat(const value& exp) {
+ return car((list<value> )exp);
+}
+
+const list<value> operands(const value& exp) {
+ return cdr((list<value> )exp);
+}
+
+const list<value> listOfValues(const list<value> exps, Env& env) {
+ if(isNil(exps))
+ return list<value> ();
+ return cons(evalExpr(car(exps), env), listOfValues(cdr(exps), env));
+}
+
+const value applyOperat(const value& exp) {
+ return cadr((list<value> )exp);
+}
+
+const value applyOperand(const value& exp) {
+ return caddr((list<value> )exp);
+}
+
+const bool isCompoundProcedure(const value& procedure) {
+ return isTaggedList(procedure, procedureSymbol);
+}
+
+const list<value> procedureParameters(const value& exp) {
+ return car(cdr((list<value> )exp));
+}
+
+const value procedureBody(const value& exp) {
+ return car(cdr(cdr((list<value> )exp)));
+}
+
+const Env procedureEnvironment(const value& exp) {
+ return (Env)car(cdr(cdr(cdr((list<value> )exp))));
+}
+
+const bool isLastExp(const list<value>& seq) {
+ return isNil(cdr(seq));
+}
+
+const value firstExp(const list<value>& seq) {
+ return car(seq);
+}
+
+const list<value> restExp(const list<value>& seq) {
+ return cdr(seq);
+}
+
+const value makeBegin(const list<value> seq) {
+ return cons(beginSymbol, seq);
+}
+
+const value evalSequence(const list<value>& exps, Env& env) {
+ if(isLastExp(exps))
+ return evalExpr(firstExp(exps), env);
+ evalExpr(firstExp(exps), env);
+ return evalSequence(restExp(exps), env);
+}
+
+const value applyProcedure(const value& procedure, list<value>& arguments) {
+ if(isPrimitiveProcedure(procedure))
+ return applyPrimitiveProcedure(procedure, arguments);
+ if(isCompoundProcedure(procedure)) {
+ Env env = extendEnvironment(procedureParameters(procedure), arguments, procedureEnvironment(procedure));
+ return evalSequence(procedureBody(procedure), env);
+ }
+ logStream() << "Unknown procedure type " << procedure << endl;
+ return value();
+}
+
+const value sequenceToExp(const list<value> exps) {
+ if(isNil(exps))
+ return exps;
+ if(isLastExp(exps))
+ return firstExp(exps);
+ return makeBegin(exps);
+}
+
+const list<value> condClauses(const value& exp) {
+ return cdr((list<value> )exp);
+}
+
+const value condPredicate(const value& clause) {
+ return car((list<value> )clause);
+}
+
+const list<value> condActions(const value& clause) {
+ return cdr((list<value> )clause);
+}
+
+const value ifPredicate(const value& exp) {
+ return car(cdr((list<value> )exp));
+}
+
+const value ifConsequent(const value& exp) {
+ return car(cdr(cdr((list<value> )exp)));
+}
+
+const value ifAlternative(const value& exp) {
+ if(!isNil(cdr(cdr(cdr((list<value> )exp)))))
+ return car(cdr(cdr(cdr((list<value> )exp))));
+ return false;
+}
+
+const bool isCond(const value& exp) {
+ return isTaggedList(exp, condSymbol);
+}
+
+const bool isCondElseClause(const value& clause) {
+ return condPredicate(clause) == elseSymbol;
+}
+
+const bool isIf(const value& exp) {
+ return isTaggedList(exp, ifSymbol);
+}
+
+const value makeIf(value predicate, value consequent, value alternative) {
+ return mklist(ifSymbol, predicate, consequent, alternative);
+}
+
+const value expandClauses(const list<value>& clauses) {
+ if(isNil(clauses))
+ return false;
+ const value first = car(clauses);
+ const list<value> rest = cdr(clauses);
+ if(isCondElseClause(first)) {
+ if(isNil(rest))
+ return sequenceToExp(condActions(first));
+ logStream() << "else clause isn't last " << clauses << endl;
+ return value();
+ }
+ return makeIf(condPredicate(first), sequenceToExp(condActions(first)), expandClauses(rest));
+}
+
+value condToIf(const value& exp) {
+ return expandClauses(condClauses(exp));
+}
+
+value evalIf(const value& exp, Env& env) {
+ if(isTrue(evalExpr(ifPredicate(exp), env)))
+ return evalExpr(ifConsequent(exp), env);
+ return evalExpr(ifAlternative(exp), env);
+}
+
+const value evalDefinition(const value& exp, Env& env) {
+ defineVariable(definitionVariable(exp), evalExpr(definitionValue(exp), env), env);
+ return definitionVariable(exp);
+}
+
+const value evalExpr(const value& exp, Env& env) {
+ if(isSelfEvaluating(exp))
+ return exp;
+ if(isQuoted(exp))
+ return textOfQuotation(exp);
+ if(isDefinition(exp))
+ return evalDefinition(exp, env);
+ if(isIf(exp))
+ return evalIf(exp, env);
+ if(isBegin(exp))
+ return evalSequence(beginActions(exp), env);
+ if(isCond(exp))
+ return evalExpr(condToIf(exp), env);
+ if(isLambdaExpr(exp))
+ return makeProcedure(lambdaParameters(exp), lambdaBody(exp), env);
+ if(isVariable(exp))
+ return lookupVariableValue(exp, env);
+ if(isApply(exp)) {
+ list<value> applyOperandValues = evalExpr(applyOperand(exp), env);
+ return applyProcedure(evalExpr(applyOperat(exp), env), applyOperandValues);
+ }
+ if(isApplication(exp)) {
+ list<value> operandValues = listOfValues(operands(exp), env);
+ return applyProcedure(evalExpr(operat(exp), env), operandValues);
+ }
+ logStream() << "Unknown expression type " << exp << endl;
+ return value();
+}
+
+const list<value> quotedParameters(const list<value>& p) {
+ if (isNil(p))
+ return p;
+ return cons<value>(mklist<value>(quoteSymbol, car(p)), quotedParameters(cdr(p)));
+}
+
+/**
+ * Evaluate an expression against a script provided as a list of values.
+ */
+const value evalScriptLoop(const value& expr, const list<value>& script, scheme::Env& env) {
+ if (isNil(script))
+ return scheme::evalExpr(expr, env);
+ scheme::evalExpr(car(script), env);
+ return evalScriptLoop(expr, cdr(script), env);
+}
+
+const value evalScript(const value& expr, const value& script, Env& env) {
+ return evalScriptLoop(expr, script, env);
+}
+
+/**
+ * Evaluate an expression against a script provided as an input stream.
+ */
+const value evalScript(const value& expr, istream& is, Env& env) {
+ return evalScript(expr, readScript(is), env);
+}
+
+}
+}
+#endif /* tuscany_scheme_eval_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/modules/scheme/io.hpp b/sca-cpp/branches/lightweight-sca/modules/scheme/io.hpp
new file mode 100644
index 0000000000..8f9d70e7fe
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/scheme/io.hpp
@@ -0,0 +1,239 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+#ifndef tuscany_scheme_io_hpp
+#define tuscany_scheme_io_hpp
+
+/**
+ * Script evaluator IO functions.
+ */
+
+#include <ctype.h>
+#include "stream.hpp"
+#include "string.hpp"
+
+#include "list.hpp"
+#include "value.hpp"
+#include "primitive.hpp"
+
+namespace tuscany {
+namespace scheme {
+
+const value rightParenthesis(mklist<value>(")"));
+const value leftParenthesis(mklist<value>("("));
+const value comment(mklist<value>(";"));
+
+const double stringToNumber(const string& str) {
+ return atof(c_str(str));
+}
+
+const bool isWhitespace(const char ch) {
+ return ch != -1 && isspace(ch);
+}
+
+const bool isIdentifierStart(const char ch) {
+ return ch != -1 && !isspace(ch) && !isdigit(ch);
+}
+
+const bool isIdentifierPart(const char ch) {
+ return ch != -1 && !isspace(ch) && ch != '(' && ch != ')';
+}
+
+const bool isDigit(const char ch) {
+ return isdigit(ch) || ch == '.';
+}
+
+const bool isLeftParenthesis(const value& token) {
+ return leftParenthesis == token;
+}
+
+const bool isRightParenthesis(const value& token) {
+ return rightParenthesis == token;
+}
+
+const char readChar(istream& in) {
+ if(in.eof()) {
+ return -1;
+ }
+ char c = (char)get(in);
+ return c;
+}
+
+const char peekChar(istream& in) {
+ if(eof(in))
+ return -1;
+ char c = (char)peek(in);
+ return c;
+}
+
+const bool isQuote(const value& token) {
+ return token == quoteSymbol;
+}
+
+const failable<value> skipComment(istream& in);
+const value readQuoted(istream& in);
+const value readIdentifier(const char chr, istream& in);
+const value readString(istream& in);
+const value readNumber(const char chr, istream& in);
+const value readValue(istream& in);
+
+const failable<value> readToken(istream& in) {
+ const char firstChar = readChar(in);
+ if(isWhitespace(firstChar))
+ return readToken(in);
+ if(firstChar == ';')
+ return skipComment(in);
+ if(firstChar == '\'')
+ return readQuoted(in);
+ if(firstChar == '(')
+ return leftParenthesis;
+ if(firstChar == ')')
+ return rightParenthesis;
+ if(firstChar == '"')
+ return readString(in);
+ if(isIdentifierStart(firstChar))
+ return readIdentifier(firstChar, in);
+ if(isDigit(firstChar))
+ return readNumber(firstChar, in);
+ if(firstChar == -1)
+ return mkfailure<value>();
+ logStream() << "Illegal lexical syntax '" << firstChar << "'" << endl;
+ return readToken(in);
+}
+
+const failable<value> skipComment(istream& in) {
+ const char nextChar = readChar(in);
+ if (nextChar == '\n')
+ return readToken(in);
+ return skipComment(in);
+}
+
+const value readQuoted(istream& in) {
+ return mklist(quoteSymbol, readValue(in));
+}
+
+const list<value> readList(const list<value>& listSoFar, istream& in) {
+ const failable<value> ftoken = readToken(in);
+ if (!hasContent(ftoken))
+ return reverse(listSoFar);
+ const value token = content(ftoken);
+ if(isRightParenthesis(token))
+ return reverse(listSoFar);
+ if(isLeftParenthesis(token))
+ return readList(cons(value(readList(list<value> (), in)), listSoFar), in);
+ return readList(cons(token, listSoFar), in);
+}
+
+const string listToString(const list<char>& l) {
+ if(isNil(l))
+ return "";
+ const char buf[1] = { car(l) };
+ return string(buf, 1) + listToString(cdr(l));
+}
+
+const list<char> readIdentifierHelper(const list<char>& listSoFar, istream& in) {
+ const char nextChar = peekChar(in);
+ if(isIdentifierPart(nextChar))
+ return readIdentifierHelper(cons(readChar(in), listSoFar), in);
+ return reverse(listSoFar);
+}
+
+const value readIdentifier(const char chr, istream& in) {
+ const value val = c_str(listToString(readIdentifierHelper(mklist(chr), in)));
+ if (val == "false")
+ return value((bool)false);
+ if (val == "true")
+ return value((bool)true);
+ if (val == "nil")
+ return value();
+ return val;
+}
+
+const list<char> readStringHelper(const list<char>& listSoFar, istream& in) {
+ const char nextChar = readChar(in);
+ if(nextChar == -1 || nextChar == '"')
+ return reverse(listSoFar);
+ if (nextChar == '\\') {
+ const char escapedChar = readChar(in);
+ if (escapedChar == -1)
+ return reverse(listSoFar);
+ return readStringHelper(cons(escapedChar, listSoFar), in);
+ }
+ return readStringHelper(cons(nextChar, listSoFar), in);
+}
+
+const value readString(istream& in) {
+ return listToString(readStringHelper(list<char>(), in));
+}
+
+const list<char> readNumberHelper(const list<char>& listSoFar, istream& in) {
+ const char nextChar = peekChar(in);
+ if(isDigit(nextChar))
+ return readNumberHelper(cons(readChar(in), listSoFar), in);
+ return reverse(listSoFar);
+}
+
+const value readNumber(const char chr, istream& in) {
+ return stringToNumber(listToString(readNumberHelper(mklist(chr), in)));
+}
+
+const value readValue(istream& in) {
+ const failable<value> fnextToken = readToken(in);
+ if (!hasContent(fnextToken))
+ return value();
+ const value nextToken = content(fnextToken);
+ if(isLeftParenthesis(nextToken))
+ return readList(list<value>(), in);
+ return nextToken;
+}
+
+const value readValue(const string s) {
+ istringstream in(s);
+ const failable<value> fnextToken = readToken(in);
+ if (!hasContent(fnextToken))
+ return value();
+ const value nextToken = content(fnextToken);
+ if(isLeftParenthesis(nextToken))
+ return readList(list<value>(), in);
+ return nextToken;
+}
+
+const bool writeValue(const value& val, ostream& out) {
+ out << val;
+ return true;
+}
+
+const string writeValue(const value& val) {
+ ostringstream out;
+ out << val;
+ return str(out);
+}
+
+const value readScript(istream& in) {
+ const value val = readValue(in);
+ if (isNil(val))
+ return list<value>();
+ return cons(val, (list<value>)readScript(in));
+}
+
+}
+}
+#endif /* tuscany_scheme_io_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/modules/scheme/json-value.cpp b/sca-cpp/branches/lightweight-sca/modules/scheme/json-value.cpp
new file mode 100644
index 0000000000..4bdf8bd37f
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/scheme/json-value.cpp
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Convert a JSON document to a scheme value.
+ */
+
+#include "fstream.hpp"
+#include "string.hpp"
+#include "../json/json.hpp"
+#include "element.hpp"
+#include "eval.hpp"
+
+namespace tuscany {
+namespace scheme {
+
+int jsonValue() {
+ const js::JSContext cx;
+ const failable<list<value> > lv = json::readJSON(streamList(cin), cx);
+ if (!hasContent(lv)) {
+ cerr << reason(lv) << " : " << rcode(lv);
+ return 1;
+ }
+ cout << writeValue(content(lv));
+ return 0;
+}
+
+}
+}
+
+int main() {
+ return tuscany::scheme::jsonValue();
+}
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/scheme/primitive.hpp b/sca-cpp/branches/lightweight-sca/modules/scheme/primitive.hpp
new file mode 100644
index 0000000000..59aee12073
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/scheme/primitive.hpp
@@ -0,0 +1,288 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+#ifndef tuscany_scheme_primitive_hpp
+#define tuscany_scheme_primitive_hpp
+
+/**
+ * Script evaluator primitive functions.
+ */
+
+#include "stream.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "parallel.hpp"
+
+namespace tuscany {
+namespace scheme {
+
+const value primitiveSymbol("primitive");
+const value quoteSymbol("'");
+const value lambdaSymbol("lambda");
+
+#ifdef WANT_THREADS
+perthread_ptr<ostream> displayOutStream;
+#else
+ostream* displayOutStream = NULL;
+#endif
+
+#ifdef WANT_THREADS
+perthread_ptr<ostream> logOutStream;
+#else
+ostream* logOutStream = NULL;
+#endif
+
+const bool setupDisplay(ostream& out) {
+ displayOutStream = &out;
+ return true;
+}
+
+ostream& displayStream() {
+ if (displayOutStream == NULL)
+ return cout;
+ return *displayOutStream;
+}
+
+const bool setupLog(ostream& out) {
+ logOutStream = &out;
+ return true;
+}
+
+ostream& logStream() {
+ if (logOutStream == NULL)
+ return cerr;
+ return *logOutStream;
+}
+
+const value carProc(const list<value>& args) {
+ return car((list<value> )car(args));
+}
+
+const value cdrProc(const list<value>& args) {
+ return cdr((list<value> )car(args));
+}
+
+const value consProc(const list<value>& args) {
+ return cons(car(args), (list<value> )cadr(args));
+}
+
+const value listProc(const list<value>& args) {
+ return args;
+}
+
+const value assocProc(const list<value>& args) {
+ return assoc(car(args), (list<list<value> >)cadr(args));
+}
+
+const value nulProc(const list<value>& args) {
+ const value v(car(args));
+ if (isNil(v))
+ return true;
+ if (isList(v))
+ return isNil(list<value>(v));
+ return false;
+}
+
+const value equalProc(const list<value>& args) {
+ return (bool)(car(args) == cadr(args));
+}
+
+const value addProc(const list<value>& args) {
+ if (isNil(cdr(args)))
+ return (double)car(args);
+ return (double)car(args) + (double)cadr(args);
+}
+
+const value subProc(const list<value>& args) {
+ if (isNil(cdr(args)))
+ return (double)0 - (double)car(args);
+ return (double)car(args) - (double)cadr(args);
+}
+
+const value mulProc(const list<value>& args) {
+ return (double)car(args) * (double)cadr(args);
+}
+
+const value divProc(const list<value>& args) {
+ return (double)car(args) / (double)cadr(args);
+}
+
+const value displayProc(const list<value>& args) {
+ if (isNil(args)) {
+ displayStream() << endl;
+ return true;
+ }
+ displayStream() << car(args);
+ return displayProc(cdr(args));
+}
+
+const value logProc(const list<value>& args) {
+ if (isNil(args)) {
+ logStream() << endl;
+ return true;
+ }
+ logStream() << car(args);
+ return logProc(cdr(args));
+}
+
+const value uuidProc(unused const list<value>& args) {
+ return mkuuid();
+}
+
+const value cadrProc(const list<value>& args) {
+ return cadr((list<value> )car(args));
+}
+
+const value caddrProc(const list<value>& args) {
+ return caddr((list<value> )car(args));
+}
+
+const value cadddrProc(const list<value>& args) {
+ return cadddr((list<value> )car(args));
+}
+
+const value cddrProc(const list<value>& args) {
+ return cddr((list<value> )car(args));
+}
+
+const value cdddrProc(const list<value>& args) {
+ return cdddr((list<value> )car(args));
+}
+
+const value appendProc(const list<value>& args) {
+ return append((list<value> )car(args), (list<value>)cadr(args));
+}
+
+const value startProc(unused const list<value>& args) {
+ return lambda<value(const list<value>&)>();
+}
+
+const value stopProc(unused const list<value>& args) {
+ return lambda<value(const list<value>&)>();
+}
+
+const value applyPrimitiveProcedure(const value& proc, list<value>& args) {
+ const lambda<value(const list<value>&)> func(cadr((list<value>)proc));
+ return func(args);
+}
+
+const bool isPrimitiveProcedure(const value& proc) {
+ return isTaggedList(proc, primitiveSymbol);
+}
+
+const bool isSelfEvaluating(const value& exp) {
+ if(isNil(exp))
+ return true;
+ if(isNumber(exp))
+ return true;
+ if(isString(exp))
+ return true;
+ if(isBool(exp))
+ return true;
+ if(isLambda(exp))
+ return true;
+ return false;
+}
+
+const value primitiveImplementation(const list<value>& proc) {
+ return car(cdr(proc));
+}
+
+template<typename F> const value primitiveProcedure(const F& f) {
+ return mklist<value>(primitiveSymbol, (lambda<value(const list<value>&)>)f);
+}
+
+const list<value> primitiveProcedureNames() {
+ return mklist<value>("car")
+ + "cdr"
+ + "cons"
+ + "list"
+ + "nul"
+ + "="
+ + "equal?"
+ + "+"
+ + "-"
+ + "*"
+ + "/"
+ + "assoc"
+ + "cadr"
+ + "caddr"
+ + "cadddr"
+ + "cddr"
+ + "cdddr"
+ + "append"
+ + "display"
+ + "log"
+ + "uuid"
+ + "start"
+ + "stop";
+}
+
+const list<value> primitiveProcedureObjects() {
+ return mklist(primitiveProcedure(carProc))
+ + primitiveProcedure(cdrProc)
+ + primitiveProcedure(consProc)
+ + primitiveProcedure(listProc)
+ + primitiveProcedure(nulProc)
+ + primitiveProcedure(equalProc)
+ + primitiveProcedure(equalProc)
+ + primitiveProcedure(addProc)
+ + primitiveProcedure(subProc)
+ + primitiveProcedure(mulProc)
+ + primitiveProcedure(divProc)
+ + primitiveProcedure(assocProc)
+ + primitiveProcedure(cadrProc)
+ + primitiveProcedure(caddrProc)
+ + primitiveProcedure(cadddrProc)
+ + primitiveProcedure(cddrProc)
+ + primitiveProcedure(cdddrProc)
+ + primitiveProcedure(appendProc)
+ + primitiveProcedure(displayProc)
+ + primitiveProcedure(logProc)
+ + primitiveProcedure(uuidProc)
+ + primitiveProcedure(startProc)
+ + primitiveProcedure(stopProc);
+}
+
+const bool isFalse(const value& exp) {
+ return (bool)exp == false;
+}
+
+const bool isTrue(const value& exp) {
+ return (bool)exp == true;
+}
+
+const bool isQuoted(const value& exp) {
+ return isTaggedList(exp, quoteSymbol);
+}
+
+const value textOfQuotation(const value& exp) {
+ return car(cdr((list<value> )exp));
+}
+
+const value makeLambda(const list<value>& parameters, const list<value>& body) {
+ return cons(lambdaSymbol, cons<value>(parameters, body));
+}
+
+}
+}
+#endif /* tuscany_scheme_primitive_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/modules/scheme/scheme-shell.cpp b/sca-cpp/branches/lightweight-sca/modules/scheme/scheme-shell.cpp
new file mode 100644
index 0000000000..4aa67c2375
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/scheme/scheme-shell.cpp
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Script evaluator shell, used for interactive testing of scripts.
+ */
+
+#include <assert.h>
+#include "gc.hpp"
+#include "stream.hpp"
+#include "string.hpp"
+#include "driver.hpp"
+
+int main() {
+ tuscany::gc_scoped_pool pool;
+ tuscany::scheme::evalDriverRun(tuscany::cin, tuscany::cout);
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/scheme/scheme-test.cpp b/sca-cpp/branches/lightweight-sca/modules/scheme/scheme-test.cpp
new file mode 100644
index 0000000000..5b69b8e588
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/scheme/scheme-test.cpp
@@ -0,0 +1,242 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test script evaluator.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "driver.hpp"
+
+namespace tuscany {
+namespace scheme {
+
+bool testEnv() {
+ gc_scoped_pool pool;
+ Env globalEnv = list<value>();
+ Env env = extendEnvironment(mklist<value>("a"), mklist<value>(1), globalEnv);
+ defineVariable("x", env, env);
+ assert(lookupVariableValue(value("x"), env) == env);
+ assert(lookupVariableValue("a", env) == value(1));
+ return true;
+}
+
+bool testEnvGC() {
+ resetLambdaCounters();
+ resetListCounters();
+ resetValueCounters();
+ testEnv();
+ assert(checkValueCounters());
+ assert(checkLambdaCounters());
+ assert(checkListCounters());
+ return true;
+}
+
+bool testRead() {
+ istringstream is("abcd");
+ assert(readValue(is) == "abcd");
+
+ istringstream is2("123");
+ assert(readValue(is2) == value(123));
+
+ istringstream is3("(abcd)");
+ assert(readValue(is3) == mklist(value("abcd")));
+
+ istringstream is4("(abcd xyz)");
+ assert(readValue(is4) == mklist<value>("abcd", "xyz"));
+
+ istringstream is5("(abcd (xyz tuv))");
+ assert(readValue(is5) == mklist<value>("abcd", mklist<value>("xyz", "tuv")));
+
+ return true;
+}
+
+bool testWrite() {
+ {
+ const list<value> i = list<value>()
+ + (list<value>() + "item" + "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"
+ + (list<value>() + "item"
+ + (list<value>() + "name" + "Apple")
+ + (list<value>() + "price" + "$2.99")))
+ + (list<value>() + "item" + "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c"
+ + (list<value>() + "item"
+ + (list<value>() + "name" + "Orange")
+ + (list<value>() + "price" + "$3.55")));
+ const list<value> a = cons<value>("Feed", cons<value>("feed-1234", i));
+ ostringstream os;
+ writeValue(a, os);
+ istringstream is(str(os));
+ assert(readValue(is) == a);
+ }
+ {
+ const list<value> i = mklist<value>("x", value());
+ const list<value> a = mklist<value>(i);
+ ostringstream os;
+ writeValue(a, os);
+ istringstream is(str(os));
+ assert(readValue(is) == a);
+ }
+ return true;
+}
+
+const string testSchemeNumber(
+ "(define (testNumber) (if (= 1 1) (display \"testNumber ok\") (error \"testNumber\"))) "
+ "(testNumber)");
+
+const string testSchemeString(
+ "(define (testString) (if (= \"abc\" \"abc\") (display \"testString ok\") (error \"testString\"))) "
+ "(testString)");
+
+const string testSchemeDefinition(
+ "(define a \"abc\") (define (testDefinition) (if (= a \"abc\") (display \"testDefinition ok\") (error \"testDefinition\"))) "
+ "(testDefinition)");
+
+const string testSchemeIf(
+ "(define (testIf) (if (= \"abc\" \"abc\") (if (= \"xyz\" \"xyz\") (display \"testIf ok\") (error \"testNestedIf\")) (error \"testIf\"))) "
+ "(testIf)");
+
+const string testSchemeCond(
+ "(define (testCond) (cond ((= \"abc\" \"abc\") (display \"testCond ok\")) (else (error \"testIf\"))))"
+ "(testCond)");
+
+const string testSchemeBegin(
+ "(define (testBegin) "
+ "(begin "
+ "(define a \"abc\") "
+ "(if (= a \"abc\") (display \"testBegin1 ok\") (error \"testBegin\")) "
+ "(define x \"xyz\") "
+ "(if (= x \"xyz\") (display \"testBegin2 ok\") (error \"testBegin\")) "
+ ") "
+ ") "
+ "(testBegin)");
+
+const string testSchemeLambda(
+ "(define sqrt (lambda (x) (* x x))) "
+ "(define (testLambda) (if (= 4 (sqrt 2)) (display \"testLambda ok\") (error \"testLambda\"))) "
+ "(testLambda)");
+
+const string testSchemeForward(
+ "(define (testLambda) (if (= 4 (sqrt 2)) (display \"testForward ok\") (error \"testForward\"))) "
+ "(define sqrt (lambda (x) (* x x))) "
+ "(testLambda)");
+
+const string evalOutput(const string& scm) {
+ istringstream is(scm);
+ ostringstream os;
+ evalDriverRun(is, os);
+ return str(os);
+}
+
+bool testEval() {
+ gc_scoped_pool pool;
+ assert(contains(evalOutput(testSchemeNumber), "testNumber ok"));
+ assert(contains(evalOutput(testSchemeString), "testString ok"));
+ assert(contains(evalOutput(testSchemeDefinition), "testDefinition ok"));
+ assert(contains(evalOutput(testSchemeIf), "testIf ok"));
+ assert(contains(evalOutput(testSchemeCond), "testCond ok"));
+ assert(contains(evalOutput(testSchemeBegin), "testBegin1 ok"));
+ assert(contains(evalOutput(testSchemeBegin), "testBegin2 ok"));
+ assert(contains(evalOutput(testSchemeLambda), "testLambda ok"));
+ assert(contains(evalOutput(testSchemeForward), "testForward ok"));
+ return true;
+}
+
+bool testEvalExpr() {
+ gc_scoped_pool pool;
+ const value exp = mklist<value>("+", 2, 3);
+ Env env = setupEnvironment();
+ const value r = evalExpr(exp, env);
+ assert(r == value(5));
+ return true;
+}
+
+bool testEvalRun() {
+ gc_scoped_pool pool;
+ evalDriverRun(cin, cout);
+ return true;
+}
+
+const value mult(const list<value>& args) {
+ const double x = car(args);
+ const double y = cadr(args);
+ return x * y;
+}
+
+const string testReturnLambda(
+ "(define (testReturnLambda) * )");
+
+const string testCallLambda(
+ "(define (testCallLambda l x y) (l x y))");
+
+bool testEvalLambda() {
+ gc_scoped_pool pool;
+ Env env = setupEnvironment();
+
+ const value trl = mklist<value>("testReturnLambda");
+ istringstream trlis(testReturnLambda);
+ const value trlv = evalScript(trl, trlis, env);
+
+ istringstream tclis(testCallLambda);
+ const value tcl = cons<value>("testCallLambda", quotedParameters(mklist<value>(trlv, 2, 3)));
+ const value tclv = evalScript(tcl, tclis, env);
+ assert(tclv == value(6));
+
+ istringstream tcelis(testCallLambda);
+ const value tcel = cons<value>("testCallLambda", quotedParameters(mklist<value>(primitiveProcedure(mult), 3, 4)));
+ const value tcelv = evalScript(tcel, tcelis, env);
+ assert(tcelv == value(12));
+ return true;
+}
+
+bool testEvalGC() {
+ resetLambdaCounters();
+ resetListCounters();
+ resetValueCounters();
+ testEval();
+ testEvalExpr();
+ testEvalLambda();
+ assert(checkValueCounters());
+ assert(checkLambdaCounters());
+ assert(checkListCounters());
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::gc_scoped_pool p;
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::scheme::testEnv();
+ tuscany::scheme::testEnvGC();
+ tuscany::scheme::testRead();
+ tuscany::scheme::testWrite();
+ tuscany::scheme::testEval();
+ tuscany::scheme::testEvalExpr();
+ tuscany::scheme::testEvalLambda();
+ tuscany::scheme::testEvalGC();
+
+ tuscany::cout << "OK" << tuscany::endl;
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/scheme/value-element.cpp b/sca-cpp/branches/lightweight-sca/modules/scheme/value-element.cpp
new file mode 100644
index 0000000000..a4acdaf2d7
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/scheme/value-element.cpp
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Convert a scheme value to a value representing an element.
+ */
+
+#include "fstream.hpp"
+#include "string.hpp"
+#include "element.hpp"
+#include "eval.hpp"
+
+namespace tuscany {
+namespace scheme {
+
+int valueElement() {
+ const value v = valuesToElements(readValue(cin));
+ cout << writeValue(v);
+ return 0;
+}
+
+}
+}
+
+int main() {
+ return tuscany::scheme::valueElement();
+}
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/scheme/value-json.cpp b/sca-cpp/branches/lightweight-sca/modules/scheme/value-json.cpp
new file mode 100644
index 0000000000..a8c875fcc8
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/scheme/value-json.cpp
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Convert a scheme value to a JSON document.
+ */
+
+#include "fstream.hpp"
+#include "string.hpp"
+#include "../json/json.hpp"
+#include "element.hpp"
+#include "eval.hpp"
+
+namespace tuscany {
+namespace scheme {
+
+int valueJSON() {
+ const js::JSContext cx;
+ failable<list<string> > s = json::writeJSON(readValue(cin), cx);
+ if (!hasContent(s)) {
+ cerr << reason(s) << " : " << rcode(s);
+ return 1;
+ }
+ write(content(s), cout);
+ return 0;
+}
+
+}
+}
+
+int main() {
+ return tuscany::scheme::valueJSON();
+}
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/scheme/value-xml.cpp b/sca-cpp/branches/lightweight-sca/modules/scheme/value-xml.cpp
new file mode 100644
index 0000000000..ff785899c6
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/scheme/value-xml.cpp
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Convert a scheme value to an XML document.
+ */
+
+#include "fstream.hpp"
+#include "string.hpp"
+#include "xml.hpp"
+#include "element.hpp"
+#include "eval.hpp"
+
+namespace tuscany {
+namespace scheme {
+
+int valueXML() {
+ failable<list<string> > s = writeXML(readValue(cin));
+ if (!hasContent(s)) {
+ cerr << reason(s) << " : " << rcode(s);
+ return 1;
+ }
+ write(content(s), cout);
+ return 0;
+}
+
+}
+}
+
+int main() {
+ return tuscany::scheme::valueXML();
+}
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/scheme/xml-value.cpp b/sca-cpp/branches/lightweight-sca/modules/scheme/xml-value.cpp
new file mode 100644
index 0000000000..d88f754aa5
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/scheme/xml-value.cpp
@@ -0,0 +1,47 @@
+/*
+ * 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$ */
+
+/**
+ * Convert an XML document to a scheme value.
+ */
+
+#include "fstream.hpp"
+#include "string.hpp"
+#include "xml.hpp"
+#include "element.hpp"
+#include "eval.hpp"
+
+namespace tuscany {
+namespace scheme {
+
+int xmlValue() {
+ const value v = readXML(streamList(cin));
+ cout << writeValue(v);
+ return 0;
+}
+
+}
+}
+
+int main() {
+ return tuscany::scheme::xmlValue();
+}
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/server/Makefile.am b/sca-cpp/branches/lightweight-sca/modules/server/Makefile.am
new file mode 100644
index 0000000000..e2fd67d9b8
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/server/Makefile.am
@@ -0,0 +1,50 @@
+# 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/*.html htdocs/test/*.xml htdocs/test/*.txt
+
+mod_LTLIBRARIES = libmod_tuscany_eval.la
+noinst_DATA = libmod_tuscany_eval${libsuffix}
+
+libmod_tuscany_eval_la_SOURCES = mod-eval.cpp
+libmod_tuscany_eval_la_LDFLAGS = -lxml2 -lcurl -lmozjs
+libmod_tuscany_eval${libsuffix}:
+ ln -s .libs/libmod_tuscany_eval${libsuffix}
+
+noinst_test_LTLIBRARIES = libimpl-test.la
+noinst_testdir = `pwd`/tmp
+noinst_DATA += libimpl-test${libsuffix}
+
+libimpl_test_la_SOURCES = impl-test.cpp
+libimpl-test${libsuffix}:
+ ln -s .libs/libimpl-test${libsuffix}
+
+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/sca-cpp/branches/lightweight-sca/modules/server/client-test.cpp b/sca-cpp/branches/lightweight-sca/modules/server/client-test.cpp
new file mode 100644
index 0000000000..3f5ff20c56
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/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/scheme";
+
+ tuscany::server::testServer();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/server/client-test.hpp b/sca-cpp/branches/lightweight-sca/modules/server/client-test.hpp
new file mode 100644
index 0000000000..1c7b26da39
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/server/client-test.hpp
@@ -0,0 +1,372 @@
+/*
+ * 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/scheme";
+bool testBlobs = true;
+
+ostream* curlWriter(const string& s, ostream* os) {
+ (*os) << s;
+ return os;
+}
+
+const bool testGet() {
+ gc_scoped_pool pool;
+ http::CURLSession ch("", "", "", "", 0);
+ {
+ 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("", "", "", "", 0);
+ 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("", "", "", "", 0);
+ const failable<value> r = http::evalExpr(mklist<value>(string("echo"), string("Hello")), testURI, ch);
+ assert(hasContent(r));
+ assert(content(r) == 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 failable<value> r = http::evalExpr(mklist<value>(string("echo"), string("Hello")), uri, ch);
+ assert(hasContent(r));
+ assert(content(r) == 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 failable<value> r = content(http::evalExpr(mklist<value>(string("echo"), blobs), uri, ch));
+ assert(hasContent(r));
+ assert(content(r) == blobs);
+ return true;
+ }
+};
+
+const bool testEvalPerf() {
+ gc_scoped_pool pool;
+ http::CURLSession ch("", "", "", "", 0);
+ 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>() + "content" + (list<value>() + "item"
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "price" + string("$2.99")));
+ const list<value> a = list<value>() + (list<value>() + "entry"
+ + (list<value>() + "title" + string("item"))
+ + (list<value>() + "id" + string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"))
+ + i);
+ http::CURLSession ch("", "", "", "", 0);
+ 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("", "", "", "", 0);
+ {
+ const list<value> i = list<value>() + "content" + (list<value>() + "item"
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "price" + string("$2.99")));
+ const list<value> val = list<value>() + (list<value>() + "entry"
+ + (list<value>() + "title" + string("item"))
+ + (list<value>() + "id" + 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>() + "content" + (list<value>() + "item"
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "blob1" + blob)
+ + (list<value>() + "blob2" + blob)
+ + (list<value>() + "price" + string("$2.99")));
+ const list<value> val = list<value>() + (list<value>() + "entry"
+ + (list<value>() + "title" + string("item"))
+ + (list<value>() + "id" + 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("", "", "", "", 0);
+ 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;
+ worker& w;
+ const int threads;
+ postThreadLoop(const lambda<bool()>& l, worker& w, const int threads) : l(l), w(w), threads(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;
+ worker w(threads);
+
+ const list<value> i = list<value>() + "content" + (list<value>() + "item"
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "price" + string("$2.99")));
+ const value val = list<value>() + (list<value>() + "entry"
+ + (list<value>() + "title" + string("item"))
+ + (list<value>() + "id" + 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, w, 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("", "", "", "", 0);
+ 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>() + "content" + (list<value>() + "item"
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "price" + string("$2.99")));
+ const value val = list<value>() + (list<value>() + "entry"
+ + (list<value>() + "title" + string("item"))
+ + (list<value>() + "id" + 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>() + "content" + (list<value>() + "item"
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "price" + string("$2.99")));
+ const list<value> a = list<value>() + (list<value>() + "entry"
+ + (list<value>() + "title" + string("item"))
+ + (list<value>() + "id" + string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"))
+ + i);
+ http::CURLSession ch("", "", "", "", 0);
+ 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("", "", "", "", 0);
+ value rc = content(http::del(testURI + "/111", ch));
+ assert(rc == value(true));
+ return true;
+}
+
+const bool testServer() {
+ tuscany::server::testGet();
+ tuscany::server::testPost();
+ tuscany::server::testPut();
+ tuscany::server::testDel();
+ tuscany::server::testEval();
+ tuscany::server::testGetPerf();
+ tuscany::server::testPostPerf();
+#ifdef WANT_THREADS
+ tuscany::server::testPostThreadPerf();
+#else
+ tuscany::server::testPostForkPerf();
+#endif
+ tuscany::server::testEvalPerf();
+ return true;
+}
+
+}
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/server/client-test.scm b/sca-cpp/branches/lightweight-sca/modules/server/client-test.scm
new file mode 100644
index 0000000000..47b799d390
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/server/client-test.scm
@@ -0,0 +1,38 @@
+; Licensed to the Apache Software Foundation (ASF) under one
+; or more contributor license agreements. See the NOTICE file
+; distributed with this work for additional information
+; regarding copyright ownership. The ASF licenses this file
+; to you under the Apache License, Version 2.0 (the
+; "License"); you may not use this file except in compliance
+; with the License. You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing,
+; software distributed under the License is distributed on an
+; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+; KIND, either express or implied. See the License for the
+; specific language governing permissions and limitations
+; under the License.
+
+; JSON-RPC test case
+
+(define (echo x ref) (ref "echo" x))
+
+; ATOMPub test case
+
+(define (get id ref)
+ (ref "get" id)
+)
+
+(define (post coll entry ref)
+ (ref "post" coll entry)
+)
+
+(define (put id entry ref)
+ (ref "put" id entry)
+)
+
+(define (delete id ref)
+ (ref "delete" id)
+)
diff --git a/sca-cpp/branches/lightweight-sca/modules/server/cpp-conf b/sca-cpp/branches/lightweight-sca/modules/server/cpp-conf
new file mode 100755
index 0000000000..6b74f60ec5
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/server/cpp-conf
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Generate a C++ server conf
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+uname=`uname -s`
+if [ $uname = "Darwin" ]; then
+ libsuffix=".dylib"
+else
+ libsuffix=".so"
+fi
+
+cat >>$root/conf/modules.conf <<EOF
+# Generated by: cpp-conf $*
+# Support for C++ SCA components
+LoadModule mod_tuscany_eval $here/libmod_tuscany_eval$libsuffix
+
+EOF
diff --git a/sca-cpp/branches/lightweight-sca/modules/server/domain-test.composite b/sca-cpp/branches/lightweight-sca/modules/server/domain-test.composite
new file mode 100644
index 0000000000..1819b3bf4c
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/server/domain-test.composite
@@ -0,0 +1,58 @@
+<?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"
+ targetNamespace="http://domain/test"
+ name="domain-test">
+
+ <component name="scheme-test">
+ <implementation.scheme script="server-test.scm"/>
+ <service name="scheme">
+ <binding.http uri="scheme"/>
+ </service>
+ </component>
+
+ <component name="property-test">
+ <implementation.scheme script="property-test.scm"/>
+ <service name="properties">
+ <binding.http uri="properties"/>
+ </service>
+ <property name="host"></property>
+ <property name="path"></property>
+ <property name="query"></property>
+ </component>
+
+ <component name="cpp-test">
+ <implementation.cpp path="." library="libimpl-test"/>
+ <service name="cpp">
+ <binding.http uri="cpp"/>
+ </service>
+ </component>
+
+ <component name="client-test">
+ <implementation.scheme script="client-test.scm"/>
+ <service name="client">
+ <binding.http uri="client"/>
+ </service>
+ <reference name="ref" target="scheme-test">
+ <binding.http/>
+ </reference>
+ </component>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/modules/server/htdocs/index.html b/sca-cpp/branches/lightweight-sca/modules/server/htdocs/index.html
new file mode 100644
index 0000000000..236864edfb
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/server/htdocs/index.html
@@ -0,0 +1,32 @@
+<!--
+ 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>
+<head>
+<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0"/>
+<meta name="apple-mobile-web-app-capable" content="yes"/>
+<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/>
+<link rel="stylesheet" type="text/css" href="/ui-min.css"/>
+<title>It works</title>
+</head>
+<body>
+<h1>It works!</h1>
+</body>
+</html>
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/entry.xml b/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/entry.xml
new file mode 100644
index 0000000000..46053c3138
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/entry.xml
@@ -0,0 +1,14 @@
+<?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/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/feed.xml b/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/feed.xml
new file mode 100644
index 0000000000..337320e4c5
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/feed.xml
@@ -0,0 +1,44 @@
+<?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/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/json-properties.txt b/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/json-properties.txt
new file mode 100644
index 0000000000..2cff8b7339
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/json-properties.txt
@@ -0,0 +1,14 @@
+{
+ "id": "1",
+ "result": {
+ "host": "localhost",
+ "path": [
+ "c",
+ "property-test"
+ ],
+ "query": {
+ "id": "1",
+ "method": "print"
+ }
+ }
+} \ No newline at end of file
diff --git a/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/json-request.txt b/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/json-request.txt
new file mode 100644
index 0000000000..b5c2457309
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/json-request.txt
@@ -0,0 +1,7 @@
+{
+ "id": 1,
+ "method": "echo",
+ "params": [
+ "Hello"
+ ]
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/json-result.txt b/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/json-result.txt
new file mode 100644
index 0000000000..72b27b67db
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/server/htdocs/test/json-result.txt
@@ -0,0 +1,4 @@
+{
+ "id": 1,
+ "result": "Hello"
+} \ No newline at end of file
diff --git a/sca-cpp/branches/lightweight-sca/modules/server/httpd-test b/sca-cpp/branches/lightweight-sca/modules/server/httpd-test
new file mode 100755
index 0000000000..195caa4562
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/server/httpd-test
@@ -0,0 +1,87 @@
+#!/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=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+curl_prefix=`cat $here/../http/curl.prefix`
+
+# Setup
+rm -rf tmp
+../http/httpd-conf tmp localhost 8090 htdocs
+../http/httpd-event-conf tmp
+./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/scheme/ >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/scheme/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/scheme/ -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/scheme/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/scheme/111 -X DELETE 2>/dev/null
+ rc=$?
+fi
+
+# Test JSON-RPC
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl http://localhost:8090/scheme/ -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
+
+# Test built-in properties
+if [ "$rc" = "0" ]; then
+ $curl_prefix/bin/curl 'http://localhost:8090/properties?id=1&method=print' >tmp/json-properties.txt 2>/dev/null
+ diff tmp/json-properties.txt htdocs/test/json-properties.txt
+ rc=$?
+fi
+
+# Cleanup
+../http/httpd-stop tmp
+sleep 2
+if [ "$rc" = "0" ]; then
+ echo "OK"
+fi
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/modules/server/impl-test.cpp b/sca-cpp/branches/lightweight-sca/modules/server/impl-test.cpp
new file mode 100644
index 0000000000..1bd0843745
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/server/impl-test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test component implementation.
+ */
+
+#include "string.hpp"
+
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+
+namespace tuscany {
+namespace server {
+
+const failable<value> get(unused const list<value>& params) {
+ return value(mklist<value>("text/html", mklist<value>("Hey")));
+}
+
+const failable<value> post(unused const list<value>& params) {
+ return value(mklist<value>(string("123456789")));
+}
+
+const failable<value> put(unused const list<value>& params) {
+ return value(true);
+}
+
+const failable<value> del(unused const list<value>& params) {
+ return value(true);
+}
+
+const failable<value> echo(const list<value>& params) {
+ return value(car(params));
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "get")
+ return tuscany::server::get(cdr(params));
+ if (func == "post")
+ return tuscany::server::post(cdr(params));
+ if (func == "put")
+ return tuscany::server::put(cdr(params));
+ if (func == "delete")
+ return tuscany::server::del(cdr(params));
+ if (func == "echo")
+ return tuscany::server::echo(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/server/mod-cpp.hpp b/sca-cpp/branches/lightweight-sca/modules/server/mod-cpp.hpp
new file mode 100644
index 0000000000..8cae35e493
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/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/sca-cpp/branches/lightweight-sca/modules/server/mod-eval.cpp b/sca-cpp/branches/lightweight-sca/modules/server/mod-eval.cpp
new file mode 100644
index 0000000000..3fd69c1fea
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/server/mod-eval.cpp
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#define WANT_HTTPD_LOG 1
+#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);
+ if (contains(itype, ".widget"))
+ return mkfailure<lambda<value(const list<value>&)> >(string("Unsupported implementation type: ") + itype, -1, false);
+ return mkfailure<lambda<value(const list<value>&)> >(string("Unsupported implementation type: ") + itype);
+}
+
+}
+}
+}
diff --git a/sca-cpp/branches/lightweight-sca/modules/server/mod-eval.hpp b/sca-cpp/branches/lightweight-sca/modules/server/mod-eval.hpp
new file mode 100644
index 0000000000..ee99baa039
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/server/mod-eval.hpp
@@ -0,0 +1,1600 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more provider 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 <sys/stat.h>
+
+#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 "../scheme/io.hpp"
+#include "../atom/atom.hpp"
+#include "../json/json.hpp"
+#include "../scdl/scdl.hpp"
+#include "../http/http.hpp"
+#include "../http/httpd.hpp"
+
+#include "apr_md5.h"
+#include "ap_provider.h"
+#include "mod_auth.h"
+
+extern "C" {
+extern module AP_MODULE_DECLARE_DATA mod_tuscany_eval;
+}
+
+namespace tuscany {
+namespace server {
+namespace modeval {
+
+/**
+ * SSL certificate configuration.
+ */
+class SSLConf {
+public:
+ SSLConf() {
+ }
+
+ string ca;
+ string cert;
+ string key;
+};
+
+/**
+ * Virtual host configuration.
+ */
+class VhostConf {
+public:
+ VhostConf() {
+ }
+
+ string domain;
+ string contribPath;
+ string composName;
+ string contributorName;
+ value contributor;
+ string authenticatorName;
+ value authenticator;
+};
+
+/**
+ * Contribution configuration.
+ */
+class ContribConf {
+public:
+ ContribConf() {
+ }
+
+ string contribPath;
+ string composName;
+};
+
+/**
+ * Composite assocs.
+ */
+class Composite {
+public:
+ Composite() {
+ }
+
+ Composite(const list<value>& refs, const list<value>& svcs, const list<value>& impls) : refs(refs), svcs(svcs), impls(impls) {
+ }
+
+ list<value> refs;
+ list<value> svcs;
+ list<value> impls;
+};
+
+/**
+ * Server configuration.
+ */
+class ServerConf {
+public:
+ ServerConf() {
+ }
+
+ ServerConf(apr_pool_t* p, const server_rec* s) : p(p), server(s), timeout(0) {
+ }
+
+ const gc_pool p;
+ const server_rec* server;
+ lambda<value(const list<value>&)> lifecycle;
+ ContribConf contribc;
+ SSLConf sslc;
+ int timeout;
+ VhostConf vhostc;
+ Composite compos;
+};
+
+/**
+ * Request configuration.
+ */
+class RequestConf {
+public:
+ RequestConf(apr_pool_t* p, const request_rec* r) : p(p), request(r), vhost(false), valias(false) {
+ }
+
+ const gc_pool p;
+ const request_rec* request;
+ bool vhost;
+ bool valias;
+ list<value> rpath;
+ list<value> vpath;
+ list<value> impls;
+};
+
+/**
+ * Authentication cache store function.
+ */
+static APR_OPTIONAL_FN_TYPE(ap_authn_cache_store) *authnCacheStore = NULL;
+
+/**
+ * Convert a result represented as a (content reason? code?) tuple 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)), isNil(cddr(v))? -1 : (int)caddr(v), false);
+}
+
+/**
+ * Store current HTTP request for access from property and proxy lambda functions.
+ */
+#ifdef WANT_THREADS
+perthread_ptr<request_rec> currentRequest;
+#else
+request_rec* currentRequest = NULL;
+#endif
+
+class ScopedRequest {
+public:
+ ScopedRequest(request_rec* r) {
+ currentRequest = r;
+ }
+
+ ~ScopedRequest() {
+ currentRequest = NULL;
+ }
+};
+
+/**
+ * Make an HTTP proxy lambda to an absolute URI
+ */
+const value mkhttpProxy(const string& uri, const int timeout, const gc_pool& p) {
+ debug(uri, "modeval::mkhttpProxy::uri");
+ return lambda<value(const list<value>&)>(http::proxy(uri, "", "", "", "", timeout, p));
+}
+
+/**
+ * Return a component implementation proxy lambda.
+ */
+class implProxy {
+public:
+ implProxy(const value& name, const list<value>& impls, const SSLConf& sslc, const int timeout) : name(name), impls(impls), sslc(sslc), timeout(timeout) {
+ }
+
+ const value callImpl(const value& cname, const list<value>& aparams) const {
+ //debug(impls, "modeval::implProxy::callImpl::impls");
+
+ // Lookup the component implementation
+ const list<value> impl(assoctree<value>(cname, impls));
+ if (isNil(impl))
+ return mkfailure<value>(string("Couldn't find component implementation: ") + cname);
+
+ // Call its lambda function
+ const lambda<value(const list<value>&)> l(cadr<value>(impl));
+ const value func = c_str(car(aparams));
+ const failable<value> val = failableResult(l(cons(func, cdr(aparams))));
+ debug(val, "modeval::implProxy::result");
+ if (!hasContent(val))
+ return value();
+ return content(val);
+ }
+
+ const value operator()(const list<value>& params) const {
+ debug(name, "modeval::implProxy::name");
+ debug(params, "modeval::implProxy::input");
+
+ // If the reference was 'wiredByImpl' use the first param as target
+ if (isNil(name)) {
+ const value uri = cadr(params);
+ const list<value> aparams = cons<value>(car(params), cddr(params));
+ debug(uri, "modeval::implProxy::wiredByImpl::uri");
+ debug(aparams, "modeval::implProxy::wiredByImpl::input");
+
+ // Use an HTTP proxy if the target is an absolute :// target
+ if (http::isAbsolute(uri)) {
+ gc_pool p(currentRequest->pool);
+
+ // Interpret a uri in the form app://appname, convert it using the scheme,
+ // top level domain and port number from the current request
+ if (http::scheme(uri, p) == "app") {
+ ostringstream appuri;
+ appuri << httpd::scheme(currentRequest) << "://" << substr(uri, 6) << "." << http::topDomain(httpd::hostName(currentRequest)) << ":" << httpd::port(currentRequest) << "/";
+ debug(str(appuri), "modeval::implProxy::httpproxy::appuri");
+ const lambda<value(const list<value>&)> px = lambda<value(const list<value>&)>(http::proxy(str(appuri), sslc.ca, sslc.cert, sslc.key, httpd::cookie(currentRequest), timeout, p));
+ return px(aparams);
+ }
+
+ // Pass our SSL certificate and the cookie from the current request
+ // only if the target is in the same top level domain
+ if (http::topDomain(http::hostName(uri, p)) == http::topDomain(httpd::hostName(currentRequest))) {
+ debug(uri, "modeval::implProxy::httpproxy::samedomain");
+ const lambda<value(const list<value>&)> px = lambda<value(const list<value>&)>(http::proxy(uri, sslc.ca, sslc.cert, sslc.key, httpd::cookie(currentRequest), timeout, p));
+ return px(aparams);
+ }
+
+ // No SSL certificate or cookie on a cross domain call
+ debug(uri, "modeval::implProxy::httpproxy::crossdomain");
+ const lambda<value(const list<value>&)> px = lambda<value(const list<value>&)>(http::proxy(uri, "", "", "", "", timeout, p));
+ return px(aparams);
+ }
+
+ // Call the component implementation
+ return callImpl(uri, aparams);
+ }
+
+ // Call the component implementation
+ return callImpl(name, params);
+ }
+
+private:
+ const value name;
+ const list<value>& impls;
+ const SSLConf& sslc;
+ const int timeout;
+};
+
+const value mkimplProxy(const value& name, const list<value>& impls, const SSLConf& sslc, const int timeout) {
+ debug(name, "modeval::implProxy::impl");
+ return lambda<value(const list<value>&)>(implProxy(name, impls, sslc, timeout));
+}
+
+/**
+ * Return a proxy lambda for an unwired reference.
+ */
+class unwiredProxy {
+public:
+ unwiredProxy(const value& name) : name(name) {
+ }
+
+ const value operator()(const list<value>& params) const {
+ debug(name, "modeval::unwiredProxy::name");
+ debug(params, "modeval::unwiredProxy::input");
+
+ // Get function returns a default empty value
+ if (car(params) == "get") {
+ debug(value(), "modeval::unwiredProxy::result");
+ return value();
+ }
+
+ // All other functions return a failure
+ return mkfailure<value>(string("Reference is not wired: ") + name);
+ }
+
+private:
+ const value name;
+};
+
+/**
+ * Make a proxy lambda for an unwired reference.
+ */
+const value mkunwiredProxy(const string& ref) {
+ debug(ref, "modeval::mkunwiredProxy::ref");
+ return lambda<value(const list<value>&)>(unwiredProxy(ref));
+}
+
+/**
+ * Convert a list of component references to a list of proxy lambdas.
+ */
+const value mkrefProxy(const value& ref, const list<value>& impls, const SSLConf& sslc, const int timeout, const gc_pool& p) {
+ const value target = scdl::target(ref);
+ const bool wbyimpl = scdl::wiredByImpl(ref);
+ debug(ref, "modeval::mkrefProxy::ref");
+ debug(target, "modeval::mkrefProxy::target");
+ debug(wbyimpl, "modeval::mkrefProxy::wiredByImpl");
+
+ // Use an HTTP proxy or an internal proxy to the component implementation
+ if (wbyimpl)
+ return mkimplProxy(value(), impls, sslc, timeout);
+ if (isNil(target))
+ return mkunwiredProxy(scdl::name(ref));
+ if (http::isAbsolute(target))
+ return mkhttpProxy(target, timeout, p);
+ return mkimplProxy(car(pathValues(target)), impls, sslc, timeout);
+}
+
+const list<value> refProxies(const list<value>& refs, const list<value>& impls, const SSLConf& sslc, const int timeout, const gc_pool& p) {
+ if (isNil(refs))
+ return refs;
+ return cons(mkrefProxy(car(refs), impls, sslc, timeout, p), refProxies(cdr(refs), impls, sslc, timeout, p));
+}
+
+/**
+ * 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 {
+ if (currentRequest == NULL)
+ return http::hostName();
+ const value h = httpd::hostName(currentRequest, v);
+ debug(h, "modeval::hostPropProxy::value");
+ return h;
+ }
+};
+
+struct appPropProxy {
+ const value v;
+ appPropProxy(const value& v) : v(v) {
+ }
+ const value operator()(unused const list<value>& params) const {
+ if (currentRequest == NULL)
+ return v;
+ const RequestConf& reqc = httpd::requestConf<RequestConf>(currentRequest, &mod_tuscany_eval);
+ const value a = isNil(reqc.vpath)? v : car(reqc.vpath);
+ debug(a, "modeval::appPropProxy::value");
+ return a;
+ }
+};
+
+struct pathPropProxy {
+ const value v;
+ pathPropProxy(const value& v) : v(v) {
+ }
+ const value operator()(unused const list<value>& params) const {
+ if (currentRequest == NULL)
+ return v;
+ const RequestConf& reqc = httpd::requestConf<RequestConf>(currentRequest, &mod_tuscany_eval);
+ const value p = reqc.rpath;
+ debug(p, "modeval::pathPropProxy::value");
+ return p;
+ }
+};
+
+struct queryPropProxy {
+ const value v;
+ queryPropProxy(const value& v) : v(v) {
+ }
+ const value operator()(unused const list<value>& params) const {
+ if (currentRequest == NULL)
+ return v;
+ const value q = httpd::unescapeArgs(httpd::queryArgs(currentRequest));
+ debug(q, "modeval::queryPropProxy::value");
+ return q;
+ }
+};
+
+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 {
+ if (currentRequest == NULL)
+ return v;
+ const char* env = apr_table_get(currentRequest->subprocess_env, c_str(name));
+ if (env == NULL || *env == '\0')
+ return v;
+ debug(name, "modeval::envPropProxy::name");
+ const value e = string(env);
+ debug(e, "modeval::envPropProxy::value");
+ return e;
+ }
+};
+
+struct realmPropProxy {
+ const value v;
+ realmPropProxy(const value& v) : v(v) {
+ }
+ const value operator()(unused const list<value>& params) const {
+ if (currentRequest == NULL)
+ return v;
+ const char* env = apr_table_get(currentRequest->subprocess_env, "REALM");
+ if (env == NULL)
+ return v;
+ const string realm = httpd::realm(string(env));
+ if (length(realm) == 0)
+ return v;
+ const value r = realm;
+ debug(r, "modeval::realmPropProxy::value");
+ return r;
+ }
+};
+
+struct timeoutPropProxy {
+ const value v;
+ timeoutPropProxy(const value& v) : v(atoi(c_str((string)v))) {
+ }
+ const value operator()(unused const list<value>& params) const {
+ if (currentRequest == NULL)
+ return v;
+ const ServerConf& sc = httpd::serverConf<ServerConf>(currentRequest, &mod_tuscany_eval);
+ const value r = sc.timeout;
+ debug(r, "modeval::timeoutPropProxy::value");
+ return r;
+ }
+};
+
+struct userPropProxy {
+ const value v;
+ userPropProxy(const value& v) : v(v) {
+ }
+ const value operator()(unused const list<value>& params) const {
+ if (currentRequest == NULL)
+ return v;
+ if (currentRequest->user == NULL)
+ return v;
+ const value u = string(currentRequest->user);
+ debug(u, "modeval::userPropProxy::value");
+ return u;
+ }
+};
+
+const value mkpropProxy(const value& prop) {
+ const value n = scdl::name(prop);
+ const value v = elementHasValue(prop)? elementValue(prop):value(string(""));
+ if (n == "app")
+ return lambda<value(const list<value>&)>(appPropProxy(v));
+ if (n == "host")
+ return lambda<value(const list<value>&)>(hostPropProxy(v));
+ if (n == "path")
+ return lambda<value(const list<value>&)>(pathPropProxy(v));
+ if (n == "query")
+ return lambda<value(const list<value>&)>(queryPropProxy(v));
+ if (n == "user")
+ return lambda<value(const list<value>&)>(userPropProxy(v));
+ if (n == "realm")
+ return lambda<value(const list<value>&)>(realmPropProxy(v));
+ if (n == "timeout")
+ return lambda<value(const list<value>&)>(timeoutPropProxy(v));
+ if (n == "email")
+ return lambda<value(const list<value>&)>(envPropProxy("EMAIL", v));
+ if (n == "nickname")
+ return lambda<value(const list<value>&)>(envPropProxy("NICKNAME", v));
+ if (n == "fullname")
+ return lambda<value(const list<value>&)>(envPropProxy("FULLNAME", v));
+ if (n == "firstname")
+ return lambda<value(const list<value>&)>(envPropProxy("FIRSTNAME", v));
+ if (n == "lastname")
+ return lambda<value(const list<value>&)>(envPropProxy("LASTNAME", v));
+ return lambda<value(const list<value>&)>(propProxy(v));
+}
+
+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(const string& contribPath, const value& comp, const list<value>& impls, const lambda<value(const list<value>&)> lifecycle, const SSLConf& sslc, const int timeout, const gc_pool& p) {
+ 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
+ const list<value> rpx(refProxies(scdl::references(comp), impls, sslc, timeout, p));
+
+ // Convert component properties 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(contribPath, impl, append(rpx, ppx), 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(const list<value>& c, const string& contribPath, const list<value>& impls, const lambda<value(const list<value>&)> lifecycle, const SSLConf& sslc, const int timeout, const gc_pool& p) {
+ if (isNil(c))
+ return c;
+ return cons<value>(mklist<value>(scdl::name(car(c)),
+ evalComponent(contribPath, car(c), impls, lifecycle, sslc, timeout, p)),
+ componentToImplementationAssoc(cdr(c), contribPath, impls, lifecycle, sslc, timeout, p));
+}
+
+/**
+ * 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)));
+}
+
+/**
+ * Get the components returned by a contributor.
+ */
+const failable<list<value> > getComponents(const lambda<value(const list<value>&)>& contributor, const string& name) {
+ const failable<value> val = failableResult(contributor(cons<value>("get", mklist<value>(mklist<value>(name)))));
+ if (!hasContent(val))
+ return mkfailure<list<value> >(val);
+ const list<value> c = assoc<value>(value("composite"), assoc<value>(value("content"), (list<list<value> >)cdr<value>(car<value>(content(val)))));
+ debug(c, "modeval::getComponents::comp");
+ if (isNil(c))
+ return mkfailure<list<value> >(string("Could not get composite: ") + name);
+ const failable<list<string> > x = writeXML(car<value>(valuesToElements(mklist<value>(mklist<value>(c)))));
+ if (!hasContent(x))
+ return mkfailure<list<value> >(x);
+ return scdl::components(readXML(content(x)));
+}
+
+/**
+ * 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> >(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));
+}
+
+/**
+ * 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 deployed composite.
+ */
+const failable<Composite> confComponents(const string& contribPath, const string& composName, const value& contributor, const string& vhost, const list<value>& impls, const lambda<value(const list<value>&)> lifecycle, const SSLConf& sslc, const int timeout, const gc_pool& p) {
+ debug(contribPath, "modeval::confComponents::contribPath");
+ debug(composName, "modeval::confComponents::composName");
+ debug(contributor, "modeval::confComponents::contributor");
+ debug(vhost, "modeval::confComponents::vhost");
+ debug(impls, "modeval::confComponents::impls");
+
+ const failable<list<value> > fcomps = isNil(contributor)?
+ readComponents(scdl::resourcePath(length(vhost) != 0? contribPath + vhost + "/" : contribPath, composName)) :
+ getComponents(contributor, vhost);
+ if (!hasContent(fcomps))
+ return mkfailure<Composite>(fcomps);
+
+ const list<value> comps = content(fcomps);
+ debug(comps, "modeval::confComponents::comps");
+
+ const list<value> refs = mkbtree(sort(componentReferenceToTargetAssoc(comps)));
+ debug(flatten(refs), "modeval::confComponents::refs");
+
+ const list<value> svcs = mkbtree(sort(uriToComponentAssoc(comps)));
+ debug(flatten(svcs), "modeval::confComponents::svcs");
+
+ const list<value> cimpls = mkbtree(sort(componentToImplementationAssoc(comps,
+ isNil(contributor)? length(vhost) != 0? contribPath + vhost + "/" : contribPath : contribPath,
+ impls, lifecycle, sslc, timeout, p)));
+ debug(flatten(cimpls), "modeval::confComponents::impls");
+
+ return Composite(refs, svcs, cimpls);
+}
+
+/**
+ * Start the components declared in a composite.
+ */
+const failable<list<value> > startComponents(const list<value>& impls) {
+ debug(flatten(impls), "modeval::startComponents::impls");
+ const failable<list<value> > fsimpls = applyLifecycleExpr(flatten(impls), mklist<value>("start"));
+ if (!hasContent(fsimpls))
+ return mkfailure<list<value> >(fsimpls);
+
+ const list<value> simpls = content(fsimpls);
+ debug(impls, "modeval::startComponents::simpls");
+ return mkbtree(sort(simpls));
+}
+
+/**
+ * Stop the components declared in a composite.
+ */
+const failable<bool> stopComponents(const list<value>& simpls) {
+ debug(flatten(simpls), "modeval::stopComponents::simpls");
+ applyLifecycleExpr(flatten(simpls), mklist<value>("stop"));
+ return true;
+}
+
+/**
+ * Handle an HTTP GET.
+ */
+const failable<int> get(const list<value>& rpath, 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>(val);
+
+ // Return JSON result
+ js::JSContext cx;
+ return httpd::writeResult(json::jsonResult(id, content(val), cx), "application/json-rpc; charset=utf-8", r);
+ }
+
+ // Evaluate the GET expression
+ const list<value> params(cddr(rpath));
+ const failable<value> val = failableResult(impl(cons<value>("get", mklist<value>(params))));
+ if (!hasContent(val))
+ return mkfailure<int>(val);
+ const value c = content(val);
+ debug(c, "modeval::get::content");
+
+ // Return a nil value as a not found status
+ if (!isList(c) && isNil(c))
+ return HTTP_NOT_FOUND;
+
+ // Check if the client requested a specific format
+ const list<value> fmt = assoc<value>("format", args);
+
+ // Write as a scheme value if requested by the client
+ if (!isNil(fmt) && cadr(fmt) == "scheme")
+ return httpd::writeResult(mklist<string>(scheme::writeValue(c)), "text/plain; charset=utf-8", r);
+
+ // Write a simple value as a JSON value
+ if (!isList(c)) {
+ js::JSContext cx;
+ if (isSymbol(c)) {
+ const list<value> lc = mklist<value>(mklist<value>("name", value(string(c))));
+ debug(lc, "modeval::get::symbol");
+ return httpd::writeResult(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8", r);
+ }
+
+ const list<value> lc = mklist<value>(mklist<value>("value", c));
+ debug(lc, "modeval::get::value");
+ return httpd::writeResult(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8", r);
+ }
+
+ // Write an empty list as a JSON empty value
+ if (isNil((list<value>)c)) {
+ js::JSContext cx;
+ debug(list<value>(), "modeval::get::empty");
+ return httpd::writeResult(json::writeJSON(list<value>(), cx), "application/json; charset=utf-8", r);
+ }
+
+ // Write content-type / content-list pair
+ if (isString(car<value>(c)) && !isNil(cdr<value>(c)) && isList(cadr<value>(c)))
+ return httpd::writeResult(convertValues<string>(cadr<value>(c)), car<value>(c), r);
+
+ // Write an assoc value as a JSON result
+ if (isSymbol(car<value>(c)) && !isNil(cdr<value>(c))) {
+ js::JSContext cx;
+ const list<value> lc = mklist<value>(c);
+ debug(lc, "modeval::get::assoc");
+ debug(valuesToElements(lc), "modeval::get::assoc::element");
+ return httpd::writeResult(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8", r);
+ }
+
+ // Write value as JSON if requested by the client
+ if (!isNil(fmt) && cadr(fmt) == "json") {
+ js::JSContext cx;
+ return httpd::writeResult(json::writeJSON(valuesToElements(c), cx), "application/json; charset=utf-8", r);
+ }
+
+ // Convert list of values to element values
+ const list<value> e = valuesToElements(c);
+ debug(e, "modeval::get::elements");
+
+ // Write an ATOM feed or entry
+ if (isList(car<value>(e)) && !isNil(car<value>(e))) {
+ const list<value> el = car<value>(e);
+ if (isSymbol(car<value>(el)) && car<value>(el) == element && !isNil(cdr<value>(el)) && isSymbol(cadr<value>(el)) && elementHasChildren(el) && !elementHasValue(el)) {
+ if (cadr<value>(el) == atom::feed)
+ return httpd::writeResult(atom::writeATOMFeed(e), "application/atom+xml; charset=utf-8", r);
+ if (cadr<value>(el) == atom::entry)
+ return httpd::writeResult(atom::writeATOMEntry(e), "application/atom+xml; charset=utf-8", r);
+ }
+ }
+
+ // Write any other compound value as a JSON value
+ js::JSContext cx;
+ return httpd::writeResult(json::writeJSON(e, cx), "application/json; charset=utf-8", r);
+}
+
+/**
+ * Handle an HTTP POST.
+ */
+const failable<int> post(const list<value>& rpath, request_rec* r, const lambda<value(const list<value>&)>& impl) {
+ debug(r->uri, "modeval::post::uri");
+
+ // 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>(val);
+
+ // Return JSON result
+ return httpd::writeResult(json::jsonResult(id, content(val), cx), "application/json-rpc; charset=utf-8", 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 int rc = httpd::setupReadPolicy(r);
+ if(rc != OK)
+ return rc;
+ const list<string> ls = httpd::read(r);
+ debug(ls, "modeval::post::input");
+ const value aval = elementsToValues(content(atom::isATOMEntry(ls)? atom::readATOMEntry(ls) : atom::readATOMFeed(ls)));
+
+ // Evaluate the POST expression
+ const failable<value> val = failableResult(impl(cons<value>("post", mklist<value>(cddr(rpath), aval))));
+ if (!hasContent(val))
+ return mkfailure<int>(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>(val);
+ return (int)content(val);
+}
+
+/**
+ * Handle an HTTP PUT.
+ */
+const failable<int> put(const list<value>& rpath, request_rec* r, const lambda<value(const list<value>&)>& impl) {
+ debug(r->uri, "modeval::put::uri");
+
+ // Read the ATOM entry
+ 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 aval = elementsToValues(content(atom::isATOMEntry(ls)? atom::readATOMEntry(ls) : atom::readATOMFeed(ls)));
+
+ // Evaluate the PUT expression and update the corresponding resource
+ const failable<value> val = failableResult(impl(cons<value>("put", mklist<value>(cddr(rpath), aval))));
+ if (!hasContent(val))
+ return mkfailure<int>(val);
+ if (val == value(false))
+ return HTTP_NOT_FOUND;
+ return OK;
+}
+
+/**
+ * Handle an HTTP DELETE.
+ */
+const failable<int> del(const list<value>& rpath, request_rec* r, const lambda<value(const list<value>&)>& impl) {
+ debug(r->uri, "modeval::delete::uri");
+
+ // Evaluate an ATOM delete request
+ const failable<value> val = failableResult(impl(cons<value>("delete", mklist<value>(cddr(rpath)))));
+ if (!hasContent(val))
+ return mkfailure<int>(val);
+ if (val == value(false))
+ return HTTP_NOT_FOUND;
+ return OK;
+}
+
+/**
+ * Proceed to handle a service component request.
+ */
+int proceedToHandler(request_rec* r, const int rc) {
+ r->handler = "mod_tuscany_eval";
+ return rc;
+}
+
+int proceedToHandler(request_rec* r, const int rc, const bool valias, const list<value>& rpath, const list<value>& vpath, const list<value>& impls) {
+ r->handler = "mod_tuscany_eval";
+ r->filename = apr_pstrdup(r->pool, c_str(string("/redirect:") + r->uri));
+
+ // Store the selected vhost, path and composite in the request
+ RequestConf& reqc = httpd::requestConf<RequestConf>(r, &mod_tuscany_eval);
+ reqc.valias = valias;
+ reqc.rpath = rpath;
+ reqc.vpath = vpath;
+ reqc.impls = impls;
+ return rc;
+}
+
+/**
+ * Route a component request to the specified component.
+ */
+int translateComponent(request_rec *r, const list<value>& rpath, const list<value>& vpath, const list<value>& impls) {
+ debug(rpath, "modeval::translateComponent::rpath");
+ debug(flatten(impls), "modeval::translateComponent::impls");
+
+ // Find the requested component
+ if (isNil(cdr(rpath)))
+ return HTTP_NOT_FOUND;
+ const list<value> impl(assoctree(cadr(rpath), impls));
+ if (isNil(impl))
+ return HTTP_NOT_FOUND;
+ debug(impl, "modeval::translateComponent::impl");
+
+ return proceedToHandler(r, OK, false, rpath, vpath, impls);;
+}
+
+/**
+ * Route a /references/component-name/reference-name request,
+ * to the target of the component reference.
+ */
+int translateReference(request_rec *r, const list<value>& rpath, const list<value>& vpath, const list<value>& refs, const list<value>& impls) {
+ debug(rpath, "modeval::translateReference::rpath");
+ debug(flatten(refs), "modeval::translateReference::refs");
+
+ // Find the requested component
+ if (isNil(cdr(rpath)))
+ return HTTP_NOT_FOUND;
+ const list<value> comp(assoctree(cadr(rpath), refs));
+ if (isNil(comp))
+ return HTTP_NOT_FOUND;
+ debug(comp, "modeval::translateReference::comp");
+
+ // Find the requested reference and target configuration
+ const list<value> ref(assoctree<value>(caddr(rpath), cadr(comp)));
+ if (isNil(ref))
+ return HTTP_NOT_FOUND;
+ debug(ref, "modeval::translateReference::ref");
+
+ const string target(cadr(ref));
+ debug(target, "modeval::translateReference::target");
+
+ // Route to an absolute target URI using mod_proxy or an HTTP client redirect
+ const list<value> pathInfo = cdddr(rpath);
+ if (http::isAbsolute(target)) {
+ string turi = target + path(pathInfo) + (r->args != NULL? string("?") + string(r->args) : string(""));
+ const string proxy(string("proxy:") + turi);
+ debug(proxy, "modeval::translateReference::proxy");
+ r->filename = apr_pstrdup(r->pool, c_str(proxy));
+ r->proxyreq = PROXYREQ_REVERSE;
+ r->handler = "proxy-server";
+ apr_table_setn(r->notes, "proxy-nocanon", "1");
+ return OK;
+ }
+
+ // Route to a relative target URI using a local internal redirect
+ // / c / target component name / request path info
+ const value tname = substr(target, 0, find(target, '/'));
+ const list<value> redir = cons<value>(string("c"), cons(tname, pathInfo));
+ debug(redir, "modeval::translateReference::redirect");
+ return proceedToHandler(r, OK, false, redir, vpath, impls);;
+}
+
+/**
+ * Find a leaf matching a request path in a tree of URI paths.
+ */
+const int matchPath(const list<value>& k, const list<value>& p) {
+ if (isNil(p))
+ return true;
+ if (isNil(k))
+ return false;
+ if (car(k) != car(p))
+ return false;
+ return matchPath(cdr(k), cdr(p));
+}
+
+const list<value> assocPath(const value& k, const list<value>& tree) {
+ if (isNil(tree))
+ return tree;
+ if (matchPath(k, car<value>(car(tree))))
+ return car(tree);
+ if (k < car<value>(car(tree)))
+ return assocPath(k, cadr(tree));
+ return assocPath(k, caddr(tree));
+}
+
+/**
+ * Route a service request to the component providing the requested service.
+ */
+int translateService(request_rec *r, const list<value>& rpath, const list<value>& vpath, const list<value>& svcs, const list<value>& impls) {
+ debug(rpath, "modeval::translateService::rpath");
+ debug(flatten(svcs), "modeval::translateService::svcs");
+
+ // Find the requested component
+ if (isNil(rpath))
+ return HTTP_NOT_FOUND;
+ const list<value> svc(assocPath(rpath, svcs));
+ if (isNil(svc))
+ return DECLINED;
+ debug(svc, "modeval::translateService::svc");
+
+ // Dispatch to the target component using a local internal redirect
+ // / c / target component name / request path info
+ const list<value> redir = cons<value>(string("c"), cons<value>(cadr(svc), httpd::pathInfo(rpath, car(svc))));
+ debug(redir, "modeval::translateService::redirect");
+ return proceedToHandler(r, OK, false, redir, vpath, impls);
+}
+
+/**
+ * Translate a request to the target app and component.
+ */
+const int translateRequest(request_rec* r, const list<value>& rpath, const list<value>& vpath, const list<value>& refs, const list<value>& svcs, const list<value>& impls) {
+ debug(vpath, "modeval::translateRequest::vpath");
+ debug(rpath, "modeval::translateRequest::rpath");
+ const string prefix = isNil(rpath)? "" : car(rpath);
+
+ // Translate a component request
+ if ((prefix == string("components") || prefix == string("c")) && translateComponent(r, rpath, vpath, impls) == OK)
+ return proceedToHandler(r, OK);
+
+ // Translate a component reference request
+ if ((prefix == string("references") || prefix == string("r")) && translateReference(r, rpath, vpath, refs, impls) == OK)
+ return proceedToHandler(r, OK);
+
+ // Attempt to translate the request to a service request
+ if (translateService(r, rpath, vpath, svcs, impls) == OK)
+ return proceedToHandler(r, OK);
+
+ // Attempt to map a request targeting the main host to an actual file
+ if (isNil(vpath)) {
+ const failable<request_rec*> fnr = httpd::internalSubRequest(r->uri, r);
+ if (!hasContent(fnr))
+ return rcode(fnr);
+ request_rec* nr = content(fnr);
+ nr->uri = r->filename;
+ const int tr = ap_core_translate(nr);
+ if (tr != OK)
+ return tr;
+ if (ap_directory_walk(nr) == OK && ap_file_walk(nr) == OK && nr->finfo.filetype != APR_NOFILE) {
+
+ // Found the target file, let the default handler serve it
+ debug(nr->filename, "modeval::translateRequest::file");
+ return DECLINED;
+ }
+ } else {
+
+ // Make sure a document root request ends with a '/' using
+ // an external redirect
+ if (isNil(rpath) && r->uri[strlen(r->uri) - 1] != '/') {
+ const string target = string(r->uri) + string("/") + (r->args != NULL? string("?") + string(r->args) : string(""));
+ debug(target, "modeval::translateRequest::location");
+ return proceedToHandler(r, httpd::externalRedirect(target, r));
+ }
+
+ // If the request didn't match a service, reference or component,
+ // redirect it to / v / app / path. This will allow mapping to
+ // the actual app resource using HTTPD aliases.
+ debug(true, "modeval::translateRequest::valias");
+ return proceedToHandler(r, OK, true, rpath, vpath, impls);
+ }
+
+ return HTTP_NOT_FOUND;
+}
+
+/**
+ * Translate a 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;
+
+ gc_scoped_pool pool(r->pool);
+
+ debug_httpdRequest(r, "modeval::translate::input");
+
+ // Get the server configuration
+ const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_eval);
+
+ // Parse the request path
+ const list<value> rpath = pathValues(r->uri);
+
+ // Let default handler handle a resource request
+ const string prefix = isNil(rpath)? "" : car(rpath);
+ if (prefix == string("vhosts") || prefix == string("v"))
+ return DECLINED;
+
+ // Get the request configuration
+ RequestConf& reqc = httpd::requestConf<RequestConf>(r, &mod_tuscany_eval);
+
+ // If the request is targeting a virtual host, configure the components
+ // in that virtual host
+ if (length(sc.vhostc.domain) != 0 && (length(sc.vhostc.contribPath) != 0 || !isNil(sc.vhostc.contributor)) && httpd::isVhostRequest(sc.server, sc.vhostc.domain, r)) {
+ const string vname = http::subDomain(httpd::hostName(r));
+ const failable<Composite> fvcompos = confComponents(sc.vhostc.contribPath, sc.vhostc.composName, sc.vhostc.contributor, vname, reqc.impls, sc.lifecycle, sc.sslc, sc.timeout, sc.p);
+ if (!hasContent(fvcompos))
+ return DECLINED;
+ const Composite vcompos = content(fvcompos);
+
+ // Flag the request as virtual host based
+ reqc.vhost = true;
+
+ // Translate the request
+ reqc.impls = vcompos.impls;
+ return translateRequest(r, rpath, mklist<value>(vname), vcompos.refs, vcompos.svcs, reqc.impls);
+ }
+
+ // Translate a request targeting the main host
+ const int rc = translateRequest(r, rpath, list<value>(), sc.compos.refs, sc.compos.svcs, sc.compos.impls);
+ if (rc != HTTP_NOT_FOUND)
+ return rc;
+
+ // Attempt to map the first segment of the request path to a virtual host
+ if (length(prefix) != 0 && (length(sc.vhostc.contribPath) != 0 || !isNil(sc.vhostc.contributor))) {
+ const string vname = prefix;
+ const failable<Composite> fvcompos = confComponents(sc.vhostc.contribPath, sc.vhostc.composName, sc.vhostc.contributor, vname, reqc.impls, sc.lifecycle, sc.sslc, sc.timeout, sc.p);
+ if (!hasContent(fvcompos))
+ return DECLINED;
+ const Composite vcompos = content(fvcompos);
+
+ // Translate the request
+ reqc.impls = vcompos.impls;
+ return translateRequest(r, cdr(rpath), mklist<value>(vname), vcompos.refs, vcompos.svcs, reqc.impls);
+ }
+ return DECLINED;
+}
+
+/**
+ * Handle a component request.
+ */
+const int handleRequest(const list<value>& rpath, request_rec *r, const list<value>& impls) {
+ debug(rpath, "modeval::handleRequest::path");
+
+ // Get the component implementation lambda
+ const list<value> impl(assoctree<value>(cadr(rpath), impls));
+ if (isNil(impl)) {
+ mkfailure<int>(string("Couldn't find component implementation: ") + cadr(rpath));
+ return HTTP_NOT_FOUND;
+ }
+ const lambda<value(const list<value>&)> l(cadr<value>(impl));
+
+ // Handle HTTP method
+ if (r->header_only)
+ return OK;
+ if(r->method_number == M_GET)
+ return httpd::reportStatus(get(rpath, r, l));
+ if(r->method_number == M_POST)
+ return httpd::reportStatus(post(rpath, r, l));
+ if(r->method_number == M_PUT)
+ return httpd::reportStatus(put(rpath, r, l));
+ if(r->method_number == M_DELETE)
+ return httpd::reportStatus(del(rpath, r, l));
+ return HTTP_NOT_IMPLEMENTED;
+}
+
+/**
+ * HTTP request handler.
+ */
+int handler(request_rec *r) {
+ if (r->handler != NULL && r->handler[0] != '\0')
+ return DECLINED;
+
+ // Attempt to translate the request
+ const int trc = translate(r);
+
+ // Pass if we couldn't translate the request
+ if(trc != OK)
+ return trc;
+ if(strcmp(r->handler, "mod_tuscany_eval"))
+ return DECLINED;
+
+ // Create a scope for the current request
+ gc_scoped_pool pool(r->pool);
+ ScopedRequest sr(r);
+
+ debug_httpdRequest(r, "modeval::handler::input");
+
+ // Get the request configuration
+ RequestConf& reqc = httpd::requestConf<RequestConf>(r, &mod_tuscany_eval);
+
+ // Handle an internal redirect as directed by the translate hook
+ if (reqc.valias) {
+ const string redir = path(cons<value>(string("v"), reqc.vhost? reqc.vpath : list<value>())) + string(r->uri) + (r->args != NULL? string("?") + string(r->args) : string(""));
+ debug(redir, "modeval::handler::internalredirect");
+ return httpd::internalRedirect(redir, r);
+ }
+ if (isNil(reqc.rpath))
+ return HTTP_NOT_FOUND;
+
+ // Get the server configuration
+ const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_eval);
+
+ // Handle a request targeting a component in a virtual host
+ if (!isNil(reqc.vpath)) {
+
+ // Start the components in the virtual host
+ const failable<list<value> > fsimpls = startComponents(reqc.impls);
+ if (!hasContent(fsimpls))
+ return HTTP_INTERNAL_SERVER_ERROR;
+ const list<value> simpls = content(fsimpls);
+
+ // Merge the components in the virtual host with the components in the main host
+ reqc.impls = mkbtree(sort(append(flatten(sc.compos.impls), flatten(simpls))));
+
+ // Handle the request against the running components
+ const int rc = handleRequest(reqc.rpath, r, reqc.impls);
+
+ // Stop the components in the virtual host
+ stopComponents(simpls);
+ return rc;
+ }
+
+ // Handle a request targeting a component in the main host
+ return handleRequest(reqc.rpath, r, sc.compos.impls);
+}
+
+/**
+ * Call an authenticator component to check a user's password.
+ */
+authn_status checkPassword(request_rec* r, const char* u, const char* p) {
+ gc_scoped_pool pool(r->pool);
+
+ // Prevent FakeBasicAuth spoofing
+ const string user = u;
+ const string password = p;
+ debug(user, "modeval::checkPassword::user");
+ if (substr(user, 0, 1) != "/" && find(user, "/") != length(user) && password == "password") {
+ mkfailure<int>(string("Encountered FakeBasicAuth spoof: ") + user, HTTP_UNAUTHORIZED);
+ return AUTH_DENIED;
+ }
+
+ // Get the server configuration
+ const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_eval);
+ if (isNil(sc.vhostc.authenticator)) {
+ mkfailure<int>("SCA authenticator not configured");
+ return AUTH_GENERAL_ERROR;
+ }
+
+ // Retrieve the user's password hash
+ const list<value> uid = pathValues(user);
+ const failable<value> val = failableResult(sc.vhostc.authenticator(cons<value>("get", mklist<value>(uid))));
+ if (!hasContent(val)) {
+ mkfailure<int>(string("SCA authentication check user failed, user not found: ") + user);
+ return AUTH_USER_NOT_FOUND;
+ }
+ const value hval = content(val);
+ const list<value> hcontent = isList(hval) && !isNil(hval) && isList(car<value>(hval)) && !isNil(car<value>(hval))? assoc<value>(value("content"), cdr<value>(car<value>(hval))) : list<value>();
+ const list<value> hassoc = isNil(hcontent)? list<value>() : assoc<value>(value("hash"), cdr<value>(hcontent));
+ if (isNil(hassoc)) {
+ mkfailure<int>(string("SCA authentication check user failed, hash not found: ") + user);
+ return AUTH_USER_NOT_FOUND;
+ }
+ const string hash = cadr<value>(hassoc);
+ if (length(hash) == 0) {
+ mkfailure<int>(string("SCA authentication check user failed: ") + user);
+ return AUTH_USER_NOT_FOUND;
+ }
+
+ // Cache the hash in the auth cache provider, if available
+ if (authnCacheStore != NULL)
+ authnCacheStore(r, "component", u, NULL, c_str(hash));
+
+ // Validate the presented password against the hash
+ const apr_status_t rv = apr_password_validate(p, c_str(hash));
+ if (rv != APR_SUCCESS) {
+ mkfailure<int>(string("SCA authentication user password check failed: ") + user);
+ return AUTH_DENIED;
+ }
+ return AUTH_GRANTED;
+}
+
+/**
+ * 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
+ stopComponents(sc.compos.impls);
+
+ // 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");
+ sc.lifecycle = mainsc.lifecycle;
+ sc.contribc = mainsc.contribc;
+ sc.vhostc = mainsc.vhostc;
+ if (sc.sslc.ca == "") sc.sslc.ca = mainsc.sslc.ca;
+ if (sc.sslc.cert == "") sc.sslc.cert = mainsc.sslc.cert;
+ if (sc.sslc.key == "") sc.sslc.key = mainsc.sslc.key;
+ sc.timeout = mainsc.timeout;
+ sc.compos = mainsc.compos;
+ 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 server name
+ ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_eval);
+ debug(httpd::serverName(s), "modeval::postConfig::serverName");
+ debug(sc.contribc.contribPath, "modeval::postConfig::contribPath");
+ debug(sc.contribc.composName, "modeval::postConfig::composName");
+
+ // 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.contribc.contribPath)) != 0) {
+ mkfailure<bool>(string("Couldn't chdir to the deployed contribution: ") + sc.contribc.contribPath);
+ return -1;
+ }
+
+ debug("modeval::postConfig::start");
+ const failable<value> r = failableResult(applyLifecycle(mklist<value>("start")));
+ if (!hasContent(r))
+ return -1;
+ debug("modeval::postConfig::setlifecycle");
+ 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;
+ debug("modeval::postConfig::setlifecycle");
+ sc.lifecycle = content(r);
+ }
+
+ // Configure the deployed components
+ const failable<Composite> compos = confComponents(sc.contribc.contribPath, sc.contribc.composName, value(), "", sc.compos.impls, sc.lifecycle, sc.sslc, sc.timeout, sc.p);
+ if (!hasContent(compos)) {
+ cfailure << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << endl;
+ return -1;
+ }
+ sc.compos = content(compos);
+
+ // 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);
+}
+
+/**
+ * Exit after a failure.
+ */
+void failureExitChild() {
+ cfailure << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << endl;
+ exit(APEXIT_CHILDFATAL);
+}
+
+/**
+ * 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)
+ failureExitChild();
+ ServerConf& sc = *psc;
+
+ // Start the components in the child process
+ const failable<list<value> > fsimpls = startComponents(sc.compos.impls);
+ if (!hasContent(fsimpls))
+ failureExitChild();
+ sc.compos.impls = content(fsimpls);
+
+ // Get the vhost contributor component implementation lambda
+ if (length(sc.vhostc.contributorName) != 0) {
+ const list<value> impl(assoctree<value>(sc.vhostc.contributorName, sc.compos.impls));
+ if (isNil(impl)) {
+ mkfailure<int>(string("Couldn't find contributor component implementation: ") + sc.vhostc.contributorName);
+ failureExitChild();
+ }
+ sc.vhostc.contributor = cadr<value>(impl);
+ }
+
+ // Get the vhost authenticator component implementation lambda
+ if (length(sc.vhostc.authenticatorName) != 0) {
+ const list<value> impl(assoctree<value>(sc.vhostc.authenticatorName, sc.compos.impls));
+ if (isNil(impl)) {
+ mkfailure<int>(string("Couldn't find authenticator component implementation: ") + sc.vhostc.authenticatorName);
+ failureExitChild();
+ }
+ sc.vhostc.authenticator = cadr<value>(impl);
+ }
+
+ // 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* 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.contribc.contribPath = 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.contribc.composName = arg;
+ return NULL;
+}
+const char* confVirtualDomain(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.vhostc.domain = 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.vhostc.contribPath = arg;
+ return NULL;
+}
+const char* confVirtualContributor(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.vhostc.contributorName = 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.vhostc.composName = arg;
+ return NULL;
+}
+const char* confAuthenticator(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.vhostc.authenticatorName = 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.sslc.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.sslc.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.sslc.key = arg;
+ return NULL;
+}
+const char* confTimeout(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.timeout = atoi(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("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("SCAVirtualDomain", (const char*(*)())confVirtualDomain, NULL, RSRC_CONF, "SCA virtual host domain"),
+ AP_INIT_TAKE1("SCAVirtualContribution", (const char*(*)())confVirtualContribution, NULL, RSRC_CONF, "SCA virtual host contribution path"),
+ AP_INIT_TAKE1("SCAVirtualContributor", (const char*(*)())confVirtualContributor, NULL, RSRC_CONF, "SCA virtual host contributor component"),
+ AP_INIT_TAKE1("SCAVirtualComposite", (const char*(*)())confVirtualComposite, NULL, RSRC_CONF, "SCA virtual composite location"),
+ AP_INIT_TAKE1("SCAAuthenticator", (const char*(*)())confAuthenticator, NULL, RSRC_CONF, "SCA authenticator component"),
+ 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"),
+ AP_INIT_TAKE1("SCAWiringTimeout", (const char*(*)())confTimeout, NULL, RSRC_CONF, "SCA wiring timeout"),
+ {NULL, NULL, NULL, 0, NO_ARGS, NULL}
+};
+
+
+const authn_provider AuthnProvider = {
+ &checkPassword,
+ NULL
+};
+
+void retrieveAuthnCacheStore() {
+ authnCacheStore = APR_RETRIEVE_OPTIONAL_FN(ap_authn_cache_store);
+}
+
+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_register_auth_provider(p, AUTHN_PROVIDER_GROUP, "component", AUTHN_PROVIDER_VERSION, &AuthnProvider, AP_AUTH_INTERNAL_PER_CONF);
+ ap_hook_optional_fn_retrieve(retrieveAuthnCacheStore, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+}
+}
+}
+
+extern "C" {
+
+module AP_MODULE_DECLARE_DATA mod_tuscany_eval = {
+ STANDARD20_MODULE_STUFF,
+ // dir config and merger
+ NULL, NULL,
+ // server config and merger
+ tuscany::httpd::makeServerConf<tuscany::server::modeval::ServerConf>, NULL,
+ // commands and hooks
+ tuscany::server::modeval::commands, tuscany::server::modeval::registerHooks
+};
+
+}
+
+#endif
diff --git a/sca-cpp/branches/lightweight-sca/modules/server/mod-scheme.hpp b/sca-cpp/branches/lightweight-sca/modules/server/mod-scheme.hpp
new file mode 100644
index 0000000000..43d7bf4041
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/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/sca-cpp/branches/lightweight-sca/modules/server/property-test.scm b/sca-cpp/branches/lightweight-sca/modules/server/property-test.scm
new file mode 100644
index 0000000000..f5ba76f1f3
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/server/property-test.scm
@@ -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.
+
+; Built-in property test case
+
+(define (print host path query) (list (list 'host (host)) (list 'path (path)) (list 'query (query))))
+
diff --git a/sca-cpp/branches/lightweight-sca/modules/server/scheme-conf b/sca-cpp/branches/lightweight-sca/modules/server/scheme-conf
new file mode 100755
index 0000000000..bc4074c8be
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/server/scheme-conf
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Generate a Scheme server conf
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+uname=`uname -s`
+if [ $uname = "Darwin" ]; then
+ libsuffix=".dylib"
+else
+ libsuffix=".so"
+fi
+
+cat >>$root/conf/modules.conf <<EOF
+# Generated by: scheme-conf $*
+# Support for Scheme SCA components
+LoadModule mod_tuscany_eval $here/libmod_tuscany_eval$libsuffix
+
+EOF
diff --git a/sca-cpp/branches/lightweight-sca/modules/server/server-conf b/sca-cpp/branches/lightweight-sca/modules/server/server-conf
new file mode 100755
index 0000000000..bfa5ac8473
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/server/server-conf
@@ -0,0 +1,40 @@
+#!/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=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+mkdir -p $1
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+# Serve Javascript scripts and CSS
+$here/../js/js-conf $1
+
+# Configure SSL cert used for wiring
+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/sca-cpp/branches/lightweight-sca/modules/server/server-test b/sca-cpp/branches/lightweight-sca/modules/server/server-test
new file mode 100755
index 0000000000..6b48d13e83
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/server/server-test
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Setup
+rm -rf tmp
+../http/httpd-conf tmp localhost 8090 htdocs
+../http/httpd-event-conf tmp
+./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
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/modules/server/server-test.scm b/sca-cpp/branches/lightweight-sca/modules/server/server-test.scm
new file mode 100644
index 0000000000..4bbff6e5c2
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/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)
+ '((feed (title "Sample Feed") (id "123456789") (entry
+ (((title "Item") (id "111") (content (item (name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99))))
+ ((title "Item") (id "222") (content (item (name "Orange") (currencyCode "USD") (currencySymbol "$") (price 3.55))))
+ ((title "Item") (id "333") (content (item (name "Pear") (currencyCode "USD") (currencySymbol "$") (price 1.55))))))))
+
+ (list (list 'entry '(title "Item") (list 'id (car id)) '(content (item (name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99))))))
+)
+
+(define (post collection item)
+ '("123456789")
+)
+
+(define (put id item)
+ true
+)
+
+(define (delete id)
+ true
+)
diff --git a/sca-cpp/branches/lightweight-sca/modules/server/wiring-test b/sca-cpp/branches/lightweight-sca/modules/server/wiring-test
new file mode 100755
index 0000000000..7e1aea22b1
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/modules/server/wiring-test
@@ -0,0 +1,80 @@
+#!/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=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+curl_prefix=`cat $here/../http/curl.prefix`
+
+# Setup
+rm -rf tmp
+../http/httpd-conf tmp localhost 8090 htdocs
+../http/httpd-event-conf tmp
+./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
+exit $rc