summaryrefslogtreecommitdiffstats
path: root/sca-cpp/branches/lightweight-sca/components/cache
diff options
context:
space:
mode:
Diffstat (limited to 'sca-cpp/branches/lightweight-sca/components/cache')
-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
23 files changed, 1598 insertions, 0 deletions
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