diff options
Diffstat (limited to 'sca-cpp/trunk/modules')
47 files changed, 5765 insertions, 0 deletions
diff --git a/sca-cpp/trunk/modules/Makefile.am b/sca-cpp/trunk/modules/Makefile.am new file mode 100644 index 0000000000..89256ae1a0 --- /dev/null +++ b/sca-cpp/trunk/modules/Makefile.am @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +SUBDIRS = atom cache eval http json scdl + diff --git a/sca-cpp/trunk/modules/atom/Makefile.am b/sca-cpp/trunk/modules/atom/Makefile.am new file mode 100644 index 0000000000..61998ece8b --- /dev/null +++ b/sca-cpp/trunk/modules/atom/Makefile.am @@ -0,0 +1,28 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +noinst_PROGRAMS = atom-test + +nobase_include_HEADERS = *.hpp + +INCLUDES = -I. -I$(top_builddir)/kernel -I${LIBXML2_INCLUDE} -I${APR_INCLUDE} + +atom_test_SOURCES = atom-test.cpp +atom_test_LDADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1 + +TESTS = atom-test + diff --git a/sca-cpp/trunk/modules/atom/atom-test.cpp b/sca-cpp/trunk/modules/atom/atom-test.cpp new file mode 100644 index 0000000000..7c14b954a0 --- /dev/null +++ b/sca-cpp/trunk/modules/atom/atom-test.cpp @@ -0,0 +1,194 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 ATOM data conversion functions. + */ + +#include <assert.h> +#include <iostream> +#include <sstream> +#include <string> +#include "slist.hpp" +#include "atom.hpp" + +namespace tuscany { +namespace atom { + +std::ostringstream* writer(const std::string& s, std::ostringstream* os) { + (*os) << s; + return os; +} + +std::string itemEntry("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<entry xmlns=\"http://www.w3.org/2005/Atom\">" + "<title type=\"text\">item</title>" + "<id>cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b</id>" + "<content type=\"application/xml\">" + "<item>" + "<name>Apple</name><price>$2.99</price>" + "</item>" + "</content>" + "<link href=\"cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b\"/>" + "</entry>\n"); + +std::string incompleteEntry("<entry xmlns=\"http://www.w3.org/2005/Atom\">" + "<title>item</title><content type=\"text/xml\">" + "<Item xmlns=\"http://services/\">" + "<name xmlns=\"\">Orange</name>" + "<price xmlns=\"\">3.55</price>" + "</Item>" + "</content>" + "</entry>"); + +std::string completedEntry("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<entry xmlns=\"http://www.w3.org/2005/Atom\">" + "<title type=\"text\">item</title>" + "<id></id>" + "<content type=\"application/xml\">" + "<Item xmlns=\"http://services/\">" + "<name xmlns=\"\">Orange</name>" + "<price xmlns=\"\">3.55</price>" + "</Item>" + "</content><link href=\"\"/>" + "</entry>\n"); + +bool testEntry() { + { + const list<value> i = list<value>() << element << "item" + << (list<value>() << element << "name" << std::string("Apple")) + << (list<value>() << element << "price" << std::string("$2.99")); + const list<value> a = mklist<value>(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); + std::ostringstream os; + writeATOMEntry<std::ostringstream*>(writer, &os, a); + assert(os.str() == itemEntry); + } + { + const list<value> a = readEntry(mklist(itemEntry)); + std::ostringstream os; + writeATOMEntry<std::ostringstream*>(writer, &os, a); + assert(os.str() == itemEntry); + } + { + const list<value> a = readEntry(mklist(incompleteEntry)); + std::ostringstream os; + writeATOMEntry<std::ostringstream*>(writer, &os, a); + assert(os.str() == completedEntry); + } + return true; +} + +std::string emptyFeed("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<feed xmlns=\"http://www.w3.org/2005/Atom\">" + "<title type=\"text\">Feed</title>" + "<id>1234</id>" + "</feed>\n"); + +std::string itemFeed("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<feed xmlns=\"http://www.w3.org/2005/Atom\">" + "<title type=\"text\">Feed</title>" + "<id>1234</id>" + "<entry xmlns=\"http://www.w3.org/2005/Atom\">" + "<title type=\"text\">item</title>" + "<id>cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b</id>" + "<content type=\"application/xml\">" + "<item>" + "<name>Apple</name><price>$2.99</price>" + "</item>" + "</content>" + "<link href=\"cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b\"/>" + "</entry>" + "<entry xmlns=\"http://www.w3.org/2005/Atom\">" + "<title type=\"text\">item</title>" + "<id>cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c</id>" + "<content type=\"application/xml\">" + "<item>" + "<name>Orange</name><price>$3.55</price>" + "</item>" + "</content>" + "<link href=\"cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c\"/>" + "</entry>" + "</feed>\n"); + +bool testFeed() { + { + std::ostringstream os; + writeATOMFeed<std::ostringstream*>(writer, &os, mklist<value>("Feed", "1234")); + assert(os.str() == emptyFeed); + } + { + const list<value> a = readFeed(mklist(emptyFeed)); + std::ostringstream os; + writeATOMFeed<std::ostringstream*>(writer, &os, a); + assert(os.str() == emptyFeed); + } + { + const list<value> i = list<value>() + << (list<value>() << "item" << "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b" + << (list<value>() << element << "item" + << (list<value>() << element << "name" << "Apple") + << (list<value>() << element << "price" << "$2.99"))) + << (list<value>() << "item" << "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c" + << (list<value>() << element << "item" + << (list<value>() << element << "name" << "Orange") + << (list<value>() << element << "price" << "$3.55"))); + const list<value> a = cons<value>("Feed", cons<value>("1234", i)); + std::ostringstream os; + writeATOMFeed<std::ostringstream*>(writer, &os, a); + assert(os.str() == itemFeed); + } + { + const list<value> i = list<value>() + << (list<value>() << "item" << "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b" + << valueToElement(list<value>() << "item" + << (list<value>() << "name" << "Apple") + << (list<value>() << "price" << "$2.99"))) + << (list<value>() << "item" << "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c" + << valueToElement(list<value>() << "item" + << (list<value>() << "name" << "Orange") + << (list<value>() << "price" << "$3.55"))); + const list<value> a = cons<value>("Feed", cons<value>("1234", i)); + std::ostringstream os; + writeATOMFeed<std::ostringstream*>(writer, &os, a); + assert(os.str() == itemFeed); + } + { + const list<value> a = readFeed(mklist(itemFeed)); + std::ostringstream os; + writeATOMFeed<std::ostringstream*>(writer, &os, a); + assert(os.str() == itemFeed); + } + return true; +} + +} +} + +int main() { + std::cout << "Testing..." << std::endl; + + tuscany::atom::testEntry(); + tuscany::atom::testFeed(); + + std::cout << "OK" << std::endl; + + return 0; +} diff --git a/sca-cpp/trunk/modules/atom/atom.hpp b/sca-cpp/trunk/modules/atom/atom.hpp new file mode 100644 index 0000000000..5054c635a0 --- /dev/null +++ b/sca-cpp/trunk/modules/atom/atom.hpp @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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_atom_hpp +#define tuscany_atom_hpp + +/** + * ATOM data conversion functions. + */ + +#include <string> +#include "list.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "xml.hpp" + +namespace tuscany { +namespace atom { + +/** + * Convert a list of elements to a list of values representing an ATOM entry. + */ +const list<value> entryValue(const list<value>& e) { + const list<value> lt = filter<value>(selector(mklist<value>(element, "title")), e); + const value t = isNil(lt)? value(std::string("")) : elementValue(car(lt)); + const list<value> li = filter<value>(selector(mklist<value>(element, "id")), e); + const value i = isNil(li)? value(std::string("")) : elementValue(car(li)); + const list<value> lc = filter<value>(selector(mklist<value>(element, "content")), e); + return mklist<value>(t, i, cadr(elementChildren(car(lc)))); +} + +/** + * Convert a list of elements to a list of values representing ATOM entries. + */ +const list<value> entriesValues(const list<value>& e) { + if (isNil(e)) + return e; + return cons<value>(entryValue(car(e)), entriesValues(cdr(e))); +} + +/** + * Convert a list of strings to a list of values representing an ATOM entry. + */ +const failable<list<value>, std::string> readEntry(const list<std::string>& ilist) { + const list<value> e = readXML(ilist); + if (isNil(e)) + return mkfailure<list<value>, std::string>("Empty entry"); + return entryValue(car(e)); +} + +/** + * Convert a list of strings to a list of values representing an ATOM feed. + */ +const failable<list<value>, std::string> readFeed(const list<std::string>& ilist) { + const list<value> f = readXML(ilist); + if (isNil(f)) + return mkfailure<list<value>, std::string>("Empty feed"); + const list<value> t = filter<value>(selector(mklist<value>(element, "title")), car(f)); + const list<value> i = filter<value>(selector(mklist<value>(element, "id")), car(f)); + const list<value> e = filter<value>(selector(mklist<value>(element, "entry")), car(f)); + if (isNil(e)) + return mklist<value>(elementValue(car(t)), elementValue(car(i))); + return cons<value>(elementValue(car(t)), cons(elementValue(car(i)), entriesValues(e))); +} + +/** + * Convert a list of values representing an ATOM entry to a list of elements. + * The first two values in the list are the entry title and id. + */ +const list<value> entryElement(const list<value>& l) { + return list<value>() + << element << "entry" << (list<value>() << attribute << "xmlns" << "http://www.w3.org/2005/Atom") + << (list<value>() << element << "title" << (list<value>() << attribute << "type" << "text") << car(l)) + << (list<value>() << element << "id" << cadr(l)) + << (list<value>() << element << "content" << (list<value>() << attribute << "type" << "application/xml") << caddr(l)) + << (list<value>() << element << "link" << (list<value>() << attribute << "href" << cadr(l))); +} + +/** + * Convert a list of values representing ATOM entries to a list of elements. + */ +const list<value> entriesElements(const list<value>& l) { + if (isNil(l)) + return l; + return cons<value>(entryElement(car(l)), entriesElements(cdr(l))); +} + +/** + * Convert a list of values representing an ATOM entry to an ATOM entry. + * The first two values in the list are the entry id and title. + */ +template<typename R> const failable<R, std::string> writeATOMEntry(const lambda<R(std::string, R)>& reduce, const R& initial, const list<value>& l) { + return writeXML<R>(reduce, initial, mklist<value>(entryElement(l))); +} + +const failable<list<std::string>, std::string> writeATOMEntry(const list<value>& l) { + const failable<list<std::string>, std::string> ls = writeATOMEntry<list<std::string> >(rcons<std::string>, list<std::string>(), l); + if (!hasValue(ls)) + return ls; + return reverse(list<std::string>(ls)); +} + +/** + * Convert a list of values representing an ATOM feed to an ATOM feed. + * The first two values in the list are the feed id and title. + */ +template<typename R> const failable<R, std::string> writeATOMFeed(const lambda<R(std::string, R)>& reduce, const R& initial, const list<value>& l) { + const list<value> f = list<value>() + << element << "feed" << (list<value>() << attribute << "xmlns" << "http://www.w3.org/2005/Atom") + << (list<value>() << element << "title" << (list<value>() << attribute << "type" << "text") << car(l)) + << (list<value>() << element << "id" << cadr(l)); + if (isNil(cddr(l))) + return writeXML<R>(reduce, initial, mklist<value>(f)); + const list<value> fe = append(f, entriesElements(cddr(l))); + return writeXML<R>(reduce, initial, mklist<value>(fe)); +} + +/** + * Convert a list of values representing an ATOM feed to a list of strings. + * The first two values in the list are the feed id and title. + */ +const failable<list<std::string>, std::string> writeATOMFeed(const list<value>& l) { + const failable<list<std::string>, std::string> ls = writeATOMFeed<list<std::string> >(rcons<std::string>, list<std::string>(), l); + if (!hasValue(ls)) + return ls; + return reverse(list<std::string>(ls)); +} + +/** + * Convert an ATOM entry containing a value to an ATOM entry containing an item element. + */ +const list<value> entryValuesToElements(const list<value> val) { + return cons(car(val), cons(cadr(val), valuesToElements(mklist<value>(cons<value>("item", (list<value>)caddr(val)))))); +} + +/** + * Convert an ATOM feed containing values to an ATOM feed containing elements. + */ +const list<value> feedValuesToElementsLoop(const list<value> val) { + if (isNil(val)) + return val; + return cons<value>(entryValuesToElements(car(val)), feedValuesToElementsLoop(cdr(val))); +} + +const list<value> feedValuesToElements(const list<value>& val) { + return cons(car<value>(val), cons<value>(cadr<value>(val), feedValuesToElementsLoop(cddr<value>(val)))); +} + +} +} + +#endif /* tuscany_atom_hpp */ diff --git a/sca-cpp/trunk/modules/cache/Makefile.am b/sca-cpp/trunk/modules/cache/Makefile.am new file mode 100644 index 0000000000..0597e91804 --- /dev/null +++ b/sca-cpp/trunk/modules/cache/Makefile.am @@ -0,0 +1,31 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +noinst_PROGRAMS = diskcache-test memcache-test + +nobase_include_HEADERS = *.hpp + +INCLUDES = -I. -I$(top_builddir)/kernel -I${LIBXML2_INCLUDE} -I${APR_INCLUDE} + +diskcache_test_SOURCES = diskcache-test.cpp +diskcache_test_LDADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1 + +memcache_test_SOURCES = memcache-test.cpp +memcache_test_LDADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1 + +TESTS = memcached-test diskcached-test + diff --git a/sca-cpp/trunk/modules/cache/cache.hpp b/sca-cpp/trunk/modules/cache/cache.hpp new file mode 100644 index 0000000000..ba66021f3d --- /dev/null +++ b/sca-cpp/trunk/modules/cache/cache.hpp @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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_cache_hpp +#define tuscany_cache_hpp + +/** + * Simple cache monad implementation. + */ + +#include <sys/stat.h> + +#include <string> +#include "value.hpp" +#include "monad.hpp" + +namespace tuscany { +namespace cache { + +/** + * Cached monad. Used to represent a value that can be cached. + * To get the value in the monad, just cast it to the value type. + */ +template<typename V> class cached { +public: + cached() : mtime(0) { + } + + cached(const lambda<V()>& lvalue, const lambda<unsigned long()> ltime) : lvalue(lvalue), ltime(ltime), mtime(0) { + } + + cached(const lambda<V()>& lvalue, const lambda<unsigned long()> ltime, const unsigned long mtime, const V& v) : lvalue(lvalue), ltime(ltime), mtime(mtime), v(v) { + } + + cached(const cached<V>& c) : lvalue(c.lvalue), ltime(c.ltime), mtime(c.mtime), v(c.v) { + } + + operator const V() const { + return v; + } + + const cached<V>& operator=(const cached<V>& c) { + if(this == &c) + return *this; + this->lvalue = c.lvalue; + this->ltime = c.ltime; + this->mtime = c.mtime; + this->v = c.v; + return *this; + } + + const bool operator!=(const cached<V>& m) const { + return !this->operator==(m); + } + + const bool operator==(const cached<V>& m) const { + if (this == &m) + return true; + return mtime == m.mtime && v == m.v; + } + +private: + lambda<V()> lvalue; + lambda<time_t()> ltime; + unsigned long mtime; + V v; + + template<typename X> friend const cached<X> latest(const cached<X>& c); + template<typename X> friend std::ostream& operator<<(std::ostream& out, const cached<X>& c); +}; + +/** + * Write a cached monad to a stream. + */ +template<typename V> std::ostream& operator<<(std::ostream& out, const cached<V>& c) { + out << c.v; + return out; +} + +/** + * Returns the latest value of a cached monad. + */ +template<typename V> const cached<V> latest(const cached<V>& c) { + unsigned long nt = c.ltime(); + if (nt == c.mtime) + return c; + return cached<V>(c.lvalue, c.ltime, nt, c.lvalue()); +} + +/** + * Returns the latest modification time of a file. + */ +const unsigned long latestFileTime(const std::string& path) { + struct stat st; + int rc = stat(path.c_str(), &st); + if (rc < 0) + return 0; + return st.st_mtim.tv_nsec; +} + +} +} + +#endif /* tuscany_cache_hpp */ diff --git a/sca-cpp/trunk/modules/cache/diskcache-test.cpp b/sca-cpp/trunk/modules/cache/diskcache-test.cpp new file mode 100644 index 0000000000..3b21de7b9e --- /dev/null +++ b/sca-cpp/trunk/modules/cache/diskcache-test.cpp @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 functions. + */ + +#include <assert.h> +#include <sys/time.h> +#include <time.h> +#include <iostream> +#include <string> +#include <fstream> +#include "slist.hpp" +#include "cache.hpp" + +namespace tuscany { +namespace cache { + +const std::string fileRead(const std::string path) { + std::ifstream is(path); + return car(streamList(is)); +} + +bool testCache() { + const std::string p("tmp/test.txt"); + const lambda<std::string(std::string)> fr(fileRead); + const lambda<time_t(std::string)> ft(latestFileTime); + + const cached<std::string> c(curry(fr, p), curry(ft, p)); + + { + std::ofstream os(p); + os << "initial"; + os.close(); + assert(std::string(latest(c)) == std::string("initial")); + } + + usleep(1000000); + + { + std::ofstream os(p); + os << "updated"; + os.close(); + assert(latest(c) != c); + assert(std::string(latest(c)) == std::string("updated")); + assert(latest(c) == latest(c)); + } + return true; +} + +} +} + +int main() { + std::cout << "Testing..." << std::endl; + + tuscany::cache::testCache(); + + std::cout << "OK" << std::endl; + + return 0; +} diff --git a/sca-cpp/trunk/modules/cache/diskcached-test b/sca-cpp/trunk/modules/cache/diskcached-test new file mode 100755 index 0000000000..4c10abeceb --- /dev/null +++ b/sca-cpp/trunk/modules/cache/diskcached-test @@ -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. + +# Setup +mkdir -p tmp + +# Test +./diskcache-test +rc=$? + +return $rc diff --git a/sca-cpp/trunk/modules/cache/memcache-test.cpp b/sca-cpp/trunk/modules/cache/memcache-test.cpp new file mode 100644 index 0000000000..3522094126 --- /dev/null +++ b/sca-cpp/trunk/modules/cache/memcache-test.cpp @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 <sys/time.h> +#include <time.h> +#include <iostream> +#include <string> +#include <fstream> +#include "memcached.hpp" + +namespace tuscany { +namespace cache { + +bool testMemCached() { + memcached::Cache cache; + memcached::addServer("localhost", 11311, cache); + + assert(hasValue(memcached::post("a", "AAA", cache))); + assert(memcached::get("a", cache) == value(std::string("AAA"))); + assert(hasValue(memcached::put("a", "aaa", cache))); + assert(memcached::get("a", cache) == value(std::string("aaa"))); + assert(hasValue(memcached::del("a", cache))); + assert(!hasValue(memcached::get("a", cache))); + + return true; +} + +const double duration(struct timeval start, struct timeval end, int count) { + long t = (end.tv_sec * 1000 + end.tv_usec / 1000) - (start.tv_sec * 1000 + start.tv_usec / 1000); + return (double)t / (double)count; +} + +bool testGetLoop(const int count, memcached::Cache& cache) { + if (count == 0) + return true; + assert(memcached::get("c", cache) == value(std::string("CCC"))); + return testGetLoop(count - 1, cache); +} + +bool testGetPerf() { + const int count = 50; + struct timeval start; + struct timeval end; + { + memcached::Cache cache; + memcached::addServer("localhost", 11311, cache); + assert(hasValue(memcached::post("c", "CCC", cache))); + + testGetLoop(5, cache); + + gettimeofday(&start, NULL); + + testGetLoop(count, cache); + + gettimeofday(&end, NULL); + std::cout << "Memcached get test " << duration(start, end, count) << " ms" << std::endl; + } + return true; +} + +} +} + +int main() { + std::cout << "Testing..." << std::endl; + + tuscany::cache::testMemCached(); + tuscany::cache::testGetPerf(); + + std::cout << "OK" << std::endl; + + return 0; +} diff --git a/sca-cpp/trunk/modules/cache/memcached-test b/sca-cpp/trunk/modules/cache/memcached-test new file mode 100755 index 0000000000..d0e0dff3de --- /dev/null +++ b/sca-cpp/trunk/modules/cache/memcached-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 +cmd="memcached -l 127.0.0.1 -m 4 -p 11311" +$cmd & +sleep 1 + +# Test +./memcache-test +rc=$? + +# Cleanup +ps -f | grep -v grep | grep "$cmd" | awk '{ print $2 }' | xargs kill +return $rc diff --git a/sca-cpp/trunk/modules/cache/memcached.hpp b/sca-cpp/trunk/modules/cache/memcached.hpp new file mode 100644 index 0000000000..28bdc0fc89 --- /dev/null +++ b/sca-cpp/trunk/modules/cache/memcached.hpp @@ -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$ */ + +#ifndef tuscany_memcached_hpp +#define tuscany_memcached_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> +#include "list.hpp" +#include "value.hpp" +#include "monad.hpp" + +namespace tuscany { +namespace memcached { + +/** + * Represents a memcached context. + */ +class Cache { +public: + Cache() { + apr_pool_create(&pool, NULL); + apr_memcache_create(pool, 1, 0, &mc); + } + ~Cache() { + apr_pool_destroy(pool); + } + + operator apr_memcache_t*() const { + return mc; + } + + operator apr_pool_t*() const { + return pool; + } + +private: + apr_pool_t* pool; + apr_memcache_t* mc; + +}; + +/** + * Add a server to the memcached context. + */ +const failable<bool, std::string> addServer(const std::string& host, const int port, const Cache& cache) { + apr_memcache_server_t *server; + const apr_status_t sc = apr_memcache_server_create(cache, host.c_str(), port, 0, 1, 1, 60, &server); + if (sc != APR_SUCCESS) + return mkfailure<bool, std::string>("Could not create server"); + const apr_status_t as = apr_memcache_add_server(cache, server); + if (as != APR_SUCCESS) + return mkfailure<bool, std::string>("Could not add server"); + return true; +} + +/** + * Post a new item to the cache. + */ +const failable<bool, std::string> post(const value& key, const value& val, const Cache& cache) { + const std::string v(val); + const apr_status_t rc = apr_memcache_add(cache, std::string(key).c_str(), const_cast<char*>(v.c_str()), v.size(), 0, 27); + if (rc != APR_SUCCESS) + return mkfailure<bool, std::string>("Could not add entry"); + return true; +} + +/** + * Update an item in the cache. + */ +const failable<bool, std::string> put(const value& key, const value& val, const Cache& cache) { + const std::string v(val); + const apr_status_t rc = apr_memcache_replace(cache, std::string(key).c_str(), const_cast<char*>(v.c_str()), v.size(), 0, 27); + if (rc != APR_SUCCESS) + return mkfailure<bool, std::string>("Could not add entry"); + return true; +} + +/** + * Get an item from the cache. + */ +const failable<value, std::string> get(const value& key, const Cache& cache) { + apr_pool_t* vpool; + const apr_status_t pc = apr_pool_create(&vpool, cache); + if (pc != APR_SUCCESS) + return mkfailure<value, std::string>("Could not allocate memory"); + + char *data; + apr_size_t size; + const apr_status_t rc = apr_memcache_getp(cache, cache, std::string(key).c_str(), &data, &size, NULL); + if (rc != APR_SUCCESS) { + apr_pool_destroy(vpool); + return mkfailure<value, std::string>("Could not get entry"); + } + + const value val(std::string(data, size)); + apr_pool_destroy(vpool); + return val; +} + +/** + * Delete an item from the cache + */ +const failable<bool, std::string> del(const value& key, const Cache& cache) { + const apr_status_t rc = apr_memcache_delete(cache, std::string(key).c_str(), 0); + if (rc != APR_SUCCESS) + return mkfailure<bool, std::string>("Could not add entry"); + return true; +} + +} +} + +#endif /* tuscany_memcached_hpp */ diff --git a/sca-cpp/trunk/modules/eval/Makefile.am b/sca-cpp/trunk/modules/eval/Makefile.am new file mode 100644 index 0000000000..68e01d42e2 --- /dev/null +++ b/sca-cpp/trunk/modules/eval/Makefile.am @@ -0,0 +1,34 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +noinst_PROGRAMS = eval-test eval-shell + +datadir=$(prefix)/modules/eval +nobase_data_DATA = *.xsd + +nobase_include_HEADERS = *.hpp + +INCLUDES = -I. -I$(top_builddir)/kernel -I${LIBXML2_INCLUDE} -I${APR_INCLUDE} + +eval_test_SOURCES = eval-test.cpp +eval_test_LDADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1 + +eval_shell_SOURCES = eval-shell.cpp +eval_shell_LDADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1 + +TESTS = eval-test + diff --git a/sca-cpp/trunk/modules/eval/driver.hpp b/sca-cpp/trunk/modules/eval/driver.hpp new file mode 100644 index 0000000000..4c69ecb0a1 --- /dev/null +++ b/sca-cpp/trunk/modules/eval/driver.hpp @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_eval_driver_hpp +#define tuscany_eval_driver_hpp + +/** + * Script evaluator main driver loop. + */ + +#include <string> +#include <iostream> +#include "eval.hpp" + +namespace tuscany { +namespace eval { + +const std::string evalOutputPrompt("; "); +const std::string evalInputPrompt("=> "); + +const bool promptForInput(const std::string str, std::ostream& out) { + out << "\n\n" << str; + return true; +} + +const bool announceOutput(const std::string str, std::ostream& out) { + out << "\n" << str; + return true; +} + +const bool userPrint(const value val, std::ostream& out) { + if(isCompoundProcedure(val)) + writeValue(mklist<value>(compoundProcedureSymbol, procedureParameters(val), procedureBody(val), "<procedure-env>"), out); + writeValue(val, out); + return true; +} + +const value evalDriverLoop(std::istream& in, std::ostream& out, Env& globalEnv, const gc_pool& pool) { + promptForInput(evalInputPrompt, out); + value input = readValue(in); + if (isNil(input)) + return input; + const value output = evalExpr(input, globalEnv, pool); + announceOutput(evalOutputPrompt, out); + userPrint(output, out); + return evalDriverLoop(in, out, globalEnv, pool); +} + +const bool evalDriverRun(std::istream& in, std::ostream& out) { + gc_pool pool; + setupDisplay(out); + Env globalEnv = setupEnvironment(pool); + evalDriverLoop(in, out, globalEnv, pool); + return true; +} + +} +} +#endif /* tuscany_eval_driver_hpp */ diff --git a/sca-cpp/trunk/modules/eval/environment.hpp b/sca-cpp/trunk/modules/eval/environment.hpp new file mode 100644 index 0000000000..fa9667b1ba --- /dev/null +++ b/sca-cpp/trunk/modules/eval/environment.hpp @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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_eval_environment_hpp +#define tuscany_eval_environment_hpp + +/** + * Script evaluator environment implementation. + */ + +#include <string> +#include "list.hpp" +#include "value.hpp" +#include "primitive.hpp" + +namespace tuscany { +namespace eval { + +typedef value Frame; +typedef list<value> Env; + +const value trueSymbol("true"); +const value falseSymbol("false"); +const value defineSymbol("define"); +const value setSymbol("set!"); +const value dotSymbol("."); + +const Env theEmptyEnvironment() { + return list<value>(); +} + +const bool isDefinition(const value& exp) { + return isTaggedList(exp, defineSymbol); +} + +const bool isAssignment(const value& exp) { + return isTaggedList(exp, setSymbol); +} + +const bool isVariable(const value& exp) { + return isSymbol(exp); +} + +const Env enclosingEnvironment(const Env& env) { + return cdr(env); +} + +const gc_pool_ptr<Frame> firstFrame(const Env& env) { + return car(env); +} + +list<value> frameVariables(const Frame& frame) { + return car((list<value> )frame); +} + +list<value> frameValues(const Frame& frame) { + return cdr((list<value> )frame); +} + +const bool isDotVariable(const value& var) { + return var == dotSymbol; +} + +const Frame makeBinding(const Frame& frameSoFar, const list<value>& variables, const list<value> values) { + if (isNil(variables)) { + if (!isNil(values)) + std::cout << "Too many arguments supplied " << values << "\n"; + return frameSoFar; + } + if (isDotVariable(car(variables))) + return makeBinding(frameSoFar, cdr(variables), mklist<value>(values)); + + if (isNil(values)) { + if (!isNil(variables)) + std::cout << "Too few arguments supplied " << variables << "\n"; + return frameSoFar; + } + + const list<value> vars = cons(car(variables), frameVariables(frameSoFar)); + const list<value> vals = cons(car(values), frameValues(frameSoFar)); + const Frame newFrame = cons(value(vars), vals); + + return makeBinding(newFrame, cdr(variables), cdr(values)); +} + +const gc_pool_ptr<Frame> makeFrame(const list<value>& variables, const list<value> values, const gc_pool& pool) { + gc_pool_ptr<Frame> frame = gc_pool_new<Frame>(pool); + *frame = value(makeBinding(cons(value(list<value>()), list<value>()), variables, values)); + return frame; +} + +const value definitionVariable(const value& exp) { + const list<value> exps(exp); + if(isSymbol(car(cdr(exps)))) + return car(cdr(exps)); + const list<value> lexps(car(cdr(exps))); + return car(lexps); +} + +const value definitionValue(const value& exp) { + const list<value> exps(exp); + if(isSymbol(car(cdr(exps)))) + return car(cdr(cdr(exps))); + const list<value> lexps(car(cdr(exps))); + return makeLambda(cdr(lexps), cdr(cdr(exps))); +} + +const value assignmentVariable(const value& exp) { + return car(cdr((list<value> )exp)); +} + +const value assignmentValue(const value& exp) { + return car(cdr(cdr((list<value> )exp))); +} + +const Frame addBindingToFrame(const value& var, const value& val, const Frame& frame) { + return cons(value(cons(var, frameVariables(frame))), cons(val, frameValues(frame))); +} + +const bool defineVariable(const value& var, const value& val, Env& env) { + *firstFrame(env) = addBindingToFrame(var, val, *firstFrame(env)); + return true; +} + +const Env extendEnvironment(const list<value>& vars, const list<value>& vals, const Env& baseEnv, const gc_pool& pool) { + return cons<value>(makeFrame(vars, vals, pool), baseEnv); +} + +const Env setupEnvironment(const gc_pool& pool) { + Env env = extendEnvironment(primitiveProcedureNames(), primitiveProcedureObjects(), theEmptyEnvironment(), pool); + defineVariable(trueSymbol, true, env); + defineVariable(falseSymbol, false, env); + return env; +} + +const value lookupEnvLoop(const value& var, const Env& env); + +const value lookupEnvScan(const value& var, const list<value>& vars, const list<value>& vals, const Env& env) { + if(isNil(vars)) + return lookupEnvLoop(var, enclosingEnvironment(env)); + if(var == car(vars)) + return car(vals); + return lookupEnvScan(var, cdr(vars), cdr(vals), env); +} + +const value lookupEnvLoop(const value& var, const Env& env) { + if(env == theEmptyEnvironment()) { + std::cout << "Unbound variable " << var << "\n"; + return value(); + } + return lookupEnvScan(var, frameVariables(*firstFrame(env)), frameValues(*firstFrame(env)), env); +} + +const value lookupVariableValue(const value& var, const Env& env) { + return lookupEnvLoop(var, env); +} + +} +} +#endif /* tuscany_eval_environment_hpp */ diff --git a/sca-cpp/trunk/modules/eval/eval-shell.cpp b/sca-cpp/trunk/modules/eval/eval-shell.cpp new file mode 100644 index 0000000000..e1c90101da --- /dev/null +++ b/sca-cpp/trunk/modules/eval/eval-shell.cpp @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * Script evaluator shell, used for interactive testing of scripts. + */ + +#include <assert.h> +#include <iostream> +#include <string> +#include <sstream> +#include "driver.hpp" + +int main() { + tuscany::eval::evalDriverRun(std::cin, std::cout); + return 0; +} diff --git a/sca-cpp/trunk/modules/eval/eval-test.cpp b/sca-cpp/trunk/modules/eval/eval-test.cpp new file mode 100644 index 0000000000..984b17b26d --- /dev/null +++ b/sca-cpp/trunk/modules/eval/eval-test.cpp @@ -0,0 +1,240 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * Test script evaluator. + */ + +#include <assert.h> +#include <iostream> +#include <string> +#include <sstream> +#include "driver.hpp" + +namespace tuscany { +namespace eval { + +bool testEnv() { + gc_pool pool; + Env globalEnv = list<value>(); + Env env = extendEnvironment(mklist<value>("a"), mklist<value>(1), globalEnv, pool); + defineVariable("x", env, env); + assert(lookupVariableValue(value("x"), env) == env); + assert(lookupVariableValue("a", env) == value(1)); + return true; +} + +bool testEnvGC() { + resetLambdaCounters(); + resetListCounters(); + resetValueCounters(); + testEnv(); + assert(countValues == 0); + assert(countLambdas == 0); + assert(countlists == 0); + //printLambdaCounters(); + //printListCounters(); + //printValueCounters(); + return true; +} + +bool testRead() { + std::istringstream is("abcd"); + assert(readValue(is) == "abcd"); + + std::istringstream is2("123"); + assert(readValue(is2) == value(123)); + + std::istringstream is3("(abcd)"); + assert(readValue(is3) == mklist(value("abcd"))); + + std::istringstream is4("(abcd xyz)"); + assert(readValue(is4) == mklist<value>("abcd", "xyz")); + + std::istringstream is5("(abcd (xyz tuv))"); + assert(readValue(is5) == mklist<value>("abcd", mklist<value>("xyz", "tuv"))); + + return true; +} + +bool testWrite() { + const list<value> i = list<value>() + << (list<value>() << "item" << "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b" + << (list<value>() << "item" + << (list<value>() << "name" << "Apple") + << (list<value>() << "price" << "$2.99"))) + << (list<value>() << "item" << "cart-53d67a61-aa5e-4e5e-8401-39edeba8b83c" + << (list<value>() << "item" + << (list<value>() << "name" << "Orange") + << (list<value>() << "price" << "$3.55"))); + const list<value> a = cons<value>("Feed", cons<value>("feed-1234", i)); + std::ostringstream os; + writeValue(a, os); + std::istringstream is(os.str()); + assert(readValue(is) == a); + return true; +} + +const std::string testSchemeNumber( + "(define (testNumber) (if (= 1 1) (display \"testNumber ok\") (error \"testNumber\"))) " + "(testNumber)"); + +const std::string testSchemeString( + "(define (testString) (if (= \"abc\" \"abc\") (display \"testString ok\") (error \"testString\"))) " + "(testString)"); + +const std::string testSchemeDefinition( + "(define a \"abc\") (define (testDefinition) (if (= a \"abc\") (display \"testDefinition ok\") (error \"testDefinition\"))) " + "(testDefinition)"); + +const std::string testSchemeIf( + "(define (testIf) (if (= \"abc\" \"abc\") (if (= \"xyz\" \"xyz\") (display \"testIf ok\") (error \"testNestedIf\")) (error \"testIf\"))) " + "(testIf)"); + +const std::string testSchemeCond( + "(define (testCond) (cond ((= \"abc\" \"abc\") (display \"testCond ok\")) (else (error \"testIf\"))))" + "(testCond)"); + +const std::string testSchemeBegin( + "(define (testBegin) " + "(begin " + "(define a \"abc\") " + "(if (= a \"abc\") (display \"testBegin1 ok\") (error \"testBegin\")) " + "(define x \"xyz\") " + "(if (= x \"xyz\") (display \"testBegin2 ok\") (error \"testBegin\")) " + ") " + ") " + "(testBegin)"); + +const std::string testSchemeLambda( + "(define sqrt (lambda (x) (* x x))) " + "(define (testLambda) (if (= 4 (sqrt 2)) (display \"testLambda ok\") (error \"testLambda\"))) " + "(testLambda)"); + +const std::string testSchemeForward( + "(define (testLambda) (if (= 4 (sqrt 2)) (display \"testForward ok\") (error \"testForward\"))) " + "(define sqrt (lambda (x) (* x x))) " + "(testLambda)"); + +bool contains(const std::string& str, const std::string& pattern) { + return str.find(pattern) != str.npos; +} + +const std::string evalOutput(const std::string& scm) { + std::istringstream is(scm); + std::ostringstream os; + evalDriverRun(is, os); + return os.str(); +} + +bool testEval() { + assert(contains(evalOutput(testSchemeNumber), "testNumber ok")); + assert(contains(evalOutput(testSchemeString), "testString ok")); + assert(contains(evalOutput(testSchemeDefinition), "testDefinition ok")); + assert(contains(evalOutput(testSchemeIf), "testIf ok")); + assert(contains(evalOutput(testSchemeCond), "testCond ok")); + assert(contains(evalOutput(testSchemeBegin), "testBegin1 ok")); + assert(contains(evalOutput(testSchemeBegin), "testBegin2 ok")); + assert(contains(evalOutput(testSchemeLambda), "testLambda ok")); + assert(contains(evalOutput(testSchemeForward), "testForward ok")); + return true; +} + +bool testEvalExpr() { + gc_pool pool; + const value exp = mklist<value>("+", 2, 3); + Env env = setupEnvironment(pool); + const value r = evalExpr(exp, env, pool); + assert(r == value(5)); + return true; +} + +bool testEvalRun() { + evalDriverRun(std::cin, std::cout); + return true; +} + +const value mult(const list<value>& args) { + const double x = car(args); + const double y = cadr(args); + return x * y; +} + +const std::string testReturnLambda( + "(define (testReturnLambda) * )"); + +const std::string testCallLambda( + "(define (testCallLambda l x y) (l x y))"); + +bool testEvalLambda() { + gc_pool pool; + Env env = setupEnvironment(pool); + + const value trl = mklist<value>("testReturnLambda"); + std::istringstream trlis(testReturnLambda); + const value trlv = evalScript(trl, trlis, env, pool); + + std::istringstream tclis(testCallLambda); + const value tcl = cons<value>("testCallLambda", quotedParameters(mklist<value>(trlv, 2, 3))); + const value tclv = evalScript(tcl, tclis, env, pool); + assert(tclv == value(6)); + + std::istringstream tcelis(testCallLambda); + const value tcel = cons<value>("testCallLambda", quotedParameters(mklist<value>(primitiveProcedure(mult), 3, 4))); + const value tcelv = evalScript(tcel, tcelis, env, pool); + assert(tcelv == value(12)); + return true; +} + +bool testEvalGC() { + resetLambdaCounters(); + resetListCounters(); + resetValueCounters(); + testEval(); + testEvalExpr(); + testEvalLambda(); + assert(countValues == 0); + assert(countLambdas == 0); + assert(countlists == 0); + //printLambdaCounters(); + //printListCounters(); + //printValueCounters(); + return true; +} + +} +} + +int main() { + std::cout << "Testing..." << std::endl; + + tuscany::eval::testEnv(); + tuscany::eval::testEnvGC(); + tuscany::eval::testRead(); + tuscany::eval::testWrite(); + tuscany::eval::testEval(); + tuscany::eval::testEvalExpr(); + tuscany::eval::testEvalLambda(); + tuscany::eval::testEvalGC(); + + std::cout << "OK" << std::endl; + return 0; +} diff --git a/sca-cpp/trunk/modules/eval/eval.hpp b/sca-cpp/trunk/modules/eval/eval.hpp new file mode 100644 index 0000000000..8c9ecfdecc --- /dev/null +++ b/sca-cpp/trunk/modules/eval/eval.hpp @@ -0,0 +1,290 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_eval_eval_hpp +#define tuscany_eval_eval_hpp + +/** + * Core script evaluation logic. + */ + +#include <string.h> +#include "list.hpp" +#include "value.hpp" +#include "primitive.hpp" +#include "io.hpp" +#include "environment.hpp" + +namespace tuscany { +namespace eval { + +const value evalExpr(const value& exp, Env& env, const gc_pool& pool); + +const value compoundProcedureSymbol("compound-procedure"); +const value procedureSymbol("procedure"); +const value applySymbol("apply"); +const value beginSymbol("begin"); +const value condSymbol("cond"); +const value elseSymbol("else"); +const value ifSymbol("if"); + +const bool isBegin(const value& exp) { + return isTaggedList(exp, beginSymbol); +} + +const list<value> beginActions(const value& exp) { + return cdr((list<value> )exp); +} + +const bool isLambdaExpr(const value& exp) { + return isTaggedList(exp, lambdaSymbol); +} + +const list<value> lambdaParameters(const value& exp) { + return car(cdr((list<value> )exp)); +} + +static list<value> lambdaBody(const value& exp) { + return cdr(cdr((list<value> )exp)); +} + +const value makeProcedure(const list<value>& parameters, const value& body, const Env& env) { + return mklist<value>(procedureSymbol, parameters, body, env); +} + +const bool isApply(const value& exp) { + return isTaggedList(exp, applySymbol); +} + +const bool isApplication(const value& exp) { + return isList(exp); +} + +const value operat(const value& exp) { + return car((list<value> )exp); +} + +const list<value> operands(const value& exp) { + return cdr((list<value> )exp); +} + +const list<value> listOfValues(const list<value> exps, Env& env, const gc_pool& pool) { + if(isNil(exps)) + return list<value> (); + return cons(evalExpr(car(exps), env, pool), listOfValues(cdr(exps), env, pool)); +} + +const value applyOperat(const value& exp) { + return cadr((list<value> )exp); +} + +const value applyOperand(const value& exp) { + return caddr((list<value> )exp); +} + +const bool isCompoundProcedure(const value& procedure) { + return isTaggedList(procedure, procedureSymbol); +} + +const list<value> procedureParameters(const value& exp) { + return car(cdr((list<value> )exp)); +} + +const value procedureBody(const value& exp) { + return car(cdr(cdr((list<value> )exp))); +} + +const Env procedureEnvironment(const value& exp) { + return (Env)car(cdr(cdr(cdr((list<value> )exp)))); +} + +const bool isLastExp(const list<value>& seq) { + return isNil(cdr(seq)); +} + +const value firstExp(const list<value>& seq) { + return car(seq); +} + +const list<value> restExp(const list<value>& seq) { + return cdr(seq); +} + +const value makeBegin(const list<value> seq) { + return cons(beginSymbol, seq); +} + +const value evalSequence(const list<value>& exps, Env& env, const gc_pool& pool) { + if(isLastExp(exps)) + return evalExpr(firstExp(exps), env, pool); + evalExpr(firstExp(exps), env, pool); + return evalSequence(restExp(exps), env, pool); +} + +const value applyProcedure(const value& procedure, list<value>& arguments, const gc_pool& pool) { + if(isPrimitiveProcedure(procedure)) + return applyPrimitiveProcedure(procedure, arguments, pool); + if(isCompoundProcedure(procedure)) { + Env env = extendEnvironment(procedureParameters(procedure), arguments, procedureEnvironment(procedure), pool); + return evalSequence(procedureBody(procedure), env, pool); + } + std::cout << "Unknown procedure type " << procedure << "\n"; + return value(); +} + +const value sequenceToExp(const list<value> exps) { + if(isNil(exps)) + return exps; + if(isLastExp(exps)) + return firstExp(exps); + return makeBegin(exps); +} + +const list<value> condClauses(const value& exp) { + return cdr((list<value> )exp); +} + +const value condPredicate(const value& clause) { + return car((list<value> )clause); +} + +const list<value> condActions(const value& clause) { + return cdr((list<value> )clause); +} + +const value ifPredicate(const value& exp) { + return car(cdr((list<value> )exp)); +} + +const value ifConsequent(const value& exp) { + return car(cdr(cdr((list<value> )exp))); +} + +const value ifAlternative(const value& exp) { + if(!isNil(cdr(cdr(cdr((list<value> )exp))))) + return car(cdr(cdr(cdr((list<value> )exp)))); + return false; +} + +const bool isCond(const value& exp) { + return isTaggedList(exp, condSymbol); +} + +const bool isCondElseClause(const value& clause) { + return condPredicate(clause) == elseSymbol; +} + +const bool isIf(const value& exp) { + return isTaggedList(exp, ifSymbol); +} + +const value makeIf(value predicate, value consequent, value alternative) { + return mklist(ifSymbol, predicate, consequent, alternative); +} + +const value expandClauses(const list<value>& clauses) { + if(isNil(clauses)) + return false; + const value first = car(clauses); + const list<value> rest = cdr(clauses); + if(isCondElseClause(first)) { + if(isNil(rest)) + return sequenceToExp(condActions(first)); + std::cout << "else clause isn't last " << clauses << "\n"; + return value(); + } + return makeIf(condPredicate(first), sequenceToExp(condActions(first)), expandClauses(rest)); +} + +value condToIf(const value& exp) { + return expandClauses(condClauses(exp)); +} + +value evalIf(const value& exp, Env& env, const gc_pool& pool) { + if(isTrue(evalExpr(ifPredicate(exp), env, pool))) + return evalExpr(ifConsequent(exp), env, pool); + return evalExpr(ifAlternative(exp), env, pool); +} + +const value evalDefinition(const value& exp, Env& env, const gc_pool& pool) { + defineVariable(definitionVariable(exp), evalExpr(definitionValue(exp), env, pool), env); + return definitionVariable(exp); +} + +const value evalExpr(const value& exp, Env& env, const gc_pool& pool) { + if(isSelfEvaluating(exp)) + return exp; + if(isQuoted(exp)) + return textOfQuotation(exp); + if(isDefinition(exp)) + return evalDefinition(exp, env, pool); + if(isIf(exp)) + return evalIf(exp, env, pool); + if(isBegin(exp)) + return evalSequence(beginActions(exp), env, pool); + if(isCond(exp)) + return evalExpr(condToIf(exp), env, pool); + if(isLambdaExpr(exp)) + return makeProcedure(lambdaParameters(exp), lambdaBody(exp), env); + if(isVariable(exp)) + return lookupVariableValue(exp, env); + if(isApply(exp)) { + list<value> applyOperandValues = evalExpr(applyOperand(exp), env, pool); + return applyProcedure(evalExpr(applyOperat(exp), env, pool), applyOperandValues, pool); + } + if(isApplication(exp)) { + list<value> operandValues = listOfValues(operands(exp), env, pool); + return applyProcedure(evalExpr(operat(exp), env, pool), operandValues, pool); + } + std::cout << "Unknown expression type " << exp << "\n"; + return value(); +} + +const list<value> quotedParameters(const list<value>& p) { + if (isNil(p)) + return p; + return cons<value>(mklist<value>(quoteSymbol, car(p)), quotedParameters(cdr(p))); +} + +/** + * Evaluate an expression against a script provided as a list of values. + */ +const value evalScriptLoop(const value& expr, const list<value>& script, eval::Env& globalEnv, const gc_pool& pool) { + if (isNil(script)) + return eval::evalExpr(expr, globalEnv, pool); + eval::evalExpr(car(script), globalEnv, pool); + return evalScriptLoop(expr, cdr(script), globalEnv, pool); +} + +const value evalScript(const value& expr, const value& script, Env& env, const gc_pool& pool) { + return evalScriptLoop(expr, script, env, pool); +} + +/** + * Evaluate an expression against a script provided as an input stream. + */ +const value evalScript(const value& expr, std::istream& is, Env& env, const gc_pool& pool) { + return evalScript(expr, readScript(is), env, pool); +} + +} +} +#endif /* tuscany_eval_eval_hpp */ diff --git a/sca-cpp/trunk/modules/eval/io.hpp b/sca-cpp/trunk/modules/eval/io.hpp new file mode 100644 index 0000000000..a898a11440 --- /dev/null +++ b/sca-cpp/trunk/modules/eval/io.hpp @@ -0,0 +1,206 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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_eval_io_hpp +#define tuscany_eval_io_hpp + +/** + * Script evaluator IO functions. + */ + +#include <iostream> +#include <string> +#include <sstream> +#include <ctype.h> + +#include "list.hpp" +#include "value.hpp" +#include "primitive.hpp" + +namespace tuscany { +namespace eval { + +const value rightParenthesis(mklist<value>(")")); +const value leftParenthesis(mklist<value>("(")); +const value comment(mklist<value>(";")); + +const double stringToNumber(const std::string& str) { + double d; + std::istringstream is(str); + is >> d; + return d; +} + +const bool isWhitespace(const char ch) { + return ch != -1 && isspace(ch); +} + +const bool isIdentifierStart(const char ch) { + return ch != -1 && !isspace(ch) && !isdigit(ch); +} + +const bool isIdentifierPart(const char ch) { + return ch != -1 && !isspace(ch) && ch != '(' && ch != ')'; +} + +const bool isDigit(const char ch) { + return isdigit(ch) || ch == '.'; +} + +const bool isLeftParenthesis(const value& token) { + return leftParenthesis == token; +} + +const bool isRightParenthesis(const value& token) { + return rightParenthesis == token; +} + +const char readChar(std::istream& in) { + if(in.eof()) { + return -1; + } + char c = in.get(); + return c; +} + +const char peekChar(std::istream& in) { + if(in.eof()) + return -1; + char c = in.peek(); + return c; +} + +const bool isQuote(const value& token) { + return token == quoteSymbol; +} + +const value skipComment(std::istream& in); +const value readQuoted(std::istream& in); +const value readIdentifier(const char chr, std::istream& in); +const value readString(const char chr, std::istream& in); +const value readNumber(const char chr, std::istream& in); +const value readValue(std::istream& in); + +const value readToken(std::istream& in) { + const char firstChar = readChar(in); + if(isWhitespace(firstChar)) + return readToken(in); + if(firstChar == ';') + return skipComment(in); + if(firstChar == '\'') + return readQuoted(in); + if(firstChar == '(') + return leftParenthesis; + if(firstChar == ')') + return rightParenthesis; + if(firstChar == '"') + return readString(firstChar, in); + if(isIdentifierStart(firstChar)) + return readIdentifier(firstChar, in); + if(isDigit(firstChar)) + return readNumber(firstChar, in); + if(firstChar == -1) + return value(); + std::cout << "Illegal lexical syntax '" << firstChar << "'" << "\n"; + return readToken(in); +} + +const value skipComment(std::istream& in) { + const char nextChar = readChar(in); + if (nextChar == '\n') + return readToken(in); + return skipComment(in); +} + +const value readQuoted(std::istream& in) { + return mklist(quoteSymbol, readValue(in)); +} + +const list<value> readList(const list<value>& listSoFar, std::istream& in) { + const value token = readToken(in); + if(isNil(token) || isRightParenthesis(token)) + return reverse(listSoFar); + if(isLeftParenthesis(token)) + return readList(cons(value(readList(list<value> (), in)), listSoFar), in); + return readList(cons(token, listSoFar), in); +} + +const std::string listToString(const list<char>& l) { + if(isNil(l)) + return ""; + return car(l) + listToString(cdr(l)); +} + +const list<char> readIdentifierHelper(const list<char>& listSoFar, std::istream& in) { + const char nextChar = peekChar(in); + if(isIdentifierPart(nextChar)) + return readIdentifierHelper(cons(readChar(in), listSoFar), in); + return reverse(listSoFar); +} + +const value readIdentifier(const char chr, std::istream& in) { + return listToString(readIdentifierHelper(mklist(chr), in)).c_str(); +} + +const list<char> readStringHelper(const list<char>& listSoFar, std::istream& in) { + const char nextChar = readChar(in); + if(nextChar != -1 && nextChar != '"') + return readStringHelper(cons(nextChar, listSoFar), in); + return reverse(listSoFar); +} + +const value readString(const char chr, std::istream& in) { + return listToString(readStringHelper(list<char>(), in)); +} + +const list<char> readNumberHelper(const list<char>& listSoFar, std::istream& in) { + const char nextChar = peekChar(in); + if(isDigit(nextChar)) + return readNumberHelper(cons(readChar(in), listSoFar), in); + return reverse(listSoFar); +} + +const value readNumber(const char chr, std::istream& in) { + return stringToNumber(listToString(readNumberHelper(mklist(chr), in))); +} + +const value readValue(std::istream& in) { + const value nextToken = readToken(in); + if(isLeftParenthesis(nextToken)) + return readList(list<value> (), in); + return nextToken; +} + +const bool writeValue(const value& val, std::ostream& out) { + out << val; + return true; +} + +const value readScript(std::istream& in) { + const value val = readValue(in); + if (isNil(val)) + return list<value>(); + return cons(val, (list<value>)readScript(in)); +} + +} +} +#endif /* tuscany_eval_io_hpp */ diff --git a/sca-cpp/trunk/modules/eval/primitive.hpp b/sca-cpp/trunk/modules/eval/primitive.hpp new file mode 100644 index 0000000000..bd36c0e226 --- /dev/null +++ b/sca-cpp/trunk/modules/eval/primitive.hpp @@ -0,0 +1,197 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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_eval_primitive_hpp +#define tuscany_eval_primitive_hpp + +/** + * Script evaluator primitive functions. + */ + +#include <apr_general.h> +#include <apr_uuid.h> +#include <iostream> +#include "function.hpp" +#include "list.hpp" +#include "value.hpp" + +namespace tuscany { +namespace eval { + +const value primitiveSymbol("primitive"); +const value quoteSymbol("'"); +const value lambdaSymbol("lambda"); + +std::ostream* displayOut = &std::cout; + +const bool setupDisplay(std::ostream& out) { + displayOut = &out; + return true; +} + +const value carProc(const list<value>& args) { + return car((list<value> )car(args)); +} + +const value cdrProc(const list<value>& args) { + return cdr((list<value> )car(args)); +} + +const value consProc(const list<value>& args) { + return cons(car(args), (list<value> )cadr(args)); +} + +const value listProc(const list<value>& args) { + return args; +} + +const value nulProc(const list<value>& args) { + return (bool)isNil(car(args)); +} + +const value equalProc(const list<value>& args) { + return (bool)(car(args) == cadr(args)); +} + +const value addProc(const list<value>& args) { + if (isNil(cdr(args))) + return (double)car(args); + return (double)car(args) + (double)cadr(args); +} + +const value subProc(const list<value>& args) { + if (isNil(cdr(args))) + return (double)0 - (double)car(args); + return (double)car(args) - (double)cadr(args); +} + +const value mulProc(const list<value>& args) { + return (double)car(args) * (double)cadr(args); +} + +const value divProc(const list<value>& args) { + return (double)car(args) / (double)cadr(args); +} + +const value displayProc(const list<value>& args) { + *displayOut << car(args); + (*displayOut).flush(); + return true; +} + +const value uuidProc(const list<value>& args) { + apr_uuid_t uuid; + apr_uuid_get(&uuid); + char buf[APR_UUID_FORMATTED_LENGTH]; + apr_uuid_format(buf, &uuid); + return std::string(buf, APR_UUID_FORMATTED_LENGTH); +} + +const value applyPrimitiveProcedure(const value& proc, list<value>& args, const gc_pool& pool) { + const lambda<value(list<value>&)> func(cadr((list<value>)proc)); + return func(args); +} + +const bool isPrimitiveProcedure(const value& proc) { + return isTaggedList(proc, primitiveSymbol); +} + +const bool isSelfEvaluating(const value& exp) { + if(isNil(exp)) + return true; + if(isNumber(exp)) + return true; + if(isString(exp)) + return true; + if(isBool(exp)) + return true; + if(isChar(exp)) + return true; + if(isLambda(exp)) + return true; + return false; +} + +const value primitiveImplementation(const list<value>& proc) { + return car(cdr(proc)); +} + +template<typename F> const value primitiveProcedure(const F& f) { + return mklist<value>(primitiveSymbol, (lambda<value(list<value>&)>)f); +} + +const list<value> primitiveProcedureNames() { + list<value> l = mklist<value>("car"); + l = cons<value>("cdr", l); + l = cons<value>("cons", l); + l = cons<value>("list", l); + l = cons<value>("nul", l); + l = cons<value>("=", l); + l = cons<value>("+", l); + l = cons<value>("-", l); + l = cons<value>("*", l); + l = cons<value>("/", l); + l = cons<value>("equal?", l); + l = cons<value>("display", l); + l = cons<value>("uuid", l); + return l; +} + +const list<value> primitiveProcedureObjects() { + list<value> l = mklist(primitiveProcedure(carProc)); + l = cons(primitiveProcedure(cdrProc), l); + l = cons(primitiveProcedure(consProc), l); + l = cons(primitiveProcedure(listProc), l); + l = cons(primitiveProcedure(nulProc), l); + l = cons(primitiveProcedure(equalProc), l); + l = cons(primitiveProcedure(addProc), l); + l = cons(primitiveProcedure(subProc), l); + l = cons(primitiveProcedure(mulProc), l); + l = cons(primitiveProcedure(divProc), l); + l = cons(primitiveProcedure(equalProc), l); + l = cons(primitiveProcedure(displayProc), l); + l = cons(primitiveProcedure(uuidProc), l); + return l; +} + +const bool isFalse(const value& exp) { + return (bool)exp == false; +} + +const bool isTrue(const value& exp) { + return (bool)exp == true; +} + +const bool isQuoted(const value& exp) { + return isTaggedList(exp, quoteSymbol); +} + +const value textOfQuotation(const value& exp) { + return car(cdr((list<value> )exp)); +} + +const value makeLambda(const list<value>& parameters, const list<value>& body) { + return cons(lambdaSymbol, cons<value>(parameters, body)); +} + +} +} +#endif /* tuscany_eval_primitive_hpp */ diff --git a/sca-cpp/trunk/modules/eval/tuscany-sca-1.1-implementation-eval.xsd b/sca-cpp/trunk/modules/eval/tuscany-sca-1.1-implementation-eval.xsd new file mode 100644 index 0000000000..bbf4935346 --- /dev/null +++ b/sca-cpp/trunk/modules/eval/tuscany-sca-1.1-implementation-eval.xsd @@ -0,0 +1,43 @@ +<?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. +--> +<schema xmlns="http://www.w3.org/2001/XMLSchema" + targetNamespace="http://tuscany.apache.org/xmlns/sca/1.1" + xmlns:sca="http://docs.oasis-open.org/ns/opencsa/sca/200903" + xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1" + elementFormDefault="qualified"> + + <import namespace="http://docs.oasis-open.org/ns/opencsa/sca/200903" schemaLocation="sca-1.1-cd04.xsd"/> + + <element name="implementation.eval" type="t:EvalImplementation" substitutionGroup="sca:implementation"/> + + <complexType name="EvalImplementation"> + <complexContent> + <extension base="sca:Implementation"> + <sequence> + <any namespace="##targetNamespace" processContents="lax" + minOccurs="0" maxOccurs="unbounded"/> + </sequence> + <attribute name="location" type="anyURI" use="required"/> + <anyAttribute namespace="##any" processContents="lax"/> + </extension> + </complexContent> + </complexType> + +</schema> diff --git a/sca-cpp/trunk/modules/http/Makefile.am b/sca-cpp/trunk/modules/http/Makefile.am new file mode 100644 index 0000000000..ba5eadbab4 --- /dev/null +++ b/sca-cpp/trunk/modules/http/Makefile.am @@ -0,0 +1,37 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +noinst_PROGRAMS = curl-test + +libdir=$(prefix)/lib +lib_LTLIBRARIES = libmod_tuscany_eval.la libmod_tuscany_wiring.la + +nobase_include_HEADERS = *.hpp + +INCLUDES = -I. -I$(top_builddir)/kernel -I${LIBXML2_INCLUDE} -I${HTTPD_INCLUDE} -I${APR_INCLUDE} -I${LIBMOZJS_INCLUDE} -I${CURL_INCLUDE} + +libmod_tuscany_eval_la_SOURCES = mod-eval.cpp +libmod_tuscany_eval_la_LIBADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1 -L${CURL_LIB} -lcurl -L${LIBMOZJS_LIB} -lmozjs + +libmod_tuscany_wiring_la_SOURCES = mod-wiring.cpp +libmod_tuscany_wiring_la_LIBADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1 -L${CURL_LIB} -lcurl -L${LIBMOZJS_LIB} -lmozjs + +curl_test_SOURCES = curl-test.cpp +curl_test_LDADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1 -L${CURL_LIB} -lcurl -L${LIBMOZJS_LIB} -lmozjs + +TESTS = httpd-test http-test wiring-test + diff --git a/sca-cpp/trunk/modules/http/conf/mime.types b/sca-cpp/trunk/modules/http/conf/mime.types new file mode 100644 index 0000000000..4279f51bca --- /dev/null +++ b/sca-cpp/trunk/modules/http/conf/mime.types @@ -0,0 +1,607 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# This file controls what Internet media types are sent to the client for +# given file extension(s). Sending the correct media type to the client +# is important so they know how to handle the content of the file. +# Extra types can either be added here or by using an AddType directive +# in your config files. For more information about Internet media types, +# please read RFC 2045, 2046, 2047, 2048, and 2077. The Internet media type +# registry is at <http://www.iana.org/assignments/media-types/>. + +# MIME type Extensions +application/activemessage +application/andrew-inset ez +application/applefile +application/atom+xml atom +application/atomicmail +application/batch-smtp +application/beep+xml +application/cals-1840 +application/cnrp+xml +application/commonground +application/cpl+xml +application/cybercash +application/dca-rft +application/dec-dx +application/dvcs +application/edi-consent +application/edifact +application/edi-x12 +application/eshop +application/font-tdpfr +application/http +application/hyperstudio +application/iges +application/index +application/index.cmd +application/index.obj +application/index.response +application/index.vnd +application/iotp +application/ipp +application/isup +application/mac-binhex40 hqx +application/mac-compactpro cpt +application/macwriteii +application/marc +application/mathematica +application/mathml+xml mathml +application/msword doc +application/news-message-id +application/news-transmission +application/ocsp-request +application/ocsp-response +application/octet-stream bin dms lha lzh exe class so dll dmg +application/oda oda +application/ogg ogg +application/parityfec +application/pdf pdf +application/pgp-encrypted +application/pgp-keys +application/pgp-signature +application/pkcs10 +application/pkcs7-mime +application/pkcs7-signature +application/pkix-cert +application/pkix-crl +application/pkixcmp +application/postscript ai eps ps +application/prs.alvestrand.titrax-sheet +application/prs.cww +application/prs.nprend +application/prs.plucker +application/qsig +application/rdf+xml rdf +application/reginfo+xml +application/remote-printing +application/riscos +application/rtf +application/sdp +application/set-payment +application/set-payment-initiation +application/set-registration +application/set-registration-initiation +application/sgml +application/sgml-open-catalog +application/sieve +application/slate +application/smil smi smil +application/srgs gram +application/srgs+xml grxml +application/timestamp-query +application/timestamp-reply +application/tve-trigger +application/vemmi +application/vnd.3gpp.pic-bw-large +application/vnd.3gpp.pic-bw-small +application/vnd.3gpp.pic-bw-var +application/vnd.3gpp.sms +application/vnd.3m.post-it-notes +application/vnd.accpac.simply.aso +application/vnd.accpac.simply.imp +application/vnd.acucobol +application/vnd.acucorp +application/vnd.adobe.xfdf +application/vnd.aether.imp +application/vnd.amiga.ami +application/vnd.anser-web-certificate-issue-initiation +application/vnd.anser-web-funds-transfer-initiation +application/vnd.audiograph +application/vnd.blueice.multipass +application/vnd.bmi +application/vnd.businessobjects +application/vnd.canon-cpdl +application/vnd.canon-lips +application/vnd.cinderella +application/vnd.claymore +application/vnd.commerce-battelle +application/vnd.commonspace +application/vnd.contact.cmsg +application/vnd.cosmocaller +application/vnd.criticaltools.wbs+xml +application/vnd.ctc-posml +application/vnd.cups-postscript +application/vnd.cups-raster +application/vnd.cups-raw +application/vnd.curl +application/vnd.cybank +application/vnd.data-vision.rdz +application/vnd.dna +application/vnd.dpgraph +application/vnd.dreamfactory +application/vnd.dxr +application/vnd.ecdis-update +application/vnd.ecowin.chart +application/vnd.ecowin.filerequest +application/vnd.ecowin.fileupdate +application/vnd.ecowin.series +application/vnd.ecowin.seriesrequest +application/vnd.ecowin.seriesupdate +application/vnd.enliven +application/vnd.epson.esf +application/vnd.epson.msf +application/vnd.epson.quickanime +application/vnd.epson.salt +application/vnd.epson.ssf +application/vnd.ericsson.quickcall +application/vnd.eudora.data +application/vnd.fdf +application/vnd.ffsns +application/vnd.fints +application/vnd.flographit +application/vnd.framemaker +application/vnd.fsc.weblaunch +application/vnd.fujitsu.oasys +application/vnd.fujitsu.oasys2 +application/vnd.fujitsu.oasys3 +application/vnd.fujitsu.oasysgp +application/vnd.fujitsu.oasysprs +application/vnd.fujixerox.ddd +application/vnd.fujixerox.docuworks +application/vnd.fujixerox.docuworks.binder +application/vnd.fut-misnet +application/vnd.grafeq +application/vnd.groove-account +application/vnd.groove-help +application/vnd.groove-identity-message +application/vnd.groove-injector +application/vnd.groove-tool-message +application/vnd.groove-tool-template +application/vnd.groove-vcard +application/vnd.hbci +application/vnd.hhe.lesson-player +application/vnd.hp-hpgl +application/vnd.hp-hpid +application/vnd.hp-hps +application/vnd.hp-pcl +application/vnd.hp-pclxl +application/vnd.httphone +application/vnd.hzn-3d-crossword +application/vnd.ibm.afplinedata +application/vnd.ibm.electronic-media +application/vnd.ibm.minipay +application/vnd.ibm.modcap +application/vnd.ibm.rights-management +application/vnd.ibm.secure-container +application/vnd.informix-visionary +application/vnd.intercon.formnet +application/vnd.intertrust.digibox +application/vnd.intertrust.nncp +application/vnd.intu.qbo +application/vnd.intu.qfx +application/vnd.irepository.package+xml +application/vnd.is-xpr +application/vnd.japannet-directory-service +application/vnd.japannet-jpnstore-wakeup +application/vnd.japannet-payment-wakeup +application/vnd.japannet-registration +application/vnd.japannet-registration-wakeup +application/vnd.japannet-setstore-wakeup +application/vnd.japannet-verification +application/vnd.japannet-verification-wakeup +application/vnd.jisp +application/vnd.kde.karbon +application/vnd.kde.kchart +application/vnd.kde.kformula +application/vnd.kde.kivio +application/vnd.kde.kontour +application/vnd.kde.kpresenter +application/vnd.kde.kspread +application/vnd.kde.kword +application/vnd.kenameaapp +application/vnd.koan +application/vnd.liberty-request+xml +application/vnd.llamagraphics.life-balance.desktop +application/vnd.llamagraphics.life-balance.exchange+xml +application/vnd.lotus-1-2-3 +application/vnd.lotus-approach +application/vnd.lotus-freelance +application/vnd.lotus-notes +application/vnd.lotus-organizer +application/vnd.lotus-screencam +application/vnd.lotus-wordpro +application/vnd.mcd +application/vnd.mediastation.cdkey +application/vnd.meridian-slingshot +application/vnd.micrografx.flo +application/vnd.micrografx.igx +application/vnd.mif mif +application/vnd.minisoft-hp3000-save +application/vnd.mitsubishi.misty-guard.trustweb +application/vnd.mobius.daf +application/vnd.mobius.dis +application/vnd.mobius.mbk +application/vnd.mobius.mqy +application/vnd.mobius.msl +application/vnd.mobius.plc +application/vnd.mobius.txf +application/vnd.mophun.application +application/vnd.mophun.certificate +application/vnd.motorola.flexsuite +application/vnd.motorola.flexsuite.adsi +application/vnd.motorola.flexsuite.fis +application/vnd.motorola.flexsuite.gotap +application/vnd.motorola.flexsuite.kmr +application/vnd.motorola.flexsuite.ttc +application/vnd.motorola.flexsuite.wem +application/vnd.mozilla.xul+xml xul +application/vnd.ms-artgalry +application/vnd.ms-asf +application/vnd.ms-excel xls +application/vnd.ms-lrm +application/vnd.ms-powerpoint ppt +application/vnd.ms-project +application/vnd.ms-tnef +application/vnd.ms-works +application/vnd.ms-wpl +application/vnd.mseq +application/vnd.msign +application/vnd.music-niff +application/vnd.musician +application/vnd.netfpx +application/vnd.noblenet-directory +application/vnd.noblenet-sealer +application/vnd.noblenet-web +application/vnd.novadigm.edm +application/vnd.novadigm.edx +application/vnd.novadigm.ext +application/vnd.obn +application/vnd.osa.netdeploy +application/vnd.palm +application/vnd.pg.format +application/vnd.pg.osasli +application/vnd.powerbuilder6 +application/vnd.powerbuilder6-s +application/vnd.powerbuilder7 +application/vnd.powerbuilder7-s +application/vnd.powerbuilder75 +application/vnd.powerbuilder75-s +application/vnd.previewsystems.box +application/vnd.publishare-delta-tree +application/vnd.pvi.ptid1 +application/vnd.pwg-multiplexed +application/vnd.pwg-xhtml-print+xml +application/vnd.quark.quarkxpress +application/vnd.rapid +application/vnd.s3sms +application/vnd.sealed.net +application/vnd.seemail +application/vnd.shana.informed.formdata +application/vnd.shana.informed.formtemplate +application/vnd.shana.informed.interchange +application/vnd.shana.informed.package +application/vnd.smaf +application/vnd.sss-cod +application/vnd.sss-dtf +application/vnd.sss-ntf +application/vnd.street-stream +application/vnd.svd +application/vnd.swiftview-ics +application/vnd.triscape.mxs +application/vnd.trueapp +application/vnd.truedoc +application/vnd.ufdl +application/vnd.uplanet.alert +application/vnd.uplanet.alert-wbxml +application/vnd.uplanet.bearer-choice +application/vnd.uplanet.bearer-choice-wbxml +application/vnd.uplanet.cacheop +application/vnd.uplanet.cacheop-wbxml +application/vnd.uplanet.channel +application/vnd.uplanet.channel-wbxml +application/vnd.uplanet.list +application/vnd.uplanet.list-wbxml +application/vnd.uplanet.listcmd +application/vnd.uplanet.listcmd-wbxml +application/vnd.uplanet.signal +application/vnd.vcx +application/vnd.vectorworks +application/vnd.vidsoft.vidconference +application/vnd.visio +application/vnd.visionary +application/vnd.vividence.scriptfile +application/vnd.vsf +application/vnd.wap.sic +application/vnd.wap.slc +application/vnd.wap.wbxml wbxml +application/vnd.wap.wmlc wmlc +application/vnd.wap.wmlscriptc wmlsc +application/vnd.webturbo +application/vnd.wrq-hp3000-labelled +application/vnd.wt.stf +application/vnd.wv.csp+wbxml +application/vnd.xara +application/vnd.xfdl +application/vnd.yamaha.hv-dic +application/vnd.yamaha.hv-script +application/vnd.yamaha.hv-voice +application/vnd.yellowriver-custom-menu +application/voicexml+xml vxml +application/watcherinfo+xml +application/whoispp-query +application/whoispp-response +application/wita +application/wordperfect5.1 +application/x-bcpio bcpio +application/x-cdlink vcd +application/x-chess-pgn pgn +application/x-compress +application/x-cpio cpio +application/x-csh csh +application/x-director dcr dir dxr +application/x-dvi dvi +application/x-futuresplash spl +application/x-gtar gtar +application/x-gzip +application/x-hdf hdf +application/x-javascript js +application/x-koan skp skd skt skm +application/x-latex latex +application/x-netcdf nc cdf +application/x-sh sh +application/x-shar shar +application/x-shockwave-flash swf +application/x-stuffit sit +application/x-sv4cpio sv4cpio +application/x-sv4crc sv4crc +application/x-tar tar +application/x-tcl tcl +application/x-tex tex +application/x-texinfo texinfo texi +application/x-troff t tr roff +application/x-troff-man man +application/x-troff-me me +application/x-troff-ms ms +application/x-ustar ustar +application/x-wais-source src +application/x400-bp +application/xhtml+xml xhtml xht +application/xslt+xml xslt +application/xml xml xsl +application/xml-dtd dtd +application/xml-external-parsed-entity +application/zip zip +audio/32kadpcm +audio/amr +audio/amr-wb +audio/basic au snd +audio/cn +audio/dat12 +audio/dsr-es201108 +audio/dvi4 +audio/evrc +audio/evrc0 +audio/g722 +audio/g.722.1 +audio/g723 +audio/g726-16 +audio/g726-24 +audio/g726-32 +audio/g726-40 +audio/g728 +audio/g729 +audio/g729D +audio/g729E +audio/gsm +audio/gsm-efr +audio/l8 +audio/l16 +audio/l20 +audio/l24 +audio/lpc +audio/midi mid midi kar +audio/mpa +audio/mpa-robust +audio/mp4a-latm +audio/mpeg mpga mp2 mp3 +audio/parityfec +audio/pcma +audio/pcmu +audio/prs.sid +audio/qcelp +audio/red +audio/smv +audio/smv0 +audio/telephone-event +audio/tone +audio/vdvi +audio/vnd.3gpp.iufp +audio/vnd.cisco.nse +audio/vnd.cns.anp1 +audio/vnd.cns.inf1 +audio/vnd.digital-winds +audio/vnd.everad.plj +audio/vnd.lucent.voice +audio/vnd.nortel.vbk +audio/vnd.nuera.ecelp4800 +audio/vnd.nuera.ecelp7470 +audio/vnd.nuera.ecelp9600 +audio/vnd.octel.sbc +audio/vnd.qcelp +audio/vnd.rhetorex.32kadpcm +audio/vnd.vmx.cvsd +audio/x-aiff aif aiff aifc +audio/x-alaw-basic +audio/x-mpegurl m3u +audio/x-pn-realaudio ram ra +audio/x-pn-realaudio-plugin +application/vnd.rn-realmedia rm +audio/x-wav wav +chemical/x-pdb pdb +chemical/x-xyz xyz +image/bmp bmp +image/cgm cgm +image/g3fax +image/gif gif +image/ief ief +image/jpeg jpeg jpg jpe +image/naplps +image/png png +image/prs.btif +image/prs.pti +image/svg+xml svg +image/t38 +image/tiff tiff tif +image/tiff-fx +image/vnd.cns.inf2 +image/vnd.djvu djvu djv +image/vnd.dwg +image/vnd.dxf +image/vnd.fastbidsheet +image/vnd.fpx +image/vnd.fst +image/vnd.fujixerox.edmics-mmr +image/vnd.fujixerox.edmics-rlc +image/vnd.globalgraphics.pgb +image/vnd.mix +image/vnd.ms-modi +image/vnd.net-fpx +image/vnd.svf +image/vnd.wap.wbmp wbmp +image/vnd.xiff +image/x-cmu-raster ras +image/x-icon ico +image/x-portable-anymap pnm +image/x-portable-bitmap pbm +image/x-portable-graymap pgm +image/x-portable-pixmap ppm +image/x-rgb rgb +image/x-xbitmap xbm +image/x-xpixmap xpm +image/x-xwindowdump xwd +message/delivery-status +message/disposition-notification +message/external-body +message/http +message/news +message/partial +message/rfc822 +message/s-http +message/sip +message/sipfrag +model/iges igs iges +model/mesh msh mesh silo +model/vnd.dwf +model/vnd.flatland.3dml +model/vnd.gdl +model/vnd.gs-gdl +model/vnd.gtw +model/vnd.mts +model/vnd.parasolid.transmit.binary +model/vnd.parasolid.transmit.text +model/vnd.vtu +model/vrml wrl vrml +multipart/alternative +multipart/appledouble +multipart/byteranges +multipart/digest +multipart/encrypted +multipart/form-data +multipart/header-set +multipart/mixed +multipart/parallel +multipart/related +multipart/report +multipart/signed +multipart/voice-message +text/calendar ics ifb +text/css css +text/directory +text/enriched +text/html html htm +text/parityfec +text/plain asc txt +text/prs.lines.tag +text/rfc822-headers +text/richtext rtx +text/rtf rtf +text/sgml sgml sgm +text/t140 +text/tab-separated-values tsv +text/uri-list +text/vnd.abc +text/vnd.curl +text/vnd.dmclientscript +text/vnd.fly +text/vnd.fmi.flexstor +text/vnd.in3d.3dml +text/vnd.in3d.spot +text/vnd.iptc.nitf +text/vnd.iptc.newsml +text/vnd.latex-z +text/vnd.motorola.reflex +text/vnd.ms-mediapackage +text/vnd.net2phone.commcenter.command +text/vnd.sun.j2me.app-descriptor +text/vnd.wap.si +text/vnd.wap.sl +text/vnd.wap.wml wml +text/vnd.wap.wmlscript wmls +text/x-setext etx +text/xml +text/xml-external-parsed-entity +video/bmpeg +video/bt656 +video/celb +video/dv +video/h261 +video/h263 +video/h263-1998 +video/h263-2000 +video/jpeg +video/mp1s +video/mp2p +video/mp2t +video/mp4v-es +video/mpv +video/mpeg mpeg mpg mpe +video/nv +video/parityfec +video/pointer +video/quicktime qt mov +video/smpte292m +video/vnd.fvt +video/vnd.motorola.video +video/vnd.motorola.videop +video/vnd.mpegurl mxu m4u +video/vnd.nokia.interleaved-multimedia +video/vnd.objectvideo +video/vnd.vivo +video/x-msvideo avi +video/x-sgi-movie movie +x-conference/x-cooltalk ice diff --git a/sca-cpp/trunk/modules/http/curl-test.cpp b/sca-cpp/trunk/modules/http/curl-test.cpp new file mode 100644 index 0000000000..863aa98828 --- /dev/null +++ b/sca-cpp/trunk/modules/http/curl-test.cpp @@ -0,0 +1,253 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * Test HTTP client functions. + */ + +#include <assert.h> +#include <sys/time.h> +#include <time.h> +#include <iostream> +#include <sstream> +#include <string> +#include "slist.hpp" +#include "curl.hpp" + +namespace tuscany { +namespace http { + +const bool contains(const std::string& str, const std::string& pattern) { + return str.find(pattern) != str.npos; +} + +const double duration(struct timeval start, struct timeval end, int count) { + long t = (end.tv_sec * 1000 + end.tv_usec / 1000) - (start.tv_sec * 1000 + start.tv_usec / 1000); + return (double)t / (double)count; +} + +std::ostringstream* curlWriter(const std::string& s, std::ostringstream* os) { + (*os) << s; + return os; +} + +const bool testGet() { + CURLHandle ch; + { + std::ostringstream os; + const failable<list<std::ostringstream*>, std::string> r = get<std::ostringstream*>(curlWriter, &os, "http://localhost:8091", ch); + assert(hasValue(r)); + assert(contains(os.str(), "HTTP/1.1 200 OK")); + assert(contains(os.str(), "It works")); + } + { + const failable<value, std::string> r = get("http://localhost:8091", ch); + assert(hasValue(r)); + const value val = r; + assert(contains(val, "It works")); + } + return true; +} + +const bool testGetLoop(const int count, CURLHandle& ch) { + if (count == 0) + return true; + const failable<value, std::string> r = get("http://localhost:8091", ch); + assert(hasValue(r)); + const value val = r; + assert(contains(val, "It works")); + return testGetLoop(count - 1, ch); +} + +const bool testGetPerf() { + const int count = 50; + CURLHandle ch; + struct timeval start; + struct timeval end; + { + testGetLoop(5, ch); + + gettimeofday(&start, NULL); + + testGetLoop(count, ch); + + gettimeofday(&end, NULL); + std::cout << "Static GET test " << duration(start, end, count) << " ms" << std::endl; + } + return true; +} + +const bool testEval() { + CURLHandle ch; + const value val = evalExpr(mklist<value>(std::string("echo"), std::string("Hello")), "http://localhost:8091/test", ch); + assert(val == std::string("Hello")); + return true; +} + +const bool testEvalLoop(const int count, CURLHandle& ch) { + if (count == 0) + return true; + const value val = evalExpr(mklist<value>(std::string("echo"), std::string("Hello")), "http://localhost:8091/test", ch); + assert(val == std::string("Hello")); + return testEvalLoop(count - 1, ch); +} + +const value blob(std::string(3000, 'A')); +const list<value> blobs = mklist(blob, blob, blob, blob, blob); + +const bool testBlobEvalLoop(const int count, CURLHandle& ch) { + if (count == 0) + return true; + const value val = evalExpr(mklist<value>(std::string("echo"), blobs), "http://localhost:8091/test", ch); + assert(val == blobs); + return testBlobEvalLoop(count - 1, ch); +} + +const bool testEvalPerf() { + const int count = 50; + CURLHandle ch; + struct timeval start; + struct timeval end; + { + testEvalLoop(5, ch); + + gettimeofday(&start, NULL); + + testEvalLoop(count, ch); + + gettimeofday(&end, NULL); + std::cout << "JSON-RPC eval echo test " << duration(start, end, count) << " ms" << std::endl; + } + { + testBlobEvalLoop(5, ch); + + gettimeofday(&start, NULL); + + testBlobEvalLoop(count, ch); + + gettimeofday(&end, NULL); + std::cout << "JSON-RPC eval blob test " << duration(start, end, count) << " ms" << std::endl; + } + return true; +} + +const bool testFeed() { + return true; +} + +bool testPost() { + const list<value> i = list<value>() + << (list<value>() << "name" << std::string("Apple")) + << (list<value>() << "price" << std::string("$2.99")); + const list<value> a = mklist<value>(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); + CURLHandle ch; + value rc = post(a, "http://localhost:8091/test", ch); + assert(rc == value(true)); + return true; +} + +const bool testPostLoop(const int count, const value& val, CURLHandle& ch) { + if (count == 0) + return true; + const value rc = post(val, "http://localhost:8091/test", ch); + assert(rc == value(true)); + return testPostLoop(count - 1, val, ch); +} + +const bool testPostPerf() { + const int count = 50; + CURLHandle ch; + struct timeval start; + struct timeval end; + { + const list<value> i = list<value>() + << (list<value>() << "name" << std::string("Apple")) + << (list<value>() << "price" << std::string("$2.99")); + const list<value> val = mklist<value>(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); + testPostLoop(5, val, ch); + + gettimeofday(&start, NULL); + + testPostLoop(count, val, ch); + + gettimeofday(&end, NULL); + std::cout << "ATOMPub POST small test " << duration(start, end, count) << " ms" << std::endl; + } + { + const list<value> i = list<value>() + << (list<value>() << "name" << std::string("Apple")) + << (list<value>() << "blob1" << blob) + << (list<value>() << "blob2" << blob) + << (list<value>() << "blob3" << blob) + << (list<value>() << "blob4" << blob) + << (list<value>() << "blob5" << blob) + << (list<value>() << "price" << std::string("$2.99")); + const list<value> val = mklist<value>(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); + testPostLoop(5, val, ch); + + gettimeofday(&start, NULL); + + testPostLoop(count, val, ch); + + gettimeofday(&end, NULL); + std::cout << "ATOMPub POST blob test " << duration(start, end, count) << " ms" << std::endl; + } + return true; +} + +const bool testPut() { + const list<value> i = list<value>() + << (list<value>() << "name" << std::string("Apple")) + << (list<value>() << "price" << std::string("$2.99")); + const list<value> a = mklist<value>(std::string("item"), std::string("cart-53d67a61-aa5e-4e5e-8401-39edeba8b83b"), i); + CURLHandle ch; + value rc = put(a, "http://localhost:8091/test/111", ch); + assert(rc == value(true)); + return true; +} + +const bool testDel() { + CURLHandle ch; + value rc = del("http://localhost:8091/test/123456789", ch); + assert(rc == value(true)); + return true; +} + +} +} + +int main() { + std::cout << "Testing..." << std::endl; + + tuscany::http::testGet(); + tuscany::http::testGetPerf(); + tuscany::http::testPost(); + tuscany::http::testPostPerf(); + tuscany::http::testEval(); + tuscany::http::testEvalPerf(); + tuscany::http::testFeed(); + tuscany::http::testPut(); + tuscany::http::testDel(); + + std::cout << "OK" << std::endl; + + return 0; +} diff --git a/sca-cpp/trunk/modules/http/curl.hpp b/sca-cpp/trunk/modules/http/curl.hpp new file mode 100644 index 0000000000..5ee3a090b0 --- /dev/null +++ b/sca-cpp/trunk/modules/http/curl.hpp @@ -0,0 +1,348 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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_curl_hpp +#define tuscany_curl_hpp + +/** + * CURL HTTP client functions. + */ + +#include <curl/curl.h> +#include <curl/types.h> +#include <curl/easy.h> +#include <string> +#include "list.hpp" +#include "value.hpp" +#include "element.hpp" +#include "monad.hpp" +#include "../atom/atom.hpp" +#include "../json/json.hpp" + +namespace tuscany { +namespace http { + +/** + * Set to true to log HTTP content. + */ +bool logContent = false; + +/** + * CURL library context, one per process. + */ +class CURLContext { +public: + CURLContext() { + curl_global_init(CURL_GLOBAL_ALL); + } + ~CURLContext() { + curl_global_cleanup(); + } +}; + +CURLContext curlContext; + +/** + * Represents a CURL session handle. + */ +class CURLHandle { +public: + CURLHandle() : h(curl_easy_init()) { + } + ~CURLHandle() { + curl_easy_cleanup(h); + } + + operator CURL*() const { + return h; + } +private: + CURL* h; +}; + +/** + * Context passed to the read callback function. + */ +class CURLReadContext { +public: + CURLReadContext(const list<std::string>& ilist) : ilist(ilist) { + } + list<std::string> ilist; +}; + +/** + * Called by CURL to read data to send. + */ +size_t readCallback(void *ptr, size_t size, size_t nmemb, void *data) { + CURLReadContext& rcx = *static_cast<CURLReadContext*>(data); + if (isNil(rcx.ilist)) + return 0; + rcx.ilist = fragment(rcx.ilist, size * nmemb); + const std::string s = car(rcx.ilist); + rcx.ilist = cdr(rcx.ilist); + s.copy((char*)ptr, s.length()); + return s.length(); +} + +/** + * Context passed to CURL write callback function. + */ +template<typename R> class CURLWriteContext { +public: + CURLWriteContext(const lambda<R(std::string, R)>& reduce, const R& accum) : reduce(reduce), accum(accum) { + } + const lambda<R(std::string, R)> reduce; + R accum; +}; + +/** + * Called by CURL to write received data. + */ +template<typename R> size_t writeCallback(void *ptr, size_t size, size_t nmemb, void *data) { + CURLWriteContext<R>& wcx = *(static_cast<CURLWriteContext<R>*> (data)); + const size_t realsize = size * nmemb; + wcx.accum = wcx.reduce(std::string((const char*)ptr, realsize), wcx.accum); + return realsize; +} + +/** + * Called by CURL to write received header data. + */ +template<typename R> size_t headerCallback(void *ptr, size_t size, size_t nmemb, void *data) { + CURLWriteContext<R>& wcx = *(static_cast<CURLWriteContext<R>*> (data)); + const size_t realsize = size * nmemb; + wcx.accum = wcx.reduce(std::string((const char*)ptr, realsize), wcx.accum); + return realsize; +} + +/** + * Apply an HTTP verb to a list containing a list of headers and a list of content, and + * a reduce function used to process the response. + */ +curl_slist* headers(curl_slist* cl, const list<std::string>& h) { + if (isNil(h)) + return cl; + return headers(curl_slist_append(cl, std::string(car(h)).c_str()), cdr(h)); +} + +template<typename R> const failable<list<R>, std::string> apply(const list<list<std::string> >& req, const lambda<R(std::string, R)>& reduce, const R& initial, const std::string& url, const std::string& verb, const CURLHandle& ch) { + + // Init the curl session + curl_easy_reset(ch); + curl_easy_setopt(ch, CURLOPT_USERAGENT, "libcurl/1.0"); + + //TODO use HTTP chunking, for now just convert request to a single string + std::ostringstream os; + write(cadr(req), os); + const std::string s = os.str(); + const int sz = s.length(); + if (sz < 1400) + curl_easy_setopt(ch, CURLOPT_TCP_NODELAY, true); + + // Setup the read, header and write callbacks + CURLReadContext rcx(mklist(s)); + curl_easy_setopt(ch, CURLOPT_READFUNCTION, (size_t (*)(void*, size_t, size_t, void*))readCallback); + curl_easy_setopt(ch, CURLOPT_READDATA, &rcx); + CURLWriteContext<R> hcx(reduce, initial); + curl_easy_setopt(ch, CURLOPT_HEADERFUNCTION, (size_t (*)(void*, size_t, size_t, void*))headerCallback<R>); + curl_easy_setopt(ch, CURLOPT_HEADERDATA, &hcx); + CURLWriteContext<R> wcx(reduce, initial); + curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, (size_t (*)(void*, size_t, size_t, void*))writeCallback<R>); + curl_easy_setopt(ch, CURLOPT_WRITEDATA, &wcx); + + // Set the request headers + curl_slist* hl = headers(NULL, car(req)); + if (hl != NULL) + curl_easy_setopt(ch, CURLOPT_HTTPHEADER, hl); + + // Apply the HTTP verb + curl_easy_setopt(ch, CURLOPT_URL, url.c_str()); + if (verb == "POST") { + curl_easy_setopt(ch, CURLOPT_POST, true); + curl_easy_setopt(ch, CURLOPT_POSTFIELDSIZE, sz); + } else if (verb == "PUT") { + curl_easy_setopt(ch, CURLOPT_UPLOAD, true); + curl_easy_setopt(ch, CURLOPT_INFILESIZE, sz); + } else if (verb == "DELETE") + curl_easy_setopt(ch, CURLOPT_CUSTOMREQUEST, "DELETE"); + const CURLcode rc = curl_easy_perform(ch); + + if (hl != NULL) + curl_slist_free_all(hl); + + // Return the HTTP return code or content + if (rc) + return mkfailure<list<R>, std::string>(curl_easy_strerror(rc)); + long httprc; + curl_easy_getinfo (ch, CURLINFO_RESPONSE_CODE, &httprc); + if (httprc != 200 && httprc != 201) { + std::ostringstream es; + es << "HTTP code " << httprc; + return mkfailure<list<R>, std::string>(es.str()); + } + return mklist<R>(hcx.accum, wcx.accum); +} + +/** + * Evaluate an expression remotely, at the given URL. + */ +const failable<value, std::string> evalExpr(const value& expr, const std::string& url, const CURLHandle& ch) { + + // Convert expression to a JSON-RPC request + json::JSONContext cx; + const failable<list<std::string>, std::string> jsreq = jsonRequest(1, car<value>(expr), cdr<value>(expr), cx); + if (!hasValue(jsreq)) + return mkfailure<value, std::string>(reason(jsreq)); + + if (logContent) { + std::cout<< "content: " << std::endl; + write(jsreq, std::cout); + std::cout<< std::endl; + std::cout.flush(); + } + + // POST it to the URL + const list<std::string> h = mklist<std::string>("Content-Type: application/json-rpc"); + const failable<list<list<std::string> >, std::string> res = apply<list<std::string> >(mklist<list<std::string> >(h, jsreq), rcons<std::string>, list<std::string>(), url, "POST", ch); + if (!hasValue(res)) + return mkfailure<value, std::string>(reason(res)); + + // Return result + if (logContent) { + std::cout << "content:" << std::endl; + write(cadr<list<std::string> >(res), std::cout); + std::cout << std::endl; + } + const list<value> val = elementsToValues(json::readJSON(cadr<list<std::string> >(res), cx)); + return cadr<value>(cadr<value>(val)); +} + +/** + * HTTP GET, return the resource at the given URL. + */ +template<typename R> const failable<list<R>, std::string> get(const lambda<R(std::string, R)>& reduce, const R& initial, const std::string& url, const CURLHandle& ch) { + const list<list<std::string> > req = mklist(list<std::string>(), list<std::string>()); + return apply(req, reduce, initial, url, "GET", ch); +} + +/** + * HTTP GET, return a list of values representing the resource at the given URL. + */ +const failable<value, std::string> get(const std::string& url, const CURLHandle& ch) { + + // Get the contents of the resource at the given URL + const failable<list<list<std::string> >, std::string> res = get<list<std::string> >(rcons<std::string>, list<std::string>(), url, ch); + if (!hasValue(res)) + return mkfailure<value, std::string>(reason(res)); + const list<list<std::string> > ls = res; + + const std::string ct; + if (ct.find("application/atom+xml") != std::string::npos) { + // TODO Return an ATOM feed + } + + // Return the content as a string value + std::ostringstream os; + write(reverse(cadr(ls)), os); + return value(os.str()); +} + +/** + * HTTP POST. + */ +const failable<value, std::string> post(const value& val, const std::string& url, const CURLHandle& ch) { + + // Convert value to an ATOM entry + const failable<list<std::string>, std::string> entry = atom::writeATOMEntry(atom::entryValuesToElements(val)); + if (!hasValue(entry)) + return mkfailure<value, std::string>(reason(entry)); + if (logContent) { + std::cout << "content:" << std::endl; + write(list<std::string>(entry), std::cout); + std::cout << std::endl; + } + + // POST it to the URL + const list<std::string> h = mklist<std::string>("Content-Type: application/atom+xml"); + const list<list<std::string> > req = mklist<list<std::string> >(h, entry); + const failable<list<list<std::string> >, std::string> res = apply<list<std::string> >(req, rcons<std::string>, list<std::string>(), url, "POST", ch); + if (!hasValue(res)) + return mkfailure<value, std::string>(reason(res)); + return value(true); +} + +/** + * HTTP PUT. + */ +const failable<value, std::string> put(const value& val, const std::string& url, const CURLHandle& ch) { + + // Convert value to an ATOM entry + const failable<list<std::string>, std::string> entry = atom::writeATOMEntry(atom::entryValuesToElements(val)); + if (!hasValue(entry)) + return mkfailure<value, std::string>(reason(entry)); + if (logContent) { + std::cout << "content:" << std::endl; + write(list<std::string>(entry), std::cout); + std::cout << std::endl; + } + + // PUT it to the URL + const list<std::string> h = mklist<std::string>("Content-Type: application/atom+xml"); + const list<list<std::string> > req = mklist<list<std::string> >(h, entry); + const failable<list<list<std::string> >, std::string> res = apply<list<std::string> >(req, rcons<std::string>, list<std::string>(), url, "PUT", ch); + if (!hasValue(res)) + return mkfailure<value, std::string>(reason(res)); + return value(true); +} + +/** + * HTTP DELETE. + */ +const failable<value, std::string> del(const std::string& url, const CURLHandle& ch) { + const list<list<std::string> > req = mklist(list<std::string>(), list<std::string>()); + const failable<list<list<std::string> >, std::string> res = apply<list<std::string> >(req, rcons<std::string>, list<std::string>(), url, "DELETE", ch); + if (!hasValue(res)) + return mkfailure<value, std::string>(reason(res)); + return value(true); +} + +/** + * HTTP client proxy function. + */ +struct proxy { + proxy(const std::string& url, const CURLHandle& ch) : url(url), ch(ch) { + } + + const value operator()(const list<value>& args) const { + failable<value, std::string> val = evalExpr(args, url, ch); + if (!hasValue(val)) + return value(); + return val; + } + + const std::string url; + const CURLHandle& ch; +}; + +} +} + +#endif /* tuscany_curl_hpp */ diff --git a/sca-cpp/trunk/modules/http/htdocs/entry.xml b/sca-cpp/trunk/modules/http/htdocs/entry.xml new file mode 100644 index 0000000000..86b8a10547 --- /dev/null +++ b/sca-cpp/trunk/modules/http/htdocs/entry.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>111</id><content type="application/xml"><item><javaClass>services.Item</javaClass><name>Apple</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>2.99</price></item></content><link href="111"/></entry> diff --git a/sca-cpp/trunk/modules/http/htdocs/feed.xml b/sca-cpp/trunk/modules/http/htdocs/feed.xml new file mode 100644 index 0000000000..5e37de6580 --- /dev/null +++ b/sca-cpp/trunk/modules/http/htdocs/feed.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<feed xmlns="http://www.w3.org/2005/Atom"><title type="text">Sample Feed</title><id>123456789</id><entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>111</id><content type="application/xml"><item><javaClass>services.Item</javaClass><name>Apple</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>2.99</price></item></content><link href="111"/></entry><entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>222</id><content type="application/xml"><item><javaClass>services.Item</javaClass><name>Orange</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>3.55</price></item></content><link href="222"/></entry><entry xmlns="http://www.w3.org/2005/Atom"><title type="text">Item</title><id>333</id><content type="application/xml"><item><javaClass>services.Item</javaClass><name>Pear</name><currencyCode>USD</currencyCode><currencySymbol>$</currencySymbol><price>1.55</price></item></content><link href="333"/></entry></feed> diff --git a/sca-cpp/trunk/modules/http/htdocs/index.html b/sca-cpp/trunk/modules/http/htdocs/index.html new file mode 100644 index 0000000000..1bfb3e30c2 --- /dev/null +++ b/sca-cpp/trunk/modules/http/htdocs/index.html @@ -0,0 +1,21 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--> + +<html><body><h1>It works!</h1></body></html> + diff --git a/sca-cpp/trunk/modules/http/htdocs/json-request.txt b/sca-cpp/trunk/modules/http/htdocs/json-request.txt new file mode 100644 index 0000000000..b4bd07fc46 --- /dev/null +++ b/sca-cpp/trunk/modules/http/htdocs/json-request.txt @@ -0,0 +1 @@ +{"id":1,"method":"echo","params":["Hello"]} diff --git a/sca-cpp/trunk/modules/http/htdocs/json-result.txt b/sca-cpp/trunk/modules/http/htdocs/json-result.txt new file mode 100644 index 0000000000..121bf74902 --- /dev/null +++ b/sca-cpp/trunk/modules/http/htdocs/json-result.txt @@ -0,0 +1 @@ +{"id":1,"result":"Hello"}
\ No newline at end of file diff --git a/sca-cpp/trunk/modules/http/http-test b/sca-cpp/trunk/modules/http/http-test new file mode 100755 index 0000000000..d70db8d469 --- /dev/null +++ b/sca-cpp/trunk/modules/http/http-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 +./httpd-conf tmp 8091 htdocs +cat >>tmp/conf/httpd.conf <<EOF + +<Location /test> +SetHandler mod_tuscany_eval +SCAContribution `pwd`/ +SCAComposite httpd-test.composite +SCAComponent httpd-test +</Location> +EOF + +apachectl -k start -d `pwd`/tmp +sleep 1 + +# Test +./curl-test +rc=$? + +# Cleanup +apachectl -k stop -d `pwd`/tmp +sleep 2 +return $rc diff --git a/sca-cpp/trunk/modules/http/httpd-client.scm b/sca-cpp/trunk/modules/http/httpd-client.scm new file mode 100644 index 0000000000..12275693f4 --- /dev/null +++ b/sca-cpp/trunk/modules/http/httpd-client.scm @@ -0,0 +1,25 @@ +; JSON-RPC test case + +(define (echo x ref) (ref "echo" x)) + +; ATOMPub test case + +(define (getall ref) + (ref "getall") +) + +(define (get id ref) + (ref "get" id) +) + +(define (post entry ref) + (ref "post" entry) +) + +(define (put id entry ref) + (ref "put" id entry) +) + +(define (delete id ref) + (ref "delete" id) +) diff --git a/sca-cpp/trunk/modules/http/httpd-conf b/sca-cpp/trunk/modules/http/httpd-conf new file mode 100755 index 0000000000..10a5b47ac2 --- /dev/null +++ b/sca-cpp/trunk/modules/http/httpd-conf @@ -0,0 +1,37 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Generate a minimal httpd.conf +here=`readlink -f $0`; here=`dirname $here` +root=`readlink -f $1` +port=$2 +htdocs=`readlink -f $3` + +mkdir -p $root +mkdir -p $root/logs +mkdir -p $root/conf +cat >$root/conf/httpd.conf <<EOF +ServerName 127.0.0.1 +Listen $port +DocumentRoot $htdocs +TypesConfig $here/conf/mime.types +LoadModule mod_tuscany_eval $here/.libs/libmod_tuscany_eval.so +LoadModule mod_tuscany_wiring $here/.libs/libmod_tuscany_wiring.so +EOF + diff --git a/sca-cpp/trunk/modules/http/httpd-test b/sca-cpp/trunk/modules/http/httpd-test new file mode 100755 index 0000000000..1d9b3cb34d --- /dev/null +++ b/sca-cpp/trunk/modules/http/httpd-test @@ -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. + +echo "Testing..." + +# Setup +./httpd-conf tmp 8090 htdocs +cat >>tmp/conf/httpd.conf <<EOF + +<Location /test> +SetHandler mod_tuscany_eval +SCAContribution `pwd`/ +SCAComposite httpd-test.composite +SCAComponent httpd-test +</Location> +EOF + +apachectl -k start -d `pwd`/tmp +sleep 1 + +# Test HTTP GET +curl http://localhost:8090/index.html 2>/dev/null >tmp/index.html +diff tmp/index.html htdocs/index.html +rc=$? + +# Test ATOMPub +if [ "$rc" = "0" ]; then + curl http://localhost:8090/test/ >tmp/feed.xml 2>/dev/null + diff tmp/feed.xml htdocs/feed.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + curl http://localhost:8090/test/111 >tmp/entry.xml 2>/dev/null + diff tmp/entry.xml htdocs/entry.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + curl http://localhost:8090/test/ -X POST -H "Content-type: application/atom+xml" --data @htdocs/entry.xml 2>/dev/null + rc=$? +fi +if [ "$rc" = "0" ]; then + curl http://localhost:8090/test/111 -X PUT -H "Content-type: application/atom+xml" --data @htdocs/entry.xml 2>/dev/null + rc=$? +fi +if [ "$rc" = "0" ]; then + curl http://localhost:8090/test/111 -X DELETE 2>/dev/null + rc=$? +fi + +# Test JSON-RPC +if [ "$rc" = "0" ]; then + curl http://localhost:8090/test/ -X POST -H "Content-type: application/json-rpc" --data @htdocs/json-request.txt >tmp/json-result.txt 2>/dev/null + diff tmp/json-result.txt htdocs/json-result.txt + rc=$? +fi + +# Cleanup +apachectl -k stop -d `pwd`/tmp +sleep 2 +if [ "$rc" = "0" ]; then + echo "OK" +fi +return $rc diff --git a/sca-cpp/trunk/modules/http/httpd-test.composite b/sca-cpp/trunk/modules/http/httpd-test.composite new file mode 100644 index 0000000000..875d26ae1b --- /dev/null +++ b/sca-cpp/trunk/modules/http/httpd-test.composite @@ -0,0 +1,42 @@ +<?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/200903" + xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1" + targetNamespace="http://test" + name="httpd-test"> + + <component name="httpd-test"> + <t:implementation.scheme uri="httpd-test.scm"/> + <service name="test"> + <t:binding.http uri="test"/> + </service> + </component> + + <component name="httpd-client"> + <t:implementation.scheme uri="httpd-client.scm"/> + <service name="client"> + <t:binding.http uri="client"/> + </service> + <reference name="ref" target="test"> + <t:binding.http/> + </reference> + </component> + +</composite> diff --git a/sca-cpp/trunk/modules/http/httpd-test.scm b/sca-cpp/trunk/modules/http/httpd-test.scm new file mode 100644 index 0000000000..0566eaf36f --- /dev/null +++ b/sca-cpp/trunk/modules/http/httpd-test.scm @@ -0,0 +1,29 @@ +; JSON-RPC test case + +(define (echo x) x) + +; ATOMPub test case + +(define (getall) + '("Sample Feed" "123456789" + ("Item" "111" ((javaClass "services.Item") (name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99))) + ("Item" "222" ((javaClass "services.Item") (name "Orange") (currencyCode "USD") (currencySymbol "$") (price 3.55))) + ("Item" "333" ((javaClass "services.Item") (name "Pear") (currencyCode "USD") (currencySymbol "$") (price 1.55)))) +) + +(define (get id) + (define entry '((javaClass "services.Item") (name "Apple") (currencyCode "USD") (currencySymbol "$") (price 2.99))) + (cons "Item" (list id entry)) +) + +(define (post entry) + "123456789" +) + +(define (put id entry) + true +) + +(define (delete id) + true +) diff --git a/sca-cpp/trunk/modules/http/httpd.hpp b/sca-cpp/trunk/modules/http/httpd.hpp new file mode 100644 index 0000000000..1271afc03c --- /dev/null +++ b/sca-cpp/trunk/modules/http/httpd.hpp @@ -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$ */ + +#ifndef tuscany_httpd_hpp +#define tuscany_httpd_hpp + +/** + * HTTPD module utility functions. + */ + +#include <string> +#include <iostream> + +#include "apr_strings.h" +#include "apr_fnmatch.h" +#include "apr_lib.h" +#define APR_WANT_STRFUNC +#include "apr_want.h" +#include "ap_config.h" + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_request.h" +#include "http_protocol.h" +#include "http_log.h" +#include "http_main.h" +#include "util_script.h" +#include "util_md5.h" + +#include "mod_core.h" + +#include "list.hpp" +#include "value.hpp" + + +namespace tuscany { +namespace httpd { + +/** + * Set to true to log requests and content. + */ +bool logRequests = false; +bool logContent = false; + +/** + * Convert a path string to a list of values. + */ +const list<std::string> pathTokens(const char* p) { + if (p == NULL || p[0] == '\0') + return list<std::string>(); + return tokenize("/", p + 1); +} + +const list<value> pathValues(const list<std::string>& l) { + if (isNil(l)) + return list<value>(); + return cons<value>(car(l), pathValues(cdr(l))); +} + +const list<value> path(const char* p) { + return pathValues(pathTokens(p)); +} + +/** + * Convert a path represented as a list of values to a string. + */ +const std::string path(const list<value>& p) { + if (isNil(p)) + return ""; + return "/" + car(p) + path(cdr(p)); +} + +/** + * Return the content type of a request. + */ +const char* optional(const char* s) { + if (s == NULL) + return "(null)"; + return s; +} + +const std::string contentType(const request_rec* r) { + return optional(apr_table_get(r->headers_in, "Content-Type")); +} + +/** + * Log HTTP request info. + */ +int logHeader(void* r, const char* key, const char* value) { + std::cout << "header key: " << key << ", value: " << value << std::endl; + return 1; +} + +const bool logRequest(request_rec* r, const std::string& msg) { + std::cout << msg << std::endl; + std::cout << "protocol: " << optional(r->protocol) << std::endl; + std::cout << "method: " << optional(r->method) << std::endl; + std::cout << "method number: " << r->method_number << std::endl; + std::cout << "content type: " << contentType(r) << std::endl; + std::cout << "content encoding: " << optional(r->content_encoding) << std::endl; + apr_table_do(logHeader, r, r->headers_in, NULL); + std::cout << "uri: " << optional(r->unparsed_uri) << std::endl; + std::cout << "path: " << optional(r->uri) << std::endl; + std::cout << "path info: " << optional(r->path_info) << std::endl; + std::cout << "filename: " << optional(r->filename) << std::endl; + std::cout << "path tokens: " << pathTokens(r->uri) << std::endl; + std::cout << "args: " << optional(r->args) << std::endl; + std::cout.flush(); + return true; +} + +/** + * Returns a list of key value pairs from the args in a query string. + */ +const list<value> queryArg(const std::string& s) { + const list<std::string> t = tokenize("=", s); + return mklist<value>(car(t).c_str(), cadr(t)); +} + +const list<list<value> > queryArgs(const request_rec* r) { + const char* a = r->args; + if (a == NULL) + return list<list<value> >(); + return map<std::string, list<value>>(queryArg, tokenize("&", a)); +} + +/** + * Converts the args received in a POST to a list of key value pairs. + */ +const list<list<value> > postArgs(const list<value>& a) { + if (isNil(a)) + return list<list<value> >(); + const list<value> l = car(a); + return cons(l, postArgs(cdr(a))); +} + +} +} + +#endif /* tuscany_httpd_hpp */ diff --git a/sca-cpp/trunk/modules/http/mod-eval.cpp b/sca-cpp/trunk/modules/http/mod-eval.cpp new file mode 100644 index 0000000000..6fef2be2cb --- /dev/null +++ b/sca-cpp/trunk/modules/http/mod-eval.cpp @@ -0,0 +1,532 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * HTTPD module used to eval component implementations. + */ + +#include <sys/stat.h> + +#include <string> +#include <iostream> +#include <sstream> +#include <fstream> + +#include "list.hpp" +#include "slist.hpp" +#include "value.hpp" +#include "element.hpp" +#include "monad.hpp" +#include "../atom/atom.hpp" +#include "../json/json.hpp" +#include "../eval/driver.hpp" +#include "../scdl/scdl.hpp" +#include "../cache/cache.hpp" +#include "curl.hpp" +#include "httpd.hpp" + +extern "C" { +extern module AP_MODULE_DECLARE_DATA mod_tuscany_eval; +} + +namespace tuscany { +namespace httpd { +namespace modeval { + +/** + * Server configuration. + */ +class ServerConf { +public: + ServerConf() : home("") { + } + std::string home; +}; + +const ServerConf& serverConf(const request_rec* r) { + return *(ServerConf*)ap_get_module_config(r->server->module_config, &mod_tuscany_eval); +} + +/** + * Port number used for wiring requests. Set it to zero to use the current + * server port number, set it to a port number to direct wiring requests + * to that port, for debugging or tracing for example. + */ +int debugWiringPort = 0; + +/** + * Directory configuration. + */ +class DirConf { +public: + DirConf() : contributionPath(""), compositeName(""), componentName(""), implementationPath("") { + } + std::string contributionPath; + std::string compositeName; + std::string componentName; + std::string implementationPath; + cache::cached<failable<value, std::string> > component; + cache::cached<failable<value, std::string> > implementation; +}; + +DirConf& dirConf(const request_rec* r) { + return *(DirConf*)ap_get_module_config(r->per_dir_config, &mod_tuscany_eval); +} + +/** + * Evaluate an expression against a component implementation. + */ +const failable<value, std::string> evalExpr(const value& expr, const value& impl) { + gc_pool pool; + eval::Env globalEnv = eval::setupEnvironment(pool); + if (logContent) { + std::cout<< "expr: " << expr << std::endl; + std::cout.flush(); + } + const value val = eval::evalScript(expr, impl, globalEnv, pool); + if (logContent) { + std::cout<< "val: " << val << std::endl; + std::cout.flush(); + } + + if (isNil(val)) + return mkfailure<value, std::string>("Could not evaluate expression"); + return val; +} + +/** + * Returns a list of param values other than the id and method args from a list + * of key value pairs. + */ +const list<value> queryParams(const list<list<value> >& a) { + if (isNil(a)) + return list<value>(); + const list<value> p = car(a); + if (car(p) == value("id") || car(p) == value("method")) + return queryParams(cdr(a)); + return cons(cadr(p), queryParams(cdr(a))); +} + +/** + * Write an HTTP result. + */ +const failable<int, std::string> writeResult(const failable<list<std::string>, std::string>& ls, const std::string& ct, request_rec* r) { + if (!hasValue(ls)) + return mkfailure<int, std::string>(reason(ls)); + std::ostringstream os; + write(ls, os); + if (logContent) { + std::cout<< "content: " << std::endl << os.str() << std::endl; + std::cout.flush(); + } + + const std::string etag(ap_md5(r->pool, (const unsigned char*)std::string(os.str()).c_str())); + const char* match = apr_table_get(r->headers_in, "If-None-Match"); + apr_table_setn(r->headers_out, "ETag", apr_pstrdup(r->pool, etag.c_str())); + if (match != NULL && etag == match) { + r->status = HTTP_NOT_MODIFIED; + return OK; + } + ap_set_content_type(r, ct.c_str()); + ap_rputs(std::string(os.str()).c_str(), r); + return OK; +} + +/** + * Handle an HTTP GET. + */ +const failable<int, std::string> get(request_rec* r, const value& impl, const list<value>& px) { + + // Inspect the query string + const list<list<value> > args = queryArgs(r); + const list<value> ia = assoc(value("id"), args); + const list<value> ma = assoc(value("method"), args); + + // Evaluate a JSON-RPC request and return a JSON result + if (!isNil(ia) && !isNil(ma)) { + + // Extract the request id, method and params + const value id = cadr(ia); + const value func = std::string(cadr(ma)).c_str(); + const list<value> params = queryParams(args); + + // Evaluate the request expression + const failable<value, std::string> val = evalExpr(cons<value>(func, eval::quotedParameters(append(params, px))), impl); + if (!hasValue(val)) + return mkfailure<int, std::string>(reason(val)); + + // Return JSON result + json::JSONContext cx; + return writeResult(json::jsonResult(id, val, cx), "application/json-rpc", r); + } + + // Evaluate an ATOM GET request and return an ATOM feed + const list<value> id(path(r->path_info)); + if (isNil(id)) { + const failable<value, std::string> val = evalExpr(cons<value>("getall", eval::quotedParameters(px)), impl); + if (!hasValue(val)) + return mkfailure<int, std::string>(reason(val)); + const value feed = val; + return writeResult(atom::writeATOMFeed(atom::feedValuesToElements(feed)), "application/atom+xml;type=feed", r); + } + + // Evaluate an ATOM GET and return an ATOM entry + const failable<value, std::string> val = evalExpr(cons<value>("get", eval::quotedParameters(cons<value>(car(id), px))), impl); + if (!hasValue(val)) + return mkfailure<int, std::string>(reason(val)); + const value entry = val; + return writeResult(atom::writeATOMEntry(atom::entryValuesToElements(entry)), "application/atom+xml;type=entry", r); + +} + +/** + * Read the content of a POST. + */ +const list<std::string> read(request_rec* r) { + char b[2048]; + const int n = ap_get_client_block(r, b, 2048); + if (n <= 0) + return list<std::string>(); + return cons(std::string(b, n), read(r)); +} + +/** + * Convert a URI value to an absolute URL. + */ +const char* url(const value& v, request_rec* r) { + std::string u = r->uri; + u.append("/"); + u.append(v); + return ap_construct_url(r->pool, u.c_str(), r); +} + +/** + * Convert an ATOM entry to a value. + */ +const value feedEntry(const list<value>& e) { + const list<value> v = elementsToValues(mklist<value>(caddr(e))); + return cons(car(e), mklist<value>(cadr(e), cdr<value>(car(v)))); +} + +/** + * Handle an HTTP POST. + */ +const failable<int, std::string> post(request_rec* r, const value& impl, const list<value>& px) { + const list<std::string> ls = read(r); + if (logContent) { + std::cout<< "content: " << std::endl; + write(ls, std::cout); + std::cout<< std::endl; + std::cout.flush(); + } + + // Evaluate a JSON-RPC request and return a JSON result + const std::string ct = contentType(r); + if (ct.find("application/json-rpc") != std::string::npos || ct.find("text/plain") != std::string::npos) { + json::JSONContext cx; + const list<value> json = elementsToValues(json::readJSON(ls, cx)); + const list<list<value> > args = postArgs(json); + + // Extract the request id, method and params + const value id = cadr(assoc(value("id"), args)); + const value func = std::string(cadr(assoc(value("method"), args))).c_str(); + const list<value> params = (list<value>)cadr(assoc(value("params"), args)); + + // Evaluate the request expression + const failable<value, std::string> val = evalExpr(cons<value>(func, eval::quotedParameters(append(params, px))), impl); + if (!hasValue(val)) + return mkfailure<int, std::string>(reason(val)); + + // Return JSON result + return writeResult(json::jsonResult(id, val, cx), "application/json-rpc", r); + } + + // Evaluate an ATOM POST request and return the created resource location + if (ct.find("application/atom+xml") != std::string::npos) { + + // Evaluate the request expression + const value entry = feedEntry(atom::readEntry(ls)); + const failable<value, std::string> val = evalExpr(cons<value>("post", eval::quotedParameters(cons<value>(entry, px))), impl); + if (!hasValue(val)) + return mkfailure<int, std::string>(reason(val)); + + // Return the created resource location + apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, url(val, r))); + r->status = HTTP_CREATED; + return OK; + } + + return HTTP_NOT_IMPLEMENTED; +} + +/** + * Handle an HTTP PUT. + */ +const failable<int, std::string> put(request_rec* r, const value& impl, const list<value>& px) { + const list<std::string> ls = read(r); + if (logContent) { + std::cout<< "content: " << std::endl; + write(ls, std::cout); + std::cout<< std::endl; + std::cout.flush(); + } + + // Evaluate an ATOM PUT request + const list<value> id(path(r->path_info)); + const value entry = feedEntry(atom::readEntry(ls)); + const failable<value, std::string> val = evalExpr(cons<value>("put", eval::quotedParameters(append(mklist<value>(entry, car(id)), px))), impl); + if (!hasValue(val)) + return mkfailure<int, std::string>(reason(val)); + if (val == value(false)) + return HTTP_NOT_FOUND; + return OK; +} + +/** + * Handle an HTTP DELETE. + */ +const failable<int, std::string> del(request_rec* r, const value& impl, const list<value>& px) { + + // Evaluate an ATOM delete request + const list<value> id(path(r->path_info)); + const failable<value, std::string> val = evalExpr(cons<value>("delete", eval::quotedParameters(cons<value>(car(id), px))), impl); + if (!hasValue(val)) + return mkfailure<int, std::string>(reason(val)); + if (val == value(false)) + return HTTP_NOT_FOUND; + return OK; +} + +/** + * Report request execution status. + */ +const int reportStatus(const failable<int, std::string>& rc) { + if (!hasValue(rc)) + return HTTP_INTERNAL_SERVER_ERROR; + return rc; +} + +/** + * Read the SCDL configuration of a component. + */ +const failable<value, std::string> readComponent(const std::string& path, const std::string& name) { + + // Read composite + std::ifstream is(path); + if (is.fail() || is.bad()) + return mkfailure<value, std::string>("Could not read composite: " + path); + + // Return the component + const list<value> c = scdl::components(readXML(streamList(is))); + const value comp = scdl::named(name, c); + if (isNil(comp)) + return mkfailure<value, std::string>("Could not find component: " + name); + return comp; +} + +const cache::cached<failable<value, std::string> > component(DirConf* conf) { + const std::string path(conf->contributionPath + conf->compositeName); + const lambda<failable<value, std::string>(std::string, std::string)> rc(readComponent); + const lambda<unsigned long(std::string)> ft(cache::latestFileTime); + return cache::cached<failable<value, std::string> >(curry(rc, path, conf->componentName), curry(ft, path)); +} + +/** + * Read a component implementation. + */ +const failable<value, std::string> readImplementation(const std::string path) { + std::ifstream is(path.c_str(), std::ios_base::in); + if (is.fail() || is.bad()) + return mkfailure<value, std::string>("Could not read implementation: " + path); + const value impl = eval::readScript(is); + if (isNil(impl)) + return mkfailure<value, std::string>("Could not read implementation: " + path); + return impl; +} + +const cache::cached<failable<value, std::string> > implementation(const std::string& path) { + const lambda<failable<value, std::string>(std::string)> ri(readImplementation); + const lambda<unsigned long(std::string)> ft(cache::latestFileTime); + return cache::cached<failable<value, std::string> >(curry(ri, path), curry(ft, path)); +} + +/** + * Convert a list of component references to a list of HTTP proxy lambdas. + */ +const value mkproxy(const value& ref, const std::string& base, const http::CURLHandle& ch) { + return eval::primitiveProcedure(http::proxy(base + std::string(scdl::name(ref)), ch)); +} + +const list<value> proxies(const list<value>& refs, const std::string& base, const http::CURLHandle& ch) { + if (isNil(refs)) + return refs; + return cons(mkproxy(car(refs), base, ch), proxies(cdr(refs), base, ch)); +} + +/** + * HTTP request handler. + */ +int handler(request_rec *r) { + if(strcmp(r->handler, "mod_tuscany_eval")) + return DECLINED; + + // Log the request + if(logRequests) + logRequest(r, "mod_tuscany_eval::handler"); + + // Set up the read policy + const int rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK); + if(rc != OK) + return rc; + ap_should_client_block(r); + if(r->read_chunked == true && r->remaining == 0) + r->chunked = true; + //apr_table_setn(r->headers_out, "Connection", "close"); + + // Retrieve the latest component configuration + DirConf& conf = dirConf(r); + conf.component = cache::latest(conf.component); + const failable<value, std::string> comp(conf.component); + if (!hasValue(comp)) + return HTTP_NOT_FOUND; + + // Retrieve the latest implementation + const std::string path = conf.contributionPath + std::string(scdl::uri(scdl::implementation(comp))); + if (path != conf.implementationPath) { + conf.implementationPath = path; + conf.implementation = cache::latest(implementation(path)); + } + else + conf.implementation = cache::latest(conf.implementation); + const failable<value, std::string> impl(conf.implementation); + if (!hasValue(impl)) + return HTTP_NOT_FOUND; + + // Convert component references to configured proxy lambdas + std::ostringstream base; + base << "http://localhost:" << (debugWiringPort == 0? ap_get_server_port(r) : debugWiringPort) << "/references/" << std::string(scdl::name(comp)) << "/"; + http::CURLHandle ch; + const list<value> px(proxies(scdl::references(comp), base.str(), ch)); + + // Handle HTTP method + if (r->header_only) + return OK; + if(r->method_number == M_GET) + return reportStatus(get(r, impl, px)); + if(r->method_number == M_POST) + return reportStatus(post(r, impl, px)); + if(r->method_number == M_PUT) + return reportStatus(put(r, impl, px)); + if(r->method_number == M_DELETE) + return reportStatus(del(r, impl, px)); + return HTTP_NOT_IMPLEMENTED; +} + +/** + * Configuration commands. + */ +const char *confHome(cmd_parms *cmd, void *dummy, const char *arg) { + ServerConf *c = (ServerConf*)ap_get_module_config(cmd->server->module_config, &mod_tuscany_eval); + c->home = arg; + return NULL; +} +const char *confContribution(cmd_parms *cmd, void *c, const char *arg) { + DirConf* conf = (DirConf*)c; + conf->contributionPath = arg; + conf->component = component(conf); + return NULL; +} +const char *confComposite(cmd_parms *cmd, void *c, const char *arg) { + DirConf* conf = (DirConf*)c; + conf->compositeName = arg; + conf->component = component(conf); + return NULL; +} +const char *confComponent(cmd_parms *cmd, void *c, const char *arg) { + DirConf* conf = (DirConf*)c; + conf->componentName = arg; + conf->component = component(conf); + return NULL; +} + +void *makeDirConf(apr_pool_t *p, char *dirspec) { + DirConf* c = new (apr_palloc(p, sizeof(DirConf))) DirConf(); + apr_pool_cleanup_register(p, c, gc_pool_cleanupCallback<DirConf>, apr_pool_cleanup_null) ; + return c; +} +void* makeServerConf(apr_pool_t *p, server_rec *s) { + ServerConf* c = new (apr_palloc(p, sizeof(ServerConf))) ServerConf(); + apr_pool_cleanup_register(p, c, gc_pool_cleanupCallback<ServerConf>, apr_pool_cleanup_null) ; + return c; +} + +/** + * HTTP server module declaration. + */ +const command_rec commands[] = { + AP_INIT_TAKE1("TuscanyHome", (const char*(*)())confHome, NULL, RSRC_CONF, "Tuscany home directory"), + AP_INIT_TAKE1("SCAContribution", (const char*(*)())confContribution, NULL, ACCESS_CONF, "SCA contribution location"), + AP_INIT_TAKE1("SCAComposite", (const char*(*)())confComposite, NULL, ACCESS_CONF, "SCA composite location"), + AP_INIT_TAKE1("SCAComponent", (const char*(*)())confComponent, NULL, ACCESS_CONF, "SCA component name"), + {NULL} +}; + +int postConfig(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { + return OK; +} + +void childInit(apr_pool_t* p, server_rec* svr_rec) { + ServerConf *c = (ServerConf*)ap_get_module_config(svr_rec->module_config, &mod_tuscany_eval); + if(c == NULL) { + std::cerr << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << std::endl; + exit(APEXIT_CHILDFATAL); + } +} + +void registerHooks(apr_pool_t *p) { + ap_hook_post_config(postConfig, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_child_init(childInit, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_handler(handler, NULL, NULL, APR_HOOK_MIDDLE); +} + +} +} +} + +extern "C" { + +module AP_MODULE_DECLARE_DATA mod_tuscany_eval = { + STANDARD20_MODULE_STUFF, + // dir config + tuscany::httpd::modeval::makeDirConf, + // dir merger, default is to override + NULL, + // server config + tuscany::httpd::modeval::makeServerConf, + // merge server config + NULL, + // command table + tuscany::httpd::modeval::commands, + // register hooks + tuscany::httpd::modeval::registerHooks +}; + +} diff --git a/sca-cpp/trunk/modules/http/mod-wiring.cpp b/sca-cpp/trunk/modules/http/mod-wiring.cpp new file mode 100644 index 0000000000..965d5a87fb --- /dev/null +++ b/sca-cpp/trunk/modules/http/mod-wiring.cpp @@ -0,0 +1,276 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * HTTPD module used to wire components. + */ + +#include <sys/stat.h> + +#include <string> +#include <iostream> +#include <sstream> +#include <fstream> + +#include "list.hpp" +#include "slist.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "../scdl/scdl.hpp" +#include "../cache/cache.hpp" +#include "httpd.hpp" + +extern "C" { +extern module AP_MODULE_DECLARE_DATA mod_tuscany_wiring; +} + +namespace tuscany { +namespace httpd { +namespace modwiring { + +/** + * Server configuration. + */ +class ServerConf { +public: + std::string home; + ServerConf() : home("") { + } +}; + +const ServerConf& serverConf(const request_rec* r) { + return *(ServerConf*)ap_get_module_config(r->server->module_config, &mod_tuscany_wiring); +} + +/** + * Set to true to wire using mod_proxy, false to wire using HTTP client redirects. + */ +const bool useModProxy = true; + +/** + * Directory configuration. + */ +class DirConf { +public: + DirConf() : contributionPath(""), compositeName("") { + } + std::string contributionPath; + std::string compositeName; + cache::cached<failable<list<value>, std::string> > components; +}; + +DirConf& dirConf(const request_rec* r) { + return *(DirConf*)ap_get_module_config(r->per_dir_config, &mod_tuscany_wiring); +} + +/** + * Read the SCDL configuration of the deployed components. + */ +const failable<list<value>, std::string> readComponents(const std::string& path) { + std::ifstream is(path); + if (is.fail() || is.bad()) + return mkfailure<list<value>, std::string>("Could not read composite: " + path); + return scdl::components(readXML(streamList(is))); +} + +const cache::cached<failable<list<value>, std::string> > components(DirConf* conf) { + const std::string path(conf->contributionPath + conf->compositeName); + const lambda<failable<list<value>, std::string>(std::string)> rc(readComponents); + const lambda<unsigned long(std::string)> ft(cache::latestFileTime); + return cache::cached<failable<list<value>, std::string> >(curry(rc, path), curry(ft, path)); +} + +/** + * Returns true if a URI is absolute. + */ +const bool isAbsolute(const std::string& uri) { + return uri.find("://") != std::string::npos; +} + +/** + * Translate an HTTP request URI. Wire a URI in the form /references/component-name/reference-name + * to the target of the reference. + */ +int translate(request_rec *r) { + if (strncmp(r->uri, "/references/", 12) != 0) + return DECLINED; + const list<value> rpath(path(r->uri)); + + // Log the request + if(logRequests) + logRequest(r, "mod_tuscany_wiring::translate"); + + // Find the requested component, reference and its target configuration + DirConf& conf = dirConf(r); + conf.components = cache::latest(conf.components); + const failable<list<value>, std::string> comps(conf.components); + if (!hasValue(comps)) + return HTTP_INTERNAL_SERVER_ERROR; + const value comp(scdl::named(cadr(rpath), list<value>(comps))); + if (isNil(comp)) + return HTTP_NOT_FOUND; + const value ref(scdl::named(caddr(rpath), scdl::references(comp))); + if (isNil(ref)) + return HTTP_NOT_FOUND; + const std::string target(scdl::target(ref)); + + // Absolute target URI + if (isAbsolute(target)) { + + // Wire using mod_proxy + if (useModProxy) { + r->filename = apr_pstrdup(r->pool, std::string("proxy:" + target).c_str()); + r->proxyreq = PROXYREQ_REVERSE; + r->handler = "proxy-server"; + return OK; + } + + // Wire using an HTTP client redirect + r->status = HTTP_MOVED_TEMPORARILY; + apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, target.c_str())); + r->handler = "mod_tuscany_wiring"; + return OK; + + } + + // Local internal redirect + r->filename = apr_pstrdup(r->pool, std::string("/redirect:/" + target).c_str()); + r->handler = "mod_tuscany_wiring"; + return OK; +} + +/** + * Construct a redirect URI. + */ +const std::string redirect(const std::string& file, const std::string& pi) { + return file + pi; +} + +const std::string redirect(const std::string& file, const std::string& pi, const std::string& args) { + return file + pi + "?" + args; +} + +/** + * HTTP request internal redirect handler. + */ +int handler(request_rec *r) { + if(strcmp(r->handler, "mod_tuscany_wiring")) + return DECLINED; + + // Log the request + if(logRequests) + logRequest(r, "mod_tuscany_wiring::handler"); + + // Do an internal redirect + if (r->filename == NULL || strncmp(r->filename, "/redirect:", 10) != 0) + return DECLINED; + if (r->args == NULL) { + ap_internal_redirect(apr_pstrdup(r->pool, redirect(r->filename + 10, r->path_info).c_str()), r); + return OK; + } + ap_internal_redirect(apr_pstrdup(r->pool, redirect(r->filename + 10, r->path_info, r->args).c_str()), r); + return OK; +} + +/** + * Configuration commands. + */ +const char *confHome(cmd_parms *cmd, void *dummy, const char *arg) { + ServerConf *c = (ServerConf*)ap_get_module_config(cmd->server->module_config, &mod_tuscany_wiring); + c->home = arg; + return NULL; +} +const char *confContribution(cmd_parms *cmd, void *c, const char *arg) { + DirConf* conf = (DirConf*)c; + conf->contributionPath = arg; + conf->components = components(conf); + return NULL; +} +const char *confComposite(cmd_parms *cmd, void *c, const char *arg) { + DirConf* conf = (DirConf*)c; + conf->compositeName = arg; + conf->components = components(conf); + return NULL; +} + +void *makeDirConf(apr_pool_t *p, char *dirspec) { + DirConf* c = new (apr_palloc(p, sizeof(DirConf))) DirConf(); + apr_pool_cleanup_register(p, c, gc_pool_cleanupCallback<DirConf>, apr_pool_cleanup_null) ; + return c; +} +void* makeServerConf(apr_pool_t *p, server_rec *s) { + ServerConf* c = new (apr_palloc(p, sizeof(ServerConf))) ServerConf(); + apr_pool_cleanup_register(p, c, gc_pool_cleanupCallback<ServerConf>, apr_pool_cleanup_null) ; + return c; +} + +/** + * HTTP server module declaration. + */ +const command_rec commands[] = { + AP_INIT_TAKE1("TuscanyHome", (const char*(*)())confHome, NULL, RSRC_CONF, "Tuscany home directory"), + AP_INIT_TAKE1("SCAContribution", (const char*(*)())confContribution, NULL, ACCESS_CONF, "SCA contribution location"), + AP_INIT_TAKE1("SCAComposite", (const char*(*)())confComposite, NULL, ACCESS_CONF, "SCA composite location"), + {NULL} +}; + +int postConfig(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { + return OK; +} + +void childInit(apr_pool_t* p, server_rec* svr_rec) { + ServerConf *conf = (ServerConf*)ap_get_module_config(svr_rec->module_config, &mod_tuscany_wiring); + if(conf == NULL) { + std::cerr << "[Tuscany] Due to one or more errors mod_tuscany_wiring loading failed. Causing apache to stop loading." << std::endl; + exit(APEXIT_CHILDFATAL); + } +} + +void registerHooks(apr_pool_t *p) { + ap_hook_post_config(postConfig, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_child_init(childInit, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_handler(handler, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_translate_name(translate, NULL, NULL, APR_HOOK_FIRST); +} + +} +} +} + +extern "C" { + +module AP_MODULE_DECLARE_DATA mod_tuscany_wiring = { + STANDARD20_MODULE_STUFF, + // dir config + tuscany::httpd::modwiring::makeDirConf, + // dir merger, default is to override + NULL, + // server config + tuscany::httpd::modwiring::makeServerConf, + // merge server config + NULL, + // command table + tuscany::httpd::modwiring::commands, + // register hooks + tuscany::httpd::modwiring::registerHooks +}; + +} diff --git a/sca-cpp/trunk/modules/http/wiring-test b/sca-cpp/trunk/modules/http/wiring-test new file mode 100755 index 0000000000..0c3f36b513 --- /dev/null +++ b/sca-cpp/trunk/modules/http/wiring-test @@ -0,0 +1,92 @@ +#!/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 +./httpd-conf tmp 8092 htdocs +cat >>tmp/conf/httpd.conf <<EOF + +<Location /test> +SetHandler mod_tuscany_eval +SCAContribution `pwd`/ +SCAComposite httpd-test.composite +SCAComponent httpd-test +</Location> + +<Location /client> +SetHandler mod_tuscany_eval +SCAContribution /home/delfinoj/SCAZone/Source/tuscany-cpp/sca/modules/http/ +SCAComposite httpd-test.composite +SCAComponent httpd-client +</Location> + +<Location /references> +SetHandler mod_tuscany_wiring +SCAContribution `pwd`/ +SCAComposite httpd-test.composite +</Location> +EOF + +apachectl -k start -d `pwd`/tmp +sleep 1 + +# Test HTTP GET +curl http://localhost:8092/index.html 2>/dev/null >tmp/index.html +diff tmp/index.html htdocs/index.html +rc=$? + +# Test ATOMPub +if [ "$rc" = "0" ]; then + curl http://localhost:8092/client/ >tmp/feed.xml 2>/dev/null + diff tmp/feed.xml htdocs/feed.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + curl http://localhost:8092/client/111 >tmp/entry.xml 2>/dev/null + diff tmp/entry.xml htdocs/entry.xml + rc=$? +fi +if [ "$rc" = "0" ]; then + curl http://localhost:8092/client/ -X POST -H "Content-type: application/atom+xml" --data @htdocs/entry.xml 2>/dev/null + rc=$? +fi +if [ "$rc" = "0" ]; then + curl http://localhost:8092/client/111 -X PUT -H "Content-type: application/atom+xml" --data @htdocs/entry.xml 2>/dev/null + rc=$? +fi +if [ "$rc" = "0" ]; then + curl http://localhost:8092/client/111 -X DELETE 2>/dev/null + rc=$? +fi + +# Test JSON-RPC +if [ "$rc" = "0" ]; then + curl http://localhost:8092/client/ -X POST -H "Content-type: application/json-rpc" --data @htdocs/json-request.txt >tmp/json-result.txt 2>/dev/null + diff tmp/json-result.txt htdocs/json-result.txt + rc=$? +fi + +# Cleanup +apachectl -k stop -d `pwd`/tmp +sleep 2 +if [ "$rc" = "0" ]; then + echo "OK" +fi +return $rc diff --git a/sca-cpp/trunk/modules/json/Makefile.am b/sca-cpp/trunk/modules/json/Makefile.am new file mode 100644 index 0000000000..ab85d910ad --- /dev/null +++ b/sca-cpp/trunk/modules/json/Makefile.am @@ -0,0 +1,28 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +noinst_PROGRAMS = json-test + +nobase_include_HEADERS = *.hpp + +INCLUDES = -I. -I$(top_builddir)/kernel -I${LIBXML2_INCLUDE} -I${APR_INCLUDE} -I${LIBMOZJS_INCLUDE} + +json_test_SOURCES = json-test.cpp +json_test_LDADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1 -L${LIBMOZJS_LIB} -lmozjs + +TESTS = json-test + diff --git a/sca-cpp/trunk/modules/json/json-test.cpp b/sca-cpp/trunk/modules/json/json-test.cpp new file mode 100644 index 0000000000..fd506cea65 --- /dev/null +++ b/sca-cpp/trunk/modules/json/json-test.cpp @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 JSON data conversion functions. + */ + +#include <assert.h> +#include <iostream> +#include <sstream> +#include <string> +#include "slist.hpp" +#include "json.hpp" + +namespace tuscany { +namespace json { + +bool testJSEval() { + JSONContext cx; + const std::string script("(function testJSON(n){ return JSON.parse(JSON.stringify(n)) })(5)"); + jsval rval; + assert(JS_EvaluateScript(cx, cx.getGlobal(), script.c_str(), script.length(), "testJSON.js", 1, &rval)); + const std::string r(JS_GetStringBytes(JS_ValueToString(cx, rval))); + assert(r == "5"); + return true; +} + +std::ostringstream* jsonWriter(const std::string& s, std::ostringstream* os) { + (*os) << s; + return os; +} + +bool testJSON() { + const JSONContext cx; + + { + const list<value> ad = mklist<value>(mklist<value>(attribute, "city", std::string("san francisco")), mklist<value>(attribute, "state", std::string("ca"))); + const list<value> ac = mklist<value>(mklist<value>(element, "id", std::string("1234")), mklist<value>(attribute, "balance", 1000)); + const list<value> cr = mklist<value>(mklist<value> (attribute, "name", std::string("jdoe")), cons<value>(element, cons<value>("address", ad)), cons<value>(element, cons<value>("account", ac))); + const list<value> c = mklist<value>(cons<value>(element, cons<value>("customer", cr))); + std::ostringstream os; + writeJSON<std::ostringstream*>(jsonWriter, &os, c, cx); + assert(os.str() == "{\"customer\":{\"name\":\"jdoe\",\"address\":{\"city\":\"san francisco\",\"state\":\"ca\"},\"account\":{\"id\":\"1234\",\"balance\":1000}}}"); + } + { + const list<value> phones = mklist<value> (std::string("408-1234"), std::string("650-1234")); + const list<value> l = mklist<value> (mklist<value> (element, "phones", phones), mklist<value> (element, "lastName", std::string("test\ttab")), mklist<value> (element, "firstName", std::string("test1"))); + + std::ostringstream os; + writeJSON<std::ostringstream*>(jsonWriter, &os, l, cx); + assert(os.str() == "{\"phones\":[\"408-1234\",\"650-1234\"],\"lastName\":\"test\\u0009tab\",\"firstName\":\"test1\"}"); + + std::istringstream is(os.str()); + const list<std::string> il = streamList(is); + const list<value> r = readJSON(il, cx); + assert(r == l); + + std::ostringstream wos; + write(writeJSON(r, cx), wos); + assert(wos.str() == os.str()); + } + return true; +} + +bool testJSONRPC() { + JSONContext cx; + { + const std::string lm("{\"id\": 1, \"method\": \"system.listMethods\", \"params\": []}"); + const list<value> e = readJSON(mklist(lm), cx); + const list<value> v = elementsToValues(e); + assert(assoc<value>("id", v) == mklist<value>("id", 1)); + assert(assoc<value>("method", v) == mklist<value>("method", std::string("system.listMethods"))); + assert(assoc<value>("params", v) == mklist<value>("params", list<value>())); + } + { + const std::string i("{\"id\":3,\"result\":[{\"price\":\"$2.99\",\"name\":\"Apple\"},{\"price\":\"$3.55\",\"name\":\"Orange\"},{\"price\":\"$1.55\",\"name\":\"Pear\"}]}"); + const list<value> e = readJSON(mklist(i), cx); + const std::string i2("{\"id\":3,\"result\":{\"0\":{\"price\":\"$2.99\",\"name\":\"Apple\"},\"1\":{\"price\":\"$3.55\",\"name\":\"Orange\"},\"2\":{\"price\":\"$1.55\",\"name\":\"Pear\"}}}"); + const list<value> e2 = readJSON(mklist(i), cx); + assert(e == e2); + } + { + const std::string i("{\"id\":3,\"result\":[{\"price\":\"$2.99\",\"name\":\"Apple\"},{\"price\":\"$3.55\",\"name\":\"Orange\"},{\"price\":\"$1.55\",\"name\":\"Pear\"}]}"); + const list<value> e = readJSON(mklist(i), cx); + std::ostringstream os; + write(writeJSON(e, cx), os); + assert(os.str() == i); + const list<value> v = elementsToValues(e); + const list<value> r = valuesToElements(v); + assert(r == e); + } + { + const list<value> r = mklist<value>(mklist<value>("id", 1), mklist<value>("result", mklist<value>(std::string("Service.get"), std::string("Service.getTotal")))); + const list<value> e = valuesToElements(r); + std::ostringstream os; + write(writeJSON(e, cx), os); + assert(os.str() == "{\"id\":1,\"result\":[\"Service.get\",\"Service.getTotal\"]}"); + } + { + const std::string f("{\"id\":1,\"result\":[\"Sample Feed\",\"123456789\",[\"Item\",\"111\",{\"javaClass\":\"services.Item\",\"name\":\"Apple\",\"currencyCode\":\"USD\",\"currencySymbol\":\"$\",\"price\":2.99}],[\"Item\",\"222\",{\"javaClass\":\"services.Item\",\"name\":\"Orange\",\"currencyCode\":\"USD\",\"currencySymbol\":\"$\",\"price\":3.55}],[\"Item\",\"333\",{\"javaClass\":\"services.Item\",\"name\":\"Pear\",\"currencyCode\":\"USD\",\"currencySymbol\":\"$\",\"price\":1.55}]]}"); + const list<value> r = readJSON(mklist(f), cx); + const list<value> v = elementsToValues(r); + const list<value> e = valuesToElements(v); + std::ostringstream os; + write(writeJSON(e, cx), os); + assert(os.str() == f); + } + return true; +} + +} +} + +int main() { + std::cout << "Testing..." << std::endl; + + tuscany::json::testJSEval(); + tuscany::json::testJSON(); + tuscany::json::testJSONRPC(); + + std::cout << "OK" << std::endl; + + return 0; +} diff --git a/sca-cpp/trunk/modules/json/json.hpp b/sca-cpp/trunk/modules/json/json.hpp new file mode 100644 index 0000000000..f6c8eb5fe8 --- /dev/null +++ b/sca-cpp/trunk/modules/json/json.hpp @@ -0,0 +1,396 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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_json_hpp +#define tuscany_json_hpp + +/** + * JSON data conversion functions. + */ + +#define XP_UNIX +#include <jsapi.h> +#include <string> +#include "list.hpp" +#include "value.hpp" +#include "element.hpp" +#include "monad.hpp" + +namespace tuscany { +namespace json { + +/** + * Report JSON errors. + */ +void reportError(JSContext *cx, const char *message, JSErrorReport *report) { + std::cerr << (const char*)(report->filename? report->filename : "<no filename>") << ":" + << (unsigned int)report->lineno << ":" << message << std::endl; +} + +/** + * Encapsulates a JavaScript runtime. Can be shared by multiple threads in + * a process. + */ +class JSONRuntime { +public: + JSONRuntime() { + // Create JS runtime + rt = JS_NewRuntime(8L * 1024L * 1024L); + if(rt == NULL) + cleanup(); + } + + ~JSONRuntime() { + } + + operator JSRuntime*() const { + return rt; + } +private: + bool cleanup() { + if(rt != NULL) { + JS_DestroyRuntime(rt); + rt = NULL; + } + JS_ShutDown(); + return true; + } + + JSRuntime* rt; +}; + +/** + * Global JavaScript runtime instance. + */ +JSONRuntime jsRuntime; + +JSClass jsGlobalClass = { "global", JSCLASS_GLOBAL_FLAGS, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS}; + + +/** + * Represents a JavaScript context. Create one per thread. + */ +class JSONContext { +public: + JSONContext() { + // Create JS context + cx = JS_NewContext(jsRuntime, 8192); + if(cx == NULL) + return; + JS_SetOptions(cx, JSOPTION_VAROBJFIX); + JS_SetVersion(cx, JSVERSION_DEFAULT); + JS_SetErrorReporter(cx, reportError); + + // Create global JS object + global = JS_NewObject(cx, &jsGlobalClass, NULL, NULL); + if(global == NULL) { + cleanup(); + return; + } + + // Populate global object with the standard globals, like Object and Array + if(!JS_InitStandardClasses(cx, global)) { + cleanup(); + return; + } + } + + ~JSONContext() { + cleanup(); + } + + operator JSContext*() const { + return cx; + } + + JSObject* getGlobal() const { + return global; + } + +private: + bool cleanup() { + if(cx != NULL) { + JS_DestroyContext(cx); + cx = NULL; + } + return true; + } + + JSContext* cx; + JSObject* global; +}; + +/** + * Converts JS properties to values. + */ +const list<value> jsPropertiesToValues(const list<value>& propertiesSoFar, JSObject* o, JSObject* i, const JSONContext& cx) { + + const value jsValToValue(const jsval& jsv, const JSONContext& cx); + + jsid id; + if(!JS_NextProperty(cx, i, &id) || id == JSVAL_VOID) + return propertiesSoFar; + jsval jsv; + if(!JS_GetPropertyById(cx, o, id, &jsv)) + return propertiesSoFar; + const value val = jsValToValue(jsv, cx); + jsval idv; + JS_IdToValue(cx, id, &idv); + if(JSVAL_IS_STRING(idv)) { + const value type = isList(val)? element : element; + return jsPropertiesToValues(cons<value> (mklist<value> (type, JS_GetStringBytes(JSVAL_TO_STRING(idv)), val), propertiesSoFar), o, i, cx); + } + return jsPropertiesToValues(cons(val, propertiesSoFar), o, i, cx); +} + +/** + * Converts a JS val to a value. + */ +const value jsValToValue(const jsval& jsv, const JSONContext& cx) { + switch(JS_TypeOfValue(cx, jsv)) { + case JSTYPE_STRING: { + return value(std::string(JS_GetStringBytes(JSVAL_TO_STRING(jsv)))); + } + case JSTYPE_BOOLEAN: { + return value((bool)JSVAL_TO_BOOLEAN(jsv)); + } + case JSTYPE_NUMBER: { + jsdouble jsd; + JS_ValueToNumber(cx, jsv, &jsd); + return value((double)jsd); + } + case JSTYPE_OBJECT: { + JSObject* o = JSVAL_TO_OBJECT(jsv); + JSObject* i = JS_NewPropertyIterator(cx, o); + if(i == NULL) + return value(list<value> ()); + const value pv = jsPropertiesToValues(list<value> (), o, i, cx); + return pv; + } + default: { + return value(); + } + } +} + +/** + * Consumes JSON strings and populates a JS object. + */ +failable<bool, std::string> consume(JSONParser* parser, const list<std::string>& ilist, const JSONContext& cx) { + if (isNil(ilist)) + return true; + JSString* jstr = JS_NewStringCopyZ(cx, car(ilist).c_str()); + if(!JS_ConsumeJSONText(cx, parser, JS_GetStringChars(jstr), JS_GetStringLength(jstr))) + return mkfailure<bool, std::string>("JS_ConsumeJSONText failed"); + return consume(parser, cdr(ilist), cx); +} + +/** + * Convert a list of strings representing a JSON document to a list of values. + */ +const failable<list<value>, std::string> readJSON(const list<std::string>& ilist, const JSONContext& cx) { + jsval val; + JSONParser* parser = JS_BeginJSONParse(cx, &val); + if(parser == NULL) + return mkfailure<list<value>, std::string>("JS_BeginJSONParse failed"); + + const failable<bool, std::string> consumed = consume(parser, ilist, cx); + + if(!JS_FinishJSONParse(cx, parser, JSVAL_NULL)) + return mkfailure<list<value>, std::string>("JS_FinishJSONParse failed"); + if(!hasValue(consumed)) + return mkfailure<list<value>, std::string>(reason(consumed)); + + return list<value>(jsValToValue(val, cx)); +} + +/** + * Converts a list of values to JS array elements. + */ +JSObject* valuesToJSElements(JSObject* a, const list<value>& l, int i, const JSONContext& cx) { + const jsval valueToJSVal(const value& val, const JSONContext& cx); + if (isNil(l)) + return a; + jsval pv = valueToJSVal(car(l), cx); + JS_SetElement(cx, a, i, &pv); + return valuesToJSElements(a, cdr(l), ++i, cx); +} + +/** + * Returns true if a list represents a JS array. + */ +const bool isJSArray(const list<value>& l) { + if(isNil(l)) + return false; + const value v = car(l); + if(isList(v)) { + const list<value> p = v; + if(isSymbol(car(p))) + return false; + } + return true; +} + + + +/** + * Converts a list of values to JS properties. + */ +JSObject* valuesToJSProperties(JSObject* o, const list<value>& l, const JSONContext& cx) { + const jsval valueToJSVal(const value& val, const JSONContext& cx); + if(isNil(l)) + return o; + const list<value> p = car(l); + jsval pv = valueToJSVal(caddr(p), cx); + JS_SetProperty(cx, o, ((std::string)cadr(p)).c_str(), &pv); + return valuesToJSProperties(o, cdr(l), cx); +} + +/** + * Converts a value to a JS val. + */ +const jsval valueToJSVal(const value& val, const JSONContext& cx) { + switch(type(val)) { + case value::String: + case value::Symbol: { + return STRING_TO_JSVAL(JS_NewStringCopyZ(cx, ((std::string)val).c_str())); + } + case value::Bool: { + return BOOLEAN_TO_JSVAL((bool)val); + } + case value::Number: { + return DOUBLE_TO_JSVAL(JS_NewDouble(cx, (double)val)); + } + case value::List: { + if (isJSArray(val)) + return OBJECT_TO_JSVAL(valuesToJSElements(JS_NewArrayObject(cx, 0, NULL), val, 0, cx)); + return OBJECT_TO_JSVAL(valuesToJSProperties(JS_NewObject(cx, NULL, NULL, NULL), val, cx)); + } + default: { + return JSVAL_VOID; + } + } +} + +const failable<bool, std::string> writeList(const list<value>& l, JSObject* o, const JSONContext& cx) { + if (isNil(l)) + return true; + + // Write an attribute + const value token(car(l)); + + if (isTaggedList(token, attribute)) { + jsval pv = valueToJSVal(attributeValue(token), cx); + JS_SetProperty(cx, o, std::string(attributeName(token)).c_str(), &pv); + + } else if (isTaggedList(token, element)) { + + // Write the value of an element + if (elementHasValue(token)) { + jsval pv = valueToJSVal(elementValue(token), cx); + JS_SetProperty(cx, o, std::string(elementName(token)).c_str(), &pv); + + } else { + + // Write a parent element + JSObject* child = JS_NewObject(cx, NULL, NULL, NULL); + jsval pv = OBJECT_TO_JSVAL(child); + JS_SetProperty(cx, o, std::string(elementName(token)).c_str(), &pv); + + // Write its children + const failable<bool, std::string> w = writeList(elementChildren(token), child, cx); + if (!hasValue(w)) + return w; + } + } + + // Go on + return writeList(cdr(l), o, cx); +} + +/** + * Context passed to the JSON write callback function. + */ +template<typename R> class WriteContext { +public: + WriteContext(const lambda<R(std::string, R)>& reduce, const R& accum, const JSONContext& cx) : cx(cx), reduce(reduce), accum(accum) { + } + const JSONContext& cx; + const lambda<R(std::string, R)> reduce; + R accum; +}; + +/** + * Called by JS_Stringify to write JSON out. + */ +template<typename R> JSBool writeCallback(const jschar *buf, uint32 len, void *data) { + WriteContext<R>& wcx = *(static_cast<WriteContext<R>*> (data)); + JSString* jstr = JS_NewUCStringCopyN(wcx.cx, buf, len); + wcx.accum = wcx.reduce(std::string(JS_GetStringBytes(jstr), JS_GetStringLength(jstr)), wcx.accum); + return JS_TRUE; +} + +/** + * Convert a list of values to a JSON document. + */ +template<typename R> const failable<R, std::string> writeJSON(const lambda<R(std::string, R)>& reduce, const R& initial, const list<value>& l, const JSONContext& cx) { + JSObject* o = JS_NewObject(cx, NULL, NULL, NULL); + jsval val = OBJECT_TO_JSVAL(o); + const failable<bool, std::string> w = writeList(l, o, cx); + if (!hasValue(w)) + return mkfailure<R, std::string>(reason(w)); + + WriteContext<R> wcx(reduce, initial, cx); + if (!JS_Stringify(cx, &val, NULL, JSVAL_NULL, writeCallback<R>, &wcx)) + return mkfailure<R, std::string>("JS_Stringify failed"); + return wcx.accum; +} + +/** + * Convert a list of values to a list of strings representing a JSON document. + */ +const failable<list<std::string>, std::string> writeJSON(const list<value>& l, const JSONContext& cx) { + const failable<list<std::string>, std::string> ls = writeJSON<list<std::string> >(rcons<std::string>, list<std::string>(), l, cx); + if (!hasValue(ls)) + return ls; + return reverse(list<std::string>(ls)); +} + +/** + * Convert a function + params to a JSON request. + */ +const failable<list<std::string>, std::string> jsonRequest(const value& id, const value& func, const value& params, json::JSONContext& cx) { + const list<value> r = mklist<value>(mklist<value>("id", id), mklist<value>("method", func), mklist<value>("params", params)); + return writeJSON(valuesToElements(r), cx); +} + +/** + * Convert a value to a JSON result. + */ +const failable<list<std::string>, std::string> jsonResult(const value& id, const value& val, JSONContext& cx) { + return writeJSON(valuesToElements(mklist<value>(mklist<value>("id", id), mklist<value>("result", val))), cx); +} + +} +} + +#endif /* tuscany_json_hpp */ diff --git a/sca-cpp/trunk/modules/scdl/Makefile.am b/sca-cpp/trunk/modules/scdl/Makefile.am new file mode 100644 index 0000000000..55a190579c --- /dev/null +++ b/sca-cpp/trunk/modules/scdl/Makefile.am @@ -0,0 +1,28 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +noinst_PROGRAMS = scdl-test + +nobase_include_HEADERS = *.hpp + +INCLUDES = -I. -I$(top_builddir)/kernel -I${LIBXML2_INCLUDE} -I${APR_INCLUDE} + +scdl_test_SOURCES = scdl-test.cpp +scdl_test_LDADD = -lpthread -L${LIBXML2_LIB} -lxml2 -L${APR_LIB} -lapr-1 -laprutil-1 + +TESTS = scdl-test + diff --git a/sca-cpp/trunk/modules/scdl/scdl-test b/sca-cpp/trunk/modules/scdl/scdl-test Binary files differnew file mode 100755 index 0000000000..acef45d225 --- /dev/null +++ b/sca-cpp/trunk/modules/scdl/scdl-test diff --git a/sca-cpp/trunk/modules/scdl/scdl-test.cpp b/sca-cpp/trunk/modules/scdl/scdl-test.cpp new file mode 100644 index 0000000000..c3da1013a8 --- /dev/null +++ b/sca-cpp/trunk/modules/scdl/scdl-test.cpp @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * Test SCDL read functions. + */ + +#include <assert.h> +#include <iostream> +#include <sstream> +#include <fstream> +#include <string> +#include "slist.hpp" +#include "scdl.hpp" + +namespace tuscany { +namespace scdl { + +bool testComposite() { + std::ifstream is("test.composite"); + const list<value> c = readXML(streamList(is)); + return true; +} + +bool testComponents() { + std::ifstream is("test.composite"); + const list<value> c = components(readXML(streamList(is))); + assert(length(c) == 4); + + const value store = car(c); + assert(name(store) == std::string("Store")); + const value impl = implementation(store); + assert(uri(impl) == std::string("store.html")); + assert(implementationType(impl) == "t:implementation.widget"); + + const value catalog = named(std::string("Catalog"), c); + assert(name(catalog) == std::string("Catalog")); + return true; +} + +bool testServices() { + std::ifstream is("test.composite"); + const list<value> c = components(readXML(streamList(is))); + const value store = car(c); + + assert(length(services(store)) == 1); + const value widget = car(services(store)); + assert(name(widget) == std::string("Widget")); + + assert(length(bindings(widget)) == 1); + const value binding = car(bindings(widget)); + assert(uri(binding) == std::string("/store")); + assert(bindingType(binding) == "t:binding.http"); + return true; +} + +bool testReferences() { + std::ifstream is("test.composite"); + const list<value> c = components(readXML(streamList(is))); + const value store = car(c); + + assert(length(references(store)) == 3); + const value catalog = car(references(store)); + assert(name(catalog) == std::string("catalog")); + assert(target(catalog) == std::string("Catalog")); + + assert(length(bindings(catalog)) == 1); + const value binding = car(bindings(catalog)); + assert(uri(binding) == value()); + assert(bindingType(binding) == "t:binding.jsonrpc"); + return true; +} + +bool testProperties() { + std::ifstream is("test.composite"); + const list<value> c = components(readXML(streamList(is))); + const value catalog = named(std::string("Catalog"), c); + + assert(length(properties(catalog)) == 1); + const value currencyCode = car(properties(catalog)); + assert(name(currencyCode) == std::string("currencyCode")); + assert(propertyValue(currencyCode) == std::string("USD")); + return true; +} + +} +} + +int main() { + std::cout << "Testing..." << std::endl; + + tuscany::scdl::testComposite(); + tuscany::scdl::testComponents(); + tuscany::scdl::testServices(); + tuscany::scdl::testReferences(); + tuscany::scdl::testProperties(); + + std::cout << "OK" << std::endl; + + return 0; +} diff --git a/sca-cpp/trunk/modules/scdl/scdl.hpp b/sca-cpp/trunk/modules/scdl/scdl.hpp new file mode 100644 index 0000000000..4d84610fb0 --- /dev/null +++ b/sca-cpp/trunk/modules/scdl/scdl.hpp @@ -0,0 +1,158 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_scdl_hpp +#define tuscany_scdl_hpp + +/** + * SCDL read functions. + */ + +#include <string> +#include "list.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "xml.hpp" + +namespace tuscany { +namespace scdl { + +/** + * Returns a list of components in a composite. + */ +const list<value> components(const value& l) { + const list<value> cs = elementChildren("composite", l); + if (isNil(cs)) + return cs; + return elementChildren("component", car(cs)); +} + +/** + * Returns the name of a component, service or reference. + */ +const value name(const value& l) { + return attributeValue("name", l); +} + +/** + * Returns the scdl declaration with the given name. + */ +struct filterName { + const value n; + filterName(const value& n) : n(n) { + } + const bool operator()(const value& v) const { + return name(v) == n; + } +}; + +const value named(const value& name, const value& l) { + const list<value> c = filter<value>(filterName(name), l); + if (isNil(c)) + return value(); + return car(c); +} + +/** + * Returns the implementation of a component. + */ +const bool filterImplementation(const value& v) { + return isElement(v) && std::string(cadr<value>(v)).find("implementation.") != std::string::npos; +} + +const value implementation(const value& l) { + const list<value> n = filter<value>(filterImplementation, l); + if (isNil(n)) + return value(); + return car(n); +} + +/** + * Returns the URI of a service, reference or implementation. + */ +const value uri(const value& l) { + return attributeValue("uri", l); +} + +/** + * Returns a list of services in a component. + */ +const list<value> services(const value& l) { + return elementChildren("service", l); +} + +/** + * Returns a list of references in a component. + */ +const list<value> references(const value& l) { + return elementChildren("reference", l); +} + +/** + * Returns a list of properties in a component. + */ +const list<value> properties(const value& l) { + return elementChildren("property", l); +} + +/** + * Returns the target of a reference. + */ +const value target(const value& l) { + return attributeValue("target", l); +} + +/** + * Returns a list of bindings in a service or reference. + */ +const bool filterBinding(const value& v) { + return isElement(v) && std::string(cadr<value>(v)).find("binding.") != std::string::npos; +} + +const list<value> bindings(const value& l) { + return filter<value>(filterBinding, l); +} + +/** + * Returns the type of an implementation. + */ +const value implementationType(const value& l) { + return elementName(l); +} + +/** + * Returns the type of a binding. + */ +const value bindingType(const value& l) { + return elementName(l); +} + +/** + * Returns the value of a property. + */ +const value propertyValue(const value& l) { + return elementValue(l); +} + +} +} + +#endif /* tuscany_scdl_hpp */ diff --git a/sca-cpp/trunk/modules/scdl/test.composite b/sca-cpp/trunk/modules/scdl/test.composite new file mode 100644 index 0000000000..b315f23f9a --- /dev/null +++ b/sca-cpp/trunk/modules/scdl/test.composite @@ -0,0 +1,67 @@ +<?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/200903" + xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1" + targetNamespace="http://store" + name="store"> + + <component name="Store"> + <t:implementation.widget uri="store.html"/> + <service name="Widget"> + <t:binding.http uri="/store"/> + </service> + <reference name="catalog" target="Catalog"> + <t:binding.jsonrpc/> + </reference> + <reference name="shoppingCart" target="ShoppingCart/Cart"> + <t:binding.atom/> + </reference> + <reference name="shoppingTotal" target="ShoppingCart/Total"> + <t:binding.jsonrpc/> + </reference> + </component> + + <component name="Catalog"> + <t:implementation.scheme uri="fruits-catalog.scm"/> + <property name="currencyCode">USD</property> + <service name="Catalog"> + <t:binding.jsonrpc/> + </service> + <reference name="currencyConverter" target="CurrencyConverter"/> + </component> + + <component name="ShoppingCart"> + <t:implementation.scheme uri="shopping-cart.scm"/> + <service name="Cart"> + <t:binding.atom uri="/ShoppingCart"/> + </service> + <service name="Total"> + <t:binding.jsonrpc/> + </service> + <reference name="cache"> + <binding.memcached uri="localhost:11311"/> + </reference> + </component> + + <component name="CurrencyConverter"> + <t:implementation.scheme uri="currency-converter.scm"/> + </component> + +</composite> |