diff options
Diffstat (limited to '')
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> |