summaryrefslogtreecommitdiffstats
path: root/sca-cpp/branches/lightweight-sca/components
diff options
context:
space:
mode:
Diffstat (limited to 'sca-cpp/branches/lightweight-sca/components')
-rw-r--r--sca-cpp/branches/lightweight-sca/components/Makefile.am19
-rw-r--r--sca-cpp/branches/lightweight-sca/components/cache/Makefile.am58
-rw-r--r--sca-cpp/branches/lightweight-sca/components/cache/adder-test.scm20
-rw-r--r--sca-cpp/branches/lightweight-sca/components/cache/cache.composite100
-rw-r--r--sca-cpp/branches/lightweight-sca/components/cache/client-test.cpp182
-rw-r--r--sca-cpp/branches/lightweight-sca/components/cache/datacache.componentType30
-rw-r--r--sca-cpp/branches/lightweight-sca/components/cache/datacache.cpp129
-rw-r--r--sca-cpp/branches/lightweight-sca/components/cache/memcache-test.cpp84
-rw-r--r--sca-cpp/branches/lightweight-sca/components/cache/memcache.componentType28
-rw-r--r--sca-cpp/branches/lightweight-sca/components/cache/memcache.cpp133
-rw-r--r--sca-cpp/branches/lightweight-sca/components/cache/memcache.hpp216
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/cache/memcached-log-conf37
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/cache/memcached-ssl-test58
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/cache/memcached-start51
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/cache/memcached-stop44
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/cache/memcached-test35
-rw-r--r--sca-cpp/branches/lightweight-sca/components/cache/memocache.componentType28
-rw-r--r--sca-cpp/branches/lightweight-sca/components/cache/memocache.cpp78
-rw-r--r--sca-cpp/branches/lightweight-sca/components/cache/partition1-test.scm21
-rw-r--r--sca-cpp/branches/lightweight-sca/components/cache/partition2-test.scm21
-rw-r--r--sca-cpp/branches/lightweight-sca/components/cache/partitioner.componentType28
-rw-r--r--sca-cpp/branches/lightweight-sca/components/cache/partitioner.cpp143
-rw-r--r--sca-cpp/branches/lightweight-sca/components/cache/select-test.scm21
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/cache/server-test53
-rw-r--r--sca-cpp/branches/lightweight-sca/components/chat/Makefile.am78
-rw-r--r--sca-cpp/branches/lightweight-sca/components/chat/chat-send.cpp55
-rw-r--r--sca-cpp/branches/lightweight-sca/components/chat/chat-sender.componentType29
-rw-r--r--sca-cpp/branches/lightweight-sca/components/chat/chat-sender.cpp151
-rw-r--r--sca-cpp/branches/lightweight-sca/components/chat/chat-sender2.componentType31
-rw-r--r--sca-cpp/branches/lightweight-sca/components/chat/chat-sender2.cpp116
-rw-r--r--sca-cpp/branches/lightweight-sca/components/chat/chat-sendreceiver.componentType30
-rw-r--r--sca-cpp/branches/lightweight-sca/components/chat/chat-sendreceiver.cpp165
-rw-r--r--sca-cpp/branches/lightweight-sca/components/chat/chat.composite51
-rw-r--r--sca-cpp/branches/lightweight-sca/components/chat/client-test.cpp114
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/chat/echo-test31
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/chat/server-test45
-rw-r--r--sca-cpp/branches/lightweight-sca/components/chat/server-test.scm20
-rw-r--r--sca-cpp/branches/lightweight-sca/components/chat/test/TestVysperServer.java79
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/chat/vysper-classpath29
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/chat/vysper-start25
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/chat/vysper-stop28
-rw-r--r--sca-cpp/branches/lightweight-sca/components/chat/xmpp-test.cpp103
-rw-r--r--sca-cpp/branches/lightweight-sca/components/chat/xmpp.hpp339
-rw-r--r--sca-cpp/branches/lightweight-sca/components/constdb/Makefile.am49
-rw-r--r--sca-cpp/branches/lightweight-sca/components/constdb/client-test.cpp139
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/constdb/constdb-test30
-rw-r--r--sca-cpp/branches/lightweight-sca/components/constdb/constdb.componentType28
-rw-r--r--sca-cpp/branches/lightweight-sca/components/constdb/constdb.composite32
-rw-r--r--sca-cpp/branches/lightweight-sca/components/constdb/constdb.cpp124
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/constdb/server-test42
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/constdb/tinycdb24
-rw-r--r--sca-cpp/branches/lightweight-sca/components/constdb/tinycdb-test.cpp82
-rw-r--r--sca-cpp/branches/lightweight-sca/components/constdb/tinycdb.hpp488
-rw-r--r--sca-cpp/branches/lightweight-sca/components/filedb/Makefile.am42
-rw-r--r--sca-cpp/branches/lightweight-sca/components/filedb/client-test.cpp139
-rw-r--r--sca-cpp/branches/lightweight-sca/components/filedb/file-test.cpp94
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/filedb/filedb-test32
-rw-r--r--sca-cpp/branches/lightweight-sca/components/filedb/filedb.componentType28
-rw-r--r--sca-cpp/branches/lightweight-sca/components/filedb/filedb.composite33
-rw-r--r--sca-cpp/branches/lightweight-sca/components/filedb/filedb.cpp126
-rw-r--r--sca-cpp/branches/lightweight-sca/components/filedb/filedb.hpp265
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/filedb/server-test42
-rw-r--r--sca-cpp/branches/lightweight-sca/components/http/Makefile.am56
-rw-r--r--sca-cpp/branches/lightweight-sca/components/http/client-test.cpp111
-rw-r--r--sca-cpp/branches/lightweight-sca/components/http/content-test.scm23
-rw-r--r--sca-cpp/branches/lightweight-sca/components/http/http.composite93
-rw-r--r--sca-cpp/branches/lightweight-sca/components/http/httpdelete.componentType29
-rw-r--r--sca-cpp/branches/lightweight-sca/components/http/httpdelete.cpp106
-rw-r--r--sca-cpp/branches/lightweight-sca/components/http/httpget.componentType29
-rw-r--r--sca-cpp/branches/lightweight-sca/components/http/httpget.cpp108
-rw-r--r--sca-cpp/branches/lightweight-sca/components/http/httppatch.componentType30
-rw-r--r--sca-cpp/branches/lightweight-sca/components/http/httppatch.cpp110
-rw-r--r--sca-cpp/branches/lightweight-sca/components/http/httppost.componentType30
-rw-r--r--sca-cpp/branches/lightweight-sca/components/http/httppost.cpp110
-rw-r--r--sca-cpp/branches/lightweight-sca/components/http/httpput.componentType30
-rw-r--r--sca-cpp/branches/lightweight-sca/components/http/httpput.cpp110
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/http/server-test40
-rw-r--r--sca-cpp/branches/lightweight-sca/components/http/server-test.scm44
-rw-r--r--sca-cpp/branches/lightweight-sca/components/http/url-test.scm23
-rw-r--r--sca-cpp/branches/lightweight-sca/components/kvdb/Makefile.am49
-rw-r--r--sca-cpp/branches/lightweight-sca/components/kvdb/client-test.cpp139
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/kvdb/kvdb-test30
-rw-r--r--sca-cpp/branches/lightweight-sca/components/kvdb/kvdb.componentType28
-rw-r--r--sca-cpp/branches/lightweight-sca/components/kvdb/kvdb.composite32
-rw-r--r--sca-cpp/branches/lightweight-sca/components/kvdb/kvdb.cpp124
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/kvdb/leveldb24
-rw-r--r--sca-cpp/branches/lightweight-sca/components/kvdb/leveldb-test.cpp82
-rw-r--r--sca-cpp/branches/lightweight-sca/components/kvdb/leveldb.hpp271
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/kvdb/server-test41
-rw-r--r--sca-cpp/branches/lightweight-sca/components/log/Makefile.am79
-rw-r--r--sca-cpp/branches/lightweight-sca/components/log/adder-test.scm20
-rw-r--r--sca-cpp/branches/lightweight-sca/components/log/client-test.cpp113
-rw-r--r--sca-cpp/branches/lightweight-sca/components/log/client-test.scm20
-rw-r--r--sca-cpp/branches/lightweight-sca/components/log/fb303.thrift112
-rw-r--r--sca-cpp/branches/lightweight-sca/components/log/log.componentType28
-rw-r--r--sca-cpp/branches/lightweight-sca/components/log/log.composite58
-rw-r--r--sca-cpp/branches/lightweight-sca/components/log/log.cpp99
-rw-r--r--sca-cpp/branches/lightweight-sca/components/log/logger.componentType29
-rw-r--r--sca-cpp/branches/lightweight-sca/components/log/logger.cpp97
-rw-r--r--sca-cpp/branches/lightweight-sca/components/log/scribe-cat.cpp79
-rw-r--r--sca-cpp/branches/lightweight-sca/components/log/scribe-status.cpp66
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/log/scribe-tail-start45
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/log/scribe-tail-stop45
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/log/scribe-test43
-rw-r--r--sca-cpp/branches/lightweight-sca/components/log/scribe.hpp198
-rw-r--r--sca-cpp/branches/lightweight-sca/components/log/scribe.thrift39
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/log/scribed-central-conf79
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/log/scribed-central-firehose-conf126
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/log/scribed-central-mkfirehose34
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/log/scribed-central-start27
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/log/scribed-central-stop32
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/log/scribed-client-conf70
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/log/scribed-client-start27
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/log/scribed-client-stop32
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/log/server-test67
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/log/thrift-pragmas32
-rw-r--r--sca-cpp/branches/lightweight-sca/components/queue/Makefile.am57
-rw-r--r--sca-cpp/branches/lightweight-sca/components/queue/client-test.cpp102
-rw-r--r--sca-cpp/branches/lightweight-sca/components/queue/qpid-test.cpp97
-rw-r--r--sca-cpp/branches/lightweight-sca/components/queue/qpid.hpp277
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/queue/qpidd-start24
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/queue/qpidd-stop30
-rw-r--r--sca-cpp/branches/lightweight-sca/components/queue/queue-listener.componentType29
-rw-r--r--sca-cpp/branches/lightweight-sca/components/queue/queue-listener.cpp159
-rw-r--r--sca-cpp/branches/lightweight-sca/components/queue/queue-sender.componentType28
-rw-r--r--sca-cpp/branches/lightweight-sca/components/queue/queue-sender.cpp73
-rw-r--r--sca-cpp/branches/lightweight-sca/components/queue/queue.composite55
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/queue/send-test31
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/queue/server-test45
-rw-r--r--sca-cpp/branches/lightweight-sca/components/queue/server-test.scm20
-rw-r--r--sca-cpp/branches/lightweight-sca/components/smtp/Makefile.am36
-rw-r--r--sca-cpp/branches/lightweight-sca/components/smtp/client-test.cpp60
-rw-r--r--sca-cpp/branches/lightweight-sca/components/smtp/content-test.scm27
-rw-r--r--sca-cpp/branches/lightweight-sca/components/smtp/from-test.scm23
-rw-r--r--sca-cpp/branches/lightweight-sca/components/smtp/password-test.scm23
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/smtp/server-test41
-rw-r--r--sca-cpp/branches/lightweight-sca/components/smtp/smtp.composite87
-rw-r--r--sca-cpp/branches/lightweight-sca/components/smtp/smtppost.componentType34
-rw-r--r--sca-cpp/branches/lightweight-sca/components/smtp/smtppost.cpp182
-rw-r--r--sca-cpp/branches/lightweight-sca/components/smtp/subject-test.scm23
-rw-r--r--sca-cpp/branches/lightweight-sca/components/smtp/to-test.scm23
-rw-r--r--sca-cpp/branches/lightweight-sca/components/smtp/url-test.scm23
-rw-r--r--sca-cpp/branches/lightweight-sca/components/smtp/user-test.scm23
-rw-r--r--sca-cpp/branches/lightweight-sca/components/sqldb/Makefile.am57
-rw-r--r--sca-cpp/branches/lightweight-sca/components/sqldb/client-test.cpp139
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/sqldb/pgsql39
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/sqldb/pgsql-archive59
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/sqldb/pgsql-backup79
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/sqldb/pgsql-clean-archive58
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/sqldb/pgsql-conf194
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/sqldb/pgsql-log-conf37
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/sqldb/pgsql-restore30
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/sqldb/pgsql-standby-conf193
-rw-r--r--sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-standby-test.cpp88
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/sqldb/pgsql-start50
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/sqldb/pgsql-stop43
-rw-r--r--sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-test.cpp82
-rw-r--r--sca-cpp/branches/lightweight-sca/components/sqldb/pgsql.hpp249
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/sqldb/server-test46
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/sqldb/sqldb-test33
-rw-r--r--sca-cpp/branches/lightweight-sca/components/sqldb/sqldb.componentType29
-rw-r--r--sca-cpp/branches/lightweight-sca/components/sqldb/sqldb.composite33
-rw-r--r--sca-cpp/branches/lightweight-sca/components/sqldb/sqldb.cpp143
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/sqldb/standby-test41
-rw-r--r--sca-cpp/branches/lightweight-sca/components/webservice/Makefile.am71
-rw-r--r--sca-cpp/branches/lightweight-sca/components/webservice/axiom-test.cpp89
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/webservice/axis2-conf64
-rw-r--r--sca-cpp/branches/lightweight-sca/components/webservice/axis2-dispatcher.cpp139
-rw-r--r--sca-cpp/branches/lightweight-sca/components/webservice/axis2-service.cpp152
-rw-r--r--sca-cpp/branches/lightweight-sca/components/webservice/axis2-test.cpp71
-rw-r--r--sca-cpp/branches/lightweight-sca/components/webservice/axis2.hpp207
-rw-r--r--sca-cpp/branches/lightweight-sca/components/webservice/axis2.xml148
-rw-r--r--sca-cpp/branches/lightweight-sca/components/webservice/client-test.cpp94
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/webservice/echo-test37
-rw-r--r--sca-cpp/branches/lightweight-sca/components/webservice/module.xml25
-rwxr-xr-xsca-cpp/branches/lightweight-sca/components/webservice/server-test51
-rw-r--r--sca-cpp/branches/lightweight-sca/components/webservice/server-test.scm21
-rw-r--r--sca-cpp/branches/lightweight-sca/components/webservice/services.xml25
-rw-r--r--sca-cpp/branches/lightweight-sca/components/webservice/webservice-client.componentType28
-rw-r--r--sca-cpp/branches/lightweight-sca/components/webservice/webservice-client.cpp66
-rw-r--r--sca-cpp/branches/lightweight-sca/components/webservice/webservice-listener.componentType28
-rw-r--r--sca-cpp/branches/lightweight-sca/components/webservice/webservice-listener.cpp85
-rw-r--r--sca-cpp/branches/lightweight-sca/components/webservice/webservice.composite47
183 files changed, 13375 insertions, 0 deletions
diff --git a/sca-cpp/branches/lightweight-sca/components/Makefile.am b/sca-cpp/branches/lightweight-sca/components/Makefile.am
new file mode 100644
index 0000000000..6d3807058c
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/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 = cache chat http smtp log constdb filedb queue sqldb webservice
+
diff --git a/sca-cpp/branches/lightweight-sca/components/cache/Makefile.am b/sca-cpp/branches/lightweight-sca/components/cache/Makefile.am
new file mode 100644
index 0000000000..0240a32bb4
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/cache/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.
+
+incl_HEADERS = *.hpp
+incldir = $(prefix)/include/components/cache
+
+dist_comp_SCRIPTS = memcached-log-conf memcached-start memcached-stop
+compdir=$(prefix)/components/cache
+
+comp_DATA = memcached.prefix
+memcached.prefix: $(top_builddir)/config.status
+ echo ${MEMCACHED_PREFIX} >memcached.prefix
+
+EXTRA_DIST = cache.composite memcache.componentType datacache.componentType memocache.componentType partitioner.componentType *.scm
+
+comp_LTLIBRARIES = libmemcache.la libdatacache.la libmemocache.la libpartitioner.la
+noinst_DATA = libmemcache${libsuffix} libdatacache${libsuffix} libmemocache${libsuffix} libpartitioner${libsuffix}
+
+libmemcache_la_SOURCES = memcache.cpp
+libmemcache${libsuffix}:
+ ln -s .libs/libmemcache${libsuffix}
+
+libdatacache_la_SOURCES = datacache.cpp
+libdatacache${libsuffix}:
+ ln -s .libs/libdatacache${libsuffix}
+
+libmemocache_la_SOURCES = memocache.cpp
+libmemocache${libsuffix}:
+ ln -s .libs/libmemocache${libsuffix}
+
+libpartitioner_la_SOURCES = partitioner.cpp
+libpartitioner${libsuffix}:
+ ln -s .libs/libpartitioner${libsuffix}
+
+memcache_test_SOURCES = memcache-test.cpp
+memcache_test_LDFLAGS = -lxml2
+
+client_test_SOURCES = client-test.cpp
+client_test_LDFLAGS = -lxml2 -lcurl -lmozjs
+
+dist_noinst_SCRIPTS = memcached-test memcached-ssl-test server-test
+noinst_PROGRAMS = memcache-test client-test
+#TESTS = memcached-test memcached-ssl-test server-test
+TESTS = memcached-test server-test
diff --git a/sca-cpp/branches/lightweight-sca/components/cache/adder-test.scm b/sca-cpp/branches/lightweight-sca/components/cache/adder-test.scm
new file mode 100644
index 0000000000..ccd5bc555f
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/cache/adder-test.scm
@@ -0,0 +1,20 @@
+; Licensed to the Apache Software Foundation (ASF) under one
+; or more contributor license agreements. See the NOTICE file
+; distributed with this work for additional information
+; regarding copyright ownership. The ASF licenses this file
+; to you under the Apache License, Version 2.0 (the
+; "License"); you may not use this file except in compliance
+; with the License. You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing,
+; software distributed under the License is distributed on an
+; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+; KIND, either express or implied. See the License for the
+; specific language governing permissions and limitations
+; under the License.
+
+; Logger test case
+
+(define (add a b) (+ a b))
diff --git a/sca-cpp/branches/lightweight-sca/components/cache/cache.composite b/sca-cpp/branches/lightweight-sca/components/cache/cache.composite
new file mode 100644
index 0000000000..a85f909db9
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/cache/cache.composite
@@ -0,0 +1,100 @@
+<?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://tuscany.apache.org/xmlns/sca/components"
+ name="memcache">
+
+ <component name="memcache">
+ <implementation.cpp path="." library="libmemcache"/>
+ <service name="memcache">
+ <binding.http uri="memcache"/>
+ </service>
+ <property name="server">localhost</property>
+ <property name="server">localhost:11212</property>
+ <property name="server">localhost:11213</property>
+ </component>
+
+ <component name="l2cache">
+ <implementation.cpp path="." library="libmemcache"/>
+ <service name="l2cache">
+ <binding.http uri="l2cache"/>
+ </service>
+ <property name="servers">localhost:11411 localhost:11412 localhost:11413</property>
+ </component>
+
+ <component name="datacache">
+ <implementation.cpp path="." library="libdatacache"/>
+ <service name="datacache">
+ <binding.http uri="datacache"/>
+ </service>
+ <reference name="l1reader" target="memcache"/>
+ <reference name="l1writer" target="memcache"/>
+ <reference name="l2reader" target="l2cache"/>
+ <reference name="l2writer" target="l2cache"/>
+ </component>
+
+ <component name="memocache">
+ <implementation.cpp path="." library="libmemocache"/>
+ <service name="memocache">
+ <binding.http uri="memocache"/>
+ </service>
+ <reference name="relay" target="adder"/>
+ <reference name="cache" target="memcache"/>
+ </component>
+
+ <component name="adder">
+ <implementation.scheme script="adder-test.scm"/>
+ <service name="adder">
+ <binding.http uri="adder"/>
+ </service>
+ </component>
+
+ <component name="partitioner">
+ <implementation.cpp path="." library="libpartitioner"/>
+ <service name="partitioner">
+ <binding.http uri="partitioner"/>
+ </service>
+ <reference name="selector" target="selector"/>
+ <reference name="partition" target="partition1"/>
+ <reference name="partition" target="partition2"/>
+ </component>
+
+ <component name="selector">
+ <implementation.scheme script="select-test.scm"/>
+ <service name="selector">
+ <binding.http uri="selector"/>
+ </service>
+ </component>
+
+ <component name="partition1">
+ <implementation.scheme script="partition1-test.scm"/>
+ <service name="partition1">
+ <binding.http uri="partition1"/>
+ </service>
+ </component>
+
+ <component name="partition2">
+ <implementation.scheme script="partition2-test.scm"/>
+ <service name="partition2">
+ <binding.http uri="partition2"/>
+ </service>
+ </component>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/cache/client-test.cpp b/sca-cpp/branches/lightweight-sca/components/cache/client-test.cpp
new file mode 100644
index 0000000000..63d1691f8b
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/cache/client-test.cpp
@@ -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$ */
+
+/**
+ * Test cache component.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "perf.hpp"
+#include "../../modules/http/http.hpp"
+
+namespace tuscany {
+namespace cache {
+
+const string memcacheuri("http://localhost:8090/memcache");
+const string datacacheuri("http://localhost:8090/datacache");
+const string memocacheuri("http://localhost:8090/memocache");
+const string partition1uri("http://localhost:8090/partitioner/a");
+const string partition2uri("http://localhost:8090/partitioner/b");
+
+bool testCache(const string& uri) {
+ http::CURLSession cs("", "", "", "", 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> a = list<value>() + (list<value>() + "entry"
+ + (list<value>() + "title" + string("item"))
+ + (list<value>() + "id" + string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"))
+ + i);
+
+ const failable<value> id = http::post(a, uri, cs);
+ assert(hasContent(id));
+
+ const string p = path(content(id));
+ {
+ const failable<value> val = http::get(uri + p, cs);
+ assert(hasContent(val));
+ assert(content(val) == a);
+ }
+
+ const list<value> j = list<value>() + "content" + (list<value>() + "item"
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "price" + string("$3.55")));
+ const list<value> b = list<value>() + (list<value>() + "entry"
+ + (list<value>() + "title" + string("item"))
+ + (list<value>() + "id" + string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"))
+ + j);
+
+ {
+ const failable<value> r = http::put(b, uri + p, cs);
+ assert(hasContent(r));
+ assert(content(r) == value(true));
+ }
+ {
+ const failable<value> val = http::get(uri + p, cs);
+ assert(hasContent(val));
+ assert(content(val) == b);
+ }
+ {
+ const failable<value> r = http::del(uri + p, cs);
+ assert(hasContent(r));
+ assert(content(r) == value(true));
+ }
+ {
+ const failable<value> val = http::get(uri + p, cs);
+ assert(!hasContent(val));
+ }
+
+ return true;
+}
+
+bool testMemcache() {
+ return testCache(memcacheuri);
+}
+
+bool testDatacache() {
+ return testCache(datacacheuri);
+}
+
+bool testMemocache() {
+ http::CURLSession cs("", "", "", "", 0);
+
+ const failable<value> res = http::evalExpr(mklist<value>(string("add"), 33, 22), memocacheuri, cs);
+ assert(hasContent(res));
+ assert((int)content(res) == 55);
+
+ const failable<value> res2 = http::evalExpr(mklist<value>(string("add"), 33, 22), memocacheuri, cs);
+ assert(hasContent(res2));
+ assert((int)content(res2) == 55);
+
+ return true;
+}
+
+bool testPartitioner() {
+ http::CURLSession cs("", "", "", "", 0);
+
+ const failable<value> res1 = http::get(partition1uri, cs);
+ assert(hasContent(res1));
+ assert(cadr<value>(content(res1)) == string("1"));
+
+ const failable<value> res2 = http::get(partition2uri, cs);
+ assert(hasContent(res2));
+ assert(cadr<value>(content(res2)) == string("2"));
+
+ return true;
+}
+
+struct getLoop {
+ const string path;
+ const value entry;
+ http::CURLSession& cs;
+ getLoop(const string& path, const value& entry, http::CURLSession& cs) : path(path), entry(entry), cs(cs) {
+ }
+ const bool operator()() const {
+ const failable<value> val = http::get(memcacheuri + path, cs);
+ assert(hasContent(val));
+ assert(content(val) == entry);
+ return true;
+ }
+};
+
+bool testGetPerf() {
+ const list<value> i = list<value>() + "content" + (list<value>() + "item"
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "price" + string("$4.55")));
+ 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 cs("", "", "", "", 0);
+ const failable<value> id = http::post(a, memcacheuri, cs);
+ assert(hasContent(id));
+ const string p = path(content(id));
+
+ const lambda<bool()> gl = getLoop(p, a, cs);
+ cout << "Cache get test " << time(gl, 5, 200) << " ms" << endl;
+
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::cache::testMemcache();
+ tuscany::cache::testDatacache();
+ tuscany::cache::testMemocache();
+ tuscany::cache::testPartitioner();
+ tuscany::cache::testGetPerf();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/cache/datacache.componentType b/sca-cpp/branches/lightweight-sca/components/cache/datacache.componentType
new file mode 100644
index 0000000000..ca6284c661
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/cache/datacache.componentType
@@ -0,0 +1,30 @@
+<?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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <service name="datacache"/>
+ <reference name="l1reader"/>
+ <reference name="l1writer"/>
+ <reference name="l2reader"/>
+ <reference name="l2writer"/>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/cache/datacache.cpp b/sca-cpp/branches/lightweight-sca/components/cache/datacache.cpp
new file mode 100644
index 0000000000..c259ec33c6
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/cache/datacache.cpp
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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$ */
+
+/**
+ * A data cache component implementation which coordinates access to two
+ * levels of backend read/write caches or stores. Each cache or store is
+ * accessed through two references: a writer reference and a reader reference.
+ *
+ * This is useful if your level2 store is made of a master and slave
+ * replicated databases, you can then wire the writer reference to the master
+ * database and the reader reference to one your slave databases (assuming
+ * that the updates eventually get replicated to the slave database, in the
+ * meantime the updates will be retrieved from the level1 cache).
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+
+namespace tuscany {
+namespace datacache {
+
+/**
+ * Get an item from the cache.
+ */
+const failable<value> get(const value& key, const lambda<value(const list<value>&)>& rcache1, const lambda<value(const list<value>&)>& wcache1, const lambda<value(const list<value>&)>& rcache2, unused const lambda<value(const list<value>&)>& wcache2) {
+
+ // Lookup level1 cache
+ const value val1 = rcache1(mklist<value>("get", key));
+ if (!isNil(val1))
+ return val1;
+
+ // Lookup level2 cache
+ const value val2 = rcache2(mklist<value>("get", key));
+ if (isNil(val2)) {
+ ostringstream os;
+ os << "Couldn't get cache entry: " << key;
+ return mkfailure<value>(str(os), 404, false);
+ }
+
+ // Update level1 cache
+ wcache1(mklist<value>("put", key, val2));
+
+ return val2;
+}
+
+/**
+ * Post an item to the cache.
+ */
+const failable<value> post(const value& key, const value& val, unused const lambda<value(const list<value>&)>& rcache1, const lambda<value(const list<value>&)>& wcache1, unused const lambda<value(const list<value>&)>& rcache2, const lambda<value(const list<value>&)>& wcache2) {
+ const value id = append<value>(key, mklist(mkuuid()));
+
+ // Update level1 cache
+ wcache1(mklist<value>("put", id, val));
+
+ // Update level2 cache
+ wcache2(mklist<value>("put", id, val));
+
+ return id;
+}
+
+/**
+ * Put an item into the cache.
+ */
+const failable<value> put(const value& key, const value& val, unused const lambda<value(const list<value>&)>& rcache1, const lambda<value(const list<value>&)>& wcache1, unused const lambda<value(const list<value>&)>& rcache2, const lambda<value(const list<value>&)>& wcache2) {
+
+ // Update level1 cache
+ wcache1(mklist<value>("put", key, val));
+
+ // Update level2 cache
+ wcache2(mklist<value>("put", key, val));
+
+ return value(true);
+}
+
+/**
+ * Delete an item from the cache.
+ */
+const failable<value> del(const value& key, unused const lambda<value(const list<value>&)>& rcache1, const lambda<value(const list<value>&)>& wcache1, unused const lambda<value(const list<value>&)>& rcache2, const lambda<value(const list<value>&)>& wcache2) {
+
+ // Delete from level1 cache
+ wcache1(mklist<value>("delete", key));
+
+ // Delete from level2 cache
+ wcache2(mklist<value>("delete", key));
+
+ return value(true);
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "get")
+ return tuscany::datacache::get(cadr(params), caddr(params), cadddr(params), caddddr(params), cadddddr(params));
+ if (func == "post")
+ return tuscany::datacache::post(cadr(params), caddr(params), cadddr(params), caddddr(params), cadddddr(params), caddddddr(params));
+ if (func == "put")
+ return tuscany::datacache::put(cadr(params), caddr(params), cadddr(params), caddddr(params), cadddddr(params), caddddddr(params));
+ if (func == "delete")
+ return tuscany::datacache::del(cadr(params), caddr(params), cadddr(params), caddddr(params), cadddddr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/cache/memcache-test.cpp b/sca-cpp/branches/lightweight-sca/components/cache/memcache-test.cpp
new file mode 100644
index 0000000000..85fc339f1a
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/cache/memcache-test.cpp
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test Memcached access functions.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "perf.hpp"
+#include "memcache.hpp"
+
+namespace tuscany {
+namespace memcache {
+
+bool testMemCached() {
+ MemCached ch(mklist<string>("localhost:11211", "localhost:11212", "localhost:11213"));
+ const value k = mklist<value>("a");
+
+ assert(hasContent(post(k, string("AAA"), ch)));
+ assert(get(k, ch) == value(string("AAA")));
+ assert(hasContent(put(k, string("aaa"), ch)));
+ assert(get(k, ch) == value(string("aaa")));
+ assert(hasContent(del(k, ch)));
+ assert(!hasContent(get(k, ch)));
+
+ return true;
+}
+
+struct getLoop {
+ const value k;
+ MemCached& ch;
+ getLoop(const value& k, MemCached& ch) : k(k), ch(ch) {
+ }
+ const bool operator()() const {
+ gc_scoped_pool p;
+ assert(get(k, ch) == value(string("CCC")));
+ return true;
+ }
+};
+
+bool testGetPerf() {
+ const value k = mklist<value>("c");
+ MemCached ch(mklist<string>("localhost:11211", "localhost:11212", "localhost:11213"));
+ assert(hasContent(post(k, string("CCC"), ch)));
+
+ const lambda<bool()> gl = getLoop(k, ch);
+ cout << "Memcached get test " << time(gl, 5, 200) << " ms" << endl;
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::gc_scoped_pool p;
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::memcache::testMemCached();
+ tuscany::memcache::testGetPerf();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/cache/memcache.componentType b/sca-cpp/branches/lightweight-sca/components/cache/memcache.componentType
new file mode 100644
index 0000000000..8eee91dad6
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/cache/memcache.componentType
@@ -0,0 +1,28 @@
+<?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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <service name="memcache"/>
+ <property name="server" type="xsd:string">localhost</property>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/cache/memcache.cpp b/sca-cpp/branches/lightweight-sca/components/cache/memcache.cpp
new file mode 100644
index 0000000000..738a6ddd5a
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/cache/memcache.cpp
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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$ */
+
+/**
+ * Memcached-based cache component implementation.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "memcache.hpp"
+
+namespace tuscany {
+namespace cache {
+
+/**
+ * Get an item from the cache.
+ */
+const failable<value> get(const list<value>& params, memcache::MemCached& ch) {
+ return memcache::get(car(params), ch);
+}
+
+/**
+ * Post an item to the cache.
+ */
+const failable<value> post(const list<value>& params, memcache::MemCached& ch) {
+ const value id = append<value>(car(params), mklist(mkuuid()));
+ const failable<bool> val = memcache::post(id, cadr(params), ch);
+ if (!hasContent(val))
+ return mkfailure<value>(val);
+ return id;
+}
+
+/**
+ * Put an item into the cache.
+ */
+const failable<value> put(const list<value>& params, memcache::MemCached& ch) {
+ const failable<bool> val = memcache::put(car(params), cadr(params), ch);
+ if (!hasContent(val))
+ return mkfailure<value>(val);
+ return value(content(val));
+}
+
+/**
+ * Delete an item from the cache.
+ */
+const failable<value> del(const list<value>& params, memcache::MemCached& ch) {
+ const failable<bool> val = memcache::del(car(params), ch);
+ if (!hasContent(val))
+ return mkfailure<value>(val);
+ return value(content(val));
+}
+
+/**
+ * Component implementation lambda function.
+ */
+class applyCache {
+public:
+ applyCache(memcache::MemCached& ch) : ch(ch) {
+ }
+
+ const value operator()(const list<value>& params) const {
+ const value func(car(params));
+ if (func == "get")
+ return get(cdr(params), ch);
+ if (func == "post")
+ return post(cdr(params), ch);
+ if (func == "put")
+ return put(cdr(params), ch);
+ if (func == "delete")
+ return del(cdr(params), ch);
+ return mkfailure<value>();
+ }
+
+private:
+ memcache::MemCached& ch;
+};
+
+/**
+ * Convert a list of properties to a list of server addresses.
+ */
+const list<string> servers(const list<value>& params) {
+ if (isNil(params))
+ return list<string>();
+ const value s = ((lambda<value(const list<value>&)>)car(params))(list<value>());
+ return cons<string>(s, servers(cdr(params)));
+}
+
+/**
+ * Start the component.
+ */
+const failable<value> start(const list<value>& params) {
+ // Connect to memcached
+ memcache::MemCached& ch = *(new (gc_new<memcache::MemCached>()) memcache::MemCached(servers(params)));
+
+ // Return the component implementation lambda function
+ return value(lambda<value(const list<value>&)>(applyCache(ch)));
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "start")
+ return tuscany::cache::start(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/cache/memcache.hpp b/sca-cpp/branches/lightweight-sca/components/cache/memcache.hpp
new file mode 100644
index 0000000000..f18405b2ec
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/cache/memcache.hpp
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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_memcache_hpp
+#define tuscany_memcache_hpp
+
+/**
+ * Memcached access functions.
+ */
+
+#include "apr.h"
+#include "apu.h"
+#include "apr_general.h"
+#include "apr_strings.h"
+#include "apr_hash.h"
+#include "apr_memcache.h"
+#include "apr_network_io.h"
+
+#include "string.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "../../modules/scheme/eval.hpp"
+
+namespace tuscany {
+namespace memcache {
+
+/**
+ * Represents a memcached context.
+ */
+class MemCached {
+public:
+ MemCached() : owner(false) {
+ }
+
+ MemCached(const string host, const int port) : p(), owner(true) {
+ debug(host, "memcache::memcached::host");
+ debug(port, "memcache::memcached::port");
+ apr_memcache_create(pool(p), 1, 0, &mc);
+ addServer(host, port);
+ }
+
+ MemCached(const list<string>& servers) : p(), owner(true) {
+ debug(servers, "memcache::memcached::servers");
+ apr_memcache_create(pool(p), (apr_uint16_t)length(servers), 0, &mc);
+ addServers(servers);
+ }
+
+ MemCached(const MemCached& c) : p(c.p), owner(false), mc(c.mc) {
+ debug("memcache::memcached::copy");
+ }
+
+ const MemCached& operator=(const MemCached& c) {
+ debug("memcache::memcached::operator=");
+ if(this == &c)
+ return *this;
+ p = c.p;
+ owner = false;
+ mc = c.mc;
+ return *this;
+ }
+
+ ~MemCached() {
+ }
+
+private:
+ gc_child_pool p;
+ bool owner;
+ apr_memcache_t* mc;
+
+ friend const failable<bool> post(const value& key, const value& val, const MemCached& cache);
+ friend const failable<bool> put(const value& key, const value& val, const MemCached& cache);
+ friend const failable<value> get(const value& key, const MemCached& cache);
+ friend const failable<bool> del(const value& key, const MemCached& cache);
+
+ /**
+ * Add servers to the memcached context.
+ */
+ const failable<bool> addServer(const string& host, const int port) {
+ apr_memcache_server_t *server;
+ const apr_status_t sc = apr_memcache_server_create(pool(p), c_str(host), (apr_port_t)port, 1, 1, 1, 600, &server);
+ if (sc != APR_SUCCESS) {
+ ostringstream os;
+ os << "Couldn't connect to memcached server: " << host << ":" << port;
+ return mkfailure<bool>(str(os));
+ }
+ const apr_status_t as = apr_memcache_add_server(mc, server);
+ if (as != APR_SUCCESS)
+ return mkfailure<bool>("Couldn't add memcached server");
+ return true;
+ }
+
+ const failable<bool> addServers(const list<string>& servers) {
+ if (isNil(servers))
+ return true;
+ const list<string> toks = tokenize(":", car(servers));
+ const failable<bool> r = addServer(car(toks), isNil(cdr(toks))? 11211 : atoi(c_str(cadr(toks))));
+ if (!hasContent(r))
+ return r;
+ return addServers(cdr(servers));
+ }
+};
+
+/**
+ * Replace spaces by tabs (as spaces are not allowed in memcached keys).
+ */
+const char* nospaces(const char* s) {
+ char* c = const_cast<char*>(s);
+ for (; *c; c++)
+ if (*c == ' ')
+ *c = '\t';
+ return s;
+}
+
+/**
+ * Post a new item to the cache.
+ */
+const failable<bool> post(const value& key, const value& val, const MemCached& cache) {
+ debug(key, "memcache::post::key");
+ debug(val, "memcache::post::value");
+
+ const string ks(scheme::writeValue(key));
+ const string vs(scheme::writeValue(val));
+ const apr_status_t rc = apr_memcache_add(cache.mc, nospaces(c_str(ks)), const_cast<char*>(c_str(vs)), length(vs), 0, 27);
+ if (rc != APR_SUCCESS) {
+ ostringstream os;
+ os << "Couldn't add memcached entry: " << key;
+ return mkfailure<bool>(str(os));
+ }
+
+ debug(true, "memcache::post::result");
+ return true;
+}
+
+/**
+ * Update an item in the cache. If the item doesn't exist it is added.
+ */
+const failable<bool> put(const value& key, const value& val, const MemCached& cache) {
+ debug(key, "memcache::put::key");
+ debug(val, "memcache::put::value");
+
+ const string ks(scheme::writeValue(key));
+ const string vs(scheme::writeValue(val));
+ const apr_status_t rc = apr_memcache_set(cache.mc, nospaces(c_str(ks)), const_cast<char*>(c_str(vs)), length(vs), 0, 27);
+ if (rc != APR_SUCCESS) {
+ ostringstream os;
+ os << "Couldn't set memcached entry: " << key;
+ return mkfailure<bool>(str(os));
+ }
+
+ debug(true, "memcache::put::result");
+ return true;
+}
+
+/**
+ * Get an item from the cache.
+ */
+const failable<value> get(const value& key, const MemCached& cache) {
+ debug(key, "memcache::get::key");
+
+ const string ks(scheme::writeValue(key));
+ char *data;
+ apr_size_t size;
+ gc_local_pool lp;
+ const apr_status_t rc = apr_memcache_getp(cache.mc, pool(lp), nospaces(c_str(ks)), &data, &size, NULL);
+ if (rc != APR_SUCCESS) {
+ ostringstream os;
+ os << "Couldn't get memcached entry: " << key;
+ return mkfailure<value>(str(os), 404, false);
+ }
+ const value val(scheme::readValue(string(data, size)));
+
+ debug(val, "memcache::get::result");
+ return val;
+}
+
+/**
+ * Delete an item from the cache
+ */
+const failable<bool> del(const value& key, const MemCached& cache) {
+ debug(key, "memcache::delete::key");
+
+ const string ks(scheme::writeValue(key));
+ const apr_status_t rc = apr_memcache_delete(cache.mc, nospaces(c_str(ks)), 0);
+ if (rc != APR_SUCCESS) {
+ ostringstream os;
+ os << "Couldn't delete memcached entry: " << key;
+ return mkfailure<bool>(str(os));
+ }
+
+ debug(true, "memcache::delete::result");
+ return true;
+}
+
+}
+}
+
+#endif /* tuscany_memcache_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/components/cache/memcached-log-conf b/sca-cpp/branches/lightweight-sca/components/cache/memcached-log-conf
new file mode 100755
index 0000000000..d8a4896eff
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/cache/memcached-log-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.
+
+# Configure memcached logging
+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/memcached
+if [ "$2" = "" ]; then
+ cat >$root/memcached/log.conf << EOF
+cat >>$root/logs/memcached
+EOF
+
+else
+ cat >$root/memcached/log.conf << EOF
+$2
+EOF
+
+fi
+
diff --git a/sca-cpp/branches/lightweight-sca/components/cache/memcached-ssl-test b/sca-cpp/branches/lightweight-sca/components/cache/memcached-ssl-test
new file mode 100755
index 0000000000..a9d42ffd83
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/cache/memcached-ssl-test
@@ -0,0 +1,58 @@
+#!/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
+../../modules/http/ssl-ca-conf tmp/ssl localhost
+../../modules/http/ssl-cert-conf tmp/ssl localhost server
+../../modules/http/ssl-cert-conf tmp/ssl localhost tunnel
+
+./memcached-start tmp 11411
+./memcached-start tmp 11412
+./memcached-start tmp 11413
+
+../../modules/http/httpd-conf tmp/tunnel localhost 8089 htdocs
+../../modules/http/httpd-event-conf tmp/tunnel
+../../modules/http/httpd-tunnel-ssl-conf tmp/tunnel
+tar -C tmp/ssl -c `../../modules/http/ssl-cert-find tmp/ssl` | tar -C tmp/tunnel -x
+../../modules/http/tunnel-ssl-conf tmp/tunnel 11211 localhost 8453 11411
+../../modules/http/tunnel-ssl-conf tmp/tunnel 11212 localhost 8453 11412
+../../modules/http/tunnel-ssl-conf tmp/tunnel 11213 localhost 8453 11413
+../../modules/http/httpd-start tmp/tunnel
+
+../../modules/http/httpd-conf tmp/server localhost 8090 htdocs
+../../modules/http/httpd-event-conf tmp/server
+tar -C tmp/ssl -c `../../modules/http/ssl-cert-find tmp/ssl` | tar -C tmp/server -x
+../../modules/http/httpd-ssl-conf tmp/server 8453
+../../modules/http/httpd-tunnel-ssl-conf tmp/server
+../../modules/http/cert-auth-conf tmp/server
+../../modules/http/httpd-start tmp/server
+sleep 1
+
+# Test
+./memcache-test 2>/dev/null
+rc=$?
+
+# Cleanup
+../../modules/http/httpd-stop tmp/tunnel
+../../modules/http/httpd-stop tmp/server
+./memcached-stop tmp 11411
+./memcached-stop tmp 11412
+./memcached-stop tmp 11413
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/components/cache/memcached-start b/sca-cpp/branches/lightweight-sca/components/cache/memcached-start
new file mode 100755
index 0000000000..ca6c4ac721
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/cache/memcached-start
@@ -0,0 +1,51 @@
+#!/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 memcached
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+addr=$2
+if [ "$addr" = "" ]; then
+ host=""
+ port="11211"
+else
+ host=`$here/../../modules/http/httpd-addr ip $addr`
+ port=`$here/../../modules/http/httpd-addr port $addr`
+fi
+
+memcached_prefix=`cat $here/memcached.prefix`
+
+if [ -f "$root/memcached/log.conf" ]; then
+ log=`cat $root/memcached/log.conf`
+ v="-v"
+else
+ mkdir -p $root/logs
+ log="cat >>$root/logs/memcached"
+ v=""
+fi
+mkdir -p $root/memcached
+echo $log > $root/memcached/logger
+
+if [ "$host" = "" ]; then
+ nohup /bin/sh -c "($memcached_prefix/bin/memcached -d -m 4 -p $port $v 2>&1 | sh $root/memcached/logger)" 1>/dev/null 2>/dev/null &
+else
+ nohup /bin/sh -c "($memcached_prefix/bin/memcached -d -l $host -m 4 -p $port $v 2>&1 | sh $root/memcached/logger)" 1>/dev/null 2>/dev/null &
+fi
+
diff --git a/sca-cpp/branches/lightweight-sca/components/cache/memcached-stop b/sca-cpp/branches/lightweight-sca/components/cache/memcached-stop
new file mode 100755
index 0000000000..a36c03c06a
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/cache/memcached-stop
@@ -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.
+
+# Stop memcached
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+addr=$2
+if [ "$addr" = "" ]; then
+ host=""
+ port="11211"
+else
+ host=`$here/../../modules/http/httpd-addr ip $addr`
+ port=`$here/../../modules/http/httpd-addr port $addr`
+fi
+
+memcached_prefix=`cat $here/memcached.prefix`
+if [ "$host" = "" ]; then
+ mc="$memcached_prefix/bin/memcached -d -m 4 -p $port"
+else
+ mc="$memcached_prefix/bin/memcached -d -l $host -m 4 -p $port"
+fi
+
+k=`ps -ef | grep -v grep | grep "${mc}" | awk '{ print $2 }'`
+if [ "$k" != "" ]; then
+ kill $k
+fi
+
diff --git a/sca-cpp/branches/lightweight-sca/components/cache/memcached-test b/sca-cpp/branches/lightweight-sca/components/cache/memcached-test
new file mode 100755
index 0000000000..dc274bd4aa
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/cache/memcached-test
@@ -0,0 +1,35 @@
+#!/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
+./memcached-start tmp 11211
+./memcached-start tmp 11212
+./memcached-start tmp 11213
+sleep 1
+
+# Test
+./memcache-test 2>/dev/null
+rc=$?
+
+# Cleanup
+./memcached-stop tmp 11211
+./memcached-stop tmp 11212
+./memcached-stop tmp 11213
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/components/cache/memocache.componentType b/sca-cpp/branches/lightweight-sca/components/cache/memocache.componentType
new file mode 100644
index 0000000000..f32677544f
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/cache/memocache.componentType
@@ -0,0 +1,28 @@
+<?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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <service name="memocache"/>
+ <reference name="relay"/>
+ <reference name="cache"/>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/cache/memocache.cpp b/sca-cpp/branches/lightweight-sca/components/cache/memocache.cpp
new file mode 100644
index 0000000000..e7e52cdc59
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/cache/memocache.cpp
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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$ */
+
+/**
+ * A cache component implementation which memoizes the value of function
+ * applications, keyed by the function arguments, in a key/value cache passed
+ * as a reference.
+ *
+ * This is useful if your functions are idempotent and applied many times to
+ * the same arguments. The results can then be retrieved quickly from the
+ * cache without actually applying the function.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+
+namespace tuscany {
+namespace memocache {
+
+/**
+ * Memoize the value of a function application in a cache.
+ */
+const failable<value> memoize(const list<value>& params, const lambda<value(const list<value>&)>& relay, const lambda<value(const list<value>&)>& cache) {
+ debug(params, "memocache::memoize::params");
+
+ // Lookup memoized value from cache
+ const value val = cache(mklist<value>("get", params));
+ if (!isNil(val)) {
+ debug(val, "memocache::memoize::cached");
+ return val;
+ }
+
+ // Apply the given function
+ const value res = relay(params);
+ debug(res, "memocache::memoize::res");
+
+ // Store the result value in the cache
+ cache(mklist<value>("put", params, res));
+
+ return res;
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "start" || func == "stop")
+ return tuscany::mkfailure<tuscany::value>();
+ const tuscany::list<tuscany::value> rev = tuscany::reverse(params);
+ return tuscany::memocache::memoize(tuscany::reverse(tuscany::cddr(rev)), tuscany::cadr(rev), tuscany::car(rev));
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/cache/partition1-test.scm b/sca-cpp/branches/lightweight-sca/components/cache/partition1-test.scm
new file mode 100644
index 0000000000..547539e2a1
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/cache/partition1-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.
+
+; Partition test case
+
+(define (get key) (list key "1"))
+
diff --git a/sca-cpp/branches/lightweight-sca/components/cache/partition2-test.scm b/sca-cpp/branches/lightweight-sca/components/cache/partition2-test.scm
new file mode 100644
index 0000000000..60644128df
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/cache/partition2-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.
+
+; Partition test case
+
+(define (get key) (list key "2"))
+
diff --git a/sca-cpp/branches/lightweight-sca/components/cache/partitioner.componentType b/sca-cpp/branches/lightweight-sca/components/cache/partitioner.componentType
new file mode 100644
index 0000000000..9ff2d4f0b5
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/cache/partitioner.componentType
@@ -0,0 +1,28 @@
+<?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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <service name="partitioner"/>
+ <reference name="selector"/>
+ <reference name="partition" multiplicity="0..n"/>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/cache/partitioner.cpp b/sca-cpp/branches/lightweight-sca/components/cache/partitioner.cpp
new file mode 100644
index 0000000000..e3f04ba112
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/cache/partitioner.cpp
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * A partitioner component implementation which forwards data access requests to a
+ * dynamically selected data store component. The selection is externalized, performed
+ * by a selector component, responsible for selecting the target data store given the
+ * data access request key and a list of references to available data store components.
+ * This pattern can be used for sharding or load balancing for example.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+
+namespace tuscany {
+namespace partitioner {
+
+/**
+ * Return the target partition for a key.
+ */
+const failable<lambda<value(const list<value>&)> > partition(const value& key, const lambda<value(const list<value>&)>& selector, const list<value>& partitions) {
+
+ // Call the selector component to convert the given key to a partition number
+ const value p = selector(mklist<value>("get", key, partitions));
+ if (isNil(p)) {
+ ostringstream os;
+ os << "Couldn't get partition number: " << key;
+ return mkfailure<lambda<value(const list<value>&)> >(str(os), -1, false);
+ }
+ return (const lambda<value(const list<value>&)>)p;
+}
+
+/**
+ * Get an item from a partition.
+ */
+const failable<value> get(const value& key, const lambda<value(const list<value>&)>& selector, const list<value>& partitions) {
+
+ // Select partition
+ const failable<lambda<value(const list<value>&)> > p = partition(key, selector, partitions);
+ if (!hasContent(p))
+ return mkfailure<value>(p);
+
+ // Get from selected partition
+ const value val = content(p)(mklist<value>("get", key));
+ if (isNil(val)) {
+ ostringstream os;
+ os << "Couldn't get entry from partition: " << key;
+ return mkfailure<value>(str(os), 404, false);
+ }
+
+ return val;
+}
+
+/**
+ * Post an item to a partition.
+ */
+const failable<value> post(const value& key, const value& val, const lambda<value(const list<value>&)>& selector, const list<value>& partitions) {
+ const value id = append<value>(key, mklist(mkuuid()));
+
+ // Select partition
+ const failable<lambda<value(const list<value>&)> > p = partition(id, selector, partitions);
+ if (!hasContent(p))
+ return mkfailure<value>(p);
+
+ // Put into select partition
+ content(p)(mklist<value>("put", id, val));
+
+ return id;
+}
+
+/**
+ * Put an item into a partition.
+ */
+const failable<value> put(const value& key, const value& val, const lambda<value(const list<value>&)>& selector, const list<value>& partitions) {
+
+ // Select partition
+ const failable<lambda<value(const list<value>&)> > p = partition(key, selector, partitions);
+ if (!hasContent(p))
+ return mkfailure<value>(p);
+
+ // Put into selected partition
+ content(p)(mklist<value>("put", key, val));
+
+ return value(true);
+}
+
+/**
+ * Delete an item from a partition.
+ */
+const failable<value> del(const value& key, const lambda<value(const list<value>&)>& selector, const list<value>& partitions) {
+
+ // Select partition
+ const failable<lambda<value(const list<value>&)> > p = partition(key, selector, partitions);
+ if (!hasContent(p))
+ return mkfailure<value>(p);
+
+ // Delete from selected partition
+ content(p)(mklist<value>("delete", key));
+
+ return value(true);
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "get")
+ return tuscany::partitioner::get(cadr(params), caddr(params), cdddr(params));
+ if (func == "post")
+ return tuscany::partitioner::post(cadr(params), caddr(params), cadddr(params), cddddr(params));
+ if (func == "put")
+ return tuscany::partitioner::put(cadr(params), caddr(params), cadddr(params), cddddr(params));
+ if (func == "delete")
+ return tuscany::partitioner::del(cadr(params), caddr(params), cdddr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/cache/select-test.scm b/sca-cpp/branches/lightweight-sca/components/cache/select-test.scm
new file mode 100644
index 0000000000..9baa82a5da
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/cache/select-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.
+
+; Partition selector test case
+
+(define (get key partitions) (if (= (car key) "a") (car partitions) (cadr partitions)))
+
diff --git a/sca-cpp/branches/lightweight-sca/components/cache/server-test b/sca-cpp/branches/lightweight-sca/components/cache/server-test
new file mode 100755
index 0000000000..951159c4c8
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/cache/server-test
@@ -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.
+
+# Setup
+rm -rf tmp
+../../modules/http/httpd-conf tmp localhost 8090 ../../modules/http/htdocs
+../../modules/http/httpd-event-conf tmp
+../../modules/server/server-conf tmp
+../../modules/server/scheme-conf tmp
+cat >>tmp/conf/httpd.conf <<EOF
+SCAContribution `pwd`/
+SCAComposite cache.composite
+EOF
+
+./memcached-start tmp 11211
+./memcached-start tmp 11212
+./memcached-start tmp 11213
+./memcached-start tmp 11411
+./memcached-start tmp 11412
+./memcached-start tmp 11413
+../../modules/http/httpd-start tmp
+sleep 2
+
+# Test
+./client-test 2>/dev/null
+rc=$?
+
+# Cleanup
+../../modules/http/httpd-stop tmp
+./memcached-stop tmp 11211
+./memcached-stop tmp 11212
+./memcached-stop tmp 11213
+./memcached-stop tmp 11411
+./memcached-stop tmp 11412
+./memcached-stop tmp 11413
+sleep 2
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/components/chat/Makefile.am b/sca-cpp/branches/lightweight-sca/components/chat/Makefile.am
new file mode 100644
index 0000000000..5c995ad452
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/chat/Makefile.am
@@ -0,0 +1,78 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT 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)/components/chat
+
+if WANT_CHAT
+
+INCLUDES = -I${LIBSTROPHE_INCLUDE}
+
+incl_HEADERS = *.hpp
+incldir = $(prefix)/include/components/chat
+
+dist_comp_SCRIPTS = vysper-start vysper-stop vysper-classpath
+compdir=$(prefix)/components/chat
+
+comp_DATA = vysper.prefix
+vysper.prefix: $(top_builddir)/config.status
+ echo ${VYSPER_PREFIX} >vysper.prefix
+
+EXTRA_DIST = chat.composite chat-sendreceiver.componentType chat-sender.componentType chat-sender2.componentType *.scm
+
+comp_LTLIBRARIES = libchat-sendreceiver.la libchat-sender.la libchat-sender2.la
+noinst_DATA = libchat-sendreceiver${libsuffix} libchat-sender${libsuffix} libchat-sender2${libsuffix}
+
+libchat_sendreceiver_la_SOURCES = chat-sendreceiver.cpp
+libchat_sendreceiver_la_LDFLAGS = -L${LIBSTROPHE_LIB} -R${LIBSTROPHE_LIB} -lstrophe -lssl -lresolv
+libchat-sendreceiver${libsuffix}:
+ ln -s .libs/libchat-sendreceiver${libsuffix}
+
+libchat_sender_la_SOURCES = chat-sender.cpp
+libchat_sender_la_LDFLAGS = -L${LIBSTROPHE_LIB} -R${LIBSTROPHE_LIB} -lstrophe -lssl -lresolv
+libchat-sender${libsuffix}:
+ ln -s .libs/libchat-sender${libsuffix}
+
+libchat_sender2_la_SOURCES = chat-sender2.cpp
+libchat_sender2_la_LDFLAGS = -L${LIBSTROPHE_LIB} -R${LIBSTROPHE_LIB} -lstrophe -lssl -lresolv
+libchat-sender2${libsuffix}:
+ ln -s .libs/libchat-sender2${libsuffix}
+
+chat_send_SOURCES = chat-send.cpp
+chat_send_LDFLAGS = -L${LIBSTROPHE_LIB} -R${LIBSTROPHE_LIB} -lstrophe -lssl -lresolv
+
+xmpp_test_SOURCES = xmpp-test.cpp
+xmpp_test_LDFLAGS = -L${LIBSTROPHE_LIB} -R${LIBSTROPHE_LIB} -lstrophe -lssl -lresolv
+
+client_test_SOURCES = client-test.cpp
+client_test_LDFLAGS = -lxml2 -lcurl -lmozjs -L${LIBSTROPHE_LIB} -R${LIBSTROPHE_LIB} -lstrophe -lssl -lresolv
+
+comp_PROGRAMS = chat-send
+
+noinst_PROGRAMS = xmpp-test client-test
+dist_noinst_SCRIPTS = server-test
+
+if WANT_VYSPER
+
+AM_JAVACFLAGS = -cp `${top_builddir}/components/chat/vysper-classpath ${VYSPER_PREFIX}`${JAVAROOT}
+dist_noinst_JAVA = test/*.java
+CLEANFILES = test/*.class
+
+dist_noinst_SCRIPTS += echo-test
+TESTS = echo-test server-test
+endif
+
+endif
diff --git a/sca-cpp/branches/lightweight-sca/components/chat/chat-send.cpp b/sca-cpp/branches/lightweight-sca/components/chat/chat-send.cpp
new file mode 100644
index 0000000000..bb3907acfd
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/chat/chat-send.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$ */
+
+/**
+ * Test sending a message to an XMPP id.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "list.hpp"
+#include "element.hpp"
+#include "monad.hpp"
+#include "value.hpp"
+#include "perf.hpp"
+#include "parallel.hpp"
+#include "xmpp.hpp"
+
+namespace tuscany {
+namespace chat {
+
+bool sendmsg(const string& jid, const string& pass, const string& to, const string& msg) {
+ XMPPClient xc(jid, pass);
+ const failable<bool> c = connect(xc);
+ assert(hasContent(c));
+ const failable<bool> p = post(to, msg, xc);
+ assert(hasContent(p));
+ return true;
+}
+
+}
+}
+
+int main(unused const int argc, const char** argv) {
+ tuscany::chat::sendmsg(argv[1], argv[2], argv[3], argv[4]);
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/chat/chat-sender.componentType b/sca-cpp/branches/lightweight-sca/components/chat/chat-sender.componentType
new file mode 100644
index 0000000000..01838f0260
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/chat/chat-sender.componentType
@@ -0,0 +1,29 @@
+<?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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <service name="send"/>
+ <property name="jid" type="xsd:string"/>
+ <property name="password" type="xsd:string"/>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/chat/chat-sender.cpp b/sca-cpp/branches/lightweight-sca/components/chat/chat-sender.cpp
new file mode 100644
index 0000000000..a4cabef8de
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/chat/chat-sender.cpp
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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$ */
+
+/**
+ * XMPP chat sender component implementation.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "parallel.hpp"
+#include "xmpp.hpp"
+
+namespace tuscany {
+namespace chat {
+namespace sender {
+
+/**
+ * Post an item to an XMPP JID.
+ */
+const failable<value> post(const list<value>& params, XMPPClient& xc) {
+ const value to = car<value>(car(params));
+ const value val = cadr(params);
+ debug(to, "chat::post::jid");
+ debug(val, "chat::post::value");
+ const failable<bool> r = post(to, val, xc);
+ if (!hasContent(r))
+ return mkfailure<value>(r);
+ return value(mklist<value>(to));
+}
+
+/**
+ * Subscribe and listen to an XMPP session.
+ */
+class noop {
+public:
+ noop() {
+ }
+
+ const failable<bool> operator()(unused const value& jid, unused const value& val, unused XMPPClient& xc) const {
+ return true;
+ }
+};
+
+class subscribe {
+public:
+ subscribe(XMPPClient& xc) : xc(xc) {
+ }
+
+ const failable<bool> operator()() const {
+ gc_pool pool;
+ debug("chat::subscribe::listen");
+ const failable<bool> r = listen(noop(), const_cast<XMPPClient&>(xc));
+ debug("chat::subscribe::stopped");
+ return r;
+ }
+
+private:
+ const lambda<failable<bool>(const value&, const value&, XMPPClient&)> l;
+ XMPPClient xc;
+};
+
+/**
+ * Chatter component lambda function
+ */
+class chatSender {
+public:
+ chatSender(XMPPClient& xc, worker& w) : xc(xc), w(w) {
+ }
+
+ const value operator()(const list<value>& params) const {
+ const tuscany::value func(car(params));
+ if (func == "post")
+ return post(cdr(params), const_cast<XMPPClient&>(xc));
+
+ // Stop the chat sender component
+ if (func != "stop")
+ return mkfailure<value>();
+ debug("chat::sender::stop");
+
+ // Disconnect and shutdown the worker thread
+ disconnect(const_cast<XMPPClient&>(xc));
+ cancel(const_cast<worker&>(w));
+ debug("chat::sender::stopped");
+
+ return failable<value>(value(lambda<value(const list<value>&)>()));
+ }
+
+private:
+ const XMPPClient xc;
+ worker w;
+};
+
+/**
+ * Start the component.
+ */
+const failable<value> start(const list<value>& params) {
+ // Extract the the XMPP JID and password
+ const list<value> props = params;
+ const value jid = ((lambda<value(const list<value>&)>)car(props))(list<value>());
+ const value pass = ((lambda<value(const list<value>&)>)cadr(props))(list<value>());
+
+ // Create an XMPP client session
+ XMPPClient xc(jid, pass, false);
+ const failable<bool> r = connect(xc);
+ if (!hasContent(r))
+ return mkfailure<value>(r);
+
+ // Listen and relay messages in a worker thread
+ worker w(3);
+ submit<failable<bool> >(w, lambda<failable<bool>()>(subscribe(xc)));
+
+ // Return the chat sender component lambda function
+ return value(lambda<value(const list<value>&)>(chatSender(xc, w)));
+}
+
+}
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "start")
+ return tuscany::chat::sender::start(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/chat/chat-sender2.componentType b/sca-cpp/branches/lightweight-sca/components/chat/chat-sender2.componentType
new file mode 100644
index 0000000000..fb7a61ed90
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/chat/chat-sender2.componentType
@@ -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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <service name="send"/>
+ <reference name="jid"/>
+ <reference name="pass"/>
+ <reference name="to"/>
+ <reference name="msg"/>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/chat/chat-sender2.cpp b/sca-cpp/branches/lightweight-sca/components/chat/chat-sender2.cpp
new file mode 100644
index 0000000000..0e00728022
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/chat/chat-sender2.cpp
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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$ */
+
+/**
+ * XMPP chat sender component implementation.
+ * This sender gets its configuration from a single property and its
+ * input data from component references instead of function parameters.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "parallel.hpp"
+#include "xmpp.hpp"
+
+namespace tuscany {
+namespace chat {
+namespace sender {
+
+/**
+ * Post an item to an XMPP JID.
+ */
+const failable<value> post(const lambda<value(const list<value>&)>& jid, const lambda<value(const list<value>&)>& pass, const lambda<value(const list<value>&)>& to, const lambda<value(const list<value>&)>& msg, const list<value>& params) {
+
+ const value vjid = jid(mklist<value>("get", params));
+ const value vpass = pass(mklist<value>("get", params));
+ const value vto = to(mklist<value>("get", params));
+ const value vmsg = msg(mklist<value>("get", params));
+ debug(vjid, "chat::post::from");
+ debug(vto, "chat::post::to");
+ debug(vmsg, "chat::post::value");
+
+ // Create an XMPP client session
+ XMPPClient xc(vjid, vpass);
+ const failable<bool> c = connect(xc);
+ if (!hasContent(c))
+ return mkfailure<value>(c);
+
+ // Post the message
+ const failable<bool> r = post(vto, vmsg, xc);
+ if (!hasContent(r))
+ return mkfailure<value>(r);
+ return value(mklist<value>(vto));
+}
+
+/**
+ * Chat sender component lambda function
+ */
+class chatSender {
+public:
+ chatSender(const lambda<value(const list<value>&)>& jid, const lambda<value(const list<value>&)>& pass, const lambda<value(const list<value>&)>& to, const lambda<value(const list<value>&)>& msg) : jid(jid), pass(pass), to(to), msg(msg) {
+ }
+
+ const value operator()(const list<value>& params) const {
+ const tuscany::value func(car(params));
+ if (func == "get")
+ return post(jid, pass, to, msg, cdr(params));
+
+ // Stop the chat sender component
+ if (func != "stop")
+ return mkfailure<value>();
+ debug("chat::sender::stop");
+ return failable<value>(value(lambda<value(const list<value>&)>()));
+ }
+
+private:
+ const lambda<value(const list<value>&)> jid;
+ const lambda<value(const list<value>&)> pass;
+ const lambda<value(const list<value>&)> to;
+ const lambda<value(const list<value>&)> msg;
+};
+
+/**
+ * Start the component.
+ */
+const failable<value> start(const list<value>& params) {
+
+ // Return the chat sender component lambda function
+ return value(lambda<value(const list<value>&)>(chatSender(car(params), cadr(params), caddr(params), cadddr(params))));
+}
+
+}
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "start")
+ return tuscany::chat::sender::start(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/chat/chat-sendreceiver.componentType b/sca-cpp/branches/lightweight-sca/components/chat/chat-sendreceiver.componentType
new file mode 100644
index 0000000000..0367c38f55
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/chat/chat-sendreceiver.componentType
@@ -0,0 +1,30 @@
+<?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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <service name="send"/>
+ <reference name="relay"/>
+ <property name="jid" type="xsd:string"/>
+ <property name="password" type="xsd:string"/>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/chat/chat-sendreceiver.cpp b/sca-cpp/branches/lightweight-sca/components/chat/chat-sendreceiver.cpp
new file mode 100644
index 0000000000..bfbd32b9ae
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/chat/chat-sendreceiver.cpp
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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$ */
+
+/**
+ * XMPP chat sender/receiver component implementation.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "parallel.hpp"
+#include "xmpp.hpp"
+
+namespace tuscany {
+namespace chat {
+namespace sendreceiver {
+
+/**
+ * Post an item to an XMPP JID.
+ */
+const failable<value> post(const list<value>& params, XMPPClient& xc) {
+ const value to = car<value>(car(params));
+ const value val = cadr(params);
+ debug(to, "chat::post::jid");
+ debug(val, "chat::post::value");
+ const failable<bool> r = post(to, val, xc);
+ if (!hasContent(r))
+ return mkfailure<value>(r);
+ return value(mklist<value>(to));
+}
+
+/**
+ * A relay function that posts the XMPP messages it receives to a relay component reference.
+ */
+class relay {
+public:
+ relay(const lambda<value(const list<value>&)>& rel) : rel(rel) {
+ }
+
+ const failable<bool> operator()(const value& jid, const value& val, unused XMPPClient& xc) const {
+ if (isNil(rel))
+ return true;
+ debug(jid, "chat::relay::jid");
+ debug(val, "chat::relay::value");
+ const value res = rel(mklist<value>("post", mklist<value>(jid), val));
+ return true;
+ }
+
+private:
+ const lambda<value(const list<value>&)> rel;
+};
+
+/**
+ * Subscribe and listen to an XMPP session.
+ */
+class subscribe {
+public:
+ subscribe(const lambda<failable<bool>(const value&, const value&, XMPPClient&)>& l, XMPPClient& xc) : l(l), xc(xc) {
+ }
+
+ const failable<bool> operator()() const {
+ gc_pool pool;
+ debug("chat::subscribe::listen");
+ const failable<bool> r = listen(l, const_cast<XMPPClient&>(xc));
+ debug("chat::subscribe::stopped");
+ return r;
+ }
+
+private:
+ const lambda<failable<bool>(const value&, const value&, XMPPClient&)> l;
+ XMPPClient xc;
+};
+
+/**
+ * Chat sender/receiver component lambda function
+ */
+class chatSenderReceiver {
+public:
+ chatSenderReceiver(XMPPClient& xc, worker& w) : xc(xc), w(w) {
+ }
+
+ const value operator()(const list<value>& params) const {
+ const tuscany::value func(car(params));
+ if (func == "post")
+ return post(cdr(params), const_cast<XMPPClient&>(xc));
+
+ // Stop the chat sender/receiver component
+ if (func != "stop")
+ return mkfailure<value>();
+ debug("chat::sendreceiver::stop");
+
+ // Disconnect and shutdown the worker thread
+ disconnect(const_cast<XMPPClient&>(xc));
+ cancel(const_cast<worker&>(w));
+ debug("chat::sendreceiver::stopped");
+
+ return failable<value>(value(lambda<value(const list<value>&)>()));
+ }
+
+private:
+ const XMPPClient xc;
+ worker w;
+};
+
+/**
+ * Start the component.
+ */
+const failable<value> start(const list<value>& params) {
+ // Extract the relay reference and the XMPP JID and password
+ const bool hasRelay = !isNil(cddr(params));
+ const value rel = hasRelay? car(params) : value(lambda<value(const list<value>&)>());
+ const list<value> props = hasRelay? cdr(params) : params;
+ const value jid = ((lambda<value(const list<value>&)>)car(props))(list<value>());
+ const value pass = ((lambda<value(const list<value>&)>)cadr(props))(list<value>());
+
+ // Create an XMPP client session
+ XMPPClient xc(jid, pass, false);
+ const failable<bool> r = connect(xc);
+ if (!hasContent(r))
+ return mkfailure<value>(r);
+
+ // Listen and relay messages in a worker thread
+ worker w(3);
+ const lambda<failable<bool>(const value&, const value&, XMPPClient&)> rl = relay(rel);
+ submit<failable<bool> >(w, lambda<failable<bool>()>(subscribe(rl, xc)));
+
+ // Return the chat sender/receiver component lambda function
+ return value(lambda<value(const list<value>&)>(chatSenderReceiver(xc, w)));
+}
+
+}
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "start")
+ return tuscany::chat::sendreceiver::start(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/chat/chat.composite b/sca-cpp/branches/lightweight-sca/components/chat/chat.composite
new file mode 100644
index 0000000000..3318ae6d8d
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/chat/chat.composite
@@ -0,0 +1,51 @@
+<?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://tuscany.apache.org/xmlns/sca/components"
+ name="chat">
+
+ <component name="print-sender">
+ <implementation.cpp path="." library="libchat-sender"/>
+ <property name="jid">sca1@localhost</property>
+ <property name="password">sca1</property>
+ <service name="print-sender">
+ <binding.http uri="print-sender"/>
+ </service>
+ </component>
+
+ <component name="print-chatter">
+ <implementation.cpp path="." library="libchat-sendreceiver"/>
+ <property name="jid">sca2@localhost</property>
+ <property name="password">sca2</property>
+ <service name="print-chatter">
+ <binding.http uri="print-chatter"/>
+ </service>
+ <reference name="relay" target="print"/>
+ </component>
+
+ <component name="print">
+ <implementation.scheme script="server-test.scm"/>
+ <service name="print">
+ <binding.http uri="print"/>
+ </service>
+ <reference name="report" target="print-chatter"/>
+ </component>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/chat/client-test.cpp b/sca-cpp/branches/lightweight-sca/components/chat/client-test.cpp
new file mode 100644
index 0000000000..220382fa89
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/chat/client-test.cpp
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 chat component.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "list.hpp"
+#include "element.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "perf.hpp"
+#include "parallel.hpp"
+#include "../../modules/http/http.hpp"
+#include "xmpp.hpp"
+
+namespace tuscany {
+namespace chat {
+
+const value jid1("sca1@localhost");
+const value pass1("sca1");
+const value jid2("sca2@localhost");
+const value pass2("sca2");
+const value jid3("sca3@localhost");
+const value pass3("sca3");
+
+const list<value> item = list<value>() + "content" + (list<value>() + "item"
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "price" + string("$2.99")));
+const list<value> entry = list<value>() + (list<value>() + "entry"
+ + (list<value>() + "title" + string("item"))
+ + (list<value>() + "id" + string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"))
+ + item);
+
+worker w(2);
+bool received;
+
+const failable<bool> listener(const value& from, const value& val, unused XMPPClient& xc) {
+ assert(contains(from, "sca2@localhost"));
+ assert(val == entry);
+ received = true;
+ return false;
+}
+
+struct subscribe {
+ XMPPClient& xc;
+ subscribe(XMPPClient& xc) : xc(xc) {
+ }
+ const failable<bool> operator()() const {
+ const lambda<failable<bool>(const value&, const value&, XMPPClient&)> l(listener);
+ listen(l, xc);
+ return true;
+ }
+};
+
+bool testListen() {
+ received = false;
+ XMPPClient& xc = *(new (gc_new<XMPPClient>()) XMPPClient(jid3, pass3));
+ const failable<bool> c = connect(xc);
+ assert(hasContent(c));
+ const lambda<failable<bool>()> subs = subscribe(xc);
+ submit(w, subs);
+ return true;
+}
+
+bool testPost() {
+ gc_scoped_pool pool;
+ http::CURLSession ch("", "", "", "", 0);
+ const failable<value> id = http::post(entry, "http://localhost:8090/print-sender/sca2@localhost", ch);
+ assert(hasContent(id));
+ return true;
+}
+
+bool testReceived() {
+ shutdown(w);
+ assert(received == true);
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::chat::testListen();
+ tuscany::chat::testPost();
+ tuscany::chat::testReceived();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/chat/echo-test b/sca-cpp/branches/lightweight-sca/components/chat/echo-test
new file mode 100755
index 0000000000..c155d4a9a8
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/chat/echo-test
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Setup
+./vysper-start
+sleep 3
+
+# Test
+./xmpp-test 2>/dev/null
+rc=$?
+
+# Cleanup
+./vysper-stop
+sleep 1
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/components/chat/server-test b/sca-cpp/branches/lightweight-sca/components/chat/server-test
new file mode 100755
index 0000000000..7b5fabfe14
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/chat/server-test
@@ -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.
+
+# Setup
+rm -rf tmp
+../../modules/http/httpd-conf tmp localhost 8090 ../../modules/http/htdocs
+../../modules/http/httpd-event-conf tmp
+../../modules/server/server-conf tmp
+../../modules/server/scheme-conf tmp
+cat >>tmp/conf/httpd.conf <<EOF
+SCAContribution `pwd`/
+SCAComposite chat.composite
+EOF
+
+./vysper-start
+sleep 3
+../../modules/http/httpd-start tmp
+sleep 2
+
+# Test
+./client-test 2>/dev/null
+rc=$?
+
+# Cleanup
+../../modules/http/httpd-stop tmp
+sleep 1
+./vysper-stop
+sleep 1
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/components/chat/server-test.scm b/sca-cpp/branches/lightweight-sca/components/chat/server-test.scm
new file mode 100644
index 0000000000..a6023708e1
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/chat/server-test.scm
@@ -0,0 +1,20 @@
+; Licensed to the Apache Software Foundation (ASF) under one
+; or more contributor license agreements. See the NOTICE file
+; distributed with this work for additional information
+; regarding copyright ownership. The ASF licenses this file
+; to you under the Apache License, Version 2.0 (the
+; "License"); you may not use this file except in compliance
+; with the License. You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing,
+; software distributed under the License is distributed on an
+; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+; KIND, either express or implied. See the License for the
+; specific language governing permissions and limitations
+; under the License.
+
+; Chat test case
+
+(define (post key val report) (report "post" '("sca3@localhost") val))
diff --git a/sca-cpp/branches/lightweight-sca/components/chat/test/TestVysperServer.java b/sca-cpp/branches/lightweight-sca/components/chat/test/TestVysperServer.java
new file mode 100644
index 0000000000..3d2b7d7c3e
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/chat/test/TestVysperServer.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package test;
+
+/**
+ * A test XMPP server, using Apache Vysper.
+ */
+import static java.lang.System.*;
+
+import java.io.File;
+
+import org.apache.vysper.mina.TCPEndpoint;
+import org.apache.vysper.stanzasession.StanzaSessionFactory;
+import org.apache.vysper.storage.StorageProviderRegistry;
+import org.apache.vysper.storage.inmemory.MemoryStorageProviderRegistry;
+import org.apache.vysper.xmpp.authorization.AccountManagement;
+import org.apache.vysper.xmpp.modules.extension.xep0049_privatedata.PrivateDataModule;
+import org.apache.vysper.xmpp.modules.extension.xep0054_vcardtemp.VcardTempModule;
+import org.apache.vysper.xmpp.modules.extension.xep0092_software_version.SoftwareVersionModule;
+import org.apache.vysper.xmpp.modules.extension.xep0119_xmppping.XmppPingModule;
+import org.apache.vysper.xmpp.modules.extension.xep0202_entity_time.EntityTimeModule;
+import org.apache.vysper.xmpp.server.XMPPServer;
+
+class TestVysperServer {
+ public static void main(final String args[]) throws Exception {
+ out.println("Starting test Vysper server...");
+
+ // Add the XMPP users used by the xmpp-test and server-test test cases
+ // If you're using your own XMPP server you need to add these users manually
+ final StorageProviderRegistry providerRegistry = new MemoryStorageProviderRegistry();
+ final AccountManagement accountManagement = (AccountManagement)providerRegistry.retrieve(AccountManagement.class);
+ accountManagement.addUser("sca1@localhost", "sca1");
+ accountManagement.addUser("sca2@localhost", "sca2");
+ accountManagement.addUser("sca3@localhost", "sca3");
+
+ // Create and start XMPP server for domain: localhost
+ final XMPPServer server = new org.apache.vysper.xmpp.server.XMPPServer("localhost");
+ server.addEndpoint(new TCPEndpoint());
+ server.addEndpoint(new StanzaSessionFactory());
+ server.setStorageProviderRegistry(providerRegistry);
+ final File cert = new File(TestVysperServer.class.getClassLoader().getResource("bogus_mina_tls.cert").getPath());
+ server.setTLSCertificateInfo(cert, "boguspw");
+ server.start();
+ server.addModule(new SoftwareVersionModule());
+ server.addModule(new EntityTimeModule());
+ server.addModule(new VcardTempModule());
+ server.addModule(new XmppPingModule());
+ server.addModule(new PrivateDataModule());
+ out.println("Test Vysper server started...");
+
+ // Wait forever
+ final Object lock = new Object();
+ synchronized(lock) {
+ lock.wait();
+ }
+
+ System.out.println("Stopping test Vysper server...");
+ server.stop();
+ out.println("Test Vysper server stopped.");
+ System.exit(0);
+ }
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/chat/vysper-classpath b/sca-cpp/branches/lightweight-sca/components/chat/vysper-classpath
new file mode 100755
index 0000000000..e164200ed2
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/chat/vysper-classpath
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Compute a classpath for running a Vysper server
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+
+if [ "$1" = "" ]; then
+ vysper_prefix=`cat $here/vysper.prefix`
+else
+ vysper_prefix=$1
+fi
+jars=`find $vysper_prefix/lib -name "*.jar" | awk '{ printf "%s:", $1 }'`
+echo "$vysper_prefix/config:$jars"
diff --git a/sca-cpp/branches/lightweight-sca/components/chat/vysper-start b/sca-cpp/branches/lightweight-sca/components/chat/vysper-start
new file mode 100755
index 0000000000..b7fcad5217
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/chat/vysper-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 Vysper test XMPP server
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+
+java_prefix=`cat $here/../../modules/java/java.prefix`
+mkdir -p $here/tmp/logs
+${java_prefix}/jre/bin/java -cp `$here/vysper-classpath`$here test.TestVysperServer 2>&1 1>>$here/tmp/logs/vysper.log &
diff --git a/sca-cpp/branches/lightweight-sca/components/chat/vysper-stop b/sca-cpp/branches/lightweight-sca/components/chat/vysper-stop
new file mode 100755
index 0000000000..0fec98400d
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/chat/vysper-stop
@@ -0,0 +1,28 @@
+#!/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 Vysper test XMPP server
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+
+java_prefix=`cat $here/../../modules/java/java.prefix`
+k=`ps -ef | grep -v grep | grep "${java_prefix}/jre/bin/java" | grep "vysper" | awk '{ print $2 }'`
+if [ "$k" != "" ]; then
+ kill $k
+fi
+
diff --git a/sca-cpp/branches/lightweight-sca/components/chat/xmpp-test.cpp b/sca-cpp/branches/lightweight-sca/components/chat/xmpp-test.cpp
new file mode 100644
index 0000000000..6b7fa3439f
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/chat/xmpp-test.cpp
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 XMPP support functions.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "list.hpp"
+#include "element.hpp"
+#include "monad.hpp"
+#include "value.hpp"
+#include "perf.hpp"
+#include "parallel.hpp"
+#include "xmpp.hpp"
+
+namespace tuscany {
+namespace chat {
+
+const value jid1("sca1@localhost");
+const value pass1("sca1");
+const value jid2("sca2@localhost");
+const value pass2("sca2");
+
+worker w(2);
+bool received;
+
+const failable<bool> listener(const value& from, const value& val, unused XMPPClient& xc) {
+ assert(contains(from, "sca1@localhost"));
+ assert(val == "hey");
+ received = true;
+ return false;
+}
+
+struct subscribe {
+ XMPPClient& xc;
+ subscribe(XMPPClient& xc) : xc(xc) {
+ }
+ const failable<bool> operator()() const {
+ const lambda<failable<bool>(const value&, const value&, XMPPClient&)> l(listener);
+ listen(l, xc);
+ return true;
+ }
+};
+
+bool testListen() {
+ received = false;
+ XMPPClient& xc = *(new (gc_new<XMPPClient>()) XMPPClient(jid2, pass2));
+ const failable<bool> c = connect(xc);
+ assert(hasContent(c));
+ const lambda<failable<bool>()> subs = subscribe(xc);
+ submit(w, subs);
+ return true;
+}
+
+bool testPost() {
+ XMPPClient xc(jid1, pass1);
+ const failable<bool> c = connect(xc);
+ assert(hasContent(c));
+ const failable<bool> p = post(jid2, "hey", xc);
+ assert(hasContent(p));
+ return true;
+}
+
+bool testReceived() {
+ shutdown(w);
+ assert(received == true);
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::chat::testListen();
+ tuscany::chat::testPost();
+ tuscany::chat::testReceived();
+
+ tuscany::cout << "OK" << tuscany::endl;
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/chat/xmpp.hpp b/sca-cpp/branches/lightweight-sca/components/chat/xmpp.hpp
new file mode 100644
index 0000000000..aa006029fa
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/chat/xmpp.hpp
@@ -0,0 +1,339 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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_queue_hpp
+#define tuscany_queue_hpp
+
+/**
+ * XMPP support functions.
+ */
+
+#include "strophe.h"
+extern "C" {
+#include "common.h"
+}
+#include "string.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "../../modules/scheme/eval.hpp"
+
+namespace tuscany {
+namespace chat {
+
+/**
+ * XMPP runtime, one per process.
+ */
+class XMPPRuntime {
+public:
+ XMPPRuntime() {
+ debug("chat::xmppruntime");
+ xmpp_initialize();
+ log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG);
+ }
+
+ ~XMPPRuntime() {
+ debug("chat::~xmppruntime");
+ xmpp_shutdown();
+ }
+
+private:
+ friend class XMPPClient;
+ xmpp_log_t* log;
+
+} xmppRuntime;
+
+/**
+ * Represents an XMPP client.
+ */
+class XMPPClient {
+public:
+ XMPPClient(const string& jid, const string& pass, bool owner = true) : owner(owner), ctx(xmpp_ctx_new(NULL, xmppRuntime.log)), conn(xmpp_conn_new(ctx)), connecting(false), connected(false), disconnecting(false) {
+ xmpp_conn_set_jid(conn, c_str(jid + "/" + mkuuid()));
+ xmpp_conn_set_pass(conn, c_str(pass));
+ debug(jid, "chat::xmppclient::jid");
+ }
+
+ XMPPClient(const XMPPClient& xc) : owner(false), ctx(xc.ctx), conn(xc.conn), listener(xc.listener), connecting(xc.connecting), connected(xc.connected), disconnecting(xc.disconnecting) {
+ debug("chat::xmppclient::copy");
+ }
+
+ const XMPPClient& operator=(const XMPPClient& xc) {
+ debug("chat::xmppclient::operator=");
+ if(this == &xc)
+ return *this;
+ owner = false;
+ ctx = xc.ctx;
+ conn = xc.conn;
+ listener = xc.listener;
+ connecting = xc.connecting;
+ connected = xc.connected;
+ disconnecting = xc.disconnecting;
+ return *this;
+ }
+
+ ~XMPPClient() {
+ debug("chat::~xmppclient");
+ extern const failable<bool> disconnect(XMPPClient& xc);
+ if (!owner)
+ return;
+ if (!disconnecting)
+ disconnect(*this);
+ xmpp_conn_release(conn);
+ xmpp_ctx_free(ctx);
+ }
+
+private:
+ friend int versionHandler(xmpp_conn_t* const conn, xmpp_stanza_t* const stanza, void* const udata);
+ friend void connHandler(xmpp_conn_t * const conn, const xmpp_conn_event_t status, const int err, xmpp_stream_error_t* const errstream, void *const udata);
+ friend int messageHandler(xmpp_conn_t* const conn, xmpp_stanza_t* const stanza, void* const udata);
+ friend const failable<bool> connect(XMPPClient& xc);
+ friend const failable<size_t> send(const char* data, const size_t len, XMPPClient& xc);
+ friend const failable<size_t> send(xmpp_stanza_t* const stanza, XMPPClient& xc);
+ friend const failable<bool> post(const value& to, const value& val, XMPPClient& xc);
+ friend const failable<bool> disconnect(XMPPClient& xc);
+ friend const failable<bool> listen(const lambda<failable<bool>(const value&, const value&, XMPPClient&)>& listener, XMPPClient& xc);
+
+ bool owner;
+ xmpp_ctx_t* ctx;
+ xmpp_conn_t* conn;
+ lambda<failable<bool>(const value&, const value&, XMPPClient&)> listener;
+ bool connecting;
+ bool connected;
+ bool disconnecting;
+};
+
+/**
+ * Make a text stanza.
+ */
+xmpp_stanza_t* textStanza(const char* text, xmpp_ctx_t* ctx) {
+ xmpp_stanza_t* stanza = xmpp_stanza_new(ctx);
+ xmpp_stanza_set_text(stanza, text);
+ return stanza;
+}
+
+/**
+ * Make a named stanza.
+ */
+xmpp_stanza_t* namedStanza(const char* ns, const char* name, xmpp_ctx_t* ctx) {
+ xmpp_stanza_t* stanza = xmpp_stanza_new(ctx);
+ xmpp_stanza_set_name(stanza, name);
+ if (ns != NULL)
+ xmpp_stanza_set_ns(stanza, ns);
+ return stanza;
+}
+
+/**
+ * Make a named stanza using a qualified name.
+ */
+xmpp_stanza_t* namedStanza(const char* name, xmpp_ctx_t* ctx) {
+ xmpp_stanza_t* stanza = xmpp_stanza_new(ctx);
+ xmpp_stanza_set_name(stanza, name);
+ return stanza;
+}
+
+/**
+ * XMPP version handler.
+ */
+int versionHandler(xmpp_conn_t* const conn, xmpp_stanza_t* const stanza, void* const udata) {
+ XMPPClient& xc = *(XMPPClient*)udata;
+
+ // Build version reply stanza
+ xmpp_stanza_t* reply = namedStanza("iq", xc.ctx);
+ xmpp_stanza_set_type(reply, "result");
+ xmpp_stanza_set_id(reply, xmpp_stanza_get_id(stanza));
+ xmpp_stanza_set_attribute(reply, "to", xmpp_stanza_get_attribute(stanza, "from"));
+ xmpp_stanza_t* query = namedStanza(xmpp_stanza_get_ns(xmpp_stanza_get_children(stanza)), "query", xc.ctx);
+ xmpp_stanza_add_child(reply, query);
+ xmpp_stanza_t* name = namedStanza("name", xc.ctx);
+ xmpp_stanza_add_child(query, name);
+ xmpp_stanza_add_child(name, textStanza("Apache Tuscany", xc.ctx));
+ xmpp_stanza_t* version = namedStanza("version", xc.ctx);
+ xmpp_stanza_add_child(query, version);
+ xmpp_stanza_add_child(version, textStanza("1.0", xc.ctx));
+
+ // Send it
+ xmpp_send(conn, reply);
+ xmpp_stanza_release(reply);
+ return 1;
+}
+
+/**
+ * XMPP message handler
+ */
+int messageHandler(unused xmpp_conn_t* const conn, xmpp_stanza_t* const stanza, void* const udata) {
+ // Ignore noise
+ if(xmpp_stanza_get_child_by_name(stanza, "body") == NULL)
+ return 1;
+ if(!strcmp(xmpp_stanza_get_attribute(stanza, "type"), "error"))
+ return 1;
+
+ // Call the client listener function
+ XMPPClient& xc = *(XMPPClient*)udata;
+ const char* from = xmpp_stanza_get_attribute(stanza, "from");
+ const char* text = xmpp_stanza_get_text(xmpp_stanza_get_child_by_name(stanza, "body"));
+ if (isNil(xc.listener))
+ return 1;
+ const value val(scheme::readValue(text));
+ debug(from, "chat::messageHandler::from");
+ debug(val, "chat::messageHandler::body");
+ const failable<bool> r = xc.listener(value(string(from)), val, xc);
+ if (!hasContent(r) || !content(r)) {
+ // Stop listening
+ xc.listener = lambda<failable<bool>(const value&, const value&, XMPPClient&)>();
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * XMPP connection handler.
+ */
+void connHandler(xmpp_conn_t * const conn, const xmpp_conn_event_t status, unused const int err, unused xmpp_stream_error_t* const errstream, void *const udata) {
+ XMPPClient& xc = *(XMPPClient*)udata;
+ xc.connecting = false;
+
+ if (status == XMPP_CONN_CONNECT) {
+ debug("chat::connHandler::connected");
+ xmpp_handler_add(conn, versionHandler, "jabber:iq:version", "iq", NULL, &xc);
+
+ // Send a <presence/> stanza so that we appear online to contacts
+ xmpp_stanza_t* pres = xmpp_stanza_new(xc.ctx);
+ xmpp_stanza_set_name(pres, "presence");
+ xmpp_send(conn, pres);
+ xmpp_stanza_release(pres);
+ xc.connected = true;
+ return;
+ }
+
+ debug("chat::connHandler::disconnected");
+ xc.connected = false;
+ if (xc.ctx->loop_status == XMPP_LOOP_RUNNING)
+ xc.ctx->loop_status = XMPP_LOOP_QUIT;
+}
+
+/**
+ * Connect to an XMPP server.
+ */
+const failable<bool> connect(XMPPClient& xc) {
+ xc.connecting = true;
+ xmpp_connect_client(xc.conn, NULL, 0, connHandler, &xc);
+ while(xc.connecting)
+ xmpp_run_once(xc.ctx, 20L);
+ if (!xc.connected)
+ return mkfailure<bool>("Couldn't connect to XMPP server");
+ return true;
+}
+
+/**
+ * Send a buffer on an XMPP session.
+ */
+const failable<size_t> send(const char* data, const size_t len, XMPPClient& xc) {
+ if (len == 0)
+ return 0;
+ const size_t written = xc.conn->tls? tls_write(xc.conn->tls, data, len) : sock_write(xc.conn->sock, data, len);
+ if (written == (size_t)-1) {
+ xc.conn->error = xc.conn->tls? tls_error(xc.conn->tls) : sock_error();
+ return mkfailure<size_t>("Couldn't send stanza to XMPP server");
+ }
+ return send(data + written, len - written, xc);
+}
+
+/**
+ * Send a string on an XMPP session.
+ */
+const failable<size_t> send(const string& data, XMPPClient& xc) {
+ return send(c_str(data), length(data), xc);
+}
+
+/**
+ * Send a stanza on an XMPP session.
+ */
+const failable<size_t> send(xmpp_stanza_t* const stanza, XMPPClient& xc) {
+ char *buf;
+ size_t len;
+ const int rc = xmpp_stanza_to_text(stanza, &buf, &len);
+ if (rc != 0)
+ return mkfailure<size_t>("Couldn't convert stanza to text");
+ const failable<size_t> r = send(buf, len, xc);
+ if (!hasContent(r)) {
+ xmpp_free(xc.conn->ctx, buf);
+ return r;
+ }
+ xmpp_debug(xc.conn->ctx, "conn", "SENT: %s", buf);
+ xmpp_free(xc.conn->ctx, buf);
+ return content(r);
+}
+
+/**
+ * Post a message to an XMPP jid.
+ */
+const failable<bool> post(const value& to, const value& val, XMPPClient& xc) {
+ debug(to, "chat::post::to");
+ debug(val, "chat::post::body");
+
+ // Convert the value to a string
+ const string vs(scheme::writeValue(val));
+
+ // Build message stanza
+ xmpp_stanza_t* stanza = namedStanza("message", xc.ctx);
+ xmpp_stanza_set_type(stanza, "chat");
+ xmpp_stanza_set_attribute(stanza, "to", c_str(string(to)));
+ xmpp_stanza_t* body = namedStanza("body", xc.ctx);
+ xmpp_stanza_add_child(stanza, body);
+ xmpp_stanza_add_child(body, textStanza(c_str(vs), xc.ctx));
+
+ // Send it
+ const failable<size_t> r = send(stanza, xc);
+ xmpp_stanza_release(stanza);
+ if (!hasContent(r))
+ return mkfailure<bool>(r);
+ return true;
+}
+
+/**
+ * Disconnect an XMPP session.
+ */
+const failable<bool> disconnect(XMPPClient& xc) {
+ xc.disconnecting = true;
+ const failable<size_t> r = send("</stream:stream>", xc);
+ if (!hasContent(r))
+ return mkfailure<bool>(r);
+ return true;
+}
+
+/**
+ * Listen to messages received by an XMPP client.
+ */
+const failable<bool> listen(const lambda<failable<bool>(const value&, const value&, XMPPClient&)>& listener, XMPPClient& xc) {
+ debug("chat::listen");
+ xc.listener = listener;
+ xmpp_handler_add(xc.conn, messageHandler, NULL, "message", NULL, &xc);
+ xc.ctx->loop_status = XMPP_LOOP_RUNNING;
+ while(xc.connected && !isNil(xc.listener) && xc.ctx->loop_status == XMPP_LOOP_RUNNING)
+ xmpp_run_once(xc.ctx, 1000L);
+ return true;
+}
+
+}
+}
+
+#endif /* tuscany_xmpp_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/components/constdb/Makefile.am b/sca-cpp/branches/lightweight-sca/components/constdb/Makefile.am
new file mode 100644
index 0000000000..e4504a53e7
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/constdb/Makefile.am
@@ -0,0 +1,49 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT 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${TINYCDB_INCLUDE}
+
+incl_HEADERS = *.hpp
+incldir = $(prefix)/include/components/constdb
+
+dist_comp_SCRIPTS = tinycdb
+compdir=$(prefix)/components/constdb
+
+comp_DATA = tinycdb.prefix
+tinycdb.prefix: $(top_builddir)/config.status
+ echo ${TINYCDB_PREFIX} >tinycdb.prefix
+
+EXTRA_DIST = constdb.composite constdb.componentType
+
+comp_LTLIBRARIES = libconstdb.la
+noinst_DATA = libconstdb${libsuffix}
+
+libconstdb_la_SOURCES = constdb.cpp
+libconstdb_la_LDFLAGS = -L${TINYCDB_LIB} -R${TINYCDB_LIB} -lcdb
+libconstdb${libsuffix}:
+ ln -s .libs/libconstdb${libsuffix}
+
+tinycdb_test_SOURCES = tinycdb-test.cpp
+tinycdb_test_LDFLAGS = -L${TINYCDB_LIB} -R${TINYCDB_LIB} -lcdb
+
+client_test_SOURCES = client-test.cpp
+client_test_LDFLAGS = -lxml2 -lcurl -lmozjs
+
+dist_noinst_SCRIPTS = constdb-test server-test
+noinst_PROGRAMS = tinycdb-test client-test
+TESTS = constdb-test server-test
+
diff --git a/sca-cpp/branches/lightweight-sca/components/constdb/client-test.cpp b/sca-cpp/branches/lightweight-sca/components/constdb/client-test.cpp
new file mode 100644
index 0000000000..ea45762cd6
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/constdb/client-test.cpp
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 NoSQL database component.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "perf.hpp"
+#include "../../modules/http/http.hpp"
+
+namespace tuscany {
+namespace constdb {
+
+const string uri("http://localhost:8090/constdb");
+
+bool testConstDb() {
+ http::CURLSession cs("", "", "", "", 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> a = list<value>() + (list<value>() + "entry"
+ + (list<value>() + "title" + string("item"))
+ + (list<value>() + "id" + string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"))
+ + i);
+
+ const failable<value> id = http::post(a, uri, cs);
+ assert(hasContent(id));
+
+ const string p = path(content(id));
+ {
+ const failable<value> val = http::get(uri + p, cs);
+ assert(hasContent(val));
+ assert(content(val) == a);
+ }
+
+ const list<value> j = list<value>() + "content" + (list<value>() + "item"
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "price" + string("$3.55")));
+ const list<value> b = list<value>() + (list<value>() + "entry"
+ + (list<value>() + "title" + string("item"))
+ + (list<value>() + "id" + string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"))
+ + j);
+
+ {
+ const failable<value> r = http::put(b, uri + p, cs);
+ assert(hasContent(r));
+ assert(content(r) == value(true));
+ }
+ {
+ const failable<value> val = http::get(uri + p, cs);
+ assert(hasContent(val));
+ assert(content(val) == b);
+ }
+ {
+ const failable<value> r = http::del(uri + p, cs);
+ assert(hasContent(r));
+ assert(content(r) == value(true));
+ }
+ {
+ const failable<value> val = http::get(uri + p, cs);
+ assert(!hasContent(val));
+ }
+
+ return true;
+}
+
+struct getLoop {
+ const string path;
+ const value entry;
+ http::CURLSession& cs;
+ getLoop(const string& path, const value& entry, http::CURLSession& cs) : path(path), entry(entry), cs(cs) {
+ }
+ const bool operator()() const {
+ const failable<value> val = http::get(uri + path, cs);
+ assert(hasContent(val));
+ assert(content(val) == entry);
+ return true;
+ }
+};
+
+bool testGetPerf() {
+ const list<value> i = list<value>() + "content" + (list<value>() + "item"
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "price" + string("$4.55")));
+ 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 cs("", "", "", "", 0);
+ const failable<value> id = http::post(a, uri, cs);
+ assert(hasContent(id));
+ const string p = path(content(id));
+
+ const lambda<bool()> gl = getLoop(p, a, cs);
+ cout << "ConstDb get test " << time(gl, 5, 200) << " ms" << endl;
+
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::constdb::testConstDb();
+ tuscany::constdb::testGetPerf();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/constdb/constdb-test b/sca-cpp/branches/lightweight-sca/components/constdb/constdb-test
new file mode 100755
index 0000000000..420b98559c
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/constdb/constdb-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.
+
+# Setup
+rm -rf tmp
+mkdir -p tmp
+./tinycdb -c -m tmp/test.cdb </dev/null
+
+# Test
+./tinycdb-test 2>/dev/null
+rc=$?
+
+# Cleanup
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/components/constdb/constdb.componentType b/sca-cpp/branches/lightweight-sca/components/constdb/constdb.componentType
new file mode 100644
index 0000000000..c96d2c8f1f
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/constdb/constdb.componentType
@@ -0,0 +1,28 @@
+<?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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <service name="constdb"/>
+ <property name="dbname" type="xsd:string"/>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/constdb/constdb.composite b/sca-cpp/branches/lightweight-sca/components/constdb/constdb.composite
new file mode 100644
index 0000000000..ea6b4907c8
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/constdb/constdb.composite
@@ -0,0 +1,32 @@
+<?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://tuscany.apache.org/xmlns/sca/components"
+ name="constdb">
+
+ <component name="constdb">
+ <implementation.cpp path="." library="libconstdb"/>
+ <property name="dbname">tmp/test.cdb</property>
+ <service name="constdb">
+ <binding.http uri="constdb"/>
+ </service>
+ </component>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/constdb/constdb.cpp b/sca-cpp/branches/lightweight-sca/components/constdb/constdb.cpp
new file mode 100644
index 0000000000..6d1cb15baf
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/constdb/constdb.cpp
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * TinyCDB-based database component implementation.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "tinycdb.hpp"
+
+namespace tuscany {
+namespace constdb {
+
+/**
+ * Get an item from the database.
+ */
+const failable<value> get(const list<value>& params, tinycdb::TinyCDB& cdb) {
+ return tinycdb::get(car(params), cdb);
+}
+
+/**
+ * Post an item to the database.
+ */
+const failable<value> post(const list<value>& params, tinycdb::TinyCDB& cdb) {
+ const value id = append<value>(car(params), mklist(mkuuid()));
+ const failable<bool> val = tinycdb::post(id, cadr(params), cdb);
+ if (!hasContent(val))
+ return mkfailure<value>(val);
+ return id;
+}
+
+/**
+ * Put an item into the database.
+ */
+const failable<value> put(const list<value>& params, tinycdb::TinyCDB& cdb) {
+ const failable<bool> val = tinycdb::put(car(params), cadr(params), cdb);
+ if (!hasContent(val))
+ return mkfailure<value>(val);
+ return value(content(val));
+}
+
+/**
+ * Delete an item from the database.
+ */
+const failable<value> del(const list<value>& params, tinycdb::TinyCDB& cdb) {
+ const failable<bool> val = tinycdb::del(car(params), cdb);
+ if (!hasContent(val))
+ return mkfailure<value>(val);
+ return value(content(val));
+}
+
+/**
+ * Component implementation lambda function.
+ */
+class applyConstDb {
+public:
+ applyConstDb(tinycdb::TinyCDB& cdb) : cdb(cdb) {
+ }
+
+ const value operator()(const list<value>& params) const {
+ const value func(car(params));
+ if (func == "get")
+ return get(cdr(params), cdb);
+ if (func == "post")
+ return post(cdr(params), cdb);
+ if (func == "put")
+ return put(cdr(params), cdb);
+ if (func == "delete")
+ return del(cdr(params), cdb);
+ return mkfailure<value>();
+ }
+
+private:
+ tinycdb::TinyCDB& cdb;
+};
+
+/**
+ * Start the component.
+ */
+const failable<value> start(unused const list<value>& params) {
+ // Connect to the configured database and table
+ const value dbname = ((lambda<value(const list<value>&)>)car(params))(list<value>());
+ tinycdb::TinyCDB& cdb = *(new (gc_new<tinycdb::TinyCDB>()) tinycdb::TinyCDB(dbname));
+
+ // Return the component implementation lambda function
+ return value(lambda<value(const list<value>&)>(applyConstDb(cdb)));
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "start")
+ return tuscany::constdb::start(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/constdb/server-test b/sca-cpp/branches/lightweight-sca/components/constdb/server-test
new file mode 100755
index 0000000000..abeceaf98e
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/constdb/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
+../../modules/http/httpd-conf tmp localhost 8090 ../../modules/http/htdocs
+../../modules/http/httpd-event-conf tmp
+../../modules/server/server-conf tmp
+../../modules/server/scheme-conf tmp
+cat >>tmp/conf/httpd.conf <<EOF
+SCAContribution `pwd`/
+SCAComposite constdb.composite
+EOF
+
+./tinycdb -c -m tmp/test.cdb </dev/null
+../../modules/http/httpd-start tmp
+sleep 2
+
+# Test
+./client-test 2>/dev/null
+rc=$?
+
+# Cleanup
+../../modules/http/httpd-stop tmp
+sleep 2
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/components/constdb/tinycdb b/sca-cpp/branches/lightweight-sca/components/constdb/tinycdb
new file mode 100755
index 0000000000..3e5c23957f
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/constdb/tinycdb
@@ -0,0 +1,24 @@
+#!/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`
+tinycdb_prefix=`cat $here/tinycdb.prefix`
+
+$tinycdb_prefix/bin/cdb $*
+
diff --git a/sca-cpp/branches/lightweight-sca/components/constdb/tinycdb-test.cpp b/sca-cpp/branches/lightweight-sca/components/constdb/tinycdb-test.cpp
new file mode 100644
index 0000000000..b3b4ea7fd7
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/constdb/tinycdb-test.cpp
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 TinyCDB access functions.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "perf.hpp"
+#include "tinycdb.hpp"
+
+namespace tuscany {
+namespace tinycdb {
+
+bool testTinyCDB() {
+ TinyCDB cdb("tmp/test.cdb");
+ const value k = mklist<value>("a");
+
+ assert(hasContent(post(k, string("AAA"), cdb)));
+ assert((get(k, cdb)) == value(string("AAA")));
+ assert(hasContent(put(k, string("aaa"), cdb)));
+ assert((get(k, cdb)) == value(string("aaa")));
+ assert(hasContent(del(k, cdb)));
+ assert(!hasContent(get(k, cdb)));
+
+ return true;
+}
+
+struct getLoop {
+ const value k;
+ TinyCDB& cdb;
+ getLoop(const value& k, TinyCDB& cdb) : k(k), cdb(cdb) {
+ }
+ const bool operator()() const {
+ assert((get(k, cdb)) == value(string("CCC")));
+ return true;
+ }
+};
+
+bool testGetPerf() {
+ const value k = mklist<value>("c");
+ TinyCDB cdb("tmp/test.cdb");
+ assert(hasContent(post(k, string("CCC"), cdb)));
+
+ const lambda<bool()> gl = getLoop(k, cdb);
+ cout << "TinyCDB get test " << time(gl, 5, 100000) << " ms" << endl;
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::tinycdb::testTinyCDB();
+ tuscany::tinycdb::testGetPerf();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/constdb/tinycdb.hpp b/sca-cpp/branches/lightweight-sca/components/constdb/tinycdb.hpp
new file mode 100644
index 0000000000..68be0a09dc
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/constdb/tinycdb.hpp
@@ -0,0 +1,488 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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_tinycdb_hpp
+#define tuscany_tinycdb_hpp
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <cdb.h>
+
+#include "string.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "../../modules/scheme/eval.hpp"
+
+namespace tuscany {
+namespace tinycdb {
+
+/**
+ * A reallocatable buffer.
+ */
+class buffer {
+public:
+ operator void*() const throw() {
+ return buf;
+ }
+
+ operator unsigned char*() const throw() {
+ return (unsigned char*)buf;
+ }
+
+ operator char*() const throw() {
+ return (char*)buf;
+ }
+
+private:
+ buffer(const unsigned int size, void* buf) : size(size), buf(buf) {
+ }
+
+ unsigned int size;
+ void* buf;
+
+ friend const buffer mkbuffer(const unsigned int sz);
+ friend const buffer mkbuffer(const buffer& b, const unsigned int newsz);
+ friend const bool free(const buffer& b);
+};
+
+/**
+ * Make a new buffer.
+ */
+const buffer mkbuffer(const unsigned int sz) {
+ return buffer(sz, malloc(sz));
+}
+
+/**
+ * Make a new buffer by reallocating an existing one.
+ */
+const buffer mkbuffer(const buffer& b, const unsigned int sz) {
+ if (sz <= b.size)
+ return b;
+ return buffer(sz, realloc(b.buf, sz));
+}
+
+/**
+ * Free a buffer.
+ */
+const bool free(const buffer&b) {
+ ::free(b.buf);
+ return true;
+}
+
+/**
+ * Convert a database name to an absolute path.
+ */
+const string absdbname(const string& name) {
+ if (length(name) == 0 || c_str(name)[0] == '/')
+ return name;
+ char cwd[512];
+ if (getcwd(cwd, sizeof(cwd)) == NULL)
+ return name;
+ return string(cwd) + "/" + name;
+}
+
+/**
+ * Represents a TinyCDB connection.
+ */
+class TinyCDB {
+public:
+ TinyCDB() : owner(false), fd(-1) {
+ st.st_ino = 0;
+ }
+
+ TinyCDB(const string& name) : owner(true), name(absdbname(name)), fd(-1) {
+ debug(name, "tinycdb::tinycdb::name");
+ st.st_ino = 0;
+ }
+
+ TinyCDB(const TinyCDB& c) : owner(false), name(c.name), fd(c.fd) {
+ debug("tinycdb::tinycdb::copy");
+ st.st_ino = c.st.st_ino;
+ }
+
+ const TinyCDB& operator=(const TinyCDB& c) {
+ debug("tinycdb::tinycdb::operator=");
+ if(this == &c)
+ return *this;
+ owner = false;
+ name = c.name;
+ fd = c.fd;
+ st.st_ino = c.st.st_ino;
+ return *this;
+ }
+
+ ~TinyCDB() {
+ if (!owner)
+ return;
+ if (fd == -1)
+ return;
+ close(fd);
+ }
+
+private:
+ bool owner;
+ string name;
+ int fd;
+ struct stat st;
+
+ friend const string dbname(const TinyCDB& cdb);
+ friend const failable<int> cdbopen(TinyCDB& cdb);
+ friend const failable<bool> cdbclose(TinyCDB& cdb);
+};
+
+/**
+ * Return the name of the database.
+ */
+const string dbname(const TinyCDB& cdb) {
+ return cdb.name;
+}
+
+/**
+ * Open a database.
+ */
+const failable<int> cdbopen(TinyCDB& cdb) {
+
+ // Get database file serial number
+ struct stat st;
+ const int s = stat(c_str(cdb.name), &st);
+ if (s == -1)
+ return mkfailure<int>(string("Couldn't tinycdb read database stat: ") + cdb.name);
+
+ // Open database for the first time
+ if (cdb.fd == -1) {
+ cdb.fd = open(c_str(cdb.name), O_RDONLY);
+ if (cdb.fd == -1)
+ return mkfailure<int>(string("Couldn't open tinycdb database file: ") + cdb.name);
+ debug(cdb.fd, "tinycdb::open::fd");
+ cdb.st = st;
+ return cdb.fd;
+ }
+
+ // Close and reopen database after a change
+ if (st.st_ino != cdb.st.st_ino) {
+
+ // Close current fd
+ close(cdb.fd);
+
+ // Reopen database
+ const int newfd = open(c_str(cdb.name), O_RDONLY);
+ if (newfd == -1)
+ return mkfailure<int>(string("Couldn't open tinycdb database file: ") + cdb.name);
+ if (newfd == cdb.fd) {
+ debug(cdb.fd, "tinycdb::open::fd");
+ cdb.st = st;
+ return cdb.fd;
+ }
+
+ // We got a different fd, dup it to the current fd then close it
+ if (fcntl(newfd, F_DUPFD, cdb.fd) == -1)
+ return mkfailure<int>(string("Couldn't dup tinycdb database file handle: ") + cdb.name);
+ close(newfd);
+
+ debug(cdb.fd, "tinycdb::open::fd");
+ cdb.st = st;
+ return cdb.fd;
+ }
+
+ // No change, just return the current fd
+ return cdb.fd;
+}
+
+/**
+ * Close a database.
+ */
+const failable<bool> cdbclose(TinyCDB& cdb) {
+ close(cdb.fd);
+ cdb.fd = -1;
+ return true;
+}
+
+/**
+ * Rewrite a database. The given update function is passed each entry, and
+ * can return true to let the entry added to the new db, false to skip the
+ * entry, or a failure.
+ */
+const failable<bool> rewrite(const lambda<failable<bool>(buffer& buf, const unsigned int klen, const unsigned int vlen)>& update, const lambda<failable<bool>(struct cdb_make&)>& finish, buffer& buf, const int tmpfd, TinyCDB& cdb) {
+
+ // Initialize new db structure
+ struct cdb_make cdbm;
+ cdb_make_start(&cdbm, tmpfd);
+
+ // Open existing db
+ failable<int> ffd = cdbopen(cdb);
+ if (!hasContent(ffd))
+ return mkfailure<bool>(ffd);
+ const int fd = content(ffd);
+
+ // Read the db header
+ unsigned int pos = 0;
+ if (lseek(fd, 0, SEEK_SET) != 0)
+ return mkfailure<bool>("Couldn't seek to tinycdb database start");
+ if (::read(fd, buf, 2048) != 2048)
+ return mkfailure<bool>("Couldn't read tinycdb database header");
+ pos += 2048;
+ unsigned int eod = cdb_unpack(buf);
+ debug(pos, "tinycdb::rewrite::eod");
+
+ // Read and add the existing entries
+ while(pos < eod) {
+ if (eod - pos < 8)
+ return mkfailure<bool>("Invalid tinycdb database format, couldn't read entry header");
+ if (::read(fd, buf, 8) != 8)
+ return mkfailure<bool>("Couldn't read tinycdb entry header");
+ pos += 8;
+ unsigned int klen = cdb_unpack(buf);
+ unsigned int vlen = cdb_unpack(((unsigned char*)buf) + 4);
+ unsigned int elen = klen + vlen;
+
+ // Read existing entry
+ buf = mkbuffer(buf, elen);
+ if (eod - pos < elen)
+ return mkfailure<bool>("Invalid tinycdb database format, couldn't read entry");
+ if ((unsigned int)::read(fd, buf, elen) != elen)
+ return mkfailure<bool>("Couldn't read tinycdb entry");
+ pos += elen;
+
+ // Apply the update function to the entry
+ debug(string((char*)buf, klen), "tinycdb::rewrite::existing key");
+ debug(string(((char*)buf) + klen, vlen), "tinycdb::rewrite::existing value");
+ const failable<bool> u = update(buf, klen, vlen);
+ if (!hasContent(u))
+ return u;
+
+ // Skip the entry if the update function returned false
+ if (u == false)
+ continue;
+
+ // Add the entry to the new db
+ if (cdb_make_add(&cdbm, buf, klen, ((unsigned char*)buf)+klen, vlen) == -1)
+ return mkfailure<bool>("Couldn'tt add tinycdb entry");
+ }
+ if (pos != eod)
+ return mkfailure<bool>("Invalid tinycdb database format");
+
+ // Call the finish function
+ const failable<bool> f = finish(cdbm);
+ if (!hasContent(f))
+ return f;
+
+ // Save the new db
+ if (cdb_make_finish(&cdbm) == -1)
+ return mkfailure<bool>("Couldn't save tinycdb database");
+
+ return true;
+}
+
+const failable<bool> rewrite(const lambda<failable<bool>(buffer& buf, const unsigned int klen, const unsigned int vlen)>& update, const lambda<failable<bool>(struct cdb_make&)>& finish, TinyCDB& cdb) {
+
+ // Create a new temporary db file
+ string tmpname = dbname(cdb) + ".XXXXXX";
+ int tmpfd = mkstemp(const_cast<char*>(c_str(tmpname)));
+ if (tmpfd == -1)
+ return mkfailure<bool>("Couldn't create temporary tinycdb database");
+
+ // Rewrite the db, apply the update function to each entry
+ buffer buf = mkbuffer(2048);
+ const failable<bool> r = rewrite(update, finish, buf, tmpfd, cdb);
+ if (!hasContent(r)) {
+ close(tmpfd);
+ free(buf);
+ return r;
+ }
+
+ // Atomically replace the db and reopen it in read mode
+ if (rename(c_str(tmpname), c_str(dbname(cdb))) == -1)
+ return mkfailure<bool>("Couldn't rename temporary tinycdb database");
+ cdbclose(cdb);
+ failable<int> ffd = cdbopen(cdb);
+ if (!hasContent(ffd))
+ return mkfailure<bool>(ffd);
+
+ return true;
+}
+
+/**
+ * Post a new item to the database.
+ */
+struct postUpdate {
+ const string ks;
+ postUpdate(const string& ks) : ks(ks) {
+ }
+ const failable<bool> operator()(buffer& buf, const unsigned int klen, unused const unsigned int vlen) const {
+ if (ks == string((char*)buf, klen))
+ return mkfailure<bool>("Key already exists in tinycdb database");
+ return true;
+ }
+};
+
+struct postFinish {
+ const string ks;
+ const string vs;
+ postFinish(const string& ks, const string& vs) : ks(ks), vs(vs) {
+ }
+ const failable<bool> operator()(struct cdb_make& cdbm) const {
+ if (cdb_make_add(&cdbm, c_str(ks), (unsigned int)length(ks), c_str(vs), (unsigned int)length(vs)) == -1)
+ return mkfailure<bool>(string("Couldn't add tinycdb entry: ") + ks);
+ return true;
+ }
+};
+
+const failable<bool> post(const value& key, const value& val, TinyCDB& cdb) {
+ debug(key, "tinycdb::post::key");
+ debug(val, "tinycdb::post::value");
+ debug(dbname(cdb), "tinycdb::post::dbname");
+
+ const string ks(scheme::writeValue(key));
+ const string vs(scheme::writeValue(val));
+
+ // Process each entry and detect existing key
+ const lambda<failable<bool>(buffer& buf, const unsigned int klen, const unsigned int vlen)> update = postUpdate(ks);
+
+ // Add the new entry to the db
+ const lambda<failable<bool>(struct cdb_make& cdbm)> finish = postFinish(ks, vs);
+
+ // Rewrite the db
+ const failable<bool> r = rewrite(update, finish, cdb);
+ debug(r, "tinycdb::post::result");
+ return r;
+}
+
+/**
+ * Update an item in the database. If the item doesn't exist it is added.
+ */
+struct putUpdate {
+ const string ks;
+ putUpdate(const string& ks) : ks(ks) {
+ }
+ const failable<bool> operator()(buffer& buf, const unsigned int klen, unused const unsigned int vlen) const {
+ if (ks == string((char*)buf, klen))
+ return false;
+ return true;
+ }
+};
+
+struct putFinish {
+ const string ks;
+ const string vs;
+ putFinish(const string& ks, const string& vs) : ks(ks), vs(vs) {
+ }
+ const failable<bool> operator()(struct cdb_make& cdbm) const {
+ if (cdb_make_add(&cdbm, c_str(ks), (unsigned int)length(ks), c_str(vs), (unsigned int)length(vs)) == -1)
+ return mkfailure<bool>(string("Couldn't add tinycdb entry: ") + ks);
+ return true;
+ }
+};
+
+const failable<bool> put(const value& key, const value& val, TinyCDB& cdb) {
+ debug(key, "tinycdb::put::key");
+ debug(val, "tinycdb::put::value");
+ debug(dbname(cdb), "tinycdb::put::dbname");
+
+ const string ks(scheme::writeValue(key));
+ const string vs(scheme::writeValue(val));
+
+ // Process each entry and skip existing key
+ const lambda<failable<bool>(buffer& buf, const unsigned int klen, const unsigned int vlen)> update = putUpdate(ks);
+
+ // Add the new entry to the db
+ const lambda<failable<bool>(struct cdb_make& cdbm)> finish = putFinish(ks, vs);
+
+ // Rewrite the db
+ const failable<bool> r = rewrite(update, finish, cdb);
+ debug(r, "tinycdb::put::result");
+ return r;
+}
+
+/**
+ * Get an item from the database.
+ */
+const failable<value> get(const value& key, TinyCDB& cdb) {
+ debug(key, "tinycdb::get::key");
+ debug(dbname(cdb), "tinycdb::get::dbname");
+
+ const failable<int> ffd = cdbopen(cdb);
+ if (!hasContent(ffd))
+ return mkfailure<value>(ffd);
+ const int fd = content(ffd);
+
+ const string ks(scheme::writeValue(key));
+
+ cdbi_t vlen;
+ if (cdb_seek(fd, c_str(ks), (unsigned int)length(ks), &vlen) <= 0) {
+ ostringstream os;
+ os << "Couldn't get tinycdb entry: " << key;
+ return mkfailure<value>(str(os), 404, false);
+ }
+ char* data = gc_cnew(vlen + 1);
+ cdb_bread(fd, data, vlen);
+ data[vlen] = '\0';
+ const value val(scheme::readValue(string(data)));
+
+ debug(val, "tinycdb::get::result");
+ return val;
+}
+
+/**
+ * Delete an item from the database
+ */
+struct delUpdate {
+ const string ks;
+ delUpdate(const string& ks) : ks(ks) {
+ }
+ const failable<bool> operator()(buffer& buf, const unsigned int klen, unused const unsigned int vlen) const {
+ if (ks == string((char*)buf, klen))
+ return false;
+ return true;
+ }
+};
+
+struct delFinish {
+ delFinish() {
+ }
+ const failable<bool> operator()(unused struct cdb_make& cdbm) const {
+ return true;
+ }
+};
+
+const failable<bool> del(const value& key, TinyCDB& cdb) {
+ debug(key, "tinycdb::delete::key");
+ debug(dbname(cdb), "tinycdb::delete::dbname");
+
+ const string ks(scheme::writeValue(key));
+
+ // Process each entry and skip existing key
+ const lambda<failable<bool>(buffer& buf, const unsigned int klen, const unsigned int vlen)> update = delUpdate(ks);
+
+ // Nothing to do to finish
+ const lambda<failable<bool>(struct cdb_make& cdbm)> finish = delFinish();
+
+ // Rewrite the db
+ const failable<bool> r = rewrite(update, finish, cdb);
+ debug(r, "tinycdb::delete::result");
+ return r;
+}
+
+}
+}
+
+#endif /* tuscany_tinycdb_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/components/filedb/Makefile.am b/sca-cpp/branches/lightweight-sca/components/filedb/Makefile.am
new file mode 100644
index 0000000000..c6589a1b7b
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/filedb/Makefile.am
@@ -0,0 +1,42 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+incl_HEADERS = *.hpp
+incldir = $(prefix)/include/components/filedb
+
+compdir=$(prefix)/components/filedb
+
+EXTRA_DIST = filedb.composite filedb.componentType
+
+comp_LTLIBRARIES = libfiledb.la
+noinst_DATA = libfiledb${libsuffix}
+
+libfiledb_la_SOURCES = filedb.cpp
+libfiledb_la_LDFLAGS = -lxml2 -lmozjs
+libfiledb${libsuffix}:
+ ln -s .libs/libfiledb${libsuffix}
+
+file_test_SOURCES = file-test.cpp
+file_test_LDFLAGS = -lxml2 -lmozjs
+
+client_test_SOURCES = client-test.cpp
+client_test_LDFLAGS = -lxml2 -lcurl -lmozjs
+
+dist_noinst_SCRIPTS = filedb-test server-test
+noinst_PROGRAMS = file-test client-test
+TESTS = filedb-test server-test
+
diff --git a/sca-cpp/branches/lightweight-sca/components/filedb/client-test.cpp b/sca-cpp/branches/lightweight-sca/components/filedb/client-test.cpp
new file mode 100644
index 0000000000..e0f98d8c3b
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/filedb/client-test.cpp
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 file database component.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "perf.hpp"
+#include "../../modules/http/http.hpp"
+
+namespace tuscany {
+namespace filedb {
+
+const string uri("http://localhost:8090/filedb");
+
+bool testFileDB() {
+ http::CURLSession cs("", "", "", "", 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> a = list<value>() + (list<value>() + "entry"
+ + (list<value>() + "title" + string("item"))
+ + (list<value>() + "id" + string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"))
+ + i);
+
+ const failable<value> id = http::post(a, uri, cs);
+ assert(hasContent(id));
+
+ const string p = path(content(id));
+ {
+ const failable<value> val = http::get(uri + p, cs);
+ assert(hasContent(val));
+ assert(content(val) == a);
+ }
+
+ const list<value> j = list<value>() + "content" + (list<value>() + "item"
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "price" + string("$3.55")));
+ const list<value> b = list<value>() + (list<value>() + "entry"
+ + (list<value>() + "title" + string("item"))
+ + (list<value>() + "id" + string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"))
+ + j);
+
+ {
+ const failable<value> r = http::put(b, uri + p, cs);
+ assert(hasContent(r));
+ assert(content(r) == value(true));
+ }
+ {
+ const failable<value> val = http::get(uri + p, cs);
+ assert(hasContent(val));
+ assert(content(val) == b);
+ }
+ {
+ const failable<value> r = http::del(uri + p, cs);
+ assert(hasContent(r));
+ assert(content(r) == value(true));
+ }
+ {
+ const failable<value> val = http::get(uri + p, cs);
+ assert(!hasContent(val));
+ }
+
+ return true;
+}
+
+struct getLoop {
+ const string path;
+ const value entry;
+ http::CURLSession& cs;
+ getLoop(const string& path, const value& entry, http::CURLSession& cs) : path(path), entry(entry), cs(cs) {
+ }
+ const bool operator()() const {
+ const failable<value> val = http::get(uri + path, cs);
+ assert(hasContent(val));
+ assert(content(val) == entry);
+ return true;
+ }
+};
+
+bool testGetPerf() {
+ const list<value> i = list<value>() + "content" + (list<value>() + "item"
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "price" + string("$4.55")));
+ 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 cs("", "", "", "", 0);
+ const failable<value> id = http::post(a, uri, cs);
+ assert(hasContent(id));
+ const string p = path(content(id));
+
+ const lambda<bool()> gl = getLoop(p, a, cs);
+ cout << "FileDB get test " << time(gl, 5, 200) << " ms" << endl;
+
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::filedb::testFileDB();
+ tuscany::filedb::testGetPerf();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/filedb/file-test.cpp b/sca-cpp/branches/lightweight-sca/components/filedb/file-test.cpp
new file mode 100644
index 0000000000..ff57bd79ce
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/filedb/file-test.cpp
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 FileDB access functions.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "perf.hpp"
+#include "filedb.hpp"
+
+namespace tuscany {
+namespace filedb {
+
+bool testFileDB(const string& dbname, const string& format) {
+ FileDB db(dbname, format);
+ const value k = mklist<value>("a", "b");
+
+ const list<value> a = mklist<value>(list<value>() + "ns1:a" + (list<value>() + "@xmlns:ns1" + string("http://aaa")) + (list<value>() + "text" + string("Hey!")));
+ const list<value> b = mklist<value>(list<value>() + "ns1:b" + (list<value>() + "@xmlns:ns1" + string("http://bbb")) + (list<value>() + "text" + string("Hey!")));
+
+ assert(hasContent(post(k, a, db)));
+ assert((get(k, db)) == value(a));
+ assert(hasContent(put(k, b, db)));
+ assert((get(k, db)) == value(b));
+ assert(hasContent(del(k, db)));
+ assert(!hasContent(get(k, db)));
+ assert(hasContent(post(k, a, db)));
+
+ return true;
+}
+
+struct getLoop {
+ const value k;
+ FileDB& db;
+ const list<value> c;
+ getLoop(const value& k, FileDB& db) : k(k), db(db),
+ c(mklist<value>(list<value>() + "ns1:c" + (list<value>() + "@xmlns:ns1" + string("http://ccc")) + (list<value>() + "text" + string("Hey!")))) {
+ }
+ const bool operator()() const {
+ assert((get(k, db)) == value(c));
+ return true;
+ }
+};
+
+bool testGetPerf(const string& dbname, const string& format) {
+ FileDB db(dbname, format);
+
+ const value k = mklist<value>("c");
+ const list<value> c = mklist<value>(list<value>() + "ns1:c" + (list<value>() + "@xmlns:ns1" + string("http://ccc")) + (list<value>() + "text" + string("Hey!")));
+ assert(hasContent(post(k, c, db)));
+
+ const lambda<bool()> gl = getLoop(k, db);
+ cout << "FileDB get test " << time(gl, 5, 5000) << " ms" << endl;
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::filedb::testFileDB("tmp/schemedb", "scheme");
+ tuscany::filedb::testGetPerf("tmp/schemedb", "scheme");
+ tuscany::filedb::testFileDB("tmp/xmldb", "xml");
+ tuscany::filedb::testGetPerf("tmp/xmldb", "xml");
+ tuscany::filedb::testFileDB("tmp/jsondb", "json");
+ tuscany::filedb::testGetPerf("tmp/jsondb", "json");
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/filedb/filedb-test b/sca-cpp/branches/lightweight-sca/components/filedb/filedb-test
new file mode 100755
index 0000000000..6d2d66424a
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/filedb/filedb-test
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Setup
+rm -rf tmp
+mkdir -p tmp
+mkdir -p tmp/schemedb
+mkdir -p tmp/xmldb
+mkdir -p tmp/jsondb
+
+# Test
+./file-test 2>/dev/null
+rc=$?
+
+# Cleanup
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/components/filedb/filedb.componentType b/sca-cpp/branches/lightweight-sca/components/filedb/filedb.componentType
new file mode 100644
index 0000000000..31f996ef3e
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/filedb/filedb.componentType
@@ -0,0 +1,28 @@
+<?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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <service name="filedb"/>
+ <property name="dbname" type="xsd:string"/>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/filedb/filedb.composite b/sca-cpp/branches/lightweight-sca/components/filedb/filedb.composite
new file mode 100644
index 0000000000..e6cc69b9db
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/filedb/filedb.composite
@@ -0,0 +1,33 @@
+<?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://tuscany.apache.org/xmlns/sca/components"
+ name="filedb">
+
+ <component name="filedb">
+ <implementation.cpp path="." library="libfiledb"/>
+ <property name="dbname">tmp/testdb</property>
+ <property name="format">scheme</property>
+ <service name="filedb">
+ <binding.http uri="filedb"/>
+ </service>
+ </component>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/filedb/filedb.cpp b/sca-cpp/branches/lightweight-sca/components/filedb/filedb.cpp
new file mode 100644
index 0000000000..8644a78574
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/filedb/filedb.cpp
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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$ */
+
+/**
+ * File based database component implementation.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "filedb.hpp"
+
+namespace tuscany {
+namespace filedb {
+
+/**
+ * Get an item from the database.
+ */
+const failable<value> get(const list<value>& params, filedb::FileDB& db) {
+ return filedb::get(car(params), db);
+}
+
+/**
+ * Post an item to the database.
+ */
+const failable<value> post(const list<value>& params, filedb::FileDB& db) {
+ const value id = append<value>(car(params), mklist(mkuuid()));
+ const failable<bool> val = filedb::post(id, cadr(params), db);
+ if (!hasContent(val))
+ return mkfailure<value>(val);
+ return id;
+}
+
+/**
+ * Put an item into the database.
+ */
+const failable<value> put(const list<value>& params, filedb::FileDB& db) {
+ const failable<bool> val = filedb::put(car(params), cadr(params), db);
+ if (!hasContent(val))
+ return mkfailure<value>(val);
+ return value(content(val));
+}
+
+/**
+ * Delete an item from the database.
+ */
+const failable<value> del(const list<value>& params, filedb::FileDB& db) {
+ const failable<bool> val = filedb::del(car(params), db);
+ if (!hasContent(val))
+ return mkfailure<value>(val);
+ return value(content(val));
+}
+
+/**
+ * Component implementation lambda function.
+ */
+class applyfiledb {
+public:
+ applyfiledb(filedb::FileDB& db) : db(db) {
+ }
+
+ const value operator()(const list<value>& params) const {
+ const value func(car(params));
+ if (func == "get")
+ return get(cdr(params), db);
+ if (func == "post")
+ return post(cdr(params), db);
+ if (func == "put")
+ return put(cdr(params), db);
+ if (func == "delete")
+ return del(cdr(params), db);
+ return mkfailure<value>();
+ }
+
+private:
+ filedb::FileDB& db;
+};
+
+/**
+ * Start the component.
+ */
+const failable<value> start(const list<value>& params) {
+ // Connect to the configured database and table
+ const value dbname = ((lambda<value(const list<value>&)>)car(params))(list<value>());
+ const value format = ((lambda<value(const list<value>&)>)cadr(params))(list<value>());
+
+ filedb::FileDB& db = *(new (gc_new<filedb::FileDB>()) filedb::FileDB(absdbname(dbname), format));
+
+ // Return the component implementation lambda function
+ return value(lambda<value(const list<value>&)>(applyfiledb(db)));
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "start")
+ return tuscany::filedb::start(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/filedb/filedb.hpp b/sca-cpp/branches/lightweight-sca/components/filedb/filedb.hpp
new file mode 100644
index 0000000000..9c3017d0d8
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/filedb/filedb.hpp
@@ -0,0 +1,265 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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_filedb_hpp
+#define tuscany_filedb_hpp
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "string.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "fstream.hpp"
+#include "element.hpp"
+#include "xml.hpp"
+#include "../../modules/scheme/eval.hpp"
+#include "../../modules/json/json.hpp"
+
+namespace tuscany {
+namespace filedb {
+
+/**
+ * Convert a database name to an absolute path.
+ */
+const string absdbname(const string& name) {
+ if (length(name) == 0 || c_str(name)[0] == '/')
+ return name;
+ char cwd[512];
+ if (getcwd(cwd, sizeof(cwd)) == NULL)
+ return name;
+ return string(cwd) + "/" + name;
+}
+
+/**
+ * Represents a FileDB connection.
+ */
+class FileDB {
+public:
+ FileDB() : owner(false) {
+ }
+
+ FileDB(const string& name, const string& format) : owner(true), name(absdbname(name)), format(format) {
+ debug(name, "filedb::filedb::name");
+ debug(format, "filedb::filedb::format");
+ }
+
+ FileDB(const FileDB& c) : owner(false), name(c.name), format(c.format) {
+ debug("filedb::filedb::copy");
+ }
+
+ const FileDB& operator=(const FileDB& c) {
+ debug("filedb::filedb::operator=");
+ if(this == &c)
+ return *this;
+ owner = false;
+ name = c.name;
+ format = c.format;
+ return *this;
+ }
+
+ ~FileDB() {
+ }
+
+private:
+ bool owner;
+ string name;
+ string format;
+
+ friend const failable<bool> write(const value& v, ostream& os, const string& format);
+ friend const failable<value> read(istream& is, const string& format);
+ friend const failable<bool> post(const value& key, const value& val, FileDB& db);
+ friend const failable<bool> put(const value& key, const value& val, FileDB& db);
+ friend const failable<value> get(const value& key, FileDB& db);
+ friend const failable<bool> del(const value& key, FileDB& db);
+};
+
+/**
+ * Convert a key to a file name.
+ */
+const string filename(const list<value>& path, const string& root) {
+ if (isNil(path))
+ return root;
+ const string name = root + "/" + (isString(car(path))? (string)car(path) : scheme::writeValue(car(path)));
+ return filename(cdr(path), name);
+}
+
+const string filename(const value& key, const string& root) {
+ if (!isList(key))
+ return filename(mklist(key), root);
+ return filename((list<value>)key, root);
+}
+
+/**
+ * Make the parent directories of a keyed file.
+ */
+const failable<bool> mkdirs(const list<value>& path, const string& root) {
+ if (isNil(cdr(path)))
+ return true;
+ const string dir = root + "/" + (isString(car(path))? (string)car(path) : scheme::writeValue(car(path)));
+ mkdir(c_str(dir), S_IRWXU);
+ return mkdirs(cdr(path), dir);
+}
+
+/**
+ * Write a value to a database file.
+ */
+const failable<bool> write(const value& v, ostream& os, const string& format) {
+ if (format == "scheme") {
+ const string vs(scheme::writeValue(v));
+ os << vs;
+ return true;
+ }
+ if (format == "xml") {
+ failable<list<string> > s = writeXML(valuesToElements(v));
+ if (!hasContent(s))
+ return mkfailure<bool>(s);
+ write(content(s), os);
+ return true;
+ }
+ if (format == "json") {
+ js::JSContext jscx;
+ failable<list<string> > s = json::writeJSON(valuesToElements(v), jscx);
+ if (!hasContent(s))
+ return mkfailure<bool>(s);
+ write(content(s), os);
+ return true;
+ }
+ return mkfailure<bool>(string("Unsupported database format: ") + format);
+}
+
+/**
+ * Read a value from a database file.
+ */
+const failable<value> read(istream& is, const string& format) {
+ if (format == "scheme") {
+ return scheme::readValue(is);
+ }
+ if (format == "xml") {
+ const value v = elementsToValues(readXML(streamList(is)));
+ return v;
+ }
+ if (format == "json") {
+ js::JSContext jscx;
+ const failable<list<value> > fv = json::readJSON(streamList(is), jscx);
+ if (!hasContent(fv))
+ return mkfailure<value>(fv);
+ const value v = elementsToValues(content(fv));
+ return v;
+ }
+ return mkfailure<value>(string("Unsupported database format: ") + format);
+}
+
+/**
+ * Post a new item to the database.
+ */
+const failable<bool> post(const value& key, const value& val, FileDB& db) {
+ debug(key, "filedb::post::key");
+ debug(val, "filedb::post::value");
+ debug(db.name, "filedb::post::dbname");
+
+ if (isList(key))
+ mkdirs(key, db.name);
+ const string fn = filename(key, db.name);
+ debug(fn, "filedb::post::filename");
+ ofstream os(fn);
+ if (os.fail()) {
+ ostringstream os;
+ os << "Couldn't post file database entry: " << key;
+ return mkfailure<bool>(str(os));
+ }
+ const failable<bool> r = write(val, os, db.format);
+
+ debug(r, "filedb::post::result");
+ return r;
+}
+
+/**
+ * Update an item in the database. If the item doesn't exist it is added.
+ */
+const failable<bool> put(const value& key, const value& val, FileDB& db) {
+ debug(key, "filedb::put::key");
+ debug(val, "filedb::put::value");
+ debug(db.name, "filedb::put::dbname");
+
+ if (isList(key))
+ mkdirs(key, db.name);
+ const string fn = filename(key, db.name);
+ debug(fn, "filedb::put::filename");
+ ofstream os(fn);
+ if (os.fail()) {
+ ostringstream os;
+ os << "Couldn't put file database entry: " << key;
+ return mkfailure<bool>(str(os));
+ }
+ const failable<bool> r = write(val, os, db.format);
+
+ debug(r, "filedb::put::result");
+ return r;
+}
+
+/**
+ * Get an item from the database.
+ */
+const failable<value> get(const value& key, FileDB& db) {
+ debug(key, "filedb::get::key");
+ debug(db.name, "filedb::get::dbname");
+
+ const string fn = filename(key, db.name);
+ debug(fn, "filedb::get::filename");
+ ifstream is(fn);
+ if (is.fail()) {
+ ostringstream os;
+ os << "Couldn't get file database entry: " << key;
+ return mkfailure<value>(str(os), 404, false);
+ }
+ const failable<value> val = read(is, db.format);
+
+ debug(val, "filedb::get::result");
+ return val;
+}
+
+/**
+ * Delete an item from the database
+ */
+const failable<bool> del(const value& key, FileDB& db) {
+ debug(key, "filedb::delete::key");
+ debug(db.name, "filedb::delete::dbname");
+
+ const string fn = filename(key, db.name);
+ debug(fn, "filedb::del::filename");
+ const int rc = unlink(c_str(fn));
+ if (rc == -1) {
+ ostringstream os;
+ os << "Couldn't delete file database entry: " << key;
+ return mkfailure<bool>(str(os));
+ }
+
+ debug(true, "filedb::delete::result");
+ return true;
+}
+
+}
+}
+
+#endif /* tuscany_filedb_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/components/filedb/server-test b/sca-cpp/branches/lightweight-sca/components/filedb/server-test
new file mode 100755
index 0000000000..94afe464e1
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/filedb/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
+../../modules/http/httpd-conf tmp localhost 8090 ../../modules/http/htdocs
+../../modules/http/httpd-event-conf tmp
+../../modules/server/server-conf tmp
+../../modules/server/scheme-conf tmp
+cat >>tmp/conf/httpd.conf <<EOF
+SCAContribution `pwd`/
+SCAComposite filedb.composite
+EOF
+
+mkdir -p tmp/testdb
+../../modules/http/httpd-start tmp
+sleep 2
+
+# Test
+./client-test 2>/dev/null
+rc=$?
+
+# Cleanup
+../../modules/http/httpd-stop tmp
+sleep 2
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/components/http/Makefile.am b/sca-cpp/branches/lightweight-sca/components/http/Makefile.am
new file mode 100644
index 0000000000..623be12298
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/http/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.
+
+compdir=$(prefix)/components/http
+
+EXTRA_DIST = http.composite httpget.componentType httppost.componentType httpput.componentType httppatch.componentType httpdelete.componentType *.scm
+
+comp_LTLIBRARIES = libhttpget.la libhttpdelete.la libhttppost.la libhttpput.la libhttppatch.la
+noinst_DATA = libhttpget${libsuffix} libhttpdelete${libsuffix} libhttppost${libsuffix} libhttpput${libsuffix} libhttppatch${libsuffix}
+
+libhttpget_la_SOURCES = httpget.cpp
+libhttpget_la_LDFLAGS = -lxml2 -lmozjs -curl
+libhttpget${libsuffix}:
+ ln -s .libs/libhttpget${libsuffix}
+
+libhttpdelete_la_SOURCES = httpdelete.cpp
+libhttpdelete_la_LDFLAGS = -lxml2 -lmozjs -curl
+libhttpdelete${libsuffix}:
+ ln -s .libs/libhttpdelete${libsuffix}
+
+libhttppost_la_SOURCES = httppost.cpp
+libhttppost_la_LDFLAGS = -lxml2 -lmozjs -curl
+libhttppost${libsuffix}:
+ ln -s .libs/libhttppost${libsuffix}
+
+libhttpput_la_SOURCES = httpput.cpp
+libhttpput_la_LDFLAGS = -lxml2 -lmozjs -curl
+libhttpput${libsuffix}:
+ ln -s .libs/libhttpput${libsuffix}
+
+libhttppatch_la_SOURCES = httppatch.cpp
+libhttppatch_la_LDFLAGS = -lxml2 -lmozjs -curl
+libhttppatch${libsuffix}:
+ ln -s .libs/libhttppatch${libsuffix}
+
+client_test_SOURCES = client-test.cpp
+client_test_LDFLAGS = -lxml2 -lcurl -lmozjs
+
+dist_noinst_SCRIPTS = server-test
+noinst_PROGRAMS = client-test
+TESTS = server-test
+
diff --git a/sca-cpp/branches/lightweight-sca/components/http/client-test.cpp b/sca-cpp/branches/lightweight-sca/components/http/client-test.cpp
new file mode 100644
index 0000000000..bb1918f1f8
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/http/client-test.cpp
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 components.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "perf.hpp"
+#include "../../modules/http/http.hpp"
+
+namespace tuscany {
+namespace http {
+
+const string getURI("http://localhost:8090/httpget");
+const string postURI("http://localhost:8090/httppost");
+const string putURI("http://localhost:8090/httpput");
+const string deleteURI("http://localhost:8090/httpdelete");
+
+bool testGet() {
+ http::CURLSession cs("", "", "", "", 0);
+
+ const failable<value> val = http::get(getURI, cs);
+ assert(hasContent(val));
+ return true;
+}
+
+struct getLoop {
+ http::CURLSession& cs;
+ getLoop(http::CURLSession& cs) : cs(cs) {
+ }
+ const bool operator()() const {
+ const failable<value> val = http::get(getURI, cs);
+ assert(hasContent(val));
+ return true;
+ }
+};
+
+bool testGetPerf() {
+ http::CURLSession cs("", "", "", "", 0);
+
+ const lambda<bool()> gl = getLoop(cs);
+ cout << "HTTP get test " << time(gl, 5, 200) << " ms" << endl;
+
+ return true;
+}
+
+bool testPost() {
+ http::CURLSession cs("", "", "", "", 0);
+
+ const failable<value> val = http::get(postURI, cs);
+ assert(hasContent(val));
+ return true;
+}
+
+bool testPut() {
+ http::CURLSession cs("", "", "", "", 0);
+
+ const failable<value> val = http::get(putURI, cs);
+ assert(hasContent(val));
+ return true;
+}
+
+bool testDelete() {
+ http::CURLSession cs("", "", "", "", 0);
+
+ const failable<value> val = http::get(deleteURI, cs);
+ assert(hasContent(val));
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::http::testGet();
+ tuscany::http::testGetPerf();
+ tuscany::http::testPost();
+ tuscany::http::testPut();
+ tuscany::http::testDelete();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/http/content-test.scm b/sca-cpp/branches/lightweight-sca/components/http/content-test.scm
new file mode 100644
index 0000000000..f381546190
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/http/content-test.scm
@@ -0,0 +1,23 @@
+; Licensed to the Apache Software Foundation (ASF) under one
+; or more contributor license agreements. See the NOTICE file
+; distributed with this work for additional information
+; regarding copyright ownership. The ASF licenses this file
+; to you under the Apache License, Version 2.0 (the
+; "License"); you may not use this file except in compliance
+; with the License. You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing,
+; software distributed under the License is distributed on an
+; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+; KIND, either express or implied. See the License for the
+; specific language governing permissions and limitations
+; under the License.
+
+; Content test case
+
+(define (get id)
+ (list (list 'entry '(title "Item") '(id "111") '(content (item (name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99)))))
+)
+
diff --git a/sca-cpp/branches/lightweight-sca/components/http/http.composite b/sca-cpp/branches/lightweight-sca/components/http/http.composite
new file mode 100644
index 0000000000..32c0da62c0
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/http/http.composite
@@ -0,0 +1,93 @@
+<?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://tuscany.apache.org/xmlns/sca/components"
+ name="http">
+
+ <component name="httpget">
+ <implementation.cpp path="." library="libhttpget"/>
+ <property name="timeout">0</property>
+ <service name="httpget">
+ <binding.http uri="httpget"/>
+ </service>
+ <reference name="url" target="url-test"/>
+ </component>
+
+ <component name="httppost">
+ <implementation.cpp path="." library="libhttppost"/>
+ <property name="timeout">0</property>
+ <service name="httppost">
+ <binding.http uri="httppost"/>
+ </service>
+ <reference name="url" target="url-test"/>
+ <reference name="content" target="content-test"/>
+ </component>
+
+ <component name="httpput">
+ <implementation.cpp path="." library="libhttpput"/>
+ <property name="timeout">0</property>
+ <service name="httpput">
+ <binding.http uri="httpput"/>
+ </service>
+ <reference name="url" target="url-test"/>
+ <reference name="content" target="content-test"/>
+ </component>
+
+ <component name="httppatch">
+ <implementation.cpp path="." library="libhttppatch"/>
+ <property name="timeout">0</property>
+ <service name="httppatch">
+ <binding.http uri="httppatch"/>
+ </service>
+ <reference name="url" target="url-test"/>
+ <reference name="content" target="content-test"/>
+ </component>
+
+ <component name="httpdelete">
+ <implementation.cpp path="." library="libhttpdelete"/>
+ <property name="timeout">0</property>
+ <service name="httpdelete">
+ <binding.http uri="httpdelete"/>
+ </service>
+ <reference name="url" target="url-test"/>
+ </component>
+
+ <component name="url-test">
+ <implementation.scheme script="url-test.scm"/>
+ <service name="url-test">
+ <binding.http uri="url-test"/>
+ </service>
+ </component>
+
+ <component name="content-test">
+ <implementation.scheme script="content-test.scm"/>
+ <service name="content-test">
+ <binding.http uri="content-test"/>
+ </service>
+ </component>
+
+ <component name="scheme-test">
+ <implementation.scheme script="server-test.scm"/>
+ <service name="scheme">
+ <binding.http uri="scheme"/>
+ </service>
+ </component>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/http/httpdelete.componentType b/sca-cpp/branches/lightweight-sca/components/http/httpdelete.componentType
new file mode 100644
index 0000000000..c2d728a538
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/http/httpdelete.componentType
@@ -0,0 +1,29 @@
+<?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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <property name="timeout"/>
+ <service name="httpdelete"/>
+ <reference name="url"/>
+
+</componentType>
diff --git a/sca-cpp/branches/lightweight-sca/components/http/httpdelete.cpp b/sca-cpp/branches/lightweight-sca/components/http/httpdelete.cpp
new file mode 100644
index 0000000000..c725461ec2
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/http/httpdelete.cpp
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 client component implementation.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "../../modules/http/http.hpp"
+
+namespace tuscany {
+namespace httpdelete {
+
+/**
+ * Evaluate an HTTP delete.
+ */
+const failable<value> get(const lambda<value(const list<value>&)>& url, http::CURLSession& ch) {
+ debug("httpdelete::get");
+ const value u = url(mklist<value>("get", list<value>()));
+ debug(u, "httpdelete::get::url");
+ return http::del(u, ch);
+}
+
+/**
+ * Component implementation lambda function.
+ */
+class applyhttp {
+public:
+ applyhttp(const lambda<value(const list<value>&)>& url, const perthread_ptr<http::CURLSession>& ch) : url(url), ch(ch) {
+ }
+
+ const value operator()(const list<value>& params) const {
+ debug(params, "httpdelete::applyhttp::params");
+ const value func(car(params));
+ if (func == "get")
+ return get(url, *ch);
+ return mkfailure<value>();
+ }
+
+private:
+ const lambda<value(const list<value>&)> url;
+ perthread_ptr<http::CURLSession> ch;
+};
+
+/**
+ * Create a new CURL session.
+ */
+class newsession {
+public:
+ newsession(const lambda<value(const list<value>&)>& timeout) : timeout(timeout) {
+ }
+ const gc_ptr<http::CURLSession> operator()() const {
+ const int t = atoi(c_str((string)timeout(list<value>())));
+ return new (gc_new<http::CURLSession>()) http::CURLSession("", "", "", "", t);
+ }
+private:
+ const lambda<value(const list<value>&)> timeout;
+};
+
+/**
+ * Start the component.
+ */
+const failable<value> start(const list<value>& params) {
+ // Create a CURL session
+ const perthread_ptr<http::CURLSession> ch = perthread_ptr<http::CURLSession>(lambda<gc_ptr<http::CURLSession>()>(newsession(cadr(params))));
+
+ // Return the component implementation lambda function
+ return value(lambda<value(const list<value>&)>(applyhttp(car(params), ch)));
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "start")
+ return tuscany::httpdelete::start(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/http/httpget.componentType b/sca-cpp/branches/lightweight-sca/components/http/httpget.componentType
new file mode 100644
index 0000000000..c6c24fbed8
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/http/httpget.componentType
@@ -0,0 +1,29 @@
+<?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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <property name="timeout"/>
+ <service name="httpget"/>
+ <reference name="url"/>
+
+</componentType>
diff --git a/sca-cpp/branches/lightweight-sca/components/http/httpget.cpp b/sca-cpp/branches/lightweight-sca/components/http/httpget.cpp
new file mode 100644
index 0000000000..884dc1a6ff
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/http/httpget.cpp
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 client component implementation.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "parallel.hpp"
+#include "../../modules/http/http.hpp"
+
+namespace tuscany {
+namespace httpget {
+
+/**
+ * Evaluate an HTTP get.
+ */
+const failable<value> get(const lambda<value(const list<value>&)>& url, http::CURLSession& ch) {
+ debug("httpget::get");
+ const value u = url(mklist<value>("get", list<value>()));
+ debug(u, "httpget::get::url");
+ return http::get(u, ch);
+}
+
+/**
+ * Component implementation lambda function.
+ */
+class applyhttp {
+public:
+ applyhttp(const lambda<value(const list<value>&)>& url, const perthread_ptr<http::CURLSession>& ch) : url(url), ch(ch) {
+ }
+
+ const value operator()(const list<value>& params) const {
+ debug(params, "httpget::applyhttp::params");
+ const value func(car(params));
+ if (func == "get")
+ return get(url, *ch);
+ return mkfailure<value>();
+ }
+
+private:
+ const lambda<value(const list<value>&)> url;
+ perthread_ptr<http::CURLSession> ch;
+};
+
+
+/**
+ * Create a new CURL session.
+ */
+class newsession {
+public:
+ newsession(const lambda<value(const list<value>&)>& timeout) : timeout(timeout) {
+ }
+ const gc_ptr<http::CURLSession> operator()() const {
+ const int t = atoi(c_str((string)timeout(list<value>())));
+ return new (gc_new<http::CURLSession>()) http::CURLSession("", "", "", "", t);
+ }
+private:
+ const lambda<value(const list<value>&)> timeout;
+};
+
+/**
+ * Start the component.
+ */
+const failable<value> start(const list<value>& params) {
+ // Create a CURL session
+ const perthread_ptr<http::CURLSession> ch = perthread_ptr<http::CURLSession>(lambda<gc_ptr<http::CURLSession>()>(newsession(cadr(params))));
+
+ // Return the component implementation lambda function
+ return value(lambda<value(const list<value>&)>(applyhttp(car(params), ch)));
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "start")
+ return tuscany::httpget::start(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/http/httppatch.componentType b/sca-cpp/branches/lightweight-sca/components/http/httppatch.componentType
new file mode 100644
index 0000000000..eb3a96078b
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/http/httppatch.componentType
@@ -0,0 +1,30 @@
+<?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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <property name="timeout"/>
+ <service name="httppatch"/>
+ <reference name="url"/>
+ <reference name="content"/>
+
+</componentType>
diff --git a/sca-cpp/branches/lightweight-sca/components/http/httppatch.cpp b/sca-cpp/branches/lightweight-sca/components/http/httppatch.cpp
new file mode 100644
index 0000000000..051b1e09ac
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/http/httppatch.cpp
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 client component implementation.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "parallel.hpp"
+#include "../../modules/http/http.hpp"
+
+namespace tuscany {
+namespace httppatch {
+
+/**
+ * Evaluate an HTTP patch.
+ */
+const failable<value> get(const lambda<value(const list<value>&)>& url, const lambda<value(const list<value>&)>& val, http::CURLSession& ch) {
+ debug("httppatch::get");
+ const value u = url(mklist<value>("get", list<value>()));
+ const value v = val(mklist<value>("get", list<value>()));
+ debug(u, "httppatch::get::url");
+ debug(v, "httppatch::get::val");
+ return http::patch(v, u, ch);
+}
+
+/**
+ * Component implementation lambda function.
+ */
+class applyhttp {
+public:
+ applyhttp(const lambda<value(const list<value>&)>& url, const lambda<value(const list<value>&)>& val, const perthread_ptr<http::CURLSession>& ch) : url(url), val(val), ch(ch) {
+ }
+
+ const value operator()(const list<value>& params) const {
+ debug(params, "httppatch::applyhttp::params");
+ const value func(car(params));
+ if (func == "get")
+ return get(url, val, *ch);
+ return mkfailure<value>();
+ }
+
+private:
+ const lambda<value(const list<value>&)> url;
+ const lambda<value(const list<value>&)> val;
+ perthread_ptr<http::CURLSession> ch;
+};
+
+/**
+ * Create a new CURL session.
+ */
+class newsession {
+public:
+ newsession(const lambda<value(const list<value>&)>& timeout) : timeout(timeout) {
+ }
+ const gc_ptr<http::CURLSession> operator()() const {
+ const int t = atoi(c_str((string)timeout(list<value>())));
+ return new (gc_new<http::CURLSession>()) http::CURLSession("", "", "", "", t);
+ }
+private:
+ const lambda<value(const list<value>&)> timeout;
+};
+
+/**
+ * Start the component.
+ */
+const failable<value> start(const list<value>& params) {
+ // Create a CURL session
+ const perthread_ptr<http::CURLSession> ch = perthread_ptr<http::CURLSession>(lambda<gc_ptr<http::CURLSession>()>(newsession(caddr(params))));
+
+ // Return the component implementation lambda function
+ return value(lambda<value(const list<value>&)>(applyhttp(car(params), cadr(params), ch)));
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "start")
+ return tuscany::httppatch::start(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/http/httppost.componentType b/sca-cpp/branches/lightweight-sca/components/http/httppost.componentType
new file mode 100644
index 0000000000..42b0096446
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/http/httppost.componentType
@@ -0,0 +1,30 @@
+<?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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <property name="timeout"/>
+ <service name="httppost"/>
+ <reference name="url"/>
+ <reference name="content"/>
+
+</componentType>
diff --git a/sca-cpp/branches/lightweight-sca/components/http/httppost.cpp b/sca-cpp/branches/lightweight-sca/components/http/httppost.cpp
new file mode 100644
index 0000000000..84fd984e19
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/http/httppost.cpp
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 client component implementation.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "parallel.hpp"
+#include "../../modules/http/http.hpp"
+
+namespace tuscany {
+namespace httppost {
+
+/**
+ * Evaluate an HTTP post.
+ */
+const failable<value> get(const lambda<value(const list<value>&)>& url, const lambda<value(const list<value>&)>& val, http::CURLSession& ch) {
+ debug("httppost::get");
+ const value u = url(mklist<value>("get", list<value>()));
+ const value v = val(mklist<value>("get", list<value>()));
+ debug(u, "httppost::get::url");
+ debug(v, "httppost::get::val");
+ return http::post(v, u, ch);
+}
+
+/**
+ * Component implementation lambda function.
+ */
+class applyhttp {
+public:
+ applyhttp(const lambda<value(const list<value>&)>& url, const lambda<value(const list<value>&)>& val, const perthread_ptr<http::CURLSession>& ch) : url(url), val(val), ch(ch) {
+ }
+
+ const value operator()(const list<value>& params) const {
+ debug(params, "httppost::applyhttp::params");
+ const value func(car(params));
+ if (func == "get")
+ return get(url, val, *ch);
+ return mkfailure<value>();
+ }
+
+private:
+ const lambda<value(const list<value>&)> url;
+ const lambda<value(const list<value>&)> val;
+ perthread_ptr<http::CURLSession> ch;
+};
+
+/**
+ * Create a new CURL session.
+ */
+class newsession {
+public:
+ newsession(const lambda<value(const list<value>&)>& timeout) : timeout(timeout) {
+ }
+ const gc_ptr<http::CURLSession> operator()() const {
+ const int t = atoi(c_str((string)timeout(list<value>())));
+ return new (gc_new<http::CURLSession>()) http::CURLSession("", "", "", "", t);
+ }
+private:
+ const lambda<const value(const list<value>&)> timeout;
+};
+
+/**
+ * Start the component.
+ */
+const failable<value> start(const list<value>& params) {
+ // Create a CURL session
+ const perthread_ptr<http::CURLSession> ch = perthread_ptr<http::CURLSession>(lambda<gc_ptr<http::CURLSession>()>(newsession(caddr(params))));
+
+ // Return the component implementation lambda function
+ return value(lambda<value(const list<value>&)>(applyhttp(car(params), cadr(params), ch)));
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "start")
+ return tuscany::httppost::start(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/http/httpput.componentType b/sca-cpp/branches/lightweight-sca/components/http/httpput.componentType
new file mode 100644
index 0000000000..791a7faa8a
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/http/httpput.componentType
@@ -0,0 +1,30 @@
+<?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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <property name="timeout"/>
+ <service name="httpput"/>
+ <reference name="url"/>
+ <reference name="content"/>
+
+</componentType>
diff --git a/sca-cpp/branches/lightweight-sca/components/http/httpput.cpp b/sca-cpp/branches/lightweight-sca/components/http/httpput.cpp
new file mode 100644
index 0000000000..2ae5da396e
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/http/httpput.cpp
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 client component implementation.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "parallel.hpp"
+#include "../../modules/http/http.hpp"
+
+namespace tuscany {
+namespace httpput {
+
+/**
+ * Evaluate an HTTP put.
+ */
+const failable<value> get(const lambda<value(const list<value>&)>& url, const lambda<value(const list<value>&)>& val, http::CURLSession& ch) {
+ debug("httpput::get");
+ const value u = url(mklist<value>("get", list<value>()));
+ const value v = val(mklist<value>("get", list<value>()));
+ debug(u, "httpput::get::url");
+ debug(v, "httpput::get::val");
+ return http::put(v, u, ch);
+}
+
+/**
+ * Component implementation lambda function.
+ */
+class applyhttp {
+public:
+ applyhttp(const lambda<value(const list<value>&)>& url, const lambda<value(const list<value>&)>& val, const perthread_ptr<http::CURLSession>& ch) : url(url), val(val), ch(ch) {
+ }
+
+ const value operator()(const list<value>& params) const {
+ debug(params, "httpput::applyhttp::params");
+ const value func(car(params));
+ if (func == "get")
+ return get(url, val, *ch);
+ return mkfailure<value>();
+ }
+
+private:
+ const lambda<value(const list<value>&)> url;
+ const lambda<value(const list<value>&)> val;
+ perthread_ptr<http::CURLSession> ch;
+};
+
+/**
+ * Create a new CURL session.
+ */
+class newsession {
+public:
+ newsession(const lambda<value(const list<value>&)>& timeout) : timeout(timeout) {
+ }
+ const gc_ptr<http::CURLSession> operator()() const {
+ const int t = atoi(c_str((string)timeout(list<value>())));
+ return new (gc_new<http::CURLSession>()) http::CURLSession("", "", "", "", t);
+ }
+private:
+ const lambda<value(const list<value>&)> timeout;
+};
+
+/**
+ * Start the component.
+ */
+const failable<value> start(const list<value>& params) {
+ // Create a CURL session
+ const perthread_ptr<http::CURLSession> ch = perthread_ptr<http::CURLSession>(lambda<gc_ptr<http::CURLSession>()>(newsession(caddr(params))));
+
+ // Return the component implementation lambda function
+ return value(lambda<value(const list<value>&)>(applyhttp(car(params), cadr(params), ch)));
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "start")
+ return tuscany::httpput::start(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/http/server-test b/sca-cpp/branches/lightweight-sca/components/http/server-test
new file mode 100755
index 0000000000..7e3116c59d
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/http/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
+../../modules/http/httpd-conf tmp localhost 8090 ../../modules/http/htdocs
+../../modules/server/server-conf tmp
+../../modules/server/scheme-conf tmp
+cat >>tmp/conf/httpd.conf <<EOF
+SCAContribution `pwd`/
+SCAComposite http.composite
+EOF
+
+../../modules/http/httpd-start tmp
+sleep 2
+
+# Test
+./client-test 2>/dev/null
+rc=$?
+
+# Cleanup
+../../modules/http/httpd-stop tmp
+sleep 2
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/components/http/server-test.scm b/sca-cpp/branches/lightweight-sca/components/http/server-test.scm
new file mode 100644
index 0000000000..4bbff6e5c2
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/http/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/components/http/url-test.scm b/sca-cpp/branches/lightweight-sca/components/http/url-test.scm
new file mode 100644
index 0000000000..dd92720c32
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/http/url-test.scm
@@ -0,0 +1,23 @@
+; Licensed to the Apache Software Foundation (ASF) under one
+; or more contributor license agreements. See the NOTICE file
+; distributed with this work for additional information
+; regarding copyright ownership. The ASF licenses this file
+; to you under the Apache License, Version 2.0 (the
+; "License"); you may not use this file except in compliance
+; with the License. You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing,
+; software distributed under the License is distributed on an
+; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+; KIND, either express or implied. See the License for the
+; specific language governing permissions and limitations
+; under the License.
+
+; URL test case
+
+(define (get id)
+ "http://localhost:8090/scheme"
+)
+
diff --git a/sca-cpp/branches/lightweight-sca/components/kvdb/Makefile.am b/sca-cpp/branches/lightweight-sca/components/kvdb/Makefile.am
new file mode 100644
index 0000000000..3212f3f5c8
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/kvdb/Makefile.am
@@ -0,0 +1,49 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT 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${LEVELDB_INCLUDE}
+
+incl_HEADERS = *.hpp
+incldir = $(prefix)/include/components/kvdb
+
+dist_comp_SCRIPTS = leveldb
+compdir=$(prefix)/components/kvdb
+
+comp_DATA = leveldb.prefix
+leveldb.prefix: $(top_builddir)/config.status
+ echo ${LEVELDB_PREFIX} >leveldb.prefix
+
+EXTRA_DIST = kvdb.composite kvdb.componentType
+
+comp_LTLIBRARIES = libkvdb.la
+noinst_DATA = libkvdb${libsuffix}
+
+libkvdb_la_SOURCES = kvdb.cpp
+libkvdb_la_LDFLAGS = -L${LEVELDB_LIB} -R${LEVELDB_LIB} -lleveldb
+libkvdb${libsuffix}:
+ ln -s .libs/libkvdb${libsuffix}
+
+leveldb_test_SOURCES = leveldb-test.cpp
+leveldb_test_LDFLAGS = -L${LEVELDB_LIB} -R${LEVELDB_LIB} -lleveldb
+
+client_test_SOURCES = client-test.cpp
+client_test_LDFLAGS = -lxml2 -lcurl -lmozjs
+
+dist_noinst_SCRIPTS = kvdb-test server-test
+noinst_PROGRAMS = leveldb-test client-test
+TESTS = kvdb-test server-test
+
diff --git a/sca-cpp/branches/lightweight-sca/components/kvdb/client-test.cpp b/sca-cpp/branches/lightweight-sca/components/kvdb/client-test.cpp
new file mode 100644
index 0000000000..5f0ef21d00
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/kvdb/client-test.cpp
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 KV (Key-Value) database component.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "perf.hpp"
+#include "../../modules/http/http.hpp"
+
+namespace tuscany {
+namespace nosqldb {
+
+const string uri("http://localhost:8090/nosqldb");
+
+bool testNoSqlDb() {
+ http::CURLSession cs("", "", "", "");
+
+ 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);
+
+ const failable<value> id = http::post(a, uri, cs);
+ assert(hasContent(id));
+
+ const string p = path(content(id));
+ {
+ const failable<value> val = http::get(uri + p, cs);
+ assert(hasContent(val));
+ assert(content(val) == a);
+ }
+
+ const list<value> j = list<value>() + "content" + (list<value>() + "item"
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "price" + string("$3.55")));
+ const list<value> b = list<value>() + (list<value>() + "entry"
+ + (list<value>() + "title" + string("item"))
+ + (list<value>() + "id" + string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"))
+ + j);
+
+ {
+ const failable<value> r = http::put(b, uri + p, cs);
+ assert(hasContent(r));
+ assert(content(r) == value(true));
+ }
+ {
+ const failable<value> val = http::get(uri + p, cs);
+ assert(hasContent(val));
+ assert(content(val) == b);
+ }
+ {
+ const failable<value> r = http::del(uri + p, cs);
+ assert(hasContent(r));
+ assert(content(r) == value(true));
+ }
+ {
+ const failable<value> val = http::get(uri + p, cs);
+ assert(!hasContent(val));
+ }
+
+ return true;
+}
+
+struct getLoop {
+ const string path;
+ const value entry;
+ http::CURLSession& cs;
+ getLoop(const string& path, const value& entry, http::CURLSession& cs) : path(path), entry(entry), cs(cs) {
+ }
+ const bool operator()() const {
+ const failable<value> val = http::get(uri + path, cs);
+ assert(hasContent(val));
+ assert(content(val) == entry);
+ return true;
+ }
+};
+
+bool testGetPerf() {
+ const list<value> i = list<value>() + "content" + (list<value>() + "item"
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "price" + string("$4.55")));
+ 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 cs("", "", "", "");
+ const failable<value> id = http::post(a, uri, cs);
+ assert(hasContent(id));
+ const string p = path(content(id));
+
+ const lambda<bool()> gl = getLoop(p, a, cs);
+ cout << "NoSqldb get test " << time(gl, 5, 200) << " ms" << endl;
+
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::nosqldb::testNoSqlDb();
+ tuscany::nosqldb::testGetPerf();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/kvdb/kvdb-test b/sca-cpp/branches/lightweight-sca/components/kvdb/kvdb-test
new file mode 100755
index 0000000000..420b98559c
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/kvdb/kvdb-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.
+
+# Setup
+rm -rf tmp
+mkdir -p tmp
+./tinycdb -c -m tmp/test.cdb </dev/null
+
+# Test
+./tinycdb-test 2>/dev/null
+rc=$?
+
+# Cleanup
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/components/kvdb/kvdb.componentType b/sca-cpp/branches/lightweight-sca/components/kvdb/kvdb.componentType
new file mode 100644
index 0000000000..1c56ab6807
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/kvdb/kvdb.componentType
@@ -0,0 +1,28 @@
+<?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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <service name="nvdb"/>
+ <property name="dbname" type="xsd:string"/>
+
+</componentType>
diff --git a/sca-cpp/branches/lightweight-sca/components/kvdb/kvdb.composite b/sca-cpp/branches/lightweight-sca/components/kvdb/kvdb.composite
new file mode 100644
index 0000000000..378f418cfc
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/kvdb/kvdb.composite
@@ -0,0 +1,32 @@
+<?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://tuscany.apache.org/xmlns/sca/components"
+ name="nvdb">
+
+ <component name="nvdb">
+ <implementation.cpp path="." library="libnvdb"/>
+ <property name="dbname">tmp/test.cdb</property>
+ <service name="nvdb">
+ <binding.http uri="nvdb"/>
+ </service>
+ </component>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/kvdb/kvdb.cpp b/sca-cpp/branches/lightweight-sca/components/kvdb/kvdb.cpp
new file mode 100644
index 0000000000..0df8f13882
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/kvdb/kvdb.cpp
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * LevelDB-based database component implementation.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "leveldb.hpp"
+
+namespace tuscany {
+namespace nvdb {
+
+/**
+ * Get an item from the database.
+ */
+const failable<value> get(const list<value>& params, leveldb::LevelDB& cdb) {
+ return leveldb::get(car(params), cdb);
+}
+
+/**
+ * Post an item to the database.
+ */
+const failable<value> post(const list<value>& params, leveldb::LevelDB& cdb) {
+ const value id = append<value>(car(params), mklist(mkuuid()));
+ const failable<bool> val = leveldb::post(id, cadr(params), cdb);
+ if (!hasContent(val))
+ return mkfailure<value>(val);
+ return id;
+}
+
+/**
+ * Put an item into the database.
+ */
+const failable<value> put(const list<value>& params, leveldb::LevelDB& cdb) {
+ const failable<bool> val = leveldb::put(car(params), cadr(params), cdb);
+ if (!hasContent(val))
+ return mkfailure<value>(val);
+ return value(content(val));
+}
+
+/**
+ * Delete an item from the database.
+ */
+const failable<value> del(const list<value>& params, leveldb::LevelDB& cdb) {
+ const failable<bool> val = leveldb::del(car(params), cdb);
+ if (!hasContent(val))
+ return mkfailure<value>(val);
+ return value(content(val));
+}
+
+/**
+ * Component implementation lambda function.
+ */
+class applyNoSqldb {
+public:
+ applyNoSqldb(leveldb::LevelDB& cdb) : cdb(cdb) {
+ }
+
+ const value operator()(const list<value>& params) const {
+ const value func(car(params));
+ if (func == "get")
+ return get(cdr(params), cdb);
+ if (func == "post")
+ return post(cdr(params), cdb);
+ if (func == "put")
+ return put(cdr(params), cdb);
+ if (func == "delete")
+ return del(cdr(params), cdb);
+ return mkfailure<value>();
+ }
+
+private:
+ leveldb::LevelDB& cdb;
+};
+
+/**
+ * Start the component.
+ */
+const failable<value> start(unused const list<value>& params) {
+ // Connect to the configured database and table
+ const value dbname = ((lambda<const value(list<value>&)>)car(params))(list<value>());
+ leveldb::LevelDB& cdb = *(new (gc_new<leveldb::LevelDB>()) leveldb::LevelDB(dbname));
+
+ // Return the component implementation lambda function
+ return value(lambda<value(const list<value>&)>(applyNoSqldb(cdb)));
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "start")
+ return tuscany::nosqldb::start(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/kvdb/leveldb b/sca-cpp/branches/lightweight-sca/components/kvdb/leveldb
new file mode 100755
index 0000000000..f9c54465f6
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/kvdb/leveldb
@@ -0,0 +1,24 @@
+#!/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`
+leveldb_prefix=`cat $here/leveldb.prefix`
+
+$leveldb_prefix/bin/cdb $*
+
diff --git a/sca-cpp/branches/lightweight-sca/components/kvdb/leveldb-test.cpp b/sca-cpp/branches/lightweight-sca/components/kvdb/leveldb-test.cpp
new file mode 100644
index 0000000000..b3b4ea7fd7
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/kvdb/leveldb-test.cpp
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 TinyCDB access functions.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "perf.hpp"
+#include "tinycdb.hpp"
+
+namespace tuscany {
+namespace tinycdb {
+
+bool testTinyCDB() {
+ TinyCDB cdb("tmp/test.cdb");
+ const value k = mklist<value>("a");
+
+ assert(hasContent(post(k, string("AAA"), cdb)));
+ assert((get(k, cdb)) == value(string("AAA")));
+ assert(hasContent(put(k, string("aaa"), cdb)));
+ assert((get(k, cdb)) == value(string("aaa")));
+ assert(hasContent(del(k, cdb)));
+ assert(!hasContent(get(k, cdb)));
+
+ return true;
+}
+
+struct getLoop {
+ const value k;
+ TinyCDB& cdb;
+ getLoop(const value& k, TinyCDB& cdb) : k(k), cdb(cdb) {
+ }
+ const bool operator()() const {
+ assert((get(k, cdb)) == value(string("CCC")));
+ return true;
+ }
+};
+
+bool testGetPerf() {
+ const value k = mklist<value>("c");
+ TinyCDB cdb("tmp/test.cdb");
+ assert(hasContent(post(k, string("CCC"), cdb)));
+
+ const lambda<bool()> gl = getLoop(k, cdb);
+ cout << "TinyCDB get test " << time(gl, 5, 100000) << " ms" << endl;
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::tinycdb::testTinyCDB();
+ tuscany::tinycdb::testGetPerf();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/kvdb/leveldb.hpp b/sca-cpp/branches/lightweight-sca/components/kvdb/leveldb.hpp
new file mode 100644
index 0000000000..05a89a76f7
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/kvdb/leveldb.hpp
@@ -0,0 +1,271 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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_leveldb_hpp
+#define tuscany_leveldb_hpp
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <leveldb/db.h>
+
+#include "string.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "../../modules/scheme/eval.hpp"
+
+namespace tuscany {
+namespace leveldb {
+
+/**
+ * A reallocatable buffer.
+ */
+class buffer {
+public:
+ operator void*() const throw() {
+ return buf;
+ }
+
+ operator unsigned char*() const throw() {
+ return (unsigned char*)buf;
+ }
+
+ operator char*() const throw() {
+ return (char*)buf;
+ }
+
+private:
+ buffer(const unsigned int size, void* buf) : size(size), buf(buf) {
+ }
+
+ unsigned int size;
+ void* buf;
+
+ friend const buffer mkbuffer(const unsigned int sz);
+ friend const buffer mkbuffer(const buffer& b, const unsigned int newsz);
+ friend const bool free(const buffer& b);
+};
+
+/**
+ * Make a new buffer.
+ */
+const buffer mkbuffer(const unsigned int sz) {
+ return buffer(sz, malloc(sz));
+}
+
+/**
+ * Make a new buffer by reallocating an existing one.
+ */
+const buffer mkbuffer(const buffer& b, const unsigned int sz) {
+ if (sz <= b.size)
+ return b;
+ return buffer(sz, realloc(b.buf, sz));
+}
+
+/**
+ * Free a buffer.
+ */
+const bool free(const buffer&b) {
+ ::free(b.buf);
+ return true;
+}
+
+/**
+ * Convert a database name to an absolute path.
+ */
+const string absdbname(const string& name) {
+ if (length(name) == 0 || c_str(name)[0] == '/')
+ return name;
+ char cwd[512];
+ if (getcwd(cwd, sizeof(cwd)) == NULL)
+ return name;
+ return string(cwd) + "/" + name;
+}
+
+/**
+ * Represents a LevelDB connection.
+ */
+class LevelDB {
+public:
+ LevelDB() : owner(false), fd(-1) {
+ debug("leveldb::leveldb");
+ st.st_ino = 0;
+ }
+
+ LevelDB(const string& name) : owner(true), name(absdbname(name)), fd(-1) {
+ debug(name, "leveldb::leveldb::name");
+ st.st_ino = 0;
+ }
+
+ LevelDB(const LevelDB& c) : owner(false), name(c.name), fd(c.fd) {
+ debug("leveldb::leveldb::copy");
+ st.st_ino = c.st.st_ino;
+ }
+
+ const LevelDB& operator=(const LevelDB& c) {
+ debug("leveldb::leveldb::operator=");
+ if(this == &c)
+ return *this;
+ owner = false;
+ name = c.name;
+ fd = c.fd;
+ st.st_ino = c.st.st_ino;
+ return *this;
+ }
+
+ ~LevelDB() {
+ debug("leveldb::~leveldb");
+ if (!owner)
+ return;
+ if (fd == -1)
+ return;
+ close(fd);
+ }
+
+private:
+ bool owner;
+ string name;
+ leveldb::DB* db;
+ struct stat st;
+
+ friend const string dbname(const LevelDB& db);
+ friend const failable<int> dbopen(LevelDB& db);
+ friend const failable<bool> dbclose(LevelDB& db);
+};
+
+/**
+ * Return the name of the database.
+ */
+const string dbname(const LevelDB& db) {
+ return db.name;
+}
+
+/**
+ * Open a database.
+ */
+const failable<int> dbopen(LevelDB& db) {
+
+ // Get database file serial number
+ struct stat st;
+ const int s = stat(c_str(db.name), &st);
+ if (s == -1)
+ return mkfailure<int>(string("Couldn't leveldb read database stat: ") + db.name);
+
+ leveldb::DB* ldb;
+ leveldb::Options options;
+ options.create_if_missing = true;
+ leveldb::Status status = leveldb::DB::Open(options, s, &ldb);
+ db.db = ldb;
+}
+
+/**
+ * Close a database.
+ */
+const failable<bool> dbclose(LevelDB& db) {
+ delete db.db;
+ db.db = NULL;
+ return true;
+}
+
+
+const failable<bool> post(const value& key, const value& val, LevelDB& db) {
+ debug(key, "leveldb::post::key");
+ debug(val, "leveldb::post::value");
+ debug(dbname(db), "leveldb::post::dbname");
+
+ const string ks(scheme::writeValue(key));
+ const string vs(scheme::writeValue(val));
+
+ put(ks, vs, db);
+
+ debug(r, "leveldb::post::result");
+ return r;
+}
+
+
+const failable<bool> put(const value& key, const value& val, LevelDB& db) {
+ debug(key, "leveldb::put::key");
+ debug(val, "leveldb::put::value");
+ debug(dbname(db), "leveldb::put::dbname");
+
+ const string ks(scheme::writeValue(key));
+ const string vs(scheme::writeValue(val));
+
+ debug(r, "leveldb::put::result");
+ return r;
+}
+
+/**
+ * Get an item from the database.
+ */
+const failable<value> get(const value& key, LevelDB& db) {
+ debug(key, "leveldb::get::key");
+ debug(dbname(db), "leveldb::get::dbname");
+
+ const string ks(scheme::writeValue(key));
+
+ std::string data;
+ db.db->Get(leveldb::ReadOptions(), key, &data);
+ const value val(scheme::readValue(string(data)));
+
+ debug(val, "leveldb::get::result");
+ return val;
+}
+
+/**
+ * Delete an item from the database
+ */
+struct delUpdate {
+ const string ks;
+ delUpdate(const string& ks) : ks(ks) {
+ }
+ const failable<bool> operator()(buffer& buf, const unsigned int klen, unused const unsigned int vlen) const {
+ if (ks == string((char*)buf, klen))
+ return false;
+ return true;
+ }
+};
+
+struct delFinish {
+ delFinish() {
+ }
+ const failable<bool> operator()(unused struct db_make& dbm) const {
+ return true;
+ }
+};
+
+const failable<bool> del(const value& key, LevelDB& db) {
+ debug(key, "leveldb::delete::key");
+ debug(dbname(db), "leveldb::delete::dbname");
+
+ const string ks(scheme::writeValue(key));
+
+ leveldb::Status s = db.db->Delete(leveldb::WriteOptions(), key);
+
+ debug(r, "leveldb::delete::result");
+ return s.ok();
+}
+
+}
+}
+
+#endif /* tuscany_leveldb_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/components/kvdb/server-test b/sca-cpp/branches/lightweight-sca/components/kvdb/server-test
new file mode 100755
index 0000000000..ebf01b9b4b
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/kvdb/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
+../../modules/http/httpd-conf tmp localhost 8090 ../../modules/http/htdocs
+../../modules/server/server-conf tmp
+../../modules/server/scheme-conf tmp
+cat >>tmp/conf/httpd.conf <<EOF
+SCAContribution `pwd`/
+SCAComposite nosqldb.composite
+EOF
+
+./leveldb -c -m tmp/test.cdb </dev/null
+../../modules/http/httpd-start tmp
+sleep 2
+
+# Test
+./client-test #2>/dev/null
+rc=$?
+
+# Cleanup
+../../modules/http/httpd-stop tmp
+sleep 2
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/components/log/Makefile.am b/sca-cpp/branches/lightweight-sca/components/log/Makefile.am
new file mode 100644
index 0000000000..0e96be5697
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/Makefile.am
@@ -0,0 +1,79 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+if WANT_LOG
+
+INCLUDES = -I${THRIFT_INCLUDE} -I${FB303_INCLUDE}
+
+incl_HEADERS = *.hpp
+incldir = $(prefix)/include/components/log
+
+dist_comp_SCRIPTS = scribed-central-conf scribed-central-firehose-conf scribed-central-mkfirehose scribed-client-conf scribed-central-start scribed-central-stop scribed-client-start scribed-client-stop scribe-tail-start scribe-tail-stop
+compdir=$(prefix)/components/log
+
+comp_DATA = scribe.prefix thrift.prefix
+scribe.prefix: $(top_builddir)/config.status
+ echo ${SCRIBE_PREFIX} >scribe.prefix
+
+thrift.prefix: $(top_builddir)/config.status
+ echo ${THRIFT_PREFIX} >thrift.prefix
+
+EXTRA_DIST = log.composite log.componentType logger.componentType *.scm *.thrift
+
+BUILT_SOURCES=gen-cpp/fb303_constants.cpp gen-cpp/fb303_types.cpp gen-cpp/scribe_constants.cpp gen-cpp/scribe.cpp gen-cpp/scribe_types.cpp gen-cpp/FacebookService.cpp gen-cpp/scribe.h
+gen-cpp/fb303_constants.cpp gen-cpp/fb303_types.cpp gen-cpp/scribe_constants.cpp gen-cpp/scribe.cpp gen-cpp/scribe_types.cpp gen-cpp/FacebookService.cpp gen-cpp/scribe.h: scribe.thrift
+ ${THRIFT_PREFIX}/bin/thrift -r --gen cpp scribe.thrift; (ls gen-cpp/*.cpp gen-cpp/*.h | xargs -I {} -t ./thrift-pragmas {})
+
+CLEANFILES = gen-cpp/*
+
+comp_LTLIBRARIES = liblog.la liblogger.la
+noinst_DATA = liblog${libsuffix} liblogger${libsuffix}
+
+nodist_liblog_la_SOURCES = gen-cpp/fb303_constants.cpp gen-cpp/fb303_types.cpp gen-cpp/scribe_constants.cpp gen-cpp/scribe.cpp gen-cpp/scribe_types.cpp gen-cpp/FacebookService.cpp gen-cpp/scribe.h
+liblog_la_CXXFLAGS = -Wno-unused-parameter -Wno-conversion -Wno-return-type
+liblog_la_SOURCES = log.cpp
+liblog_la_LDFLAGS = -L${THRIFT_LIB} -R${THRIFT_LIB} -lthrift -L${FB303_LIB} -R${FB303_LIB} -lfb303 -L${SCRIBE_LIB} -R${SCRIBE_LIB} -lscribe
+liblog${libsuffix}:
+ ln -s .libs/liblog${libsuffix}
+
+nodist_liblogger_la_SOURCES = gen-cpp/fb303_constants.cpp gen-cpp/fb303_types.cpp gen-cpp/scribe_constants.cpp gen-cpp/scribe.cpp gen-cpp/scribe_types.cpp gen-cpp/FacebookService.cpp gen-cpp/scribe.h
+liblogger_la_CXXFLAGS = -Wno-unused-parameter -Wno-conversion -Wno-return-type
+liblogger_la_SOURCES = logger.cpp
+liblogger_la_LDFLAGS = -L${THRIFT_LIB} -R${THRIFT_LIB} -lthrift -L${FB303_LIB} -R${FB303_LIB} -lfb303 -L${SCRIBE_LIB} -R${SCRIBE_LIB} -lscribe
+liblogger${libsuffix}:
+ ln -s .libs/liblogger${libsuffix}
+
+comp_PROGRAMS = scribe-cat scribe-status
+
+nodist_scribe_cat_SOURCES = gen-cpp/fb303_constants.cpp gen-cpp/fb303_types.cpp gen-cpp/scribe_constants.cpp gen-cpp/scribe.cpp gen-cpp/scribe_types.cpp gen-cpp/FacebookService.cpp gen-cpp/scribe.h
+scribe_cat_CXXFLAGS = -Wno-unused-parameter -Wno-conversion -Wno-return-type
+scribe_cat_SOURCES = scribe-cat.cpp
+scribe_cat_LDFLAGS = -L${THRIFT_LIB} -R${THRIFT_LIB} -lthrift -L${FB303_LIB} -R${FB303_LIB} -lfb303 -L${SCRIBE_LIB} -R${SCRIBE_LIB} -lscribe
+
+nodist_scribe_status_SOURCES = gen-cpp/fb303_constants.cpp gen-cpp/fb303_types.cpp gen-cpp/scribe_constants.cpp gen-cpp/scribe.cpp gen-cpp/scribe_types.cpp gen-cpp/FacebookService.cpp gen-cpp/scribe.h
+scribe_status_CXXFLAGS = -Wno-unused-parameter -Wno-conversion -Wno-return-type
+scribe_status_SOURCES = scribe-status.cpp
+scribe_status_LDFLAGS = -L${THRIFT_LIB} -R${THRIFT_LIB} -lthrift -L${FB303_LIB} -R${FB303_LIB} -lfb303 -L${SCRIBE_LIB} -R${SCRIBE_LIB} -lscribe
+
+client_test_SOURCES = client-test.cpp
+client_test_LDFLAGS = -lxml2 -lcurl -lmozjs
+
+dist_noinst_SCRIPTS = scribe-test server-test
+noinst_PROGRAMS = client-test
+TESTS = scribe-test server-test
+
+endif
diff --git a/sca-cpp/branches/lightweight-sca/components/log/adder-test.scm b/sca-cpp/branches/lightweight-sca/components/log/adder-test.scm
new file mode 100644
index 0000000000..ccd5bc555f
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/adder-test.scm
@@ -0,0 +1,20 @@
+; Licensed to the Apache Software Foundation (ASF) under one
+; or more contributor license agreements. See the NOTICE file
+; distributed with this work for additional information
+; regarding copyright ownership. The ASF licenses this file
+; to you under the Apache License, Version 2.0 (the
+; "License"); you may not use this file except in compliance
+; with the License. You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing,
+; software distributed under the License is distributed on an
+; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+; KIND, either express or implied. See the License for the
+; specific language governing permissions and limitations
+; under the License.
+
+; Logger test case
+
+(define (add a b) (+ a b))
diff --git a/sca-cpp/branches/lightweight-sca/components/log/client-test.cpp b/sca-cpp/branches/lightweight-sca/components/log/client-test.cpp
new file mode 100644
index 0000000000..d8cac5ee81
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/client-test.cpp
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 log component.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "perf.hpp"
+#include "../../modules/http/http.hpp"
+
+namespace tuscany {
+namespace log {
+
+const string uri("http://localhost:8090/log");
+
+bool testLog() {
+ http::CURLSession cs("", "", "", "", 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> a = list<value>() + (list<value>() + "entry"
+ + (list<value>() + "title" + string("item"))
+ + (list<value>() + "id" + string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"))
+ + i);
+
+ const failable<value> id = http::post(a, uri, cs);
+ assert(hasContent(id));
+
+ return true;
+}
+
+struct logLoop {
+ const value a;
+ const string uri;
+ http::CURLSession& cs;
+ logLoop(const value& a, const string& uri, http::CURLSession& cs) : a(a), uri(uri), cs(cs) {
+ }
+ const bool operator()() const {
+ const failable<value> id = http::post(a, uri, cs);
+ assert(hasContent(id));
+ return true;
+ }
+};
+
+bool testLogPerf() {
+ 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 cs("", "", "", "", 0);
+ const failable<value> id = http::post(a, uri, cs);
+ assert(hasContent(id));
+
+ const lambda<bool()> ll = logLoop(a, uri, cs);
+ cout << "Log test " << time(ll, 5, 200) << " ms" << endl;
+
+ return true;
+}
+
+bool testLogger() {
+ http::CURLSession cs("", "", "", "", 0);
+
+ const failable<value> res = http::evalExpr(mklist<value>(string("sum"), 33, 22), string("http://localhost:8090/client"), cs);
+ assert(hasContent(res));
+ assert((int)content(res) == 55);
+
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::log::testLog();
+ tuscany::log::testLogPerf();
+ tuscany::log::testLogger();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/log/client-test.scm b/sca-cpp/branches/lightweight-sca/components/log/client-test.scm
new file mode 100644
index 0000000000..1da6ca3564
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/client-test.scm
@@ -0,0 +1,20 @@
+; Licensed to the Apache Software Foundation (ASF) under one
+; or more contributor license agreements. See the NOTICE file
+; distributed with this work for additional information
+; regarding copyright ownership. The ASF licenses this file
+; to you under the Apache License, Version 2.0 (the
+; "License"); you may not use this file except in compliance
+; with the License. You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing,
+; software distributed under the License is distributed on an
+; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+; KIND, either express or implied. See the License for the
+; specific language governing permissions and limitations
+; under the License.
+
+; Logger test case
+
+(define (sum a b adder) (adder "add" a b))
diff --git a/sca-cpp/branches/lightweight-sca/components/log/fb303.thrift b/sca-cpp/branches/lightweight-sca/components/log/fb303.thrift
new file mode 100644
index 0000000000..66c8315274
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/fb303.thrift
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * fb303.thrift
+ */
+
+namespace java com.facebook.fb303
+namespace cpp facebook.fb303
+namespace perl Facebook.FB303
+
+/**
+ * Common status reporting mechanism across all services
+ */
+enum fb_status {
+ DEAD = 0,
+ STARTING = 1,
+ ALIVE = 2,
+ STOPPING = 3,
+ STOPPED = 4,
+ WARNING = 5,
+}
+
+/**
+ * Standard base service
+ */
+service FacebookService {
+
+ /**
+ * Returns a descriptive name of the service
+ */
+ string getName(),
+
+ /**
+ * Returns the version of the service
+ */
+ string getVersion(),
+
+ /**
+ * Gets the status of this service
+ */
+ fb_status getStatus(),
+
+ /**
+ * User friendly description of status, such as why the service is in
+ * the dead or warning state, or what is being started or stopped.
+ */
+ string getStatusDetails(),
+
+ /**
+ * Gets the counters for this service
+ */
+ map<string, i64> getCounters(),
+
+ /**
+ * Gets the value of a single counter
+ */
+ i64 getCounter(1: string key),
+
+ /**
+ * Sets an option
+ */
+ void setOption(1: string key, 2: string value),
+
+ /**
+ * Gets an option
+ */
+ string getOption(1: string key),
+
+ /**
+ * Gets all options
+ */
+ map<string, string> getOptions(),
+
+ /**
+ * Returns a CPU profile over the given time interval (client and server
+ * must agree on the profile format).
+ */
+ string getCpuProfile(1: i32 profileDurationInSec),
+
+ /**
+ * Returns the unix time that the server has been running since
+ */
+ i64 aliveSince(),
+
+ /**
+ * Tell the server to reload its configuration, reopen log files, etc
+ */
+ oneway void reinitialize(),
+
+ /**
+ * Suggest a shutdown to the server
+ */
+ oneway void shutdown(),
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/log/log.componentType b/sca-cpp/branches/lightweight-sca/components/log/log.componentType
new file mode 100644
index 0000000000..e661f568f1
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/log.componentType
@@ -0,0 +1,28 @@
+<?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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <service name="log"/>
+ <property name="category" type="xsd:string">default</property>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/log/log.composite b/sca-cpp/branches/lightweight-sca/components/log/log.composite
new file mode 100644
index 0000000000..c6755f3655
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/log.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://tuscany.apache.org/xmlns/sca/components"
+ name="log">
+
+ <component name="log">
+ <implementation.cpp path="." library="liblog"/>
+ <property name="host"></property>
+ <property name="category">default</property>
+ <service name="log">
+ <binding.http uri="log"/>
+ </service>
+ </component>
+
+ <component name="client">
+ <implementation.scheme script="client-test.scm"/>
+ <service name="client">
+ <binding.http uri="client"/>
+ </service>
+ <reference name="adder" target="logger"/>
+ </component>
+
+ <component name="logger">
+ <implementation.cpp path="." library="liblogger"/>
+ <property name="host"></property>
+ <property name="category">default</property>
+ <service name="logger">
+ <binding.http uri="logger"/>
+ </service>
+ <reference name="relay" target="adder"/>
+ </component>
+
+ <component name="adder">
+ <implementation.scheme script="adder-test.scm"/>
+ <service name="adder">
+ <binding.http uri="adder"/>
+ </service>
+ </component>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/log/log.cpp b/sca-cpp/branches/lightweight-sca/components/log/log.cpp
new file mode 100644
index 0000000000..f7f7086192
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/log.cpp
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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$ */
+
+/**
+ * Scribe-based log component implementation.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "../../modules/http/http.hpp"
+#include "scribe.hpp"
+
+namespace tuscany {
+namespace log {
+
+/**
+ * Post an item to the Scribe log.
+ */
+const failable<value> post(const list<value>& params, const value& host, const value& category, scribe::Scribe& sc) {
+ debug(cadr(params), "log::post::value");
+ const failable<bool> val = scribe::log(cadr(params), host, category, sc);
+ if (!hasContent(val))
+ return mkfailure<value>(val);
+ return value(mklist<value>(true));
+}
+
+/**
+ * Component implementation lambda function.
+ */
+class applyLog {
+public:
+ applyLog(const value& host, const value& category, scribe::Scribe& sc) : host(host), category(category), sc(sc) {
+ }
+
+ const value operator()(const list<value>& params) const {
+ const value func(car(params));
+ if (func == "post")
+ return post(cdr(params), host, category, sc);
+ return mkfailure<value>();
+ }
+
+private:
+ const value host;
+ const value category;
+ scribe::Scribe& sc;
+};
+
+/**
+ * Start the component.
+ */
+const failable<value> start(const list<value>& params) {
+ // Connect to Scribe
+ scribe::Scribe& sc = *(new (gc_new<scribe::Scribe>()) scribe::Scribe("localhost", 1464));
+
+ // Extract the configured category
+ const value host = ((lambda<value(const list<value>&)>)car(params))(list<value>());
+ const value category = ((lambda<value(const list<value>&)>)cadr(params))(list<value>());
+ debug(host, "log::start::host");
+ debug(category, "log::start::category");
+
+ // Return the component implementation lambda function
+ return value(lambda<value(const list<value>&)>(applyLog(host, category, sc)));
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "start")
+ return tuscany::log::start(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/log/logger.componentType b/sca-cpp/branches/lightweight-sca/components/log/logger.componentType
new file mode 100644
index 0000000000..1c9546e685
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/logger.componentType
@@ -0,0 +1,29 @@
+<?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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <service name="logger"/>
+ <reference name="relay"/>
+ <property name="category" type="xsd:string">default</property>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/log/logger.cpp b/sca-cpp/branches/lightweight-sca/components/log/logger.cpp
new file mode 100644
index 0000000000..d7969036ab
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/logger.cpp
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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$ */
+
+/**
+ * Scribe-based logger component implementation, used to intercept
+ * and log service invocations.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "../../modules/http/http.hpp"
+#include "scribe.hpp"
+
+namespace tuscany {
+namespace logger {
+
+/**
+ * Component implementation lambda function.
+ */
+class applyLog {
+public:
+ applyLog(const lambda<value(const list<value>&)>& relay, const value& host, const value& category, scribe::Scribe& sc) : relay(relay), host(host), category(category), sc(sc) {
+ }
+
+ const value operator()(const list<value>& params) const {
+ // Log the function params
+ debug(params, "logger::apply::params");
+ scribe::log(params, host, category, sc);
+
+ // Relay the function call
+ const failable<value> res = relay(params);
+
+ // Log the result
+ scribe::log(res, host, category, sc);
+ return res;
+ }
+
+private:
+ const lambda<value(const list<value>&)> relay;
+ const value host;
+ const value category;
+ scribe::Scribe& sc;
+};
+
+/**
+ * Start the component.
+ */
+const failable<value> start(const list<value>& params) {
+ // Connect to Scribe
+ scribe::Scribe& sc = *(new (gc_new<scribe::Scribe>()) scribe::Scribe("localhost", 1464));
+
+ // Extract the configured relay service and category
+ const value rel = car(params);
+ const value host = ((lambda<value(const list<value>&)>)cadr(params))(list<value>());
+ const value category = ((lambda<value(const list<value>&)>)caddr(params))(list<value>());
+ debug(host, "logger::start::host");
+ debug(category, "logger::start::category");
+
+ // Return the component implementation lambda function
+ return value(lambda<value(const list<value>&)>(applyLog(rel, host, category, sc)));
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "start")
+ return tuscany::logger::start(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/log/scribe-cat.cpp b/sca-cpp/branches/lightweight-sca/components/log/scribe-cat.cpp
new file mode 100644
index 0000000000..254e2167ae
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/scribe-cat.cpp
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * A utility that logs stdin into a scribe log.
+ */
+
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+
+#undef debug
+#define debug(...)
+#include "scribe.hpp"
+
+namespace tuscany {
+namespace scribecat {
+
+int cat(const string& host, const string& category, const string& type) {
+ // Connect to Scribe
+ scribe::Scribe& sc = *(new (gc_new<scribe::Scribe>()) scribe::Scribe(host, 1464));
+
+ // Read lines from stdin and log them
+ char buf[8193];
+ for (;;) {
+ gc_scoped_pool p;
+
+ // Write line prefix
+ ostringstream os;
+ if (length(type) != 0)
+ os << "[" << logTime() << "] [" << type << "] ";
+ const string prefix = str(os);
+ const int pl = length(prefix);
+ strcpy(buf, c_str(prefix));
+
+ // Read log line
+ const char* s = fgets(buf + pl, 8192 - pl, stdin);
+ if (s == NULL)
+ return 0;
+
+ // Remove trailing '\n'
+ const size_t l = strlen(s);
+ if (l > 0)
+ buf[pl + l - 1] = '\0';
+
+ // Log the line
+ const failable<bool> val = scribe::log(buf, host, category, sc);
+ if (!hasContent(val))
+ return 1;
+ }
+}
+
+}
+}
+
+int main(const int argc, const char** argv) {
+ return tuscany::scribecat::cat(argc < 2? "localhost" : argv[1], argc < 3? "default" : argv[2], argc < 4? "" : argv[3]);
+}
+
diff --git a/sca-cpp/branches/lightweight-sca/components/log/scribe-status.cpp b/sca-cpp/branches/lightweight-sca/components/log/scribe-status.cpp
new file mode 100644
index 0000000000..79f7572947
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/scribe-status.cpp
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * A utility that logs stdin into a scribe log.
+ */
+
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+
+#undef debug
+#define debug(...)
+#include "scribe.hpp"
+
+namespace tuscany {
+namespace scribestatus {
+
+const int status(const string& host, const int port) {
+ // Connect to Scribe
+ scribe::Scribe& sc = *(new (gc_new<scribe::Scribe>()) scribe::Scribe(host, port));
+
+ // Get its status
+ const failable<string> fs = scribe::status(sc);
+
+ // Interpret and display results
+ if (!hasContent(fs)) {
+ cerr << reason(fs) << " : " << rcode(fs) << endl;
+ return 2;
+ }
+ const string s = content(fs);
+ cout << s << endl;
+ if (s == "ALIVE")
+ return 0;
+ if (s == "STARTING" || s == "STOPPING" || s == "WARNING")
+ return 1;
+ return 2;
+}
+
+}
+}
+
+int main(const int argc, const char** argv) {
+ return tuscany::scribestatus::status(argc < 2? "localhost" : argv[1], argc < 3? 1463 : atoi(argv[2]));
+}
+
diff --git a/sca-cpp/branches/lightweight-sca/components/log/scribe-tail-start b/sca-cpp/branches/lightweight-sca/components/log/scribe-tail-start
new file mode 100755
index 0000000000..fc469b5488
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/scribe-tail-start
@@ -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.
+
+# Tail a file and pipe into scribe-cat
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+
+category=""
+type=""
+file=""
+if [ "$3" != "" ]; then
+ category=$1
+ type=$2
+ file=$3
+else
+ if [ "$2" != "" ]; then
+ category=$1
+ file=$2
+ else
+ file=$1
+ fi
+fi
+host=`hostname`
+
+mkdir -p `dirname $file`
+touch $file
+file=`echo "import os; print os.path.realpath('$file')" | python`
+
+tail -f -n 0 $file | $here/scribe-cat $host $category $type &
+
diff --git a/sca-cpp/branches/lightweight-sca/components/log/scribe-tail-stop b/sca-cpp/branches/lightweight-sca/components/log/scribe-tail-stop
new file mode 100755
index 0000000000..0d43570a6f
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/scribe-tail-stop
@@ -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.
+
+# Stop tailing a file
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+
+category=""
+type=""
+file=""
+if [ "$3" != "" ]; then
+ category=$1
+ type=$2
+ file=$3
+else
+ if [ "$2" != "" ]; then
+ category=$1
+ file=$2
+ else
+ file=$1
+ fi
+fi
+file=`echo "import os; print os.path.realpath('$file')" | python`
+
+cmd="tail -f -n 0 $file"
+k=`ps -ef | grep -v grep | grep "${cmd}" | awk '{ print $2 }'`
+if [ "$k" != "" ]; then
+ kill $k
+fi
+
diff --git a/sca-cpp/branches/lightweight-sca/components/log/scribe-test b/sca-cpp/branches/lightweight-sca/components/log/scribe-test
new file mode 100755
index 0000000000..bc34f6f650
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/scribe-test
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+echo "Testing..."
+
+# Setup
+rm -rf tmp
+./scribed-central-conf tmp
+./scribed-client-conf tmp localhost
+./scribed-central-start tmp
+./scribed-client-start tmp
+sleep 1
+
+# Test logging a message
+echo test | ./scribe-cat >/dev/null
+sleep 4
+grep test tmp/scribe/logs/central/default/default_current >/dev/null
+rc=$?
+
+# Cleanup
+./scribed-client-stop tmp
+./scribed-central-stop tmp
+sleep 1
+if [ "$rc" = "0" ]; then
+ echo "OK"
+fi
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/components/log/scribe.hpp b/sca-cpp/branches/lightweight-sca/components/log/scribe.hpp
new file mode 100644
index 0000000000..0f0570be64
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/scribe.hpp
@@ -0,0 +1,198 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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_scribe_hpp
+#define tuscany_scribe_hpp
+
+/**
+ * Scribe logging functions.
+ */
+
+// Work around redundant defines in Scribe headers
+#undef PACKAGE
+#undef PACKAGE_BUGREPORT
+#undef PACKAGE_NAME
+#undef PACKAGE_STRING
+#undef PACKAGE_TARNAME
+#undef PACKAGE_VERSION
+#undef VERSION
+#undef OK
+
+// Ignore integer conversion issues in Thrift and Scribe headers
+#ifdef WANT_MAINTAINER_WARNINGS
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
+
+#include <protocol/TBinaryProtocol.h>
+#include <transport/TSocket.h>
+#include <transport/TTransportUtils.h>
+
+#include "gen-cpp/scribe.h"
+
+#ifdef WANT_MAINTAINER_WARNINGS
+#pragma GCC diagnostic warning "-Wconversion"
+#endif
+
+#include "string.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "../../modules/scheme/eval.hpp"
+
+namespace tuscany {
+namespace scribe {
+
+/**
+ * Represents a Scribe connection.
+ */
+class Scribe {
+public:
+ Scribe() : owner(false) {
+ }
+
+ Scribe(const string host, const int port) : owner(true) {
+ debug(host, "scribe::scribe::host");
+ debug(port, "scribe::scribe::port");
+ init(host, port);
+ }
+
+ Scribe(const Scribe& c) : owner(false) {
+ debug("scribe::scribe::copy");
+ client = c.client;
+ transport = c.transport;
+ }
+
+ const Scribe& operator=(const Scribe& c) {
+ debug("scribe::scribe::operator=");
+ if(this == &c)
+ return *this;
+ owner = false;
+ client = c.client;
+ transport = c.transport;
+ return *this;
+ }
+
+ ~Scribe() {
+ if (!owner)
+ return;
+ try {
+ transport->close();
+ delete client;
+ } catch (const std::exception& e) {
+ mkfailure<bool>(e.what());
+ }
+ }
+
+private:
+ bool owner;
+ ::scribe::thrift::scribeClient* client;
+ boost::shared_ptr<apache::thrift::transport::TTransport> transport;
+
+ friend const failable<bool> log(const value& val, const string& host, const value& category, const Scribe& sc);
+ friend const failable<string> status(const Scribe& sc);
+
+ /**
+ * Initialize the Scribe connection.
+ */
+ const failable<bool> init(const string& host, const int port) {
+ try {
+ boost::shared_ptr<apache::thrift::transport::TTransport> sock(new apache::thrift::transport::TSocket(c_str(host), port));
+ boost::shared_ptr<apache::thrift::transport::TFramedTransport> framedSock(new apache::thrift::transport::TFramedTransport(sock));
+ transport = framedSock;
+ boost::shared_ptr<apache::thrift::protocol::TProtocol> proto(new apache::thrift::protocol::TBinaryProtocol(transport));
+ client = new ::scribe::thrift::scribeClient(proto);
+ transport->open();
+ return true;
+ } catch (const std::exception& e) {
+ return mkfailure<bool>(e.what());
+ }
+ }
+};
+
+/**
+ * Log an item.
+ */
+const failable<bool> log(const value& val, const string& host, const value& category, const Scribe& sc) {
+ debug(val, "scribe::log::value");
+ debug(category, "scribe::log::category");
+
+ const value cat = isString(category)? value(c_str(category)):category;
+ const string cs(scheme::writeValue(cat));
+ const string vs(scheme::writeValue(val));
+ ostringstream os;
+ os << "[" << host << "] " << vs;
+
+ try {
+ ::scribe::thrift::LogEntry entry;
+ entry.category = c_str(cs);
+ entry.message = c_str(str(os));
+ std::vector< ::scribe::thrift::LogEntry> msgs;
+ msgs.push_back(entry);
+
+ int result = sc.client->Log(msgs);
+ if (result != ::scribe::thrift::OK)
+ return mkfailure<bool>("Could not log value, retry later");
+ } catch (const std::exception& e) {
+ return mkfailure<bool>(e.what());
+ }
+
+ debug(true, "scribe::log::result");
+ return true;
+}
+
+/**
+ * Return Scribe status.
+ */
+const failable<string> status(const Scribe& sc) {
+ debug("scribe::status");
+
+ try {
+ ::facebook::fb303::fb_status s = sc.client->getStatus();
+ switch(s) {
+ case ::facebook::fb303::DEAD:
+ debug("DEAD", "scribe::status::result");
+ return string("DEAD");
+ case ::facebook::fb303::STARTING:
+ debug("STARTING", "scribe::status::result");
+ return string("STARTING");
+ case ::facebook::fb303::ALIVE:
+ debug("ALIVE", "scribe::status::result");
+ return string("ALIVE");
+ case ::facebook::fb303::STOPPING:
+ debug("STOPPING", "scribe::status::result");
+ return string("STOPPING");
+ case ::facebook::fb303::STOPPED:
+ debug("STOPPED", "scribe::status::result");
+ return string("STOPPED");
+ case ::facebook::fb303::WARNING:
+ debug("WARNING", "scribe::status::result");
+ return string("WARNING");
+ }
+ return mkfailure<string>("Unknown status");
+ } catch (const std::exception& e) {
+ return mkfailure<string>(e.what());
+ }
+}
+
+}
+}
+
+#endif /* tuscany_scribe_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/components/log/scribe.thrift b/sca-cpp/branches/lightweight-sca/components/log/scribe.thrift
new file mode 100644
index 0000000000..592e8b630e
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/scribe.thrift
@@ -0,0 +1,39 @@
+#!/usr/local/bin/thrift --cpp --php
+
+## Copyright (c) 2007-2008 Facebook
+##
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+##
+## See accompanying file LICENSE or visit the Scribe site at:
+## http://developers.facebook.com/scribe/
+
+include "fb303.thrift"
+
+namespace cpp scribe.thrift
+
+enum ResultCode
+{
+ OK,
+ TRY_LATER
+}
+
+struct LogEntry
+{
+ 1: string category,
+ 2: string message
+}
+
+service scribe extends fb303.FacebookService
+{
+ ResultCode Log(1: list<LogEntry> messages);
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/log/scribed-central-conf b/sca-cpp/branches/lightweight-sca/components/log/scribed-central-conf
new file mode 100755
index 0000000000..3c62107da5
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/scribed-central-conf
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Generate a Scribe central 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`
+
+host=$2
+if [ "$host" = "" ]; then
+ host="*"
+fi
+
+mkdir -p $root/scribe/conf
+mkdir -p $root/scribe/logs/central
+mkdir -p $root/scribe/logs/central-secondary
+
+cat >$root/scribe/conf/scribe-central.conf <<EOF
+# Generated by: scribed-central-conf $*
+# Scribe central configuration
+host=$host
+port=1463
+max_msg_per_second=2000000
+check_interval=3
+
+# Log store configuration
+<store>
+category=default
+type=buffer
+target_write_size=20480
+max_write_interval=1
+buffer_send_rate=2
+retry_interval=30
+retry_interval_range=10
+
+<primary>
+category=default
+type=file
+target_write_size=20480
+max_write_interval=1
+fs_type=std
+file_path=$root/scribe/logs/central
+base_filename=central
+max_size=1000000
+add_newlines=1
+rotate_period=daily
+rotate_hour=0
+rotate_minute=10
+</primary>
+
+<secondary>
+category=default
+type=file
+target_write_size=20480
+max_write_interval=1
+fs_type=std
+file_path=$root/scribe/logs/central-secondary
+base_filename=central
+max_size=3000000
+</secondary>
+
+</store>
+EOF
diff --git a/sca-cpp/branches/lightweight-sca/components/log/scribed-central-firehose-conf b/sca-cpp/branches/lightweight-sca/components/log/scribed-central-firehose-conf
new file mode 100755
index 0000000000..f76aed58c9
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/scribed-central-firehose-conf
@@ -0,0 +1,126 @@
+#!/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 Scribe central 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`
+
+host=$2
+if [ "$host" = "" ]; then
+ host="*"
+fi
+
+mkdir -p $root/scribe/conf
+mkdir -p $root/scribe/logs/central
+mkdir -p $root/scribe/logs/central-secondary
+mkdir -p $root/scribe/logs/firehose
+mkdir -p $root/scribe/logs/firehose-secondary
+
+cat >$root/scribe/conf/scribe-central.conf <<EOF
+# Generated by: scribed-central-conf $*
+# Scribe central configuration
+host=$host
+port=1463
+max_msg_per_second=2000000
+check_interval=3
+
+# Log store configuration
+<store>
+category=default
+type=multi
+target_write_size=20480
+max_write_interval=1
+
+<store0>
+category=default
+type=buffer
+target_write_size=20480
+max_write_interval=1
+buffer_send_rate=2
+retry_interval=30
+retry_interval_range=10
+
+<primary>
+category=default
+type=file
+target_write_size=20480
+max_write_interval=1
+fs_type=std
+file_path=$root/scribe/logs/central
+base_filename=central
+max_size=1000000
+add_newlines=1
+rotate_period=daily
+rotate_hour=0
+rotate_minute=10
+</primary>
+
+<secondary>
+category=default
+type=file
+target_write_size=20480
+max_write_interval=1
+fs_type=std
+file_path=$root/scribe/logs/central-secondary
+base_filename=central
+max_size=3000000
+</secondary>
+
+</store0>
+
+<store1>
+category=default
+type=buffer
+target_write_size=20480
+max_write_interval=1
+buffer_send_rate=2
+retry_interval=30
+retry_interval_range=10
+
+<primary>
+category=default
+type=file
+target_write_size=20480
+max_write_interval=1
+fs_type=std
+file_path=$root/scribe/logs/firehose
+base_filename=central
+max_size=1000000
+add_newlines=1
+write_stats=no
+create_symlink=no
+</primary>
+
+<secondary>
+category=default
+type=file
+target_write_size=20480
+max_write_interval=1
+fs_type=std
+file_path=$root/scribe/logs/firehose-secondary
+base_filename=central
+max_size=3000000
+</secondary>
+
+</store1>
+
+</store>
+
+EOF
diff --git a/sca-cpp/branches/lightweight-sca/components/log/scribed-central-mkfirehose b/sca-cpp/branches/lightweight-sca/components/log/scribed-central-mkfirehose
new file mode 100755
index 0000000000..9b02305d1d
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/scribed-central-mkfirehose
@@ -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.
+
+# Create a firehose fifo pipe for a log category
+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`
+
+category=$2
+if [ "$category" = "" ]; then
+ category="default"
+fi
+
+mkdir -p $root/scribe/logs/firehose/$category
+if [ ! -e "$root/scribe/logs/firehose/$category/$category""_00000" ]; then
+ mkfifo "$root/scribe/logs/firehose/$category/$category""_00000"
+fi
+
diff --git a/sca-cpp/branches/lightweight-sca/components/log/scribed-central-start b/sca-cpp/branches/lightweight-sca/components/log/scribed-central-start
new file mode 100755
index 0000000000..9aff8a1466
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/scribed-central-start
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Start central scribed
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+scribe_prefix=`cat $here/scribe.prefix`
+thrift_prefix=`cat $here/thrift.prefix`
+export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${thrift_prefix}/lib:${thrift_prefix}/contrib/fb303/lib:${scribe_prefix}/lib
+$scribe_prefix/bin/scribed -c $root/scribe/conf/scribe-central.conf 1>$root/scribe/logs/central.log 2>&1 &
diff --git a/sca-cpp/branches/lightweight-sca/components/log/scribed-central-stop b/sca-cpp/branches/lightweight-sca/components/log/scribed-central-stop
new file mode 100755
index 0000000000..2d301149b8
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/scribed-central-stop
@@ -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.
+
+# Stop central scribed
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+scribe_prefix=`cat $here/scribe.prefix`
+thrift_prefix=`cat $here/thrift.prefix`
+scribed="$scribe_prefix/bin/scribed -c $root/scribe/conf/scribe-central.conf"
+
+k=`ps -ef | grep -v grep | grep "${scribed}" | awk '{ print $2 }'`
+if [ "$k" != "" ]; then
+ kill $k
+fi
+
diff --git a/sca-cpp/branches/lightweight-sca/components/log/scribed-client-conf b/sca-cpp/branches/lightweight-sca/components/log/scribed-client-conf
new file mode 100755
index 0000000000..e0a35581c7
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/scribed-client-conf
@@ -0,0 +1,70 @@
+#!/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 Scribe client 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`
+
+if [ "$3" = "" ]; then
+ host="*"
+ central=$2
+else
+ host=$2
+ central=$3
+fi
+
+mkdir -p $root/scribe/conf
+mkdir -p $root/scribe/logs/client-secondary
+
+cat >$root/scribe/conf/scribe-client.conf <<EOF
+# Generated by: scribed-client-conf $*
+# Scribe client configuration
+host=$host
+port=1464
+max_msg_per_second=2000000
+check_interval=3
+
+# Forward all messages to central Scribe on port 1463
+# Save them locally as well
+<store>
+category=default
+type=buffer
+
+target_write_size=20480
+max_write_interval=1
+buffer_send_rate=1
+retry_interval=30
+retry_interval_range=10
+
+<primary>
+type=network
+remote_host=$central
+remote_port=1463
+</primary>
+
+<secondary>
+type=file
+fs_type=std
+file_path=$root/scribe/logs/client-secondary
+base_filename=client
+max_size=3000000
+</secondary>
+</store>
+EOF
diff --git a/sca-cpp/branches/lightweight-sca/components/log/scribed-client-start b/sca-cpp/branches/lightweight-sca/components/log/scribed-client-start
new file mode 100755
index 0000000000..acf52c535f
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/scribed-client-start
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Start client scribed
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+scribe_prefix=`cat $here/scribe.prefix`
+thrift_prefix=`cat $here/thrift.prefix`
+export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${thrift_prefix}/lib:${thrift_prefix}/contrib/fb303/lib:${scribe_prefix}/lib
+$scribe_prefix/bin/scribed -c $root/scribe/conf/scribe-client.conf 1>$root/scribe/logs/client.log 2>&1 &
diff --git a/sca-cpp/branches/lightweight-sca/components/log/scribed-client-stop b/sca-cpp/branches/lightweight-sca/components/log/scribed-client-stop
new file mode 100755
index 0000000000..d6a6345f29
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/scribed-client-stop
@@ -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.
+
+# Stop client scribed
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+scribe_prefix=`cat $here/scribe.prefix`
+thrift_prefix=`cat $here/thrift.prefix`
+scribed="$scribe_prefix/bin/scribed -c $root/scribe/conf/scribe-client.conf"
+
+k=`ps -ef | grep -v grep | grep "${scribed}" | awk '{ print $2 }'`
+if [ "$k" != "" ]; then
+ kill $k
+fi
+
diff --git a/sca-cpp/branches/lightweight-sca/components/log/server-test b/sca-cpp/branches/lightweight-sca/components/log/server-test
new file mode 100755
index 0000000000..6c9cf47135
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/server-test
@@ -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.
+
+# Setup
+rm -rf tmp
+../../modules/http/httpd-conf tmp localhost 8090 ../../modules/http/htdocs
+../../modules/http/httpd-event-conf tmp
+../../modules/server/server-conf tmp
+../../modules/server/scheme-conf tmp
+cat >>tmp/conf/httpd.conf <<EOF
+SCAContribution `pwd`/
+SCAComposite log.composite
+EOF
+
+rm -rf tmp/scribe
+./scribed-central-conf tmp
+./scribed-client-conf tmp localhost
+./scribed-central-start tmp
+./scribed-client-start tmp
+sleep 1
+../../modules/http/httpd-start tmp
+sleep 2
+
+# Test
+./client-test 2>/dev/null
+rc=$?
+if [ "$rc" = "0" ]; then
+ echo "Testing..."
+ sleep 4
+ grep "Apple" tmp/scribe/logs/central/default/default_current >/dev/null
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ grep "(add 33 22)" tmp/scribe/logs/central/default/default_current >/dev/null
+ rc=$?
+fi
+if [ "$rc" = "0" ]; then
+ grep "55" tmp/scribe/logs/central/default/default_current >/dev/null
+ rc=$?
+fi
+
+# Cleanup
+../../modules/http/httpd-stop tmp
+sleep 1
+./scribed-client-stop tmp
+./scribed-central-stop tmp
+sleep 1
+if [ "$rc" = "0" ]; then
+ echo "OK"
+fi
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/components/log/thrift-pragmas b/sca-cpp/branches/lightweight-sca/components/log/thrift-pragmas
new file mode 100755
index 0000000000..3950ce9984
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/log/thrift-pragmas
@@ -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.
+
+# Patch generated Thrift files, disable some compile warnings
+
+tmpfile=`mktemp -t thrift-pragmas.XXX`
+cat >$tmpfile << EOF
+#pragma GCC diagnostic ignored "-Wconversion"
+#pragma GCC diagnostic ignored "-Wunused"
+#pragma GCC diagnostic ignored "-Wreturn-type"
+EOF
+
+cat $1 >>$tmpfile
+cat $tmpfile >$1
+
+rm $tmpfile
diff --git a/sca-cpp/branches/lightweight-sca/components/queue/Makefile.am b/sca-cpp/branches/lightweight-sca/components/queue/Makefile.am
new file mode 100644
index 0000000000..c44722a523
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/queue/Makefile.am
@@ -0,0 +1,57 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT 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_QUEUE
+
+INCLUDES = -I${QPIDC_INCLUDE}
+
+incl_HEADERS = *.hpp
+incldir = $(prefix)/include/components/queue
+
+dist_comp_SCRIPTS = qpidd-start qpidd-stop
+compdir=$(prefix)/components/queue
+
+comp_DATA = qpidc.prefix
+qpidc.prefix: $(top_builddir)/config.status
+ echo ${QPIDC_PREFIX} >qpidc.prefix
+
+EXTRA_DIST = queue.composite queue-sender.componentType queue-listener.componentType *.scm
+
+comp_LTLIBRARIES = libqueue-sender.la libqueue-listener.la
+noinst_DATA = libqueue-sender${libsuffix} libqueue-listener${libsuffix}
+
+libqueue_sender_la_SOURCES = queue-sender.cpp
+libqueue_sender_la_LDFLAGS = -L${QPIDC_LIB} -R${QPIDC_LIB} -lqpidclient -lqpidcommon
+libqueue-sender${libsuffix}:
+ ln -s .libs/libqueue-sender${libsuffix}
+
+libqueue_listener_la_SOURCES = queue-listener.cpp
+libqueue_listener_la_LDFLAGS = -L${QPIDC_LIB} -R${QPIDC_LIB} -lqpidclient -lqpidcommon
+libqueue-listener${libsuffix}:
+ ln -s .libs/libqueue-listener${libsuffix}
+
+qpid_test_SOURCES = qpid-test.cpp
+qpid_test_LDFLAGS = -L${QPIDC_LIB} -R${QPIDC_LIB} -lqpidclient -lqpidcommon
+
+client_test_SOURCES = client-test.cpp
+client_test_LDFLAGS = -lxml2 -lcurl -lmozjs -L${QPIDC_LIB} -R${QPIDC_LIB} -lqpidclient -lqpidcommon
+
+dist_noinst_SCRIPTS = send-test server-test
+noinst_PROGRAMS = qpid-test client-test
+TESTS = send-test server-test
+
+endif
diff --git a/sca-cpp/branches/lightweight-sca/components/queue/client-test.cpp b/sca-cpp/branches/lightweight-sca/components/queue/client-test.cpp
new file mode 100644
index 0000000000..30bfe07bf7
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/queue/client-test.cpp
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test queue component.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "list.hpp"
+#include "element.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "perf.hpp"
+#include "../../modules/http/http.hpp"
+#include "qpid.hpp"
+
+// Ignore conversion issues and redundant declarations in Qpid headers
+#ifdef WANT_MAINTAINER_WARNINGS
+#pragma GCC diagnostic ignored "-Wconversion"
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#endif
+
+namespace tuscany {
+namespace queue {
+
+const value key(mklist<value>(string("report")));
+const string qname("reportq");
+
+const list<value> item = list<value>() + "content" + (list<value>() + "item"
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "price" + string("$2.99")));
+const list<value> entry = list<value>() + (list<value>() + "entry"
+ + (list<value>() + "title" + string("item"))
+ + (list<value>() + "id" + string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"))
+ + item);
+
+bool testDeclareQueue() {
+ QpidConnection qc;
+ QpidSession qs(qc);
+ const failable<bool> r = declareQueue(key, qname, qs);
+ assert(hasContent(r));
+ return true;
+}
+
+const bool listener(const value& k, const value& v) {
+ cerr << "k " << k << " v " << v << endl;
+ assert(k == key);
+ assert(v == entry);
+ return false;
+}
+
+bool testListen() {
+ QpidConnection qc;
+ QpidSession qs(qc);
+ QpidSubscription qsub(qs);
+ const lambda<bool(const value&, const value&)> l(listener);
+ listen(qname, l, qsub);
+ return true;
+}
+
+bool testPost() {
+ gc_scoped_pool pool;
+ http::CURLSession ch("", "", "", "");
+ const failable<value> id = http::post(entry, "http://localhost:8090/print-sender", ch);
+ assert(hasContent(id));
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::queue::testDeclareQueue();
+ tuscany::queue::testPost();
+ tuscany::queue::testListen();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/queue/qpid-test.cpp b/sca-cpp/branches/lightweight-sca/components/queue/qpid-test.cpp
new file mode 100644
index 0000000000..27db7734b0
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/queue/qpid-test.cpp
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 Qpid support functions.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "list.hpp"
+#include "element.hpp"
+#include "monad.hpp"
+#include "value.hpp"
+#include "perf.hpp"
+#include "qpid.hpp"
+
+// Ignore conversion issues and redundant declarations in Qpid headers
+#ifdef WANT_MAINTAINER_WARNINGS
+#pragma GCC diagnostic ignored "-Wconversion"
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#endif
+
+namespace tuscany {
+namespace queue {
+
+const value key(mklist<value>("test"));
+const string qname("testq");
+
+bool testDeclareQueue() {
+ QpidConnection qc;
+ QpidSession qs(qc);
+ const failable<bool> r = declareQueue(key, qname, qs);
+ assert(hasContent(r));
+ return true;
+}
+
+const list<value> item = list<value>()
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "price" + string("$2.99"));
+const list<value> entry = mklist<value>(string("item"), string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), item);
+
+bool testPost() {
+ QpidConnection qc;
+ QpidSession qs(qc);
+ const failable<bool> r = post(key, entry, qs);
+ assert(hasContent(r));
+ return true;
+}
+
+const bool listener(const value& k, const value& v) {
+ assert(k == key);
+ assert(v == entry);
+ return false;
+}
+
+bool testListen() {
+ QpidConnection qc;
+ QpidSession qs(qc);
+ QpidSubscription qsub(qs);
+ const lambda<bool(const value&, const value&)> l(listener);
+ listen(qname, l, qsub);
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::queue::testDeclareQueue();
+ tuscany::queue::testPost();
+ tuscany::queue::testListen();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/queue/qpid.hpp b/sca-cpp/branches/lightweight-sca/components/queue/qpid.hpp
new file mode 100644
index 0000000000..ef53c529e8
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/queue/qpid.hpp
@@ -0,0 +1,277 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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_qpid_hpp
+#define tuscany_qpid_hpp
+
+/**
+ * AMQP queue access functions.
+ */
+
+// Ignore conversion issues and redundant declarations in Qpid headers
+#ifdef WANT_MAINTAINER_WARNINGS
+#pragma GCC diagnostic ignored "-Wconversion"
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#endif
+
+#include <qpid/client/Connection.h>
+#include <qpid/client/Session.h>
+#include <qpid/client/MessageListener.h>
+#include <qpid/client/SubscriptionManager.h>
+
+#include "string.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "../../modules/scheme/eval.hpp"
+
+namespace tuscany {
+namespace queue {
+
+/**
+ * Represents a Qpid connection.
+ */
+class QpidConnection {
+public:
+ QpidConnection() : owner(true) {
+ debug("queue::qpidonnection");
+ c.open("localhost", 5672);
+ }
+
+ QpidConnection(const bool owner) : owner(owner) {
+ debug("queue::qpidonnection");
+ c.open("localhost", 5672);
+ }
+
+ QpidConnection(const QpidConnection& qc) : owner(false), c(qc.c) {
+ debug("queue::qpidonnection::copy");
+ }
+
+ const QpidConnection& operator=(const QpidConnection& qc) {
+ debug("queue::qpidonnection::operator=");
+ if(this == &c)
+ return *this;
+ owner = false;
+ c = qc.c;
+ return *this;
+ }
+
+ ~QpidConnection() {
+ debug("queue::~qpidonnection");
+ if (!owner)
+ return;
+ c.close();
+ }
+
+private:
+ friend const failable<bool> close(QpidConnection& qc);
+ friend class QpidSession;
+
+ const bool owner;
+ qpid::client::Connection c;
+
+};
+
+/**
+ * Close a Qpid connection.
+ */
+const failable<bool> close(QpidConnection& qc) {
+ qc.c.close();
+ return true;
+}
+
+/**
+ * Represents a Qpid session.
+ */
+class QpidSession {
+public:
+ QpidSession(QpidConnection& qc) : owner(true), s(qc.c.newSession()) {
+ debug("queue::qpidsession");
+ }
+
+ QpidSession(QpidConnection& qc, const bool owner) : owner(owner), s(qc.c.newSession()) {
+ debug("queue::qpidsession");
+ }
+
+ QpidSession(const QpidSession& qs) : owner(false), s(qs.s) {
+ debug("queue::qpidsession::copy");
+ }
+
+ ~QpidSession() {
+ debug("queue::~qpidsession");
+ if (!owner)
+ return;
+ s.close();
+ }
+
+private:
+ friend const failable<bool> close(QpidSession& qs);
+ friend const failable<bool> declareQueue(const value& key, const string& name, QpidSession& qs);
+ friend const failable<bool> post(const value& key, const value& val, QpidSession& qs);
+ friend class QpidSubscription;
+
+ const bool owner;
+ qpid::client::Session s;
+};
+
+/**
+ * Close a Qpid session.
+ */
+const failable<bool> close(QpidSession& qs) {
+ try {
+ qs.s.close();
+ } catch (const qpid::Exception& e) {
+ return mkfailure<bool>(string("Qpid failure: ") + e.what());
+ }
+ return true;
+}
+
+/**
+ * Declare a key / AMQP queue pair.
+ */
+const failable<bool> declareQueue(const value& key, const string& name, QpidSession& qs) {
+ const string ks(scheme::writeValue(key));
+ try {
+ qs.s.queueDeclare(qpid::client::arg::queue=c_str(name));
+ qs.s.exchangeBind(qpid::client::arg::exchange="amq.direct", qpid::client::arg::queue=c_str(name), qpid::client::arg::bindingKey=c_str(ks));
+ } catch (const qpid::Exception& e) {
+ return mkfailure<bool>(string("Qpid failure: ") + e.what());
+ }
+ return true;
+}
+
+/**
+ * Post a key / value pair message to an AMQP broker.
+ */
+const failable<bool> post(const value& key, const value& val, QpidSession& qs) {
+
+ // Send in a message with the given key.
+ const string ks(scheme::writeValue(key));
+ const string vs(scheme::writeValue(val));
+ try {
+ qpid::client::Message message;
+ message.getDeliveryProperties().setRoutingKey(c_str(ks));
+ message.setData(c_str(vs));
+ qs.s.messageTransfer(qpid::client::arg::content=message, qpid::client::arg::destination="amq.direct");
+ } catch (const qpid::Exception& e) {
+ return mkfailure<bool>(string("Qpid failure: ") + e.what());
+ }
+ return true;
+}
+
+/**
+ * Represents a Qpid subscription.
+ */
+class QpidSubscription {
+public:
+ QpidSubscription(QpidSession& qs) : owner(true), subs(qs.s) {
+ }
+
+ QpidSubscription(QpidSession& qs, const bool owner) : owner(owner), subs(qs.s) {
+ }
+
+ QpidSubscription(const QpidSubscription& qsub) : owner(false), subs(qsub.subs) {
+ }
+
+ ~QpidSubscription() {
+ if (!owner)
+ return;
+ try {
+ subs.stop();
+ } catch (const qpid::Exception& e) {
+ mkfailure<bool>(string("Qpid failure: ") + e.what());
+ }
+ }
+
+private:
+ friend const failable<bool> listen(const string& name, const lambda<bool(const value&, const value&)>& l, QpidSubscription& qsub);
+ friend const failable<bool> stop(QpidSubscription& qsub);
+
+ const bool owner;
+ qpid::client::SubscriptionManager subs;
+};
+
+/**
+ * Register a listener function with an AMQP queue.
+ */
+class Listener : public qpid::client::MessageListener {
+public:
+ Listener(const lambda<bool(const value&, const value&)> l, qpid::client::SubscriptionManager& subs) : l(l), subs(subs) {
+ }
+
+ virtual void received(qpid::client::Message& msg) {
+
+ // Call the listener function
+ const value k(scheme::readValue(msg.getDeliveryProperties().getRoutingKey().c_str()));
+ const value v(scheme::readValue(msg.getData().c_str()));
+ const bool r = l(k, v);
+ if (!r) {
+ try {
+ subs.cancel(msg.getDestination());
+ } catch (const qpid::Exception& e) {
+ mkfailure<bool>(string("Qpid failure: ") + e.what());
+ }
+ }
+ }
+
+private:
+ const lambda<bool(const value&, const value&)> l;
+ qpid::client::SubscriptionManager& subs;
+};
+
+
+const failable<bool> listen(const string& name, const lambda<bool(const value&, const value&)>& l, QpidSubscription& qsub) {
+ debug("queue::listen");
+ Listener listener(l, qsub.subs);
+ try {
+ qsub.subs.subscribe(listener, c_str(name));
+ qsub.subs.run();
+ } catch (const qpid::Exception& e) {
+ return mkfailure<bool>(string("Qpid failure: ") + e.what());
+ }
+ debug("queue::listen::stopped");
+ return true;
+}
+
+/**
+ * Stop an AMQP subscription.
+ */
+const failable<bool> stop(QpidSubscription& qsub) {
+ debug("queue::stop");
+ try {
+ qsub.subs.stop();
+ } catch (const qpid::Exception& e) {
+ return mkfailure<bool>(string("Qpid failure: ") + e.what());
+ }
+ debug("queue::stopped");
+ return true;
+}
+
+}
+}
+
+// Re-enable conversion and redundant declarations warnings
+#ifdef WANT_MAINTAINER_WARNINGS
+#pragma GCC diagnostic warning "-Wconversion"
+#pragma GCC diagnostic warning "-Wredundant-decls"
+#endif
+
+#endif /* tuscany_qpid_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/components/queue/qpidd-start b/sca-cpp/branches/lightweight-sca/components/queue/qpidd-start
new file mode 100755
index 0000000000..a65065182c
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/queue/qpidd-start
@@ -0,0 +1,24 @@
+#!/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 qpidd
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+
+qpid_prefix=`cat $here/qpidc.prefix`
+$qpid_prefix/sbin/qpidd &
diff --git a/sca-cpp/branches/lightweight-sca/components/queue/qpidd-stop b/sca-cpp/branches/lightweight-sca/components/queue/qpidd-stop
new file mode 100755
index 0000000000..c8af680d78
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/queue/qpidd-stop
@@ -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.
+
+# Stop qpidd
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+
+qpid_prefix=`cat $here/qpidc.prefix`
+qpidd="$qpid_prefix/sbin/qpidd"
+
+k=`ps -ef | grep -v grep | grep "${qpidd}" | awk '{ print $2 }'`
+if [ "$k" != "" ]; then
+ kill $k
+fi
+
diff --git a/sca-cpp/branches/lightweight-sca/components/queue/queue-listener.componentType b/sca-cpp/branches/lightweight-sca/components/queue/queue-listener.componentType
new file mode 100644
index 0000000000..1e94f9a2df
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/queue/queue-listener.componentType
@@ -0,0 +1,29 @@
+<?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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <reference name="relay"/>
+ <property name="key" type="xsd:string"/>
+ <property name="queue" type="xsd:string"/>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/queue/queue-listener.cpp b/sca-cpp/branches/lightweight-sca/components/queue/queue-listener.cpp
new file mode 100644
index 0000000000..483d0de65a
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/queue/queue-listener.cpp
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * AMQP queue listener component implementation.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "parallel.hpp"
+#include "qpid.hpp"
+
+// Ignore conversion issues and redundant declarations in Qpid headers
+#ifdef WANT_MAINTAINER_WARNINGS
+#pragma GCC diagnostic ignored "-Wconversion"
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#endif
+
+namespace tuscany {
+namespace queue {
+
+/**
+ * A relay function that posts the AMQP messages it receives to a relay component reference.
+ */
+class relay {
+public:
+ relay(const lambda<value(const list<value>&)>& rel) : rel(rel) {
+ }
+
+ const bool operator()(const value& k, const value& v) const {
+ debug(k, "queue::relay::key");
+ debug(v, "queue::relay::value");
+ const value res = rel(mklist<value>("post", isList(k)? (list<value>)k : mklist<value>(k), v));
+ return true;
+ }
+
+private:
+ const lambda<value(const list<value>&)> rel;
+};
+
+/**
+ * Subscribe and listen to an AMQP queue.
+ */
+class subscribe {
+public:
+ subscribe(const string& qname, const lambda<bool(const value&, const value&)>& l, const QpidSubscription& qsub) : qname(qname), l(l), qsub(qsub) {
+ }
+
+ const failable<bool> operator()() const {
+ gc_pool pool;
+ debug(qname, "queue::subscribe::listen");
+ const failable<bool> r = listen(qname, l, const_cast<QpidSubscription&>(qsub));
+ debug(qname, "queue::subscribe::stopped");
+ return r;
+ }
+
+private:
+ const string qname;
+ const lambda<bool(const value&, const value&)> l;
+ const QpidSubscription qsub;
+};
+
+/**
+ * Listener lambda function, responsible for starting an AMQP subscription in a worker thread, and
+ * apply any function calls to the listener component. The only supported function is stop(),
+ * called to stop the listener component and shutdown the worker thread.
+ */
+class listener {
+public:
+ listener(QpidConnection& qc, QpidSession& qs, QpidSubscription& qsub, worker& w) : qc(qc), qs(qs), qsub(qsub), w(w) {
+ }
+
+ const value operator()(const list<value>& params) const {
+ const tuscany::value func(car(params));
+
+ // Stop the component
+ if (func != "stop")
+ return mkfailure<value>();
+ debug("queue::listener::stop");
+
+ // TODO check why stop() and close() hang in child processes
+ stop(const_cast<QpidSubscription&>(qsub));
+ close(const_cast<QpidSession&>(qs));
+ close(const_cast<QpidConnection&>(qc));
+ cancel(const_cast<worker&>(w));
+
+ debug("queue::listener::stopped");
+ return failable<value>(value(lambda<value(const list<value>&)>()));
+ }
+
+private:
+ QpidConnection qc;
+ QpidSession qs;
+ QpidSubscription qsub;
+ worker w;
+};
+
+/**
+ * Start the component.
+ */
+const failable<value> start(const list<value>& params) {
+ // Extract the relay reference and the AMQP key and queue name
+ const value rel = car(params);
+ const value pk = ((lambda<value(const list<value>&)>)cadr(params))(list<value>());
+ const value key = isList(pk)? (list<value>)pk : mklist<value>(pk);
+ const value qname = ((lambda<value(const list<value>&)>)caddr(params))(list<value>());
+
+ // Create an AMQP session
+ QpidConnection qc(false);
+ QpidSession qs(qc, false);
+
+ // Declare the configured AMQP key / queue pair
+ declareQueue(key, qname, qs);
+
+ // Listen and relay messages in a worker thread
+ QpidSubscription qsub(qs, false);
+ worker w(3);
+ const lambda<bool(const value&, const value&)> rl = relay(rel);
+ submit<failable<bool> >(w, lambda<failable<bool>()>(subscribe(qname, rl, qsub)));
+
+ // Return the listener component lambda function
+ return value(lambda<value(const list<value>&)>(listener(qc, qs, qsub, w)));
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "start")
+ return tuscany::queue::start(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/queue/queue-sender.componentType b/sca-cpp/branches/lightweight-sca/components/queue/queue-sender.componentType
new file mode 100644
index 0000000000..fc06bf2dcf
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/queue/queue-sender.componentType
@@ -0,0 +1,28 @@
+<?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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <service name="sender"/>
+ <property name="key" type="xsd:string"/>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/queue/queue-sender.cpp b/sca-cpp/branches/lightweight-sca/components/queue/queue-sender.cpp
new file mode 100644
index 0000000000..202a0e4435
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/queue/queue-sender.cpp
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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$ */
+
+/**
+ * AMQP queue sender component implementation.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "qpid.hpp"
+
+// Ignore conversion issues and redundant declarations in Qpid headers
+#ifdef WANT_MAINTAINER_WARNINGS
+#pragma GCC diagnostic ignored "-Wconversion"
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#endif
+
+namespace tuscany {
+namespace queue {
+
+/**
+ * Post an item to a queue.
+ */
+const failable<value> post(const list<value>& params) {
+ QpidConnection qc;
+ QpidSession qs(qc);
+
+ // Post the item
+ const value pk = ((lambda<value(const list<value>&)>)caddr(params))(list<value>());
+ const value key = isList(pk)? append<value>(pk, (list<value>)car(params)) : cons<value>(pk, (list<value>)car(params));
+ debug(key, "queue::post::key");
+ debug(cadr(params), "queue::post::value");
+ const failable<bool> r = post(key, cadr(params), qs);
+ if (!hasContent(r))
+ return mkfailure<value>(r);
+ return key;
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "post")
+ return tuscany::queue::post(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/queue/queue.composite b/sca-cpp/branches/lightweight-sca/components/queue/queue.composite
new file mode 100644
index 0000000000..9b6939e4bc
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/queue/queue.composite
@@ -0,0 +1,55 @@
+<?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://tuscany.apache.org/xmlns/sca/components"
+ name="queue">
+
+ <component name="print-sender">
+ <implementation.cpp path="." library="libqueue-sender"/>
+ <property name="key">print</property>
+ <service name="print-sender">
+ <binding.http uri="print-sender"/>
+ </service>
+ </component>
+
+ <component name="print-listener">
+ <implementation.cpp path="." library="libqueue-listener"/>
+ <property name="key">print</property>
+ <property name="queue">printq</property>
+ <reference name="relay" target="print"/>
+ </component>
+
+ <component name="print">
+ <implementation.scheme script="server-test.scm"/>
+ <service name="print">
+ <binding.http uri="print"/>
+ </service>
+ <reference name="report" target="report-sender"/>
+ </component>
+
+ <component name="report-sender">
+ <implementation.cpp path="." library="libqueue-sender"/>
+ <property name="key">report</property>
+ <service name="report-sender">
+ <binding.http uri="report-sender"/>
+ </service>
+ </component>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/queue/send-test b/sca-cpp/branches/lightweight-sca/components/queue/send-test
new file mode 100755
index 0000000000..f2cc53d851
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/queue/send-test
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Setup
+./qpidd-start
+sleep 1
+
+# Test
+./qpid-test 2>/dev/null
+rc=$?
+
+# Cleanup
+./qpidd-stop
+sleep 1
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/components/queue/server-test b/sca-cpp/branches/lightweight-sca/components/queue/server-test
new file mode 100755
index 0000000000..269d3f9376
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/queue/server-test
@@ -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.
+
+# Setup
+rm -rf tmp
+../../modules/http/httpd-conf tmp localhost 8090 ../../modules/http/htdocs
+../../modules/http/httpd-event-conf tmp
+../../modules/server/server-conf tmp
+../../modules/server/scheme-conf tmp
+cat >>tmp/conf/httpd.conf <<EOF
+SCAContribution `pwd`/
+SCAComposite queue.composite
+EOF
+
+./qpidd-start
+sleep 1
+../../modules/http/httpd-start tmp
+sleep 2
+
+# Test
+./client-test 2>/dev/null
+rc=$?
+
+# Cleanup
+../../modules/http/httpd-stop tmp
+sleep 1
+./qpidd-stop
+sleep 1
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/components/queue/server-test.scm b/sca-cpp/branches/lightweight-sca/components/queue/server-test.scm
new file mode 100644
index 0000000000..1a89ce8b31
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/queue/server-test.scm
@@ -0,0 +1,20 @@
+; Licensed to the Apache Software Foundation (ASF) under one
+; or more contributor license agreements. See the NOTICE file
+; distributed with this work for additional information
+; regarding copyright ownership. The ASF licenses this file
+; to you under the Apache License, Version 2.0 (the
+; "License"); you may not use this file except in compliance
+; with the License. You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing,
+; software distributed under the License is distributed on an
+; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+; KIND, either express or implied. See the License for the
+; specific language governing permissions and limitations
+; under the License.
+
+; Queue test case
+
+(define (post key val report) (report "post" '() val))
diff --git a/sca-cpp/branches/lightweight-sca/components/smtp/Makefile.am b/sca-cpp/branches/lightweight-sca/components/smtp/Makefile.am
new file mode 100644
index 0000000000..41fa686b9a
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/smtp/Makefile.am
@@ -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.
+
+compdir=$(prefix)/components/smtp
+
+EXTRA_DIST = smtp.composite smtppost.componentType *.scm
+
+comp_LTLIBRARIES = libsmtppost.la
+noinst_DATA = libsmtppost${libsuffix}
+
+libsmtppost_la_SOURCES = smtppost.cpp
+libsmtppost_la_LDFLAGS = -lxml2 -lmozjs -curl
+libsmtppost${libsuffix}:
+ ln -s .libs/libsmtppost${libsuffix}
+
+client_test_SOURCES = client-test.cpp
+client_test_LDFLAGS = -lxml2 -lcurl -lmozjs
+
+dist_noinst_SCRIPTS = server-test
+noinst_PROGRAMS = client-test
+#TESTS = server-test
+
diff --git a/sca-cpp/branches/lightweight-sca/components/smtp/client-test.cpp b/sca-cpp/branches/lightweight-sca/components/smtp/client-test.cpp
new file mode 100644
index 0000000000..10274a6248
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/smtp/client-test.cpp
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test SMTP post component.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "perf.hpp"
+#include "../../modules/http/http.hpp"
+
+namespace tuscany {
+namespace smtp {
+
+const string postURI("http://localhost:8090/smtppost");
+
+bool testPost() {
+ http::CURLSession cs("", "", "", "", 0);
+
+ const failable<value> val = http::get(postURI, cs);
+ assert(hasContent(val));
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::smtp::testPost();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/smtp/content-test.scm b/sca-cpp/branches/lightweight-sca/components/smtp/content-test.scm
new file mode 100644
index 0000000000..eaf4a868c5
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/smtp/content-test.scm
@@ -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.
+
+; Content test case
+
+(define (get id)
+(list "text/plain; charset=utf-8" (list
+"test test test
+
+- Jean-Sebastien"
+))
+)
+
diff --git a/sca-cpp/branches/lightweight-sca/components/smtp/from-test.scm b/sca-cpp/branches/lightweight-sca/components/smtp/from-test.scm
new file mode 100644
index 0000000000..083c060844
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/smtp/from-test.scm
@@ -0,0 +1,23 @@
+; Licensed to the Apache Software Foundation (ASF) under one
+; or more contributor license agreements. See the NOTICE file
+; distributed with this work for additional information
+; regarding copyright ownership. The ASF licenses this file
+; to you under the Apache License, Version 2.0 (the
+; "License"); you may not use this file except in compliance
+; with the License. You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing,
+; software distributed under the License is distributed on an
+; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+; KIND, either express or implied. See the License for the
+; specific language governing permissions and limitations
+; under the License.
+
+; From test case
+
+(define (get id)
+ "<jane@example.com>"
+)
+
diff --git a/sca-cpp/branches/lightweight-sca/components/smtp/password-test.scm b/sca-cpp/branches/lightweight-sca/components/smtp/password-test.scm
new file mode 100644
index 0000000000..0a28776865
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/smtp/password-test.scm
@@ -0,0 +1,23 @@
+; Licensed to the Apache Software Foundation (ASF) under one
+; or more contributor license agreements. See the NOTICE file
+; distributed with this work for additional information
+; regarding copyright ownership. The ASF licenses this file
+; to you under the Apache License, Version 2.0 (the
+; "License"); you may not use this file except in compliance
+; with the License. You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing,
+; software distributed under the License is distributed on an
+; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+; KIND, either express or implied. See the License for the
+; specific language governing permissions and limitations
+; under the License.
+
+; Password test case
+
+(define (get id)
+ "password"
+)
+
diff --git a/sca-cpp/branches/lightweight-sca/components/smtp/server-test b/sca-cpp/branches/lightweight-sca/components/smtp/server-test
new file mode 100755
index 0000000000..c111021c85
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/smtp/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
+../../modules/http/httpd-conf tmp localhost 8090 ../../modules/http/htdocs
+../../modules/http/httpd-loglevel-conf tmp debug
+../../modules/server/server-conf tmp
+../../modules/server/scheme-conf tmp
+cat >>tmp/conf/httpd.conf <<EOF
+SCAContribution `pwd`/
+SCAComposite smtp.composite
+EOF
+
+../../modules/http/httpd-start tmp
+sleep 2
+
+# Test
+./client-test #2>/dev/null
+rc=$?
+
+# Cleanup
+../../modules/http/httpd-stop tmp
+sleep 2
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/components/smtp/smtp.composite b/sca-cpp/branches/lightweight-sca/components/smtp/smtp.composite
new file mode 100644
index 0000000000..c740b68bc9
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/smtp/smtp.composite
@@ -0,0 +1,87 @@
+<?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://tuscany.apache.org/xmlns/sca/components"
+ name="smtp">
+
+ <component name="smtppost">
+ <implementation.cpp path="." library="libsmtppost"/>
+ <service name="smtppost">
+ <binding.http uri="smtppost"/>
+ </service>
+ <reference name="url" target="url-test"/>
+ <reference name="user" target="user-test"/>
+ <reference name="password" target="password-test"/>
+ <reference name="from" target="from-test"/>
+ <reference name="to" target="to-test"/>
+ <reference name="subject" target="subject-test"/>
+ <reference name="content" target="content-test"/>
+ </component>
+
+ <component name="url-test">
+ <implementation.scheme script="url-test.scm"/>
+ <service name="url-test">
+ <binding.http uri="url-test"/>
+ </service>
+ </component>
+
+ <component name="password-test">
+ <implementation.scheme script="password-test.scm"/>
+ <service name="password-test">
+ <binding.http uri="password-test"/>
+ </service>
+ </component>
+
+ <component name="user-test">
+ <implementation.scheme script="user-test.scm"/>
+ <service name="user-test">
+ <binding.http uri="user-test"/>
+ </service>
+ </component>
+
+ <component name="from-test">
+ <implementation.scheme script="from-test.scm"/>
+ <service name="from-test">
+ <binding.http uri="from-test"/>
+ </service>
+ </component>
+
+ <component name="to-test">
+ <implementation.scheme script="to-test.scm"/>
+ <service name="to-test">
+ <binding.http uri="to-test"/>
+ </service>
+ </component>
+
+ <component name="subject-test">
+ <implementation.scheme script="subject-test.scm"/>
+ <service name="subject-test">
+ <binding.http uri="subject-test"/>
+ </service>
+ </component>
+
+ <component name="content-test">
+ <implementation.scheme script="content-test.scm"/>
+ <service name="content-test">
+ <binding.http uri="content-test"/>
+ </service>
+ </component>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/smtp/smtppost.componentType b/sca-cpp/branches/lightweight-sca/components/smtp/smtppost.componentType
new file mode 100644
index 0000000000..f33673b53e
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/smtp/smtppost.componentType
@@ -0,0 +1,34 @@
+<?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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <service name="smtppost"/>
+ <reference name="url"/>
+ <reference name="user"/>
+ <reference name="password"/>
+ <reference name="from"/>
+ <reference name="to"/>
+ <reference name="subject"/>
+ <reference name="content"/>
+
+</componentType>
diff --git a/sca-cpp/branches/lightweight-sca/components/smtp/smtppost.cpp b/sca-cpp/branches/lightweight-sca/components/smtp/smtppost.cpp
new file mode 100644
index 0000000000..1030ccc223
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/smtp/smtppost.cpp
@@ -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$ */
+
+/**
+ * SMTP client component implementation.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "parallel.hpp"
+#include "../../modules/http/http.hpp"
+
+namespace tuscany {
+namespace smtppost {
+
+/**
+ * Post/send an email message using SMTP.
+ */
+const failable<value> post(const string& url, const string& user, const string& pass, const string& from, const string& to, const string& subject, const value& val, http::CURLSession& cs) {
+ // Convert value to a content request
+ const failable<list<list<string> > > freq = http::contentRequest(val, url);
+ if (!hasContent(freq))
+ return mkfailure<value>(freq);
+ const list<list<string> > req = content(freq);
+ debug(req, "smtp::post::input");
+
+ // Setup the CURL session
+ const failable<CURL*> fch = http::setup(url, cs);
+ if (!hasContent(fch)) {
+ http::cleanup(cs);
+ return mkfailure<value>(fch);
+ }
+ CURL* ch = content(fch);
+ curl_easy_setopt(ch, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);
+
+ // Convert message to a string
+ ostringstream os;
+ os << "From: " << from << "\n";
+ os << "To: " << to << "\n";
+ os << "Subject: " << subject << "\n";
+ os << car(car(req)) << "\n\n";
+ write(cadr(req), os);
+
+ // Setup the read callbacks
+ http::CURLReadContext rcx(mklist(str(os)));
+ curl_easy_setopt(ch, CURLOPT_READFUNCTION, (size_t (*)(void*, size_t, size_t, void*))http::readCallback);
+ curl_easy_setopt(ch, CURLOPT_READDATA, &rcx);
+
+ // Setup the message properties
+ curl_easy_setopt(ch, CURLOPT_USERNAME, c_str(user));
+ curl_easy_setopt(ch, CURLOPT_PASSWORD, c_str(pass));
+ curl_easy_setopt(ch, CURLOPT_MAIL_FROM, c_str(from));
+ struct curl_slist* rcpt = curl_slist_append(NULL, c_str(to));
+ curl_easy_setopt(ch, CURLOPT_MAIL_RCPT, rcpt);
+
+ // Perform the SMTP send
+ const CURLcode rc = curl_easy_perform(ch);
+
+ // Free the recipients
+ curl_slist_free_all(rcpt);
+
+ // Return the CURL return code or true
+ if (rc) {
+ http::cleanup(cs);
+ return mkfailure<value>(string(curl_easy_strerror(rc)));
+ }
+
+ http::cleanup(cs);
+ return value(true);
+}
+
+/**
+ * Evaluate an SMTP post/send.
+ */
+const failable<value> get(const lambda<value(const list<value>&)>& url,
+ const lambda<value(const list<value>&)>& user, const lambda<value(const list<value>&)>& pass,
+ const lambda<value(const list<value>&)>& from, const lambda<value(const list<value>&)>& to,
+ const lambda<value(const list<value>&)>& subject, const lambda<value(const list<value>&)>& val,
+ http::CURLSession& ch) {
+ debug("smtppost::get");
+ const value u = url(mklist<value>("get", list<value>()));
+ const value i = user(mklist<value>("get", list<value>()));
+ const value p = pass(mklist<value>("get", list<value>()));
+ const value f = from(mklist<value>("get", list<value>()));
+ const value t = to(mklist<value>("get", list<value>()));
+ const value s = subject(mklist<value>("get", list<value>()));
+ const value v = val(mklist<value>("get", list<value>()));
+ debug(u, "smtppost::get::url");
+ debug(i, "smtppost::get::user");
+ debug(p, "smtppost::get::pass");
+ debug(f, "smtppost::get::from");
+ debug(t, "smtppost::get::to");
+ debug(s, "smtppost::get::subject");
+ debug(v, "smtppost::get::val");
+ return post(u, i, p, f, t, s, v, ch);
+}
+
+/**
+ * Component implementation lambda function.
+ */
+class applysmtp {
+public:
+ applysmtp(const lambda<value(const list<value>&)>& url,
+ const lambda<value(const list<value>&)>& user, const lambda<value(const list<value>&)>& pass,
+ const lambda<value(const list<value>&)>& from, const lambda<value(const list<value>&)>& to,
+ const lambda<value(const list<value>&)>& subject, const lambda<value(const list<value>&)>& val,
+ perthread_ptr<http::CURLSession>& ch) :
+ url(url), user(user), pass(pass), from(from), to(to), subject(subject), val(val), ch(ch) {
+ }
+
+ const value operator()(const list<value>& params) const {
+ debug(params, "smtppost::applysmtp::params");
+ const value func(car(params));
+ if (func == "get")
+ return get(url, user, pass, from, to, subject, val, *ch);
+ return mkfailure<value>();
+ }
+
+private:
+ const lambda<value(const list<value>&)> url;
+ const lambda<value(const list<value>&)> user;
+ const lambda<value(const list<value>&)> pass;
+ const lambda<value(const list<value>&)> from;
+ const lambda<value(const list<value>&)> to;
+ const lambda<value(const list<value>&)> subject;
+ const lambda<value(const list<value>&)> val;
+ perthread_ptr<http::CURLSession> ch;
+};
+
+/**
+ * Create a new CURL session.
+ */
+const gc_ptr<http::CURLSession> newsession() {
+ return new (gc_new<http::CURLSession>()) http::CURLSession("", "", "", "", 0);
+}
+
+/**
+ * Start the component.
+ */
+const failable<value> start(const list<value>& params) {
+ // Create a CURL session
+ perthread_ptr<http::CURLSession> ch = perthread_ptr<http::CURLSession>(lambda<gc_ptr<http::CURLSession>()>(newsession));
+
+ // Return the component implementation lambda function
+ return value(lambda<value(const list<value>&)>(applysmtp(car(params), cadr(params), caddr(params), cadddr(params), caddddr(params), cadddddr(params), caddddddr(params), ch)));
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "start")
+ return tuscany::smtppost::start(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/smtp/subject-test.scm b/sca-cpp/branches/lightweight-sca/components/smtp/subject-test.scm
new file mode 100644
index 0000000000..6a296cbea8
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/smtp/subject-test.scm
@@ -0,0 +1,23 @@
+; Licensed to the Apache Software Foundation (ASF) under one
+; or more contributor license agreements. See the NOTICE file
+; distributed with this work for additional information
+; regarding copyright ownership. The ASF licenses this file
+; to you under the Apache License, Version 2.0 (the
+; "License"); you may not use this file except in compliance
+; with the License. You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing,
+; software distributed under the License is distributed on an
+; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+; KIND, either express or implied. See the License for the
+; specific language governing permissions and limitations
+; under the License.
+
+; Subject test case
+
+(define (get id)
+ "Test email"
+)
+
diff --git a/sca-cpp/branches/lightweight-sca/components/smtp/to-test.scm b/sca-cpp/branches/lightweight-sca/components/smtp/to-test.scm
new file mode 100644
index 0000000000..079df89301
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/smtp/to-test.scm
@@ -0,0 +1,23 @@
+; Licensed to the Apache Software Foundation (ASF) under one
+; or more contributor license agreements. See the NOTICE file
+; distributed with this work for additional information
+; regarding copyright ownership. The ASF licenses this file
+; to you under the Apache License, Version 2.0 (the
+; "License"); you may not use this file except in compliance
+; with the License. You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing,
+; software distributed under the License is distributed on an
+; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+; KIND, either express or implied. See the License for the
+; specific language governing permissions and limitations
+; under the License.
+
+; To test case
+
+(define (get id)
+ "<jane@example.com>"
+)
+
diff --git a/sca-cpp/branches/lightweight-sca/components/smtp/url-test.scm b/sca-cpp/branches/lightweight-sca/components/smtp/url-test.scm
new file mode 100644
index 0000000000..aa73c59d5c
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/smtp/url-test.scm
@@ -0,0 +1,23 @@
+; Licensed to the Apache Software Foundation (ASF) under one
+; or more contributor license agreements. See the NOTICE file
+; distributed with this work for additional information
+; regarding copyright ownership. The ASF licenses this file
+; to you under the Apache License, Version 2.0 (the
+; "License"); you may not use this file except in compliance
+; with the License. You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing,
+; software distributed under the License is distributed on an
+; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+; KIND, either express or implied. See the License for the
+; specific language governing permissions and limitations
+; under the License.
+
+; URL test case
+
+(define (get id)
+ "smtp://smtp.gmail.com:587"
+)
+
diff --git a/sca-cpp/branches/lightweight-sca/components/smtp/user-test.scm b/sca-cpp/branches/lightweight-sca/components/smtp/user-test.scm
new file mode 100644
index 0000000000..13862ac70c
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/smtp/user-test.scm
@@ -0,0 +1,23 @@
+; Licensed to the Apache Software Foundation (ASF) under one
+; or more contributor license agreements. See the NOTICE file
+; distributed with this work for additional information
+; regarding copyright ownership. The ASF licenses this file
+; to you under the Apache License, Version 2.0 (the
+; "License"); you may not use this file except in compliance
+; with the License. You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing,
+; software distributed under the License is distributed on an
+; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+; KIND, either express or implied. See the License for the
+; specific language governing permissions and limitations
+; under the License.
+
+; URL test case
+
+(define (get id)
+ "jane"
+)
+
diff --git a/sca-cpp/branches/lightweight-sca/components/sqldb/Makefile.am b/sca-cpp/branches/lightweight-sca/components/sqldb/Makefile.am
new file mode 100644
index 0000000000..9ce5f26713
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/sqldb/Makefile.am
@@ -0,0 +1,57 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT 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_SQLDB
+
+INCLUDES = -I${PGSQL_INCLUDE}
+
+incl_HEADERS = *.hpp
+incldir = $(prefix)/include/components/sqldb
+
+dist_comp_SCRIPTS = pgsql-conf pgsql-log-conf pgsql-start pgsql-stop pgsql pgsql-standby-conf pgsql-archive pgsql-backup pgsql-restore pgsql-clean-archive
+compdir=$(prefix)/components/sqldb
+
+comp_DATA = pgsql.prefix pgbouncer.prefix
+pgsql.prefix: $(top_builddir)/config.status
+ echo ${PGSQL_PREFIX} >pgsql.prefix
+pgbouncer.prefix: $(top_builddir)/config.status
+ echo ${PGBOUNCER_PREFIX} >pgbouncer.prefix
+
+EXTRA_DIST = sqldb.composite sqldb.componentType
+
+comp_LTLIBRARIES = libsqldb.la
+noinst_DATA = libsqldb${libsuffix}
+
+libsqldb_la_SOURCES = sqldb.cpp
+libsqldb_la_LDFLAGS = -L${PGSQL_LIB} -R${PGSQL_LIB} -lpq
+libsqldb${libsuffix}:
+ ln -s .libs/libsqldb${libsuffix}
+
+pgsql_test_SOURCES = pgsql-test.cpp
+pgsql_test_LDFLAGS = -L${PGSQL_LIB} -R${PGSQL_LIB} -lpq
+
+pgsql_standby_test_SOURCES = pgsql-standby-test.cpp
+pgsql_standby_test_LDFLAGS = -L${PGSQL_LIB} -R${PGSQL_LIB} -lpq
+
+client_test_SOURCES = client-test.cpp
+client_test_LDFLAGS = -lxml2 -lcurl -lmozjs
+
+dist_noinst_SCRIPTS = sqldb-test standby-test server-test
+noinst_PROGRAMS = pgsql-test pgsql-standby-test client-test
+TESTS = sqldb-test standby-test server-test
+
+endif
diff --git a/sca-cpp/branches/lightweight-sca/components/sqldb/client-test.cpp b/sca-cpp/branches/lightweight-sca/components/sqldb/client-test.cpp
new file mode 100644
index 0000000000..0cbcb57363
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/sqldb/client-test.cpp
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 SQL database component.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "perf.hpp"
+#include "../../modules/http/http.hpp"
+
+namespace tuscany {
+namespace sqldb {
+
+const string uri("http://localhost:8090/sqldb");
+
+bool testSqlDb() {
+ http::CURLSession cs("", "", "", "", 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> a = list<value>() + (list<value>() + "entry"
+ + (list<value>() + "title" + string("item"))
+ + (list<value>() + "id" + string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"))
+ + i);
+
+ const failable<value> id = http::post(a, uri, cs);
+ assert(hasContent(id));
+
+ const string p = path(content(id));
+ {
+ const failable<value> val = http::get(uri + p, cs);
+ assert(hasContent(val));
+ assert(content(val) == a);
+ }
+
+ const list<value> j = list<value>() + "content" + (list<value>() + "item"
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "price" + string("$3.55")));
+ const list<value> b = list<value>() + (list<value>() + "entry"
+ + (list<value>() + "title" + string("item"))
+ + (list<value>() + "id" + string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"))
+ + j);
+
+ {
+ const failable<value> r = http::put(b, uri + p, cs);
+ assert(hasContent(r));
+ assert(content(r) == value(true));
+ }
+ {
+ const failable<value> val = http::get(uri + p, cs);
+ assert(hasContent(val));
+ assert(content(val) == b);
+ }
+ {
+ const failable<value> r = http::del(uri + p, cs);
+ assert(hasContent(r));
+ assert(content(r) == value(true));
+ }
+ {
+ const failable<value> val = http::get(uri + p, cs);
+ assert(!hasContent(val));
+ }
+
+ return true;
+}
+
+struct getLoop {
+ const string path;
+ const value entry;
+ http::CURLSession& cs;
+ getLoop(const string& path, const value& entry, http::CURLSession& cs) : path(path), entry(entry), cs(cs) {
+ }
+ const bool operator()() const {
+ const failable<value> val = http::get(uri + path, cs);
+ assert(hasContent(val));
+ assert(content(val) == entry);
+ return true;
+ }
+};
+
+bool testGetPerf() {
+ const list<value> i = list<value>() + "content" + (list<value>() + "item"
+ + (list<value>() + "name" + string("Apple"))
+ + (list<value>() + "price" + string("$4.55")));
+ 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 cs("", "", "", "", 0);
+ const failable<value> id = http::post(a, uri, cs);
+ assert(hasContent(id));
+ const string p = path(content(id));
+
+ const lambda<bool()> gl = getLoop(p, a, cs);
+ cout << "Sqldb get test " << time(gl, 5, 200) << " ms" << endl;
+
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::sqldb::testSqlDb();
+ tuscany::sqldb::testGetPerf();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql
new file mode 100755
index 0000000000..8a60068ab7
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Run SQL command
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+pgsql_prefix=`cat $here/pgsql.prefix`
+
+if [ "$2" = "" ]; then
+ host="localhost"
+ port="6432"
+ cmd="$1"
+else
+ host="$1"
+ port="$2"
+ cmd="$3"
+fi
+
+if [ "$cmd" = "" ]; then
+ $pgsql_prefix/bin/psql -h $host -p $port db
+else
+ $pgsql_prefix/bin/psql -h $host -p $port -c "$cmd" db
+fi
+
diff --git a/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-archive b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-archive
new file mode 100755
index 0000000000..128e6eb539
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-archive
@@ -0,0 +1,59 @@
+#!/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.
+
+# PostgreSQL archive command
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+host=$2
+port=$3
+walp=$4
+walf=$5
+
+# Copy WAL to archive directory
+if [ ! -f $root/sqldb/archive/$walf ]; then
+ cp $walp $root/sqldb/archive/$walf
+ rc=$?
+ if [ "$rc" != "0" ]; then
+ exit $rc
+ fi
+fi
+
+# Trigger a backup if we have 10 WAL files archived since the last backup
+lastbak=`ls $root/sqldb/archive | sort -r | grep "\.backup$" | head -1`
+if [ "$lastbak" = "" ]; then
+ exit 0
+fi
+wals=`ls $root/sqldb/archive | sort -r | grep -v "\.backup\.tar\.gz$"`
+
+w=0
+for f in $wals; do
+ if [ "$f" = "$lastbak" ]; then
+ break
+ fi
+ w=$((w+1))
+
+ if [ "$w" = "10" ]; then
+ nohup /bin/sh -c "$here/pgsql-backup $root $host $port" 1>/dev/null 2>/dev/null &
+ break
+ fi
+done
+
+exit 0
+
diff --git a/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-backup b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-backup
new file mode 100755
index 0000000000..ff660ad20d
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-backup
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Backup postgresql data directory
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+if [ "$2" = "" ]; then
+ host="localhost"
+ port="6432"
+else
+ host="$2"
+ port="$3"
+fi
+
+pgsql_prefix=`cat $here/pgsql.prefix`
+
+if [ -f "$root/sqldb/log.conf" ]; then
+ pgsql_log=`cat $root/sqldb/log.conf`
+else
+ mkdir -p $root/logs
+ pgsql_log="cat >>$root/logs/postgresql"
+fi
+mkdir -p $root/sqldb
+echo $pgsql_log >$root/sqldb/logger
+
+mkdir -p $root/sqldb/backup
+mkdir -p $root/sqldb/archive
+
+# Make sure that only one backup is in progress at a time
+if [ -f $root/sqldb/backup/inprogress ]; then
+ exit 0
+fi
+touch $root/sqldb/backup/inprogress
+
+# Backup
+stamp=`date +%Y%m%d%H%M%S`
+$pgsql_prefix/bin/psql -h $host -p $port -c "SELECT pg_start_backup('$stamp', true)" db 2>&1 | sh $root/sqldb/logger
+
+uname=`uname -s`
+if [ $uname = "Darwin" ]; then
+ tar=gnutar
+else
+ tar=tar
+fi
+$tar -C $root/sqldb --exclude data/postmaster.pid --exclude data/postmaster.opts --exclude data/pg_xlog --ignore-failed-read -czf $root/sqldb/backup/$stamp.backup.tar.gz data
+rc=$?
+if [ "$rc" = "0" ]; then
+ mv $root/sqldb/backup/$stamp.backup.tar.gz $root/sqldb/archive/$stamp.backup.tar.gz
+fi
+
+$pgsql_prefix/bin/psql -h $host -p $port -c "SELECT pg_stop_backup()" db 2>&1 | sh $root/sqldb/logger
+
+if [ "$rc" != "0" ]; then
+ rm -f $root/sqldb/backup/inprogress
+ exit $rc
+fi
+
+# Clean obsolete backup and WAL files
+$here/pgsql-clean-archive $root
+
+rm -f $root/sqldb/backup/inprogress
+
diff --git a/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-clean-archive b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-clean-archive
new file mode 100755
index 0000000000..0e52fade89
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-clean-archive
@@ -0,0 +1,58 @@
+#!/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.
+
+# Cleanup database archive.
+# Keep the last two backups and corresponding WAL files.
+
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+baks=`ls $root/sqldb/archive | sort -r | grep "\.backup$" | head -2`
+wals=`ls $root/sqldb/archive | sort -r | grep -v "\.backup\.tar\.gz$"`
+tars=`ls $root/sqldb/archive | sort -r | grep "\.backup\.tar\.gz$"`
+
+for f in $baks; do
+ if [ "$lastbak" = "" ]; then
+ lastbak=$f
+ else
+ prevbak=$f
+ fi
+done
+
+w=0
+for f in $wals; do
+ if [ "$w" = "2" ]; then
+ rm $root/sqldb/archive/$f
+ fi
+ if [ "$w" = "1" ]; then
+ w=2
+ fi
+ if [ "$f" = "$prevbak" ]; then
+ w=1
+ fi
+done
+
+t=0
+for f in $tars; do
+ if [ "$t" = "2" ]; then
+ rm $root/sqldb/archive/$f
+ fi
+ t=$((t+1))
+done
+
diff --git a/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-conf b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-conf
new file mode 100755
index 0000000000..8adbb902c9
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-conf
@@ -0,0 +1,194 @@
+#!/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 a postgresql master server
+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`
+
+addr=$2
+if [ "$addr" = "" ]; then
+ host="localhost"
+ listen="*"
+ port="5432"
+else
+ host=`$here/../../modules/http/httpd-addr ip $addr`
+ if [ "$host" = "" ]; then
+ host="localhost"
+ listen="*"
+ else
+ listen=$host
+ fi
+ port=`$here/../../modules/http/httpd-addr port $addr`
+fi
+bport=`expr $port + 1000`
+
+pgsql_prefix=`cat $here/pgsql.prefix`
+pgbouncer_prefix=`cat $here/pgbouncer.prefix`
+
+user=`id -un`
+
+mkdir -p $root/sqldb/data
+chmod 700 $root/sqldb/data
+mkdir -p $root/sqldb/archive
+mkdir -p $root/sqldb/backup
+
+if [ -f "$root/sqldb/log.conf" ]; then
+ pgsql_log=`cat $root/sqldb/log.conf`
+else
+ mkdir -p $root/logs
+ pgsql_log="cat >>$root/logs/postgresql"
+fi
+mkdir -p $root/sqldb
+mkdir -p $root/sqldb/tmp
+echo $pgsql_log >$root/sqldb/logger
+
+# Initialize PostgreSQL
+if [ ! -f $root/sqldb/data/postgresql.conf ]; then
+ $pgsql_prefix/bin/pg_ctl init -D $root/sqldb/data 2>&1 | sh $root/sqldb/logger
+ cp $root/sqldb/data/postgresql.conf $root/sqldb/data/postgresql-init.conf
+ cp $root/sqldb/data/pg_hba.conf $root/sqldb/data/pg_hba-init.conf
+fi
+
+# Generate server configuration
+cp $root/sqldb/data/postgresql-init.conf $root/sqldb/data/postgresql.conf
+cat >>$root/sqldb/data/postgresql.conf <<EOF
+
+# Generated by: pgsql-conf $*
+
+# Setup logging
+log_min_messages = NOTICE
+log_min_error_statement = NOTICE
+log_min_duration_statement = -1
+log_checkpoints = on
+log_connections = off
+log_disconnections = off
+log_duration = off
+log_lock_waits = on
+log_statement = none
+
+# Listen
+listen_addresses = '$listen'
+port = $port
+unix_socket_directory = '$root/sqldb/tmp'
+
+# Setup archival
+archive_mode = on
+archive_command = '$here/pgsql-archive $root $host $bport %p %f'
+
+# Setup hot standby with streaming replication
+wal_level = hot_standby
+max_wal_senders = 5
+wal_keep_segments = 32
+
+EOF
+
+# Generate client auth configuration
+cp $root/sqldb/data/pg_hba-init.conf $root/sqldb/data/pg_hba.conf
+cat >>$root/sqldb/data/pg_hba.conf <<EOF
+
+# Generated by: pgsql-conf $*
+# TYPE DATABASE USER CIDR-ADDRESS METHOD
+host all all samenet trust
+host replication all samenet trust
+
+EOF
+
+# Create the db if it's not created yet
+nohup /bin/sh -c "($pgsql_prefix/bin/pg_ctl start -W -D $root/sqldb/data 2>&1 | sh $root/sqldb/logger)" 1>/dev/null 2>/dev/null &
+sti=0
+while [ $sti -ne 30 ]; do
+ st=`$pgsql_prefix/bin/pg_ctl status -D $root/sqldb/data | grep 'server is running'`
+ if [ "$st" != "" ]; then
+ break
+ fi
+ sleep 1
+ sti=$((sti+1))
+done
+
+$pgsql_prefix/bin/createdb -h $host -p $port db 2>&1 | sh $root/sqldb/logger
+
+# Create default user roles
+$pgsql_prefix/bin/psql -h $host -p $port -c "create role standby with login replication" db 2>&1 | sh $root/sqldb/logger
+$pgsql_prefix/bin/psql -h $host -p $port -c "create role bouncer with login" db 2>&1 | sh $root/sqldb/logger
+
+# Backup the db if there's no backup for it yet
+baks=`ls $root/sqldb/archive | sort -r | grep "\.backup\.tar\.gz$"`
+if [ "$baks" = "" ]; then
+ $here/pgsql-backup $root $host $port
+fi
+
+$pgsql_prefix/bin/pg_ctl stop -w -D $root/sqldb/data 2>&1 | sh $root/sqldb/logger
+
+# Generate database restore script
+mkdir -p $root/sqldb/scripts
+cat >$root/sqldb/scripts/restore <<EOF
+#!/bin/sh
+$here/pgsql-restore $root
+EOF
+chmod 700 $root/sqldb/scripts/restore
+
+# Configure HTTPD to serve backup and archive files
+if [ -f "$root/conf/httpd.conf" ]; then
+ cat >>$root/conf/httpd.conf <<EOF
+# Generated by: pgsql-conf $*
+
+# Serve PostgreSQL backup and WAL archive files
+ScriptAlias /pgsql-restore "$root/sqldb/scripts/restore"
+Alias /pgsql-archive "$root/sqldb/archive"
+
+EOF
+
+fi
+
+# Configure PgBouncer
+mkdir -p $root/logs
+id=`id -un`
+cat >$root/sqldb/data/pgbouncer.conf <<EOF
+
+[databases]
+db = host=$host port=$port dbname=db user=bouncer
+
+[pgbouncer]
+pool_mode = session
+listen_addr = $listen
+listen_port = $bport
+unix_socket_dir =
+auth_type = trust
+auth_file=$root/sqldb/data/pgbouncer-auth.conf
+logfile = $root/logs/pgbouncer
+pidfile = $root/logs/pgbouncer.pid
+max_client_conn = 1000
+pool_mode = transaction
+server_reset_query =
+default_pool_size = 500
+min_pool_size = 5
+reserve_pool_size = 50
+log_connections = 0
+log_disconnections = 0
+stats_period = 3600
+admin_users = $id
+
+EOF
+
+cat >$root/sqldb/data/pgbouncer-auth.conf <<EOF
+"$id" "password"
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-log-conf b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-log-conf
new file mode 100755
index 0000000000..c68b4b718d
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-log-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.
+
+# Configure postgresql logging
+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/sqldb
+if [ "$2" = "" ]; then
+ cat >$root/sqldb/log.conf << EOF
+cat >>$root/logs/postgresql
+EOF
+
+else
+ cat >$root/sqldb/log.conf << EOF
+$2
+EOF
+
+fi
+
diff --git a/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-restore b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-restore
new file mode 100755
index 0000000000..e91eba2a6b
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-restore
@@ -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.
+
+# Backup postgresql data directory
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+tar=`ls $root/sqldb/archive | sort -r | grep "\.backup\.tar\.gz$" | head -1`
+
+echo "Content-type: application/x-compressed"
+echo ""
+
+cat $root/sqldb/archive/$tar
+
diff --git a/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-standby-conf b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-standby-conf
new file mode 100755
index 0000000000..5f76b5b332
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-standby-conf
@@ -0,0 +1,193 @@
+#!/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 a postgresql hot standby server
+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`
+
+# Server address
+addr=$2
+if [ "$addr" = "" ]; then
+ host="localhost"
+ listen="*"
+ port="5432"
+else
+ host=`$here/../../modules/http/httpd-addr ip $addr`
+ if [ "$host" = "" ]; then
+ host="localhost"
+ listen="*"
+ else
+ listen=$host
+ fi
+ port=`$here/../../modules/http/httpd-addr port $addr`
+fi
+bport=`expr $port + 1000`
+
+# Master server address
+if [ "$3" = "" ]; then
+ mhost="localhost"
+ mport="5432"
+ mhttpport="80"
+else
+ mhost="$3"
+ mport="$4"
+ mhttpport="$5"
+fi
+
+pgsql_prefix=`cat $here/pgsql.prefix`
+pgbouncer_prefix=`cat $here/pgbouncer.prefix`
+
+user=`id -un`
+
+mkdir -p $root/sqldb/data
+chmod 700 $root/sqldb/data
+mkdir -p $root/sqldb/archive
+mkdir -p $root/sqldb/backup
+
+if [ -f "$root/sqldb/log.conf" ]; then
+ pgsql_log=`cat $root/sqldb/log.conf`
+else
+ mkdir -p $root/logs
+ pgsql_log="cat >>$root/logs/postgresql"
+fi
+mkdir -p $root/sqldb
+mkdir -p $root/sqldb/tmp
+echo $pgsql_log >$root/sqldb/logger
+
+# Initialize from a backup of the master
+if [ ! -f $root/sqldb/data/postgresql.conf ]; then
+ uname=`uname -s`
+ if [ $uname = "Darwin" ]; then
+ tar=gnutar
+ else
+ tar=tar
+ fi
+ (curl -L -# http://$mhost:$mhttpport/pgsql-restore | $tar -C $root/sqldb -xz) 2>&1 | grep -v "100.0%" | sh $root/sqldb/logger
+ mkdir -p $root/sqldb/data/pg_xlog/archive_status
+ chmod 700 $root/sqldb/data/pg_xlog/archive_status
+fi
+
+# Generate server configuration
+cp $root/sqldb/data/postgresql-init.conf $root/sqldb/data/postgresql.conf
+cat >>$root/sqldb/data/postgresql.conf <<EOF
+
+# Generated by: pgsql-standby-conf $*
+
+# Setup logging
+log_min_messages = NOTICE
+log_min_error_statement = NOTICE
+log_min_duration_statement = -1
+log_checkpoints = on
+log_connections = off
+log_disconnections = off
+log_duration = off
+log_lock_waits = on
+log_statement = none
+
+# Listen
+listen_addresses = '$listen'
+port = $port
+unix_socket_directory = '$root/sqldb/tmp'
+
+# Setup archival
+archive_mode = on
+archive_command = '$here/pgsql-archive $root $host $bport %p %f'
+
+# Setup hot standby with streaming replication
+wal_level = hot_standby
+max_wal_senders = 5
+wal_keep_segments = 32
+
+# Enable hot standby
+hot_standby = on
+
+EOF
+
+# Generate recovery configuration
+cat >$root/sqldb/data/recovery.conf << EOF
+# Generated by: pgsql-standby-conf $*
+
+# Start in standby mode
+standby_mode = 'on'
+primary_conninfo = 'host=$mhost port=$mport user=standby'
+
+# Failover
+trigger_file = '$root/sqldb/failover'
+
+restore_command = 'curl -L -# http://$mhost:$mhttpport/pgsql-archive/%f -o "%p" 2>&1 | grep -v "100.0%"'
+
+EOF
+
+# Generate database restore script
+mkdir -p $root/sqldb/scripts
+cat >$root/sqldb/scripts/restore <<EOF
+#!/bin/sh
+$here/pgsql-restore $root
+EOF
+chmod 700 $root/sqldb/scripts/restore
+
+# Configure HTTPD to serve backup and archive files
+if [ -f "$root/conf/httpd.conf" ]; then
+ cat >>$root/conf/httpd.conf <<EOF
+# Generated by: pgsql-standby-conf $*
+
+# Serve PostgreSQL backup and WAL archive files
+ScriptAlias /pgsql-restore "$root/sqldb/scripts/restore"
+Alias /pgsql-archive "$root/sqldb/archive"
+
+EOF
+
+fi
+
+# Configure PgBouncer
+mkdir -p $root/logs
+id=`id -un`
+cat >$root/sqldb/data/pgbouncer.conf <<EOF
+
+[databases]
+db = host=$host port=$port dbname=db user=bouncer
+
+[pgbouncer]
+pool_mode = session
+listen_addr = $listen
+listen_port = $bport
+unix_socket_dir =
+auth_type = trust
+auth_file=$root/sqldb/data/pgbouncer-auth.conf
+logfile = $root/logs/pgbouncer
+pidfile = $root/logs/pgbouncer.pid
+max_client_conn = 1000
+pool_mode = transaction
+server_reset_query =
+default_pool_size = 500
+min_pool_size = 5
+reserve_pool_size = 50
+log_connections = 0
+log_disconnections = 0
+stats_period = 3600
+admin_users = $id
+
+EOF
+
+cat >$root/sqldb/data/pgbouncer-auth.conf <<EOF
+"$id" "password"
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-standby-test.cpp b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-standby-test.cpp
new file mode 100644
index 0000000000..2cd25f874a
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-standby-test.cpp
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test PostgreSQL hot standby support.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "perf.hpp"
+#include "pgsql.hpp"
+
+namespace tuscany {
+namespace pgsql {
+
+bool testPGSql() {
+ PGSql wpg("host=localhost port=6432 dbname=db", "test");
+ PGSql rpg("host=localhost port=6433 dbname=db", "test");
+ const value k = mklist<value>("a");
+
+ assert(hasContent(post(k, string("AAA"), wpg)));
+ sleep(1);
+ assert((get(k, rpg)) == value(string("AAA")));
+ assert(hasContent(put(k, string("aaa"), wpg)));
+ sleep(1);
+ assert((get(k, rpg)) == value(string("aaa")));
+ assert(hasContent(del(k, wpg)));
+ sleep(1);
+ assert(!hasContent(get(k, rpg)));
+
+ return true;
+}
+
+struct getLoop {
+ const value k;
+ PGSql& pg;
+ getLoop(const value& k, PGSql& pg) : k(k), pg(pg) {
+ }
+ const bool operator()() const {
+ assert((get(k, pg)) == value(string("CCC")));
+ return true;
+ }
+};
+
+bool testGetPerf() {
+ const value k = mklist<value>("c");
+ PGSql wpg("host=localhost port=6432 dbname=db", "test");
+ PGSql rpg("host=localhost port=6433 dbname=db", "test");
+ assert(hasContent(post(k, string("CCC"), wpg)));
+ sleep(1);
+
+ const lambda<bool()> gl = getLoop(k, rpg);
+ cout << "PGSql get test " << time(gl, 5, 200) << " ms" << endl;
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::pgsql::testPGSql();
+ tuscany::pgsql::testGetPerf();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-start b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-start
new file mode 100755
index 0000000000..6b388d29f0
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-start
@@ -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.
+
+# Start postgresql
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+pgsql_prefix=`cat $here/pgsql.prefix`
+pgbouncer_prefix=`cat $here/pgbouncer.prefix`
+
+if [ -f "$root/sqldb/log.conf" ]; then
+ pgsql_log=`cat $root/sqldb/log.conf`
+else
+ mkdir -p $root/logs
+ pgsql_log="cat >>$root/logs/postgresql"
+fi
+mkdir -p $root/sqldb
+echo $pgsql_log >$root/sqldb/logger
+
+nohup /bin/sh -c "($pgsql_prefix/bin/pg_ctl start -W -D $root/sqldb/data 2>&1 | sh $root/sqldb/logger)" 1>/dev/null 2>/dev/null &
+sti=0
+while [ $sti -ne 30 ]; do
+ st=`$pgsql_prefix/bin/pg_ctl status -D $root/sqldb/data | grep 'server is running'`
+ if [ "$st" != "" ]; then
+ break
+ fi
+ sleep 1
+ sti=$((sti+1))
+done
+
+# Start PgBouncer
+mkdir -p $root/logs
+$pgbouncer_prefix/bin/pgbouncer -q -d $root/sqldb/data/pgbouncer.conf
+
diff --git a/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-stop b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-stop
new file mode 100755
index 0000000000..16b6506838
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-stop
@@ -0,0 +1,43 @@
+#!/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 postgresql
+here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
+root=`echo "import os; print os.path.realpath('$1')" | python`
+
+pgsql_prefix=`cat $here/pgsql.prefix`
+pgbouncer_prefix=`cat $here/pgbouncer.prefix`
+
+if [ -f "$root/sqldb/log.conf" ]; then
+ pgsql_log=`cat $root/sqldb/log.conf`
+else
+ mkdir -p $root/logs
+ pgsql_log="cat >>$root/logs/postgresql"
+fi
+mkdir -p $root/sqldb
+echo $pgsql_log >$root/sqldb/logger
+
+pgb="$pgbouncer_prefix/bin/pgbouncer -q -d $root/sqldb/data/pgbouncer.conf"
+k=`ps -ef | grep -v grep | grep "${pgb}" | awk '{ print $2 }'`
+if [ "$k" != "" ]; then
+ kill $k
+fi
+
+$pgsql_prefix/bin/pg_ctl stop -w -D $root/sqldb/data 2>&1 | sh $root/sqldb/logger
+
diff --git a/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-test.cpp b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-test.cpp
new file mode 100644
index 0000000000..d10ab5f4c2
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql-test.cpp
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 PostgreSQL access functions.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "perf.hpp"
+#include "pgsql.hpp"
+
+namespace tuscany {
+namespace pgsql {
+
+bool testPGSql() {
+ PGSql pg("host=localhost port=6432 dbname=db", "test");
+ const value k = mklist<value>("a");
+
+ assert(hasContent(post(k, string("AAA"), pg)));
+ assert((get(k, pg)) == value(string("AAA")));
+ assert(hasContent(put(k, string("aaa"), pg)));
+ assert((get(k, pg)) == value(string("aaa")));
+ assert(hasContent(del(k, pg)));
+ assert(!hasContent(get(k, pg)));
+
+ return true;
+}
+
+struct getLoop {
+ const value k;
+ PGSql& pg;
+ getLoop(const value& k, PGSql& pg) : k(k), pg(pg) {
+ }
+ const bool operator()() const {
+ assert((get(k, pg)) == value(string("CCC")));
+ return true;
+ }
+};
+
+bool testGetPerf() {
+ const value k = mklist<value>("c");
+ PGSql pg("host=localhost port=6432 dbname=db", "test");
+ assert(hasContent(post(k, string("CCC"), pg)));
+
+ const lambda<bool()> gl = getLoop(k, pg);
+ cout << "PGSql get test " << time(gl, 5, 200) << " ms" << endl;
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::pgsql::testPGSql();
+ tuscany::pgsql::testGetPerf();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql.hpp b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql.hpp
new file mode 100644
index 0000000000..581cd943e6
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/sqldb/pgsql.hpp
@@ -0,0 +1,249 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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_pgsql_hpp
+#define tuscany_pgsql_hpp
+
+/**
+ * PostgreSQL access functions.
+ */
+
+#include <libpq-fe.h>
+
+#include "string.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "../../modules/scheme/eval.hpp"
+
+namespace tuscany {
+namespace pgsql {
+
+/**
+ * Return and clear a Postgres result failure.
+ */
+const string pgfailure(PGresult* r, PGconn* conn) {
+ const string re = PQresultErrorMessage(r);
+ PQclear(r);
+ if (length(re) != 0)
+ return re;
+ const string ce = PQerrorMessage(conn);
+ return ce;
+}
+
+/**
+ * Represents a PGSql connection.
+ */
+class PGSql {
+public:
+ PGSql() : owner(false) {
+ debug("pgsql::pgsql");
+ }
+
+ PGSql(const string& conninfo, const string& table) : owner(true), conn(NULL), conninfo(conninfo), table(table) {
+ debug(conninfo, "pgsql::pgsql::conninfo");
+ debug(table, "pgsql::pgsql::table");
+
+ // Connect to the database
+ conn = PQconnectdb(c_str(conninfo));
+ if (PQstatus(conn) != CONNECTION_OK) {
+ mkfailure<bool>(string("Couldn't connect to postgresql database: ") + PQerrorMessage(conn));
+ return;
+ }
+
+ // Find the name of the first column in the target table
+ // Assume that's the key we need to use
+ string ks = string("select a.attname from pg_attribute a, pg_class c where a.attrelid = c.relfilenode and c.relname = '") + table + string("' and a.attnum in (1, 2) order by a.attnum;");
+ PGresult* kr = PQexec(conn, c_str(ks));
+ if (PQresultStatus(kr) != PGRES_TUPLES_OK) {
+ mkfailure<bool>(string("Couldn't execute postgresql column select statement: ") + pgfailure(kr, conn));
+ return;
+ }
+ if (PQntuples(kr) != 2) {
+ PQclear(kr);
+ mkfailure<bool>(string("Couldn't find postgresql table key and value column names"));
+ return;
+ }
+ kname = PQgetvalue(kr, 0, 0);
+ vname = PQgetvalue(kr, 1, 0);
+ PQclear(kr);
+ }
+
+ PGSql(const PGSql& c) : owner(false), conn(c.conn), conninfo(c.conninfo), table(c.table) {
+ debug("pgsql::pgsql::copy");
+ }
+
+ const PGSql& operator=(const PGSql& c) {
+ debug("pgsql::pgsql::operator=");
+ if(this == &c)
+ return *this;
+ owner = false;
+ conn = c.conn;
+ conninfo = c.conninfo;
+ table = c.table;
+ return *this;
+ }
+
+ ~PGSql() {
+ debug("pgsql::~pgsql");
+ if (!owner)
+ return;
+ if (conn == NULL)
+ return;
+ PQfinish(conn);
+ }
+
+private:
+ bool owner;
+ PGconn *conn;
+ string conninfo;
+ string table;
+ string kname;
+ string vname;
+
+ friend const failable<bool> setup(const PGSql& pgsql);
+ friend const failable<bool> post(const value& key, const value& val, const PGSql& pgsql);
+ friend const failable<bool> put(const value& key, const value& val, const PGSql& pgsql);
+ friend const failable<value> get(const value& key, const PGSql& pgsql);
+ friend const failable<bool> del(const value& key, const PGSql& pgsql);
+};
+
+/**
+ * Setup the database connection if necessary.
+ */
+const failable<bool> setup(const PGSql& pgsql) {
+ debug("pgsql::setup");
+ if (PQstatus(pgsql.conn) == CONNECTION_OK)
+ return true;
+ debug("pgsql::setup::reset");
+ PQreset(pgsql.conn);
+ if (PQstatus(pgsql.conn) != CONNECTION_OK)
+ return mkfailure<bool>(string("Couldn't reconnect to postgresql database: ") + PQerrorMessage(pgsql.conn));
+ return true;
+}
+
+/**
+ * Post a new item to the database.
+ */
+const failable<bool> post(const value& key, const value& val, const PGSql& pgsql) {
+ debug(key, "pgsql::post::key");
+ debug(val, "pgsql::post::value");
+ debug(pgsql.conninfo, "pgsql::post::conninfo");
+ debug(pgsql.table, "pgsql::post::table");
+ setup(pgsql);
+
+ const string ks(scheme::writeValue(key));
+ const string vs(scheme::writeValue(val));
+ const char* params[2] = { c_str(ks), c_str(vs) };
+ PGresult* r = PQexecParams(pgsql.conn, c_str(string("insert into ") + pgsql.table + string(" values($1, $2);")), 2, NULL, params, NULL, NULL, 0);
+ if (PQresultStatus(r) != PGRES_COMMAND_OK)
+ return mkfailure<bool>(string("Couldn't execute insert postgresql SQL statement: ") + pgfailure(r, pgsql.conn));
+ PQclear(r);
+
+ debug(true, "pgsql::post::result");
+ return true;
+}
+
+/**
+ * Update an item in the database. If the item doesn't exist it is added.
+ */
+const failable<bool> put(const value& key, const value& val, const PGSql& pgsql) {
+ debug(key, "pgsql::put::key");
+ debug(val, "pgsql::put::value");
+ debug(pgsql.conninfo, "pgsql::put::conninfo");
+ debug(pgsql.table, "pgsql::put::table");
+ setup(pgsql);
+
+ const string ks(scheme::writeValue(key));
+ const string vs(scheme::writeValue(val));
+ const char* params[2] = { c_str(ks), c_str(vs) };
+ PGresult* r = PQexecParams(pgsql.conn, c_str(string("update ") + pgsql.table + string(" set ") + pgsql.vname + string(" = $2 where ") + pgsql.kname + string(" = $1;")), 2, NULL, params, NULL, NULL, 0);
+ if (PQresultStatus(r) != PGRES_COMMAND_OK)
+ return mkfailure<bool>(string("Couldn't execute update postgresql SQL statement: ") + pgfailure(r, pgsql.conn));
+ const string t = PQcmdTuples(r);
+ if (t != "0") {
+ PQclear(r);
+ debug(true, "pgsql::put::result");
+ return true;
+ }
+ PQclear(r);
+
+ PGresult* pr = PQexecParams(pgsql.conn, c_str(string("insert into ") + pgsql.table + string(" values($1, $2);")), 2, NULL, params, NULL, NULL, 0);
+ if (PQresultStatus(pr) != PGRES_COMMAND_OK)
+ return mkfailure<bool>(string("Couldn't execute insert postgresql SQL statement: ") + pgfailure(pr, pgsql.conn));
+ PQclear(pr);
+
+ debug(true, "pgsql::put::result");
+ return true;
+}
+
+/**
+ * Get an item from the database.
+ */
+const failable<value> get(const value& key, const PGSql& pgsql) {
+ debug(key, "pgsql::get::key");
+ debug(pgsql.conninfo, "pgsql::get::conninfo");
+ debug(pgsql.table, "pgsql::get::table");
+ setup(pgsql);
+
+ const string ks(scheme::writeValue(key));
+ const char* params[1] = { c_str(ks) };
+ PGresult* r = PQexecParams(pgsql.conn, c_str(string("select * from ") + pgsql.table + string(" where ") + pgsql.kname + string(" = $1;")), 1, NULL, params, NULL, NULL, 0);
+ if (PQresultStatus(r) != PGRES_TUPLES_OK)
+ return mkfailure<value>(string("Couldn't execute select postgresql SQL statement: ") + pgfailure(r, pgsql.conn));
+ if (PQntuples(r) < 1) {
+ PQclear(r);
+ ostringstream os;
+ os << "Couldn't get postgresql entry: " << key;
+ return mkfailure<value>(str(os), 404, false);
+ }
+ const char* data = PQgetvalue(r, 0, 1);
+ const value val(scheme::readValue(string(data)));
+ PQclear(r);
+
+ debug(val, "pgsql::get::result");
+ return val;
+}
+
+/**
+ * Delete an item from the database
+ */
+const failable<bool> del(const value& key, const PGSql& pgsql) {
+ debug(key, "pgsql::delete::key");
+ debug(pgsql.conninfo, "pgsql::delete::conninfo");
+ debug(pgsql.table, "pgsql::delete::table");
+ setup(pgsql);
+
+ const string ks(scheme::writeValue(key));
+ const char* params[1] = { c_str(ks) };
+ PGresult* r = PQexecParams(pgsql.conn, c_str(string("delete from ") + pgsql.table + string(" where ") + pgsql.kname + string(" = $1;")), 1, NULL, params, NULL, NULL, 0);
+ if (PQresultStatus(r) != PGRES_COMMAND_OK)
+ return mkfailure<bool>(string("Couldn't execute delete postgresql SQL statement: ") + pgfailure(r, pgsql.conn));
+ PQclear(r);
+
+ debug(true, "pgsql::delete::result");
+ return true;
+}
+
+}
+}
+
+#endif /* tuscany_pgsql_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/components/sqldb/server-test b/sca-cpp/branches/lightweight-sca/components/sqldb/server-test
new file mode 100755
index 0000000000..db756ec1c4
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/sqldb/server-test
@@ -0,0 +1,46 @@
+#!/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
+../../modules/http/httpd-conf tmp localhost 8090 ../../modules/http/htdocs
+../../modules/http/httpd-event-conf tmp
+./pgsql-conf tmp
+./pgsql-start tmp
+./pgsql "drop table test;" 1>/dev/null 2>&1
+./pgsql "create table test(key text, value text);" 1>/dev/null 2>&1
+
+../../modules/server/server-conf tmp
+../../modules/server/scheme-conf tmp
+cat >>tmp/conf/httpd.conf <<EOF
+SCAContribution `pwd`/
+SCAComposite sqldb.composite
+EOF
+
+../../modules/http/httpd-start tmp
+sleep 2
+
+# Test
+./client-test 2>/dev/null
+rc=$?
+
+# Cleanup
+../../modules/http/httpd-stop tmp
+./pgsql-stop tmp
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/components/sqldb/sqldb-test b/sca-cpp/branches/lightweight-sca/components/sqldb/sqldb-test
new file mode 100755
index 0000000000..cb023fec3a
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/sqldb/sqldb-test
@@ -0,0 +1,33 @@
+#!/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
+./pgsql-conf tmp
+./pgsql-start tmp
+./pgsql "drop table test;" 1>/dev/null 2>&1
+./pgsql "create table test(key text, value text);" 1>/dev/null 2>&1
+
+# Test
+./pgsql-test 2>/dev/null
+rc=$?
+
+# Cleanup
+./pgsql-stop tmp
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/components/sqldb/sqldb.componentType b/sca-cpp/branches/lightweight-sca/components/sqldb/sqldb.componentType
new file mode 100644
index 0000000000..bd024213bd
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/sqldb/sqldb.componentType
@@ -0,0 +1,29 @@
+<?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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <service name="sqldb"/>
+ <property name="conninfo" type="xsd:string">host=localhost port=6432 dbname=db</property>
+ <property name="table" type=xsd:string"/>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/sqldb/sqldb.composite b/sca-cpp/branches/lightweight-sca/components/sqldb/sqldb.composite
new file mode 100644
index 0000000000..9e102893b5
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/sqldb/sqldb.composite
@@ -0,0 +1,33 @@
+<?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://tuscany.apache.org/xmlns/sca/components"
+ name="sqldb">
+
+ <component name="sqldb">
+ <implementation.cpp path="." library="libsqldb"/>
+ <property name="conninfo">host=localhost port=6432 dbname=db</property>
+ <property name="table">test</property>
+ <service name="sqldb">
+ <binding.http uri="sqldb"/>
+ </service>
+ </component>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/sqldb/sqldb.cpp b/sca-cpp/branches/lightweight-sca/components/sqldb/sqldb.cpp
new file mode 100644
index 0000000000..9925897693
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/sqldb/sqldb.cpp
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * PostgreSQL-based database component implementation.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "parallel.hpp"
+#include "pgsql.hpp"
+
+namespace tuscany {
+namespace sqldb {
+
+/**
+ * Get an item from the database.
+ */
+const failable<value> get(const list<value>& params, pgsql::PGSql& pg) {
+ return pgsql::get(car(params), pg);
+}
+
+/**
+ * Post an item to the database.
+ */
+const failable<value> post(const list<value>& params, pgsql::PGSql& pg) {
+ const value id = append<value>(car(params), mklist(mkuuid()));
+ const failable<bool> val = pgsql::post(id, cadr(params), pg);
+ if (!hasContent(val))
+ return mkfailure<value>(val);
+ return id;
+}
+
+/**
+ * Put an item into the database.
+ */
+const failable<value> put(const list<value>& params, pgsql::PGSql& pg) {
+ const failable<bool> val = pgsql::put(car(params), cadr(params), pg);
+ if (!hasContent(val))
+ return mkfailure<value>(val);
+ return value(content(val));
+}
+
+/**
+ * Delete an item from the database.
+ */
+const failable<value> del(const list<value>& params, pgsql::PGSql& pg) {
+ const failable<bool> val = pgsql::del(car(params), pg);
+ if (!hasContent(val))
+ return mkfailure<value>(val);
+ return value(content(val));
+}
+
+/**
+ * Component implementation lambda function.
+ */
+class applySqldb {
+public:
+ applySqldb(const perthread_ptr<pgsql::PGSql>& pg) : pg(pg) {
+ }
+
+ const value operator()(const list<value>& params) const {
+ const value func(car(params));
+ if (func == "get")
+ return get(cdr(params), *pg);
+ if (func == "post")
+ return post(cdr(params), *pg);
+ if (func == "put")
+ return put(cdr(params), *pg);
+ if (func == "delete")
+ return del(cdr(params), *pg);
+ return mkfailure<value>();
+ }
+
+private:
+ const perthread_ptr<pgsql::PGSql> pg;
+};
+
+/**
+ * Lambda function that creates a new database connection.
+ */
+class newPGSql {
+public:
+ newPGSql(const string& conninfo, const string& table) : conninfo(conninfo), table(table) {
+ }
+
+ const gc_ptr<pgsql::PGSql> operator()() const {
+ return new (gc_new<pgsql::PGSql>()) pgsql::PGSql(conninfo, table);
+ }
+
+private:
+ const string conninfo;
+ const string table;
+};
+
+/**
+ * Start the component.
+ */
+const failable<value> start(unused const list<value>& params) {
+ // Connect to the configured database and table
+ const value conninfo = ((lambda<value(const list<value>&)>)car(params))(list<value>());
+ const value table = ((lambda<value(const list<value>&)>)cadr(params))(list<value>());
+ const perthread_ptr<pgsql::PGSql> pg(lambda<gc_ptr<pgsql::PGSql>()>(newPGSql(conninfo, table)));
+
+ // Return the component implementation lambda function
+ return value(lambda<value(const list<value>&)>(applySqldb(pg)));
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "start")
+ return tuscany::sqldb::start(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/sqldb/standby-test b/sca-cpp/branches/lightweight-sca/components/sqldb/standby-test
new file mode 100755
index 0000000000..3c91e477e4
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/sqldb/standby-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
+../../modules/http/httpd-conf tmp/master localhost 8090 tmp/master/htdocs
+../../modules/http/httpd-event-conf tmp
+./pgsql-conf tmp/master 5432
+./pgsql-start tmp/master
+./pgsql localhost 6432 "drop table test;" 1>/dev/null 2>&1
+./pgsql localhost 6432 "create table test(key text, value text);" 1>/dev/null 2>&1
+../../modules/http/httpd-start tmp/master
+sleep 2
+./pgsql-standby-conf tmp/standby 5433 localhost 5432 8090
+./pgsql-start tmp/standby
+
+# Test
+./pgsql-standby-test 2>/dev/null
+rc=$?
+
+# Cleanup
+../../modules/http/httpd-stop tmp/master
+./pgsql-stop tmp/standby
+./pgsql-stop tmp/master
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/components/webservice/Makefile.am b/sca-cpp/branches/lightweight-sca/components/webservice/Makefile.am
new file mode 100644
index 0000000000..242b97dce8
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/webservice/Makefile.am
@@ -0,0 +1,71 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT 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_WEBSERVICE
+
+INCLUDES = -I${AXIS2C_INCLUDE}
+
+incl_HEADERS = *.hpp
+incldir = $(prefix)/include/components/webservice
+
+dist_comp_SCRIPTS = axis2-conf
+compdir=$(prefix)/components/webservice
+
+comp_DATA = axis2c.prefix axis2.xml services.xml module.xml
+
+EXTRA_DIST = webservice.composite webservice-client.componentType webservice-listener.componentType axis2.xml services.xml module.xml *.scm
+
+axis2c.prefix: $(top_builddir)/config.status
+ echo ${AXIS2C_PREFIX} >axis2c.prefix
+
+comp_LTLIBRARIES = libwebservice-client.la libwebservice-listener.la libaxis2-dispatcher.la libaxis2-service.la
+noinst_DATA = libwebservice-client${libsuffix} libwebservice-listener${libsuffix} libaxis2-dispatcher${libsuffix} libaxis2-service${libsuffix}
+
+libwebservice_client_la_SOURCES = webservice-client.cpp
+libwebservice_client_la_LDFLAGS = -lxml2 -L${AXIS2C_LIB} -R${AXIS2C_LIB} -laxis2_engine -laxis2_axiom -laxutil
+libwebservice-client${libsuffix}:
+ ln -s .libs/libwebservice-client${libsuffix}
+
+libwebservice_listener_la_SOURCES = webservice-listener.cpp
+libwebservice_listener_la_LDFLAGS = -lxml2 -L${AXIS2C_LIB} -R${AXIS2C_LIB} -laxis2_engine -laxis2_axiom -laxutil
+libwebservice-listener${libsuffix}:
+ ln -s .libs/libwebservice-listener${libsuffix}
+
+libaxis2_dispatcher_la_SOURCES = axis2-dispatcher.cpp
+libaxis2_dispatcher_la_LDFLAGS = -lxml2 -L${AXIS2C_LIB} -R${AXIS2C_LIB} -laxis2_engine -laxis2_axiom -laxutil
+libaxis2-dispatcher${libsuffix}:
+ ln -s .libs/libaxis2-dispatcher${libsuffix}
+
+libaxis2_service_la_SOURCES = axis2-service.cpp
+libaxis2_service_la_LDFLAGS = -lxml2 -L${AXIS2C_LIB} -R${AXIS2C_LIB} -laxis2_engine -laxis2_axiom -laxutil
+libaxis2-service${libsuffix}:
+ ln -s .libs/libaxis2-service${libsuffix}
+
+axiom_test_SOURCES = axiom-test.cpp
+axiom_test_LDFLAGS = -lxml2 -L${AXIS2C_LIB} -R${AXIS2C_LIB} -laxis2_engine -laxis2_axiom -laxutil
+
+axis2_test_SOURCES = axis2-test.cpp
+axis2_test_LDFLAGS = -lxml2 -L${AXIS2C_LIB} -R${AXIS2C_LIB} -laxis2_engine -laxis2_axiom -laxutil
+
+client_test_SOURCES = client-test.cpp
+client_test_LDFLAGS = -lxml2 -lcurl -lmozjs -L${AXIS2C_LIB} -R${AXIS2C_LIB} -laxis2_engine -laxis2_axiom -laxutil
+
+dist_noinst_SCRIPTS = echo-test server-test
+noinst_PROGRAMS = axiom-test axis2-test client-test
+TESTS = axiom-test echo-test server-test
+
+endif
diff --git a/sca-cpp/branches/lightweight-sca/components/webservice/axiom-test.cpp b/sca-cpp/branches/lightweight-sca/components/webservice/axiom-test.cpp
new file mode 100644
index 0000000000..75ce2452fd
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/webservice/axiom-test.cpp
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Test Web service Axiom support functions.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "list.hpp"
+#include "element.hpp"
+#include "monad.hpp"
+#include "value.hpp"
+#include "perf.hpp"
+#include "axis2.hpp"
+
+namespace tuscany {
+namespace webservice {
+
+const string customerElement =
+"<customer>"
+"<name>jdoe</name>"
+"<address><city>san francisco</city><state>ca</state></address>"
+"<account><id>1234</id><balance>1000</balance></account>"
+"<account><id>6789</id><balance>2000</balance></account>"
+"<account><id>4567</id><balance>3000</balance></account>"
+"</customer>";
+
+const string echo("<ns1:echoString xmlns:ns1=\"http://ws.apache.org/axis2/services/echo\">\n"
+ " <text>Hello World!</text>\n"
+ "</ns1:echoString>");
+
+bool testAxiom() {
+ const Axis2Context ax;
+ {
+ const failable<axiom_node_t*> n = stringToAxiomNode(customerElement, ax);
+ assert(hasContent(n));
+ const failable<const string> c = axiomNodeToString(content(n), ax);
+ assert(hasContent(c));
+ assert(content(c) == customerElement);
+ }
+ {
+ const list<value> arg = mklist<value>(
+ list<value>() + "ns1:echoString"
+ + (list<value>() + "@xmlns:ns1" + string("http://ws.apache.org/axis2/services/echo"))
+ + (list<value>() + "text" + string("Hello World!")));
+ const failable<axiom_node_t*> n = valuesToAxiomNode(arg, ax);
+ assert(hasContent(n));
+ const failable<const string> x = axiomNodeToString(content(n), ax);
+ assert(hasContent(x));
+ assert(content(x) == echo);
+ const failable<const list<value> > l = axiomNodeToValues(content(n), ax);
+ assert(hasContent(l));
+ assert(l == arg);
+ }
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::webservice::testAxiom();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/webservice/axis2-conf b/sca-cpp/branches/lightweight-sca/components/webservice/axis2-conf
new file mode 100755
index 0000000000..c302737ae9
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/webservice/axis2-conf
@@ -0,0 +1,64 @@
+#!/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 Axis2 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`
+axis2_prefix=`cat axis2c.prefix`
+
+# Create an Axis2 home directory
+mkdir -p $root/axis2c
+ln -f -s $axis2_prefix/lib $root/axis2c/lib
+mkdir -p $root/axis2c/logs
+mkdir -p $root/axis2c/modules
+ln -f -s $axis2_prefix/modules/addressing $root/axis2c/modules/addressing
+ln -f -s $axis2_prefix/modules/logging $root/axis2c/modules/logging
+mkdir -p $root/axis2c/services
+
+# Install Tuscany Axis2 module and service
+mkdir -p $root/axis2c/modules/tuscany
+ln -f -s $here/libaxis2-dispatcher.so $root/axis2c/modules/tuscany/libaxis2-dispatcher.so
+ln -f -s $here/module.xml $root/axis2c/modules/tuscany/module.xml
+mkdir -p $root/axis2c/services/tuscany
+ln -f -s $here/libaxis2-service.so $root/axis2c/services/tuscany/libaxis2-service.so
+ln -f -s $here/services.xml $root/axis2c/services/tuscany/services.xml
+cp $here/axis2.xml $root/axis2c/axis2.xml
+
+# Configure HTTPD Axis2 module
+cat >>$root/conf/modules.conf <<EOF
+# Generated by: axis2-conf $*
+# Support for Web Services
+LoadModule axis2_module $root/axis2c/lib/libmod_axis2.so
+
+EOF
+
+cat >>$root/conf/httpd.conf <<EOF
+# Generated by: axis2-conf $*
+# Support for Web Services
+SCASetEnv AXIS2C_HOME $root/axis2c
+Axis2RepoPath $root/axis2c
+Axis2LogFile $root/axis2c/logs/mod_axis2.log
+Axis2LogLevel debug
+<Location /axis2>
+ SetHandler axis2_module
+</Location>
+
+EOF
+
diff --git a/sca-cpp/branches/lightweight-sca/components/webservice/axis2-dispatcher.cpp b/sca-cpp/branches/lightweight-sca/components/webservice/axis2-dispatcher.cpp
new file mode 100644
index 0000000000..dafa6fd229
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/webservice/axis2-dispatcher.cpp
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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$ */
+
+/**
+ * Axis2/C module that dispatches all server requests to the Tuscany Axis/2C service.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "axis2.hpp"
+
+namespace tuscany {
+namespace webservice {
+
+/**
+ * Determine the service and operation to dispatch a request to.
+ */
+axis2_svc_t* AXIS2_CALL dispatchFindService(axis2_msg_ctx_t* msg_ctx, const axutil_env_t* env) {
+ const axis2_conf_ctx_t* conf_ctx = axis2_msg_ctx_get_conf_ctx(msg_ctx, env);
+ const axis2_conf_t* conf = axis2_conf_ctx_get_conf(conf_ctx, env);
+ axis2_svc_t* svc = axis2_conf_get_svc(conf, env, "TuscanyService");
+ return svc;
+}
+
+axis2_op_t *AXIS2_CALL dispatchFindOp(unused axis2_msg_ctx_t* msg_ctx, const axutil_env_t* env, axis2_svc_t* svc) {
+ axutil_qname_t* op_qname = axutil_qname_create(env, "execute", NULL, NULL);
+ axis2_op_t *op = axis2_svc_get_op_with_name(svc, env, axutil_qname_get_localpart(op_qname, env));
+ axutil_qname_free(op_qname, env);
+ return op;
+}
+
+/**
+ * Dispatcher invoke function, called by Axis2/C.
+ */
+axis2_status_t AXIS2_CALL dispatchInvoke( struct axis2_handler* handler, const axutil_env_t* env, axis2_msg_ctx_t* msg_ctx) {
+ if (!(axis2_msg_ctx_get_server_side(msg_ctx, env)))
+ return AXIS2_SUCCESS;
+ axis2_msg_ctx_set_find_svc(msg_ctx, env, dispatchFindService);
+ axis2_msg_ctx_set_find_op(msg_ctx, env, dispatchFindOp);
+ return axis2_disp_find_svc_and_op(handler, env, msg_ctx);
+}
+
+/**
+ * Create a dispatch handler.
+ */
+AXIS2_EXPORT axis2_handler_t* AXIS2_CALL dispatchHandler(const axutil_env_t* env, unused axutil_string_t* name) {
+ axis2_handler_t *handler = axis2_handler_create(env);
+ if (handler == NULL)
+ return NULL;
+ axis2_handler_set_invoke(handler, env, dispatchInvoke);
+ return handler;
+}
+
+/**
+ * Initialize dispatch module.
+ */
+axis2_status_t AXIS2_CALL dispatchInit(unused axis2_module_t * module, unused const axutil_env_t * env, unused axis2_conf_ctx_t * conf_ctx, unused axis2_module_desc_t * module_desc) {
+ return AXIS2_SUCCESS;
+}
+
+/**
+ * Initialize dispatch module function map.
+ */
+axis2_status_t AXIS2_CALL dispatchFuncMap(axis2_module_t * module, const axutil_env_t * env) {
+ module->handler_create_func_map = axutil_hash_make(env);
+ axutil_hash_set(module->handler_create_func_map, "TuscanyDispatcher", AXIS2_HASH_KEY_STRING, (const void *)dispatchHandler);
+ return AXIS2_SUCCESS;
+}
+
+/**
+ * Shutdown dispatch module.
+ */
+axis2_status_t AXIS2_CALL dispatchShutdown(axis2_module_t* module, const axutil_env_t* env) {
+ if (module->handler_create_func_map != NULL) {
+ axutil_hash_free(module->handler_create_func_map, env);
+ module->handler_create_func_map = NULL;
+ }
+ AXIS2_FREE(env->allocator, module);
+ return AXIS2_SUCCESS;
+}
+
+/**
+ * Return a new dispatch module.
+ */
+const axis2_module_ops_t dispatchOps = {
+ dispatchInit,
+ dispatchShutdown,
+ dispatchFuncMap
+};
+
+axis2_module_t * dispatchModule(const axutil_env_t* env) {
+ axis2_module_t *module = (axis2_module_t*)AXIS2_MALLOC(env->allocator, sizeof(axis2_module_t));
+ if (module == NULL)
+ return NULL;
+ module->ops = &dispatchOps;
+ module->handler_create_func_map = NULL;
+ return module;
+}
+
+}
+}
+
+extern "C"
+{
+
+/**
+ * Axis2/C module entry point functions.
+ */
+AXIS2_EXPORT int axis2_get_instance(axis2_module_t** inst, const axutil_env_t* env) {
+ *inst = tuscany::webservice::dispatchModule(env);
+ if(*inst == NULL)
+ return AXIS2_FAILURE;
+ return AXIS2_SUCCESS;
+}
+
+AXIS2_EXPORT int axis2_remove_instance(axis2_module_t* inst, const axutil_env_t* env) {
+ if (inst != NULL)
+ return tuscany::webservice::dispatchShutdown(inst, env);
+ return AXIS2_FAILURE;
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/webservice/axis2-service.cpp b/sca-cpp/branches/lightweight-sca/components/webservice/axis2-service.cpp
new file mode 100644
index 0000000000..4df0543370
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/webservice/axis2-service.cpp
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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$ */
+
+/**
+ * Axis2/C service implementation that dispatches requests to SCA Web service components.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "value.hpp"
+#include "string.hpp"
+#include "../../modules/http/httpd.hpp"
+#include "axis2.hpp"
+
+namespace tuscany {
+namespace webservice {
+
+/**
+ * Initialize the service.
+ */
+int AXIS2_CALL serviceInit(unused axis2_svc_skeleton_t* svc_skeleton, unused const axutil_env_t* env) {
+ return AXIS2_SUCCESS;
+}
+
+/**
+ * Free the service.
+ */
+int AXIS2_CALL serviceFree(axis2_svc_skeleton_t* svc_skeleton, const axutil_env_t* env) {
+ if (svc_skeleton)
+ AXIS2_FREE(env->allocator, svc_skeleton);
+ return AXIS2_SUCCESS;
+}
+
+typedef struct axis2_apache2_out_transport_info {
+ axis2_http_out_transport_info_t out_transport_info;
+ request_rec *request;
+ axis2_char_t *encoding;
+} axis2_apache2_out_transport_info_t;
+
+extern "C" {
+ extern module axis2_module;
+}
+
+/**
+ * Service invoke function, called by Axis2/C.
+ */
+axiom_node_t *AXIS2_CALL serviceInvoke(unused axis2_svc_skeleton_t* svc_skeleton, const axutil_env_t* env, axiom_node_t* node, axis2_msg_ctx_t* msg_ctx) {
+
+ // Check that we have an input node
+ if (node == NULL || axiom_node_get_node_type(node, env) != AXIOM_ELEMENT)
+ return NULL;
+ axiom_element_t *e = (axiom_element_t *) axiom_node_get_data_element(node, env);
+ if (e == NULL)
+ return NULL;
+
+ // Get the function name
+ const char* func = axiom_element_get_localname(e, env);
+ if (func == NULL)
+ return NULL;
+
+ // Get the target endpoint address
+ const axis2_endpoint_ref_t* epr = axis2_msg_ctx_get_from(msg_ctx, env);
+ if (epr == NULL)
+ return NULL;
+ string address = axis2_endpoint_ref_get_address(epr, env);
+
+ // Get the underlying HTTPD request
+ axis2_out_transport_info_t* tinfo = axis2_msg_ctx_get_out_transport_info(msg_ctx, env);
+ axis2_apache2_out_transport_info_t* httpinfo = (axis2_apache2_out_transport_info_t*)tinfo;
+ request_rec* r = httpinfo->request;
+ debug_httpdRequest(r, "webservice::serviceInvoke");
+
+ // Parse the request Axiom node and construct request expression
+ Axis2Context ax(env);
+ const failable<const list<value> > lv = axiomNodeToValues(node, ax);
+ if (!hasContent(lv))
+ return NULL;
+ const value expr = mklist<value>(func, content(lv));
+ debug(expr, "webservice::serviceInvoke::expr");
+
+ // Retrieve the target lambda function from the HTTPD request and invoke it
+ const value* rv = const_cast<const value*>((value*)ap_get_module_config(r->request_config, &axis2_module));
+ cout << "relay: " << rv << endl;
+ const lambda<value(const list<value>&)> relay = *rv;
+ const value res = relay(expr);
+ debug(res, "webservice::serviceInvoke::result");
+
+ // Construct response Axiom node
+ const failable<axiom_node_t*> rnode = valuesToAxiomNode(res, ax);
+ if (!hasContent(rnode))
+ return NULL;
+ return content(rnode);
+}
+
+/**
+ * Return a new service skeleton.
+ */
+const axis2_svc_skeleton_ops_t serviceOps = {
+ serviceInit,
+ serviceInvoke,
+ NULL,
+ serviceFree,
+ NULL
+};
+
+AXIS2_EXTERN axis2_svc_skeleton_t *AXIS2_CALL serviceSkeleton(const axutil_env_t* env) {
+ axis2_svc_skeleton_t* svc_skeleton = (axis2_svc_skeleton_t*)AXIS2_MALLOC(env->allocator, sizeof(axis2_svc_skeleton_t));
+ svc_skeleton->ops = &serviceOps;
+ svc_skeleton->func_array = NULL;
+ return svc_skeleton;
+}
+
+}
+}
+
+extern "C"
+{
+
+/**
+ * Axis2/C service entry point functions.
+ */
+AXIS2_EXPORT int axis2_get_instance(struct axis2_svc_skeleton** inst, const axutil_env_t* env) {
+ *inst = tuscany::webservice::serviceSkeleton(env);
+ if (inst == NULL)
+ return AXIS2_FAILURE;
+ return AXIS2_SUCCESS;
+}
+
+AXIS2_EXPORT int axis2_remove_instance(axis2_svc_skeleton_t* inst, const axutil_env_t* env) {
+ if (inst != NULL)
+ return AXIS2_SVC_SKELETON_FREE(inst, env);
+ return AXIS2_FAILURE;
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/webservice/axis2-test.cpp b/sca-cpp/branches/lightweight-sca/components/webservice/axis2-test.cpp
new file mode 100644
index 0000000000..d7c2f3b671
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/webservice/axis2-test.cpp
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 WebService Axis2 client support functions.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "list.hpp"
+#include "element.hpp"
+#include "monad.hpp"
+#include "value.hpp"
+#include "perf.hpp"
+#include "axis2.hpp"
+
+namespace tuscany {
+namespace webservice {
+
+bool testEval() {
+ const Axis2Context ax;
+
+ const value func = "http://ws.apache.org/axis2/c/samples/echoString";
+ const list<value> arg = mklist<value>(
+ list<value>() + "ns1:echoString"
+ + (list<value>() + "@xmlns:ns1" + string("http://ws.apache.org/axis2/services/echo"))
+ + (list<value>() + "text" + string("Hello World!")));
+
+ const failable<value> rval = evalExpr(mklist<value>(func, arg, string("http://localhost:9090/axis2/services/echo")), ax);
+ assert(hasContent(rval));
+
+ const list<value> r = mklist<value>(
+ list<value>() + "ns1:echoString"
+ + (list<value>() + "@xmlns:ns1" + string("http://ws.apache.org/axis2/c/samples"))
+ + (list<value>() + "text" + string("Hello World!")));
+ assert(content(rval) == r);
+
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::webservice::testEval();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/webservice/axis2.hpp b/sca-cpp/branches/lightweight-sca/components/webservice/axis2.hpp
new file mode 100644
index 0000000000..9bad109ff0
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/webservice/axis2.hpp
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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_webservice_hpp
+#define tuscany_webservice_hpp
+
+/**
+ * Web service invocation functions using Axis2.
+ */
+#include "config.hpp"
+
+// Ignore redundant declarations in Axiom headers
+#ifdef WANT_MAINTAINER_WARNINGS
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#endif
+#include <axiom.h>
+#include <axis2_client.h>
+#include <axis2_module.h>
+#include <axis2_addr_mod.h>
+#include <axis2_conf_ctx.h>
+#include <axis2_disp.h>
+#include <axis2_http_out_transport_info.h>
+#ifdef WANT_MAINTAINER_WARNINGS
+#pragma GCC diagnostic warning "-Wredundant-decls"
+#endif
+
+#include "string.hpp"
+#include "sstream.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "xml.hpp"
+#include "monad.hpp"
+
+namespace tuscany {
+namespace webservice {
+
+/**
+ * Represents an Axis2 runtime context.
+ */
+class Axis2Context {
+public:
+ Axis2Context() : env(axutil_env_create_all("axis2.log", AXIS2_LOG_LEVEL_WARNING)), owner(true) {
+ debug("webservice::axis2context");
+ }
+
+ Axis2Context(const Axis2Context& ax) : env(ax.env), owner(false) {
+ debug("webservice::axis2context::copy");
+ }
+
+ const Axis2Context& operator=(const Axis2Context& ax) {
+ debug("webservice::axis2context::operator=");
+ if(this == &ax)
+ return *this;
+ env = ax.env;
+ owner = false;
+ return *this;
+ }
+
+ Axis2Context(const axutil_env_t* env) : env(const_cast<axutil_env_t*>(env)), owner(false) {
+ debug("webservice::axis2context::env");
+ }
+
+ ~Axis2Context() {
+ debug("webservice::~axis2context");
+ if (!owner || env == NULL)
+ return;
+ axutil_env_free(env);
+ }
+
+private:
+ axutil_env_t* env;
+ bool owner;
+
+ friend const axutil_env_t* env(const Axis2Context& ax);
+};
+
+const axutil_env_t* env(const Axis2Context& ax) {
+ return ax.env;
+}
+
+/**
+ * Return the latest Axis2 error in an Axis2 context.
+ */
+const string axis2Error(const Axis2Context& ax) {
+ ostringstream os;
+ os << env(ax)->error->error_number << " : " << AXIS2_ERROR_GET_MESSAGE(env(ax)->error);
+ return str(os);
+}
+
+/**
+ * Convert a string to an Axiom node.
+ */
+const failable<axiom_node_t*> stringToAxiomNode(const string& s, const Axis2Context& ax) {
+ axiom_node_t* node = axiom_node_create_from_buffer(env(ax), const_cast<axis2_char_t*>(c_str(s)));
+ if (node == NULL)
+ return mkfailure<axiom_node_t*>(string("Couldn't convert XML to Axiom node: ") + axis2Error(ax));
+ return node;
+}
+
+/**
+ * Convert a list of values representing XML elements to an Axiom node.
+ */
+const failable<axiom_node_t*> valuesToAxiomNode(const list<value>& l, const Axis2Context& ax) {
+ const failable<list<string> > xml = writeXML(valuesToElements(l), false);
+ if (!hasContent(xml))
+ return mkfailure<axiom_node_t*>(xml);
+ ostringstream os;
+ write(content(xml), os);
+ return stringToAxiomNode(str(os), ax);
+}
+
+/**
+ * Convert an axiom node to a string.
+ */
+const failable<const string> axiomNodeToString(axiom_node_t* node, const Axis2Context& ax) {
+ const char* c = axiom_node_to_string(node, env(ax));
+ if (c == NULL)
+ return mkfailure<const string>(string("Couldn't convert Axiom node to XML: ") + axis2Error(ax));
+ const string s(c);
+ AXIS2_FREE(env(ax)->allocator, const_cast<char*>(c));
+ return s;
+}
+
+/**
+ * Convert an axiom node to a list of values representing XML elements.
+ */
+const failable<const list<value> > axiomNodeToValues(axiom_node_t* node, const Axis2Context& ax) {
+ const failable<const string> s = axiomNodeToString(node, ax);
+ if (!hasContent(s))
+ return mkfailure<const list<value> >(s);
+ istringstream is(content(s));
+ const failable<const list<value> > l = readXML(streamList(is));
+ if (!hasContent(l))
+ return l;
+ return elementsToValues(content(l));
+}
+
+/**
+ * Evaluate an expression in the form (soap-action-string, document, uri). Send the
+ * SOAP action and document to the Web Service at the given URI using Axis2.
+ */
+const failable<value> evalExpr(const value& expr, const Axis2Context& ax) {
+ debug(expr, "webservice::evalExpr::input");
+
+ // Extract func name and single argument
+ const value func(car<value>(expr));
+ const list<value> param(cadr<value>(expr));
+ const value uri(caddr<value>(expr));
+
+ // Create Axis2 client
+ axis2_svc_client_t *client = axis2_svc_client_create(env(ax), getenv("AXIS2C_HOME"));
+ if (client == NULL)
+ return mkfailure<value>("Couldn't create Axis2 client: " + axis2Error(ax));
+ axis2_endpoint_ref_t *epr = axis2_endpoint_ref_create(env(ax), c_str(uri));
+ axis2_options_t *opt = axis2_options_create(env(ax));
+ axis2_options_set_to(opt, env(ax), epr);
+ axis2_options_set_action(opt, env(ax), (const axis2_char_t*)c_str(func));
+ axis2_svc_client_set_options(client, env(ax), opt);
+ axis2_svc_client_engage_module(client, env(ax), AXIS2_MODULE_ADDRESSING);
+
+ // Construct request Axiom node
+ const failable<axiom_node_t*> req = valuesToAxiomNode(param, ax);
+ if (!hasContent(req))
+ return mkfailure<value>(req);
+
+ // Call the Web service
+ axiom_node_t* res = axis2_svc_client_send_receive(client, env(ax), content(req));
+ if (res == NULL) {
+ axis2_svc_client_free(client, env(ax));
+ return mkfailure<value>("Couldn't invoke Axis2 service: " + axis2Error(ax));
+ }
+
+ // Parse result Axiom node
+ const failable<const list<value> > lval = axiomNodeToValues(res, ax);
+ if (!hasContent(lval))
+ return mkfailure<value>(lval);
+ const value rval = content(lval);
+ debug(rval, "webservice::evalExpr::result");
+
+ // Cleanup
+ axis2_svc_client_free(client, env(ax));
+
+ return rval;
+}
+
+}
+}
+
+#endif /* tuscany_webservice_hpp */
diff --git a/sca-cpp/branches/lightweight-sca/components/webservice/axis2.xml b/sca-cpp/branches/lightweight-sca/components/webservice/axis2.xml
new file mode 100644
index 0000000000..ea9b6d2194
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/webservice/axis2.xml
@@ -0,0 +1,148 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<axisconfig name="Axis2/C">
+ <!-- ================================================= -->
+ <!-- Parameters -->
+ <!-- ================================================= -->
+ <!-- Uncomment following to enable MTOM support globally -->
+ <!--parameter name="enableMTOM" locked="false">true</parameter-->
+
+ <!-- Set the suitable size for optimum memory usage when sending large attachments -->
+ <!--parameter name="MTOMBufferSize" locked="false">10</parameter-->
+ <!--parameter name="MTOMMaxBuffers" locked="false">1000</parameter-->
+ <!--parameter name="EnableMTOMServiceCallback" locked="false">true</parameter-->
+ <!--parameter name="attachmentDIR" locked="false">/path/to/the/attachment/caching/dir/</parameter-->
+ <!--parameter name="MTOMCachingCallback" locked="false">/path/to/the/caching_callback</parameter-->
+ <!--parameter name="MTOMSendingCallback" locked="false">/path/to/the/sending_callback</parameter-->
+
+ <!-- Enable REST -->
+ <parameter name="enableREST" locked="false">true</parameter>
+
+ <!-- Uncomment following to persist op_ctx, useful with RM -->
+ <!--parameter name="persistOperationContext" locked="false">true</parameter-->
+
+ <!--if you want to extract the service archive file and work with that please uncomment this-->
+ <!--else , it wont extract archive file or does not take into consideration if someone drop-->
+ <!--exploded directory into /service directory-->
+ <!--<parameter name="extractServiceArchive" locked="false">true</parameter>-->
+
+
+ <!-- ================================================= -->
+ <!-- Message Receivers -->
+ <!-- ================================================= -->
+ <!-- This is the Deafult Message Receiver for the Request Response style Operations -->
+ <!--messageReceiver mep="INOUT" class="axis2_receivers"/-->
+
+
+ <!-- ================================================= -->
+ <!-- Transport Ins -->
+ <!-- ================================================= -->
+
+ <transportReceiver name="http" class="axis2_http_receiver">
+ <parameter name="port" locked="false">6060</parameter>
+ <parameter name="exposeHeaders" locked="true">false</parameter>
+ </transportReceiver>
+
+ <!--transportReceiver name="https" class="axis2_http_receiver">
+ <parameter name="port" locked="false">6060</parameter>
+ <parameter name="exposeHeaders" locked="true">false</parameter>
+ </transportReceiver-->
+
+ <!--transportReceiver name="tcp" class="axis2_tcp_receiver">
+ <parameter name="port" locked="false">6060</parameter>
+ </transportReceiver-->
+
+
+ <!-- ================================================= -->
+ <!-- Transport Outs -->
+ <!-- ================================================= -->
+
+ <transportSender name="http" class="axis2_http_sender">
+ <parameter name="PROTOCOL" locked="false">HTTP/1.1</parameter>
+ <parameter name="xml-declaration" insert="false"/>
+ <!--parameter name="Transfer-Encoding">chunked</parameter-->
+ <!--parameter name="HTTP-Authentication" username="" password="" locked="true"/-->
+ <!--parameter name="PROXY" proxy_host="127.0.0.1" proxy_port="8080" proxy_username="" proxy_password="" locked="true"/-->
+ </transportSender>
+
+ <!-- Uncomment the following with appropriate parameters to enable the SSL transport sender.
+ Also make sure that the appropriate transport receiver is enabled above.-->
+ <!--transportSender name="https" class="axis2_http_sender">
+ <parameter name="PROTOCOL" locked="false">HTTP/1.1</parameter>
+ <parameter name="xml-declaration" insert="false"/>
+ </transportSender>
+ <parameter name="SERVER_CERT">/path/to/ca/certificate</parameter>
+ <parameter name="KEY_FILE">/path/to/client/certificate/chain/file</parameter>
+ <parameter name="SSL_PASSPHRASE">passphrase</parameter>
+ -->
+
+ <!-- Uncomment this one with the appropriate papameters to enable the TCP transport Sender-->
+ <!--transportSender name="tcp" class="axis2_tcp_sender">
+ <parameter name="PROTOCOL" locked="false">TCP</parameter>
+ <parameter name="xml-declaration" insert="false"/>
+ </transportSender-->
+
+
+ <!-- ================================================= -->
+ <!-- Global Modules -->
+ <!-- ================================================= -->
+ <!-- Comment this to disable Addressing -->
+ <module ref="addressing"/>
+
+ <!-- Tuscany dispatcher module -->
+ <module ref="tuscany"/>
+
+ <!--Configuring module , providing paramters for modules whether they refer or not-->
+ <!--<moduleConfig name="addressing">-->
+ <!--<parameter name="addressingPara" locked="false">N/A</parameter>-->
+ <!--</moduleConfig>-->
+
+ <!-- ================================================= -->
+ <!-- Phases -->
+ <!-- ================================================= -->
+ <phaseOrder type="inflow">
+ <!-- System pre defined phases -->
+ <phase name="Transport"/>
+ <phase name="PreDispatch"/>
+ <phase name="Dispatch"/>
+ <phase name="PostDispatch"/>
+ <!--phase name="Security"/-->
+ <!-- End system pre defined phases -->
+ <!-- After PostDispatch phase, module or service author can add any phase as required -->
+ <!-- User defined phases could be added here -->
+ <!--phase name="userphase1"/-->
+ </phaseOrder>
+ <phaseOrder type="outflow">
+ <!-- User defined phases could be added here -->
+ <!--phase name="userphase1"/-->
+ <!--system predefined phase-->
+ <phase name="MessageOut"/>
+ <!--phase name="Security"/-->
+ </phaseOrder>
+ <phaseOrder type="INfaultflow">
+ <!-- User defined phases could be added here -->
+ <!--phase name="userphase1"/-->
+ </phaseOrder>
+ <phaseOrder type="Outfaultflow">
+ <!-- User defined phases could be added here -->
+ <!--phase name="userphase1"/-->
+ <phase name="MessageOut"/>
+ </phaseOrder>
+</axisconfig>
+
diff --git a/sca-cpp/branches/lightweight-sca/components/webservice/client-test.cpp b/sca-cpp/branches/lightweight-sca/components/webservice/client-test.cpp
new file mode 100644
index 0000000000..16dd659b22
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/webservice/client-test.cpp
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 Web service component.
+ */
+
+#include <assert.h>
+#include "stream.hpp"
+#include "string.hpp"
+#include "list.hpp"
+#include "element.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "perf.hpp"
+#include "../../modules/http/http.hpp"
+#include "axis2.hpp"
+
+namespace tuscany {
+namespace webservice {
+
+
+bool testModAxis2() {
+ const Axis2Context ax;
+
+ const value func = "http://ws.apache.org/axis2/c/samples/echoString";
+ const list<value> arg = mklist<value>(
+ list<value>() + "ns1:echoString"
+ + (list<value>() + "@xmlns:ns1" + string("http://ws.apache.org/axis2/services/echo"))
+ + (list<value>() + "text" + string("Hello World!")));
+
+ const failable<value> rval = evalExpr(mklist<value>(func, arg, string("http://localhost:8090/echo-listener")), ax);
+ assert(hasContent(rval));
+
+ const list<value> r = mklist<value>(
+ list<value>() + "ns1:echoString"
+ + (list<value>() + "@xmlns:ns1" + string("http://ws.apache.org/axis2/services/echo"))
+ + (list<value>() + "text" + string("Hello World!")));
+ assert(content(rval) == r);
+
+ return true;
+}
+
+bool testEval() {
+ http::CURLSession cs("", "", "", "");
+
+ const value func = "http://ws.apache.org/axis2/c/samples/echoString";
+ const list<value> arg = mklist<value>(
+ list<value>() + "ns1:echoString"
+ + (list<value>() + "@xmlns:ns1" + string("http://ws.apache.org/axis2/services/echo"))
+ + (list<value>() + "text" + string("Hello World!")));
+
+ const failable<value> rval = http::evalExpr(mklist<value>(func, arg), "http://localhost:8090/echo-client", cs);
+ assert(hasContent(rval));
+
+ const list<value> r = mklist<value>(
+ list<value>() + "ns1:echoString"
+ + (list<value>() + "@xmlns:ns1" + string("http://ws.apache.org/axis2/c/samples"))
+ + (list<value>() + "text" + string("Hello World!")));
+ assert(content(rval) == r);
+ return true;
+}
+
+}
+}
+
+int main() {
+ tuscany::cout << "Testing..." << tuscany::endl;
+
+ tuscany::webservice::testModAxis2();
+ tuscany::webservice::testEval();
+
+ tuscany::cout << "OK" << tuscany::endl;
+
+ return 0;
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/webservice/echo-test b/sca-cpp/branches/lightweight-sca/components/webservice/echo-test
new file mode 100755
index 0000000000..1056a6c668
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/webservice/echo-test
@@ -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.
+
+# Setup
+axis2_prefix=`cat axis2c.prefix`
+export AXIS2C_HOME=$axis2_prefix
+axis2="$axis2_prefix/bin/axis2_http_server"
+pwd=`pwd`
+cd "$axis2_prefix/bin"
+$axis2 &
+cd $pwd
+sleep 1
+
+# Test
+./axis2-test 2>/dev/null
+rc=$?
+
+# Cleanup
+kill `ps -f | grep -v grep | grep "$axis2" | awk '{ print $2 }'`
+sleep 1
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/components/webservice/module.xml b/sca-cpp/branches/lightweight-sca/components/webservice/module.xml
new file mode 100644
index 0000000000..8f6ba5018f
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/webservice/module.xml
@@ -0,0 +1,25 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<module name="tuscany" class="axis2-dispatcher">
+ <inflow>
+ <handler name="TuscanyDispatcher" class="axis2-dispatcher">
+ <order phase="Dispatch"/>
+ </handler>
+ </inflow>
+</module>
diff --git a/sca-cpp/branches/lightweight-sca/components/webservice/server-test b/sca-cpp/branches/lightweight-sca/components/webservice/server-test
new file mode 100755
index 0000000000..cb12accbfb
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/webservice/server-test
@@ -0,0 +1,51 @@
+#!/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
+../../modules/http/httpd-conf tmp localhost 8090 ../../modules/http/htdocs
+../../modules/http/httpd-event-conf tmp
+../../modules/server/server-conf tmp
+../../modules/server/scheme-conf tmp
+./axis2-conf tmp
+cat >>tmp/conf/httpd.conf <<EOF
+SCAContribution `pwd`/
+SCAComposite webservice.composite
+EOF
+
+../../modules/http/httpd-start tmp
+
+axis2_prefix=`cat axis2c.prefix`
+export AXIS2C_HOME=$axis2_prefix
+axis2="$axis2_prefix/bin/axis2_http_server"
+pwd=`pwd`
+cd "$axis2_prefix/bin"
+$axis2 &
+cd $pwd
+sleep 2
+
+# Test
+./client-test 2>/dev/null
+rc=$?
+
+# Cleanup
+kill `ps -f | grep -v grep | grep "${axis2}" | awk '{ print $2 }'`
+../../modules/http/httpd-stop tmp
+sleep 2
+exit $rc
diff --git a/sca-cpp/branches/lightweight-sca/components/webservice/server-test.scm b/sca-cpp/branches/lightweight-sca/components/webservice/server-test.scm
new file mode 100644
index 0000000000..44e4eee92a
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/webservice/server-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.
+
+; Web service test case
+
+(define (echoString x) x)
+
diff --git a/sca-cpp/branches/lightweight-sca/components/webservice/services.xml b/sca-cpp/branches/lightweight-sca/components/webservice/services.xml
new file mode 100644
index 0000000000..0adf136f4f
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/webservice/services.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<serviceGroup>
+<service name="TuscanyService">
+ <parameter name="ServiceClass" locked="xsd:false">axis2-service</parameter>
+ <operation name="execute"/>
+</service>
+</serviceGroup>
diff --git a/sca-cpp/branches/lightweight-sca/components/webservice/webservice-client.componentType b/sca-cpp/branches/lightweight-sca/components/webservice/webservice-client.componentType
new file mode 100644
index 0000000000..043087fe98
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/webservice/webservice-client.componentType
@@ -0,0 +1,28 @@
+<?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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <service name="client"/>
+ <property name="uri" type="xsd:string"/>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/webservice/webservice-client.cpp b/sca-cpp/branches/lightweight-sca/components/webservice/webservice-client.cpp
new file mode 100644
index 0000000000..76d4905bf8
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/webservice/webservice-client.cpp
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+/**
+ * Web service client component implementation.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "axis2.hpp"
+
+namespace tuscany {
+namespace webservice {
+
+/**
+ * Apply a function provided by a remote Web service using Axis2.
+ */
+const failable<value> apply(const value& func, const list<value>& params) {
+ const Axis2Context ax;
+
+ // Extract parameters
+ const value doc = car<value>(params);
+ const lambda<value(const list<value>&)> l = cadr<value>(params);
+
+ // Call the URI property lambda function to get the configured URI
+ const value uri = l(list<value>());
+
+ // Evaluate using Axis2
+ return evalExpr(mklist<value>(func, doc, uri), ax);
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "start")
+ return tuscany::mkfailure<tuscany::value>();
+ return tuscany::webservice::apply(func, cdr(params));
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/webservice/webservice-listener.componentType b/sca-cpp/branches/lightweight-sca/components/webservice/webservice-listener.componentType
new file mode 100644
index 0000000000..ecd46b3c90
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/webservice/webservice-listener.componentType
@@ -0,0 +1,28 @@
+<?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.
+-->
+<componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace="http://tuscany.apache.org/xmlns/sca/components">
+
+ <service name="service"/>
+ <reference name="relay"/>
+
+</composite>
diff --git a/sca-cpp/branches/lightweight-sca/components/webservice/webservice-listener.cpp b/sca-cpp/branches/lightweight-sca/components/webservice/webservice-listener.cpp
new file mode 100644
index 0000000000..29ebef4bcb
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/webservice/webservice-listener.cpp
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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$ */
+
+/**
+ * Web service listener component implementation.
+ */
+
+#define WANT_HTTPD_LOG 1
+#include "string.hpp"
+#include "function.hpp"
+#include "list.hpp"
+#include "value.hpp"
+#include "monad.hpp"
+#include "../../modules/http/httpd.hpp"
+#include "axis2.hpp"
+
+namespace tuscany {
+namespace webservice {
+
+/**
+ * Redirect an HTTP request to the Axis2/C HTTPD module. The given relay lambda function
+ * is stored in the HTTPD request, for later retrieval by the Axis2 service to relay the request
+ * to a target component.
+ */
+extern "C" {
+ extern module axis2_module;
+}
+
+const value redirectToAxis2(const string& uri, request_rec* r, const value& relay) {
+ const failable<request_rec*, int> nr = httpd::internalRedirectRequest(uri, r);
+ if (!hasContent(nr))
+ return value(reason(nr), rcode(nr));
+ ap_set_module_config(content(nr)->request_config, &axis2_module, const_cast<void*>((const void*)&relay));
+ return value(httpd::internalRedirect(content(nr)));
+}
+
+/**
+ * Handle an HTTP request.
+ */
+const failable<value> handle(const list<value>& params) {
+
+ // Extract HTTPD request from the params
+ request_rec* r = httpd::request(car(params));
+ debug_httpdRequest(r, "webservice::handle");
+
+ // Extract the relay lambda from the params and store it in the HTTPD request,
+ // for later retrieval by our Axis2 service
+ const value relay = cadr(params);
+ cout << "relay: " << &relay << endl;
+
+ // Redirect HTTPD request to Mod-axis2
+ return redirectToAxis2(string("/axis2") + r->uri + r->args != NULL? string("?") + r->args : string(""), r, relay);
+}
+
+}
+}
+
+extern "C" {
+
+const tuscany::value apply(const tuscany::list<tuscany::value>& params) {
+ const tuscany::value func(car(params));
+ if (func == "handle")
+ return tuscany::webservice::handle(cdr(params));
+ return tuscany::mkfailure<tuscany::value>();
+}
+
+}
diff --git a/sca-cpp/branches/lightweight-sca/components/webservice/webservice.composite b/sca-cpp/branches/lightweight-sca/components/webservice/webservice.composite
new file mode 100644
index 0000000000..7b7c76b632
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/components/webservice/webservice.composite
@@ -0,0 +1,47 @@
+<?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://tuscany.apache.org/xmlns/sca/components"
+ name="webservice">
+
+ <component name="webservice-client">
+ <implementation.cpp path="." library="libwebservice-client"/>
+ <property name="uri">http://localhost:9090/axis2/services/echo</property>
+ <service name="webservice-client">
+ <binding.jsonrpc uri="echo-client"/>
+ </service>
+ </component>
+
+ <component name="webservice-listener">
+ <implementation.cpp path="." library="libwebservice-listener"/>
+ <service name="webservice-listener">
+ <binding.http uri="echo-listener"/>
+ </service>
+ <reference name="relay" target="echo"/>
+ </component>
+
+ <component name="echo">
+ <implementation.scheme script="server-test.scm"/>
+ <service name="echo">
+ <binding.jsonrpc uri="echo"/>
+ </service>
+ </component>
+
+</composite>